pax_global_header00006660000000000000000000000064150722631530014516gustar00rootroot0000000000000052 comment=1c4beefc2da81e02f9b7cfdb3994713e0ffa763b astropy-astropy-201cddb/000077500000000000000000000000001507226315300153775ustar00rootroot00000000000000astropy-astropy-201cddb/.astropy-root000066400000000000000000000000001507226315300200500ustar00rootroot00000000000000astropy-astropy-201cddb/.circleci/000077500000000000000000000000001507226315300172325ustar00rootroot00000000000000astropy-astropy-201cddb/.circleci/config.yml000066400000000000000000000070771507226315300212350ustar00rootroot00000000000000version: 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 >> 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 >> 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-mpl360-cov" - "py311-test-image-mpldev-cov" - deploy-reference-images: name: baseline-<< matrix.jobname >> matrix: parameters: jobname: - "py311-test-image-mpl360-cov" - "py311-test-image-mpldev-cov" requires: - << matrix.jobname >> filters: branches: only: - main astropy-astropy-201cddb/.devcontainer/000077500000000000000000000000001507226315300201365ustar00rootroot00000000000000astropy-astropy-201cddb/.devcontainer/devcontainer.json000066400000000000000000000011111507226315300235040ustar00rootroot00000000000000{ "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" ] } } } astropy-astropy-201cddb/.devcontainer/welcome-message.txt000066400000000000000000000011211507226315300237470ustar00rootroot00000000000000👋 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] ⭐⭐ ================================================================================== ⭐⭐ astropy-astropy-201cddb/.flake8000066400000000000000000000021451507226315300165540ustar00rootroot00000000000000[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 astropy-astropy-201cddb/.git-blame-ignore-revs000066400000000000000000000033531507226315300215030ustar00rootroot00000000000000# 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 astropy-astropy-201cddb/.gitattributes000066400000000000000000000001121507226315300202640ustar00rootroot00000000000000*.fits -text astropy/io/fits/tiled_compression/tests/data/*fits binary astropy-astropy-201cddb/.github/000077500000000000000000000000001507226315300167375ustar00rootroot00000000000000astropy-astropy-201cddb/.github/CODEOWNERS000066400000000000000000000043501507226315300203340ustar00rootroot00000000000000# 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 astropy/io/registry @nstarman # astropy/io/votable astropy/modeling @astropy/modeling # astropy/nddata # astropy/samp astropy/stats @larrybradley astropy/table @taldcroft 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 docs/time @taldcroft # docs/timeseries # docs/uncertainty # docs/units # docs/utils docs/visualization @astrofrog @larrybradley docs/wcs @mcara .pre-commit-config.yaml @WilliamJamieson @nstarman .ruff.toml @WilliamJamieson @nstarman astropy-astropy-201cddb/.github/ISSUE_TEMPLATE/000077500000000000000000000000001507226315300211225ustar00rootroot00000000000000astropy-astropy-201cddb/.github/ISSUE_TEMPLATE/bug_report.yaml000066400000000000000000000046051507226315300241630ustar00rootroot00000000000000name: 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 try: astropy.system_info() except AttributeError: import platform; print(platform.platform()) import sys; print("Python", sys.version) import astropy; print("astropy", astropy.__version__) import numpy; print("Numpy", numpy.__version__) import erfa; print("pyerfa", erfa.__version__) try: import scipy print("Scipy", scipy.__version__) except ImportError: print("Scipy not installed") try: import matplotlib print("Matplotlib", matplotlib.__version__) except ImportError: print("Matplotlib not installed") ``` ``` # Paste the result here ``` astropy-astropy-201cddb/.github/ISSUE_TEMPLATE/config.yml000066400000000000000000000002701507226315300231110ustar00rootroot00000000000000contact_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." astropy-astropy-201cddb/.github/ISSUE_TEMPLATE/feature_request.yaml000066400000000000000000000025371507226315300252200ustar00rootroot00000000000000name: 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. astropy-astropy-201cddb/.github/PULL_REQUEST_TEMPLATE.md000066400000000000000000000040001507226315300225320ustar00rootroot00000000000000 ### 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. astropy-astropy-201cddb/.github/dependabot.yml000066400000000000000000000011171507226315300215670ustar00rootroot00000000000000# 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 schedule: interval: "monthly" groups: actions: patterns: - "*" astropy-astropy-201cddb/.github/labeler.yml000066400000000000000000000111071507226315300210700ustar00rootroot00000000000000Docs: - 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 - pyproject.toml - 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 astropy-astropy-201cddb/.github/workflows/000077500000000000000000000000001507226315300207745ustar00rootroot00000000000000astropy-astropy-201cddb/.github/workflows/CFF-test.yml000066400000000000000000000011161507226315300230710ustar00rootroot00000000000000name: 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@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - uses: citation-file-format/cffconvert-github-action@4cf11baa70a673bfdf9dad0acc7ee33b3f4b6084 # 2.0.0 with: args: --validate astropy-astropy-201cddb/.github/workflows/check_changelog.yml000066400000000000000000000010461507226315300246040ustar00rootroot00000000000000name: 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 steps: - uses: scientific-python/action-towncrier-changelog@1d7332022f76e36fe8ce2d716b851f3f98063c62 # v1.0.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} BOT_USERNAME: gilesbot astropy-astropy-201cddb/.github/workflows/check_milestone.yml000066400000000000000000000021511507226315300246520ustar00rootroot00000000000000name: 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@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.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.`); } astropy-astropy-201cddb/.github/workflows/ci_benchmark.yml000066400000000000000000000063301507226315300241260ustar00rootroot00000000000000### 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@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false fetch-depth: 0 - uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.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@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 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@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 if: always() with: name: asv-benchmark-results path: | results/ astropy-astropy-201cddb/.github/workflows/ci_cron_daily.yml000066400000000000000000000042641507226315300243230ustar00rootroot00000000000000name: 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" 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@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false fetch-depth: 0 - name: Set up Python uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.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}} astropy-astropy-201cddb/.github/workflows/ci_cron_weekly.yml000066400000000000000000000141371507226315300245210ustar00rootroot00000000000000name: 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' 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.13 with dev version of infrastructure dependencies os: ubuntu-latest python: '3.13' toxenv: py313-test-devinfra - name: Documentation link check os: ubuntu-latest python: '3.x' toxenv: linkcheck toxposargs: --color steps: - name: Checkout code uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false fetch-depth: 0 - name: Set up Python uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.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}} 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: Python 3.12 # 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@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false fetch-depth: 0 - uses: uraimo/run-on-arch-action@4141da824ffb5eda88d221d9cf835f6a61ed98d9 # v3.0.0 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 pip3 install -v --no-build-isolation -e .[test] pip3 list python3 -m pytest --pyargs astropy -m "not hypothesis" astropy-astropy-201cddb/.github/workflows/ci_workflows.yml000066400000000000000000000106051507226315300242310ustar00rootroot00000000000000name: 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@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.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}`); } tests: needs: [initial_checks] uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@28e947497bed4d6ec3fa1d66d198e95a1d17bc63 # v2.2.1 secrets: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} with: setenv: | ARCH_ON_CI: "normal" IS_CRON: "false" 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 - name: Python 3.12 in Parallel with all optional dependencies linux: py312-test-alldeps-fitsio libraries: apt: - language-pack-fr - tzdata - libbz2-dev - libcfitsio-dev toxargs: -v --develop posargs: -n=4 --run-slow - 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.12 with all optional dependencies (Windows) windows: py312-test-alldeps posargs: --durations=50 - name: Python 3.12 with all optional dependencies (MacOS X) macos: py312-test-alldeps posargs: --durations=50 --run-slow runs-on: macos-latest # FIXME: Add aarch64 to name when bump Python version. - name: Python 3.11 Double test (Run tests twice) linux: py311-test-double runs-on: ubuntu-24.04-arm posargs: -n=4 setenv: | ARCH_ON_CI: "arm64" IS_CRON: "false" allowed_failures: needs: [initial_checks] uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@28e947497bed4d6ec3fa1d66d198e95a1d17bc63 # v2.2.1 with: setenv: | ARCH_ON_CI: "normal" IS_CRON: "false" submodules: false coverage: '' libraries: | apt: - language-pack-de - tzdata envs: | - name: (Allowed Failure) Python 3.13 with remote data and dev version of key dependencies linux: py313-test-devdeps posargs: --remote-data=any --verbose test_wheel_building: needs: [initial_checks] # 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@28e947497bed4d6ec3fa1d66d198e95a1d17bc63 # v2.2.1 with: upload_to_pypi: false upload_to_anaconda: false test_extras: test test_command: pytest -p no:warnings --astropy-header -m "not hypothesis" -k "not test_data_out_of_range and not test_set_locale and not TestQuantityTyping" --pyargs astropy targets: | - cp311-manylinux_x86_64 astropy-astropy-201cddb/.github/workflows/codeql-analysis.yml000066400000000000000000000062641507226315300246170ustar00rootroot00000000000000# 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@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 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@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.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 astropy-astropy-201cddb/.github/workflows/open_actions.yml000066400000000000000000000140311507226315300241770ustar00rootroot00000000000000name: "When Opened" on: issues: types: - opened 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@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.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.` }) - name: Greet new contributors uses: actions/first-interaction@753c925c8d1ac6fede23781875376600628d9b5d # v3.0.0 with: repo-token: "${{ secrets.GITHUB_TOKEN }}" issue-message: > Welcome to Astropy 👋 and thank you for your first issue! A project member will respond to you as soon as possible; in the meantime, please double-check the [guidelines for submitting issues](https://github.com/astropy/astropy/blob/main/CONTRIBUTING.md#reporting-issues) and make sure you've provided the requested details. GitHub issues in the Astropy repository are used to track bug reports and feature requests; If your issue poses a question about how to use Astropy, please instead raise your question in the [Astropy Discourse user forum](https://community.openastronomy.org/c/astropy/8) and close this issue. If you feel that this issue has not been responded to in a timely manner, please send a message directly to the [development mailing list](http://groups.google.com/group/astropy-dev). If the issue is urgent or sensitive in nature (e.g., a security vulnerability) please send an e-mail directly to the private e-mail feedback@astropy.org. pr-message: > Welcome to Astropy 👋 and congratulations on your first pull request! 🎉 A project member will respond to you as soon as possible; in the meantime, please have a look over the [Checklist for Contributed Code](https://github.com/astropy/astropy/blob/main/CONTRIBUTING.md#checklist-for-contributed-code) and make sure you've addressed as many of the questions there as possible. If you feel that this pull request has not been responded to in a timely manner, please send a message directly to the [development mailing list](http://groups.google.com/group/astropy-dev). If the issue is urgent or sensitive in nature (e.g., a security vulnerability) please send an e-mail directly to the private e-mail feedback@astropy.org. - name: 'Comment Draft PR' uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 if: github.event.pull_request.draft == true 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 draft pull request! Do you know that you can use `[ci skip]` or `[skip ci]` in your commit messages to skip running continuous integration tests until you are ready?' }) # 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 }} astropy-astropy-201cddb/.github/workflows/publish.yml000066400000000000000000000102211507226315300231610ustar00rootroot00000000000000name: 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_and_publish: # This does the actual wheel building and publishing 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@28e947497bed4d6ec3fa1d66d198e95a1d17bc63 # v2.2.1 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 upload to PyPI for all tags starting with v but not ones ending in .dev upload_to_pypi: ${{ startsWith(github.ref, 'refs/tags/v') && !endsWith(github.ref, '.dev') && (github.event_name == 'push') }} upload_to_anaconda: ${{ (github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') }} anaconda_user: astropy anaconda_package: astropy anaconda_keep_n_latest: 10 # For nightly wheels as well as when building with the 'Build all wheels' label, we disable # the build isolation and explicitly install the latest developer version of numpy as well as # the latest stable versions of all other build-time dependencies. env: | CIBW_BEFORE_BUILD: '${{ ((github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || contains(github.event.pull_request.labels.*.name, 'Build all wheels')) && 'pip install -U --pre --extra-index-url https://pypi.anaconda.org/scientific-python-nightly-wheels/simple setuptools>=77.0.0 setuptools_scm cython numpy>=0.0.dev0 extension-helpers') || '' }}' CIBW_BUILD_FRONTEND: '${{ ((github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || contains(github.event.pull_request.labels.*.name, 'Build all wheels')) && 'pip; args: --no-build-isolation') || 'build' }}' 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 -p no:warnings --astropy-header -m "not hypothesis" -k "not test_data_out_of_range and not test_set_locale and not TestQuantityTyping" --pyargs astropy targets: | # Linux wheels - cp311-manylinux_x86_64 - cp312-manylinux_x86_64 - cp313-manylinux_x86_64 - cp314-manylinux_x86_64 - target: cp311-manylinux_aarch64 runs-on: ubuntu-24.04-arm - target: cp312-manylinux_aarch64 runs-on: ubuntu-24.04-arm - target: cp313-manylinux_aarch64 runs-on: ubuntu-24.04-arm - target: cp314-manylinux_aarch64 runs-on: ubuntu-24.04-arm # Note that following wheels are not currently tested: - cp311-musllinux_x86_64 - cp312-musllinux_x86_64 - cp313-musllinux_x86_64 - cp314-musllinux_x86_64 # MacOS X wheels - as noted in https://github.com/astropy/astropy/pull/12379 we deliberately # do not build universal2 wheels. - cp311*macosx_x86_64 - cp312*macosx_x86_64 - cp313*macosx_x86_64 - cp314*macosx_x86_64 - cp311*macosx_arm64 - cp312*macosx_arm64 - cp313*macosx_arm64 - cp314*macosx_arm64 # Windows wheels - cp311*win32 - cp312*win32 - cp313*win32 - cp314*win32 - cp311*win_amd64 - cp312*win_amd64 - cp313*win_amd64 - cp314*win_amd64 secrets: pypi_token: ${{ secrets.pypi_token }} anaconda_token: ${{ secrets.anaconda_token }} astropy-astropy-201cddb/.github/workflows/stalebot.yml000066400000000000000000000011121507226315300233270ustar00rootroot00000000000000name: 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@aed4b8aa0b1cbdb4ef09d476e46ebe0768e75d4b # main env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} STALEBOT_MAX_ISSUES: -1 STALEBOT_MAX_PRS: -1 STALEBOT_SLEEP: 10 astropy-astropy-201cddb/.github/workflows/update_astropy_iers_data_pin.py000066400000000000000000000013711507226315300272740ustar00rootroot00000000000000import sys from pathlib import Path import requests metadata = requests.get( "https://pypi.org/pypi/astropy-iers-data/json", timeout=10 ).json() last_version = metadata["info"]["version"] project_file = Path("pyproject.toml") lines = project_file.read_text().splitlines() changed_lines = 0 for iline in range(len(lines)): if "astropy-iers-data" in lines[iline]: changed_lines += 1 lines[iline] = f' "astropy-iers-data>={last_version}",' if changed_lines == 0: print(f"No line found containing astropy-iers-data in {project_file}") sys.exit(1) elif changed_lines > 1: print(f"More than one line found containing astropy-iers-data in {project_file}") sys.exit(1) project_file.write_text("\n".join(lines) + "\n") astropy-astropy-201cddb/.github/workflows/update_astropy_iers_data_pin.yml000066400000000000000000000041441507226315300274460ustar00rootroot00000000000000# 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: schedule: - cron: '0 0 2 * *' # Monthly workflow_dispatch: permissions: contents: read jobs: update-astropy-iers-data-pin: permissions: contents: write # for peter-evans/create-pull-request to create branch pull-requests: write # for peter-evans/create-pull-request 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@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - name: Set up Python uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 with: python-version: 3.x - name: Install dependencies run: pip install requests - name: Run update script run: python .github/workflows/update_astropy_iers_data_pin.py - name: Commit changes run: | git config user.name github-actions git config user.email github-actions@github.com git add pyproject.toml if ! git diff --cached --exit-code; then git commit -m "Update minimum required version of astropy-iers-data" fi - name: Create Pull Request uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8 with: branch: update-astropy-iers-data-pin branch-suffix: timestamp delete-branch: true labels: no-changelog-entry-needed, utils.iers title: Update minimum required version of astropy-iers-data body: | This is an automated update of the minimum version of astropy-iers-data package. :pray: Please apply backport labels to any active backport branches for v6.0.x or later. :pray: :warning: Please close and re-open this pull request to trigger the CI. :warning: astropy-astropy-201cddb/.gitignore000066400000000000000000000024101507226315300173640ustar00rootroot00000000000000# Compiled files *.py[cod] *.a *.o *.so *.pyd *.dll __pycache__ # Ignore .c files by default to avoid including generated code. If you want to # 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/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 *.egg-info dist build eggs .eggs parts bin var 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 astropy-astropy-201cddb/.mailmap000066400000000000000000000622211507226315300170230ustar00rootroot00000000000000Aarya Patil Aarya Patil Adam Ginsburg Adam Ginsburg Adam Ginsburg Adele Plunkett Adrian Price-Whelan Adrian Price-Whelan Akshat Dixit Akshat1Nar Albert Y. Shih Aleh Khvalko Aleksi Suutarinen Alex Conley Alex Conley 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 Benjamin Roulston Benjamin Winkel Bhavya Khandelwal Bogdan Nicula Brett Graham Brett Morris Brett Morris Brett Morris Brett Morris Brett M. 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 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 Dan Ryan Daniel Ryan DanRyanIrish 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 Deen-Dot Deen-dot <80238871+Deen-dot@users.noreply.github.com> Demitri Muna Demitri Muna Demitri Muna Derek Homeier Derek Homeier Derek Homeier <709020+dhomeier@users.noreply.github.com> Diego Asterio de Zaballa Douglas Burke Dylan Gregersen Edward Gomez Edward Slavich Ed 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 Emir Esteban Pardo SÃĄnchez Francesco Montesano Gabriel Brammer Gabriel Brammer Gabriel Brammer Gabe Brammer Gabriel Perren Gabriel Perren Geert Barentsen George Galvin Gerrit Schellenberger Gilles Landais Giorgio Calderone Graham Kanarek Guillaume Pernot Guillaume Pernot Gustavo Bragança Hannes Breytenbach Hans Moritz GÃŧnther Hans Moritz GÃŧnther Harry Ferguson Henrik Norman Henrik Norman Henry Schreiner HÊlvio Peixoto Himanshu Pathak Humna Awan Igor Lemos Ivo Busko Ivo Busko Jaime AndrÊs Jake VanderPlas Jake VanderPlas Jake VanderPlas James Davies James McCormac James Tocknell James Tocknell 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 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 Kang Wang Karan Grover Karl Gordon Karl D. Gordon Karl Vyhmeister Kelle Cruz Kevin Gullikson Kirill Tchernyshyov Kris Stern Kris Stern Kunam Balaram Reddy Kyle Barbary 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 luz paz Maximilian Linhoff Maximilian NÃļthe Maximilian Linhoff 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 H. van Kerkwijk Marten van Kerkwijk Marten Henric van Kerkwijk Matt Davis Matteo Bachetti Matthew Craig Matthias Stein Matthieu Baumann Mavani Bhautik 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 Mubin17 <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> Ricky O'Steen <39831871+rosteen@users.noreply.github.com> Ritiek Malhotra Ritwick DSouza Robel Geda Robel Geda robelgeda 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 Sebastian 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 AlinderS Simon Conseil Simon Conseil Simon Conseil Simon Conseil Simon Conseil Simon Liedtke Somia Floret Somia FLORET Somia Floret Somia Floret <57394764+somilia@users.noreply.github.com> Sourabh Cheedella Steve Crawford Steve Crawford Steve Crawford Stuart Littlefair Stuart Mumford Sudheesh Singanamalla Sudheesh Singanamalla 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 Timothy Ellsworth Bowers Tom Aldcroft Tom Donaldson Tom J Wilson Tyler Finethy VSN Reddy Janga Vishnunarayan K I Vital FernÃĄndez William Jamieson Yannick Copin Yash Kumar Yash Nandwana Yash Sharma Yingqi Ying <33911276+dyq0811@users.noreply.github.com> Zach Edwards Zac Hatfield-Dodds ZÊ Vinicius Zhiyuan Ma Jerry Ma Zhiyuan Ma Zhiyuan Ma astropy-astropy-201cddb/.pre-commit-config.yaml000066400000000000000000000063751507226315300216730ustar00rootroot00000000000000ci: autofix_prs: false autoupdate_schedule: 'monthly' repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v5.0.0 hooks: - id: check-added-large-files args: ["--enforce-all", "--maxkb=300"] exclude: "^(\ 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/woodruffw/zizmor-pre-commit rev: v1.6.0 hooks: - id: zizmor - repo: https://github.com/codespell-project/codespell rev: v2.4.1 hooks: - id: codespell args: ["--write-changes"] additional_dependencies: - tomli - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.11.8 hooks: - id: ruff args: ["--fix", "--show-fixes"] - id: ruff-format - repo: https://github.com/scientific-python/cookie rev: 2025.05.02 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 astropy-astropy-201cddb/.pycodestyle000066400000000000000000000001141507226315300177400ustar00rootroot00000000000000[pycodestyle] max-line-length = 88 exclude = extern,*parsetab.py,*lextab.py astropy-astropy-201cddb/.pyinstaller/000077500000000000000000000000001507226315300200235ustar00rootroot00000000000000astropy-astropy-201cddb/.pyinstaller/hooks/000077500000000000000000000000001507226315300211465ustar00rootroot00000000000000astropy-astropy-201cddb/.pyinstaller/hooks/hook-astropy_iers_data.py000066400000000000000000000001501507226315300261660ustar00rootroot00000000000000from PyInstaller.utils.hooks import collect_data_files datas = collect_data_files("astropy_iers_data") astropy-astropy-201cddb/.pyinstaller/hooks/hook-skyfield.py000066400000000000000000000003611507226315300242700ustar00rootroot00000000000000# 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") astropy-astropy-201cddb/.pyinstaller/run_astropy_tests.py000066400000000000000000000113331507226315300242050ustar00rootroot00000000000000import 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", ] # 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", ], ) ) astropy-astropy-201cddb/.readthedocs.yaml000066400000000000000000000011251507226315300206250ustar00rootroot00000000000000version: 2 build: os: "ubuntu-22.04" tools: python: "mambaforge-4.10" 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: [] astropy-astropy-201cddb/.ruff.toml000066400000000000000000000245031507226315300173200ustar00rootroot00000000000000extend = "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 "D300", # triple-single-quotes "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. # 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 "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 # 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` # 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 # flake8-quotes (Q) "Q000", # use double quotes ] lint.unfixable = [ "E711" # NoneComparison. Hard to fix b/c numpy has it's own None. ] [lint.extend-per-file-ignores] "__init__.py" = ["F401"] "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/*" = [] "astropy/constants/*" = [] "astropy/convolution/*" = [] "astropy/coordinates/*" = [ "PTH", # all flake8-use-pathlib "DTZ007", # call-datetime-strptime-without-zone "RET504", # unnecessary-assign ] "astropy/cosmology/*" = [ "PT019", # pytest-fixture-param-without-value "RET504", # unnecessary-assign ] "astropy/io/*" = [ "G001", # logging-string-format "G004", # logging-f-string "PLR0911", # too-many-return-statements "PTH116", # os-stat "PTH117", # os-path-isabs "RET504", # unnecessary-assign "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 ] "astropy/io/fits/*" = [ "B007", # UnusedLoopControlVariable "PTH", ] "astropy/io/misc/*" = [ "B007", # UnusedLoopControlVariable ] "astropy/io/registry/*" = [ "PTH", ] "astropy/io/votable/*" = [ "B007", # UnusedLoopControlVariable "PTH", ] "astropy/logger.py" = [] "astropy/modeling/*" = [ "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/*" = [ "RET504", # unnecessary-assign ] "astropy/samp/*" = [ "PTH", # all flake8-use-pathlib "RET504", # unnecessary-assign ] "astropy/stats/*" = [ "B007", # UnusedLoopControlVariable "PLR0911", # too-many-return-statements "RET504", # unnecessary-assign ] "astropy/table/*" = [ "RET504", # unnecessary-assign "S605", # Starting a process with a shell, possible injection detected "TRY300", # Consider `else` block ] "astropy/tests/*" = [ "PTH", # all flake8-use-pathlib ] "astropy/time/*" = [ "B007", # UnusedLoopControlVariable "FIX003", # Line contains XXX. replace XXX with TODO "PIE794", # duplicate-class-field-definition "RET504", # unnecessary-assign ] "astropy/timeseries/*" = [ "PLR0911", # too-many-return-statements "RET504", # unnecessary-assign ] "astropy/units/*" = [ "N812", # lowercase-imported-as-non-lowercase "PLR0911", # too-many-return-statements "RET504", # unnecessary-assign "TRY300", # Consider `else` block ] "astropy/uncertainty/*" = [] "astropy/utils/*" = [ "B007", # UnusedLoopControlVariable "N811", # constant-imported-as-non-constant "PLR0911", # too-many-return-statements "PTH", # all flake8-use-pathlib "RET504", # unnecessary-assign "S321", # Suspicious-ftp-lib-usage "TRY300", # Consider `else` block ] "astropy/visualization/*" = [ "B015", "PLR0911", # too-many-return-statements ] "astropy/wcs/*" = [ "F821", # undefined-name "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" astropy-astropy-201cddb/CHANGES.rst000066400000000000000000024614711507226315300172200ustar00rootroot00000000000000Version 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/master/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 Tcmb0 Neff m_nu Ob0
FlatLambdaCDM Planck18 67.66 0.30966 2.7255 3.046 0.0 0.0 0.06 0.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 Tcmb0 Neff m_nu Ob0
FlatLambdaCDM Planck18 67.66 0.30966 2.7255 3.046 0.0 0.0 0.06 0.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) astropy-astropy-201cddb/astropy/cosmology/_src/io/builtin/latex.py000066400000000000000000000162751507226315300256200ustar00rootroot00000000000000r"""|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) astropy-astropy-201cddb/astropy/cosmology/_src/io/builtin/mapping.py000066400000000000000000000460401507226315300261270ustar00rootroot00000000000000# 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) astropy-astropy-201cddb/astropy/cosmology/_src/io/builtin/model.py000066400000000000000000000247051507226315300256000ustar00rootroot00000000000000# 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.""" cosmo = 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 }, ) return cosmo @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 result = getattr(cosmo, self._method_name)(*args[: self.n_inputs]) return result ############################################################################## 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 model = CosmoModel( **cosmology.parameters, name=cosmology.name, meta=copy.deepcopy(cosmology.meta) ) return model 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) astropy-astropy-201cddb/astropy/cosmology/_src/io/builtin/row.py000066400000000000000000000274661507226315300253160ustar00rootroot00000000000000# 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) astropy-astropy-201cddb/astropy/cosmology/_src/io/builtin/table.py000066400000000000000000000515021507226315300255620ustar00rootroot00000000000000# 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) astropy-astropy-201cddb/astropy/cosmology/_src/io/builtin/utils.py000066400000000000000000000065231507226315300256360ustar00rootroot00000000000000# 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 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", # ===== deprecated paths ===== "astropy.cosmology.flrw.base.FLRW": "astropy.cosmology.FLRW", "astropy.cosmology.flrw.lambdacdm.LambdaCDM": "astropy.cosmology.LambdaCDM", "astropy.cosmology.flrw.lambdacdm.FlatLambdaCDM": ( "astropy.cosmology.flrw.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.""" 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 col = Column( data=np.reshape(value, shape), name=parameter.name, dtype=None, # inferred from the data description=parameter.__doc__, format=None, meta=meta, ) return col 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, ) astropy-astropy-201cddb/astropy/cosmology/_src/io/builtin/yaml.py000066400000000000000000000174121507226315300254370ustar00rootroot00000000000000# 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. 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 from .utils import FULLQUALNAME_SUBSTITUTIONS as QNS __all__: list[str] = [] # nothing is publicly scoped ############################################################################## # 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__}" tag = "!" + QNS.get( fqn, fqn ) # Possibly sub fully qualified name for a preferred path 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) astropy-astropy-201cddb/astropy/cosmology/_src/io/connect.py000066400000000000000000000306711507226315300244620ustar00rootroot00000000000000# 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): cosmo = self.registry.read(self._cls, *args, **kwargs) return cosmo 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): cosmo = self.registry.read(self._cls, obj, *args, format=format, **kwargs) return cosmo 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) astropy-astropy-201cddb/astropy/cosmology/_src/parameter/000077500000000000000000000000001507226315300240215ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/cosmology/_src/parameter/__init__.py000066400000000000000000000011331507226315300261300ustar00rootroot00000000000000"""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 .dataclass_utils import all_parameters from .descriptors import ParametersAttribute astropy-astropy-201cddb/astropy/cosmology/_src/parameter/converter.py000066400000000000000000000053051507226315300264050ustar00rootroot00000000000000# 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 Any import astropy.units as u FValidateCallable = Callable[[object, object, Any], Any] _REGISTRY_FVALIDATORS: dict[str, FValidateCallable] = {} def _register_validator(key, fvalidate=None): """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, param, value): """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, param, value): """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, param, value): """""" 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, param, value): """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 astropy-astropy-201cddb/astropy/cosmology/_src/parameter/core.py000066400000000000000000000252711507226315300253320ustar00rootroot00000000000000# 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 Any, Union import astropy.units as u from .converter import _REGISTRY_FVALIDATORS, FValidateCallable, _register_validator class Sentinel(Enum): """Sentinel values for Parameter fields.""" MISSING = auto() """A sentinel value signifying a missing default.""" def __repr__(self): 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, cosmo_cls=None): # 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, value): """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): """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, value): """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=None): """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): """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)})" astropy-astropy-201cddb/astropy/cosmology/_src/parameter/dataclass_utils.py000066400000000000000000000016201507226315300275510ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst __all__: list[str] = [] # nothing is publicly scoped from dataclasses import Field from astropy.cosmology._src.utils import all_cls_vars from .core import Parameter def all_parameters(obj: object, /) -> dict[str, Field | Parameter]: """Get all `Parameter` fields of a dataclass. Parameters ---------- obj : object | type A dataclass. Returns ------- dict[str, Field | Parameter] All fields of the dataclass, including those not yet finalized in the class, if it's still under construction, e.g. in ``__init_subclass__``. """ return { k: (v if isinstance(v, Parameter) else v.default) for k, v in all_cls_vars(obj).items() if ( isinstance(v, Parameter) or (isinstance(v, Field) and isinstance(v.default, Parameter)) ) } astropy-astropy-201cddb/astropy/cosmology/_src/parameter/descriptors.py000066400000000000000000000046711507226315300267440ustar00rootroot00000000000000# 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) astropy-astropy-201cddb/astropy/cosmology/_src/setup_package.py000066400000000000000000000015501507226315300252270ustar00rootroot00000000000000# 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, ), ] astropy-astropy-201cddb/astropy/cosmology/_src/signature_deprecations.c000066400000000000000000000147171507226315300267600ustar00rootroot00000000000000// 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 #define PY_SSIZE_T_CLEAN #include #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); Py_TYPE(self)->tp_free((PyObject*)self); } static PyObject* depr_kws_wrap_new(PyTypeObject* type, PyObject* args, PyObject* kwds) { DeprKwsObject* self = (DeprKwsObject*)type->tp_alloc(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_GET_SIZE(names); for (i = 0; i < n_names; ++i) { PyObject* name = PyTuple_GET_ITEM(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}, {"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_GET_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_GET_ITEM(self->names, i); has_kw = PyDict_Contains(kwds, name); if (has_kw) { PyList_SET_ITEM(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_GET_ITEM(deprecated_kwargs, 0)); s = arguments = respectively = ""; pronoun = "it"; } const char* names_utf8 = PyUnicode_AsUTF8(names_unicode); snprintf(names_char, NAMES_CHAR_SIZE, "%s", names_utf8); PyObject *since_unicode = PyObject_Str(self->since); const char* since_utf8 = PyUnicode_AsUTF8(since_unicode); 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); } 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 PyTypeObject DeprKwsWrap = { PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "signature_deprecations.wrap", .tp_doc = PyDoc_STR("wrap a function with deprecated keyword arguments"), .tp_basicsize = sizeof(DeprKwsObject), .tp_itemsize = 0, .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, .tp_dictoffset = offsetof(DeprKwsObject, dict), .tp_new = depr_kws_wrap_new, .tp_init = (initproc)depr_kws_wrap_init, .tp_dealloc = (destructor)depr_kws_wrap_dealloc, .tp_members = depr_kws_wrap_members, .tp_call = (ternaryfunc)depr_kws_wrap_call, .tp_descr_get = depr_kws_wrap_get, }; static struct PyModuleDef module = { 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; if (PyType_Ready(&DeprKwsWrap) < 0) return NULL; m = PyModule_Create(&module); if (m == NULL) return NULL; Py_INCREF(&DeprKwsWrap); if (PyModule_AddObject(m, "_depr_kws_wrap", (PyObject*)&DeprKwsWrap) < 0) { Py_DECREF(&DeprKwsWrap); Py_DECREF(m); return NULL; } return m; } astropy-astropy-201cddb/astropy/cosmology/_src/tests/000077500000000000000000000000001507226315300232035ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/cosmology/_src/tests/__init__.py000066400000000000000000000000001507226315300253020ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/cosmology/_src/tests/conftest.py000066400000000000000000000004061507226315300254020ustar00rootroot00000000000000# 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 astropy-astropy-201cddb/astropy/cosmology/_src/tests/flrw/000077500000000000000000000000001507226315300241555ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/cosmology/_src/tests/flrw/__init__.py000066400000000000000000000000001507226315300262540ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/cosmology/_src/tests/flrw/conftest.py000066400000000000000000000016741507226315300263640ustar00rootroot00000000000000# 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) astropy-astropy-201cddb/astropy/cosmology/_src/tests/flrw/data/000077500000000000000000000000001507226315300250665ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/cosmology/_src/tests/flrw/data/cosmo_closed.ecsv000066400000000000000000000037601507226315300304270ustar00rootroot00000000000000# %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 astropy-astropy-201cddb/astropy/cosmology/_src/tests/flrw/data/cosmo_flat.ecsv000066400000000000000000000037521507226315300301050ustar00rootroot00000000000000# %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 astropy-astropy-201cddb/astropy/cosmology/_src/tests/flrw/data/cosmo_open.ecsv000066400000000000000000000037561507226315300301240ustar00rootroot00000000000000# %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 astropy-astropy-201cddb/astropy/cosmology/_src/tests/flrw/test_base.py000066400000000000000000000513231507226315300265040ustar00rootroot00000000000000# 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, quad 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 ############################################################################## @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() ############################################################################## 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") def nonflatcosmo(self): """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._massivenu 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) astropy-astropy-201cddb/astropy/cosmology/_src/tests/flrw/test_flrw.py000066400000000000000000000073721507226315300265510ustar00rootroot00000000000000# 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) astropy-astropy-201cddb/astropy/cosmology/_src/tests/flrw/test_lambdacdm.py000066400000000000000000001164041507226315300275000ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst """Testing :mod:`astropy.cosmology.flrw.lambdacdm`.""" import pathlib 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 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_z1z2(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_z1z2(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_z1z2(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_z1z2(z1, z2), tcos._comoving_transverse_distance_z1z2(z1, z2), ) # Test non-flat cases to avoid simply testing # comoving_distance_z1z2. 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_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.angular_diameter_distance_z1z2(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) # No baryons specified means baryons = 0 and gives a warning. tba = copy.copy(ba) tba.arguments["Ob0"] = None with pytest.warns(DeprecationWarning, match="Ob0=None is deprecated"): cosmo = 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 # ============================================================================= 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) astropy-astropy-201cddb/astropy/cosmology/_src/tests/flrw/test_w.py000066400000000000000000000055441507226315300260440ustar00rootroot00000000000000# 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)) astropy-astropy-201cddb/astropy/cosmology/_src/tests/flrw/test_w0cdm.py000066400000000000000000000171401507226315300266030ustar00rootroot00000000000000# 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, ) astropy-astropy-201cddb/astropy/cosmology/_src/tests/flrw/test_w0wacdm.py000066400000000000000000000235511507226315300271360ustar00rootroot00000000000000# 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, ) astropy-astropy-201cddb/astropy/cosmology/_src/tests/flrw/test_w0wzcdm.py000066400000000000000000000255251507226315300271720ustar00rootroot00000000000000# 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 ) astropy-astropy-201cddb/astropy/cosmology/_src/tests/flrw/test_wpwazpcdm.py000066400000000000000000000266431507226315300276150ustar00rootroot00000000000000# 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 ) astropy-astropy-201cddb/astropy/cosmology/_src/tests/funcs/000077500000000000000000000000001507226315300243215ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/cosmology/_src/tests/funcs/__init__.py000066400000000000000000000000001507226315300264200ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/cosmology/_src/tests/funcs/test_comparison.py000066400000000000000000000266361507226315300301210ustar00rootroot00000000000000# 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") def cosmo(self): return Planck18 @pytest.fixture(scope="class") def cosmo_eqvxflat(self, 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"} ), ) def format(self, request): return request.param @pytest.fixture(scope="class") def xfail_cant_autoidentify(self, format): """`pytest.fixture` form of method ``can_autoidentify`.""" if not self.can_autodentify(format): pytest.xfail("cannot autoidentify") @pytest.fixture(scope="class") def converted(self, 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") def pert_cosmo(self, cosmo): # change one parameter p, v = next(iter(cosmo.parameters.items())) cosmo2 = cosmo.clone( **{p: v * 1.0001 if v != 0 else 0.001 * getattr(v, "unit", 1)} ) return cosmo2 @pytest.fixture(scope="class") def pert_cosmo_eqvxflat(self, 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") def pert_converted(self, 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") def converted(self, 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 ) astropy-astropy-201cddb/astropy/cosmology/_src/tests/funcs/test_funcs.py000066400000000000000000000316471507226315300270630ustar00rootroot00000000000000# 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_z1z2(z1, z2), lambda z1: cosmo._comoving_transverse_distance_z1z2(z1, z2), lambda z1: cosmo.angular_diameter_distance_z1z2(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) astropy-astropy-201cddb/astropy/cosmology/_src/tests/helper.py000066400000000000000000000057261507226315300250460ustar00rootroot00000000000000# 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. """ import inspect import pytest from astropy.cosmology._src import core __all__ = ["clean_registry", "get_redshift_methods"] ############################################################################### # FUNCTIONS def get_redshift_methods(cosmology, include_private=True, include_z2=True): """Get redshift methods from a cosmology. Parameters ---------- cosmology : |Cosmology| class or instance include_private : bool Whether to include private methods, i.e. starts with an underscore. include_z2 : bool Whether to include methods that are functions of 2 (or more) redshifts, not the more common 1 redshift argument. Returns ------- set[str] The names of the redshift methods on `cosmology`, satisfying `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 methods. if callable(m) and (not n.startswith("_") or include_private): 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 astropy-astropy-201cddb/astropy/cosmology/_src/tests/io/000077500000000000000000000000001507226315300236125ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/cosmology/_src/tests/io/__init__.py000066400000000000000000000000001507226315300257110ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/cosmology/_src/tests/io/base.py000066400000000000000000000164111507226315300251010ustar00rootroot00000000000000# 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") def from_format(self): """Convert to Cosmology using ``Cosmology.from_format()``.""" return Cosmology.from_format @pytest.fixture(scope="class") def to_format(self, cosmo): """Convert Cosmology instance using ``.to_format()``.""" return cosmo.to_format def can_autodentify(self, 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") def read(self): """Read Cosmology instance using ``Cosmology.read()``.""" return Cosmology.read @pytest.fixture(scope="class") def write(self, 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) def setup(self): """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) def cosmo(self, request): """Cosmology instance.""" if isinstance(request.param, str): # CosmologyWithKwargs return _COSMOLOGY_CLASSES[request.param](Tcmb0=3) return request.param @pytest.fixture(scope="class") def cosmo_cls(self, 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") def from_format(self): """Convert to Cosmology using function ``from``.""" def use_from_format(*args, **kwargs): kwargs.pop("format", None) # specific to Cosmology.from_format return self.functions["from"](*args, **kwargs) return use_from_format @pytest.fixture(scope="class") def to_format(self, cosmo): """Convert Cosmology to format using function ``to``.""" def use_to_format(*args, **kwargs): return self.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") def read(self): """Read Cosmology from file using function ``read``.""" def use_read(*args, **kwargs): kwargs.pop("format", None) # specific to Cosmology.from_format return self.functions["read"](*args, **kwargs) return use_read @pytest.fixture(scope="class") def write(self, cosmo): """Write Cosmology to file using function ``write``.""" def use_write(*args, **kwargs): return self.functions["write"](cosmo, *args, **kwargs) return use_write astropy-astropy-201cddb/astropy/cosmology/_src/tests/io/test_.py000066400000000000000000000015121507226315300253010ustar00rootroot00000000000000# 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"} 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 astropy-astropy-201cddb/astropy/cosmology/_src/tests/io/test_connect.py000066400000000000000000000243561507226315300266660ustar00rootroot00000000000000# 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_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), ("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, ): """ 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) def cosmo(self, request): return getattr(cosmology.realizations, request.param) @pytest.fixture(scope="class") def cosmo_cls(self, 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) def cosmo(self, request): return getattr(cosmology.realizations, request.param) @pytest.fixture(scope="class") def cosmo_cls(self, 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") astropy-astropy-201cddb/astropy/cosmology/_src/tests/io/test_cosmology.py000066400000000000000000000044041507226315300272400ustar00rootroot00000000000000# 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} astropy-astropy-201cddb/astropy/cosmology/_src/tests/io/test_ecsv.py000066400000000000000000000201041507226315300261600ustar00rootroot00000000000000# 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} astropy-astropy-201cddb/astropy/cosmology/_src/tests/io/test_html.py000066400000000000000000000234431507226315300261750ustar00rootroot00000000000000# 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() astropy-astropy-201cddb/astropy/cosmology/_src/tests/io/test_json.py000066400000000000000000000136011507226315300261750ustar00rootroot00000000000000# 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) def register_and_unregister_json(self): """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} astropy-astropy-201cddb/astropy/cosmology/_src/tests/io/test_latex.py000066400000000000000000000070671507226315300263520ustar00rootroot00000000000000# 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() astropy-astropy-201cddb/astropy/cosmology/_src/tests/io/test_mapping.py000066400000000000000000000223231507226315300266600ustar00rootroot00000000000000# 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.""" astropy-astropy-201cddb/astropy/cosmology/_src/tests/io/test_model.py000066400000000000000000000147541507226315300263360ustar00rootroot00000000000000# 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") def method_name(self, 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} astropy-astropy-201cddb/astropy/cosmology/_src/tests/io/test_row.py000066400000000000000000000124751507226315300260430ustar00rootroot00000000000000# 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} astropy-astropy-201cddb/astropy/cosmology/_src/tests/io/test_table.py000066400000000000000000000231511507226315300263140ustar00rootroot00000000000000# 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} astropy-astropy-201cddb/astropy/cosmology/_src/tests/io/test_yaml.py000066400000000000000000000154361507226315300261760ustar00rootroot00000000000000# 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) def setup(self): """ 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` """ astropy-astropy-201cddb/astropy/cosmology/_src/tests/parameter/000077500000000000000000000000001507226315300251635ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/cosmology/_src/tests/parameter/__init__.py000066400000000000000000000000001507226315300272620ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/cosmology/_src/tests/parameter/conftest.py000066400000000000000000000004251507226315300273630ustar00rootroot00000000000000# 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 astropy-astropy-201cddb/astropy/cosmology/_src/tests/parameter/test_descriptors.py000066400000000000000000000121011507226315300311300ustar00rootroot00000000000000# 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.descriptors import ParametersAttribute from astropy.cosmology._src.utils import all_cls_vars 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_cls_vars(cosmo_cls).items() if (isinstance(v, Parameter) and (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_cls_vars(cosmo).items() if (isinstance(v, Parameter) and (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, {}) astropy-astropy-201cddb/astropy/cosmology/_src/tests/parameter/test_parameter.py000066400000000000000000000365151507226315300305660ustar00rootroot00000000000000# 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): 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"]) def cosmo_cls(self, request): """Cosmology class.""" return self.classes[request.param] @pytest.fixture(scope="class") def cosmo(self, cosmo_cls): """Cosmology instance""" return cosmo_cls() @pytest.fixture(scope="class") def param(self, cosmo_cls): """Get Parameter 'param' from cosmology class.""" return cosmo_cls.parameters["param"] @pytest.fixture(scope="class") def param_cls(self, 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): 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()) astropy-astropy-201cddb/astropy/cosmology/_src/tests/test_core.py000066400000000000000000000417131507226315300255520ustar00rootroot00000000000000# 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): 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") def cosmo_cls(self): """The Cosmology class as a :func:`pytest.fixture`.""" return self.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") def cosmo(self, cosmo_cls): """The cosmology instance with which to test.""" ba = inspect.signature(self.cls).bind(*self.cls_args, **self.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): 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 astropy-astropy-201cddb/astropy/cosmology/_src/tests/test_deprecated_modules.py000066400000000000000000000052001507226315300304410ustar00rootroot00000000000000"""Testing deprecated modules in `astropy.cosmology`.""" import re import warnings import pytest def test_connect(): """Test `astropy.cosmology.connect`.""" from astropy.cosmology import connect try: del connect.CosmologyFromFormat except Exception: pass with ( warnings.catch_warnings(), # Always raise warning so 2x test sees it too. pytest.deprecated_call( match=re.escape("The module `astropy.cosmology.connect` is deprecated"), ), ): warnings.simplefilter("always") from astropy.cosmology.connect import CosmologyFromFormat # noqa: F401 def test_core(): """Test `astropy.cosmology.core`.""" from astropy.cosmology import core try: del core.Cosmology except Exception: pass with ( warnings.catch_warnings(), # Always raise warning so 2x test sees it too. pytest.deprecated_call( match=re.escape("The module `astropy.cosmology.core` is deprecated"), ), ): warnings.simplefilter("always") from astropy.cosmology.core import Cosmology # noqa: F401 def test_flrw(): """Test `astropy.cosmology.core`.""" from astropy.cosmology import flrw try: del flrw.FLRW except Exception: pass with ( warnings.catch_warnings(), # Always raise warning so 2x test sees it too. pytest.deprecated_call( match=re.escape("The module `astropy.cosmology.flrw` is deprecated"), ), ): warnings.simplefilter("always") from astropy.cosmology.flrw import FLRW # noqa: F401 def test_funcs(): """Test `astropy.cosmology.core`.""" from astropy.cosmology import funcs try: del funcs.z_at_value except Exception: pass with ( warnings.catch_warnings(), # Always raise warning so 2x test sees it too. pytest.deprecated_call( match=re.escape("The module `astropy.cosmology.funcs` is deprecated") ), ): warnings.simplefilter("always") from astropy.cosmology.funcs import z_at_value # noqa: F401 def test_parameter(): """Test `astropy.cosmology.parameter`.""" from astropy.cosmology import parameter try: del parameter.Parameter except Exception: pass with ( warnings.catch_warnings(), # Always raise warning so 2x test sees it too. pytest.deprecated_call( match=re.escape("The module `astropy.cosmology.parameter` is deprecated"), ), ): warnings.simplefilter("always") from astropy.cosmology.parameter import Parameter # noqa: F401 astropy-astropy-201cddb/astropy/cosmology/_src/tests/test_parameters.py000066400000000000000000000025761507226315300267710ustar00rootroot00000000000000# 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 astropy-astropy-201cddb/astropy/cosmology/_src/tests/test_realizations.py000066400000000000000000000070051507226315300273220ustar00rootroot00000000000000# 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 astropy-astropy-201cddb/astropy/cosmology/_src/tests/test_units.py000066400000000000000000000343551507226315300257700ustar00rootroot00000000000000"""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") def cosmo(self): """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" astropy-astropy-201cddb/astropy/cosmology/_src/tests/test_utils.py000066400000000000000000000140671507226315300257640ustar00rootroot00000000000000# 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 ( all_cls_vars, 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) # ------------------------------------------------------------------- def test_all_cls_vars(): """Test :func:`astropy.cosmology._src.utils.all_cls_vars`.""" class ClassA: a = 1 b = 2 all_vars = all_cls_vars(ClassA) public_all_vars = {k: v for k, v in all_vars.items() if not k.startswith("_")} assert public_all_vars == {"a": 1, "b": 2} class ClassB(ClassA): c = 3 all_vars = all_cls_vars(ClassB) public_all_vars = {k: v for k, v in all_vars.items() if not k.startswith("_")} assert public_all_vars == {"a": 1, "b": 2, "c": 3} assert "a" not in vars(ClassB) assert "b" not in vars(ClassB) 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) astropy-astropy-201cddb/astropy/cosmology/_src/traits/000077500000000000000000000000001507226315300233475ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/cosmology/_src/traits/__init__.py000066400000000000000000000006451507226315300254650ustar00rootroot00000000000000# 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__ = [ "ScaleFactor", "TemperatureCMB", "_BaryonComponent", "_CriticalDensity", ] from .baryons import _BaryonComponent from .rhocrit import _CriticalDensity from .scale_factor import ScaleFactor from .tcmb import TemperatureCMB astropy-astropy-201cddb/astropy/cosmology/_src/traits/baryons.py000066400000000000000000000025661507226315300254070ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst """Baryon component. This is private API. See `~astropy.cosmology.traits` for public API. """ __all__ = ["_BaryonComponent"] from collections.abc import Callable from typing import Any from numpy.typing import ArrayLike, NDArray from astropy.cosmology._src.utils import aszarr, deprecated_keywords from astropy.units import Quantity class _BaryonComponent: """The cosmology has attributes and methods for the baryon density.""" Ob0: float """Omega baryons: density of baryonic matter in units of the critical density at z=0.""" inv_efunc: Callable[[NDArray[Any]], NDArray[Any]] @deprecated_keywords("z", since="7.0") def Ob(self, z: Quantity | ArrayLike) -> NDArray[Any] | float: """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. Returns ------- Ob : ndarray or float The density of baryonic matter relative to the critical density at each redshift. Returns `float` if the input is scalar. """ z = aszarr(z) return self.Ob0 * (z + 1.0) ** 3 * self.inv_efunc(z) ** 2 astropy-astropy-201cddb/astropy/cosmology/_src/traits/rhocrit.py000066400000000000000000000022711507226315300253750ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst """Critical density. This is private API. See `~astropy.cosmology.traits` for public API. """ __all__ = ["_CriticalDensity"] from collections.abc import Callable from typing import Any from numpy.typing import ArrayLike, NDArray from astropy.cosmology._src.utils import deprecated_keywords 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]] @deprecated_keywords("z", since="7.0") 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. Returns ------- rho : Quantity ['mass density'] Critical density at each input redshift. """ return self.critical_density0 * self.efunc(z) ** 2 astropy-astropy-201cddb/astropy/cosmology/_src/traits/scale_factor.py000066400000000000000000000031121507226315300263430ustar00rootroot00000000000000# 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, deprecated_keywords 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 @deprecated_keywords("z", since="7.0") 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. Returns ------- |Quantity| Scale factor at each input redshift. """ return self.scale_factor0 / (aszarr(z) + 1) astropy-astropy-201cddb/astropy/cosmology/_src/traits/tcmb.py000066400000000000000000000027321507226315300246520ustar00rootroot00000000000000# 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, deprecated_keywords from astropy.units import Quantity class TemperatureCMB: """The trait for computing the cosmological background temperature.""" Tcmb0: Quantity """Temperature of the CMB at z=0.""" @deprecated_keywords("z", since="7.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. 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) astropy-astropy-201cddb/astropy/cosmology/_src/typing.py000066400000000000000000000006021507226315300237230ustar00rootroot00000000000000"""Static typing for :mod:`astropy.cosmology`. PRIVATE API.""" # Licensed under a 3-clause BSD style license - see LICENSE.rst __all__ = ["_CosmoT"] from typing import TYPE_CHECKING, TypeVar if TYPE_CHECKING: import astropy.cosmology _CosmoT = TypeVar("_CosmoT", bound="astropy.cosmology.Cosmology") """Type variable for :class:`~astropy.cosmology.Cosmology` and subclasses.""" astropy-astropy-201cddb/astropy/cosmology/_src/units.py000066400000000000000000000017561507226315300235660ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst """Cosmological units.""" __all__ = ["littleh", "redshift"] 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 = 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 = u.def_unit( ["littleh"], namespace=_ns, prefixes=False, doc='Reduced/"dimensionless" Hubble constant', format={"latex": r"h_{100}"}, ) astropy-astropy-201cddb/astropy/cosmology/_src/units_equivalencies.py000066400000000000000000000303571507226315300265020ustar00rootroot00000000000000# 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}) astropy-astropy-201cddb/astropy/cosmology/_src/utils.py000066400000000000000000000072261507226315300235620ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst __all__: list[str] = [] # nothing is publicly scoped import functools import operator from collections.abc import Callable from numbers import Number from typing import Any, TypeVar import numpy as np from astropy.units import Quantity # isort: split import astropy.cosmology._src.units as cu from .signature_deprecations import _depr_kws_wrap _F = TypeVar("_F", bound=Callable[..., Any]) 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 def aszarr(z): """Redshift as a `~numbers.Number` or |ndarray| / |Quantity| / |Column|. Allows for any ndarray ducktype by checking for attribute "shape". """ if isinstance(z, (Number, np.generic)): # scalars return z elif hasattr(z, "shape"): # ducktypes NumPy array if getattr(z, "__module__", "").startswith("pandas"): # See https://github.com/astropy/astropy/issues/15576. Pandas does not play # well with others and will ignore unit-ful calculations so we need to # convert to it's underlying value. z = z.values if hasattr(z, "unit"): # Quantity Column return (z << cu.redshift).value # for speed only use enabled equivs return z # not one of the preferred types: Number / array ducktype return Quantity(z, cu.redshift).value def all_cls_vars(obj: object | type, /) -> dict[str, Any]: """Return all variables in the whole class hierarchy.""" cls = obj if isinstance(obj, type) else obj.__class__ return functools.reduce(operator.__or__, map(vars, cls.mro()[::-1])) def deprecated_keywords(*kws, since): """Deprecate calling one or more arguments as keywords. Parameters ---------- *kws: str Names of the arguments that will become positional-only. since : str or number or sequence of str or number The release at which the old argument became deprecated. """ return functools.partial(_depr_kws, kws=kws, since=since) def _depr_kws(func: _F, /, kws: tuple[str, ...], since: str) -> _F: wrapper = _depr_kws_wrap(func, kws, since) functools.update_wrapper(wrapper, func) return wrapper astropy-astropy-201cddb/astropy/cosmology/connect.py000066400000000000000000000014321507226315300231160ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst __all__ = [ # noqa: F822 "CosmologyFromFormat", "CosmologyRead", "CosmologyToFormat", "CosmologyWrite", ] import sys import warnings from typing import Any def __getattr__(name: str) -> Any: if name not in __all__ + ["convert_registry", "readwrite_registry"]: raise AttributeError(f"module {__name__!r} has no attribute {name!r}.") from ._src.io import connect obj = getattr(connect, name) setattr(sys.modules[__name__], name, obj) warnings.warn( "The module `astropy.cosmology.connect` is deprecated since v7.1 and will be " "removed in a future version. Import from `astropy.cosmology.io` instead.", category=DeprecationWarning, ) return obj astropy-astropy-201cddb/astropy/cosmology/core.py000066400000000000000000000017501507226315300224200ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst """Astropy cosmology core module. .. deprecated:: 7.1 This module is deprecated and will be removed in a future version. All the public classes and functions have been and will continue to be available in the :mod:`~astropy.cosmology` module. """ __all__ = ["Cosmology", "CosmologyError", "FlatCosmologyMixin"] # noqa: F822 import sys import warnings from typing import Any def __getattr__(name: str) -> Any: if name not in __all__ + ["_COSMOLOGY_CLASSES", "dataclass_decorator"]: raise AttributeError(f"module {__name__!r} has no attribute {name!r}.") from ._src import core obj = getattr(core, name) setattr(sys.modules[__name__], name, obj) warnings.warn( "The module `astropy.cosmology.core` is deprecated since v7.1 and will be " "removed in a future version. Import from `astropy.cosmology` instead.", category=DeprecationWarning, ) return obj astropy-astropy-201cddb/astropy/cosmology/data/000077500000000000000000000000001507226315300220245ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/cosmology/data/Planck13.ecsv000066400000000000000000000035241507226315300242660ustar00rootroot00000000000000# %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 astropy-astropy-201cddb/astropy/cosmology/data/Planck15.ecsv000066400000000000000000000035251507226315300242710ustar00rootroot00000000000000# %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 astropy-astropy-201cddb/astropy/cosmology/data/Planck18.ecsv000066400000000000000000000035351507226315300242750ustar00rootroot00000000000000# %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 astropy-astropy-201cddb/astropy/cosmology/data/WMAP1.ecsv000066400000000000000000000036051507226315300235370ustar00rootroot00000000000000# %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 astropy-astropy-201cddb/astropy/cosmology/data/WMAP3.ecsv000066400000000000000000000037131507226315300235410ustar00rootroot00000000000000# %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 astropy-astropy-201cddb/astropy/cosmology/data/WMAP5.ecsv000066400000000000000000000035141507226315300235420ustar00rootroot00000000000000# %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 astropy-astropy-201cddb/astropy/cosmology/data/WMAP7.ecsv000066400000000000000000000035111507226315300235410ustar00rootroot00000000000000# %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 astropy-astropy-201cddb/astropy/cosmology/data/WMAP9.ecsv000066400000000000000000000035401507226315300235450ustar00rootroot00000000000000# %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 astropy-astropy-201cddb/astropy/cosmology/flrw.py000066400000000000000000000022611507226315300224400ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst """Astropy cosmology flrw module. .. deprecated:: 7.1 This module is deprecated and will be removed in a future version. All the public classes and functions have been and will continue to be available in the :mod:`~astropy.cosmology` module. """ __all__ = [ # noqa: RUF100, RUF022, F822 # base "FLRW", "FlatFLRWMixin", # lambdacdm "LambdaCDM", "FlatLambdaCDM", # w0cdm "wCDM", "FlatwCDM", # w0wacdm "w0waCDM", "Flatw0waCDM", # w0wzcdm "w0wzCDM", "Flatw0wzCDM", # wpwazpcdm "wpwaCDM", "FlatwpwaCDM", ] import sys import warnings from typing import Any def __getattr__(name: str) -> Any: if name not in __all__: raise AttributeError(f"module {__name__!r} has no attribute {name!r}.") from ._src import flrw obj = getattr(flrw, name) setattr(sys.modules[__name__], name, obj) warnings.warn( "The module `astropy.cosmology.flrw` is deprecated since v7.1 and will be " "removed in a future version. Import from `astropy.cosmology` instead.", category=DeprecationWarning, ) return obj astropy-astropy-201cddb/astropy/cosmology/funcs.py000066400000000000000000000016151507226315300226060ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst """Astropy cosmology funcs module. .. deprecated:: 7.1 This module is deprecated and will be removed in v8. All the public classes and functions have been and will be available in the :mod:`~astropy.cosmology` module. """ __all__ = ["cosmology_equal", "z_at_value"] # noqa: F822 import sys import warnings from typing import Any def __getattr__(name: str) -> Any: if name not in __all__: raise AttributeError(f"module {__name__!r} has no attribute {name!r}.") from ._src import funcs obj = getattr(funcs, name) setattr(sys.modules[__name__], name, obj) warnings.warn( "The module `astropy.cosmology.funcs` is deprecated since v7.1 and will be " "removed in a future version. Import from `astropy.cosmology` instead.", category=DeprecationWarning, ) return obj astropy-astropy-201cddb/astropy/cosmology/io.py000066400000000000000000000017311507226315300220760ustar00rootroot00000000000000# 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, ) astropy-astropy-201cddb/astropy/cosmology/parameter.py000066400000000000000000000016041507226315300234460ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst """Astropy cosmology parameter module. .. deprecated:: 7.1 This module is deprecated and will be removed in Astropy v8. All the public classes and functions have been and will be available in the :mod:`~astropy.cosmology` module. """ __all__ = ["Parameter"] # noqa: F822 import sys import warnings from typing import Any def __getattr__(name: str) -> Any: if name != "Parameter": raise AttributeError(f"module {__name__!r} has no attribute {name!r}.") from ._src.parameter import Parameter setattr(sys.modules[__name__], name, Parameter) warnings.warn( "The module `astropy.cosmology.parameter` is deprecated since v7.1 and will be " "removed in a future version. Import from `astropy.cosmology` instead.", category=DeprecationWarning, ) return Parameter astropy-astropy-201cddb/astropy/cosmology/parameters.py000066400000000000000000000017761507226315300236430ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst """This module contains dictionaries with sets of parameters for a given cosmology. The list of cosmologies available are given by the tuple `available`. """ import sys from types import MappingProxyType from .realizations import available __all__ = ["available"] __all__ += [ # noqa: F822 "WMAP1", "WMAP3", "WMAP5", "WMAP7", "WMAP9", "Planck13", "Planck15", "Planck18", ] def __getattr__(name): """Get parameters of cosmology representations with lazy import from ``PEP 562``.""" from astropy.cosmology import realizations cosmo = getattr(realizations, name) m = cosmo.to_format("mapping", cosmology_as_str=True, move_from_meta=True) proxy = MappingProxyType(m) # Cache in this module so `__getattr__` is only called once per `name`. setattr(sys.modules[__name__], name, proxy) return proxy def __dir__(): """Directory, including lazily-imported objects.""" return __all__ astropy-astropy-201cddb/astropy/cosmology/realizations.py000066400000000000000000000032241507226315300241720ustar00rootroot00000000000000# 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__ astropy-astropy-201cddb/astropy/cosmology/traits.py000066400000000000000000000004031507226315300227700ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst """Traits for building ``astropy`` :class:`~astropy.cosmology.Cosmology` classes.""" __all__ = [ "ScaleFactor", "TemperatureCMB", ] from ._src.traits import ScaleFactor, TemperatureCMB astropy-astropy-201cddb/astropy/cosmology/units.py000066400000000000000000000024021507226315300226250ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst """Cosmological units and equivalencies.""" from astropy.units.utils import generate_unit_summary as _generate_unit_summary __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 ._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) astropy-astropy-201cddb/astropy/extern/000077500000000000000000000000001507226315300204055ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/extern/README.rst000066400000000000000000000040511507226315300220740ustar00rootroot00000000000000astropy.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 (``$HOME/.astropy/config``) 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/ astropy-astropy-201cddb/astropy/extern/__init__.py000066400000000000000000000007111507226315300225150ustar00rootroot00000000000000# 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. See the README.rst in this directory of the Astropy source repository for more details. """ astropy-astropy-201cddb/astropy/extern/_strptime.py000066400000000000000000000537641507226315300230040ustar00rootroot00000000000000"""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) astropy-astropy-201cddb/astropy/extern/configobj/000077500000000000000000000000001507226315300223455ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/extern/configobj/__init__.py000066400000000000000000000000001507226315300244440ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/extern/configobj/configobj.py000077500000000000000000002531441507226315300246730ustar00rootroot00000000000000# 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): # intentially 'str' because it's just whatever the "normal" # string type is for the python version we're dealing with 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: # intentially 'str' because it's just whatever the "normal" # string type is for the python version we're dealing with 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""" astropy-astropy-201cddb/astropy/extern/configobj/validate.py000077500000000000000000001331261507226315300245210ustar00rootroot00000000000000# 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) astropy-astropy-201cddb/astropy/extern/ply/000077500000000000000000000000001507226315300212115ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/extern/ply/__init__.py000066400000000000000000000001471507226315300233240ustar00rootroot00000000000000# PLY package # Author: David Beazley (dave@dabeaz.com) __version__ = '3.11' __all__ = ['lex','yacc'] astropy-astropy-201cddb/astropy/extern/ply/cpp.py000066400000000000000000001015471507226315300223550ustar00rootroot00000000000000# ----------------------------------------------------------------------------- # 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) astropy-astropy-201cddb/astropy/extern/ply/ctokens.py000066400000000000000000000061231507226315300232330ustar00rootroot00000000000000# ---------------------------------------------------------------------- # 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 astropy-astropy-201cddb/astropy/extern/ply/lex.py000066400000000000000000001236311507226315300223610ustar00rootroot00000000000000# ----------------------------------------------------------------------------- # 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 astropy-astropy-201cddb/astropy/extern/ply/yacc.py000066400000000000000000004150101507226315300225030ustar00rootroot00000000000000# ----------------------------------------------------------------------------- # 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. pickle_protocol = 0 # Protocol to use when writing pickle files # 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 def read_pickle(self, filename): try: import cPickle as pickle except ImportError: import pickle if not os.path.exists(filename): raise ImportError in_f = open(filename, 'rb') tabversion = pickle.load(in_f) if tabversion != __tabversion__: raise VersionError('yacc table file version is out of date') self.lr_method = pickle.load(in_f) signature = pickle.load(in_f) self.lr_action = pickle.load(in_f) self.lr_goto = pickle.load(in_f) productions = pickle.load(in_f) self.lr_productions = [] for p in productions: self.lr_productions.append(MiniProduction(*p)) in_f.close() return 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 # ----------------------------------------------------------------------------- # pickle_table() # # This function pickles the LR parsing tables to a supplied file object # ----------------------------------------------------------------------------- def pickle_table(self, filename, signature=''): try: import cPickle as pickle except ImportError: import pickle with open(filename, 'wb') as outf: pickle.dump(__tabversion__, outf, pickle_protocol) pickle.dump(self.lr_method, outf, pickle_protocol) pickle.dump(signature, outf, pickle_protocol) pickle.dump(self.lr_action, outf, pickle_protocol) pickle.dump(self.lr_goto, outf, pickle_protocol) outp = [] for p in self.lr_productions: if p.func: outp.append((p.str, p.name, p.len, p.func, os.path.basename(p.file), p.line)) else: outp.append((str(p), p.name, p.len, None, None, None)) pickle.dump(outp, outf, pickle_protocol) # ----------------------------------------------------------------------------- # === 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, picklefile=None): if tabmodule is None: tabmodule = tab_module # Reference to the parsing method of the last built parser global parse # If pickling is enabled, table files are not created if picklefile: write_tables = 0 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() if picklefile: read_signature = lr.read_pickle(picklefile) else: 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)) # Write a pickled version of the tables if picklefile: try: lr.pickle_table(picklefile, signature) except IOError as e: errorlog.warning("Couldn't create %r. %s" % (picklefile, e)) # Build the parser lr.bind_callables(pinfo.pdict) parser = LRParser(lr, pinfo.error_func) parse = parser.parse return parser astropy-astropy-201cddb/astropy/extern/ply/ygen.py000066400000000000000000000043061507226315300225300ustar00rootroot00000000000000# 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() astropy-astropy-201cddb/astropy/io/000077500000000000000000000000001507226315300175075ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/io/__init__.py000066400000000000000000000002641507226315300216220ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst """ This subpackage contains modules and packages for interpreting data storage formats used by and in astropy. """ astropy-astropy-201cddb/astropy/io/ascii/000077500000000000000000000000001507226315300205775ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/io/ascii/__init__.py000066400000000000000000000036121507226315300227120ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst """An extensible ASCII table reader and writer.""" from astropy import config as _config from . import connect from .basic import ( Basic, BasicData, BasicHeader, CommentedHeader, Csv, NoHeader, Rdb, Tab, ) from .cds import Cds 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 .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 .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", ) conf = Conf() astropy-astropy-201cddb/astropy/io/ascii/basic.py000066400000000000000000000274241507226315300222430ustar00rootroot00000000000000# 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 delimited files with various options for column header definition. :Copyright: Smithsonian Astrophysical Observatory (2011) :Author: Tom Aldcroft (aldcroft@head.cfa.harvard.edu) """ import re from . import core 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): 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 # Data starts after the header column definition, blank lines ignored 1 2 3 4 5 6 """ _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): """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 """ _format_name = "no_header" _description = "Basic table with no headers" header_class = NoHeaderHeader data_class = NoHeaderData 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): """Character-delimited table with column names in a comment line. 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. 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. """ splitter_class = TabHeaderSplitter class TabData(BasicData): """ Reader for data of tables with tab separated data. """ splitter_class = TabDataSplitter class Tab(Basic): """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 """ _format_name = "tab" _description = "Basic table with tab-separated values" header_class = TabHeader data_class = TabData 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 ``--``. 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 """ _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 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``. 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 Parameters ---------- lines : list List of table lines Returns ------- None """ 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") self.names, raw_types = header_vals_list if len(self.names) != len(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(self.colnames)) rdb_types = [] for col in self.cols: # 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 astropy-astropy-201cddb/astropy/io/ascii/cds.py000066400000000000000000000377221507226315300217350ustar00rootroot00000000000000# 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 :Copyright: Smithsonian Astrophysical Observatory (2011) :Author: Tom Aldcroft (aldcroft@head.cfa.harvard.edu) """ import fnmatch import itertools import os import re from contextlib import suppress from pathlib import Path 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 class CdsHeader(core.BaseHeader): _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()) if not match: raise ValueError( f'Unrecognized {self._subfmt} format "{col.raw_type}" for column' f'"{col.name}"' ) return match.group(1) def get_cols(self, lines): """ Initialize the header Column objects from the table ``lines`` for a CDS/MRT header. 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 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 for line in f: line = line.strip() if in_header: lines.append(line) 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, ) if match: # Split 'name' in case in contains multiple files 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): in_header = True lines.append(line) break else: 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): 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 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 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")) 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" else: 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") 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: if len(cols[-1].description) > 0: cols[-1].description += " " cols[-1].description += line.strip() else: raise ValueError(f'Line "{line}" not parsable as CDS header') self.names = [x.name for x in cols] self.cols = cols class CdsData(core.BaseData): """CDS table data reader.""" _subfmt = "CDS" splitter_class = fixedwidth.FixedWidthSplitter def process_lines(self, lines): """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 _is_section_delimiter(x)] if not i_sections: raise core.InconsistentTableError( f"No {self._subfmt} section delimiter found" ) return lines[i_sections[-1] + 1 :] class Cds(core.BaseReader): """CDS format table. See: https://vizier.unistra.fr/doc/catstd.htx Example:: 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 -------------------------------------------------------------------------------- 1- 3 I3 --- Index Running identification number 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 ``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:: >>> 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** When ``Cds`` reader object is created with a ``readme`` parameter passed to it at initialization, then when the ``read`` method is executed with a table filename, the header information for the specified table is taken from the ``readme`` file. An ``InconsistentTableError`` is raised if the ``readme`` file does not have header information for the given table. >>> 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("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 = 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("data/vizier/table1.dat") Traceback (most recent call last): ... InconsistentTableError: No CDS section delimiter found Caveats: * 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. """ _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): super().__init__() self.header.readme = readme def write(self, table=None): """Not available for the CDS class (raises NotImplementedError).""" raise NotImplementedError def read(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): table = super().read(lines) return table else: return super().read(table) astropy-astropy-201cddb/astropy/io/ascii/connect.py000066400000000000000000000033151507226315300226040ustar00rootroot00000000000000# 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 astropy-astropy-201cddb/astropy/io/ascii/core.py000066400000000000000000002021041507226315300221000ustar00rootroot00000000000000# 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. :Copyright: Smithsonian Astrophysical Observatory (2010) :Author: Tom Aldcroft (aldcroft@head.cfa.harvard.edu) """ from __future__ import annotations import copy import csv import fnmatch import functools import inspect import itertools 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 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): """ 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 StrType(NoType): """ Indicates that a column consists of text data. """ class NumType(NoType): """ Indicates that a column consists of numerical data. """ class FloatType(NumType): """ Describes floating-point data. """ class BoolType(NoType): """ Describes boolean data. """ class IntType(NumType): """ Describes integer data. """ class AllType(StrType, FloatType, IntType): """ Subclass of all other data types. 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 * **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): self.name = name self.type = NoType # Generic type (Int, Float, Str etc) self.dtype = None # Numpy dtype if available self.str_vals = [] self.fill_values = {} self.shape = [] self.subtype = None class BaseInputter: """ 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. 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") 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) 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' ) return self.process_lines(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 data line processing. 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. """ return lines 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 each iteration. There are two methods that are intended to be overridden, first ``process_line()`` to do pre-processing on each input line before splitting and ``process_val()`` to do post-processing on each split string value. By default these apply the string ``strip()`` function. These can be set to another function via the instance attribute or be disabled entirely, for example:: reader.header.splitter.process_val = lambda x: x.lstrip() reader.data.splitter.process_val = None """ 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. """ return line.strip() def process_val(self, val: str) -> str: """Remove whitespace at the beginning or end of value.""" return val.strip() def __call__(self, lines): if self.process_line: lines = (self.process_line(x) for x in lines) for line in lines: vals = line.split(self.delimiter) if self.process_val: yield [self.process_val(x) for x in vals] else: yield vals def join(self, vals: list[str]) -> str: if self.delimiter is None: 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 attributes are taken from the csv Dialect class. Typical usage:: # lines = .. splitter = ascii.DefaultSplitter() for col_vals in splitter(lines): for col_val in col_vals: ... """ 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 == r"\s": line = _replace_tab_with_space(line, self.escapechar, self.quotechar) return line.strip() + "\n" 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. 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] 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): delimiter = " " if self.delimiter is None else str(self.delimiter) if self.csv_writer is None: 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] out = self.csv_writer.writerow(vals).rstrip("\r\n") return out 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" for char in line: if char == quotechar and lastchar != escapechar: in_quote = not in_quote if char == "\t" and not in_quote: char = " " lastchar = char newline.append(char) 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 callable(line_or_func): return line_or_func(lines) elif line_or_func: if line_or_func >= 0: return line_or_func else: n_lines = sum(1 for line in lines) return n_lines + line_or_func else: return line_or_func class BaseHeader: """ Base table header reader. """ 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 """ regular expression for comment lines """ splitter_class: ClassVar[type[BaseSplitter]] = DefaultSplitter """ Splitter class for splitting data lines into columns """ names = None """ 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.splitter_class() def _set_cols_from_names(self): 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. 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 # 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") self.names = next(self.splitter([line])) self._set_cols_from_names() def process_lines(self, lines): """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.strip() and (not self.comment or not re_comment.match(line)): yield line 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 zip( range(self.start_line), itertools.cycle(self.write_spacer_lines) ): lines.append(spacer_line) lines.append(self.splitter.join([x.info.name for x in self.cols])) @property 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 """ colnames = self.colnames for name in names: if name not in colnames: raise KeyError(f"Column {name} does not exist") 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 """ try: idx = self.colnames.index(name) except ValueError: raise KeyError(f"Column {name} does not exist") 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 def get_col_type(self, col): try: type_map_key = self.get_type_map_key(col) return self.col_type_map[type_map_key.lower()] except KeyError: raise ValueError( f'Unknown data type ""{col.raw_type}"" for column "{col.name}"' ) 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 """ 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): # 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``. Parameters ---------- lines : list All lines in table Returns ------- lines : list 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 list(nonblank_lines) 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) if start_line is not None or end_line is not None: self.data_lines = data_lines[slice(start_line, end_line)] else: # Don't copy entire data lines unless necessary self.data_lines = data_lines def get_str_vals(self): """Return a generator that returns a list of column values (as strings) for each data line. """ return self.splitter(self.data_lines) def masks(self, cols): """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. """ if self.fill_values: self._set_fill_values(cols) self._set_masks(cols) def _set_fill_values(self, cols): """READ, WRITE: Set fill values of individual cols based on fill_values of BaseData. fill values has the following form: = (, , ...) fill_values = or list of 's """ if self.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 for replacement in reversed(self.fill_values): if len(replacement) < 2: 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 ): cols[i].fill_values[replacement[0]] = str(replacement[1]) def _set_masks(self, cols): """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 = 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): """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)) 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 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. """ # 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 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) # 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) 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 ""') return trues def generic_converter(vals): return np.array(vals, numpy_type) converter = bool_converter if converter_type is BoolType else generic_converter return converter, converter_type 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). """ # 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: 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("converter_type must be a subclass of NoType") if issubclass(converter_type, col.type): converters_out.append((converter_func, converter_type)) 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: 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) # 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: col.data = converter_func(col.str_vals) col.type = converter_type 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) return new_names 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) 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) 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``) """ 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)) 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) 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. Typical usage is to instantiate a Reader() object and customize the ``header``, ``data``, ``inputter``, and ``outputter`` attributes. Each of these is an object of the corresponding class. There is one method ``inconsistent_handler`` that can be used to customize the behavior of ``read()`` in the event that a data row doesn't match the header. 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 = 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. The ``table`` parameter is any string or object that can be processed by the instance ``inputter``. For the base Inputter class ``table`` can be 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 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. 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) # 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) # 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_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_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 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) table = self.outputter(self.header.cols, self.meta) self.cols = self.header.cols 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 the header. 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 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() return str_vals @property 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)] else: comment_lines = [] return comment_lines 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 Returns ------- table : `astropy.table.Table` Output table for writing """ 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 = new_cols self.data.cols = new_cols self.header.table_meta = table.meta # 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:: col1 col2 col3 1 \ 2 3 4 5 \ 6 """ 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): 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.replace(self.continuation_char, self.replace_char)) else: parts.append(line) outlines.append("".join(parts)) parts = [] return outlines class WhitespaceSplitter(DefaultSplitter): 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 ): in_quote = not in_quote if char == "\t" and not in_quote: char = " " lastchar = char newline.append(char) 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. """ 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", "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. """ 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 astropy-astropy-201cddb/astropy/io/ascii/cparser.pyx000066400000000000000000001154141507226315300230060ustar00rootroot00000000000000# 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 astropy-astropy-201cddb/astropy/io/ascii/daophot.py000066400000000000000000000346371507226315300226240ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst """ An extensible ASCII table reader and writer. Classes to read DAOphot table format :Copyright: Smithsonian Astrophysical Observatory (2011) :Author: Tom Aldcroft (aldcroft@head.cfa.harvard.edu) """ import itertools as itt import re 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): """ DAOphot format table. Example:: #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 # #N SHARPNESS CHI PIER PERROR \\ #U ## ## ## perrors \\ #F %-23.3f %-12.3f %-6d %-13s # 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 output table ``meta`` attribute:: >>> 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 ... 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 ... Any column values of INDEF are interpreted as a missing value and will be masked out in the resultant table. 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. """ _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 astropy-astropy-201cddb/astropy/io/ascii/docs.py000066400000000000000000000176771507226315300221230ustar00rootroot00000000000000READ_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), } astropy-astropy-201cddb/astropy/io/ascii/ecsv.py000066400000000000000000000476641507226315300221320ustar00rootroot00000000000000# 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 json import re import warnings from collections import OrderedDict import numpy as np from astropy.io.ascii.core import convert_numpy 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 EcsvHeader(basic.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 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]) lines.append(self.splitter.join([x.info.name for x in self.cols])) 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 if "meta" in header: self.table_meta = header["meta"] 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. out = serialize._construct_mixins_from_columns(out) return 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] = "" out = [col.str_vals for col in self.cols] return out 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"): out = serialize.represent_mixins_as_columns(table) return out astropy-astropy-201cddb/astropy/io/ascii/fastbasic.py000066400000000000000000000376201507226315300231200ustar00rootroot00000000000000# 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) astropy-astropy-201cddb/astropy/io/ascii/fixedwidth.py000066400000000000000000000416141507226315300233160ustar00rootroot00000000000000# 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. :Copyright: Smithsonian Astrophysical Observatory (2011) :Author: Tom Aldcroft (aldcroft@head.cfa.harvard.edu) """ 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 ``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. 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 = "" bookend = False delimiter = "|" def __call__(self, lines): for line in lines: 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 "" 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)] return bookend_left + padded_delim.join(vals) + bookend_right class FixedWidthHeaderSplitter(DefaultSplitter): """Splitter class that splits on ``|``.""" delimiter = "|" class FixedWidthHeader(basic.BasicHeader): """ Fixed width table header reader. """ 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") return line 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. Parameters ---------- lines : list List of table lines """ 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) ) # 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.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" ) vals, starts, ends = self.get_fixedwidth_params(data_lines[0]) self.names = [self.auto_format.format(i) for i in range(1, len(vals) + 1)] else: # This bit of code handles two cases: # start_line = and position_line = None # Single header line where that line is used to determine both the # column positions and names. # start_line = and position_line = # Two header lines where the first line defines the column names and # the second line defines the column positions if position_line is not None: # Define self.col_starts and self.col_ends so that the call to # get_fixedwidth_params below will use those to find the header # column names. Note that get_fixedwidth_params returns Python # 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 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() 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[i] col.end = ends[i] def get_fixedwidth_params(self, line): """ 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. 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. # 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 # 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" ) vals = [line[start:end].strip() for start, end in zip(starts, ends)] 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] ends = [] for val in vals: if val: ends.append(starts[-1] + len(val)) starts.append(ends[-1] + 1) else: starts[-1] += 1 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") 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 def write(self, lines): # Header line not written until data are formatted. Until then it is # not known how wide each column will be for fixed width. pass 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): 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: vals = [self.header.position_char * width for width in widths] lines.append(self.splitter.join(vals, widths)) for vals in vals_list: lines.append(self.splitter.join(vals, widths)) return lines 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 # No delimiter with column positions specified as input Col1 Col2Col3 1.2hello there 3 2.4many words 7 See the :ref:`astropy:fixed_width_gallery` for specific usage examples. """ _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.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): """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 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 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=[], ) 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. """ 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): """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. col1 col2 <== header_start = 0 ----- ------------ <== position_line = 1, position_char = "-" 1 bee flies <== data_start = 2 2 fish swims # Pretty-printed table +------+------------+ | Col1 | Col2 | +------+------------+ | 1.2 | "hello" | | 2.4 | there world| +------+------------+ See the :ref:`astropy:fixed_width_gallery` for specific usage examples. """ _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 astropy-astropy-201cddb/astropy/io/ascii/html.py000066400000000000000000000442461507226315300221270ustar00rootroot00000000000000# 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 soup_list = [SoupString(x) for x in table.find_all("tr")] return soup_list 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
astropy-astropy-201cddb/astropy/io/ascii/tests/data/html2.html000066400000000000000000000005721507226315300245720ustar00rootroot00000000000000 Row with no data elements Some junk
A B
1 2.5000000000000000001 3
1a 1 3.5
astropy-astropy-201cddb/astropy/io/ascii/tests/data/ipac.dat000066400000000000000000000010501507226315300242540ustar00rootroot00000000000000\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 astropy-astropy-201cddb/astropy/io/ascii/tests/data/ipac.dat.Z000066400000000000000000000005411507226315300244700ustar00rootroot00000000000000\Ō¸Ąc' =@Äh €‹6oÂ4ØC†‹ef0äBFb„ D\)CÄ8 dĀ#‡8t˰Ą0GŽ"6Ž)“į˘7Öų˜đD(A†€Ø™įÎ9dæ€8Ą“§O t„:&Œ?ƒē "Ė9oæHe#°ĖĒ APA“FjŨ˛_Ëā Ķ›oĖ”QmɟmڔȐˆĮå„ė2™2c(Cž& å Aۑēōã9pčäķ˛kĮ]Ë)cđąéŲĩmCHG3dÜA˜~<M9Ž“ķ‰ šŽ›4Ŋ…7]úcįĐuOĪnr9g¤&§ĖüņX6ÁMŸŪŊ…ÍÚÍ×A˙ą{›ãÉ+€ŧ~ŧŒ.´C 4šv|Yƒ 04hā{ Å Ã 4x9Ā !…b¨á„Úpa††8⇊ča‰’b astropy-astropy-201cddb/astropy/io/ascii/tests/data/ipac.dat.bz2000066400000000000000000000006011507226315300247510ustar00rootroot00000000000000BZh91AY&SYÆ "Z´ß€vPãō( L„/īßä0Yύ5O(ɨõ6IŊD4 PˆĻ$õ24ɂ`Œ„0 ŒĻŅ’Ÿĸš4Pze7ĒmũR¤•­p—oĪekm`ŽlaŽ€paûį>Ũ¨dŅĪŌäg܃[•Čëßé´ōÂ"ŗ|—yG:SÕ|ūî´ ŽÛz´rw(Rī9A]Äoü†ŒÃTÂÔI¨žlŲ–˜ËUQēÉ(ļŽ`üë,mŧŽ‚™6“’L…$|(ˆN›â°ĘŖ{‚ Đ]3âj:ŗ•äËįWmČŦĮ-Rž…’jfĻwpßP=(kÖ`Į¸ˇ~8ĖĐŅØ}›éĶ`ÅܑN$1‚Č–€astropy-astropy-201cddb/astropy/io/ascii/tests/data/ipac.dat.xz000066400000000000000000000005001507226315300247130ustar00rootroot00000000000000ũ7zXZæÖ´F!t/åŖā&].IĮxBØFęÕčO’đŦĮT4?“ÕCm(e7)&yЖÁåĘøĖsFķ´ėGŊŨILxĨ–9;ÎÃėN"čVĄE^ŨPLr7‘ņ`ĒíRÍ9dģÂН$JŪNÂķ䟱hú#Ë\†Ą¸ ͌ÔráNāF9U­ÄŠ›Žų XŅB#ģĘČ(ÕQ îÆÕMŅÆíj†Ôæg4×öj°l ]ß~ûŪ(īũīÅ[ĒclŽq#Ž$YôĢ‹Ėļ7*n<čŖ=$¤oų3ķ–ŽK(V׿5×[á$ģđVb֙ĩļAˇ&[Äqš3MÂ2ī Ę<ķ†WL°Ēfå–Ōõr>.Fĩ`œ§娄ąÄgûYZastropy-astropy-201cddb/astropy/io/ascii/tests/data/latex1.tex000066400000000000000000000004271507226315300245750ustar00rootroot00000000000000\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} astropy-astropy-201cddb/astropy/io/ascii/tests/data/latex1.tex.gz000066400000000000000000000003061507226315300252100ustar00rootroot00000000000000‹pPlatex1.texEMÂ@…÷=‹ÆčŌŋgPOĀf:Ĩ•iC§ĻÍdîîTM]ŧXQËƒĢ„RŪõ;¸¤;ĨČS‚ë ĶNkø [›âÄ ‚q/ ‘qĘ|Kfå=#8—{¸}ē|§ פž€5Ŋœ ;@qÉb}Q’9eûõ˜QœĨ(f ÂJ…īÄÁ&+Iõ-ąøąėsōhĄĮ§?FŌzÕ\ģüîágŠûüastropy-astropy-201cddb/astropy/io/ascii/tests/data/latex2.tex000066400000000000000000000007211507226315300245730ustar00rootroot00000000000000\begin{deluxetable}{llrl} %\tabletypesize{\scriptsize} %\rotate \tablecaption{Log of observations\label{tab:obslog}} \tablewidth{0pt} \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} astropy-astropy-201cddb/astropy/io/ascii/tests/data/latex3.tex000066400000000000000000000001661507226315300245770ustar00rootroot00000000000000 \begin{tabular}{lrr}\hline cola & colb & colc\\ \hline a & 1 & 2\\ \midrule b & 3 & 4\\ \hline \end{tabular} astropy-astropy-201cddb/astropy/io/ascii/tests/data/nls1_stackinfo.dbout000066400000000000000000000477301507226315300266420ustar00rootroot00000000000000 |objID |osrcid |xsrcid |SpecObjID |ra |dec |obsid |ccdid |z |modelMag_i |modelMagErr_i |modelMag_r |modelMagErr_r |expo |theta |rad_ecf_39 |detlim90 |fBlim90 |-----------------------|-----------------|---------------|-----------------------|--------------------|--------------------|-----------|-----------|--------------------|--------------------|--------------------|--------------------|--------------------|--------------------|--------------------|--------------------|--------------------|-------------------- | 277955213|S000.7044P00.7513|XS04861B6_005 | 10943136| 0.704453| 0.751336| 4861| 6| 0.086550| 15.462060| 0.003840| 16.063650| 0.003888| 5104.621261| 0.105533| 3.022382| 15.117712| 0.311318 | 889974380|S002.9051P14.7003|XS03957B7_004 | 21189832| 2.905195| 14.700391| 3957| 7| 0.131820| 16.466050| 0.004807| 16.992690| 0.004917| 1479.207035| 0.118550| 3.016342| 17.364280| 0.880407 | 661258793|S005.7709M01.1287|XS04079B7_003 | 10999832| 5.770986| -1.128731| 4079| 7| 0.166355| 17.232030| 0.008332| 17.549760| 0.007209| 1540.924685| 0.073783| 1.489627| 11.915912| 0.561011 | 809266720|S006.9683P00.4376|XS04080B7_003 | 11027112| 6.968335| 0.437687| 4080| 7| 0.205337| 17.600880| 0.007790| 18.047560| 0.007439| 1373.690631| 0.073017| 1.489627| 15.480587| 0.807865 | 275803698|S014.7729P00.1143|XS02179B6_001 | 11140928| 14.772956| 0.114358| 2179| 6| 0.718880| 17.487000| 0.006978| 17.441360| 0.005979| 2043.570572| 0.091283| 1.453126| 13.288200| 0.676781 | 610324605|S029.2184M00.2061|XS04081B7_004 | 11365768| 29.218458| -0.206140| 4081| 7| 0.163040| 17.522280| 0.006957| 17.821940| 0.006828| 1513.497218| 0.073333| 1.489627| 12.188137| 0.580337 | 819359440|S029.9901P00.5529|XS05777B1_005 | 11365080| 29.990162| 0.552903| 5777| 1| 0.311778| 18.508300| 0.013120| 18.822060| 0.011235| 16875.600510| 0.173000| 5.127182| 29.849694| 0.201770 | 359375943|S037.1728P00.8690|XS04083B7_002 | 11478640| 37.172803| 0.869065| 4083| 7| 0.186225| 17.741220| 0.008360| 18.157300| 0.007994| 1600.672011| 0.074100| 1.489627| 12.060426| 0.546492 | 680002094|S048.6144M01.1978|XS04084B7_001 | 11619072| 48.614411| -1.197867| 4084| 7| 0.387004| 18.084100| 0.008811| 18.047740| 0.007107| 1688.844386| 0.074850| 1.489627| 14.418508| 0.665490 | 207476987|S122.0691P21.1492|XS03785B1_003 | 54178104| 122.069156| 21.149206| 3785| 1| 0.142121| 18.795740| 0.014157| 19.272550| 0.014808| 15935.690359| 0.148833| 5.116525| 25.744492| 0.182462 | 314622107|S124.9642P36.8307|XS04119B3_002 | 25158064| 124.964241| 36.830793| 4119| 3| 0.736540| 19.246110| 0.015329| 19.180730| 0.011400| 6686.525810| 0.191800| 7.738524| 30.212630| 0.663496 | 499048612|S128.7287P55.5725|XS04940B7_008 | 50209680| 128.728748| 55.572530| 4940| 7| 0.241157| 16.196610| 0.006701| 16.845690| 0.008232| 85385.450431| 0.021583| 0.327020| 25.359343| 0.022349 | 509872023|S130.2167P13.2152|NULL | 68308384| 130.216762| 13.215295| 2130| 7| 0.170352| 17.437750| 0.009265| 17.989470| 0.010459| 22105.895051| 0.010694| 0.232184| 8.703367| 0.030060 | 337394906|S134.7069P27.8194|NULL | 54460872| 134.706929| 27.819409| 5821| 3| 0.090713| 15.495630| 0.004090| 15.933850| 0.004758| 20139.691101| 0.153217| 5.127182| 26.899264| 0.175787 | 204612149|S140.7808P30.9906|XS04122B5_001 | 54657400| 140.780817| 30.990687| 4122| 5| 0.629145| 18.845160| 0.012765| 18.948480| 0.010724| 4173.745162| 0.192050| 7.739623| 37.336536| 0.819048 | 731490396|S147.5151P17.1590|XS03274B2_001 | 66732256| 147.515194| 17.159084| 3274| 2| 0.195364| 17.472340| 0.006327| 17.783260| 0.006028| 14096.036370| 0.032833| 0.366684| 9.702502| 0.075172 | 138368206|S147.6362P59.8164|NULL | 12773752| 147.636280| 59.816408| 3036| 2| 0.652411| 19.914220| 0.029210| 20.094790| 0.024926| 4012.606072| 0.167283| 5.127182| 21.727958| 0.697762 | 561051767|S151.8587P12.8156|XS05606B7_004 | 49112864| 151.858761| 12.815617| 5606| 7| 0.240653| 15.175160| 0.004690| 15.348870| 0.004204| 33943.906753| 0.008806| 0.243602| 8.594830| 0.019169 | 827223175|S153.3119M00.8760|XS04085B7_001 | 7622024| 153.311933| -0.876011| 4085| 7| 0.275749| 17.638600| 0.006945| 17.638410| 0.005750| 1769.308133| 0.074400| 1.489627| 12.371362| 0.512717 | 125920375|S160.6255P01.0399|XS04086B7_004 | 7762256| 160.625571| 1.039913| 4086| 7| 0.115493| 16.476400| 0.006180| 16.952690| 0.005979| 1351.602676| 0.074017| 1.489627| 12.388117| 0.674828 | 126051412|S160.8870P01.0191|XS04086B2_001 | 7762456| 160.887017| 1.019120| 4086| 2| 0.071893| 15.403520| 0.003958| 15.830900| 0.003798| 1470.312820| 0.188000| 7.763980| 24.843778| 2.297003 | 199471676|S169.6261P40.4316|XS00868B3_001 | 40555520| 169.626193| 40.431669| 868| 3| 0.154596| 15.520440| 0.003612| 15.843520| 0.003574| 15875.864312| 0.039917| 0.409867| 10.331539| 0.069317 | 911117410|S174.3501P30.0602|XS04161B7_011 | 62510944| 174.350159| 30.060294| 4161| 7| 0.695136| 19.910250| 0.032209| 20.022840| 0.021641| 14988.033880| 0.062233| 0.614561| 11.836136| 0.055041 | 302536231|S179.8826P29.2455|XS00874B3_007 | 62651000| 179.882670| 29.245515| 874| 3| 0.724488| 17.965190| 0.007865| 18.090560| 0.007281| 94375.899791| 0.004833| 0.230755| 8.240796| 0.009124 | 302601830|S179.9533P29.1580|XS00874B2_001 | 62623640| 179.953385| 29.158023| 874| 2| 0.083344| 15.802610| 0.004156| 16.238050| 0.004098| 84775.542942| 0.105917| 3.022382| 21.690631| 0.026736 | 115261957|S180.7950P57.6803|XS05757B0_022 | 37008112| 180.795062| 57.680354| 5757| 0| 0.759025| 18.066390| 0.008409| 18.060240| 0.006947| 40390.627482| 0.178917| 5.125388| 34.575626| 0.096856 | 607275593|S183.4289P02.8802|XS04934B3_004 | 14602336| 183.428996| 2.880256| 4934| 3| 0.641174| 19.083390| 0.016683| 19.264170| 0.014156| 17374.807609| 0.067033| 1.489627| 12.291123| 0.078403 | 425979958|S183.5631P00.9198|XS04087B7_004 | 8100808| 183.563163| 0.919874| 4087| 7| 0.395653| 18.254720| 0.010882| 18.328170| 0.008583| 1743.376400| 0.075183| 1.489627| 12.265030| 0.491269 | 189855768|S184.4790P58.6599|XS03558B3_002 | 37036288| 184.479077| 58.659912| 3558| 3| 0.023181| 14.626880| 0.002469| 14.904420| 0.002339| 6129.941952| 0.003750| 0.232403| 7.666659| 0.136118 | 619169285|S187.0751P44.2172|NULL | 38612200| 187.075137| 44.217228| 938| 0| 0.662250| 17.907240| 0.007109| 18.053730| 0.006975| 2298.154599| 0.172200| 5.125388| 20.352558| 0.848250 | 325588542|S187.5646P03.0485|XS04040B7_001 | 14659784| 187.564673| 3.048508| 4040| 7| 0.137670| 16.402290| 0.004927| 17.103210| 0.005467| 3409.797684| 0.010833| 0.243602| 7.851051| 0.160035 | 574503609|S187.6176P47.8825|NULL | 40921304| 187.617696| 47.882592| 3071| 3| 0.259120| 18.357610| 0.011731| 18.646700| 0.010925| 6222.550176| 0.197167| 7.763595| 30.051725| 0.668981 | 101878322|S188.4820P13.0754|XS02107B7_001 | 45509408| 188.482006| 13.075423| 2107| 7| 0.480211| 18.623910| 0.015033| 19.178470| 0.015434| 5550.153407| 0.015417| 0.276499| 8.218379| 0.089948 | 834099774|S188.5555P47.8975|XS03055B7_001 | 40921752| 188.555591| 47.897583| 3055| 7| 0.372812| 16.768010| 0.004767| 16.822040| 0.004038| 4452.821575| 0.009889| 0.243602| 8.075360| 0.119255 | 528223925|S191.3095P01.1419|NULL | 8213952| 191.309592| 1.141912| 2974| 2| 0.091196| 16.407150| 0.006573| 16.882680| 0.006505| 5665.430694| 0.160700| 5.127182| 21.105785| 0.481827 | 430960732|S194.9316P01.0486|XS04088B7_005 | 8269800| 194.931643| 1.048622| 4088| 7| 0.394569| 18.230550| 0.009419| 18.305880| 0.007674| 1532.367693| 0.075000| 1.489627| 11.697405| 0.529051 | 040450702|S196.9301P46.7193|NULL | 41090688| 196.930172| 46.719346| 3244| 6| 0.600141| 19.711200| 0.021784| 20.631250| 0.030904| 8481.301760| 0.184333| 5.111493| 28.487300| 0.409704 | 895335014|S197.7853P00.5310|XS04089B7_006 | 8297720| 197.785328| 0.531036| 4089| 7| 0.429236| 17.838440| 0.007412| 17.883200| 0.006128| 1342.669846| 0.075917| 1.489627| 11.975790| 0.622564 | 362199556|S206.2204P00.0889|NULL | 8438656| 206.220450| 0.088956| 2251| 6| 0.087128| 15.878880| 0.003993| 16.339870| 0.003999| 7732.826167| 0.146900| 5.125388| 24.196409| 0.276815 | 390308579|S213.1444M00.5833|XS04090B7_001 | 8550616| 213.144471| -0.583347| 4090| 7| 0.126940| 16.924460| 0.008082| 17.337560| 0.007611| 1850.463370| 0.074433| 1.489627| 12.129062| 0.475026 | 444464848|S213.7065P36.2111|XS04163B1_002 | 46269424| 213.706536| 36.211187| 4163| 1| 0.180925| 17.916410| 0.009867| 18.346860| 0.009661| 81178.124219| 0.092700| 1.460516| 17.286353| 0.023684 | 222587913|S216.7550P44.2825|XS06112B2_004 | 36276768| 216.755074| 44.282505| 6112| 2| 0.735436| 19.039310| 0.015654| 19.133000| 0.012307| 7202.822662| 0.137533| 3.019678| 17.903948| 0.270855 | 929145428|S217.6259M00.1875|XS04091B7_004 | 8607176| 217.625904| -0.187530| 4091| 7| 0.103307| 17.334130| 0.007846| 17.791860| 0.007610| 1362.631234| 0.075700| 1.489627| 11.674970| 0.622522 | 428847268|S217.6691P36.8177|XS04126B7_001 | 38894856| 217.669106| 36.817754| 4126| 7| 0.566053| 18.744800| 0.010372| 19.168410| 0.011316| 2834.413800| 0.009583| 0.243602| 7.405304| 0.178125 | 440484921|S219.7460P03.5965|XS03290B1_006 | 16516928| 219.746065| 3.596520| 3290| 1| 0.733848| 18.461360| 0.009410| 18.429130| 0.008255| 48647.675049| 0.137250| 3.014202| 26.440169| 0.059182 | 468047975|S222.3062P00.4019|XS04092B7_004 | 8691936| 222.306273| 0.401911| 4092| 7| 0.440801| 18.675470| 0.012882| 18.855400| 0.010686| 1574.467045| 0.052750| 0.607436| 10.031873| 0.470555 | 468113483|S222.3862P00.3767|XS04092B7_001 | 8691984| 222.386270| 0.376752| 4092| 7| 0.080563| 16.388650| 0.004431| 16.884420| 0.004493| 1920.873200| 0.074717| 1.489627| 12.712145| 0.488745 | 931439168|S222.8459M00.1071|XS04093B7_001 | 8691960| 222.845909| -0.107191| 4093| 7| 0.138627| 17.058580| 0.006735| 17.488910| 0.006241| 1898.411113| 0.075567| 1.489627| 11.967944| 0.467229 | 262643238|S235.8184P54.0905|XS00822B6_002 | 17361832| 235.818430| 54.090581| 822| 6| 0.245121| 17.540910| 0.006667| 17.778130| 0.006324| 3636.411721| 0.140183| 3.019678| 18.358903| 0.439665 | 926158050|S240.8326P42.3631|NULL | 37599160| 240.832606| 42.363127| 5609| 6| 0.245845| 18.507290| 0.014520| 18.790040| 0.011571| 11325.862421| 0.133133| 3.019841| 18.982950| 0.171760 | 608676499|S245.9044P31.1722|XS05607B7_001 | 39992048| 245.904431| 31.172231| 5607| 7| 0.235655| 18.073020| 0.012637| 18.487890| 0.011318| 16902.229821| 0.033283| 0.375642| 10.006948| 0.042823 | 960066205|S246.4348P15.8271|XS03229B1_001 | 62172624| 246.434806| 15.827186| 3229| 1| 0.798335| 18.653970| 0.012212| 18.576130| 0.008949| 42261.803686| 0.138833| 3.014202| 25.770130| 0.068354 | 134019205|S256.4454P63.1831|XS04094B7_002 | 9845344| 256.445484| 63.183108| 4094| 7| 0.119156| 17.496630| 0.006940| 17.887740| 0.006407| 1714.376381| 0.075200| 1.489627| 11.843095| 0.497777 | 134609053|S257.3217P61.8895|XS04864B6_004 | 9902624| 257.321721| 61.889546| 4864| 6| 0.292492| 18.075020| 0.010429| 18.285270| 0.008331| 3266.690360| 0.109617| 3.019678| 16.988875| 0.540981 | 213815608|S260.0418P26.6255|XS04361B3_014 | 27578480| 260.041831| 26.625566| 4361| 3| 0.159240| 14.936350| 0.003416| 15.449310| 0.003666| 23666.399953| 0.037933| 0.399900| 23.603727| 0.111820 | 849702763|S264.1609P53.9090|XS04863B6_002 | 10155040| 264.160900| 53.909041| 4863| 6| 0.407487| 18.748560| 0.014215| 19.233860| 0.015527| 4649.657613| 0.107100| 3.022382| 16.279548| 0.369717 | 801664702|S349.5880P00.4935|NULL | 10774344| 349.588069| 0.493526| 4938| 7| 0.376296| 18.852000| 0.018428| 19.022390| 0.013564| 28181.469589| 0.117017| 3.046228| 21.942945| 0.059519 | 275333773|S354.7242P00.8034|XS04095B7_002 | 10859368| 354.724289| 0.803473| 4095| 7| 0.169759| 17.812580| 0.009228| 18.205800| 0.008355| 1513.825375| 0.075283| 1.489627| 12.057631| 0.593043 astropy-astropy-201cddb/astropy/io/ascii/tests/data/no_data_cds.dat000066400000000000000000000044511507226315300256060ustar00rootroot00000000000000 Title: Spitzer Observations of NGC 1333: A Study of Structure and Evolution in a Nearby Embedded Cluster Authors: Gutermuth R.A., Myers P.C., Megeath S.T., Allen L.E., Pipher J.L., Muzerolle J., Porras A., Winston E., Fazio G. Table: Spitzer-identified YSOs: Addendum ================================================================================ Byte-by-byte Description of file: datafile3.txt -------------------------------------------------------------------------------- Bytes Format Units Label Explanations -------------------------------------------------------------------------------- 1- 3 I3 --- Index Running identification number 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) - continuation of description 17 A1 --- DE- Sign of the Declination (J2000) 18- 19 I2 deg DEd Degree of Declination (J2000) 21- 22 I2 arcmin DEm Arcminute of Declination (J2000) 24- 27 F4.1 arcsec DEs Arcsecond of Declination (J2000) 29- 68 A40 --- Match Literature match 70- 75 A6 --- Class Source classification (1) 77-80 F4.2 mag AK ? The K band extinction (2) 82-86 F5.2 --- Fit ? Fit of IRAC photometry (3) -------------------------------------------------------------------------------- Note (1): Asterisks mark "deeply embedded" sources with questionable IRAC colors or incomplete IRAC photometry and relatively bright MIPS 24 micron photometry. Note (2): Only provided for sources with valid JHK_S_ photometry. Note (3): Defined as the slope of the linear least squares fit to the 3.6 - 8.0 micron SEDs in log{lambda} F_{lambda} vs log{lambda} space. Extinction is not accounted for in these values. High extinction can bias Fit to higher values. -------------------------------------------------------------------------------- astropy-astropy-201cddb/astropy/io/ascii/tests/data/no_data_daophot.dat000066400000000000000000000010411507226315300264630ustar00rootroot00000000000000#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 #N SHARPNESS CHI PIER PERROR \ #U ## ## ## perrors \ #F %-23.3f %-12.3f %-6d %-13s astropy-astropy-201cddb/astropy/io/ascii/tests/data/no_data_ipac.dat000066400000000000000000000010051507226315300257410ustar00rootroot00000000000000\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 | astropy-astropy-201cddb/astropy/io/ascii/tests/data/no_data_sextractor.dat000066400000000000000000000001721507226315300272270ustar00rootroot00000000000000# 1 NUMBER Galaxy ID number # 2 FLUX_ISO # 3 FLUXERR_ISO # 4 VALUES Note column 5 is missing # 6 FLAG astropy-astropy-201cddb/astropy/io/ascii/tests/data/no_data_with_header.dat000066400000000000000000000000061507226315300273100ustar00rootroot00000000000000a b c astropy-astropy-201cddb/astropy/io/ascii/tests/data/no_data_without_header.dat000066400000000000000000000000251507226315300300410ustar00rootroot00000000000000# blank data table astropy-astropy-201cddb/astropy/io/ascii/tests/data/sextractor.dat000066400000000000000000000005611507226315300255440ustar00rootroot00000000000000# 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 astropy-astropy-201cddb/astropy/io/ascii/tests/data/sextractor2.dat000066400000000000000000000013431507226315300256250ustar00rootroot00000000000000# 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 astropy-astropy-201cddb/astropy/io/ascii/tests/data/sextractor3.dat000066400000000000000000000021261507226315300256260ustar00rootroot00000000000000# 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 astropy-astropy-201cddb/astropy/io/ascii/tests/data/short.rdb000066400000000000000000000002341507226315300245010ustar00rootroot00000000000000 # blank lines agasc_id n_noids n_obs N N N 115345072 1 1 # comment 335416352 3 8 266612160 1 1 645803280 1 1 117309912 1 1 114950920 1 1 335025040 2 24 astropy-astropy-201cddb/astropy/io/ascii/tests/data/short.rdb.Z000066400000000000000000000002221507226315300247060ustar00rootroot00000000000000 Œ!†M7k@°IãĻ˜€Ü 3gĖ—4d¸ųâæ Æ9ŋŧķP“'ŒŖÆ 5`ܐ‘ F̀ ŽyĶĻM7tΘQƒF DiÎH€#  PcČ8 ŖæM6`â€1CÆVĢ1VƸ1FŽRÁŠĨ‘#fUm†U04ό˜4ĒŌ”Aĸastropy-astropy-201cddb/astropy/io/ascii/tests/data/short.rdb.bz2000066400000000000000000000002221507226315300251720ustar00rootroot00000000000000BZh91AY&SY–@D _€0Hāž¯Œ r"š6Ēdx ÔÁ0¤Úž§¨ĸá­{0øxÚN ˜š—âfg‡å˛ĨldXE´û‰“Ņ<ē&Ļ‘ÕCĩ2ĸÁĩR)!ąAېH$‹Æ,íŸĢ؝f"É3ĄGŽR?ÅܑN$eastropy-astropy-201cddb/astropy/io/ascii/tests/data/short.rdb.gz000066400000000000000000000002241507226315300251170ustar00rootroot00000000000000‹pPshort.rdb-ŒË Ã0DĪģUÜĀ~´˛ÔDZ0ō‡ bKĨ"ė0‡áņ†ÁÉ­gŽw–ztÄüÎ}[ĘuŠ­ė}t[;ž`™MŊŅ,ĀĀč&ˇĩë:ęUÍsPPˆ(!tĪ‚ˇH*ņ!æY)%–?ųd”äqã…ÄČˆGüā¨ü7œastropy-astropy-201cddb/astropy/io/ascii/tests/data/short.rdb.xz000066400000000000000000000003001507226315300251330ustar00rootroot00000000000000ũ7zXZæÖ´F!t/åŖā›€] Œ•yhëfÖŧØã)Ã_ĀČNųŇ:|uÅėĀ'öoáuķŌXŲzŖ×HÛ_Îh$BīœÛ?h0ĩÔ ûąVvl•ĢÄ,ŽéS]ĐÔ"7î7Ø xĄŗč9žõf`Œ,x ņ•Õĩîû…'ÄĪŊ&¤Œ ]ÅŪĢž­ÕõB“)ú™<œœ~ÅbąÄgûYZastropy-astropy-201cddb/astropy/io/ascii/tests/data/short.tab000066400000000000000000000001721507226315300245010ustar00rootroot00000000000000agasc_id n_noids n_obs 115345072 1 1 335416352 3 8 266612160 1 1 645803280 1 1 117309912 1 1 114950920 1 1 335025040 2 24 astropy-astropy-201cddb/astropy/io/ascii/tests/data/simple.txt000066400000000000000000000001751507226315300247070ustar00rootroot00000000000000 'test 1a' test2 test3 test4 # fun1 fun2 fun3 fun4 fun5 top1 top2 top3 top4 hat1 hat2 hat3 hat4 astropy-astropy-201cddb/astropy/io/ascii/tests/data/simple2.txt000066400000000000000000000003651507226315300247720ustar00rootroot00000000000000obsid | redshift | X | Y | object | rad 3102 | 0.32 | 4167 | 4085 | Q1250+568-A | 9 3102 | 0.32 | 4706 | 3916 | Q1250+568-B | 14 877 | 0.22 | 4378 | 3892 | 'Source 82' | 12.5 astropy-astropy-201cddb/astropy/io/ascii/tests/data/simple3.txt000066400000000000000000000001441507226315300247660ustar00rootroot00000000000000obsid|redshift|X|Y|object|rad 877|0.22|4378|3892|'Sou,rce82'|12.5 3102|0.32|4167|4085|Q1250+568-A|9 astropy-astropy-201cddb/astropy/io/ascii/tests/data/simple4.txt000066400000000000000000000002701507226315300247670ustar00rootroot000000000000003102 | 0.32 | 4167 | 4085 | Q1250+568-A | 9 3102 | 0.32 | 4706 | 3916 | Q1250+568-B | 14 877 | 0.22 | 4378 | 3892 | 'Source 82' | 12.5 astropy-astropy-201cddb/astropy/io/ascii/tests/data/simple5.txt000066400000000000000000000003571507226315300247760ustar00rootroot00000000000000# Purposely make an ill-formed data file (in last row) 3102 | 0.32 | 4167 | 4085 | Q1250+568-A | 9 3102 | 0.32 | 4706 | 3916 | Q1250+568-B | 14 877 | 4378 | 3892 | 'Source 82' | 12.5 astropy-astropy-201cddb/astropy/io/ascii/tests/data/simple_csv.csv000066400000000000000000000000211507226315300255240ustar00rootroot00000000000000a,b,c 1,2,3 4,5,6astropy-astropy-201cddb/astropy/io/ascii/tests/data/simple_csv_missing.csv000066400000000000000000000000161507226315300272610ustar00rootroot00000000000000a,b,c 1 4,5,6 astropy-astropy-201cddb/astropy/io/ascii/tests/data/space_delim_blank_lines.txt000066400000000000000000000003531507226315300302220ustar00rootroot00000000000000obsid offset x y name oaa 3102 0.32 4167 4085 Q1250+568-A 9 3102 0.32 4706 3916 Q1250+568-B 14 877 0.22 4378 3892 "Source 82" 12.5 astropy-astropy-201cddb/astropy/io/ascii/tests/data/space_delim_no_header.dat000066400000000000000000000000301507226315300276060ustar00rootroot000000000000001 3.4 hello 2 6.4 world astropy-astropy-201cddb/astropy/io/ascii/tests/data/space_delim_no_names.dat000066400000000000000000000000101507226315300274570ustar00rootroot000000000000001 2 3 4 astropy-astropy-201cddb/astropy/io/ascii/tests/data/subtypes.ecsv000066400000000000000000000153361507226315300254220ustar00rootroot00000000000000# %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]]" astropy-astropy-201cddb/astropy/io/ascii/tests/data/test4.dat000066400000000000000000000013401507226315300244050ustar00rootroot00000000000000# whitespace separated zabs1.nh p1.gamma p1.ampl statname statval 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 # comment here 0.0913252611282 1.28738450369 0.000703999531569 chi2gehrels 416.904139981 0.0943815607455 1.29839188657 0.000708725775733 chi2datavar 572.734008 0.0943792771442 1.29837677223 0.00070871697621 chi2xspecvar 572.734013473 0.0867953584196 1.27046735536 0.000699532088738 cstat 512.433488994 0.0846479114132 1.26584338176 0.000697063608605 chi2constvar 440.651434041 astropy-astropy-201cddb/astropy/io/ascii/tests/data/test5.dat000066400000000000000000000026771507226315300244240ustar00rootroot00000000000000# whitespace separated with lines to skip ------------------------------------------ zabs1.nh p1.gamma p1.ampl statname statval ------------------------------------------ 0.095196313612 1.29238107724 0.000709438701165 chi2xspecvar 455.385700456 0.0898827896112 1.27317260145 0.000703680688865 cstat 450.402806957 0.0845373292976 1.26032264432 0.000697817633266 chi2constvar 427.888401816 0.0813955290921 1.25278166998 0.000694773889339 chi2modvar 422.655226097 0.0837813193374 1.26108631851 0.000697168659777 cash -582096.060739 0.0877788113875 1.27498889089 0.000700963122261 chi2gehrels 336.255262001 0.0886095763534 1.27831934755 0.000702152760295 chi2datavar 427.87097831 0.0886062881606 1.27831561342 0.000702152575029 chi2xspecvar 427.870972282 0.0837839157029 1.26109967845 0.000697177275745 cstat 423.869897301 0.0848856095291 1.26216881055 0.000697245258092 chi2constvar 495.692552206 0.0834040516574 1.25034791909 0.000694504650678 chi2modvar 448.488349352 0.0863275923367 1.25920642303 0.000697302969088 cash -581109.867406 0.0910593842926 1.27434931431 0.000701687557965 chi2gehrels 362.107884887 0.0925984360666 1.27857224315 0.000703586368322 chi2datavar 467.653055046 0.0926057133247 1.27858701992 0.000703594356786 chi2xspecvar 467.653060082 0.0863257498551 1.259192667 0.000697300429366 cstat 451.536967896 0.0880503692681 1.2588289844 0.000698437310968 chi2constvar 439.513117058 0.0852962921333 1.25214407357 0.000696223065852 chi2modvar 443.456904712 astropy-astropy-201cddb/astropy/io/ascii/tests/data/vizier/000077500000000000000000000000001507226315300241625ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/io/ascii/tests/data/vizier/ReadMe000066400000000000000000000114511507226315300252440ustar00rootroot00000000000000J/A+A/511/A56 Abundances of five open clusters (Pancino+, 2010) ================================================================================ Chemical abundance analysis of the open clusters Cr 110, NGC 2420, NGC 7789, and M 67 (NGC 2682). Pancino E., Carrera R., Rossetti, E., Gallart C. =2010A&A...511A..56P ================================================================================ ADC_Keywords: Clusters, open ; Stars, giant ; Equivalent widths ; Spectroscopy Keywords: stars: abundances - Galaxy: disk - open clusters and associations: general Abstract: The present number of Galactic open clusters that have high resolution abundance determinations, not only of [Fe/H], but also of other key elements, is largely insufficient to enable a clear modeling of the Galactic disk chemical evolution. To increase the number of Galactic open clusters with high quality measurements, we obtained high resolution (R~30000), high quality (S/N~50-100 per pixel), echelle spectra with the fiber spectrograph FOCES, at Calar Alto, Spain, for three red clump stars in each of five Open Clusters. We used the classical equivalent width analysis method to obtain accurate abundances of sixteen elements: Al, Ba, Ca, Co, Cr, Fe, La, Mg, Na, Nd, Ni, Sc, Si, Ti, V, and Y. We also derived the oxygen abundance using spectral synthesis of the 6300{AA} forbidden line. Description: Atomic data and equivalent widths for 15 red clump giants in 5 open clusters: Cr 110, NGC 2099, NGC 2420, M 67, NGC 7789. File Summary: -------------------------------------------------------------------------------- FileName Lrecl Records Explanations -------------------------------------------------------------------------------- ReadMe 80 . This file table1.dat 103 15 Observing logs and programme stars information table5.dat 56 5265 Atomic data and equivalent widths -------------------------------------------------------------------------------- See also: J/A+A/455/271 : Abundances of red giants in NGC 6441 (Gratton+, 2006) J/A+A/464/953 : Abundances of red giants in NGC 6441 (Gratton+, 2007) J/A+A/505/117 : Abund. of red giants in 15 globular clusters (Carretta+, 2009) Byte-by-byte Description of file: table1.dat -------------------------------------------------------------------------------- Bytes Format Units Label Explanations -------------------------------------------------------------------------------- 1- 7 A7 --- Cluster Cluster name 9- 12 I4 --- Star Star number within the cluster 14- 15 I2 h RAh Right ascension (J2000) 17- 18 I2 min RAm Right ascension (J2000) 20- 23 F4.1 s RAs Right ascension (J2000) 25 A1 --- DE- Declination sign (J2000) 26- 27 I2 deg DEd Declination (J2000) 29- 30 I2 arcmin DEm Declination (J2000) 32- 35 F4.1 arcsec DEs Declination (J2000) 37- 41 F5.2 mag Bmag B magnitude 43- 47 F5.2 mag Vmag V magnitude 49- 53 F5.2 mag Icmag ?=- Cousins I magnitude 55- 59 F5.2 mag Rmag ?=- R magnitude 61- 65 F5.2 mag Ksmag Ks magnitude 67 I1 --- NExp Number of exposures 69- 73 I5 s TExp Total exposure time 75- 77 I3 --- S/N Signal-to-nois ratio 79-103 A25 --- SName Simbad name -------------------------------------------------------------------------------- Byte-by-byte Description of file: table5.dat -------------------------------------------------------------------------------- Bytes Format Units Label Explanations -------------------------------------------------------------------------------- 1- 7 A7 --- Cluster Cluster name 9- 12 I4 --- Star Star number within the cluster 14- 20 F7.2 0.1nm Wave Wavelength in Angstroms 22- 23 A2 --- El Element name 24 I1 --- ion Ionization stage (1 for neutral element) 26- 30 F5.2 eV chiEx Excitation potential 32- 37 F6.2 --- loggf Logarithm of the oscillator strength 39- 43 F5.1 0.1pm EW ?=-9.9 Equivalent width (in mA) 46- 49 F4.1 0.1pm e_EW ?=-9.9 rms uncertainty on EW 51- 56 F6.3 --- Q ?=-9.999 DAOSPEC quality parameter Q (large values are bad) -------------------------------------------------------------------------------- Acknowledgements: Elena Pancino, elena.pancino(at)oabo.inaf.it ================================================================================ (End) Elena Pancino [INAF-OABo, Italy], Patricia Vannier [CDS] 23-Nov-2009 astropy-astropy-201cddb/astropy/io/ascii/tests/data/vizier/table1.dat000066400000000000000000000026611507226315300260310ustar00rootroot00000000000000Cr110 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 astropy-astropy-201cddb/astropy/io/ascii/tests/data/vizier/table5.dat000066400000000000000000000053511507226315300260340ustar00rootroot00000000000000Cr110 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 astropy-astropy-201cddb/astropy/io/ascii/tests/data/vots_spec.dat000066400000000000000000000134731507226315300253610ustar00rootroot00000000000000#################################################################################### ## ## VOTable-Simple Specification ## ## 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 ## 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 ## performing database table creation and ingest. ## ## A python application is available which uses the STILTS java package to ## convert from a VOTS format to any of the (many) output formats supported by ## STILTS. This application can also generate a documentation file (in ## reStructured Text format) or a Django model definition from a VOTS table. ## ## Key VOTable and STILTS references: ## Full spec: http://www.ivoa.net/Documents/latest/VOT.html ## Datatypes: http://www.ivoa.net/Documents/REC/VOTable/VOTable-20040811.html#ToC11 ## FIELD def: http://www.ivoa.net/Documents/REC/VOTable/VOTable-20040811.html#ToC25 ## STILTS : http://www.star.bris.ac.uk/~mbt/stilts/ ## ## The VOTable-Simple format consists of header information followed by the tabular ## data elements. The VOTS header lines are all preceded by a single '#' character. ## Comments are preceded by '##' at the beginning of a line. ## ## The VOTS header defines the metadata associated with the table. In the ## VOTable-Simple format words in all CAPS (followed by ::) refer to the ## corresponding metadata elements in the VOTable specification. For instance ## the DESCRIPTION:: keyword precedes the lines that are used in the VOTable ## element. The COOSYS::, PARAM::, and FIELD:: keywords are ## each followed by a whitespace-delimited table that defines the corresponding ## 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 throughout the table. ## ##---------------------------------------------------------------------------------- ## Table description, corresponding to the VOTable TABLE::DESCRIPTION element. ##---------------------------------------------------------------------------------- # DESCRIPTION:: # This is a sample table that shows a proposed format for generation of tables # for the C-COSMOS collaboration. This format is compatible with simple 'awk' or # S-mongo style processing but also allows full self-documentation and conversion # to more robust data formats (FITS, VOTable, postgres database ingest, etc). # ##---------------------------------------------------------------------------------- ## Coordinate system specification COOSYS. This is a "future" feature, as the ## current conversion code does not use this field. ##---------------------------------------------------------------------------------- # COOSYS:: # ID equinox epoch system # J2000 J2000. J2000. eq_FK5 # ##---------------------------------------------------------------------------------- ## Set the TABLE::PARAM values, which are values that apply for the entire table. ##---------------------------------------------------------------------------------- # PARAM:: # name datatype value description # version string 1.1 'Table version' # date string 2007/12/01 'Table release date' # ##---------------------------------------------------------------------------------- ## Define the column names via the FIELD element. The attributes 'name', ## '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 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) ## The quantities in a column of the table may be expressed in some physical ## unit, which is specified by the unit attribute of the FIELD. The syntax of ## the unit string is defined in reference [3]; it is basically written as a ## string without blanks or spaces, where the symbols . or * indicate a ## multiplication, / stands for the division, and no special symbol is ## required for a power. Examples are unit="m2" for m2, unit="cm-2.s-1.keV-1" ## for cm-2s-1keV-1, or unit="erg/s" for erg s-1. The references [3] provide ## also the list of the valid symbols, which is essentially restricted to the ## Systeme International (SI) conventions, plus a few astronomical extensions ## concerning units used for time, angular, distance and energy measurements. ##---------------------------------------------------------------------------------- # FIELD:: # name datatype unit ucd description # id int '' 'meta.id' 'C-COSMOS short identifier number' # name string '' '' 'C-COSMOS long identifier name' # ra double deg 'meta.cryptic' 'Right Ascension' # dec double deg '' Declination # flux float erg/cm2/s '' Flux # ##---------------------------------------------------------------------------------- ## Now the actual field data in the order specified by the FIELD:: list. ## The data fields can be separated by tabs or spaces. If using spaces, ## any fields that contain a space must be enclosed in single quotes. ## 12 'CXOCS J193423+022312' 150.01212 2.52322 1.21e-13 13 'CXOCS J193322+024444' 150.02323 2.54444 1.21e-14 14 'CXOCS J195555+025555' 150.04444 2.55555 1.21e-15 astropy-astropy-201cddb/astropy/io/ascii/tests/data/whitespace.dat000066400000000000000000000001561507226315300255020ustar00rootroot00000000000000 "quoted colname with tab inside" col2 col3 val1 "val2 with tab" 2 val3 val4 3 astropy-astropy-201cddb/astropy/io/ascii/tests/test_c_reader.py000066400000000000000000001651171507226315300251310ustar00rootroot00000000000000# 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) astropy-astropy-201cddb/astropy/io/ascii/tests/test_cds.py000066400000000000000000000665711507226315300241420ustar00rootroot00000000000000# 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() import pytest 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 astropy-astropy-201cddb/astropy/io/ascii/tests/test_cds_header_from_readme.py000066400000000000000000000163761507226315300300100ustar00rootroot00000000000000# 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 import units as u from astropy.io import ascii from .common import ( setup_function, # noqa: F401 teardown_function, # noqa: F401 ) def read_table1(readme, data): reader = ascii.Cds(readme) return reader.read(data) def read_table2(readme, data): reader = ascii.get_reader(reader_cls=ascii.Cds, readme=readme) reader.outputter = ascii.TableOutputter() return reader.read(data) def read_table3(readme, data): 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 = "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 len(table) == 18 assert_allclose(table["Lambda"][0], 6476.09) assert table["Fnu"][-1] == "0.489005" def test_glob_header(): 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 len(table) == 291 assert table["Name"][-1] == "J1914+0953" assert table["BibCode"][-2] == "2005A&A...432..235R" def test_header_from_readme(): 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")): assert val == Bmag[i] table = r.read("data/vizier/table5.dat") 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, ] 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)" astropy-astropy-201cddb/astropy/io/ascii/tests/test_compressed.py000066400000000000000000000036441507226315300255250ustar00rootroot00000000000000# 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()) astropy-astropy-201cddb/astropy/io/ascii/tests/test_connect.py000066400000000000000000000073321507226315300250100ustar00rootroot00000000000000# 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) astropy-astropy-201cddb/astropy/io/ascii/tests/test_ecsv.py000066400000000000000000001026511507226315300243170ustar00rootroot00000000000000# 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 os import sys from contextlib import nullcontext from io import StringIO import numpy as np import pytest import yaml from astropy import units as u from astropy.io import ascii from astropy.io.ascii.ecsv import DELIMITERS, InvalidEcsvDatatypeWarning 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.masked import Masked from .common import TEST_DIR 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_write_read_roundtrip(): """ 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 for delimiter in DELIMITERS: out = StringIO() t.write(out, format="ascii.ecsv", delimiter=delimiter) t2s = [ Table.read(out.getvalue(), format="ascii.ecsv"), 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) def test_bad_header_start(): """ Bad header without initial # %ECSV x.x """ lines = copy.copy(SIMPLE_LINES) lines[0] = "# %ECV 0.9" with pytest.raises(ascii.InconsistentTableError): Table.read("\n".join(lines), format="ascii.ecsv", guess=False) def test_bad_delimiter_input(): """ Illegal delimiter in input """ lines = copy.copy(SIMPLE_LINES) lines.insert(2, "# delimiter: |") with pytest.raises(ValueError) as err: Table.read("\n".join(lines), format="ascii.ecsv", guess=False) assert "only space and comma are allowed" in str(err.value) def test_multidim_input(): """ 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="ascii.ecsv") 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(): """ 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="ascii.ecsv") 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(): """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="ascii.ecsv") assert t.dtype == t2.dtype assert len(t2) == 0 def test_csv_ecsv_colnames_mismatch(): """ 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: ascii.read(lines, format="ecsv") 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(): """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="ascii.ecsv") 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): """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="ascii.ecsv") 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: import itertools idxs = [idx for idx, _ in zip(itertools.cycle([0, 1]), 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): """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="ascii.ecsv") 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): """ 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) 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): """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) 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) assert t4["l"].unit is unit assert np.all(t4["l"] == t["l"]) def test_read_masked_bool(): txt = """\ # %ECSV 1.0 # --- # datatype: # - {name: col0, datatype: bool} # schema: astropy-2.0 col0 1 0 True "" False """ dat = ascii.read(txt, format="ecsv") 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): # 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) t2 = Table.read(out.getvalue(), format="ascii.ecsv") 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 = ascii.read(txt, format="ecsv") assert t["a"].dtype.kind == "U" assert t["a"][0] == "[1,2]" def test_multidim_bad_shape(): """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="ascii.ecsv") 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(): """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="ascii.ecsv") def test_read_bad_datatype(): """Test a malformed ECSV file""" txt = """\ # %ECSV 1.0 # --- # datatype: # - {name: a, datatype: object} # schema: astropy-2.0 a fail [3,4]""" with pytest.warns( InvalidEcsvDatatypeWarning, match="unexpected datatype 'object' of column 'a' is not in allowed", ): t = Table.read(txt, format="ascii.ecsv") assert t["a"][0] == "fail" assert type(t["a"][1]) is str assert type(t["a"].dtype) == np.dtype("O") def test_read_complex(): """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="ascii.ecsv") assert t["a"].dtype.type is np.complex128 def test_read_str(): """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="ascii.ecsv") assert isinstance(t["a"][1], str) assert isinstance(t["a"][0], np.str_) def test_read_bad_datatype_for_object_subtype(): """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="ascii.ecsv") def test_full_repr_roundtrip(): """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="ascii.ecsv") assert np.all(t["a"] == t2["a"]) 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 out = yaml.safe_load("\n".join(lines)) return out def _make_expected_values(cols): from pprint import pformat 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): """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="ascii.ecsv") 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(): """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")) 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(): """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="ascii.ecsv") 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(): """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="ascii.ecsv") 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): 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="ascii.ecsv") 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(): # 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="ascii.ecsv") # Note that value under time does not roundtrip assert (qt2["t"] == t).all() assert (qt2["t"].mask == t.mask).all() astropy-astropy-201cddb/astropy/io/ascii/tests/test_fixedwidth.py000066400000000000000000000424461507226315300255230ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst from io import StringIO import numpy as np import pytest from numpy.testing import assert_allclose from astropy.io import ascii from astropy.io.ascii.core import InconsistentTableError from .common import assert_equal_splitlines def test_read_normal(): """Nice, typical fixed format table""" table = """ # comment (with blank line above) | Col1 | Col2 | | 1.2 | "hello" | | 2.4 |'s worlds| """ reader = ascii.get_reader(reader_cls=ascii.FixedWidth) dat = reader.read(table) 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(): """Nice, typical fixed format table with col names provided""" table = """ # comment (with blank line above) | Col1 | Col2 | | 1.2 | "hello" | | 2.4 |'s worlds| """ reader = ascii.get_reader(reader_cls=ascii.FixedWidth, names=("name1", "name2")) dat = reader.read(table) assert dat.colnames == ["name1", "name2"] assert_allclose(dat[1][0], 2.4) def test_read_normal_names_include(): """Nice, typical fixed format table with col names provided""" table = """ # comment (with blank line above) | Col1 | Col2 | Col3 | | 1.2 | "hello" | 3 | | 2.4 |'s worlds| 7 | """ reader = ascii.get_reader( reader_cls=ascii.FixedWidth, names=("name1", "name2", "name3"), include_names=("name1", "name3"), ) dat = reader.read(table) assert dat.colnames == ["name1", "name3"] assert_allclose(dat[1][0], 2.4) assert dat[0][1] == 3 def test_read_normal_exclude(): """Nice, typical fixed format table with col name excluded""" table = """ # comment (with blank line above) | Col1 | Col2 | | 1.2 | "hello" | | 2.4 |'s worlds| """ reader = ascii.get_reader(reader_cls=ascii.FixedWidth, exclude_names=("Col1",)) dat = reader.read(table) assert dat.colnames == ["Col2"] assert dat[1][0] == "'s worlds" def test_read_weird(): """Weird input table with data values chopped by col extent""" table = """ Col1 | Col2 | 1.2 "hello" 2.4 sdf's worlds """ reader = ascii.get_reader(reader_cls=ascii.FixedWidth) dat = reader.read(table) 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(): """Table with double delimiters""" table = """ || Name || Phone || TCP|| | John | 555-1234 |192.168.1.10X| | Mary | 555-2134 |192.168.1.12X| | Bob | 555-4527 | 192.168.1.9X| """ 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(): """Table with space delimiter""" table = """ Name --Phone- ----TCP----- John 555-1234 192.168.1.10 Mary 555-2134 192.168.1.12 Bob 555-4527 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(): """Table with no header row and auto-column naming""" table = """ | John | 555-1234 |192.168.1.10| | Mary | 555-2134 |192.168.1.12| | Bob | 555-4527 | 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(): """Table with no header row and with col names provided. Second 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| """ 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(): """Table with no header row and auto-column naming""" table = """ | John | 555-1234 |192.168.1.10| | Mary | 555-2134 |192.168.1.12| | Bob | 555-4527 | 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(): """Table with no header row and with col names provided. Second 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| """ 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(): """Table with no delimiter with column start and end values specified.""" table = """ # 5 9 17 18 28 # | | || | John 555- 1234 192.168.1.10 Mary 555- 2134 192.168.1.12 Bob 555- 4527 192.168.1.9 """ 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 = """\ | Col1 | Col2 | Col3 | Col4 | | 1.2 | "hello" | 1 | a | | 2.4 | 's worlds | 2 | 2 | """ dat = ascii.read(table, format="fixed_width") def test_write_normal(): """Write a table as a normal fixed width table.""" 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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" 2.4 '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 ======= =========== 1.2 "hello" 2.4 '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(): """Read text table designed for humans and test having position line before the header line""" table = """ +------+----------+ | Col1 | Col2 | +------|----------+ | 1.2 | "hello" | | 2.4 | '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 = 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 = 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 = 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", [ lambda path: os.fspath(path), lambda path: Path(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 = """\
C1 C2 C3
1 1.0 1.0 a a a
2 2.0 2.0 b b b
3 3.0 3.0 c c c
""" 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 = """\
C1 C2 C3
1 1.0 1.0 a
2 2.0 2.0 b b
3 3.0 3.0 c c c
""" 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 = """\
C1 C2 C3
1 1.0 .. 1.0 a .. a
2 2.0 .. 2.0 b .. b
3 3.0 .. 3.0 c .. 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) astropy-astropy-201cddb/astropy/table/meta.py000066400000000000000000000323611507226315300214740ustar00rootroot00000000000000import 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 astropy-astropy-201cddb/astropy/table/mixins/000077500000000000000000000000001507226315300214765ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/table/mixins/__init__.py000066400000000000000000000000001507226315300235750ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/table/mixins/dask.py000066400000000000000000000017601507226315300227760ustar00rootroot00000000000000import 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 astropy-astropy-201cddb/astropy/table/mixins/registry.py000066400000000000000000000052631507226315300237260ustar00rootroot00000000000000# 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__, None ) # 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) astropy-astropy-201cddb/astropy/table/mixins/tests/000077500000000000000000000000001507226315300226405ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/table/mixins/tests/__init__.py000066400000000000000000000000001507226315300247370ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/table/mixins/tests/test_dask.py000066400000000000000000000037711507226315300252030ustar00rootroot00000000000000import 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" astropy-astropy-201cddb/astropy/table/mixins/tests/test_registry.py000066400000000000000000000060651507226315300261300ustar00rootroot00000000000000from 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" ) astropy-astropy-201cddb/astropy/table/ndarray_mixin.py000066400000000000000000000041731507226315300234120ustar00rootroot00000000000000# 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 if callable(super().__array_finalize__): 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) astropy-astropy-201cddb/astropy/table/notebook_backends.py000066400000000000000000000106071507226315300242170ustar00rootroot00000000000000# 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) astropy-astropy-201cddb/astropy/table/np_utils.py000066400000000000000000000135501507226315300224020ustar00rootroot00000000000000""" High-level operations for numpy structured arrays. Some code and inspiration taken from numpy.lib.recfunctions.join_by(). Redistribution license restrictions apply. """ import collections from collections import Counter, OrderedDict from collections.abc import Sequence import numpy as np __all__ = ["TableMergeError"] class TableMergeError(ValueError): pass 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 structured ndarrays ``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.dtype.names: 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.dtype.names 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 = common_dtype(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("Key columns 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 common_dtype(cols): """ Use numpy to find the common dtype for a list of structured ndarray columns. Only allow columns within the following fundamental numpy data types: np.bool_, np.object_, np.number, np.character, np.void """ np_types = (np.bool_, np.object_, np.number, np.character, np.void) uniq_types = { tuple(issubclass(col.dtype.type, np_type) for np_type in np_types) for col in cols } if len(uniq_types) > 1: # Embed into the exception the actual list of incompatible types. incompat_types = [col.dtype.name for col in cols] tme = TableMergeError(f"Columns have incompatible types {incompat_types}") tme._incompat_types = incompat_types raise tme arrs = [np.empty(1, dtype=col.dtype) for col in cols] # For string-type arrays need to explicitly fill in non-zero # values or the final arr_common = .. step is unpredictable. for arr in arrs: if arr.dtype.kind in ("S", "U"): arr[0] = "0" * arr.itemsize arr_common = np.array([arr[0] for arr in arrs]) return arr_common.dtype.str def _check_for_sequence_of_structured_arrays(arrays): err = "`arrays` arg must be a sequence (e.g. list) of structured arrays" if not isinstance(arrays, Sequence): raise TypeError(err) for array in arrays: # Must be structured array if not isinstance(array, np.ndarray) or array.dtype.names is None: raise TypeError(err) if len(arrays) == 0: raise ValueError("`arrays` arg must include at least one array") astropy-astropy-201cddb/astropy/table/operations.py000066400000000000000000001644201507226315300227330ustar00rootroot00000000000000"""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 .np_utils import TableMergeError 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"]} 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.shape = col1.shape + (1,) if col2.ndim == 1: col2.shape = 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:] try: col.shape = (len(tables), n_row) + col.shape[1:] except AttributeError: 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 = common_dtype(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 common_dtype(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.common_dtype(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 = common_dtype([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). array_mask.shape = (col.shape[0],) + (1,) * (len(col.shape) - 1) # 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 astropy-astropy-201cddb/astropy/table/pandas.py000066400000000000000000000032741507226315300220150ustar00rootroot00000000000000ascii_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 astropy-astropy-201cddb/astropy/table/pprint.py000066400000000000000000000736621507226315300220730ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst import fnmatch import os import re import sys from shutil import get_terminal_size import numpy as np from astropy import log from astropy.utils.console import Getch, color_print, conf from astropy.utils.data_info import dtype_info_name __all__ = [] def default_format_func(format_, val): if isinstance(val, bytes): return val.decode("utf-8", errors="replace") else: return str(val) # The first three functions are helpers for _auto_format_func 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(). """ 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. """ 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 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 ---------- col_name : object, optional Hashable object to identify column like id or name. Default is None. possible_string_format_functions : func, optional Function that yields possible string formatting functions (defaults to internal function to do this). Returns ------- Wrapped ``auto_format_func`` function """ 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. Returns the formatted value. """ if format_ is None: return default_format_func(format_, val) 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." ) # 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) col.info._format_funcs[format_] = format_func return out return _auto_format_func 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. These may be fnmatch unix-style globs. """ 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.table.conf.max_lines`` 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.table.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 precedent, 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 multidims: # 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]) elif multidims_has_zero: # Any zero dimension means there is no data to print return "" else: left = format_func(col_format, col[(idx,) + multidim0]) right = format_func(col_format, col[(idx,) + multidim1]) return f"{left} .. {right}" elif is_scalar: return format_func(col_format, col) else: return format_func(col_format, col[idx]) # 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}"' ) 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 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 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 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") astropy-astropy-201cddb/astropy/table/row.py000066400000000000000000000160151507226315300213530ustar00rootroot00000000000000# 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 from astropy.utils.compat import COPY_IF_NEEDED 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=COPY_IF_NEEDED): """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) astropy-astropy-201cddb/astropy/table/scripts/000077500000000000000000000000001507226315300216565ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/table/scripts/__init__.py000066400000000000000000000000001507226315300237550ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/table/scripts/showtable.py000066400000000000000000000135671507226315300242340ustar00rootroot00000000000000# 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 """ ) ) 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) astropy-astropy-201cddb/astropy/table/serialize.py000066400000000000000000000464341507226315300225430ustar00rootroot00000000000000# 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 _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) astropy-astropy-201cddb/astropy/table/setup_package.py000066400000000000000000000011061507226315300233520ustar00rootroot00000000000000# 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 astropy-astropy-201cddb/astropy/table/soco.py000066400000000000000000000120721507226315300215060ustar00rootroot00000000000000# 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 itertools import starmap 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(starmap(Node, zip(node_keys, row_index))) self._unique = unique def add(self, key, value): """ 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, value)) def find(self, key): """ Find rows corresponding to the given key. """ return [node.value for node in self._nodes.irange(key, key)] def remove(self, key, data=None): """ 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): """ Decrement rows larger than the given row. """ for node in self._nodes: if node.value > row: node.value -= 1 def shift_right(self, row): """ 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): """ 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 result.items() def sort(self): """ 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, upper, bounds=(True, True)): """ Return row values in the given range. """ iterator = self._nodes.irange(lower, upper, bounds) return [node.value for node in iterator] def replace_rows(self, row_map): """ 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}>" astropy-astropy-201cddb/astropy/table/sorted_array.py000066400000000000000000000221171507226315300232420ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst import numpy as np 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, row_index, unique=False): self.data = data self.row_index = row_index self.num_cols = len(getattr(data, "colnames", [])) self.unique = unique @property def cols(self): return list(self.data.columns.values()) def add(self, key, row): """ 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): """ 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, upper, bounds): """ Find values in the given range. Parameters ---------- lower : tuple Lower search bound upper : tuple Upper search bound 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. """ lower_pos = self.find_pos(lower, 0) upper_pos = 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, data): """ 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): """ 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): """ 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): """ 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): """ 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): """ Make row order align with key order. """ self.row_index = np.arange(len(self.row_index)) def sorted_data(self): """ 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}" astropy-astropy-201cddb/astropy/table/table.py000066400000000000000000004717141507226315300216460ustar00rootroot00000000000000# 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 from pathlib import Path 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 ShapedLikeNDArray, deprecated from astropy.utils.compat import COPY_IF_NEEDED, NUMPY_LT_1_25 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_pandas", "Table.to_pandas"): ["pandas"]} _pprint_docs = """ {__doc__} 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) def _get_names_from_list_of_dict(rows): """Return list of column names if ``rows`` is a list of dict that defines table data. 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): """OrderedDict subclass for a set of columns. This class enhances item access to provide convenient access to columns by name or index, including slice access. It also handles renaming of columns. 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 ---------- cols : dict, list, tuple; optional Column objects as data structure that can init dict (see above) """ def __init__(self, cols={}): if isinstance(cols, (list, tuple)): # `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(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, str): return OrderedDict.__getitem__(self, 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 self.__class__([self[x] for x in item]) elif isinstance(item, slice): return self.__class__([self[x] for x in list(self)[item]]) else: raise IndexError( f"Illegal key or index value for {type(self).__name__} object" ) 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). 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'``. """ if item in self and not validated: raise ValueError( f"Cannot replace column '{item}'. Use Table.replace_column() instead." ) super().__setitem__(item, value) def __repr__(self): names = (f"'{x}'" for x in self.keys()) return f"<{self.__class__.__name__} names=({','.join(names)})>" 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") 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__}" ) # 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) 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. Parameters ---------- cls : class or tuple thereof Column class (including mixin) or tuple of Column classes. 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 def not_isinstance(self, cls): """ Return a list of columns which are not instances of the specified classes. Parameters ---------- cls : class or tuple thereof Column class (including mixin) or tuple of Column classes. 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 # When the deprecation period of setdefault() and update() is over then they # need to be rewritten to raise an error, not removed. @deprecated( since="6.1", alternative="t.setdefault()", name="t.columns.setdefault()" ) def setdefault(self, key, default): return super().setdefault(key, default) @deprecated(since="6.1", alternative="t.update()", name="t.columns.update()") def update(self, *args, **kwargs): return super().update(*args, **kwargs) class TableAttribute(MetaAttribute): """ Descriptor to define a custom attribute for a Table subclass. 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. 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. See the `~astropy.utils.metadata.MetaAttribute` documentation for additional details. Parameters ---------- default : object Default value for attribute 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}} """ class PprintIncludeExclude(TableAttribute): """Maintain tuple that controls table column visibility for print output. This is a descriptor that inherits from MetaAttribute so that the attribute value is stored in the table meta['__attributes__']. This gets used for the ``pprint_include_names`` and ``pprint_exclude_names`` Table attributes. """ def __get__(self, instance, owner_cls): """Get the attribute. This normally returns an instance of this class which is stored on the owner object. """ # 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``. Parameters ---------- instance : object Instance that owns the attribute names : None, str, list, tuple Column name(s) to store, or None to clear """ 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: # This stores names into instance.meta['__attributes__'] as tuple return super().__set__(instance, tuple(names)) def __call__(self): """Get the value of the attribute. Returns ------- names : None, tuple Include/exclude names """ # Get the value from instance.meta['__attributes__'] instance = self._instance_ref() return super().__get__(instance, instance.__class__) def __repr__(self): if hasattr(self, "_instance_ref"): out = f"<{self.__class__.__name__} name={self.name} value={self()}>" else: out = super().__repr__() return out def _add_remove_setup(self, names): """Common setup for add and remove. - 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 def add(self, names): """Add ``names`` to the include/exclude attribute. Parameters ---------- names : str, list, tuple Column name(s) to add """ 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 remove(self, names): """Remove ``names`` from the include/exclude attribute. Parameters ---------- names : str, list, tuple Column name(s) to remove """ self._remove(names, raise_exc=True) def _remove(self, names, raise_exc=False): """Remove ``names`` with optional checking if they exist.""" instance, names, value = self._add_remove_setup(names) # 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 # 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 ---------- names : None, str, list, tuple Column name(s) to store, or None to clear """ class _Context: def __init__(self, descriptor_self): self.descriptor_self = descriptor_self self.names_orig = descriptor_self() def __enter__(self): pass def __exit__(self, type, value, tb): descriptor_self = self.descriptor_self instance = descriptor_self._instance_ref() descriptor_self.__set__(instance, self.names_orig) def __repr__(self): return repr(self.descriptor_self) ctx = _Context(descriptor_self=self) instance = self._instance_ref() self.__set__(instance, names) return ctx class Table: """A class to represent tables of heterogeneous data. `~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. `~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. See also: https://docs.astropy.org/en/stable/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. """ meta = MetaData(copy=False, default_factory=dict) # Define class attributes for core container objects to allow for subclass # customization. Row = Row Column = Column MaskedColumn = MaskedColumn TableColumns = TableColumns TableFormatter = TableFormatter # Unified I/O read and write methods from .connect read = UnifiedReadWriteMethod(TableRead) write = UnifiedReadWriteMethod(TableWrite) pprint_exclude_names = PprintIncludeExclude() pprint_include_names = PprintIncludeExclude() 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). 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). 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) dtype = [] cols = self.columns.values() if names is not None: cols = [col for col in cols if col.info.name in names] 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]) 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._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 = COPY_IF_NEEDED 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)): # 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: init_func = self._init_from_ndarray # _struct n_cols = len(data.dtype.names) 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, Mapping): init_func = self._init_from_dict default_names = list(data) n_cols = len(default_names) elif isinstance(data, Table): # 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: 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(f"Data type {type(data)} not allowed to init Table") # 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 names = [None if name is None else str(name) for name in names] self._check_names_dtype(names, dtype, n_cols) # Finally do the real initialization init_func(data, names, dtype, n_cols, copy) # Set table meta. If copy=True then deepcopy meta otherwise use the # user-supplied meta directly. if meta is not None: self.meta = deepcopy(meta) if copy else meta # Update meta with TableAttributes supplied as kwargs in Table init. # This takes precedence over previously-defined meta. if meta_table_attrs: for attr, value in meta_table_attrs.items(): setattr(self, attr, value) # Whatever happens above, the masked property should be set to a boolean if self.masked not in (None, True, False): raise TypeError("masked property must be None, True or False") self._set_column_attribute("unit", units) self._set_column_attribute("description", descriptions) def _set_column_attribute(self, attr, values): """Set ``attr`` for columns to ``values``, which can be either a dict (keyed by column name) or a dict of name: value pairs. This is used for handling the ``units`` and ``descriptions`` kwargs to ``__init__``. """ if not values: return if isinstance(values, Row): # For a Row object transform to an equivalent dict. values = {name: values[name] for name in values.colnames} if not isinstance(values, Mapping): # If not a dict map, assume iterable and map to dict if the right length if len(values) != len(self.columns): raise ValueError( f"sequence of {attr} values must match number of columns" ) values = dict(zip(self.colnames, values)) for name, value in values.items(): if name not in self.columns: raise ValueError( f"invalid column name {name} for setting {attr} attribute" ) # Special case: ignore unit if it is an empty or blank string if attr == "unit" and isinstance(value, str): if value.strip() == "": value = None if value is not None and value is not np.ma.masked: col = self[name] if attr == "unit" and isinstance(col, Quantity): # Update the Quantity unit in-place col <<= value else: setattr(col.info, attr, value) def __getstate__(self): columns = OrderedDict( (key, col if isinstance(col, BaseColumn) else col_copy(col)) for key, col in self.columns.items() ) return (columns, self.meta) def __setstate__(self, state): columns, meta = state self.__init__(columns, meta=meta) @property def mask(self): # Dynamic view of available masks if self.masked or self.has_masked_columns or self.has_masked_values: mask_table = Table( [ getattr(col, "mask", FalseArray(col.shape)) for col in self.itercols() ], names=self.colnames, copy=False, ) # Set hidden attribute to force inplace setitem so that code like # t.mask['a'] = [1, 0, 1] will correctly set the underlying mask. # See #5556 for discussion. mask_table._setitem_inplace = True else: mask_table = None return mask_table @mask.setter def mask(self, val): self.mask[:] = val @property def _mask(self): """This is needed so that comparison of a masked Table and a MaskedArray works. The requirement comes from numpy.ma.core so don't remove this property. """ return self.as_array().mask def filled(self, fill_value=None): """Return copy of self, with masked values filled. If input ``fill_value`` supplied then that value is used for all masked entries in the table. Otherwise the individual ``fill_value`` defined for each table column is used. Parameters ---------- fill_value : str If supplied, this ``fill_value`` is used for all masked entries in the entire table. Returns ------- filled_table : `~astropy.table.Table` New table with masked values filled """ if self.masked or self.has_masked_columns or self.has_masked_values: # Get new columns with masked values filled, then create Table with those # new cols (copy=False) but deepcopy the meta. data = [ col.filled(fill_value) if hasattr(col, "filled") else col for col in self.itercols() ] return self.__class__(data, meta=deepcopy(self.meta), copy=False) else: # Return copy of the original object. return self.copy() @property def indices(self): """ Return the indices associated with columns of the table as a TableIndices object. """ lst = [] for column in self.columns.values(): for index in column.info.indices: if sum(index is x for x in lst) == 0: # ensure uniqueness lst.append(index) return TableIndices(lst) @property def loc(self): """ Return a TableLoc object that can be used for retrieving rows by index in a given data range. Note that both loc and iloc work only with single-column indices. """ return TableLoc(self) @property def loc_indices(self): """ Return a TableLocIndices object that can be used for retrieving the row indices corresponding to given table index key value or values. """ return TableLocIndices(self) @property def iloc(self): """ Return a TableILoc object that can be used for retrieving indexed rows in the order they appear in the index. """ return TableILoc(self) def add_index(self, colnames, engine=None, unique=False): """ Insert a new index among one or more columns. If there are no indices, make this index the primary table index. Parameters ---------- colnames : str or list List of column names (or a single column name) to index engine : type or None Indexing engine class to use, either `~astropy.table.SortedArray`, `~astropy.table.BST`, or `~astropy.table.SCEngine`. If the supplied argument is None (by default), use `~astropy.table.SortedArray`. unique : bool (default: False) If set to True, an exception will be raised if duplicate rows exist. Raises ------ ValueError If any selected column does not support indexing, or has more than one dimension. ValueError If unique=True and duplicate rows are found. """ if isinstance(colnames, str): colnames = (colnames,) columns = self.columns[tuple(colnames)].values() # make sure all columns support indexing for col in columns: if not getattr(col.info, "_supports_indexing", False): raise ValueError( f'Cannot create an index on column "{col.info.name}", ' f'of type "{type(col)}"' ) if col.ndim > 1: raise ValueError( f"Multi-dimensional column {col.info.name!r} " "cannot be used as an index." ) is_primary = not self.indices index = Index(columns, engine=engine, unique=unique) sliced_index = SlicedIndex(index, slice(0, 0, None), original=True) if is_primary: self.primary_key = colnames for col in columns: col.info.indices.append(sliced_index) def remove_indices(self, colname): """ Remove all indices involving the given column. If the primary index is removed, the new primary index will be the most recently added remaining index. Parameters ---------- colname : str Name of column """ col = self.columns[colname] for index in self.indices: try: index.col_position(col.info.name) except ValueError: pass else: for c in index.columns: c.info.indices.remove(index) def index_mode(self, mode): """ Return a context manager for an indexing mode. Parameters ---------- mode : str Either 'freeze', 'copy_on_getitem', or 'discard_on_copy'. In 'discard_on_copy' mode, indices are not copied whenever columns or tables are copied. In 'freeze' mode, indices are not modified whenever columns are modified; at the exit of the context, indices refresh themselves based on column values. This mode is intended for scenarios in which one intends to make many additions or modifications in an indexed column. In 'copy_on_getitem' mode, indices are copied when taking column slices as well as table slices, so col[i0:i1] will preserve indices. """ return _IndexModeContext(self, mode) def __array__(self, dtype=None, copy=COPY_IF_NEEDED): """Support converting Table 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 dtype is not None: if np.dtype(dtype) != object: raise ValueError("Datatype coercion is not allowed") 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 = [] 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)) # 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) 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"] for i, line in enumerate(lines): if i < n_header: color_print(line, "red") else: 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. """ 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 def show_in_notebook(self, *, backend="ipydatagrid", **kwargs): """Render the table in HTML and show it in the Jupyter notebook. .. note:: The method API was modified in v7.0 to include a ``backend`` argument and require only keyword arguments. Parameters ---------- backend : {"ipydatagrid", "classic"} Backend to use for rendering (default="ipydatagrid"). The "classic" backend is deprecated since v6.1. **kwargs : dict, optional Keyword arguments as accepted by desired backend. See `astropy.table.notebook_backends` for the available backends and their respective keyword arguments. Raises ------ NotImplementedError Requested backend is not supported. 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``. """ 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 @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:: 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 Parameters ---------- max_lines : int 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. """ 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, str): return self.columns[item] 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(f"Illegal type {type(item)} for table item access") def __setitem__(self, item, value): # 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, str): self.remove_column(item) 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) 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 IndexError("illegal key or index value") 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 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): # 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 NotImplemented def __ior__(self, other): try: self.update(other) return self except TypeError: return NotImplemented def index_column(self, name): """ Return the positional index of column ``name``. Parameters ---------- name : str column name Returns ------- 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(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 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 : object Data object for the new column index : int or None 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 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:] if isinstance(col, np.ndarray): col = np.broadcast_to(col, shape=new_shape, subok=True) elif isinstance(col, ShapedLikeNDArray): col = col._apply(np.broadcast_to, shape=new_shape, subok=True) # broadcast_to() results in a read-only array. Apparently it only changes # the view to look like the broadcasted array. 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 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 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") 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") 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: # 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 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): """ Remove a column from the table. This can also be done with:: del table[name] Parameters ---------- 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. Parameters ---------- 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 _convert_string_dtype(self, in_kind, out_kind, encode_decode_func): """ Convert string-like columns to/from bytestring and unicode (internal only). Parameters ---------- 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. 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.char.decode) 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.char.encode) def keep_columns(self, names): """ Keep only the columns specified (remove the others). 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 by setting the ``name`` attribute of the ``info`` property of the column:: table[name].info.name = new_name Parameters ---------- name : str 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(f"Column {name} does not exist") self.columns[name].info.name = new_name def rename_columns(self, names, new_names): """ Rename multiple columns. 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: 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. 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 """ self.insert_row(len(self), vals, mask) 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") if len(self.columns) != len(vals): 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 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] 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. 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 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 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: return ~eq def _rows_equal(self, other): """ Row-wise comparison of table with any other object. This is actual implementation for __eq__. 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. This is the same as the ``==`` comparison for tables. Parameters ---------- other : Table or DataFrame or ndarray An object to compare with table Examples -------- Comparing 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._rows_equal(t2) array([ True, True]) """ 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 if not NUMPY_LT_1_25 else DeprecationWarning, ) # 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: try: result = self.as_array() == other except allowed_numpy_exceptions: result = False return result 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. 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 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_pandas(self, index=None, use_nullable_int=True): """ 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 Specify DataFrame index mode use_nullable_int : bool, default=True Convert integer MaskedColumn to pandas nullable integer type. If ``use_nullable_int=False`` then the column is converted to float with NaN. 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 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 pandas import DataFrame, Series if index is not False: if index in (None, True): # Default is to use the table primary key if available and a single column if self.primary_key and len(self.primary_key) == 1: index = self.primary_key[0] else: index = False else: if index not in self.colnames: raise ValueError( "index must be None, False, True or a table column name" ) def _encode_mixins(tbl): """Encode a Table ``tbl`` that may have mixin columns to a Table with only astropy Columns + appropriate meta-data to allow subsequent decoding. """ from astropy.time import TimeBase, TimeDelta from . import serialize # Convert any Time or TimeDelta columns and pay attention to masking time_cols = [col for col in tbl.itercols() if isinstance(col, TimeBase)] if time_cols: # Make a light copy of table and clear any indices new_cols = [] for col in tbl.itercols(): new_col = ( col_copy(col, copy_indices=False) if col.info.indices else col ) new_cols.append(new_col) tbl = tbl.__class__(new_cols, copy=False) # Certain subclasses (e.g. TimeSeries) may generate new indices on # table creation, so make sure there are no indices on the table. for col in tbl.itercols(): col.info.indices.clear() for col in time_cols: if isinstance(col, TimeDelta): # Convert to nanoseconds (matches astropy datetime64 support) new_col = (col.sec * 1e9).astype("timedelta64[ns]") nat = np.timedelta64("NaT") else: new_col = col.datetime64.copy() nat = np.datetime64("NaT") if col.masked: new_col[col.mask] = nat tbl[col.info.name] = new_col # Convert the table to one with no mixins, only Column objects. encode_tbl = serialize.represent_mixins_as_columns(tbl) return encode_tbl tbl = _encode_mixins(self) badcols = [name for name, col in self.columns.items() if len(col.shape) > 1] if badcols: raise ValueError( f"Cannot convert a table with multidimensional columns to a " f"pandas DataFrame. Offending columns are: {badcols}\n" f"One can filter out such columns using:\n" f"names = [name for name in tbl.colnames if len(tbl[name].shape) <= 1]\n" f"tbl[names].to_pandas(...)" ) out = OrderedDict() for name, column in tbl.columns.items(): if getattr(column.dtype, "isnative", True): out[name] = column else: out[name] = column.data.byteswap().view(column.dtype.newbyteorder("=")) if isinstance(column, MaskedColumn) and np.any(column.mask): if column.dtype.kind in ["i", "u"]: pd_dtype = column.dtype.name if use_nullable_int: # Convert int64 to Int64, uint32 to UInt32, etc for nullable types pd_dtype = pd_dtype.replace("i", "I").replace("u", "U") out[name] = Series(out[name], dtype=pd_dtype) elif column.dtype.kind not in ["f", "c"]: out[name] = column.astype(object).filled(np.nan) kwargs = {} if index: idx = out.pop(index) kwargs["index"] = idx # We add the table index to Series inputs (MaskedColumn with int values) to override # its default RangeIndex, see #11432 for v in out.values(): if isinstance(v, Series): v.index = idx df = DataFrame(out, **kwargs) if index: # Explicitly set the pandas DataFrame index to the original table # index name. df.index.name = idx.info.name return df @classmethod def from_pandas(cls, dataframe, index=False, units=None): """ 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 Include the index column in the returned table (default=False) units: dict 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 """ out = OrderedDict() names = list(dataframe.columns) columns = [dataframe[name] for name in names] datas = [np.array(column) for column in columns] masks = [np.array(column.isnull()) for column in columns] if index: index_name = dataframe.index.name or "index" while index_name in names: index_name = "_" + index_name + "_" names.insert(0, index_name) columns.insert(0, dataframe.index) datas.insert(0, np.array(dataframe.index)) masks.insert(0, np.zeros(len(dataframe), dtype=bool)) if units is None: units = [None] * len(names) else: if not isinstance(units, Mapping): raise TypeError('Expected a Mapping "column-name" -> "unit"') not_found = set(units.keys()) - set(names) if not_found: warnings.warn(f"`units` contains additional columns: {not_found}") units = [units.get(name) for name in names] for name, column, data, mask, unit in zip(names, columns, datas, masks, units): if column.dtype.kind in ["u", "i"] and np.any(mask): # Special-case support for pandas nullable int np_dtype = str(column.dtype).lower() data = np.zeros(shape=column.shape, dtype=np_dtype) data[~mask] = column[~mask] out[name] = MaskedColumn( data=data, name=name, mask=mask, unit=unit, copy=False ) continue if data.dtype.kind == "O": # If all elements of an object array are string-like or np.nan # then coerce back to a native numpy str/unicode array. string_types = (str, bytes) nan = np.nan if all(isinstance(x, string_types) or x is nan for x in data): # Force any missing (null) values to b''. Numpy will # upcast to str/unicode as needed. We go via a list to # avoid replacing objects in a view of the pandas array and # to ensure numpy initializes to string or bytes correctly. data = np.array([b"" if m else d for (d, m) in zip(data, mask)]) # Numpy datetime64 if data.dtype.kind == "M": from astropy.time import Time out[name] = Time(data, format="datetime64") if np.any(mask): out[name][mask] = np.ma.masked out[name].format = "isot" # Numpy timedelta64 elif data.dtype.kind == "m": from astropy.time import TimeDelta data_sec = data.astype("timedelta64[ns]").astype(np.float64) / 1e9 out[name] = TimeDelta(data_sec, format="sec") if np.any(mask): out[name][mask] = np.ma.masked else: if np.any(mask): out[name] = MaskedColumn(data=data, name=name, mask=mask, unit=unit) else: out[name] = Column(data=data, name=name, unit=unit) return cls(out) 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=COPY_IF_NEEDED, 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: 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) return super()._convert_data_to_col( data, copy=copy, default_name=default_name, dtype=dtype, name=name ) astropy-astropy-201cddb/astropy/table/table_helpers.py000066400000000000000000000117351507226315300233610ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst """ Helper functions for table development, mostly creating useful tables for testing. """ import string from itertools import cycle import numpy as np 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. """ import warnings from astropy.io.votable.table import parse from astropy.utils.data import get_pkg_data_filename 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}>" astropy-astropy-201cddb/astropy/table/tests/000077500000000000000000000000001507226315300213315ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/table/tests/__init__.py000066400000000000000000000000001507226315300234300ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/table/tests/conftest.py000066400000000000000000000170471507226315300235410ustar00rootroot00000000000000# 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, Column): """Show that the formatted output from str() works""" from astropy import conf 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.asarray(d) assert np.all(np_data == d) 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 # 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=( "string operation on non-string array" if NUMPY_LT_2_0 else "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_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, 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, 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, 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, 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, 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, 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, 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, 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, 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. """ from inspect import currentframe, getframeinfo 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" @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 astropy-astropy-201cddb/astropy/table/tests/test_groups.py000066400000000000000000000564521507226315300242750ustar00rootroot00000000000000# 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="", " 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>>", ] astropy-astropy-201cddb/astropy/table/tests/test_info.py000066400000000000000000000276261507226315300237120ustar00rootroot00000000000000# 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 astropy-astropy-201cddb/astropy/table/tests/test_init_table.py000066400000000000000000000564761507226315300250760ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst from collections import OrderedDict, UserDict from collections.abc import Mapping 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_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_dtype_mismatch(self, table_type): self._setup(table_type) with pytest.raises(ValueError): table_type(self.data, names=("a",), dtype=("i4", "f4", "i4")) def test_names_cols_mismatch(self, table_type): self._setup(table_type) with pytest.raises(ValueError): table_type(self.data, names=("a",), dtype="i4") @pytest.mark.usefixtures("table_type") class BaseInitFromListLike(BaseInitFrom): def test_names_cols_mismatch(self, table_type): self._setup(table_type) with pytest.raises(ValueError): table_type(self.data, names=["a"], dtype=[int]) def test_names_copy_false(self, table_type): self._setup(table_type) with pytest.raises(ValueError): 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") 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, table_type): """Init with ndarray and copy=False and show that this is a reference to input ndarray""" 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 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, 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, 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_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, table_type): self._setup(table_type) with pytest.raises(ValueError): 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(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_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, 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 test_ndarray_ref(self, table_type): """Init with ndarray and copy=False and show that table uses reference to input ndarray""" 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_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, 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(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) # 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(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, 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_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, 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, 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, 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]) astropy-astropy-201cddb/astropy/table/tests/test_item_access.py000066400000000000000000000220631507226315300252240ustar00rootroot00000000000000# 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 """ import numpy as np import pytest from astropy.table import Row @pytest.mark.usefixtures("table_data") class BaseTestItems: pass @pytest.mark.usefixtures("table_data") class TestTableColumnsItems(BaseTestItems): 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""" 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""" 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 def test_mult_columns(self, table_data): """Access TableColumns with "fancy indexing" 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["b", "c"] assert tc2[1].name == "c" assert tc2[1][1] == 8 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 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][1] == 8 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 @pytest.mark.usefixtures("table_data") class TestTableItems(BaseTestItems): @pytest.mark.parametrize("idx", [1, np.int64(1), np.array(1)]) def test_column(self, table_data, idx): """Column access returns REFERENCE to data""" 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 @pytest.mark.parametrize("idx", [1, np.int64(1), np.array(1)]) def test_row(self, table_data, idx): """Row access returns REFERENCE to data""" 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"] == 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"] == 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"] == 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""" 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] astropy-astropy-201cddb/astropy/table/tests/test_jsviewer.py000066400000000000000000000165331507226315300246100ustar00rootroot00000000000000import 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
a b
""" 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") astropy-astropy-201cddb/astropy/table/tests/test_masked.py000066400000000000000000000645211507226315300242160ustar00rootroot00000000000000# 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.compat import NUMPY_LT_2_0 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"] if not NUMPY_LT_2_0: DTYPES_TEST_INIT.extend( [ 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]) astropy-astropy-201cddb/astropy/table/tests/test_mixin.py000066400000000000000000001066601507226315300240770ustar00rootroot00000000000000# 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.helper import skycoord_equal from astropy.coordinates.tests.test_representation import representation_equal 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.compat import NUMPY_LT_2_0 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. """ from astropy.io.ascii.connect import _get_connectors_table 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., 1., 0.)", ] if NUMPY_LT_2_0 else [ " col0 ", "---------------", "(0.0, 1.0, 0.0)", ], id="cartesian_wo_unit", ), pytest.param( coordinates.CartesianRepresentation([0], [1], [0], unit="m"), [ " col0 ", " m ", "------------", "(0., 1., 0.)", ] if NUMPY_LT_2_0 else [ " 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., 20., 1.)", ] if NUMPY_LT_2_0 else [ " 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., 20.)", ] if NUMPY_LT_2_0 else [ " 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., 2., 10.)", ] if NUMPY_LT_2_0 else [ " 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., 1) .. (3., 3) one .. three", "(2., 2) .. (4., 4) two .. four", ] if NUMPY_LT_2_0 else [ " 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. from astropy.timeseries import TimeSeries 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) astropy-astropy-201cddb/astropy/table/tests/test_pickle.py000066400000000000000000000100221507226315300242040ustar00rootroot00000000000000import pickle import numpy as np from astropy.coordinates import 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) 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) 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) 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) 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) 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_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) 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 astropy-astropy-201cddb/astropy/table/tests/test_pprint.py000066400000000000000000001071571507226315300242710ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst from io import StringIO from shutil import get_terminal_size import numpy as np import pytest from astropy import conf, 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.exceptions import AstropyDeprecationWarning 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]], 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", ] 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
", ] @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). """ self._setup(table_type) arr = np.arange(4000, dtype=np.float64).reshape(100, 40) with conf.set_temp("max_width", None), 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 ", " 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 == [ " 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", " 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", " 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 """ 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""" 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 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 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" # 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 }" # New-style that is almost old-style 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 " # New-style has precedence t["a"].format = "%4.2f {0:}" assert str(t["a"]) == " a \n-------\n%4.2f 1\n%4.2f 2" # Invalid format spec with pytest.raises(ValueError): t["a"].format = "fail" assert t["a"].format == "%4.2f {0:}" # format did not change def test_column_format_with_threshold(self, table_type): from astropy import conf with 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): from astropy import conf with 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 astropy-astropy-201cddb/astropy/table/tests/test_row.py000066400000000000000000000303411507226315300235520ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst import sys import numpy as np import pytest 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 @pytest.mark.usefixtures("table_types") class TestRow: def _setup(self, table_types): self._table_type = table_types.Table self._column_type = table_types.Column @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, 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[0] == 2 assert row[1] == 5 assert row.meta is table.meta assert row.colnames == table.colnames assert row.columns is table.columns with pytest.raises(IndexError): row[2] if sys.byteorder == "little": assert str(row.dtype) == "[('a', 'i8'), ('b', '>i8')]" 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 if table_types.Table is not MaskedTable: assert table["a"][1] == 10 def test_left_equal(self, table_types): """Compare a table row to the corresponding structured array 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, table_types): """Compare a table row to the corresponding structured array 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, table_types): """Test right equal""" 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) def test_convert_numpy_array(self, table_types): self._setup(table_types) d = self.t[1] np_data = np.array(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) 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")]) 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 from numpy.testing import assert_array_equal 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]) astropy-astropy-201cddb/astropy/table/tests/test_showtable.py000066400000000000000000000144001507226315300247310ustar00rootroot00000000000000import os import re import numpy as np import pytest from astropy.table.scripts import showtable 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): from astropy.units import UnitsWarning 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", ] astropy-astropy-201cddb/astropy/table/tests/test_subclass.py000066400000000000000000000045711507226315300245700ustar00rootroot00000000000000# 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"] astropy-astropy-201cddb/astropy/table/tests/test_table.py000066400000000000000000003571771507226315300240550ustar00rootroot00000000000000# 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 io import StringIO 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.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, TimeDelta from astropy.utils.compat import NUMPY_LT_1_25 from astropy.utils.compat.optional_deps import HAS_PANDAS 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 MIXIN_COLS, 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): t[1] = (20, 21, 22) with pytest.raises(ValueError): t[1] = 0 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_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 = 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"])) 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_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"] 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 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) @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, table_types): self._setup(table_types) t = table_types.Table([self.a]) with pytest.raises(ValueError): t.add_column( table_types.Column(name="b", data=[4, 5, 6, 7]) ) # data too long def test_too_short(self, table_types): self._setup(table_types) t = table_types.Table([self.a]) with pytest.raises(ValueError): t.add_column(table_types.Column(name="b", data=[4, 5])) # data too short @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, table_types): self._setup(table_types) t = table_types.Table() t.add_column(self.a, 1) def test_3(self, table_types): self._setup(table_types) t = table_types.Table() t.add_column(self.a, -1) def test_5(self, table_types): self._setup(table_types) t = table_types.Table() with pytest.raises(ValueError): t.index_column("b") 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.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.colnames == ["a", "c", "b"] def test_10(self, table_types): self._setup(table_types) t = table_types.Table() t.add_column(self.a) ia = t.index_column("a") t.add_column(self.b, ia + 1) t.add_column(self.c, ia) 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_types.Table([cols["a"], cols["b"], self.c]) # Construct with add_column 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 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 @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"] 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])) 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"] 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"] 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_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(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", "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_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] # 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 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_table_row(self, table_types): self._setup(table_types) t = self.t 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, 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, "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"])) def test_add_with_list(self, table_types): self._setup(table_types) t = self.t 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", "10"])) 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", ""])) 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}) 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, table_types): self._setup(table_types) t = self.t with pytest.raises(TypeError): t.add_row(1) def test_add_row_failures(self, table_types): self._setup(table_types) t = self.t 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["a"][2] == 10 @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,) 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) 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) @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])) 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 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_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_delitem_row_slice(self, table_types): self._setup(table_types) self.t.add_column(self.b) 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"] @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")) 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) 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. """ non_native_order = (">", "<")[sys.byteorder != "little"] filename = get_pkg_data_filename("data/tb.fits", "astropy.io.fits.tests") t = table_types.Table.read(filename) arr = t.as_array() for idx in range(len(arr.dtype)): assert arr.dtype[idx].byteorder != non_native_order 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 def test_convert_numpy_object_array(self, table_types): d = table_types.Table([[1, 2], [3, 4]], names=("a", "b")) # Single table np_d = np.array(d, dtype=object) assert isinstance(np_d, np.ndarray) assert np_d[()] is d 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) 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) if NUMPY_LT_1_25: with pytest.raises(ValueError, match="unable to compare column c"): # Type mismatch in column c causes FutureWarning t1.values_equal(2) with pytest.raises(ValueError, match="unable to compare column c"): t1.values_equal([1, 2]) else: 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.skipif(not HAS_PANDAS, reason="requires pandas") class TestPandas: def test_simple(self): 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"] = ["a", "b", "c"] d = t.to_pandas() for column in t.columns: if column == "u": assert np.all(t["u"] == np.array(["a", "b", "c"])) assert d[column].dtype == np.dtype("O") # upstream feature of pandas elif column == "s": assert np.all(t["s"] == np.array(["a", "b", "c"])) assert d[column].dtype == np.dtype("O") # upstream feature of pandas else: # We should be able to compare exact values here assert np.all(t[column] == d[column]) if t[column].dtype.isnative: assert d[column].dtype == t[column].dtype else: assert d[column].dtype == t[column].dtype.newbyteorder() # 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"]] t2 = table.Table.from_pandas(d) for column in t.columns: if column in ("u", "s"): assert np.all(t[column] == t2[column]) else: assert_allclose(t[column], t2[column]) if t[column].dtype.isnative: assert t[column].dtype == t2[column].dtype else: assert t[column].dtype.newbyteorder() == t2[column].dtype @pytest.mark.parametrize("unsigned", ["u", ""]) @pytest.mark.parametrize("bits", [8, 16, 32, 64]) def test_nullable_int(self, unsigned, bits): np_dtype = f"{unsigned}int{bits}" c = MaskedColumn([1, 2], mask=[False, True], dtype=np_dtype) t = Table([c]) df = t.to_pandas() pd_dtype = np_dtype.replace("i", "I").replace("u", "U") assert str(df["col0"].dtype) == pd_dtype t2 = Table.from_pandas(df) assert str(t2["col0"].dtype) == np_dtype assert np.all(t2["col0"].mask == [False, True]) assert np.all(t2["col0"] == c) def test_2d(self): t = table.Table() t["a"] = [1, 2, 3] t["b"] = np.ones((3, 2)) with pytest.raises( ValueError, match="Cannot convert a table with multidimensional columns" ): t.to_pandas() def test_mixin_pandas(self): 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 = t.to_pandas() t2 = table.Table.from_pandas(tp) 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 ) # For pandas, Time, TimeDelta are the mixins that round-trip the class assert isinstance(t2["time"], 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 isinstance(t2["dt"], TimeDelta) assert np.allclose(t2["dt"].value, [0, 2, 4, 6]) assert t2["dt"].format == "sec" @pytest.mark.parametrize("use_IndexedTable", [False, True]) def test_to_pandas_index(self, use_IndexedTable): """Test to_pandas() with different indexing options. This also tests the fix for #12014. The exception seen there is reproduced here without the fix. """ import pandas as pd class IndexedTable(table.QTable): """Always index the first column""" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.add_index(self.colnames[0]) row_index = pd.RangeIndex(0, 2, 1) tm_index = pd.DatetimeIndex( ["1998-01-01", "2002-01-01"], dtype="datetime64[ns]", name="tm", freq=None ) 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"]) tp = t.to_pandas() if not use_IndexedTable: assert np.all(tp.index == row_index) tp = t.to_pandas(index="tm") assert np.all(tp.index == tm_index) t.add_index("tm") tp = t.to_pandas() assert np.all(tp.index == tm_index) # Make sure writing to pandas didn't hack the original table assert t["tm"].info.indices tp = t.to_pandas(index=True) assert np.all(tp.index == tm_index) tp = t.to_pandas(index=False) assert np.all(tp.index == row_index) with pytest.raises(ValueError) as err: t.to_pandas(index="not a column") assert "index must be None, False" in str(err.value) def test_mixin_pandas_masked(self): 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 = t.to_pandas() assert np.all(tp["tm"].isnull() == [False, True, False]) assert np.all(tp["dt"].isnull() == [False, True, False]) t2 = table.Table.from_pandas(tp) assert np.all(t2["tm"].mask == tm.mask) assert np.ma.allclose(t2["tm"].jd, tm.jd, rtol=1e-14, atol=1e-14) assert np.all(t2["dt"].mask == dt.mask) assert np.ma.allclose(t2["dt"].jd, dt.jd, rtol=1e-14, atol=1e-14) def test_from_pandas_index(self): tm = Time([1998, 2002], format="jyear") x = [1, 2] t = table.Table([tm, x], names=["tm", "x"]) tp = t.to_pandas(index="tm") t2 = table.Table.from_pandas(tp) assert t2.colnames == ["x"] t2 = table.Table.from_pandas(tp, index=True) assert t2.colnames == ["tm", "x"] assert np.allclose(t2["tm"].jyear, tm.jyear) @pytest.mark.parametrize("use_nullable_int", [True, False]) def test_masking(self, use_nullable_int): 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"] = ["a", "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 d = t.to_pandas(use_nullable_int=use_nullable_int) else: from pandas.core.dtypes.cast import IntCastingNaNError with pytest.raises( IntCastingNaNError, match=r"Cannot convert non-finite values \(NA or inf\) to integer", ): d = t.to_pandas(use_nullable_int=use_nullable_int) return # Do not continue t2 = table.Table.from_pandas(d) for name, column in t.columns.items(): assert np.all(column.data == t2[name].data) if hasattr(t2[name], "mask"): assert np.all(column.mask == t2[name].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" # This warning pops up when use_nullable_int is False # for pandas 1.5.2. with np.errstate(invalid="ignore"): assert_array_equal(column.data, t2[name].data.astype(column.dtype)) else: if column.dtype.byteorder in ("=", "|"): assert column.dtype == t2[name].dtype else: assert column.dtype.newbyteorder() == t2[name].dtype def test_units(self): import pandas as pd import astropy.units as u df = pd.DataFrame({"x": [1, 2, 3], "t": [1.3, 1.2, 1.8]}) t = table.Table.from_pandas(df, 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): table.Table.from_pandas(df, units=[u.m, u.s]) # test warning is raised if additional columns in units dict with pytest.warns(UserWarning) as record: table.Table.from_pandas(df, units={"x": u.m, "t": u.s, "y": u.m}) assert len(record) == 1 assert "{'y'}" in record[0].message.args[0] def test_to_pandas_masked_int_data_with__index(self): data = {"data": [0, 1, 2], "index": [10, 11, 12]} t = table.Table(data=data, masked=True) t.add_index("index") t["data"].mask = [1, 1, 0] df = t.to_pandas() assert df["data"].iloc[-1] == 2 @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): 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. """ from inspect import currentframe, getframeinfo 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_fail(): dat = [[1.0, 2.0], ["aa", "bb"]] with pytest.raises( ValueError, match="sequence of unit values must match number of columns" ): Table(dat, units=[u.m]) with pytest.raises( ValueError, match="invalid column name c for setting unit attribute" ): Table(dat, units={"c": u.m}) 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 astropy-astropy-201cddb/astropy/tests/000077500000000000000000000000001507226315300202425ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/tests/__init__.py000066400000000000000000000003451507226315300223550ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst """ This package contains utilities to run the astropy test suite, tools for writing tests, and general tests that are not associated with a particular package. """ astropy-astropy-201cddb/astropy/tests/command.py000066400000000000000000000327141507226315300222410ustar00rootroot00000000000000"""Implements the wrapper for the Astropy test runner. This is for backward-compatibility for other downstream packages and can be removed once astropy-helpers has reached end-of-life. """ import os import shutil import stat import subprocess import sys import tempfile from contextlib import contextmanager from setuptools import Command from astropy.logger import log from astropy.utils.decorators import deprecated @contextmanager def _suppress_stdout(): """ A context manager to temporarily disable stdout. Used later when installing a temporary copy of astropy to avoid a very verbose output. """ with open(os.devnull, "w") as devnull: old_stdout = sys.stdout sys.stdout = devnull try: yield finally: sys.stdout = old_stdout @deprecated("6.0") class FixRemoteDataOption(type): """ This metaclass is used to catch cases where the user is running the tests with --remote-data. We've now changed the --remote-data option so that it takes arguments, but we still want --remote-data to work as before and to enable all remote tests. With this metaclass, we can modify sys.argv before setuptools try to parse the command-line options. """ def __init__(cls, name, bases, dct): try: idx = sys.argv.index("--remote-data") except ValueError: pass else: sys.argv[idx] = "--remote-data=any" try: idx = sys.argv.index("-R") except ValueError: pass else: sys.argv[idx] = "-R=any" super().__init__(name, bases, dct) @deprecated("6.0") class AstropyTest(Command, metaclass=FixRemoteDataOption): description = "Run the tests for this package" user_options = [ ( "package=", "P", "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.", ), ( "test-path=", "t", "Specify a test location by path. If a relative path to a .py file, " 'it is relative to the built package, so e.g., a leading "astropy/" ' "is necessary. If a relative path to a .rst file, it is relative to " "the directory *below* the --docs-path directory, so a leading " '"docs/" is usually necessary. May also be an absolute path.', ), ("verbose-results", "V", "Turn on verbose output from pytest."), ("plugins=", "p", "Plugins to enable when running pytest."), ("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. Should be " "one of none/astropy/any (defaults to none).", ), ("pdb", "d", "Start the interactive Python debugger on errors."), ("coverage", "c", "Create a coverage report. Requires the coverage package."), ( "parallel=", "j", "Run the tests in parallel on the specified number of " 'CPUs. If "auto", all the cores on the machine will be ' "used. Requires the pytest-xdist plugin.", ), ( "docs-path=", None, "The path to the documentation .rst files. If not provided, and " 'the current directory contains a directory called "docs", that ' "will be used.", ), ("skip-docs", None, "Don't test the documentation .rst files."), ( "repeat=", None, "How many times to repeat each test (can be used to check for " "sporadic failures).", ), ( "temp-root=", None, "The root directory in which to create the temporary testing files. " "If unspecified the system default is used (e.g. /tmp) as explained " "in the documentation for tempfile.mkstemp.", ), ( "verbose-install", None, "Turn on terminal output from the installation of astropy in a " "temporary folder.", ), ("readonly", None, "Make the temporary installation being tested read-only."), ] package_name = "" 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 = "none" self.pdb = False self.coverage = False self.parallel = 0 self.docs_path = None self.skip_docs = False self.repeat = None self.temp_root = None self.verbose_install = False self.readonly = False def finalize_options(self): # Normally we would validate the options here, but that's handled in # run_tests pass def generate_testing_command(self): """ Build a Python script to run the tests. """ cmd_pre = "" # Commands to run before the test function cmd_post = "" # Commands to run after the test function if self.coverage: pre, post = self._generate_coverage_commands() cmd_pre += pre cmd_post += post set_flag = "import builtins; builtins._ASTROPY_TEST_ = True" cmd = ( # see _build_temp_install below "{cmd_pre}{0}; import {1.package_name}, sys; result = (" "{1.package_name}.test(" "package={1.package!r}, " "test_path={1.test_path!r}, " "args={1.args!r}, " "plugins={1.plugins!r}, " "verbose={1.verbose_results!r}, " "pastebin={1.pastebin!r}, " "remote_data={1.remote_data!r}, " "pdb={1.pdb!r}, " "parallel={1.parallel!r}, " "docs_path={1.docs_path!r}, " "skip_docs={1.skip_docs!r}, " "add_local_eggs_to_path=True, " "repeat={1.repeat!r})); " "{cmd_post}" "sys.exit(result)" ) return cmd.format(set_flag, self, cmd_pre=cmd_pre, cmd_post=cmd_post) def run(self): """Run the tests!""" # Install the runtime dependencies. if self.distribution.install_requires: self.distribution.fetch_build_eggs(self.distribution.install_requires) # Ensure there is a doc path if self.docs_path is None: cfg_docs_dir = self.distribution.get_option_dict("build_docs").get( "source_dir", None ) # Some affiliated packages use this. # See astropy/package-template#157 if cfg_docs_dir is not None and os.path.exists(cfg_docs_dir[1]): self.docs_path = os.path.abspath(cfg_docs_dir[1]) # fall back on a default path of "docs" elif os.path.exists("docs"): # pragma: no cover self.docs_path = os.path.abspath("docs") # Build a testing install of the package self._build_temp_install() # Install the test dependencies # NOTE: we do this here after _build_temp_install because there is # a weird but which occurs if psutil is installed in this way before # astropy is built, Cython can have segmentation fault. Strange, eh? if self.distribution.tests_require: self.distribution.fetch_build_eggs(self.distribution.tests_require) # Copy any additional dependencies that may have been installed via # tests_requires or install_requires. We then pass the # add_local_eggs_to_path=True option to package.test() to make sure the # eggs get included in the path. if os.path.exists(".eggs"): shutil.copytree(".eggs", os.path.join(self.testing_path, ".eggs")) # This option exists so that we can make sure that the tests don't # write to an installed location. if self.readonly: log.info("changing permissions of temporary installation to read-only") self._change_permissions_testing_path(writable=False) # Run everything in a try: finally: so that the tmp dir gets deleted. try: # Construct this modules testing command cmd = self.generate_testing_command() # 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 testproc = subprocess.Popen( [sys.executable, "-c", cmd], cwd=self.testing_path, close_fds=False ) retcode = testproc.wait() except KeyboardInterrupt: import signal # If a keyboard interrupt is handled, pass it to the test # subprocess to prompt pytest to initiate its teardown testproc.send_signal(signal.SIGINT) retcode = testproc.wait() finally: # Remove temporary directory if self.readonly: self._change_permissions_testing_path(writable=True) shutil.rmtree(self.tmp_dir) raise SystemExit(retcode) def _build_temp_install(self): """ Install the package and to a temporary directory for the purposes of testing. This allows us to test the install command, include the entry points, and also avoids creating pyc and __pycache__ directories inside the build directory. """ # On OSX the default path for temp files is under /var, but in most # cases on OSX /var is actually a symlink to /private/var; ensure we # dereference that link, because pytest is very sensitive to relative # paths... tmp_dir = tempfile.mkdtemp( prefix=self.package_name + "-test-", dir=self.temp_root ) self.tmp_dir = os.path.realpath(tmp_dir) log.info(f"installing to temporary directory: {self.tmp_dir}") # We now install the package to the temporary directory. We do this # rather than build and copy because this will ensure that e.g. entry # points work. self.reinitialize_command("install") install_cmd = self.distribution.get_command_obj("install") install_cmd.prefix = self.tmp_dir if self.verbose_install: self.run_command("install") else: with _suppress_stdout(): self.run_command("install") # We now get the path to the site-packages directory that was created # inside self.tmp_dir install_cmd = self.get_finalized_command("install") self.testing_path = install_cmd.install_lib # Ideally, docs_path is set properly in run(), but if it is still # not set here, do not pretend it is, otherwise bad things happen. # See astropy/package-template#157 if self.docs_path is not None: new_docs_path = os.path.join( self.testing_path, os.path.basename(self.docs_path) ) shutil.copytree(self.docs_path, new_docs_path) self.docs_path = new_docs_path shutil.copy("pyproject.toml", self.testing_path) def _change_permissions_testing_path(self, writable=False): if writable: basic_flags = stat.S_IRUSR | stat.S_IWUSR else: basic_flags = stat.S_IRUSR for root, dirs, files in os.walk(self.testing_path): for dirname in dirs: os.chmod(os.path.join(root, dirname), basic_flags | stat.S_IXUSR) for filename in files: os.chmod(os.path.join(root, filename), basic_flags) def _generate_coverage_commands(self): """ This method creates the post and pre commands if coverage is to be generated. """ if self.parallel != 0: raise ValueError("--coverage can not be used with --parallel") try: import coverage # noqa: F401 except ImportError: raise ImportError( "--coverage requires that the coverage package is installed." ) # Don't use get_pkg_data_filename here, because it # requires importing astropy.config and thus screwing # up coverage results for those packages. coveragerc = os.path.join( self.testing_path, self.package_name.replace(".", "/"), "tests", "coveragerc", ) with open(coveragerc) as fd: coveragerc_content = fd.read() coveragerc_content = coveragerc_content.replace( "{packagename}", self.package_name.replace(".", "/") ) tmp_coveragerc = os.path.join(self.tmp_dir, "coveragerc") with open(tmp_coveragerc, "wb") as tmp: tmp.write(coveragerc_content.encode("utf-8")) cmd_pre = ( "import coverage; cov =" f' coverage.coverage(data_file=r"{os.path.abspath(".coverage")}",' f' config_file=r"{os.path.abspath(tmp_coveragerc)}"); cov.start();' ) cmd_post = ( "cov.stop(); from astropy.tests.helper import _save_coverage;" f' _save_coverage(cov, result, r"{os.path.abspath(".")}",' f' r"{os.path.abspath(self.testing_path)}");' ) return cmd_pre, cmd_post astropy-astropy-201cddb/astropy/tests/figures/000077500000000000000000000000001507226315300217065ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/tests/figures/__init__.py000066400000000000000000000001421507226315300240140ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst from .helpers import figure_test astropy-astropy-201cddb/astropy/tests/figures/helpers.py000066400000000000000000000027541507226315300237320ustar00rootroot00000000000000# 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 astropy-astropy-201cddb/astropy/tests/figures/py311-test-image-mpl360-cov.json000066400000000000000000000217271507226315300273300ustar00rootroot00000000000000{ "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": "44fd1a202f8baa894a289bac150b3b0eda9b069a06fc8f49ce14d77e743481e0", "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_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" } astropy-astropy-201cddb/astropy/tests/figures/py311-test-image-mpldev-cov.json000066400000000000000000000217271507226315300275760ustar00rootroot00000000000000{ "astropy.visualization.wcsaxes.tests.test_frame.TestFrame.test_custom_frame": "d79ed179997672de0a929aedee3c2ac590e04b544558808ac9bace8937a60275", "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_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" } astropy-astropy-201cddb/astropy/tests/helper.py000066400000000000000000000160201507226315300220720ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst """ 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 os import pickle import sys import pytest from astropy.units import allclose as quantity_allclose # noqa: F401 # For backward-compatibility with affiliated packages from .runner import TestRunner # noqa: F401 __all__ = [ "assert_follows_unicode_guidelines", "assert_quantity_allclose", "check_pickling_recovery", "generic_recursive_equality_test", "pickle_protocol", ] # 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" def _save_coverage(cov, result, rootdir, testing_path): """ This method is called after the tests have been run in coverage mode to cleanup and then save the coverage data and report. """ from astropy.utils.console import color_print if result != 0: return # The coverage report includes the full path to the temporary # directory, so we replace all the paths with the true source # path. Note that this will not work properly for packages that still # rely on 2to3. try: # Coverage 4.0: _harvest_data has been renamed to get_data, the # lines dict is private cov.get_data() except AttributeError: # Coverage < 4.0 cov._harvest_data() lines = cov.data.lines else: lines = cov.data._lines for key in list(lines.keys()): new_path = os.path.relpath( os.path.realpath(key), os.path.realpath(testing_path) ) new_path = os.path.abspath(os.path.join(rootdir, new_path)) lines[new_path] = lines.pop(key) color_print("Saving coverage data in .coverage...", "green") cov.save() color_print("Saving HTML coverage report in htmlcov...", "green") cov.html_report(directory=os.path.join(rootdir, "htmlcov")) 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): bytes_x = bytes(x) unicode_x = str(x) repr_x = repr(x) assert isinstance(bytes_x, bytes) bytes_x.decode("ascii") assert isinstance(unicode_x, str) unicode_x.encode("ascii") assert isinstance(repr_x, str) if isinstance(repr_x, bytes): repr_x.decode("ascii") else: repr_x.encode("ascii") if roundtrip is not None: assert x.__class__(bytes_x) == x assert x.__class__(unicode_x) == x assert eval(repr_x, roundtrip) == x with conf.set_temp("unicode_output", True): bytes_x = bytes(x) unicode_x = str(x) repr_x = repr(x) assert isinstance(bytes_x, bytes) bytes_x.decode("ascii") assert isinstance(unicode_x, str) assert isinstance(repr_x, str) if isinstance(repr_x, bytes): repr_x.decode("ascii") else: repr_x.encode("ascii") if roundtrip is not None: assert x.__class__(bytes_x) == x assert x.__class__(unicode_x) == x assert eval(repr_x, roundtrip) == 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). * 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. (Originally from astropy.table.tests.test_pickle). """ return request.param 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: 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 ) 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) 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. 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 __tracebackhide__ = True np.testing.assert_allclose( *_unquantify_allclose_arguments(actual, desired, rtol, atol), **kwargs ) _skip_docstring_tests_with_optimized_python = pytest.mark.skipif( sys.flags.optimize >= 2, reason="docstrings are not testable in optimized mode" ) astropy-astropy-201cddb/astropy/tests/runner.py000066400000000000000000000516251507226315300221360ustar00rootroot00000000000000"""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 pathlib import Path from astropy.utils import find_current_module from astropy.utils.exceptions import AstropyDeprecationWarning, AstropyWarning __all__ = ["TestRunner", "TestRunnerBase", "keyword"] 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 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 :class:`~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``. 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): # 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_xdg_config = os.environ.get("XDG_CONFIG_HOME") with tempfile.TemporaryDirectory("astropy_config") as astropy_config: Path(astropy_config, "astropy").mkdir() os.environ["XDG_CONFIG_HOME"] = astropy_config try: return pytest.main(args=args, plugins=plugins) finally: if orig_xdg_config is None: os.environ.pop("XDG_CONFIG_HOME", None) else: os.environ["XDG_CONFIG_HOME"] = orig_xdg_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 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 len(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) astropy-astropy-201cddb/astropy/tests/test_logger.py000066400000000000000000000372341507226315300231430ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst import importlib import locale import logging import sys import warnings import pytest 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 # this file gets executed. _excepthook = sys.__excepthook__ _showwarning = warnings.showwarning def setup_function(function): # Reset modules to default importlib.reload(warnings) importlib.reload(sys) # Reset internal original hooks log._showwarning_orig = None log._excepthook_orig = None # Set up the logger log._set_defaults() # Reset hooks if log.warnings_logging_enabled(): log.disable_warnings_logging() if log.exception_logging_enabled(): log.disable_exception_logging() teardown_module = setup_function def test_warnings_logging_disable_no_enable(): with pytest.raises(LoggingError, match=r"Warnings logging has not been enabled"): log.disable_warnings_logging() def test_warnings_logging_enable_twice(): log.enable_warnings_logging() with pytest.raises( LoggingError, match=r"Warnings logging has already been enabled" ): log.enable_warnings_logging() def test_warnings_logging_overridden(): log.enable_warnings_logging() warnings.showwarning = lambda: None 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() def test_warnings_logging(): # Without warnings logging 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", AstropyUserWarning) assert len(log_list) == 0 assert len(warn_list) == 1 # 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", 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.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 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", AstropyUserWarning) assert len(log_list) == 0 assert len(warn_list) == 1 def test_warnings_logging_with_custom_class(): 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", 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.startswith( "CustomAstropyWarningClass: This is a warning" ) assert log_list[0].origin == "astropy.tests.test_logger" 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")) log.disable_warnings_logging() assert len(log_list) == 1 assert len(warn_list) == 0 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, match=r"Exception logging has not been enabled"): log.disable_exception_logging() def test_exception_logging_enable_twice(): log.enable_exception_logging() with pytest.raises( LoggingError, match=r"Exception logging has already been enabled" ): log.enable_exception_logging() @pytest.mark.skipif( _WITHIN_IPYTHON, reason="Cannot override exception handler in IPython" ) def test_exception_logging_overridden(): log.enable_exception_logging() 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() @pytest.mark.xfail("_WITHIN_IPYTHON") def test_exception_logging(): # Without exception logging with pytest.raises(Exception, match="This is an Exception"): with log.log_to_list() as log_list: raise Exception("This is an Exception") assert len(log_list) == 0 # 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: raise Exception("This is an Exception") except Exception as exc: sys.excepthook(*sys.exc_info()) assert exc.args[0] == "This is an Exception" else: raise AssertionError() # exception should have been raised assert len(log_list) == 1 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. 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: raise Exception("This is an Exception") except Exception as exc: sys.excepthook(*sys.exc_info()) assert exc.args[0] == "This is an Exception" else: 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 astropy.utils.collections import HomogeneousList lst = HomogeneousList(int) try: log.enable_exception_logging() with log.log_to_list() as log_list: 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 " ) else: raise AssertionError() assert len(log_list) == 1 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" @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 """ 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 try: if level is not None: log.setLevel(level) 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": assert len(log_list) == 4 elif level == "INFO": assert len(log_list) == 3 elif level == "WARN": assert len(log_list) == 2 elif level == "ERROR": assert len(log_list) == 1 # Check list content 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.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].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].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: log.error("Error message") log.warning("Warning message") 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: log.error("Error 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: log.error("Error message") log.warning("Warning message") assert len(log_list) == 0 @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 try: if level is not None: log.setLevel(level) 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() finally: log.setLevel(orig_level) 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": assert len(log_entries) == 4 elif level == "INFO": assert len(log_entries) == 3 elif level == "WARN": assert len(log_entries) == 2 elif level == "ERROR": assert len(log_entries) == 1 # Check list content assert eval(log_entries[0].strip())[-3:] == ( "astropy.tests.test_logger", "ERROR", "Error message", ) if len(log_entries) >= 2: assert eval(log_entries[1].strip())[-3:] == ( "astropy.tests.test_logger", "WARNING", "Warning message", ) if len(log_entries) >= 3: assert eval(log_entries[2].strip())[-3:] == ( "astropy.tests.test_logger", "INFO", "Information message", ) if len(log_entries) >= 4: assert eval(log_entries[3].strip())[-3:] == ( "astropy.tests.test_logger", "DEBUG", "Debug message", ) 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()) with log.log_to_file(log_path, filter_level="ERROR"): log.error("Error message") log.warning("Warning message") log_file.close() log_file = local_path.open("rb") log_entries = log_file.readlines() log_file.close() assert len(log_entries) == 1 assert eval(log_entries[0].strip())[-2:] == ("ERROR", "Error message") 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"): log.error("Error message") log.warning("Warning message") log_file.close() 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(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.wcs"): log.error("Error message") log.warning("Warning message") log_file.close() 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 astropy-astropy-201cddb/astropy/tests/tests/000077500000000000000000000000001507226315300214045ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/tests/tests/__init__.py000066400000000000000000000000001507226315300235030ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/tests/tests/test_imports.py000066400000000000000000000035201507226315300245120ustar00rootroot00000000000000# 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 astropy-astropy-201cddb/astropy/tests/tests/test_quantity_helpers.py000066400000000000000000000030021507226315300264100ustar00rootroot00000000000000import 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) astropy-astropy-201cddb/astropy/tests/tests/test_run_tests.py000066400000000000000000000011371507226315300250450ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst import pytest # test helper.run_tests function from astropy import test as run_tests # run_tests should raise ValueError when asked to run on a module it can't find def test_module_not_found(): with pytest.raises(ValueError): run_tests(package="fake.module") # run_tests should raise ValueError when passed an invalid pastebin= option def test_pastebin_keyword(): with pytest.raises(ValueError): run_tests(pastebin="not_an_option") def test_unicode_literal_conversion(): assert isinstance("ÃĨngstrÃļm", str) astropy-astropy-201cddb/astropy/tests/tests/test_runner.py000066400000000000000000000041451507226315300243320ustar00rootroot00000000000000import 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 def test_disable_kwarg(): class no_remote_data(_TestRunner): @keyword() def remote_data(self, remote_data, kwargs): return NotImplemented r = no_remote_data(".") with pytest.raises(TypeError): r.run_tests(remote_data="bob") def test_wrong_kwarg(): r = _TestRunner(".") with pytest.raises(TypeError): r.run_tests(spam="eggs") def test_invalid_kwarg(): class bad_return(_TestRunnerBase): @keyword() def remote_data(self, remote_data, kwargs): return "bob" r = bad_return(".") with pytest.raises(TypeError): r.run_tests(remote_data="bob") def test_new_kwarg(): class Spam(_TestRunnerBase): @keyword() def spam(self, spam, kwargs): return [spam] 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] 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] r = Spam(".") assert "eggs" in r.run_tests.__doc__ assert "Spam Spam Spam" in r.run_tests.__doc__ astropy-astropy-201cddb/astropy/time/000077500000000000000000000000001507226315300200365ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/time/__init__.py000066400000000000000000000032561507226315300221550ustar00rootroot00000000000000# 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 astropy-astropy-201cddb/astropy/time/core.py000066400000000000000000003746661507226315300213660ustar00rootroot00000000000000# 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 COPY_IF_NEEDED 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', '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 not None and val.size > 1: try: val.shape = shape except Exception: for val2 in reshaped: val2.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: 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}" ) 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=COPY_IF_NEEDED, ) 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: 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." ) 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', '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=COPY_IF_NEEDED, ): 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}" ) 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 (unicodedata.normalize("NFKC", name) for name in self._names): if name in namespace and self != namespace[name]: raise ValueError( f"Object with NFKC normalized name {name!r} already exists in " f"given namespace ({namespace[name]!r})." ) 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 __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, detailed_exception=False) # Try a shortcut except (AttributeError, ValueError): # No `f._validate_unit()` (AttributeError) # or `s` was a composite unit (ValueError). 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 astropy-astropy-201cddb/astropy/units/decorators.py000066400000000000000000000303451507226315300227660ustar00rootroot00000000000000# 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 astropy-astropy-201cddb/astropy/units/deprecated.py000066400000000000000000000044121507226315300227150ustar00rootroot00000000000000# 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 .utils 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) astropy-astropy-201cddb/astropy/units/equivalencies.py000066400000000000000000000731711507226315300234620ustar00rootroot00000000000000# 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 __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", ] 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", ) astropy-astropy-201cddb/astropy/units/errors.py000066400000000000000000000022571507226315300221360ustar00rootroot00000000000000# 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""" astropy-astropy-201cddb/astropy/units/format/000077500000000000000000000000001507226315300215325ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/units/format/__init__.py000066400000000000000000000044161507226315300236500ustar00rootroot00000000000000# 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}.") astropy-astropy-201cddb/astropy/units/format/base.py000066400000000000000000000236201507226315300230210ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst import warnings from collections.abc import Iterable from typing import ClassVar, Literal 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.errors import 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, *, 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. 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"Can not 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 ValueError as e: registry = get_current_unit_registry() if t.value in registry.aliases: return registry.aliases[t.value] raise ValueError(f"At col {t.lexpos}, {str(e)}") @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, unit: str, detailed_exception: bool = True) -> UnitBase: try: return cls._units[unit] except KeyError: if detailed_exception: raise ValueError(cls._invalid_unit_error_message(unit)) from None raise ValueError() from None @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) -> 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) for base in unit.bases], unit.powers, _error_check=False, ) if isinstance(unit, NamedUnit): try: return cls._validate_unit(unit._get_format_name(cls.name)) except ValueError: if isinstance(unit, Unit): return cls._decompose_to_known_units(unit._represents) raise raise TypeError( f"unit argument must be a 'NamedUnit' or 'CompositeUnit', not {type(unit)}" ) astropy-astropy-201cddb/astropy/units/format/cds.py000066400000000000000000000203541507226315300226610ustar00rootroot00000000000000# 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.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", "SIGN", "UINT", "UFLOAT", "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_UFLOAT(t): r"((\d+\.?\d+)|(\.\d+))([eE][+-]?\d+)?" if not re.search(r"[eE\.]", t.value): t.type = "UINT" t.value = int(t.value) 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 = float(t.value + "1") 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 : signed_float X UINT signed_int | UINT X UINT signed_int | UINT signed_int | UINT | signed_float """ if len(p) == 5: if p[3] != 10: raise ValueError("Only base ten exponents are allowed in CDS") p[0] = p[1] * 10.0 ** p[4] elif len(p) == 3: if p[1] != 10: raise ValueError("Only base ten exponents are allowed in CDS") p[0] = 10.0 ** p[2] elif len(p) == 2: p[0] = p[1] def p_unit_with_power(p): """ unit_with_power : UNIT numeric_power | UNIT """ if len(p) == 2: p[0] = p[1] else: p[0] = p[1] ** p[2] def p_numeric_power(p): """ numeric_power : sign UINT """ p[0] = p[1] * p[2] def p_sign(p): """ sign : SIGN | """ if len(p) == 2: p[0] = p[1] else: p[0] = 1.0 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_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 ) -> 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) astropy-astropy-201cddb/astropy/units/format/cds_lextab.py000066400000000000000000000026471507226315300242250ustar00rootroot00000000000000# 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', 'OPEN_BRACKET', 'OPEN_PAREN', 'PRODUCT', 'SIGN', 'UFLOAT', 'UINT', 'UNIT', 'X')) _lexreflags = 32 _lexliterals = '' _lexstateinfo = {'INITIAL': 'inclusive'} _lexstatere = {'INITIAL': [('(?P((\\d+\\.?\\d+)|(\\.\\d+))([eE][+-]?\\d+)?)|(?P\\d+)|(?P[+-](?=\\d))|(?P[x×])|(?P%|°|\\\\h|(a|eps|mu)0|((?!\\d)\\w)+)|(?P---|-)|(?P\\.)|(?P\\()|(?P\\))|(?P\\[)|(?P\\])|(?P/)', [None, ('t_UFLOAT', 'UFLOAT'), None, None, None, None, ('t_UINT', 'UINT'), ('t_SIGN', 'SIGN'), ('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 = {} astropy-astropy-201cddb/astropy/units/format/cds_parsetab.py000066400000000000000000000141021507226315300245340ustar00rootroot00000000000000# 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 OPEN_BRACKET OPEN_PAREN PRODUCT SIGN UFLOAT UINT 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 : signed_float X UINT signed_int\n | UINT X UINT signed_int\n | UINT signed_int\n | UINT\n | signed_float\n \n unit_with_power : UNIT numeric_power\n | UNIT\n \n numeric_power : sign UINT\n \n sign : SIGN\n |\n \n signed_int : SIGN UINT\n \n signed_float : sign UINT\n | sign UFLOAT\n ' _lr_action_items = {'DIMENSIONLESS':([0,5,],[4,20,]),'OPEN_BRACKET':([0,],[5,]),'UINT':([0,10,13,16,21,22,24,31,],[7,25,-23,-24,35,36,37,40,]),'DIVISION':([0,2,3,5,6,7,8,9,11,14,15,16,17,19,23,25,26,27,28,29,30,32,37,38,39,40,41,42,],[12,12,18,12,-19,-18,-7,-8,-10,-13,12,-21,18,18,-17,-26,-27,12,-11,18,-20,-12,-25,18,-14,-22,-15,-16,]),'SIGN':([0,7,16,35,36,],[13,24,13,24,24,]),'UFLOAT':([0,10,13,],[-24,26,-23,]),'OPEN_PAREN':([0,2,5,6,7,12,15,18,23,25,26,27,37,41,42,],[15,15,15,-19,-18,15,15,15,-17,-26,-27,15,-25,-15,-16,]),'UNIT':([0,2,5,6,7,12,15,18,23,25,26,27,37,41,42,],[16,16,16,-19,-18,16,16,16,-17,-26,-27,16,-25,-15,-16,]),'$end':([1,2,3,4,6,7,8,9,11,14,16,17,23,25,26,28,30,32,33,34,37,38,39,40,41,42,],[0,-6,-2,-3,-19,-18,-7,-8,-10,-13,-21,-1,-17,-26,-27,-11,-20,-12,-4,-5,-25,-9,-14,-22,-15,-16,]),'X':([6,7,25,26,],[21,22,-26,-27,]),'CLOSE_BRACKET':([8,9,11,14,16,19,20,28,30,32,38,39,40,],[-7,-8,-10,-13,-21,33,34,-11,-20,-12,-9,-14,-22,]),'CLOSE_PAREN':([8,9,11,14,16,28,29,30,32,38,39,40,],[-7,-8,-10,-13,-21,-11,39,-20,-12,-9,-14,-22,]),'PRODUCT':([11,14,16,30,39,40,],[27,-13,-21,-20,-14,-22,]),} _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,15,27,],[3,17,19,29,38,]),'signed_float':([0,],[6,]),'product_of_units':([0,2,5,15,27,],[8,8,8,8,8,]),'division_of_units':([0,2,5,15,27,],[9,9,9,9,9,]),'sign':([0,16,],[10,31,]),'unit_expression':([0,2,5,12,15,18,27,],[11,11,11,28,11,32,11,]),'unit_with_power':([0,2,5,12,15,18,27,],[14,14,14,14,14,14,14,]),'signed_int':([7,35,36,],[23,41,42,]),'numeric_power':([16,],[30,]),} _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',143), ('main -> combined_units','main',1,'p_main','cds.py',144), ('main -> DIMENSIONLESS','main',1,'p_main','cds.py',145), ('main -> OPEN_BRACKET combined_units CLOSE_BRACKET','main',3,'p_main','cds.py',146), ('main -> OPEN_BRACKET DIMENSIONLESS CLOSE_BRACKET','main',3,'p_main','cds.py',147), ('main -> factor','main',1,'p_main','cds.py',148), ('combined_units -> product_of_units','combined_units',1,'p_combined_units','cds.py',162), ('combined_units -> division_of_units','combined_units',1,'p_combined_units','cds.py',163), ('product_of_units -> unit_expression PRODUCT combined_units','product_of_units',3,'p_product_of_units','cds.py',169), ('product_of_units -> unit_expression','product_of_units',1,'p_product_of_units','cds.py',170), ('division_of_units -> DIVISION unit_expression','division_of_units',2,'p_division_of_units','cds.py',179), ('division_of_units -> combined_units DIVISION unit_expression','division_of_units',3,'p_division_of_units','cds.py',180), ('unit_expression -> unit_with_power','unit_expression',1,'p_unit_expression','cds.py',189), ('unit_expression -> OPEN_PAREN combined_units CLOSE_PAREN','unit_expression',3,'p_unit_expression','cds.py',190), ('factor -> signed_float X UINT signed_int','factor',4,'p_factor','cds.py',199), ('factor -> UINT X UINT signed_int','factor',4,'p_factor','cds.py',200), ('factor -> UINT signed_int','factor',2,'p_factor','cds.py',201), ('factor -> UINT','factor',1,'p_factor','cds.py',202), ('factor -> signed_float','factor',1,'p_factor','cds.py',203), ('unit_with_power -> UNIT numeric_power','unit_with_power',2,'p_unit_with_power','cds.py',218), ('unit_with_power -> UNIT','unit_with_power',1,'p_unit_with_power','cds.py',219), ('numeric_power -> sign UINT','numeric_power',2,'p_numeric_power','cds.py',228), ('sign -> SIGN','sign',1,'p_sign','cds.py',234), ('sign -> ','sign',0,'p_sign','cds.py',235), ('signed_int -> SIGN UINT','signed_int',2,'p_signed_int','cds.py',244), ('signed_float -> sign UINT','signed_float',2,'p_signed_float','cds.py',250), ('signed_float -> sign UFLOAT','signed_float',2,'p_signed_float','cds.py',251), ] astropy-astropy-201cddb/astropy/units/format/console.py000066400000000000000000000032771507226315300235570ustar00rootroot00000000000000# 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 . 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 ) -> str: # Change default of fraction to False, i.e., we typeset # without a fraction by default. return super().to_string(unit, fraction=fraction) astropy-astropy-201cddb/astropy/units/format/fits.py000066400000000000000000000065121507226315300230550ustar00rootroot00000000000000# 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.errors import UnitScaleError from astropy.utils import classproperty from . import Base, utils 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 # add some units up-front for which we don't want to use prefixes # and that have different names from the astropy default. names = {"Celsius": u.deg_C, "deg C": u.deg_C} 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 special_cases = {"dbyte": u.Unit("dbyte", 0.1 * u.byte)} for key, _ in utils.get_non_keyword_units(bases, prefixes): names[key] = special_cases[key] if key in special_cases else 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 ) -> 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 astropy-astropy-201cddb/astropy/units/format/generic.py000066400000000000000000000405331507226315300235250ustar00rootroot00000000000000# 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.errors import UnitsWarning from astropy.units.typing import UnitScale from astropy.utils import classproperty, parsing from astropy.utils.misc import did_you_mean 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"): function_unit = cls._validate_unit(p[1]) # 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. """ @classmethod def _validate_unit(cls, s: str, detailed_exception: bool = True) -> UnitBase: registry = get_current_unit_registry().registry 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" if s in registry: return registry[s] if detailed_exception: raise ValueError(f"{s} is not a valid unit. {did_you_mean(s, registry)}") else: raise ValueError() _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) astropy-astropy-201cddb/astropy/units/format/generic_lextab.py000066400000000000000000000026521507226315300250640ustar00rootroot00000000000000# 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 = {} astropy-astropy-201cddb/astropy/units/format/generic_parsetab.py000066400000000000000000000330671507226315300254120ustar00rootroot00000000000000# 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), ] astropy-astropy-201cddb/astropy/units/format/latex.py000066400000000000000000000053321507226315300232240ustar00rootroot00000000000000# 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.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", ) -> 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 ) -> str: return super().to_string(unit, fraction=fraction) astropy-astropy-201cddb/astropy/units/format/ogip.py000066400000000000000000000301751507226315300230500ustar00rootroot00000000000000# 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.errors import UnitParserWarning, UnitsWarning from astropy.units.typing import UnitScale from astropy.utils import classproperty, parsing from astropy.utils.parsing import ThreadSafeParser from . import utils 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", "SIGN", "UFLOAT", "LIT10", "UINT", "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 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 names = { unit: getattr(u, unit) for unit, _ in utils.get_non_keyword_units(bases, prefixes) } simple_units = [ "angstrom", "arcmin", "arcsec", "AU", "barn", "bin", "byte", "chan", "count", "d", "deg", "erg", "G", "h", "lyr", "mag", "min", "photon", "pixel", "voxel", "yr", ] # fmt: skip names.update((unit, getattr(u, unit)) for unit in simple_units) # 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_UFLOAT(t): r"(((\d+\.?\d*)|(\.\d+))([eE][+-]?\d+))|(((\d+\.\d*)|(\.\d+))([eE][+-]?\d+)?)" 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 = 1 if t.value == "+" else -1 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 | signed_float | signed_float POWER numeric_power | signed_int 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 : UINT | signed_float | OPEN_PAREN signed_int CLOSE_PAREN | OPEN_PAREN signed_float CLOSE_PAREN | OPEN_PAREN signed_float DIVISION UINT 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_sign(p): """ sign : SIGN | """ if len(p) == 2: p[0] = p[1] else: p[0] = 1.0 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_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" ) -> str: # Remove units that aren't known to the format unit = cls._decompose_to_known_units(unit) 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) @classmethod def _validate_unit(cls, unit: str, detailed_exception: bool = True) -> UnitBase: if unit in cls._deprecated_units: warnings.warn( f"The unit '{unit}' has been deprecated in the OGIP standard.", UnitsWarning, ) return super()._validate_unit(unit, detailed_exception) astropy-astropy-201cddb/astropy/units/format/ogip_lextab.py000066400000000000000000000032771507226315300244120ustar00rootroot00000000000000# 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', 'FUNCNAME', 'LIT10', 'OPEN_PAREN', 'POWER', 'SIGN', 'STAR', 'UFLOAT', 'UINT', 'UNIT', 'UNKNOWN', 'WHITESPACE')) _lexreflags = 64 _lexliterals = '' _lexstateinfo = {'INITIAL': 'inclusive'} _lexstatere = {'INITIAL': [('(?P(((\\d+\\.?\\d*)|(\\.\\d+))([eE][+-]?\\d+))|(((\\d+\\.\\d*)|(\\.\\d+))([eE][+-]?\\d+)?))|(?P\\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_UFLOAT', 'UFLOAT'), None, None, None, None, None, None, None, None, None, None, ('t_UINT', 'UINT'), ('t_SIGN', 'SIGN'), ('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 = {} astropy-astropy-201cddb/astropy/units/format/ogip_parsetab.py000066400000000000000000000231561507226315300247320ustar00rootroot00000000000000# 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 FUNCNAME LIT10 OPEN_PAREN POWER SIGN STAR UFLOAT UINT 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 | signed_float\n | signed_float POWER numeric_power\n | signed_int POWER numeric_power\n \n product : WHITESPACE\n | STAR\n | WHITESPACE STAR\n | WHITESPACE STAR WHITESPACE\n | STAR WHITESPACE\n \n numeric_power : UINT\n | signed_float\n | OPEN_PAREN signed_int CLOSE_PAREN\n | OPEN_PAREN signed_float CLOSE_PAREN\n | OPEN_PAREN signed_float DIVISION UINT CLOSE_PAREN\n \n sign : SIGN\n |\n \n signed_int : SIGN UINT\n \n signed_float : sign UINT\n | sign UFLOAT\n ' _lr_action_items = {'UNKNOWN':([0,],[2,]),'LIT10':([0,],[8,]),'UNIT':([0,4,8,9,13,14,18,19,20,21,23,28,31,32,34,37,38,40,41,42,45,46,51,57,58,66,],[11,11,-21,-22,11,11,11,11,-25,-26,11,11,-38,-39,11,-27,-29,-20,-30,-31,-23,-24,-28,-32,-33,-34,]),'OPEN_PAREN':([0,4,8,9,11,13,14,17,18,19,20,21,23,24,25,26,27,28,31,32,34,37,38,40,41,42,45,46,51,55,57,58,60,62,66,],[13,13,-21,-22,28,13,13,34,13,13,-25,-26,13,43,43,43,43,13,-38,-39,13,-27,-29,-20,-30,-31,-23,-24,-28,43,-32,-33,43,43,-34,]),'DIVISION':([0,3,4,5,6,7,8,9,11,12,13,22,23,28,29,30,31,32,34,35,36,39,40,41,42,45,46,47,48,49,50,53,54,56,57,58,61,64,65,66,],[14,19,14,-5,-6,-7,-21,-22,-11,-12,14,19,14,14,19,-9,-38,-39,14,-8,-10,19,-20,-30,-31,-23,-24,-13,19,-15,19,59,-14,-18,-32,-33,-17,-16,-19,-34,]),'SIGN':([0,24,25,26,27,43,55,60,62,],[16,44,44,44,44,16,44,44,44,]),'FUNCNAME':([0,4,8,9,13,14,18,19,20,21,23,28,31,32,34,37,38,40,41,42,45,46,51,57,58,66,],[17,17,-21,-22,17,17,17,17,-25,-26,17,17,-38,-39,17,-27,-29,-20,-30,-31,-23,-24,-28,-32,-33,-34,]),'UINT':([0,15,16,24,25,26,27,43,44,55,59,60,62,],[-36,31,33,41,41,41,41,-36,-35,41,63,41,41,]),'UFLOAT':([0,15,16,24,25,26,27,43,44,55,60,62,],[-36,32,-35,-36,-36,-36,-36,-36,-35,-36,-36,-36,]),'$end':([1,2,3,5,6,7,11,12,22,30,31,32,35,36,39,41,42,47,49,54,56,57,58,61,64,65,66,],[0,-1,-2,-5,-6,-7,-11,-12,-3,-9,-38,-39,-8,-10,-4,-30,-31,-13,-15,-14,-18,-32,-33,-17,-16,-19,-34,]),'WHITESPACE':([3,4,5,6,7,8,9,11,12,21,22,29,30,31,32,35,36,37,39,40,41,42,45,46,47,48,49,50,54,56,57,58,61,64,65,66,],[20,23,-5,-6,-7,-21,-22,-11,-12,38,20,20,-9,-38,-39,-8,-10,51,20,-20,-30,-31,-23,-24,-13,20,-15,20,-14,-18,-32,-33,-17,-16,-19,-34,]),'STAR':([3,5,6,7,11,12,20,22,29,30,31,32,35,36,39,41,42,47,48,49,50,54,56,57,58,61,64,65,66,],[21,-5,-6,-7,-11,-12,37,21,21,-9,-38,-39,-8,-10,21,-30,-31,-13,21,-15,21,-14,-18,-32,-33,-17,-16,-19,-34,]),'CLOSE_PAREN':([5,6,7,11,12,29,30,31,32,33,35,36,41,42,47,48,49,50,52,53,54,56,57,58,61,63,64,65,66,],[-5,-6,-7,-11,-12,49,-9,-38,-39,-37,-8,-10,-30,-31,-13,54,-15,56,57,58,-14,-18,-32,-33,-17,66,-16,-19,-34,]),'POWER':([8,9,10,11,31,32,33,49,54,56,],[24,25,26,27,-38,-39,-37,55,60,62,]),} _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,13,23,28,34,],[3,22,29,39,48,50,]),'scale_factor':([0,],[4,]),'unit_expression':([0,4,13,14,18,19,23,28,34,],[5,5,5,30,35,36,5,5,5,]),'product_of_units':([0,4,13,23,28,34,],[6,6,6,6,6,6,]),'division_of_units':([0,4,13,23,28,34,],[7,7,7,7,7,7,]),'signed_float':([0,24,25,26,27,43,55,60,62,],[9,42,42,42,42,53,42,42,42,]),'signed_int':([0,43,],[10,52,]),'function':([0,4,13,14,18,19,23,28,34,],[12,12,12,12,12,12,12,12,12,]),'sign':([0,24,25,26,27,43,55,60,62,],[15,15,15,15,15,15,15,15,15,]),'product':([3,22,29,39,48,50,],[18,18,18,18,18,18,]),'numeric_power':([24,25,26,27,55,60,62,],[40,45,46,47,61,64,65,]),} _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',165), ('main -> complete_expression','main',1,'p_main','ogip.py',166), ('main -> scale_factor complete_expression','main',2,'p_main','ogip.py',167), ('main -> scale_factor WHITESPACE complete_expression','main',3,'p_main','ogip.py',168), ('complete_expression -> unit_expression','complete_expression',1,'p_complete_expression','ogip.py',180), ('complete_expression -> product_of_units','complete_expression',1,'p_complete_expression','ogip.py',181), ('complete_expression -> division_of_units','complete_expression',1,'p_complete_expression','ogip.py',182), ('product_of_units -> complete_expression product unit_expression','product_of_units',3,'p_product_of_units','ogip.py',190), ('division_of_units -> DIVISION unit_expression','division_of_units',2,'p_division_of_units','ogip.py',196), ('division_of_units -> complete_expression DIVISION unit_expression','division_of_units',3,'p_division_of_units','ogip.py',197), ('unit_expression -> UNIT','unit_expression',1,'p_unit_expression','ogip.py',207), ('unit_expression -> function','unit_expression',1,'p_unit_expression','ogip.py',208), ('unit_expression -> UNIT POWER numeric_power','unit_expression',3,'p_unit_expression','ogip.py',209), ('unit_expression -> UNIT OPEN_PAREN complete_expression CLOSE_PAREN','unit_expression',4,'p_unit_expression','ogip.py',210), ('unit_expression -> OPEN_PAREN complete_expression CLOSE_PAREN','unit_expression',3,'p_unit_expression','ogip.py',211), ('unit_expression -> UNIT OPEN_PAREN complete_expression CLOSE_PAREN POWER numeric_power','unit_expression',6,'p_unit_expression','ogip.py',212), ('unit_expression -> OPEN_PAREN complete_expression CLOSE_PAREN POWER numeric_power','unit_expression',5,'p_unit_expression','ogip.py',213), ('function -> FUNCNAME OPEN_PAREN complete_expression CLOSE_PAREN','function',4,'p_function','ogip.py',242), ('function -> FUNCNAME OPEN_PAREN complete_expression CLOSE_PAREN POWER numeric_power','function',6,'p_function','ogip.py',243), ('scale_factor -> LIT10 POWER numeric_power','scale_factor',3,'p_scale_factor','ogip.py',258), ('scale_factor -> LIT10','scale_factor',1,'p_scale_factor','ogip.py',259), ('scale_factor -> signed_float','scale_factor',1,'p_scale_factor','ogip.py',260), ('scale_factor -> signed_float POWER numeric_power','scale_factor',3,'p_scale_factor','ogip.py',261), ('scale_factor -> signed_int POWER numeric_power','scale_factor',3,'p_scale_factor','ogip.py',262), ('product -> WHITESPACE','product',1,'p_product','ogip.py',277), ('product -> STAR','product',1,'p_product','ogip.py',278), ('product -> WHITESPACE STAR','product',2,'p_product','ogip.py',279), ('product -> WHITESPACE STAR WHITESPACE','product',3,'p_product','ogip.py',280), ('product -> STAR WHITESPACE','product',2,'p_product','ogip.py',281), ('numeric_power -> UINT','numeric_power',1,'p_numeric_power','ogip.py',286), ('numeric_power -> signed_float','numeric_power',1,'p_numeric_power','ogip.py',287), ('numeric_power -> OPEN_PAREN signed_int CLOSE_PAREN','numeric_power',3,'p_numeric_power','ogip.py',288), ('numeric_power -> OPEN_PAREN signed_float CLOSE_PAREN','numeric_power',3,'p_numeric_power','ogip.py',289), ('numeric_power -> OPEN_PAREN signed_float DIVISION UINT CLOSE_PAREN','numeric_power',5,'p_numeric_power','ogip.py',290), ('sign -> SIGN','sign',1,'p_sign','ogip.py',308), ('sign -> ','sign',0,'p_sign','ogip.py',309), ('signed_int -> SIGN UINT','signed_int',2,'p_signed_int','ogip.py',318), ('signed_float -> sign UINT','signed_float',2,'p_signed_float','ogip.py',324), ('signed_float -> sign UFLOAT','signed_float',2,'p_signed_float','ogip.py',325), ] astropy-astropy-201cddb/astropy/units/format/unicode_format.py000066400000000000000000000037321507226315300251070ustar00rootroot00000000000000# 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) astropy-astropy-201cddb/astropy/units/format/utils.py000066400000000000000000000007261507226315300232510ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst """ Utilities shared by the different formats. """ from collections.abc import Generator, Iterable, Sequence from keyword import iskeyword def get_non_keyword_units( bases: Iterable[str], prefixes: Sequence[str] ) -> Generator[tuple[str, str], None, None]: for base in bases: for prefix in prefixes: if not iskeyword(unit := prefix + base): yield unit, base astropy-astropy-201cddb/astropy/units/format/vounit.py000066400000000000000000000201301507226315300234240ustar00rootroot00000000000000# 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.errors import ( UnitParserWarning, UnitScaleError, UnitsError, UnitsWarning, ) from astropy.units.typing import UnitScale from astropy.utils import classproperty from . import Base, utils 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 = {} 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 key, base in utils.get_non_keyword_units(bases, prefixes): if key 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"]) 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) -> 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) @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 ) -> str: # Remove units that aren't known to the format unit = cls._decompose_to_known_units(unit) 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] ) @classmethod def _validate_unit(cls, unit: str, detailed_exception: bool = True) -> UnitBase: if unit in cls._deprecated_units: warnings.warn( UnitsWarning( f"The unit '{unit}' has been deprecated in the VOUnit standard." f" Suggested: {cls.to_string(cls._units[unit]._represents)}." ) ) return super()._validate_unit(unit, detailed_exception) astropy-astropy-201cddb/astropy/units/function/000077500000000000000000000000001507226315300220675ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/units/function/__init__.py000066400000000000000000000007711507226315300242050ustar00rootroot00000000000000# 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__ astropy-astropy-201cddb/astropy/units/function/core.py000066400000000000000000000721141507226315300233760ustar00rootroot00000000000000# 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 astropy.units import ( Quantity, Unit, UnitBase, UnitConversionError, UnitsError, UnitTypeError, dimensionless_unscaled, ) from astropy.units.typing import PhysicalTypeID from astropy.utils.compat import COPY_IF_NEEDED, NUMPY_LT_2_0 if NUMPY_LT_2_0: from numpy.core import umath as np_umath else: from numpy._core import umath as np_umath __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=COPY_IF_NEEDED, 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 ) astropy-astropy-201cddb/astropy/units/function/logarithmic.py000066400000000000000000000357741507226315300247630ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst import numbers import numpy as np from astropy.units import ( CompositeUnit, Unit, UnitConversionError, UnitsError, UnitTypeError, dimensionless_unscaled, ) from astropy.utils import lazyproperty from astropy.utils.compat.numpycompat import NUMPY_LT_2_0 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 @lazyproperty 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``. """ @lazyproperty 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``. """ @lazyproperty 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``. """ @lazyproperty 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) if NUMPY_LT_2_0: def ptp(self, axis=None, out=None): unit = self.unit._copy(dimensionless_unscaled) return self._wrap_function(np.ptp, axis, out=out, unit=unit) else: 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) 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 astropy-astropy-201cddb/astropy/units/function/mixin.py000066400000000000000000000012551507226315300235700ustar00rootroot00000000000000# 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 astropy-astropy-201cddb/astropy/units/function/units.py000066400000000000000000000066241507226315300236130ustar00rootroot00000000000000# 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.utils 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) ] ) ) astropy-astropy-201cddb/astropy/units/imperial.py000066400000000000000000000122741507226315300224240ustar00rootroot00000000000000# 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] = [] # Units are added at the end from . import si from .core import add_enabled_units, def_unit from .utils 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(): """ 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()) astropy-astropy-201cddb/astropy/units/misc.py000066400000000000000000000076021507226315300215540ustar00rootroot00000000000000# 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 .utils 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()) astropy-astropy-201cddb/astropy/units/photometric.py000066400000000000000000000051201507226315300231470ustar00rootroot00000000000000# 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. """ # avoid ruff complaints about undefined names defined by def_unit # ruff: noqa: F821 import numpy as np from astropy.constants.si import L_bol0 from . import astrophys, cgs, si from .core import Unit, def_unit from .utils import generate_dunder_all, generate_unit_summary __all__ = ["zero_point_flux"] # 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." ), ) 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. """ flux_unit0 = Unit(flux0) return [(maggy, flux_unit0)] ########################################################################### # 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()) astropy-astropy-201cddb/astropy/units/physical.py000066400000000000000000000531731507226315300224410ustar00rootroot00000000000000# 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 astropy.utils.compat import COPY_IF_NEEDED 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 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=COPY_IF_NEEDED).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) astropy-astropy-201cddb/astropy/units/quantity.py000066400000000000000000002526761507226315300225140ustar00rootroot00000000000000# 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 Self import numpy as np from astropy import config as _config from astropy.utils.compat.numpycompat import COPY_IF_NEEDED, NUMPY_LT_2_0 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 = ("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"] = COPY_IF_NEEDED 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] 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=COPY_IF_NEEDED 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 = COPY_IF_NEEDED 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): # The first part of the regex string matches any integer/float; # the second parts adds possible trailing .+-, which will break # the float function below and ensure things like 1.2.3deg # will not work. pattern = ( r"\s*[+-]?" r"((\d+\.?\d*)|(\.\d+)|([nN][aA][nN])|" r"([iI][nN][fF]([iI][nN][iI][tT][yY]){0,1}))" r"([eE][+-]?\d+)?" r"[.+-]?" ) v = re.match(pattern, value) unit_string = None try: value = float(v.group()) except Exception: raise TypeError( f'Cannot parse "{value}" as a {cls.__name__}. It does not ' "start with a number." ) unit_string = v.string[v.end() :].strip() if unit_string: value_unit = Unit(unit_string) 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 = COPY_IF_NEEDED # 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): # Check whether super().__array_finalize should be called # (sadly, ndarray.__array_finalize__ is None; we cannot be sure # what is above us). super_array_finalize = super().__array_finalize__ if super_array_finalize is not None: 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.array(obj, copy=COPY_IF_NEEDED, subok=True) # 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 = 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" ) # Otherwise return a generator def quantity_iter(): for val in self.value: yield self._new_view(val) return quantity_iter() 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=COPY_IF_NEEDED, 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 if NUMPY_LT_2_0: def itemset(self, *args): if len(args) == 0: raise ValueError("itemset must have at least one argument") self.view(np.ndarray).itemset(*(args[:-1] + (self._to_own_unit(args[-1]),))) 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 if NUMPY_LT_2_0: def argsort(self, axis=-1, kind=None, order=None): return self.view(np.ndarray).argsort(axis=axis, kind=kind, order=order) else: 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 = 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=COPY_IF_NEEDED) desired = Quantity(desired, subok=True, copy=COPY_IF_NEEDED) 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=COPY_IF_NEEDED) 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=COPY_IF_NEEDED) try: rtol = rtol.to(dimensionless_unscaled) except Exception: raise UnitsError("'rtol' should be dimensionless") return actual.value, desired.value, rtol.value, atol.value astropy-astropy-201cddb/astropy/units/quantity_helper/000077500000000000000000000000001507226315300234575ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/units/quantity_helper/__init__.py000066400000000000000000000011141507226315300255650ustar00rootroot00000000000000# 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 astropy-astropy-201cddb/astropy/units/quantity_helper/converters.py000066400000000000000000000363461507226315300262370ustar00rootroot00000000000000# 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 is 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 astropy-astropy-201cddb/astropy/units/quantity_helper/erfa.py000066400000000000000000000352541507226315300247570ustar00rootroot00000000000000# 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. from erfa import dt_eraASTROM, dt_eraLDBODY, dt_pv from erfa import ufunc as erfa_ufunc 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): if not has_matching_structure(unit, dtype): msg = {dt_pv: "pv", dt_eraLDBODY: "ldbody", dt_eraASTROM: "astrom"}.get( dtype, "function" ) raise UnitTypeError(f"{msg} 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 astropy.units.astrophys import AU, Msun from astropy.units.si import day, radian return StructuredUnit((Msun, radian, (AU, AU / day)), erfa_ufunc.dt_eraLDBODY) def astrom_unit(): 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, ), erfa_ufunc.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(): 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) astropy-astropy-201cddb/astropy/units/quantity_helper/function_helpers.py000066400000000000000000001461241507226315300274100ustar00rootroot00000000000000# 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 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 ( COPY_IF_NEEDED, NUMPY_LT_1_24, NUMPY_LT_2_0, NUMPY_LT_2_1, NUMPY_LT_2_2, ) if NUMPY_LT_2_0: import numpy.core as np_core else: import numpy._core as np_core # In 1.17, overrides are enabled by default, but it is still possible to # turn them off using an environment variable. We use getattr since it # is planned to remove that possibility in later numpy versions. ARRAY_FUNCTION_ENABLED = getattr(np_core.overrides, "ENABLE_ARRAY_FUNCTION", True) 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.average, 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} if NUMPY_LT_2_0: # functions (re)moved in numpy 2.0; alias for np.round in NUMPY_LT_1_25 SUBCLASS_SAFE_FUNCTIONS |= { np.msort, np.round_, # noqa: NPY003, NPY201 np.trapz, # noqa: NPY201 np.product, # noqa: NPY003, NPY201 np.cumproduct, # noqa: NPY003, NPY201 } if not NUMPY_LT_2_0: # 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} # 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 if NUMPY_LT_2_0: UNSUPPORTED_FUNCTIONS |= { # removed in numpy 2.0 np.sometrue, np.alltrue, # noqa: NPY003, NPY201 } # 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, } | ({np.asfarray} if NUMPY_LT_2_0 else set()) # noqa: NPY201 ) # 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 @function_helper def putmask(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 @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=COPY_IF_NEEDED, 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=COPY_IF_NEEDED, 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 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 @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() if NUMPY_LT_2_0: @function_helper def arange(*args, start=None, stop=None, step=None, dtype=None): return arange_impl(*args, start=start, stop=stop, step=step, dtype=dtype) else: @function_helper def arange(*args, start=None, stop=None, step=None, dtype=None, device=None): return arange_impl( *args, start=start, stop=stop, step=step, dtype=dtype, device=device ) def arange_impl(*args, start=None, stop=None, step=None, dtype=None, **kwargs): # NumPy is supposed to validate the input parameters before this dispatched # function is reached. Nevertheless, we'll sprinkle a few rundundant # sanity checks in the form of `assert` statements. # As they are not part of the business logic, it is fine if they are # compiled-away (e.g. the Python interpreter runs with -O) assert len(args) <= 4 # bind positional arguments to their meaningful names # following the (complex) logic of np.arange match args: case (pos1,): assert stop is None or start is None if stop is None: stop = pos1 elif start is None: start = pos1 case start, stop, *rest: if start is not None and stop is None: start, stop = stop, start match rest: # rebind step and dtype if possible case (step,): pass case step, dtype: pass # as the only required argument, we want stop to set the unit of the output # so it's important that it comes first in the qty_kwargs qty_kwargs = { k: v for k, v in (("stop", stop), ("start", start), ("step", step)) if v is not None } out_unit = getattr(stop, "unit", UNIT_FROM_LIKE_ARG) if out_unit is UNIT_FROM_LIKE_ARG: if hasattr(start, "unit") or hasattr(step, "unit"): raise TypeError( "stop without a unit cannot be combined with start or step with a unit." ) kwargs.update(qty_kwargs) else: # Convert possible start, step to stop units. new_values, _ = _quantities2arrays(*qty_kwargs.values()) kwargs.update(zip(qty_kwargs.keys(), new_values)) kwargs["dtype"] = dtype return (), kwargs, out_unit, None if NUMPY_LT_2_0: @function_helper(helps={np.empty, np.ones, np.zeros}) def creation_helper(shape, dtype=None, order="C"): return (shape, dtype, order), {}, UNIT_FROM_LIKE_ARG, None else: @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 if NUMPY_LT_2_0: @function_helper def full(shape, fill_value, dtype=None, order="C"): return full_impl(shape, fill_value, dtype, order) else: @function_helper def full(shape, fill_value, dtype=None, order="C", *, device=None): return full_impl(shape, fill_value, dtype, order, device=device) def full_impl(shape, fill_value, *args, **kwargs): 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) + args, kwargs, 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 @function_helper def array(object, dtype=None, *, copy=True, order="K", subok=False, ndmin=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} return (object, dtype), kwargs, out_unit, None if NUMPY_LT_2_0: asarray_impl_1_helps = {np.asarray, np.asanyarray} asarray_impl_2_helps = {} elif NUMPY_LT_2_1: asarray_impl_1_helps = {np.asanyarray} asarray_impl_2_helps = {np.asarray} else: asarray_impl_1_helps = {} 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, 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, ) if NUMPY_LT_1_24: @function_helper(helps={np.histogram}) def histogram_pre_1_24( a, bins=10, range=None, normed=None, weights=None, density=None ): args, kwargs, unit, out = histogram( a, bins=bins, range=range, weights=weights, density=density or normed ) kwargs["normed"] = normed kwargs["density"] = density return args, kwargs, unit, out @function_helper(helps={np.histogram2d}) def histogram2d_pre_1_24( x, y, bins=10, range=None, normed=None, weights=None, density=None ): args, kwargs, unit, out = histogram2d( x, y, bins=bins, range=range, weights=weights, density=density or normed, ) kwargs["normed"] = normed kwargs["density"] = density return args, kwargs, unit, out @function_helper(helps={np.histogramdd}) def histogramdd_pre_1_24( sample, bins=10, range=None, normed=None, weights=None, density=None ): args, kwargs, unit, out = histogramdd( sample, bins=bins, range=range, weights=weights, density=density or normed, ) kwargs["normed"] = normed kwargs["density"] = density return args, kwargs, unit, out @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, **kwargs, ): # having **kwargs allows to support equal_nan (for not NUMPY_LT_1_24) 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, 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 @function_helper # np.in1d deprecated in not NUMPY_LT_2_0. 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. formatter = args[6] if len(args) >= 7 else kwargs.get("formatter") if formatter is None: a = a.value else: # See whether it covers our dtype. if NUMPY_LT_2_0: from numpy.core.arrayprint import _get_format_function, _make_options_dict else: 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) 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 if NUMPY_LT_2_0: @function_helper(module=np.linalg) def pinv(a, rcond=1e-15, *args, **kwargs): rcond = _interpret_tol(rcond, a.unit) return (a.view(np.ndarray), rcond) + args, kwargs, 1 / a.unit, None else: @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="warn" if NUMPY_LT_2_0 else 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 if NUMPY_LT_2_0: @function_helper(module=np.linalg) def cholesky(a): return (a.value,), {}, a.unit**0.5, None else: @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 if not NUMPY_LT_2_0: # these functions were added in numpy 2.0 @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, ) astropy-astropy-201cddb/astropy/units/quantity_helper/helpers.py000066400000000000000000000424241507226315300255010ustar00rootroot00000000000000# 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 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_0, NUMPY_LT_2_1, NUMPY_LT_2_2, NUMPY_LT_2_3, ) if NUMPY_LT_2_0: from numpy.core import umath as np_umath else: from numpy._core import umath as np_umath 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): return [None, None], _d(unit1) * _d(unit2) def helper_division(f, unit1, unit2): return [None, None], _d(unit1) / _d(unit2) 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, } if not NUMPY_LT_2_0: # 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, ) 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 if not NUMPY_LT_2_0: 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 astropy-astropy-201cddb/astropy/units/quantity_helper/scipy_special.py000066400000000000000000000057531507226315300266720ustar00rootroot00000000000000# 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 ) astropy-astropy-201cddb/astropy/units/required_by_vounit.py000066400000000000000000000022551507226315300245360ustar00rootroot00000000000000# 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 .utils 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()) astropy-astropy-201cddb/astropy/units/si.py000066400000000000000000000241161507226315300212330ustar00rootroot00000000000000# 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 .utils 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, format={"latex": r"\mathcal{l}", "unicode": "ℓ"}, 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, exclude_prefixes=["a"], 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()) astropy-astropy-201cddb/astropy/units/structured.py000066400000000000000000000502501507226315300230220ustar00rootroot00000000000000# 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 astropy.utils.compat.numpycompat import NUMPY_LT_1_24 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. """ applied = tuple(func(part) for part in self.values()) if NUMPY_LT_1_24: results = np.array(applied, self._units.dtype)[()] else: results = np.void(applied, 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 __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) astropy-astropy-201cddb/astropy/units/tests/000077500000000000000000000000001507226315300214045ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/units/tests/__init__.py000066400000000000000000000000001507226315300235030ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/units/tests/test_aliases.py000066400000000000000000000057621507226315300244500ustar00rootroot00000000000000# 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}) astropy-astropy-201cddb/astropy/units/tests/test_deprecated.py000066400000000000000000000040051507226315300251140ustar00rootroot00000000000000# 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 astropy-astropy-201cddb/astropy/units/tests/test_equivalencies.py000066400000000000000000001032321507226315300256530ustar00rootroot00000000000000# 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) astropy-astropy-201cddb/astropy/units/tests/test_format.py000066400000000000000000001120751507226315300243130ustar00rootroot00000000000000# 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, 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" class RoundtripBase: def check_roundtrip(self, unit, output_format=None): if output_format is None: output_format = self.format_.name with warnings.catch_warnings(): warnings.simplefilter("ignore") # Same warning shows up multiple times s = unit.to_string(output_format) if s in self.format_._deprecated_units: 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="unicode") self.check_roundtrip_decompose(unit) 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, 765), (u_format.VOUnit, 1303), (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 = "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 @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 astropy-astropy-201cddb/astropy/units/tests/test_logarithmic.py000066400000000000000000001104651507226315300253260ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst """ Test the Logarithmic Units and Quantities """ import itertools 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 from astropy.utils.compat.numpycompat import NUMPY_LT_2_0 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) 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", 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( "lu_unit, physical_unit", itertools.product(lu_units, pu_sample) ) 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( "lu_cls, physical_unit", itertools.product(lu_subclasses + [u.LogUnit], pu_sample), ) 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( "lu_unit, physical_unit", itertools.product(lu_units, pu_sample) ) 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( "flu_unit, tlu_unit, physical_unit", itertools.product(lu_units, lu_units, pu_sample), ) 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", 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( "lq_cls, physical_unit", itertools.product(lq_subclasses, pu_sample) ) 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: def setup_method(self): self.mJy = np.arange(1.0, 5.0).reshape(2, 2) * u.mag(u.Jy) self.m1 = np.arange(1.0, 5.5, 0.5).reshape(3, 3) * u.mag() self.mags = (self.mJy, self.m1) @pytest.mark.parametrize( "method", ( "mean", "min", "max", "round", "trace", "std", "var", "diff", "ediff1d", ), ) def test_always_ok(self, method): for mag in self.mags: 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 @pytest.mark.skipif(not NUMPY_LT_2_0, reason="ptp method removed in numpy 2.0") def test_always_ok_ptp(self): for mag in self.mags: res = mag.ptp() assert np.all(res.value == mag._function_view.ptp().value) assert res.unit == u.mag() def test_clip(self): for mag in self.mags: 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_only_ok_if_dimensionless(self, method): res = getattr(self.m1, method)() assert np.all(res.value == getattr(self.m1._function_view, method)().value) assert res.unit == self.m1.unit with pytest.raises(TypeError): getattr(self.mJy, method)() def test_dot(self): assert np.all(self.m1.dot(self.m1).value == self.m1.value.dot(self.m1.value)) @pytest.mark.parametrize("method", ("prod", "cumprod")) def test_never_ok(self, method): with pytest.raises(TypeError): getattr(self.mJy, method)() with pytest.raises(TypeError): getattr(self.m1, method)() class TestLogQuantityFunctions: # TODO: add tests for all supported functions! def setup_method(self): self.mJy = np.arange(1.0, 5.0).reshape(2, 2) * u.mag(u.Jy) self.m1 = np.arange(1.0, 5.5, 0.5).reshape(3, 3) * u.mag() self.mags = (self.mJy, self.m1) def test_ptp(self): for mag in self.mags: res = np.ptp(mag) assert np.all(res.value == np.ptp(mag._function_view).value) assert res.unit == u.mag() astropy-astropy-201cddb/astropy/units/tests/test_photometric.py000066400000000000000000000022171507226315300253540ustar00rootroot00000000000000# 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) astropy-astropy-201cddb/astropy/units/tests/test_physical.py000066400000000000000000000451671507226315300246460ustar00rootroot00000000000000# 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 astropy-astropy-201cddb/astropy/units/tests/test_quantity.py000066400000000000000000002105131507226315300246750ustar00rootroot00000000000000# 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.compat import COPY_IF_NEEDED 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 # See https://github.com/numpy/numpy/issues/5074 # It seems unlikely this will be resolved, so xfail'ing it. @pytest.mark.xfail(reason="list multiplication only works for numpy <=1.10") def test_numeric_converter_to_index_in_practice(self): """Test that use of __index__ actually works.""" q4 = u.Quantity(2, u.dimensionless_unscaled, dtype=int) assert q4 * ["a", "b", "c"] == ["a", "b", "c", "a", "b", "c"] 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_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) 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(ValueError): u.Quantity("5E") with pytest.raises(ValueError): u.Quantity("5 foo") 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=COPY_IF_NEEDED): 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 = super().__array_finalize__ if super_array_finalize is not None: 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 astropy-astropy-201cddb/astropy/units/tests/test_quantity_annotations.py000066400000000000000000000232631507226315300273160ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst # ruff: noqa: FA100, FA102 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) astropy-astropy-201cddb/astropy/units/tests/test_quantity_array_methods.py000066400000000000000000000527041507226315300276240ustar00rootroot00000000000000# 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.numpycompat import NUMPY_LT_2_0 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 @pytest.mark.skipif(not NUMPY_LT_2_0, reason="itemset method removed in numpy 2.0") def test_itemset(self): q1 = u.Quantity(np.array([1, 2, 3]), u.m / u.km, dtype=int) assert q1.item(1) == 2 * q1.unit q1.itemset(1, 1) assert q1.item(1) == 1000 * u.m / u.km q1.itemset(1, 100 * u.cm / u.km) assert q1.item(1) == 1 * u.m / u.km with pytest.raises(TypeError): q1.itemset(1, 1.5 * u.m / u.km) with pytest.raises(ValueError): q1.itemset() 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) astropy-astropy-201cddb/astropy/units/tests/test_quantity_decorator.py000066400000000000000000000301061507226315300267350ustar00rootroot00000000000000# 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 astropy-astropy-201cddb/astropy/units/tests/test_quantity_erfa_ufuncs.py000066400000000000000000000656031507226315300272650ustar00rootroot00000000000000# 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) astropy-astropy-201cddb/astropy/units/tests/test_quantity_helpers.py000066400000000000000000000013241507226315300264150ustar00rootroot00000000000000""" 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)) astropy-astropy-201cddb/astropy/units/tests/test_quantity_info.py000066400000000000000000000117441507226315300257150ustar00rootroot00000000000000# 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) astropy-astropy-201cddb/astropy/units/tests/test_quantity_non_ufuncs.py000066400000000000000000003124151507226315300271360ustar00rootroot00000000000000# 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 astropy import units as u from astropy.units.quantity_helper.function_helpers import ( ARRAY_FUNCTION_ENABLED, DISPATCHED_FUNCTIONS, FUNCTION_HELPERS, IGNORED_FUNCTIONS, SUBCLASS_SAFE_FUNCTIONS, SUPPORTED_NEP35_FUNCTIONS, TBD_FUNCTIONS, UNSUPPORTED_FUNCTIONS, ) from astropy.utils.compat import ( NUMPY_LT_1_24, NUMPY_LT_1_25, NUMPY_LT_2_0, NUMPY_LT_2_1, NUMPY_LT_2_2, ) 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 ARCSEC_PER_DEGREE = 60 * 60 ARCSEC_PER_RADIAN = ARCSEC_PER_DEGREE * np.rad2deg(1) needs_array_function = pytest.mark.xfail( not ARRAY_FUNCTION_ENABLED, reason="Needs __array_function__ support" ) # 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]: if NUMPY_LT_1_25: def allows_array_function_override(f): return ( hasattr(f, "__wrapped__") and f is not np.printoptions and not f.__name__.startswith("_") ) else: from numpy.testing.overrides import allows_array_function_override 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 if not NUMPY_LT_2_0: 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) @needs_array_function 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) @needs_array_function @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): @needs_array_function def test_copy(self): self.check(np.copy) # Also as kwarg copy = np.copy(a=self.q) assert_array_equal(copy, self.q) @pytest.mark.skipif(not NUMPY_LT_2_0, reason="np.asfarray is removed in NumPy 2.0") @needs_array_function def test_asfarray(self): self.check(np.asfarray) # noqa: NPY201 farray = np.asfarray(a=self.q) # noqa: NPY201 assert_array_equal(farray, 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) @needs_array_function 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) if not NUMPY_LT_2_0: def test_astype(self): int32q = self.q.astype("int32") assert_array_equal(np.astype(int32q, "int32"), int32q) @needs_array_function @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 * u.radian, None), {}, np.rad2deg(np.arange(10, dtype=float) * ARCSEC_PER_DEGREE), id="pos: stop, followed by 1 None", ), pytest.param( (10 * u.radian, None, None), {}, np.rad2deg(np.arange(10, dtype=float) * ARCSEC_PER_DEGREE), id="pos: stop, followed by 2 None", ), pytest.param( (10 * u.radian, None, None, None), {}, np.rad2deg(np.arange(10, dtype=float) * ARCSEC_PER_DEGREE), id="pos: stop, followed by 3 None", ), ], ) def test_arange(self, args, kwargs, expected): arr = np.arange(*args, **kwargs, like=u.Quantity([], u.degree)) assert type(arr) is u.Quantity assert arr.unit == u.radian 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_pos_dtype(self): arr = np.arange(0 * u.s, 10 * u.s, 1 * u.s, int, like=u.Quantity([], u.radian)) assert type(arr) is u.Quantity assert arr.unit == u.s assert arr.dtype == np.dtype(int) 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 a = np.arange(start=1 * u.s, stop=10 * u.min, like=Q) b = np.arange(stop=10 * u.min, start=1 * u.s, like=Q) assert a.unit == u.min assert b.unit == u.min 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 precedent 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) @needs_array_function 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) @needs_array_function 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) @needs_array_function 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) @needs_array_function 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) @needs_array_function 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) @needs_array_function 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()]) @needs_array_function def test_stack(self): self.check(np.stack) @needs_array_function def test_column_stack(self): self.check(np.column_stack) @needs_array_function def test_hstack(self): self.check(np.hstack) @needs_array_function def test_vstack(self): self.check(np.vstack) @needs_array_function def test_dstack(self): self.check(np.dstack) @needs_array_function 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) @needs_array_function 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) @needs_array_function 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) @needs_array_function 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) @pytest.mark.skipif(not NUMPY_LT_2_0, reason="np.sometrue is removed in NumPy 2.0") @pytest.mark.filterwarnings("ignore:`sometrue` is deprecated as of NumPy 1.25.0") def test_sometrue(self): with pytest.raises(TypeError): np.sometrue(self.q) # noqa: NPY003, NPY201 @pytest.mark.skipif(not NUMPY_LT_2_0, reason="np.alltrue is removed in NumPy 2.0") @pytest.mark.filterwarnings("ignore:`alltrue` is deprecated as of NumPy 1.25.0") def test_alltrue(self): with pytest.raises(TypeError): np.alltrue(self.q) # noqa: NPY003, NPY201 def test_prod(self): with pytest.raises(u.UnitsError): np.prod(self.q) @pytest.mark.skipif(not NUMPY_LT_2_0, reason="np.product is removed in NumPy 2.0") @pytest.mark.filterwarnings("ignore:`product` is deprecated as of NumPy 1.25.0") def test_product(self): with pytest.raises(u.UnitsError): np.product(self.q) # noqa: NPY003, NPY201 def test_cumprod(self): with pytest.raises(u.UnitsError): np.cumprod(self.q) @pytest.mark.skipif( not NUMPY_LT_2_0, reason="np.cumproduct is removed in NumPy 2.0" ) @pytest.mark.filterwarnings("ignore:`cumproduct` is deprecated as of NumPy 1.25.0") def test_cumproduct(self): with pytest.raises(u.UnitsError): np.cumproduct(self.q) # noqa: NPY003, NPY201 @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) @pytest.mark.skipif(not NUMPY_LT_2_0, reason="np.round_ is removed in NumPy 2.0") @pytest.mark.filterwarnings("ignore:`round_` is deprecated as of NumPy 1.25.0") def test_round_(self): self.check(np.round_) # noqa: NPY003, NPY201 def test_around(self): self.check(np.around) 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) @needs_array_function 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) @needs_array_function 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) @needs_array_function 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) @needs_array_function 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) @needs_array_function 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) @needs_array_function def test_tril(self): self.check(np.tril) @needs_array_function def test_triu(self): self.check(np.triu) @needs_array_function 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) @needs_array_function 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) @needs_array_function 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_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) @needs_array_function 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) @needs_array_function 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) @needs_array_function 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) @needs_array_function 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) @needs_array_function 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) @needs_array_function 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) @needs_array_function 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) @needs_array_function 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 """ @needs_array_function 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) @needs_array_function 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()) @needs_array_function 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 @needs_array_function 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 @needs_array_function 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 @needs_array_function 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) @needs_array_function 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) @needs_array_function 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) if NUMPY_LT_2_0: def test_trapz(self): self.check_trapezoid(np.trapz) # noqa: NPY201 else: 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) @needs_array_function 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) @needs_array_function 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) @needs_array_function 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) @needs_array_function 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: @needs_array_function 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) @needs_array_function 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: @needs_array_function 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) @needs_array_function 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) @needs_array_function 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]) @needs_array_function 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), ) @needs_array_function 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]) @needs_array_function 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) @needs_array_function 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, ], ) @needs_array_function 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), ) @needs_array_function 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, ], ) @needs_array_function 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)), ) @needs_array_function 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) @needs_array_function 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) @needs_array_function 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) @needs_array_function 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) @pytest.mark.skipif(not NUMPY_LT_1_24, reason="np.msort is deprecated") def test_msort(self): self.check(np.msort) @needs_array_function 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 @needs_array_function 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!) out3 = np.array2string( self.q, None, None, None, ", ", "", np._NoValue, {"float": str} ) 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 @needs_array_function 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)" @needs_array_function 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") @needs_array_function def test_packbits(self): with pytest.raises(TypeError): np.packbits(self.q) with pytest.raises(TypeError): np.packbits(self.uint_q) @needs_array_function def test_unpackbits(self): with pytest.raises(TypeError): np.unpackbits(self.q) with pytest.raises(TypeError): np.unpackbits(self.uint_q) @needs_array_function 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) @needs_array_function 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) @needs_array_function 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) @needs_array_function @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) if not NUMPY_LT_2_0: @needs_array_function 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) @needs_array_function 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) @needs_array_function 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) @needs_array_function def test_unique_values(self): values = np.unique(self.q, equal_nan=False) res = np.unique_values(self.q) assert_array_equal(res, values) @needs_array_function @pytest.mark.parametrize("kwargs", ({}, dict(return_indices=True))) def test_intersect1d(self, kwargs): self.check2(np.intersect1d, **kwargs) @needs_array_function def test_setxor1d(self): self.check2(np.setxor1d) @needs_array_function 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])) @needs_array_function def test_setdiff1d(self): self.check2(np.setdiff1d) @needs_array_function @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 @needs_array_function 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) @needs_array_function 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 @needs_array_function 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) @needs_array_function 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) @needs_array_function 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) @needs_array_function 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) @needs_array_function 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) @needs_array_function 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) if not NUMPY_LT_2_0: pinv3 = np.linalg.pinv(self.q, rtol=rcond) assert_array_equal(pinv3, expected2) @needs_array_function 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) @needs_array_function 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. @needs_array_function 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") @needs_array_function 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) @needs_array_function 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) @needs_array_function 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) @needs_array_function 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) @needs_array_function 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) @needs_array_function 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) @needs_array_function 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)) @needs_array_function 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) @needs_array_function 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) @needs_array_function 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) if not NUMPY_LT_2_0: # 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) @needs_array_function 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)) @needs_array_function 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)) @needs_array_function def test_svdvals(self): _, ref, _ = np.linalg.svd(self.q) res = np.linalg.svdvals(self.q) assert_allclose(res, ref, rtol=5e-16) @needs_array_function def test_vecdot(self): ref = (self.q * self.q).sum(-1) res = np.linalg.vecdot(self.q, self.q) assert_array_equal(res, ref) @needs_array_function def test_tensordot(self): ref = np.tensordot(self.q, self.q) res = np.linalg.tensordot(self.q, self.q) assert_array_equal(res, ref) @needs_array_function 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)) @needs_array_function 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) @needs_array_function 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 @needs_array_function 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", 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) @needs_array_function 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 @needs_array_function 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: pytest.skip("Non Python function cannot be inspected at runtime") 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: pytest.skip("Non Python function cannot be inspected at runtime") 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 because # they are only intended for testing purposes. # For instance, quantile has such a parameter '_q_unit' keyword_allowed_helper = { name for name in keyword_allowed_helper if not name.startswith("_") } 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 astropy-astropy-201cddb/astropy/units/tests/test_quantity_typing.py000066400000000000000000000050151507226315300262660ustar00rootroot00000000000000# 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) astropy-astropy-201cddb/astropy/units/tests/test_quantity_ufuncs.py000066400000000000000000001656211507226315300262710ustar00rootroot00000000000000# 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.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_1_25, NUMPY_LT_2_0, NUMPY_LT_2_3 from astropy.utils.compat.optional_deps import HAS_SCIPY if NUMPY_LT_2_0: from numpy.core import umath as np_umath else: from numpy._core import umath as np_umath 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: # Note that this test may fail if scipy is present, although the # scipy.special ufuncs are only loaded on demand. This is because # if a prior test has already imported scipy.special, then this test will be # disrupted. # The test passes independently of whether erfa is already loaded # (which will be the case for a full test, since coordinates uses it). @pytest.mark.skipif(HAS_SCIPY, reason="scipy coverage is known to be incomplete") 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 that all ufuncs we cover come from numpy or erfa. # (Since coverage for erfa is 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) } assert all_q_ufuncs - all_np_ufuncs - all_erfa_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) @pytest.mark.skipif(NUMPY_LT_2_0, reason="vecdot only added in numpy 2.0") 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])) 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) @pytest.mark.xfail( NUMPY_LT_1_25, reason="where array_ufunc support introduced in numpy 1.25" ) 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) astropy-astropy-201cddb/astropy/units/tests/test_structured.py000066400000000000000000000737171507226315300252400ustar00rootroot00000000000000# 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 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) astropy-astropy-201cddb/astropy/units/tests/test_units.py000066400000000000000000001130331507226315300241600ustar00rootroot00000000000000# 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", [ pytest.param("h", id="simple_conflict"), pytest.param("ʰ", id="NFKC_normalization"), ], ) def test_duplicate_define(name): namespace = {"h": u.h} with pytest.raises( ValueError, match=( "^Object with NFKC normalized name 'h' already exists in given namespace " r'\(Unit\("h"\)\)\.$' ), ): 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_unit_summary_prefixes(): """ Test for a few units that the unit summary table correctly reports whether or not that unit supports prefixes. Regression test for https://github.com/astropy/astropy/issues/3835 """ from astropy.units import astrophys for summary in utils._iter_unit_summary(astrophys.__dict__): unit, _, _, _, prefixes = summary if unit.name == "lyr": assert prefixes elif unit.name == "pc": assert prefixes elif unit.name == "barn": assert prefixes elif unit.name == "cycle": assert prefixes == "No" elif unit.name == "spat": assert prefixes == "No" elif unit.name == "vox": assert prefixes == "Yes" 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() astropy-astropy-201cddb/astropy/units/tests/test_utils.py000066400000000000000000000100771507226315300241620ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst """ Test utilities for `astropy.units`. """ # 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.utils 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)", ) ) astropy-astropy-201cddb/astropy/units/typing.py000066400000000000000000000047221507226315300221330ustar00rootroot00000000000000"""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], ...] astropy-astropy-201cddb/astropy/units/utils.py000066400000000000000000000225741507226315300217660ustar00rootroot00000000000000# 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. """ import io import keyword import re import unicodedata from collections.abc import Generator, Mapping from fractions import Fraction from typing import TYPE_CHECKING, Literal, SupportsFloat import numpy as np from numpy import finfo from .errors import UnitScaleError from .typing import UnitPower, UnitPowerLike, UnitScale, UnitScaleLike if TYPE_CHECKING: import astropy.units.core _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 _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 _iter_unit_summary( namespace: Mapping[str, object], ) -> Generator[ tuple["astropy.units.core.NamedUnit", str, str, str, Literal["Yes", "No"]], None, None, ]: """ Generates the ``(unit, doc, represents, aliases, prefixes)`` tuple used to format the unit summary docs in `generate_unit_summary`. """ from . import core # Get all of the units, and keep track of which ones have SI # prefixes units = [] has_prefixes = set() for key, val in namespace.items(): # Skip non-unit items if not isinstance(val, core.UnitBase): continue if not isinstance(val, core.NamedUnit): raise TypeError(f"{key!r} must be defined with 'def_unit()'") # Skip aliases if key != val.name: continue if isinstance(val, core.PrefixUnit): # This will return the root unit that is scaled by the prefix # attached to it has_prefixes.add(val._represents.bases[0].name) else: units.append(val) # Sort alphabetically, case insensitive units.sort(key=lambda x: x.name.lower()) for unit in units: doc = _get_first_sentence(unit.__doc__).strip() represents = "" if isinstance(unit, core.Unit): represents = f":math:`{unit._represents.to_string('latex')[1:-1]}`" aliases = ", ".join(f"``{x}``" for x in unit.aliases) yield ( unit, doc, represents, aliases, "Yes" if unit.name in has_prefixes else "No", ) 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. """ docstring = io.StringIO() docstring.write( """ .. list-table:: Available Units :header-rows: 1 :widths: 10 20 20 20 1 * - Unit - Description - Represents - Aliases - SI Prefixes """ ) template = """ * - ``{}`` - {} - {} - {} - {} """ for unit_summary in _iter_unit_summary(namespace): docstring.write(template.format(*unit_summary)) return docstring.getvalue() 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. """ from .core import PrefixUnit faux_namespace = {} for unit in namespace.values(): if isinstance(unit, PrefixUnit): base_unit = unit.represents.bases[0] faux_namespace[base_unit.name] = base_unit docstring = io.StringIO() template = """ * - Prefixes for ``{}`` - {} prefixes - {} - {} - Only """ for unit_summary in _iter_unit_summary(faux_namespace): docstring.write(template.format(*unit_summary)) return docstring.getvalue() def generate_dunder_all(namespace: Mapping[str, object]) -> list[str]: from .core import UnitBase 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) ) ] 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 denom = getattr(p, "denominator", None) if denom is None: # This returns either a (simple) Fraction or the same float. p = maybe_simple_fraction(p) # If still a float, nothing more to be done. if isinstance(p, float): return p # 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 astropy-astropy-201cddb/astropy/utils/000077500000000000000000000000001507226315300202405ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/utils/__init__.py000066400000000000000000000014551507226315300223560ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst """ This subpackage contains developer-oriented utilities used by Astropy. 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. 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. """ from .codegen import * from .decorators import * from .introspection import * from .misc import * from .shapes import * astropy-astropy-201cddb/astropy/utils/codegen.py000066400000000000000000000077071507226315300222310ustar00rootroot00000000000000# 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 astropy-astropy-201cddb/astropy/utils/collections.py000066400000000000000000000027441507226315300231370ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst """ A module containing specialized collection classes. """ class HomogeneousList(list): """ A subclass of list that contains only elements of a given type or 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 ---------- types : sequence of types The types to accept. values : sequence, optional An initial set of values. """ self._types = types super().__init__() self.extend(values) def _assert(self, x): if not isinstance(x, self._types): raise TypeError( f"homogeneous list must contain only objects of type '{self._types}'" ) def __iadd__(self, other): 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 super().append(x) def insert(self, i, x): self._assert(x) return super().insert(i, x) def extend(self, x): for item in x: self._assert(item) super().append(item) astropy-astropy-201cddb/astropy/utils/compat/000077500000000000000000000000001507226315300215235ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/utils/compat/__init__.py000066400000000000000000000005121507226315300236320ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst """ 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. """ # Importing this module will also install monkey-patches defined in it from .numpycompat import * astropy-astropy-201cddb/astropy/utils/compat/numpycompat.py000066400000000000000000000016451507226315300244570ustar00rootroot00000000000000# 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 numpy as np from astropy.utils import minversion __all__ = [ "COPY_IF_NEEDED", "NUMPY_LT_1_24", "NUMPY_LT_1_25", "NUMPY_LT_1_26", "NUMPY_LT_2_0", "NUMPY_LT_2_1", "NUMPY_LT_2_2", "NUMPY_LT_2_3", ] # 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_1_24 = not minversion(np, "1.24") NUMPY_LT_1_25 = not minversion(np, "1.25") NUMPY_LT_1_26 = not minversion(np, "1.26") NUMPY_LT_2_0 = not minversion(np, "2.0") 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") COPY_IF_NEEDED = False if NUMPY_LT_2_0 else None astropy-astropy-201cddb/astropy/utils/compat/optional_deps.py000066400000000000000000000026141507226315300247400ustar00rootroot00000000000000# 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", "fsspec", "h5py", "html5lib", "ipykernel", "IPython", "ipywidgets", "ipydatagrid", "jplephem", "lxml", "matplotlib", "mpmath", "pandas", "PIL", "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}.") astropy-astropy-201cddb/astropy/utils/console.py000066400000000000000000000773121507226315300222660ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst """ Utilities for console input and output. """ 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: import fcntl import signal import termios _CAN_RESIZE_TERMINAL = True except ImportError: _CAN_RESIZE_TERMINAL = False import numpy as np from astropy import conf from astropy.utils.compat.optional_deps import ( HAS_IPYKERNEL, HAS_IPYTHON, HAS_IPYWIDGETS, ) from .decorators import classproperty, deprecated __all__ = [ "ProgressBar", "ProgressBarOrSpinner", "Spinner", "color_print", "human_file_size", "human_time", "isatty", "print_code_line", "terminal_size", ] class _IPython: """Singleton class given access to IPython streams, etc.""" @classproperty def OutStream(cls): if not hasattr(cls, "_OutStream"): if HAS_IPYKERNEL: from ipykernel.iostream import OutStream cls._OutStream = OutStream else: cls._OutStream = None return cls._OutStream @classproperty def ipyio(cls): if not hasattr(cls, "_ipyio"): if HAS_IPYTHON: from IPython.utils import io cls._ipyio = io else: cls._ipyio = None return cls._ipyio def isatty(file): """ 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 ( multiprocessing.current_process().name != "MainProcess" or threading.current_thread().name != "MainThread" ): return False if hasattr(file, "isatty"): return file.isatty() if _IPython.OutStream is None or (not isinstance(file, _IPython.OutStream)): return False return getattr(file, "name", None) == "stdout" @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 _IPython.OutStream is None: # 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 # 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. :: color_print('This is the color ', 'default', 'GREEN', 'green') Parameters ---------- 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. *color* is 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). 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. end : str, optional The ending of the message. Defaults to ``\\n``. The end will be printed after resetting any color or font state. """ file = kwargs.get("file", sys.stdout) write = file.write if isatty(file) and conf.use_color: for i in range(0, len(args), 2): msg = args[i] if i + 1 == len(args): color = "" else: color = args[i + 1] if color: msg = _color_text(msg, color) # 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 range(0, len(args), 2): msg = args[i] write(msg) write(end) def human_time(seconds): """ Returns a human-friendly time string that is always exactly 6 characters long. Depending on the number of seconds given, can be one of:: 1w 3d 2d 4h 1h 5m 1m 4s 15s Will be in color if console coloring is turned on. Parameters ---------- seconds : int The number of seconds to represent Returns ------- time : str A human-friendly representation of the given number of seconds that is always exactly 6 characters. """ units = [ ("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 f" {seconds:2d}s" for i in range(len(units) - 1): unit1, limit1 = units[i] unit2, limit2 = units[i + 1] if seconds >= limit1: 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 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_or_items, ipython_widget=False, file=None): """ Parameters ---------- 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 : :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, or special case hacks to detect the IPython console), the progress bar will be completely silent. """ 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 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() 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 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.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 progress bar via the console or notebook accordingly. """ # Update self.value if value is None: value = self._current_value + 1 self._current_value = value # Choose the appropriate environment if self._ipython_widget: self._update_ipython_widget(value) else: 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: frac = float(value) / float(self._total) file = self._file write = file.write 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(">", "green", file=file, end="") write("-" * (self._bar_length - bar_fill - 1)) write("|") if value >= self._total: t = time.time() - self._start_time prefix = " " elif value <= 0: t = None prefix = "" else: t = ((time.time() - self._start_time) * (1.0 - frac)) / frac 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=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) ProgressBar.map(work, range(50)) Parameters ---------- function : function Function to call for each step items : sequence 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 : :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. 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). """ 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 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. 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 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 : :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. 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). """ # concurrent.futures import here to avoid import failure when running # in pyodide/Emscripten from concurrent.futures import ProcessPoolExecutor, as_completed results = [] 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:: with Spinner("Reticulating splines", "green") as s: for item in enumerate(items): s.update() """ _default_unicode_chars = "◓◑◒◐" _default_ascii_chars = "-/|\\" def __init__(self, msg, color="default", file=None, step=1, chars=None): """ Parameters ---------- msg : str The message to print color : str, optional 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 : :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, 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 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 conf.unicode_output: chars = self._default_unicode_chars else: chars = self._default_ascii_chars self._chars = chars 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("\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 _ in range(self._step): yield index = (index + 1) % len(chars) def __enter__(self): return self def __exit__(self, exc_type, exc_value, traceback): file = self._file write = file.write flush = file.flush if not self._silent: write("\r") color_print(self._msg, self._color, file=file, end="") if exc_type is None: color_print(" [Done]", "green", file=file) else: 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="") self._file.flush() while True: yield class ProgressBarOrSpinner: """ A class that displays either a `ProgressBar` or `Spinner` depending on whether the total size of the operation is known or not. It is designed to be used with the ``with`` statement:: if file.has_length(): length = file.get_length() else: length = None bytes_read = 0 with ProgressBarOrSpinner(length) as bar: while file.read(blocksize): bytes_read += blocksize bar.update(bytes_read) """ def __init__(self, total, msg, color="default", file=None): """ Parameters ---------- total : int or None If an int, the number of increments in the process being tracked and a `ProgressBar` is displayed. If `None`, a `Spinner` is displayed. msg : str The message to display above the `ProgressBar` or alongside the `Spinner`. color : str, optional 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 : :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 `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) else: self._is_spinner = False color_print(msg, color, file=file) self._obj = ProgressBar(total, file=file) def __enter__(self): return self def __exit__(self, exc_type, exc_value, traceback): return self._obj.__exit__(exc_type, exc_value, traceback) def update(self, value): """ Update the progress bar to the given value (out of the total given to the constructor. """ self._obj.update(value) 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 accordingly and 'â€Ļ' characters are inserted at the front and/or end. It looks like this:: there_is_a_syntax_error_here : ^ Parameters ---------- line : unicode The line of code to display col : int, optional The character in the line to highlight. ``col`` must be less than ``len(line)``. file : :term:`file-like (writeable)`, optional Where to write to. Defaults to `sys.stdout`. tabwidth : int, optional The number of spaces per tab (``'\\t'``) character. Default is 8. All tabs will be converted to spaces to ensure that the caret lines up with the correct column. width : int, optional The width of the display, beyond which the line will be 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: 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("\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 + len(ellipsis) :] width -= len(ellipsis) new_col = col col -= offset color_print(ellipsis, "darkgrey", file=file, end="") if len(line) > width: write(line[: width - len(ellipsis)]) color_print(ellipsis, "darkgrey", file=file) else: write(line) write("\n") if col is not None: write(" " * col) color_print("^", "red", file=file) # 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: """Get a single character from standard input without screen echo. Returns ------- char : str (one character) """ def __init__(self): try: self.impl = _GetchWindows() except ImportError: self.impl = _GetchUnix() def __call__(self): return self.impl() class _GetchUnix: def __init__(self): import sys # noqa: F401 import tty # noqa: F401 def __call__(self): import sys import termios import tty fd = sys.stdin.fileno() old_settings = termios.tcgetattr(fd) try: tty.setraw(sys.stdin.fileno()) ch = sys.stdin.read(1) finally: termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) return ch class _GetchWindows: def __init__(self): import msvcrt # noqa: F401 def __call__(self): import msvcrt return msvcrt.getch() astropy-astropy-201cddb/astropy/utils/data.py000066400000000000000000002454601507226315300215360ustar00rootroot00000000000000# 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 zipfile from importlib import import_module from tempfile import NamedTemporaryFile, TemporaryDirectory, gettempdir from types import MappingProxyType from warnings import warn import astropy_iers_data import astropy.config.paths from astropy import config as _config 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 detactment 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 detactment 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 uses 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, 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. .. versionadded:: 5.2 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 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 reading 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, package=None, show_progress=True, remote_timeout=None ): """ 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, package=None, encoding=None, cache=True): """ 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, package=None, pattern="*"): """ 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): """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, package=None): """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, ) 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, pkgname="astropy"): """ 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 def get_free_space_in_dir(path, unit=False): """ Given a path to a directory, returns the amount of free space on that filesystem. Parameters ---------- path : str 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 not os.path.isdir(path): 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, size): """ Determines if a given directory has enough space to hold a file of a given size. Parameters ---------- path : 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=False, ssl_context=None, allow_insecure=False): """ 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, timeout=None, http_headers=None, ftp_tls=False, ssl_context=None, allow_insecure=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 hashshable 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) # 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, show_progress=True, timeout=None, remote_url=None, cache=False, pkgname="astropy", http_headers=None, ftp_tls=None, ssl_context=None, allow_insecure=False, ): 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: dldir = _get_download_cache_loc(pkgname) 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: 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: if os.path.exists(f.name): try: os.remove(f.name) except OSError: pass raise return f.name def download_file( remote_url, cache=False, show_progress=True, timeout=None, sources=None, pkgname="astropy", http_headers=None, ssl_context=None, allow_insecure=False, ): """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 ``~/.astropy/cache``. 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) 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 = os.path.join(dldir, _url_to_dirname(url_key), "contents") if os.path.exists(filename): return os.path.abspath(filename) 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 as e: # errno 8 is from SSL "EOF occurred in violation of protocol" if ( hasattr(e, "reason") and hasattr(e.reason, "errno") and e.reason.errno == 8 ): e.reason.strerror = f"{e.reason.strerror}. requested URL: {remote_url}" e.reason.args = (e.reason.errno, e.reason.strerror) errors[source_url] = e except TimeoutError as e: 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, 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)) if conf.delete_temporary_downloads_at_exit: _tempfilestodel.append(f_name) return os.path.abspath(f_name) def is_url_in_cache(url_key, pkgname="astropy"): """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 ``~/.astropy/cache``. 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 """ try: dldir = _get_download_cache_loc(pkgname) except OSError: return False filename = os.path.join(dldir, _url_to_dirname(url_key), "contents") return os.path.exists(filename) def cache_total_size(pkgname="astropy"): """Return the total size in bytes of all files in the cache.""" size = 0 dldir = _get_download_cache_loc(pkgname=pkgname) 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): with astropy.config.paths.set_temp_config(kwargs.pop("temp_config")): with astropy.config.paths.set_temp_cache(kwargs.pop("temp_cache")): return download_file(**kwargs) def download_files_in_parallel( urls, cache="update", show_progress=True, timeout=None, sources=None, multiprocessing_start_method=None, pkgname="astropy", ): """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 ``~/.astropy/cache``. 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 .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, ) 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, None), pkgname=pkgname, temp_cache=astropy.config.paths.set_temp_cache._temp_path, temp_config=astropy.config.paths.set_temp_config._temp_path, ) for u in combined_urls ], file=progress, multiprocess=True, multiprocessing_start_method=multiprocessing_start_method, ) paths = [] 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 = [] @atexit.register def _deltemps(): if _tempfilestodel is not None: while len(_tempfilestodel) > 0: fn = _tempfilestodel.pop() if os.path.isfile(fn): try: os.remove(fn) except OSError: # oh well we tried # could be held open by some process, on Windows pass elif os.path.isdir(fn): 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=None, pkgname="astropy"): """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 ``~/.astropy/cache``. """ try: dldir = _get_download_cache_loc(pkgname) except OSError as e: # Problem arose when trying to open the cache # Just a warning, though msg = "Not clearing data cache - cache inaccessible due to " estr = "" if len(e.args) < 1 else (": " + str(e)) warn(CacheMissingWarning(msg + e.__class__.__name__ + estr)) return try: if hashorurl is None: # Optional: delete old incompatible caches too _rmtree(dldir) elif _is_url(hashorurl): filepath = os.path.join(dldir, _url_to_dirname(hashorurl)) _rmtree(filepath) else: # Not a URL, it should be either a filename or a hash filepath = os.path.join(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 = os.path.join(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)) def _get_download_cache_loc(pkgname="astropy"): """Finds the path to the cache directory and makes them if they don't exist. Parameters ---------- pkgname : `str`, optional The package name to use to locate the download cache. i.e. for ``pkgname='astropy'`` the default cache location is ``~/.astropy/cache``. Returns ------- datadir : str The path to the data cache directory. """ try: datadir = astropy.config.paths.get_cache_dir_path(pkgname) / "download" / "url" if not datadir.exists(): try: datadir.mkdir(parents=True) except OSError: if not datadir.exists(): raise elif not datadir.is_dir(): raise OSError(f"Data cache directory {datadir} is not a directory") return datadir except OSError as e: msg = "Remote data cache could not be accessed due to " estr = "" if len(e.args) < 1 else (": " + str(e)) warn(CacheMissingWarning(msg + e.__class__.__name__ + estr)) raise def _url_to_dirname(url): 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() _NOTHING = MappingProxyType({}) 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, bad_urls=None, bad_files=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="astropy"): """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 ``~/.astropy/cache``. 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 = set() messages = set() dldir = _get_download_cache_loc(pkgname=pkgname) with os.scandir(dldir) as it: for entry in it: f = os.path.abspath(os.path.join(dldir, entry.name)) 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 = os.path.join(f, sf) bad_files.add(sf) messages.add(f"Unexpected file f{sf}") urlf = os.path.join(f, "url") url = None if not os.path.isfile(urlf): 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 os.path.isfile(os.path.join(f, "contents")): 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=bad_files) def _rmtree(path, replace=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, ) ) raise except OSError as e: if e.errno == errno.EXDEV: warn(e.strerror, AstropyWarning) 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, AstropyWarning) shutil.move(replace, path) else: raise def import_file_to_cache( url_key, filename, remove_original=False, pkgname="astropy", *, replace=True ): """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 ``~/.astropy/cache``. 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) cache_dirname = _url_to_dirname(url_key) local_dirname = os.path.join(cache_dir, cache_dirname) local_filename = os.path.join(local_dirname, "contents") with TemporaryDirectory( prefix="temp_dir", dir=cache_dir, ignore_cleanup_errors=True ) as temp_dir: temp_filename = os.path.join(temp_dir, "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) with open(os.path.join(temp_dir, "url"), "w", encoding="utf-8") as f: f.write(url_key) 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="astropy"): """ 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 ``~/.astropy/cache``. 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).keys()) def cache_contents(pkgname="astropy"): """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. """ r = {} try: dldir = _get_download_cache_loc(pkgname=pkgname) except OSError: return _NOTHING with os.scandir(dldir) as it: for entry in it: if entry.is_dir: url = get_file_contents( os.path.join(dldir, entry.name, "url"), encoding="utf-8" ) r[url] = os.path.abspath(os.path.join(dldir, entry.name, "contents")) return MappingProxyType(r) def export_download_cache( filename_or_obj, urls=None, overwrite=False, pkgname="astropy" ): """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 ``~/.astropy/cache``. 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=None, update_cache=False, pkgname="astropy" ): """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 ``~/.astropy/cache``. 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 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 ) astropy-astropy-201cddb/astropy/utils/data_info.py000066400000000000000000000661331507226315300225470ustar00rootroot00000000000000# 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 if datshape else "" 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 = () # 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 = () # 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 = 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.np_utils 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.common_dtype(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 astropy-astropy-201cddb/astropy/utils/decorators.py000066400000000000000000001305531507226315300227660ustar00rootroot00000000000000# 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 astropy-astropy-201cddb/astropy/utils/diff.py000066400000000000000000000140631507226315300215260ustar00rootroot00000000000000import difflib import functools 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 np.isnan(a) and np.isnan(b): return False return not np.allclose(a, b, rtol=rtol, atol=atol) else: 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 astropy-astropy-201cddb/astropy/utils/exceptions.py000066400000000000000000000047061507226315300230020ustar00rootroot00000000000000# 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}.") astropy-astropy-201cddb/astropy/utils/iers/000077500000000000000000000000001507226315300212025ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/utils/iers/__init__.py000066400000000000000000000000241507226315300233070ustar00rootroot00000000000000from .iers import * astropy-astropy-201cddb/astropy/utils/iers/data/000077500000000000000000000000001507226315300221135ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/utils/iers/data/ReadMe.eopc04_IAU2000000066400000000000000000000041721507226315300252700ustar00rootroot00000000000000Table: 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 -------------------------------------------------------------------------------- astropy-astropy-201cddb/astropy/utils/iers/iers.py000066400000000000000000001452501507226315300225250ustar00rootroot00000000000000# 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) ] # 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) astropy-astropy-201cddb/astropy/utils/iers/tests/000077500000000000000000000000001507226315300223445ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/utils/iers/tests/__init__.py000066400000000000000000000000001507226315300244430ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/utils/iers/tests/data/000077500000000000000000000000001507226315300232555ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/utils/iers/tests/data/finals2000A-2016-02-30-test000066400000000000000000001023521507226315300270640ustar00rootroot0000000000000016 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 astropy-astropy-201cddb/astropy/utils/iers/tests/data/finals2000A-2016-04-30-test000066400000000000000000001021751507226315300270710ustar00rootroot0000000000000016 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 astropy-astropy-201cddb/astropy/utils/iers/tests/data/iers_a_excerpt000066400000000000000000000260201507226315300261740ustar00rootroot0000000000000015 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 astropy-astropy-201cddb/astropy/utils/iers/tests/data/iers_b_old_style_excerpt000066400000000000000000000103541507226315300302560ustar00rootroot00000000000000 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 astropy-astropy-201cddb/astropy/utils/iers/tests/data/leap-seconds.list000066400000000000000000000246461507226315300265430ustar00rootroot00000000000000# # 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 astropy-astropy-201cddb/astropy/utils/iers/tests/test_iers.py000066400000000000000000000522021507226315300247200ustar00rootroot00000000000000# 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] astropy-astropy-201cddb/astropy/utils/iers/tests/test_leap_second.py000066400000000000000000000513671507226315300262450ustar00rootroot00000000000000# 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]) astropy-astropy-201cddb/astropy/utils/introspection.py000066400000000000000000000330741507226315300235210ustar00rootroot00000000000000# 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 astropy >>> minversion(astropy, '0.4.4') 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) astropy-astropy-201cddb/astropy/utils/masked/000077500000000000000000000000001507226315300215045ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/utils/masked/__init__.py000066400000000000000000000005521507226315300236170ustar00rootroot00000000000000# 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 * astropy-astropy-201cddb/astropy/utils/masked/core.py000066400000000000000000001561171507226315300230210ustar00rootroot00000000000000# 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 COPY_IF_NEEDED, NUMPY_LT_2_0 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. 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: 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=COPY_IF_NEEDED): """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=COPY_IF_NEEDED): 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=COPY_IF_NEEDED) 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"} new_info = type( cls.__name__ + "Info", (MaskedArraySubclassInfo, data_info.__class__), dict(attr_names=attr_names), ) cls.info = new_info() # The two pieces typically overridden. @classmethod def from_unmasked(cls, data, mask=None, copy=COPY_IF_NEEDED): # 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 be None, but # just in case someone creates a new mixin, we check. super_array_finalize = super().__array_finalize__ if super_array_finalize: # pragma: no cover 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): old_shape = self.shape self._mask.shape = shape # Reshape array proper in try/except just in case some broadcasting # or so causes it to fail. try: super(MaskedNDArray, type(self)).shape.__set__(self, shape) except Exception as exc: self._mask.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. if NUMPY_LT_2_0: in_sig, out_sig = np.lib.function_base._parse_gufunc_signature( ufunc.signature.replace(" ", "") ) else: ( 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 out is None: # Can only get here if neither input nor output was masked, but # perhaps where was masked (possible in "not NUMPY_LT_1_25"). # We don't support this. return NotImplemented 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 elif NUMPY_LT_2_0: order = np.core._internal._newnames(self.dtype, order) 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) if not NUMPY_LT_2_0: argsort_kwargs["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 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 """ 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(".") if name == base_class_name: 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 raise AttributeError(f"module '{__name__}' has no attribute '{key}'") astropy-astropy-201cddb/astropy/utils/masked/function_helpers.py000066400000000000000000001364731507226315300254430ustar00rootroot00000000000000# 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 from astropy.units.quantity_helper.function_helpers import FunctionAssigner from astropy.utils.compat import NUMPY_LT_1_24, NUMPY_LT_2_0, NUMPY_LT_2_1, NUMPY_LT_2_2 if NUMPY_LT_2_0: import numpy.core as np_core from numpy.lib.function_base import ( _check_interpolation_as_method, _quantile_is_valid, _ureduce, ) else: import numpy._core as np_core from numpy.lib._function_base_impl import ( _check_interpolation_as_method, _quantile_is_valid, _ureduce, ) # 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 if NUMPY_LT_2_0: # Safe in < 2.0, because it deferred to the method. Overridden in >= 2.0. MASKED_SAFE_FUNCTIONS |= {np.ptp} # Removed in numpy 2.0. Just an alias to vstack. MASKED_SAFE_FUNCTIONS |= {np.row_stack} # noqa: NPY201 # renamed in numpy 2.0 MASKED_SAFE_FUNCTIONS |= {np.trapz} # noqa: NPY201 if not NUMPY_LT_2_0: # new in numpy 2.0 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} | ({np.asfarray} if NUMPY_LT_2_0 else set()) # noqa: NPY201 ) ) 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 if not NUMPY_LT_2_0: @dispatched_function def empty_like( 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 @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 else: @dispatched_function def empty_like(prototype, dtype=None, order="K", subok=True, shape=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 ) 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 ) return unmasked, mask, None @dispatched_function def zeros_like(a, dtype=None, order="K", subok=True, shape=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 ) return unmasked, False, None @dispatched_function def ones_like(a, dtype=None, order="K", subok=True, shape=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 ) return unmasked, False, None @dispatched_function def full_like(a, fill_value, dtype=None, order="K", subok=True, shape=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) 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) @dispatched_function def putmask(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) @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 if NUMPY_LT_2_0: @dispatched_function def msort(a): result = a.copy() result.sort(axis=0) return result, None, None else: # 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 @dispatched_function def concatenate(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 @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) return_type = list if NUMPY_LT_2_0 else tuple 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 = return_type( (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 a = Masked(a) if NUMPY_LT_1_24: r, k = _ureduce( a, func=_masked_median, axis=axis, out=out, overwrite_input=overwrite_input, ) result = (r.reshape(k) if keepdims else r) if out is None else out else: result = _ureduce( 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: if NUMPY_LT_2_0: if "weights" in kwargs: kwargs.pop("weights") result = np.lib.function_base._quantile_unchecked(unmasked, q, **kwargs) else: 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")) 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 kwargs["method"] = _check_interpolation_as_method( kwargs.get("method", "linear"), interpolation, "quantile" ) return a, q, axis, out, kwargs if NUMPY_LT_1_24: @dispatched_function def quantile( a, q, axis=None, out=None, overwrite_input=False, method="linear", keepdims=False, *, interpolation=None, ): a, q, axis, out, kwargs = _preprocess_quantile( a, q, axis, out, overwrite_input=overwrite_input, method=method, keepdims=keepdims, interpolation=interpolation, ) keepdims = kwargs.pop("keepdims", False) r, k = _ureduce(a, func=_masked_quantile, q=q, axis=axis, out=out, **kwargs) result = (r.reshape(q.shape + k) if keepdims else r) if out is None else out return result, None, None elif NUMPY_LT_2_0: @dispatched_function def quantile( a, q, axis=None, out=None, overwrite_input=False, method="linear", keepdims=False, *, interpolation=None, ): a, q, axis, out, kwargs = _preprocess_quantile( a, q, axis, out, overwrite_input=overwrite_input, method=method, keepdims=keepdims, 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, 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 @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): if NUMPY_LT_2_0: from numpy.core.arrayprint import _get_format_function else: from numpy._core.arrayprint import _get_format_function return cls(_get_format_function(data, **options)) def _array2string(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. if NUMPY_LT_2_0: from numpy.core.arrayprint import _formatArray, _leading_trailing else: 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 @dispatched_function def array2string( a, max_line_width=None, precision=None, suppress_small=None, separator=" ", prefix="", style=np._NoValue, formatter=None, threshold=None, edgeitems=None, sign=None, floatmode=None, suffix="", *, legacy=None, ): # Copied from numpy.core.arrayprint, but using _array2string above. if NUMPY_LT_2_1: if NUMPY_LT_2_0: from numpy.core.arrayprint import _format_options else: 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 NUMPY_LT_2_0: from numpy.core.arrayprint import _make_options_dict else: from numpy._core.arrayprint import _make_options_dict overrides = _make_options_dict( precision, threshold, edgeitems, max_line_width, suppress_small, None, None, sign, formatter, 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(a, options, separator, prefix) return result, None, None 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 elif not NUMPY_LT_2_0: 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 _nplibnanfunctions = np.lib.nanfunctions if NUMPY_LT_2_0 else np.lib._nanfunctions_impl for nanfuncname in _nplibnanfunctions.__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_1_24: # "kind" argument introduced in 1.24. @dispatched_function def in1d(ar1, ar2, assume_unique=False, invert=False): mask = _copy_of_mask(ar1).ravel() return _in1d(ar1, ar2, assume_unique, invert), mask, None @dispatched_function def isin(element, test_elements, assume_unique=False, invert=False): element = np.asanyarray(element) result = _in1d(element, test_elements, assume_unique, invert) result.shape = element.shape return result, _copy_of_mask(element), None else: @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) result.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__ ) astropy-astropy-201cddb/astropy/utils/masked/tests/000077500000000000000000000000001507226315300226465ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/utils/masked/tests/__init__.py000066400000000000000000000000001507226315300247450ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/utils/masked/tests/test_containers.py000066400000000000000000000140571507226315300264330ustar00rootroot00000000000000# 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 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_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_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) @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) astropy-astropy-201cddb/astropy/utils/masked/tests/test_dynamic_subclasses.py000066400000000000000000000007421507226315300301350ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst """Test importability of common Masked 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) astropy-astropy-201cddb/astropy/utils/masked/tests/test_function_helpers.py000066400000000000000000002002051507226315300276250ustar00rootroot00000000000000# 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_1_24, NUMPY_LT_1_25, NUMPY_LT_2_0, NUMPY_LT_2_1, NUMPY_LT_2_2, ) 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) if not NUMPY_LT_2_0: 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, list if NUMPY_LT_2_0 else 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) @pytest.mark.skipif(not NUMPY_LT_2_0, reason="np.asfarray is removed in NumPy 2.0") def test_asfarray(self): self.check(np.asfarray) # noqa: NPY201 farray = np.asfarray(a=self.ma) # noqa: NPY201 assert_array_equal(farray, self.ma) if not NUMPY_LT_2_0: 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(prototype=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) @pytest.mark.skipif(not NUMPY_LT_2_0, reason="np.sometrue is removed in NumPy 2.0") @pytest.mark.filterwarnings("ignore:`sometrue` is deprecated as of NumPy 1.25.0") def test_sometrue(self): self.check(np.sometrue, method="any") # noqa: NPY003, NPY201 @pytest.mark.skipif(not NUMPY_LT_2_0, reason="np.alltrue is removed in NumPy 2.0") @pytest.mark.filterwarnings("ignore:`alltrue` is deprecated as of NumPy 1.25.0") def test_alltrue(self): self.check(np.alltrue, method="all") # noqa: NPY003, NPY201 def test_prod(self): self.check(np.prod) @pytest.mark.skipif(not NUMPY_LT_2_0, reason="np.product is removed in NumPy 2.0") @pytest.mark.filterwarnings("ignore:`product` is deprecated as of NumPy 1.25.0") def test_product(self): self.check(np.product, method="prod") # noqa: NPY003, NPY201 def test_cumprod(self): self.check(np.cumprod) @pytest.mark.skipif( not NUMPY_LT_2_0, reason="np.cumproduct is removed in NumPy 2.0" ) @pytest.mark.filterwarnings("ignore:`cumproduct` is deprecated as of NumPy 1.25.0") def test_cumproduct(self): self.check(np.cumproduct, method="cumprod") # noqa: NPY003, NPY201 def test_round(self): self.check(np.round, method="round") @pytest.mark.skipif(not NUMPY_LT_2_0, reason="np.round_ is removed in NumPy 2.0") @pytest.mark.filterwarnings("ignore:`round_` is deprecated as of NumPy 1.25.0") def test_round_(self): self.check(np.round_, method="round") # noqa: NPY003, NPY201 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): 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) if NUMPY_LT_2_0: # Method is removed in numpy 2.0. o3 = self.ma.ptp(**kwargs) assert_array_equal(o3.unmasked, expected.unmasked) assert_array_equal(o3.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. if NUMPY_LT_1_25 and kwargs.get("keepdims", False): # numpy bug gh-22714 prevents using out with keepdims=True. # This is fixed in numpy 1.25. return 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])) if NUMPY_LT_2_0: def test_trapz(self): self.check_trapezoid(np.trapz) # noqa: NPY201 else: 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 if NUMPY_LT_2_0 or function is not np.geomspace: 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) @pytest.mark.skipif(not NUMPY_LT_1_24, reason="np.msort is deprecated") def test_msort(self): o = np.msort(self.ma) expected = np.sort(self.ma, axis=0) 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!) out3 = np.array2string( self.ma, None, None, None, ", ", "", np._NoValue, {"int": hex} ) 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]) @pytest.mark.skipif(NUMPY_LT_2_0, reason="new in numpy 2.0") def check_unique(self, test): for name in test._fields: assert_array_equal(getattr(test, name), getattr(self, name)) @pytest.mark.skipif(NUMPY_LT_2_0, reason="new in numpy 2.0") def test_unique_all(self): test = np.unique_all(self.data) assert len(test) == 4 self.check_unique(test) @pytest.mark.skipif(NUMPY_LT_2_0, reason="new in numpy 2.0") def test_unique_counts(self): test = np.unique_counts(self.data) assert len(test) == 2 self.check_unique(test) @pytest.mark.skipif(NUMPY_LT_2_0, reason="new in numpy 2.0") def test_unique_inverse(self): test = np.unique_inverse(self.data) assert len(test) == 2 self.check_unique(test) @pytest.mark.skipif(NUMPY_LT_2_0, reason="new in numpy 2.0") 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.filterwarnings("ignore:in1d.*deprecated") # not NUMPY_LT_2_0 def test_in1d(self): # Once we require numpy>=2.0, 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(NUMPY_LT_1_24, reason="kind introduced in numpy 1.24") 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", 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 astropy-astropy-201cddb/astropy/utils/masked/tests/test_functions.py000066400000000000000000000640251507226315300262760ustar00rootroot00000000000000# 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.compat.numpycompat import NUMPY_LT_1_25 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) @pytest.mark.xfail(NUMPY_LT_1_25, reason="masked where not supported in numpy<1.25") def test_ufunc_inplace_error_masked_where(self): # Input and output are not masked, but where is. # Note: prior to numpy 1.25, we cannot control this. 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) astropy-astropy-201cddb/astropy/utils/masked/tests/test_masked.py000066400000000000000000001664701507226315300255410ustar00rootroot00000000000000# 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_0, 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) mms2 = mms.view() mms2.shape = mms2.shape[::-1] 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): ma_reshape = self.ma.copy() ma_reshape.shape = (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_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., ——)" if NUMPY_LT_2_0 else "(1.0, ———)") assert str(msa[1]) == str(sa[1]) == ("(3., 4.)" if NUMPY_LT_2_0 else "(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.000, ———)" if NUMPY_LT_2_0 else "(1.0, ———)") assert ( str(msa[1]) == str(sa[1]) == ("(3.000, 4.000)" if NUMPY_LT_2_0 else "(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(): from astropy.visualization.wcsaxes.utils import MATPLOTLIB_LT_3_8 if MATPLOTLIB_LT_3_8: pytest.skip("never worked before matplotlib 3.8") # 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) astropy-astropy-201cddb/astropy/utils/masked/tests/test_table.py000066400000000000000000000147151507226315300253560ustar00rootroot00000000000000# 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]) astropy-astropy-201cddb/astropy/utils/metadata/000077500000000000000000000000001507226315300220205ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/utils/metadata/__init__.py000066400000000000000000000007421507226315300241340ustar00rootroot00000000000000# 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__ astropy-astropy-201cddb/astropy/utils/metadata/core.py000066400000000000000000000167761507226315300233430ustar00rootroot00000000000000# 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}>" astropy-astropy-201cddb/astropy/utils/metadata/exceptions.py000066400000000000000000000004761507226315300245620ustar00rootroot00000000000000# 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 astropy-astropy-201cddb/astropy/utils/metadata/merge.py000066400000000000000000000261201507226315300234720ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst """Metadata merging.""" import warnings from contextlib import contextmanager from copy import deepcopy from functools import wraps import numpy as np from .exceptions import MergeConflictError, MergeConflictWarning from .utils import common_dtype __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) # Wrap ``merge`` classmethod to catch any exception and re-raise as # MergeConflictError. if isinstance((merge_ := members.get("merge")), classmethod): orig_merge = merge_.__func__ @wraps(orig_merge) def merge(cls, left, right): try: return orig_merge(cls, left, right) except Exception as err: raise MergeConflictError(err) cls.merge = classmethod(merge) # 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)) return cls 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) common_dtype([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 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, val in right.items(): # If no conflict then insert val into out dict and continue if key not in out: out[key] = deepcopy(val) continue # There is a conflict that must be resolved if _both_isinstance(left[key], right[key], dict): out[key] = merge( left[key], right[key], merge_func, metadata_conflicts=metadata_conflicts ) else: try: if merge_func is None: for left_type, right_type, merge_cls in MERGE_STRATEGIES: if not merge_cls.enabled: continue if isinstance(left[key], left_type) and isinstance( right[key], right_type ): out[key] = merge_cls.merge(left[key], right[key]) break else: raise MergeConflictError else: out[key] = merge_func(left[key], right[key]) except MergeConflictError: # Pick the metadata item that is not None, or they are both not # None, then if they are equal, there is no conflict, and if # they are different, there is a conflict and we pick the one # on the right (or raise an error). if left[key] is None: # This may not seem necessary since out[key] gets set to # right[key], but not all objects support != which is # needed for one of the if clauses. out[key] = right[key] elif right[key] is None: out[key] = left[key] elif _not_equal(left[key], right[key]): if metadata_conflicts == "warn": warnings.warn( warn_str_func(key, left[key], right[key]), MergeConflictWarning, ) elif metadata_conflicts == "error": raise MergeConflictError( error_str_func(key, left[key], right[key]) ) elif metadata_conflicts != "silent": raise ValueError( "metadata_conflicts argument must be one " 'of "silent", "warn", or "error"' ) out[key] = right[key] else: out[key] = right[key] return out astropy-astropy-201cddb/astropy/utils/metadata/tests/000077500000000000000000000000001507226315300231625ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/utils/metadata/tests/__init__.py000066400000000000000000000000001507226315300252610ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/utils/metadata/tests/test_metadata.py000066400000000000000000000161611507226315300263600ustar00rootroot00000000000000from 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, ) 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_common_dtype_string(): u3 = np.array(["123"]) u4 = np.array(["1234"]) b3 = np.array([b"123"]) b5 = np.array([b"12345"]) assert common_dtype([u3, u4]).endswith("U4") assert common_dtype([b5, u4]).endswith("U5") assert common_dtype([b3, b5]).endswith("S5") def test_common_dtype_basic(): i8 = np.array(1, dtype=np.int64) f8 = np.array(1, dtype=np.float64) u3 = np.array("123") with pytest.raises(MergeConflictError): common_dtype([i8, u3]) assert common_dtype([i8, i8]).endswith("i8") assert common_dtype([i8, f8]).endswith("f8") astropy-astropy-201cddb/astropy/utils/metadata/utils.py000066400000000000000000000035671507226315300235450ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst """This module contains helper functions handling metadata.""" import numpy as np from astropy.utils.misc import dtype_bytes_or_chars 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) """ np_types = (np.bool_, np.object_, np.number, np.character, np.void) uniq_types = { tuple(issubclass(dtype(arr).type, np_type) for np_type in np_types) for arr in arrs } if len(uniq_types) > 1: # Embed into the exception the actual list of incompatible types. incompat_types = [dtype(arr).name for arr in arrs] tme = MergeConflictError(f"Arrays have incompatible types {incompat_types}") tme._incompat_types = incompat_types raise tme arrs = [np.empty(1, dtype=dtype(arr)) for arr in arrs] # For string-type arrays need to explicitly fill in non-zero # values or the final arr_common = .. step is unpredictable. for i, arr in enumerate(arrs): if arr.dtype.kind in ("S", "U"): arrs[i] = [ ("0" if arr.dtype.kind == "U" else b"0") * dtype_bytes_or_chars(arr.dtype) ] arr_common = np.array([arr[0] for arr in arrs]) return ( arr_common.dtype.str if arr_common.dtype.names is None else arr_common.dtype.descr ) astropy-astropy-201cddb/astropy/utils/misc.py000066400000000000000000000410241507226315300215460ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst """ A "grab bag" of relatively small general-purpose utilities that don't have a clear module/package to live in. """ import contextlib import difflib import inspect import json import locale import os import re import sys import threading import traceback import unicodedata from collections import defaultdict from collections.abc import Callable, Iterable from contextlib import contextmanager from itertools import chain from astropy.utils import deprecated __all__ = [ "JsonCustomEncoder", "NumpyRNGContext", "dtype_bytes_or_chars", "find_api_page", "format_exception", "indent", "is_path_hidden", "isiterable", "silence", "walk_skip_hidden", ] NOT_OVERWRITING_MSG = ( "File {} already exists. If you mean to replace it " 'then use the argument "overwrite=True".' ) # A useful regex for tests. _NOT_OVERWRITING_MSG_MATCH = ( r"File .* already exists\. If you mean to " r"replace it then use the argument " r'"overwrite=True"\.' ) def isiterable(obj): """Returns `True` if the given object is iterable.""" try: iter(obj) return True except TypeError: return False @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" return indented class _DummyFile: """A noop writeable object.""" def write(self, s): pass @contextlib.contextmanager def silence(): """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. """ tb = traceback.extract_tb(sys.exc_info()[2], limit=1) if len(tb) > 0: filename, lineno, func, text = tb[0] else: filename = lineno = func = text = "" return msg.format( *args, filename=filename, lineno=lineno, func=func, text=text, **kwargs ) 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 ---------- seed : int The value to use to seed the numpy RNG Examples -------- A typical use case might be:: with NumpyRNGContext(): from numpy import random randarr = random.randn(100) ... run your test using `randarr` ... #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. """ def __init__(self, seed): self.seed = seed def __enter__(self): from numpy import random self.startstate = random.get_state() random.seed(self.seed) def __exit__(self, exc_type, exc_value, traceback): from numpy import random random.set_state(self.startstate) def find_api_page(obj, version=None, openinbrowser=True, timeout=None): """ 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 ---------- 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 ------- url : str The loaded URL Raises ------ ValueError If the documentation can't be found """ import webbrowser from zlib import decompress from astropy.utils.data import get_readable_fileobj 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__ 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 # _has_hidden_attribute() can be deleted together with deprecated is_path_hidden() and # walk_skip_hidden(). if sys.platform == "win32": import ctypes def _has_hidden_attribute(filepath): """ Returns True if the given filepath has the hidden attribute on MS-Windows. Based on a post here: https://stackoverflow.com/questions/284115/cross-platform-hidden-file-detection. """ if isinstance(filepath, bytes): filepath = filepath.decode(sys.getfilesystemencoding()) try: attrs = ctypes.windll.kernel32.GetFileAttributesW(filepath) result = bool(attrs & 2) and attrs != -1 except AttributeError: result = False return result else: def _has_hidden_attribute(filepath): return False @deprecated(since="6.0") def is_path_hidden(filepath): """ Determines if a given file or directory is hidden. Parameters ---------- filepath : str The path to a file or directory Returns ------- hidden : bool Returns `True` if the file is hidden """ name = os.path.basename(os.path.abspath(filepath)) if isinstance(name, bytes): is_dotted = name.startswith(b".") else: is_dotted = name.startswith(".") return is_dotted or _has_hidden_attribute(filepath) @deprecated(since="6.0") def walk_skip_hidden(top, onerror=None, followlinks=False): """ A wrapper for `os.walk` that skips hidden files and directories. This function does not have the parameter ``topdown`` from `os.walk`: the directories must always be recursed top-down when using this function. See Also -------- os.walk : For a description of the parameters """ for root, dirs, files in os.walk( top, topdown=True, onerror=onerror, followlinks=followlinks ): # These lists must be updated in-place so os.walk will skip # hidden directories dirs[:] = [d for d in dirs if not is_path_hidden(d)] files[:] = [f for f in files if not is_path_hidden(f)] yield root, dirs, files class JsonCustomEncoder(json.JSONEncoder): """Support for data types that JSON default encoder does not do. This includes: * Numpy array or number * Complex number * Set * Bytes * astropy.UnitBase * astropy.Quantity Examples -------- >>> import json >>> import numpy as np >>> from astropy.utils.misc import JsonCustomEncoder >>> json.dumps(np.arange(3), cls=JsonCustomEncoder) '[0, 1, 2]' """ def default(self, obj): import numpy as np 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) def strip_accents(s: str) -> str: """ Remove accents from a Unicode string. This helps with matching "ÃĨngstrÃļm" to "angstrom", for example. """ 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. Parameters ---------- s : str candidates : iterable of str Note that str itself does not cause an error, but the output might not be what was expected. n : int The maximum number of results to include. See `difflib.get_close_matches`. 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`. 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. 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 = threading.Lock() @contextmanager def _set_locale(name): """ 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. This code taken from https://stackoverflow.com/questions/18593661/how-do-i-strftime-a-date-object-in-a-different-locale. Parameters ---------- name : str Locale name, e.g. "C" or "fr_FR". """ name = str(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) def dtype_bytes_or_chars(dtype): """ 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) astropy-astropy-201cddb/astropy/utils/setup_package.py000066400000000000000000000005531507226315300234300ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst from os.path import dirname, join, relpath from setuptools import Extension ASTROPY_UTILS_ROOT = dirname(__file__) def get_extensions(): return [ Extension( "astropy.utils._compiler", [relpath(join(ASTROPY_UTILS_ROOT, "src", "compiler.c"))], ) ] astropy-astropy-201cddb/astropy/utils/shapes.py000066400000000000000000000421251507226315300221010ustar00rootroot00000000000000# 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 from numpy.typing import NDArray from astropy.utils.compat import NUMPY_LT_2_0 from astropy.utils.decorators import deprecated if NUMPY_LT_2_0: import numpy.core as np_core from numpy.core.multiarray import normalize_axis_index else: import numpy._core as np_core from numpy.lib.array_utils import normalize_axis_index __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: return NotImplementedError("cannot pass 'out' argument to 'take.") return self._apply("take", indices, axis=axis, mode=mode) 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, } # 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) # 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) astropy-astropy-201cddb/astropy/utils/src/000077500000000000000000000000001507226315300210275ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/utils/src/compiler.c000066400000000000000000000051651507226315300230140ustar00rootroot00000000000000#include /*************************************************************************** * Macros for determining the compiler version. * * These are borrowed from boost, and majorly abridged to include only * the compilers we care about. ***************************************************************************/ #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 }; static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, "_compiler", NULL, sizeof(struct module_state), NULL, NULL, NULL, NULL, NULL }; PyMODINIT_FUNC PyInit__compiler(void) { PyObject* m; m = PyModule_Create(&moduledef); if (m == NULL) return NULL; PyModule_AddStringConstant(m, "compiler", COMPILER); return m; } astropy-astropy-201cddb/astropy/utils/state.py000066400000000000000000000041171507226315300217350ustar00rootroot00000000000000""" 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 astropy-astropy-201cddb/astropy/utils/system_info.py000066400000000000000000000036301507226315300231530ustar00rootroot00000000000000r""" 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)) astropy-astropy-201cddb/astropy/utils/tests/000077500000000000000000000000001507226315300214025ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/utils/tests/__init__.py000066400000000000000000000000001507226315300235010ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/utils/tests/data/000077500000000000000000000000001507226315300223135ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/utils/tests/data/.hidden_file.txt000066400000000000000000000000441507226315300253620ustar00rootroot00000000000000This is a deliberately hidden file. astropy-astropy-201cddb/astropy/utils/tests/data/alias.cfg000066400000000000000000000000521507226315300240620ustar00rootroot00000000000000[cosmology.core] default_cosmology = WMAP7astropy-astropy-201cddb/astropy/utils/tests/data/dataurl/000077500000000000000000000000001507226315300237475ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/utils/tests/data/dataurl/index.html000066400000000000000000000000001507226315300257320ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/utils/tests/data/dataurl_mirror/000077500000000000000000000000001507226315300253415ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/utils/tests/data/dataurl_mirror/index.html000066400000000000000000000000001507226315300273240ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/utils/tests/data/local.dat000066400000000000000000000001051507226315300240730ustar00rootroot00000000000000This file is used in the test_local_data_* testing functions CONTENT astropy-astropy-201cddb/astropy/utils/tests/data/local.dat.Z000066400000000000000000000001111507226315300243000ustar00rootroot00000000000000TФ™ÂL6e@ QgN2 Ũ€ ƒ&!2sč|aķfL6_ȄĄ拊‰é¤qsĻ`7cTžq3§‚!OœP)ĸĶ&astropy-astropy-201cddb/astropy/utils/tests/data/local.dat.bz2000066400000000000000000000001401507226315300245660ustar00rootroot00000000000000BZh91AY&SYa”fc W€@ „¯åŽ Hj™yCzBx ‰j!i‚ÁÅWÃr,Ī#X–Ë$g á+¨ãqÍL÷OŒ*TÉĀ„ĻĸoÅܑN$e˜Āastropy-astropy-201cddb/astropy/utils/tests/data/local.dat.gz000066400000000000000000000001361507226315300245160ustar00rootroot00000000000000‹ŒpPlocal.dat ÉČ,VHËĖIUŌĨÅŠ) ™y %Š %ŠÅ%ņ9ųɉ9ņ)‰%‰ņZ`‘Ėŧt…´Ōŧä’Ėüŧb.gŋWŋ.%/+gEastropy-astropy-201cddb/astropy/utils/tests/data/local.dat.xz000066400000000000000000000002001507226315300245270ustar00rootroot00000000000000ũ7zXZæÖ´F!t/åŖāDB]* 'd°8sʋ Ņp@BĻ2N™I4ܯš´ŌDx˜wä˙EF•ˇ*—ųęæ˙ē BEO/ŋА÷€’le•ĩîŽÔ^Eîpļķ}YZastropy-astropy-201cddb/astropy/utils/tests/data/test_package/000077500000000000000000000000001507226315300247455ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/utils/tests/data/test_package/__init__.py000066400000000000000000000002011507226315300270470ustar00rootroot00000000000000from astropy.utils.data import get_pkg_data_filename def get_data_filename(): return get_pkg_data_filename("data/foo.txt") astropy-astropy-201cddb/astropy/utils/tests/data/test_package/data/000077500000000000000000000000001507226315300256565ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/utils/tests/data/test_package/data/foo.txt000066400000000000000000000000001507226315300271700ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/utils/tests/data/unicode.txt000066400000000000000000000000421507226315300244760ustar00rootroot00000000000000האסטרונומי פיי×Ēון astropy-astropy-201cddb/astropy/utils/tests/data/unicode.txt.Z000066400000000000000000000000441507226315300247100ustar00rootroot00000000000000×(]ƒt-Ô5L×P]Ģt ÔÂkžŽeq”Ä‹Ē~j astropy-astropy-201cddb/astropy/utils/tests/data/unicode.txt.bz2000066400000000000000000000000741507226315300251770ustar00rootroot00000000000000BZh91AY&SYÛYe Đ2@FaäP€ !¨2íPĻYKĀlÛŖd|]ÉáB@_me”astropy-astropy-201cddb/astropy/utils/tests/data/unicode.txt.gz000066400000000000000000000000641507226315300251210ustar00rootroot00000000000000‹yaf˙ģ>åú„ë ¯Ī¸žâúÔë €xŪõ™ ח\Ÿ „Ģ€Üų\Vv;Ō"astropy-astropy-201cddb/astropy/utils/tests/data/unicode.txt.xz000066400000000000000000000001341507226315300251400ustar00rootroot00000000000000ũ7zXZæÖ´F!t/åŖ!האסטרונומי פיי×Ēון \â錓~ž:"ļ*OĐļķ}YZastropy-astropy-201cddb/astropy/utils/tests/test_codegen.py000066400000000000000000000024511507226315300244210ustar00rootroot00000000000000# 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") astropy-astropy-201cddb/astropy/utils/tests/test_collections.py000066400000000000000000000033111507226315300253270ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst import pytest from astropy.utils import collections def test_homogeneous_list(): l = collections.HomogeneousList(int) with pytest.raises(TypeError): l.append(5.0) def test_homogeneous_list2(): l = collections.HomogeneousList(int) 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] def test_homogeneous_list5(): 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] astropy-astropy-201cddb/astropy/utils/tests/test_console.py000066400000000000000000000122531507226315300244600ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst import io import sys 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 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" assert 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(): # This stuff is hard to test, at least smoke test it console.color_print("foo", "green") console.color_print("foo", "green", "bar", "red") def test_color_print2(): # Test that this automatically detects that io.StringIO is # not a tty stream = io.StringIO() console.color_print("foo", "green", file=stream) assert stream.getvalue() == "foo\n" stream = io.StringIO() console.color_print("foo", "green", "bar", "red", "baz", file=stream) assert stream.getvalue() == "foobarbaz\n" @pytest.mark.skipif(sys.platform.startswith("win"), reason="Cannot test on Windows") def test_color_print3(): # Test that this thinks the FakeTTY is a tty and applies colors. stream = FakeTTY() console.color_print("foo", "green", file=stream) assert stream.getvalue() == "\x1b[0;32mfoo\x1b[0m\n" stream = FakeTTY() console.color_print("foo", "green", "bar", "red", "baz", file=stream) assert stream.getvalue() == "\x1b[0;32mfoo\x1b[0m\x1b[0;31mbar\x1b[0mbaz\n" def test_color_print_unicode(): 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 _ in range(50): bar.update() def test_progress_bar2(): for _ in console.ProgressBar(range(50)): pass def test_progress_bar3(): 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): assert pytest.raises(u.UnitConversionError, console.human_file_size, size) astropy-astropy-201cddb/astropy/utils/tests/test_data.py000066400000000000000000002401611507226315300237300ustar00rootroot00000000000000# 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 tempfile import NamedTemporaryFile, TemporaryDirectory import pytest import astropy.utils.data from astropy import units as _u # u is taken from astropy.config import paths 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): 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") def test_temp_cache(tmp_path): dldir0 = _get_download_cache_loc() check_download_cache() with paths.set_temp_cache(tmp_path): dldir1 = _get_download_cache_loc() check_download_cache() assert dldir1 != dldir0 dldir2 = _get_download_cache_loc() check_download_cache() assert dldir2 != dldir1 assert dldir2 == dldir0 # Check that things are okay even if we exit via an exception class Special(Exception): pass try: with paths.set_temp_cache(tmp_path): dldir3 = _get_download_cache_loc() check_download_cache() assert dldir3 == dldir1 raise Special except Special: pass dldir4 = _get_download_cache_loc() check_download_cache() assert dldir4 != dldir3 assert dldir4 == dldir0 @pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") @pytest.mark.parametrize("parallel", [False, True]) def test_download_with_sources_and_bogus_original( valid_urls, invalid_urls, temp_cache, parallel ): # 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 parallel: rs = download_files_in_parallel( [u for (u, c, c_bad) in urls], cache=True, sources=sources ) else: rs = [ download_file(u, cache=True, sources=sources.get(u)) for (u, c, c_bad) in urls ] 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(lambda u: slurp_url(u), [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) 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() 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(tmp_path, valid_urls, method): urls = [] # tmp_path is shared between many tests, and that can cause weird # interactions if we set the temporary cache too directly with paths.set_temp_cache(tmp_path): 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() assert not url_set.intersection(get_cached_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) 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") 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 config and cache directories are not searched monkeypatch.setenv("XDG_CONFIG_HOME", "foo") monkeypatch.delenv("XDG_CONFIG_HOME") monkeypatch.setenv("XDG_CACHE_HOME", "bar") monkeypatch.delenv("XDG_CACHE_HOME") monkeypatch.setattr(paths.set_temp_config, "_temp_path", None) monkeypatch.setattr(paths.set_temp_cache, "_temp_path", None) # 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() monkeypatch.setattr(paths._SetTempPath, "_find_or_create_root_dir", osraiser) # make sure the config dir search fails with pytest.raises(OSError): paths.get_cache_dir(rootname="astropy") with pytest.raises(OSError): 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) partial_warn_msgs = ["remote data cache could not be accessed", "temporary file"] if n_warns == 4: partial_warn_msgs.extend(["socket", "socket"]) for wl in warning_lines: cur_w = str(wl).lower() for i, partial_msg in enumerate(partial_warn_msgs): if partial_msg in cur_w: del partial_warn_msgs[i] break assert len(partial_warn_msgs) == 0, ( f"Got some unexpected warnings: {partial_warn_msgs}" ) assert n_warns in (2, 4), f"Expected 2 or 4 warnings, got {n_warns}" assert os.path.isfile(fnout) # clearing the cache should be a no-up that doesn't affect fnout with pytest.warns(CacheMissingWarning) as record: clear_download_cache(TESTURL) assert len(record) == 2 assert ( record[0].message.args[0] == "Remote data cache could not be accessed due to OSError" ) assert "Not clearing data cache - cache inaccessible" in record[1].message.args[0] 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() 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) 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 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): fn = str(tmp_path / "file") with open(fn, "w") as f: f.write("content") assert os.path.exists(fn) with pytest.raises(RuntimeError): 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 = os.path.abspath(os.path.join(dldir, "bogus")) 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 = download_file(u, cache=True) bf = os.path.abspath(os.path.join(os.path.dirname(f), "bogus")) 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_cleanup(temp_cache, valid_urls): u, c = next(valid_urls) fn = download_file(u, cache=True) dldir = _get_download_cache_loc() bf1 = os.path.abspath(os.path.join(dldir, "bogus1")) with open(bf1, "w") as f: f.write("bogus file that exists") bf2 = os.path.abspath(os.path.join(os.path.dirname(fn), "bogus2")) with open(bf2, "w") as f: f.write("other bogus file that exists") bf3 = os.path.abspath(os.path.join(dldir, "contents")) with open(bf3, "w") as f: f.write("awkwardly-named bogus file that exists") u2, c2 = next(valid_urls) f2 = download_file(u, cache=True) os.unlink(f2) bf4 = os.path.dirname(f2) 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(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, 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 each of the steps in the path is wrong somehow. """ def check_quietly_ignores_bogus_cache(): """We want a broken cache to produce a warning but then astropy should act like there isn't a cache. """ with pytest.warns(CacheMissingWarning): assert not get_cached_urls() with pytest.warns(CacheMissingWarning): assert not is_url_in_cache("http://www.example.com/") with pytest.warns(CacheMissingWarning): assert not cache_contents() with pytest.warns(CacheMissingWarning): u, c = next(valid_urls) 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 pytest.warns(CacheMissingWarning): assert not is_url_in_cache(u) with pytest.warns(CacheMissingWarning): with pytest.raises(OSError): check_download_cache() dldir = _get_download_cache_loc() # 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" ct = "contents\n" os.mkdir(fn) with paths.set_temp_cache(fn): shutil.rmtree(fn) with open(fn, "w") as f: f.write(ct) with pytest.raises(OSError): paths.get_cache_dir() check_quietly_ignores_bogus_cache() assert dldir == _get_download_cache_loc() assert get_file_contents(fn) == ct, "File should not be harmed." # See what happens when set_temp_cache is pointed at a file with pytest.raises(OSError): with paths.set_temp_cache(fn): pass assert dldir == _get_download_cache_loc() assert get_file_contents(str(fn)) == ct # Now the cache directory is normal but the subdirectory it wants # to make is a file cd = tmp_path / "astropy" with open(cd, "w") as f: f.write(ct) with paths.set_temp_cache(tmp_path): check_quietly_ignores_bogus_cache() assert dldir == _get_download_cache_loc() assert get_file_contents(cd) == ct os.remove(cd) # Ditto one level deeper os.makedirs(cd) cd = tmp_path / "astropy" / "download" with open(cd, "w") as f: f.write(ct) with paths.set_temp_cache(tmp_path): check_quietly_ignores_bogus_cache() assert dldir == _get_download_cache_loc() assert get_file_contents(cd) == ct os.remove(cd) # Ditto another level deeper os.makedirs(cd) cd = tmp_path / "astropy" / "download" / "url" with open(cd, "w") as f: f.write(ct) with paths.set_temp_cache(tmp_path): check_quietly_ignores_bogus_cache() assert dldir == _get_download_cache_loc() assert get_file_contents(cd) == ct os.remove(cd) @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") def test_pkgname_isolation(temp_cache, valid_urls): a = "bogus_cache_name" assert not get_cached_urls() assert not get_cached_urls(pkgname=a) 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 assert not get_cached_urls(pkgname=a) clear_download_cache() assert not get_cached_urls() assert not get_cached_urls(pkgname=a) @pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") def test_transport_cache_via_zip(temp_cache, valid_urls): a = "bogus_cache_name" assert not get_cached_urls() assert not get_cached_urls(pkgname=a) 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 = "bogus_cache_name" assert not get_cached_urls() assert not get_cached_urls(pkgname=a) 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 astropy-astropy-201cddb/astropy/utils/tests/test_data_info.py000066400000000000000000000060671507226315300247500ustar00rootroot00000000000000# 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 astropy-astropy-201cddb/astropy/utils/tests/test_introspection.py000066400000000000000000000124731507226315300257220ustar00rootroot00000000000000# 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 astropy-astropy-201cddb/astropy/utils/tests/test_misc.py000066400000000000000000000132401507226315300237460ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst import json import locale import os 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 data, misc from astropy.utils.exceptions import AstropyDeprecationWarning def test_isiterable(): assert misc.isiterable(2) is False assert misc.isiterable([2]) is True assert misc.isiterable([1, 2, 3]) is True assert misc.isiterable(np.array(2)) is False assert misc.isiterable(np.array([1, 2, 3])) is True @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_is_path_hidden_deprecation(): with pytest.warns( AstropyDeprecationWarning, match="^The is_path_hidden function is deprecated" ): misc.is_path_hidden("data") # This is the only test that uses astropy/utils/tests/data/.hidden_file.txt def test_skip_hidden(): path = data.get_pkg_data_path("data") for _, _, files in os.walk(path): assert ".hidden_file.txt" in files assert "local.dat" in files # break after the first level since the data dir contains some other # subdirectories that don't have these files break with pytest.warns( AstropyDeprecationWarning, match="^The .*_hidden function is deprecated" ): for _, _, files in misc.walk_skip_hidden(path): assert ".hidden_file.txt" not in files assert "local.dat" in files break 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") astropy-astropy-201cddb/astropy/utils/tests/test_parsing.py000066400000000000000000000060071507226315300244610ustar00rootroot00000000000000# 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 astropy-astropy-201cddb/astropy/utils/tests/test_progress_bar_func.py000066400000000000000000000011111507226315300265100ustar00rootroot00000000000000import 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 astropy-astropy-201cddb/astropy/utils/tests/test_shapes.py000066400000000000000000000043461507226315300243050ustar00rootroot00000000000000# 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 check_broadcast, simplify_basic_index, unbroadcast 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) 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) astropy-astropy-201cddb/astropy/utils/tests/test_state.py000066400000000000000000000011311507226315300241270ustar00rootroot00000000000000# 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 astropy-astropy-201cddb/astropy/utils/tests/test_xml.py000066400000000000000000000062431507226315300236200ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst import io import pytest from astropy.utils.compat.optional_deps import HAS_BLEACH from astropy.utils.xml import check, unescaper, writer def test_writer(): fh = io.StringIO() w = writer.XMLWriter(fh) with w.tag("html"): with w.tag("body"): w.data("This is the content") w.comment("comment") value = "".join(fh.getvalue().split()) assert value == "Thisisthecontent" def test_check_id(): assert check.check_id("Fof32") assert check.check_id("_Fof32") assert not check.check_id("32Fof") def test_fix_id(): assert check.fix_id("Fof32") == "Fof32" assert check.fix_id("@#f") == "___f" def test_check_token(): assert check.check_token("token") assert not check.check_token("token\rtoken") def test_check_mime_content_type(): assert check.check_mime_content_type("image/jpeg") assert not check.check_mime_content_type("image") 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" ) astropy-astropy-201cddb/astropy/utils/xml/000077500000000000000000000000001507226315300210405ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/utils/xml/__init__.py000066400000000000000000000000001507226315300231370ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/utils/xml/check.py000066400000000000000000000042151507226315300224710ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst """ A collection of functions for checking various XML-related strings for standards compliance. """ import re import urllib.parse def check_id(ID): """ Returns `True` if *ID* is a valid XML ID. """ return re.match(r"^[A-Za-z_][A-Za-z0-9_\.\-]*$", ID) is not None def fix_id(ID): """ Given an arbitrary string, create one that can be used as an xml id. This is rather simplistic at the moment, since it just replaces non-valid characters with underscores. """ if re.match(r"^[A-Za-z_][A-Za-z0-9_\.\-]*$", 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:] ) return corrected return "" _token_regex = r"(?![\r\l\t ])[^\r\l\t]*(?![\r\l\t ])" 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"[^\r\n\t ]?([^\r\n\t ]| [^\r\n\t ])*[^\r\n\t ]?$", token) is not None ) 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 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 ): return False try: urllib.parse.urlparse(uri) except Exception: return False return True astropy-astropy-201cddb/astropy/utils/xml/iterparser.py000066400000000000000000000134331507226315300235760ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst """ This module includes a fast iterator-based XML parser. """ # STDLIB import contextlib import io import sys # ASTROPY from astropy.utils import data from ._iterparser import IterParser as _fast_iterparse __all__ = ["get_xml_encoding", "get_xml_iterator", "xml_readlines"] @contextlib.contextmanager def _convert_to_fd_or_read_function(fd): """ Returns a function suitable for streaming input, or a file object. This function is only useful if passing off to C code where: - If it's a real file object, we want to use it as a real C file object to avoid the Python overhead. - 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. 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. 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. Returns ------- fd : context-dependent See above. """ if callable(fd): yield fd return with data.get_readable_fileobj(fd, encoding="binary") as new_fd: if sys.platform.startswith("win"): yield new_fd.read else: if isinstance(new_fd, io.FileIO): yield new_fd else: yield new_fd.read def _slow_iterparse(fd, buffersize=2**10): from xml.parsers import expat if not callable(fd): read = fd.read else: read = fd queue = [] text = [] def start(name, attr): queue.append( (True, name, attr, (parser.CurrentLineNumber, parser.CurrentColumnNumber)) ) del text[:] def end(name): queue.append( ( False, name, "".join(text).strip(), (parser.CurrentLineNumber, parser.CurrentColumnNumber), ) ) parser = expat.ParserCreate() parser.specified_attributes = True parser.StartElementHandler = start parser.EndElementHandler = end parser.CharacterDataHandler = text.append Parse = parser.Parse data = read(buffersize) while data: Parse(data, False) yield from queue del queue[:] data = read(buffersize) Parse("", True) yield from queue @contextlib.contextmanager 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``. Parameters ---------- 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 ------- parts : iterator The iterator returns 4-tuples (*start*, *tag*, *data*, *pos*): - *start*: when `True` is a start element event, otherwise an end element event. - *tag*: The name of the element - *data*: Depends on the value of *event*: - if *start* == `True`, data is a dictionary of attributes - if *start* == `False`, data is a string containing the text content of the element - *pos*: Tuple (*line*, *col*) indicating the source of the event. """ with _convert_to_fd_or_read_function(source) as fd: if _debug_python_based_parser: context = _slow_iterparse(fd) else: context = _fast_iterparse(fd) yield iter(context) def get_xml_encoding(source): """ Determine the encoding of an XML file by reading its header. Parameters ---------- 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 = next(iterator) if not start or tag != "xml": raise OSError("Invalid XML file") # The XML spec says that no encoding === utf-8 return data.get("encoding") or "utf-8" def xml_readlines(source): """ Get the lines from a given XML file. Correctly determines the encoding and always returns unicode. Parameters ---------- 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 ------- lines : list of unicode """ encoding = get_xml_encoding(source) with data.get_readable_fileobj(source, encoding=encoding) as input: input.seek(0) xml_lines = input.readlines() return xml_lines astropy-astropy-201cddb/astropy/utils/xml/setup_package.py000066400000000000000000000031701507226315300242260ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst import os import sys from collections import defaultdict from os.path import join from setuptools import Extension from extension_helpers import pkg_config def get_extensions(build_type="release"): XML_DIR = "astropy/utils/xml/src" cfg = defaultdict(list) cfg["sources"] = [join(XML_DIR, "iterparse.c")] 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" 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. 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: 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)] astropy-astropy-201cddb/astropy/utils/xml/src/000077500000000000000000000000001507226315300216275ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/utils/xml/src/.gitignore000066400000000000000000000000051507226315300236120ustar00rootroot00000000000000!*.c astropy-astropy-201cddb/astropy/utils/xml/src/expat_config.h000066400000000000000000000073741507226315300244610ustar00rootroot00000000000000/* expat_config.h. Generated from expat_config.h.in by configure. */ /* expat_config.h.in. Generated from configure.ac by autoheader. */ /* Define if building universal (internal helper macro) */ /* #undef AC_APPLE_UNIVERSAL_BUILD */ /* 1234 = LILENDIAN, 4321 = BIGENDIAN */ #define BYTEORDER 1234 /* 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 to 1 if you have the header file. */ #define HAVE_DLFCN_H 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 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 `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 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_STDLIB_H 1 /* Define to 1 if you have the header file. */ #define HAVE_STRINGS_H 1 /* Define to 1 if you have the header file. */ #define HAVE_STRING_H 1 /* 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 /* Define to 1 if you have the header file. */ #define HAVE_UNISTD_H 1 /* Define to the sub-directory where libtool stores uninstalled libraries. */ #define LT_OBJDIR ".libs/" /* 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 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.2.9" /* 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.2.9" /* Define to 1 if you have the ANSI C header files. */ #define STDC_HEADERS 1 /* Version number of package */ #define VERSION "2.2.9" /* 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. */ #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 1 /* Define to make XML Namespaces functionality available. */ #define XML_NS 1 /* Define to empty if `const' does not conform to ANSI C. */ /* #undef const */ /* Define to `long int' if does not define. */ /* #undef off_t */ /* Define to `unsigned int' if does not define. */ /* #undef size_t */ astropy-astropy-201cddb/astropy/utils/xml/src/iterparse.c000066400000000000000000001147401507226315300240000ustar00rootroot00000000000000/****************************************************************************** * C extension code for astropy.utils.xml.iterparse * * Everything in this file could be implemented in Python and * is included for performance reasons only. * * It has two main parts: * * - An IterParser object which parses an XML file using the expat * library, feeding expat events through a Python iterator. It is * 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.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 occurring element in a VO file, * "TD". * * - Two functions, escape_xml() and escape_xml_cdata() that escape * XML much faster than the alternatives in the Python standard * library. ******************************************************************************/ #include #include #include "structmember.h" #include "expat.h" /****************************************************************************** * Convenience macros and functions ******************************************************************************/ #ifdef _MSC_VER #define inline #endif #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) { /* 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; } /****************************************************************************** * Python version compatibility macros ******************************************************************************/ #if BYTEORDER == 1234 # define TD_AS_INT 0x00004454 # define TD_AS_INT_MASK 0x00ffffff #else # 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 */ /* File-like object reading */ 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 */ /* XML event 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 dequeuing events */ PyObject* error_type; PyObject* error_value; PyObject* error_traceback; /* Store the position for any XML exceptions that may be returned later */ unsigned long last_line; unsigned long last_col; /* "Constants" for efficiency */ 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 ******************************************************************************/ /** * 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) { Py_ssize_t n = req_size; char *new_mem = NULL; if (req_size < self->text_alloc) { return 0; } /* Calculate the next-highest power of two */ n = next_power_of_2(n); if (n < req_size) { PyErr_SetString(PyExc_MemoryError, "Out of memory for XML text."); return -1; } new_mem = malloc(n * sizeof(XML_Char)); if (new_mem == NULL) { PyErr_SetString(PyExc_MemoryError, "Out of memory for XML text."); return -1; } memcpy(new_mem, self->text, (size_t)(self->text_size + 1) * sizeof(XML_Char)); free(self->text); self->text = new_mem; self->text_alloc = n; return 0; } #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. * * For the first chunk of text, all whitespace characters before the * 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) { Py_ssize_t new_size; if (len == 0) { return 0; } /* If this is the first chunk, handle whitespace */ if (self->text_size == 0) { while (len && IS_WHITESPACE(*data)) { ++data; --len; } } /* Grow text buffer if necessary */ new_size = self->text_size + len; if (text_realloc(self, new_size + 1)) { return -1; } 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; return 0; } /* * Erase all content from the text buffer. */ static void text_clear(IterParser *self) { self->text[0] = (XML_Char)0; self->text_size = 0; } /****************************************************************************** * XML event handling ******************************************************************************/ /* * Make a "position tuple" from the current expat parser state. This * is used to communicate the position of the parser within the file * to the Python side for generation of meaningful error messages. * * It is of the form (line, col), where line and col are both PyInts. */ static inline PyObject* make_pos(const IterParser *self) { return Py_BuildValue( "(nn)", (size_t)self->last_line, (size_t)self->last_col); } /* * Removes the namespace from an element or attribute name, that is, * remove everything before the first colon. The namespace is not * needed to parse standards-compliant VOTable files. * * The returned pointer is an internal pointer to the buffer passed * in. */ static const XML_Char * remove_namespace(const XML_Char *name) { const XML_Char* name_start = NULL; /* If there is a namespace specifier, just chop it off */ for (name_start = name; *name_start != '\0'; ++name_start) { if (*name_start == ':') { break; } } if (*name_start == ':') { ++name_start; } else { name_start = name; } return name_start; } /* * Handle the expat startElement event. */ 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* pos = NULL; /* If we've already had an error in a previous call, don't make things worse. */ if (PyErr_Occurred() != NULL) { XML_StopParser(self->parser, 0); return; } /* Don't overflow the queue -- in practice this should *never* happen */ if (self->queue_write_idx < self->queue_size) { tuple = PyTuple_New(4); if (tuple == NULL) { goto fail; } Py_INCREF(Py_True); PyTuple_SET_ITEM(tuple, 0, Py_True); /* This is an egregious but effective optimization. Since by far the most frequently occurring element name in a large VOTABLE file is TD, we explicitly check for it here with 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) { Py_INCREF(self->td_singleton); PyTuple_SetItem(tuple, 1, self->td_singleton); } else { name_start = remove_namespace(name); pyname = PyUnicode_FromString(name_start); if (pyname == NULL) { goto fail; } PyTuple_SetItem(tuple, 1, pyname); pyname = NULL; } if (*att_ptr) { pyatts = PyDict_New(); if (pyatts == NULL) { goto fail; } do { 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 { 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); 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 { PyErr_SetString( PyExc_RuntimeError, "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) { 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. */ if (PyErr_Occurred() != NULL) { XML_StopParser(self->parser, 0); return; } /* Don't overflow the queue -- in practice this should *never* happen */ if (self->queue_write_idx < self->queue_size) { tuple = PyTuple_New(4); if (tuple == NULL) { goto fail; } Py_INCREF(Py_False); PyTuple_SET_ITEM(tuple, 0, Py_False); /* This is an egregious but effective optimization. Since by far the most frequently occurring element name in a large VOTABLE file is TD, we explicitly check for it here with 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) { Py_INCREF(self->td_singleton); PyTuple_SetItem(tuple, 1, self->td_singleton); } else { name_start = remove_namespace(name); pyname = PyUnicode_FromString(name_start); if (pyname == NULL) { goto fail; } PyTuple_SetItem(tuple, 1, pyname); pyname = NULL; } /* Cut whitespace off the end of the string */ end = self->text + self->text_size - 1; while (end >= self->text && IS_WHITESPACE(*end)) { --end; --self->text_size; } pytext = PyUnicode_FromStringAndSize(self->text, self->text_size); if (pytext == NULL) { goto fail; } PyTuple_SetItem(tuple, 2, pytext); pytext = NULL; 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 { PyErr_SetString( PyExc_RuntimeError, "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) { /* If we've already had an error in a previous call, don't make things worse. */ if (PyErr_Occurred() != NULL) { XML_StopParser(self->parser, 0); return; } if (self->text_size == 0) { self->last_line = (unsigned long)XML_GetCurrentLineNumber( self->parser); self->last_col = (unsigned long)XML_GetCurrentColumnNumber( self->parser); } if (self->keep_text) { (void)text_append(self, text, (Py_ssize_t)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) { 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) { goto fail; } Py_INCREF(Py_True); PyTuple_SET_ITEM(tuple, 0, Py_True); xml_str = PyUnicode_FromString("xml"); if (xml_str == NULL) { goto fail; } PyTuple_SET_ITEM(tuple, 1, xml_str); xml_str = NULL; attrs = PyDict_New(); if (attrs == NULL) { goto fail; } if (encoding) { encoding_str = PyUnicode_FromString(encoding); } else { encoding_str = PyUnicode_FromString(""); } 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 { version_str = PyUnicode_FromString(""); } 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_SET_ITEM(tuple, 2, attrs); attrs = NULL; self->last_line = (unsigned long)XML_GetCurrentLineNumber( self->parser); self->last_col = (unsigned long)XML_GetCurrentColumnNumber( self->parser); pos = make_pos(self); if (pos == NULL) { goto fail; } PyTuple_SetItem(tuple, 3, pos); pos = NULL; self->queue[self->queue_write_idx++] = tuple; } else { PyErr_SetString( PyExc_RuntimeError, "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) { Py_INCREF(self); return (PyObject*) self; } /* * Get the next element from the iterator. * * The expat event handlers above (startElement, endElement, characterData) add * elements to the queue, which are then dequeued by this method. * * Care must be taken to store and later raise exceptions. Any * exceptions raised in the expat callbacks must be stored and then * later thrown once the queue is emptied, otherwise the exception is * raised "too early" in queue order. */ static PyObject * IterParser_next(IterParser* self) { PyObject* data = NULL; XML_Char* buf; Py_ssize_t buflen; /* Is there anything in the queue to return? */ if (self->queue_read_idx < self->queue_write_idx) { return self->queue[self->queue_read_idx++]; } /* Now that the queue is empty, is there an error we need to raise? */ if (self->error_type) { PyErr_Restore(self->error_type, self->error_value, self->error_traceback); self->error_type = NULL; self->error_value = NULL; self->error_traceback = NULL; return NULL; } /* The queue is empty -- have we already fed the entire file to expat? If so, we are done and indicate the end of the iterator by simply returning NULL. */ if (self->done) { return NULL; } self->queue_read_idx = 0; self->queue_write_idx = 0; do { /* Handle a generic Python read method */ if (self->read) { data = PyObject_CallObject(self->read, self->read_args); if (data == NULL) { goto fail; } if (PyBytes_AsStringAndSize(data, &buf, &buflen) == -1) { Py_DECREF(data); goto fail; } 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 if we've got one. */ } else { buflen = (Py_ssize_t)read( self->file, self->buffer, (size_t)self->buffersize); if (buflen == -1) { PyErr_SetFromErrno(PyExc_OSError); goto fail; } 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 note of it -- it won't be thrown until the queue is emptied. */ if (PyErr_Occurred() != NULL) { goto fail; } /* expat raised an error, make note of it -- it won't be thrown until the queue is emptied. */ Py_XDECREF(data); PyErr_Format( PyExc_ValueError, "%lu:%lu: %s", XML_GetCurrentLineNumber(self->parser), XML_GetCurrentColumnNumber(self->parser), XML_ErrorString(XML_GetErrorCode(self->parser))); goto fail; } Py_XDECREF(data); if (PyErr_Occurred() != NULL) { goto fail; } } while (self->queue_write_idx == 0 && self->done == 0); if (self->queue_write_idx == 0) { return NULL; } if (self->queue_write_idx >= self->queue_size) { PyErr_SetString( PyExc_RuntimeError, "XML queue overflow. This most likely indicates an internal bug."); return NULL; } return self->queue[self->queue_read_idx++]; 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. */ PyErr_Fetch(&self->error_type, &self->error_value, &self->error_traceback); PyErr_Clear(); if (self->queue_read_idx < self->queue_write_idx) { return self->queue[self->queue_read_idx++]; } PyErr_Restore(self->error_type, self->error_value, self->error_traceback); self->error_type = NULL; self->error_value = NULL; self->error_traceback = NULL; return NULL; } /****************************************************************************** * IterParser object lifetime ******************************************************************************/ /* To support cyclical garbage collection, all PyObject's must be visited. */ static int IterParser_traverse(IterParser *self, visitproc visit, void *arg) { int vret; Py_ssize_t read_index; 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 (self->fd) { vret = visit(self->fd, arg); if (vret != 0) return vret; } if (self->read) { vret = visit(self->read, arg); if (vret != 0) return vret; } if (self->read_args) { vret = visit(self->read_args, arg); if (vret != 0) return vret; } if (self->dict_singleton) { vret = visit(self->dict_singleton, arg); if (vret != 0) return vret; } if (self->td_singleton) { vret = visit(self->td_singleton, arg); if (vret != 0) return vret; } if (self->error_type) { vret = visit(self->error_type, arg); if (vret != 0) return vret; } if (self->error_value) { vret = visit(self->error_value, arg); if (vret != 0) return vret; } if (self->error_traceback) { vret = visit(self->error_traceback, arg); if (vret != 0) return vret; } return 0; } /* To support cyclical garbage collection */ static int IterParser_clear(IterParser *self) { PyObject *tmp; while (self->queue_read_idx < self->queue_write_idx) { tmp = self->queue[self->queue_read_idx]; self->queue[self->queue_read_idx] = NULL; Py_XDECREF(tmp); self->queue_read_idx++; } tmp = self->fd; self->fd = NULL; Py_XDECREF(tmp); tmp = self->read; self->read = NULL; Py_XDECREF(tmp); tmp = self->read_args; self->read_args = NULL; Py_XDECREF(tmp); tmp = self->dict_singleton; self->dict_singleton = NULL; Py_XDECREF(tmp); tmp = self->td_singleton; self->td_singleton = NULL; Py_XDECREF(tmp); tmp = self->error_type; self->error_type = NULL; Py_XDECREF(tmp); tmp = self->error_value; self->error_value = NULL; Py_XDECREF(tmp); tmp = self->error_traceback; self->error_traceback = NULL; Py_XDECREF(tmp); return 0; } /* * Deallocate the IterParser object. For the internal PyObject*, just * punt to IterParser_clear. */ 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; if (self->parser != NULL) { XML_ParserFree(self->parser); self->parser = NULL; } Py_TYPE(self)->tp_free((PyObject*)self); } /* * Initialize the memory for an IterParser object */ static PyObject * IterParser_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { IterParser *self = NULL; self = (IterParser *)type->tp_alloc(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->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->error_traceback = NULL; } return (PyObject *)self; } /* * Initialize an IterParser object * * The Python arguments are: * * *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) { 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)) { return -1; } /* Keep the buffersize within a reasonable range */ 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. */ read = PyObject_GetAttrString(fd, "read"); if (read != NULL) { fd = read; } #else self->file = PyObject_AsFileDescriptor(fd); if (self->file != -1) { /* This is a real C file handle or descriptor. We therefore need to allocate our own read buffer, and get the real C object. */ self->buffer = malloc((size_t)self->buffersize); if (self->buffer == NULL) { PyErr_SetString(PyExc_MemoryError, "Out of memory"); goto fail; } self->fd = fd; Py_INCREF(self->fd); lseek(self->file, 0, SEEK_SET); } 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 { PyErr_SetString( 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_write_idx = 0; self->done = 0; self->text = malloc((size_t)buffersize * sizeof(XML_Char)); self->text_alloc = buffersize; if (self->text == NULL) { PyErr_SetString(PyExc_MemoryError, "Out of memory"); goto fail; } text_clear(self); self->read_args = Py_BuildValue("(n)", buffersize); if (self->read_args == NULL) { goto fail; } self->dict_singleton = PyDict_New(); if (self->dict_singleton == NULL) { goto fail; } self->td_singleton = PyUnicode_FromString("TD"); if (self->td_singleton == NULL) { goto fail; } if (queue_realloc(self, buffersize)) { goto fail; } /* Set up an expat parser with our callbacks */ self->parser = XML_ParserCreate(NULL); if (self->parser == NULL) { PyErr_SetString(PyExc_MemoryError, "Out of memory"); goto fail; } 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); Py_XDECREF(read); return 0; fail: Py_XDECREF(read); Py_XDECREF(self->fd); Py_XDECREF(self->read); free(self->text); Py_XDECREF(self->dict_singleton); Py_XDECREF(self->td_singleton); Py_XDECREF(self->read_args); free(self->queue); return -1; } static PyMemberDef IterParser_members[] = { {NULL} /* Sentinel */ }; static PyMethodDef IterParser_methods[] = { {NULL} /* Sentinel */ }; static PyTypeObject IterParserType = { PyVarObject_HEAD_INIT(NULL, 0) "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 */ }; /****************************************************************************** * XML escaping ******************************************************************************/ /* These are in reverse order by input character */ static const char* escapes_cdata[] = { ">", ">", "<", "<", "&", "&", "\0", "\0", }; /* These are in reverse order by input character */ 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. * * 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, const char** escapes) { PyObject* input_obj; PyObject* input_coerce = NULL; PyObject* output_obj; char* input = NULL; Py_ssize_t input_len; char* output = NULL; Py_ssize_t output_len; if (!PyArg_ParseTuple(args, "O:escape_xml", &input_obj)) { return 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; } output_len = _escape_xml_impl(input, input_len, &output, escapes); if (output_len < 0) { Py_DECREF(input_coerce); return NULL; } if (output_len > 0) { Py_DECREF(input_coerce); output_obj = PyUnicode_FromStringAndSize(output, output_len); free(output); return output_obj; } 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; } output_len = _escape_xml_impl(input, input_len, &output, escapes); if (output_len < 0) { Py_DECREF(input_coerce); return NULL; } if (output_len > 0) { Py_DECREF(input_coerce); output_obj = PyBytes_FromStringAndSize(output, output_len); free(output); return output_obj; } return input_coerce; } PyErr_SetString(PyExc_TypeError, "must be convertible to str or bytes"); return NULL; } static PyObject* escape_xml(PyObject* self, PyObject *args) { return _escape_xml(self, args, escapes); } static PyObject* escape_xml_cdata(PyObject* self, PyObject *args) { 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, "Fast method to escape XML strings"}, {NULL} /* Sentinel */ }; struct module_state { void* none; }; static int module_traverse(PyObject* m, visitproc visit, void* arg) { return 0; } static int module_clear(PyObject* m) { return 0; } static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, "_iterparser", "Fast XML parser", sizeof(struct module_state), module_methods, NULL, module_traverse, module_clear, NULL }; PyMODINIT_FUNC PyInit__iterparser(void) { PyObject* m; m = PyModule_Create(&moduledef); if (m == NULL) return NULL; if (PyType_Ready(&IterParserType) < 0) return NULL; Py_INCREF(&IterParserType); PyModule_AddObject(m, "IterParser", (PyObject *)&IterParserType); return m; } astropy-astropy-201cddb/astropy/utils/xml/src/iterparse.map000066400000000000000000000001351507226315300243230ustar00rootroot00000000000000VERS_1.0 { global: init_iterparser; PyInit__iterparser; local: *; }; astropy-astropy-201cddb/astropy/utils/xml/tests/000077500000000000000000000000001507226315300222025ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/utils/xml/tests/__init__.py000066400000000000000000000000001507226315300243010ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/utils/xml/tests/test_iterparse.py000066400000000000000000000112261507226315300256130ustar00rootroot00000000000000# 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) astropy-astropy-201cddb/astropy/utils/xml/unescaper.py000066400000000000000000000026141507226315300234020ustar00rootroot00000000000000# 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 astropy-astropy-201cddb/astropy/utils/xml/validate.py000066400000000000000000000027561507226315300232150ustar00rootroot00000000000000# 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 library at some point in the future, if something appropriate could be found. """ import os import subprocess from signal import Signals def validate_schema(filename, schema_file): """ Validates an XML file against a schema or DTD. Parameters ---------- filename : str The path to the XML file to validate schema_file : str The path to the XML schema or DTD Returns ------- returncode, stdout, stderr : int, str, str Returns the returncode from xmllint and the stdout and stderr as strings """ base, ext = os.path.splitext(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", 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( f"xmllint was terminated by signal '{Signals(-p.returncode).name}'" ) return p.returncode, stdout, stderr astropy-astropy-201cddb/astropy/utils/xml/writer.py000066400000000000000000000237431507226315300227370ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst """ Contains a class that makes it simple to stream out well-formed and nicely-indented XML. """ import contextlib import textwrap from astropy.utils.compat.optional_deps import HAS_BLEACH from ._iterparser import escape_xml as xml_escape from ._iterparser import escape_xml_cdata as xml_escape_cdata class XMLWriter: """ A class to write well-formed and nicely indented XML. Use like this:: w = XMLWriter(fh) with w.tag('html'): with w.tag('body'): w.data('This is the content') Which produces:: This is the content """ def __init__(self, file): """ Parameters ---------- file : :term:`file-like (writeable)` """ self.write = file.write if hasattr(file, "flush"): self.flush = file.flush self._open = 0 # true if start tag is open self._tags = [] self._data = [] self._indentation = " " * 64 self.xml_escape_cdata = xml_escape_cdata self.xml_escape = xml_escape def _flush(self, indent=True, wrap=False): """ Flush internal buffers. """ if self._open: if indent: self.write(">\n") else: self.write(">") self._open = 0 if 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("\n") self.write(self.xml_escape_cdata(data)) self.write("\n") self.write(self.get_indentation_spaces()) else: self.write(self.xml_escape_cdata(data)) self._data = [] def start(self, tag, attrib={}, **extra): """ Opens a new element. Attributes can be given as keyword arguments, or as a string/string dictionary. The method returns an opaque identifier that can be passed to the :meth:`close` method, to close all open elements up to and including this one. Parameters ---------- tag : str The element name attrib : dict of str -> str Attribute dictionary. Alternatively, attributes can be given as keyword arguments. Returns ------- id : int Returns an element identifier. """ self._flush() # This is just busy work -- we know our tag names are clean # tag = xml_escape_cdata(tag) self._data = [] self._tags.append(tag) self.write(self.get_indentation_spaces(-1)) self.write(f"<{tag}") if attrib or extra: attrib = attrib.copy() attrib.update(extra) 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(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 creating wrapper elements using the ``with`` statement. Examples -------- >>> with writer.tag('foo'): # doctest: +SKIP ... writer.element('bar') ... # is implicitly closed here ... Parameters are the same as to `start`. """ self.start(tag, attrib, **extra) yield self.end(tag) def comment(self, comment): """ Adds a comment to the output stream. Parameters ---------- comment : str Comment text, as a Unicode string. """ self._flush() self.write(self.get_indentation_spaces()) self.write(f"\n") def data(self, text): """ Adds character data to the output stream. Parameters ---------- text : str Character data, as a Unicode string. """ self._data.append(text) def end(self, tag=None, indent=True, wrap=False): """ Closes the current element (opened by the most recent call to `start`). Parameters ---------- tag : str Element name. If given, the tag must match the start tag. If omitted, the current element is closed. """ if 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: 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("/>\n") return if indent: self.write(self.get_indentation_spaces()) self.write(f"\n") def close(self, id): """ Closes open elements, up to (and including) the element identified by the given identifier. Parameters ---------- id : int Element identifier, as returned by the `start` method. """ while len(self._tags) > id: self.end() 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 can be omitted. """ self.start(tag, attrib, **extra) if text: 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 def get_indentation(self): """ Returns the number of indentation levels the file is currently in. """ return len(self._tags) 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] @staticmethod def object_attrs(obj, attrs): """ Converts an object with a bunch of attributes on an object into a dictionary for use by the `XMLWriter`. Parameters ---------- obj : object Any Python object attrs : sequence of str Attribute names to pull from the object Returns ------- attrs : dict Maps attribute names to the values retrieved from ``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("_", "-")] = str(getattr(obj, attr)) return d astropy-astropy-201cddb/astropy/version.py000066400000000000000000000017301507226315300211400ustar00rootroot00000000000000# 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: from packaging.version import Version 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 astropy-astropy-201cddb/astropy/visualization/000077500000000000000000000000001507226315300220015ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/visualization/__init__.py000066400000000000000000000005531507226315300241150ustar00rootroot00000000000000# 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 * astropy-astropy-201cddb/astropy/visualization/basic_rgb.py000066400000000000000000000161551507226315300242760ustar00rootroot00000000000000# 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 astropy-astropy-201cddb/astropy/visualization/hist.py000066400000000000000000000045511507226315300233270ustar00rootroot00000000000000# 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) astropy-astropy-201cddb/astropy/visualization/interval.py000066400000000000000000000245201507226315300242020ustar00rootroot00000000000000# 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", "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 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 astropy-astropy-201cddb/astropy/visualization/lupton_rgb.py000066400000000000000000000610511507226315300245310ustar00rootroot00000000000000# 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 astropy-astropy-201cddb/astropy/visualization/mpl_normalize.py000066400000000000000000000515131507226315300252300ustar00rootroot00000000000000""" 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 deprecated_renamed_argument, 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", "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 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, ) @deprecated_renamed_argument(["min_cut", "max_cut"], ["vmin", "vmax"], ["6.1", "6.1"]) 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) 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"] astropy-astropy-201cddb/astropy/visualization/mpl_style.py000066400000000000000000000041121507226315300243610ustar00rootroot00000000000000# 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.""" astropy-astropy-201cddb/astropy/visualization/scripts/000077500000000000000000000000001507226315300234705ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/visualization/scripts/__init__.py000066400000000000000000000001001507226315300255700ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst astropy-astropy-201cddb/astropy/visualization/scripts/fits2bitmap.py000066400000000000000000000206661507226315300263000ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst import warnings from pathlib import Path from astropy import log from astropy.io.fits import getdata from astropy.utils.decorators import deprecated_renamed_argument from astropy.visualization.mpl_normalize import simple_norm __all__ = ["fits2bitmap", "main"] @deprecated_renamed_argument(["min_cut", "max_cut"], ["vmin", "vmax"], ["6.1", "6.1"]) 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." ) # the mutually exclusive groups can be removed when the deprecated # min_cut and max_cut are removed vmin_group = parser.add_mutually_exclusive_group() vmax_group = parser.add_mutually_exclusive_group() 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)." ), ) vmin_group.add_argument( "--vmin", type=float, default=None, help="The pixel value of the minimum cut level (Default is the image minimum).", ) vmax_group.add_argument( "--vmax", type=float, default=None, help="The pixel value of the maximum cut level (Default is the image maximum).", ) vmin_group.add_argument( "--min_cut", type=float, default=None, help="The pixel value of the minimum cut level (Deprecated, use vmin instead; default is the image minimum).", ) vmax_group.add_argument( "--max_cut", type=float, default=None, help="The pixel value of the maximum cut level (Deprecated, use vmax instead; 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) if args.min_cut is not None: warnings.warn('The "--min_cut" argument is deprecated. Use "--vmin" instead.') args.vmin = args.min_cut if args.max_cut is not None: warnings.warn('The "--max_cut" argument is deprecated. Use "--vmax" instead.') args.vmax = args.max_cut 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, ) astropy-astropy-201cddb/astropy/visualization/scripts/tests/000077500000000000000000000000001507226315300246325ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/visualization/scripts/tests/__init__.py000066400000000000000000000001001507226315300267320ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst astropy-astropy-201cddb/astropy/visualization/scripts/tests/test_fits2bitmap.py000066400000000000000000000051301507226315300304660ustar00rootroot00000000000000# 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 def test_min_max_cut_deprecations(self, tmp_path): filename = str(tmp_path / self.filename) fits.writeto(filename, self.array) with pytest.raises(SystemExit): main([filename, "--min_cut=0.1", "--vmin=0.1"]) with pytest.raises(SystemExit): main([filename, "--max_cut=0.1", "--vmax=0.1"]) astropy-astropy-201cddb/astropy/visualization/stretch.py000066400000000000000000000710261507226315300240350ustar00rootroot00000000000000# 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 ) astropy-astropy-201cddb/astropy/visualization/tests/000077500000000000000000000000001507226315300231435ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/visualization/tests/__init__.py000066400000000000000000000001001507226315300252430ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst astropy-astropy-201cddb/astropy/visualization/tests/test_basic_rgb.py000066400000000000000000000267601507226315300265020ustar00rootroot00000000000000# 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, ) astropy-astropy-201cddb/astropy/visualization/tests/test_histogram.py000066400000000000000000000046561507226315300265640ustar00rootroot00000000000000# 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(rseed=0): rng = np.random.default_rng(rseed) 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(rseed=0): rng = np.random.default_rng(rseed) 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(rseed=0): rng = np.random.default_rng(rseed) 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) astropy-astropy-201cddb/astropy/visualization/tests/test_interval.py000066400000000000000000000122201507226315300263750ustar00rootroot00000000000000# 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, 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) 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)) astropy-astropy-201cddb/astropy/visualization/tests/test_lupton_rgb.py000066400000000000000000000307541507226315300267400ustar00rootroot00000000000000# 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) astropy-astropy-201cddb/astropy/visualization/tests/test_norm.py000066400000000000000000000303441507226315300255330ustar00rootroot00000000000000# 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, 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) astropy-astropy-201cddb/astropy/visualization/tests/test_stretch.py000066400000000000000000000131321507226315300262300ustar00rootroot00000000000000# 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 astropy-astropy-201cddb/astropy/visualization/tests/test_time.py000066400000000000000000000206541507226315300255210ustar00rootroot00000000000000# 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", ] astropy-astropy-201cddb/astropy/visualization/tests/test_units.py000066400000000000000000000120171507226315300257170ustar00rootroot00000000000000# 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 quantity_support @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 astropy-astropy-201cddb/astropy/visualization/time.py000066400000000000000000000225121507226315300233130ustar00rootroot00000000000000# 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) astropy-astropy-201cddb/astropy/visualization/transform.py000066400000000000000000000020731507226315300243700ustar00rootroot00000000000000# 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) astropy-astropy-201cddb/astropy/visualization/units.py000066400000000000000000000066101507226315300235200ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst from contextlib import ContextDecorator import numpy as np __all__ = ["quantity_support"] __doctest_skip__ = ["quantity_support"] def quantity_support(format="latex_inline"): """ Enable support for plotting `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) [...] ... ax.plot([101, 125, 150] * u.cm) [...] ... 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 : `astropy.units.format.Base` subclass or str The name of a format or a formatter class. If not provided, defaults to ``latex_inline``. """ from matplotlib import ticker, units from astropy import units as u def rad_fn(x, pos=None): n = int((x / np.pi) * 2.0 + 0.25) if n == 0: return "0" elif n == 1: return "Ī€/2" elif n == 2: return "Ī€" elif n % 2 == 0: return f"{n // 2}Ī€" else: return f"{n}Ī€/2" class MplQuantityConverter(units.ConversionInterface, ContextDecorator): 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): if unit == u.radian: 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 isinstance(val, list) and val and isinstance(val[0], u.Quantity): return [v.to_value(unit) for v in val] else: return val @staticmethod def default_units(x, axis): if hasattr(x, "unit"): return x.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] return MplQuantityConverter() astropy-astropy-201cddb/astropy/visualization/wcsaxes/000077500000000000000000000000001507226315300234565ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/visualization/wcsaxes/__init__.py000066400000000000000000000023601507226315300255700ustar00rootroot00000000000000# 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() astropy-astropy-201cddb/astropy/visualization/wcsaxes/_auto.py000066400000000000000000000117411507226315300251430ustar00rootroot00000000000000from 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): if "#" in coord.get_ticks_position(): coord.set_ticks_position([spine, "#"]) if "#" in coord.get_ticklabel_position(): coord.set_ticklabel_position([spine, "#"]) if "#" in coord.get_axislabel_position(): coord.set_axislabel_position([spine, "#"]) astropy-astropy-201cddb/astropy/visualization/wcsaxes/axislabels.py000066400000000000000000000116431507226315300261640ustar00rootroot00000000000000# 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 = 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) astropy-astropy-201cddb/astropy/visualization/wcsaxes/coordinate_helpers.py000066400000000000000000001604701507226315300277110ustar00rootroot00000000000000# 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.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 from .utils import MATPLOTLIB_LT_3_8 __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): """ 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. """ 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") 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 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 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 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 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: if MATPLOTLIB_LT_3_8: for line in self._grid.collections: line.set(**self._grid_lines_kwargs) line.draw(renderer) else: 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: if MATPLOTLIB_LT_3_8: for line in self._grid.collections: line.remove() else: 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"] astropy-astropy-201cddb/astropy/visualization/wcsaxes/coordinate_range.py000066400000000000000000000124031507226315300273330ustar00rootroot00000000000000# 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 astropy-astropy-201cddb/astropy/visualization/wcsaxes/coordinates_map.py000066400000000000000000000167331507226315300272110ustar00rootroot00000000000000# 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"" astropy-astropy-201cddb/astropy/visualization/wcsaxes/core.py000066400000000000000000001122011507226315300247550ustar00rootroot00000000000000# 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. """ astropy-astropy-201cddb/astropy/visualization/wcsaxes/formatter_locator.py000066400000000000000000000532031507226315300275610ustar00rootroot00000000000000# 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 microarcecond, # 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 [] astropy-astropy-201cddb/astropy/visualization/wcsaxes/frame.py000066400000000000000000000266701507226315300251350ustar00rootroot00000000000000# 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) 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) astropy-astropy-201cddb/astropy/visualization/wcsaxes/grid_paths.py000066400000000000000000000076111507226315300261610ustar00rootroot00000000000000# 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) astropy-astropy-201cddb/astropy/visualization/wcsaxes/helpers.py000066400000000000000000000141401507226315300254720ustar00rootroot00000000000000# 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) astropy-astropy-201cddb/astropy/visualization/wcsaxes/patches.py000066400000000000000000000171341507226315300254650ustar00rootroot00000000000000# 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 from astropy.coordinates.matrix_utilities import 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) astropy-astropy-201cddb/astropy/visualization/wcsaxes/tests/000077500000000000000000000000001507226315300246205ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/visualization/wcsaxes/tests/__init__.py000066400000000000000000000004541507226315300267340ustar00rootroot00000000000000# 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. astropy-astropy-201cddb/astropy/visualization/wcsaxes/tests/data/000077500000000000000000000000001507226315300255315ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/visualization/wcsaxes/tests/data/2MASS_k_header000066400000000000000000000021551507226315300301260ustar00rootroot00000000000000WCSAXES = 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 astropy-astropy-201cddb/astropy/visualization/wcsaxes/tests/data/cube_header000066400000000000000000000031231507226315300277010ustar00rootroot00000000000000WCSAXES = 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 astropy-astropy-201cddb/astropy/visualization/wcsaxes/tests/data/msx_header000066400000000000000000000020341507226315300275720ustar00rootroot00000000000000WCSAXES = 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 astropy-astropy-201cddb/astropy/visualization/wcsaxes/tests/data/rosat_header000066400000000000000000000020341507226315300301130ustar00rootroot00000000000000WCSAXES = 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 astropy-astropy-201cddb/astropy/visualization/wcsaxes/tests/data/slice_header000066400000000000000000000021431507226315300300630ustar00rootroot00000000000000WCSAXES = 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 astropy-astropy-201cddb/astropy/visualization/wcsaxes/tests/test_coordinate_helpers.py000066400000000000000000000154411507226315300321070ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst from unittest.mock import 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) 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) 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_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 astropy-astropy-201cddb/astropy/visualization/wcsaxes/tests/test_display_world_coordinates.py000066400000000000000000000146641507226315300335120ustar00rootroot00000000000000# 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) astropy-astropy-201cddb/astropy/visualization/wcsaxes/tests/test_formatter_locator.py000066400000000000000000000531641507226315300317700ustar00rootroot00000000000000# 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) astropy-astropy-201cddb/astropy/visualization/wcsaxes/tests/test_frame.py000066400000000000000000000110141507226315300273200ustar00rootroot00000000000000# 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" astropy-astropy-201cddb/astropy/visualization/wcsaxes/tests/test_grid_paths.py000066400000000000000000000020321507226315300303520ustar00rootroot00000000000000import 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:]) astropy-astropy-201cddb/astropy/visualization/wcsaxes/tests/test_images.py000066400000000000000000001403001507226315300274740ustar00rootroot00000000000000# 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, tmp_path): # 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.savefig(tmp_path / "nothing") 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. """ import matplotlib.pyplot as plt fig = plt.figure() 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. plt.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. """ import matplotlib.pyplot as plt fig = plt.figure() ax = fig.add_subplot(1, 1, 1, projection=spatial_wcs_2d_small_angle, slices=slices) (lines,) = ax.plot([10, 12, 14, 12, 10], "-o", color="orange") # Draw to trigger rendering the ticks. plt.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 astropy-astropy-201cddb/astropy/visualization/wcsaxes/tests/test_misc.py000066400000000000000000000663421507226315300271770ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst import warnings from contextlib import nullcontext 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 BaseLowLevelWCS, 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 MATPLOTLIB_LT_3_7 = Version(mpl.__version__) < Version("3.7") 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) if MATPLOTLIB_LT_3_7: ctx = pytest.warns( UserWarning, match="No contour levels were found within the data range" ) else: ctx = nullcontext() with ctx: 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(None) 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(None) 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 class ArcminWCS(BaseLowLevelWCS): def __init__(self, wcs_deg): self.wcs_deg = wcs_deg @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 ["arcmin", "arcmin"] @property def world_axis_names(self): return ["RA", "DEC"] def pixel_to_world_values(self, *pixel_arrays): world = self.wcs_deg.pixel_to_world_values(*pixel_arrays) return world[0] * 60, world[1] * 60 def world_to_pixel_values(self, *world_arrays): world = world_arrays[0] / 60, world_arrays[1] / 60 return self.wcs_deg.world_to_pixel_values(*world_arrays) @property def world_axis_object_components(self): return [ ("celestial", 0, "spherical.lon.arcmin"), ("celestial", 1, "spherical.lat.arcmin"), ] @property def world_axis_object_classes(self): return { "celestial": (SkyCoord, (), {"unit": "arcmin"}), } 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) 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 = ArcminWCS(wcs_deg) 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)) astropy-astropy-201cddb/astropy/visualization/wcsaxes/tests/test_transform_coord_meta.py000066400000000000000000000111611507226315300324400ustar00rootroot00000000000000# 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 astropy-astropy-201cddb/astropy/visualization/wcsaxes/tests/test_transforms.py000066400000000000000000000001001507226315300304160ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst astropy-astropy-201cddb/astropy/visualization/wcsaxes/tests/test_utils.py000066400000000000000000000102001507226315300273620ustar00rootroot00000000000000# 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) astropy-astropy-201cddb/astropy/visualization/wcsaxes/tests/test_wcsapi.py000066400000000000000000000631711507226315300275270ustar00rootroot00000000000000# 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 ( 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() # 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 properley # (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 astropy-astropy-201cddb/astropy/visualization/wcsaxes/ticklabels.py000066400000000000000000000312071507226315300261500ustar00rootroot00000000000000# 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 = 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) astropy-astropy-201cddb/astropy/visualization/wcsaxes/ticks.py000066400000000000000000000146251507226315300251550ustar00rootroot00000000000000# 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 = 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() astropy-astropy-201cddb/astropy/visualization/wcsaxes/transforms.py000066400000000000000000000144131507226315300262310ustar00rootroot00000000000000# 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. """ astropy-astropy-201cddb/astropy/visualization/wcsaxes/utils.py000066400000000000000000000144741507226315300252020ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst import matplotlib as mpl import numpy as np from astropy import units as u from astropy.coordinates import BaseCoordinateFrame, UnitSphericalRepresentation from astropy.utils.introspection import minversion __all__ = [ "select_step_degree", "select_step_hour", "select_step_scalar", "transform_contour_set_inplace", ] MATPLOTLIB_LT_3_8 = not minversion(mpl, "3.8") 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 = [] if MATPLOTLIB_LT_3_8: for collection in cset.collections: paths = collection.get_paths() if len(paths) == 0: continue 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]) else: 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 astropy-astropy-201cddb/astropy/visualization/wcsaxes/wcsapi.py000066400000000000000000000357121507226315300253260ustar00rootroot00000000000000# 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 __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 __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) astropy-astropy-201cddb/astropy/wcs/000077500000000000000000000000001507226315300176745ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/wcs/__init__.py000066400000000000000000000026011507226315300220040ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst """World Coordinate System (WCS) transformations in FITS files. .. _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 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 . import utils from .wcs import * from .wcs import InvalidTabularParametersError # just for docs def get_include(): """ Get the path to astropy.wcs's C header files. """ import os return os.path.join(os.path.dirname(__file__), "include") astropy-astropy-201cddb/astropy/wcs/docstrings.py000066400000000000000000002412711507226315300224340ustar00rootroot00000000000000# 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). # Therefore, the docstrings are written here in doc/docstrings.py, # which are then converted by setup.py into docstrings.h, which is # included by pywcs.c __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 matrix. The `SIP`_ ``A_i_j`` matrix used for pixel to focal plane transformation. Its values may be changed in place, but it may not be resized, without creating a new `~astropy.wcs.Sip` object. """ a_order = """ ``int`` (read-only) Order of the polynomial (``A_ORDER``). """ 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 (if present) - SIP distortion correction (if present) - FITS WCS distortion correction (if present) - wcslib "core" WCS transformation The first three (the distortion corrections) are done in parallel. Parameters ---------- pixcrd : ndarray Array of pixel coordinates as ``double array[ncoord][nelem]``. {ORIGIN()} Returns ------- world : ndarray Returns an array of world coordinates as ``double array[ncoord][nelem]``. Raises ------ 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. InvalidTransformError Ill-conditioned coordinate transformation parameters. """ alt = """ ``str`` Character code for alternate coordinate descriptions. For example, the ``"a"`` in keyword names such as ``CTYPEia``. This is a space character for the primary coordinate description, or one of the 26 upper-case letters, A-Z. """ ap = """ ``double array[ap_order+1][ap_order+1]`` Focal plane to pixel transformation matrix. The `SIP`_ ``AP_i_j`` matrix used for focal plane to pixel transformation. Its values may be changed in place, but it may not be resized, without creating a new `~astropy.wcs.Sip` object. """ ap_order = """ ``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. - First digit (i.e. 1000s): - 0: Non-specific coordinate type. - 1: Stokes coordinate. - 2: Celestial coordinate (including ``CUBEFACE``). - 3: Spectral coordinate. - Second digit (i.e. 100s): - 0: Linear axis. - 1: Quantized axis (``STOKES``, ``CUBEFACE``). - 2: Non-linear celestial axis. - 3: Non-linear spectral axis. - 4: Logarithmic axis. - 5: Tabular axis. - Third digit (i.e. 10s): - 0: Group number, e.g. lookup table number - The fourth digit is used as a qualifier depending on the axis type. - For celestial axes: - 0: Longitude coordinate. - 1: Latitude coordinate. - 2: ``CUBEFACE`` number. - For lookup tables: the axis number in a multidimensional table. ``CTYPEia`` in ``"4-3"`` form with unrecognized algorithm code will have its type set to -1 and generate an error. """ b = """ ``double array[b_order+1][b_order+1]`` Pixel to focal plane transformation matrix. The `SIP`_ ``B_i_j`` matrix used for pixel to focal plane transformation. Its values may be changed in place, but it may not be resized, without creating a new `~astropy.wcs.Sip` object. """ b_order = """ ``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. The `SIP`_ ``BP_i_j`` matrix used for focal plane to pixel transformation. Its values may be changed in place, but it may not be resized, without creating a new `~astropy.wcs.Sip` object. """ bp_order = """ ``int`` (read-only) Order of the polynomial (``BP_ORDER``). """ cd = """ ``double array[naxis][naxis]`` The ``CDi_ja`` linear transformation matrix. For historical compatibility, three alternate specifications of the 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 these alternatives are present in the header. These alternate specifications of the linear transformation matrix are translated immediately to ``PCi_ja`` by `~astropy.wcs.Wcsprm.set` and are nowhere visible to the lower-level routines. In particular, `~astropy.wcs.Wcsprm.set` resets `~astropy.wcs.Wcsprm.cdelt` to unity if ``CDi_ja`` is present (and no ``PCi_ja``). If no ``CROTAia`` is associated with the latitude axis, `~astropy.wcs.Wcsprm.set` reverts to a unity ``PCi_ja`` matrix. """ cdelt = """ ``double array[naxis]`` Coordinate increments (``CDELTia``) for each coord axis. If a ``CDi_ja`` linear transformation matrix is present, a warning is raised and `~astropy.wcs.Wcsprm.cdelt` is ignored. The ``CDi_ja`` matrix may be deleted by:: del wcs.wcs.cd An undefined value is represented by NaN. """ cdfix = """ cdfix() Fix erroneously omitted ``CDi_ja`` keywords. 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 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. Returns ------- success : int Returns ``0`` for success; ``-1`` if no change required. """ cel_offset = """ ``boolean`` Is there an offset? If `True`, an offset will be applied to ``(x, y)`` to force ``(x, y) = (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``. Returns ------- success : int Returns ``0`` for success; ``-1`` if no change required. """ cname = """ ``list of strings`` A list of the coordinate axis names, from ``CNAMEia``. """ colax = """ ``int array[naxis]`` An array recording the column numbers for each axis in a pixel list. """ colnum = """ ``int`` Column of FITS binary table associated with this WCS. Where the coordinate representation is associated with an image-array column in a FITS binary table, this property may be used to record the relevant column number. 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) Perform the unit conversion on the elements of the given *array*, returning an array of the same shape. """ coord = """ ``double array[K_M]...[K_2][K_1][M]`` The tabular coordinate array. Has the dimensions:: (K_M, ... K_2, K_1, M) (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. """ copy = """ Creates a deep copy of the WCS object. """ cpdis1 = """ `~astropy.wcs.DistortionLookupTable` The pre-linear transformation distortion lookup table, ``CPDIS1``. """ cpdis2 = """ `~astropy.wcs.DistortionLookupTable` The pre-linear transformation distortion lookup table, ``CPDIS2``. """ crder = """ ``double array[naxis]`` The random error in each coordinate axis, ``CRDERia``. 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 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 these alternatives are present in the header. These alternate specifications of the linear transformation matrix are translated immediately to ``PCi_ja`` by `~astropy.wcs.Wcsprm.set` and are nowhere visible to the lower-level routines. In particular, `~astropy.wcs.Wcsprm.set` resets `~astropy.wcs.Wcsprm.cdelt` to unity if ``CDi_ja`` is present (and no ``PCi_ja``). If no ``CROTAia`` is associated with the latitude axis, `~astropy.wcs.Wcsprm.set` reverts to a unity ``PCi_ja`` matrix. """ crpix = """ ``double array[naxis]`` Coordinate reference pixels (``CRPIXja``) for each pixel axis. """ crval = """ ``double array[naxis]`` Coordinate reference values (``CRVALia``) for each coordinate axis. """ crval_tabprm = """ ``double array[M]`` Index values for the reference pixel for each of the tabular coord axes. """ csyer = """ ``double array[naxis]`` The systematic error in the coordinate value axes, ``CSYERia``. An undefined value is represented by NaN. """ ctype = """ ``list of strings[naxis]`` List of ``CTYPEia`` keyvalues. The `~astropy.wcs.Wcsprm.ctype` keyword values must be in upper case and there must be zero or one pair of matched celestial axis types, and zero or one spectral axis. """ cubeface = """ ``int`` Index into the ``pixcrd`` (pixel coordinate) array for the ``CUBEFACE`` axis. This is used for quadcube projections where the cube faces are stored on a separate axis. The quadcube projections (``TSC``, ``CSC``, ``QSC``) may be represented in FITS in either of two ways: - The six faces may be laid out in one plane and numbered as follows:: 0 4 3 2 1 4 3 2 5 Faces 2, 3 and 4 may appear on one side or the other (or both). The world-to-pixel routines map faces 2, 3 and 4 to the left but the pixel-to-world routines accept them on either side. - The ``COBE`` convention in which the six faces are stored in a three-dimensional structure using a ``CUBEFACE`` axis indexed from 0 to 5 as above. These routines support both methods; `~astropy.wcs.Wcsprm.set` determines which is being used by the presence or absence of a ``CUBEFACE`` axis in `~astropy.wcs.Wcsprm.ctype`. `~astropy.wcs.Wcsprm.p2s` and `~astropy.wcs.Wcsprm.s2p` translate the ``CUBEFACE`` axis representation to the single plane representation understood by the lower-level projection routines. """ cunit = """ ``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. As ``CUNITia`` is an optional header keyword, `~astropy.wcs.Wcsprm.cunit` may be left blank but otherwise is expected to contain a standard units specification as defined by WCS Paper I. `~astropy.wcs.Wcsprm.unitfix` is available to translate commonly used non-standard units specifications but this must be done 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.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.cdelt`, `~astropy.wcs.Wcsprm.crval`, and `~astropy.wcs.Wcsprm.cd` to SI units. It then resets `~astropy.wcs.Wcsprm.cunit` accordingly. `~astropy.wcs.Wcsprm.set` ignores `~astropy.wcs.Wcsprm.cunit` for other coordinate types; `~astropy.wcs.Wcsprm.cunit` may be used to label coordinate values. """ cylfix = """ cylfix() Fixes WCS keyvalues for malformed cylindrical projections. Returns ------- success : int Returns ``0`` for success; ``-1`` if no change required. """ data = """ ``float array`` The array data for the `~astropy.wcs.DistortionLookupTable`. """ data_wtbarr = """ ``double array`` The array data for the BINTABLE. """ dateavg = """ ``string`` Representative mid-point of the date of observation. In ISO format, ``yyyy-mm-ddThh:mm:ss``. See also -------- astropy.wcs.Wcsprm.dateobs """ dateobs = """ ``string`` Start of the date of observation. In ISO format, ``yyyy-mm-ddThh:mm:ss``. See also -------- astropy.wcs.Wcsprm.dateavg """ datfix = """ datfix() Translates the old ``DATE-OBS`` date format to year-2000 standard form ``(yyyy-mm-ddThh:mm:ss)`` and derives ``MJD-OBS`` from it if not already set. Alternatively, if `~astropy.wcs.Wcsprm.mjdobs` is set and `~astropy.wcs.Wcsprm.dateobs` isn't, then `~astropy.wcs.Wcsprm.datfix` derives `~astropy.wcs.Wcsprm.dateobs` from it. If both are set but disagree by more than half a day then `ValueError` is raised. Returns ------- success : int Returns ``0`` for success; ``-1`` if no change required. """ delta = """ ``double array[M]`` (read-only) Interpolated indices into the coord array. Array of interpolated indices into the coordinate array such that Upsilon_m, as defined in Paper III, is equal to (`~astropy.wcs.Tabprm.p0` [m] + 1) + delta[m]. """ det2im = """ Convert detector coordinates to image plane coordinates. """ det2im1 = """ A `~astropy.wcs.DistortionLookupTable` object for detector to image plane correction in the *x*-axis. """ det2im2 = """ A `~astropy.wcs.DistortionLookupTable` object for detector to image plane correction in the *y*-axis. """ dims = """ ``int array[ndim]`` (read-only) The dimensions of the tabular array `~astropy.wcs.Wtbarr.data`. """ DistortionLookupTable = """ DistortionLookupTable(table, crpix, crval, cdelt) Represents a single lookup table for a `distortion paper`_ transformation. Parameters ---------- table : 2-dimensional array The distortion lookup table. crpix : 2-tuple The distortion array reference pixel, in FITS Header format: 1-based indexing, (x,y) order. crval : 2-tuple 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. ``EQUINOXa`` (or ``EPOCH`` in older headers). Not applicable to ICRS equatorial or ecliptic coordinates. An undefined value is represented by NaN. """ extlev = """ ``int`` (read-only) ``EXTLEV`` identifying the binary table extension. """ extnam = """ ``str`` (read-only) ``EXTNAME`` identifying the binary table extension. """ extrema = """ ``double array[K_M]...[K_2][2][M]`` (read-only) An array recording the minimum and maximum value of each element of the coordinate vector in each row of the coordinate array, with the dimensions:: (K_M, ... K_2, 2, M) (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. """ find_all_wcs = """ find_all_wcs(relax=0, keysel=0) Find all WCS transformations in the header. Parameters ---------- header : str The raw FITS header data. relax : bool or int Degree of permissiveness: - `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:`astropy:relaxread` for details. keysel : sequence of flags Used to restrict the keyword types considered: - ``WCSHDR_IMGHEAD``: Image header keywords. - ``WCSHDR_BIMGARR``: Binary table image array. - ``WCSHDR_PIXLIST``: Pixel list keywords. If zero, there is no restriction. If -1, `wcspih` is called, rather than `wcstbh`. Returns ------- wcs_list : list of `~astropy.wcs.Wcsprm` """ fix = """ fix(translate_units='', naxis=0) Applies all of the corrections handled separately by `~astropy.wcs.Wcsprm.datfix`, `~astropy.wcs.Wcsprm.unitfix`, `~astropy.wcs.Wcsprm.celfix`, `~astropy.wcs.Wcsprm.spcfix`, `~astropy.wcs.Wcsprm.cylfix` and `~astropy.wcs.Wcsprm.cdfix`. Parameters ---------- 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 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. Returns ------- status : dict Returns a dictionary containing the following keys, each referring to a status string for each of the sub-fix functions that were called: - `~astropy.wcs.Wcsprm.cdfix` - `~astropy.wcs.Wcsprm.datfix` - `~astropy.wcs.Wcsprm.unitfix` - `~astropy.wcs.Wcsprm.celfix` - `~astropy.wcs.Wcsprm.spcfix` - `~astropy.wcs.Wcsprm.cylfix` """ get_offset = """ get_offset(x, y) -> (x, y) Returns the offset as defined in the distortion lookup table. Returns ------- coordinate : (2,) tuple The offset from the distortion table for pixel point (*x*, *y*). """ get_cdelt = """ get_cdelt() -> numpy.ndarray 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 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() -> numpy.ndarray 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 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 Returns ``PSi_ma`` keywords for each *i* and *m* as list of tuples. Returns ------- ps : list Returned as a list of tuples of the form (*i*, *m*, *value*): - *i*: int. Axis number, as in ``PSi_ma``, (i.e. 1-relative) - *m*: int. Parameter number, as in ``PSi_ma``, (i.e. 0-relative) - *value*: string. Parameter value. See also -------- astropy.wcs.Wcsprm.set_ps : Set ``PSi_ma`` values """ get_pv = """ get_pv() -> list 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) - *m*: int. Parameter number, as in ``PVi_ma``, (i.e. 0-relative) - *value*: string. Parameter value. See also -------- astropy.wcs.Wcsprm.set_pv : Set ``PVi_ma`` values Notes ----- Note that, if they were not given, `~astropy.wcs.Wcsprm.set` resets the entries for ``PVi_1a``, ``PVi_2a``, ``PVi_3a``, and ``PVi_4a`` for longitude axis *i* to match (``phi_0``, ``theta_0``), the native longitude and latitude of the reference point given by ``LONPOLEa`` and ``LATPOLEa``. """ has_cd = """ has_cd() -> bool Returns `True` if ``CDi_ja`` is present. ``CDi_ja`` is an alternate specification of the linear transformation matrix, maintained for historical compatibility. 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. While ``CDi_ja`` may not formally co-exist with ``PCi_ja``, it may co-exist with ``CDELTia`` and ``CROTAia`` which are to be ignored. See also -------- astropy.wcs.Wcsprm.cd : Get the raw ``CDi_ja`` values. """ has_cdi_ja = """ has_cdi_ja() -> bool Alias for `~astropy.wcs.Wcsprm.has_cd`. Maintained for backward compatibility. """ has_crota = """ has_crota() -> bool Returns `True` if ``CROTAia`` is present. ``CROTAia`` is an alternate specification of the linear transformation matrix, maintained for historical compatibility. 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. ``CROTAia`` may not formally co-exist with ``PCi_ja``. ``CROTAia`` and ``CDELTia`` may formally co-exist with ``CDi_ja`` but if so are to be ignored. See also -------- astropy.wcs.Wcsprm.crota : Get the raw ``CROTAia`` values """ has_crotaia = """ has_crotaia() -> bool Alias for `~astropy.wcs.Wcsprm.has_crota`. Maintained for backward compatibility. """ has_pc = """ has_pc() -> bool Returns `True` if ``PCi_ja`` is present. ``PCi_ja`` is the recommended way to specify the linear transformation matrix. See also -------- astropy.wcs.Wcsprm.pc : Get the raw ``PCi_ja`` values """ has_pci_ja = """ has_pci_ja() -> bool Alias for `~astropy.wcs.Wcsprm.has_pc`. Maintained for backward compatibility. """ hgln_obs = """ ``double`` Stonyhurst heliographic longitude of the observer. If undefined, this is set to `None`. """ 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. """ imgpix_matrix = """ ``double array[2][2]`` (read-only) Inverse of the ``CDELT`` or ``PC`` matrix. Inverse containing the product of the ``CDELTia`` diagonal matrix and the ``PCi_ja`` matrix. """ is_unity = """ is_unity() -> bool Returns `True` if the linear transformation matrix (`~astropy.wcs.Wcsprm.cd`) is unity. """ K = """ ``int array[M]`` (read-only) The lengths of the axes of the coordinate array. An array of length `M` whose elements record the lengths of the axes of the coordinate array and of each indexing vector. """ kind = """ ``str`` (read-only) ``wcstab`` array type. Character identifying the ``wcstab`` array type: - ``'c'``: coordinate array, - ``'i'``: index vector. """ lat = """ ``int`` (read-only) The index into the world coord array containing latitude values. """ latpole = """ ``double`` The native latitude of the celestial pole, ``LATPOLEa`` (deg). """ lattyp = """ ``string`` (read-only) Celestial axis type for latitude. For example, "RA", "DEC", "GLON", "GLAT", etc. extracted from "RA--", "DEC-", "GLON", "GLAT", etc. in the first four characters of ``CTYPEia`` but with trailing dashes removed. """ lng = """ ``int`` (read-only) The index into the world coord array containing longitude values. """ lngtyp = """ ``string`` (read-only) Celestial axis type for longitude. For example, "RA", "DEC", "GLON", "GLAT", etc. extracted from "RA--", "DEC-", "GLON", "GLAT", etc. in the first four characters of ``CTYPEia`` but with trailing dashes removed. """ lonpole = """ ``double`` The native longitude of the celestial pole. ``LONPOLEa`` (deg). """ M = """ ``int`` (read-only) Number of tabular coordinate axes. """ m = """ ``int`` (read-only) ``wcstab`` axis number for index vectors. """ map = """ ``int array[M]`` Association between axes. 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. When the intermediate and world coordinate arrays contain the full complement of coordinate elements in image-order, as will usually be the case, then ``map[m-1] == i-1`` for axis *i* in the *N*-dimensional image (1 <= *i* <= *N*). In terms of the FITS keywords:: map[PVi_3a - 1] == i - 1. However, a different association may result if the intermediate coordinates, for example, only contains a (relevant) subset of intermediate world coordinate elements. For example, if *M* == 1 for an image with *N* > 1, it is possible to fill the intermediate coordinates with the relevant coordinate element with ``nelem`` set to 1. In this case ``map[0] = 0`` regardless of the value of *i*. """ mix = f""" mix(mixpix, mixcel, vspan, vstep, viter, world, pixcrd, origin) Given either the celestial longitude or latitude plus an element of the pixel coordinate, solves for the remaining elements by iterating on the unknown celestial coordinate element using `~astropy.wcs.Wcsprm.s2p`. Parameters ---------- mixpix : int Which element on the pixel coordinate is given. mixcel : int Which element of the celestial coordinate is given. If *mixcel* = ``1``, celestial longitude is given in ``world[self.lng]``, latitude returned in ``world[self.lat]``. If *mixcel* = ``2``, celestial latitude is given in ``world[self.lat]``, longitude returned in ``world[self.lng]``. 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 ``(-120,+120)`` is the same as ``(240,480)``, except that the solution will be returned with the same normalization, i.e. lie within the interval specified. vstep : float Step size for solution search, in degrees. If ``0``, a sensible, although perhaps non-optimal default will be used. viter : int If a solution is not found then the step size will be halved and the search recommenced. *viter* controls how many times the step size is halved. The allowed range is 5 - 10. 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 : ndarray Pixel coordinates as ``double array[naxis]``. The element indicated by *mixpix* is given and the remaining elements will be written in-place. {ORIGIN()} Returns ------- result : dict Returns a dictionary with the following keys: - *phi* (``double array[naxis]``) - *theta* (``double array[naxis]``) - Longitude and latitude in the native coordinate system of the projection, in degrees. - *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]``) - Another reference to the *world* argument passed in. Raises ------ MemoryError Memory allocation failed. SingularMatrixError Linear transformation matrix is singular. InconsistentAxisTypesError Inconsistent or unrecognized coordinate axis types. ValueError Invalid parameter value. InvalidTransformError Invalid coordinate transformation parameters. InvalidTransformError Ill-conditioned coordinate transformation parameters. InvalidCoordinateError Invalid world coordinate. NoSolutionError No solution found in the specified interval. See also -------- astropy.wcs.Wcsprm.lat, astropy.wcs.Wcsprm.lng Get the axes numbers for latitude and longitude 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 invocations of `~astropy.wcs.Wcsprm.mix` 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 `~astropy.wcs.Wcsprm.mix` only ever returns one. Because of its generality, `~astropy.wcs.Wcsprm.mix` 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. """ mjdavg = """ ``double`` Modified Julian Date corresponding to ``DATE-AVG``. ``(MJD = JD - 2400000.5)``. An undefined value is represented by NaN. See also -------- astropy.wcs.Wcsprm.mjdobs """ mjdobs = """ ``double`` Modified Julian Date corresponding to ``DATE-OBS``. ``(MJD = JD - 2400000.5)``. An undefined value is represented by NaN. See also -------- astropy.wcs.Wcsprm.mjdavg """ name = """ ``string`` The name given to the coordinate representation ``WCSNAMEa``. """ naxis = """ ``int`` (read-only) The number of axes (pixel and coordinate). Given by the ``NAXIS`` or ``WCSAXESa`` keyvalues. The number of coordinate axes is determined at parsing time, and can not be subsequently changed. It is determined from the highest of the following: 1. ``NAXIS`` 2. ``WCSAXESa`` 3. 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. This value may differ for different coordinate representations of the same image. """ nc = """ ``int`` (read-only) Total number of coord vectors in the coord array. Total number of coordinate vectors in the coordinate array being the product K_1 * K_2 * ... * K_M. """ ndim = """ ``int`` (read-only) Expected dimensionality of the ``wcstab`` array. """ obsgeo = """ ``double array[3]`` Location of the observer in a standard terrestrial reference frame. ``OBSGEO-X``, ``OBSGEO-Y``, ``OBSGEO-Z`` (in meters). An undefined value is represented by NaN. """ p0 = """ ``int array[M]`` Interpolated indices into the coordinate array. 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 = f""" p2s(pixcrd, origin) Converts pixel to world coordinates. Parameters ---------- pixcrd : ndarray Array of pixel coordinates as ``double array[ncoord][nelem]``. {ORIGIN()} Returns ------- result : dict Returns a dictionary with the following keys: - *imgcrd*: ndarray - 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*: ndarray - Array as ``double array[ncoord]``. - *theta*: ndarray - Longitude and latitude in the native coordinate system of the projection, in degrees, as ``double array[ncoord]``. - *world*: ndarray - 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*: ndarray - Status return value for each coordinate as ``int array[ncoord]``. ``0`` for success, ``1+`` for invalid pixel coordinate. Raises ------ MemoryError Memory allocation failed. SingularMatrixError Linear transformation matrix is singular. InconsistentAxisTypesError Inconsistent or unrecognized coordinate axis types. ValueError Invalid parameter value. ValueError *x*- and *y*-coordinate arrays are not the same size. InvalidTransformError Invalid coordinate transformation parameters. InvalidTransformError Ill-conditioned coordinate transformation parameters. See also -------- astropy.wcs.Wcsprm.lat, astropy.wcs.Wcsprm.lng Definition of the latitude and longitude axes """ p4_pix2foc = f""" p4_pix2foc(*pixcrd, origin*) -> ``double array[ncoord][nelem]`` Convert pixel coordinates to focal plane coordinates using `distortion paper`_ lookup-table correction. Parameters ---------- pixcrd : ndarray Array of pixel coordinates as ``double array[ncoord][nelem]``. {ORIGIN()} Returns ------- foccrd : ndarray Returns an array of focal plane coordinates as ``double array[ncoord][nelem]``. Raises ------ MemoryError Memory allocation failed. ValueError Invalid coordinate transformation parameters. """ pc = """ ``double array[naxis][naxis]`` The ``PCi_ja`` (pixel coordinate) transformation matrix. The order is:: [[PC1_1, PC1_2], [PC2_1, PC2_2]] For historical compatibility, three alternate specifications of the 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 these alternatives are present in the header. These alternate specifications of the linear transformation matrix are translated immediately to ``PCi_ja`` by `~astropy.wcs.Wcsprm.set` and are nowhere visible to the lower-level routines. In particular, `~astropy.wcs.Wcsprm.set` resets `~astropy.wcs.Wcsprm.cdelt` to unity if ``CDi_ja`` is present (and no ``PCi_ja``). If no ``CROTAia`` is associated with the latitude axis, `~astropy.wcs.Wcsprm.set` reverts to a unity ``PCi_ja`` matrix. """ phi0 = """ ``double`` The native latitude of the fiducial point. The point whose celestial coordinates are given in ``ref[1:2]``. If undefined (NaN) the initialization routine, `~astropy.wcs.Wcsprm.set`, will set this to a projection-specific default. See also -------- astropy.wcs.Wcsprm.theta0 """ pix2foc = f""" pix2foc(*pixcrd, origin*) -> ``double array[ncoord][nelem]`` Perform both `SIP`_ polynomial and `distortion paper`_ lookup-table correction in parallel. Parameters ---------- pixcrd : ndarray Array of pixel coordinates as ``double array[ncoord][nelem]``. {ORIGIN()} Returns ------- foccrd : ndarray Returns an array of focal plane coordinates as ``double array[ncoord][nelem]``. Raises ------ MemoryError Memory allocation failed. ValueError Invalid coordinate transformation parameters. """ piximg_matrix = """ ``double array[2][2]`` (read-only) Matrix containing the product of the ``CDELTia`` diagonal matrix and the ``PCi_ja`` matrix. """ print_contents = """ print_contents() Print the contents of the `~astropy.wcs.Wcsprm` 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_tabprm = """ print_contents() 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``. """ restfrq = """ ``double`` Rest frequency (Hz) from ``RESTFRQa``. An undefined value is represented by NaN. """ restwav = """ ``double`` Rest wavelength (m) from ``RESTWAVa``. An undefined value is represented by NaN. """ row = """ ``int`` (read-only) Table row number. """ rsun_ref = """ ``double`` Reference radius of the Sun used in coordinate calculations (m). If undefined, this is set to `None`. """ s2p = f""" s2p(world, origin) Transforms world coordinates to pixel coordinates. Parameters ---------- world : ndarray Array of world coordinates, in decimal degrees, as ``double array[ncoord][nelem]``. {ORIGIN()} Returns ------- result : dict Returns a dictionary with the following keys: - *phi*: ``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]`` - Array of intermediate world coordinates. For celestial axes, ``imgcrd[][self.lng]`` and ``imgcrd[][self.lat]`` are the projected *x*-, and *y*-coordinates, in pseudo \"degrees\". For quadcube projections with a ``CUBEFACE`` axis, the face number is also returned in ``imgcrd[][self.cubeface]``. For spectral axes, ``imgcrd[][self.spec]`` is the intermediate spectral coordinate, in SI units. - *pixcrd*: ``double array[ncoord][nelem]`` - Array of pixel coordinates. Pixel coordinates are zero-based. - *stat*: ``int array[ncoord]`` - Status return value for each coordinate. ``0`` for success, ``1+`` for invalid pixel coordinate. Raises ------ MemoryError Memory allocation failed. SingularMatrixError Linear transformation matrix is singular. InconsistentAxisTypesError Inconsistent or unrecognized coordinate axis types. ValueError Invalid parameter value. InvalidTransformError Invalid coordinate transformation parameters. InvalidTransformError Ill-conditioned coordinate transformation parameters. See also -------- astropy.wcs.Wcsprm.lat, astropy.wcs.Wcsprm.lng Definition of the latitude and longitude axes """ sense = """ ``int array[M]`` +1 if monotonically increasing, -1 if decreasing. A vector of length `~astropy.wcs.Tabprm.M` whose elements indicate whether the corresponding indexing vector is monotonically increasing (+1), or decreasing (-1). """ set = """ set() Sets up a WCS object for use according to information supplied within it. Note that this routine need not be called directly; it will be invoked by `~astropy.wcs.Wcsprm.p2s` and `~astropy.wcs.Wcsprm.s2p` if necessary. Some attributes that are based on other attributes (such as `~astropy.wcs.Wcsprm.lattyp` on `~astropy.wcs.Wcsprm.ctype`) may not be correct until after `~astropy.wcs.Wcsprm.set` is called. `~astropy.wcs.Wcsprm.set` strips off trailing blanks in all string members. `~astropy.wcs.Wcsprm.set` recognizes the ``NCP`` projection and converts it to the equivalent ``SIN`` projection and it also recognizes ``GLS`` as a synonym for ``SFL``. It does alias translation for the AIPS spectral types (``FREQ-LSR``, ``FELO-HEL``, etc.) but without changing the input header keywords. Raises ------ MemoryError Memory allocation failed. SingularMatrixError Linear transformation matrix is singular. InconsistentAxisTypesError Inconsistent or unrecognized coordinate axis types. ValueError Invalid parameter value. InvalidTransformError Invalid coordinate transformation parameters. InvalidTransformError Ill-conditioned coordinate transformation parameters. """ set_tabprm = """ set() Allocates memory for work arrays. Also sets up the class 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. 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(ps) Sets ``PSi_ma`` keywords for each *i* and *m*. Parameters ---------- ps : sequence of tuple The input must be a sequence of tuples of the form (*i*, *m*, *value*): - *i*: int. Axis number, as in ``PSi_ma``, (i.e. 1-relative) - *m*: int. Parameter number, as in ``PSi_ma``, (i.e. 0-relative) - *value*: string. Parameter value. See also -------- astropy.wcs.Wcsprm.get_ps """ set_pv = """ set_pv(pv) Sets ``PVi_ma`` keywords for each *i* and *m*. Parameters ---------- pv : list of tuple The input must be a sequence of tuples of the form (*i*, *m*, *value*): - *i*: int. Axis number, as in ``PVi_ma``, (i.e. 1-relative) - *m*: int. Parameter number, as in ``PVi_ma``, (i.e. 0-relative) - *value*: float. Parameter value. See also -------- astropy.wcs.Wcsprm.get_pv """ sip = """ Get/set the `~astropy.wcs.Sip` object for performing `SIP`_ distortion correction. """ Sip = """ Sip(*a, b, ap, bp, crpix*) The `~astropy.wcs.Sip` class performs polynomial distortion correction using the `SIP`_ convention in both directions. Parameters ---------- 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 : 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 : 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 : 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 : ndarray The reference pixel as ``double array[2]``. Notes ----- Shupe, D. L., M. Moshir, J. Li, D. Makovoz and R. Narron. 2005. "The SIP Convention for Representing Distortion in FITS Image Headers." ADASS XIV. """ 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 : ndarray Array of focal plane coordinates as ``double array[ncoord][nelem]``. {ORIGIN()} Returns ------- pixcrd : ndarray Returns an array of pixel coordinates as ``double array[ncoord][nelem]``. Raises ------ MemoryError Memory allocation failed. ValueError Invalid coordinate transformation parameters. """ 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 : ndarray Array of pixel coordinates as ``double array[ncoord][nelem]``. {ORIGIN()} Returns ------- foccrd : ndarray Returns an array of focal plane coordinates as ``double array[ncoord][nelem]``. Raises ------ MemoryError Memory allocation failed. ValueError Invalid coordinate transformation parameters. """ spcfix = """ spcfix() -> int Translates AIPS-convention spectral coordinate types. {``FREQ``, ``VELO``, ``FELO``}-{``OBS``, ``HEL``, ``LSR``} (e.g. ``FREQ-LSR``, ``VELO-OBS``, ``FELO-HEL``) Returns ------- success : int Returns ``0`` for success; ``-1`` if no change required. """ spec = """ ``int`` (read-only) The index containing the spectral axis values. """ specsys = """ ``string`` Spectral reference frame (standard of rest), ``SPECSYSa``. See also -------- astropy.wcs.Wcsprm.ssysobs, astropy.wcs.Wcsprm.velosys """ sptr = """ sptr(ctype, i=-1) Translates the spectral axis in a WCS object. For example, a ``FREQ`` axis may be translated into ``ZOPT-F2W`` and vice versa. Parameters ---------- 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 by, and must be consistent with, the first four characters. Wildcarding may be used, i.e. if the final three characters are specified as ``\"???\"``, or if just the eighth character is specified as ``\"?\"``, the correct algorithm code will be substituted and returned. i : int Index of the spectral axis (0-relative). If ``i < 0`` (or not provided), it will be set to the first spectral axis identified from the ``CTYPE`` keyvalues in the FITS header. Raises ------ MemoryError Memory allocation failed. SingularMatrixError Linear transformation matrix is singular. InconsistentAxisTypesError Inconsistent or unrecognized coordinate axis types. ValueError Invalid parameter value. InvalidTransformError Invalid coordinate transformation parameters. InvalidTransformError Ill-conditioned coordinate transformation parameters. InvalidSubimageSpecificationError Invalid subimage specification (no spectral axis). """ ssysobs = """ ``string`` Spectral reference frame. The spectral reference frame in which there is no differential variation in the spectral coordinate across the field-of-view, ``SSYSOBSa``. See also -------- astropy.wcs.Wcsprm.specsys, astropy.wcs.Wcsprm.velosys """ ssyssrc = """ ``string`` Spectral reference frame for redshift. The spectral reference frame (standard of rest) in which the redshift was measured, ``SSYSSRCa``. """ sub = """ sub(axes) Extracts the coordinate description for a subimage from a `~astropy.wcs.WCS` object. 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. `sub` can also add axes to a wcsprm object. The new axes will be created using the defaults set by the Wcsprm constructor which produce a simple, unnamed, linear axis with world coordinates equal to the pixel coordinate. These default values can be changed before invoking `set`. Parameters ---------- axes : int or a sequence. - If an int, include the first *N* axes in their original order. - If a sequence, may contain a combination of image axis numbers (1-relative) or special axis identifiers (see below). Order is significant; ``axes[0]`` is the axis number of the input image that corresponds to the first axis in the subimage, etc. Use an axis number of 0 to create a new axis using the defaults. - If ``0``, ``[]`` or ``None``, do a deep copy. Coordinate axes types may be specified using either strings or special integer constants. The available types are: - ``'longitude'`` / ``WCSSUB_LONGITUDE``: Celestial longitude - ``'latitude'`` / ``WCSSUB_LATITUDE``: Celestial latitude - ``'cubeface'`` / ``WCSSUB_CUBEFACE``: Quadcube ``CUBEFACE`` axis - ``'spectral'`` / ``WCSSUB_SPECTRAL``: Spectral axis - ``'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'``. Returns ------- new_wcs : `~astropy.wcs.WCS` object Raises ------ MemoryError Memory allocation failed. InvalidSubimageSpecificationError Invalid subimage specification (no spectral axis). NonseparableSubimageCoordinateSystemError Non-separable subimage coordinate system. Notes ----- Combinations of subimage axes of particular types may be extracted in the same order as they occur in the input image by combining the integer constants with the 'binary or' (``|``) operator. For example:: wcs.sub([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, the resulting object would have three dimensions. For convenience, ``WCSSUB_CELESTIAL`` is defined as the combination ``WCSSUB_LONGITUDE | WCSSUB_LATITUDE | WCSSUB_CUBEFACE``. The codes may also be negated to extract all but the types specified, for example:: wcs.sub([ WCSSUB_LONGITUDE, WCSSUB_LATITUDE, WCSSUB_CUBEFACE, -(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 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 number of axes in the input image. """ tab = """ ``list of Tabprm`` Tabular coordinate objects. A list of tabular coordinate objects associated with this WCS. """ Tabprm = """ A class to store the information related to tabular coordinates, i.e., coordinates that are defined via a lookup table. This class can not be constructed directly from Python, but instead is returned from `~astropy.wcs.Wcsprm.tab`. """ theta0 = """ ``double`` The native longitude of the fiducial point. The point whose celestial coordinates are given in ``ref[1:2]``. If undefined (NaN) the initialization routine, `~astropy.wcs.Wcsprm.set`, will set this to a projection-specific default. See also -------- astropy.wcs.Wcsprm.phi0 """ to_header = """ to_header(relax=False) `to_header` translates a WCS object into a FITS header. The details of the header depends on context: - If the `~astropy.wcs.Wcsprm.colnum` member is non-zero then a binary table image array header will be produced. - Otherwise, if the `~astropy.wcs.Wcsprm.colax` member is set non-zero then a pixel list header will be produced. - Otherwise, a primary image or image extension header will be produced. The output header will almost certainly differ from the input in a number of respects: 1. The output header only contains WCS-related keywords. In particular, it does not contain syntactically-required keywords such as ``SIMPLE``, ``NAXIS``, ``BITPIX``, or ``END``. 2. Deprecated (e.g. ``CROTAn``) or non-standard usage will be translated to standard (this is partially dependent on whether ``fix`` was applied). 3. Quantities will be converted to the units used internally, basically SI with the addition of degrees. 4. Floating-point quantities may be given to a different decimal precision. 5. Elements of the ``PCi_j`` 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. 6. Additional keywords such as ``WCSAXES``, ``CUNITia``, ``LONPOLEa`` and ``LATPOLEa`` may appear. 7. The original keycomments will be lost, although `~astropy.wcs.Wcsprm.to_header` tries hard to write meaningful comments. 8. Keyword order may be changed. 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.WCS` object. Parameters ---------- relax : bool or int Degree of permissiveness: - `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 write. See :ref:`astropy:relaxwrite` for details. Returns ------- header : str Raw FITS header as a string. """ ttype = """ ``str`` (read-only) ``TTYPEn`` identifying the column of the binary table that contains the wcstab array. """ unitfix = """ unitfix(translate_units='') Translates non-standard ``CUNITia`` keyvalues. For example, ``DEG`` -> ``deg``, also stripping off unnecessary whitespace. Parameters ---------- translate_units : str, 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. Returns ------- success : int Returns ``0`` for success; ``-1`` if no change required. """ velangl = """ ``double`` Velocity angle. The angle in degrees that should be used to decompose an observed velocity into radial and transverse components. An undefined value is represented by NaN. """ velosys = """ ``double`` Relative radial velocity. The relative radial velocity (m/s) between the observer and the selected standard of rest in the direction of the celestial reference coordinate, ``VELOSYSa``. An undefined value is represented by NaN. See also -------- astropy.wcs.Wcsprm.specsys, astropy.wcs.Wcsprm.ssysobs """ velref = """ ``int`` AIPS velocity code. From ``VELREF`` keyword. """ wcs = """ A `~astropy.wcs.Wcsprm` object to perform the basic `wcslib`_ WCS transformation. """ Wcs = """ Wcs(*sip, cpdis, wcsprm, det2im*) Wcs objects amalgamate basic WCS (as provided by `wcslib`_), with `SIP`_ and `distortion paper`_ operations. To perform all distortion corrections and WCS transformation, use ``all_pix2world``. Parameters ---------- sip : `~astropy.wcs.Sip` object or None cpdis : (2,) tuple of `~astropy.wcs.DistortionLookupTable` or None wcsprm : `~astropy.wcs.Wcsprm` 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` 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. However, it does recognize free-format character (NOST 100-2.0, 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 : `~astropy.io.fits.Header`, str, or None. If ``None``, the object will be initialized to default values. 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 provided if *header* is also provided.) relax : bool or int, optional Degree of permissiveness: - `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:`astropy:relaxread` for details. naxis : int, optional The number of world coordinates axes for the object. (*naxis* may only be provided if *header* is `None`.) keysel : sequence of flag bits, optional Vector of flag bits that may be used to restrict the keyword types considered: - ``WCSHDR_IMGHEAD``: Image header keywords. - ``WCSHDR_BIMGARR``: Binary table image array. - ``WCSHDR_PIXLIST``: Pixel list keywords. If zero, there is no restriction. If -1, the underlying wcslib function ``wcspih()`` is called, rather than ``wcstbh()``. colsel : sequence of int A sequence of table column numbers used to restrict the keywords considered. `None` indicates no restriction. Raises ------ MemoryError Memory allocation failed. ValueError Invalid key. KeyError 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). This class can not be constructed directly from Python, but instead is returned from `~astropy.wcs.Wcsprm.wtb`. """ zsource = """ ``double`` The redshift, ``ZSOURCEa``, of the source. 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. """ astropy-astropy-201cddb/astropy/wcs/include/000077500000000000000000000000001507226315300213175ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/wcs/include/.gitignore000066400000000000000000000000311507226315300233010ustar00rootroot00000000000000docstrings.h wcsconfig.h astropy-astropy-201cddb/astropy/wcs/include/astropy_wcs/000077500000000000000000000000001507226315300236745ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/wcs/include/astropy_wcs/astropy_wcs.h000066400000000000000000000007421507226315300264250ustar00rootroot00000000000000/* Author: Michael Droettboom mdroe@stsci.edu */ #ifndef __ASTROPY_WCS_H__ #define __ASTROPY_WCS_H__ /* util.h must be imported first */ #include "pyutil.h" #include "pipeline.h" typedef struct { PyObject_HEAD pipeline_t x; /*@shared@*/ PyObject* py_det2im[2]; /*@null@*/ /*@shared@*/ PyObject* py_sip; /*@shared@*/ PyObject* py_distortion_lookup[2]; /*@null@*/ /*@shared@*/ PyObject* py_wcsprm; } Wcs; #endif /* __ASTROPY_WCS_H__ */ astropy-astropy-201cddb/astropy/wcs/include/astropy_wcs/astropy_wcs_api.h000066400000000000000000000112641507226315300272570ustar00rootroot00000000000000#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 */ astropy-astropy-201cddb/astropy/wcs/include/astropy_wcs/distortion.h000066400000000000000000000051401507226315300262430ustar00rootroot00000000000000/* Author: Michael Droettboom mdroe@stsci.edu */ #ifndef __DISTORTION_H__ #define __DISTORTION_H__ #include "util.h" /* TODO: This is all two-dimensional. Should be made multi-dimensional in the future. */ #define NAXES 2 #define MAXAXES 6 /** A structure to contain the information for a single distortion lookup table */ typedef struct { unsigned int naxis[NAXES]; /* size of distortion image */ double crpix[NAXES]; double crval[NAXES]; double cdelt[NAXES]; /* The data is not "owned" by this structure. It is the user's responsibility to free it. */ /*@shared@*/ /*@null@*/ float *data; } distortion_lookup_t; /** Initialize a lookup table to reasonable default values. */ int distortion_lookup_t_init(distortion_lookup_t* lookup); /** Cleanup after a lookup table. Currently does nothing, but may do something in the future, so please call it when you are done with the lookup table. It does not free the data pointed to be the lookup table -- it is the user's responsibility to free that array. */ void distortion_lookup_t_free(distortion_lookup_t* lookup); /** Lookup the distortion offset for a particular pixel coordinate in the lookup table. @param lookup A lookup table object @param A coordinate pair @return The offset as determined by binlinear interpolation in the lookup table */ double get_distortion_offset( const distortion_lookup_t * const lookup, const double * const img /* [NAXES] */); /** Perform just the distortion table part of the FITS WCS distortion paper. @param naxes @param lookups A pair of lookup table objects @param nelem @param pix [in]: An array of pixel coordinates @param foc [out]: An array of focal plane coordinates @return A wcslib error code */ int p4_pix2foc( const unsigned int naxes, const distortion_lookup_t** lookups, /* [NAXES] */ const unsigned int nelem, const double* pix, /* [NAXES][nelem] */ double *foc /* [NAXES][nelem] */); /** 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 @param lookups A pair of lookup table objects @param nelem @param pix [in]: An array of pixel coordinates @param foc [in/out]: An array of focal plane coordinates @return A wcslib error code */ int p4_pix2deltas( const unsigned int naxes, const distortion_lookup_t** lookups, /* [NAXES] */ const unsigned int nelem, const double* pix, /* [NAXES][nelem] */ double *foc /* [NAXES][nelem] */); #endif /* __DISTORTION_H__ */ astropy-astropy-201cddb/astropy/wcs/include/astropy_wcs/distortion_wrap.h000066400000000000000000000006231507226315300272750ustar00rootroot00000000000000/* Author: Michael Droettboom mdroe@stsci.edu */ #ifndef __DISTORTION_WRAP_H__ #define __DISTORTION_WRAP_H__ #include "pyutil.h" #include "distortion.h" extern PyTypeObject PyDistLookupType; typedef struct { PyObject_HEAD distortion_lookup_t x; /*@null@*/ /*@shared@*/ PyArrayObject* py_data; } PyDistLookup; int _setup_distortion_type( PyObject* m); #endif astropy-astropy-201cddb/astropy/wcs/include/astropy_wcs/isnan.h000066400000000000000000000022361507226315300251600ustar00rootroot00000000000000#ifndef __ISNAN_H__ #define __ISNAN_H__ #include "wcsconfig.h" typedef unsigned WCSLIB_INT64 Int64; #if !defined(U64) #define U64(u) (* (Int64 *) &(u) ) #endif /* U64 */ #if !defined(isnan64) #if !defined(_MSC_VER) #define isnan64(u) \ ( (( U64(u) & 0x7ff0000000000000LL) == 0x7ff0000000000000LL) && ((U64(u) & 0x000fffffffffffffLL) != 0)) ? 1:0 #else #define isnan64(u) \ ( (( U64(u) & 0x7ff0000000000000i64) == 0x7ff0000000000000i64) && ((U64(u) & 0x000fffffffffffffi64) != 0)) ? 1:0 #endif #endif /* isnan64 */ #if !defined(isinf64) #if !defined(_MSC_VER) #define isinf64(u) \ ( (( U64(u) & 0x7ff0000000000000LL) == 0x7ff0000000000000LL) && ((U64(u) & 0x000fffffffffffffLL) == 0)) ? 1:0 #else #define isinf64(u) \ ( (( U64(u) & 0x7ff0000000000000i64) == 0x7ff0000000000000i64) && ((U64(u) & 0x000fffffffffffffi64) == 0)) ? 1:0 #endif #endif /* isinf64 */ #if !defined(isfinite64) #if !defined(_MSC_VER) #define isfinite64(u) \ ( (( U64(u) & 0x7ff0000000000000LL) != 0x7ff0000000000000LL)) ? 1:0 #else #define isfinite64(u) \ ( (( U64(u) & 0x7ff0000000000000i64) != 0x7ff0000000000000i64)) ? 1:0 #endif #endif /* isfinite64 */ #endif /* __ISNAN_H__ */ astropy-astropy-201cddb/astropy/wcs/include/astropy_wcs/pipeline.h000066400000000000000000000044271507226315300256610ustar00rootroot00000000000000/* Author: Michael Droettboom mdroe@stsci.edu */ #ifndef __PIPELINE_H__ #define __PIPELINE_H__ #include "sip.h" #include "distortion.h" #include "wcs.h" typedef struct { distortion_lookup_t* det2im[2]; /*@shared@*/ /*@null@*/ sip_t* sip; distortion_lookup_t* cpdis[2]; /*@shared@*/ /*@null@*/ struct wcsprm* wcs; struct wcserr* err; } pipeline_t; /** Initialize all the values in a pipeline_t to NULL. */ void pipeline_clear( pipeline_t* pipeline); /** Set all the values of a pipeline_t. */ void pipeline_init( pipeline_t* pipeline, /*@shared@*/ distortion_lookup_t** det2im /* [2] */, /*@shared@*/ sip_t* sip, /*@shared@*/ distortion_lookup_t** cpdis /* [2] */, /*@shared@*/ struct wcsprm* wcs); /** Free all the temporary buffers of a pipeline_t. It does not free the underlying sip_t, distortion_lookup_t or wcsprm objects. */ void pipeline_free( pipeline_t* pipeline); /** Perform the entire pipeline from pixel coordinates to world coordinates, in the following order: - Detector to image plane correction (optionally) - SIP distortion correction (optionally) - FITS WCS distortion paper correction (optionally) - wcslib WCS transformation @param ncoord: @param nelem: @param pixcrd [in]: Array of pixel coordinates. @param world [out]: Array of world coordinates (output). @return: A wcslib error code. */ int pipeline_all_pixel2world( pipeline_t* pipeline, const unsigned int ncoord, const unsigned int nelem, const double* const pixcrd /* [ncoord][nelem] */, double* world /* [ncoord][nelem] */); /** Perform just the distortion correction part of the pipeline from pixel coordinates to focal plane coordinates. - Detector to image plane correction (optionally) - SIP distortion correction (optionally) - FITS WCS distortion paper correction (optionally) @param ncoord: @param nelem: @param pixcrd [in]: Array of pixel coordinates. @param foc [out]: Array of focal plane coordinates. @return: A wcslib error code. */ int pipeline_pix2foc( pipeline_t* pipeline, const unsigned int ncoord, const unsigned int nelem, const double* const pixcrd /* [ncoord][nelem] */, double* foc /* [ncoord][nelem] */); #endif astropy-astropy-201cddb/astropy/wcs/include/astropy_wcs/pyutil.h000066400000000000000000000147201507226315300253770ustar00rootroot00000000000000/* Author: Michael Droettboom mdroe@stsci.edu */ #ifndef __PYUTIL_H__ #define __PYUTIL_H__ #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 PyObject* PyArrayProxy_New( PyObject* self, int nd, const npy_intp* dims, int typenum, const void* data); PyObject* PyArrayReadOnlyProxy_New( PyObject* self, int nd, const npy_intp* dims, int typenum, const void* data); /*@null@*/ PyObject * PyStrListProxy_New( PyObject* owner, Py_ssize_t size, Py_ssize_t maxsize, char (*array)[72] ); int _setup_str_list_proxy_type( PyObject* m); static INLINE void offset_c_array( double* value, npy_intp size, double offset) { double* end = value + size; for ( ; value != end; ++value) { *value += offset; } } static INLINE void nan2undefined( double* value, unsigned int nvalues) { double* end = value + nvalues; for ( ; value != end; ++value) { if (isnan64(*value)) { *value = UNDEFINED; } } } static INLINE void undefined2nan( double* value, unsigned int nvalues) { double* end = value + nvalues; for ( ; value != end; ++value) { if (*value == UNDEFINED) { *value = (double)NPY_NAN; } } } void preoffset_array( PyArrayObject* array, int value); void unoffset_array( PyArrayObject* array, int value); void copy_array_to_c_double( PyArrayObject* array, double* dest); void copy_array_to_c_int( PyArrayObject* array, int* dest); /** Returns TRUE if pointer is NULL, and sets Python exception */ int is_null(/*@null@*/ void *); typedef void (*value_fixer_t)(double*, unsigned int); void wcsprm_c2python( /*@null@*/ struct wcsprm* x); void wcsprm_python2c( /*@null@*/ struct wcsprm* x); /*************************************************************************** * Exceptions * ***************************************************************************/ extern PyObject* WcsExc_SingularMatrix; extern PyObject* WcsExc_InconsistentAxisTypes; extern PyObject* WcsExc_InvalidTransform; extern PyObject* WcsExc_InvalidCoordinate; extern PyObject* WcsExc_NoSolution; 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 * wcs_errmsg. */ extern PyObject** wcs_errexc[14]; #define WCS_ERRMSG_MAX 14 #define WCSFIX_ERRMSG_MAX 11 int _define_exceptions(PyObject* m); const char* wcslib_get_error_message(int stat); void wcserr_to_python_exc(const struct wcserr *err); void wcs_to_python_exc(const struct wcsprm *wcs); void wcshdr_err_to_python_exc(int status, const struct wcsprm *wcs); void wcserr_fix_to_python_exc(const struct wcserr *err); /*************************************************************************** Property helpers ***************************************************************************/ static INLINE int check_delete( const char* propname, PyObject* value) { if (value == NULL) { PyErr_Format(PyExc_TypeError, "'%s' can not be deleted", propname); return -1; } return 0; } static INLINE PyObject* get_string( /*@unused@*/ const char* propname, const char* value) { return PyUnicode_FromString(value); } int set_string( const char* propname, PyObject* value, char* dest, Py_ssize_t maxlen); static INLINE PyObject* get_bool( /*@unused@*/ const char* propname, long value) { return PyBool_FromLong(value); } int set_bool( const char* propname, PyObject* value, int* dest); static INLINE PyObject* get_int( /*@unused@*/ const char* propname, long value) { return PyLong_FromLong(value); } int set_int( const char* propname, PyObject* value, int* dest); static INLINE PyObject* get_double( const char* propname, double value) { return PyFloat_FromDouble(value); } int set_double( const char* propname, PyObject* value, double* dest); /*@null@*/ static INLINE PyObject* get_double_array( /*@unused@*/ const char* propname, double* value, int ndims, const npy_intp* dims, /*@shared@*/ PyObject* owner) { return PyArrayProxy_New(owner, ndims, dims, NPY_DOUBLE, value); } /*@null@*/ static INLINE PyObject* get_double_array_readonly( /*@unused@*/ const char* propname, double* value, int ndims, const npy_intp* dims, /*@shared@*/ PyObject* owner) { return PyArrayReadOnlyProxy_New(owner, ndims, dims, NPY_DOUBLE, value); } int set_double_array( const char* propname, PyObject* value, int ndims, const npy_intp* dims, double* dest); /*@null@*/ static INLINE PyObject* get_int_array( /*@unused@*/ const char* propname, int* value, int ndims, const npy_intp* dims, /*@shared@*/ PyObject* owner) { return PyArrayProxy_New(owner, ndims, dims, NPY_INT, value); } int set_int_array( const char* propname, PyObject* value, int ndims, const npy_intp* dims, int* dest); static INLINE PyObject* get_str_list( /*@unused@*/ const char* propname, char (*array)[72], Py_ssize_t len, Py_ssize_t maxlen, PyObject* owner) { return PyStrListProxy_New(owner, len, maxlen, array); } int set_str_list( const char* propname, PyObject* value, Py_ssize_t len, Py_ssize_t maxlen, char (*dest)[72]); PyObject* get_pscards( const char* propname, struct pscard* ps, int nps); int set_pscards( const char* propname, PyObject* value, struct pscard** ps, int *nps, int *npsmax); PyObject* get_pvcards( const char* propname, struct pvcard* pv, int npv); int set_pvcards( const char* propname, PyObject* value, struct pvcard** pv, int *npv, int *npvmax); PyObject* get_deepcopy( PyObject* obj, PyObject* memo); /*************************************************************************** Miscellaneous helper functions ***************************************************************************/ int parse_unsafe_unit_conversion_spec( const char* arg, int* ctrl); #endif /* __PYUTIL_H__ */ astropy-astropy-201cddb/astropy/wcs/include/astropy_wcs/sip.h000066400000000000000000000101601507226315300246360ustar00rootroot00000000000000/* Author: Michael Droettboom mdroe@stsci.edu */ #ifndef __SIP_H__ #define __SIP_H__ #include "util.h" typedef struct { unsigned int a_order; /*@null@*/ /*@shared@*/ double* a; unsigned int b_order; /*@null@*/ /*@shared@*/ double* b; unsigned int ap_order; /*@null@*/ /*@shared@*/ double* ap; unsigned int bp_order; /*@null@*/ /*@shared@*/ double* bp; double crpix[2]; /*@null@*/ double* scratch; struct wcserr* err; } sip_t; /** Sets all the values of the sip_t structure to NULLs or zeros. */ void sip_clear(sip_t* sip); /** Set the values of the sip_t structure. The values expected are all exactly as defined in the FITS SIP header keywords. The arrays/matrices are all *copied* into the SIP struct. To free the memory that sip_t allocates for itself, call sip_free. @param a_order: The order of the A_i_j matrix @param a: The A_i_j array, which must be of size [a_order+1][a_order+1] @param b_order: The order of the B_i_j matrix @param b: The B_i_j array, which must be of size [b_order+1][b_order+1] @param ap_order: The order of the AP_i_j matrix @param ap: The AP_i_j array, which must be of size [ap_order+1][ap_order+1] @param bp_order: The order of the BP_i_j matrix @param bp: The BP_i_j array, which must be of size [bp_order+1][bp_order+1] @param crpix: The position of the reference pixel */ int sip_init( sip_t* sip, const unsigned int a_order, const double* a, const unsigned int b_order, const double* b, const unsigned int ap_order, const double* ap, const unsigned int bp_order, const double* bp, const double* crpix /* [2] */); /** Frees the memory allocated for the sip_t struct. */ void sip_free(sip_t* sip); /** Converts pixel coordinates to focal plane coordinates using the SIP polynomial distortion convention, and the values stored in the sip_t struct. @param naxes @param nelem @param pix [in]: An array of pixel coordinates @param foc [out]: An array of focal plane coordinates @return A wcslib error code */ int sip_pix2foc( const sip_t* sip, const unsigned int naxes, const unsigned int nelem, const double* pix /* [NAXES][nelem] */, double* foc /* [NAXES][nelem] */); /** Computes the offset deltas necessary to convert pixel coordinates to focal plane coordinates using the SIP polynomial distortion convention, and the values stored in the sip_t struct. The deltas are added to the existing values in pix. @param naxes @param nelem @param pix [in]: An array of pixel coordinates @param foc [in/out]: An array of deltas, that when added to pix results in focal plane coordinates. @return A wcslib error code */ int sip_pix2deltas( const sip_t* sip, const unsigned int naxes, const unsigned int nelem, const double* pix /* [NAXES][nelem] */, double* foc /* [NAXES][nelem] */); /** Adds the offset deltas necessary to convert focal plane coordinates to pixel coordinates using the SIP polynomial distortion convention, and the values stored in the sip_t struct. The deltas are added to the existing values in pix. @param naxes @param nelem @param foc [in]: An array of focal plane coordinates @param pix [in/out]: An array of pixel coordinates @return A wcslib error code */ int sip_foc2pix( const sip_t* sip, const unsigned int naxes, const unsigned int nelem, const double* foc /* [NAXES][nelem] */, double* pix /* [NAXES][nelem] */); /** Computes the offset deltas necessary to convert focal plane coordinates to pixel coordinates using the SIP polynomial distortion convention, and the values stored in the sip_t struct. The deltas are added to the existing values in foc. @param naxes @param nelem @param foc [in]: An array of focal plane coordinates @param foc [in/out]: An array of deltas, that when added to pix results in focal plane coordinates. @return A wcslib error code */ int sip_foc2deltas( const sip_t* sip, const unsigned int naxes, const unsigned int nelem, const double* foc /* [NAXES][nelem] */, double* deltas /* [NAXES][nelem] */); #endif astropy-astropy-201cddb/astropy/wcs/include/astropy_wcs/sip_wrap.h000066400000000000000000000004261507226315300256730ustar00rootroot00000000000000/* Author: Michael Droettboom mdroe@stsci.edu */ #ifndef __SIP_WRAP_H__ #define __SIP_WRAP_H__ #include "pyutil.h" #include "sip.h" extern PyTypeObject PySipType; typedef struct { PyObject_HEAD sip_t x; } PySip; int _setup_sip_type( PyObject* m); #endif astropy-astropy-201cddb/astropy/wcs/include/astropy_wcs/str_list_proxy.h000066400000000000000000000014441507226315300271540ustar00rootroot00000000000000/* Author: Michael Droettboom mdroe@stsci.edu */ #ifndef __STR_LIST_PROXY_H__ #define __STR_LIST_PROXY_H__ #include "pyutil.h" /*************************************************************************** * List-of-strings proxy object * * A Python object that looks like a list of strings, but is back by a C * char * list[]; ***************************************************************************/ typedef int (*str_verify_fn)(const char *); /*@null@*/ PyObject * PyStrListProxy_New( PyObject* owner, Py_ssize_t size, Py_ssize_t maxsize, 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); #endif /* __STR_LIST_PROXY_H__ */ astropy-astropy-201cddb/astropy/wcs/include/astropy_wcs/unit_list_proxy.h000066400000000000000000000016701507226315300273240ustar00rootroot00000000000000/* 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 _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) { return PyUnitListProxy_New(owner, len, array); } int set_unit_list( PyObject *owner, const char* propname, PyObject* value, Py_ssize_t len, char (*dest)[72]); #endif /* __UNIT_LIST_PROXY_H__ */ astropy-astropy-201cddb/astropy/wcs/include/astropy_wcs/util.h000066400000000000000000000010321507226315300250160ustar00rootroot00000000000000/* Author: Michael Droettboom mdroe@stsci.edu */ #ifndef __UTIL_H__ #define __UTIL_H__ #ifdef __SUNPRO_C #define INLINE #endif #ifdef _MSC_VER #define INLINE __inline #endif #ifndef INLINE #define INLINE inline #endif #include #include #include "isnan.h" #undef CLAMP #define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x))) void set_invalid_to_nan( const int ncoord, const int nelem, double* const data, const int* const stat); #endif /* __UTIL_H__ */ astropy-astropy-201cddb/astropy/wcs/include/astropy_wcs/wcslib_auxprm_wrap.h000066400000000000000000000005141507226315300277550ustar00rootroot00000000000000#ifndef __WCSLIB_AUXPRM_WRAP_H__ #define __WCSLIB_AUXPRM_WRAP_H__ #include "pyutil.h" #include "wcs.h" extern PyTypeObject 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 astropy-astropy-201cddb/astropy/wcs/include/astropy_wcs/wcslib_celprm_wrap.h000066400000000000000000000005721507226315300277270ustar00rootroot00000000000000#ifndef __WCSLIB_CELPRM_WRAP_H__ #define __WCSLIB_CELPRM_WRAP_H__ #include "pyutil.h" #include "wcs.h" extern PyTypeObject 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 astropy-astropy-201cddb/astropy/wcs/include/astropy_wcs/wcslib_prjprm_wrap.h000066400000000000000000000005661507226315300277620ustar00rootroot00000000000000#ifndef __WCSLIB_PRJPRM_WRAP_H__ #define __WCSLIB_PRJPRM_WRAP_H__ #include "pyutil.h" #include "wcs.h" extern PyTypeObject 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 astropy-astropy-201cddb/astropy/wcs/include/astropy_wcs/wcslib_tabprm_wrap.h000066400000000000000000000006101507226315300277230ustar00rootroot00000000000000/* Author: Michael Droettboom mdroe@stsci.edu */ #ifndef __WCSLIB_TABPRM_WRAP_H__ #define __WCSLIB_TABPRM_WRAP_H__ #include "pyutil.h" #include "wcs.h" extern PyTypeObject PyTabprmType; typedef struct { PyObject_HEAD struct tabprm* x; PyObject* owner; } PyTabprm; PyTabprm* PyTabprm_cnew(PyObject* wcsprm, struct tabprm* x); int _setup_tabprm_type(PyObject* m); #endif astropy-astropy-201cddb/astropy/wcs/include/astropy_wcs/wcslib_units_wrap.h000066400000000000000000000010201507226315300275740ustar00rootroot00000000000000/* Author: Michael Droettboom mdroe@stsci.edu */ #ifndef __WCSLIB_UNITS_WRAP_H__ #define __WCSLIB_UNITS_WRAP_H__ #include "pyutil.h" #include "wcsunits.h" extern PyTypeObject PyUnitsType; typedef struct { PyObject_HEAD char have[80]; char want[80]; double scale; double offset; double power; } PyUnits; PyUnits* PyUnits_cnew( const char* const have, const char* const want, const double scale, const double offset, const double power); int _setup_units_type(PyObject* m); #endif astropy-astropy-201cddb/astropy/wcs/include/astropy_wcs/wcslib_wrap.h000066400000000000000000000007261507226315300263660ustar00rootroot00000000000000/* Author: Michael Droettboom */ #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); int _update_wtbarr_from_hdulist(PyObject *hdulist, struct wtbarr *wtb); void _set_wtbarr_callback(PyObject* callback); #endif astropy-astropy-201cddb/astropy/wcs/include/astropy_wcs/wcslib_wtbarr_wrap.h000066400000000000000000000006101507226315300277370ustar00rootroot00000000000000/* Author: Michael Droettboom mdroe@stsci.edu */ #ifndef __WCSLIB_WTBARR_WRAP_H__ #define __WCSLIB_WTBARR_WRAP_H__ #include "pyutil.h" #include "wcs.h" extern PyTypeObject PyWtbarrType; typedef struct { PyObject_HEAD struct wtbarr* x; PyObject* owner; } PyWtbarr; PyWtbarr* PyWtbarr_cnew(PyObject* wcsprm, struct wtbarr* x); int _setup_wtbarr_type(PyObject* m); #endif astropy-astropy-201cddb/astropy/wcs/include/astropy_wcs_api.h000066400000000000000000000001511507226315300246730ustar00rootroot00000000000000#error "Since version 0.3, astropy.wcs public API should be imported as \"astropy_wcs/astropy_wcs_api.h" astropy-astropy-201cddb/astropy/wcs/include/wcslib/000077500000000000000000000000001507226315300226025ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/wcs/include/wcslib/.empty000066400000000000000000000000001507226315300237270ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/wcs/include/wcslib/.gitignore000066400000000000000000000001371507226315300245730ustar00rootroot00000000000000# We copy header files here from `cextern/wcslib/C`, but they should # be ignored by git. *.h astropy-astropy-201cddb/astropy/wcs/setup_package.py000066400000000000000000000233501507226315300230640ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst 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 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 = "8.4" def b(s): 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") 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. 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 hardcode for them. """ try: try: import ctypes except ImportError: raise ValueError() if ctypes.sizeof(ctypes.c_longlong) == 8: return "long long int" elif ctypes.sizeof(ctypes.c_long) == 8: return "long int" elif ctypes.sizeof(ctypes.c_int) == 8: return "int" else: raise ValueError() except ValueError: return "long long int" def write_wcsconfig_h(paths): """ Writes out the wcsconfig.h header with local configuration. """ 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 {WCSVERSION} /* 64-bit integer data type. */ #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 on code that *uses* astropy.wcs, in addition to astropy.wcs itself. */ #if defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__) || defined (__MINGW64__) #ifndef YY_NO_UNISTD_H #define YY_NO_UNISTD_H #endif #ifndef _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_WARNINGS #endif #ifndef _NO_OLDNAMES #define _NO_OLDNAMES #endif #ifndef NO_OLDNAMES #define NO_OLDNAMES #endif #ifndef __STDC__ #define __STDC__ 1 #endif #endif """ ) content = h_file.getvalue().encode("ascii") for path in paths: write_if_different(path, content) ###################################################################### # GENERATE DOCSTRINGS IN C def generate_c_docstrings(): docstrings = import_file(os.path.join(WCSROOT, "docstrings.py")) docstrings = docstrings.__dict__ keys = [ 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" h_file = io.StringIO() h_file.write( """/* DO NOT EDIT! This file is autogenerated by astropy/wcs/setup_package.py. To edit its contents, edit astropy/wcs/docstrings.py */ #ifndef __DOCSTRINGS_H__ #define __DOCSTRINGS_H__ """ ) for key in keys: val = docs[key] h_file.write(f"extern char doc_{key}[{len(val)}];\n") h_file.write("\n#endif\n\n") write_if_different( join(WCSROOT, "include", "astropy_wcs", "docstrings.h"), h_file.getvalue().encode("utf-8"), ) c_file = io.StringIO() c_file.write( """/* DO NOT EDIT! This file is autogenerated by astropy/wcs/setup_package.py. To edit its contents, edit astropy/wcs/docstrings.py The weirdness here with strncpy is because some C compilers, notably MSVC, do not support string literals greater than 256 characters. */ #include #include "astropy_wcs/docstrings.h" """ ) for key in keys: val = docs[key] 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) 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) 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") 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(): generate_c_docstrings() ###################################################################### # DISTUTILS SETUP 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", "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)] astropy-astropy-201cddb/astropy/wcs/src/000077500000000000000000000000001507226315300204635ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/wcs/src/.gitignore000066400000000000000000000001541507226315300224530ustar00rootroot00000000000000# Don't ignore *.c files in this directory tree. We don't have any # Cython files here. !*.c docstrings.c astropy-astropy-201cddb/astropy/wcs/src/astropy_wcs.c000066400000000000000000000553271507226315300232200ustar00rootroot00000000000000/* Author: Michael Droettboom mdroe@stsci.edu */ #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 */ static int Wcs_traverse( Wcs* self, visitproc visit, void* arg) { Py_VISIT(self->py_det2im[0]); Py_VISIT(self->py_det2im[1]); Py_VISIT(self->py_sip); Py_VISIT(self->py_distortion_lookup[0]); Py_VISIT(self->py_distortion_lookup[1]); Py_VISIT(self->py_wcsprm); return 0; } static int Wcs_clear( Wcs* self) { 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; } static void Wcs_dealloc( Wcs* self) { PyObject_GC_UnTrack(self); Wcs_clear(self); pipeline_free(&self->x); Py_TYPE(self)->tp_free((PyObject*)self); } /*@null@*/ static PyObject * Wcs_new( PyTypeObject* type, /*@unused@*/ PyObject* args, /*@unused@*/ PyObject* kwds) { Wcs* self; self = (Wcs*)type->tp_alloc(type, 0); if (self != NULL) { pipeline_clear(&self->x); self->py_det2im[0] = NULL; self->py_det2im[1] = NULL; self->py_sip = NULL; self->py_distortion_lookup[0] = NULL; self->py_distortion_lookup[1] = NULL; self->py_wcsprm = NULL; } return (PyObject*)self; } static int Wcs_init( Wcs* self, PyObject* args, /*@unused@*/ PyObject* kwds) { size_t i; PyObject* py_sip; PyObject* py_wcsprm; PyObject* py_distortion_lookup[2]; PyObject* py_det2im[2]; if (!PyArg_ParseTuple (args, "O(OO)O(OO):Wcs.__init__", &py_sip, &py_distortion_lookup[0], &py_distortion_lookup[1], &py_wcsprm, &py_det2im[0], &py_det2im[1])) { return -1; } /* 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)) { 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)) { 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)) { 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)) { 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); } return 0; } /*@null@*/ static PyObject* Wcs_all_pix2world( Wcs* self, PyObject* args, PyObject* kwds) { int naxis = 2; PyObject* pixcrd_obj = NULL; int origin = 1; PyArrayObject* pixcrd = NULL; PyArrayObject* world = NULL; int status = -1; const char* keywords[] = { "pixcrd", "origin", NULL }; if (!PyArg_ParseTupleAndKeywords( args, kwds, "Oi:all_pix2world", (char **)keywords, &pixcrd_obj, &origin)) { return NULL; } naxis = self->x.wcs->naxis; pixcrd = (PyArrayObject*)PyArray_ContiguousFromAny(pixcrd_obj, NPY_DOUBLE, 2, 2); if (pixcrd == NULL) { return NULL; } if (PyArray_DIM(pixcrd, 1) < naxis) { PyErr_Format( PyExc_RuntimeError, "Input array must be 2-dimensional, where the second dimension >= %d", naxis); goto exit; } world = (PyArrayObject*)PyArray_SimpleNew(2, PyArray_DIMS(pixcrd), NPY_DOUBLE); if (world == NULL) { goto exit; } /* 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), (double*)PyArray_DATA(pixcrd), (double*)PyArray_DATA(world)); wcsprm_c2python(self->x.wcs); unoffset_array(pixcrd, origin); Py_END_ALLOW_THREADS /* unoffset_array(world, origin); */ exit: Py_XDECREF(pixcrd); if (status == 0 || status == 8) { return (PyObject*)world; } else { Py_XDECREF(world); if (status == -1) { PyErr_SetString( PyExc_ValueError, "Wrong number of dimensions in input array. Expected 2."); return NULL; } else { if (status == -1) { /* exception already set */ return NULL; } else { wcserr_to_python_exc(self->x.err); return NULL; } } } } /*@null@*/ static PyObject* Wcs_p4_pix2foc( Wcs* self, PyObject* args, PyObject* kwds) { PyObject* pixcrd_obj = NULL; int origin = 1; PyArrayObject* pixcrd = NULL; PyArrayObject* foccrd = NULL; int status = -1; const char* keywords[] = { "pixcrd", "origin", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "Oi:p4_pix2foc", (char **)keywords, &pixcrd_obj, &origin)) { return NULL; } if (self->x.cpdis[0] == NULL && self->x.cpdis[1] == NULL) { Py_INCREF(pixcrd_obj); return pixcrd_obj; } pixcrd = (PyArrayObject*)PyArray_ContiguousFromAny(pixcrd_obj, NPY_DOUBLE, 2, 2); if (pixcrd == NULL) { return NULL; } if (PyArray_DIM(pixcrd, 1) != NAXES) { PyErr_SetString(PyExc_ValueError, "Pixel array must be an Nx2 array"); goto exit; } foccrd = (PyArrayObject*)PyArray_SimpleNew(2, PyArray_DIMS(pixcrd), NPY_DOUBLE); if (foccrd == NULL) { status = 2; goto exit; } Py_BEGIN_ALLOW_THREADS preoffset_array(pixcrd, origin); status = p4_pix2foc(2, (void *)self->x.cpdis, (unsigned int)PyArray_DIM(pixcrd, 0), (double*)PyArray_DATA(pixcrd), (double*)PyArray_DATA(foccrd)); unoffset_array(pixcrd, origin); unoffset_array(foccrd, origin); Py_END_ALLOW_THREADS exit: Py_XDECREF(pixcrd); if (status == 0) { return (PyObject*)foccrd; } else { Py_XDECREF(foccrd); if (status == -1) { /* Exception already set */ return NULL; } else { PyErr_SetString(PyExc_MemoryError, "NULL pointer passed"); return NULL; } } } /*@null@*/ static PyObject* Wcs_det2im( Wcs* self, PyObject* args, PyObject* kwds) { PyObject* detcrd_obj = NULL; int origin = 1; PyArrayObject* detcrd = NULL; PyArrayObject* imcrd = NULL; int status = -1; const char* keywords[] = { "detcrd", "origin", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "Oi:det2im", (char **)keywords, &detcrd_obj, &origin)) { return NULL; } if (self->x.det2im[0] == NULL && self->x.det2im[1] == NULL) { Py_INCREF(detcrd_obj); return detcrd_obj; } detcrd = (PyArrayObject*)PyArray_ContiguousFromAny(detcrd_obj, NPY_DOUBLE, 2, 2); if (detcrd == NULL) { return NULL; } if (PyArray_DIM(detcrd, 1) != NAXES) { PyErr_SetString(PyExc_ValueError, "Pixel array must be an Nx2 array"); goto exit; } imcrd = (PyArrayObject*)PyArray_SimpleNew(2, PyArray_DIMS(detcrd), NPY_DOUBLE); if (imcrd == NULL) { status = 2; goto exit; } Py_BEGIN_ALLOW_THREADS preoffset_array(detcrd, origin); status = p4_pix2foc(2, (void *)self->x.det2im, (unsigned int)PyArray_DIM(detcrd, 0), (double*)PyArray_DATA(detcrd), (double*)PyArray_DATA(imcrd)); unoffset_array(detcrd, origin); unoffset_array(imcrd, origin); Py_END_ALLOW_THREADS exit: Py_XDECREF(detcrd); if (status == 0) { return (PyObject*)imcrd; } else { Py_XDECREF(imcrd); if (status == -1) { /* Exception already set */ return NULL; } else { PyErr_SetString(PyExc_MemoryError, "NULL pointer passed"); return NULL; } } } /*@null@*/ static PyObject* Wcs_pix2foc( Wcs* self, PyObject* args, PyObject* kwds) { PyObject* pixcrd_obj = NULL; int origin = 1; PyArrayObject* pixcrd = NULL; PyArrayObject* foccrd = NULL; int status = -1; const char* keywords[] = { "pixcrd", "origin", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "Oi:pix2foc", (char **)keywords, &pixcrd_obj, &origin)) { return NULL; } pixcrd = (PyArrayObject*)PyArray_ContiguousFromAny(pixcrd_obj, NPY_DOUBLE, 2, 2); if (pixcrd == NULL) { return NULL; } if (PyArray_DIM(pixcrd, 1) != NAXES) { PyErr_SetString(PyExc_ValueError, "Pixel array must be an Nx2 array"); goto _exit; } foccrd = (PyArrayObject*)PyArray_SimpleNew(2, PyArray_DIMS(pixcrd), NPY_DOUBLE); if (foccrd == NULL) { goto _exit; } Py_BEGIN_ALLOW_THREADS preoffset_array(pixcrd, origin); status = pipeline_pix2foc(&self->x, (unsigned int)PyArray_DIM(pixcrd, 0), (unsigned int)PyArray_DIM(pixcrd, 1), (double*)PyArray_DATA(pixcrd), (double*)PyArray_DATA(foccrd)); unoffset_array(pixcrd, origin); unoffset_array(foccrd, origin); Py_END_ALLOW_THREADS _exit: Py_XDECREF(pixcrd); if (status == 0) { return (PyObject*)foccrd; } else { Py_XDECREF(foccrd); if (status == -1) { /* Exception already set */ return NULL; } else { wcserr_to_python_exc(self->x.err); return NULL; } } } /*@null@*/ static PyObject* Wcs_get_wcs( Wcs* self, /*@unused@*/ void* closure) { if (self->py_wcsprm) { Py_INCREF(self->py_wcsprm); return self->py_wcsprm; } Py_INCREF(Py_None); return Py_None; } static int Wcs_set_wcs( Wcs* self, /*@shared@*/ PyObject* value, /*@unused@*/ void* closure) { Py_CLEAR(self->py_wcsprm); self->x.wcs = NULL; if (value != NULL && value != Py_None) { if (!PyObject_TypeCheck(value, &PyWcsprmType)) { PyErr_SetString(PyExc_TypeError, "wcs must be Wcsprm object"); return -1; } Py_INCREF(value); self->py_wcsprm = value; self->x.wcs = &(((PyWcsprm*)value)->x); } return 0; } static PyObject* Wcs_get_cpdis1( Wcs* self, /*@unused@*/ void* closure) { if (self->py_distortion_lookup[0]) { Py_INCREF(self->py_distortion_lookup[0]); return self->py_distortion_lookup[0]; } Py_INCREF(Py_None); return Py_None; } static int Wcs_set_cpdis1( Wcs* self, /*@shared@*/ PyObject* value, /*@unused@*/ void* closure) { Py_CLEAR(self->py_distortion_lookup[0]); self->x.cpdis[0] = NULL; if (value != NULL && value != Py_None) { if (!PyObject_TypeCheck(value, &PyDistLookupType)) { PyErr_SetString(PyExc_TypeError, "cpdis1 must be DistortionLookupTable object"); return -1; } Py_INCREF(value); self->py_distortion_lookup[0] = value; self->x.cpdis[0] = &(((PyDistLookup*)value)->x); } return 0; } /*@shared@*/ static PyObject* Wcs_get_cpdis2( Wcs* self, /*@unused@*/ void* closure) { if (self->py_distortion_lookup[1]) { Py_INCREF(self->py_distortion_lookup[1]); return self->py_distortion_lookup[1]; } Py_INCREF(Py_None); return Py_None; } static int Wcs_set_cpdis2( Wcs* self, /*@shared@*/ PyObject* value, /*@unused@*/ void* closure) { Py_CLEAR(self->py_distortion_lookup[1]); self->x.cpdis[1] = NULL; if (value != NULL && value != Py_None) { if (!PyObject_TypeCheck(value, &PyDistLookupType)) { PyErr_SetString(PyExc_TypeError, "cpdis2 must be DistortionLookupTable object"); return -1; } Py_INCREF(value); self->py_distortion_lookup[1] = value; self->x.cpdis[1] = &(((PyDistLookup*)value)->x); } return 0; } static PyObject* Wcs_get_det2im1( Wcs* self, /*@unused@*/ void* closure) { if (self->py_det2im[0]) { Py_INCREF(self->py_det2im[0]); return self->py_det2im[0]; } Py_INCREF(Py_None); return Py_None; } static int Wcs_set_det2im1( Wcs* self, /*@shared@*/ PyObject* value, /*@unused@*/ void* closure) { Py_CLEAR(self->py_det2im[0]); self->x.det2im[0] = NULL; if (value != NULL && value != Py_None) { if (!PyObject_TypeCheck(value, &PyDistLookupType)) { PyErr_SetString(PyExc_TypeError, "det2im1 must be DistortionLookupTable object"); return -1; } Py_INCREF(value); self->py_det2im[0] = value; self->x.det2im[0] = &(((PyDistLookup*)value)->x); } return 0; } /*@shared@*/ static PyObject* Wcs_get_det2im2( Wcs* self, /*@unused@*/ void* closure) { if (self->py_det2im[1]) { Py_INCREF(self->py_det2im[1]); return self->py_det2im[1]; } Py_INCREF(Py_None); return Py_None; } static int Wcs_set_det2im2( Wcs* self, /*@shared@*/ PyObject* value, /*@unused@*/ void* closure) { Py_CLEAR(self->py_det2im[1]); self->x.det2im[1] = NULL; if (value != NULL && value != Py_None) { if (!PyObject_TypeCheck(value, &PyDistLookupType)) { PyErr_SetString(PyExc_TypeError, "det2im2 must be DistortionLookupTable object"); return -1; } Py_INCREF(value); self->py_det2im[1] = value; self->x.det2im[1] = &(((PyDistLookup*)value)->x); } return 0; } /*@shared@*/ static PyObject* Wcs_get_sip( Wcs* self, /*@unused@*/ void* closure) { if (self->py_sip) { Py_INCREF(self->py_sip); return self->py_sip; } Py_INCREF(Py_None); return Py_None; } static int Wcs_set_sip( Wcs* self, /*@shared@*/ PyObject* value, /*@unused@*/ void* closure) { Py_CLEAR(self->py_sip); self->x.sip = NULL; if (value != NULL && value != Py_None) { if (!PyObject_TypeCheck(value, &PySipType)) { PyErr_SetString(PyExc_TypeError, "sip must be Sip object"); return -1; } Py_INCREF(value); self->py_sip = value; self->x.sip = &(((PySip*)value)->x); } return 0; } static PyObject* _sanity_check( PyObject* self, PyObject* args, PyObject* kwds) { if (sizeof(WCSLIB_INT64) != 8) { Py_INCREF(Py_False); return Py_False; } Py_INCREF(Py_True); return Py_True; } /*************************************************************************** * Wcs definition structures */ static PyGetSetDef Wcs_getset[] = { {"det2im1", (getter)Wcs_get_det2im1, (setter)Wcs_set_det2im1, (char *)doc_det2im1}, {"det2im2", (getter)Wcs_get_det2im2, (setter)Wcs_set_det2im2, (char *)doc_det2im2}, {"cpdis1", (getter)Wcs_get_cpdis1, (setter)Wcs_set_cpdis1, (char *)doc_cpdis1}, {"cpdis2", (getter)Wcs_get_cpdis2, (setter)Wcs_set_cpdis2, (char *)doc_cpdis2}, {"sip", (getter)Wcs_get_sip, (setter)Wcs_set_sip, (char *)doc_sip}, {"wcs", (getter)Wcs_get_wcs, (setter)Wcs_set_wcs, (char *)doc_wcs}, {NULL} }; static PyMethodDef Wcs_methods[] = { {"_all_pix2world", (PyCFunction)Wcs_all_pix2world, METH_VARARGS|METH_KEYWORDS, doc_all_pix2world}, {"_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}, {NULL} }; 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 = { PyVarObject_HEAD_INIT(NULL, 0) "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 */ }; /*************************************************************************** * Module-level ***************************************************************************/ int _setup_wcs_type( PyObject* m) { if (PyType_Ready(&WcsType) < 0) return -1; Py_INCREF(&WcsType); return PyModule_AddObject(m, "_Wcs", (PyObject *)&WcsType); } struct module_state { /* The Sun compiler can't handle empty structs */ #if defined(__SUNPRO_C) || defined(_MSC_VER) int _dummy; #endif }; static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, "_wcs", NULL, sizeof(struct module_state), module_methods, NULL, NULL, NULL, NULL }; PyMODINIT_FUNC PyInit__wcs(void) { PyObject* m; wcs_errexc[0] = NULL; /* Success */ wcs_errexc[1] = &PyExc_MemoryError; /* Null wcsprm pointer passed */ wcs_errexc[2] = &PyExc_MemoryError; /* Memory allocation failed */ wcs_errexc[3] = &WcsExc_SingularMatrix; /* Linear transformation matrix is singular */ wcs_errexc[4] = &WcsExc_InconsistentAxisTypes; /* Inconsistent or unrecognized coordinate axis types */ wcs_errexc[5] = &PyExc_ValueError; /* Invalid parameter value */ wcs_errexc[6] = &WcsExc_InvalidTransform; /* Invalid coordinate transformation parameters */ wcs_errexc[7] = &WcsExc_InvalidTransform; /* Ill-conditioned coordinate transformation parameters */ wcs_errexc[8] = &WcsExc_InvalidCoordinate; /* One or more of the pixel coordinates were invalid, */ /* as indicated by the stat vector */ wcs_errexc[9] = &WcsExc_InvalidCoordinate; /* One or more of the world coordinates were invalid, */ /* as indicated by the stat vector */ wcs_errexc[10] = &WcsExc_InvalidCoordinate; /* Invalid world coordinate */ wcs_errexc[11] = &WcsExc_NoSolution; /* no solution found in the specified interval */ wcs_errexc[12] = &WcsExc_InvalidSubimageSpecification; /* Invalid subimage specification (no spectral axis) */ wcs_errexc[13] = &WcsExc_NonseparableSubimageCoordinateSystem; /* Non-separable subimage coordinate system */ m = PyModule_Create(&moduledef); if (m == NULL) return NULL; import_array(); 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_wtbarr_type(m) || _setup_distortion_type(m) || _setup_sip_type(m) || _setup_wcs_type(m) || _define_exceptions(m)) { Py_DECREF(m); return NULL; } #ifdef HAVE_WCSLIB_VERSION if (PyModule_AddStringConstant(m, "__version__", wcslib_version(NULL))) { return NULL; } #else if (PyModule_AddStringConstant(m, "__version__", "4.x")) { return NULL; } #endif return m; } astropy-astropy-201cddb/astropy/wcs/src/astropy_wcs_api.c000066400000000000000000000025371507226315300240440ustar00rootroot00000000000000#define NO_IMPORT_ARRAY #include "astropy_wcs/astropy_wcs_api.h" int AstropyWcs_GetCVersion(void) { return REVISION; } void* AstropyWcs_API[] = { /* 0 */ (void *)AstropyWcs_GetCVersion, /* pyutil.h */ /* 1 */ (void *)wcsprm_python2c, /* 2 */ (void *)wcsprm_c2python, /* distortion.h */ /* 3 */ (void *)distortion_lookup_t_init, /* 4 */ (void *)distortion_lookup_t_free, /* 5 */ (void *)get_distortion_offset, /* 6 */ (void *)p4_pix2foc, /* 7 */ (void *)p4_pix2deltas, /* sip.h */ /* 8 */ (void *)sip_clear, /* 9 */ (void *)sip_init, /* 10 */ (void *)sip_free, /* 11 */ (void *)sip_pix2foc, /* 12 */ (void *)sip_pix2deltas, /* 13 */ (void *)sip_foc2pix, /* 14 */ (void *)sip_foc2deltas, /* pipeline.h */ /* 15 */ (void *)pipeline_clear, /* 16 */ (void *)pipeline_init, /* 17 */ (void *)pipeline_free, /* 18 */ (void *)pipeline_all_pixel2world, /* 19 */ (void *)pipeline_pix2foc, /* wcs.h */ /* 20 */ (void *)wcsp2s, /* 21 */ (void *)wcss2p, /* 22 */ (void *)wcsprt, /* new for api version 2 */ /* 23 */ (void *)wcslib_get_error_message, /* new for api version 3 */ /* 24 */ (void *)wcsprintf_buf }; int _setup_api(PyObject *m) { PyObject* c_api; c_api = PyCapsule_New((void *)AstropyWcs_API, "_wcs._ASTROPY_WCS_API", NULL); PyModule_AddObject(m, "_ASTROPY_WCS_API", c_api); return 0; } astropy-astropy-201cddb/astropy/wcs/src/distortion.c000066400000000000000000000131411507226315300230250ustar00rootroot00000000000000/* Author: Michael Droettboom mdroe@stsci.edu */ #include "astropy_wcs/distortion.h" #include #include #include #include /* TODO: n-dimensional support */ int distortion_lookup_t_init( distortion_lookup_t* lookup) { unsigned int i; for (i = 0; i < NAXES; ++i) { lookup->naxis[i] = 0; lookup->crpix[i] = 0.0; lookup->crval[i] = 0.0; lookup->cdelt[i] = 1.0; } lookup->data = NULL; return 0; } void distortion_lookup_t_free( /*@unused@*/ distortion_lookup_t* lookup) { /*@empty@*/ } /** * Get a value at a specific integral location in the lookup table. * (This is nothing more special than an array lookup with range * checking.) */ static INLINE float get_dist_clamp( const float* const data, const unsigned int* const naxis, const int x, const int y) { return data[ ((naxis[0] * CLAMP(y, 0, (long)naxis[1] - 1)) + CLAMP(x, 0, (long)naxis[0] - 1))]; } static INLINE float get_dist( const float* const data, const unsigned int* const naxis, const int x, const int y) { return data[(naxis[0] * y) + x]; } /** * Converts a pixel coordinate to a fractional coordinate in the * lookup table on a single axis */ static INLINE double image_coord_to_distortion_coord( const distortion_lookup_t * const lookup, const unsigned int axis, const double img) { double result; assert(lookup != NULL); assert(axis < NAXES); /* 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; return CLAMP(result, 0.0, (double)(lookup->naxis[axis] - 1)); } /** * Converts a pixel coordinate to a fractional coordinate in the * lookup table. */ static INLINE void image_coords_to_distortion_coords( const distortion_lookup_t * const lookup, const double * const img /* [NAXES] */, /* Output parameters */ /*@out@*/ double *dist /* [NAXES] */) { unsigned int i; assert(lookup != NULL); assert(img != NULL); assert(dist != NULL); for (i = 0; i < NAXES; ++i) { dist[i] = image_coord_to_distortion_coord(lookup, i, img[i]); } } INLINE double get_distortion_offset( const distortion_lookup_t * const lookup, const double * const img /*[NAXES]*/) { double dist[NAXES]; double dist_floor[NAXES]; int dist_ifloor[NAXES]; double dist_weight[NAXES]; double dist_iweight[NAXES]; double result; const unsigned int* naxis = lookup->naxis; const float* data = lookup->data; unsigned int i; assert(lookup != NULL); assert(img != NULL); image_coords_to_distortion_coords(lookup, img, dist); for (i = 0; i < NAXES; ++i) { dist_floor[i] = floor(dist[i]); dist_ifloor[i] = (int)dist_floor[i]; dist_weight[i] = dist[i] - dist_floor[i]; dist_iweight[i] = 1.0 - dist_weight[i]; } /* If we may need to clamp the lookups, use this slower approach */ if (dist_ifloor[0] < 0 || dist_ifloor[1] < 0 || dist_ifloor[0] >= (long)lookup->naxis[0] - 1 || dist_ifloor[1] >= (long)lookup->naxis[1] - 1) { result = (double)get_dist_clamp(data, naxis, dist_ifloor[0], dist_ifloor[1]) * dist_iweight[0] * dist_iweight[1] + (double)get_dist_clamp(data, naxis, dist_ifloor[0], dist_ifloor[1] + 1) * dist_iweight[0] * dist_weight[1] + (double)get_dist_clamp(data, naxis, dist_ifloor[0] + 1, dist_ifloor[1]) * dist_weight[0] * dist_iweight[1] + (double)get_dist_clamp(data, naxis, dist_ifloor[0] + 1, dist_ifloor[1] + 1) * dist_weight[0] * dist_weight[1]; /* Else, we don't need to clamp 4 times for each pixel */ } else { result = (double)get_dist(data, naxis, dist_ifloor[0], dist_ifloor[1]) * dist_iweight[0] * dist_iweight[1] + (double)get_dist(data, naxis, dist_ifloor[0], dist_ifloor[1] + 1) * dist_iweight[0] * dist_weight[1] + (double)get_dist(data, naxis, dist_ifloor[0] + 1, dist_ifloor[1]) * dist_weight[0] * dist_iweight[1] + (double)get_dist(data, naxis, dist_ifloor[0] + 1, dist_ifloor[1] + 1) * dist_weight[0] * dist_weight[1]; } return result; } int p4_pix2deltas( const unsigned int naxes, const distortion_lookup_t **lookup, /* [NAXES] */ const unsigned int nelem, const double* pix, /* [NAXES][nelem] */ double *foc /* [NAXES][nelem] */) { int i; double* foc0; 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 for (k = 0; k < naxes; ++k) { if (lookup[k] != NULL) { assert(lookup[k]->data != NULL); } } #endif if (pix == NULL || foc == NULL) { return 1; } pixend = pix + nelem * NAXES; /* This can't be parallelized, because pix may be equal to foc */ /* For the same reason, i needs to be in the inner loop */ for (pix0 = pix, foc0 = foc; pix0 < pixend; pix0 += NAXES, foc0 += NAXES) { for (i = 0; i < NAXES; ++i) { if (lookup[i]) { foc0[i] += get_distortion_offset(lookup[i], pix0); } } } return 0; } int p4_pix2foc( const unsigned int naxes, const distortion_lookup_t **lookup, /* [NAXES] */ const unsigned int nelem, const double* pix, /* [NAXES][nelem] */ double *foc /* [NAXES][nelem] */) { assert(pix); assert(foc); if (pix != foc) { memcpy(foc, pix, sizeof(double) * naxes * nelem); } return p4_pix2deltas(naxes, lookup, nelem, pix, foc); } astropy-astropy-201cddb/astropy/wcs/src/distortion_wrap.c000066400000000000000000000212121507226315300240540ustar00rootroot00000000000000/* Author: Michael Droettboom mdroe@stsci.edu */ #define NO_IMPORT_ARRAY #include "astropy_wcs/distortion_wrap.h" #include "astropy_wcs/docstrings.h" #include /* From Python */ static int PyDistLookup_traverse( PyDistLookup* self, visitproc visit, void* arg) { Py_VISIT(self->py_data); return 0; } static int PyDistLookup_clear( PyDistLookup* self) { Py_CLEAR(self->py_data); return 0; } 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); } /*@null@*/ static PyObject * PyDistLookup_new( PyTypeObject* type, /*@unused@*/ PyObject* args, /*@unused@*/ PyObject* kwds) { PyDistLookup* self; self = (PyDistLookup*)type->tp_alloc(type, 0); if (self != NULL) { if (distortion_lookup_t_init(&self->x)) { return NULL; } self->py_data = NULL; } return (PyObject*)self; } static int PyDistLookup_init( PyDistLookup* self, PyObject* args, /*@unused@*/ PyObject* kwds) { PyObject* py_array_obj = NULL; PyArrayObject* array_obj = NULL; if (!PyArg_ParseTuple(args, "O(dd)(dd)(dd):DistortionLookupTable.__init__", &py_array_obj, &(self->x.crpix[0]), &(self->x.crpix[1]), &(self->x.crval[0]), &(self->x.crval[1]), &(self->x.cdelt[0]), &(self->x.cdelt[1]))) { return -1; } array_obj = (PyArrayObject*)PyArray_ContiguousFromAny(py_array_obj, NPY_FLOAT32, 2, 2); if (array_obj == NULL) { return -1; } self->py_data = array_obj; self->x.naxis[0] = (unsigned int)PyArray_DIM(array_obj, 1); self->x.naxis[1] = (unsigned int)PyArray_DIM(array_obj, 0); self->x.data = (float *)PyArray_DATA(array_obj); return 0; } static PyObject* PyDistLookup_get_cdelt( PyDistLookup* self, /*@unused@*/ void* closure) { Py_ssize_t naxis = 2; return get_double_array("cdelt", self->x.cdelt, 1, &naxis, (PyObject*)self); } static int PyDistLookup_set_cdelt( PyDistLookup* self, PyObject* value, /*@unused@*/ void* closure) { npy_intp naxis = 2; return set_double_array("cdelt", value, 1, &naxis, self->x.cdelt); } static PyObject* PyDistLookup_get_crpix( PyDistLookup* self, /*@unused@*/ void* closure) { Py_ssize_t naxis = 2; return get_double_array("crpix", self->x.crpix, 1, &naxis, (PyObject*)self); } static int PyDistLookup_set_crpix( PyDistLookup* self, PyObject* value, /*@unused@*/ void* closure) { npy_intp naxis = 2; return set_double_array("crpix", value, 1, &naxis, self->x.crpix); } static PyObject* PyDistLookup_get_crval( PyDistLookup* self, /*@unused@*/ void* closure) { Py_ssize_t naxis = 2; return get_double_array("crval", self->x.crval, 1, &naxis, (PyObject*)self); } static int PyDistLookup_set_crval( PyDistLookup* self, PyObject* value, /*@unused@*/ void* closure) { npy_intp naxis = 2; return set_double_array("crval", value, 1, &naxis, self->x.crval); } /*@shared@*/ static PyObject* PyDistLookup_get_data( PyDistLookup* self, /*@unused@*/ void* closure) { if (self->py_data == NULL) { Py_INCREF(Py_None); return Py_None; } else { Py_INCREF(self->py_data); return (PyObject*)self->py_data; } } static int PyDistLookup_set_data( PyDistLookup* self, PyObject* value, /*@unused@*/ void* closure) { PyArrayObject* value_array = NULL; if (value == NULL) { Py_CLEAR(self->py_data); self->x.data = NULL; return 0; } value_array = (PyArrayObject*)PyArray_ContiguousFromAny(value, NPY_FLOAT32, 2, 2); if (value_array == NULL) { return -1; } Py_XDECREF(self->py_data); self->py_data = value_array; self->x.naxis[0] = (unsigned int)PyArray_DIM(value_array, 1); self->x.naxis[1] = (unsigned int)PyArray_DIM(value_array, 0); self->x.data = (float *)PyArray_DATA(value_array); return 0; } /*@null@*/ static PyObject* PyDistLookup_get_offset( PyDistLookup* self, PyObject* args, /*@unused@*/ PyObject* kwds) { double coord[NAXES]; double result; if (self->x.data == NULL) { PyErr_SetString(PyExc_RuntimeError, "No data has been set for the lookup table"); return NULL; } if (!PyArg_ParseTuple(args, "dd:get_offset", &coord[0], &coord[1])) { return NULL; } result = get_distortion_offset(&self->x, coord); return PyFloat_FromDouble(result); } static PyObject* PyDistLookup___copy__( PyDistLookup* self, /*@unused@*/ PyObject* args, /*@unused@*/ PyObject* kwds) { PyDistLookup* copy = NULL; int i = 0; copy = (PyDistLookup*)PyDistLookup_new(&PyDistLookupType, NULL, NULL); if (copy == NULL) { return NULL; } for (i = 0; i < 2; ++i) { copy->x.naxis[i] = self->x.naxis[i]; copy->x.crpix[i] = self->x.crpix[i]; copy->x.crval[i] = self->x.crval[i]; copy->x.cdelt[i] = self->x.cdelt[i]; } if (self->py_data) { PyDistLookup_set_data(copy, (PyObject*)self->py_data, NULL); } return (PyObject*)copy; } static PyObject* PyDistLookup___deepcopy__( PyDistLookup* self, PyObject* memo, /*@unused@*/ PyObject* kwds) { PyDistLookup* copy; PyObject* obj_copy; int i = 0; copy = (PyDistLookup*)PyDistLookup_new(&PyDistLookupType, NULL, NULL); if (copy == NULL) { return NULL; } for (i = 0; i < 2; ++i) { copy->x.naxis[i] = self->x.naxis[i]; copy->x.crpix[i] = self->x.crpix[i]; copy->x.crval[i] = self->x.crval[i]; copy->x.cdelt[i] = self->x.cdelt[i]; } if (self->py_data) { obj_copy = get_deepcopy((PyObject*)self->py_data, memo); if (obj_copy == NULL) { Py_DECREF(copy); return NULL; } PyDistLookup_set_data(copy, (PyObject*)obj_copy, NULL); Py_DECREF(obj_copy); } return (PyObject*)copy; } static PyGetSetDef PyDistLookup_getset[] = { {"cdelt", (getter)PyDistLookup_get_cdelt, (setter)PyDistLookup_set_cdelt, (char *)doc_cdelt}, {"crpix", (getter)PyDistLookup_get_crpix, (setter)PyDistLookup_set_crpix, (char *)doc_crpix}, {"crval", (getter)PyDistLookup_get_crval, (setter)PyDistLookup_set_crval, (char *)doc_crval}, {"data", (getter)PyDistLookup_get_data, (setter)PyDistLookup_set_data, (char *)doc_data}, {NULL} }; static PyMethodDef PyDistLookup_methods[] = { {"__copy__", (PyCFunction)PyDistLookup___copy__, METH_NOARGS, NULL}, {"__deepcopy__", (PyCFunction)PyDistLookup___deepcopy__, METH_O, NULL}, {"get_offset", (PyCFunction)PyDistLookup_get_offset, METH_VARARGS, doc_get_offset}, {NULL} }; PyTypeObject PyDistLookupType = { PyVarObject_HEAD_INIT(NULL, 0) "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 */ }; int _setup_distortion_type( PyObject* m) { if (PyType_Ready(&PyDistLookupType) < 0) { return -1; } Py_INCREF(&PyDistLookupType); return PyModule_AddObject(m, "DistortionLookupTable", (PyObject *)&PyDistLookupType); } astropy-astropy-201cddb/astropy/wcs/src/pipeline.c000066400000000000000000000153251507226315300224420ustar00rootroot00000000000000/* Author: Michael Droettboom mdroe@stsci.edu */ #include "astropy_wcs/pipeline.h" #include "astropy_wcs/util.h" #include "wcserr.h" #include #include #include #define PIP_ERRMSG(status) WCSERR_SET(status) void pipeline_clear( pipeline_t* pipeline) { pipeline->det2im[0] = NULL; pipeline->det2im[1] = NULL; pipeline->sip = NULL; pipeline->cpdis[0] = NULL; pipeline->cpdis[1] = NULL; pipeline->wcs = NULL; pipeline->err = NULL; } void pipeline_init( pipeline_t* pipeline, /*@shared@*/ distortion_lookup_t** det2im /* [2] */, /*@shared@*/ sip_t* sip, /*@shared@*/ distortion_lookup_t** cpdis /* [2] */, /*@shared@*/ struct wcsprm* wcs) { pipeline->det2im[0] = det2im[0]; pipeline->det2im[1] = det2im[1]; pipeline->sip = sip; pipeline->cpdis[0] = cpdis[0]; pipeline->cpdis[1] = cpdis[1]; pipeline->wcs = wcs; pipeline->err = NULL; } void pipeline_free( pipeline_t* pipeline) { free(pipeline->err); pipeline->err = NULL; } int pipeline_all_pixel2world( pipeline_t* pipeline, const unsigned int ncoord, const unsigned int nelem, const double* const pixcrd /* [ncoord][nelem] */, double* world /* [ncoord][nelem] */) { static const char* function = "pipeline_all_pixel2world"; const double* wcs_input = NULL; double* wcs_output = NULL; int has_det2im; int has_sip; int has_p4; int has_wcs; int status = 1; struct wcserr **err; /* Temporary buffer for performing WCS calculations */ unsigned char* buffer = NULL; unsigned char* mem = NULL; /*@null@*/ double* tmp; /*@null@*/ double* imgcrd; /*@null@*/ double* phi; /*@null@*/ double* theta; /*@null@*/ int* stat; if (pipeline == NULL || pixcrd == NULL || world == NULL) { return WCSERR_NULL_POINTER; } err = &(pipeline->err); 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; has_wcs = pipeline->wcs != NULL; if (has_det2im || has_sip || has_p4) { if (nelem != 2) { status = wcserr_set( PIP_ERRMSG(WCSERR_BAD_COORD_TRANS), "Data must be 2-dimensional when Paper IV lookup table or SIP transform is present."); goto exit; } } 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 */ ncoord * sizeof(double) + /* theta */ ncoord * nelem * sizeof(double) + /* tmp */ ncoord * nelem * sizeof(int) /* stat */ ); if (buffer == NULL) { status = wcserr_set( PIP_ERRMSG(WCSERR_MEMORY), "Memory allocation failed"); goto exit; } imgcrd = (double *)mem; mem += ncoord * nelem * sizeof(double); phi = (double *)mem; mem += ncoord * sizeof(double); theta = (double *)mem; mem += ncoord * sizeof(double); tmp = (double *)mem; mem += ncoord * nelem * sizeof(double); stat = (int *)mem; /* mem += ncoord * nelem * sizeof(int); */ if (has_det2im || has_sip || has_p4) { status = pipeline_pix2foc(pipeline, ncoord, nelem, pixcrd, tmp); if (status != 0) { goto exit; } wcs_input = tmp; wcs_output = world; } else { wcs_input = pixcrd; wcs_output = world; } 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); } if (status == 8) { set_invalid_to_nan((int)ncoord, (int)nelem, wcs_output, stat); } } else { if (has_det2im || has_sip || has_p4) { status = pipeline_pix2foc(pipeline, ncoord, nelem, pixcrd, world); } } exit: free(buffer); return status; } int pipeline_pix2foc( pipeline_t* pipeline, const unsigned int ncoord, const unsigned int nelem, const double* const pixcrd /* [ncoord][nelem] */, double* foc /* [ncoord][nelem] */) { static const char* function = "pipeline_pix2foc"; int has_det2im; int has_sip; int has_p4; const double * input = NULL; double * tmp = NULL; int status = 1; struct wcserr **err; assert(nelem == 2); assert(pixcrd != foc); if (pipeline == NULL || pixcrd == NULL || foc == NULL) { return WCSERR_NULL_POINTER; } 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; if (has_det2im) { if (has_sip || has_p4) { tmp = malloc(ncoord * nelem * sizeof(double)); if (tmp == NULL) { status = wcserr_set( PIP_ERRMSG(WCSERR_MEMORY), "Memory allocation failed"); goto exit; } memcpy(tmp, pixcrd, sizeof(double) * ncoord * nelem); status = p4_pix2deltas(2, (void*)pipeline->det2im, ncoord, pixcrd, tmp); if (status) { wcserr_set(PIP_ERRMSG(WCSERR_NULL_POINTER), "NULL pointer passed"); goto exit; } input = tmp; memcpy(foc, input, sizeof(double) * ncoord * nelem); } else { memcpy(foc, pixcrd, sizeof(double) * ncoord * nelem); status = p4_pix2deltas(2, (void*)pipeline->det2im, ncoord, pixcrd, foc); if (status) { wcserr_set(PIP_ERRMSG(WCSERR_NULL_POINTER), "NULL pointer passed"); goto exit; } } } else { /* Copy pixcrd to foc as a starting point. The "deltas" functions below will undistort from there */ memcpy(foc, pixcrd, sizeof(double) * ncoord * nelem); input = pixcrd; } 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; } } if (has_p4) { status = p4_pix2deltas(2, (void*)pipeline->cpdis, ncoord, input, foc); if (status) { wcserr_set(PIP_ERRMSG(WCSERR_NULL_POINTER), "NULL pointer passed"); goto exit; } } status = 0; exit: free(tmp); return status; } astropy-astropy-201cddb/astropy/wcs/src/pyutil.c000066400000000000000000000513731507226315300221660ustar00rootroot00000000000000/* Author: Michael Droettboom mdroe@stsci.edu */ #define NO_IMPORT_ARRAY /* util.h must be imported first */ #include "astropy_wcs/pyutil.h" #include "astropy_wcs/docstrings.h" #include "wcsfix.h" #include "wcshdr.h" #include "wcsprintf.h" #include "wcsunits.h" /*@null@*/ static INLINE PyObject* _PyArrayProxy_New( /*@shared@*/ PyObject* self, int nd, const npy_intp* dims, int typenum, const void* data, const int flags) { PyArray_Descr* type_descr = NULL; PyObject* result = NULL; type_descr = (PyArray_Descr*)PyArray_DescrFromType(typenum); if (type_descr == NULL) { return NULL; } result = (PyObject*)PyArray_NewFromDescr( &PyArray_Type, type_descr, nd, (npy_intp*)dims, NULL, (void*)data, NPY_ARRAY_C_CONTIGUOUS | flags, NULL); if (result == NULL) { return NULL; } Py_INCREF(self); PyArray_SetBaseObject((PyArrayObject *)result, self); return result; } /*@null@*/ PyObject* PyArrayProxy_New( /*@shared@*/ PyObject* self, int nd, const npy_intp* dims, int typenum, const void* data) { return _PyArrayProxy_New(self, nd, dims, typenum, data, NPY_ARRAY_WRITEABLE); } /*@null@*/ PyObject* PyArrayReadOnlyProxy_New( /*@shared@*/ PyObject* self, int nd, const npy_intp* dims, int typenum, const void* data) { return _PyArrayProxy_New(self, nd, dims, typenum, data, 0); } void preoffset_array( PyArrayObject* array, int value) { npy_intp size; double *data; if (value == 1) { return; } size = PyArray_Size((PyObject*)array); data = (double*)PyArray_DATA(array); offset_c_array(data, size, (double)(1 - value)); } void unoffset_array( PyArrayObject* array, int value) { npy_intp size; double *data; if (value == 1) { return; } size = PyArray_Size((PyObject*)array); data = (double*)PyArray_DATA(array); offset_c_array(data, size, (double)-(1 - value)); } void copy_array_to_c_double( PyArrayObject* array, double* dest) { npy_intp size = 1; double* data = NULL; size = PyArray_Size((PyObject*)array); data = (double*)PyArray_DATA(array); memcpy(dest, data, size * sizeof(double)); } void copy_array_to_c_int( PyArrayObject* array, int* dest) { npy_intp size = 1; int* data = NULL; size = PyArray_Size((PyObject*)array); data = (int*)PyArray_DATA(array); memcpy(dest, data, size * sizeof(int)); } int is_null( /*@null@*/ void *p) { if (p == NULL) { PyErr_SetString(PyExc_AssertionError, "Underlying object is NULL."); return 1; } return 0; } /* wcslib represents undefined values using its own special constant, UNDEFINED. To be consistent with the Pythonic way of doing things, it's nicer to represent undefined values using NaN. Unfortunately, in order to get nice mutable arrays in Python, Python must be able to edit the wcsprm values directly. The solution is to store NaNs in the struct "canonically", but convert those NaNs to/from UNDEFINED around every call into a wcslib function. It's not as computationally expensive as it sounds, as all these arrays are quite small. */ static INLINE void wcsprm_fix_values( struct wcsprm* x, value_fixer_t value_fixer) { unsigned int naxis = (unsigned int)x->naxis; value_fixer(x->cd, naxis * naxis); value_fixer(x->cdelt, naxis); value_fixer(x->crder, naxis); value_fixer(x->crota, naxis); value_fixer(x->crpix, naxis); value_fixer(x->crval, naxis); value_fixer(x->csyer, naxis); value_fixer(&x->equinox, 1); value_fixer(&x->latpole, 1); value_fixer(&x->lonpole, 1); value_fixer(&x->mjdavg, 1); value_fixer(&x->mjdobs, 1); value_fixer(x->obsgeo, 6); value_fixer(&x->cel.phi0, 1); value_fixer(&x->restfrq, 1); value_fixer(&x->restwav, 1); value_fixer(&x->cel.theta0, 1); 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 wcsprm_c2python( /*@null@*/ struct wcsprm* x) { if (x != NULL) { wcsprm_fix_values(x, &undefined2nan); } } void wcsprm_python2c( /*@null@*/ struct wcsprm* x) { if (x != NULL) { wcsprm_fix_values(x, &nan2undefined); } } /*************************************************************************** * Exceptions * ***************************************************************************/ PyObject* WcsExc_Wcs; PyObject* WcsExc_SingularMatrix; PyObject* WcsExc_InconsistentAxisTypes; PyObject* WcsExc_InvalidTransform; PyObject* WcsExc_InvalidCoordinate; PyObject* WcsExc_NoSolution; 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 * wcs_errmsg. */ 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 = _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); \ 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); DEFINE_EXCEPTION(InvalidCoordinate); DEFINE_EXCEPTION(NoSolution); DEFINE_EXCEPTION(InvalidSubimageSpecification); DEFINE_EXCEPTION(NonseparableSubimageCoordinateSystem); DEFINE_EXCEPTION(NoWcsKeywordsFound); DEFINE_EXCEPTION(InvalidTabularParameters); DEFINE_EXCEPTION(InvalidPrjParameters); return 0; } const char* wcslib_get_error_message(int status) { return wcs_errmsg[status]; } void wcserr_to_python_exc(const struct wcserr *err) { PyObject *exc; if (err == NULL) { PyErr_SetString(PyExc_RuntimeError, "NULL error object in wcslib"); } else { if (err->status > 0 && err->status <= WCS_ERRMSG_MAX) { exc = *wcs_errexc[err->status]; } 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()); } } void wcs_to_python_exc(const struct wcsprm *wcs) { PyObject* exc; const struct wcserr *err = wcs->err; if (err == NULL) { PyErr_SetString(PyExc_RuntimeError, "NULL error object in wcslib"); } else { if (err->status > 0 && err->status < WCS_ERRMSG_MAX) { exc = *wcs_errexc[err->status]; } else { exc = PyExc_RuntimeError; } /* This is technically not thread-safe -- make sure we have the GIL */ wcsprintf_set(NULL); wcsperr(wcs, ""); PyErr_SetString(exc, wcsprintf_buf()); } } void wcserr_fix_to_python_exc(const struct wcserr *err) { PyObject *exc; if (err == NULL) { PyErr_SetString(PyExc_RuntimeError, "NULL error object in wcslib"); } else { if (err->status > 0 && err->status <= FIXERR_NO_REF_PIX_VAL) { 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()); } } void 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 { PyErr_Format( PyExc_ValueError, "Internal error in wcslib header parser:\n %s", wcsprintf_buf() ); } } /*************************************************************************** Property helpers ***************************************************************************/ #define SHAPE_STR_LEN 2048 /* Helper function to display the desired shape of an array as a string, eg. 2x2 */ static void shape_to_string( int ndims, const npy_intp* dims, char* str /* [SHAPE_STR_LEN] */) { int i; char value[32]; /* More than large enough to hold string rep of a 64-bit integer (way overkill) */ if (ndims > 3) { strncpy(str, "ERROR", 6); return; } str[0] = 0; for (i = 0; i < ndims; ++i) { snprintf(value, 32, "%d", (int)dims[i]); strncat(str, value, 32); if (i != ndims - 1) { strncat(str, "x", 2); } } } /* get_string is inlined */ int set_string( const char* propname, PyObject* value, char* dest, Py_ssize_t maxlen) { char* buffer; Py_ssize_t len; PyObject* ascii_obj = NULL; int result = -1; if (check_delete(propname, value)) { 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; } if (len >= maxlen) { PyErr_Format( PyExc_ValueError, "'%s' length must be less than %u characters.", propname, (unsigned int) maxlen); goto end; } strncpy(dest, buffer, (size_t)len + 1); result = 0; end: Py_XDECREF(ascii_obj); return result; } /* get_bool is inlined */ int set_bool( const char* propname, PyObject* value, int* dest) { if (check_delete(propname, value)) { return -1; } *dest = PyObject_IsTrue(value); return 0; } /* get_int is inlined */ int set_int( const char* propname, PyObject* value, int* dest) { long value_int; if (check_delete(propname, value)) { return -1; } value_int = PyLong_AsLong(value); if (value_int == -1 && PyErr_Occurred()) { return -1; } if ((unsigned long)value_int > 0x7fffffff) { PyErr_SetString(PyExc_OverflowError, "integer value too large"); return -1; } *dest = (int)value_int; return 0; } /* get_double is inlined */ int set_double( const char* propname, PyObject* value, double* dest) { if (check_delete(propname, value)) { return -1; } *dest = PyFloat_AsDouble(value); if (PyErr_Occurred()) { return -1; } else { return 0; } } /* get_double_array is inlined */ int set_double_array( const char* propname, PyObject* value, int ndims, const npy_intp* dims, double* dest) { PyArrayObject* value_array = NULL; npy_int i = 0; char shape_str[SHAPE_STR_LEN]; if (check_delete(propname, value)) { return -1; } value_array = (PyArrayObject*)PyArray_ContiguousFromAny(value, NPY_DOUBLE, ndims, ndims); if (value_array == NULL) { return -1; } if (dims != NULL) { for (i = 0; i < ndims; ++i) { if (PyArray_DIM(value_array, i) != dims[i]) { shape_to_string(ndims, dims, shape_str); PyErr_Format( PyExc_ValueError, "'%s' array is the wrong shape, must be %s", propname, shape_str); Py_DECREF(value_array); return -1; } } } copy_array_to_c_double(value_array, dest); Py_DECREF(value_array); return 0; } int set_int_array( const char* propname, PyObject* value, int ndims, const npy_intp* dims, int* dest) { PyArrayObject* value_array = NULL; npy_int i = 0; char shape_str[SHAPE_STR_LEN]; if (check_delete(propname, value)) { return -1; } value_array = (PyArrayObject*)PyArray_ContiguousFromAny(value, NPY_INT, ndims, ndims); if (value_array == NULL) { return -1; } if (dims != NULL) { for (i = 0; i < ndims; ++i) { if (PyArray_DIM(value_array, i) != dims[i]) { shape_to_string(ndims, dims, shape_str); PyErr_Format( PyExc_ValueError, "'%s' array is the wrong shape, must be %s", propname, shape_str); Py_DECREF(value_array); return -1; } } } copy_array_to_c_int(value_array, dest); Py_DECREF(value_array); return 0; } /* get_str_list is inlined */ int set_str_list( const char* propname, PyObject* value, Py_ssize_t len, Py_ssize_t maxlen, char (*dest)[72]) { PyObject* str = NULL; Py_ssize_t input_len; Py_ssize_t i = 0; if (check_delete(propname, value)) { return -1; } if (maxlen == 0) { maxlen = 68; } 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; } /* We go through the list twice, once to verify that the list is in the correct format, and then again to do the data copy. This way, we won't partially copy the contents and then throw an exception. */ for (i = 0; i < len; ++i) { str = PySequence_GetItem(value, i); if (str == NULL) { return -1; } if (!(PyBytes_CheckExact(str) || PyUnicode_CheckExact(str))) { PyErr_Format( PyExc_TypeError, "'%s' must be a sequence of bytes or strings", propname); Py_DECREF(str); return -1; } input_len = PySequence_Size(str); if (input_len > maxlen) { PyErr_Format( PyExc_ValueError, "Each entry in '%s' must be less than %u characters", propname, (unsigned int)maxlen); Py_DECREF(str); return -1; } else if (input_len == -1) { Py_DECREF(str); return -1; } Py_DECREF(str); } for (i = 0; i < len; ++i) { str = PySequence_GetItem(value, i); 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; } if (set_string(propname, str, dest[i], maxlen)) { PyErr_Clear(); PyErr_Format( PyExc_RuntimeError, "Input values have changed underneath us. Something is seriously wrong."); Py_DECREF(str); return -1; } Py_DECREF(str); } return 0; } /*@null@*/ PyObject* get_pscards( /*@unused@*/ const char* propname, struct pscard* ps, int nps) { PyObject* result = NULL; PyObject* subresult = NULL; Py_ssize_t i = 0; if (nps < 0) { nps = 0; } result = PyList_New((Py_ssize_t)nps); if (result == NULL) { 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) { Py_DECREF(result); return NULL; } if (PyList_SetItem(result, i, subresult)) { Py_DECREF(subresult); Py_DECREF(result); return NULL; } } return result; } int set_pscards( /*@unused@*/ const char* propname, PyObject* value, struct pscard** ps, int *nps, int *npsmax) { PyObject* subvalue = NULL; Py_ssize_t i = 0; Py_ssize_t size = 0; int ival = 0; int mval = 0; const char* strvalue = 0; void* newmem = NULL; if (!PySequence_Check(value)) return -1; size = PySequence_Size(value); if (size > 0x7fffffff) { /* Must be a 32-bit size */ return -1; } if (size > (Py_ssize_t)*npsmax) { newmem = malloc(sizeof(struct pscard) * size); if (newmem == NULL) { PyErr_SetString(PyExc_MemoryError, "Could not allocate memory."); return -1; } free(*ps); *ps = newmem; *npsmax = (int)size; } /* 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, "iis", &ival, &mval, &strvalue)) { Py_DECREF(subvalue); return -1; } Py_DECREF(subvalue); } for (i = 0; i < size; ++i) { subvalue = PySequence_GetItem(value, i); if (subvalue == NULL) { return -1; } if (!PyArg_ParseTuple(subvalue, "iis", &ival, &mval, &strvalue)) { Py_DECREF(subvalue); return -1; } Py_DECREF(subvalue); (*ps)[i].i = ival; (*ps)[i].m = mval; strncpy((*ps)[i].value, strvalue, 72); (*ps)[i].value[71] = '\0'; (*nps) = (int)(i + 1); } return 0; } /*@null@*/ PyObject* get_pvcards( /*@unused@*/ const char* propname, struct pvcard* pv, int npv) { PyObject* result = NULL; 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) { Py_DECREF(result); return NULL; } if (PyList_SetItem(result, i, subresult)) { Py_DECREF(subresult); Py_DECREF(result); return NULL; } } return result; } int set_pvcards( /*@propname@*/ const char* propname, PyObject* value, struct pvcard** pv, int *npv, int *npvmax) { 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; size = PySequence_Fast_GET_SIZE(value); newmem = malloc(sizeof(struct pvcard) * 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; } for (i = 0; i < size; ++i) { if (!PyArg_ParseTuple(PySequence_Fast_GET_ITEM(value, i), "iid", &newmem[i].i, &newmem[i].m, &newmem[i].value)) { goto done; } } 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; ret = 0; done: Py_XDECREF(fastseq); free(newmem); return ret; } PyObject* get_deepcopy( PyObject* obj, PyObject* memo) { if (PyObject_HasAttrString(obj, "__deepcopy__")) { return PyObject_CallMethod(obj, "__deepcopy__", "O", memo); } else { return PyObject_CallMethod(obj, "__copy__", ""); } } /*************************************************************************** * Miscellaneous helper functions * ***************************************************************************/ int parse_unsafe_unit_conversion_spec( const char* arg, int* ctrl) { const char* p = NULL; *ctrl = 0; for (p = arg; *p != '\0'; ++p) { switch (*p) { case 's': case 'S': *ctrl |= 1; break; case 'h': case 'H': *ctrl |= 2; break; case 'd': case 'D': *ctrl |= 4; break; default: PyErr_SetString( PyExc_ValueError, "translate_units may only contain the characters 's', 'h' or 'd'"); return 1; } } return 0; } astropy-astropy-201cddb/astropy/wcs/src/sip.c000066400000000000000000000166121507226315300214300ustar00rootroot00000000000000/* Author: Michael Droettboom mdroe@stsci.edu */ #include "astropy_wcs/sip.h" #include #include #include #include #include #define SIP_ERRMSG(status) WCSERR_SET(status) void sip_clear( sip_t* sip) { assert(sip != NULL); sip->a_order = 0; sip->a = NULL; sip->b_order = 0; sip->b = NULL; sip->ap_order = 0; sip->ap = NULL; sip->bp_order = 0; sip->bp = NULL; sip->crpix[0] = 0.0; sip->crpix[1] = 0.0; sip->scratch = NULL; sip->err = NULL; } int sip_init( sip_t* sip, const unsigned int a_order, const double* a, const unsigned int b_order, const double* b, const unsigned int ap_order, const double* ap, const unsigned int bp_order, const double* bp, const double* crpix /* [2] */) { 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"; assert(sip != NULL); sip_clear(sip); err = &(sip->err); /* We we have one of A/B or AP/BP, we must have both. */ if ((a == NULL) ^ (b == NULL)) { return wcserr_set( SIP_ERRMSG(WCSERR_BAD_COORD_TRANS), "Both A and B SIP transform must be defined"); } if ((ap == NULL) ^ (bp == NULL)) { return wcserr_set( SIP_ERRMSG(WCSERR_BAD_COORD_TRANS), "Both AP and BP SIP transform must be defined"); } if (a != NULL) { sip->a_order = a_order; a_size = (size_t)(a_order + 1u) * (a_order + 1u) * sizeof(double); sip->a = malloc(a_size); if (sip->a == NULL) { sip_free(sip); status = wcserr_set( SIP_ERRMSG(WCSERR_MEMORY), "Memory allocation failed"); goto exit; } memcpy(sip->a, a, a_size); if (a_order > scratch_size) { scratch_size = a_order; } sip->b_order = b_order; b_size = (size_t)(b_order + 1u) * (b_order + 1u) * sizeof(double); sip->b = malloc(b_size); if (sip->b == NULL) { sip_free(sip); status = wcserr_set( SIP_ERRMSG(WCSERR_MEMORY), "Memory allocation failed"); goto exit; } memcpy(sip->b, b, b_size); if (b_order > scratch_size) { scratch_size = b_order; } } if (ap != NULL) { sip->ap_order = ap_order; ap_size = (size_t)(ap_order + 1u) * (ap_order + 1u) * sizeof(double); sip->ap = malloc(ap_size); if (sip->ap == NULL) { sip_free(sip); status = wcserr_set( SIP_ERRMSG(WCSERR_MEMORY), "Memory allocation failed"); goto exit; } memcpy(sip->ap, ap, ap_size); if (ap_order > scratch_size) { scratch_size = ap_order; } sip->bp_order = bp_order; bp_size = (size_t)(bp_order + 1u) * (bp_order + 1u) * sizeof(double); sip->bp = malloc(bp_size); if (sip->bp == NULL) { sip_free(sip); status = wcserr_set( SIP_ERRMSG(WCSERR_MEMORY), "Memory allocation failed"); goto exit; } memcpy(sip->bp, bp, bp_size); if (bp_order > scratch_size) { scratch_size = bp_order; } } 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]; sip->crpix[1] = crpix[1]; exit: return status; } void sip_free(sip_t* sip) { free(sip->a); sip->a = NULL; free(sip->b); sip->b = NULL; free(sip->ap); sip->ap = NULL; free(sip->bp); sip->bp = NULL; free(sip->scratch); sip->scratch = NULL; free(sip->err); sip->err = NULL; } static INLINE double lu( const unsigned int order, const double* const matrix, const int x, const int y) { int index; assert(x >= 0 && x <= (int)order); assert(y >= 0 && y <= (int)order); index = x * ((int)order + 1) + y; assert(index >= 0 && index < ((int)order + 1) * ((int)order + 1)); return matrix[index]; } static int sip_compute( /*@unused@*/ const unsigned int naxes, const unsigned int nelem, const unsigned int m, /*@null@*/ const double* a, const unsigned int n, /*@null@*/ const double* b, const double* crpix /* [2] */, /*@null@*/ double* tmp, /*@null@*/ const double* input /* [NAXES][nelem] */, /*@null@*/ double* output /* [NAXES][nelem] */) { unsigned int i; int j, k; double x, y; double sum; const double* input_ptr; double* output_ptr; assert(a != NULL); assert(b != NULL); assert(crpix != NULL); assert(tmp != NULL); assert(input != NULL); assert(output != NULL); /* Avoid segfaults */ if (input == NULL || output == NULL || tmp == NULL || crpix == NULL) { return 1; } /* If we have one, we must have both... */ if ((a == NULL) ^ (b == NULL)) { return 6; } /* If no distortion, just return values */ if (a == NULL /* && b == NULL ... implied */) { return 0; } input_ptr = input; output_ptr = output; for (i = 0; i < nelem; ++i) { x = *input_ptr++ - crpix[0]; y = *input_ptr++ - crpix[1]; for (j = 0; j <= (int)m; ++j) { tmp[j] = lu(m, a, (int)m-j, j); for (k = j-1; k >= 0; --k) { tmp[j] = (y * tmp[j]) + lu(m, a, (int)m-j, k); } } sum = tmp[0]; for (j = (int)m; j > 0; --j) { sum = x * sum + tmp[(int)m - j + 1]; } *output_ptr++ += sum; for (j = 0; j <= (int)n; ++j) { tmp[j] = lu(n, b, (int)n-j, j); for (k = j-1; k >= 0; --k) { tmp[j] = (y * tmp[j]) + lu(n, b, (int)n-j, k); } } sum = tmp[0]; for (j = (int)n; j > 0; --j) { sum = x * sum + tmp[n - j + 1]; } *output_ptr++ += sum; } return 0; } int sip_pix2deltas( const sip_t* sip, const unsigned int naxes, const unsigned int nelem, const double* pix /* [NAXES][nelem] */, double* deltas /* [NAXES][nelem] */) { if (sip == NULL) { return 1; } return sip_compute(naxes, nelem, sip->a_order, sip->a, sip->b_order, sip->b, sip->crpix, (double *)sip->scratch, pix, deltas); } int sip_foc2deltas( const sip_t* sip, const unsigned int naxes, const unsigned int nelem, const double* foc /* [NAXES][nelem] */, double* deltas /* [NAXES][nelem] */) { if (sip == NULL) { return 1; } return sip_compute(naxes, nelem, sip->ap_order, sip->ap, sip->bp_order, sip->bp, sip->crpix, (double *)sip->scratch, foc, deltas); } int sip_pix2foc( const sip_t* sip, const unsigned int naxes, const unsigned int nelem, const double* pix /* [NAXES][nelem] */, double* foc /* [NAXES][nelem] */) { assert(pix); assert(foc); if (pix != foc) { memcpy(foc, pix, sizeof(double) * naxes * nelem); } return sip_pix2deltas(sip, naxes, nelem, pix, foc); } int sip_foc2pix( const sip_t* sip, const unsigned int naxes, const unsigned int nelem, const double* foc /* [NAXES][nelem] */, double* pix /* [NAXES][nelem] */) { assert(pix); assert(foc); if (pix != foc) { memcpy(pix, foc, sizeof(double) * naxes * nelem); } return sip_foc2deltas(sip, naxes, nelem, foc, pix); } astropy-astropy-201cddb/astropy/wcs/src/sip_wrap.c000066400000000000000000000320501507226315300224530ustar00rootroot00000000000000/* Author: Michael Droettboom mdroe@stsci.edu */ #define NO_IMPORT_ARRAY #include "astropy_wcs/sip_wrap.h" #include "astropy_wcs/docstrings.h" #include "wcs.h" static void PySip_dealloc( PySip* self) { sip_free(&self->x); Py_TYPE(self)->tp_free((PyObject*)self); } /*@null@*/ static PyObject * PySip_new( PyTypeObject* type, /*@unused@*/ PyObject* args, /*@unused@*/ PyObject* kwds) { PySip* self; self = (PySip*)type->tp_alloc(type, 0); if (self != NULL) { sip_clear(&self->x); } return (PyObject*)self; } static int convert_matrix( /*@null@*/ PyObject* pyobj, PyArrayObject** array, double** data, unsigned int* order) { if (pyobj == Py_None) { *array = NULL; *data = NULL; *order = 0; return 0; } *array = (PyArrayObject*)PyArray_ContiguousFromAny( pyobj, NPY_DOUBLE, 2, 2); if (*array == NULL) { return -1; } if (PyArray_DIM(*array, 0) != PyArray_DIM(*array, 1)) { PyErr_SetString(PyExc_ValueError, "Matrix must be square."); return -1; } *data = (double*)PyArray_DATA(*array); *order = (unsigned int)PyArray_DIM(*array, 0) - 1; return 0; } static int PySip_init( PySip* self, PyObject* args, /*@unused@*/ PyObject* kwds) { PyObject* py_a = NULL; PyObject* py_b = NULL; PyObject* py_ap = NULL; PyObject* py_bp = NULL; PyObject* py_crpix = NULL; PyArrayObject* a = NULL; PyArrayObject* b = NULL; PyArrayObject* ap = NULL; PyArrayObject* bp = NULL; PyArrayObject* crpix = NULL; double* a_data = NULL; double* b_data = NULL; double* ap_data = NULL; double* bp_data = NULL; unsigned int a_order = 0; unsigned int b_order = 0; unsigned int ap_order = 0; unsigned int bp_order = 0; int status = -1; if (!PyArg_ParseTuple(args, "OOOOO:Sip.__init__", &py_a, &py_b, &py_ap, &py_bp, &py_crpix)) { return -1; } if (convert_matrix(py_a, &a, &a_data, &a_order) || convert_matrix(py_b, &b, &b_data, &b_order) || convert_matrix(py_ap, &ap, &ap_data, &ap_order) || convert_matrix(py_bp, &bp, &bp_data, &bp_order)) { goto exit; } crpix = (PyArrayObject*)PyArray_ContiguousFromAny(py_crpix, NPY_DOUBLE, 1, 1); if (crpix == NULL) { goto exit; } if (PyArray_DIM(crpix, 0) != 2) { PyErr_SetString(PyExc_ValueError, "CRPIX wrong length"); goto exit; } status = sip_init(&self->x, a_order, a_data, b_order, b_data, ap_order, ap_data, bp_order, bp_data, PyArray_DATA(crpix)); exit: Py_XDECREF(a); Py_XDECREF(b); Py_XDECREF(ap); Py_XDECREF(bp); Py_XDECREF(crpix); if (status == 0) { return 0; } else if (status == -1) { /* Exception already set */ return -1; } else { wcserr_to_python_exc(self->x.err); return -1; } } /*@null@*/ static PyObject* PySip_pix2foc( PySip* self, PyObject* args, PyObject* kwds) { PyObject* pixcrd_obj = NULL; 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 }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "Oi:pix2foc", (char **)keywords, &pixcrd_obj, &origin)) { return NULL; } if (self->x.a == NULL || self->x.b == NULL) { PyErr_SetString( PyExc_ValueError, "SIP object does not have coefficients for pix2foc transformation (A and B)"); return NULL; } pixcrd = (PyArrayObject*)PyArray_ContiguousFromAny(pixcrd_obj, NPY_DOUBLE, 2, 2); if (pixcrd == NULL) { goto exit; } if (PyArray_DIM(pixcrd, 1) != 2) { PyErr_SetString(PyExc_ValueError, "Pixel array must be an Nx2 array"); goto exit; } foccrd = (PyArrayObject*)PyArray_SimpleNew(2, PyArray_DIMS(pixcrd), NPY_DOUBLE); if (foccrd == NULL) { goto exit; } Py_BEGIN_ALLOW_THREADS preoffset_array(pixcrd, origin); status = sip_pix2foc(&self->x, (unsigned int)PyArray_DIM(pixcrd, 1), (unsigned int)PyArray_DIM(pixcrd, 0), (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); if (status == 0) { return (PyObject*)foccrd; } else { Py_XDECREF(foccrd); if (status == -1) { /* Exception already set */ return NULL; } else { wcserr_to_python_exc(self->x.err); return NULL; } } } /*@null@*/ static PyObject* PySip_foc2pix( PySip* self, PyObject* args, PyObject* kwds) { PyObject* foccrd_obj = NULL; int origin = 1; 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 }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "Oi:foc2pix", (char **)keywords, &foccrd_obj, &origin)) { return NULL; } if (self->x.ap == NULL || self->x.bp == NULL) { PyErr_SetString( PyExc_ValueError, "SIP object does not have coefficients for foc2pix transformation (AP and BP)"); return NULL; } foccrd = (PyArrayObject*)PyArray_ContiguousFromAny(foccrd_obj, NPY_DOUBLE, 2, 2); if (foccrd == NULL) { goto exit; } if (PyArray_DIM(foccrd, 1) != 2) { PyErr_SetString(PyExc_ValueError, "Pixel array must be an Nx2 array"); goto exit; } pixcrd = (PyArrayObject*)PyArray_SimpleNew(2, PyArray_DIMS(foccrd), NPY_DOUBLE); if (pixcrd == NULL) { status = 2; goto exit; } 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); if (status == 0) { return (PyObject*)pixcrd; } else { Py_XDECREF(pixcrd); if (status == -1) { /* Exception already set */ return NULL; } else { wcserr_to_python_exc(self->x.err); return NULL; } } } /*@null@*/ static PyObject* PySip_get_a( PySip* self, /*@unused@*/ void* closure) { npy_intp dims[2]; if (self->x.a == NULL) { Py_INCREF(Py_None); return Py_None; } dims[0] = (npy_intp)self->x.a_order + 1; dims[1] = (npy_intp)self->x.a_order + 1; return get_double_array("a", self->x.a, 2, dims, (PyObject*)self); } /*@null@*/ static PyObject* PySip_get_b( PySip* self, /*@unused@*/ void* closure) { npy_intp dims[2]; if (self->x.b == NULL) { Py_INCREF(Py_None); return Py_None; } dims[0] = (npy_intp)self->x.b_order + 1; dims[1] = (npy_intp)self->x.b_order + 1; return get_double_array("b", self->x.b, 2, dims, (PyObject*)self); } /*@null@*/ static PyObject* PySip_get_ap( PySip* self, /*@unused@*/ void* closure) { npy_intp dims[2]; if (self->x.ap == NULL) { Py_INCREF(Py_None); return Py_None; } dims[0] = (npy_intp)self->x.ap_order + 1; dims[1] = (npy_intp)self->x.ap_order + 1; return get_double_array("ap", self->x.ap, 2, dims, (PyObject*)self); } /*@null@*/ static PyObject* PySip_get_bp( PySip* self, /*@unused@*/ void* closure) { npy_intp dims[2]; if (self->x.bp == NULL) { Py_INCREF(Py_None); return Py_None; } dims[0] = (npy_intp)self->x.bp_order + 1; dims[1] = (npy_intp)self->x.bp_order + 1; return get_double_array("bp", self->x.bp, 2, dims, (PyObject*)self); } static PyObject* PySip_get_a_order( PySip* self, /*@unused@*/ void* closure) { return get_int("a_order", (long int)self->x.a_order); } static PyObject* PySip_get_b_order( PySip* self, /*@unused@*/ void* closure) { return get_int("b_order", (long int)self->x.b_order); } static PyObject* PySip_get_ap_order( PySip* self, /*@unused@*/ void* closure) { return get_int("ap_order", (long int)self->x.ap_order); } static PyObject* PySip_get_bp_order( PySip* self, /*@unused@*/ void* closure) { return get_int("bp_order", (long int)self->x.bp_order); } static PyObject* PySip_get_crpix( PySip* self, /*@unused@*/ void* closure) { Py_ssize_t naxis = 2; return get_double_array("crpix", self->x.crpix, 1, &naxis, (PyObject*)self); } static PyObject* PySip___copy__( PySip* self, /*@unused@*/ PyObject* args, /*@unused@*/ PyObject* kwds) { PySip* copy = NULL; copy = (PySip*)PySip_new(&PySipType, NULL, NULL); if (copy == NULL) { return NULL; } if (sip_init(©->x, self->x.a_order, self->x.a, self->x.b_order, self->x.b, self->x.ap_order, self->x.ap, self->x.bp_order, self->x.bp, self->x.crpix)) { Py_DECREF(copy); return NULL; } return (PyObject*)copy; } static PyGetSetDef PySip_getset[] = { {"a", (getter)PySip_get_a, NULL, (char *)doc_a}, {"a_order", (getter)PySip_get_a_order, NULL, (char *)doc_a_order}, {"b", (getter)PySip_get_b, NULL, (char *)doc_b}, {"b_order", (getter)PySip_get_b_order, NULL, (char *)doc_b_order}, {"ap", (getter)PySip_get_ap, NULL, (char *)doc_ap}, {"ap_order", (getter)PySip_get_ap_order, NULL, (char *)doc_ap_order}, {"bp", (getter)PySip_get_bp, NULL, (char *)doc_bp}, {"bp_order", (getter)PySip_get_bp_order, NULL, (char *)doc_bp_order}, {"crpix", (getter)PySip_get_crpix, NULL, (char *)doc_crpix}, {NULL} }; static PyMethodDef PySip_methods[] = { {"__copy__", (PyCFunction)PySip___copy__, METH_NOARGS, NULL}, {"__deepcopy__", (PyCFunction)PySip___copy__, METH_O, NULL}, {"pix2foc", (PyCFunction)PySip_pix2foc, METH_VARARGS|METH_KEYWORDS, doc_sip_pix2foc}, {"foc2pix", (PyCFunction)PySip_foc2pix, METH_VARARGS|METH_KEYWORDS, doc_sip_foc2pix}, {NULL} }; PyTypeObject PySipType = { PyVarObject_HEAD_INIT(NULL, 0) "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 */ }; int _setup_sip_type( PyObject* m) { if (PyType_Ready(&PySipType) < 0) return -1; Py_INCREF(&PySipType); return PyModule_AddObject(m, "Sip", (PyObject *)&PySipType); } astropy-astropy-201cddb/astropy/wcs/src/str_list_proxy.c000066400000000000000000000141771507226315300237450ustar00rootroot00000000000000/* Author: Michael Droettboom mdroe@stsci.edu */ #define NO_IMPORT_ARRAY #include "astropy_wcs/pyutil.h" /*************************************************************************** * List-of-strings proxy object ***************************************************************************/ static PyTypeObject PyStrListProxyType; typedef struct { PyObject_HEAD /*@null@*/ /*@shared@*/ PyObject* pyobject; Py_ssize_t size; Py_ssize_t maxsize; char (*array)[72]; } PyStrListProxy; static void PyStrListProxy_dealloc( PyStrListProxy* self) { PyObject_GC_UnTrack(self); Py_XDECREF(self->pyobject); Py_TYPE(self)->tp_free((PyObject*)self); } /*@null@*/ static PyObject * PyStrListProxy_new( PyTypeObject* type, /*@unused@*/ PyObject* args, /*@unused@*/ PyObject* kwds) { PyStrListProxy* self = NULL; self = (PyStrListProxy*)type->tp_alloc(type, 0); if (self != NULL) { self->pyobject = NULL; } return (PyObject*)self; } static int PyStrListProxy_traverse( PyStrListProxy* self, visitproc visit, void *arg) { Py_VISIT(self->pyobject); return 0; } static int PyStrListProxy_clear( PyStrListProxy *self) { Py_CLEAR(self->pyobject); return 0; } /*@null@*/ PyObject * PyStrListProxy_New( /*@shared@*/ PyObject* owner, Py_ssize_t size, Py_ssize_t maxsize, char (*array)[72]) { PyStrListProxy* self = NULL; if (maxsize == 0) { maxsize = 68; } self = (PyStrListProxy*)PyStrListProxyType.tp_alloc(&PyStrListProxyType, 0); if (self == NULL) { return NULL; } Py_XINCREF(owner); self->pyobject = owner; self->size = size; self->maxsize = maxsize; self->array = array; return (PyObject*)self; } static Py_ssize_t PyStrListProxy_len( PyStrListProxy* self) { return self->size; } /*@null@*/ static PyObject* PyStrListProxy_getitem( PyStrListProxy* self, Py_ssize_t index) { if (index >= self->size || index < 0) { PyErr_SetString(PyExc_IndexError, "index out of range"); return NULL; } return get_string("string", self->array[index]); } static int PyStrListProxy_setitem( PyStrListProxy* self, Py_ssize_t index, PyObject* arg) { if (index >= self->size || index < 0) { PyErr_SetString(PyExc_IndexError, "index out of range"); return -1; } return set_string("string", arg, self->array[index], self->maxsize); } /*@null@*/ PyObject* str_list_proxy_repr( char (*array)[72], Py_ssize_t size, Py_ssize_t maxsize) { char* buffer = NULL; char* wp = NULL; char* rp = NULL; Py_ssize_t i = 0; Py_ssize_t j = 0; PyObject* result = NULL; /* These are in descending order, so we can exit the loop quickly. They are in pairs: (char_to_escape, char_escaped) */ const char* escapes = "\\\\''\rr\ff\vv\nn\tt\bb\aa"; const char* e = NULL; char next_char = '\0'; /* Overallocating to allow for escaped characters */ buffer = malloc((size_t)size*maxsize*2 + 2); if (buffer == NULL) { PyErr_SetString(PyExc_MemoryError, "Could not allocate memory."); return NULL; } wp = buffer; *wp++ = '['; for (i = 0; i < size; ++i) { *wp++ = '\''; rp = array[i]; for (j = 0; j < maxsize && *rp != '\0'; ++j) { /* Check if this character should be escaped */ e = escapes; next_char = *rp++; do { if (next_char > *e) { break; } else if (next_char == *e) { *wp++ = '\\'; next_char = *(++e); break; } else { e += 2; } } while (*e != '\0'); *wp++ = next_char; } *wp++ = '\''; /* Add a comma for all but the last one */ if (i != size - 1) { *wp++ = ','; *wp++ = ' '; } } *wp++ = ']'; *wp++ = '\0'; result = PyUnicode_FromString(buffer); free(buffer); return result; } /*@null@*/ static PyObject* PyStrListProxy_repr( PyStrListProxy* self) { return str_list_proxy_repr(self->array, self->size, self->maxsize); } static PySequenceMethods PyStrListProxy_sequence_methods = { (lenfunc)PyStrListProxy_len, NULL, NULL, (ssizeargfunc)PyStrListProxy_getitem, NULL, (ssizeobjargproc)PyStrListProxy_setitem, NULL, NULL, NULL, NULL }; static PyTypeObject PyStrListProxyType = { PyVarObject_HEAD_INIT(NULL, 0) "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 */ }; int _setup_str_list_proxy_type( /*@unused@*/ PyObject* m) { if (PyType_Ready(&PyStrListProxyType) < 0) { return 1; } return 0; } astropy-astropy-201cddb/astropy/wcs/src/unit_list_proxy.c000066400000000000000000000206661507226315300241140ustar00rootroot00000000000000/* Author: Michael Droettboom mdroe@stsci.edu */ #define NO_IMPORT_ARRAY #include "astropy_wcs/pyutil.h" #include "astropy_wcs/str_list_proxy.h" /*************************************************************************** * List-of-units proxy object ***************************************************************************/ #define MAXSIZE 68 #define ARRAYSIZE 72 static PyTypeObject PyUnitListProxyType; typedef struct { PyObject_HEAD /*@null@*/ /*@shared@*/ PyObject* pyobject; Py_ssize_t size; char (*array)[ARRAYSIZE]; PyObject* unit_class; } PyUnitListProxy; static void PyUnitListProxy_dealloc( PyUnitListProxy* self) { PyObject_GC_UnTrack(self); Py_XDECREF(self->pyobject); Py_TYPE(self)->tp_free((PyObject*)self); } /*@null@*/ static PyObject * PyUnitListProxy_new( PyTypeObject* type, /*@unused@*/ PyObject* args, /*@unused@*/ PyObject* kwds) { PyUnitListProxy* self = NULL; self = (PyUnitListProxy*)type->tp_alloc(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); 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]) { 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); self = (PyUnitListProxy*)PyUnitListProxyType.tp_alloc( &PyUnitListProxyType, 0); if (self == NULL) { return NULL; } Py_XINCREF(owner); self->pyobject = owner; self->size = size; self->array = array; self->unit_class = unit_class; 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, &PyUnitListProxyType) || !PyObject_TypeCheck(b, &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) { 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 PySequenceMethods PyUnitListProxy_sequence_methods = { (lenfunc)PyUnitListProxy_len, NULL, NULL, (ssizeargfunc)PyUnitListProxy_getitem, NULL, (ssizeobjargproc)PyUnitListProxy_setitem, NULL, NULL, NULL, NULL }; static PyTypeObject PyUnitListProxyType = { PyVarObject_HEAD_INIT(NULL, 0) "astropy.wcs.UnitListProxy", /*tp_name*/ sizeof(PyUnitListProxy), /*tp_basicsize*/ 0, /*tp_itemsize*/ (destructor)PyUnitListProxy_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ (reprfunc)PyUnitListProxy_repr, /*tp_repr*/ 0, /*tp_as_number*/ &PyUnitListProxy_sequence_methods, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash */ 0, /*tp_call*/ (reprfunc)PyUnitListProxy_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)PyUnitListProxy_traverse, /* tp_traverse */ (inquiry)PyUnitListProxy_clear, /* tp_clear */ (richcmpfunc)PyUnitListProxy_richcmp, /* 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 */ PyUnitListProxy_new, /* tp_new */ }; 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); 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) { if (PyType_Ready(&PyUnitListProxyType) < 0) { return 1; } return 0; } astropy-astropy-201cddb/astropy/wcs/src/util.c000066400000000000000000000017351507226315300216120ustar00rootroot00000000000000/* Author: Michael Droettboom mdroe@stsci.edu */ #define NO_IMPORT_ARRAY #include "astropy_wcs/util.h" #include #include void set_invalid_to_nan( const int ncoord, const int nelem, double* const data, const int* const stat) { int i = 0; double* d = data; const int* s = stat; const int* s_end = stat + ncoord; double n; #ifndef NAN #define INF (DBL_MAX+DBL_MAX) #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) { 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; } } } astropy-astropy-201cddb/astropy/wcs/src/wcslib_auxprm_wrap.c000066400000000000000000000271011507226315300245400ustar00rootroot00000000000000#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; self = (PyAuxprm*)type->tp_alloc(type, 0); return (PyObject*)self; } static int PyAuxprm_traverse(PyAuxprm* self, visitproc visit, void *arg) { Py_VISIT(self->owner); return 0; } static int PyAuxprm_clear(PyAuxprm* self) { Py_CLEAR(self->owner); return 0; } static void PyAuxprm_dealloc(PyAuxprm* self) { PyAuxprm_clear(self); Py_TYPE(self)->tp_free((PyObject*)self); } PyAuxprm* PyAuxprm_cnew(PyObject* wcsprm, struct auxprm* x) { PyAuxprm* self; self = (PyAuxprm*)(&PyAuxprmType)->tp_alloc(&PyAuxprmType, 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} }; PyTypeObject PyAuxprmType = { PyVarObject_HEAD_INIT(NULL, 0) "astropy.wcs.Auxprm", /*tp_name*/ sizeof(PyAuxprm), /*tp_basicsize*/ 0, /*tp_itemsize*/ (destructor)PyAuxprm_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*/ (reprfunc)PyAuxprm___str__, /*tp_str*/ 0, /*tp_getattro*/ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ doc_Auxprm, /* tp_doc */ (traverseproc)PyAuxprm_traverse, /* tp_traverse */ (inquiry)PyAuxprm_clear, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ 0, /* tp_methods */ 0, /* tp_members */ PyAuxprm_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 */ }; int _setup_auxprm_type(PyObject* m) { if (PyType_Ready(&PyAuxprmType) < 0) { return -1; } Py_INCREF(&PyAuxprmType); PyModule_AddObject(m, "Auxprm", (PyObject *)&PyAuxprmType); return 0; } astropy-astropy-201cddb/astropy/wcs/src/wcslib_celprm_wrap.c000066400000000000000000000324641507226315300245160ustar00rootroot00000000000000#define NO_IMPORT_ARRAY #include "astropy_wcs/wcslib_celprm_wrap.h" #include "astropy_wcs/wcslib_prjprm_wrap.h" #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; self = (PyCelprm*)type->tp_alloc(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); 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); } Py_TYPE(self)->tp_free((PyObject*)self); } 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; self = (PyCelprm*)(&PyCelprmType)->tp_alloc(&PyCelprmType, 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(&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} }; PyTypeObject PyCelprmType = { PyVarObject_HEAD_INIT(NULL, 0) "astropy.wcs.Celprm", /*tp_name*/ sizeof(PyCelprm), /*tp_basicsize*/ 0, /*tp_itemsize*/ (destructor)PyCelprm_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*/ (reprfunc)PyCelprm___str__, /*tp_str*/ 0, /*tp_getattro*/ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ doc_Celprm, /* tp_doc */ (traverseproc)PyCelprm_traverse, /* tp_traverse */ (inquiry)PyCelprm_clear, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ PyCelprm_methods, /* tp_methods */ 0, /* tp_members */ PyCelprm_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 */ PyCelprm_new, /* tp_new */ }; int _setup_celprm_type(PyObject* m) { if (PyType_Ready(&PyCelprmType) < 0) return -1; Py_INCREF(&PyCelprmType); PyModule_AddObject(m, "Celprm", (PyObject *)&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; } astropy-astropy-201cddb/astropy/wcs/src/wcslib_prjprm_wrap.c000066400000000000000000000666661507226315300245610ustar00rootroot00000000000000#define NO_IMPORT_ARRAY #include #include #include "astropy_wcs/wcslib_celprm_wrap.h" #include "astropy_wcs/wcslib_prjprm_wrap.h" #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; self = (PyPrjprm*)type->tp_alloc(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); 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); } Py_TYPE(self)->tp_free((PyObject*)self); } PyPrjprm* PyPrjprm_cnew(PyObject* celprm_obj, struct prjprm* x, int* prefcount) { PyPrjprm* self; self = (PyPrjprm*)(&PyPrjprmType)->tp_alloc(&PyPrjprmType, 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(&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(x1); Py_XDECREF(x2); Py_XDECREF(prj_x1); Py_XDECREF(prj_x2); Py_XDECREF(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; PyObject* value = 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} }; PyTypeObject PyPrjprmType = { PyVarObject_HEAD_INIT(NULL, 0) "astropy.wcs.Prjprm", /*tp_name*/ sizeof(PyPrjprm), /*tp_basicsize*/ 0, /*tp_itemsize*/ (destructor)PyPrjprm_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*/ (reprfunc)PyPrjprm___str__, /*tp_str*/ 0, /*tp_getattro*/ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ doc_Prjprm, /* tp_doc */ (traverseproc)PyPrjprm_traverse, /* tp_traverse */ (inquiry)PyPrjprm_clear, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ PyPrjprm_methods, /* tp_methods */ 0, /* tp_members */ PyPrjprm_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 */ PyPrjprm_new, /* tp_new */ }; int _setup_prjprm_type(PyObject* m) { if (PyType_Ready(&PyPrjprmType) < 0) return -1; Py_INCREF(&PyPrjprmType); PyModule_AddObject(m, "Prjprm", (PyObject *)&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; } astropy-astropy-201cddb/astropy/wcs/src/wcslib_tabprm_wrap.c000077500000000000000000000245341507226315300245230ustar00rootroot00000000000000/* Author: Michael Droettboom mdroe@stsci.edu */ #define NO_IMPORT_ARRAY #include "astropy_wcs/wcslib_tabprm_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" /*************************************************************************** * Helper functions * ***************************************************************************/ static INLINE void note_change(PyTabprm* self) { self->x->flag = 0; } static int make_fancy_dims(PyTabprm* self, int* ndims, npy_intp* dims) { int i, M; M = self->x->M; if (M + 1 > NPY_MAXDIMS) { PyErr_SetString(PyExc_ValueError, "Too many dimensions"); return -1; } *ndims = M + 1; for (i = 0; i < M; ++i) { dims[i] = self->x->K[M-1-i]; } dims[M] = M; return 0; } PyObject** tab_errexc[6]; static void wcslib_tab_to_python_exc(int status) { if (status > 0 && status < 6) { PyErr_SetString(*tab_errexc[status], tab_errmsg[status]); } else { PyErr_SetString( PyExc_RuntimeError, "Unknown error occurred. Something is seriously wrong."); } } /*************************************************************************** * PyTabprm methods */ static int PyTabprm_traverse( PyTabprm* self, visitproc visit, void *arg) { Py_VISIT(self->owner); return 0; } static int PyTabprm_clear( PyTabprm* self) { Py_CLEAR(self->owner); return 0; } static void PyTabprm_dealloc( PyTabprm* self) { PyTabprm_clear(self); Py_TYPE(self)->tp_free((PyObject*)self); } PyTabprm* PyTabprm_cnew(PyObject* wcsprm, struct tabprm* x) { PyTabprm* self; self = (PyTabprm*)(&PyTabprmType)->tp_alloc(&PyTabprmType, 0); if (self == NULL) return NULL; self->x = x; Py_INCREF(wcsprm); self->owner = wcsprm; return self; } static int PyTabprm_cset( PyTabprm* self) { int status = 0; status = tabset(self->x); if (status == 0) { return 0; } else { wcslib_tab_to_python_exc(status); return -1; } } /*@null@*/ static PyObject* PyTabprm_set( PyTabprm* self) { if (PyTabprm_cset(self)) { return NULL; } Py_RETURN_NONE; } /*@null@*/ static PyObject* PyTabprm_print_contents( PyTabprm* self) { if (PyTabprm_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); tabprt(self->x); printf("%s", wcsprintf_buf()); fflush(stdout); Py_RETURN_NONE; } /*@null@*/ static PyObject* PyTabprm___str__( PyTabprm* self) { if (PyTabprm_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); tabprt(self->x); return PyUnicode_FromString(wcsprintf_buf()); } /*************************************************************************** * Member getters/setters (properties) */ /*@null@*/ static PyObject* PyTabprm_get_coord( PyTabprm* self, /*@unused@*/ void* closure) { int ndims; npy_intp dims[NPY_MAXDIMS]; if (is_null(self->x->coord)) { return NULL; } if (make_fancy_dims(self, &ndims, dims)) { return NULL; } return get_double_array("coord", self->x->coord, ndims, dims, (PyObject*)self); } /*@null@*/ static int PyTabprm_set_coord( PyTabprm* self, PyObject* value, /*@unused@*/ void* closure) { int ndims; npy_intp dims[NPY_MAXDIMS]; if (is_null(self->x->coord)) { return -1; } if (make_fancy_dims(self, &ndims, dims)) { return -1; } return set_double_array("coord", value, ndims, dims, self->x->coord); } /*@null@*/ static PyObject* PyTabprm_get_crval( PyTabprm* self, /*@unused@*/ void* closure) { Py_ssize_t M = 0; if (is_null(self->x->crval)) { return NULL; } M = (Py_ssize_t)self->x->M; return get_double_array("crval", self->x->crval, 1, &M, (PyObject*)self); } static int PyTabprm_set_crval( PyTabprm* self, PyObject* value, /*@unused@*/ void* closure) { npy_intp M = 0; if (is_null(self->x->crval)) { return -1; } M = (Py_ssize_t)self->x->M; note_change(self); return set_double_array("crval", value, 1, &M, self->x->crval); } /*@null@*/ static PyObject* PyTabprm_get_delta( PyTabprm* self, /*@unused@*/ void* closure) { Py_ssize_t M = 0; if (is_null(self->x->delta)) { return NULL; } M = (Py_ssize_t)self->x->M; return get_double_array("delta", self->x->delta, 1, &M, (PyObject*)self); } /*@null@*/ static PyObject* PyTabprm_get_extrema( PyTabprm* self, /*@unused@*/ void* closure) { int ndims; npy_intp dims[NPY_MAXDIMS]; if (is_null(self->x->coord)) { return NULL; } if (make_fancy_dims(self, &ndims, dims)) { return NULL; } dims[ndims-2] = 2; return get_double_array("extrema", self->x->extrema, ndims, dims, (PyObject*)self); } /*@null@*/ static PyObject* PyTabprm_get_K( PyTabprm* self, /*@unused@*/ void* closure) { Py_ssize_t M = 0; if (is_null(self->x->K)) { return NULL; } M = (Py_ssize_t)self->x->M; return get_int_array("K", self->x->K, 1, &M, (PyObject*)self); } /*@null@*/ static PyObject* PyTabprm_get_M( PyTabprm* self, /*@unused@*/ void* closure) { return get_int("M", self->x->M); } /*@null@*/ static PyObject* PyTabprm_get_map( PyTabprm* self, /*@unused@*/ void* closure) { Py_ssize_t M = 0; if (is_null(self->x->map)) { return NULL; } M = (Py_ssize_t)self->x->M; return get_int_array("map", self->x->map, 1, &M, (PyObject*)self); } static int PyTabprm_set_map( PyTabprm* self, PyObject* value, /*@unused@*/ void* closure) { npy_intp M = 0; if (is_null(self->x->map)) { return -1; } M = (Py_ssize_t)self->x->M; note_change(self); return set_int_array("map", value, 1, &M, self->x->map); } /*@null@*/ static PyObject* PyTabprm_get_nc( PyTabprm* self, /*@unused@*/ void* closure) { return get_int("nc", self->x->nc); } /*@null@*/ static PyObject* PyTabprm_get_p0( PyTabprm* self, /*@unused@*/ void* closure) { Py_ssize_t M = 0; if (is_null(self->x->p0)) { return NULL; } M = (Py_ssize_t)self->x->M; return get_int_array("p0", self->x->p0, 1, &M, (PyObject*)self); } /*@null@*/ static PyObject* PyTabprm_get_sense( PyTabprm* self, /*@unused@*/ void* closure) { Py_ssize_t M = 0; if (is_null(self->x->sense)) { return NULL; } M = (Py_ssize_t)self->x->M; return get_int_array("sense", self->x->sense, 1, &M, (PyObject*)self); } /*************************************************************************** * PyTabprm definition structures */ static PyGetSetDef PyTabprm_getset[] = { {"coord", (getter)PyTabprm_get_coord, (setter)PyTabprm_set_coord, (char *)doc_coord}, {"crval", (getter)PyTabprm_get_crval, (setter)PyTabprm_set_crval, (char *)doc_crval_tabprm}, {"delta", (getter)PyTabprm_get_delta, NULL, (char *)doc_delta}, {"extrema", (getter)PyTabprm_get_extrema, NULL, (char *)doc_extrema}, {"K", (getter)PyTabprm_get_K, NULL, (char *)doc_K}, {"M", (getter)PyTabprm_get_M, NULL, (char *)doc_M}, {"map", (getter)PyTabprm_get_map, (setter)PyTabprm_set_map, (char *)doc_map}, {"nc", (getter)PyTabprm_get_nc, NULL, (char *)doc_nc}, {"p0", (getter)PyTabprm_get_p0, NULL, (char *)doc_p0}, {"sense", (getter)PyTabprm_get_sense, NULL, (char *)doc_sense}, {NULL} }; static PyMethodDef PyTabprm_methods[] = { {"print_contents", (PyCFunction)PyTabprm_print_contents, METH_NOARGS, doc_print_contents_tabprm}, {"set", (PyCFunction)PyTabprm_set, METH_NOARGS, doc_set_tabprm}, {NULL} }; PyTypeObject PyTabprmType = { PyVarObject_HEAD_INIT(NULL, 0) "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*/ 0, /*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 */ }; int _setup_tabprm_type( PyObject* m) { if (PyType_Ready(&PyTabprmType) < 0) { return -1; } Py_INCREF(&PyTabprmType); PyModule_AddObject(m, "Tabprm", (PyObject *)&PyTabprmType); tab_errexc[0] = NULL; /* Success */ tab_errexc[1] = &PyExc_MemoryError; /* Null wcsprm pointer passed */ tab_errexc[2] = &PyExc_MemoryError; /* Memory allocation failed */ tab_errexc[3] = &WcsExc_InvalidTabularParameters; /* Invalid tabular parameters */ tab_errexc[4] = &WcsExc_InvalidCoordinate; /* One or more of the x coordinates were invalid */ tab_errexc[5] = &WcsExc_InvalidCoordinate; /* One or more of the world coordinates were invalid */ return 0; } astropy-astropy-201cddb/astropy/wcs/src/wcslib_wrap.c000077500000000000000000003005761507226315300231610ustar00rootroot00000000000000/* Author: Michael Droettboom mdroe@stsci.edu */ #define NO_IMPORT_ARRAY #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 #include #include #include #include #include #include #include #include #include #include #include "astropy_wcs/isnan.h" #include "astropy_wcs/distortion.h" /* 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" /*************************************************************************** * Helper functions * ***************************************************************************/ enum e_altlin { has_pc = 1, has_cd = 2, has_crota = 4 }; static int is_valid_alt_key( const char* key) { if (key[1] != '\0' || !(key[0] == ' ' || (key[0] >= 'A' && key[0] <= 'Z'))) { PyErr_SetString(PyExc_ValueError, "key must be ' ' or 'A'-'Z'"); return 0; } 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; } /*************************************************************************** * 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(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 PyWcsprm_cset(PyWcsprm* self, const int convert); static INLINE void note_change(PyWcsprm* self) { self->x.flag = 0; } static void PyWcsprm_dealloc( PyWcsprm* self) { wcsfree(&self->x); Py_TYPE(self)->tp_free((PyObject*)self); } static PyWcsprm* PyWcsprm_cnew(void) { PyWcsprm* self; self = (PyWcsprm*)(&PyWcsprmType)->tp_alloc(&PyWcsprmType, 0); return self; } static PyObject * PyWcsprm_new( PyTypeObject* type, /*@unused@*/ PyObject* args, /*@unused@*/ PyObject* kwds) { PyWcsprm* self; self = (PyWcsprm*)type->tp_alloc(type, 0); return (PyObject*)self; } static int PyWcsprm_init( PyWcsprm* self, PyObject* args, PyObject* kwds) { int status; PyObject* header_obj = NULL; PyObject* hdulist = NULL; char * header = NULL; Py_ssize_t header_length = 0; Py_ssize_t nkeyrec = 0; const char * key = " "; PyObject* relax_obj = NULL; int relax = 0; int naxis = -1; int keysel = -1; PyObject* colsel = Py_None; PyArrayObject* colsel_array = NULL; int* colsel_data = NULL; int* colsel_ints = NULL; int warnings = 1; int nreject = 0; int nwcs = 0; struct wcsprm* wcs = NULL; int i, j; const char* keywords[] = {"header", "key", "relax", "naxis", "keysel", "colsel", "warnings", "hdulist", NULL}; if (!PyArg_ParseTupleAndKeywords( args, kwds, "|OsOiiOiO:WCSBase.__init__", (char **)keywords, &header_obj, &key, &relax_obj, &naxis, &keysel, &colsel, &warnings, &hdulist)) { return -1; } if (header_obj == NULL || header_obj == Py_None) { if (keysel > 0) { PyErr_SetString( PyExc_ValueError, "If no header is provided, keysel may not be provided either."); return -1; } if (colsel != Py_None) { PyErr_SetString( PyExc_ValueError, "If no header is provided, colsel may not be provided either."); return -1; } /* Default number of axes is 2 */ if (naxis < 0) { naxis = 2; } if (naxis < 1 || naxis > 15) { PyErr_SetString( PyExc_ValueError, "naxis must be in range 1-15"); return -1; } self->x.flag = -1; status = wcsini(1, naxis, &self->x); if (status != 0) { PyErr_SetString( PyExc_MemoryError, self->x.err->msg); return -1; } self->x.alt[0] = key[0]; if (PyWcsprm_cset(self, 0)) { return -1; } wcsprm_c2python(&self->x); return 0; } else { /* header != NULL */ if (PyBytes_AsStringAndSize(header_obj, &header, &header_length)) { return -1; } if (relax_obj == Py_True) { relax = WCSHDR_all; } else if (relax_obj == NULL || relax_obj == Py_False) { relax = WCSHDR_none; } else { relax = (int)PyLong_AsLong(relax_obj); if (relax == -1) { PyErr_SetString( PyExc_ValueError, "relax must be True, False or an integer."); return -1; } } if (!is_valid_alt_key(key)) { return -1; } if (naxis >= 0) { PyErr_SetString( PyExc_ValueError, "naxis may not be provided if a header is provided."); return -1; } nkeyrec = header_length / 80; if (nkeyrec > 0x7fffffff) { PyErr_SetString( PyExc_MemoryError, "header is too long"); return -1; } if (colsel != Py_None) { colsel_array = (PyArrayObject*) PyArray_ContiguousFromAny( colsel, NPY_INT, 1, 1); if (colsel_array == NULL) { return -1; } colsel_ints = malloc(sizeof(int) * (PyArray_DIM(colsel_array, 0) + 1)); if (colsel_ints == NULL) { Py_DECREF(colsel_array); PyErr_SetString( PyExc_MemoryError, "Memory allocation error."); return -1; } 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_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, 0, &nreject, &nwcs, &wcs); } else { status = wcsbth( header, (int)nkeyrec, relax, 0, keysel, colsel_ints, &nreject, &nwcs, &wcs); } free(colsel_ints); if (status != 0) { 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"); return -1; } /* Find the desired WCS */ for (i = 0; i < nwcs; ++i) { if (wcs[i].alt[0] == key[0]) { break; } } if (i >= nwcs) { wcsvfree(&nwcs, &wcs); PyErr_Format( PyExc_KeyError, "No WCS with key '%s' was found in the given header", key); return -1; } if (wcscopy(1, wcs + i, &self->x) != 0) { wcsvfree(&nwcs, &wcs); PyErr_SetString( PyExc_MemoryError, self->x.err->msg); 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); return 0; } } /*@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; 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); if (status == 0) { if (PyWcsprm_cset(copy, 0)) { Py_XDECREF(copy); return NULL; } wcsprm_c2python(©->x); return (PyObject*)copy; } else { Py_XDECREF(copy); wcs_to_python_exc(&(self->x)); return NULL; } } PyObject* PyWcsprm_find_all_wcs( PyObject* __, PyObject* args, PyObject* kwds) { PyObject* header_obj = NULL; char * header = NULL; Py_ssize_t header_length = 0; Py_ssize_t nkeyrec = 0; PyObject* relax_obj = NULL; int relax = 0; int keysel = 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", "warnings", NULL}; int status = -1; if (!PyArg_ParseTupleAndKeywords( args, kwds, "O|Oii:find_all_wcs", (char **)keywords, &header_obj, &relax_obj, &keysel, &warnings)) { return NULL; } if (PyBytes_AsStringAndSize(header_obj, &header, &header_length)) { return NULL; } nkeyrec = header_length / 80; if (nkeyrec > 0x7fffffff) { PyErr_SetString( PyExc_MemoryError, "header is too long"); return NULL; } if (relax_obj == Py_True) { relax = WCSHDR_all; } else if (relax_obj == NULL || relax_obj == Py_False) { relax = WCSHDR_none; } else { relax = (int)PyLong_AsLong(relax_obj); if (relax == -1) { PyErr_SetString( PyExc_ValueError, "relax must be True, False or an integer."); return 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. */ 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, 0, &nreject, &nwcs, &wcs); } else { status = wcsbth( header, (int)nkeyrec, relax, 0, keysel, NULL, &nreject, &nwcs, &wcs); } Py_END_ALLOW_THREADS if (status != 0) { wcshdr_err_to_python_exc(status, wcs); return NULL; } result = PyList_New(nwcs); if (result == NULL) { wcsvfree(&nwcs, &wcs); return NULL; } for (i = 0; i < nwcs; ++i) { subresult = PyWcsprm_cnew(); if (wcscopy(1, wcs + i, &subresult->x) != 0) { Py_DECREF(result); wcsvfree(&nwcs, &wcs); PyErr_SetString( PyExc_MemoryError, "Could not initialize wcsprm object"); return NULL; } if (PyList_SetItem(result, i, (PyObject *)subresult) == -1) { Py_DECREF(subresult); Py_DECREF(result); wcsvfree(&nwcs, &wcs); return NULL; } subresult->x.flag = 0; wcsprm_c2python(&subresult->x); } wcsvfree(&nwcs, &wcs); return result; } static PyObject* PyWcsprm_cdfix( PyWcsprm* self) { int status = 0; wcsprm_python2c(&self->x); status = cdfix(&self->x); wcsprm_c2python(&self->x); if (status == -1 || status == 0) { return PyLong_FromLong((long)status); } else { wcserr_fix_to_python_exc(self->x.err); return NULL; } } static PyObject* PyWcsprm_celfix( PyWcsprm* self) { int status = 0; wcsprm_python2c(&self->x); status = celfix(&self->x); wcsprm_c2python(&self->x); if (status == -1 || status == 0) { return PyLong_FromLong((long)status); } else { wcserr_fix_to_python_exc(self->x.err); return NULL; } } static PyObject * PyWcsprm_compare( PyWcsprm* self, PyObject* args, PyObject* kwds) { 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!|id:compare", (char **)keywords, &PyWcsprmType, &other, &cmp, &tolerance)) { return NULL; } 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; } naxis = (int*)PyArray_DATA(naxis_array); } wcsprm_python2c(&self->x); status = cylfix(naxis, &self->x); wcsprm_c2python(&self->x); Py_XDECREF(naxis_array); if (status == -1 || status == 0) { return PyLong_FromLong((long)status); } else { wcserr_fix_to_python_exc(self->x.err); return NULL; } } static PyObject* PyWcsprm_datfix( PyWcsprm* self) { int status = 0; wcsprm_python2c(&self->x); status = datfix(&self->x); wcsprm_c2python(&self->x); if (status == -1 || status == 0) { return PyLong_FromLong((long)status); } else { wcserr_fix_to_python_exc(self->x.err); return NULL; } } /*@null@*/ static PyObject* PyWcsprm_fix( PyWcsprm* self, PyObject* args, PyObject* kwds) { const char* translate_units = NULL; int ctrl = 0; PyObject* naxis_obj = NULL; PyArrayObject* naxis_array = NULL; int* naxis = NULL; int stat[NWCSFIX]; struct wcserr err[NWCSFIX]; PyObject* subresult; PyObject* result; int i = 0; int msg_index = 0; const char* message; struct message_map_entry { const char* name; const int index; }; const struct message_map_entry message_map[NWCSFIX] = { {"cdfix", CDFIX}, {"datfix", DATFIX}, #if (NWCSFIX > 6) {"obsfix", OBSFIX}, #endif {"unitfix", UNITFIX}, {"celfix", CELFIX}, {"spcfix", SPCFIX}, {"cylfix", CYLFIX} }; const char* keywords[] = {"translate_units", "naxis", NULL}; if (!PyArg_ParseTupleAndKeywords( args, kwds, "|sO:fix", (char **)keywords, &translate_units, &naxis_obj)) { return NULL; } if (translate_units != NULL) { if (parse_unsafe_unit_conversion_spec(translate_units, &ctrl)) { 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 Wcprm object (%d).", self->x.naxis); Py_DECREF(naxis_array); return NULL; } naxis = (int*)PyArray_DATA(naxis_array); } memset(err, 0, sizeof(struct wcserr) * NWCSFIX); wcsprm_python2c(&self->x); wcsfixi(ctrl, naxis, &self->x, stat, err); wcsprm_c2python(&self->x); /* We're done with this already, so deref now so we don't have to remember later */ Py_XDECREF(naxis_array); result = PyDict_New(); if (result == NULL) { return NULL; } for (i = 0; i < NWCSFIX; ++i) { msg_index = stat[message_map[i].index]; message = err[message_map[i].index].msg; if (message == NULL || message[0] == 0) { if (msg_index == FIXERR_SUCCESS) { message = "Success"; } else { message = "No change"; } } subresult = PyUnicode_FromString(message); if (subresult == NULL || PyDict_SetItemString(result, message_map[i].name, subresult)) { Py_XDECREF(subresult); Py_XDECREF(result); return NULL; } Py_XDECREF(subresult); } return result; } /*@null@*/ static PyObject* PyWcsprm_get_cdelt_func( PyWcsprm* self, /*@unused@*/ PyObject* args, /*@unused@*/ PyObject* kwds) { Py_ssize_t naxis = 0; if (is_null(self->x.cdelt)) { return NULL; } if (PyWcsprm_cset(self, 1)) { return NULL; } naxis = self->x.naxis; return get_double_array_readonly("cdelt", self->x.cdelt, 1, &naxis, (PyObject*)self); } /*@null@*/ static PyObject* PyWcsprm_get_pc_func( PyWcsprm* self, /*@unused@*/ PyObject* args, /*@unused@*/ PyObject* kwds) { npy_intp dims[2]; if (is_null(self->x.pc)) { return NULL; } if (PyWcsprm_cset(self, 1)) { return NULL; } dims[0] = self->x.naxis; dims[1] = self->x.naxis; return get_double_array_readonly("pc", self->x.pc, 2, dims, (PyObject*)self); } /*@null@*/ static PyObject* PyWcsprm_get_ps( PyWcsprm* self, /*@unused@*/ PyObject* args, /*@unused@*/ PyObject* kwds) { return get_pscards("ps", self->x.ps, self->x.nps); } /*@null@*/ static PyObject* PyWcsprm_get_pv( PyWcsprm* self, /*@unused@*/ PyObject* args, /*@unused@*/ PyObject* kwds) { return get_pvcards("pv", self->x.pv, self->x.npv); } static PyObject* PyWcsprm_has_cdi_ja( PyWcsprm* self) { int result = 0; result = self->x.altlin & has_cd; return PyBool_FromLong(result); } static PyObject* PyWcsprm_has_crotaia( PyWcsprm* self) { int result = 0; result = self->x.altlin & has_crota; return PyBool_FromLong(result); } static PyObject* PyWcsprm_has_pci_ja( PyWcsprm* self) { int result = 0; result = (self->x.altlin == 0 || self->x.altlin & has_pc); return PyBool_FromLong(result); } static PyObject* PyWcsprm_is_unity( PyWcsprm* self) { if (PyWcsprm_cset(self, 1)) { return NULL; } return PyBool_FromLong(self->x.lin.unity); } /*@null@*/ static PyObject* PyWcsprm_mix( PyWcsprm* self, PyObject* args, PyObject* kwds) { int mixpix = 0; int mixcel = 0; double vspan[2] = {0, 0}; double vstep = 0; int viter = 0; Py_ssize_t naxis = 0; PyObject* world_obj = NULL; PyObject* pixcrd_obj = NULL; int origin = 1; PyArrayObject* world = NULL; PyArrayObject* phi = NULL; PyArrayObject* theta = NULL; PyArrayObject* imgcrd = NULL; PyArrayObject* pixcrd = NULL; int status = -1; PyObject* result = NULL; const char* keywords[] = { "mixpix", "mixcel", "vspan", "vstep", "viter", "world", "pixcrd", "origin", NULL }; if (!PyArg_ParseTupleAndKeywords( args, kwds, "ii(dd)diOOi:mix", (char **)keywords, &mixpix, &mixcel, &vspan[0], &vspan[1], &vstep, &viter, &world_obj, &pixcrd_obj, &origin)) { return NULL; } if (viter < 5 || viter > 10) { PyErr_SetString( PyExc_ValueError, "viter must be in the range 5 - 10"); goto exit; } world = (PyArrayObject*)PyArray_ContiguousFromAny (world_obj, NPY_DOUBLE, 1, 1); if (world == NULL) { PyErr_SetString( PyExc_TypeError, "Argument 6 (world) must be a 1-dimensional numpy array"); goto exit; } if ((int)PyArray_DIM(world, 0) != self->x.naxis) { PyErr_Format( PyExc_TypeError, "Argument 6 (world) must be the same length as the number " "of axes (%d)", self->x.naxis); goto exit; } pixcrd = (PyArrayObject*)PyArray_ContiguousFromAny (pixcrd_obj, NPY_DOUBLE, 1, 1); if (pixcrd == NULL) { PyErr_SetString( PyExc_TypeError, "Argument 7 (pixcrd) must be a 1-dimensional numpy array"); goto exit; } if ((int)PyArray_DIM(pixcrd, 0) != self->x.naxis) { PyErr_Format( PyExc_TypeError, "Argument 7 (pixcrd) must be the same length as the " "number of axes (%d)", self->x.naxis); goto exit; } if (mixpix < 1 || mixpix > self->x.naxis) { PyErr_SetString( PyExc_ValueError, "Argument 1 (mixpix) must specify a pixel coordinate " "axis number"); goto exit; } if (mixcel < 1 || mixcel > 2) { PyErr_SetString( PyExc_ValueError, "Argument 2 (mixcel) must specify a celestial coordinate " "axis number (1 for latitude, 2 for longitude)"); goto exit; } /* Now we allocate a bunch of numpy arrays to store the * results in. */ naxis = (Py_ssize_t)self->x.naxis; phi = (PyArrayObject*)PyArray_SimpleNew (1, &naxis, NPY_DOUBLE); if (phi == NULL) { goto exit; } theta = (PyArrayObject*)PyArray_SimpleNew (1, &naxis, NPY_DOUBLE); if (theta == NULL) { goto exit; } imgcrd = (PyArrayObject*)PyArray_SimpleNew (1, &naxis, NPY_DOUBLE); if (imgcrd == NULL) { goto exit; } /* Convert pixel coordinates to 1-based */ Py_BEGIN_ALLOW_THREADS preoffset_array(pixcrd, origin); wcsprm_python2c(&self->x); status = wcsmix( &self->x, mixpix, mixcel, vspan, vstep, viter, (double*)PyArray_DATA(world), (double*)PyArray_DATA(phi), (double*)PyArray_DATA(theta), (double*)PyArray_DATA(imgcrd), (double*)PyArray_DATA(pixcrd)); wcsprm_c2python(&self->x); unoffset_array(pixcrd, origin); unoffset_array(imgcrd, origin); Py_END_ALLOW_THREADS if (status == 0) { result = PyDict_New(); if (result == NULL || PyDict_SetItemString(result, "imgcrd", (PyObject*)imgcrd) || PyDict_SetItemString(result, "phi", (PyObject*)phi) || PyDict_SetItemString(result, "theta", (PyObject*)theta) || PyDict_SetItemString(result, "world", (PyObject*)world)) { goto exit; } } exit: Py_XDECREF(world); Py_XDECREF(phi); Py_XDECREF(theta); Py_XDECREF(imgcrd); Py_XDECREF(pixcrd); if (status == 0) { return result; } else { Py_XDECREF(result); if (status == -1) { /* The error message has already been set */ return NULL; } else { wcs_to_python_exc(&(self->x)); return NULL; } } } /*@null@*/ static PyObject* PyWcsprm_p2s( PyWcsprm* self, PyObject* args, PyObject* kwds) { int naxis = 2; int ncoord = 0; int nelem = 0; PyObject* pixcrd_obj = NULL; int origin = 1; PyArrayObject* pixcrd = NULL; PyArrayObject* imgcrd = NULL; PyArrayObject* phi = NULL; PyArrayObject* theta = NULL; PyArrayObject* world = NULL; PyArrayObject* stat = NULL; PyObject* result = NULL; int status = 0; const char* keywords[] = { "pixcrd", "origin", NULL }; if (!PyArg_ParseTupleAndKeywords( args, kwds, "Oi:p2s", (char **)keywords, &pixcrd_obj, &origin)) { return NULL; } naxis = self->x.naxis; pixcrd = (PyArrayObject*)PyArray_ContiguousFromAny (pixcrd_obj, NPY_DOUBLE, 2, 2); if (pixcrd == NULL) { return NULL; } if (PyArray_DIM(pixcrd, 1) < naxis) { PyErr_Format( PyExc_RuntimeError, "Input array must be 2-dimensional, where the second dimension >= %d", naxis); goto exit; } /* Now we allocate a bunch of numpy arrays to store the results in. */ imgcrd = (PyArrayObject*)PyArray_SimpleNew( 2, PyArray_DIMS(pixcrd), NPY_DOUBLE); if (imgcrd == NULL) { goto exit; } phi = (PyArrayObject*)PyArray_SimpleNew( 1, PyArray_DIMS(pixcrd), NPY_DOUBLE); if (phi == NULL) { goto exit; } theta = (PyArrayObject*)PyArray_SimpleNew( 1, PyArray_DIMS(pixcrd), NPY_DOUBLE); if (theta == NULL) { goto exit; } world = (PyArrayObject*)PyArray_SimpleNew( 2, PyArray_DIMS(pixcrd), NPY_DOUBLE); if (world == NULL) { goto exit; } stat = (PyArrayObject*)PyArray_SimpleNew( 1, PyArray_DIMS(pixcrd), NPY_INT); if (stat == NULL) { goto exit; } /* 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( &self->x, ncoord, nelem, (double*)PyArray_DATA(pixcrd), (double*)PyArray_DATA(imgcrd), (double*)PyArray_DATA(phi), (double*)PyArray_DATA(theta), (double*)PyArray_DATA(world), (int*)PyArray_DATA(stat)); wcsprm_c2python(&self->x); unoffset_array(pixcrd, origin); /* unoffset_array(world, origin); */ unoffset_array(imgcrd, origin); if (status == 8) { set_invalid_to_nan( ncoord, nelem, (double*)PyArray_DATA(imgcrd), (int*)PyArray_DATA(stat)); set_invalid_to_nan( ncoord, 1, (double*)PyArray_DATA(phi), (int*)PyArray_DATA(stat)); set_invalid_to_nan( ncoord, 1, (double*)PyArray_DATA(theta), (int*)PyArray_DATA(stat)); set_invalid_to_nan( ncoord, nelem, (double*)PyArray_DATA(world), (int*)PyArray_DATA(stat)); } Py_END_ALLOW_THREADS if (status == 0 || status == 8) { result = PyDict_New(); if (result == NULL || PyDict_SetItemString(result, "imgcrd", (PyObject*)imgcrd) || PyDict_SetItemString(result, "phi", (PyObject*)phi) || PyDict_SetItemString(result, "theta", (PyObject*)theta) || PyDict_SetItemString(result, "world", (PyObject*)world) || PyDict_SetItemString(result, "stat", (PyObject*)stat)) { goto exit; } } exit: Py_XDECREF(pixcrd); Py_XDECREF(imgcrd); Py_XDECREF(phi); Py_XDECREF(theta); Py_XDECREF(world); Py_XDECREF(stat); if (status == 0 || status == 8) { return result; } else { Py_XDECREF(result); if (status == -1) { /* Exception already set */ return NULL; } else { wcs_to_python_exc(&(self->x)); return NULL; } } } /*@null@*/ static PyObject* PyWcsprm_s2p( PyWcsprm* self, PyObject* args, PyObject* kwds) { int naxis = 2; int ncoord = 0; int nelem = 0; PyObject* world_obj = NULL; int origin = 1; PyArrayObject* world = NULL; PyArrayObject* phi = NULL; PyArrayObject* theta = NULL; PyArrayObject* imgcrd = NULL; PyArrayObject* pixcrd = NULL; PyArrayObject* stat = NULL; PyObject* result = NULL; int status = -1; const char* keywords[] = { "world", "origin", NULL }; if (!PyArg_ParseTupleAndKeywords( args, kwds, "Oi:s2p", (char **)keywords, &world_obj, &origin)) { return NULL; } naxis = self->x.naxis; world = (PyArrayObject*)PyArray_ContiguousFromAny( world_obj, NPY_DOUBLE, 2, 2); if (world == NULL) { return NULL; } if (PyArray_DIM(world, 1) < naxis) { PyErr_Format( PyExc_RuntimeError, "Input array must be 2-dimensional, where the second dimension >= %d", naxis); goto exit; } /* Now we allocate a bunch of numpy arrays to store the * results in. */ phi = (PyArrayObject*)PyArray_SimpleNew( 1, PyArray_DIMS(world), NPY_DOUBLE); if (phi == NULL) { goto exit; } theta = (PyArrayObject*)PyArray_SimpleNew( 1, PyArray_DIMS(world), NPY_DOUBLE); if (phi == NULL) { goto exit; } imgcrd = (PyArrayObject*)PyArray_SimpleNew( 2, PyArray_DIMS(world), NPY_DOUBLE); if (theta == NULL) { goto exit; } pixcrd = (PyArrayObject*)PyArray_SimpleNew( 2, PyArray_DIMS(world), NPY_DOUBLE); if (pixcrd == NULL) { goto exit; } stat = (PyArrayObject*)PyArray_SimpleNew( 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), (double*)PyArray_DATA(phi), (double*)PyArray_DATA(theta), (double*)PyArray_DATA(imgcrd), (double*)PyArray_DATA(pixcrd), (int*)PyArray_DATA(stat)); wcsprm_c2python(&self->x); /* unoffset_array(world, origin); */ unoffset_array(pixcrd, origin); unoffset_array(imgcrd, origin); if (status == 9) { set_invalid_to_nan( ncoord, 1, (double*)PyArray_DATA(phi), (int*)PyArray_DATA(stat)); set_invalid_to_nan( ncoord, 1, (double*)PyArray_DATA(theta), (int*)PyArray_DATA(stat)); set_invalid_to_nan( ncoord, nelem, (double*)PyArray_DATA(imgcrd), (int*)PyArray_DATA(stat)); set_invalid_to_nan( ncoord, nelem, (double*)PyArray_DATA(pixcrd), (int*)PyArray_DATA(stat)); } Py_END_ALLOW_THREADS if (status == 0 || status == 9) { result = PyDict_New(); if (result == NULL || PyDict_SetItemString(result, "phi", (PyObject*)phi) || PyDict_SetItemString(result, "theta", (PyObject*)theta) || PyDict_SetItemString(result, "imgcrd", (PyObject*)imgcrd) || PyDict_SetItemString(result, "pixcrd", (PyObject*)pixcrd) || PyDict_SetItemString(result, "stat", (PyObject*)stat)) { goto exit; } } exit: Py_XDECREF(pixcrd); Py_XDECREF(imgcrd); Py_XDECREF(phi); Py_XDECREF(theta); Py_XDECREF(world); Py_XDECREF(stat); if (status == 0 || status == 9) { return result; } else { Py_XDECREF(result); if (status == -1) { /* Exception already set */ return NULL; } else { wcs_to_python_exc(&(self->x)); return NULL; } } } static 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; } if (convert) wcsprm_python2c(&self->x); status = wcsset(&self->x); if (convert) wcsprm_c2python(&self->x); if (status == 0) { return 0; } else { wcs_to_python_exc(&(self->x)); return 1; } } /*@null@*/ static PyObject* PyWcsprm_set( PyWcsprm* self) { if (PyWcsprm_cset(self, 1)) { return NULL; } Py_INCREF(Py_None); return Py_None; } /*@null@*/ static PyObject* PyWcsprm_set_ps( PyWcsprm* self, PyObject* arg, /*@unused@*/ PyObject* kwds) { if (is_null(self->x.ps)) { return NULL; } if (set_pscards("ps", arg, &self->x.ps, &self->x.nps, &self->x.npsmax)) { self->x.m_ps = self->x.ps; return NULL; } self->x.m_ps = self->x.ps; note_change(self); Py_INCREF(Py_None); return Py_None; } /*@null@*/ static PyObject* PyWcsprm_set_pv( PyWcsprm* self, PyObject* arg, /*@unused@*/ PyObject* kwds) { if (is_null(self->x.pv)) { return NULL; } 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; } } /* TODO: This is convenient for debugging for now -- but it's not very * Pythonic. It should probably be hooked into __str__ or something. */ /*@null@*/ static PyObject* PyWcsprm_print_contents( PyWcsprm* 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); wcsprm_python2c(&self->x); if (PyWcsprm_cset(self, 0)) { wcsprm_c2python(&self->x); return NULL; } wcsprt(&self->x); wcsprm_c2python(&self->x); printf("%s", wcsprintf_buf()); fflush(stdout); Py_INCREF(Py_None); return Py_None; } /*@null@*/ static PyObject* PyWcsprm_spcfix( PyWcsprm* self) { int status = 0; wcsprm_python2c(&self->x); status = spcfix(&self->x); wcsprm_c2python(&self->x); if (status == -1 || status == 0) { return PyLong_FromLong((long)status); } else { wcserr_fix_to_python_exc(self->x.err); return NULL; } } /*@null@*/ static PyObject* PyWcsprm_sptr( PyWcsprm* self, PyObject* args, PyObject* kwds) { int i = -1; const char* py_ctype = NULL; char ctype[9]; int status = 0; const char* keywords[] = {"ctype", "i", NULL}; if (!PyArg_ParseTupleAndKeywords( args, kwds, "s|i:sptr", (char **)keywords, &py_ctype, &i)) { return NULL; } if (strlen(py_ctype) > 8) { PyErr_SetString( PyExc_ValueError, "ctype string has more than 8 characters."); } strncpy(ctype, py_ctype, 9); wcsprm_python2c(&self->x); status = wcssptr(&self->x, &i, ctype); wcsprm_c2python(&self->x); if (status == 0) { Py_INCREF(Py_None); return Py_None; } else { wcs_to_python_exc(&(self->x)); return NULL; } } /*@null@*/ static PyObject* PyWcsprm___str__( PyWcsprm* 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); wcsprm_python2c(&self->x); if (PyWcsprm_cset(self, 0)) { wcsprm_c2python(&self->x); return NULL; } wcsprt(&self->x); wcsprm_c2python(&self->x); return PyUnicode_FromString(wcsprintf_buf()); } 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, &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* PyWcsprm_sub( PyWcsprm* self, PyObject* args, PyObject* kwds) { 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, &py_axes)) { goto exit; } wcslib_version(wcslib_ver); if (py_axes == NULL || py_axes == Py_None) { /* leave all variables as is */ } 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) * 2); if (axes == NULL) { PyErr_SetString(PyExc_MemoryError, "Out of memory"); goto exit; } for (i = 0; i < nsub; ++i) { element = PySequence_GetItem(py_axes, i); if (element == 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); } if (strncmp(element_str, "longitude", 10) == 0) { element_val = WCSSUB_LONGITUDE; } else if (strncmp(element_str, "latitude", 9) == 0) { element_val = WCSSUB_LATITUDE; } else if (strncmp(element_str, "cubeface", 9) == 0) { element_val = WCSSUB_CUBEFACE; } else if (strncmp(element_str, "spectral", 9) == 0) { 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, #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; } Py_CLEAR(element_utf8); } else if (PyLong_Check(element)) { tmp = (Py_ssize_t)PyLong_AsSsize_t(element); if (tmp == -1 && PyErr_Occurred()) { goto exit; } element_val = (int)tmp; } else { PyErr_SetString( PyExc_TypeError, "axes sequence must contain either strings or ints"); goto exit; } axes[i] = element_val; Py_CLEAR(element); } } else if (PyLong_Check(py_axes)) { tmp = (Py_ssize_t)PyLong_AsSsize_t(py_axes); if (tmp == -1 && PyErr_Occurred()) { goto exit; } nsub = (int)tmp; if (nsub < 0 || nsub > self->x.naxis) { PyErr_Format( PyExc_ValueError, "If axes is an int, it must be in the range 0-self.naxis (%d)", self->x.naxis); goto exit; } } else { PyErr_SetString( PyExc_TypeError, "axes must None, a sequence or an integer"); goto exit; } py_dest_wcs = (PyWcsprm*)PyWcsprm_cnew(); py_dest_wcs->x.flag = -1; status = wcsini(0, nsub, &py_dest_wcs->x); if (status != 0) { goto exit; } wcsprm_python2c(&self->x); status = wcssub(1, &self->x, &nsub, axes, &py_dest_wcs->x); wcsprm_c2python(&self->x); if (PyWcsprm_cset(py_dest_wcs, 0)) { status = -1; goto exit; } wcsprm_c2python(&py_dest_wcs->x); if (status != 0) { goto exit; } exit: free(axes); Py_XDECREF(element); Py_XDECREF(element_utf8); if (status == 0) { return (PyObject*)py_dest_wcs; } else if (status == -1) { Py_XDECREF(py_dest_wcs); /* Exception already set */ return NULL; } else { wcs_to_python_exc(&(py_dest_wcs->x)); Py_XDECREF(py_dest_wcs); return NULL; } } /*@null@*/ static PyObject* PyWcsprm_to_header( PyWcsprm* self, PyObject* args, PyObject* kwds) { PyObject* relax_obj = NULL; int relax = 0; int nkeyrec = 0; char* header = NULL; int status = -1; PyObject* result = NULL; const char* keywords[] = {"relax", NULL}; if (!PyArg_ParseTupleAndKeywords( args, kwds, "|O:to_header", (char **)keywords, &relax_obj)) { goto exit; } if (relax_obj == Py_True) { relax = WCSHDO_all; } else if (relax_obj == NULL || relax_obj == Py_False) { relax = WCSHDO_safe; } else { relax = (int)PyLong_AsLong(relax_obj); if (relax == -1) { PyErr_SetString( PyExc_ValueError, "relax must be True, False or an integer."); return NULL; } } 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; } /* 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); exit: free(header); return result; } /*@null@*/ static PyObject* PyWcsprm_unitfix( PyWcsprm* self, PyObject* args, PyObject* kwds) { const char* translate_units = NULL; int ctrl = 0; int status = 0; const char* keywords[] = {"translate_units", NULL}; if (!PyArg_ParseTupleAndKeywords( args, kwds, "|s:unitfix", (char **)keywords, &translate_units)) { return NULL; } if (translate_units != NULL) { if (parse_unsafe_unit_conversion_spec(translate_units, &ctrl)) { return NULL; } } status = unitfix(ctrl, &self->x); if (status == -1 || status == 0) { return PyLong_FromLong((long)status); } else { wcserr_fix_to_python_exc(self->x.err); return NULL; } } /*************************************************************************** * Member getters/setters (properties) */ /*@null@*/ static PyObject* PyWcsprm_get_alt( PyWcsprm* self, /*@unused@*/ void* closure) { if (is_null(self->x.alt)) { return NULL; } /* Force a null-termination of this single-character string */ self->x.alt[1] = '\0'; return get_string("alt", self->x.alt); } static int PyWcsprm_set_alt( PyWcsprm* self, PyObject* value, /*@unused@*/ void* closure) { char value_string[2]; if (is_null(self->x.alt)) { return -1; } if (value == NULL) { /* deletion */ self->x.alt[0] = ' '; self->x.alt[1] = '\0'; note_change(self); return 0; } if (set_string("alt", value, value_string, 2)) { return -1; } if (!is_valid_alt_key(value_string)) { return -1; } strncpy(self->x.alt, value_string, 2); return 0; } /*@null@*/ static PyObject* PyWcsprm_get_axis_types( PyWcsprm* self, /*@unused@*/ void* closure) { Py_ssize_t naxis = 0; if (is_null(self->x.types)) { return NULL; } if (PyWcsprm_cset(self, 1)) { return NULL; } naxis = (Py_ssize_t)self->x.naxis; 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]; if (is_null(self->x.cd)) { return NULL; } if ((self->x.altlin & has_cd) == 0) { PyErr_SetString(PyExc_AttributeError, "No cd is present."); return NULL; } dims[0] = self->x.naxis; dims[1] = self->x.naxis; return get_double_array("cd", self->x.cd, 2, dims, (PyObject*)self); } static int PyWcsprm_set_cd( PyWcsprm* self, PyObject* value, /*@unused@*/ void* closure) { npy_intp dims[2]; if (is_null(self->x.cd)) { return -1; } if (value == NULL) { self->x.altlin &= ~has_cd; note_change(self); return 0; } dims[0] = self->x.naxis; dims[1] = self->x.naxis; if (set_double_array("cd", value, 2, dims, self->x.cd)) { return -1; } self->x.altlin |= has_cd; note_change(self); return 0; } /*@null@*/ static PyObject* PyWcsprm_get_cdelt( PyWcsprm* self, /*@unused@*/ void* closure) { Py_ssize_t naxis = 0; if (is_null(self->x.cdelt)) { return NULL; } naxis = self->x.naxis; if (self->x.altlin & has_cd) { PyErr_WarnEx(NULL, "cdelt will be ignored since cd is present", 1); } return get_double_array("cdelt", self->x.cdelt, 1, &naxis, (PyObject*)self); } /*@null@*/ static int PyWcsprm_set_cdelt( PyWcsprm* self, PyObject* value, /*@unused@*/ void* closure) { npy_intp dims; if (is_null(self->x.cdelt)) { return -1; } dims = (npy_int)self->x.naxis; if (self->x.altlin & has_cd) { PyErr_WarnEx(NULL, "cdelt will be ignored since cd is present", 1); } note_change(self); return set_double_array("cdelt", value, 1, &dims, self->x.cdelt); } static PyObject* PyWcsprm_get_cel_offset( PyWcsprm* self, /*@unused@*/ void* closure) { return get_bool("cel_offset", self->x.cel.offset); } static int PyWcsprm_set_cel_offset( PyWcsprm* self, PyObject* value, /*@unused@*/ void* closure) { note_change(self); return set_bool("cel_offset", value, &self->x.cel.offset); } /*@null@*/ static PyObject* PyWcsprm_get_cname( PyWcsprm* self, /*@unused@*/ void* closure) { if (is_null(self->x.cname)) { return NULL; } return get_str_list("cname", self->x.cname, (Py_ssize_t)self->x.naxis, 68, (PyObject*)self); } /*@null@*/ static int PyWcsprm_set_cname( PyWcsprm* self, PyObject* value, /*@unused@*/ void* closure) { if (is_null(self->x.cname)) { return -1; } return set_str_list("cname", value, (Py_ssize_t)self->x.naxis, 0, self->x.cname); } /*@null@*/ static PyObject* PyWcsprm_get_colax( PyWcsprm* self, /*@unused@*/ void* closure) { Py_ssize_t naxis = 0; if (is_null(self->x.colax)) { return NULL; } naxis = (Py_ssize_t)self->x.naxis; return get_int_array("colax", self->x.colax, 1, &naxis, (PyObject*)self); } static int PyWcsprm_set_colax( PyWcsprm* self, PyObject* value, /*@unused@*/ void* closure) { npy_intp naxis = 0; if (is_null(self->x.colax)) { return -1; } naxis = (Py_ssize_t)self->x.naxis; return set_int_array("colax", value, 1, &naxis, self->x.colax); } static PyObject* PyWcsprm_get_colnum( PyWcsprm* self, /*@unused@*/ void* closure) { return get_int("colnum", self->x.colnum); } static int PyWcsprm_set_colnum( PyWcsprm* self, PyObject* value, /*@unused@*/ void* closure) { return set_int("colnum", value, &self->x.colnum); } /*@null@*/ static PyObject* PyWcsprm_get_crder( PyWcsprm* self, /*@unused@*/ void* closure) { Py_ssize_t naxis = 0; if (is_null(self->x.crder)) { return NULL; } naxis = (Py_ssize_t)self->x.naxis; return get_double_array("crder", self->x.crder, 1, &naxis, (PyObject*)self); } static int PyWcsprm_set_crder( PyWcsprm* self, PyObject* value, /*@unused@*/ void* closure) { npy_intp naxis = 0; if (is_null(self->x.crder)) { return -1; } naxis = (Py_ssize_t)self->x.naxis; return set_double_array("crder", value, 1, &naxis, self->x.crder); } /*@null@*/ static PyObject* PyWcsprm_get_crota( PyWcsprm* self, /*@unused@*/ void* closure) { Py_ssize_t naxis = 0; if (is_null(self->x.crota)) { return NULL; } if ((self->x.altlin & has_crota) == 0) { PyErr_SetString(PyExc_AttributeError, "No crota is present."); return NULL; } naxis = (Py_ssize_t)self->x.naxis; return get_double_array("crota", self->x.crota, 1, &naxis, (PyObject*)self); } static int PyWcsprm_set_crota( PyWcsprm* self, PyObject* value, /*@unused@*/ void* closure) { npy_intp naxis = 0; if (is_null(self->x.crota)) { return -1; } if (value == NULL) { /* Deletion */ self->x.altlin &= ~has_crota; note_change(self); return 0; } naxis = (Py_ssize_t)self->x.naxis; if (set_double_array("crota", value, 1, &naxis, self->x.crota)) { return -1; } self->x.altlin |= has_crota; note_change(self); return 0; } /*@null@*/ static PyObject* PyWcsprm_get_crpix( PyWcsprm* self, /*@unused@*/ void* closure) { Py_ssize_t naxis = 0; if (is_null(self->x.crpix)) { return NULL; } naxis = (Py_ssize_t)self->x.naxis; return get_double_array("crpix", self->x.crpix, 1, &naxis, (PyObject*)self); } static int PyWcsprm_set_crpix( PyWcsprm* self, PyObject* value, /*@unused@*/ void* closure) { npy_intp naxis = 0; if (is_null(self->x.crpix)) { return -1; } naxis = (Py_ssize_t)self->x.naxis; note_change(self); return set_double_array("crpix", value, 1, &naxis, self->x.crpix); } /*@null@*/ static PyObject* PyWcsprm_get_crval( PyWcsprm* self, /*@unused@*/ void* closure) { Py_ssize_t naxis = 0; if (is_null(self->x.crval)) { return NULL; } naxis = (Py_ssize_t)self->x.naxis; return get_double_array("crval", self->x.crval, 1, &naxis, (PyObject*)self); } static int PyWcsprm_set_crval( PyWcsprm* self, PyObject* value, /*@unused@*/ void* closure) { npy_intp naxis; if (is_null(self->x.crval)) { return -1; } naxis = (Py_ssize_t)self->x.naxis; note_change(self); return set_double_array("crval", value, 1, &naxis, self->x.crval); } /*@null@*/ static PyObject* PyWcsprm_get_csyer( PyWcsprm* self, /*@unused@*/ void* closure) { Py_ssize_t naxis; if (is_null(self->x.csyer)) { return NULL; } naxis = (Py_ssize_t)self->x.naxis; return get_double_array("csyer", self->x.csyer, 1, &naxis, (PyObject*)self); } static int PyWcsprm_set_csyer( PyWcsprm* self, PyObject* value, /*@unused@*/ void* closure) { npy_intp naxis; if (is_null(self->x.csyer)) { return -1; } naxis = (Py_ssize_t)self->x.naxis; return set_double_array("csyer", value, 1, &naxis, self->x.csyer); } /*@null@*/ static PyObject* PyWcsprm_get_ctype( PyWcsprm* self, /*@unused@*/ void* closure) { if (is_null(self->x.ctype)) { return NULL; } return get_str_list("ctype", self->x.ctype, self->x.naxis, 68, (PyObject*)self); } static int PyWcsprm_set_ctype( PyWcsprm* self, PyObject* value, /*@unused@*/ void* closure) { if (is_null(self->x.ctype)) { return -1; } note_change(self); return set_str_list("ctype", value, (Py_ssize_t)self->x.naxis, 0, self->x.ctype); } static PyObject* PyWcsprm_get_cubeface( PyWcsprm* self, /*@unused@*/ void* closure) { return get_int("cubeface", self->x.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; } return get_unit_list( "cunit", self->x.cunit, (Py_ssize_t)self->x.naxis, (PyObject*)self); } static int PyWcsprm_set_cunit( PyWcsprm* self, PyObject* value, /*@unused@*/ void* closure) { if (is_null(self->x.cunit)) { return -1; } note_change(self); 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, /*@unused@*/ void* closure) { if (is_null(self->x.trefpos)) { return NULL; } return get_string("trefpos", self->x.trefpos); } static int PyWcsprm_set_trefpos( PyWcsprm* self, PyObject* value, /*@unused@*/ void* closure) { if (is_null(self->x.trefpos)) { return -1; } return set_string("trefpos", value, self->x.trefpos, 72); } /*@null@*/ static PyObject* PyWcsprm_get_trefdir( PyWcsprm* self, /*@unused@*/ void* closure) { if (is_null(self->x.trefdir)) { return NULL; } return get_string("trefdir", self->x.trefdir); } static int PyWcsprm_set_trefdir( PyWcsprm* self, PyObject* value, /*@unused@*/ void* closure) { if (is_null(self->x.trefdir)) { return -1; } return set_string("trefdir", value, self->x.trefdir, 72); } /*@null@*/ static PyObject* PyWcsprm_get_timeunit( PyWcsprm* self, /*@unused@*/ void* closure) { if (is_null(self->x.timeunit)) { return NULL; } return get_string("timeunit", self->x.timeunit); } static int PyWcsprm_set_timeunit( PyWcsprm* self, PyObject* value, /*@unused@*/ void* closure) { if (is_null(self->x.timeunit)) { return -1; } return set_string("timeunit", value, self->x.timeunit, 72); } /*@null@*/ static PyObject* PyWcsprm_get_plephem( PyWcsprm* self, /*@unused@*/ void* closure) { if (is_null(self->x.plephem)) { return NULL; } return get_string("plephem", self->x.plephem); } static int PyWcsprm_set_plephem( PyWcsprm* self, PyObject* value, /*@unused@*/ void* closure) { if (is_null(self->x.plephem)) { return -1; } return set_string("plephem", value, self->x.plephem, 72); } static PyObject* PyWcsprm_get_tstart( PyWcsprm* self, /*@unused@*/ void* closure) { return get_double("tstart", self->x.tstart); } static int PyWcsprm_set_tstart( PyWcsprm* self, PyObject* value, /*@unused@*/ void* closure) { if (value == NULL) { self->x.tstart = (double)NPY_NAN; return 0; } return set_double("tstart", value, &self->x.tstart); } static PyObject* PyWcsprm_get_tstop( PyWcsprm* self, /*@unused@*/ void* closure) { return get_double("tstop", self->x.tstop); } static int PyWcsprm_set_tstop( PyWcsprm* self, PyObject* value, /*@unused@*/ void* closure) { if (value == NULL) { self->x.tstop = (double)NPY_NAN; return 0; } return set_double("tstop", value, &self->x.tstop); } static PyObject* PyWcsprm_get_telapse( PyWcsprm* self, /*@unused@*/ void* closure) { 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 set_double("telapse", value, &self->x.telapse); } static PyObject* PyWcsprm_get_timeoffs( PyWcsprm* self, /*@unused@*/ void* closure) { return get_double("timeoffs", self->x.timeoffs); } static int PyWcsprm_set_timeoffs( PyWcsprm* self, PyObject* value, /*@unused@*/ void* closure) { if (value == NULL) { self->x.timeoffs = (double)NPY_NAN; return 0; } return set_double("timeoffs", value, &self->x.timeoffs); } static PyObject* PyWcsprm_get_timsyer( PyWcsprm* self, /*@unused@*/ void* closure) { return get_double("timsyer", self->x.timsyer); } static int PyWcsprm_set_timsyer( PyWcsprm* self, PyObject* value, /*@unused@*/ void* closure) { if (value == NULL) { self->x.timsyer = (double)NPY_NAN; return 0; } return set_double("timsyer", value, &self->x.timsyer); } static PyObject* PyWcsprm_get_timrder( PyWcsprm* self, /*@unused@*/ void* closure) { return get_double("timrder", self->x.timrder); } static int PyWcsprm_set_timrder( PyWcsprm* self, PyObject* value, /*@unused@*/ void* closure) { if (value == NULL) { self->x.timrder = (double)NPY_NAN; return 0; } return set_double("timrder", value, &self->x.timrder); } static PyObject* PyWcsprm_get_timedel( PyWcsprm* self, /*@unused@*/ void* closure) { return get_double("timedel", self->x.timedel); } static int PyWcsprm_set_timedel( PyWcsprm* self, PyObject* value, /*@unused@*/ void* closure) { if (value == NULL) { self->x.timedel = (double)NPY_NAN; return 0; } return set_double("timedel", value, &self->x.timedel); } static PyObject* PyWcsprm_get_timepixr( PyWcsprm* self, /*@unused@*/ void* closure) { return get_double("timepixr", self->x.timepixr); } static int PyWcsprm_set_timepixr( PyWcsprm* self, PyObject* value, /*@unused@*/ void* closure) { if (value == NULL) { self->x.timepixr = (double)NPY_NAN; return 0; } return set_double("timepixr", value, &self->x.timepixr); } /*@null@*/ static PyObject* PyWcsprm_get_obsorbit( PyWcsprm* self, /*@unused@*/ void* closure) { if (is_null(self->x.obsorbit)) { return NULL; } return get_string("obsorbit", self->x.obsorbit); } static int PyWcsprm_set_obsorbit( PyWcsprm* self, PyObject* value, /*@unused@*/ void* closure) { 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.xposure = (double)NPY_NAN; return 0; } return set_double("xposure", value, &self->x.xposure); } /*@null@*/ static PyObject* PyWcsprm_get_name( PyWcsprm* self, /*@unused@*/ void* closure) { if (is_null(self->x.wcsname)) { return NULL; } return get_string("name", self->x.wcsname); } static int PyWcsprm_set_name( PyWcsprm* self, PyObject* value, /*@unused@*/ void* closure) { if (is_null(self->x.wcsname)) { return -1; } return set_string("name", value, self->x.wcsname, 72); } static PyObject* PyWcsprm_get_naxis( PyWcsprm* self, /*@unused@*/ void* closure) { return get_int("naxis", self->x.naxis); } /*@null@*/ static PyObject* PyWcsprm_get_obsgeo( PyWcsprm* self, /*@unused@*/ void* closure) { Py_ssize_t size = 6; if (is_null(self->x.obsgeo)) { return NULL; } return get_double_array("obsgeo", self->x.obsgeo, 1, &size, (PyObject*)self); } static int PyWcsprm_set_obsgeo( PyWcsprm* self, PyObject* value, /*@unused@*/ void* closure) { npy_intp size = 6; if (is_null(self->x.obsgeo)) { return -1; } 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; } return set_double_array("obsgeo", value, 1, &size, self->x.obsgeo); } /*@null@*/ static PyObject* PyWcsprm_get_pc( PyWcsprm* self, /*@unused@*/ void* closure) { npy_intp dims[2]; if (is_null(self->x.pc)) { return NULL; } if (self->x.altlin != 0 && (self->x.altlin & has_pc) == 0) { PyErr_SetString(PyExc_AttributeError, "No pc is present."); return NULL; } dims[0] = self->x.naxis; dims[1] = self->x.naxis; return get_double_array("pc", self->x.pc, 2, dims, (PyObject*)self); } static int PyWcsprm_set_pc( PyWcsprm* self, PyObject* value, /*@unused@*/ void* closure) { npy_intp dims[2]; int i, j, naxis; double* pc; if (is_null(self->x.pc)) { return -1; } note_change(self); if (value == NULL) { /* deletion */ self->x.altlin &= ~has_pc; /* If this results in deleting all flags, pc is still the default, so we should set the pc matrix itself to default values. */ naxis = self->x.naxis; pc = self->x.pc; for (i = 0; i < naxis; i++) { for (j = 0; j < naxis; j++) { if (j == i) { *pc = 1.0; } else { *pc = 0.0; } pc++; } } note_change(self); return 0; } dims[0] = self->x.naxis; dims[1] = self->x.naxis; if (set_double_array("pc", value, 2, dims, self->x.pc)) { return -1; } self->x.altlin |= has_pc; note_change(self); return 0; } static PyObject* PyWcsprm_get_phi0( PyWcsprm* self, /*@unused@*/ void* closure) { return get_double("phi0", self->x.cel.phi0); } static int PyWcsprm_set_phi0( PyWcsprm* self, PyObject* value, /*@unused@*/ void* closure) { note_change(self); if (value == NULL) { self->x.cel.phi0 = (double)NPY_NAN; return 0; } return set_double("phi0", value, &(self->x.cel.phi0)); } static PyObject* PyWcsprm_get_piximg_matrix( PyWcsprm* self, /*@unused@*/ void* closure) { npy_intp dims[2]; if (is_null(self->x.lin.piximg)) { return NULL; } if (PyWcsprm_cset(self, 1)) { return NULL; } dims[0] = self->x.naxis; dims[1] = self->x.naxis; return get_double_array("piximg_matrix", self->x.lin.piximg, 2, dims, (PyObject*)self); } static PyObject* PyWcsprm_get_radesys( PyWcsprm* self, /*@unused@*/ void* closure) { if (is_null(self->x.radesys)) { return NULL; } return get_string("radesys", self->x.radesys); } static int PyWcsprm_set_radesys( PyWcsprm* self, PyObject* value, /*@unused@*/ void* closure) { if (is_null(self->x.radesys)) { return -1; } return set_string("radesys", value, self->x.radesys, 72); } static PyObject* PyWcsprm_get_restfrq( PyWcsprm* self, /*@unused@*/ void* closure) { return get_double("restfrq", self->x.restfrq); } static int PyWcsprm_set_restfrq( PyWcsprm* self, PyObject* value, /*@unused@*/ void* closure) { if (value == NULL) { /* deletion */ self->x.restfrq = (double)NPY_NAN; return 0; } note_change(self); return set_double("restfrq", value, &self->x.restfrq); } static PyObject* PyWcsprm_get_restwav( PyWcsprm* self, /*@unused@*/ void* closure) { return get_double("restwav", self->x.restwav); } static int PyWcsprm_set_restwav( PyWcsprm* self, PyObject* value, /*@unused@*/ void* closure) { if (value == NULL) { /* deletion */ self->x.restwav = (double)NPY_NAN; return 0; } note_change(self); return set_double("restwav", value, &self->x.restwav); } static PyObject* PyWcsprm_get_spec( PyWcsprm* self, /*@unused@*/ void* closure) { return get_int("spec", self->x.spec); } /*@null@*/ static PyObject* PyWcsprm_get_specsys( PyWcsprm* self, /*@unused@*/ void* closure) { if (is_null(self->x.specsys)) { return NULL; } return get_string("specsys", self->x.specsys); } static int PyWcsprm_set_specsys( PyWcsprm* self, PyObject* value, /*@unused@*/ void* closure) { if (is_null(self->x.specsys)) { return -1; } return set_string("specsys", value, self->x.specsys, 72); } /*@null@*/ static PyObject* PyWcsprm_get_ssysobs( PyWcsprm* self, /*@unused@*/ void* closure) { if (is_null(self->x.ssysobs)) { return NULL; } return get_string("ssysobs", self->x.ssysobs); } static int PyWcsprm_set_ssysobs( PyWcsprm* self, PyObject* value, /*@unused@*/ void* closure) { if (is_null(self->x.ssysobs)) { return -1; } note_change(self); return set_string("ssysobs", value, self->x.ssysobs, 72); } /*@null@*/ static PyObject* PyWcsprm_get_ssyssrc( PyWcsprm* self, /*@unused@*/ void* closure) { if (is_null(self->x.ssyssrc)) { return NULL; } return get_string("ssyssrc", self->x.ssyssrc); } static int PyWcsprm_set_ssyssrc( PyWcsprm* self, PyObject* value, /*@unused@*/ void* closure) { if (is_null(self->x.ssyssrc)) { return -1; } return set_string("ssyssrc", value, self->x.ssyssrc, 72); } static PyObject* PyWcsprm_get_tab( PyWcsprm* self, /*@unused@*/ void* closure) { PyObject* result; PyObject* subresult; int i, ntab; ntab = self->x.ntab; result = PyList_New(ntab); if (result == NULL) { return NULL; } for (i = 0; i < ntab; ++i) { subresult = (PyObject *)PyTabprm_cnew((PyObject *)self, &(self->x.tab[i])); if (subresult == NULL) { Py_DECREF(result); return NULL; } if (PyList_SetItem(result, i, subresult) == -1) { Py_DECREF(subresult); Py_DECREF(result); return NULL; } } return result; } static PyObject* PyWcsprm_get_theta0( PyWcsprm* self, /*@unused@*/ void* closure) { return get_double("theta0", self->x.cel.theta0); } static int PyWcsprm_set_theta0( PyWcsprm* self, PyObject* value, /*@unused@*/ void* closure) { note_change(self); if (value == NULL) { self->x.cel.theta0 = (double)NPY_NAN; return 0; } return set_double("theta0", value, &self->x.cel.theta0); } static PyObject* PyWcsprm_get_velangl( PyWcsprm* self, /*@unused@*/ void* closure) { return get_double("velangl", self->x.velangl); } static int PyWcsprm_set_velangl( PyWcsprm* self, PyObject* value, /*@unused@*/ void* closure) { if (value == NULL) { /* deletion */ self->x.velangl = (double)NPY_NAN; return 0; } return set_double("velangl", value, &self->x.velangl); } static PyObject* PyWcsprm_get_velosys( PyWcsprm* self, /*@unused@*/ void* closure) { return get_double("velosys", self->x.velosys); } static int PyWcsprm_set_velosys( PyWcsprm* self, PyObject* value, /*@unused@*/ void* closure) { if (value == NULL) { /* deletion */ self->x.velosys = (double)NPY_NAN; return 0; } return set_double("velosys", value, &self->x.velosys); } 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; nwtb = self->x.nwtb; list = PyList_New(nwtb); if (list == 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; } PyList_SET_ITEM(list, i, elem); } return list; } static PyObject* PyWcsprm_get_zsource( PyWcsprm* self, /*@unused@*/ void* closure) { return get_double("zsource", self->x.zsource); } static int PyWcsprm_set_zsource( PyWcsprm* self, PyObject* value, /*@unused@*/ void* closure) { if (value == NULL) { /* deletion */ self->x.zsource = (double)NPY_NAN; return 0; } return set_double("zsource", value, &self->x.zsource); } 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}, {"cname", (getter)PyWcsprm_get_cname, (setter)PyWcsprm_set_cname, (char *)doc_cname}, {"colax", (getter)PyWcsprm_get_colax, (setter)PyWcsprm_set_colax, (char *)doc_colax}, {"colnum", (getter)PyWcsprm_get_colnum, (setter)PyWcsprm_set_colnum, (char *)doc_colnum}, {"crder", (getter)PyWcsprm_get_crder, (setter)PyWcsprm_set_crder, (char *)doc_crder}, {"crota", (getter)PyWcsprm_get_crota, (setter)PyWcsprm_set_crota, (char *)doc_crota}, {"crpix", (getter)PyWcsprm_get_crpix, (setter)PyWcsprm_set_crpix, (char *)doc_crpix}, {"crval", (getter)PyWcsprm_get_crval, (setter)PyWcsprm_set_crval, (char *)doc_crval}, {"csyer", (getter)PyWcsprm_get_csyer, (setter)PyWcsprm_set_csyer, (char *)doc_csyer}, {"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}, {"lng", (getter)PyWcsprm_get_lng, NULL, (char *)doc_lng}, {"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}, {"spec", (getter)PyWcsprm_get_spec, NULL, (char *)doc_spec}, {"specsys", (getter)PyWcsprm_get_specsys, (setter)PyWcsprm_set_specsys, (char *)doc_specsys}, {"ssysobs", (getter)PyWcsprm_get_ssysobs, (setter)PyWcsprm_set_ssysobs, (char *)doc_ssysobs}, {"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}, {"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}, {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}, {"__deepcopy__", (PyCFunction)PyWcsprm_copy, METH_O, doc_copy}, {"fix", (PyCFunction)PyWcsprm_fix, METH_VARARGS|METH_KEYWORDS, doc_fix}, {"get_cdelt", (PyCFunction)PyWcsprm_get_cdelt_func, METH_NOARGS, doc_get_cdelt}, {"get_pc", (PyCFunction)PyWcsprm_get_pc_func, METH_NOARGS, doc_get_pc}, {"get_ps", (PyCFunction)PyWcsprm_get_ps, METH_NOARGS, doc_get_ps}, {"get_pv", (PyCFunction)PyWcsprm_get_pv, METH_NOARGS, doc_get_pv}, {"has_cd", (PyCFunction)PyWcsprm_has_cdi_ja, METH_NOARGS, doc_has_cd}, {"has_cdi_ja", (PyCFunction)PyWcsprm_has_cdi_ja, METH_NOARGS, doc_has_cdi_ja}, {"has_crota", (PyCFunction)PyWcsprm_has_crotaia, METH_NOARGS, doc_has_crota}, {"has_crotaia", (PyCFunction)PyWcsprm_has_crotaia, METH_NOARGS, doc_has_crotaia}, {"has_pc", (PyCFunction)PyWcsprm_has_pci_ja, METH_NOARGS, doc_has_pc}, {"has_pci_ja", (PyCFunction)PyWcsprm_has_pci_ja, METH_NOARGS, doc_has_pci_ja}, {"is_unity", (PyCFunction)PyWcsprm_is_unity, METH_NOARGS, doc_is_unity}, {"mix", (PyCFunction)PyWcsprm_mix, METH_VARARGS|METH_KEYWORDS, doc_mix}, {"p2s", (PyCFunction)PyWcsprm_p2s, METH_VARARGS|METH_KEYWORDS, doc_p2s}, {"print_contents", (PyCFunction)PyWcsprm_print_contents, METH_NOARGS, doc_print_contents}, {"s2p", (PyCFunction)PyWcsprm_s2p, METH_VARARGS|METH_KEYWORDS, doc_s2p}, {"set", (PyCFunction)PyWcsprm_set, METH_NOARGS, doc_set}, {"set_ps", (PyCFunction)PyWcsprm_set_ps, METH_O, doc_set_ps}, {"set_pv", (PyCFunction)PyWcsprm_set_pv, METH_O, doc_set_pv}, {"spcfix", (PyCFunction)PyWcsprm_spcfix, METH_NOARGS, doc_spcfix}, {"sptr", (PyCFunction)PyWcsprm_sptr, METH_VARARGS|METH_KEYWORDS, doc_sptr}, {"sub", (PyCFunction)PyWcsprm_sub, METH_VARARGS|METH_KEYWORDS, doc_sub}, {"to_header", (PyCFunction)PyWcsprm_to_header, METH_VARARGS|METH_KEYWORDS, doc_to_header}, {"unitfix", (PyCFunction)PyWcsprm_unitfix, METH_VARARGS|METH_KEYWORDS, doc_unitfix}, {NULL} }; PyTypeObject PyWcsprmType = { PyVarObject_HEAD_INIT(NULL, 0) "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 */ PyWcsprm_richcompare, /* 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 */ }; #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(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) { if (PyType_Ready(&PyWcsprmType) < 0) { return -1; } Py_INCREF(&PyWcsprmType); wcsprintf_set(NULL); wcserr_enable(1); return ( PyModule_AddObject(m, "Wcsprm", (PyObject *)&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) || CONSTANT(WCSHDR_LONGKEY) || CONSTANT(WCSHDR_CNAMn) || CONSTANT(WCSHDR_AUXIMG) || CONSTANT(WCSHDR_ALLIMG) || CONSTANT(WCSHDO_none) || CONSTANT(WCSHDO_all) || CONSTANT(WCSHDO_safe) || CONSTANT(WCSHDO_DOBSn) || CONSTANT(WCSHDO_TPCn_ka) || CONSTANT(WCSHDO_PVn_ma) || CONSTANT(WCSHDO_CRPXna) || CONSTANT(WCSHDO_CNAMna) || 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)); } astropy-astropy-201cddb/astropy/wcs/src/wcslib_wtbarr_wrap.c000066400000000000000000000157041507226315300245330ustar00rootroot00000000000000#define NO_IMPORT_ARRAY #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 (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" /*************************************************************************** * PyWtbarr methods * ***************************************************************************/ static PyObject* PyWtbarr_new(PyTypeObject* type, PyObject* args, PyObject* kwds) { PyWtbarr* self; self = (PyWtbarr*)type->tp_alloc(type, 0); return (PyObject*)self; } static int PyWtbarr_traverse(PyWtbarr* self, visitproc visit, void *arg) { Py_VISIT(self->owner); return 0; } static int PyWtbarr_clear(PyWtbarr* self) { Py_CLEAR(self->owner); return 0; } static void PyWtbarr_dealloc(PyWtbarr* self) { PyWtbarr_clear(self); Py_TYPE(self)->tp_free((PyObject*)self); } PyWtbarr* PyWtbarr_cnew(PyObject* wcsprm, struct wtbarr* x) { PyWtbarr* self; self = (PyWtbarr*)(&PyWtbarrType)->tp_alloc(&PyWtbarrType, 0); if (self == NULL) return NULL; self->x = x; Py_INCREF(wcsprm); self->owner = wcsprm; return self; } static void wtbarrprt(const struct wtbarr *wtb) { int i, nd, ndim; if (wtb == 0x0) return; 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); 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; } 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; } 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()); } /*************************************************************************** * Member getters/setters (properties) */ static PyObject* PyWtbarr_get_i(PyWtbarr* self, void* closure) { return get_int("i", self->x->i); } static PyObject* PyWtbarr_get_m(PyWtbarr* self, void* closure) { return get_int("m", self->x->m); } static PyObject* PyWtbarr_get_extver(PyWtbarr* self, void* closure) { return get_int("extver", self->x->extver); } static PyObject* PyWtbarr_get_extlev(PyWtbarr* self, void* closure) { return get_int("extlev", self->x->extlev); } static PyObject* PyWtbarr_get_ndim(PyWtbarr* self, void* closure) { return get_int("ndim", self->x->ndim); } static PyObject* PyWtbarr_get_row(PyWtbarr* self, void* closure) { return get_int("row", self->x->row); } static PyObject* PyWtbarr_get_extnam(PyWtbarr* self, void* closure) { if (is_null(self->x->extnam)) return NULL; return get_string("extnam", self->x->extnam); } static PyObject* PyWtbarr_get_ttype(PyWtbarr* self, void* closure) { if (is_null(self->x->ttype)) return NULL; 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[] = { {"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 = { PyVarObject_HEAD_INIT(NULL, 0) "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*/ (reprfunc)PyWtbarr___str__, /*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 */ }; int _setup_wtbarr_type(PyObject* m) { if (PyType_Ready(&PyWtbarrType) < 0) { return -1; } Py_INCREF(&PyWtbarrType); PyModule_AddObject(m, "Wtbarr", (PyObject *)&PyWtbarrType); return 0; } astropy-astropy-201cddb/astropy/wcs/tests/000077500000000000000000000000001507226315300210365ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/wcs/tests/__init__.py000066400000000000000000000001001507226315300231360ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst astropy-astropy-201cddb/astropy/wcs/tests/conftest.py000066400000000000000000000017271507226315300232440ustar00rootroot00000000000000# 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 astropy-astropy-201cddb/astropy/wcs/tests/data/000077500000000000000000000000001507226315300217475ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/wcs/tests/data/2wcses.hdr000066400000000000000000000207001507226315300236530ustar00rootroot00000000000000SIMPLE = 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 astropy-astropy-201cddb/astropy/wcs/tests/data/3d_cd.hdr000066400000000000000000000024001507226315300234160ustar00rootroot00000000000000CD1_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 astropy-astropy-201cddb/astropy/wcs/tests/data/chandra-pixlist-wcs.hdr000066400000000000000000000146401507226315300263370ustar00rootroot00000000000000DATE = '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 astropy-astropy-201cddb/astropy/wcs/tests/data/defunct_keywords.hdr000066400000000000000000000060631507226315300260320ustar00rootroot00000000000000SIMPLE = 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 ENDastropy-astropy-201cddb/astropy/wcs/tests/data/dist.fits000066400000000000000000000550001507226315300236010ustar00rootroot00000000000000SIMPLE = 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 = 0 / Pixel coordinate of reference point CRPIX2 = 0 / Pixel coordinate of reference point CDELT1 = 1 / Coordinate increment at reference point CDELT2 = 1 / Coordinate increment at reference point CRVAL1 = 0 / Coordinate value at reference point CRVAL2 = 0 / Coordinate value at reference point LATPOLE = 90 / [deg] Native latitude of celestial pole RESTFRQ = 0 / [Hz] Line rest frequency RESTWAV = 0 / [Hz] Line rest wavelength AXISCORR= 2 END XTENSION= 'IMAGE ' / Image extension BITPIX = -32 / array data type NAXIS = 1 / number of array dimensions NAXIS1 = 4096 PCOUNT = 0 / number of parameters GCOUNT = 1 / number of groups EXTNAME = 'D2IMARR ' / extension name CRPIX1 = 2048.0 CRPIX2 = 0.0 CRVAL1 = 2048.0 CRVAL2 = 0.0 CDELT1 = 1.0 CDELT2 = 1.0 END š}JšŠ!Žēė?ē\A8ē yŨēČ/jēđÕ¨ģ šbģ 6 ģÃDģžģT9ģ ĸņģ‡;ģ*y^ģEģ]Bwģkģm CģbŖģNØcģ6ģŗ}ģÂģ#:ģ?oģ_īnģxõģƒīģ€Äqģ[Gũģŋ0ēĩŊēHē˜ē(ã˛ēąũ¸ģ#Ÿ:ģEŊģ1*iģn5ĀēŠQ§;4Eī;-wŧ;+Y;(A ;$úÅ;"˜û;R!;+Ã;(Ā; 8ž;(H:ú…õ:ņŊe:íŪā:č6<:åõ:ÛâZ:ĪD3:ˇTÄ:šnÂ:p˜¸:!ŋ9❴9’ =98 zd¸Ķaše­˛šåiŠēCÉáē“ō#ēŋp¸ēãw1ģ ,Pģ õkģÔģžģ‡ģXlģX3ģ$ØYģ=KSģX bģgüģnŦģfYMģTö9ģ= œģ"ÕÉģX+ģū°ģ7ģWŽ>ģsRÄ삎ģƒUģhnûģ-Ö{ēÕąƒēgaøēÕÜē…ēĀ‚ģĪēģE,ģ2í[ģY4ąģ33y;dÍ;.Ļ(;+Ė ;)%Į;%gî;#xŦ; 1Ō;O;uš;Zm;įŠ:ūéČ:ķa :īžA:é:åõ:ßa:Ķ*Ã:ŋ7[:ĸ/T:‚ČĻ:3X”9õ¨F9§Đ97į48|RȸŒŅ.š=”āšžáËē+R‹ē…÷ē´ôo稞kģĻ™ģCžģ´ģ0Ÿģ.×ģ “ģfčģŸģ5āŖģQÁ^ģd}SģnŦģieŠģZ–\ģCąģ)`Įģ4+ģŒģ/ąĘģN‡ģm4îģ€×ļģ„Qģs^ģ?…ũē÷ú:ēˆnwēæ#ē*ÖēkēöAģ>Jqģ:OģD/ģmlÛ:Áé;1jË;,>‘;*s‰;&GŸ;#åÔ;!‚;°; ~;Ī; ’;ĻÎ:ôh:đąÜ:ëS|:åõ:áúĪ:ÖŠ‡:Æ`:ŠNß:ŒÖæ:H˜ą:͏9ģüb9aØZ8ļ–ˇûĢΚvFšĸŽÉēĻüēoøēŠĻĘēĪ0ēûR&ģ°Įģ æzģIJģžģˆģ č$ģΙģ/ÕģJœ ģ`‰^ģl}wģlåģ_\*ģJAđģ0ŒdģÄĖģjxģ(xNģF+>ģeņŖģ|ãĪģ„e­ģ|sTģO­“ģ ûČē ĨUē3ūÖēųē@bģēÍqĄģ0ޝģB8Öģ5üģz˜7gŠĀ;4é;,Īô;*æų;''P;$Ŗ‰;!ņ3;7Đ;Õ;Ų0; Øķ;Øˇ:÷æÔ:ņ–í:ėP:ææ­:äŌH:ŲËC:ËU„:ąß:”ŽŊ:_Eč:ț9Đôö9‚P8îĮ7'烏ũZšƒ$pēŽhēV#ŧēkēÆOēíWģ §ģ ĨÛģ3ģžģägģû7ģoŽģ(ņ˙ģC1Zģ\*ėģjscģmhžģcē0ģP_Äģ7Î_ģ…ģßŲģ!zČģ=u ģ]ĀTģwĨŠģƒ˛Ŋģl+ģ^šCģ UIēŊ*ŠēO1ēø‚ē!æzēŠA2ģâ›ģF ‘ģ1*iģi:õēāZÛ;0W;-¯Ĩ;+Y;(yˆ;%;"Đä;Š ;ŗž;˜’; āX;˜:ûe˜:ō-6:îN˛:č¤L:åõ:ÜÁū:ЋĨ:š :œ. :u֍:%ßī9åí‰9—I9š78(nиÃg5š[2šÚ˙ėē=Ŧhēs–ēŧŅÎēāÉģÜÚģ e=ģeģžģ֞ģ‰ģxŽģ#ˆäģ;q0ģV…ģguģnŦģg8ōģV}šģ>Ëįģ$•ģĮūģ¯9ģ5—ÎģU'=ģqËfģ‚`úģƒW2ģk\/ģ2‘;*Ģr;&ˆ;$ž;!Ik;2;Bf;‰Ą; ‰d; :õGļ:đče:ëS|:åõ:âÚs:׉+:ĮfÔ:ĢQ5:ū:Nļ*: L9Á:79lT8Äˎ§Îmšy)ššZFēi%ēhúöēϘ ēĖŽē÷͘ģ†Oģ VKģü›ģžģПģ ģļ–ģ-ˆwģH¤×ģ_qŌģl Ļģl‹ļģ`sˇģKÉMģ2ƒ“ģDģŪrģ&šģCü!ģcúpģ{”Wģ„Išģ~H‹ģSĶÚģ‘âē§|Õē:MēØĨē8ĸēÃxoģ,ˆgģCĄ^ģ3´*ģw{šŨę&;5ü;-Ũ;+œ;'kē;$Ŗ‰;");[;ņ§;I; HÅ;H‰:øÆx:ņ–í:ė˙!:įV}:åB:Ú;:ˤú:ŗÕË:–ŨÖ:ec_:ÄP9Ö2Ë9‡ŽS9$ē7§ēƒ¸īšz§€šöä‚ēPCēš\bēÃŅ6ē騊ģ >ģ ­ģĸčģžģ~fģS|ģX"ģ'w“ģA:)ģZŪ(ģiģ“ģmؐģd™ŌģQį"ģ9Ļģãģԟģģ~ģ;}Ôģ[ĒąģvV3ģƒ^āģ‚™ģaāčģ$ëcēÅgēVLē —lēRē „ŅģܰģF cģ1-āģcũ ģRŗ;,';-įŽ;+…Ä;(ąr;%;#Í;Áō;#;ÕŪ; z¯;ë:ü°Œ:ō::č¤L:åõ:ŨĄ :ŅkG:ģä:íP:{a:*> 9ë+]9œ†æ9(E8Dc;¸ŽoÜšPļZšĐ„=ē7ŽíēŒõēē2âēŨđŪģdģ ĶŋģÔŌģįĒģžļģĘ ģl6ģ"9nģ9ąęģTũ¤ģf<ŅģnŦģgåžģW÷!ģ@‹,ģ&@ģ7Đģ_Ãģ3 žģRÔPģpDģņ(샟rģn3ģ6Ōeēæ]ēzyÆē(ĩē ũķ炞zģ~ ģB}-ģ6džģNI"ģUAŦ;~ę;/ŅÕ;+ōė;*Ģ;%×Á;#žx; ĄĨ;âķ;U=;:;ļ;„Đ:ķwY:đ}å:ęW:åõ:āi:Ôę :Âĩč:ĨĪå:ˆz:=Ô=:ø9ąx9LŪ…8šиQŊš-íŗš°ĒŠē #âē}ķõē¯ļšēÔũģ( ģD÷ģŨŠģTĶģŋģxgģ § ģmŌģ2AÛģNBÎģb¸šģm]5ģj÷8ģ],ÎģG+ģ,ßXģķsģ͜ģ+÷CģJ‰ņģiĘģG}ģ„mGģx]õģG˜tģ΅ē“ÉÎē(—Šē ČēT%ēዸģ7öjģ>Ļģ;äŸģzĢ:VŒ™;3*;,`;*ã\;&ˇr;$U§;!U;ĸU;ĸ;ųt; ų5;ąZ:ö'Y:ņX6:믒:æí:ãē:ØhÍ:ČļI:­€M:Ėz:T–đ: ĘŠ9Æx 9uĒË8Ō ˇ'âš | š‘āē +Pēb` ēŖ‰QēĘ_ēôU ģNfģ ÆģSXģžģ˜ļģ JÔģŸ ģ,ģFŌĀģ^ZEģkÕģlĶNģa‹DģMPĢģ4OgģcēģnĄģ$ųŊģAÍ ģbAģzDâģ„'†ģ€ęģWŠģ(ēŽyūēA]ē¸Iē0+äēēŧģ(6[ģDđĶģ2d´ģsÖēc$ņ;5!);-?Æ;+P„;'ۋ;$ÂĐ;"a;ī/;ƒã;¸Ö; ¸™;¸Z:ųĻ:ņ–í:ínô:įÆN:åąę:Û€:Íôo:ĩ•:˜¯:kYĸ:"9Ûpž9ŒĖ'9  c7āū†¸á äšp+Ôšî(ēIčÉē—M¤ēÁĸ翆Ĩģ ^wģ …ģ,0ģžģF}ģČXģ@–ģ&(ģ?BųģYŽŗģhÛđģnŦģeyvģSn€ģ;Lîģ!ģ ˆģNtģ9†ĨģYŗ‚ģtÚ~ģƒ ģ‚‘`ģe'“ģ)wGēÍcē^Ŗē 6UēÂ}瘟ģÖÅģEĶzģ2 „ģ^̍ģA3;#˘;.6>;+Ŋ­;(é[;%/ø;#@ˇ;ųÜ;“c; Į; ęƒ;wž:ū:ķ Ų:ī.U:č¤L:åõ:ہD:ŌJë:Ŋ/ü:Ÿ˙ļ:€):.œP9đi19ĄÄē9/B38`W§¸šÎ°šF:­šĮ <ē1qtē‰vx玓÷ēÛQōģ=īģ ¨ģD¤ģ“ģfĖģ{ģTĒģ éųģ7ØIģSvFģe].ģnŦģhXģYF–ģB)Õģ'Ĩžģ§ĄģOģ1ŠoģPĨ7ģnŧ¨ģ—ģƒ×\ģpߨģ;^žēī;Ņē‚ŋē†îē _ ēx+Ķģ@7ģ@Ŋåģ8WmģHū5ģcú:ã’ļ;0Šö;,*Ö;*;“;&Š;#žx; Ų;RÅ;Å;Šä;Ѝ;,:ķį-:đąÜ:ë6Š:åõ:áö:ÕÉ­:Äu/:§-:ŠĨd:C:N+9ļŊL9WZ-8¨¸'Îmš#ršŠ­‹ēæ ēvöÖēŦļ?ēŅēšēūŅ…ģxŅģ vŽģŒŧģžģ@~ģ Įüģ]ģ0—ģLƒˆģaĄ,ģlídģkžōģ^D[ģHē3ģ.žŸģĶģYģ*7ũģHZØģgéHģ~/đģ„mGģz{°ģK†Ôģ dŸēš‡ē-ßķē-%ēJ ē×âģ4wŨģ@y$ģ8cŽģ{Ž 9ܔÂ;4 ¸;,—ū;*æų;&īZ;$;!š>;˙Û;é;iE; i ;Y:÷˙:ņ–í:ėf:ævÁ:äb[:ŲHq:Ęŋ:¯¯e:’‹Á:YÔÄ:I69ËĩŪ9|§å8ā8˛Iš~횊#1ēíyē\BĻē z”ēČ/íēđÖ{ģ š‰ģ 5īģÃ*ģžģTSģ Ŗģ‡|ģ*yšģEyģ]B¸ģkFģm 7ģbĸĐģNØ ģ6­ģŗ/ģĪģ#:wģ?ņģ_īīģxõmģƒīģ€ÄJģ[G ģžēĩģäēHwē—ėē(åBēą˙­ģ# AģEĨģ1*iģn6ÔēŠES;4FĐ;-w¯;+Y;(A“;$ú¸;"˜î;R;+ž;(§; 8x;(.:ú…Ã:ņŊK:íŪÆ:č6":åõ:Ûâ):ĪCã:ˇTY:šnW:p—v:!€˛9āŽr9’ û9 8 s¯¸Ķ¨še°(šåkˇēCËPē“ōöēŋqWēãwčģ ,žģ õQģĶëģžģ”ģX†ģXgģ$اģ=KĮģX ŧģgüNģnŦģfYģTõŪģ= 4ģ"ÕbģXģū˙ģ7vģWŽĖģsS 삎%ģƒAģhn8ģ-ÕwēÕ¯–ēg_ņēÕ@ē…čēÂFģĐÚģEjģ2í(ģY5ęģ3/P;g—;.Ļ;+Ė ;)%¯;%gâ;#xŸ; 1Å;5;u;ZT;į:ūé–:ķa :īž(:éu:åõ:ß`ë:Ķ*’:ŋ6ö:ĸ.Ī:‚Č:3WR9õ§9§Ž97äą8|L¸ŒÔtš=–ƒšžãÖē+Sųē…÷éē´õ 稺 ģ§ģC‘ģ´vģ0Ÿģ.äģ ­ģgģŸ`ģ5áģQÁÆģd}ŠģnŦģieTģZ– ģCą3ģ)`_ģ3÷ģĀŲģ/˛>ģN‡‚ģm5Jģ€×Üģ„Dģs~Ãģ?…ē÷ø3ēˆm%ēåē+BēkBēöÅģ>K!ģ:NœģD02ģmk :ÁđĨ;1j˜;,>‘;*s|;&G’;#åČ;!v;—; q;ļ; z;Ļ´:ôgá:đąÜ:ëS|:åõ:áú:ÖŠT:Æ:ŠN{:ŒÖi:H—::ˏ9ģû 9aÕÖ8ļ;ˇûŋxšwęšĸ°nē¨6ēoų¸ēЧēĪÍēûR÷ģ°ēģ æ_ģÄĨģžģ•ģ čXģÎæģ/1ģJœ}ģ`‰ ģl}“ģlÉģ_[įģJA‘ģ0‹ęģÄģjŸģ(xļģF+Āģeōģ|äģ„e´ģ|rßģOŦœģ ú¸ē ŖĐē3ũjēøÍē@eēÍt ģ0¯ŽģB8jģ5üģz˜ā7mĸ;4é\;,Īč;*æų;''C;$Ŗ‰;!ņ';7Ä;ģ;Ų; ØÛ;؞:÷æŖ:ņ–í:ė7:ææ“:äŌ.:ŲË):ËU9:ąŪˆ:”Ž@:_D‰:ĮŌ9Đķŗ9‚O=8íūl7'’@¸ũĄšƒ&睤ēV%-ēkÖēÆŌēíWëģ Îģ ĨÁģ2üģžģä‚ģû^ģođģ(ō[ģC1Īģ\+,ģjsŠģmhŖģcšûģP_gģ7Íõģ'ģßįģ!{0ģ=u}ģ]ĀÖģwĨ÷샞Ņģlģ^™‚ģ T9ēŊ(ŅēO’ē÷áē!č'ēŠCKģãĐģF vģ1*iģi<7ēāN…;0Xp;-¯˜;+Y;(y|;%;"Đ×;‰ũ;ŗĻ;˜y; ā3;˜:ûef:ō-:îN™:č¤L:åõ:ÜÁË:Ћr:š§:œ-Ĩ:uÕ`:%Ūô9åė[9—Gå9—´8(h¸Ãl š[4|šÛ`ē=­ÕētgēŧŌkēāfģŨ)ģ e"ģdæģžģÖĢģ–ģxÃģ#‰3ģ;q›ģV…^ģgĒģnŦģg8ŊģV}<ģ>Ë{ģ$”ŠģĮãģ¯Šģ5˜GģU'ĖģqËÂģ‚aģƒWģk[‡ģ2;1ēŨü'ēpۆēɀē[ē‰ąBģ Ŋ"ģCöŪģ4Ĩ ģSĀ.ģEŋŸ;ôĀ;/)ņ;+Ė ;)•€;%ŸĘ;#°ˆ; iŽ;s;åS;Ę%;tÖ:˙É8:ķa :đ ų:é€F:åõ:ßų:Ô 4:Āö=:¤;:…fú:8•<9úäî9Ŧ@x9B`„8Œ x¸{ĩĻš6™fšˇŠkē%bķē‚y[ē˛V!ēÖ0›ģįģģD÷ģ5¨ģ0Ÿģöúģ°]ģ‡xģ‡Ôģ4ģP~ģcæģmÍ"ģjD÷ģ[åģEVyģ+§ģšģŗvģ-ģģLSģk‰Âģ€0"ģ„G-ģuö`ģCŠ$ģZJēŽËē#Xēkûē_c+ēë™øģ;‘;*Ģe;&{;$ą;!I^;2i;BZ;‰‡; ‰K;†:õGƒ:đčL:ëS|:åõ:âÚA:׈ø:Įf‰:ĢP¸::N´Ë: KS9Á9 9lQŠ8Ä Šˇ§âšzĖšš\Qēj_ēhü›ēϘÄēĖŽĸē÷Ôjģ†\ģ V1ģüģžģĐŦģ ĩģļ×ģ-ˆĶģHĨNģ_rģl Āģl‹›ģ`stģKČīģ2ƒģôģŪģ&šoģCü¨ģcúéģ{”¨ģ„Iŋģ~H!ģSŌãģŌē§{6ē:âēØpē8 …ēÃz‰ģ,‰fģCĄ ģ3´zģwRšŨ˛];5‘;-Đ;+Ž;'k ;$Ŗ‰;");B;ņ;Hé; HŦ;Hp:øÆG:ņ–í:ė˙:įVd:åA˙:Ú:ú:ˤ¯:ŗÕh:–ŨX:eb:ÃV9Ö19‡'9"a7§§š¸ī ešzŠõšöæēPŗēš]ēÃŅšēéŲ[ģ >Pģ “ģĸÎģžģ~tģS¤ģXdģ'wâģA: ģZŪwģiģĮģmØvģd™žģQæÄģ9;ģ„ģԑģģéģ;~Nģ[Ģ)ģvVƒģƒ^ķģ‚xģaā%ģ$ęSēÅzēV­ē –Ëē”ē †ęģŨäģF qģ1-ĢģcūbģM2;,%;-į;+…ˇ;(ąe;%;#Ā;Áæ;#w;ÕŅ; z–;Ō:ü°A:ōœî:îžj:č¤L:åõ:ŨĄo:Ņk:ģg:ėë:{5:*=&9ë*09œ…š9(Cˆ8D\ø¸ŽtĮšP¸ĪšĐ†´ē7\ēŒõØēē3ēŨņ{ģŗģ ͞ģÔ¸ģįģžÃģĘ­ģlxģ"9Ŋģ9˛SģTūģf=ģnŦģgåvģWöĶģ@ŠÄģ&âģ7ĩģ`ģ3ĄģRÔÖģpDdģņB샟eģn2Yģ6ŅKēæ}Vēzw‹ē'ąē ū”ē‚´'ģMģB}˜ģ6dRģNJpģU>_;č;/ŅŦ;+ōā;*ž;%×ŗ;#žx; Ą—;âŲ;U$;9÷;‘;„Ē:ķw@:đ}Ė:ęVĶ:åõ:āhî:ÔéØ:Âĩ„:ĨĪ‚:ˆä:=Ķ:a9ą~L9LÜ+8šޏQÆõš-đ)š°ŦLē %ē}õ˜ē¯ˇ7ēÔ€ģ(tģD÷ģŨcģTÆģŋģxtģ §Ôģn ģ2BFģNC7ģb¸úģm]Oģj÷ģ],ŽģGÃģ,Ūņģķ@ģĶŌģ+÷¯ģJŠwģiĘ{ģGĀģ„mGģx]bģG—ƒģÍkē“Čqē(–Rē ũēTŪēáŽ"ģ7÷@ģ>Ĩ•ģ;å…ģzM:VĄ’;3)ß;,`;*ãN;&ˇd;$Uš;!G;ĸ;;Ąū;ųY; ų;ą5:ö'':ņX:ë¯y:æÕ:ãšå:Øhœ:Čĩū:­Ī:Ė:T•Ä: Éā9ÆvŪ9uŠ98Ōāˇ( hš }¯š‘Ÿîē ,ŠēbaŽēŖŠēĘ_‰ēôUÚģNtģ ÆģS?ģžģ˜Ãģ JûģŸJģ,tģFĶ(ģ^Z‡ģkīģlĶ@ģa‹ģMPQģ4Oģclģnģģ$ú%ģA͏ģbēģzE2ģ„'“ģ€ĩģWÅģ&ëēŽxQēAŽē¸ē0-Įēēž'ģ(7uģDđ‚ģ2eģsũēc ;5!E;-?š;+Pw;'Ûq;$ÂÃ;"`ø;ī;ƒģ;¸ģ; ¸~;¸B:ųĨę:ņ–í:ínÚ:įÆ5:åąŌ:ÛN:Íô$:ĩ”Ž:˜ŽŦ:kXv:!‡9Ûor9ŒĘû9 ž 7āņ˙¸á*šp.Jšî*)ēIę8ē—N[ēÁĸ ēæ‡\ģ ^Ŧģ …eģ, ģžģFŠģČrģ@×ģ&(lģ?CnģYģhÜ$ģnŦģeyBģSn&ģ;L†ģ!ŗģ {ģNÁģ9‡ģYŗúģtÚÜģƒ ģ‚‘>ģe&Ëģ)v:ēÍa ē^Ąwē 5´ēÃŋ瘠æģ×ųģE͇ģ2 Oģ^Ŧøģ;˛;#Ζ;.6#;+ŊŸ;(éM;%/ë;#@Š;ųĪ;“I; ē; ęh;wŖ:ũ˙´:ķ Ā:ī.<:č¤L:åõ:ہ:ŌJš:Ŋ/~:Ÿ˙8:€(„:.›V9đh9ĄÃ9/@ĸ8`Qd¸šŅöšF=#šĮĸGē1râē‰wI玔”ēÛRģ>>ģ ›ģDŠģ†ģfÚģ{™ģTëģ ęGģ7ØŋģSvĄģe]bģnŦģh1ģYFHģB){ģ'ĨC짇ģœģ1ŠãģPĨšģnŊģŋģƒ×Nģpß7ģ;]Āēī9¸ē‚ž"ē…âē _Ģēx.ųģAyģ@žQģ8VôģH˙\ģcŦ:ãšÉ;0ŠÁ;,*Č;*;†;&œ;#žx; Ų€;RĒ;Äõ;ŠÉ;Ќ;,e:ķį:đąÜ:ë6v:åõ:áÅ:ÕÉ|:ÄtË:§ŽČ:ФÎ:Cä:Mb9ļŧ 9WWÔ8¨ã¸'ØBš#t|šŠ¯/ēįGēvøyēŦļöēŅģVēūŌXģxÃģ vsģŒ¯ģžģ@‹ģ Č0ģĢģ0—ęģLƒīģaĄnģlí~ģkžĖģ^DģHšŲģ.ž7ģŌãģ€ģ*8dģH[YģgéŊģ~02ģ„mGģz{8ģK…âģ c…ēšôē-Ū{ē-[ēJ\ēׂKģ4xŗģ@x¸ģ8d!ģ{Ģ9ÜĮí;4 ƒ;,—ō;*æų;&īM;$ƒ;!š0;˙Ī;Ī;i+; hî;Xī:÷Ę:ņ–í:ėJ:ævĻ:äbB:ŲH?:Ęs:¯Žé:’‹\:Y͘:Hn9Ë´˛9|ĻU8ā˛Iš€‘šŠ$Ôēîŗē\Dē {KēČ0nēđ×Lģ šąģ 5ÔģÃģžģTnģ Ŗ@ģ‡žģ*zģEáģ]Búģklģm )ģbĸģN׎ģ6Eģ˛áģÜģ#:Ūģ?žsģ_đqģxõēģƒīĒģ€Ä#ģ[FCģŊēĩēēHĘē—ˇē(æđ瞯ģ#Ą[ģE‰ģ1*iģn7ûēŠ8;4GÂ;-wĸ;+Y;(A†;$úĢ;"˜á;R;+u;(Œ; 8P;(:ú…Ž:ņŊ1:íŪŦ:č6:åõ:Ûáō:ĪC˜:ˇSõ:šmķ:p–J:!¸9ā­F9’Ī9ą8 mk¸Ķīše˛šåmÃēCĖžē“ķČēŋqôēãxŸģ ,íģ õ6ģĶÃģžģĸģXĄģX›ģ$Øöģ=L>ģX ģgü‚ģnŦģfXåģTõ„ģ= Ėģ"ÔúģW÷ģ˙Mģ7ëģW\ģsSz삎8ģƒ.ģhmvģ-ÔtēÕ­Šēg]×ēԟē†ŋēÄ)ģŌģEŽģ2ėķģY7:ģ3*Ú;j”;.Ĩô;+Ė ;)%“;%gÔ;#x’; 1¸;;uf;Z9;įu:ūéa:ķa :īž :éY:åõ:ß`´:Ķ*[:ŋ6‹:ĸ.Q:‚Įo:3V&9õĨØ9§a97âW8|EиŒ×ģš=˜'šžåäē+Uiē…øŧē´õĒ稺Ļģ§kģC„ģ´[ģ0Ÿģ.ņģ ĮģgQģŸĸģ5áģQÂ/ģd}žģnŦģie!ģZ•ŊģC°Ųģ)_øģ3ÃģÁ'ģ/˛ŗģN‡øģm5¤ģ€Øģ„7ģs~'ģ?„ē÷ö,ēˆkĶēäē+­ēkgēöJģ>KÜģ:N$ģD1Yģmi:Áø¸;1jc;,>‘;*so;&G…;#åē;!h;Â|; d;›; ^;Ϛ:ôgĒ:đąÜ:ëS|:åõ:áúh:ÖŠ:ÆÄ:ŠN:ŒÕâ:H•Û:Ëđ9ģųķ9aĶ{8ļˇûĶ!šyšĸ˛ēŠpēoû\ēŠ¨8ēĪkēûSËģ°­ģ æEģĘģžģĸģ čģĪ6ģ/ŒģJœôģ`‰áģl}­ģl°ģ_[ĻģJA7ģ0‹vģÄ1ģjÆģ(yģF,Aģeōģ|äkģ„eēģ|rkģOĢĨģ ų¨ē ĸJē3û˙ēø—ē@gXēÍvuģ0°­ģB7ūģ5ũ!ģz™ˇ7tx;4é';,ĪÚ;*æų;''6;$Ŗ‰;!ņ;7ļ;Ą;Øü; Øŋ;؃:÷æm:ņ–í:ė:ææy:äŌ:ŲË:ËTé:ąŪ:”­ē:_C:Æû9Đō‡9‚N8íûK7'l̏ũįšƒ'ˇē°ŪēV&›ēlŽēÆUēíXŊģ öģ Ĩ§ģ2âģžģäœģû†ģp2ģ(ōˇģC2Fģ\+mģjs˛ģmh‰ģcšĮģP_ ģ7͍ģĖģßôģ!{˜ģ=uōģ]ÁXģwĻF샞äģkŨģ^˜žģ S(ēŊ'ēOķē÷@ē!éÕēŠEdģåģF [ģ1*iģi=xēāAO;0Yb;-¯‹;+Y;(yn;%;"ĐĘ;‰đ;ŗŠ;˜^; ā ;—å:ûe0:ō-:îN~:č¤L:åõ:ÜÁ–:Ћ=:š;:œ-9:uÔ:%Ũč9åë9—FŖ9•Z8(a׸Ãq š[6ōšÛÔē=¯Fēu9ēŧĶēā‘ģŨwģ eģdĖģžģÖ¸ģŖģxøģ#‰‚ģ;rģV…ģģgŪģnŦģg8‰ģV|âģ>Ëģ$”AģĮÉ읨ģ5˜ŧģU(ZģqĖģ‚a.ģƒW ģkZŪģ2:!ēŨú9ēpŲLēČ}ē2牞īģ žcģC÷!ģ4¤ĄģSÁ~ģEģ*;÷ŧ;/)É;+Ė ;)•e;%ŸŊ;#°z; iĄ;rė;å7;Ę ;tŽ:˙É:ķa :đ ā:é€+:åõ:ßų:Ô ˙:ĀõŌ:¤Ī:…fY:8“û9úãŦ9Ŧ?59B^8Œ¸{ŋzš6› šˇĢē%d-ē‚z-ē˛VžēÖ1ģč$ģD÷ģ5ģ0Ÿģ÷ģ°jģ‡Ŧģˆģ4öģPčģcžģmÍ<ģjDÃģ[å3ģEVģ+?ģfģŗĒģ-ģ„ģLÉģkŠ)ģ€0Iģ„G ģuõŌģC¨;ģYGēŽzē#WGēlfē_eåēëœaģ;= ģ‘;*ĢX;&n;$¤;!IQ;2M;BL;‰m; ‰0;l:õGN:đč1:ëS|:åõ:âÚ :׈Á:Įf8:ĢP1:ú:NŗR: J}9Á7Č9lO$8Ä Nˇ§õŋš|pšš^_ēkšēhū>ēĻ™}ēĖ$ē÷Õ;ģ†iģ Vģü€ģžģĐšģ éģˇģ-‰.ģHĨÄģ_rUģl Ûģl‹ģ`s3ģKȕģ2‚ĨģĻģŪ§ģ&š×ģCũ*ģcû]ģ{”õģ„IÆģ~GēģSŅîģÁē§y—ē:wēØ:ē8 hēÃ|Ąģ,ŠeģC Ŋģ3´Ëģw(šŨz•;5‘1;-Ã;+€;'k„;$Ŗ‰;");';ņs;HÎ; H‘;HU:øÆ:ņ–í:ėūí:įVI:åAå:Ú:á:ˤ^:ŗÔü:–ÜŌ:e`‰:ÂI9Ö0\9‡‹ä9Ũ7§“—¸ī ŦšzŦišöč™ēP "ēš]ĐēÃŌ<ēéÚ/ģ >„ģ xģĸ´ģžģ~ģSËģXĨģ'x0ģA;ģZŪÅģiģũģmØZģd™iģQæjģ9ŒĶģ*ģԄģŧQģ;~Âģ[̟ģvVŅģƒ_ģ‚Xģaßcģ$éBēōēVē –)ēÖē ‰ģßģF ~ģ1-tģc˙¤ģGą;,$;-įt;+…Š;(ąX;%;#ŗ;ÁØ;#\;ÕÄ; z|;ˇ:ü¯đ:ōœÔ:îžP:č¤L:åõ:ŨĄ8:Ņjß:ē˙á:ė€:{ō:*<9ë(î9œ„w9(AÚ8DVB¸Žy°šPģCšĐ‰(ē7‘ĖēŒöĒēē4ēŨōģŽģ ͤģԝģįģžĐģĘēģlšģ": ģ9˛ģģTū[ģf=;ģnŦģgåOģWö‚ģ@ŠYģ&‡ģ7›ģ`bģ3Ą‹ģRÕXģpDžģņ\샟Xģn1ąģ6Đ;ēæ{OēzuPē&Žē ˙6ē‚ĩÔģ€ģB~ģ6cčģNKĀģU;;„å;/Ņ„;+ōŌ;*;%×Ļ;#žx; Ą‰;âž;U ;9Ũ;i;„‚:ķw%:đ}ą:ęV:åõ:āhĶ:Ôéĸ:Âĩ:ĨĪ:ˆC:=ŅĪ:Ā9ą} 9LŲ§8šS¸QĐÉš-ōŸš°­ņē &Xē}÷;睎ÔēÔģ(ÜģD÷ģŨ<ģTšģŋģxģ ¨ ģnoģ2BŽģNCŸģbš;ģm]jģjöéģ],KģGWģ,Ū…ģķ ģÔģ+øģJŠøģiĘãģHģ„mGģx\ĶģG–™ģĖ[ē“Įē(•ē 3ēT ˜ēᐋģ7øģ>Ĩģ;æiģzī:VļŠ;3)Ē;,_û;*ã@;&ˇV;$UŒ;!:;ĸ;Ąâ;ų>; ų;ą :ö&ņ:ņX:ë¯^:æē:㚯:Øhe:ČĩŽ:­I:ËĢ:T”‚: É 9Æuœ9u§8Ō„ˇ(0ģš Rš‘Ąųē -Äēbb˙窊ŋēĘ` ēôVŦģNģ ÅéģS$ģžģ˜Đģ K"ģŸŒģ,ĐģFĶ’ģ^ZČģkž ģlĶ3ģaŠŋģMOķģ4N”ģcģnÕģ$úģAÎģb.ģzE€ģ„' ģ€ģWŽéģ%ÛēŽvąēAēˇŨē0/ĒēēĀAģ(8ģDđ2ģ2eVģs%ēbí);5!`;-?Ģ;+Pj;'ÛV;$Âĩ;"`ę;îų;ƒ“;¸ ; ¸c;¸&:ųĨ´:ņ–í:ínĀ:įÆ:åąļ:Û:ÍķĶ:ĩ”B:˜ŽA:kW4: z9Ûn09ŒÉš9 ›…7ā䔸áršp0žšî,4ēIë§ē—OēÁŖ#翈ģ ^áģ …Jģ+áģžģF—ģȍģAģ&(ģģ?CåģYPģhÜZģnŦģey ģSmČģ;Lģ!Gģ mģOģ9‡“ģY´pģtÛ6ģƒ *ģ‚‘ģe&ģ)u6ēÍ_ē^Ÿpē 5ēÅē˜ĸĘģŲ.ģEĶ”ģ2 ģ^ŽFģ61;#Г;.6;+Ŋ’;(é@;%/Ū;#@œ;ųÁ;“.; ­; ęM;wˆ:ũ˙d:ķ Ĩ:ī.":č¤L:åõ:Ū€Ü:ŌJƒ:Ŋ.ų:Ÿūŗ:€'ã:.šJ9đfÂ9ĄÂK9/>õ8`JޏšÕ=šF?—šĮ¤Tē1tSē‰x玕3ēÛS.ģ>ģ ŽģDoģyģfįģ{ŗģU-ģ ę–ģ7Ų5ģSvũģe]–ģnŦģhģYEøģB)ģ'¤åģ§mģíģ1Ē\ģPĻ@ģnŊ`쁿ģƒ×AģpŪ›ģ;\Éēī7˛ē‚ŧŅē„Ūē `Lēx2ģBģģ@žŊģ8V{ģI‚ģbũ_:ãĸÜ;0ŠŠ;,*ģ;*;y;&;#žx; Ųr;R;ÄÛ;ŠŽ;Šr;,<:ķæø:đąÜ:ë6A:åõ:áŽ:ÕÉF:Ät_:§Ž]:Ф-:Cĸ:LŒ9ļēŨ9WUP8¨‰¸'âš#vōšŠ°ŌēčēvúēŦˇŦēŅģķēūĶ*ģxļģ vYģŒĸģžģ@™ģ Čfģúģ0˜GģL„YģaĄ¯ģlí˜ģkž¤ģ^CÖģHš{ģ.ĖģŌ­ģŠģ*8ĐģH[āģgę5ģ~0uģ„mGģzzÂģK„ųģ buēšoē-Ũē-ŒēJ‚ēׄĩģ4y‹ģ@xMģ8dĩģ{M9Üû;4 L;,—ä;*æų;&ī@;$u;!š#;˙Á;´;i; hÔ;XĮ:÷•:ņ–í:ė1:æv‹:äb':ŲH :Ę#:¯Žb:’Šņ:YŌV:G—9Ëŗp9|¤§8ßũē˛ëíš‚5šŠ&xēīîē\E„ē |ēČ0ōēđØģ šØģ 5ēģÂ÷ģžģTˆģ Ŗhģˆģ*zrģEJģ]C;ģk“ģm ģbĸOģN×Tģ6 Ú작ģéģ#;Iģ?žøģ_đ÷ģxö ģƒī¸ģ€Ãûģ[E_ģģõēĩ¸bēH+ē—„ē(čē˛ģģ#ĸbģEnģ1*iģn9#ēŠ*æ;4Hŗ;-w”;+Y;(Ax;$úž;"˜Ô;Qų;+M;(r; 8(;'ø:ú…W:ņŊ:íŪ‘:č5í:åõ:ÛáŊ:ĪCG:ˇS‰:šm‡:p•:!~Ŧ9āŦ9’Œ9.8 fĩ¸Ķ6šeĩšåoÎēCÎ.ē“ôšēŋr‘ēãyUģ -;ģ õģ͜ģžģ¯ģXģģXĪģ$ŲFģ=L´ģX uģgüļģnŦģfXąģTõ(ģ= eģ"ԒģWŨģ˙ģ7cģWīģsSØģ‚ˇMģƒģhlŦģ-ĶhēÕĢŦēg[ŅēÔ燇ēÅëģĶ.ģEíģ2ėĀģY8Šģ3&f;m‘;.ĨÚ;+Ė ;)%y;%gÆ;#x…; 1Ē;;uK;Z;įZ:ūé*:ķa :īķ:é>:åõ:ß`:Ķ*&:ŋ6 :ĸ-Ë:‚ÆÎ:3Tä9õ¤–9§97ßŌ8|?¸ŒÛš=™Ëšžįđē+V×ē…ųē´öG稴Cģ§ÔģCvģ´Aģ0Ÿģ.ūģ áģg…ģŸãģ5âģQ˜ģd}ōģnŦģidíģZ•pģC°~ģ)_ģ3ģÁuģ/ŗ,ģNˆpģm6쀨,ģ„*ģs}†ģ?ƒē÷ôēˆjwēãē,ēkWēö ĸģ>LŒģ:MŗģD2mģmgL:ÂË;1j.;,>‘;*sa;&Gw;#å­;![;Âa; V;€; D;Ļ:ôgu:đąÜ:ëS|:åõ:áú2:Ö¨ę:Æt:ŠM¤:ŒÕ\:H”c:Ë9ģøą9aĐ÷8ļ žˇûæËš{1šĸŗĩēĒŦēoũēŠ¨ņēĪēûTœģ° ģ æ+ģċģžģ¯ģ čÁģ΃ģ/čģJjģ`Š!ģl}Æģl–ģ_[fģJ@Üģ0‹ģÃãģjíģ(y†ģF,Ãģeķģ|äģģ„eÁģ|qņģOĒ§ģ øŽē  ¸ē3úˆēøeē@iēÍxĩģ0ą›ģB7›ģ5ũĢģzš7y˙ŋ;4čô;,ĪÍ;*æų;''(;$Ŗ‰;!ņ ;7Ē;†;Øá; ØĨ;Øi:÷æ7:ņ–í:ė:ææ^:äŅø:ŲĘô:ËT˜:ąŨ{:”­4:_Aš:Æ%9ĐņE9‚LĪ8í÷đ7'Dh¸ũ /šƒ)[ē˛ēV( ēmE篨ēíYģ ģ ĨŒģ2Čģžģäļģû­ģpsģ(ķģC2ēģ\+¯ģjsØģmhoģcš“ģP^°ģ7Í%ģrģāģ!{˙ģ=vfģ]ÁŲģwĻ–ģƒ˛ųģkĩģ^—öģ RēŊ%OēOEēöŠē!ëfēŠGYģæ%ģF Bģ1*iģi>Ļēā4û;0ZC;-¯~;+Y;(ya;%;"Đŧ;‰â;ŗp;˜C; ßâ;—Ę:ûdû:ō,į:îNd:č¤L:åõ:ÜÁa:Ћ:šĐ:œ,Î:uŌÜ:%ÜÜ9åé×9—Ea9’Ö8([!¸Ãuôš[9fšÛKē=°´ēv ēŧĶĨēā‘ĄģŨÆģ dîģdąģžģÖÆģ°ģy,ģ#‰Đģ;rlģV†ģgģnŦģg8UģV|†ģ>ĘĢģ$“ŲģĮ°ģ°%ģ5™0ģU(éģqĖwģ‚aHģƒV÷ģkZ/ģ29ēŨø<ēpÖüēĮqēú牴€ģ ŋģC÷`ģ4¤<ģSÂļģEˇ;ú†;/)Ŗ;+Ė ;)•L;%Ÿ°;#°m; i“;rŌ;å;Éđ;t†:˙ČÎ:ķa :đ Ä:é€:åõ:ßøæ:Ô É:Āõf:¤d:…e¸:8’š9úâk9Ŧ=ķ9B[{8Œ¸{ÉPš6œŦšˇŦ°ē%eiē‚zūē˛W[ēÖ1 ģčģD÷ģ5Yģ0Ÿģ÷ģ°wģ‡áģˆWģ4]ģPPģcžNģmÍVģjDģ[äåģEUŠģ+×ģ2ģŗŨģ-ģøģL‘=ģkŠ‘ģ€0pģ„GģuõCģC§IģX:ēŽē#VēlĘē_hpē랡ģ;=Đģ‘;*ĢL;&a;$—;!IE;23;B?;‰R; ‰;P:õG:đč:ëS|:åõ:âŲÕ:׈Œ:Įeč:ĢOĢ:t:NąÛ: IĻ9Á6†9lL 8Äôˇ¨ hš~šš`jēlÕēh˙āēϚ3ēĖ§ē÷Ö ģ†wģ UũģüsģžģĐĮģ ģˇZģ-‰ŠģHĻ:ģ_r–ģl õģl‹gģ`rōģKČ:ģ2‚1ģXģŪÁģ&ē?ģCũĢģcûĶģ{•Cģ„IĖģ~GRģSĐöģŽąē§wéē:˙ēØē8+ēÃ~–ģ,‹SģC qģ3ĩģwņšŨFƒ;5‘K;-ļ;+t;'kl;$Ŗ‰;"(õ;;ņZ;Hŗ; Hw;H::øÅÛ:ņ–í:ėūĶ:įV/:åAĘ:Ú:Å:ˤ :ŗÔ‘:–ÜL:e_:Á=9Ö/9‡Šĸ9X7§u¸īōšzŽßšöę§ēP ēš^‰ēÃŌŋēéÚ˙ģ >¸ģ ^ģĸšģžģ~ŽģSōģXįģ'xģA;‹ģZßģiŧ1ģmØ@ģd™5ģQæģ9ŒlģŒĪģÔwģŧšģ;7ģ[ŦģvWģƒ_ģ‚7ģaŪ ģ$č2ēÅŸēV nē •“ē砊øģā:ģF Šģ1-CģdŅģB;,˙;-įg;+…;(ąK;%;#Ļ;ÁĖ;#C;Õˇ; zb;œ:ü¯Ÿ:ōœ¸:îž5:č¤L:åõ:ŨĄ:ŅjĒ:ē˙Z:ė:{°:*; 9ë'Ŧ9œƒ49(@,8DOŒ¸Ž~œšPŊššĐ‹œē7“;ēŒ÷{ēē4ēēŨōˇģŽPģ Ķ—ģԃģį‚ģžŨģĘČģlûģ":Zģ9ŗ%ģTūˇģf=oģnŦģgå'ģWö5ģ@‰ōģ&,ģ7‚ģ`¯ģ3ĸģRÕÚģpEģņv샟Kģn1ģ6Ī*ēæyIēzsē%Ēē ˙Ė炎eģŧģB~gģ6cƒģNLúģU7ũ;‡¯;/Ņ^;+ōÆ;*„;%י;#žx; Ą};âĨ;Tđ;9Ä;C;„Z:ķw :đ}•:ęVh:åõ:āhš:Ôél:´­:ĨÎĢ:ˆĸ:=Ќ:9ą{Į9L×"8šø¸Qڞš-õ𰝓ē '’ē}øŨ睏qēÔ†ģ)FģD÷ģŨģTŦģŋ,ģxŽģ ¨=ģnžģ2CģND ģbš~ģm]„ģjöÁģ], ģGđģ,ŪģōÖģÔ:ģ+øģJ‹zģiËKģHBģ„mGģx\EģG•°ģËKē“ÅÎē(“äē eēT %ēá’Ëģ7øáģ>¤‘ģ;į>ģz˜:VĘ;3)w;,_î;*ã4;&ˇJ;$U€;!-;ĸ;ĄĘ;ų%; øé;°į:ö&Ā:ņWč:ë¯D:æž:ãšy:Øh0:Čĩ]:­~Ã:Ë?:T“@: Č39ÆtZ9uĨß8Ō)ˇ(X š €õš‘¤ē .˙ēbdmēŖ‹vēĘ`ŽēôW}ģNģ ÅĪģS ģžģ˜Ũģ KIģŸÎģ,,ģFĶûģ^[ ģkž#ģlĶ'ģaŠģMO˜ģ4N,ģbÍģnīģ$úøģAΒģb¤ģzEÎģ„'­ģ€MģWŽ ģ$ËēŽuēApēˇĢē01mēēÂ6ģ(9•ģDīįģ2eĄģs8ēbĶ;5!y;-?Ÿ;+P];'Û=;$Š;"`Ū;îā;ƒm;¸‡; ¸J;¸:ųĨ‚:ņ–í:ínĻ:įÆ:åąœ:Ûã:Íķƒ:ĩ“Ø:˜­Õ:kUō:n9Ûlí9ŒČw9 ™7ā×(¸á¸šp33šî.@ēIíē—OĖēÁŖĻ翈Ęģ _ģ …0ģ+ēģžģF¤ģȧģAZģ&) ģ?D\ģYŸģh܍ģnŦģexØģSmlģ;K˛ģ!āģ `ģO`ģ9ˆģY´äģtÛ‘ģƒ =삐ũģe%Eģ)t3ēÍ].ē^iē 4}ēÆ.瘤ģÚNģEĶ ģ2 įģ^¯€ģ1 ;#Ōo;.5ī;+Ŋ†;(é3;%/Ņ;#@;ųĩ;“;  ; ę4;wo:ũ˙:ķ Œ:ī.:č¤L:åõ:Ū€Ļ:ŌJM:Ŋ.s:Ÿū,:€'B:.™=9đe€9ĄÁ 9/=G8`C÷¸šØƒšFB šĮĻaē1uÁē‰xí玕ĐēÛSËģ>Ûģ ģDUģlģfôģ{ÍģUnģ ęäģ7ŲŠģSwYģe]ËģnŦģhŒâģYEĒģB(Âģ'¤Šģ§Sģ;ģ1ĒĐģPĻÂģnŊģ쁀 ģƒ×4ģpŪģ;[Ōēī5Ģē‚ģ€ēƒÛē `âēx5ģCčģ@ŋ!ģ8V ģI–ģbúJ:ãĒe;0ŠY;,*Ž;*;m;&‚;#žx; Ųf;Rv;ÄÁ;Š•;ŠX;,:ķæŪ:đąÜ:ë6:åõ:á]:ÕÉ:Äsõ:§ņ:ŠŖŒ:C`:Kĩ9ļš›9WRË8¨-¸'ëëš#yfšŠ˛tēéŊēvûžēŦ¸eēŅŧ‘ēūĶüģxŠģ v?ģŒ•ģžģ@Ļģ șģHģ0˜ĄģL„ĀģaĄđģlí˛ģkž}ģ^C–ģHš ģ.dģŌyģĪģ*98ģH\bģgęĢģ~0ĩģ„mGģzzNģK„ģ adēšęē-ÛĨē-ÂēJŌē׆õģ4zRģ@wčģ8e?ģ{Œõ9Ũ*Û;4 ;,—Ø;*æų;&ī3;$i;!š;˙ĩ;›;h÷; hē;XĄ:÷b:ņ–í:ė:ævr:äb:ŲG×:ĘØ:¯­ä:’І:YŅ:FÁ9˲-9|ĸú8ßú_ŗIastropy-astropy-201cddb/astropy/wcs/tests/data/dist_lookup.fits.gz000066400000000000000000002056031507226315300256170ustar00rootroot00000000000000‹‡žUdist_lookup.fitsė›{xSežĮõYoĢûŦãuĮãXMzî'e5ß$…hÚÄ$@ëekڜļ4ŠšUÅa]o⅛Ü-7ËM šĒ(ƒEÅûz{|æQםugöŲß{’´i›BĒūąŧ˙ĀSÎųô÷žŋûīŧø]Ĩ^ˇ“ã†qyV€+æĒcҚXŧ>Á%c\‰+āįÉ`4Œ‡ōŊĀqpŧŽōxâãņ`# &ƒ\˛ąAĪOéXeļr—ŸëĮ/šĒ¯Ōã\Ŧ&K×ëŅD8Mäå9ËÎ2Gûíķ2`§ÖŠujZ§ÖŠujZ§Ö˙›ÅĘIŋËS6Œ+r•Ú†S%Íåüs1įĒÖęœ>!™ŽJOÄ;Nũl’Ä~ŽŸÅž×ĪOČËxūDŌôĀûįĩ{F–zÕ/4ãÁz=ŠĮķī4Ŋ†'tâÕÆcŠ†ãąØĸūŖĖVę$^‘ßî2~ÔŲ^œí–%éN@3xŖœžãČ×ÁG;=Ą:lfÃL>‘į5/šxK@†H–!˛VD@G0Ч;ÁšpDįÆ\­Õãôã<=ĄËg+1ÜĨÄ$%n°Čķ–bâ[Žaę;ĀH0‘äęcĄpM¸:˜Ė/ĒĢl„Ķį ôܯ†Ŗuz<œä’u:××ã\ ‘ŽōŸŸ7Ģ1Vš†WĒjīėú “QŸĐK¤â:éŅ$IØ#Ë\ôų9ŨN{Āį)ķį(¸˜ĢЇkë’Q=‘āRŅpōDöōãŽbnt‰ŗÛœ}„ËËšβ€ĢÄeˇ(¤ũ,‘,†(=Æ+&lu]¸,pą8'^ķSK˜ģčübņHˆŗĮbņP8Ę<ÃߘHęõ\0â|zÄp ooM?­Ņvŋ­ÜéīMŧīQô z‡ vĨŖ<ņ^äe‹™ÅĶ ĻęQãz×ŖÕä}á z¤Ģ/OŧxQ6x}å˛šsåSĖĒÄ+ĒEāUÍ›jÂqŠ*Á á7.Ié\0y<ĻÁËČgŌD3¯ČŠ&XdQŗX­Œ—ĐĢc¤ÛŪí ¯Ķ¯Čg3™L[™ÉīōfCBą§rvläô2löķŲ;ņ ųŠN{yš˛gyĄ’Ä#ž`­ŧĸŠjÕ$•&^!eÉd8aĒČ'į7Œ7ĮÍI37Ąƒ'<ÅlUD…rˆE•5I䉧ö×˜å‰ų3/ŠĒdÉT4Uēķrˇ×“€Œ—–ĪDVeŲ"˂(iŠÖ}ŋĮįĨtF1ņ:ė7ķéåŧ–göĢŠIčIJK\9Ą&RU„5’["IŋĒĢ=Oė%¯ĸwŧRCÁųę+Ãßâzu¸!ĢÎl9Xß Gk9–ÎĶBwãX(¯ĸËK>[ĨÍK ã‘ë* ĪwŪ¯ĪfÔĻ z<ÉŌoާÅáîE9…ÆÜ×,ō™´ąTâązČĄWG˜Ų›Ŋ{säËŨĢ"›I"ž7û´-ZéąÚáÕT3PĐŌGŲJlö€‡ÉĮŒ–`Q-’"™ũŽĶ#ąęp˛‘ R˜Ž§ål`y„KЙ’/Ģ“ąüĩG˙/:~§Íá s-qQeÃƝÍGSĀéķ˙DRä.;U(NŸMčn¤kĻrˆt#ÃÜVȞ|ƒá1ŠhU8ĨŒ"—1ŗXå‰Ũy/)]yâ ypĨ;¤ëņ´XF˙F/s‰đ]zTR74ld’D†—GžžØ{Ūɞ˙ņšÃOĀCÖáĢānrVŒöø?‡•t,&OŠĮá¤|iŗûYš+p%*¯Ž.ĘŖø—öŗę÷įŒWâļ•ļëC3[yUVDĘG"oôŦĪbš6ĘÃ8råë8=^›(ŽŽ‹)HëRH<_ˆw‹ˇŖŸ1‰fAČÆ?Ļ_€ĢÖR§ éÜ]zŸŊ—ķĄ|4‹Hz—yl,:aÕÃpĨޞÜüĢX+ä™üVOŽ_ŸĒĪôú'Ä<[ynžTD‘õÕē ļžōœļ˛vžhæeĢ HŲü[¯ŖŊ…Ņb,‡ÍwS;O0+˛&˲”é?¨0ˆŗMŗ*ŧjá rĄú•3ö,š%Cŧ*ĘŧhØsAį'įÄgÕĸЊdUž— =?ĪŅ}›^É×ŋ<‡č*ĨSfÔ,"KE_ÎŨ•œF8$†ëŠį‰åđĘØ‡Į!œČpeShHoĐŖėĢ9Ցņp°*ĸõšA¨IEĢ; ņsxlîi2ōŲØ‰tĮJĶ1Éēŧtfņ]ybFĀByv/ĶŽ1? Ģŧ•Ō¯ÆĘIāąīúĩ{™û4=č×Ķ Ąp‚tœ~?{dy¯˙8ŧé¤s˛ú¤… åđúĒß<"w’ī$õ›ÆwåŽß4ĪĐ¯˜Õ¯&Q}@šR(hŧĨOúÛõ+ö§~ÅŽúONŋíŧ~Ōo;¯Ÿôۙw˛úáō<ž .Ā>¯Į"‘ØxvÉēx,U[אJrÉô0•ĐCC¸ęx,dc™ĢęÉJú{%5Ž•‰Æ¨š&ÜÎK\GĪĢÆsôg帚ęĘpŊ RyČËŲ§ģ=UŖōęxzBéO¯Ō+I^AŦÉ}%īŖÕÕ!Ą’´,¯ũáî)ĢO+āpØÜŪļ´HTņ Ô*˛ĘžÍkņā ØX}ĀĻUąh‚UT¨mā-šĩī<—ƒ~›;ĪŧđÔCÄ%úœ%OTT3m”–•į%k!ũ%ãIYžÄ‹f:7™ˇR*đÖę âFĐæŠ_hÃ}įųl§ŋ‚Ũ**š)}^E'xåDō•Ķv{ē?T¯"/s¨Ī˨×ĘŲ÷…ĸ¨>ŪpĘô†§´oŠēė>Ą‡xīHc.zRį—šĪåéá>WŸWú>—§ßô‘žĪ՝W¨>ė§;G>.}ŋ¤0^ų æąû†|E!ŊÖøŅIé×ā‰ũČ3î›yzēoV/-_žûf}į÷õrôŅųž^aŧũvēŗWĪíaõœŗģŋ –‚âŠÛčÄ;Yų2ņŲĶ_ņŲyķHW™§ŧÛ~Ež/hŋÆ}BOæûjúJĄJiS2æĪä_ã>a–gÜ)”xJ–’\āŧŨ¸O˜åW yÅ"ˆĒRāŧظO˜áenjš,Čą°ũR=Ti7ęĄĸ“Ōk'^ú™×Īō•÷7¯Ÿ÷č?Ģ×l0úöÁ*Q/W†CÕ']o0Ą+3ĖÂE5ĸ_ēēŗ*%¨˛(ŠŒ6D •đÔ˙?ĩN­ž6-ߏ]Ō1´]|ģŸ*ĮŋW´ĸ-úV?v3fũvŽ( ,Úú6˙G<āĀš×žÅWĖRX}ˇ3n—Ņâ€Y+—aūwgᩅ˙Šië€÷îÁ‚'“xdÕ˙āáÖŨxėĨķąéüĨØxų$č×/Åܙlz÷5Ŧxh š/9m—^Š–3¯Ä|შņĐ.4}õXqÛ§˜5ük4oāđBŗ–īÛo‹Xü›é˜ũõrėRÛ0÷ÎíXĶ|>ūJuØcgâƒŗ&bIđüņņąėŠÛ0yū;8xúŧ¸6„Månl͌—=īböÔ÷ĐV1 M˜}ÆXxs+Ļŋû öVkŽųÛãAĀS§ĩáĐ÷gãOS“øęĘÛŅüÁØyū ėģAÆĖ ð÷Ãe˜ûá­xr~-šĻÄÖ3įáđą"<:īž(ũ–ũų|lŨø<ÖĪoAĶ‚Ũ˜9J@Kô1,—ĸõɘ>hZļŠXdž‚ˇV|ƒ'íįaFŲ4~ã~lÖ>ĮēĄ&^p)ž°/Žüm§`Ķ*ö]v.ļĪ8†O†×áȟÆęžÆ–kŋFĶđ3°ë‡Ĩd'åØVžëÃŞoṋßDëE‹ąJ˙›˙æÅ6˛ĩĮ/›ŒĮūw/֜6 ;ļ^€ŊŖíßļāŲΆbķ{°ö’AËÆLũï°öĄí˜)ÍÃĢSbÁ1OįqäžģđÚ=÷ĄųCņGĐR;ūf žŒõü9ØöÛŖxōˆˆ¯ģ{>܉]æ`™ú:–\63ëĮ’3ŋÄąƒ>L9} ļģ˙†Gf˙Oür!ÍøšLŋƜ§‡ŖĨâ,ē;ŠÕnدô ûŨ‡#Ûļ`Š÷,|i"V\ų4ϧ,žą;^ƒEãGbÁ=w`Ū”AhQžÄĻa‹°mō¯°tŠûæī’ îĮîbî…Wcē?Œš#^Eķ ė?k ¯ēmõ÷bΜĐúׯëâTėŧeö4nFëĀ!Xoŋ›'=†į7\oĢØĪĢ|;/|€ ÷a^ãŖxë‹8ޚˆŠw Å´o7`ŲĶąîðäōK°Ĩmšī-G‹ëž˙‡ÃXbžŽWwŒÄēÔŊØ5€Įá#§ãųÕk1kĀ=xéũQظօĄĮ°úģéx÷ö7°4Z‚×ß|­tV‡†>ŽÃƒ'āķWb×ŅĶą÷?gãĀÎ Ŧ QœX1ĶŦMhųd8žŊâŦûƏ-_œ­ãcîͰūũߥuÉŖXōÖĨX÷åfŦŧn öŋRŽ)Ž÷ąôƒ—1+y[Cįãp=ëg.zĢfÁōëæ`ņáØô7+ĮƒĪtŪ¸øģéŋžÁÔŠ|˛į÷X¯†ņčŌG°eh/ļúq˙šī1yėۘuĮģØkkÂüõoáđ{ևnÅĒ—ųfĖQKąj´Ķæ\ˆ]ŸŒĀâˇvĸi×:<ˆÖŋŋƒÎBGķŠÉØ=lōÆŅ#'Ŗų´CØpÕĩxåŅ6ûšo7Û+¯Ē°;ãˆ}āŽ—đĖļšØų~ ļ~´oŦŠųŖ&`始ąč“/°cÖZLŅâíVĖŨļ›LIÜŖKØ÷Ųt4ŊoĮ~΃8ē`6ļNšë'āŗíoāå&ļW´˙ŨÁËąčļĶņâÖ8^ K˛ŪŽ5;‚XyW n{+ūü:¸ Ëīū'Ŧ>ë/x器1ī"ėžā(6oŪ‹%įY°āŅmhû?’Î2*ÎŦëļqwww׎Û,âîîîî.„āîR8PNQ¸ģkqâîvĪûŨ=čA¤ĪŲ{­9ĶĄ:DMövsķúát˙ ƒDvõš†vļ+˛æ2ⷜÂÄm.#>!ŗXÂŖUbįŦ!äĩ™›{ōbų 2|¤øú=%ĸ¤Ŗ=ŅŋBâr¨Ÿ$ģŽøq]&!˛"ģ¤`SŊGäõ8"ŒÜŅ>Y‚—K(ĄYø°ŽÄxŪEų˜¨|cHž4 MÂd’ĩũpŪˇ‚ŦĮ#ŠL¸A@‹ū8¤Ép8°—^SĐô?G†ë0ÔãŦđú&zĄųadĨ#Đ\OåĮ„[ĮĖņ}OKŦėNJFŊ΃1ÉøMÎ!ķ[J—†¨ņˊ´÷ųÄŽ¯{\äņK7Ÿī"˙ėMÂû_ÁõÍY’Kg kĩƒGÍņ$°ŗ¤aJ_žm¸Ę;wI×A’kx`1›ŋŸđØIKr„ąæõIĘŦ"Ņ  6“˛ i[’ŪģˆôäĀ|°á­Õ ĒšŋÜ ­ūą¯ ŸÛÄũ!Äŋ›HIt•ßšęt ų؞Ü0˙Ev8ū߂đ owû8bej\÷î$đX[ĖÖ‰¨ ėoėlM(ęT‹¤öŸČ{ˇ–ō¤ƒ$7˙üņ12ëĄōÚDډ ˙Õ#ōä”"7Ŗļ§`ošÍ2ŽL#)+ķÃQ$<^NäŒ ¸Ôī‡ßģ䨿‘õė+ú22?ˆhÛ˙'ŠÜŧ šf ԈrRÃŌÚ ˙‹]I]Z)év×Dr`ÍDIBũpÉQąWßí‡S0û9ũģ6÷Ž"ŧošÁJ<žÖĮųx4ļOģP°*Ģ€ŗÜķYO¤į7Ŧæ4Įčšō}Čn7„'owJēUwÔÛņK‚|Šä ŲO‰dMĩdXÄž×´ãUĪ÷<Ũ߄ŒōSxõ؄MĄ%ēĮŋ1]˙äĘgb7đéD‰¤v­Ž’QYS$ccģHLu¤Ęt"_7r÷í bv|ĸ°A(!˙ŽĄŠ~ŒëŦ:ČFå˛ŽĄ†CI}Í;íXî=)ií”HT\ ’}SÉ:Ōˇ'FČ˙Äv´” ™ ȍ #Ėl.…á)„”m§g‚š3y$ęYŊpũā@TÍKÖ&ĄË]MĩOC"NžÃ˙ņlãMņZrŸëupŸˇ…ŦŨfŸ¨!zŅ,!~āÜ7ŋ"lÍŧN<&uyŪYšDŨŠEŸ##öíl|û“ŧlŸ¤Žô—¤ßūļi6¤´īÅ]Ī=xôX‰ŲČ'ØÚ„‘œĩ^b:č§Á’üI|ˆąDzj ņ3ĶɒVáŧõÎoI(؃ßÁTÜ,“­¸KéŠ}¨mĮÂx4¯ß,œ4Œ/ÆdÜ×֊”ņa\ŽLôŒNøÅ¤érUĪք?؏jũQŠú^ÅÚáŌá'H™ļ°ĨSpĢ_OŨ†(?‡aojˆjÃdäe‰ķMlũōĒÛÄ*3đßŪ•ÂeyaDæÆė6ųšæ#Ō;CPÔdŖŋ܏{šbįæ _Įú›h<°&˜‚ŨGđ^zžØė‹(ß<Åm°US†‘×[*iīAr{Ī=ƒQå–ŗĮū5{ĸ]‘fuĄänÄDɔŋÛ$uīeđy×Tt™HlĨúār¤OPÉ~‘û㠉š’ųļõ ēīũfP:ĘÍ`ąŽą$掍¤öYc >› NYKîeŅgVĄOˆ$ĸÁT”ßlšÕõ)–› š0 ;\Wv gĸŒ­“H_€aĄŅwßâķ:›3'l'´EW~WE2žëÖĄķ &{—Yũš(+Æ č>īQ˙nˆGŲÄøEi/ØÛ¤tËųxžMÁâÛ?Ôŋęq§Čë~× 8SŒOr §ŗto 'K•˜~^Œîw}lƒÆĸYŸ'¸ÉŋĪŗ)X‚WāKüSΐøĒ ûWøo™ÅĮņÛSƒnīâû. Lũ•;ũj(’߯ë˛)6õö’™tHė|5ҧ&ŖonOęķÜŦ=7Ÿ|RĢâņ&#ĮœĮ§{IO4“čÚūgā<ŽélÕíŗ ›Ž6pđ.‰Ú éčsœĩßRĩšŲ¯Ķ)ŊČÛŠ<Į6$AäŖWÍd*†O–ėŪ`0Éu–AėY…A×÷g%‡ŊH;˛‹XĢpôûũPZA@ø>RõKŧĶ˛įũ…ß5Dž/ßRÔ哈rüRsį~bnv}C{ÔÎõp ˇĮuž ęÎVD×/š˜HÕŨ6č<įcj6ˇĄ'ņ|5 -wV'Ą^Xˆ‡ĸ>.ũ]0Ķ;ã7ļƒp 7:ūÂŗvÚ~j2ט nƒËäE¨ĻāĢęˆoîs"ĶZãf<û†íHĒŋ“kËĶ H~‡"°”üąŽDŸŦ~'ÖŗĻS’úī [pūĩĮü䛯bĄū@lˇU$wŲ>ĪKÁŪ–vŖëP‡W/WŲŽ=‰'ŗąû|ĨAo¤Ŗ÷ÛGdŊTÔûIɛwü°l" m|ËāęËû¯ūģbĐŋĐ`J…™dŗÆCŌ°ŧ’GMj¸W÷9šEjR=’ŅüģCĻGŌ]ĸÉ40įyJ˜dáõ’’‚]’{÷ŽK‚‡ŋ“tV^&įžÆ_:PāÍžũõ„žÄi_*š,ę%J ęŪ+‘U=āæ0b{ŪĄ"į>I-e„DŋÃÉŋžNJĖJ˙’^4EÅ:ĸŌöyĒO‚ŖO*𞚘HE)3ōˆąąĀËڜĐN‘Ä×o‚ã1?bÕŋąéĸ'mm'ėDJ+o0 ?Qš!ũÔuuĒ/Ö¤<ņĀŖŸ=i—?a”X-b9Á¯ëød2™{`Ūj5î‰Ņ˜N "*r >É÷J™ŨvŌĩũ ™ŧā­Œ6tÂ:b&Æû Iė= ŋwÄ<ÜÄģŨi‚×ūâūĶēü{,öŠö'”I1o"ŧųõLņ:Ũ(ëņ^ŌpWS~Ŋž'Y7aģä×ĸ=ũŪH~]Brū‰ģ¤ƒtļ¤Åč<ÜģUÜWĖßŨÂcČ ōƒPŸ¸BzĢ)„%ôäËĻ*I÷!’‰ĒV’žšķmØģĐo${ė$ĸõVÁ}GÂē_Á˙™žÃ/R˛XôY%îŖ2ŋž)œé˙‹’÷ČØXƒ‹U8zErŨv'Až¤.ĩH›v…™?Ī=ū(îŖÁ-žã-ĮâÆL¤ųÉ<‘ÉÃÁč“' ›1 Į3¯P/Ī'g|ūÉ-ņû¸šāÎ}I—‘iQA”sG+˙ŗá‰ĮŠ7¸ųßCŨ]Jä Úw °/;Ǝŧ߯EũÁÜŋŧžŋ ŦĶGÁō~‹ņ>úųy-ëEFZ]”ˇ\xŊĮŽûįKÚ>{'9P÷•dmÍGɄĢŊ$ĩŽw'Ûõ ™­Û~đ yûE^|tDáŲšqĄwV“ŧĮu é ÷SŲuOĪ>āUæ;ū>('xTÂjŸ#Ā­ļnāļû"Ąëú’˛,ˆˆ1ķ‘jÄ?¯ōņkāņĄU¸ˆķ–.ē.MÄėėäŊĪ~Ō?˙&Áį é_Ž•áÉ}ËxŪØq¯āĮÃJĸ…C¨’: Ø&§līL-?âœđ’ ķÃHr|GZÃaxŊ;I˜˙|´§NĄûø؁ŲxēõGūæ!!Õ* Gô뒁™Ú‡„zbnī-@-Xæz;lG¸aZž’€eĮˆœp†´ų'13õGąģ>ǞFțL'~ÔZcđ ĖæÖ/_b7Cˇ"ۆĐ%ų€Ž¤ÔŅā[ū‡^„tzMˆļ˙°0´k^‘ē gĮh뗠Z8ĨŨ‰ŽŪ„ũė^$žđFĩ7éü’¯‘ܰ†HįÖÂ!ß}ļ6LJ‚_ēÅ{ŽkÍq6Ãîø0ÂîÜĮ#é-ŪK ˙ú˜_?žô­"§äŸœ~K‹{*zŗŒ71ĪšŋqúO ?%%ũå(‚ĨKqđ.FZõ]ėáGęŸ@׊VgÖņėvžˇ÷P=ĸ;:ÃQøÎ˛#)û%‹D?Ŧ#āÃuROeÔ¸‡í§ˆíl„ĸ¸ŊLŽ"ä0ūž˙”‹žŗįĻsąĩڊS™Ú˛CwøKpŌObFépļŠWúJÂõ˙pÍé‡yŅW܃§bģ¨ ÎŠQœp(ž^üęÄÃy6Q ģáuö6z[4ÐŒåΨZ„¤ė'\:¯Ö¨Ž\¤dĐ0 Mj0~.'╏ßņznŪpÂ{đúw‰5įM„3ļÅæLūŋë ôŲEŪöōZīeiõ|*›ūÂŌ|9;wįĒî8Wã‘G7B]kŧÕ ÷˛“X/ICŗg?AĒ+xT˛į‘kpy;>Û=Ȋ?EÉ'’Ë)ģž‚Ī™}$Üi„v¸šđÆ"Wė¸ęö˙ũ>(ߎ ÄošČáVäŪ‹lâĸnzá‘5˙凸1Ս”˜¯Ø\ŠEņb éŌÎTKß p˙ƒzŪ21Įˆš| Ë„öiŽŽT‡_Ô`RŽĩĻčo!Ew^ã°v‚ā bš ¯JŧAŒyŠkE&´ÄĢRÜE]#ėŦį?u0ō6âuĢČČ*ĮjĄpŒ­NÄÅË Ë¯ÁNžD‘aC¤ˇaŸ¸š{pr,ÄŅ͈⍠^ÍĮŅŲįē]ŠwŸÅÂ{ƒ <ģ¯6Š8/&Áž5qĪļ“°ž1Îēár!՝&<RÅĸ@r2bYi€÷˜Ž(úŊĀ÷?ņ,Ļâĸۀy‹`ĖZ\ =^°ģwSâæ=Gĩė6—)ÉüũJŌ5û”äü¸B´ņģY>œčTsÂŖö“ҰĪĮņˇģEäô8LnūÃeH{ĸrqß‡ßcO”ʆĸ3¯–%!Oé†SÁ]|NØĄģ~ •2oŋ>x.oKĀÂ8ũ´$ŲĶÕįsÄLōĀįČ(Bž&wЌYlņzn7_†įúRä­Ū“áŋ ĪēŪØ¯~Wã Ī!ãöZäq9÷O}ÄNÔPēô2!§Ąžā÷Ãũņ™Ņ–đ€XnŪ*#DÖßŲļäMęD`åAÜ۝#ŲŠ6įŽ$%V8ĖĮ\L§Ųãųū ŌÖsđYԇļĩ :UƒzÎVôSzĶX‰c@2f\Hpæ‘:ƒkÜ07?†|‡Q][ā‘cL@ĩÎ1[ũũ€[B!ŌØŲHĶhģŒ$)ū caĩū ÛÛĘxĢā^ ö‡˙ZBäBĸ'NĮË<šęĨŗÄYŦŠu sÉmÜ/ŲQëōŒ+ĕ¯ĮåĘ^ĖķĻäq‚Ėq÷a‚iĸü°ũ0åēĨøŋڋÅŪŽ¤øŽÁûDŦû×q¸5™”…7ÄmÃeĪu´ æ öRa™$8ä×i\/-Äį’7Š œˆ]ˇëÖRüOÂOí‚ĶĐåv?Å\’O`? ˆ\Ũrī{›Ķ×8|և`‘Dā’WhõŪŦŋíJĀÅ"4û§“Ûĩ>A+ |.¸ģ-š˙ô¸ŽčJD¸EņC HlBĖģ˜$?Cg|ĶģûęIÉX7\qĶiĀŋË&üÛhŧ‚‹ņž×gá5Y-IąpÆéZ8ūŲ ÖmÃxF:ņ{Ú!Û*˜Ž÷‘ŲŖĐ ĪÂéwĒ V#ģaâîŋņh÷6!Ī},! ŅĪĪ#ŦNs”ĨɏըÔ钖ŲŲDœšO;ô}ŗˆsÚFĨ3‰ĶČZ8ķzM$Ä| Õč.ô%ŗz<˛×kđ¸€Vr]°Ā@r7×Áš™L’X}YŌë4ŋÂĩ”îh÷Õö‰'{ĢÖ“īéčŖ(‡ŽCx?Ɉ Ė3h' ŲåböߒS5=]Ď]æqÅ9Ât#xd]BėՕ$¨ÎķpĘe¸uūGXf1^N¸Í6ĀkĶXÚė"ZםČC¯]ˆ=ĸ"MpˆØHĖf#Ōk ˛IWˆŊŲˇ÷Ä”c3ā5RÃO8^šˆîÎ*—‰Žčļ ×CĨ8L8HHŠčƒ’Ú¸vī‹EüGœËÕ8_3%îøwŋ%LēĢ<ҝ7lÉú{ŸÂâáņŖđ+‰ŸATūáđcNˇ‚pļ C9ņ4ú/cqō–ĐqČ*ǐļu#­īO G^ aČ24ĢT¤~žŽf_ åwBq¸Œąjp œÆžû‡¤­EĻ\"Áá'Î{ ŋë…æwf›‘Đ)‹ŒgÆˇŨ$[Úí&˙Lôņƒpũ@Ąčĸ´ølî>Õ MzBTĖ”;ÚbûâÖWŽ™ŧųŪvä2Ļæ~ ×Ŗŧj9ķĶ1ÖôCļŊ6ÎžŠ¨o—Œ^Š]ŗdôˇŠ =’LÆ'&ĸ5ž‰‰ėū7ö‘î0ۊņäz"tÜ6Â/9`wąVí4$Š­û’÷%RŧĒ59įßcģp2NĢŅį1Q>īȜ,\ ŲOô ÂQŒÎA1këq:.Åąj.îēžøvi‰{tŽáOPūŗ'iän&m°EČA˛åFÄO;HhøzĖOØākē[g`šrœ˛ĻˇHš?§¤aØEL&ŲV8‹`u¯Íø…9“‡¤įesŽYâ­Q‰đBī˙Į-#ņjéLÁéI¤ˆÄ゠ķÜ*"v^AŨv%;zqZܙ——‚„ū(ęˆ[2RŗņŠ•7í'>ߝŋØĨŦÂ?Ė$jBâG >‚;ĶWr+†üũ†xnŪEÉühÔIAŋXÅę[C¸gZ€ÔIĪWb× :ĸ|ŗ”ŅÛēĨ^dĮ›[§Ša=ąÛr”pˇ1¤~}Ųū.üI<īˍûmĐųüá\u[âG]sW‹Ã¸Û´G]~„kgĖč†ëöPŠŧâ/8Îkø2â3Ļ2Ü ÷äôJ4ƒūï—Ō\)ĄW„¯;lĀ7ŊVŋŋ ;˙’g?â20ŗƒ-Qüˇœ8O Ž…}p-pÅSąķÉą¸ēw&ÚÉĮSO‰Ų,öržCā°ĮŸđ~ŽDõÉÅķîIˇŠ[ Z-;-iųĩŽ>Yn“‰“MÅ/ŖŽČĶ!ÂŨ˙÷ßŨ¤DĖXC褆dĒüļđ(w<—ŨâŌė0üÍOāg÷øąRl—Šņ˜Õ˜,ëˇÄDÆā˛é+Á _ā´h FŨɲ8*>ŸAúÄŨŪ[ōâ×Mä#‹~Žé>ņQ1]¤+ŠcO`ŧģŌvįI™ŨKxē1ŸLŖņŊŪŗ¸_^GËT’Æ­Á÷ķ\b—Ė$ŪĮ_día"—ˆ|ŲõŨūņ„5ÛDȧúėl"Ģ÷lܛ<$ŧŊāŋö•(ÆÆ°ō įm+‘EXîëkĖŽKÎd-Î#ŨvˆØ3ŋŅå8â)Y'<Í §Ûâ^Åž*ÚõAŊ_GÄæ|´ŊQ/x‹ßô‚§*Ņ|ũʑc˙sÅXuŊ‹ęí=|Ėū#â~)V˜ûū$îA6WĨQ E‰í<Éåi‰˙z7ƒđ¨9FJ`Áepž0ƒÔfķqˆ&Ųú*˛¸ĩHNĄ9(rņô2lk¸}…îm°X[Hėɑ˙P’n:Ģđ9¤6‰ÂzÚvÔ[†Đ&×Î 5S›ôʤ_ÉbQÛBU¤$aCc…û[™,ÆFڙĀO¸Æ^Ã}’ž‡Ėū*žmīB"}˙ÃûZ?ÔÅŪ¸ŪÂõTî–›KÄĩæĸCžp7÷ĨSÄį8,߅õĸ4ŠĮL'Éå>ø Ī Ķ Fą­é=?2!‰gÍí0Ŋøƒ}âšmēĀsņ’@Ų“RˇmF˙á"Á+ņ@øÂ­›în…§ëy’î$´ē—û"Ÿūäá ēÂã„î+ÆÚÂã×~ø-Xķ}k|vM#f¤„ĢŸâ=ą[Zķ ÷•éøŧš‰2âAwū"}SHâšÎ„Š.ĸZ4‹ÛfbeĒ'rė[d˙Æ4č:Î¤Ė•rûĩ=×WÜÆuCmnÎ*Ā~˜ČÍcŖ‘Kpí[äJëīöAWéIų‚R v¤8ĀO jēĐHėõyœÚņÆĄˆÂģĨTúÔ!üãY‘ŗ‰d—(ŋÂ9UpĒCm4eŊŅnדū OĶgxî .ßÛŌĨ¨{ģOŽķ‡ÛȎĮã¯ė„ˇü Ųå H|ßäy$÷Í ¯^0A‡Üyļü(Úp\Gī^(:´?öĪüy~õ’pŨ‡¤m7О˛&¸zūŗ žŋ§šåX—=ÆŨׄüQË°Ņ—Ržë>áĩą­EÖ҃ü‚LTÅ3PüļÁYīOôėK5¯ÄŅđ.ÉŪÂ×$D\6!@#ôEü×'Áz^IeDZYĸŽ>Č­ŧ|¤?ˇPxdîi‚­Uhļ7&ÄšĒ6čæ!ŊäĪz] ¯§=Ģ˙á—wÔÛUč‡ú“Ŧ_HÔĘŨ¸Î]DrOs’fdŖk,¸ŋõl‹ûĸ]WFHXSŒ:5Auxqķ×÷JNfØŗÕņo3õ‰¸øBĘáÍĩ›qīū ĸ#ĢpKŧOā_âļ—`6@ėØ6/”ųxzw§NbgĮ_MČíâ߇Õ8ˆčCŊ°oÜ;§0B^æa÷æ3žīâĒ÷"Úc)‰‹Čđ)ÄæŦ ĘŪ%„˙یŠ0,KÖ–ßī˜'<ú´ ›™ŨÍĮz\ Š•KŅ{NÄķVdŦMĒiKŽ~[GčúJîŽhGd‡ RūϐŠ˙ņzÄöšOÖâ§âƒUĀfÜĖ ¸=6 Õ°b÷ũÅÍäĻUJ"W >ZÕ¯Ŗ!øšÕ`ŗ};ķ“ HX(Î]FÆÍtōnęEîÖ&­ÎbĖœCˇ/’gĢ%v^”¤M— yŅBŌqŒ/ŌAQ|PÆ÷["ũˇBüú”ÕķĐoxNlû({Ü%cäeî7 îį<_Įj~8á+]EˆũiLĖŗ&Ü nM¸Čqm§ĩø•^ÍÚŠŌÆ# ܜn“ä~žŦÁíšģõÁ3ūRZŧ—ŲØtG›ŅаaéxīmOđˆŋxĪöAæ¯čÜķ¤…âŊČ oŲ\Bcbû3šåh|ŒÃēĨoÜîcŪô ęe†â 8t˜…G­NŽÅõQ5Rƒ‡ĮÎÅęŋtŪUŽCcuž¨Žkp]ŋ“¨o;°t§˜AxŦžGæ ļĻ 1;Khŋ ÂßPxĨ„ IōG#°Jū˙Wūâwņq#ŪãaōŌõõņopÆ­WGÂwU°p0f­Ü0Ķ'0ė#! WXÚĸXļ÷1›y\öų‰‹yâ{Iå$ÉĒ éT5œXO°Áŗ‡]IÄbĶMâū‹!âÍrŧYčī×!ŋ^)ŖÂƒ!yÖXRœ!Š1^=Ių*˛ģë&‚–Ž<`Œļj?•]ņû´c Ū­ÅyGė&YÛīâg8ėۄ|n ą˙f";ÕĮš5„Ũ˜NáŸy8XÍÃįĨ/E'ˆ|õüߟĮ&F?—ēũpÕ9öá ĘŲÜ{õ“ ĸ;6“uBøZ^4SĮŖxaJNäËĐDÖNÁŋõbΈLüšOÕÔDÔˇz Øá€õ”^˙õ"ė°x~ž1”ÖwÆđcwlŽyØ}ššņõ.~j@Ô^Ņ!gâ°v!~LÉŒ`Ķâ=>}Wg<ųũc PũAc-Îtâ(i‘OQØ‰ŽŅ=”ōĪļXNæR=õÚŌnú#(*‰ģŗEŌyû~ÉÁ¨…’“#ŽHęęä$ ¯$´R°ũĶS(ūÎëŒãM?"”[ø&“ÜtCŌY0wõmáœ%OQZ´{ž ‡"‚BÅ Žh€ou3ü˙ę(=ЧZ!¨äĸË#Txnü@@PŦ„{īTríą%“‰?øˇÆn¸×ģˆÅôG<ØXƒģUĸöĮUģ)}T¸P!¯\uc'Üzŧ!æ_m’å+ČÛ9—ąŅFqcîk<75'Ü"—Ā 7üv\Å÷ëN‘3ëˆúסÜDÂΌ&š V“Žâ¸Ž>Ÿ“č+v“æ`ŽzöLžøUQēščИ ÖŌQŠGˆĪ ĸųsĪW‡™ŸšØÉoąM]LܰMXžN!ēŌHŧV-<ʲpŪÉ õNÁÁM‘¯ ´åjᇰrQSx ū™3)Ø~‡p§+¸<Ę$zŦ™Ŗ×ņŽÎhI§ŊŸ$ î´āõ‘D~.%oâô#"ˆŽ%æĐEÅ;bZMŖ īKIÜ=ŊA“˜HÂN+DG_Á˙˜#øcĩē’ŧWŸDöß úk ~ây B¯ŠOĒI9A=5¨3§-x-˙ĐdR’ˆl9 ㆂĨ […ų­î„LėAĖĀbb\l°ZҚˆĮ:|/¯]Ų×+ĩHœíŨրŠ7?H­īBNņ^<ŋŽÆßdž°É5‚ۊÜđAûæ/^ō—”ŨoNÉW‡ŽĮ{â$~oŒī#w,ĨyČÆ­&x° ëŪ”÷‰”đXÚū@U%øöEBī}Äjg#žŨČÄ!zÎ˙}Fyę0ęíßPŽXƒŋ|%1 í| e¯ņ$N^JÜNlûŨÂōH&eÕ#lMøäh=[ ƘMȞ„ö?„lÅÁG ˆhS@aĘ@îåã㚏uŨŠÄt¯Ąčj8Ų‘øFŪDŋāŖ¸G3âÅ3ķ[‚l3V'öŠ^˛Ãë˜>Ÿâņq9LN‹ŗxOšM΃Θų|"õÜg‚ķúūū7¯ĮÉTđŠҤu Ü/ÎeŽ_\"z"uīl4ķîRh1ičzėÜŊE¤_†ËŅËØ~îÉĄ!ĄDyũã°ŨB_&ßT‰üš!ōcc¸›,úû}&6ŽkŲ›Œ8CÜ_G5Ö ÷q3ũ2ŒāëåhnU—4õŽ"íÂđ:o~Ë$"įĩl%eî̈ôœDÆ,gŦŋ1ô™i‘hë-Fß- ÷Q8­v&ŋ¨w_Ä`­Ž&¤û ŧ‚ŸKˆš+8ûi9&‡–ŖžŅƒ„×ūĸĪō‰ųû§:züūkˆŗä ™í'ĸ)V’]8EƒĨ8‡‘û¤;ž5HkmÆuābģcûî–đē7÷sëiM"  {ąÅË8ÛGIJũ„?>CØH%ÉŽ2ä]cņē”FÔŊ8t#›’ĐŌ mŽ'ŠĄCņM{…÷1[}'^s Īá7Pöėy{<‚ßĸ|#¸qw>oīĸ”^&r‘=_ļ)īˆų~”ŧEāķĢø\Y‰‡b˛hņüᓜCčœ/(ļūÃjûQ˧Ģđ}ņ€uqÄ<š†Ōö ö•Ŗë|NøįPŸT_Öĸlēųá8"UŲÄôëˆTYŽË?;Üĸn#ą•„Ô×čĻwFÚ¯ņēũ˜ôŨ68IÎĸš†›—Te%ũøL|ˇtÄ˙ÛqœmˆqÔälGy€đĶCŌĮâÔ%RÜOm´KlP¸‚´DÜQИ1:ãúæ^ęã<9åžá<„sī$b›ėÁ¯G w^÷žūäũŋHšjÆqÍ*ÂŦBj'zÜ˙ ROoânÚSrz,šÉky1ļ^YÂ÷§žŖĖĻ%æÍ&ļ ‡īûĐHFŖō™Jdö7‡˜+Å|wģķĒ é-või<;>õ&Đ*U?SĸŠSče@fēļŊqZMDŋnčU-Î¨F×w!ąęqŨEzÁĘ^Â-(ĸ YŨpũƒlø2d“ún8 Ŋ\¸_ ĢÃAÜq˙‰ĪßdKCˆĪŠā†ŨZn=œO‘bvސ%ĩĮŅūģ8o"ö[>QŲCÃ6Á“œ$xHo< šāĶšÛ_á˜ĐĨ‹^ôâ ü֜#hm !†uđLJ%¨ēŽŖ °ß”Onŗ+$™ĩãū×Įøß]ÉCë7Ø÷xˆA }:ãŊDđöžxŨ\†´×0åqÆe^q)ĘļEkx…üČ(B;&ĄTl'jC%R§Ëų^C§ 8°1īĖšųL¸Eˇc$66"ĘĖß#pۓ/rJFņx+\_T‰,ģBÚâ…X‰ž+Đ]vÂ9ūRsÔŋ“ô#‹ßņÉ|~¯pƒ—ËЎZŠnîėæũ"ąžéäîŖķX‹zŨІOEm×÷­ņ*nō7žĩ"*āĄĨN7ßDžK W™áŸŋ˙ÜFqųU\ĻGė6 ĮDoûŲŅ8Ō:¨Ū!zų^BŽBūá—ØņĶDũ¨'œŠįK" Z~¤hRNe“ Ģ<€ˇĻŌâTė6ô%¸ūqd×[ĸUL§Ü䇿âö|ņs@:úŲJôe;ČŗíŲéÖ9cq6ŸOhˆ 7>ÆáÛc6v‹5ųLԔWČ3Ī’ŌŦ+÷‡Äēgáøš?†H‰ÎũGhÛ\|:š‘ļ­áŽsn2į0ÆŅm/ŨÔ¤~ŲIHkkS†s§•đŊ(ŅÍ[ëā+,ō-žC(˛CPi/ŧl› îüFæÁķDÖJ¸íbâķįcŅā~‡Û MŧL•Ťe ¸wŪ‡`Ŗaø‡tD龕Ûēķøœ)é­Č_]#čxļõÖãũ~˛EqkkIV— â5ë‘ĐÁ‚ûėđ~sˇËq1ī…›Û’öœĮįŸ”Upč7û€ĄhCžâķ°/ y$Ö]]ČÛ3k×wø_iúF(_ßā1ȅĢ qŨJŽ÷¯?Ģ ^ø4$ÔF|+ë“=°.–ë͉ܖFŌ2ácŊ[0úqOrã\0.o×Ŗn¨C~ņ^Ŋ?#ũŗŽ„Ī÷0i}œkĢĖ‘uÛHR›øoEiA{BĮ†’tũ;y“\1Iۃõåõ<Ēęį%W\R?q{øz,7ÆpKÅbB$ÆÕXPüÜĪŨ ÜžįņûOä{-'Öžēũ˜ĩíJöë)wYšb]d„ØĪ 7ÅãĀacĻR2ÍEQSô63H/Fč#ÜžA7y&ōQÂÛÚß $ũ4ڐM‰nĀ\Î]"ˇņ)Rk= ŽŪbƒ÷ėߘĐ?W æFøúļ„˙ƒęģ ‰M— ĩLėŌËHWM"4q:[Bw>Åįj é1g‘wŪÁÕĘ-Ø=C~ĪŲD‚æÆyŠšævt Ō+b÷ËĻ‘rŋ‚Ũ˙í$,|ÂÖÕŦ‚„OO QÆŖŊڗ°´.ŪũÕLdqË9d Ī­*Êv:˛‘ÃPÚ‹ß;‰Ų9Xvģu¨š;c¸“˛—{î_qÛւ[#ŋuÄīzÂ2 ÷S[DÆĪDąđž˜—Uĸ›ĻālÚŸs1<øŧ—”&mqŧMėÁ_Bæ’tc&E;„˙Ú7ÎŌ§ŽÂĨiPnrDVŋž9ũˆūOæÕŨØxÍÅŠr&ĘߏyTw.1…OĪŧ‹ē‘đøÎÄŪė…÷˛cTŋ~MņĻÖŗ …_šOQācĻBÛ{ū§æ“´Íįõ‚Ī’F"ՂŦGdnå¸&L'JđC|ōub77C^p‘Ä1ŗņ{ŧ‚ŧÍ\Hģ„ŸŽįaâ÷i,rŋĶ^ųJšS ãĩܛ{]â;NŠu<*Āg}ĨČÂĪÄîÅnnÁxYčzēŊĻO.`ņn0ū7bwû?R&ā=4˜0ĪÕøÍ÷&rJ"Y_Ú#+Œĸâ~Cĸ÷OâūúT˛ƒŊĐ?jūÉâH)û˙#Ē´!WággDöUOTģ/ œ= “‹ pÛhËų7áeĒnāx0‡"˙Č,âĐ'UĖáS’LS‘%ž ˛<§Ķ¤ZMF{FKĻ"NüúÕ$ΉG-9žz_ąc­( 4Åî…Č™uÃȤ?^ãcžč†ŧ×|<ĪŽøéŅDnN^iģ]XŸxG„Ŋö÷;LĨ)6“ ^„īÆ4Bšú°ä!öw.skÎP2+Ŋ)<´ã¨ÖÄO%îļmŦ‰žøĪ…ņŽ{Žûá…_ÛáŪÛ]°õuŌ'Ū2—Œ%čڃÔnŊÕyŋCUŪķžWąp×§Čúuo‡ƒ(sŒq)ߊúW5>/ÅšVí#Nˆß›ØچmZ-˛2‰xĨ@Qg˛č՞܎>|ßŧnPŋņÚáøÄī$÷ząÅuHœ›Œ§l"ú™ķ Ië†åØ`ŧúŠŽˆíNôÆČLƒIMqGŪ˛9ÖÚ ‚ĢTØ*^ Üyš"ŖlÁøs{.ŽĩiļE'ō$ū„īMü î›5͏ĸĀš q]BqkđŖĶ'qúlFuP´öÉkz“‚ęyxň>ũo9V‰ČfMD6$ŋq3 ÜīƒQã…XArŽųS%ŪO6}h8Ų~!_Eôž/¸vpÁÍęŒpK#Ô#]p÷;ŽĻîgtnA‚{jđČߎGš”ĀķihæWâY-Ø˛° ™ ŸrĢw ÁīéŸt\†GdoTũaķĒÚ•ß íDä†íØ4#đõ<įĘë*đ–`|ų&U)›Ņu?Öööökq|°õĻAx{gpģŸž¨Ė¨L%iūVÜƒĖ„ˇčņČÜJŌÂ?8JQ¸bȐâŠ-„uŪ†e—bėfoÁrEgœ~Äyu?äFũDOw@ßjúz¸5šJHÔtŠ~ōŖŋÎ„¯1@Uh‹Sž†ÂqøĻg’++Âį§ČÁíx~œ:d='~Cw‚įšā—ĻÃĘŗ5'QÔ2Įųo%šßöŧ]ļ†DŨK.ūÕ2‡Į9¸ u'Đ;ˆÂŋņėۊáGđ6j"~ž7^ƒ<ĸ)Îų¸ĪēŒoÎkŌ<ېĢĮ¸íaBė 8Qō„ÔÎŋņ5߃åJ{Ė7ps’n›Ę¯æöFá€Ö$\ĘkĮæ_Ģp,0ÃūŪ9BbE掰B^î‡Üøß§u ŸÃšĩķ N™j⡡Į,?÷íI đ@Û¤šā%ö(‹MqÖŦ%Æq0>-Nál`NÜĮčSŽˆ<ĀŽˆˇhœ“šÜɂ› p1҉ø–“Hđ’S´dîWŨ ŲŊ§mĢIújN@ę/t˙sč ūÄkVPčs˜ä¸gDúØĸŧ2•ŧÄÄÆ/$Ž… ˙Ē(ŧ_´âĻķâÉđ;ˇŽđ/Ņ%˙#3/ƒ˜=ãH5oDrŧœčVwq™Č-3CōäžŧŽķˆ2íü–•";ã-ŧQôPŖĆåaōĄ‚įm >šZRĢå2bŽ쌏9?ąœŪ ‹”/¤z4Äü|ˇúúGdæÅ!œ˜X˙ũ8Į×r(ĄŊŪ“t6‡@§†ØZ– ),&TũJpÍq¤ŅŸ‰ļŒį‹ieÄpķ\w;…õ:ˆÄ ?)ŧ íßå$×ÚÁũi%¸/,BwņĨgΠŋ°ۙzä Â}Oëņ)ڋīÂÃxęG m ž´ø6“qēÕŨáÄښāëŪŽ¤˛ČBû X{ŗÕK‰›ĐgW;ÂÍļ¸ČЇĻķY۞ō˛.„)ŦqŠ$ļāÚdkܨ¨‹E᝸Í_ׅ^õ‡×Ã[ÄI+‰;ŦÁøÆ4Á Đ7=Jfí$_hGÜ|Á돑ŨûĢČ(CtŊëā>zŅã=ˆ oObĮ6ø:$uæu¤wq?%­= o!ĩŨtljõ"xÃôą†D$–ãŅo5†ëŸ‘6UøKn%vEčŨīķŠ›Čcˆ:9Õ­p"+oāĒ Déú¸-ßø°‰€Á˜–%ĄūPFÅ•ÃøwŠ&lædō=„xuEmŋûØēČ?Ũ%//”ŧĪŽšt#Ed&|šŠŊņģeC˞xÁ]ŖIzŋÄÁÚ €‚#ŸHĩ{€Ú]Npæb‘kxíŲŽƒ­ĻĒÉČX‹š}6ūå“éŊrô4xt÷Ö;Ĩ֖°ÆčÜí))Ū‡e›dÖ_Aŧ*–ė“&Hßĩå~ŗŊDŽ[F˛Ŋs˙áŪĸ;ĶˆžPŸsۏã1â5ēčU‚÷̰ų´U°gĸ‡V‘˛î4í´”<ũĮ̚͟…õĸŋämœLĒam2|pۘäĮžØmŲ \1˜Œ—ãņĢ?Ÿ æčVnE6f>S͉80ÜCģqļu&lÎ Ü5ĪpˇŽSšēk=ņ Œúô;^u›āŌų™Č¤Ž¤(Ф$ÁĀ™(ĢK„$6x~J‰ \EZzWĸĢwĄ{¯MAži¯FEaDMė=%YŠÕ‡øÍŸ#Šĸ3‰b>W]Âú¨úģųØģŒÎb#>ˇą“Ø‘×#—:HhÛíûĨX?š…ģ`›ã‰hšĪ%¨Q7nŪŠOÂô›œß#-ņģp”ŸXÅNįxQ)9‘ŠčŒ!0BN åAdG›ro¤I÷kČZv–ûwį>b,A‡ē“<ĻōÁëeŲ„Ō÷fuņ;xŠŸŌö„ĩų‰îøÁĩ9nĘÂ}F%‘JŅ'ƒ÷ū…hmáĨÎéū(ÁĩĢŅ{‡"ũėŠĶäÚDõHÆÕ.–]Yâü~„“ŋ€Úk&˛—§ąÕ6ĖŅՒ<ûПôË0:Ū€ņž$…õqŋ‚§3J°j‚˛ņ4ĸ›G’4k!žgķqĢs’‹(6~ƒZ{ˇžøŦ>ŒËĨy…õÆ+n U‹o%Üį6InÛšOhØ8’îî“t/Ūd0ŋW4wãûcĩĄ’´:{pœå(öԂô…V„–ĪõƒĨ5D6BČĒ5ČߏBér™€II¯#ķ­‡Ķæ%†bâ:bÛÆ×یˆÅé„F^åķé÷D´.&á[’pŖĩ8_YŽŪLKyŊD䭑43—’ūüMÂqĪwWeRĒę„ĮŅĪ8ŊåėĮ’8Ķô>Ų‹Î 6 áP)žƒHYŪ‡ā-ˇEˇįF8Ō ĩ‰nŋˆDŋĨøŸ­DzZ‰˙aAÅhŨŸâūm7aÅļXdīĻxÖ |nô'mšļaÉč=üąxčƒįÔŲȚ7ĀæÃ4ôƒ Ž5ŽĀZ/0==Ֆ[„}pewķíTîøŽĸ`,?ë•úזƸZNCõĪĶŊ.ȏŦ%|á(BS7" o‡cĘ>Ŋ:ĘŖ섇šĸĢqDžõ* }ģķxĸ éĮ†ë§Ä$LAԈ]¨Û='úÂ2ĸÍwōāŲb¯­#ær,ncW[̓đ­wHęšDØËŸh‹yKZAIv=ŅĨĮä◗‚OÄdâ$ž¨6$ācņŽČčö¤é€öÁ7∞‰Ņ öûJŒŽČS)ŋA2ëÎ,‰mÂÉõĩ$Ã'Ė"cč3âĸEwÆ,$úÎ@´īęđV?ß;Ex~]€"},F§â ;Ģ$šA =ņI|ƒ‹}2Ú #…ÛŦ¤dA† „$TįĄõ؛÷Ũû(-‡cfxƒĖ­ ]ģ›— žĸA­§“âbHÅĄ‹(ĶZæ˙˙ƒØøÍĮ™„íĀ-ø˛-] Žz"ø~=Ú]ŗqīĐ ×ļ{Đ.@„ų Ä>y—`Ę_x/ڄmŠčÄU7ˆŲ/ąØ2\×Ī–ü+Ņ$t ĻT˛Iėĸŋ+(^=!nr–`Œ|’;-ÄŽeG4ˇžÚ`*Q˛Ī¸uę†&bŽQ÷yŗč›ņģžë’Lr,E™ũŲÛzøŋOô™ā‰>”ú5BŅũ1ļ‹"ΰĪũ> Øŗ§9ĪđœųšË›ąņųCyˇ’Ņõc%?ûū6Ø]gœDļĶ[˛īs?úAĸ‹ ×e˙÷^qąRwâÖš]ķ[­Wĸė Æūu'n^‡ēļ†]ŨØŽ]MiÅj<÷&.7€Č‘=p øÍuŗŪHŋųáÕ՚‹aÅdXâ)Ã(}%Aî ˇĶ'*öͧN<ąiÅĸ'ÎâíøĪôĪøz ?9lNÕžŸx§–rcîE ZõAW/˜ĐÉM žøŧØÎØÎîKˆčīs°kc%ã+niūøéŽ‘°ā*Ą=ˆĪ>H\Ę>WØ s˛ĮjQøb,:ËÁ$=ÚFÚ¸4b_nF=ÂT¸úiRĻX _}ë ׹ę…¯đŖđObÎjYŖžeLxŨ÷ØīDVī~DUŒÆiXG|ŸāķĖ‚ō \FôG/ŊŪėžĒ$÷įÂiMOãŲ”[û.āzx:Î^et1^ƒ.‘s!÷N_(ZbBĀS=ęūĪČč+\¤Y_2¯Āw^3ōĻî Į‰û÷ĪR´3—¨÷ĸÛG{’'˜ õ Ân~#T5šģktį´ØņÜLXLę„îŠėļ˙´› [PŪ˙ŠÉ]?lŽ,$VvtŋØM,'įüQƊÂIō—Ôo‚sK7’Vp…¤P w#ŋÁ)d>H‘EšqÚÝđų‡įŊ–Dåô&­t)ĒĖr4O|‰Ū÷ĖygXŌ@ė9!ËãâĶ‹Č(ąk“ውÄfũäu;ŖŲē…$ë,Á,yXĮ ŋŽŽCčõĩ„öÅšô6­ĸ)åįôht‹‘nk…~ŧpʸĢ|ČŌ‰Ô†&8•Ķ“ß(à ũŽíņƄNûKáØŅ<Î"{§&ËUbGbķŖ+3‘{úbŋâ.‚ģÜ6?ĻāDĸÅ3I:†ÄFn‚׏yá#•:áŲļ3ē…‰pƒpá!ICI˙žäÂõIš2s9î¸ÖÂūķqĸEŽĒ‹íqŗŠŠ]CJŗĒ,Į,˙oÛ(ŊŸ”3ÉĐfāÛũ‘eÍqČu&kųrŒDhjDđ6?˛DāR(šo?“¤ĸhÜDœíPZŒáEÁ(œÂrą~Ą0ņ"á.Ō÷9˛ÖŗH9ÛģMˆđ‚m‡Ũ$4Ę$fū˙Ū#7Õˇû8?ĢĄâxG”-f‹9Ÿ@Rîun­øÎƒ×=qß>ŽĘ+ƒHŨûüočûôãĻ­’´…NDŽ:ɍŧ]x¨˙"oy ũU/üEö°ž6aØJËQŨ9FøąK!{2”mcJ'^Fīƒ>|áŨĪ` Šč뛸ī.āúëëØ™ ،ˆķđxú¸ŪŊI´CFķLķ_S 8Bt›’oÆbÜB,ĢšwfÅ5†hk‘gHøŒS¤Ū‹ŗÉjRz ,p”¨ÔŲD5•W!žiNĨķŽĸ¯L"Áũ"Ą‡ˆ~\@ĐÅ~$ôÜÆũˆî×|ŖĐ 3ž=âđԌT‘Ũ9›°w=ļęÜ3mJāÔL3û.>Cęŗ‹Č­ûô /Ą­öa­đÂîô%\§iq›ß‘¸g2Ԋ7w§;ū)kIģQgq—žI^XN'tîĸĮuĄ &€­ų8=Ū‡“´ īŊĩ‰Yé†DԌ2đ¨ė†ōP‚Į撴ˇæ†JĖžzŖ ÍB9§ÎŨsív žCėpJ´&í\G’šm%/é"Ū/ûû|-9&áh›îF÷WGņƒøė=N¸á+ úŒæí5.7§á7ÔOķB´ŋ䘧,"Ãä ifrwE"úĻ‹)œęŒ&ՐœhW”īa~ņ:^!¤¤KIûĄ%#Ĩ>r‡܍ėʃŠ[ņ5úBlž ÉŨ¯’yW×ãÜ(kĸŨrÂ<ģũ$PyÛ ‘{=† ˆ÷(oOĮûščÕĖĪÜņ¸FđAœ\Úcęrģhcræ3÷:ކ‘š]Åßõ4+ŋĻ\ÅŨą2‘/G1ą4ĮŪęÉĶōĐ F’øŒVķqyú_Ī…ŋŒ!­V>E=LHÚ>ˆŠečFĪ&ļy!ŠS⡠Ĩ`ŅR (õDs‚ŦŦu’Úí’pz:IäšųšhRę6Duh(IËē ßyƒČ\7áЅ¸ŧŦĀųįnLK;a‘ڐ0Pũî‚įŒæx”î"ėÜ ÖFŲŽ3^ņĢpãGĘy˛b#úŧ/(J˜ØÍøiÂ}šR=é°äڒú$ S.mMĀĀ(JBgcoŒ¯Õ<ÔGOpwŅ1Ō[aˆ~ĸŞŗÄé…r˛…m{ö$›ø§k yߞč 5ØíjĪc2oĪ{s“¨úRĸtįjęRN=]‡ķ_3šu&Yķ“đ“¨˙ÕÃģaüëî°UFįbëūŨ€ų<¨Ķˆ¨r-Až*2/D}g‰ëO ĩ ˇūBrcj¸ī8YÅ[‰tšDæáUH;Đj ō{ŅLÁļZŠrz&Ji5w^¤ ķ~Méˇ0ü?OĮ’Ø›™đl=”Š˙ęoŠwŋ§Čf $ĮĩĮ†eÄøˇ@ۀšņd lZø`æaCŪ/žÅģĻ q›‡<ī öëPîØˆŗąpļ"š=‰éüYt[ēŸ&w™A§žPŧų!ų‡„oˆŪ‹}Ŋ˙ˇÂgÕãqû6`öč:uȚ˛{yž˙7#¯ĸ1ũĮũ>ÆyŲáØ¸ߋ+¨zہ¤ļFD˙xFZrĄK_“\Ī—PO?|5‡0üī: ‚r3ˆˇäŠĄ'~΅D˙œBė…jRoŒâķ›ĸ7ĻPr˙Oy`Ōyn8Qŧ%ũZ6fīļđPô@ė{‘sMĢ ūâNņāŧNö¤Â8Ÿ’x=ßĘdÜŗ]Ī“Zķ%͚ ŧņtר 7ŅĸwžN˜5žGī‘ĒTY”Hä“@ŧc=IԌ$¨ūY¤žÁd×īGœę<ŠÎņęüˆ¤ĖZøŋlDžt?<ßāŊį2Uļ$­ŪEÁîÄ}s¨’ĄßpyņŌŽ7 æQ#œëTŊĶ3ũĐÕŦĮųøbЇ9ŖífI´{<åņ”ß'rŠO†oán×=ŧėU‹ÔÍQ”åå‘ŌķąĄĶÉą˜”ąŊ%7Ÿ]4pZ‡ÂG›ČęFäž%¤´û—â?ô¤ėŊp’‡ĪĐ҇ßc‰4ĘĨ ŧ!ßË^Pŗ]ø îNŽUŊ”Čæg)8ę…ôZ‰× Ų$˛Ž÷TB.Õ^ÚEãž¨ļ™ ÛgÎŨú]¨ą˙ÁŊŊø—JÆz3Šū5åū°¸yūīûĘ×ĸü´ųëãä4N"/ʟʾQŧ,ßJBß’n‹]ųģf›¤ÉĨE’~ÅŅ’ÚÆ—yanĪßĩ5& u­k’z­×sWū—Üĩõy:ö,OŪ*Čs÷ä͆;’Ú}‚(¨WČģ /ū$ŸåcÃÚT%îæcūOî]8ĘĢōˇ|^hLâˆS|=ÚŅ€˙\ fEž˙‹7}ēQ:鎭„gû.éēJî­íCĻ,IÜacrÎdķâháĻũšÛg2ijoÂ+)ļ5äa´ŖÄįcÁ°Ļ–’A—šQ:j"Aî]I īb@Ōû> &[ÍĢX)ūķŪP8LBø„ŠxQāüēqM2IkŪ÷h/âŸ=‘(ęĢ .žË‹’ÉDXˆškŨ?˙7ØFj)ą* ã÷NŌûmDŗė4ž­%]åJÖö‘Ŧø “ėh*Ų2$F˛¨Ų#‰A?ɘ s%­—DK:§7“4ŲĀã‡1| Η4ÉŲ*ąa˛ÁΈa’Ô…%ŨSS$Ŋˇ‘ôĒ“L÷ʔ4×ū”LčžR2¤ø¨¤éJSIĢC$›Ž’´˜ųXŌ¨b̤V“jIƒŨE<ô*ŖēĄđŠÁé<3o.u÷<Œ‰Š?HĘ^Šc瓎:ˆg3žã×wÕ{›Ÿu“äh-Iģ‘uŪ5!’ĸ~ÆÄ´Oáž~×ķęp*…Ŗ>[9–ílHĻEŋâqË į\Á€]ŋI:Õ)ä•Á ģStĐčŠ*ROɨ!ąŪĐFâwī:/‘hDFŦ )'H|cÚCĪŨ+ÄdžĄ˛“P4^ĒÉZrO~&rC_|"o“z"‡4Uô5zÉ(/sÉÎņŪ<¨¨¤xM0ÁŊŅoŗ!˛ei‰‰öî‰tČ]b^íŧ´Š—ųī%u úHÚtL’Ô.ŠĮãKA’zÛōeÕIÃĒÛ<íĖ‹i2ŧ6´"!aÅķūRÕ-ƒŌĩ$­ļ…KZųKˇļ“Ôuˆ”tô~%iušFŌÖžXŌđJ#I×sí$uÚũ'i=4’üĨÉņhGęĒŽ֌!đO9S•ätxC\=Bžõä{ũŲČú×Ļ2v1euŨˆÜ%˜U¸4ˆ aË#´{ŠŅøØÖģœāuņî.okĐ%\#˙ä"â{vGPEI§.øÎ#qÕY˛— æŨÕŠdD>åIg{Ԝ’mš†Xš‚ė¯ĶQ#/ą59~/yũ‰įÕŨx˛*™¤Ixh€OF'ä¯vā¸Å›œ~ƒPņ”ölGfoōŽ#7xŪCÎĄIīHŒá TĻÂ='g–ŒĪg]š=PƒG‚)ļKĒ ?îŒŋÅ2rPiEĖ/‚ûŧ ­ļūKb›§9ËÅœä÷ÆÛ\×“#„æÍ"éā BÎŦ&6m aV hļ ^rŒŽķnBļļCsg Ų̉ŋ|’Ē­ņü˜síŖų$ŗĀŅ+éL\'ŽD5b ō‹}‰ž´§č‚Ÿ‘} ÍĨÁĖføūø@ĖŦ><:PÛŊ"RŊ)LHĀiU,ŽĢjˆŪֆ€˛ŋč‡Ö#'*ši/Œff ‰B}ËīJĸKE&x]AZ/Š´C^ä­-#Ķ<ŸŠõ{šõä q{S¨ü@yí$9!oŋû†ač‚˙ÃŪŪ–[ ­‘f,āSëŸhĒ7˛w!-ŪķHyœ˜‡‰R/!wKsūzxš/q¯đṫvÛ[áRoŅė-Fîōœ ‡Q˜/Š!÷Ī12—ôôę•=y;ī0ŠŨϏ?ŸAh)ŪÎD^Ô%ëÁΉM.E=WÜÍĄČÍüYo‰ķĀ—ėOßFÂŊø1[bãęĄ+tĀ(Pxđ+#B[ļĨؓ{§Åķũß÷8;ķĖĮ ˇäxŊiŒüŒ?Ļņ¸ŦSā×UÆŖyãQO÷ėūCxš#ĘÜßčŖĪĸ1[6î*?QhP€Ėڐ8ƒÃ‚=r‰\Ŋœ˜7ÂÛæãŊ͗ÄÉ9Čöģ ’¯GŅĖyŋWÄ­Ū€Žų`äĖņˇž÷ŋ„í?†Ķ“oDY‰ųYø—˜9;)üx„&øøn%ûY|ÃÉš‡Ũi”æ6Ę{ ‹Ø'öĢ)3÷Ŗl5¯ûÂ)LŊH˜R†į°5âëũÄ;1ņ{DbŸķDdEĶ/›0Ÿķ~öĮkđ$bÎöĸ<4ģ–')ß{ˆđ Ļ<ÚĐEÜë)ō>EĄđđ#ö”Ũ -/÷ƒiÍeŠĪ&hĢÆûˆlŨ‡č_ŖHūk‹ŨÁ…T~'zų|’fëģŠÔĒ”Ö]‰ų&åDle1ķ_)ˇ@đ%'Ōįī åīX%Üē ˇ~d5Úų$hĢ(3ž_Åĸ(lw×ÁvlBlĮķha5 psŽGú$5 1­ūÉ$åÅ1ÜR:“ēp()')ŦŸ„ŗđžōß=É]ÕyŖlÛØāų'†ĖĩWQK~`Z~…Dų0B;í#_“@FÍ#ü˙&-m"rU*ҧ{cÕ2ŠäõPéNˆõp˛ë\Eņũ“Čį΍ۜĐ ŗÃõ(&ßßp˛ą#ôl}Dn^€j‘-Q§a%æTe6‘Č›íP_ŋŠôzSôšŪ¸>¨Ā˙ņ^nš¨H,īŅ֍ĐH"gĘņ^°ëKbî~Ÿ%-âúÕ8¸TđäŲ~t=„Úë‰ljŝą?q~—he3ŧ$M‘ÆyžŽwwĸûÚĄ¯eûIL}C䰍h§Â?Rgød­Ŗ8=a4]Vrod3’f,%ėŧđą;W ;øcã…ØYŨÆ%XEÂw"ō×ãŗĖUŲbėÄwëcĸYdĻåf-ÁúAcTŨģĄāMĖëE”. & ē1š…Đûl"oD9Ē™—)°_IöÆD,ˇ™"sŊ„g×6Øn Įú÷˛ĪßĮKjOâĨdÂ׍!č×|”-qnü‹ĐŽ­ 3re×ēũxŽČGŊq1Ž7ŊHîlʝ‚ ;œÅCãIÛDâ[Ī%áŪ[ü§×ÁÅ9Žŧ‡3Éíų—€Šëq92ƒ1S>ׯ#5]€LŅ?g1˙oļĄĐˆķWĮ­˛vŊ>âäyGY îĀáû 24Ŗ9SßéĨû¨&}%bô5_ž'æq*;_$ęČXũZãvå.žûĪ‹Ž[…˙Pá×[ĢņhžœÛaũ°ū{ Ÿ‘ŅOÎEЕ9¨îŲ ÁųÎčî÷Á§`7ÖJ‘Öm¸=ņ4a.—¸ßz$‰Ožĸųąm­j;ģ‘´m*•Wž6ä9îIņ[ĩ—ƒF7:GĨƒČ4Rsī“Tw;i{ųMP W,1šb‡c`gFv <|eƒÅŨĩ"ú|4gžZqg•NO#Đŋ¯K›Đˇ[‚ĸŨ@ŦƒļáuĒ5%ĶĀ|oO܆¯F>GB\›Ŗ”ļhÃåíČ~< eÂ,äՃ1;RDÚ1÷™"ķĖĢI’ÄĮ$šc%šīČYv‚ø‚UmŖŧ‰øaCÉiäŠ_c2ēîFU™ƒĮ¸$ ‰#xÂMüƒ7ÚååÂĮ]˙l‚d˙ÚÄŋ¯ƒž¯˙ú^Ä&áŊ¯‰į& Z–A˛ø\bD ~;ö#˜ÃwW3ŌĘa^ĩßŋC8ļ!–ˆ&ŠxË!:ĸ:ŖæøĮĸ›Ķ‰Į‰žxīL°Ų"¤{+Č4‰&ûĪM‚­&dg;Ėæ…ŗ@Ač¨'b/ŨBėįX ÆZqM­1ėx(œdÚĪe¤øŠÍųĨî-cÍŌÎŋEøīŲD\ꀮâ%š˛uØxˇboK”Ī%äėō!Ŗûlĸį‹iUCœv=š7˙ *Ōĸ¨ŽÄĪ΋—+D ŅEŨVp·&¸[ÃĒ~5Q7Ņw`œzŸĀ˙úŖ_9ŠĐĪĩIzВĖ:.Dų@ærMTū‹7{oęŽA„í5&nŽĀļ¨vēãņĩînâŒų¨×x“žã˛ŗ…čįĨđvJæĸÚņíũZxŦ¨@Ũ¸Ō%ļ(gÎÅ-ŗ—KëÕ~ō?#ņ™/îĮÍUj4g—tÍCw-ŠĢ‹PG ~kŅû•x‰Ŧ×įt"čŽ`Š—o Üũ Ĩö)ú·ņ?:‹°%“đ/N‰aęhæ‰|ü ĩĨÆ‰Ųŗ™”ąŽdČ;‘áڟë-ëá[<•qĶķq*ˆ$æũCąĢsđ:˜@đßqD^‰“ņlMũ9ĸ)O]'×Š7ÉģŖ}‰ŪLwr%™Ö%”ǝsģå¨}^ēØ ÷Učîˆģō-Æ%¤†ÕAøũ8Œyĩ𤖋‰•ĩ!biéĪ}Ä>7"+|˜GÖÎ?ÜuÛEMÅr‚’­•†“Öņ Ūž_…oū‡˙üŸ7!/į1šËå8† ÚHiâ'’ û ŸUCz`æÎøŽöA3Ëm÷v$}ūC€õXܲŒņM9Ë}É8\?4ĮææhÜî|<‚¨Ųb&š¸Ũ‰āUƒ0ŋt ÕËß(ÎŽâÅPŽKv’0ę™ö_Ņ>V 1xIö[Ņģeļ"xō<<žĩÄâęŌŪ›’Đŋ>)Ĩ3‰˜*2äÄ{˛Žģ’ŋuA­Ŗ™äMą_].†LĮũöV>ô4 ×í!!ĪËpJ™‰ōũe%ņxū;Nöƒ0,ˇDv¤3ųuŽŧs5qÆWP/z…ūƒŠ­û)rßNr“<¸z“ܡÃČz‰ËĶ›¤ęœČēy‚jÁsæ1Ŗpš“‚G-Kʏ]!GųŊ2uÎ6l,Žá;ĐuĶß\uí‹ÆX—ļ#žŅw*úŦD5lÁ^éäŪĐâŪɉ€]~„÷?,2|,ģ.RØ}ų-gĸŒÆÍp+雋Éoü E­hÂģĮ˙|-ÂĨcWõƒÂÆųØŦ–¨ë‹Æō„ā€7ÜL}ūį44G×23•éMîģ\ Fڏ °qč;ĨöČ#ôīT 9dŊ!čy QŪ9ø@eŸ]8liIė1G4įsfDYV‰ąrŖ°ž1yEą¤ēŒF;1uųԚžĀķæCjĻy = üŖĶWBGZ]:ø¨zQŠŌfō`ąķG> ŽģIi˜RĖōÜ Âđ‹wđ̀Py"Úĸ ”EĻ„ :M`Ī΄ÕzG=WĶä˜{á]Ķ’¨įž„ũ[/Šgۘø‘ßŅú5įyëķx›ĩ'ø-X7Š$)ī6Öķ"ĐÎIōg;bN”bgŗŠč'ŸŖ…dÛÚRÕû;ø…Í]<ī{o˜Žļj҃Ī;ė(™-[ ėŗZt] ÚĀ+ÜjVÍ÷søŽ1A6ū‘VëQī‹%üĮEB‹įĄ]“Hūk9Š&Ák;ņŋ‘„2¸mP7”úDÛā#˛ČFw—ĘŸ†¤uįvFÜēÖ%ûŨ䉡xfˆîų2÷ņėí'b÷îŠÂ‰°ģ´\Ümps‚k#÷×YFÁBWŌ~Öâ‘ŧ-na"Ģf8™įŗI˛?ĻøÖڂYØ|žz\D'Í%îøV\JOR3Ą.ž+šâ?y,Žééūīū Qv˛&ZV„K¯ËTģÛÅm„M|™1NS2ø )úÜŪļ›™8WÛ۔=jOlĮd ˙&8)‚Ā/ŖŲפ åhI™ŌG|}ÜīzˇņęŲ•˛ŗ‰ũ͚ØŌl’‚™īČj8‡čQ.$:ŸBųrĮđ<öī‰Ŋ$ĪÍE“ąŊ7đfģāę…u Ēc‰õĒY=2"Îî:F’“y¯æî„ÎúĪæõ 9WŸ P,žņŗŨgBŋ~EŅ!Uî;bžsmC*ŸÅy÷ċįÚ•č¤Đ5š×â.ē5Ą?]ÉĒ™JĐpŅM˙bzȝû!DŒ?FŲÚmȡQdģ”ôŒ†’Ú’nY@ZZ+, ęs3î=Ž ā´ęöë đAæ=(›‰„Ŧ2Į&'ŸøÔ‹ŲŊj;˜øî“q;°ë:<ö,,Ev!ĀÅ#[ī!kå.<û#ŪrIį&‘ŲžáFŖđĶ­"æēǜ Žî_r í_üßOÄŦÅ;4[Ļ’[?„¸#đ¯-ØdÔ!’Gąx¸'3ĘęŸGoߎŧ/íD>#Ąã”‹ōŦO Ž{KoN%jŖxzcĒ] Îô.ŲW¸iŊFxq_î]€K­Ä'^§âāW˛ė¯…rUŌžzûá †oÁ}]ŪSXV€Cņ>b“T‚…§ķPÜa×N¨‚?ķyÍsū˛G*wãIG/¸'˛Gt™õÂÅ)öúëč>$¨åjôĮ–<`Ēé}đÎXC¸{Ũ.Ōs.~5C}û€ČŪxEvĄjt&6ŗH\ߊéJë†îÍ0Øš÷ÁÔØĒ–ė‘Iäšĩøĩž†ÍŽuø˜åĄ>mæ*üüŠđ:ø Īyxo)Czi!'áģŧļķŌ°ËO'j×,ûĢŦWŒēPôxzĄÛeH|ĀĨîvÂG>|֚ÂZ*Ę"ßōqu"ÁĢĮā}ēš˙{ĪĐŨsIĢ("Ŧ …¨Ų]đKÚJHå@üžø—2‡æļDÕ­Dģ)û5gŸÆņĐTũķy—cs!˙Ū„}ß‘¨ĢÎáiŪ“K Į%°Y&JUâ”JĖ–ČHū>K÷ūâî†*›Œ×Í)[2OxJ/&č%K[˙'™õ™WvЗ5ÅĨõ@ŧˇ _ûŽōH#Ž÷5%Ę.Ũ˙Ŋw‡+ū–3ˆúŲ õ€×˜ŲõÆ)ā+ƯüŅÅ]"ĻãÔF˙đ]ĨĮ{ßPÜĢ ™¸ÛŨqšģ ‹_sŅm€Ėõ0s[“_@N‹å8,oNĘÎMøÚĨáÕ÷.oāđßL¤Ãīc7}<‰ËV’LOÄÂM_lĸí Âl{’›Ty7ąģ–J"'Gđh Úq#¸ŧ˙ lÖÅë‘Î˙ūŌo nģŪ‘XnNØjáĶæä+û°ę )šIøîVPŨéĮ¤54Ånvgb…ŋiO}%ך1!c—Ą?YÅƒčŖ„íW _&¸|ö34§^Ŗ\iBB‹ąd¨BđÖļ'Åŗ’ßíq,­—e6Š=ūƒBņ(ķĩ#ȟ~3ņ,TwĻrų ޞĩdļ6¤˛NÉāų’Fây´í):[’†Ûōw.Žín0áIšų¸unÁí“‘M įá^3Â']Âōųôy=ņĢ÷‹ũy¯)]ĶŲîlâî¯#ųÛtÉf䯞NXGf Ũ°Œ{sÆnyލ :ŦזōnË;Jæcwy‡`ļ\TcÂ0ŲģEî—‘{}ŪFƒ ´‰:Ž)aiŠņŒĀū‚3VÄ#aą­Ū"3ųKÔČē¤ēŸ'øĪoT)j’7uĨtËSŪ†ö•4vžHYŗ ˜%TUrˇÔ ’zw>Sđ7ŸzíŅM åö !¤\؉ŋtžģžūY‰ÃI[ ˇ;ãķû0)[ÄqAmĻíÔK—Ū”†ėÁmp~ĘđuÎÅßĒ™čÉ ‚ĸ,)ÁŖÎB¤^zrö/Gę\E˜Æ¯U‹Ø3¸šõö o6ŗm‚Ë:Ô%ôÂH’ÂIŋÕ“‡ą¨ƒ4đ žÉVhäīp•—$ŗÃÅá Ēåv¤nPqōÂ%t=ŪRémBáÕ‚×8Ņ›u­I zĀÃļ9¨2ŧQm؊ÕF1ë/ûŖÜ•Ž>Éä˙ŪûÛČĄGvßÁõëCqŸWņļëŠ|úg´v)dÎĖ$q¸čöĶČŽ3 ¯Ēf¯$ˇÕFJôäū‚|ũö"g#)ŠÍ5dĨŊ•Lí•(yÕĶQŌ×wúMÜl6õ˙ūŸŋ×ŧOßĒÎö~Jôųix]˙ß÷Áäáôā)ĻGú?¤×ē?'Žīc|GõÄPū˜ä9īI•äsbCĄŸŧ‰jOČø!¨Ä%^!c‚%Ę bŋ–yâöŊ ķÚf¸ļŧA˛ŧ3I‰īHNuGĢ’?ŲXĖžčˇm‰x×8ĸYĩYxûfÜs#ˆo`KđõūxŽIņĄ4"?ž&ë™ā˛ ņ¯•ˆëŸPd­F’ģTí.rî"ŋž7Ņ­Ļh؈°Ã‘Ÿ7Ā!øņEŠØÔ^Cčáģ/ãzž9ņSzãqa c”čAØÁŸÜŒ^Įš^zŌÚĮžh 9˛y^××øúČīv$eîwá`ŊPuˇŒxoĮ­¸ÚGpãé¸e}ĀâđC4 Zb‘q€´ŋ%ĩ>˜šĻQžũ…#ŊÁwr~d(™/ŅĖ_ iņŊĨC}ņ0jŽlŌhLvįks‡wč5Įˆž?‚“0 )ÁęĐVąŸx\žįWJ„Ļ%ÎģŪP\ë3Ž=Ū`vÕOīč×ęíų ‹Â÷ČķĄOŪCFÖG‘ÃĸcnĮąq,Ņ_#ũ9€{ū'Pŧ˛%}~G"ļ š{‰æ„O@Āô;(ŨĒ„Ķ˜$ú]„wŖZqį!-ˆšMĖ"’a7ų7R E˛FŊYL`¤pžĶņĖt$xšØa]qÛV“2ē!éxx¤‚Œ}ŨĐ|ŽÆbÆŧÆdqŗö<ÜAĀ­\"ļY>‰ÄÂ&äļĒÁ›ŋ|zöĀēã\ļ[ļ.›ÔGƒˆMw!ύ.ōUöx9ĪžS°ßá€÷ŲŋÄÅŨA>÷$>ĶEūXlđË˙ŌņnBԊŨD-$):„xįҤühLá*SėÃŗÄŽj ޜƒŦė AķŽāQČŲīpO鎷n†×ƒøēšCëũ¸úE‹ëä^ũŠwV4Y“§"_=Ģ6ƒP˙†ēã<|‡ˆŊŧ8Uä@sōV|ĮkĢp Ŗ—ÄūÜÃŗ"ģfbÛîãM-Dī.ŧwÚ´AJܚŧÅĨw3á OøėũgTÛˇ(ú’• ˆQPPPAr˜Uc* TŒˆ&0 bˆ$3 ( ˆ’$K”Œ¤Ycĸ‚¨ FĖ9g0ōú^‹wÎ˙îŗ÷9íî÷Úš÷Ŋģú7uŌgÕ}ô_ŗZ‘АK¨c>v>•4oôĀ å9¸$ū#ėŠC¸čÚüĒo'Ôp/|nPæ4Nãd؟Mė9|’]€“†9âlŠEW]p§‹+ˇäaŅÉZܨ9įngũl„ķĄįÜ€Ķˇa2sN{ˆÃßĀ OâŒ;6¸zÉ4ÜĒi7úˇ~Ŧ7jq"žã֌Û/ŽÄU/õpT ôWJņéoĪpÉņ8fßgœuô3NüøßxŽ‰ë ˇāS ÎãœãüsĒgqŌX3\Ątķ_Jq”´æMÅŲqÉ!N{\ķ-…yžŽO—ÃU-ßqm'ԏ¸č“NķĪĮå3×âĢ`™<\ũ% _™ˇ_[ĢļÆâ e'pGŪSœøi>{B'ķˇpØDŧ^Ž 7…ķŽz nt0ÁGy0ÚųSЇYá ą¸øˆ1¸ųNkˇá[ũøô x¯Áī_vâúg+qr'ŽIۆÅ•žOņŲ1kp“O Î7\Zφ›Šnā¸ņp•Ĩ3ޤ]˜×ü„‹×žÃ|ĘcĖUŋÃüūL\Šđ§ ’pÍ}Üõō;ÎXŒŗßĪÆg§¯Ä…Š›qRĐ-\í!>˙ wzÅĸɰO¯ÁqPƒOũ‰ß5™žå{ČN¯e&ΒpŰV O9áË 28q×f\ôc3Îŧ™‚‹gŊÅą'“ņũwø¸ŅNœēj‰>N9°wõ–ãĸ“ p-ŗö˜ˆÃ÷ÅãŌcpŦ1?gŽë=ępé×a8yHžđÕliŒĪʔáĖŪ~œĩ1ß9Z‰ķhâD%M|9`.ė5ōūā;F¯`¯,™[bpŅšŸ¸Üĸ ĻĮãĶWÖ⊇é¸îûy\kÉâr§$œ.ҌoÃ57ēpĶēcøō&œ‘Xˆš+ŋpĘĖG¸ÖzNÚN˜ŧ×H†áæé7ņ˸ũ˜6×ô„b‘īÜöéžøč*>ķáN•]‚ĪN1ÅQqbûĖåíÁÍß ÆūÁĨąGņ1Ŗ@œygõĮ9õãņéM‘¸:o>…pÎÜ͸­f!.ɛ+ŋEc˙׸ūģ!Nxց/]îÁŅ›Ā6Ã\€ųm øl Î{Ąˆ[ķ†ãnœ„q¸ÃĪ'XĘã8Ö­˜ŠĶÔ+đŲpSŊŲ:Üöō ކžėrˆ n9|Ÿ|ˆ Ŋ¯ã,ŨßĐ=ĮyĘŌ8ŊŪ_T7ÆÍíw0YĮâS—āęEpØ}U8Ģpåƒøōo]ü`Ģ &ģzņåÉâ8[ŨšŋĮ§gfᘟqwã:ÜQ˙ —/[ŒSėp|'.ęŪ‰OúJá“׿āLi\z1ˇjĮ…ą pęÁh|ļō:ŽĐy†sîgâËnáĐŌ“¸Bn,Nz9ėߋ v/ÂŲ§ããgtqQÂÜÉMøVálÜ 2•xā¸ö$pu+ŪĢ =ÂęÁĐ šâ“/(Ž%ō8ÛX\e;ßüp'lËÁņĪ§âKpí}Š3ôëpÎč¯Đ;ˇā˛Ø5`ą÷XÔ§‡‹æā;ĨOp•Í<œ—tįÚA,=€[Ū]í ƒđiß ¸Úīĩ¨`nÅM\r: +ÄėÅĩŽģ0ˇ{"NnvÆ5Ĩ¯qćH|&SŸz؂;Ę^á‚uđŊ¨ÍXäŗsâp›ÔAoMœ—~Gį:ÁųõāŦÚTœ3ąįˇwāŖëw㙯¸2ô->ž}ō*Ž:…“Áåė2\v/—ļŸÄmåķđ)øĖœav§ZŊÆ'^áëQ9øî•F\Ąm û`6NãRđēŸ“qžŖ.Ē;‰ĢŽÜÆ×āûOëfãÜi˙ö;&âŌÆ\Žr WxŲâfÉY¸Ú6™ā ÅŅ8+úŪīpmĨŽd`7Øp>KˇāÂŧķødŒ.ˆ×Äšõr¸FÖ'ûáĖ]ž¸ô&œ)9įT¤ā3q"\žûN›Úˆ“VáæÚp6wáŽį¯pœ´+NË*‚z›įų |ėëUܑ~§?ĸøŌŒøRčn\—@ņÅ+ĢņÁT\,-¡>¯Äĩî ¸ŽS—ŠĮˇĨ\đ‘Đå¸t3ô•jRøb5ôˆŠA¸rÍ'|Ļm$noPÄéëqšüœøÍ§ß›N‹š!:8,–fžˆķ\㋆āczģp˛ŗ \˙ |lL ž÷iMXƒ÷ļāŦ‹~ø¤ƒÎ_…ËúwāK¯úđ‰ˆ¸ųėœrÄŸŽũ€k“‚1ĮΧģ§ā&‹"\lQģ—¨áJpb%Ԅú§đ3šŽO =Feq‚ôX™˜ŧāJčÅkVčā4é!¸E ÖzŲSÜėīŠĪ~ëŧvßÃM—ĶqæĐ`\ą.ĶË{pĸęZ쉿Īņ•ˆß¸Ä[ ûUŪÄÅá ¸dÜ |ē§įŋۊ‹Yãfõû˜ÚÅßßâ„9•˜ûŗLžŒ/ėrĀWçŊwā„Ÿą8Kō Î…^:⟸âã,Ė÷¯ÆuŊî˜o:„ŗíNāF^œņ~+Nļۍķį­ĮŨŪ:8'ÁmÁˇpÆˇSøÖéf\§€ĢÃ9žßs 'ī’ÁWv↍}øhxüÂ1&8ąeÎQ†+wâÔ3í8!R'†ÉâķՃqް įīŨõsΘ6×ŪČÂĮ`rw ސˆ¯ZBķėۇ+RŸāŧR!Ž„¯éũÂ§ĖĄ‡œŽŽO]…ëFīĮĮí7á|į8ˇ"WÛõⴃ8ø NŦõĮEr8ûË1Ũy 7ˆšāĶ:VøVË\Ŧ1­lĮ—˜Ą¸Võ ÎUQÄMÍM¸ę÷T\Ÿ%†SG<Ā™sÜpĩę7\ūÜ œ,n´ĶFãæQ8×ãÎvãqWįsL‚q]ũ,\Ģs §7¸ãĸ¨`|Íļ_2ļÅŠ7q‚Ä|¯D SĢp.I6ÄõÂL ũčĩR´‡āÂFG^wrÚãÕ!˜–ņ¸Čšįny†îy‰Ręp­Ō||ģŋ_šjE?_brğ´›‹3,žãøUcđɊT؟ú¸iƒ‰tÃ%aŦ7˙Âé5ĒøÂS3|n/ԖwĐ¯OĕõQ¸Ō] ŸžW]+ÃųŠ Đķ¸UG W˜‡ĶŨáüÕļ8w>÷K_(ŅÆg? Ãi–—ņÁ÷QøØ,\⡇&äãėģ0íü‚[Ü"đE'MÜÖ§„¯<ŒÅM]ĪqÎÄŪ۟ā×ZŧS)ęR.ŋfÜöĀeWŸâ§Ëēqõa'\­†ĶGiãæžHÜĸ= —mŲ€/ÄâË×;pžs*.WŸĢæéáÆ÷Ų¸<ũ=Ž,Ÿ€OüŽģŊņd¨ÁZøž”ī~zX›…›ŸÂĄ/ļá’ĸl|rĖå%ú_Sļ™ĢÃđFëN\%vjķ|ö×eœj/‹/lû;¯§ãæû¸2Ę 7(vãĢš3q†<ÂM&†¸kä5\ÕļĶ%F¸‘.ÁWâjūzįčí_ޏ䕎ũíŒkģWãËņ…&¨y0.+Ãqžƒ1žsäNY?Gū†ãŧÖáŒķ°Ž‹¯ã“ŽÅ8†1Á…+GáüW¯p‚4ô„7>âęōŊ¸Ļá}];qdZŽåˆWa]€sîļ'N/ˆÂŨCŊqĒŠN‹{ˆ/ŊŽs¯Åĩ ÃqNÕ1\Ų:‡ŋįpCDΞ÷ί18ūeNĩ˜‡ËŊ^âˇ:*¸Ķe;~°wŽ:ķöøœf&îŽÃ¸%y#>ĩq"ŽšŠĸīã´#wpãš,ׄđĶ:Wˆⴛe8wJžB#qZĮ œ\ũoŋj-}„Ģš8 ~nņ\ûe&ΏwÂÉ~ãņ)Ũx|6@ ŸđøęáéØ+H7,é72øĻV.^9^Ÿ9˙Ķ‘ŋqíéc¸tr,Ž‹oąƒqĘģøŌgxŠãįÂͅY¸fC>wo1žV÷_yk;7Æã*i/œgk‡/ۛáĒ` |MjŽWSĮiŗZqŊlŽV3ÅÕˇáŒüøŧJ+ÎŽ‹Å)K&âÛuo0/ģ Ÿ 2Āyy2øėč͸ijÛî…OĪw‡=˜ gô9XwîzĒÃiîθdô\dđ×ȍÄ]°ČŨ ‹ŠÖâ†PÎLjESváÄŌ|"Ž‹Â3a†e/ŽįˆĪßMĀūũpNžĮš‡îãä SpTĐ|\äû×{TBúįã=35q•[7N[ĶãĀĶiƒĒpIûgœ´z2Ē€ULÃd~6Žp†Ī-™‹ĪOŨŒSvíÁ×dÔqįw?\]|Âe¯ßācŧ-ü>˙8'öÃ1Ŋ¸*į1Nxŗ?kõĮ5YPoš'₎¯pSŒį„ķ8"ÚgjÅ-Ėp‘WÊÁÜæHœ}S _øÔ€¯éāWrš¸=ķ=ė߸ŊĐSß͏%Ž —ŽÂŊ;mņ“č?ÖâG&_đ)—'âKõžøüđå¸}˜ >;~/ÎęÔÄįÅâôO|FkÎßâˆīĪīĀągž@ÍąÃˇĻÛá˜Øû¸¤vN}?{oԁķx>ģŌŸ_°õæāÜ÷kpĸ|?NîRÆÉVē¸îĢzÕk|*A“öV\ŋŸvsÆÁį­đ•ļĶ^ŧÁécĮá|Īøäņ2|§5 ‹Nƒ-‡Ā|[~ÂuƒĖqÄčˇO†žcæĻ?Į'Ü5p۟hÜ4 ^˜ŒŪ)áæU}¸Úy0&'p‰d.ī5•-0ÅÃ11PÅĄ3ûq=?_럸˙(–:ĪppšŊĀA¨­7{ž­ũ mmm=í˙FÚŗˇē¯÷Ôö đņÜļsãömÚ˙‹Āŗg/ÕÖū˙dhf ųÜŊŊŨwi¯u÷q×öŲååųŋĘį`ģtļ“ö˜O[ûßōmķŨēÆĶ[{ûē˙wæ[˙žŌ˙y>“˙(ŸšąĀōu5˙I>Ķ˙äúLūĪįs´[āâāüŸä3ū?ܯ—ģˇûVOOī˙øN˙û˙Y>“˙CžõŪÛ}Ŋūgšū-f,u^cžķœĪäßįÛ¸ÍÃÛv›ļû–íÛÖkģlü–Ī_ųūãûũ/å›á0ũ?šâŸø'ū‰âŸøß"é}sDcrŌoĻqnļÖ\ÆŽŽŲh÷Ą+Ÿ ­!ō–˛DōÜt"^7‚H9 ˆlÖG2â0K&íZIf(#+°?ŲésÖʕÉ2rf‚›sŗĨĮ_ķg ō$ÛVĪÍ­÷.jŽŧ6E$<ū@T÷°•›ŧčÚ;‚Ģü˛ģYą™Hå&‘ĄvidPe ‘0N'Snų8ĸ¸îŅJ×"N‘… +ÉÚ}NĮFΞ<ŪØ•G“‰Ģ/UķÉdD}$1*ŋD%[ÍߑĀÕ$Dq!Ųy`%™ú›ëũÁpaÛüš1Īt¸A“ēšÍC5šoļdęœ„Y0”L7‚ĩˇåĶđR“5ŲÔĘLe'í g'اŗ:O;ŲąJÁŦfcĢ&ˇŸĻŪÆĘĒcÅs¯2_6 g>”ße^;íũk<ūhcē 3FŦf î3‘ŨΘY‰åŒĖ•hųĩFā~sšuĩj|ÍĪÂĢ" ŋ™ĸŦ’N5÷ˇŊO•+ČŊÄĩ2æ~.š@†–]$˛Īv‰ĻŠŋֈôā0"˙B—h\M"FŠ]dNŠ,Yž’lŲįOļŒzJVšM"sl?ãČĩddĶeĸ˛)€č8™“§Ī§ČŪĮm$Äŗ˜ėŨéLĮ‘!?›šŒÎėO7'ņ]‚ŗYC¸+;2‰qI)ÁÖŨÄâ—$Ų8Ōj}A=kū2š5hûÂNzöÕĶsduO…ŗc?šącŦÚŲA]ŦŌ-cVîægVōf ĶûŦ“ų˙–yíČ<{VĖ<é˜ÅÜjĩcZ]B˜ōûRĖņžY5ãŖŌ|RĐūå› Øņ€õ;ņ‹VzÜÁÚßbËEuŊ8ą’ ÜbĘĨnÖâ.ˆ-āŪ.Ņ C”ōˆ|Î"e6âī=ķd=ü͊¨m/!úû‰Érâ6é ش̓lŨPG<8câ¸Ē‰X]]EÆČ„’á÷o‘ąģ›‰ĐW–Ŧ¨w&ū9ä`O‚ļL&+“íˆļËlŽŸÁ-P5ádú‚šąĪvsļĻdĖŖ“dæØŋjČFæ+÷`ųÖxI4kXšķw ŲƎWƎkÂjEČąęgęXåÄcŦƒĨŦtĸ:ķĶ3‘ųdhÉŧs¨`^dĪûkÜmøÃ´ÉaD*ōLĘYÆˇ;Š˙ŊāéÛpAÂėá­d+kũmíĩįÖ[ˆ\MĨEˇûßpÂĸ \ĖÎxŽ^ģĘ=ˆn!ƒīŪ&C<…DÆSė¯ņč†ũ#N†%:aOģØ,• ë5}˙Ú3ë­?‘Åû’ÉÔÖįD÷„ Qžø‡Œ*‹„ĢÄEOHv´Ž#Á̐ƒKLÉf7#bŠ0×ķG…ķęiā”.…pŠ2Ą\xé "Gģo)Ä<'€8ÕŨ#3š”ŲĄYŖI#ūŽ!'’Øņé7X˛ãŦöFŽڊÍĒ<ŨĖŊ4äķ‹éoŲüW ų0T™y¸ęīōŨ’é”[ĪÔËb˛‡Ĩ3Ąëcëø‚ĪF ŠlŗxMu¤NrížÂk"˄sĸšŧ ÜxwÆU¸äēG‰įNdčô^2Øĸũ¯"ūí-‘.[@†žNÆŽ%æâ:ÄQB•x¤–“­WdČĻSˆëĶkdÆâ'd˛öpĸĻÜNÔÎØC.žĖ‹žC6‡/'F?Iˆ=C|ŋ} v§‡1×ųÜQMMNgY5'Ŋ9†[ĩz÷:á(aŲ‚ü%É´“jÄ7(›‰˙bÁšÎņūģ†zŗ„ąēŖëØąŗ7ŗŖMƒX5ŧŽžÉʆ¯e%Ô2˜¯öÕˇž˙­†<ēŊ“š>™š¸Ž“)\hÁDK˙aĒ'1rķ4 Œ•`‹ŗ¸u“âåšŧĢ[EĘģ&Š’—Īä†mĪmøņËMÂ]>:†ëŊžH†Z@äĪ%ŋNū}îļ§ųÁADŊ:ž†ų“YŲ7ČĒdː\˛åH5YŊŗ†Ėm™OLÆ •éDųž7™ ’"vŋƏ^dĪņ%$dļˆėŸu„8]ŋM†Į>âōövqŒæ|Nōfg=8‹;°„­'v™+‰Yå$⯞GÔķjkķ‹5ÔÃN™ÂęmæūĒ!ã&j˛c¸˛#>æąÃgK°ōŗÎŗRŗÂ™ž אūąĖķÅû˜§XšÍĒ0—ŗ0gēZ™øžnÆĶc3˛ÉQĐmÚ*8|ÔI ĻËYåA y/Ŗ%ڝ(úö$˜s”ąã’ŨÖpįŽYr/oF…ŠŖˆ‚j1‘Ęzņw ŲŋŽ vÚKT—Ž#zūqĖ™H–ßĶ'IŲz3†ŦMę Ÿ#‚¨\ĸõ˜ īL'Zd?a÷u×ĶåÄoą9¸ō –N"î^Ũ×ÜyĩõÜâáķ8Ų„-œĻØ".#äQŋ[FfĘ{ÃÄãˇ6ŧč k ŽĮ>?ĘNmbõËŊ˙Ē!:ƒ¯°Z ŸX Všë;dw)+3č5ķĢ,’ų”:˜yWōßjHOfsm3,Ną'Lę[GfŠ;3iĒļāõä‚”CĪú3FZO÷`kKļß9V/ĩ‡9ĢŖošČ¯ƒ8Îô×ķáĩ– š~–ČtvņᧉDZ5‘š”A”ļX’q‚mDPūŠ8˙Î&ëĐĩŋkČĶ4˛¤w,ą9b@&Œą!*VíDcÕ9b–‘Fu:¯é2$čŖ>9˜îMļ™ &–žÜŗ+¸Ũģ.pjúUœüīĪ\pÅn"Ŋ_žā}†Ärmq˜=ޏØąû_ąF×ēØÉŸŪ°“$ Ųņ7XēV;3‡õč3Ģę"d%v°f5+ŌÎ|ö_Æ|Ø÷”yĩDú¯ņx°qĶųJžiŦzÁä´=eÂdv3ÂN!#ĻÔ'(? ˜w/Ô:yt{íB^KdøöŒ¨ėt§­ķˆÛ÷ų+WZĖuŠũ&âúōאž"á|ˆŸ•&Ōëɐŧ 2zk1ĩŊCđU˛ĻVDļŽ° ›z­É ƒĢÄ^m21¸ŋ”¨}ŪIT¯§ÉŊvdNøG˛ņ’9`ŋ„lĩ'+žûD"u5”‹kšĘé'DpR ͜ŗôxîá‘Zbõm™šĢIØ'édˇL!ŗ82‰5›¤ÆŦVc'=vc'Ôxŗē.yėØ vt– Ģ–5’öŨ‡•ũnÁJ”/gžé-c>.ĩd^—cžÉŧes#˜›Ž0ÍÕŗ™âĀųLĖåŨŒsüfČĨÁ…yīéVë9‰ŊGj"…ŊōĸØÔœüdGnÍjmîTøBîŌ*,갎Č]M$–ū]CĢ‰ÜœN22]–ˆ$3;ęÉĘå3ČæÔ.˛åēq?2Ėw,$æ ֓QQŪ–ItŋķÄöŠ,Y#iKvODBô‘@ÉWdILĄ/͕ėúÃŲǝā¤jgsƓŸpJô-]Éô׉éÚJ˛SĒ›ķĐũČZŒČNģĀNv aõîauÛŲqî2ė˜÷FėȃŲáĩŨŦ|m+UģœųžĢųW yģí ķü‚ķ´üsG"šōe;Sub8“X:ŒŲŧ,‹=.HpĮ+MyYO —áaõāĪĄÚ‡ōOD^“;EīôGqŗ_´p ķŗš†ŦŅÜŗC9D~Ĩ?Q=@¤ Œ˙Ž!3÷‘AÍK‰ŠM<™ ?†LÃßɲŖgȆ;ūÚ3žģüˆĶļ@ÂÎ{GÆĒ"Ę adŒƒ@Žeg‚‰Īíũä ŧ Ž]M<õâČD{îŠĮ nåŠhNÁŌ…SkÜĮ%ĻK•îķdÆõŨÄtHYY˜O´lÍXË‰ėËŠėdž˜Õéʎ7ęculjX­īwY8VeČEvČ̃ŦŒķGLšųŦp‹y?ÅåŋՐ{AĶ™Ž9¯Ūč“Ū_ÍNÔ`Ϥ8 >l4d6q#lfŊzI^mōÕ2ŅLĻXt1q&;|¸pŋŽ&Ž—ģŗđ‘ž'CåķÉ éŲDB+ŽHŦģLdö ÜOí—߉ÕÍŲÄÉo&ņ\1å¯ņØĩ”,›OđÄ.ĸ—“FTō2‰ú°Ĥ`;qĖą"Ûh> :@Ūp#^÷ove÷žŊÆŸ=ĀizĮÉŽ’å&_'b6VĆJŦ—l%ŗūXÉĖãcÕŦqÄ7Ö`R7;‰c'(ü]CnEąš‹ÎŗĒį•Xł'ėā‚|VÜ ŠųüÜđīŪŨ˛āīŪ]ãĶÕRĖ4ų1y҆LDÎZÆn¸#é0YPÍĪ,Ynh?RēVÎ]īAĸ‚â#œÆ3yÎī[7WT=ģƉ¸?×BÉЀkDÖī(‘ˆ¤÷î6û‰ÂÅ_DsĖ bœCæ #î™dËÛĩdsÚ'˛rē+™Å™‘)iédÄõDuø22Éę7™ĩü&Y˙rŲoôá¯˛{O™{¨“ČŽMį’Õs†÷?s’†ãš9âC¸s˙bŗK—X{%{ŽĘ îodÍekYƒ„?õîŪ/guŖbØąĸŠėčū;ėĨ—ŦŌÆŦÜFYVrŲHĻWĻ˙īsWkķ˝y˛Æ•š5ksIú,SZęÉ;ŌĀ,ëlb”f[ ZĢî öæ=ĩ~ e]1zļ˜Hz˙[Q„Ž 'ũvį†sšŒŨĮÁ˙ošŨ{Ā˙áDŪâ=øŌ€˙ˆėŠ‘ā˙Ŋāøøŋü?üoEÖdĩƒ˙e‰åĐbđŋøŋ üīū˙ūūß ūO˙Ī˙k“QuZ\yÂÍRķį¤ŪpúO1WŪ_Lt2lÁ˙9û߲“+˜áĚĪl˙—ø?ü_ ūūW˙§˙ķĀ˙ā˙)ā˙sā˙šā˙ ā˙qūŸČ´Ų=˙/˙/aŧü˜ą‚%Ū‚c˛?ǚōÖ ;Kk¯+å‹<ËôEO7×pv­Žā˙oÜŲKĀ˙/Ā˙iā˙…Dúúë˙˙ׯ˙į˙/‚˙)øßaĀ˙ęÄųë=đŋ9ø |N üßū÷˙_&;´Á˙ãÁ˙ļākbØåĪu•ÆsëW}˙ۀ˙_p13‰bûmđ˙&đŋ>YfņŠ˜9˛ŖÖ'€˙‡ øŋü˙üŸū?ūˇ˙˙Á˙1ā˙ø˙ëƒ˙}˙îŨWŋ˙cÎZ~c˛Ļ´˙Ĩŗ%‹_ķ¤yGVOĘ­÷Î^Ųé#ÆÕ=îæ&;}˙ŋä*ŋ*p7ĪČŠđ˙â{ā ˙§üüī ūßū/˙¯˙o˙/˙˙Ī˙˙˙Īû˙Û27ÂúĀ˙áLĄc-ø?ˆq¨leäæ: ˛‚- Ŧ› jō wWŪ}Y”ėļļiˇá×Iđø?‹ëíÜūw˙›€˙Ŗü_ū¯˙ß˙_'ŗrČ*oeđ=ø˙$øß‡Ėm~ūE4”ŋ€˙ŖĀ˙ÛÁ˙ųā˙—ā˙|đ˙Yđ˙XâÔŽéāōöXrŒÆ#đŋg-ʝÛN Âׁ˙‡€˙wEĸžŠŦEėđ;ø˙(ø?cĀ˙Šā ūb‡Īz ū/˙{˙íü?üŋ–yjÛĪÜf~€˙KÁ˙Ā˙KĪ5bĖČs‹Ũ&Š‚ÃQząqîVyyĸÚ÷˛žĸŨ_EߞEqŽƒÎpÉ+ÔÁ˙"îå-Wđŋ#ø?üuĀ˙>ā˙lđ˙Zđ˙#đøßüüīūO# Ÿ5˙Yđø?ü˙’°ûˆkáDđ˙Ađ˙gđø<Ņ}åĝWŲÍ-VĖÉÆˇršâĮšŒC¯ˆzĪđ˙đ!ņøŗ† žoĘŒū˙׀˙=ü_ūū÷˙_˙Įƒ˙;Ā˙ļ˧”wā˙M˙â˙íāUđ˙z&õÍkf˛3I8Ađz’ą %T$С ŗžîÖW[âsQäXÛ&j?TÉYŸ˙orœŲŽįãđ˙đøŋüŸ ū?ū/˙€˙y"¨p"ÎÄČ:Ąô€˙ĄßøvØD4‘ Ŗ˙̈Æjb–9üß ū_ ūĮāO˛Ítąŧ6—{ļo1ˇÛ˙§ĻWÎÉ˙9ÎWęéķÁ˙‰ā˙EÄaŽq™ēŠ•zū/˙?˙§˙Ī ø?ü ü¯Ë* zÁ˙ąā˙Ķāđ˙eæÕâëûÃ;ĻķåMđø?˜ “~¯+˙Ũåûį æŨlŦQRģđÂW‘ᇉĸ˛ĸyœöxMnß×DŽ´ÆŠë˙û øßüü?üīūī$ŖˇiS<“8ØAZûüŋü¯ū?JėUĪƒ{Á˙6ā˙đ)™ëčŌđũ€˙×û„ƒDęĘf.Žy§ŋü˙›st{xtąęĩ!SsNöi(ø>ŗ8ķk6ņ>ø0øßüŋüŸ ūˇ˙ŗŦZæđ˙2đ˙hđŋø_ü?üŋ üßÅ<=˙§0ÍU˜âmā5ÆųD3¤åā‚ß=oĀēCæAžė‘žRQlú;NŪ ‚[ã~”;1üūŋ ū÷˙K‚˙§ ø˙ø˙'øøß‡ĖŧŽū? ūūˇ˙K€˙‚˙ÅĀ˙[Ā˙„čūū˙sd÷ SđøŋŠ,‰>OFLx͕,älG”€˙•9cËW§Gô­|Á˙ÁÄÔSšė”ūÍyČ*€˙g€˙Eāđ˙đ˙9vÜę>đŋ øŸ€˙ ø?ø3ßs^ ø˙6øßüßūw˙ë2UĮK˜Ä’"fķR?fôØÃ‚;ÛŊ‘—~ äŌd­|ė¯}¨8Iä5eŽčŨ¤‰ÜėWÖ\‚ƒ+ø?ƒ{vØ ü_ū÷˙Ëø?üī ūŋūßBĻŲm#ËĸÅȆģēūĮā˙ ā˙CdŦęđ˙đÖ˙˙˙ß˙́˙§Ī ­dĸÃ]q—ãVfús •œÚšŗ\bÆ3ĸrã;øÎå! ČĘĸ>ĸÅV°:WãÁ˙:ā˙tđ˙|đ˙kđøŋ ü?ü΁˙7€˙O1ŋû+Á˙õāô/ūū˙÷ƒ˙­™@ũfĘÉł¤™įĸF6YÖĢŽ­MžöM4S¨#ē˜eĪ™x?âÂũgs5ĮswE™ŠSÁ˙'Á˙ā˙Cā˙Ûā˙$đ˙kĸũĘüüŸ ū_3āđ˙,đ˙đŋø˙0ø?ü˙ü/"Ûx đ˙Yđŋøßа+ęš÷‚Z.¸n§úü_ČLųNÄlw›ē…Äzņ2Ģߍxˆ1“[Ā˙ ā˙fđ˙!đʀ˙Á˙§YÕϝā˙đ$øßü?đw–~ū?ūßË4ų´2yG›™ˆė̌Ũ°'Œäü5‚jĒ-X˛¤Ķ:_åq­ŪM,ŌņŲ+*(5â4žÛs~}ģĀ˙߸kd1÷§c)ø˙ø'øŋtĀ˙GˆBŗ1øŋü˙ ü˙üŋ üŋüü¯Nf‰n˙­Č8WU‡’IÖkÉ,×eāžė›üík27ô‘õå’Užp†÷ö€˙]š9’n܍āšÄ<ā ą ¨%ÖkĮ’=Q…‚ģ߂˙‚˙? ø>ø˙ øßüßūo˙Ofå6ŧc%—ž˙_đ˙đ.øƒ˙§˙w2Ĩ%/Á˙nĖ˛ë‰ŒŌŦm‚ÖĘ ÁŪ\ëâĒŠ˙æ˙ĀXQ„n''ũî į6]•ËØ3—k6ŪÆ}¸1ü_ūī˙đ˙6đŋ øŋŽLÚ­ ūū˙ūßūŸū&7JĀ˙3ˆfÅCđ˙]đ˙=2-m'YõaøŸ˙Ÿ˙ëיvd/ÍUqŖ¸Y*—Ā˙5œūŗ\…øĸ“é ū˙Û˙V?škMÖÜžüŸ5ā˙Cā˙Bđ˙Cđ˙ đŋ/ø?üīūW˙˙›€˙‹Ā˙ƒ˙ö˙ÃOā˙DĻÆé+“ŧõ3ã?‡{ Vđ xĻāØā6ĒzšĩÂÖÍĩ×UŪ‹<+REOˇžãė.×qqŸ›Á˙_¸Įéā˙ˇāKđ˙š˙g˙3Áõ'Á˙߈pŪT˛tˆ:ø?øo˙/} ū!BųËd\įĸŧ¤—höĪ˙ŋ†ęūß ūˇ˙O!ļBÛéÃupëWÜā†vĻsÝåbš&ŎĄā˙búæ3YfÅŗIˇŲQ›ŽŗSrž ø?üß ū?ū¯˙˙¯˙ Ā˙žā÷˙˙/đ%øßüüŋ ž¸1[đ6 ŧ) ūˇ˙ë‘i!Š\ī7.lķunĖMđ1ˇYe=÷mĐđ˙]đŋ.™n˛ŒlŨ8ŠičĢ˙{ ø;ø?ü΁˙7€˙÷‚˙ׂ˙O‚˙W‚˙“Á˙§˙˙7€˙ĩ˜ jMāE&˛ë3ëÄFĻõƒ€|˙,pŋžĮēZqdÍĪëcEģĸDYÃ8Õ/nûO7Ž ßškĶÜĖũtj˙ƒ˙g€˙üŸū"m–ā˙Hđ˙đ˙Cđ1øŋ üŸ ūˇ˙û˙MĀ˙ˆūŽ1dρ-ø? üŋü΁˙7“…9dČ÷|.#kgöŗüŸĀŲŦ_Ę]ņķ%ÆđŊØęąø=–ėNĒąōū×˙ßđŋå€˙ÕÁ˙Õā˙LVéæ/đ˙Yđ˙Ađ˙Ö˙k‚˙w˙UĀ˙ŠLĢķKđŋ8sŧ˜cVŲ)1*~ Ú?;(Yŋũ9×JoKaío™įĸƒz5œXŲ=nņ„“\ę–ŖÜqîíRü˙ üŸū4ā˙đ˙!ĸæe ū¯!vĻ3Á˙[Á˙ā˙pđ˙Eđŋø?ŠŒ‘ž ū˙ūO˙"+–˙ܙā˙'$hsY™XG´ô9^o$ˇ@é3øß†û˛‹+˜ą–Œy* ūwũÛ˙ÂîíÖxŲđ˙‘˙ođ˙%đ˙đø%ø_ž•Nx ū7˙K€˙÷˙‹˙ãÁ˙Bđ? ū0žŖ™ņ=/O_Ģ fžhXn­ī~ŖöÜY‘Ģå'ŅŠNNX:“‹ņpôú`îAŦô¨ūā˙Áā˙Ëū¯˙Ÿ˙÷˙ƒ ë-ū'ā˙{ū'‹÷>˙+ƒ˙mÁ˙-ā˙›āJ\ô—“—5Á˙SĀ˙ōdķō{ĔËõüp^ˇæqJÍû8ÅÁĻ\xÅ{"WįAėzĮķėLâÔ°ŸĖ “ØĄŊüß2ā°L:ūß ūĪdGyŋ˙[˙åÁ˙%āÉ˙?˙OđŋĶ)ÛĪÔí`˛ÛĀ˙Ë븟‚‹EĶ,xĨ˜uäčŅĩ{ÎîYž”Õ寝5ä‚w‚˙ë¸nM"ņBwĀ˙Aûŋwø |¸ˆŒ S˙˙ŋ iāū?ū7˙+˙§˙ׁ˙Oƒ˙CĀ˙-dsÄDđ˙lđŋ6ņũÚAėōY"ļĖž;Ēž›ĶYbĪIoYĮ­ō|ĘŊNq'ŦP› ß^2-åņŨ×Č$Č‚˙ ø)øø?üŋü?ü?üŋüo ūßūw˙›ü‹˙åÁ˙ ākđŋ -õƒq8ŗˆ‘›ŗOĐ`Õ,Ø˛@×ēIÖĄ&īëY‘ō^/QōĘUܰÍÉ܆?SšÜ¤}Üåh]ގ‹˙o˙ÃÚø8āø˙%Q¯Q'†áSĀ˙Māđ˙đøß ü‚˜į€˙sÁ˙yā˙Äî÷$âņsø˙+ø˙ŲoΧkīČđč.oWĮ¨ûƒ˙ÎZawno&1ˆđ$v<øŋ•øŌõė>ū7˙Ÿ˙‚˙Cü/ū×˙ī˙_˙Į‚˙]Ā˙Jא?oÁ˙ŗÁ˙OÁ˙Á˙+™3ŋ˜øģŒįępfdãlAˇą›āđ‘'1­Ģŧ¸„Ú÷ EĸŨEEß^$qޞãĀ˙íÜšÄŊŧmū˙€˙+üūo˙įŊcbËĒ€˙΃˙ƒ˙—ƒ˙7‚˙7Aäuđ˙$đŅĸ Á˙ā˙tđ˙5rpÕ đ˙Râ~߃čžÄÜųáQÜbÅĄā˙‰œĻD7—.IÔīk€˙÷ƒ˙}ˆG6l_ÎŒÖ˙û€˙ Ā˙Küüüīūo˙ī˙×0ŋJ?€˙;Á˙Ž˙âkæÚĻû ×˙üČėQúÄLBę‚×úī)!ū}ÛÛÖĶãڒ]e"Gb'j×įŦb/s‘ßNsœš&×ķi$tTüŸ ūO˙Į€˙ÛÁ˙ø_DÆ1ōāūw˙ŗū_ū˙/˙˙Ÿ˙¯˙!‹ē–‚˙ëĀ˙nā˙Ĩd›I9ąlgšg{pģ}9ĩ %œ|ŋ%\ÕI¤Ŗ Ū/E,=^‡šõąÎJ„<˙‡€˙;Ā˙1ā˙Ü˙‡ƒ˙ëĀ˙˛ā˙đ˙Fđ0ø<øŋüŸ:ā˙ëā˙,đŋ:ø_ üoĮ¯ĩ1bÃęåûäķnåY'pĒ]ØŪ"2ü”.*+ūČiOXĘíëeÁ˙\§„$ø˙î€˙ø_ū×˙‘!ų˛ā˙íā˙đ:Y#bÁ˙›ČĻo/Á˙sÁ˙KĀ˙Ąā9ĸÚ)C&÷˙÷˙“ŗÄ˙öŋÛubŸD¤.ģrqĩ8ũŖ8Š!fœŗ\"÷0æąę ūW$ėŗdˇô{fquø˙4k°ę'øßüŋü ūgĀ˙Zā˙Gā˙Šā đŋķmÂGđŋø%øŸ˙—‚˙=Á˙%ā˙ŲLLk!ã|<ü/'¸ā›"đ͋˛î ŠŅ7&"…‹Dą™ß9yÃ&n͚~đ˙YîŌjyîËâ"đ‘;˙ü¯;ā˙v"7wø?üßū?ūoĮȂ˙͈{ÄY2Ágbîpü? üßū!ļW‡‘5RSĀ˙ā˙ đYrô%1ūWâWÎŲĒ)rR5=œąŦĪz[ĸo ū7˙/&;įÖŧū_ ū/˙o˙Ãx4V˙_ącŪ}˙Į˙3Ā˙ā˙ ā˙Ę˙׃˙5Ā˙ÕāuæĘįëā ūˇe6/1gFkīÜŲf,ˆl äN†Z=xrŧöápo‘—qč97ûu—°@…k8ĨÃ= S˙?˙Ī!Ō“ø?üETlÕÁ˙ü˙üīū_>āâ´ĩü¯ūˇ˙o%c ˙Į‘e•^ÄįŽ%øø ø™čkÉ]Y5Š[™îūWįÔÎKp‰Y—ˆĘ­)ā˙ãā˙‘deÉLĸeiÂę´Į˙ƒ˙˙mĀ˙÷Ā˙)ā˙đ˙đ1ø>ø? üŋüŸū÷ßũxüá§|gŌ˙œaõ3S’į>Ŧkd6ÎMũjŊzNImr—P4sZ‚čbļ3gâãąhp5'ˏ;NSĀ˙Qā˙Ŗā˙íā˙]ā˙įā˙3d˜‹!ø?žXŨ˛˙˙˙Įø0Y6Ŗü?šče?˙o˙ŋ%&§ŨˆcŽ ø5ø˙øß–xŨ;IXˇîŊUL¯q!÷8ŲÕķ¸S-"† øŸ˙ī!ŗÅ‚Éšß̘ĮYwÁ˙āūßūđŋ7ø?üü_ū÷˙#æķŗž˙Ûø˙(ø&øøøßąSĖb$įE ĒšÁGë|ĨđÚ{fFĮ ŦƒĶxÄųũÍՁ˙?sŽO˙Wƒ˙—˙ŗüŸū_ū˙AŒíÉŧeā˙Rđ"ė›l˛ŌŽüŋ‡LIŊ ū/˙€˙ÛÁ˙ČúWāßŋũŋģ‘Ė ųNd×ørÉĘÛ9Þaā˙HnŽt0w#¤Š˜ī:ūŸū¯˙ĪÜũŠūŸū2āđŋ/ø_üßū¯˙˙w‚˙/‚˙O øßüü¯ū—˙ë€˙×0Į">2Ë:3Jö ‚Ö3^‚ŊŲyÖ÷s¯ß‚˙ƒ-D8é÷ī8ˇ!\Æ^ đ˙'îÃÍ1ā˙›āŽH6öø? üŋŒ˙{“*sČ ;üū ū_ūw&–Cnƒ˙cÁ˙/‰;üßūī%ßäÁ˙ĩ$đÜOâ:cEžrUĩk¸YʓÁ˙!œ>ĖI…$!:YkĀ˙Ûūöŋ@‡+˜ôŒ5Ÿuü|Ā˙ā˙4đ'Ģĩđ;ø>øßü¯ĪJëõ˙—€˙ÕĀ˙ąĖ ‹ˇūįÁ˙öā˙pđ˙aÆëÄ fėū"Áƒ"IÁąA‘ÕSŦÖUÕ^i)ōŦR=Ũ~•ŗģ:…‹û’ĖõŽåīr!r†,ø_üŸ2ā˙Rđ?˙ˇŨ[3Á˙•ā_đõ€˙ËÁ˙úāOđ˙1đø?•XÅx€˙߃˙o‚˙įƒ˙5Ȇ-EÄđúŽĢ°ˆ[īz ü/É ?|˜‹9˙š(^‡2ô-øŋž,ŗŽ fÖąŖļƂ˙oøø˙ø?üü/ūŸ ūū_ūŸôˇ˙S?ƒ˙§ø˙øø_›É2le‚/\e˜ŸžfĮ ōÄ´V÷;Ŧ÷NYyGB$LjÕ=˙ĀM^|ƒ ũÅU~ŗįnVځ˙;Á˙~āÛ˙˙˙?˙‡‚˙‹G’ā˙Dđ˙§ŋũŋö-øß üŋüū˙ûŖ3Ä!Ė‹lĩ”˙€˙'‘û‘i•¸Ū/Ú°MūܘĮŠÜ ƒáÜæK¸o˛YdęŧHÂĖ?CĻ›n%[×ĪcÅZÁ˙Süīū÷˙ƒ˙]Á˙îā˙Åā˙@đ˙tđ˙đŋīŋķŋ)Ķ˙ üīÃĖĪ˙ģ1ŗŽ{€˙Õ¤¯\āŪŪk]-_WķķYĢHc‰(ëL§zēˆÛū[ ü˙‘k=˜ûéœūĪ˙O˙đ)øŋüü˙‚ĖIŨDVGŒ˙_˙Įƒ˙ב96wÁ˙âdäšĮā˙ˇā˙Cā˙sÄķŗ øŸ€˙ČŪɂĶydHßQ.#Ŗ–3ûū÷äl6āŽTãō“ā˙“ā˙B˛;ų€ÕŽūŗŦų‹/ā˙+ū×đŋø˙4ø˙ø˙!ø?üŋüo5ā đ˙JæÉĩ>æÖĨ/ā˙DĻŧgøßœYe› ū/h˙8C<¯Íúmī+ŊéjˉNtåÄ*Fr‹õåšÔ­3Ā˙7šˇËĸɐáŖÁ˙‘ā˙æ˙Wƒ˙‹Á˙;‰ūįáāü_ ūŋ ūßū#Ž+ËĀ˙ãÁ˙Ūdøqđ?ú­˙ß˙Įƒ˙ŋƒ˙ĢČʄoD{‘"Į_Ā-6ƒ“ųVĀ}ŊŽ+°_FÆ<ūĮûj.÷Āü'kŧÜüŋĀ˙îū§ā˙Gā/đŋĢp˙ø˙ķsíOæ“Ásđ˙Úņŋ;ø_† ogR Ž2žw™ņwī žžŧ!H˜á.Đ:Öm­ī:ļöœī‘Ģ`čΠšœ°Ŧ’‹ņ“˙į€˙3ĄGŊ@†Ŧ}ū/đ˙đžËüßūßBR%ëGđŋ ø˙™zАčÆŨ˙—‘Qeã‰E’1ø˙%øßü?‹\Ü ū7"Ļާq^Ũŋ8Ĩ‹;Ā˙?¸đĘ@"w6üŋüˆSc5™Q#ÎũqüjĀ˙ķĀ˙eā˙­ā˙8đø4;´åø˙Ķß|uĀ˙MāĨŋkHß3đ øø-ęYËX ~<Ú,(~`×=֑ęĩ{ZE–)Eĸšĸ2nŧįFđ˙Žb‰+ø?ü5ā˙eūW˙ûƒ˙‚˙—s‰ÄQŌüīū_ ū&ŽOž’.dō˜āSđøŋĖ‹‚˙cÁ˙^āeđŋąË›MĖ2ÜŅ‘šœÎbiđ˙%nÕ/îuZ&øŋüŋ‹LK=C|÷|f†cMgw øøßüŸūŸū7˙O˙/˙́˙Á˙#Á˙j˙Ũ˙ˇēĀ˙‘ĖEĪLá‚BđŋãP~‡‘›+h°<$Ø2/ÃēIúKMžė‘ō~eQōęMܰ-]܆ū\n˛ø?ëíū÷'r.ŋÁ˙;ü‘Čˎ˙/˙GYš ø?ü˙üŋžŦöūūgĀ˙&ā˙Ĩā˙2›ū/˙_'{N؁˙/ƒ˙§öodøŅr.Ī˙%ĮŒč˙æŦ‡NæÎí'GœĀ˙;ˆY•ø˙Ŗ¨gæAđ˙\đ5øßüŋåo˙ë˙˙/˙Ÿ˙ûƒ˙Ļīü˙_˙˙/˙΃˙‡€˙cÁ˙ķĪUJĖČĄ ÛHCp8"K 6zŠUŪßÚ÷ŠŸDģKŊĘãåb¸ä•áā˙ŗā˙~đ-ø%ø?yĀ˙GÁ˙ŸĀ˙ĪÁ˙Ā˙GČōû*d#5˙ۀ˙-Ā˙CÁ˙[ˆVdøßü/"ė+âZ¤Lü–h˙5Ā˙VÄũ^Ņ}aʝ–Î-˛“=ĮiJIpā@õ ø?üoG֊]#ƒqk0ĻüŋüŸ ūŸ;ā˙\đ˙eđŋøŋüŋüŸūO˙ׂ˙-ūÅ˙’ā˙Ķā˙åāUfΰ2fĢ x­W$H9h.С1°žž ˛ļdŠČ‘‰Ú¤pVq,Ųģü΁˙kĀ˙Žā˙đ˙Ađ0øŋüß ū˙ ū_Ngƃ˙kĀ˙Ûü¯ūˇ 6áŸĀ˙ā˙`đ)1Ë˙ß&^3ÔÁ˙~ā˙šā˙ÁIJm÷l×ln÷N5Nmüiđ˙e.¸Æ“HQđ˙zđ˙Aâ0īqąúÍJ.˙¯˙_˙‡€˙“üŋüŸÃĒ:˙7˙íĀ˙ËÁ˙ōā˙ā˙ ū˙ˇ0•ÅLÎÕB&L*‹ļG‚˙ĩå{. æŨgŦÜWģđvšČđ̞¨Ŧ4ŽĶÖ;ÆíëëáJkw€˙ņ‰3ü¯ ū/˙€˙‚˙…ā˙:bj7‚8L—˙ī˙‡€˙Ā˙rÄ^åø QûÔ ūgĀ˙Ȝ#ģÉÆÖšā<ā˙ÄūD‘jËŝŋĘéĮ=˙{rÎCÆsĩ‚˙’ŠŲą„}>üŸČ,ž4üīūū7˙Ī˙‚˙ Á˙ƒĀ˙—Ā˙ãYŲž—ŦDŲsđ=ķqÉ đ˙tđøøßüŋ‚)Ūü?qŽŗa†4› .ø,øæÜˇî›XŖŋtˆH!đĢ(6[Š“ŸŌĪ­Yģ•;uÄü”û˛dø? üßū—đø6ø˙*10˙˙$+]åĀ1:ā=đ˙đ"øß€ŒZsü˙üßū?ūĪ˙_˙ƒ˙įƒ˙ČŨVŽÄw8gĢâū/äŒMļr\Ŗ%Ņœ Ķß?˙g’ƒ­š5×ĩĀ˙ģĀ˙éā˙ā!øŋüü˙üŋüü?ü?ü<ā˙\đŋø?…š#ūüŠŠģÉ$w1›]>1Ŗĩļ îly+ˆlöČ%‰Y=¸Ą_ûPĩVäef&z7ņ›ũVüüŸ ūŋDäWŠ€˙Á˙܀˙ŗÁ˙yā˙ådÂD92mēø?üõˇ˙n‚˙ˇvnĢōüŋüŋšÂĀÁ•OĀ˙ąā! Ž#ŸA&ú˜qWVLáVĻnäĖ÷qjĻp‰ŲQDåļø?ƒ˜*<'+K‰–ÉV§c;;Åâ=ø˙øß ü ü ū¯˙Ģ‚˙3Ā˙æā˙íāuđ$øđŋø?ü?üŋüoūĮĖ”$Ąāƒg  ŗAV`$´ˇ^m×[›|ķ h&V]ĖuįL|Ā˙oÁ˙íā˙Vđ˙đøßüŋü˙üüŋ ü˙ü_@œü—˙[˙öä#đ˙i‚õÁ˙ā'ĸŽÄ‚˙ÛÁ˙āB‚‡€˙ÍÁ˙b„uíāŪ[rÁ$Ķ8xü˙ˆ °P$bv/ˆÍŲąā92[<žŦų1‡y|Úüŋ ü_ūß ūđ˙:đŋøŋüüŋü?üoĀ˙Cüŋü¯Ę4íüĘäE}d"NE2vCW3’sĢÕĸ“‚%‹Z­ķ‡ÖęŊ~-ŌŲĩNTPáÃiŧä9ŋŸu\Q­'wÆ‚˙˙ Ā˙ĶĀ˙'üŸ ūO"šZŗĀ˙™ā˙zđ˙đė›}ā˙xđŋ6ø-øøŋLX“YnkĀ˙…ā˙Ē˙;€˙‡Y÷ĩ\˛Ō+ÎđNøŋ‘›3¨ƒģqč+1ßŊˆØøŋ ֞ĶČžČ¯‚žIwĀ˙ŖĀ˙7üoūßūū¯˙Ÿd•6ô‚˙9đ&ø̀˙G€˙ŊÁ˙ŠĖ-ûĖ%Š›Liņđ˙!fŲ5eFifƒ ĩÂP°7ë‡õũĢΌžũõëģū‰â˙ŅņĪûëū{XZü¯Žæ?É÷ŸŧĖĖė˙|ž˙ŋŋn‰ĶôÅļ‹ũ˙Ęûëūŗ|˙Õ÷×ũķũīzĨų˙wß_÷_Ë÷Īûëū‰âŸø'ū‰˙;ũ6ē†ĩü 7Ē‘“øXô$îĒPWiŦĐ0žR¨šËN¨xT$”\÷Õũ™ŒÜYu66ۊŗģ˙…Xŧ?G^FSöã%ę÷šE ×ũĸ•o{hꠍtV” !üLÍ7’;ī1ÃËĖØ´ëŠėåŽtöŦ ˛ہVÛĖ@K'ŨFZ$Ø˛ÕØSIÎėwà 4{Z9ǰ>‚ēöŪBSrP¤*ö×/CN?}‘ĩĮF$?û5;%A—ÔYPK‡uôø-´x}Í=üƒÆœNŖ'ĢbéáˆStWë]:.ņ‘Ŧ;Tåü:g•ĩy§AįúäĶ] …ôø–ƒ´-NŠÎĒIcŸŦBûŠÄPˇD‰pø_ĄtÜÔY'†ø—‡PĖ— H}ķ &9s0šg¨M§†‹SW…ÔOį8MxdCĪlîĻüķ8ÚĻ:š^œ¸„VŸBũo? v×Ѓ™+šôÁž•ORaĢS$9ū 2øÎ ŋ Q˜^Š _‰ŧ'VŖšÖ&ČŧŽ-ȟ‚ĸZ×"î‡=ēZ6 qK-QšüY”ÉėD!‹îĄ}ĶĶĐBądö‰Fˇ<Ķ‘ôÜhĸģ˛hæhUš™ū’Q˧ŠzGhBŪ zđÎ:4$”|úšJTp3Y`ũDHb3= Ų{mĨlí(_vĖ9ę9ۅë!íK×QՇ“¨đî5”å4Ú8°ŠĖI#ˇ˜ôŨ~Oímxš´ĘšöHÔĶį/˛č÷&úĶû$?Ü>‹×PžĮĢúņ AŧĖÛ¯ô‘”͍€Âĸ>БC™ņ¯Ũ҇Û(iA3:uķĘŨ4mÛ[ā"˜D-īė§nŗyēSĨ†Ō@ úõn/‰­xEuO^yÜP~ÂÍûüÄĘ^^wéV^ãŒ'/•hGīFԚ*O§nI÷ ĄŠ…ŋE(†ũ`c€ėôÃŅao Ä/ŗA¯Õ Ål|З[•čÖÔ@Ô-žŨ‘ߊî'ī@w'ųĄûģīŖK¯‡ĄĒ[į‘ˆx!ꩀBģ& 1ĢlŲĖÉjÄ|ĸ3ų’ F­ŧŖž;¨OĶkē6ŋšœš™Ļ(ÚԜŅît֛‹ä'ÎŲgÍöíFŽ.(âĻ&Jģ}qMgPĪK}2ŪĮ˜ÍžOŅT˙ (õŠ8ĸšŅm[5ß°•PĩjŠŸ¤‡’žĐŌējúád6¯ÜÜʏ2åÕ{öķãUoķlįiŪtÄV^_;œß3ŽWÜd@ÖĻ…ÛÛ艝ŋŠãĮ_äyŅ/FŨ. I-LCS¯§ĄŨAč”Ez8/N(i9X(ž!=×ü7Öĸžz3ô{ŌrĄXžũ/AŨĸ.Tđ§UÍŽj˛ĩP oÛljČTā~r4Ũ˜ŧÄK¨‰į&j´¯€n=…îW*§šÍî´Ú9Œ–äŊ ģ6ē“O1Ãí\4[LĻ#Ī_—ĐéqY¨ūĐRôė×áPû^ļ]@sŊûQŒž9jŦ‘DwCšĐĶŦ¨zØ[4æe5 ÜEWČŽŖU­"^fė}^›Ôņ^§ķš/)¯§ú€j7đĖũ^?ŗ‘§Wď2)§īÜĸ™Æ4Îí$]~8Œ$ĒÚ˛õqW‘ÆÕÅČrĖbļXĨeGwí „b™ųB™ĶˇĐwƒEčD–PvūmĄŧķ áÁk…2ŗĄû{8tIW ņ‹–ĸķ2´}į]öIČ5KÂbōÍg8ĩ~x€zfŖAņö4ąY‡Ö4ÛŅ §ÆĐķ2†´RoõPą§âķČ´ž[Ŧlc˛°‹G'Ÿ˙@M‰EčklPãbûiŧ%:¸y*ŖŒî;œFkQÃē•(u?ösįëlĒ4—fŊפŊüčŊĒü¤4^ët6¯Ž*ÎOH¯āÍUjy‹­ø)"K^oW/ų”Ą ĒčąŨûčú ŖÉņą‹ŲSsoĄ!ŽIČZO„vĄR‡=čîŧ1¨oé0ôķczĄkƒŪœ"ÂÁözB9‹ũBÉîBŠwßŅ“ę3čÆČ,t+ŗ]ē9­W„gę‹L‡ÖøšĶ+?×Ņģ+ŅG‚$ÚŦ‹–älĸ‹ŅFōôö RwkŲ¸í5{st1 :„Ũõõįę~z…æĪ3@į=Đõ_ʨĮž]õĘ@j[PjËp„œōØØ)M$î~-Ũ~~2Ŋ횞—Íöã‡|Jæå6„ķƒt-x…Q¯ų‘īŒøqēåŧŪܧŧÎU^Js'mŲ Cĩ‡Ķ%?Αnũ•ÖqŪíė÷Mŋ‘UĮL´Fé;Ęiy‰Ї#^LÕ^ύŠLĩo¨@OØNĄd}<ú‘é†~šĐŗŧ¨=%]Ē›ˆoZĒÂvÜ>ɝgF-"_Ķä%hKT;}ayž~ Qĸŋœ†Ō+VŌ÷¯BiOCMŧe@'gpTú°+ųžėë#üO,@õg…’J„Æĸ…ãŠPžAē;Ŋ‰nFīxĄˇCRQõ4üæ)ŌĮŦîåH:Ōģ—>ôĄͯčĨ=V´CĨŒŪļ—ĨĨJč]Ķ‹ôwĮ'^ņe ¯°ĸŸ>LGšWčÉÃĨtb˙7rrîĻÚhÃ/ė—ūČĮ#úŅ€˛îBÅ]Ū(ąŖĨ˜\CĨļgö]7C/¸dôŠûz˛QuĪIFk‡ĸú~{”ųųrŽ×a[{šļ1TãŌHšēîŊÛmÃKß?ĘģŨČ+ūžÄkTNäÕW‹ņĘŠQôZÎ,ē[ŌjŊ›E.&plĖĩƒ(¸åē–´Q8ėÄFĄĩÔXÖuÁ,4õ›<ĘÚēŨ='&”iJ¨ŦE-/_ĸ˛°2”"+†ŧK$Ļ^t’ÚęĢŦEÞČŌÃëüi´z-UrĻĨĶÚ+iôãˇôĮ‡dz=)šm}G#/ Ŗ3šČÁ¸Ûė„ûŌHęę&´Íæ:ipåÖOG™VËБÕīPTvŠ9ĀĄBæ:ßzĩ D×ėäQ‡×tŽv*Ēėr’ÎĄø‰šČo`‹ŧ·ę^lĄ§§ßĨ¯lķ*áNŧVY(?鰈74ŠäÍL4ųąfđ*hšķCĒå'Nd‚kØÛL Új{õ˛*øÖ ŠFä¤i›sS ­ū$…Ē— A?öŒĘhgĸ'§uЅÉr¨ÉŨUj°I:‹ˆAõŌb¸ ;J™‹ítÑz’sϧ|žSō}í’âéÛ5BzÅGšÆĻtęĘ1TâĀ RâƜX[Į~÷)B32ĸC)cQÁÎltĘÄ4 FnrĢ‘—Z<а"¨đ¨*ĒŨŽr‡čĄęz(īÍ'TĻuÅ,Œŧ"V##šÃĻ\IdėM¨IŦ*ÍHËĻ)đ#^6ķz ]ŧÉŠ<Ū\>”Ÿb~ƒ×W›ÎK…͊ŅötF¸-ÉKīg阴Hū3:3ī)úÃˇ?ū$ĸ'ļ:â T6D"yôavú•g…n´ŧCt‹#j:ĩE­ËBŗ,ļ°Īže3 ם ‰)¨ĖŽÔnVMœ^@ķ,ŽĐŗŖ,čÃ™ô†… -UœNËŠé¸ĢäčpgbĄˇK? ÎChÖmä-‰â´ŖQ\ûO4ëÕ4Jņ2ĖG ,ŋ!÷éČ{Ņp´ŗ6ËíAĮō dŋģ(čí d:ށ­¯õfļŋĩ$ጿP“š!4pŦmßÉ+~YΏûZ1å~œį-GßæZÛyåĒzÚ,“Geדö3ėí™ûP°Â(tūÃGĄøÉÛBãm¤Ûy6ģH¯­ÖČCEûTĐ=—ߨĢÎUâÄ}§¨ä¨?Ę4ŗDÁŗV §Tiö÷VNîš69įÁQTģ”yYIc¯@ŋnāKë_´ŌüQyÔņë Ē3Ŗœ´FN„;\f-æŗ.kö°ßu×ĸ…Ŗį‰ĩČ˙ęj´ÔmySÄ~ž„äôß yq]Äē/Fsn\Gī`ŊBČś(.k ^ ‰Æ‰oc÷Ū^Mä&]§’gR'ûkÔ#؟rÎîüā׹ŧVŅ~ŒŠ*oĀĩō8į6ÄwōŖ¸úĖ+›ĖÖ$iŖ‘ē‹$Jē|ŨÜ*T´ņšžĄëöŋ'AlHŊ%Zh¤RcŽĸ ÁOûg!:õd -mDšãÛPĻzJēöŲž^ÍnûUDļë= ēĨÎtEÂ-jmķZÜÛJ}žëPģûKI‹Ū2škäßb÷t9ąīÚ+Аˇ–Č,)-6úŒ–nûˆ–ÉX#Á¤>e*qÅŠûĸņą.ČŠÉŲŦC†žËĐÜŨb(Ôô#ōk=€EU!Å :Ŧ×įlŽhÂSŌv܄ÎǝĸĄÖ‡č•{üpÛ;ŧöå0^kņ>Ū°âo??œŸ˛’×kV¤_S’hĐÉĸũŽ ŲĘ4dPoÍRáHã ĄõĨM4~L(5zD´›Ž˛‰ŪKģÚ°ĘÅ["žņęė>ŠHMĒ~ U„,FūuėÃ{ë¸\ŸOäģÜ~Ē۝D‡jŪ"7ö Ļ“vūĸ˛ŽķČÆˆbÁÄŗ1ėžK’ėŸtd<øšÁÕ#˙§AhvúV4ĮtbâĒĐl/k4ķō~¤ģ iWŊEŖŸ+ĄY=[‘˛Ģ5›f¨ËŪ*|‰f/ëBfs´Y~Ô v˛'¸gŦÍõŦGîqĸŽ/&ĐŗŊôÃ÷,^ąÔîû…šãĮ8ۏKāc[yM['ú6Z–†Ŋᒕ´ŧ#_IAōÖ U—' Ķms¨ÁŊ äūŧ…DkØ9ÆĶ~ZŽß$ĸ6§‘čæŊ0tÍëj[S‰÷¤Ą¸ÔHĩP‰‰Œl /§ŌåT5î2žÁ“ÚxŸŖŠYĢIæĩ@.ų¸ ģÆâ,ûxŲīÅČõ3šįhŒ Ü$ĐäōˆŲ(‹,ŸĪC:ŽwИ–=ėĮŸ/Ųg—Y.<„].ųZ0v¤{Rrû>RÍˆLe:Ķ?pz6ûIÚŊ]ä‚ŅjĒ6j6 (¤ÅĖ=ÚŖ¤ĮK—čķÃ$ôyņī;ųAõļüØ3 .~åGžôĻ÷6gŌĀÅwĖ lÄPāé7¨Ņ#ũļŌĒ<|"Ô{ Ox ;ƒ43ã3k¸ĮĐķfŠčČĀJzôĄm_WĶį‹žĶ/×čG‡ ôIėdzÕĶûÚĩ´7ī ũĩå m—ųBÃdpSŌôŅ^ƒ ¨u[žPVf°pėėBSŋ0Úąī!ŊؗK˛ču4Oé ų´8€}!7ímnFŠs¨pĘîZ†üU> a&­lÔĄ'lܚQŦļøsî•SQ‰ĩ⎄ņ$Øš…œJ× ĘļŸY%į˃EvfR:ûvŽZbŨ\T [Į•% ÚȰž ŠÜ-D‹ÆŗŧÎ,V%ã%ÛKsÉėeHÍj’3ŊÂū‘¸ĀŽŲÄÜJ8Á$/ü#:j_Lŧ´gSårszüíiäč—ëčĢŗŲôšĶ/úrĩ>å”ͤŪFš4O‹ž0ųD$MĸZŗO°š.(lÕ,ÔføZ(ŲS,ųŠM8yÉ~œU?Jk4¯”íÎKxG9Ë>Ō}w{)QšO~ˆ‚\ĄŒÂ"T4ļEĒ|F*ÍƒŲ›%ˆ}tü5ûđĮ0v‡Í$v¨Ÿ5S2Īûd˗>2íĒ"'UÉõ~Ņg 7b¯yô"ũPdąÚŨ<ƒfo{„°„š-ūFėGÚ_ûŲæ uė1röG¨82^ŋé8̞o{°åQ=ŦYļģ`ęJv{R);Û –ŒŠ1ĨcĖĨi02 ­kĶĮōĢčGĩ;ô­Fm›$FĶ×yŅŊ.ųt–ĸPG‚땈íÜolt§ ŋį‚.ymJŒÜ#”šļN8"(…_™ėČΝ)á™ā/ŧļ-哞‘ģķ°‘r™fŖŲû Ũ´Qrˆ:Žc‡ÖŨČAÚ{{eōfd_<é8`ËãģE‰Û) _mNJŋ ˇž‘°U›¸gļ+ŸîŽa+Z>#ŊŽz„ Î"/d~ßšeh °ũ!ČY!YšŧC# H#p=äԊ‹DÚA>HąŠ”=(ldŠ:jŲũ.¤tcû^R‹Yōn!ЊM*ĐĖoËhÃī z?O>zvŨ¸HsbfSß;ķč˜÷ dVH$sgHYíyQ-­—‹\ŦAŲįöĸö…“ŅųFcTģŽ÷ž6‹wĶßČÛšŊáuægŅßų$Ôې=ítÔ˜ˆĻ&=FvûKPāÂ#čāŪpäom°=ĖáņyhķÎxäyô$[3(†‹K+$e:舉c(ōĨ*ę×HŌÂ›Ė™Œul/@’ hČøWH•… ÂŅŲ—(x×R´nC-˛ˇ?ëD öW ÍUįŅQåL´)â YžŊūŨœ­9:ˆ} }éÖ=G׹(4›lRRĮÖû4ö~íĩĄgczgÍeÚy_…6VQ9ZāČ4UuądßŗÜÅml™ŲR4Rē mģuĨ8=GëŖÜДÁ6|PŽ ŋæŗ&?=ô3?BŨšíC  V´ŧɍž=UJĪyEPę’AŗÎäPƒĢßjŖĪ %­.äT} mz< Ųī‰AĖ‚nä”÷íđTF*KŦ؆īųāá›ø•ļwųŠ3æķōčĻOuĩ=ÚHøØųĪ÷CĮÆĸôĩ7QŊöktÅę9:Ģ1Ĩļڠ؅(čœŌÚšŸUĒĒ"ŸŌŽQÁnęļ™Ō5OsŠĄY?iéF֘SA¨šĨČ}īVbéŖAbúŽĮ/˜ë˙°ĨļėÊl@åö—ü$Ü_ŒŽjûŖĸRčTĪ'”ŧŊyë¯@ĶĻũfߝĀ>Ž6B*Ŋ l\œaŦ§RëžQËĀFęû|͔™L+u—Ōãą48c&õˆcb*Đâų.(ôL% æ —ãųhNčk¤ˇ`1š¸lkŖmN„~åˇö}ág. åĩ+Zč-ķäëĨ$R:pŗôGeĨ5¨>wĒË+Eí+fĸ{ūŅ-Į§¨YÖWŽGË͗!Ũ ;E\ūĒ,×@ņj9âuXMgˇÎ§–ō¨zMé/IGŠP;‹atÁœ^Ęn'Ņm!‚Ž!+93˙7ĖÉSߐøÜ4äyŨiĒ"ģŅå‚áčQæTa(:{1™ŧÉ&ögŅ 6ĪüsKī.ŲtȋÔ^Фō_QO}ŋd%u[Ŋ„Z-§ ÁÚAųhv} ĢmA‡×ĖBî5~H?rÛAĖŲžV$*R’îˆØË/›ú„×;ũ˜>œ8œŽp4`}žîCûuŸĄKģŋ WÕRčķ¯*ôY¨ƒžž FŋŠvĄ3œŅũ÷ˆüTB!מ!•IWˆÚÜx:–fR“ãŖ¨•šõZMUlĨ‡ŠiRC*åĩoĶFËąô"?’6Ũ–§q+_Ķ 3ŨÉ'7Rũå™&:Æôėí`ËĮīGKŸFI–čåî!BÉ7rB‰7Ačú„u(A&fFHrõöĶã$ļŪīē¨äë%ĸyõ;9ũ{ 5öŪA}tĸč¸}jС—sß÷ą ĨŅ™(9Īŋ…âWėĨI<#ŊWœû2ą’nˇ¸EŠ)ņ3CŪņƒ:ŌTģZxŲMé;…DŨĒBɄÂ‘+…ĒÅSâ…#îu •ã넲׏ÅĄ×m;ߊbæi#‡ųÜŊģãÉû_ąT]Ī›:$Ļ͐W…´ČMŸ^y1ģļ“W–<ĪM?͏ŦCßū@´ét-˛MĻ+×#:=ā u=×DMĨŖHë„Ilô „ü„ŨčŌŪŠBŠû„ĘOĶ…2+šŅ%'! š„ˎ_E‚ÚĨhØÁė$Š7ĸ2%?.á¸.ššŨ*†÷$Ŗš\†9fˇn÷FoQJˆ :}e.Ē4Ŋ‚Â2üY^&ŸTv)Rƒ‡Ū´)ĕ×P1æ§.íĄ¯r7ĐšAŲ‹."TxŋH(&Ņ'ÔYv@hĄ‘.´J‰ĸŲ›„Ļ‹ŋ'ø/j>NĘJMEŨ˜ m÷u؛ˆ›ōZDJ<éˆkĄtęĩĪôÄĩ#ôÂS^aÃOų=ŸŸîČ;=_ÎO]pWW1ĻoøPbTEKfŅBí=ôÆãtzWō-éŪFuްÂ1ŗĖL8¨įƒpĖ…›BĨŠčƒĮ-TōģĄ|\ĐÜ`ļøJ0[J‡˛5;G0‰ÃƒZī!8=dģĪįÔĐ1(nŌ=TŸŌ…ō•w 8ī-(čH=~lU >H]ņãe­ōöé#xárzeō"R;š*—Ŗī9ķ„ZŠ6BŗŪ#™tžĐåũĄ“s‰KlNŲ÷Q8vUƒPļ<5{œAΆėų Æ|Æ 2Íx.iÜ"G'-U§EĨOéõfŪāÅ~}˙1ū EhSĮ/ĪIá'&NāÅw˙ĸ] %éÞ/ŧäYŽķ!‡7ÉUæõđú<ΆŽŽīD͇Å^Úĩmf u’…CŠ‹Đ­kĘ(+QĨfŽCIΗЂŲĐę^ųF )Úר;…Đđ§~ČđĢZ‡*iÔøã:uč#ÚąE™Üą|Ö}E­Ev4s] ?Ži“ėŪĩûũķë4Õģ˛9ŨœŠúô4…#ú„†Ķ2…BáUáfÂ%B‡‹^BŗÂÉziBÅ?]¨‡œAŅ]3Âģ3ÜĐ)N\.SIJÆ\ĨîcŌ§øą1üĒĮU|Bäu>ˇÂ›OĘ]ËûēúņhQę(zž~PöAˆĐ°Č_¸„ę wí#ÜĨ0U¸ãē^;#t6ļÚ>Ų.ģh1ęŲv…Ôžes㎠ū8hã1ëéÚŧ6úyô!ŪnŲ>\猝h[Í7 Z˗}z‡‰íįíŒĻđŠ­+čįL-^včwŪ{ōOĖä‰D|Œ¸ŋåÚ[~đjoîų}ôãļ†pžLĐŖå­pq×DĄCqĒĐ>ßXčâW-´ŋm(´ û,4ī^$œæę%d^ '[#œĀŊęŦhNc!vÎęlE¯#Ÿ EŽėåpkē>ö /g7ˆˇūæŋ ā‹‹Vũķüķ?Ī?˙gņŋõųgĶžūįųįw}˙<˙üOü˙Ä?ņOü˙a ķį‰ŦðQäĸŪ9ēĩ6ŒÖųĖKĩ›đã?:ōŖęxÍ[ymŧXÚTz•šĶuũqäTį'îÖĄÁė÷‹‰h[ęvtôå&ttS, ,ëAÎáļȲđ(š‘7éĮÄ#EKöVÁ'ļqŅ!ær’=i/ #ęŠūq *u'‰Ô ŠČ˔áT]zŨžY›ú˙(Ą;Ī> ™XZįv‹6[Ņ,ũW4ėb#u[—G'øO'?Lˆu‰ÍoĪF.íƒŅŽ‚hOËoyEųŸD1Í)čČāīhK™-˛ ėĐŦˆˆņ?‚&l˙ˆô7{°ßT‹ŲĄįX’5æ4•œ@eZ÷“Ëū äĸØ'rûōX*3§SwĪĄL×Wēôģ=ō&‹–_hϧģ†ŅõÛ4~ÆBzüņēüUĩŦk"w ¯rĪ[˛]-ŲHãËM¤a=GĒļ~8Åĩ ‹é΍ 4cŽÍēąæ…ĐS‹gŅ”ŗ…ôPąŨöú3e<ĢHN ˇ õÛ vnōöÖĘyHgæ)´ôqŠuĨüē‚ÂoFn°F˜oo‘LÍ46åä)ļ*r,Ģũā q ›LNôˇ“c&ķ…E+QĒ~4Ž;cĶOĨũĮĶAki´Á"ę#y„*Ũ%cWÍdŽx—# _W=ņ,Ljų„2tîĸĒ ÔļGˆnN CÍR(_M ũŌGžÁž(9v*r<‹ŌŊN í#O!ãĻiHf—›L=8íAB">'–Œ–9I*JĶ‘ÕŠîą2:Nž”šHž +?l¤Ą_NĶŖ“Ũhō\;Zđô=ÛĸB+nŽ ŠÅëiô™ytÛĘtĒŋū i1Ęcļ~{ŎŠ~Åļ­¸6ŒG9mPŦÔ/äˇé)b‚‡ĸĄŠė‘ŗ{™˛ Nĩ4›œō3¤3>ĒЈŪÕ4=ÅV¨Ų߁šžE#î?!C§¸ĶaÎÔeK=]€,Šæã0áĶɎ}ú•­ļąEÖųãŅI=?T¯‘‹JG'ĸܗ§QKZ.ē]'DwzĘЕÍĪŸm‚Ē'&ŖzG#t-œĸ+FoPšķY¨?éĄB$[ŪÎf÷eŨ#T—u’‚ícŠyį,ēÃâ <<”z›xŅØ™*4úQšŅxÆĢ8ŌÂ~J9U7š÷<˜Ë ëė s>6Sš>ž$KM"Â=¯‰¤u7ķĨ¨øßC‡õ8ä}8ÍØõ™¯DfmQėëøũLĪëͤķŽ*]vå,MޏJģž'Ņ~ÛˇBĩķ“QåöA[9ŧŒEÖØQ'ؓŖ&<#Œ–9{ėäc|ųx.@§—8 Ú„S¨´R´ĩŖ:Ũ]čâ|)t‹¯B_‡mDooŖ Ģtąé0ēŊŊ]Y‹PɛQ(lģšņą1ü–ęÄ1ŋ÷ĮrŨë.’ĻDOēčãZļCƒæ‡<§§ēPĸ”M[‡Ķs›&Ō|3qšũQŒoj¤c•¨0g=Ú¨M^ũŽ&y_šūˇ)ĸĖ žŗ¯ė`íĨ‡"-™t¸Í mUGķ]ĘĐĻY!ĩ)™ b†­ĸ˛Ŋ4ŦŅ6ŸĄũq1ü¸ņ+Ņ'š hWĮBĻŪ¯žJE7ĐõmsŠOK ž;“Î9RGJÎeŖ^¸ õŦį(lí^T1ŗ S7t:!ˇ¸ J…šÍĻĄO'ąP~š“Pi7úš=ŨUđFĪC~ ;Ķ|ŅŠZ´ôÉ 4îÉ/öŽÎ$AŪŲn’1ō+•s]IŦ ø'-F¯Ą-ôއ}ĒčCŸÆŽŖ=ąhãB3Zîŗ…&âįô°ĸuJxH>žR Š^w¸¨°;ėŠĮ.ėŊėÕD[öÉÜzäPT‰Bå%Īk´âw ō3yŠŨ/FŖT1ĻŖ×Pĩ$mģ]ŠŪ=0Wœ6“ˇü厎VŪa‹GÔPiãQtĪ+ÍJ~C‹Ę­fŅ(‹™ÔúG.u7˜—ĒŠÆhBgîŖŧ'cQĻ’ʞ‰Î-} 'ĩ G­PN0LŽI1Jî=Š^z† îy(!ē ™m›Įū|=™]ûų*ņĘßNÍ××Ķ={&Ķ +Ú ÉŌvKé÷‰%ŧŒ˛/w4Ž—w„ū‘ŲL?'éŌ×)Cikå|ZĐ5ú¯œDĩjn’‹ž ŒÍŸ:öũŒÜvüFŪŽČõē žšĨY,Aßl‘ëõčڑtiOXĘN?gD.œĨÔšk­_ĒÄÍ å§š ­/BŪ.j=/™&K| tķCzũŠ­1JŖ™˛âô@ītēôŪ>"ēî 8Ũ4=˙yš+Ũ<HĄÃyT×'ƒžd ĨfGÖV 'ŋlĒop@ßdŽ æŪW(Īl1ŠhtDcn|bcĻ`vˆŅ’zĻZ §tWž%͞ĘĶ&˜{ãôĪÂü`¯}‡WŌ{ÄËL?ĪK“F^ÖĨ~ŧkNËūÔ҃-ÍtJŸ4š8ŗĮ.ėF3VžD ōų(ëÔn”Zį†ō?H ÖŊKQI° ʨœ‰2cw#ŸÂl$nļŸ ņmāĒÆÆŅQ¤—ĻŪ,æĨß˙â­6Ŋ@ҝ—°GLŌčdŗ•4üg5ŊdEo$xĶÚしŦēËÍŧȋU­âĨæûņRB3úÂô"å?tĐd“V:÷ĶŌ¨;ŨčÉŖ%Ģ&ĸáMtžŋ]ŋŽģžFŊƒGĄG>I¨é^ "Ä ;´l•gËwå1[ęôHųŽ{tË5ú4Wž×?wEÕ|bWŦˇ§ƒ,–ĐčOŖhũŖ7ôŽä%z{ŨDúJû*}—)FŸžŖåæSčč‡ų‚‚Ul)û ōŪŽf×ŧAÕĮĐIÄ”Bw75 ÷–e¨ģú%Ę=ĪĄŖ#ĐϝīŅŦåÉHēŗa’Ėoô“ ø’€š‹H‡ ĸ’Á‡¨ģE&Nĸ™sVĐKfĶĮ ƒé›Ąé×M˛ŧ„˛&/ŖcEjÚŅ~>ÍũęJ}_ž#7ī*˛ÁƒUĐ´Š,Tŧúz,ž>VA?ÄZ„r"SáČxeá°ã/…b}¨MĪ E?ŌG%‘ėÎķ$&øŨb@īëÚņÔĻĄ“1ĮØÂOÎäÅģ‡ôø4JKĐ÷'Žķ2bYüā\/^nÆS^Ücm:PHŠ÷ĻõĄĄ‘/˛Ø54jČIdīUŒĪ—EQÃÆĸŦ“ķQi>‹2­ÖŖ ˇwÉ& 3mūŧMvígãúns%ƒ‡“—1 Qw'[—h’ ö éÜ,AGΊĨŗ‹ŪĶø'zaģíqč 7üĐ{x)íŸĒEáa´Qp&ĘĪĨëīļ“žÃO˜ĮQKĐāįãĐ]7ÔüpzĒž,”*Qjėęręz?ęÎø&äļ]¨rDÁ#^ A*_ÉŽ$'~_îÆ]ŠA%/،ĸ@Ē&)A“Ũ,iÛ­Cü ŠD~ˆŲ/~øÍ2^1ޘ¯ÚKKž=Ą:S☋%;j”2Ž„FĮŧCSGĢŖeīũĐ;!ōŨŖ€adŠ2HĖESÃ,sëLzj;Z#”dĢZr÷ mHä;Jfp—öę3§dö3O×įO“ēÄCtj˙Nšö,™ō%ŖiЧ¸-ôÆ\OÚy3’œ*Ĩ‡VõRg}ŌÛ­Čå/Ėe‹AÆ7æĸĜ|t%ęRÆPá„ę‹Bķ"UĄIéZĄå°B͜1čö t|´;ŦåEî ^Ԍ7ŠŧŪĮ3į"ęOŅpî<áŧ§'”Ëéũ#ūŧœøg^ŖH‹×s˜×øvœ¯ŋEĢãdŠû“q$Lw#;íeÖTŗO“ēØV…ũėŗ¯1HyFĒÉmėÙ'Ų—1ģŲ†áŅāÖUhÁË_ČĶ*äž&Ξß{žyrąœkē]be+ĻÍĨų°ĨũlÖķKėR÷"ŽĐ´“J}Rƒ^Ą‰Ē+hÆR7š.íBķU÷WŌüû5tˇėęčEŨÚLR?ĪTnÎGRüEtŦŽA-JC…˛—į u]?Ͳ…ÆįBķģEBÍɧĐķ;Ö(ņB'ÛhHŪZōô„F+/cÕÁŖųCPũŗÍČöl‚ -b;õÖzA[|ōķ?fE¯gĮĢų?ĸ}uĐŗŧ;HsméF]5ō(g?gžø•i/ Ēåm‘ČM9ƒ5{$Á6=~ÆÖÉWŗžGŨŲŖËĻ Ĩf;d(ZŒ&,‡†Ī-aSíg=˛ė’ÆīėtŨŸŦ/ëĀŽ?÷šŧqļˆ?M|v Ŗ‡ŖžŽŅÃũ_iÂũ'4úÚßõ†žqYOĶ'mĸÁĐyüŠrúk˛ö­ˇĩa 5Ō皌2ŪōčémĄÂNCĄŽšģĐČĖ[h~Wh1§PŗČõ/ŗAÕŨ_Ø_‡%I[V(Ē›ËKŒØÎ[Ũí@ 6HEšČŠß¤kÆ<¤5k¯ĐGsSø!i°–ÖķÃZĶ×/6Ķ ŗčYÉšŨÖKįošGž>~DŽ}[E{–’ÁÅkšžgßŲŖĄylôƒvYˆÛĒہŒRMæ°×6ždË4‡˛­{oŗī~˙f/>Ę:ĘqĖÚĮ#‰zŧ'Ų0ÍˆÔ ũFŽlQŖcũmŠũ&5ēįOÚēÆĢÅĐBG4įŲ\ëūˆn ëŖ3”Ô¨ÜĘvîû6 Ļ?~'ŌĒëDi&ÆčÖEOĄäÔbĄÆâQB“rOĄų\ĄĀxžpŦX z9w=ʔx†$ČRŌpW‹&ĢÅđ’'jx4­ Ų7J´Î܇ô[LįĀžKī¯ĄŸwŅĪŨô]Ņ[úqy+ŊĶL/mĸį*õiÍÅ34ėķtĒ{ԅ´Z%F?O2râƒŲŨ?ØĢĄÛŲO]æė—ģ̑ܤũIV\fņT V–8°­'Î Ų>Hũq{e_2“>Ŧ—¤ Tü"Ą’ķwԑ҉tJB*õpûLũã;idõNzzú1ZH3œčŽŊč´}†t°˜;IŲYÁŦÛØËV‹$ĐD[otdß T[؉'÷ Uö\NuLˆļ_âņ“…ãŋüDo‡ ”]ۀd[XRĄCã6Ú7vo<û4›Ō4s yEZvĻã÷ҏÎ<Ę+\Ĩ%ŸÖĶü§i%ZIų°ĘĢēŌō°Z´ā=žķ•NŪ=™ėČßRĢũæ}ú&›‡&eÕŖųáhÁnC4eV’§M,ŅģšÎc\e°G6­@Ŗ~ ‘Ãw¤Ų(ɞœŋ(îÚL^Ĩ\#‹ļV tJüSēf5ĻnsŅ(‡ķ4sÆhZ8Ų†ÆSĨį_SŊĖ-p."ЎG°ãK6ą1ŲĒ@ą¨$RŨÖ4J:Õ ĩC3„(¨@8ķĖaáäôuBņ‰ˆ3Ų‡tį&ņîyt™Ŋe´˜̘†ŽŦa+ĘWGŠRsŠyž+]Z5‘Îm3ĸķŪĻĶpƘf]ŧJķ§ĨŌŦBzjĐēq7šˇ>Š;rŦ“š×sŠ-§ ČdL Ú]LЎûO‘ËũeČžx'˛öMfûunŗéA˙?!šė‘ h JANQ÷É!Éu9ŦŠ[“W°KđgībĒž‚īp¤LÂ>ęQWMņ4IbxāKC.QŖėST:ĒÄĒŦä۝aßúNBÖíž(xö”N‡ĄĘÄ­čfÛZôÖkŸpx\…p‚Ívá”iŪBm—BĄÄ‚ Tŧö8{īP!9čCˇũĸˇŋ­äĩÛBQąø äņ´)m]ÎČĶN˛baŲöŅ—KŦ"÷­¤f­éÁÆ´`a=Åh҃÷ļŅŲbōg„—¤šĪ8U=dã^ sĨīČëÆ;´šZ 99Ė@‹Ĩĸš• и—‘ėËķė+ߤ°ĩŲVFĸÅųhé ed0Ŗ)_DCßņHIV’ņ>—ÄžĨ&Ŋ˜úÔ Ŗ‡„įiŒŅ)<ԇŽŋŠB§3TšTŸ,ÎËN:W†Æ¤4#oV %\˛AE~–¨ĄMŨ¨ŗGw$Q˙šŸBšĮēBšīģҝĐĶč:JDŅķLØKƓ3JZ4ļd}]SÆë_´G*›Qūž-h˙ŸLdĖ=`ī9;ąWW°#ŗÕ8éīkIWq"=ŧx­J™Dkĸ6ŌÃģ\é°wt„F6‰ŋžŗ>4š -™ŒĻÜ­G —!WÍ(Øx2r;táŽĢhâ›ãˆ5Ŋ†Ļ–y kJhãΛhŸX":đcÚqār>҆,5Ö#ëʧėUÃd߯j:AM—mˤû}8bh„åzå-ĸīˇĶõkŋQém[DŖNČą÷öØ ķØ§(|Ķ ”?Ë ]ĐOC´W n…UčU{2zßሞŊNAį_頚ÚũčĐ÷Lv­Ã.*YŅB‹ŸēđCRxãP%ôr™=:_t ßVƒ"õëQ˜˜ ØK‘ũ_ļĘęqņb¨Ŋģ[ZAķtÆŅÄ3!tÚ Ē×\JNnyÉm:ĢÆ†TQ$õÙĮˇ …§˙ Cú(˜˙„6îûŠvü’Dą% čX„ŠŦvD1/– ¤ų™Č;¨ŲlHA23ąAbėFãjN~pųę\C…Z§čęų&4¸ĖŠ›:ĐČQ„&–9Ō´ Ũ4Ąu,eĪĮîlÜĪŨh¨éO´Í"%¤.GÕų¨S9ĩ=ŌDįą3jYįŠÎĪFÕ[^ĸ“ŪĢŅŽe›Ø8)Sԟ“B .e͎7ĮņŖĻũAuÍŅYÉÁčD§Ę>Š‹Ēē PĻZō`˛7šęøSä]Â*ē~ür6o=x÷!mQHî/s'Kco ÚVē˛ ;´âĒ‹ČØŪ 9üqD‡RÁkķPrtJ™1ņž{PuvJė|„‚ú둋Žûŧ6I­&Ú!)$˛5›JOūN—‘ôPa1ÔCÃĘ hÁ"ķ)ÆåDĐãM´ĐK…æ|_Ní—K°qVzR û|í94S}ЏâƒÎ˜}G O›ĐY10›Ø#Tåø e;îDGčũS1â~Lœ¤ •\‹iæ'L;– hßũzô.`+ēešŠēUŦŅmÛFt+:åüÔAs[UŲúņYˁU)äânEęšš†ĩ+ĐHžēĩ0tœĖMrt÷MfPí$6ą1یFîˇЎywQÎÃ4$ŌãPYür”xq(*Øā‰ ÚûŅúûšl׏Gĸ÷›HA+՝3‘.Ú¯JÃOŌĶé´ĩ—Đ÷?ĮŌ'ã‹éõČíô ŋ˜šŧĸš:‹iƙzJ,‘æ äéFö7šœ8šHl™Īw }…’ãėQųÛҍšŋ ĩíkD—.n@Eˇ´Đą¨\äĢaØÕ×Ų)Ņ—o—Čû)÷høļ:šøS›g­sš!T¸ēG(é%„jŘ@”ØX€æ÷ˇ˛õ‡ĢČÜ jtdÂ]jÜLŗåĻҌ}:4ŧ]›ÎęBč¯_ܚ66Ėđ˛‚ēęŲZ’ä#Ņy WtaSĘm?‚5LG›~NBS&„˛‡Už'+č :EęFOLŖyŽ9ô"}N_ĪXÁËÉķRŋx­ūsŧJg/§ŠžšN/ˇ]§Uv>´4͎žIũ¤įĐq’Ą$#8“ķįÅ> kGÁŠúč¤ß;$˛ũˆÚũˇĄŽ%;QMÜvˇō˛}Âfõ ŗ~,´!)å=Tęgščp›ī•ĸųŊ‘“ŋ Gœy 6ÛY(ÅÜFįŗļ mŧzĐŽė^-éķã‡ÕōÖR„Ÿjcƛŋėã5oNãĨGŨĻS^ŅK=ĩ´*|ŸëM…q‹H-öã^FŠą9ŋc}oŠ’°Bå­ē¨ũdj×ū…ˆÆt¨* ĩÅ\šŗš¨ˇU“ĮT=ā9 “ēG#ŧ:¨o[ˆPę­˛Pĸŧ}|ō]}=陃äŽOĮ"ĒĨ8‚úO=JĶsP‘O8=7ümũ†)o ÃíÚĮE˛ĮŲ:‰cH“UCkĄ3["Đŗŗ_҇%§ĐUũmčđŗ­hėÁėô „9¤BÎŽÎ%%s’ČĶH'j}x>uģPK]š:iøØ.Úö͎WŲ¯Ę Måį›—ņĶûķ– Nü¤į/xšEöôÍÅlzŅæ+ÍÜ;œÎüu“y*ęíÍ–<ëCŒäG´§ĮEėéF•ŖP‰w JŠŒBSîLgŗžlŲ$š†]Tfo2ÔÜĻ{jЕnĶč¨ÍīŅŗÕčōge”ëi™^6æČÛŧ…šį\¤G˲)õ˜@2ŖĪebé÷irôĶē=´Ĩ/†F{fĶ™˛5äÄ6ž ōuEz5(ÔÔ e‚ЇÅBąr]tsķG”5?9Á9)5ö4›fQÅæ_@˛AĐčįeHõä#d9Iīõe]៧ōj!´Âe¯ė8•˜?âq×;žĸÆ]eų}F´˙}ؒK‹ß¸ŌÕûsIÃčM"§åvlŠR#R7 %;V#w‰h¯žō+´As/:ą7|’Ø}ļ…œlŖÉņŽ\z&GrõWPņ#ŖÉ)VЉŅJŽ;b?’ŧ“=´g=9î2œN-YBÃ$ŪŅŗĖa^L~?˛Đˆąō*¯ēc%¯,Jßĩí¤Å{ŠĶđOdęŦˇlŪŧhúá>¨?f蚔ú¸™ĸöëŅ‹ĒĨ¨‚/ÍD۝´PĐĢ(”$~՟û&_į$T;ąK8ĸčpHž攪ČĻ{(IL Ĩ%Ŋx­õ<’ß͛×yķzŠöŧúHŽ—mą§īžhŅV"IŖw;SmË5$ÖĘēVīĨ3ģÛ"ũ)ąMˆoB6¨i_ĖūšrŒŊĢeĘ.q\AŧÕ[HíRšëâOÚz‘ÜsŸšBM6ōũ áHˇą¨ŠzšY~Îē|ŠšļĶ#'ŋRūúG^Lü.¯ņʉ7Nėâ=gđ“]Ây ŋ ø{kZ>A—šJŠ“°{+ØåōȸĻ;5ôųĄÛęŪč–ōAtŅ7q†PFöÄtŖ÷%Ķ…ŌG7u×bĄë#ĄŪáŨ#ÂåKû…–GĐËí#‘Qđ=Ē‘u€>^]ĖkYņ“úˇņjĩü¸|#~XÔa^îb?/16…ŪN¯ĄŠĢĐu"G°æ rdwÎy +<5…ŊËļēK°Į¤?ą›ˇd’%Žcn9ą=‹ÜŪŨLtēę™ĮŅNhxV.r=´C8ÖŽ•ģ=d[ĸæŅŦA?/W°7āõĻÆđČ9ŽŸUz•Ÿķđ;o'ôæ3"yåßoéĶ˙W)w˙ upœD‰=DļÃ4OwŠ~đpžŸO*ĸ[æa—n%ĄŽX׃elĖéZQcÉCîŒ:TkM¤VßΧ4ô$)IaëAũ Ŧ– ÉŽúú'Ū¯ßŪeƒTĢ=LŌˇnbžtž™e ąÂ0\Ņ_‚Ž˜.hģĨƒG-!0ęu—č5¸ēņ!n´˜EĨ. 6ÎaÁĩ7¨?¸‹  öģ Æu¤Ŗkx4<|/MRŠ MĻ7_ëØVmbōvÕ´ącĢ’ĪÅņʈNļY!§ĪjwjŪRH'ˇ“<쀖˙Ĩ-¨Ē;FđŊ)˜wö 6‹û‚'Jė…ŠļbÁč˜-TįĪ€×ą 8= ]å1hfĄĀ¨œo0e¨…„Ė-čâÄ3[2ĖvžáėŸË i+oÉyģŸžŖCĘxsåk–mōaÛŪSôŠŪSÅäBÚė{B\&Œ9gAæŽz¸úK íūãĐ˙q—VĒ0đ¸#*kü0Ĩ_‡Örŋ{ÎųsĪk¸§o°7ÄķÛø"×0ßvWĪp˜Ÿ4#cfš_ÍÕŅ3<Ø3ROÔøļū¤ŋ#–Íđ)ĀKÅ™ņđúƒ#=a~ û$‚7›2™X2‘Y…}O,Š' ßÖk&&˛“ü9[5ž´"ŪčjņB#ÃĄūŽĨũÁßāÕņd˙šDĄMÛ$qîÍ&ž­+’5ųä8Oü<‹›|4mF˛°›Åņ†zvôôŧÎ`¸ƒ÷ųøđp8Úcá ¤cąD16™0—ŗo°w¸7Øęŧŧ$ŒÂ7o_ÆLˆd“é>Gcã‘}ËDÍ`/îéBûARįŋ x;Âá0?H€āS+_CĄ=”oX”•âxCænXdĘ\?|aô‡Ø&Čmē6oĪ0?–s‰E}I_āŸį-˙ęí˛Ņ.~ˆģBŧ,äâ%WƒčėĻų&úē&dIÖeAŅC-‚ˎ…RÉč$ĸQgŦx‡‚ŨKčÂˇQWDÍDa(¸:ŧžâx˛~ãųAđÂOņk†!О@/ā+–„×ę ÷ƒ?|‹ŪlZĀˇËŒŽ¯Ģ(_M(ÕžžâūJæ‹xĒ? 17ĩĄŨ{zúFOō …ŋĶņX$7nĻÍDÔäĮ͘uæũĶąDōP1=ī žžė J%3Ķi“fÉT,15—ĶéŽ`×`0.ĒĪBN:"‰ąT$“áŖÉ1sÅũÛ1ė Å­x™HGÆĖÕú#ÜėE<ͯK‹øRŧp4JIG3XЧϖ ÷ ‡@RQ_|˛Ø.j풒ˆį^ГėôØ<ÁB­æ—Ãčßx-ĸhävE˛/™˜¸@@ …;oĪ.^1|ąčS8:9ËŪ°73ŅdĘäNBÄäd+šĪ,.‚ũĐží ķm|šN`3ÔLÚ—ĖæåũAđŠöĸ.|ÛhoÕõ<8<:ūmęéí m‘ õex&Eļęzd5x,ũ=};Ŧúv/Mø˙ų{ú5Ŧ¤°Gâ|&I,×ŋ§¸ž`~ôŒôÅÆcdŗ“æŌդ̺{`p0ÔUī.Ā# FÚLEbisŒOĻųčd2•Š%&xs|܌fįgsW¸;ē!ŧ ´ŋéĨņ:ûģ€đ*øŽA š‰ąH";W?É6I#.ĄŪđ^ĸĒd(Š:/Gr]s&öĘU4ŊBÛ;ēŪč â ˆ´ßā…Ez*ūĶ&Ŗ3 ;‹â/2ËāIËáŨp=yĢŅ5MÔ†,ĸŠxjIxĘÛ§Ž1ž6ĮW‡vMÕDQÕ­4žÁž.”?>W?$MT % ‰‚ZX?2Éø4qņ!¯eŒxÁķI~YUD0Ĩ¯”ūņd‹¯ †ČzP•Ōöņ O4d.QV%Uû´ŌđTЧúÁ2´D‚P .OŖû'û E W úT#x%úW_ÃøC<ƒâI~I׈U ŠQ:ß@Ūŋ˛ŦŠš Jā^ÄĶKŒg!gŸ,K ā]ؐĨÛ'Š9û US:–%]ē û¤ĩõ‡(¯1ž˛ÆxęãikŒ§¯1žąÆxĩœ„5Å-¨˛,‹ŨŠÖ™UԏQ)¯/PÖtUŅ5Ĩäú1Jë‡OŊ=UĄŧɊTz=ĨõçC}“u¨†úRēūĒųų 螮Ē&é˛R˛Ūj_ĀŅuYPZĸŌí[ãú1:W?CSEA0 €¨ĨÛ7W?$p­,(%` _ŖÄxļꇨˆ€s ¨ūMØGë‡Oô̆(Ē*)Š(Š7aŸ´Æų+¯1ž˛ÆxęãikŒ§¯1žąÆxk[?Fײ~äįl‘ ŋĄB¤&“ŲäD:’šŒEq4á“û˛‘XŪÎāŠ|xŌä÷ė˛F>|~āãŸ?'ZâSüÁH†‡Į4”Ĩ1~ß ™F %g"ņå§OĄąXbßtzb˛•?ËNōã͉1´f<œ"äËbdDIŒņĄÄØl’9™1#éčäŧ9Qr:Å[ųéD6įE,~įtÂl%ÅŲUdgÆ9ƒ‰‰xŌœÎdĶ2]xŸfÎx؈kxC§Sđ#ƒ—1QĻfāŨÉ,ßŨ\Ā~iŧ9î–m‹­™˙õ¸ų .bH†?HfrédÔĖ ŋc‰l’ÎƒŌ°G‰,X7…OņŊ1 ÃÆ“é)ēį…üQ,ŖĖŠXīæžĩ ˙(öüyøž><>`ΗÃwŽMGá÷‘,ųÂp*-4r.@,/ö$28ę„íWƒ#ö„ųÉf:1…†B„ÂĪūāūn24?˙×ŊzL°gˇ8“Ëā‘Ą#Ĩ Iãëūiø„]%ĸqĢ"ŲČ>0|>^$z_"y0nŽM˜S¤‹%Āģ3|jz_<%ķœĖ_›Ķ˛sÔya m›ÜŠ?•V>˜É$Ŗ1:qJŽķ{1pJŧá…ãģ|ļÅ “$ĄšL$§fZa+ŖĀ,ķä 2<ÆĘØjøâ<ļ/8˛xŪ) ĸ ũA_äPljzŠ*ׁH|Ú\^O‘ųŠ0€KÜŪ@ĮÎPį0š?8–Ɉ’Ļ ĘüûĢûÍhvu÷BsxÃCArŋQ›xUCŲ_ˆ7D\ā'f#ßŧoEl^¯+ԉ÷4‰WD^Pũ‹ņēĖh,ģ,Voņŋ?0ĸā—Õ<Ū)-D —ŸãŧŅExĸŽū€œĮ]-^'īI˙×4ôų|ÃÁūĻ‚ˇÛ†üA?Fj6’˜@]ĖQHüš"wn é˙š`ãÁ/o ohŠû*Ôw°Ī~˜hãąt&ģâ?k xŌjđ2f4 ådĀÎĄŋ öŠų~\1QSE'įK đ‚¨č€Šsm$>fFcSP6ÆĖ‰´šøÖÁ#öų48_*ˇT]…=¯ÂūŨ`įĐĀp0ßßËQRI7ā„IûĄĄdn8ūˆ› öą07Á“n¯p‹æŽģ#éԌvë b¨ø‘ht!~ djebPë"û:sŸy°Čũž]Ąģéüė“ úCÖ5AÆųŗĐŠŋÕĀh5 ‰† ČĩēΌ›Žhî|ØÎŽPī0Ũ?Å¯Ë˛j( ¤ēdČp>Rˆs7,˛fj~ķđ¤Üũ}8EĸjȲŽtЇūŊĀÎ.ņ^:ß ö‰Ē$IÕ ‘âuvaw•ŽZm]<)?V4A h˛aĀ‘Ÿž÷oOĘŲxr@`EA(Béx9ûˆ? įēHÎį%đė8ዀđĒĻ€3 šeÎ_;K“ČũEEg˛ŠĻcŧHĨáIÔ>ÉPMĐu‚šĖ_JĮ“r|uADI‚đKãKūqØ÷×÷×÷×÷×÷×M_V<ʞŗ˙Ž Đåp 0o1oąGØ#ĖítŲģRŽ}m?n?ÎÜÎ}ãūœ>wœqœa7˛™IfŌū•ũ+ĮĮtq6OÖŦcļü Į,sÍžŸÛ_ÄŞt­Û]ueێ9nƒõfnšõĩãkûƒ°>šŋ­ŽVæĖ/đŅöēœ?­šr=]ũ@1Ô^‹GuoCsÅ=…<ŦįåĘ/ŦÄÃ1ËÅō¨ÛŨzŊõzÃvg‚9\ ފ̞xāk&Põg°˙Ž.äaģLōpU÷ļÄę¯Ų#”}dn_čō/ ‹ˇ]Î1!<*ģ=ī3×đ™MaŧŒ×ũ?qįē;×éFõėuæ0sØŪgīsŧYū!r°Ŋa{c![ÔÅĮō;‡í7ČÃō >gÎĻ…<˜ˇœ/ō`TÜSķ¯îĪmĩļZ|UųÍ˙ĐĐĖážážŠŦ/s&h~P°rö[ņe¯ŗ×9N8N°]įÛ™Ũ–íö”=åøVîuՅƃÂ+^v>\$ēō<ęÚ+îÁÜG~žgkgkÖsÛđY1ļœ×÷Ļܯō¨ėŽēŅu™šŒđøŦXZČŖ~cĶ—žˇŊ,Ɩ•įÅ*TÅ=ŽōāļqÛrŋŨæb],ģØ5Âb ũQų|ÍqäÁžcĪaėVvoųظ]jĢŠCŽqæ2õ Éņ\ž.9ĖYȑ{ķ[äĄūÉ×Xũ´ÜŅ琇{oe7Ž•yx˙Ĩéo@ŗ w,åĮæøBčĖŒ+×ĶŽ§-íߨĩĨņ°×ąęŸäuKņ@ŸÛv9›jžĒúō`˙ ŗÕūæ­íÛVœ9_nėöĒYī|ŲķŦģ…ÛfģD~?G˙ tx”Kåķ$ÍĪû~âŪë˜eĪU<ęũŊ÷÷îŽōCeˇÚŽRũ[˜ũøˆ9P¨ŗ¨SĖnüßãĢŋ^Ũ)ÛîļŋgĪņˆãöVöV;—[Œ<œŸŌēązc!ôîųB¨š‹yp‡<—ŅȂ?m_o_oÕ(/ĢüzS*ĩh=‡šˇ uvNĢđwØsĄíČÛŌ]Œ)ˆĻˆ+Ȇذ+ˆØä;ČÄļ‹j.DV.Žō~¸ŠJ\{ĸųM¸ÎĶx*ûęŪ ēØ2ļŒ<{„.ÛŨLˆ}rrʞü\´{đ˛›ÚjLԟ<“#¸ ĖíŋĨ_Åxāį-Ũĩ›s<œŸ‚WŦū­€GaŽPî×'”™ęcVũf/.Å­wũŠ}Í)ãbB\ÜęčcÅ-.Ö˛ÉÅV}‰ÕĪ~ŧėyŒ.w Ío‰ôZ”ÆUžĪ$ĶIwÚ ČüŠÆƒŽ§QO€ÉeT\æ˛ëhÃöú‹N‡Ķ~aOrÃÜ0sšętËŲæ7*ž+ˆ)b?÷÷PŽŅ#å?C}b_ĢūQĶ û< õ˙[ũ#ö5´k­{/eâb+náNq§°ž5>/ŨĶö^];2AS…ēhnē÷BfŪRuĄ8Ēw´3wũ-[Á=Î=ŽLČc§Ŗ“+Ë+ŋh9|Į?uM„>Ųæl؎ū°”ąRājŨēųÅÚ¯š—(ŋCøŽZĄö!˛Įh˙@Î š~;oŽ úbĶŪĪōušÔ]Đ-rNÄå˜õŧŋŲÜđZg y RX\ņīHŋQ1ˇĐ/ČWũEŨ蚨ŲúÎ4QĘãĘŨQSWuēęt…‡ŨJÎW5ÖīwĖņ°?Čîw>E™Xį‹ģŸŨoņ¨ŋ^œ‡Ĩ0Øģ˜Zûēō¨š>lŋr×Ɏ?5Ë9įtT~ŅTŨzŨ{žō‹|˙Ŋ{ŨõZGx\#×XšŽáŨĘu–/ڎšÃcü\žŸČÕ1ZMĐ.övtøãûbRMTlˆ'ĪaœmŽNįÖįÖš'å{Ę÷ØŪĩŊË ×ÔĩmžķuŨ¨ųˆĒ9{cmëSÁ™öÆMĪanX<ÉR‡ö[šą9‡æj—e'úĀâgʕx /sŲųNÃˇũmŸQŊ˛ŋč:ÚTŊõŠĐ'Ũī†>ioôޏŸá^â^O\ˇcžs˜ŗÎ§ÜNŽ‘œC­sŧįūˇú“¨Ä/į÷˜…v“žFë=ymé5đ¨øן yŸđĀčb^e^ĩxԗ Ī4UcTáǘnŊœŲõj˙/wŊzį:P¨Į{Ũqō@ÅĩxāūsšķtīQŅį=Ę]!~š  ũąŒvĢk3ÚJsŨĘ_QŖũ=[zļ´˙Ŗ÷ŧ́^˛ô˜ÆĪÜ<Ãęōsx^y¯wsí›lYų隞‚-s~˖Á‰Č¤ s9?"ŊŒķaRC t–ô19ũĨķ ČũėĮ_ą˙˜.Œ-Š2ąæGøŧÂĶøēūĪÛŌĘ/×U~Aû†|]9Kõ‰ģÂ]!ÚU„÷û~įˇöē9ž?V}čųãr<čt‚yĪ*dޞ‹T˙E<ˆíKō g|v+]öž˙oīĖÃŖĒŌü`ęîK­ŠJeŠėû HÂ"‹†ĨĄ1„@ !B°ŅVT†elĖfitĞvD¤EÛ%âH cĶЊ€€dܰE~į{Î=U•ĸô<=ĪoūČ}r“ēŠ›ķšīzŪÃSGOņ'u÷›ãQ˙@‚õeCˆGö*“"8ø<” įH4ũ)¨-Â9ØČ™pĩÁö0­‘š_ķߡxøø!‚Lˆ.ÃŅf™ãŅWBŋ ÕËņDÂ8ô۔éWĮĄôrK4¯Ė]híÎ6ԕĄ8ŦŒT߲}q9ƒø\ŸÄUD'h„DØŗ´'cŸ×Ø,ÂĻ9튃ķDŗ3Öˇą? Ÿį~lŧāxʞĨŒ ֕– ›û’æ9ÛŦųÄfQü÷Øø ˇō”ĖīŖsč)úĮúĮС'>OˆäpēãĀaĖ ĢJ/áĀ\"œÃņTōĐØ•‘´ˇÁ!Ÿqļi팃ûíí ĒĐÆęÂĶV@ęÖÄĒÔŠčŧJ×ķžY°@b<Ãx‡ņ¸Ēôržë|Wéø3:ėuŪTvAs´Ö úđĪöŪGæ'a~N{-´7/ŸAާ+™[" ›ÍÚĖĪ$ÄaŽOūʗԑÃņPėfd>’3Ŧ>f8‡Ŋ*Nrŧ‚ü€zŗOŒW;ČŠ1ãÉĄ?ĪæØzš^ÎæXާč<+‚õĪņŒÃņTü ß9ûЏmigâĶôÉ!Ûh5‡s JԘh=WXl ų„æ”)!Á šOŪl.CG ¯Ņ)‰Û4oöąúœŦš9ËtNė48/fÂ:Ēdfõ)Đ36ŌĐáÃŦČšÄ)ŠËĪ‹kkĪXhĶZåeΟl•ū¤žĒŊ§Ŋ'*|šĄŦįĘ9¤7čŦéčˆYjXx]Ë*`’ë­\ɁuŽĢáđ5¤'ÄĩŗnãđūG~^NĨģ§‘æ ‡9UkUģyÕxÆņk˙äø…žĮAÂė€d-Ū÷ZAŖí –ɘxGîŒtshŽĄ­ĶցÂüžˆU͘Gâ7 °YÆĄ–bü“KT.¯ÚXļ;} īđ ™]ŅYÆY>ړɷz¯’wWr-ĢaKĘtWžšŒíËĒm\;­Vũc0đuÖ˛üŋņ˙FÛÆúĒŧßĮē°ÂŒ[đ¯tåB8Č×÷Đg‚n͸#ŪĢĩj­ÎŅ9;Œäî_–Uā|ŽĮe—kú_Ņ84ɸ€<č>”?¤ßū’ŗ aúmÎVĖ;r@që2FgŒvíˆäĀƉhôĢÂ8ø:%_ū•ũ(|]^fN÷æ?WrgÆĢÎįāļßĸžŗ?™0}#jÃ×3ĘB×$.Ŧ­úŸīT>¤īøÄƁ<čúĘĐxÅ+æßúĮöũÎ!XmBd7ĮsŋįvÂ,G-åū ›ŌÆhcx˙qįÃÚ6ž\c]†xĨvˇuí1§úrü×ÃĨ&"‡ūŧk,‹sëøĮŒ„u…Œ É+˛žOßåūwĢf'†÷`Ę9$^‹×\×°8FŸél}v4gŽ3—QčŗŅĪRâĮErë„u’f?¸?öZĢ9Õĸhĸ9vz3Ö:"¯Pāão¨[YĩËæ XŸt}åčŽxė%byš×lĶZ gĖ.‹7÷]V°] e5lG9Ļī öļAˇcWîƒŊąõņIŽ}ąĢ6öV˙ķŪ]öĢ>ģ ՉÕ͎ǒŌpi“­­#‡šĶ÷yŌî¤Ũž‹|=X_Yķ)m̈xw1(I菄únŧŋĪęAu‘Ģ6āŠëĮz°1Įgø—žØģËņ™rŒ¯Üąsp}m'ŧ˙á '>ļFDßĩQ_iŪ gÚîˇŨ/ŋ°čņRōĪ—ÃÖā´~đ΁y­6‹UD °Íg}RHėQGwÆah4†qĀŽ }ąĨ;ƁҪ§noIûyâ“úŪP|Šäā9UúqûO/ĮÁ|$~aī_”Íčũ Ī€>,X}Öį!ҟĖ?šÄŗĘŗ*)Į3Ú:ßč¸=†Fcu˜Ÿ‡Ö-Ą{KBFúYī.äQؘŊ…ûš3×Ŧ ë?#ؚJč˙˛ā$ūߨįÂ×%äiČ.Âqģ¸]Ų(ngŅÖžü}ūsŠMöŖq ūsžÆĒŊ­Ŋíl…Ā‘”“Ũ{ôröü¯ž>ë2gÁ?Xוsøļž äöpš_Îu\_ŅÆx|fyīvH¯ŨœÃ3Ā3Ā~Tí.b"~ĀDęÅD9¯œĮYŧ… {M?é¸ÕqĢÚ"-V^Œ“š;GũŗôDŪ oPM)E\+Ž…DĩĘČ÷,Îrõ+ÔŊR+čeŸĸu“Ui=ušēœ~÷ƒzĘUĒ×ČÛåíRÄõ]ė'jĻŧ]؍ŸäqÚ yžš…z›Ø.~ÁD9"~a–ûnôŸsfi#åēh`ĐOę'#9ô“ eīHũ؛ûǏ‰ŽEÆkŒÄü™ų3p(K•Ĩ˛*Ģj•[Ęr•mÔ}ČWCžĒÔ ļÆe_jŨĸqč5âœÃpļ¤ŽwÜ`ŽÔ6Ē™Ž|1E$4€DŲGä‰˙\Ņ]EwųĪÉu >`ŽtŪĢm”ˇk÷zĩæJsĨëģÔõįōnŒ]ĸėĶ1!OžŽ#Gâ ũ-ņ phũ!ŒãuŊøYvUvUŧéÎøYßĪĒū0`Oąæ{R˙­kQė×"õĪ÷ÚpãKĮąRŸ/Ũ1¸•sô˙0ŋįP— R(NËáŲ ĨYiŽŋ9]÷M€V@mh węú>)>ЎQb_œÃģJ<á?g–ãgé1é1EÔ~„(ĸ"Ēŋwߓĩ´ovE¯Š^ũ~›–hw*}ÁK‹ŨZčī?ēßÔôņæ!ĩ… lĘ^Ģrû’n“nĶ^Ņ^!y;ëŨō錪jYQŠ÷EØÆĸės­)(íÛŗä ŽŌ mØG¤rNĮ0܎?&7dĩĨ´ĮρÎUę˙ÂãLĐq{eÉķ”8]T“žļŊl{b˜8~™\Ô7ģęüā“ƒOö_•÷Ĩë:=]éĢôÕO‚$nCęĮŗÎ]ōi1hÃ^d/âūų"ĒņeBlÁÖJmP÷ĒeŊ÷'/ĸ*8ÄvûĻôæ‚ŌŒSî1ŒCiö4ú&8npܐxwī˜ō‘}æĨ´ÛGāuK ã"9”8ˆ˜Į(l/‹qˆqŌcęƒŪ™…Tô|rčģƒOVôʝīž–%Ŋ)/‚ß(įˇĩj‹m5DZė¸Õ?ßņ,ęĘ휃˛¨Z7û”¸ėėũŠķĪÆ×ęķ•ĨˆeLĘ>ķ´yžŽ§ÛR3Ŗœ÷fĩ•˜50ĢHqoSOI?0ŠŽĘũ-]T⤧Ĩ§Ą*ūÛKŪGÕųŠ^ÅŖ|Âöží}p f!æB‡7#'6y8”Ûř‘ú|BōYöФ[ĢEˆCŅãâ4Æ/O:͎Žĸŧĸ.Ė#ūB8<Ŋ27øĶÕŠĪ“˜ˇ!nƒûĮ/ŠgŦUÖjĒĻâ|)‡8S{â̊{Ų#ā‰>ŸäFkJŗ^͇ĸë=q¯¨nJzŋÔį/ŠåfĄĐĀ9äë'8Ā΁Ėaf{Īģoaú@ū€OĪNG¤ŠäDÎBõbqHŲžíur;F/o@D>WMø8īRË( Éã–ŊЧ`/Ō98 ˆĮj&ė‹åDâ-=ōīK<ĸė#yœ0Pģ" Ę=Ę=ō ‘†Ú~Ęķ;8´ūfļ17āŠ„DÚžœƒÛV$Îā€FP—@āPĢÔĒÎ9 Ô#~¸üŪ™`‚ƒû“ú­úm4äķŋÍ˙öôrÆ"ōú ߊ„"ÆÁΜ#Ä#}dŧFžĻ]ŒŲ‹PĖēŒ/|ĩJ{E~‡Y˙LŠ.l[ČsĻņ˜Ä1âČįLc„6XèËõŠÎŧ#$Ü×ÁáyĶķfæ†ÄwÍl­8‡°ŠČUp@l}`_ŒƒÅLJÖÍûbÜg?į€o@'Ņ9RéÄÛ#9đ;Ņ8ŒuqWšŽIŪķŪķŽUŽU°):~K¸MÁË)Sø<ĩŠ ÆŽuc–…zËņg1(äw˜6ôn[ā >žOoķ(f!ę+×wj&~‹’ŦU–°->~ņ—â/qöŧīyšČfļ™­ŋ¨ŋø?á ÁA,éKÎAš^A>dŲ›éŖ#‡kMō[žaļĮô>XŧbB}„œÍÍ‘×á×Ä V­éM&xū\Ī„įî¯lžō_§Ŧ,B}_ØJ|ŸäLQ’/ˆo‹oËũ‰Đų„ĐĖ|ŧej&*ŪP.aņ‰æ|°ļ͍Õ.ZōĩüĩøOâ?á,ŨeûÔąq Qĸūäīå ņwD{ĩd8æC ‰ä°U[qęĮ4ÔȌ"Ćp˚Â}›s€O9ģ5îGŖŊ^ŸE‰ÆÁč9…“āl\cŌ>#įđ(Éoa>ĒK˜Įģ!ōvng!ccš?×ĩ&RŒCē˒N8˜>bˇzEŽe­kŠž4’šĸ3Ē— 9œ žûĻ+sā:׉ąĀ[q%VͲšŋZāŖ6w\øšyˆú߃Χ™ˇ]ËÄČwKđaîĪAŋ˙ˆÕŽÄ’˛ÍXš?¯ķaįĄēä…2&l>NâÆJ¯sŋĀ;7õķŽr%Ė6¨į|-įæËæË‘ė…˙m*ŧžŠāPëÕzJb1đņsa•6‡q0‘3ũ0OY^Ā÷4ĘŪnÍC,áöÄ9XŨ.ļ#ŽR! ļjĖ=ŦZ„æCąsōų,ĩAä2vaĐÕs #Žq.‘”z=TCá9å*9˜>.åP—ģĮ°Y-‹ĶœC.Ô"íŠ{yH,Ŗ WÂęEÆČĪJ†Ø"Ī‘įˆįÅķŧ>į|>ÅúYÄÛ1ö ōĖ숎K¨Q_„ė§cœeVÄ|ZŲ7/Ģ-ÎAsĻÅ+ļ‰mĐÆNcîûLĸsxú¤}čéÉĄū^ũ=į ‹Â9BvÅæ&s¸Į8…8l[l[x|ŠäĀ<>6ÍUڍW¯kCq b=ŸŽJ_ģĶîä` ˛–×´LxŊ%lļ"#2%0yöæŽ(ÜæĪėpnWĖūôļā|+ŒClS6iĮĩãÔĸ.ËĄœ‡KŊląáz:™[Eá€M1Û"ûk´Š˙(œÃnq`|Ąņ‡ō ē ÜÎ x1Ōķ!áöÄ*Â¸ėŒ–Ājįta+›û?šāܕúqâõȆF­Që‹Éy$į˙tÎMũ™Å׎v…8‹yU(.ŗr]>Ä^[,ą´žÖLˇ͇ąÂXĄdYĘ;:âZ}~ WŪ­-˜Ģ͘‹žĪÄžÔwŋ´ü_p/Đ?PtįąŦš‚ŌøßaÖ‡× Áy e@–´ę–MĘ&XVĐĪ=–XļÎĄôu]—ōlüŸŊ3ûRäEā°;]×Atã€>¨ŸŦ ‰˛Ô>ÅõĄq jöhčÛA‘æit;rØļ B 8g}ž&*Gŧ/<#<ƒŗük"ģ͍FĻZĻmwŽLôŪ›ÛÚRĒVËâRĶ˙š30m†ģ]Nˇ‰ÛäO Âw\¤FŠQøN;ĀfGlÎĘkrV"ĪŠ™†›Õ áũT‚°+ęc˜HФ¨1j î˅ūĨĪåÃŌdi˛ė•Ŋâŋ"‘v¨›Ã9´&BÆodÚ& ‡ĩíI7įŸN8WĒeFfāÚžwö?Ü˙pNãŒō„ōDt"lV•ķ<^Kuä`š:’ƒĘUs¨˛~Rŧy§s˛ļ]Ū-UÛ&KÕRĩ‘ûį^ŧbnĖÛīķAÛ+wŋājĐrZ8‡í^Š‘˙ĖĮɁņŖ‡Ëô"•Ą‡ËãŦJĄßīĩ„0k„5á ôL8 ”ˆR,QŠ•âpų×Ú?Ëģ…Ãā€Āž@ņ?Ū[î¸ßįY_Øī–Ķ„ŌHķuĪzįųžˇĸs8nĀZ ãp•ēˇ™ŽžC>ŦíæĘŖDŠĄqKÌ">*>*Û/ÎčE8,ļß—š96mFė8å ø‡:AĀ˙†í^uBü‚!ŊÂ|ŨņGã ÆnÁx˜wĘÛņŊĢ4ĨŨõę[0aĐp‡æß,÷ kä%A›"‚ŋå ĩšØՈđ9tÃ9ØčC$q(÷ëMŽ÷ŒOõ‡q?ũa¯îՍ›Cžõ)câhŒƒ¨†ŒƒŅ9z·á&™átål^GøbˆÃ~wgÂ3ō¯Ĩxũ"lËV`+ĀØ¯løíwčM`€áuå~"OāŠ$š>wĀō="ō'æë”˲#Ū‹æõ8ŧß8čø#_‡ ŠåįXÉt&¤ŊđˆCíÉ⠞˜Ō†ū0Æ?gÄž(…R܇ũŽĀĩYŠKÕļÃ_¤jŽÎŅûTéÁ„Ķę„PÄbö̘>BX›åđųHŗ0ų­Ëqā/Gã€N‚q÷˜tĖvÚvZ,abLÛ]xCF™ą\Ú"mQ×+öē´×vLÚë~ č7eõŠŗíGä%ėoĘKnÜDí™ęB|ĀVmĢFæū˲ĢŠP—Ŗ˙Ėk–åqŽ5qķÜf­šÃqÆqÆėiž.bģ‘öKž^kÉi˙ˆmgÚXO|Žg…ąüRŽĒĘž_güÔ9’Ų3†ŒC¯ Îņ:á`]Ŧúsāp”°øá8“ļ$§ÎßϰôLE |Ŗ|ÉA˛Į­ÄiL0nˆøōWHđįÎ!í5~—\ZV?8¯âŋ “#9ßĀÁûŧĪÁŸy°žmvBöÁƒ•YŗqņÜė™ļ¤ā­„BÛ…ŖÄoŽ•Ę 1z΁üÁ'@Ņ8ÄoÄol¯Û^ĮĪJ€ięK‰#JīĒnjÆÁüūâ ,Ö3Į¸Ã9ŸGÉŋ¯hvÚ f!ĩŠæëšÃŨ;ŽäĒį@PČ ÕÍÆ:ÎAãî1u¯^ĄŒáö%,büAÁØ ^‡€CŠp éOôT<.ū Š€ˆoČKh-AŽ1īVšYž#ûøXŲŧIoĶÛØw–MaÍoyĸ¯øDß˙,8įZŋvœņˇ7C3L7Ą˜HōŦ5vc]rŽī¨žÎĄŒ!z°ęÄÎ8äLčXĮ(vŸī”į=ˇ3üī D¤Hžīø<ŠÕ%î1Ųž‚s‘öMā0{:Îđ,ÎĄĐpwkGŽ =ŅlHė‹ŽŪv-ėJKÆÍ}ƒsYuœRA^"6FjRÄ,Û1øˆ9úā˙O‡×㜉Ėŧ÷ąyĢIœ Xũ Ō“VoiŒÂ¸ŲQĒKäOH=Ŋƒ ė ūÍōĮå9äĎ:á bįāuį`ŲÁqƒGq&„8´Aė{ÖÃÛõ6cû?f!úT„:ŒÃQ’ē7pŒ‘°jNū„s°(eÛĪōņķīī!o#×Ņ|iO6ŲÆs ­Yx^ÛmåeŪ'āņČĘWŗgōšÜ#1Ž3B){Õ~ˇˇY})X›“ø^áYįėŨį8Cs^X4 Œ,>Q´×7­Ũ2;î`"—‡ãĢrŊvÖšßšßŊ3yaāZûqŪP­sęÎ8ümŊŗĘīëķTâ&ÍÃø]üįZÕô4{ Ĩ„7Œ#vIļ'̇§1’ L´›ĀáÛėÛėŧ(ž(Ūûęuá°ŧ ԟˇ°xÎ@ëÆ°&X‚=ūŦ–L[RvãŸVĖΊ3{ōúHĪ…="V°¸Ā3œœ{"큰܍ˇŲ7a 5 ĒJT˜â{ĘNí&FĄė$r=H´ŗîi3Ōf8Ū įĐÆjc˙Žđ<ĒOõ‡ũmĨžŠŲå÷Ĩ-a _Š/9~Ĩį2^s€ƒzFšäHē^Čŧ u˛˛SÎLe$â{D\LbūØ.zäāØoļ˙āáŖĘû­ėđųĪ?iǟ;ˇnļ \1´7vĨΨ¨ĢÍČ)))ôåô.(ę[Ú1ø˛ûwûūû“\i˙îūõŗjšZ}séG _íũĸīßíË%ãŪ2sR]>XxR+6ĀæĻMķ.ŋÜNö'.ŋšËUlā}ˀŖ‡ōuē˙euÍŦɍ3}ŗk°m-ös¨ml™5ˇķû ŧÜũr|Y›[f_é&áûT ŽZ12úũōÃykZf^qOõĘÛF /V‰Īŋ;u¨ĻúZ߀ōĄ–-fUΟk}üŦĢØ{TՈęaėķŅs*ø§Ā‡>z]MCją/yrc ,fvS]m=nŪŲ‡6 }Ū:Ũ/ĩē|TeÅđƒ/JnĖ?‰z˛o@MCũ¤&öYÚˇĩséũFĖ>Ÿ:ĐĐ85'ûvĖm1kô,ėn||žœĢáÍũķ˛û +HÆ:ē"< dŨ2­ĩšžļĻáīâe÷2qXÍÔYtsŪÎys¯†7īĖËî7zø€ĘęQåDÍcCãËņ5×OYã›ÍšCž7šîÎúKvC§ŧyWĪ›w5ŧųŒwHŪÁŗæÖM­kJÍ÷%74ʊPđs§ŸANyŲũ†Öŋ˛zâˆĒ‰l“‡‘°įŅÍlģÉuMõwօ°kÃÔ} /ģÛĘ šãø"yķ¯ôyáåC uÛ(ė'\Y=`âŪpū\|ūŧiõĩĶ: Ŧž[ZÔOîxŋ1•Õt?õœėŦėĸ,$Ž@ØũÂŪN÷ũ¸ÜæöāT]9rĐĐhû˙ā“éGMkĒkžÖØ0ŲÚ$ĨyFĢ5PbGŗØ–93yĖø_<°yMCcã ÜYnÄĶȝ †Ķ™5­žIuž–fžÁËbÔs§Eą3š)ĶĪܟ[ĶÔÜąĸ™îĻCī_‡ü§ú!ŋÅUuü’Ší˛“FŌ‡“‚§ܞ ›¯ÔĐŋ…ęŖšeæLk“šhã›ÛˆŊ}hkļ=W•õ#Ū8ŠĻvÆT’ņfao–*r˙ēų53g7ÔĨ_rŋú)oN¸ɓ#1{˜`7Ŗ+Ų Ū[€ ČŨŲĢ=…‰ā!fŽĶekCƄ͘3éĶeJ†Jé'ąû6Ínl¨ą6đĄé&īHÁ{ęėīÄ@sHã˙?ˇí ūPaaÅ?Nŗ‚W­ÔŌ1ˆE36Č/†L›<€ēâĒ(øŦÅésÍėú<ũŽŖëč:ēŽŽŖëč:ēŽŽŖëøGeʔ)wÄÄĔå$ŅsØŅ­LÍËËĀĩėÄíȹ߉â_g׎-S/^<k™̤×ū:,æ5v­¤LÛŗįM\Ë8ÔJ¯}8q?{_ˇ‘eúęՏāZúü_Ŗ×f|đsz­ûŊeƔ)5¸–úō$vĪģS|ėڊ23//גOŋúsúžĸCÖû*ËĖ‹ŋĮĩ@Ü víWß[īËė{öėÂĩÄįréß;ļžŽ—Œe^™cõęU¸–pK"}ߑWį2†nĨeÎ)S&áš˙3×qz­˛ĖzßmeŽŧŧ,\‹Û1ŪķpQû{ŨōĘ\/ū€kžåã(Ãқ۴žËĘÜ{öėÆ5oō5ô}Į˙5Åzæ7•yV¯~×bÛ{”áZ{éhKļ˜ŽŖëč:ēŽŽŖëč:ēŽŽã˙Ėņ˙˜HVဝastropy-astropy-201cddb/astropy/wcs/tests/data/header_newlines.fits000066400000000000000000001111001507226315300257640ustar00rootroot00000000000000SIMPLE = T / Fits standard BITPIX = -64 / FOUR-BYTE SINGLE PRECISION FLOATING POINT NAXIS = 2 / STANDARD FITS FORMAT NAXIS1 = 1 / STANDARD FITS FORMAT NAXIS2 = 1 / 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 astropy-astropy-201cddb/astropy/wcs/tests/data/header_with_time.fits000066400000000000000000000207001507226315300261360ustar00rootroot00000000000000SIMPLE = 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 astropy-astropy-201cddb/astropy/wcs/tests/data/header_with_time_wcslib71.fits000066400000000000000000000212601507226315300276530ustar00rootroot00000000000000SIMPLE = 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 astropy-astropy-201cddb/astropy/wcs/tests/data/ie6d07ujq_wcs.fits000066400000000000000000000702001507226315300252270ustar00rootroot00000000000000SIMPLE = 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 astropy-astropy-201cddb/astropy/wcs/tests/data/irac_sip.hdr000066400000000000000000000456401507226315300242500ustar00rootroot00000000000000SIMPLE = 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 astropy-astropy-201cddb/astropy/wcs/tests/data/j94f05bgq_flt.fits000066400000000000000000002431001507226315300251160ustar00rootroot00000000000000SIMPLE = 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 astropy-astropy-201cddb/astropy/wcs/tests/data/locale.hdr000066400000000000000000000055001507226315300237050ustar00rootroot00000000000000WCSAXES = 2 / Number of coordinate axes CRPIX1 = 1920.5 / Pixel coordinate of reference point CRPIX2 = 1920.5 / Pixel coordinate of reference point CDELT1 = -0.000416666666667 / [deg] Coordinate increment at reference point CDELT2 = 0.000416666666667 / [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 = 36.661 / [deg] Coordinate value at reference point CRVAL2 = -4.48 / [deg] Coordinate value at reference point LONPOLE = 180 / [deg] Native longitude of celestial pole LATPOLE = -4.48 / [deg] Native latitude of celestial pole RESTFRQ = 0 / [Hz] Line rest frequency RESTWAV = 0 / [Hz] Line rest wavelength EQUINOX = 2000 / [yr] Equinox of equatorial coordinates END astropy-astropy-201cddb/astropy/wcs/tests/data/maps/000077500000000000000000000000001507226315300227075ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/wcs/tests/data/maps/1904-66_AIR.hdr000066400000000000000000000221001507226315300247220ustar00rootroot00000000000000SIMPLE = T BITPIX = -32 / IEEE (big-endian) 32-bit floating point data NAXIS = 2 NAXIS1 = 192 NAXIS2 = 192 BUNIT = 'JY/BEAM ' CTYPE1 = 'RA---AIR' CRPIX1 = -2.347545010835E+02 CDELT1 = -6.666666666667E-02 CRVAL1 = 0.000000000000E+00 CTYPE2 = 'DEC--AIR' CRPIX2 = 8.339330824422E+00 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_1 = 4.500000000000E+01 / Projection parameter 1 EQUINOX = 2.000000000000E+03 / Equinox of equatorial coordinates BMAJ = 2.399999936422E-01 / Beam major axis in degrees BMIN = 2.399999936422E-01 / Beam minor axis in degrees BPA = 0.000000000000E+00 / Beam position angle in degrees RESTFRQ = 1.420405750000E+09 / Line rest frequency, Hz HISTORY Parkes Multibeam continuum map HISTORY Formed on Mon 2004/02/09 01:43:31 GMT by "pksgridzilla" which was HISTORY compiled on Feb 9 2004 12:08:02 (local time) within HISTORY AIPS++ version 19.405.00 dated . HISTORY Polarization mode: A and B aggregated HISTORY Gridding parameters: HISTORY Method: WGTMED HISTORY Clip fraction: 0.000 HISTORY Tsys weighting: applied HISTORY Beam weight order: 1 HISTORY Beam FWHM: 14.4 arcmin HISTORY Beam normalization: applied HISTORY Smoothing kernel type: TOP-HAT HISTORY Kernel FWHM: 12.0 arcmin HISTORY Cutoff radius: 6.0 arcmin HISTORY Beam RSS cutoff: 0.0 HISTORY Input data sets: HISTORY 97-10-09_0356_193558-66_206a.sdfits HISTORY 97-10-12_0142_182123-66_193a.sdfits HISTORY 97-10-12_0151_182707-66_194a.sdfits HISTORY 97-10-12_0200_183252-66_195a.sdfits HISTORY 97-11-07_0510_183836-66_196a.sdfits HISTORY 97-11-07_0519_184420-66_197a.sdfits HISTORY 97-11-07_0528_185004-66_198a.sdfits HISTORY 97-11-07_0537_185548-66_199a.sdfits HISTORY 97-11-07_0546_190132-66_200a.sdfits HISTORY 97-11-07_0556_190717-66_201a.sdfits HISTORY 97-11-07_0645_191301-66_202a.sdfits HISTORY 97-11-07_0654_191845-66_203a.sdfits HISTORY 97-11-07_0703_192429-66_204a.sdfits HISTORY 97-11-07_0712_193013-66_205a.sdfits HISTORY 97-11-07_0724_194142-66_207a.sdfits HISTORY 97-11-18_0256_193815-66_206c.sdfits HISTORY 97-11-18_0306_194359-66_207c.sdfits HISTORY 97-11-19_0447_182341-66_193c.sdfits HISTORY 97-11-19_0456_182925-66_194c.sdfits HISTORY 97-11-19_0507_190350-66_200c.sdfits HISTORY 97-11-19_0516_190934-66_201c.sdfits HISTORY 97-11-19_0525_191519-66_202c.sdfits HISTORY 97-11-19_0534_192103-66_203c.sdfits HISTORY 97-11-19_0544_192647-66_204c.sdfits HISTORY 97-11-19_0553_193231-66_205c.sdfits HISTORY 97-11-19_0602_183509-66_195c.sdfits HISTORY 97-11-19_0612_184053-66_196c.sdfits HISTORY 97-11-19_0622_184638-66_197c.sdfits HISTORY 97-11-19_0631_185222-66_198c.sdfits HISTORY 97-11-19_0640_185806-66_199c.sdfits HISTORY 98-03-24_2107_193706-66_206b.sdfits HISTORY 98-03-24_2116_194251-66_207b.sdfits HISTORY 98-03-25_2020_190826-66_201b.sdfits HISTORY 98-03-25_2029_191410-66_202b.sdfits HISTORY 98-03-25_2038_191954-66_203b.sdfits HISTORY 98-03-25_2047_192538-66_204b.sdfits HISTORY 98-03-25_2056_193122-66_205b.sdfits HISTORY 98-03-26_2048_190459-66_200d.sdfits HISTORY 98-03-27_2034_191627-66_202d.sdfits HISTORY 98-03-27_2043_192212-66_203d.sdfits HISTORY 98-03-27_2052_192756-66_204d.sdfits HISTORY 98-03-27_2102_193340-66_205d.sdfits HISTORY 98-03-27_2111_193924-66_206d.sdfits HISTORY 98-03-27_2120_194508-66_207d.sdfits HISTORY 98-03-27_2130_191043-66_201d.sdfits HISTORY 98-05-10_2123_182232-66_193b.sdfits HISTORY 98-05-10_2133_182816-66_194b.sdfits HISTORY 98-05-10_2142_183400-66_195b.sdfits HISTORY 98-05-10_2151_183945-66_196b.sdfits HISTORY 98-05-10_2200_184529-66_197b.sdfits HISTORY 98-05-10_2209_185113-66_198b.sdfits HISTORY 98-05-10_2219_185657-66_199b.sdfits HISTORY 98-05-10_2228_190241-66_200b.sdfits HISTORY 98-05-13_2132_182450-66_193d.sdfits HISTORY 98-05-13_2151_183034-66_194d.sdfits HISTORY 98-05-13_2200_183618-66_195d.sdfits HISTORY 98-05-13_2210_184202-66_196d.sdfits HISTORY 98-05-13_2219_184746-66_197d.sdfits HISTORY 98-05-13_2228_185331-66_198d.sdfits HISTORY 98-05-13_2237_185915-66_199d.sdfits HISTORY 98-05-25_1711_182559-66_193e.sdfits HISTORY 98-05-25_1720_183143-66_194e.sdfits HISTORY 98-05-25_1729_183727-66_195e.sdfits HISTORY 98-05-25_1738_184311-66_196e.sdfits HISTORY 98-05-25_1747_184855-66_197e.sdfits HISTORY 98-05-25_1756_185439-66_198e.sdfits HISTORY 98-05-25_1806_190024-66_199e.sdfits HISTORY 98-05-25_1815_190608-66_200e.sdfits HISTORY 98-05-25_1824_191152-66_201e.sdfits HISTORY 98-05-25_1833_191736-66_202e.sdfits HISTORY 98-05-25_1842_192320-66_203e.sdfits HISTORY 98-05-25_1851_192905-66_204e.sdfits HISTORY 98-05-25_1901_193449-66_205e.sdfits HISTORY 98-05-25_1910_194033-66_206e.sdfits HISTORY 98-05-25_1919_194617-66_207e.sdfits HISTORY Original FITS filename "1904-66_AIR.continuum.fits". HISTORY Noise level of continuum map: 61 mJy (RMS) astropy-astropy-201cddb/astropy/wcs/tests/data/maps/1904-66_AIT.hdr000066400000000000000000000217601507226315300247370ustar00rootroot00000000000000SIMPLE = T BITPIX = -32 / IEEE (big-endian) 32-bit floating point data NAXIS = 2 NAXIS1 = 192 NAXIS2 = 192 BUNIT = 'JY/BEAM ' CTYPE1 = 'RA---AIT' CRPIX1 = -2.462317116277E+02 CDELT1 = -6.666666666667E-02 CRVAL1 = 0.000000000000E+00 CTYPE2 = 'DEC--AIT' CRPIX2 = 7.115850027049E+00 CDELT2 = 6.666666666667E-02 CRVAL2 = -9.000000000000E+01 LONPOLE = 1.800000000000E+02 / Native longitude of celestial pole LATPOLE = 0.000000000000E+00 / Native latitude of celestial pole EQUINOX = 2.000000000000E+03 / Equinox of equatorial coordinates BMAJ = 2.399999936422E-01 / Beam major axis in degrees BMIN = 2.399999936422E-01 / Beam minor axis in degrees BPA = 0.000000000000E+00 / Beam position angle in degrees RESTFRQ = 1.420405750000E+09 / Line rest frequency, Hz HISTORY Parkes Multibeam continuum map HISTORY Formed on Mon 2004/02/09 02:04:34 GMT by "pksgridzilla" which was HISTORY compiled on Feb 9 2004 12:08:02 (local time) within HISTORY AIPS++ version 19.405.00 dated . HISTORY Polarization mode: A and B aggregated HISTORY Gridding parameters: HISTORY Method: WGTMED HISTORY Clip fraction: 0.000 HISTORY Tsys weighting: applied HISTORY Beam weight order: 1 HISTORY Beam FWHM: 14.4 arcmin HISTORY Beam normalization: applied HISTORY Smoothing kernel type: TOP-HAT HISTORY Kernel FWHM: 12.0 arcmin HISTORY Cutoff radius: 6.0 arcmin HISTORY Beam RSS cutoff: 0.0 HISTORY Input data sets: HISTORY 97-10-09_0356_193558-66_206a.sdfits HISTORY 97-10-12_0142_182123-66_193a.sdfits HISTORY 97-10-12_0151_182707-66_194a.sdfits HISTORY 97-10-12_0200_183252-66_195a.sdfits HISTORY 97-11-07_0510_183836-66_196a.sdfits HISTORY 97-11-07_0519_184420-66_197a.sdfits HISTORY 97-11-07_0528_185004-66_198a.sdfits HISTORY 97-11-07_0537_185548-66_199a.sdfits HISTORY 97-11-07_0546_190132-66_200a.sdfits HISTORY 97-11-07_0556_190717-66_201a.sdfits HISTORY 97-11-07_0645_191301-66_202a.sdfits HISTORY 97-11-07_0654_191845-66_203a.sdfits HISTORY 97-11-07_0703_192429-66_204a.sdfits HISTORY 97-11-07_0712_193013-66_205a.sdfits HISTORY 97-11-07_0724_194142-66_207a.sdfits HISTORY 97-11-18_0256_193815-66_206c.sdfits HISTORY 97-11-18_0306_194359-66_207c.sdfits HISTORY 97-11-19_0447_182341-66_193c.sdfits HISTORY 97-11-19_0456_182925-66_194c.sdfits HISTORY 97-11-19_0507_190350-66_200c.sdfits HISTORY 97-11-19_0516_190934-66_201c.sdfits HISTORY 97-11-19_0525_191519-66_202c.sdfits HISTORY 97-11-19_0534_192103-66_203c.sdfits HISTORY 97-11-19_0544_192647-66_204c.sdfits HISTORY 97-11-19_0553_193231-66_205c.sdfits HISTORY 97-11-19_0602_183509-66_195c.sdfits HISTORY 97-11-19_0612_184053-66_196c.sdfits HISTORY 97-11-19_0622_184638-66_197c.sdfits HISTORY 97-11-19_0631_185222-66_198c.sdfits HISTORY 97-11-19_0640_185806-66_199c.sdfits HISTORY 98-03-24_2107_193706-66_206b.sdfits HISTORY 98-03-24_2116_194251-66_207b.sdfits HISTORY 98-03-25_2020_190826-66_201b.sdfits HISTORY 98-03-25_2029_191410-66_202b.sdfits HISTORY 98-03-25_2038_191954-66_203b.sdfits HISTORY 98-03-25_2047_192538-66_204b.sdfits HISTORY 98-03-25_2056_193122-66_205b.sdfits HISTORY 98-03-26_2048_190459-66_200d.sdfits HISTORY 98-03-27_2034_191627-66_202d.sdfits HISTORY 98-03-27_2043_192212-66_203d.sdfits HISTORY 98-03-27_2052_192756-66_204d.sdfits HISTORY 98-03-27_2102_193340-66_205d.sdfits HISTORY 98-03-27_2111_193924-66_206d.sdfits HISTORY 98-03-27_2120_194508-66_207d.sdfits HISTORY 98-03-27_2130_191043-66_201d.sdfits HISTORY 98-05-10_2123_182232-66_193b.sdfits HISTORY 98-05-10_2133_182816-66_194b.sdfits HISTORY 98-05-10_2142_183400-66_195b.sdfits HISTORY 98-05-10_2151_183945-66_196b.sdfits HISTORY 98-05-10_2200_184529-66_197b.sdfits HISTORY 98-05-10_2209_185113-66_198b.sdfits HISTORY 98-05-10_2219_185657-66_199b.sdfits HISTORY 98-05-10_2228_190241-66_200b.sdfits HISTORY 98-05-13_2132_182450-66_193d.sdfits HISTORY 98-05-13_2151_183034-66_194d.sdfits HISTORY 98-05-13_2200_183618-66_195d.sdfits HISTORY 98-05-13_2210_184202-66_196d.sdfits HISTORY 98-05-13_2219_184746-66_197d.sdfits HISTORY 98-05-13_2228_185331-66_198d.sdfits HISTORY 98-05-13_2237_185915-66_199d.sdfits HISTORY 98-05-25_1711_182559-66_193e.sdfits HISTORY 98-05-25_1720_183143-66_194e.sdfits HISTORY 98-05-25_1729_183727-66_195e.sdfits HISTORY 98-05-25_1738_184311-66_196e.sdfits HISTORY 98-05-25_1747_184855-66_197e.sdfits HISTORY 98-05-25_1756_185439-66_198e.sdfits HISTORY 98-05-25_1806_190024-66_199e.sdfits HISTORY 98-05-25_1815_190608-66_200e.sdfits HISTORY 98-05-25_1824_191152-66_201e.sdfits HISTORY 98-05-25_1833_191736-66_202e.sdfits HISTORY 98-05-25_1842_192320-66_203e.sdfits HISTORY 98-05-25_1851_192905-66_204e.sdfits HISTORY 98-05-25_1901_193449-66_205e.sdfits HISTORY 98-05-25_1910_194033-66_206e.sdfits HISTORY 98-05-25_1919_194617-66_207e.sdfits HISTORY Original FITS filename "1904-66_AIT.continuum.fits". HISTORY Noise level of continuum map: 61 mJy (RMS) astropy-astropy-201cddb/astropy/wcs/tests/data/maps/1904-66_ARC.hdr000066400000000000000000000217601507226315300247270ustar00rootroot00000000000000SIMPLE = T BITPIX = -32 / IEEE (big-endian) 32-bit floating point data NAXIS = 2 NAXIS1 = 192 NAXIS2 = 192 BUNIT = 'JY/BEAM ' CTYPE1 = 'RA---ARC' CRPIX1 = -2.469419019050E+02 CDELT1 = -6.666666666667E-02 CRVAL1 = 0.000000000000E+00 CTYPE2 = 'DEC--ARC' CRPIX2 = 5.082274450444E+00 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 EQUINOX = 2.000000000000E+03 / Equinox of equatorial coordinates BMAJ = 2.399999936422E-01 / Beam major axis in degrees BMIN = 2.399999936422E-01 / Beam minor axis in degrees BPA = 0.000000000000E+00 / Beam position angle in degrees RESTFRQ = 1.420405750000E+09 / Line rest frequency, Hz HISTORY Parkes Multibeam continuum map HISTORY Formed on Mon 2004/02/09 01:35:43 GMT by "pksgridzilla" which was HISTORY compiled on Feb 9 2004 12:08:02 (local time) within HISTORY AIPS++ version 19.405.00 dated . HISTORY Polarization mode: A and B aggregated HISTORY Gridding parameters: HISTORY Method: WGTMED HISTORY Clip fraction: 0.000 HISTORY Tsys weighting: applied HISTORY Beam weight order: 1 HISTORY Beam FWHM: 14.4 arcmin HISTORY Beam normalization: applied HISTORY Smoothing kernel type: TOP-HAT HISTORY Kernel FWHM: 12.0 arcmin HISTORY Cutoff radius: 6.0 arcmin HISTORY Beam RSS cutoff: 0.0 HISTORY Input data sets: HISTORY 97-10-09_0356_193558-66_206a.sdfits HISTORY 97-10-12_0142_182123-66_193a.sdfits HISTORY 97-10-12_0151_182707-66_194a.sdfits HISTORY 97-10-12_0200_183252-66_195a.sdfits HISTORY 97-11-07_0510_183836-66_196a.sdfits HISTORY 97-11-07_0519_184420-66_197a.sdfits HISTORY 97-11-07_0528_185004-66_198a.sdfits HISTORY 97-11-07_0537_185548-66_199a.sdfits HISTORY 97-11-07_0546_190132-66_200a.sdfits HISTORY 97-11-07_0556_190717-66_201a.sdfits HISTORY 97-11-07_0645_191301-66_202a.sdfits HISTORY 97-11-07_0654_191845-66_203a.sdfits HISTORY 97-11-07_0703_192429-66_204a.sdfits HISTORY 97-11-07_0712_193013-66_205a.sdfits HISTORY 97-11-07_0724_194142-66_207a.sdfits HISTORY 97-11-18_0256_193815-66_206c.sdfits HISTORY 97-11-18_0306_194359-66_207c.sdfits HISTORY 97-11-19_0447_182341-66_193c.sdfits HISTORY 97-11-19_0456_182925-66_194c.sdfits HISTORY 97-11-19_0507_190350-66_200c.sdfits HISTORY 97-11-19_0516_190934-66_201c.sdfits HISTORY 97-11-19_0525_191519-66_202c.sdfits HISTORY 97-11-19_0534_192103-66_203c.sdfits HISTORY 97-11-19_0544_192647-66_204c.sdfits HISTORY 97-11-19_0553_193231-66_205c.sdfits HISTORY 97-11-19_0602_183509-66_195c.sdfits HISTORY 97-11-19_0612_184053-66_196c.sdfits HISTORY 97-11-19_0622_184638-66_197c.sdfits HISTORY 97-11-19_0631_185222-66_198c.sdfits HISTORY 97-11-19_0640_185806-66_199c.sdfits HISTORY 98-03-24_2107_193706-66_206b.sdfits HISTORY 98-03-24_2116_194251-66_207b.sdfits HISTORY 98-03-25_2020_190826-66_201b.sdfits HISTORY 98-03-25_2029_191410-66_202b.sdfits HISTORY 98-03-25_2038_191954-66_203b.sdfits HISTORY 98-03-25_2047_192538-66_204b.sdfits HISTORY 98-03-25_2056_193122-66_205b.sdfits HISTORY 98-03-26_2048_190459-66_200d.sdfits HISTORY 98-03-27_2034_191627-66_202d.sdfits HISTORY 98-03-27_2043_192212-66_203d.sdfits HISTORY 98-03-27_2052_192756-66_204d.sdfits HISTORY 98-03-27_2102_193340-66_205d.sdfits HISTORY 98-03-27_2111_193924-66_206d.sdfits HISTORY 98-03-27_2120_194508-66_207d.sdfits HISTORY 98-03-27_2130_191043-66_201d.sdfits HISTORY 98-05-10_2123_182232-66_193b.sdfits HISTORY 98-05-10_2133_182816-66_194b.sdfits HISTORY 98-05-10_2142_183400-66_195b.sdfits HISTORY 98-05-10_2151_183945-66_196b.sdfits HISTORY 98-05-10_2200_184529-66_197b.sdfits HISTORY 98-05-10_2209_185113-66_198b.sdfits HISTORY 98-05-10_2219_185657-66_199b.sdfits HISTORY 98-05-10_2228_190241-66_200b.sdfits HISTORY 98-05-13_2132_182450-66_193d.sdfits HISTORY 98-05-13_2151_183034-66_194d.sdfits HISTORY 98-05-13_2200_183618-66_195d.sdfits HISTORY 98-05-13_2210_184202-66_196d.sdfits HISTORY 98-05-13_2219_184746-66_197d.sdfits HISTORY 98-05-13_2228_185331-66_198d.sdfits HISTORY 98-05-13_2237_185915-66_199d.sdfits HISTORY 98-05-25_1711_182559-66_193e.sdfits HISTORY 98-05-25_1720_183143-66_194e.sdfits HISTORY 98-05-25_1729_183727-66_195e.sdfits HISTORY 98-05-25_1738_184311-66_196e.sdfits HISTORY 98-05-25_1747_184855-66_197e.sdfits HISTORY 98-05-25_1756_185439-66_198e.sdfits HISTORY 98-05-25_1806_190024-66_199e.sdfits HISTORY 98-05-25_1815_190608-66_200e.sdfits HISTORY 98-05-25_1824_191152-66_201e.sdfits HISTORY 98-05-25_1833_191736-66_202e.sdfits HISTORY 98-05-25_1842_192320-66_203e.sdfits HISTORY 98-05-25_1851_192905-66_204e.sdfits HISTORY 98-05-25_1901_193449-66_205e.sdfits HISTORY 98-05-25_1910_194033-66_206e.sdfits HISTORY 98-05-25_1919_194617-66_207e.sdfits HISTORY Original FITS filename "1904-66_ARC.continuum.fits". HISTORY Noise level of continuum map: 61 mJy (RMS) astropy-astropy-201cddb/astropy/wcs/tests/data/maps/1904-66_AZP.hdr000066400000000000000000000222201507226315300247440ustar00rootroot00000000000000SIMPLE = T BITPIX = -32 / IEEE (big-endian) 32-bit floating point data NAXIS = 2 NAXIS1 = 192 NAXIS2 = 192 BUNIT = 'JY/BEAM ' CTYPE1 = 'RA---AZP' CRPIX1 = -2.541100848779E+02 CDELT1 = -6.666666666667E-02 CRVAL1 = 0.000000000000E+00 CTYPE2 = 'DEC--AZP' CRPIX2 = -1.134948542534E+01 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_1 = 2.000000000000E+00 / Projection parameter 1 PV2_2 = 3.000000000000E+01 / Projection parameter 2 EQUINOX = 2.000000000000E+03 / Equinox of equatorial coordinates BMAJ = 2.399999936422E-01 / Beam major axis in degrees BMIN = 2.399999936422E-01 / Beam minor axis in degrees BPA = 0.000000000000E+00 / Beam position angle in degrees RESTFRQ = 1.420405750000E+09 / Line rest frequency, Hz HISTORY Parkes Multibeam continuum map HISTORY Formed on Mon 2004/02/09 01:16:54 GMT by "pksgridzilla" which was HISTORY compiled on Feb 9 2004 12:08:02 (local time) within HISTORY AIPS++ version 19.405.00 dated . HISTORY Polarization mode: A and B aggregated HISTORY Gridding parameters: HISTORY Method: WGTMED HISTORY Clip fraction: 0.000 HISTORY Tsys weighting: applied HISTORY Beam weight order: 1 HISTORY Beam FWHM: 14.4 arcmin HISTORY Beam normalization: applied HISTORY Smoothing kernel type: TOP-HAT HISTORY Kernel FWHM: 12.0 arcmin HISTORY Cutoff radius: 6.0 arcmin HISTORY Beam RSS cutoff: 0.0 HISTORY Input data sets: HISTORY 97-10-09_0356_193558-66_206a.sdfits HISTORY 97-10-12_0142_182123-66_193a.sdfits HISTORY 97-10-12_0151_182707-66_194a.sdfits HISTORY 97-10-12_0200_183252-66_195a.sdfits HISTORY 97-11-07_0510_183836-66_196a.sdfits HISTORY 97-11-07_0519_184420-66_197a.sdfits HISTORY 97-11-07_0528_185004-66_198a.sdfits HISTORY 97-11-07_0537_185548-66_199a.sdfits HISTORY 97-11-07_0546_190132-66_200a.sdfits HISTORY 97-11-07_0556_190717-66_201a.sdfits HISTORY 97-11-07_0645_191301-66_202a.sdfits HISTORY 97-11-07_0654_191845-66_203a.sdfits HISTORY 97-11-07_0703_192429-66_204a.sdfits HISTORY 97-11-07_0712_193013-66_205a.sdfits HISTORY 97-11-07_0724_194142-66_207a.sdfits HISTORY 97-11-18_0256_193815-66_206c.sdfits HISTORY 97-11-18_0306_194359-66_207c.sdfits HISTORY 97-11-19_0447_182341-66_193c.sdfits HISTORY 97-11-19_0456_182925-66_194c.sdfits HISTORY 97-11-19_0507_190350-66_200c.sdfits HISTORY 97-11-19_0516_190934-66_201c.sdfits HISTORY 97-11-19_0525_191519-66_202c.sdfits HISTORY 97-11-19_0534_192103-66_203c.sdfits HISTORY 97-11-19_0544_192647-66_204c.sdfits HISTORY 97-11-19_0553_193231-66_205c.sdfits HISTORY 97-11-19_0602_183509-66_195c.sdfits HISTORY 97-11-19_0612_184053-66_196c.sdfits HISTORY 97-11-19_0622_184638-66_197c.sdfits HISTORY 97-11-19_0631_185222-66_198c.sdfits HISTORY 97-11-19_0640_185806-66_199c.sdfits HISTORY 98-03-24_2107_193706-66_206b.sdfits HISTORY 98-03-24_2116_194251-66_207b.sdfits HISTORY 98-03-25_2020_190826-66_201b.sdfits HISTORY 98-03-25_2029_191410-66_202b.sdfits HISTORY 98-03-25_2038_191954-66_203b.sdfits HISTORY 98-03-25_2047_192538-66_204b.sdfits HISTORY 98-03-25_2056_193122-66_205b.sdfits HISTORY 98-03-26_2048_190459-66_200d.sdfits HISTORY 98-03-27_2034_191627-66_202d.sdfits HISTORY 98-03-27_2043_192212-66_203d.sdfits HISTORY 98-03-27_2052_192756-66_204d.sdfits HISTORY 98-03-27_2102_193340-66_205d.sdfits HISTORY 98-03-27_2111_193924-66_206d.sdfits HISTORY 98-03-27_2120_194508-66_207d.sdfits HISTORY 98-03-27_2130_191043-66_201d.sdfits HISTORY 98-05-10_2123_182232-66_193b.sdfits HISTORY 98-05-10_2133_182816-66_194b.sdfits HISTORY 98-05-10_2142_183400-66_195b.sdfits HISTORY 98-05-10_2151_183945-66_196b.sdfits HISTORY 98-05-10_2200_184529-66_197b.sdfits HISTORY 98-05-10_2209_185113-66_198b.sdfits HISTORY 98-05-10_2219_185657-66_199b.sdfits HISTORY 98-05-10_2228_190241-66_200b.sdfits HISTORY 98-05-13_2132_182450-66_193d.sdfits HISTORY 98-05-13_2151_183034-66_194d.sdfits HISTORY 98-05-13_2200_183618-66_195d.sdfits HISTORY 98-05-13_2210_184202-66_196d.sdfits HISTORY 98-05-13_2219_184746-66_197d.sdfits HISTORY 98-05-13_2228_185331-66_198d.sdfits HISTORY 98-05-13_2237_185915-66_199d.sdfits HISTORY 98-05-25_1711_182559-66_193e.sdfits HISTORY 98-05-25_1720_183143-66_194e.sdfits HISTORY 98-05-25_1729_183727-66_195e.sdfits HISTORY 98-05-25_1738_184311-66_196e.sdfits HISTORY 98-05-25_1747_184855-66_197e.sdfits HISTORY 98-05-25_1756_185439-66_198e.sdfits HISTORY 98-05-25_1806_190024-66_199e.sdfits HISTORY 98-05-25_1815_190608-66_200e.sdfits HISTORY 98-05-25_1824_191152-66_201e.sdfits HISTORY 98-05-25_1833_191736-66_202e.sdfits HISTORY 98-05-25_1842_192320-66_203e.sdfits HISTORY 98-05-25_1851_192905-66_204e.sdfits HISTORY 98-05-25_1901_193449-66_205e.sdfits HISTORY 98-05-25_1910_194033-66_206e.sdfits HISTORY 98-05-25_1919_194617-66_207e.sdfits HISTORY Original FITS filename "1904-66_AZP.continuum.fits". HISTORY Noise level of continuum map: 61 mJy (RMS) astropy-astropy-201cddb/astropy/wcs/tests/data/maps/1904-66_BON.hdr000066400000000000000000000221001507226315300247250ustar00rootroot00000000000000SIMPLE = T BITPIX = -32 / IEEE (big-endian) 32-bit floating point data NAXIS = 2 NAXIS1 = 192 NAXIS2 = 192 BUNIT = 'JY/BEAM ' CTYPE1 = 'RA---BON' CRPIX1 = -2.431263982441E+02 CDELT1 = -6.666666666667E-02 CRVAL1 = 0.000000000000E+00 CTYPE2 = 'DEC--BON' CRPIX2 = -3.307412668190E+01 CDELT2 = 6.666666666667E-02 CRVAL2 = -9.000000000000E+01 LONPOLE = 1.800000000000E+02 / Native longitude of celestial pole LATPOLE = 0.000000000000E+00 / Native latitude of celestial pole PV2_1 = 4.500000000000E+01 / Projection parameter 1 EQUINOX = 2.000000000000E+03 / Equinox of equatorial coordinates BMAJ = 2.399999936422E-01 / Beam major axis in degrees BMIN = 2.399999936422E-01 / Beam minor axis in degrees BPA = 0.000000000000E+00 / Beam position angle in degrees RESTFRQ = 1.420405750000E+09 / Line rest frequency, Hz HISTORY Parkes Multibeam continuum map HISTORY Formed on Mon 2004/02/09 02:17:44 GMT by "pksgridzilla" which was HISTORY compiled on Feb 9 2004 12:08:02 (local time) within HISTORY AIPS++ version 19.405.00 dated . HISTORY Polarization mode: A and B aggregated HISTORY Gridding parameters: HISTORY Method: WGTMED HISTORY Clip fraction: 0.000 HISTORY Tsys weighting: applied HISTORY Beam weight order: 1 HISTORY Beam FWHM: 14.4 arcmin HISTORY Beam normalization: applied HISTORY Smoothing kernel type: TOP-HAT HISTORY Kernel FWHM: 12.0 arcmin HISTORY Cutoff radius: 6.0 arcmin HISTORY Beam RSS cutoff: 0.0 HISTORY Input data sets: HISTORY 97-10-09_0356_193558-66_206a.sdfits HISTORY 97-10-12_0142_182123-66_193a.sdfits HISTORY 97-10-12_0151_182707-66_194a.sdfits HISTORY 97-10-12_0200_183252-66_195a.sdfits HISTORY 97-11-07_0510_183836-66_196a.sdfits HISTORY 97-11-07_0519_184420-66_197a.sdfits HISTORY 97-11-07_0528_185004-66_198a.sdfits HISTORY 97-11-07_0537_185548-66_199a.sdfits HISTORY 97-11-07_0546_190132-66_200a.sdfits HISTORY 97-11-07_0556_190717-66_201a.sdfits HISTORY 97-11-07_0645_191301-66_202a.sdfits HISTORY 97-11-07_0654_191845-66_203a.sdfits HISTORY 97-11-07_0703_192429-66_204a.sdfits HISTORY 97-11-07_0712_193013-66_205a.sdfits HISTORY 97-11-07_0724_194142-66_207a.sdfits HISTORY 97-11-18_0256_193815-66_206c.sdfits HISTORY 97-11-18_0306_194359-66_207c.sdfits HISTORY 97-11-19_0447_182341-66_193c.sdfits HISTORY 97-11-19_0456_182925-66_194c.sdfits HISTORY 97-11-19_0507_190350-66_200c.sdfits HISTORY 97-11-19_0516_190934-66_201c.sdfits HISTORY 97-11-19_0525_191519-66_202c.sdfits HISTORY 97-11-19_0534_192103-66_203c.sdfits HISTORY 97-11-19_0544_192647-66_204c.sdfits HISTORY 97-11-19_0553_193231-66_205c.sdfits HISTORY 97-11-19_0602_183509-66_195c.sdfits HISTORY 97-11-19_0612_184053-66_196c.sdfits HISTORY 97-11-19_0622_184638-66_197c.sdfits HISTORY 97-11-19_0631_185222-66_198c.sdfits HISTORY 97-11-19_0640_185806-66_199c.sdfits HISTORY 98-03-24_2107_193706-66_206b.sdfits HISTORY 98-03-24_2116_194251-66_207b.sdfits HISTORY 98-03-25_2020_190826-66_201b.sdfits HISTORY 98-03-25_2029_191410-66_202b.sdfits HISTORY 98-03-25_2038_191954-66_203b.sdfits HISTORY 98-03-25_2047_192538-66_204b.sdfits HISTORY 98-03-25_2056_193122-66_205b.sdfits HISTORY 98-03-26_2048_190459-66_200d.sdfits HISTORY 98-03-27_2034_191627-66_202d.sdfits HISTORY 98-03-27_2043_192212-66_203d.sdfits HISTORY 98-03-27_2052_192756-66_204d.sdfits HISTORY 98-03-27_2102_193340-66_205d.sdfits HISTORY 98-03-27_2111_193924-66_206d.sdfits HISTORY 98-03-27_2120_194508-66_207d.sdfits HISTORY 98-03-27_2130_191043-66_201d.sdfits HISTORY 98-05-10_2123_182232-66_193b.sdfits HISTORY 98-05-10_2133_182816-66_194b.sdfits HISTORY 98-05-10_2142_183400-66_195b.sdfits HISTORY 98-05-10_2151_183945-66_196b.sdfits HISTORY 98-05-10_2200_184529-66_197b.sdfits HISTORY 98-05-10_2209_185113-66_198b.sdfits HISTORY 98-05-10_2219_185657-66_199b.sdfits HISTORY 98-05-10_2228_190241-66_200b.sdfits HISTORY 98-05-13_2132_182450-66_193d.sdfits HISTORY 98-05-13_2151_183034-66_194d.sdfits HISTORY 98-05-13_2200_183618-66_195d.sdfits HISTORY 98-05-13_2210_184202-66_196d.sdfits HISTORY 98-05-13_2219_184746-66_197d.sdfits HISTORY 98-05-13_2228_185331-66_198d.sdfits HISTORY 98-05-13_2237_185915-66_199d.sdfits HISTORY 98-05-25_1711_182559-66_193e.sdfits HISTORY 98-05-25_1720_183143-66_194e.sdfits HISTORY 98-05-25_1729_183727-66_195e.sdfits HISTORY 98-05-25_1738_184311-66_196e.sdfits HISTORY 98-05-25_1747_184855-66_197e.sdfits HISTORY 98-05-25_1756_185439-66_198e.sdfits HISTORY 98-05-25_1806_190024-66_199e.sdfits HISTORY 98-05-25_1815_190608-66_200e.sdfits HISTORY 98-05-25_1824_191152-66_201e.sdfits HISTORY 98-05-25_1833_191736-66_202e.sdfits HISTORY 98-05-25_1842_192320-66_203e.sdfits HISTORY 98-05-25_1851_192905-66_204e.sdfits HISTORY 98-05-25_1901_193449-66_205e.sdfits HISTORY 98-05-25_1910_194033-66_206e.sdfits HISTORY 98-05-25_1919_194617-66_207e.sdfits HISTORY Original FITS filename "1904-66_BON.continuum.fits". HISTORY Noise level of continuum map: 61 mJy (RMS) astropy-astropy-201cddb/astropy/wcs/tests/data/maps/1904-66_CAR.hdr000066400000000000000000000217601507226315300247270ustar00rootroot00000000000000SIMPLE = T BITPIX = -32 / IEEE (big-endian) 32-bit floating point data NAXIS = 2 NAXIS1 = 192 NAXIS2 = 192 BUNIT = 'JY/BEAM ' CTYPE1 = 'RA---CAR' CRPIX1 = -2.482173814412E+02 CDELT1 = -6.666666666667E-02 CRVAL1 = 0.000000000000E+00 CTYPE2 = 'DEC--CAR' CRPIX2 = 7.527038199745E+00 CDELT2 = 6.666666666667E-02 CRVAL2 = -9.000000000000E+01 LONPOLE = 1.800000000000E+02 / Native longitude of celestial pole LATPOLE = 0.000000000000E+00 / Native latitude of celestial pole EQUINOX = 2.000000000000E+03 / Equinox of equatorial coordinates BMAJ = 2.399999936422E-01 / Beam major axis in degrees BMIN = 2.399999936422E-01 / Beam minor axis in degrees BPA = 0.000000000000E+00 / Beam position angle in degrees RESTFRQ = 1.420405750000E+09 / Line rest frequency, Hz HISTORY Parkes Multibeam continuum map HISTORY Formed on Mon 2004/02/09 01:51:20 GMT by "pksgridzilla" which was HISTORY compiled on Feb 9 2004 12:08:02 (local time) within HISTORY AIPS++ version 19.405.00 dated . HISTORY Polarization mode: A and B aggregated HISTORY Gridding parameters: HISTORY Method: WGTMED HISTORY Clip fraction: 0.000 HISTORY Tsys weighting: applied HISTORY Beam weight order: 1 HISTORY Beam FWHM: 14.4 arcmin HISTORY Beam normalization: applied HISTORY Smoothing kernel type: TOP-HAT HISTORY Kernel FWHM: 12.0 arcmin HISTORY Cutoff radius: 6.0 arcmin HISTORY Beam RSS cutoff: 0.0 HISTORY Input data sets: HISTORY 97-10-09_0356_193558-66_206a.sdfits HISTORY 97-10-12_0142_182123-66_193a.sdfits HISTORY 97-10-12_0151_182707-66_194a.sdfits HISTORY 97-10-12_0200_183252-66_195a.sdfits HISTORY 97-11-07_0510_183836-66_196a.sdfits HISTORY 97-11-07_0519_184420-66_197a.sdfits HISTORY 97-11-07_0528_185004-66_198a.sdfits HISTORY 97-11-07_0537_185548-66_199a.sdfits HISTORY 97-11-07_0546_190132-66_200a.sdfits HISTORY 97-11-07_0556_190717-66_201a.sdfits HISTORY 97-11-07_0645_191301-66_202a.sdfits HISTORY 97-11-07_0654_191845-66_203a.sdfits HISTORY 97-11-07_0703_192429-66_204a.sdfits HISTORY 97-11-07_0712_193013-66_205a.sdfits HISTORY 97-11-07_0724_194142-66_207a.sdfits HISTORY 97-11-18_0256_193815-66_206c.sdfits HISTORY 97-11-18_0306_194359-66_207c.sdfits HISTORY 97-11-19_0447_182341-66_193c.sdfits HISTORY 97-11-19_0456_182925-66_194c.sdfits HISTORY 97-11-19_0507_190350-66_200c.sdfits HISTORY 97-11-19_0516_190934-66_201c.sdfits HISTORY 97-11-19_0525_191519-66_202c.sdfits HISTORY 97-11-19_0534_192103-66_203c.sdfits HISTORY 97-11-19_0544_192647-66_204c.sdfits HISTORY 97-11-19_0553_193231-66_205c.sdfits HISTORY 97-11-19_0602_183509-66_195c.sdfits HISTORY 97-11-19_0612_184053-66_196c.sdfits HISTORY 97-11-19_0622_184638-66_197c.sdfits HISTORY 97-11-19_0631_185222-66_198c.sdfits HISTORY 97-11-19_0640_185806-66_199c.sdfits HISTORY 98-03-24_2107_193706-66_206b.sdfits HISTORY 98-03-24_2116_194251-66_207b.sdfits HISTORY 98-03-25_2020_190826-66_201b.sdfits HISTORY 98-03-25_2029_191410-66_202b.sdfits HISTORY 98-03-25_2038_191954-66_203b.sdfits HISTORY 98-03-25_2047_192538-66_204b.sdfits HISTORY 98-03-25_2056_193122-66_205b.sdfits HISTORY 98-03-26_2048_190459-66_200d.sdfits HISTORY 98-03-27_2034_191627-66_202d.sdfits HISTORY 98-03-27_2043_192212-66_203d.sdfits HISTORY 98-03-27_2052_192756-66_204d.sdfits HISTORY 98-03-27_2102_193340-66_205d.sdfits HISTORY 98-03-27_2111_193924-66_206d.sdfits HISTORY 98-03-27_2120_194508-66_207d.sdfits HISTORY 98-03-27_2130_191043-66_201d.sdfits HISTORY 98-05-10_2123_182232-66_193b.sdfits HISTORY 98-05-10_2133_182816-66_194b.sdfits HISTORY 98-05-10_2142_183400-66_195b.sdfits HISTORY 98-05-10_2151_183945-66_196b.sdfits HISTORY 98-05-10_2200_184529-66_197b.sdfits HISTORY 98-05-10_2209_185113-66_198b.sdfits HISTORY 98-05-10_2219_185657-66_199b.sdfits HISTORY 98-05-10_2228_190241-66_200b.sdfits HISTORY 98-05-13_2132_182450-66_193d.sdfits HISTORY 98-05-13_2151_183034-66_194d.sdfits HISTORY 98-05-13_2200_183618-66_195d.sdfits HISTORY 98-05-13_2210_184202-66_196d.sdfits HISTORY 98-05-13_2219_184746-66_197d.sdfits HISTORY 98-05-13_2228_185331-66_198d.sdfits HISTORY 98-05-13_2237_185915-66_199d.sdfits HISTORY 98-05-25_1711_182559-66_193e.sdfits HISTORY 98-05-25_1720_183143-66_194e.sdfits HISTORY 98-05-25_1729_183727-66_195e.sdfits HISTORY 98-05-25_1738_184311-66_196e.sdfits HISTORY 98-05-25_1747_184855-66_197e.sdfits HISTORY 98-05-25_1756_185439-66_198e.sdfits HISTORY 98-05-25_1806_190024-66_199e.sdfits HISTORY 98-05-25_1815_190608-66_200e.sdfits HISTORY 98-05-25_1824_191152-66_201e.sdfits HISTORY 98-05-25_1833_191736-66_202e.sdfits HISTORY 98-05-25_1842_192320-66_203e.sdfits HISTORY 98-05-25_1851_192905-66_204e.sdfits HISTORY 98-05-25_1901_193449-66_205e.sdfits HISTORY 98-05-25_1910_194033-66_206e.sdfits HISTORY 98-05-25_1919_194617-66_207e.sdfits HISTORY Original FITS filename "1904-66_CAR.continuum.fits". HISTORY Noise level of continuum map: 61 mJy (RMS) astropy-astropy-201cddb/astropy/wcs/tests/data/maps/1904-66_CEA.hdr000066400000000000000000000221001507226315300246770ustar00rootroot00000000000000SIMPLE = T BITPIX = -32 / IEEE (big-endian) 32-bit floating point data NAXIS = 2 NAXIS1 = 192 NAXIS2 = 192 BUNIT = 'JY/BEAM ' CTYPE1 = 'RA---CEA' CRPIX1 = -2.482173814412E+02 CDELT1 = -6.666666666667E-02 CRVAL1 = 0.000000000000E+00 CTYPE2 = 'DEC--CEA' CRPIX2 = 7.688571124876E+00 CDELT2 = 6.666666666667E-02 CRVAL2 = -9.000000000000E+01 LONPOLE = 1.800000000000E+02 / Native longitude of celestial pole LATPOLE = 0.000000000000E+00 / Native latitude of celestial pole PV2_1 = 1.000000000000E+00 / Projection parameter 1 EQUINOX = 2.000000000000E+03 / Equinox of equatorial coordinates BMAJ = 2.399999936422E-01 / Beam major axis in degrees BMIN = 2.399999936422E-01 / Beam minor axis in degrees BPA = 0.000000000000E+00 / Beam position angle in degrees RESTFRQ = 1.420405750000E+09 / Line rest frequency, Hz HISTORY Parkes Multibeam continuum map HISTORY Formed on Mon 2004/02/09 01:48:41 GMT by "pksgridzilla" which was HISTORY compiled on Feb 9 2004 12:08:02 (local time) within HISTORY AIPS++ version 19.405.00 dated . HISTORY Polarization mode: A and B aggregated HISTORY Gridding parameters: HISTORY Method: WGTMED HISTORY Clip fraction: 0.000 HISTORY Tsys weighting: applied HISTORY Beam weight order: 1 HISTORY Beam FWHM: 14.4 arcmin HISTORY Beam normalization: applied HISTORY Smoothing kernel type: TOP-HAT HISTORY Kernel FWHM: 12.0 arcmin HISTORY Cutoff radius: 6.0 arcmin HISTORY Beam RSS cutoff: 0.0 HISTORY Input data sets: HISTORY 97-10-09_0356_193558-66_206a.sdfits HISTORY 97-10-12_0142_182123-66_193a.sdfits HISTORY 97-10-12_0151_182707-66_194a.sdfits HISTORY 97-10-12_0200_183252-66_195a.sdfits HISTORY 97-11-07_0510_183836-66_196a.sdfits HISTORY 97-11-07_0519_184420-66_197a.sdfits HISTORY 97-11-07_0528_185004-66_198a.sdfits HISTORY 97-11-07_0537_185548-66_199a.sdfits HISTORY 97-11-07_0546_190132-66_200a.sdfits HISTORY 97-11-07_0556_190717-66_201a.sdfits HISTORY 97-11-07_0645_191301-66_202a.sdfits HISTORY 97-11-07_0654_191845-66_203a.sdfits HISTORY 97-11-07_0703_192429-66_204a.sdfits HISTORY 97-11-07_0712_193013-66_205a.sdfits HISTORY 97-11-07_0724_194142-66_207a.sdfits HISTORY 97-11-18_0256_193815-66_206c.sdfits HISTORY 97-11-18_0306_194359-66_207c.sdfits HISTORY 97-11-19_0447_182341-66_193c.sdfits HISTORY 97-11-19_0456_182925-66_194c.sdfits HISTORY 97-11-19_0507_190350-66_200c.sdfits HISTORY 97-11-19_0516_190934-66_201c.sdfits HISTORY 97-11-19_0525_191519-66_202c.sdfits HISTORY 97-11-19_0534_192103-66_203c.sdfits HISTORY 97-11-19_0544_192647-66_204c.sdfits HISTORY 97-11-19_0553_193231-66_205c.sdfits HISTORY 97-11-19_0602_183509-66_195c.sdfits HISTORY 97-11-19_0612_184053-66_196c.sdfits HISTORY 97-11-19_0622_184638-66_197c.sdfits HISTORY 97-11-19_0631_185222-66_198c.sdfits HISTORY 97-11-19_0640_185806-66_199c.sdfits HISTORY 98-03-24_2107_193706-66_206b.sdfits HISTORY 98-03-24_2116_194251-66_207b.sdfits HISTORY 98-03-25_2020_190826-66_201b.sdfits HISTORY 98-03-25_2029_191410-66_202b.sdfits HISTORY 98-03-25_2038_191954-66_203b.sdfits HISTORY 98-03-25_2047_192538-66_204b.sdfits HISTORY 98-03-25_2056_193122-66_205b.sdfits HISTORY 98-03-26_2048_190459-66_200d.sdfits HISTORY 98-03-27_2034_191627-66_202d.sdfits HISTORY 98-03-27_2043_192212-66_203d.sdfits HISTORY 98-03-27_2052_192756-66_204d.sdfits HISTORY 98-03-27_2102_193340-66_205d.sdfits HISTORY 98-03-27_2111_193924-66_206d.sdfits HISTORY 98-03-27_2120_194508-66_207d.sdfits HISTORY 98-03-27_2130_191043-66_201d.sdfits HISTORY 98-05-10_2123_182232-66_193b.sdfits HISTORY 98-05-10_2133_182816-66_194b.sdfits HISTORY 98-05-10_2142_183400-66_195b.sdfits HISTORY 98-05-10_2151_183945-66_196b.sdfits HISTORY 98-05-10_2200_184529-66_197b.sdfits HISTORY 98-05-10_2209_185113-66_198b.sdfits HISTORY 98-05-10_2219_185657-66_199b.sdfits HISTORY 98-05-10_2228_190241-66_200b.sdfits HISTORY 98-05-13_2132_182450-66_193d.sdfits HISTORY 98-05-13_2151_183034-66_194d.sdfits HISTORY 98-05-13_2200_183618-66_195d.sdfits HISTORY 98-05-13_2210_184202-66_196d.sdfits HISTORY 98-05-13_2219_184746-66_197d.sdfits HISTORY 98-05-13_2228_185331-66_198d.sdfits HISTORY 98-05-13_2237_185915-66_199d.sdfits HISTORY 98-05-25_1711_182559-66_193e.sdfits HISTORY 98-05-25_1720_183143-66_194e.sdfits HISTORY 98-05-25_1729_183727-66_195e.sdfits HISTORY 98-05-25_1738_184311-66_196e.sdfits HISTORY 98-05-25_1747_184855-66_197e.sdfits HISTORY 98-05-25_1756_185439-66_198e.sdfits HISTORY 98-05-25_1806_190024-66_199e.sdfits HISTORY 98-05-25_1815_190608-66_200e.sdfits HISTORY 98-05-25_1824_191152-66_201e.sdfits HISTORY 98-05-25_1833_191736-66_202e.sdfits HISTORY 98-05-25_1842_192320-66_203e.sdfits HISTORY 98-05-25_1851_192905-66_204e.sdfits HISTORY 98-05-25_1901_193449-66_205e.sdfits HISTORY 98-05-25_1910_194033-66_206e.sdfits HISTORY 98-05-25_1919_194617-66_207e.sdfits HISTORY Original FITS filename "1904-66_CEA.continuum.fits". HISTORY Noise level of continuum map: 61 mJy (RMS) astropy-astropy-201cddb/astropy/wcs/tests/data/maps/1904-66_COD.hdr000066400000000000000000000222201507226315300247170ustar00rootroot00000000000000SIMPLE = T BITPIX = -32 / IEEE (big-endian) 32-bit floating point data NAXIS = 2 NAXIS1 = 192 NAXIS2 = 192 BUNIT = 'JY/BEAM ' CTYPE1 = 'RA---COD' CRPIX1 = -2.153431714695E+02 CDELT1 = -6.666666666667E-02 CRVAL1 = 0.000000000000E+00 CTYPE2 = 'DEC--COD' CRPIX2 = 1.561302682707E+01 CDELT2 = 6.666666666667E-02 CRVAL2 = -9.000000000000E+01 LONPOLE = 1.800000000000E+02 / Native longitude of celestial pole LATPOLE = -4.500000000000E+01 / Native latitude of celestial pole PV2_1 = 4.500000000000E+01 / Projection parameter 1 PV2_2 = 2.500000000000E+01 / Projection parameter 2 EQUINOX = 2.000000000000E+03 / Equinox of equatorial coordinates BMAJ = 2.399999936422E-01 / Beam major axis in degrees BMIN = 2.399999936422E-01 / Beam minor axis in degrees BPA = 0.000000000000E+00 / Beam position angle in degrees RESTFRQ = 1.420405750000E+09 / Line rest frequency, Hz HISTORY Parkes Multibeam continuum map HISTORY Formed on Mon 2004/02/09 02:12:30 GMT by "pksgridzilla" which was HISTORY compiled on Feb 9 2004 12:08:02 (local time) within HISTORY AIPS++ version 19.405.00 dated . HISTORY Polarization mode: A and B aggregated HISTORY Gridding parameters: HISTORY Method: WGTMED HISTORY Clip fraction: 0.000 HISTORY Tsys weighting: applied HISTORY Beam weight order: 1 HISTORY Beam FWHM: 14.4 arcmin HISTORY Beam normalization: applied HISTORY Smoothing kernel type: TOP-HAT HISTORY Kernel FWHM: 12.0 arcmin HISTORY Cutoff radius: 6.0 arcmin HISTORY Beam RSS cutoff: 0.0 HISTORY Input data sets: HISTORY 97-10-09_0356_193558-66_206a.sdfits HISTORY 97-10-12_0142_182123-66_193a.sdfits HISTORY 97-10-12_0151_182707-66_194a.sdfits HISTORY 97-10-12_0200_183252-66_195a.sdfits HISTORY 97-11-07_0510_183836-66_196a.sdfits HISTORY 97-11-07_0519_184420-66_197a.sdfits HISTORY 97-11-07_0528_185004-66_198a.sdfits HISTORY 97-11-07_0537_185548-66_199a.sdfits HISTORY 97-11-07_0546_190132-66_200a.sdfits HISTORY 97-11-07_0556_190717-66_201a.sdfits HISTORY 97-11-07_0645_191301-66_202a.sdfits HISTORY 97-11-07_0654_191845-66_203a.sdfits HISTORY 97-11-07_0703_192429-66_204a.sdfits HISTORY 97-11-07_0712_193013-66_205a.sdfits HISTORY 97-11-07_0724_194142-66_207a.sdfits HISTORY 97-11-18_0256_193815-66_206c.sdfits HISTORY 97-11-18_0306_194359-66_207c.sdfits HISTORY 97-11-19_0447_182341-66_193c.sdfits HISTORY 97-11-19_0456_182925-66_194c.sdfits HISTORY 97-11-19_0507_190350-66_200c.sdfits HISTORY 97-11-19_0516_190934-66_201c.sdfits HISTORY 97-11-19_0525_191519-66_202c.sdfits HISTORY 97-11-19_0534_192103-66_203c.sdfits HISTORY 97-11-19_0544_192647-66_204c.sdfits HISTORY 97-11-19_0553_193231-66_205c.sdfits HISTORY 97-11-19_0602_183509-66_195c.sdfits HISTORY 97-11-19_0612_184053-66_196c.sdfits HISTORY 97-11-19_0622_184638-66_197c.sdfits HISTORY 97-11-19_0631_185222-66_198c.sdfits HISTORY 97-11-19_0640_185806-66_199c.sdfits HISTORY 98-03-24_2107_193706-66_206b.sdfits HISTORY 98-03-24_2116_194251-66_207b.sdfits HISTORY 98-03-25_2020_190826-66_201b.sdfits HISTORY 98-03-25_2029_191410-66_202b.sdfits HISTORY 98-03-25_2038_191954-66_203b.sdfits HISTORY 98-03-25_2047_192538-66_204b.sdfits HISTORY 98-03-25_2056_193122-66_205b.sdfits HISTORY 98-03-26_2048_190459-66_200d.sdfits HISTORY 98-03-27_2034_191627-66_202d.sdfits HISTORY 98-03-27_2043_192212-66_203d.sdfits HISTORY 98-03-27_2052_192756-66_204d.sdfits HISTORY 98-03-27_2102_193340-66_205d.sdfits HISTORY 98-03-27_2111_193924-66_206d.sdfits HISTORY 98-03-27_2120_194508-66_207d.sdfits HISTORY 98-03-27_2130_191043-66_201d.sdfits HISTORY 98-05-10_2123_182232-66_193b.sdfits HISTORY 98-05-10_2133_182816-66_194b.sdfits HISTORY 98-05-10_2142_183400-66_195b.sdfits HISTORY 98-05-10_2151_183945-66_196b.sdfits HISTORY 98-05-10_2200_184529-66_197b.sdfits HISTORY 98-05-10_2209_185113-66_198b.sdfits HISTORY 98-05-10_2219_185657-66_199b.sdfits HISTORY 98-05-10_2228_190241-66_200b.sdfits HISTORY 98-05-13_2132_182450-66_193d.sdfits HISTORY 98-05-13_2151_183034-66_194d.sdfits HISTORY 98-05-13_2200_183618-66_195d.sdfits HISTORY 98-05-13_2210_184202-66_196d.sdfits HISTORY 98-05-13_2219_184746-66_197d.sdfits HISTORY 98-05-13_2228_185331-66_198d.sdfits HISTORY 98-05-13_2237_185915-66_199d.sdfits HISTORY 98-05-25_1711_182559-66_193e.sdfits HISTORY 98-05-25_1720_183143-66_194e.sdfits HISTORY 98-05-25_1729_183727-66_195e.sdfits HISTORY 98-05-25_1738_184311-66_196e.sdfits HISTORY 98-05-25_1747_184855-66_197e.sdfits HISTORY 98-05-25_1756_185439-66_198e.sdfits HISTORY 98-05-25_1806_190024-66_199e.sdfits HISTORY 98-05-25_1815_190608-66_200e.sdfits HISTORY 98-05-25_1824_191152-66_201e.sdfits HISTORY 98-05-25_1833_191736-66_202e.sdfits HISTORY 98-05-25_1842_192320-66_203e.sdfits HISTORY 98-05-25_1851_192905-66_204e.sdfits HISTORY 98-05-25_1901_193449-66_205e.sdfits HISTORY 98-05-25_1910_194033-66_206e.sdfits HISTORY 98-05-25_1919_194617-66_207e.sdfits HISTORY Original FITS filename "1904-66_COD.continuum.fits". HISTORY Noise level of continuum map: 61 mJy (RMS) astropy-astropy-201cddb/astropy/wcs/tests/data/maps/1904-66_COE.hdr000066400000000000000000000222201507226315300247200ustar00rootroot00000000000000SIMPLE = T BITPIX = -32 / IEEE (big-endian) 32-bit floating point data NAXIS = 2 NAXIS1 = 192 NAXIS2 = 192 BUNIT = 'JY/BEAM ' CTYPE1 = 'RA---COE' CRPIX1 = -2.230375366798E+02 CDELT1 = -6.666666666667E-02 CRVAL1 = 0.000000000000E+00 CTYPE2 = 'DEC--COE' CRPIX2 = -1.435249668783E+01 CDELT2 = 6.666666666667E-02 CRVAL2 = -9.000000000000E+01 LONPOLE = 1.800000000000E+02 / Native longitude of celestial pole LATPOLE = 4.500000000000E+01 / Native latitude of celestial pole PV2_1 = -4.500000000000E+01 / Projection parameter 1 PV2_2 = 2.500000000000E+01 / Projection parameter 2 EQUINOX = 2.000000000000E+03 / Equinox of equatorial coordinates BMAJ = 2.399999936422E-01 / Beam major axis in degrees BMIN = 2.399999936422E-01 / Beam minor axis in degrees BPA = 0.000000000000E+00 / Beam position angle in degrees RESTFRQ = 1.420405750000E+09 / Line rest frequency, Hz HISTORY Parkes Multibeam continuum map HISTORY Formed on Mon 2004/02/09 02:09:50 GMT by "pksgridzilla" which was HISTORY compiled on Feb 9 2004 12:08:02 (local time) within HISTORY AIPS++ version 19.405.00 dated . HISTORY Polarization mode: A and B aggregated HISTORY Gridding parameters: HISTORY Method: WGTMED HISTORY Clip fraction: 0.000 HISTORY Tsys weighting: applied HISTORY Beam weight order: 1 HISTORY Beam FWHM: 14.4 arcmin HISTORY Beam normalization: applied HISTORY Smoothing kernel type: TOP-HAT HISTORY Kernel FWHM: 12.0 arcmin HISTORY Cutoff radius: 6.0 arcmin HISTORY Beam RSS cutoff: 0.0 HISTORY Input data sets: HISTORY 97-10-09_0356_193558-66_206a.sdfits HISTORY 97-10-12_0142_182123-66_193a.sdfits HISTORY 97-10-12_0151_182707-66_194a.sdfits HISTORY 97-10-12_0200_183252-66_195a.sdfits HISTORY 97-11-07_0510_183836-66_196a.sdfits HISTORY 97-11-07_0519_184420-66_197a.sdfits HISTORY 97-11-07_0528_185004-66_198a.sdfits HISTORY 97-11-07_0537_185548-66_199a.sdfits HISTORY 97-11-07_0546_190132-66_200a.sdfits HISTORY 97-11-07_0556_190717-66_201a.sdfits HISTORY 97-11-07_0645_191301-66_202a.sdfits HISTORY 97-11-07_0654_191845-66_203a.sdfits HISTORY 97-11-07_0703_192429-66_204a.sdfits HISTORY 97-11-07_0712_193013-66_205a.sdfits HISTORY 97-11-07_0724_194142-66_207a.sdfits HISTORY 97-11-18_0256_193815-66_206c.sdfits HISTORY 97-11-18_0306_194359-66_207c.sdfits HISTORY 97-11-19_0447_182341-66_193c.sdfits HISTORY 97-11-19_0456_182925-66_194c.sdfits HISTORY 97-11-19_0507_190350-66_200c.sdfits HISTORY 97-11-19_0516_190934-66_201c.sdfits HISTORY 97-11-19_0525_191519-66_202c.sdfits HISTORY 97-11-19_0534_192103-66_203c.sdfits HISTORY 97-11-19_0544_192647-66_204c.sdfits HISTORY 97-11-19_0553_193231-66_205c.sdfits HISTORY 97-11-19_0602_183509-66_195c.sdfits HISTORY 97-11-19_0612_184053-66_196c.sdfits HISTORY 97-11-19_0622_184638-66_197c.sdfits HISTORY 97-11-19_0631_185222-66_198c.sdfits HISTORY 97-11-19_0640_185806-66_199c.sdfits HISTORY 98-03-24_2107_193706-66_206b.sdfits HISTORY 98-03-24_2116_194251-66_207b.sdfits HISTORY 98-03-25_2020_190826-66_201b.sdfits HISTORY 98-03-25_2029_191410-66_202b.sdfits HISTORY 98-03-25_2038_191954-66_203b.sdfits HISTORY 98-03-25_2047_192538-66_204b.sdfits HISTORY 98-03-25_2056_193122-66_205b.sdfits HISTORY 98-03-26_2048_190459-66_200d.sdfits HISTORY 98-03-27_2034_191627-66_202d.sdfits HISTORY 98-03-27_2043_192212-66_203d.sdfits HISTORY 98-03-27_2052_192756-66_204d.sdfits HISTORY 98-03-27_2102_193340-66_205d.sdfits HISTORY 98-03-27_2111_193924-66_206d.sdfits HISTORY 98-03-27_2120_194508-66_207d.sdfits HISTORY 98-03-27_2130_191043-66_201d.sdfits HISTORY 98-05-10_2123_182232-66_193b.sdfits HISTORY 98-05-10_2133_182816-66_194b.sdfits HISTORY 98-05-10_2142_183400-66_195b.sdfits HISTORY 98-05-10_2151_183945-66_196b.sdfits HISTORY 98-05-10_2200_184529-66_197b.sdfits HISTORY 98-05-10_2209_185113-66_198b.sdfits HISTORY 98-05-10_2219_185657-66_199b.sdfits HISTORY 98-05-10_2228_190241-66_200b.sdfits HISTORY 98-05-13_2132_182450-66_193d.sdfits HISTORY 98-05-13_2151_183034-66_194d.sdfits HISTORY 98-05-13_2200_183618-66_195d.sdfits HISTORY 98-05-13_2210_184202-66_196d.sdfits HISTORY 98-05-13_2219_184746-66_197d.sdfits HISTORY 98-05-13_2228_185331-66_198d.sdfits HISTORY 98-05-13_2237_185915-66_199d.sdfits HISTORY 98-05-25_1711_182559-66_193e.sdfits HISTORY 98-05-25_1720_183143-66_194e.sdfits HISTORY 98-05-25_1729_183727-66_195e.sdfits HISTORY 98-05-25_1738_184311-66_196e.sdfits HISTORY 98-05-25_1747_184855-66_197e.sdfits HISTORY 98-05-25_1756_185439-66_198e.sdfits HISTORY 98-05-25_1806_190024-66_199e.sdfits HISTORY 98-05-25_1815_190608-66_200e.sdfits HISTORY 98-05-25_1824_191152-66_201e.sdfits HISTORY 98-05-25_1833_191736-66_202e.sdfits HISTORY 98-05-25_1842_192320-66_203e.sdfits HISTORY 98-05-25_1851_192905-66_204e.sdfits HISTORY 98-05-25_1901_193449-66_205e.sdfits HISTORY 98-05-25_1910_194033-66_206e.sdfits HISTORY 98-05-25_1919_194617-66_207e.sdfits HISTORY Original FITS filename "1904-66_COE.continuum.fits". HISTORY Noise level of continuum map: 62 mJy (RMS) astropy-astropy-201cddb/astropy/wcs/tests/data/maps/1904-66_COO.hdr000066400000000000000000000222201507226315300247320ustar00rootroot00000000000000SIMPLE = T BITPIX = -32 / IEEE (big-endian) 32-bit floating point data NAXIS = 2 NAXIS1 = 192 NAXIS2 = 192 BUNIT = 'JY/BEAM ' CTYPE1 = 'RA---COO' CRPIX1 = -2.136486051767E+02 CDELT1 = -6.666666666667E-02 CRVAL1 = 0.000000000000E+00 CTYPE2 = 'DEC--COO' CRPIX2 = 1.292640949564E+01 CDELT2 = 6.666666666667E-02 CRVAL2 = -9.000000000000E+01 LONPOLE = 1.800000000000E+02 / Native longitude of celestial pole LATPOLE = -4.500000000000E+01 / Native latitude of celestial pole PV2_1 = 4.500000000000E+01 / Projection parameter 1 PV2_2 = 2.500000000000E+01 / Projection parameter 2 EQUINOX = 2.000000000000E+03 / Equinox of equatorial coordinates BMAJ = 2.399999936422E-01 / Beam major axis in degrees BMIN = 2.399999936422E-01 / Beam minor axis in degrees BPA = 0.000000000000E+00 / Beam position angle in degrees RESTFRQ = 1.420405750000E+09 / Line rest frequency, Hz HISTORY Parkes Multibeam continuum map HISTORY Formed on Mon 2004/02/09 02:15:07 GMT by "pksgridzilla" which was HISTORY compiled on Feb 9 2004 12:08:02 (local time) within HISTORY AIPS++ version 19.405.00 dated . HISTORY Polarization mode: A and B aggregated HISTORY Gridding parameters: HISTORY Method: WGTMED HISTORY Clip fraction: 0.000 HISTORY Tsys weighting: applied HISTORY Beam weight order: 1 HISTORY Beam FWHM: 14.4 arcmin HISTORY Beam normalization: applied HISTORY Smoothing kernel type: TOP-HAT HISTORY Kernel FWHM: 12.0 arcmin HISTORY Cutoff radius: 6.0 arcmin HISTORY Beam RSS cutoff: 0.0 HISTORY Input data sets: HISTORY 97-10-09_0356_193558-66_206a.sdfits HISTORY 97-10-12_0142_182123-66_193a.sdfits HISTORY 97-10-12_0151_182707-66_194a.sdfits HISTORY 97-10-12_0200_183252-66_195a.sdfits HISTORY 97-11-07_0510_183836-66_196a.sdfits HISTORY 97-11-07_0519_184420-66_197a.sdfits HISTORY 97-11-07_0528_185004-66_198a.sdfits HISTORY 97-11-07_0537_185548-66_199a.sdfits HISTORY 97-11-07_0546_190132-66_200a.sdfits HISTORY 97-11-07_0556_190717-66_201a.sdfits HISTORY 97-11-07_0645_191301-66_202a.sdfits HISTORY 97-11-07_0654_191845-66_203a.sdfits HISTORY 97-11-07_0703_192429-66_204a.sdfits HISTORY 97-11-07_0712_193013-66_205a.sdfits HISTORY 97-11-07_0724_194142-66_207a.sdfits HISTORY 97-11-18_0256_193815-66_206c.sdfits HISTORY 97-11-18_0306_194359-66_207c.sdfits HISTORY 97-11-19_0447_182341-66_193c.sdfits HISTORY 97-11-19_0456_182925-66_194c.sdfits HISTORY 97-11-19_0507_190350-66_200c.sdfits HISTORY 97-11-19_0516_190934-66_201c.sdfits HISTORY 97-11-19_0525_191519-66_202c.sdfits HISTORY 97-11-19_0534_192103-66_203c.sdfits HISTORY 97-11-19_0544_192647-66_204c.sdfits HISTORY 97-11-19_0553_193231-66_205c.sdfits HISTORY 97-11-19_0602_183509-66_195c.sdfits HISTORY 97-11-19_0612_184053-66_196c.sdfits HISTORY 97-11-19_0622_184638-66_197c.sdfits HISTORY 97-11-19_0631_185222-66_198c.sdfits HISTORY 97-11-19_0640_185806-66_199c.sdfits HISTORY 98-03-24_2107_193706-66_206b.sdfits HISTORY 98-03-24_2116_194251-66_207b.sdfits HISTORY 98-03-25_2020_190826-66_201b.sdfits HISTORY 98-03-25_2029_191410-66_202b.sdfits HISTORY 98-03-25_2038_191954-66_203b.sdfits HISTORY 98-03-25_2047_192538-66_204b.sdfits HISTORY 98-03-25_2056_193122-66_205b.sdfits HISTORY 98-03-26_2048_190459-66_200d.sdfits HISTORY 98-03-27_2034_191627-66_202d.sdfits HISTORY 98-03-27_2043_192212-66_203d.sdfits HISTORY 98-03-27_2052_192756-66_204d.sdfits HISTORY 98-03-27_2102_193340-66_205d.sdfits HISTORY 98-03-27_2111_193924-66_206d.sdfits HISTORY 98-03-27_2120_194508-66_207d.sdfits HISTORY 98-03-27_2130_191043-66_201d.sdfits HISTORY 98-05-10_2123_182232-66_193b.sdfits HISTORY 98-05-10_2133_182816-66_194b.sdfits HISTORY 98-05-10_2142_183400-66_195b.sdfits HISTORY 98-05-10_2151_183945-66_196b.sdfits HISTORY 98-05-10_2200_184529-66_197b.sdfits HISTORY 98-05-10_2209_185113-66_198b.sdfits HISTORY 98-05-10_2219_185657-66_199b.sdfits HISTORY 98-05-10_2228_190241-66_200b.sdfits HISTORY 98-05-13_2132_182450-66_193d.sdfits HISTORY 98-05-13_2151_183034-66_194d.sdfits HISTORY 98-05-13_2200_183618-66_195d.sdfits HISTORY 98-05-13_2210_184202-66_196d.sdfits HISTORY 98-05-13_2219_184746-66_197d.sdfits HISTORY 98-05-13_2228_185331-66_198d.sdfits HISTORY 98-05-13_2237_185915-66_199d.sdfits HISTORY 98-05-25_1711_182559-66_193e.sdfits HISTORY 98-05-25_1720_183143-66_194e.sdfits HISTORY 98-05-25_1729_183727-66_195e.sdfits HISTORY 98-05-25_1738_184311-66_196e.sdfits HISTORY 98-05-25_1747_184855-66_197e.sdfits HISTORY 98-05-25_1756_185439-66_198e.sdfits HISTORY 98-05-25_1806_190024-66_199e.sdfits HISTORY 98-05-25_1815_190608-66_200e.sdfits HISTORY 98-05-25_1824_191152-66_201e.sdfits HISTORY 98-05-25_1833_191736-66_202e.sdfits HISTORY 98-05-25_1842_192320-66_203e.sdfits HISTORY 98-05-25_1851_192905-66_204e.sdfits HISTORY 98-05-25_1901_193449-66_205e.sdfits HISTORY 98-05-25_1910_194033-66_206e.sdfits HISTORY 98-05-25_1919_194617-66_207e.sdfits HISTORY Original FITS filename "1904-66_COO.continuum.fits". HISTORY Noise level of continuum map: 62 mJy (RMS) astropy-astropy-201cddb/astropy/wcs/tests/data/maps/1904-66_COP.hdr000066400000000000000000000222201507226315300247330ustar00rootroot00000000000000SIMPLE = T BITPIX = -32 / IEEE (big-endian) 32-bit floating point data NAXIS = 2 NAXIS1 = 192 NAXIS2 = 192 BUNIT = 'JY/BEAM ' CTYPE1 = 'RA---COP' CRPIX1 = -2.151923139086E+02 CDELT1 = -6.666666666667E-02 CRVAL1 = 0.000000000000E+00 CTYPE2 = 'DEC--COP' CRPIX2 = 1.505768272737E+01 CDELT2 = 6.666666666667E-02 CRVAL2 = -9.000000000000E+01 LONPOLE = 1.800000000000E+02 / Native longitude of celestial pole LATPOLE = -4.500000000000E+01 / Native latitude of celestial pole PV2_1 = 4.500000000000E+01 / Projection parameter 1 PV2_2 = 2.500000000000E+01 / Projection parameter 2 EQUINOX = 2.000000000000E+03 / Equinox of equatorial coordinates BMAJ = 2.399999936422E-01 / Beam major axis in degrees BMIN = 2.399999936422E-01 / Beam minor axis in degrees BPA = 0.000000000000E+00 / Beam position angle in degrees RESTFRQ = 1.420405750000E+09 / Line rest frequency, Hz HISTORY Parkes Multibeam continuum map HISTORY Formed on Mon 2004/02/09 02:07:13 GMT by "pksgridzilla" which was HISTORY compiled on Feb 9 2004 12:08:02 (local time) within HISTORY AIPS++ version 19.405.00 dated . HISTORY Polarization mode: A and B aggregated HISTORY Gridding parameters: HISTORY Method: WGTMED HISTORY Clip fraction: 0.000 HISTORY Tsys weighting: applied HISTORY Beam weight order: 1 HISTORY Beam FWHM: 14.4 arcmin HISTORY Beam normalization: applied HISTORY Smoothing kernel type: TOP-HAT HISTORY Kernel FWHM: 12.0 arcmin HISTORY Cutoff radius: 6.0 arcmin HISTORY Beam RSS cutoff: 0.0 HISTORY Input data sets: HISTORY 97-10-09_0356_193558-66_206a.sdfits HISTORY 97-10-12_0142_182123-66_193a.sdfits HISTORY 97-10-12_0151_182707-66_194a.sdfits HISTORY 97-10-12_0200_183252-66_195a.sdfits HISTORY 97-11-07_0510_183836-66_196a.sdfits HISTORY 97-11-07_0519_184420-66_197a.sdfits HISTORY 97-11-07_0528_185004-66_198a.sdfits HISTORY 97-11-07_0537_185548-66_199a.sdfits HISTORY 97-11-07_0546_190132-66_200a.sdfits HISTORY 97-11-07_0556_190717-66_201a.sdfits HISTORY 97-11-07_0645_191301-66_202a.sdfits HISTORY 97-11-07_0654_191845-66_203a.sdfits HISTORY 97-11-07_0703_192429-66_204a.sdfits HISTORY 97-11-07_0712_193013-66_205a.sdfits HISTORY 97-11-07_0724_194142-66_207a.sdfits HISTORY 97-11-18_0256_193815-66_206c.sdfits HISTORY 97-11-18_0306_194359-66_207c.sdfits HISTORY 97-11-19_0447_182341-66_193c.sdfits HISTORY 97-11-19_0456_182925-66_194c.sdfits HISTORY 97-11-19_0507_190350-66_200c.sdfits HISTORY 97-11-19_0516_190934-66_201c.sdfits HISTORY 97-11-19_0525_191519-66_202c.sdfits HISTORY 97-11-19_0534_192103-66_203c.sdfits HISTORY 97-11-19_0544_192647-66_204c.sdfits HISTORY 97-11-19_0553_193231-66_205c.sdfits HISTORY 97-11-19_0602_183509-66_195c.sdfits HISTORY 97-11-19_0612_184053-66_196c.sdfits HISTORY 97-11-19_0622_184638-66_197c.sdfits HISTORY 97-11-19_0631_185222-66_198c.sdfits HISTORY 97-11-19_0640_185806-66_199c.sdfits HISTORY 98-03-24_2107_193706-66_206b.sdfits HISTORY 98-03-24_2116_194251-66_207b.sdfits HISTORY 98-03-25_2020_190826-66_201b.sdfits HISTORY 98-03-25_2029_191410-66_202b.sdfits HISTORY 98-03-25_2038_191954-66_203b.sdfits HISTORY 98-03-25_2047_192538-66_204b.sdfits HISTORY 98-03-25_2056_193122-66_205b.sdfits HISTORY 98-03-26_2048_190459-66_200d.sdfits HISTORY 98-03-27_2034_191627-66_202d.sdfits HISTORY 98-03-27_2043_192212-66_203d.sdfits HISTORY 98-03-27_2052_192756-66_204d.sdfits HISTORY 98-03-27_2102_193340-66_205d.sdfits HISTORY 98-03-27_2111_193924-66_206d.sdfits HISTORY 98-03-27_2120_194508-66_207d.sdfits HISTORY 98-03-27_2130_191043-66_201d.sdfits HISTORY 98-05-10_2123_182232-66_193b.sdfits HISTORY 98-05-10_2133_182816-66_194b.sdfits HISTORY 98-05-10_2142_183400-66_195b.sdfits HISTORY 98-05-10_2151_183945-66_196b.sdfits HISTORY 98-05-10_2200_184529-66_197b.sdfits HISTORY 98-05-10_2209_185113-66_198b.sdfits HISTORY 98-05-10_2219_185657-66_199b.sdfits HISTORY 98-05-10_2228_190241-66_200b.sdfits HISTORY 98-05-13_2132_182450-66_193d.sdfits HISTORY 98-05-13_2151_183034-66_194d.sdfits HISTORY 98-05-13_2200_183618-66_195d.sdfits HISTORY 98-05-13_2210_184202-66_196d.sdfits HISTORY 98-05-13_2219_184746-66_197d.sdfits HISTORY 98-05-13_2228_185331-66_198d.sdfits HISTORY 98-05-13_2237_185915-66_199d.sdfits HISTORY 98-05-25_1711_182559-66_193e.sdfits HISTORY 98-05-25_1720_183143-66_194e.sdfits HISTORY 98-05-25_1729_183727-66_195e.sdfits HISTORY 98-05-25_1738_184311-66_196e.sdfits HISTORY 98-05-25_1747_184855-66_197e.sdfits HISTORY 98-05-25_1756_185439-66_198e.sdfits HISTORY 98-05-25_1806_190024-66_199e.sdfits HISTORY 98-05-25_1815_190608-66_200e.sdfits HISTORY 98-05-25_1824_191152-66_201e.sdfits HISTORY 98-05-25_1833_191736-66_202e.sdfits HISTORY 98-05-25_1842_192320-66_203e.sdfits HISTORY 98-05-25_1851_192905-66_204e.sdfits HISTORY 98-05-25_1901_193449-66_205e.sdfits HISTORY 98-05-25_1910_194033-66_206e.sdfits HISTORY 98-05-25_1919_194617-66_207e.sdfits HISTORY Original FITS filename "1904-66_COP.continuum.fits". HISTORY Noise level of continuum map: 61 mJy (RMS) astropy-astropy-201cddb/astropy/wcs/tests/data/maps/1904-66_CSC.hdr000066400000000000000000000217601507226315300247320ustar00rootroot00000000000000SIMPLE = T BITPIX = -32 / IEEE (big-endian) 32-bit floating point data NAXIS = 2 NAXIS1 = 192 NAXIS2 = 192 BUNIT = 'JY/BEAM ' CTYPE1 = 'RA---CSC' CRPIX1 = -2.686531829635E+02 CDELT1 = -6.666666666667E-02 CRVAL1 = 0.000000000000E+00 CTYPE2 = 'DEC--CSC' CRPIX2 = -7.043520126533E+00 CDELT2 = 6.666666666667E-02 CRVAL2 = -9.000000000000E+01 LONPOLE = 1.800000000000E+02 / Native longitude of celestial pole LATPOLE = 0.000000000000E+00 / Native latitude of celestial pole EQUINOX = 2.000000000000E+03 / Equinox of equatorial coordinates BMAJ = 2.399999936422E-01 / Beam major axis in degrees BMIN = 2.399999936422E-01 / Beam minor axis in degrees BPA = 0.000000000000E+00 / Beam position angle in degrees RESTFRQ = 1.420405750000E+09 / Line rest frequency, Hz HISTORY Parkes Multibeam continuum map HISTORY Formed on Mon 2004/02/09 02:25:39 GMT by "pksgridzilla" which was HISTORY compiled on Feb 9 2004 12:08:02 (local time) within HISTORY AIPS++ version 19.405.00 dated . HISTORY Polarization mode: A and B aggregated HISTORY Gridding parameters: HISTORY Method: WGTMED HISTORY Clip fraction: 0.000 HISTORY Tsys weighting: applied HISTORY Beam weight order: 1 HISTORY Beam FWHM: 14.4 arcmin HISTORY Beam normalization: applied HISTORY Smoothing kernel type: TOP-HAT HISTORY Kernel FWHM: 12.0 arcmin HISTORY Cutoff radius: 6.0 arcmin HISTORY Beam RSS cutoff: 0.0 HISTORY Input data sets: HISTORY 97-10-09_0356_193558-66_206a.sdfits HISTORY 97-10-12_0142_182123-66_193a.sdfits HISTORY 97-10-12_0151_182707-66_194a.sdfits HISTORY 97-10-12_0200_183252-66_195a.sdfits HISTORY 97-11-07_0510_183836-66_196a.sdfits HISTORY 97-11-07_0519_184420-66_197a.sdfits HISTORY 97-11-07_0528_185004-66_198a.sdfits HISTORY 97-11-07_0537_185548-66_199a.sdfits HISTORY 97-11-07_0546_190132-66_200a.sdfits HISTORY 97-11-07_0556_190717-66_201a.sdfits HISTORY 97-11-07_0645_191301-66_202a.sdfits HISTORY 97-11-07_0654_191845-66_203a.sdfits HISTORY 97-11-07_0703_192429-66_204a.sdfits HISTORY 97-11-07_0712_193013-66_205a.sdfits HISTORY 97-11-07_0724_194142-66_207a.sdfits HISTORY 97-11-18_0256_193815-66_206c.sdfits HISTORY 97-11-18_0306_194359-66_207c.sdfits HISTORY 97-11-19_0447_182341-66_193c.sdfits HISTORY 97-11-19_0456_182925-66_194c.sdfits HISTORY 97-11-19_0507_190350-66_200c.sdfits HISTORY 97-11-19_0516_190934-66_201c.sdfits HISTORY 97-11-19_0525_191519-66_202c.sdfits HISTORY 97-11-19_0534_192103-66_203c.sdfits HISTORY 97-11-19_0544_192647-66_204c.sdfits HISTORY 97-11-19_0553_193231-66_205c.sdfits HISTORY 97-11-19_0602_183509-66_195c.sdfits HISTORY 97-11-19_0612_184053-66_196c.sdfits HISTORY 97-11-19_0622_184638-66_197c.sdfits HISTORY 97-11-19_0631_185222-66_198c.sdfits HISTORY 97-11-19_0640_185806-66_199c.sdfits HISTORY 98-03-24_2107_193706-66_206b.sdfits HISTORY 98-03-24_2116_194251-66_207b.sdfits HISTORY 98-03-25_2020_190826-66_201b.sdfits HISTORY 98-03-25_2029_191410-66_202b.sdfits HISTORY 98-03-25_2038_191954-66_203b.sdfits HISTORY 98-03-25_2047_192538-66_204b.sdfits HISTORY 98-03-25_2056_193122-66_205b.sdfits HISTORY 98-03-26_2048_190459-66_200d.sdfits HISTORY 98-03-27_2034_191627-66_202d.sdfits HISTORY 98-03-27_2043_192212-66_203d.sdfits HISTORY 98-03-27_2052_192756-66_204d.sdfits HISTORY 98-03-27_2102_193340-66_205d.sdfits HISTORY 98-03-27_2111_193924-66_206d.sdfits HISTORY 98-03-27_2120_194508-66_207d.sdfits HISTORY 98-03-27_2130_191043-66_201d.sdfits HISTORY 98-05-10_2123_182232-66_193b.sdfits HISTORY 98-05-10_2133_182816-66_194b.sdfits HISTORY 98-05-10_2142_183400-66_195b.sdfits HISTORY 98-05-10_2151_183945-66_196b.sdfits HISTORY 98-05-10_2200_184529-66_197b.sdfits HISTORY 98-05-10_2209_185113-66_198b.sdfits HISTORY 98-05-10_2219_185657-66_199b.sdfits HISTORY 98-05-10_2228_190241-66_200b.sdfits HISTORY 98-05-13_2132_182450-66_193d.sdfits HISTORY 98-05-13_2151_183034-66_194d.sdfits HISTORY 98-05-13_2200_183618-66_195d.sdfits HISTORY 98-05-13_2210_184202-66_196d.sdfits HISTORY 98-05-13_2219_184746-66_197d.sdfits HISTORY 98-05-13_2228_185331-66_198d.sdfits HISTORY 98-05-13_2237_185915-66_199d.sdfits HISTORY 98-05-25_1711_182559-66_193e.sdfits HISTORY 98-05-25_1720_183143-66_194e.sdfits HISTORY 98-05-25_1729_183727-66_195e.sdfits HISTORY 98-05-25_1738_184311-66_196e.sdfits HISTORY 98-05-25_1747_184855-66_197e.sdfits HISTORY 98-05-25_1756_185439-66_198e.sdfits HISTORY 98-05-25_1806_190024-66_199e.sdfits HISTORY 98-05-25_1815_190608-66_200e.sdfits HISTORY 98-05-25_1824_191152-66_201e.sdfits HISTORY 98-05-25_1833_191736-66_202e.sdfits HISTORY 98-05-25_1842_192320-66_203e.sdfits HISTORY 98-05-25_1851_192905-66_204e.sdfits HISTORY 98-05-25_1901_193449-66_205e.sdfits HISTORY 98-05-25_1910_194033-66_206e.sdfits HISTORY 98-05-25_1919_194617-66_207e.sdfits HISTORY Original FITS filename "1904-66_CSC.continuum.fits". HISTORY Noise level of continuum map: 61 mJy (RMS) astropy-astropy-201cddb/astropy/wcs/tests/data/maps/1904-66_CYP.hdr000066400000000000000000000222201507226315300247450ustar00rootroot00000000000000SIMPLE = T BITPIX = -32 / IEEE (big-endian) 32-bit floating point data NAXIS = 2 NAXIS1 = 192 NAXIS2 = 192 BUNIT = 'JY/BEAM ' CTYPE1 = 'RA---CYP' CRPIX1 = -1.471055514007E+02 CDELT1 = -6.666666666667E-02 CRVAL1 = 0.000000000000E+00 CTYPE2 = 'DEC--CYP' CRPIX2 = 2.056099939277E+01 CDELT2 = 6.666666666667E-02 CRVAL2 = -9.000000000000E+01 LONPOLE = 1.800000000000E+02 / Native longitude of celestial pole LATPOLE = 0.000000000000E+00 / Native latitude of celestial pole PV2_1 = 1.000000000000E+00 / Projection parameter 1 PV2_2 = 7.071067811870E-01 / Projection parameter 2 EQUINOX = 2.000000000000E+03 / Equinox of equatorial coordinates BMAJ = 2.399999936422E-01 / Beam major axis in degrees BMIN = 2.399999936422E-01 / Beam minor axis in degrees BPA = 0.000000000000E+00 / Beam position angle in degrees RESTFRQ = 1.420405750000E+09 / Line rest frequency, Hz HISTORY Parkes Multibeam continuum map HISTORY Formed on Mon 2004/02/09 01:46:07 GMT by "pksgridzilla" which was HISTORY compiled on Feb 9 2004 12:08:02 (local time) within HISTORY AIPS++ version 19.405.00 dated . HISTORY Polarization mode: A and B aggregated HISTORY Gridding parameters: HISTORY Method: WGTMED HISTORY Clip fraction: 0.000 HISTORY Tsys weighting: applied HISTORY Beam weight order: 1 HISTORY Beam FWHM: 14.4 arcmin HISTORY Beam normalization: applied HISTORY Smoothing kernel type: TOP-HAT HISTORY Kernel FWHM: 12.0 arcmin HISTORY Cutoff radius: 6.0 arcmin HISTORY Beam RSS cutoff: 0.0 HISTORY Input data sets: HISTORY 97-10-09_0356_193558-66_206a.sdfits HISTORY 97-10-12_0142_182123-66_193a.sdfits HISTORY 97-10-12_0151_182707-66_194a.sdfits HISTORY 97-10-12_0200_183252-66_195a.sdfits HISTORY 97-11-07_0510_183836-66_196a.sdfits HISTORY 97-11-07_0519_184420-66_197a.sdfits HISTORY 97-11-07_0528_185004-66_198a.sdfits HISTORY 97-11-07_0537_185548-66_199a.sdfits HISTORY 97-11-07_0546_190132-66_200a.sdfits HISTORY 97-11-07_0556_190717-66_201a.sdfits HISTORY 97-11-07_0645_191301-66_202a.sdfits HISTORY 97-11-07_0654_191845-66_203a.sdfits HISTORY 97-11-07_0703_192429-66_204a.sdfits HISTORY 97-11-07_0712_193013-66_205a.sdfits HISTORY 97-11-07_0724_194142-66_207a.sdfits HISTORY 97-11-18_0256_193815-66_206c.sdfits HISTORY 97-11-18_0306_194359-66_207c.sdfits HISTORY 97-11-19_0447_182341-66_193c.sdfits HISTORY 97-11-19_0456_182925-66_194c.sdfits HISTORY 97-11-19_0507_190350-66_200c.sdfits HISTORY 97-11-19_0516_190934-66_201c.sdfits HISTORY 97-11-19_0525_191519-66_202c.sdfits HISTORY 97-11-19_0534_192103-66_203c.sdfits HISTORY 97-11-19_0544_192647-66_204c.sdfits HISTORY 97-11-19_0553_193231-66_205c.sdfits HISTORY 97-11-19_0602_183509-66_195c.sdfits HISTORY 97-11-19_0612_184053-66_196c.sdfits HISTORY 97-11-19_0622_184638-66_197c.sdfits HISTORY 97-11-19_0631_185222-66_198c.sdfits HISTORY 97-11-19_0640_185806-66_199c.sdfits HISTORY 98-03-24_2107_193706-66_206b.sdfits HISTORY 98-03-24_2116_194251-66_207b.sdfits HISTORY 98-03-25_2020_190826-66_201b.sdfits HISTORY 98-03-25_2029_191410-66_202b.sdfits HISTORY 98-03-25_2038_191954-66_203b.sdfits HISTORY 98-03-25_2047_192538-66_204b.sdfits HISTORY 98-03-25_2056_193122-66_205b.sdfits HISTORY 98-03-26_2048_190459-66_200d.sdfits HISTORY 98-03-27_2034_191627-66_202d.sdfits HISTORY 98-03-27_2043_192212-66_203d.sdfits HISTORY 98-03-27_2052_192756-66_204d.sdfits HISTORY 98-03-27_2102_193340-66_205d.sdfits HISTORY 98-03-27_2111_193924-66_206d.sdfits HISTORY 98-03-27_2120_194508-66_207d.sdfits HISTORY 98-03-27_2130_191043-66_201d.sdfits HISTORY 98-05-10_2123_182232-66_193b.sdfits HISTORY 98-05-10_2133_182816-66_194b.sdfits HISTORY 98-05-10_2142_183400-66_195b.sdfits HISTORY 98-05-10_2151_183945-66_196b.sdfits HISTORY 98-05-10_2200_184529-66_197b.sdfits HISTORY 98-05-10_2209_185113-66_198b.sdfits HISTORY 98-05-10_2219_185657-66_199b.sdfits HISTORY 98-05-10_2228_190241-66_200b.sdfits HISTORY 98-05-13_2132_182450-66_193d.sdfits HISTORY 98-05-13_2151_183034-66_194d.sdfits HISTORY 98-05-13_2200_183618-66_195d.sdfits HISTORY 98-05-13_2210_184202-66_196d.sdfits HISTORY 98-05-13_2219_184746-66_197d.sdfits HISTORY 98-05-13_2228_185331-66_198d.sdfits HISTORY 98-05-13_2237_185915-66_199d.sdfits HISTORY 98-05-25_1711_182559-66_193e.sdfits HISTORY 98-05-25_1720_183143-66_194e.sdfits HISTORY 98-05-25_1729_183727-66_195e.sdfits HISTORY 98-05-25_1738_184311-66_196e.sdfits HISTORY 98-05-25_1747_184855-66_197e.sdfits HISTORY 98-05-25_1756_185439-66_198e.sdfits HISTORY 98-05-25_1806_190024-66_199e.sdfits HISTORY 98-05-25_1815_190608-66_200e.sdfits HISTORY 98-05-25_1824_191152-66_201e.sdfits HISTORY 98-05-25_1833_191736-66_202e.sdfits HISTORY 98-05-25_1842_192320-66_203e.sdfits HISTORY 98-05-25_1851_192905-66_204e.sdfits HISTORY 98-05-25_1901_193449-66_205e.sdfits HISTORY 98-05-25_1910_194033-66_206e.sdfits HISTORY 98-05-25_1919_194617-66_207e.sdfits HISTORY Original FITS filename "1904-66_CYP.continuum.fits". HISTORY Noise level of continuum map: 62 mJy (RMS) astropy-astropy-201cddb/astropy/wcs/tests/data/maps/1904-66_HPX.hdr000066400000000000000000000223401507226315300247540ustar00rootroot00000000000000SIMPLE = T / file does conform to FITS standard BITPIX = -32 / IEEE (big-endian) 32-bit floating point data NAXIS = 2 / number of data axes NAXIS1 = 192 / length of data axis 1 NAXIS2 = 192 / length of data axis 2 EXTEND = T / FITS dataset may contain extensions COMMENT FITS (Flexible Image Transport System) format is defined in 'AstronomyCOMMENT and Astrophysics', volume 376, page 359; bibcode: 2001A&A...376..359H BUNIT = 'Jy/beam ' / Pixel value is flux density CTYPE1 = 'RA---HPX' CRPIX1 = -248.217381441188 CDELT1 = -0.0666666666666667 CRVAL1 = 0. CTYPE2 = 'DEC--HPX' CRPIX2 = -8.21754831338666 CDELT2 = 0.0666666666666667 CRVAL2 = -90. LONPOLE = 180. / Native longitude of celestial pole LATPOLE = 0. / Native latitude of celestial pole RADESYS = 'FK5 ' / Equatorial coordinate system EQUINOX = 2000.0 / Equinox of equatorial coordinates BMAJ = 0.24000 / Beam major axis in degrees BMIN = 0.24000 / Beam minor axis in degrees BPA = 0.0 / Beam position angle in degrees HISTORY Single-dish continuum map HISTORY Formed on Mon 2005/03/07 04:03:52 GMT by "pksgridzilla" which was HISTORY compiled on Mar 6 2005 08:00:15 (local time) within HISTORY AIPS++ version 19.986.00 dated . HISTORY Polarization mode: A and B aggregated HISTORY Gridding parameters: HISTORY Method: WGTMED HISTORY Clip fraction: 0.000 HISTORY Tsys weighting: applied HISTORY Beam weight order: 1 HISTORY Beam FWHM: 14.4 arcmin HISTORY Beam normalization: applied HISTORY Smoothing kernel type: TOP-HAT HISTORY Kernel FWHM: 12.0 arcmin HISTORY Cutoff radius: 6.0 arcmin HISTORY Beam RSS cutoff: 0.0 HISTORY Input data sets: HISTORY 97-10-09_0356_193558-66_206a.sdfits HISTORY 97-10-12_0142_182123-66_193a.sdfits HISTORY 97-10-12_0151_182707-66_194a.sdfits HISTORY 97-10-12_0200_183252-66_195a.sdfits HISTORY 97-11-07_0510_183836-66_196a.sdfits HISTORY 97-11-07_0519_184420-66_197a.sdfits HISTORY 97-11-07_0528_185004-66_198a.sdfits HISTORY 97-11-07_0537_185548-66_199a.sdfits HISTORY 97-11-07_0546_190132-66_200a.sdfits HISTORY 97-11-07_0556_190717-66_201a.sdfits HISTORY 97-11-07_0645_191301-66_202a.sdfits HISTORY 97-11-07_0654_191845-66_203a.sdfits HISTORY 97-11-07_0703_192429-66_204a.sdfits HISTORY 97-11-07_0712_193013-66_205a.sdfits HISTORY 97-11-07_0724_194142-66_207a.sdfits HISTORY 97-11-18_0256_193815-66_206c.sdfits HISTORY 97-11-18_0306_194359-66_207c.sdfits HISTORY 97-11-19_0447_182341-66_193c.sdfits HISTORY 97-11-19_0456_182925-66_194c.sdfits HISTORY 97-11-19_0507_190350-66_200c.sdfits HISTORY 97-11-19_0516_190934-66_201c.sdfits HISTORY 97-11-19_0525_191519-66_202c.sdfits HISTORY 97-11-19_0534_192103-66_203c.sdfits HISTORY 97-11-19_0544_192647-66_204c.sdfits HISTORY 97-11-19_0553_193231-66_205c.sdfits HISTORY 97-11-19_0602_183509-66_195c.sdfits HISTORY 97-11-19_0612_184053-66_196c.sdfits HISTORY 97-11-19_0622_184638-66_197c.sdfits HISTORY 97-11-19_0631_185222-66_198c.sdfits HISTORY 97-11-19_0640_185806-66_199c.sdfits HISTORY 98-03-24_2107_193706-66_206b.sdfits HISTORY 98-03-24_2116_194251-66_207b.sdfits HISTORY 98-03-25_2020_190826-66_201b.sdfits HISTORY 98-03-25_2029_191410-66_202b.sdfits HISTORY 98-03-25_2038_191954-66_203b.sdfits HISTORY 98-03-25_2047_192538-66_204b.sdfits HISTORY 98-03-25_2056_193122-66_205b.sdfits HISTORY 98-03-26_2048_190459-66_200d.sdfits HISTORY 98-03-27_2034_191627-66_202d.sdfits HISTORY 98-03-27_2043_192212-66_203d.sdfits HISTORY 98-03-27_2052_192756-66_204d.sdfits HISTORY 98-03-27_2102_193340-66_205d.sdfits HISTORY 98-03-27_2111_193924-66_206d.sdfits HISTORY 98-03-27_2120_194508-66_207d.sdfits HISTORY 98-03-27_2130_191043-66_201d.sdfits HISTORY 98-05-10_2123_182232-66_193b.sdfits HISTORY 98-05-10_2133_182816-66_194b.sdfits HISTORY 98-05-10_2142_183400-66_195b.sdfits HISTORY 98-05-10_2151_183945-66_196b.sdfits HISTORY 98-05-10_2200_184529-66_197b.sdfits HISTORY 98-05-10_2209_185113-66_198b.sdfits HISTORY 98-05-10_2219_185657-66_199b.sdfits HISTORY 98-05-10_2228_190241-66_200b.sdfits HISTORY 98-05-13_2132_182450-66_193d.sdfits HISTORY 98-05-13_2151_183034-66_194d.sdfits HISTORY 98-05-13_2200_183618-66_195d.sdfits HISTORY 98-05-13_2210_184202-66_196d.sdfits HISTORY 98-05-13_2219_184746-66_197d.sdfits HISTORY 98-05-13_2228_185331-66_198d.sdfits HISTORY 98-05-13_2237_185915-66_199d.sdfits HISTORY 98-05-25_1711_182559-66_193e.sdfits HISTORY 98-05-25_1720_183143-66_194e.sdfits HISTORY 98-05-25_1729_183727-66_195e.sdfits HISTORY 98-05-25_1738_184311-66_196e.sdfits HISTORY 98-05-25_1747_184855-66_197e.sdfits HISTORY 98-05-25_1756_185439-66_198e.sdfits HISTORY 98-05-25_1806_190024-66_199e.sdfits HISTORY 98-05-25_1815_190608-66_200e.sdfits HISTORY 98-05-25_1824_191152-66_201e.sdfits HISTORY 98-05-25_1833_191736-66_202e.sdfits HISTORY 98-05-25_1842_192320-66_203e.sdfits HISTORY 98-05-25_1851_192905-66_204e.sdfits HISTORY 98-05-25_1901_193449-66_205e.sdfits HISTORY 98-05-25_1910_194033-66_206e.sdfits HISTORY 98-05-25_1919_194617-66_207e.sdfits HISTORY Original FITS filename "1904-66_HPX.continuum.fits". HISTORY Noise level of continuum map: 57 mJy (RMS) astropy-astropy-201cddb/astropy/wcs/tests/data/maps/1904-66_MER.hdr000066400000000000000000000217601507226315300247450ustar00rootroot00000000000000SIMPLE = T BITPIX = -32 / IEEE (big-endian) 32-bit floating point data NAXIS = 2 NAXIS1 = 192 NAXIS2 = 192 BUNIT = 'JY/BEAM ' CTYPE1 = 'RA---MER' CRPIX1 = -2.482173814412E+02 CDELT1 = -6.666666666667E-02 CRVAL1 = 0.000000000000E+00 CTYPE2 = 'DEC--MER' CRPIX2 = 7.364978412864E+00 CDELT2 = 6.666666666667E-02 CRVAL2 = -9.000000000000E+01 LONPOLE = 1.800000000000E+02 / Native longitude of celestial pole LATPOLE = 0.000000000000E+00 / Native latitude of celestial pole EQUINOX = 2.000000000000E+03 / Equinox of equatorial coordinates BMAJ = 2.399999936422E-01 / Beam major axis in degrees BMIN = 2.399999936422E-01 / Beam minor axis in degrees BPA = 0.000000000000E+00 / Beam position angle in degrees RESTFRQ = 1.420405750000E+09 / Line rest frequency, Hz HISTORY Parkes Multibeam continuum map HISTORY Formed on Mon 2004/02/09 01:53:59 GMT by "pksgridzilla" which was HISTORY compiled on Feb 9 2004 12:08:02 (local time) within HISTORY AIPS++ version 19.405.00 dated . HISTORY Polarization mode: A and B aggregated HISTORY Gridding parameters: HISTORY Method: WGTMED HISTORY Clip fraction: 0.000 HISTORY Tsys weighting: applied HISTORY Beam weight order: 1 HISTORY Beam FWHM: 14.4 arcmin HISTORY Beam normalization: applied HISTORY Smoothing kernel type: TOP-HAT HISTORY Kernel FWHM: 12.0 arcmin HISTORY Cutoff radius: 6.0 arcmin HISTORY Beam RSS cutoff: 0.0 HISTORY Input data sets: HISTORY 97-10-09_0356_193558-66_206a.sdfits HISTORY 97-10-12_0142_182123-66_193a.sdfits HISTORY 97-10-12_0151_182707-66_194a.sdfits HISTORY 97-10-12_0200_183252-66_195a.sdfits HISTORY 97-11-07_0510_183836-66_196a.sdfits HISTORY 97-11-07_0519_184420-66_197a.sdfits HISTORY 97-11-07_0528_185004-66_198a.sdfits HISTORY 97-11-07_0537_185548-66_199a.sdfits HISTORY 97-11-07_0546_190132-66_200a.sdfits HISTORY 97-11-07_0556_190717-66_201a.sdfits HISTORY 97-11-07_0645_191301-66_202a.sdfits HISTORY 97-11-07_0654_191845-66_203a.sdfits HISTORY 97-11-07_0703_192429-66_204a.sdfits HISTORY 97-11-07_0712_193013-66_205a.sdfits HISTORY 97-11-07_0724_194142-66_207a.sdfits HISTORY 97-11-18_0256_193815-66_206c.sdfits HISTORY 97-11-18_0306_194359-66_207c.sdfits HISTORY 97-11-19_0447_182341-66_193c.sdfits HISTORY 97-11-19_0456_182925-66_194c.sdfits HISTORY 97-11-19_0507_190350-66_200c.sdfits HISTORY 97-11-19_0516_190934-66_201c.sdfits HISTORY 97-11-19_0525_191519-66_202c.sdfits HISTORY 97-11-19_0534_192103-66_203c.sdfits HISTORY 97-11-19_0544_192647-66_204c.sdfits HISTORY 97-11-19_0553_193231-66_205c.sdfits HISTORY 97-11-19_0602_183509-66_195c.sdfits HISTORY 97-11-19_0612_184053-66_196c.sdfits HISTORY 97-11-19_0622_184638-66_197c.sdfits HISTORY 97-11-19_0631_185222-66_198c.sdfits HISTORY 97-11-19_0640_185806-66_199c.sdfits HISTORY 98-03-24_2107_193706-66_206b.sdfits HISTORY 98-03-24_2116_194251-66_207b.sdfits HISTORY 98-03-25_2020_190826-66_201b.sdfits HISTORY 98-03-25_2029_191410-66_202b.sdfits HISTORY 98-03-25_2038_191954-66_203b.sdfits HISTORY 98-03-25_2047_192538-66_204b.sdfits HISTORY 98-03-25_2056_193122-66_205b.sdfits HISTORY 98-03-26_2048_190459-66_200d.sdfits HISTORY 98-03-27_2034_191627-66_202d.sdfits HISTORY 98-03-27_2043_192212-66_203d.sdfits HISTORY 98-03-27_2052_192756-66_204d.sdfits HISTORY 98-03-27_2102_193340-66_205d.sdfits HISTORY 98-03-27_2111_193924-66_206d.sdfits HISTORY 98-03-27_2120_194508-66_207d.sdfits HISTORY 98-03-27_2130_191043-66_201d.sdfits HISTORY 98-05-10_2123_182232-66_193b.sdfits HISTORY 98-05-10_2133_182816-66_194b.sdfits HISTORY 98-05-10_2142_183400-66_195b.sdfits HISTORY 98-05-10_2151_183945-66_196b.sdfits HISTORY 98-05-10_2200_184529-66_197b.sdfits HISTORY 98-05-10_2209_185113-66_198b.sdfits HISTORY 98-05-10_2219_185657-66_199b.sdfits HISTORY 98-05-10_2228_190241-66_200b.sdfits HISTORY 98-05-13_2132_182450-66_193d.sdfits HISTORY 98-05-13_2151_183034-66_194d.sdfits HISTORY 98-05-13_2200_183618-66_195d.sdfits HISTORY 98-05-13_2210_184202-66_196d.sdfits HISTORY 98-05-13_2219_184746-66_197d.sdfits HISTORY 98-05-13_2228_185331-66_198d.sdfits HISTORY 98-05-13_2237_185915-66_199d.sdfits HISTORY 98-05-25_1711_182559-66_193e.sdfits HISTORY 98-05-25_1720_183143-66_194e.sdfits HISTORY 98-05-25_1729_183727-66_195e.sdfits HISTORY 98-05-25_1738_184311-66_196e.sdfits HISTORY 98-05-25_1747_184855-66_197e.sdfits HISTORY 98-05-25_1756_185439-66_198e.sdfits HISTORY 98-05-25_1806_190024-66_199e.sdfits HISTORY 98-05-25_1815_190608-66_200e.sdfits HISTORY 98-05-25_1824_191152-66_201e.sdfits HISTORY 98-05-25_1833_191736-66_202e.sdfits HISTORY 98-05-25_1842_192320-66_203e.sdfits HISTORY 98-05-25_1851_192905-66_204e.sdfits HISTORY 98-05-25_1901_193449-66_205e.sdfits HISTORY 98-05-25_1910_194033-66_206e.sdfits HISTORY 98-05-25_1919_194617-66_207e.sdfits HISTORY Original FITS filename "1904-66_MER.continuum.fits". HISTORY Noise level of continuum map: 61 mJy (RMS) astropy-astropy-201cddb/astropy/wcs/tests/data/maps/1904-66_MOL.hdr000066400000000000000000000217601507226315300247510ustar00rootroot00000000000000SIMPLE = T BITPIX = -32 / IEEE (big-endian) 32-bit floating point data NAXIS = 2 NAXIS1 = 192 NAXIS2 = 192 BUNIT = 'JY/BEAM ' CTYPE1 = 'RA---MOL' CRPIX1 = -2.127655947497E+02 CDELT1 = -6.666666666667E-02 CRVAL1 = 0.000000000000E+00 CTYPE2 = 'DEC--MOL' CRPIX2 = -2.310670994515E+00 CDELT2 = 6.666666666667E-02 CRVAL2 = -9.000000000000E+01 LONPOLE = 1.800000000000E+02 / Native longitude of celestial pole LATPOLE = 0.000000000000E+00 / Native latitude of celestial pole EQUINOX = 2.000000000000E+03 / Equinox of equatorial coordinates BMAJ = 2.399999936422E-01 / Beam major axis in degrees BMIN = 2.399999936422E-01 / Beam minor axis in degrees BPA = 0.000000000000E+00 / Beam position angle in degrees RESTFRQ = 1.420405750000E+09 / Line rest frequency, Hz HISTORY Parkes Multibeam continuum map HISTORY Formed on Mon 2004/02/09 02:01:55 GMT by "pksgridzilla" which was HISTORY compiled on Feb 9 2004 12:08:02 (local time) within HISTORY AIPS++ version 19.405.00 dated . HISTORY Polarization mode: A and B aggregated HISTORY Gridding parameters: HISTORY Method: WGTMED HISTORY Clip fraction: 0.000 HISTORY Tsys weighting: applied HISTORY Beam weight order: 1 HISTORY Beam FWHM: 14.4 arcmin HISTORY Beam normalization: applied HISTORY Smoothing kernel type: TOP-HAT HISTORY Kernel FWHM: 12.0 arcmin HISTORY Cutoff radius: 6.0 arcmin HISTORY Beam RSS cutoff: 0.0 HISTORY Input data sets: HISTORY 97-10-09_0356_193558-66_206a.sdfits HISTORY 97-10-12_0142_182123-66_193a.sdfits HISTORY 97-10-12_0151_182707-66_194a.sdfits HISTORY 97-10-12_0200_183252-66_195a.sdfits HISTORY 97-11-07_0510_183836-66_196a.sdfits HISTORY 97-11-07_0519_184420-66_197a.sdfits HISTORY 97-11-07_0528_185004-66_198a.sdfits HISTORY 97-11-07_0537_185548-66_199a.sdfits HISTORY 97-11-07_0546_190132-66_200a.sdfits HISTORY 97-11-07_0556_190717-66_201a.sdfits HISTORY 97-11-07_0645_191301-66_202a.sdfits HISTORY 97-11-07_0654_191845-66_203a.sdfits HISTORY 97-11-07_0703_192429-66_204a.sdfits HISTORY 97-11-07_0712_193013-66_205a.sdfits HISTORY 97-11-07_0724_194142-66_207a.sdfits HISTORY 97-11-18_0256_193815-66_206c.sdfits HISTORY 97-11-18_0306_194359-66_207c.sdfits HISTORY 97-11-19_0447_182341-66_193c.sdfits HISTORY 97-11-19_0456_182925-66_194c.sdfits HISTORY 97-11-19_0507_190350-66_200c.sdfits HISTORY 97-11-19_0516_190934-66_201c.sdfits HISTORY 97-11-19_0525_191519-66_202c.sdfits HISTORY 97-11-19_0534_192103-66_203c.sdfits HISTORY 97-11-19_0544_192647-66_204c.sdfits HISTORY 97-11-19_0553_193231-66_205c.sdfits HISTORY 97-11-19_0602_183509-66_195c.sdfits HISTORY 97-11-19_0612_184053-66_196c.sdfits HISTORY 97-11-19_0622_184638-66_197c.sdfits HISTORY 97-11-19_0631_185222-66_198c.sdfits HISTORY 97-11-19_0640_185806-66_199c.sdfits HISTORY 98-03-24_2107_193706-66_206b.sdfits HISTORY 98-03-24_2116_194251-66_207b.sdfits HISTORY 98-03-25_2020_190826-66_201b.sdfits HISTORY 98-03-25_2029_191410-66_202b.sdfits HISTORY 98-03-25_2038_191954-66_203b.sdfits HISTORY 98-03-25_2047_192538-66_204b.sdfits HISTORY 98-03-25_2056_193122-66_205b.sdfits HISTORY 98-03-26_2048_190459-66_200d.sdfits HISTORY 98-03-27_2034_191627-66_202d.sdfits HISTORY 98-03-27_2043_192212-66_203d.sdfits HISTORY 98-03-27_2052_192756-66_204d.sdfits HISTORY 98-03-27_2102_193340-66_205d.sdfits HISTORY 98-03-27_2111_193924-66_206d.sdfits HISTORY 98-03-27_2120_194508-66_207d.sdfits HISTORY 98-03-27_2130_191043-66_201d.sdfits HISTORY 98-05-10_2123_182232-66_193b.sdfits HISTORY 98-05-10_2133_182816-66_194b.sdfits HISTORY 98-05-10_2142_183400-66_195b.sdfits HISTORY 98-05-10_2151_183945-66_196b.sdfits HISTORY 98-05-10_2200_184529-66_197b.sdfits HISTORY 98-05-10_2209_185113-66_198b.sdfits HISTORY 98-05-10_2219_185657-66_199b.sdfits HISTORY 98-05-10_2228_190241-66_200b.sdfits HISTORY 98-05-13_2132_182450-66_193d.sdfits HISTORY 98-05-13_2151_183034-66_194d.sdfits HISTORY 98-05-13_2200_183618-66_195d.sdfits HISTORY 98-05-13_2210_184202-66_196d.sdfits HISTORY 98-05-13_2219_184746-66_197d.sdfits HISTORY 98-05-13_2228_185331-66_198d.sdfits HISTORY 98-05-13_2237_185915-66_199d.sdfits HISTORY 98-05-25_1711_182559-66_193e.sdfits HISTORY 98-05-25_1720_183143-66_194e.sdfits HISTORY 98-05-25_1729_183727-66_195e.sdfits HISTORY 98-05-25_1738_184311-66_196e.sdfits HISTORY 98-05-25_1747_184855-66_197e.sdfits HISTORY 98-05-25_1756_185439-66_198e.sdfits HISTORY 98-05-25_1806_190024-66_199e.sdfits HISTORY 98-05-25_1815_190608-66_200e.sdfits HISTORY 98-05-25_1824_191152-66_201e.sdfits HISTORY 98-05-25_1833_191736-66_202e.sdfits HISTORY 98-05-25_1842_192320-66_203e.sdfits HISTORY 98-05-25_1851_192905-66_204e.sdfits HISTORY 98-05-25_1901_193449-66_205e.sdfits HISTORY 98-05-25_1910_194033-66_206e.sdfits HISTORY 98-05-25_1919_194617-66_207e.sdfits HISTORY Original FITS filename "1904-66_MOL.continuum.fits". HISTORY Noise level of continuum map: 61 mJy (RMS) astropy-astropy-201cddb/astropy/wcs/tests/data/maps/1904-66_NCP.hdr000066400000000000000000000222201507226315300247320ustar00rootroot00000000000000SIMPLE = T BITPIX = -32 / IEEE (big-endian) 32-bit floating point data NAXIS = 2 NAXIS1 = 192 NAXIS2 = 192 BUNIT = 'JY/BEAM ' CTYPE1 = 'RA---SIN' CRPIX1 = -2.371895431541E+02 CDELT1 = -6.666666666667E-02 CRVAL1 = 0.000000000000E+00 CTYPE2 = 'DEC--SIN' CRPIX2 = 7.688572009351E+00 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_1 = 0.000000000000E+00 / Projection parameter 1 PV2_2 = -1.216796447506E-08 / Projection parameter 2 EQUINOX = 2.000000000000E+03 / Equinox of equatorial coordinates BMAJ = 2.399999936422E-01 / Beam major axis in degrees BMIN = 2.399999936422E-01 / Beam minor axis in degrees BPA = 0.000000000000E+00 / Beam position angle in degrees RESTFRQ = 1.420405750000E+09 / Line rest frequency, Hz HISTORY Parkes Multibeam continuum map HISTORY Formed on Mon 2004/02/09 01:33:03 GMT by "pksgridzilla" which was HISTORY compiled on Feb 9 2004 12:08:02 (local time) within HISTORY AIPS++ version 19.405.00 dated . HISTORY Polarization mode: A and B aggregated HISTORY Gridding parameters: HISTORY Method: WGTMED HISTORY Clip fraction: 0.000 HISTORY Tsys weighting: applied HISTORY Beam weight order: 1 HISTORY Beam FWHM: 14.4 arcmin HISTORY Beam normalization: applied HISTORY Smoothing kernel type: TOP-HAT HISTORY Kernel FWHM: 12.0 arcmin HISTORY Cutoff radius: 6.0 arcmin HISTORY Beam RSS cutoff: 0.0 HISTORY Input data sets: HISTORY 97-10-09_0356_193558-66_206a.sdfits HISTORY 97-10-12_0142_182123-66_193a.sdfits HISTORY 97-10-12_0151_182707-66_194a.sdfits HISTORY 97-10-12_0200_183252-66_195a.sdfits HISTORY 97-11-07_0510_183836-66_196a.sdfits HISTORY 97-11-07_0519_184420-66_197a.sdfits HISTORY 97-11-07_0528_185004-66_198a.sdfits HISTORY 97-11-07_0537_185548-66_199a.sdfits HISTORY 97-11-07_0546_190132-66_200a.sdfits HISTORY 97-11-07_0556_190717-66_201a.sdfits HISTORY 97-11-07_0645_191301-66_202a.sdfits HISTORY 97-11-07_0654_191845-66_203a.sdfits HISTORY 97-11-07_0703_192429-66_204a.sdfits HISTORY 97-11-07_0712_193013-66_205a.sdfits HISTORY 97-11-07_0724_194142-66_207a.sdfits HISTORY 97-11-18_0256_193815-66_206c.sdfits HISTORY 97-11-18_0306_194359-66_207c.sdfits HISTORY 97-11-19_0447_182341-66_193c.sdfits HISTORY 97-11-19_0456_182925-66_194c.sdfits HISTORY 97-11-19_0507_190350-66_200c.sdfits HISTORY 97-11-19_0516_190934-66_201c.sdfits HISTORY 97-11-19_0525_191519-66_202c.sdfits HISTORY 97-11-19_0534_192103-66_203c.sdfits HISTORY 97-11-19_0544_192647-66_204c.sdfits HISTORY 97-11-19_0553_193231-66_205c.sdfits HISTORY 97-11-19_0602_183509-66_195c.sdfits HISTORY 97-11-19_0612_184053-66_196c.sdfits HISTORY 97-11-19_0622_184638-66_197c.sdfits HISTORY 97-11-19_0631_185222-66_198c.sdfits HISTORY 97-11-19_0640_185806-66_199c.sdfits HISTORY 98-03-24_2107_193706-66_206b.sdfits HISTORY 98-03-24_2116_194251-66_207b.sdfits HISTORY 98-03-25_2020_190826-66_201b.sdfits HISTORY 98-03-25_2029_191410-66_202b.sdfits HISTORY 98-03-25_2038_191954-66_203b.sdfits HISTORY 98-03-25_2047_192538-66_204b.sdfits HISTORY 98-03-25_2056_193122-66_205b.sdfits HISTORY 98-03-26_2048_190459-66_200d.sdfits HISTORY 98-03-27_2034_191627-66_202d.sdfits HISTORY 98-03-27_2043_192212-66_203d.sdfits HISTORY 98-03-27_2052_192756-66_204d.sdfits HISTORY 98-03-27_2102_193340-66_205d.sdfits HISTORY 98-03-27_2111_193924-66_206d.sdfits HISTORY 98-03-27_2120_194508-66_207d.sdfits HISTORY 98-03-27_2130_191043-66_201d.sdfits HISTORY 98-05-10_2123_182232-66_193b.sdfits HISTORY 98-05-10_2133_182816-66_194b.sdfits HISTORY 98-05-10_2142_183400-66_195b.sdfits HISTORY 98-05-10_2151_183945-66_196b.sdfits HISTORY 98-05-10_2200_184529-66_197b.sdfits HISTORY 98-05-10_2209_185113-66_198b.sdfits HISTORY 98-05-10_2219_185657-66_199b.sdfits HISTORY 98-05-10_2228_190241-66_200b.sdfits HISTORY 98-05-13_2132_182450-66_193d.sdfits HISTORY 98-05-13_2151_183034-66_194d.sdfits HISTORY 98-05-13_2200_183618-66_195d.sdfits HISTORY 98-05-13_2210_184202-66_196d.sdfits HISTORY 98-05-13_2219_184746-66_197d.sdfits HISTORY 98-05-13_2228_185331-66_198d.sdfits HISTORY 98-05-13_2237_185915-66_199d.sdfits HISTORY 98-05-25_1711_182559-66_193e.sdfits HISTORY 98-05-25_1720_183143-66_194e.sdfits HISTORY 98-05-25_1729_183727-66_195e.sdfits HISTORY 98-05-25_1738_184311-66_196e.sdfits HISTORY 98-05-25_1747_184855-66_197e.sdfits HISTORY 98-05-25_1756_185439-66_198e.sdfits HISTORY 98-05-25_1806_190024-66_199e.sdfits HISTORY 98-05-25_1815_190608-66_200e.sdfits HISTORY 98-05-25_1824_191152-66_201e.sdfits HISTORY 98-05-25_1833_191736-66_202e.sdfits HISTORY 98-05-25_1842_192320-66_203e.sdfits HISTORY 98-05-25_1851_192905-66_204e.sdfits HISTORY 98-05-25_1901_193449-66_205e.sdfits HISTORY 98-05-25_1910_194033-66_206e.sdfits HISTORY 98-05-25_1919_194617-66_207e.sdfits HISTORY Original FITS filename "1904-66_NCP.continuum.fits". HISTORY Noise level of continuum map: 61 mJy (RMS) astropy-astropy-201cddb/astropy/wcs/tests/data/maps/1904-66_PAR.hdr000066400000000000000000000217601507226315300247440ustar00rootroot00000000000000SIMPLE = T BITPIX = -32 / IEEE (big-endian) 32-bit floating point data NAXIS = 2 NAXIS1 = 192 NAXIS2 = 192 BUNIT = 'JY/BEAM ' CTYPE1 = 'RA---PAR' CRPIX1 = -2.465551494284E+02 CDELT1 = -6.666666666667E-02 CRVAL1 = 0.000000000000E+00 CTYPE2 = 'DEC--PAR' CRPIX2 = 3.322937769653E+00 CDELT2 = 6.666666666667E-02 CRVAL2 = -9.000000000000E+01 LONPOLE = 1.800000000000E+02 / Native longitude of celestial pole LATPOLE = 0.000000000000E+00 / Native latitude of celestial pole EQUINOX = 2.000000000000E+03 / Equinox of equatorial coordinates BMAJ = 2.399999936422E-01 / Beam major axis in degrees BMIN = 2.399999936422E-01 / Beam minor axis in degrees BPA = 0.000000000000E+00 / Beam position angle in degrees RESTFRQ = 1.420405750000E+09 / Line rest frequency, Hz HISTORY Parkes Multibeam continuum map HISTORY Formed on Mon 2004/02/09 01:59:17 GMT by "pksgridzilla" which was HISTORY compiled on Feb 9 2004 12:08:02 (local time) within HISTORY AIPS++ version 19.405.00 dated . HISTORY Polarization mode: A and B aggregated HISTORY Gridding parameters: HISTORY Method: WGTMED HISTORY Clip fraction: 0.000 HISTORY Tsys weighting: applied HISTORY Beam weight order: 1 HISTORY Beam FWHM: 14.4 arcmin HISTORY Beam normalization: applied HISTORY Smoothing kernel type: TOP-HAT HISTORY Kernel FWHM: 12.0 arcmin HISTORY Cutoff radius: 6.0 arcmin HISTORY Beam RSS cutoff: 0.0 HISTORY Input data sets: HISTORY 97-10-09_0356_193558-66_206a.sdfits HISTORY 97-10-12_0142_182123-66_193a.sdfits HISTORY 97-10-12_0151_182707-66_194a.sdfits HISTORY 97-10-12_0200_183252-66_195a.sdfits HISTORY 97-11-07_0510_183836-66_196a.sdfits HISTORY 97-11-07_0519_184420-66_197a.sdfits HISTORY 97-11-07_0528_185004-66_198a.sdfits HISTORY 97-11-07_0537_185548-66_199a.sdfits HISTORY 97-11-07_0546_190132-66_200a.sdfits HISTORY 97-11-07_0556_190717-66_201a.sdfits HISTORY 97-11-07_0645_191301-66_202a.sdfits HISTORY 97-11-07_0654_191845-66_203a.sdfits HISTORY 97-11-07_0703_192429-66_204a.sdfits HISTORY 97-11-07_0712_193013-66_205a.sdfits HISTORY 97-11-07_0724_194142-66_207a.sdfits HISTORY 97-11-18_0256_193815-66_206c.sdfits HISTORY 97-11-18_0306_194359-66_207c.sdfits HISTORY 97-11-19_0447_182341-66_193c.sdfits HISTORY 97-11-19_0456_182925-66_194c.sdfits HISTORY 97-11-19_0507_190350-66_200c.sdfits HISTORY 97-11-19_0516_190934-66_201c.sdfits HISTORY 97-11-19_0525_191519-66_202c.sdfits HISTORY 97-11-19_0534_192103-66_203c.sdfits HISTORY 97-11-19_0544_192647-66_204c.sdfits HISTORY 97-11-19_0553_193231-66_205c.sdfits HISTORY 97-11-19_0602_183509-66_195c.sdfits HISTORY 97-11-19_0612_184053-66_196c.sdfits HISTORY 97-11-19_0622_184638-66_197c.sdfits HISTORY 97-11-19_0631_185222-66_198c.sdfits HISTORY 97-11-19_0640_185806-66_199c.sdfits HISTORY 98-03-24_2107_193706-66_206b.sdfits HISTORY 98-03-24_2116_194251-66_207b.sdfits HISTORY 98-03-25_2020_190826-66_201b.sdfits HISTORY 98-03-25_2029_191410-66_202b.sdfits HISTORY 98-03-25_2038_191954-66_203b.sdfits HISTORY 98-03-25_2047_192538-66_204b.sdfits HISTORY 98-03-25_2056_193122-66_205b.sdfits HISTORY 98-03-26_2048_190459-66_200d.sdfits HISTORY 98-03-27_2034_191627-66_202d.sdfits HISTORY 98-03-27_2043_192212-66_203d.sdfits HISTORY 98-03-27_2052_192756-66_204d.sdfits HISTORY 98-03-27_2102_193340-66_205d.sdfits HISTORY 98-03-27_2111_193924-66_206d.sdfits HISTORY 98-03-27_2120_194508-66_207d.sdfits HISTORY 98-03-27_2130_191043-66_201d.sdfits HISTORY 98-05-10_2123_182232-66_193b.sdfits HISTORY 98-05-10_2133_182816-66_194b.sdfits HISTORY 98-05-10_2142_183400-66_195b.sdfits HISTORY 98-05-10_2151_183945-66_196b.sdfits HISTORY 98-05-10_2200_184529-66_197b.sdfits HISTORY 98-05-10_2209_185113-66_198b.sdfits HISTORY 98-05-10_2219_185657-66_199b.sdfits HISTORY 98-05-10_2228_190241-66_200b.sdfits HISTORY 98-05-13_2132_182450-66_193d.sdfits HISTORY 98-05-13_2151_183034-66_194d.sdfits HISTORY 98-05-13_2200_183618-66_195d.sdfits HISTORY 98-05-13_2210_184202-66_196d.sdfits HISTORY 98-05-13_2219_184746-66_197d.sdfits HISTORY 98-05-13_2228_185331-66_198d.sdfits HISTORY 98-05-13_2237_185915-66_199d.sdfits HISTORY 98-05-25_1711_182559-66_193e.sdfits HISTORY 98-05-25_1720_183143-66_194e.sdfits HISTORY 98-05-25_1729_183727-66_195e.sdfits HISTORY 98-05-25_1738_184311-66_196e.sdfits HISTORY 98-05-25_1747_184855-66_197e.sdfits HISTORY 98-05-25_1756_185439-66_198e.sdfits HISTORY 98-05-25_1806_190024-66_199e.sdfits HISTORY 98-05-25_1815_190608-66_200e.sdfits HISTORY 98-05-25_1824_191152-66_201e.sdfits HISTORY 98-05-25_1833_191736-66_202e.sdfits HISTORY 98-05-25_1842_192320-66_203e.sdfits HISTORY 98-05-25_1851_192905-66_204e.sdfits HISTORY 98-05-25_1901_193449-66_205e.sdfits HISTORY 98-05-25_1910_194033-66_206e.sdfits HISTORY 98-05-25_1919_194617-66_207e.sdfits HISTORY Original FITS filename "1904-66_PAR.continuum.fits". HISTORY Noise level of continuum map: 61 mJy (RMS) astropy-astropy-201cddb/astropy/wcs/tests/data/maps/1904-66_PCO.hdr000066400000000000000000000217601507226315300247430ustar00rootroot00000000000000SIMPLE = T BITPIX = -32 / IEEE (big-endian) 32-bit floating point data NAXIS = 2 NAXIS1 = 192 NAXIS2 = 192 BUNIT = 'JY/BEAM ' CTYPE1 = 'RA---PCO' CRPIX1 = -2.462486098896E+02 CDELT1 = -6.666666666667E-02 CRVAL1 = 0.000000000000E+00 CTYPE2 = 'DEC--PCO' CRPIX2 = 3.620782775517E-01 CDELT2 = 6.666666666667E-02 CRVAL2 = -9.000000000000E+01 LONPOLE = 1.800000000000E+02 / Native longitude of celestial pole LATPOLE = 0.000000000000E+00 / Native latitude of celestial pole EQUINOX = 2.000000000000E+03 / Equinox of equatorial coordinates BMAJ = 2.399999936422E-01 / Beam major axis in degrees BMIN = 2.399999936422E-01 / Beam minor axis in degrees BPA = 0.000000000000E+00 / Beam position angle in degrees RESTFRQ = 1.420405750000E+09 / Line rest frequency, Hz HISTORY Parkes Multibeam continuum map HISTORY Formed on Mon 2004/02/09 02:20:22 GMT by "pksgridzilla" which was HISTORY compiled on Feb 9 2004 12:08:02 (local time) within HISTORY AIPS++ version 19.405.00 dated . HISTORY Polarization mode: A and B aggregated HISTORY Gridding parameters: HISTORY Method: WGTMED HISTORY Clip fraction: 0.000 HISTORY Tsys weighting: applied HISTORY Beam weight order: 1 HISTORY Beam FWHM: 14.4 arcmin HISTORY Beam normalization: applied HISTORY Smoothing kernel type: TOP-HAT HISTORY Kernel FWHM: 12.0 arcmin HISTORY Cutoff radius: 6.0 arcmin HISTORY Beam RSS cutoff: 0.0 HISTORY Input data sets: HISTORY 97-10-09_0356_193558-66_206a.sdfits HISTORY 97-10-12_0142_182123-66_193a.sdfits HISTORY 97-10-12_0151_182707-66_194a.sdfits HISTORY 97-10-12_0200_183252-66_195a.sdfits HISTORY 97-11-07_0510_183836-66_196a.sdfits HISTORY 97-11-07_0519_184420-66_197a.sdfits HISTORY 97-11-07_0528_185004-66_198a.sdfits HISTORY 97-11-07_0537_185548-66_199a.sdfits HISTORY 97-11-07_0546_190132-66_200a.sdfits HISTORY 97-11-07_0556_190717-66_201a.sdfits HISTORY 97-11-07_0645_191301-66_202a.sdfits HISTORY 97-11-07_0654_191845-66_203a.sdfits HISTORY 97-11-07_0703_192429-66_204a.sdfits HISTORY 97-11-07_0712_193013-66_205a.sdfits HISTORY 97-11-07_0724_194142-66_207a.sdfits HISTORY 97-11-18_0256_193815-66_206c.sdfits HISTORY 97-11-18_0306_194359-66_207c.sdfits HISTORY 97-11-19_0447_182341-66_193c.sdfits HISTORY 97-11-19_0456_182925-66_194c.sdfits HISTORY 97-11-19_0507_190350-66_200c.sdfits HISTORY 97-11-19_0516_190934-66_201c.sdfits HISTORY 97-11-19_0525_191519-66_202c.sdfits HISTORY 97-11-19_0534_192103-66_203c.sdfits HISTORY 97-11-19_0544_192647-66_204c.sdfits HISTORY 97-11-19_0553_193231-66_205c.sdfits HISTORY 97-11-19_0602_183509-66_195c.sdfits HISTORY 97-11-19_0612_184053-66_196c.sdfits HISTORY 97-11-19_0622_184638-66_197c.sdfits HISTORY 97-11-19_0631_185222-66_198c.sdfits HISTORY 97-11-19_0640_185806-66_199c.sdfits HISTORY 98-03-24_2107_193706-66_206b.sdfits HISTORY 98-03-24_2116_194251-66_207b.sdfits HISTORY 98-03-25_2020_190826-66_201b.sdfits HISTORY 98-03-25_2029_191410-66_202b.sdfits HISTORY 98-03-25_2038_191954-66_203b.sdfits HISTORY 98-03-25_2047_192538-66_204b.sdfits HISTORY 98-03-25_2056_193122-66_205b.sdfits HISTORY 98-03-26_2048_190459-66_200d.sdfits HISTORY 98-03-27_2034_191627-66_202d.sdfits HISTORY 98-03-27_2043_192212-66_203d.sdfits HISTORY 98-03-27_2052_192756-66_204d.sdfits HISTORY 98-03-27_2102_193340-66_205d.sdfits HISTORY 98-03-27_2111_193924-66_206d.sdfits HISTORY 98-03-27_2120_194508-66_207d.sdfits HISTORY 98-03-27_2130_191043-66_201d.sdfits HISTORY 98-05-10_2123_182232-66_193b.sdfits HISTORY 98-05-10_2133_182816-66_194b.sdfits HISTORY 98-05-10_2142_183400-66_195b.sdfits HISTORY 98-05-10_2151_183945-66_196b.sdfits HISTORY 98-05-10_2200_184529-66_197b.sdfits HISTORY 98-05-10_2209_185113-66_198b.sdfits HISTORY 98-05-10_2219_185657-66_199b.sdfits HISTORY 98-05-10_2228_190241-66_200b.sdfits HISTORY 98-05-13_2132_182450-66_193d.sdfits HISTORY 98-05-13_2151_183034-66_194d.sdfits HISTORY 98-05-13_2200_183618-66_195d.sdfits HISTORY 98-05-13_2210_184202-66_196d.sdfits HISTORY 98-05-13_2219_184746-66_197d.sdfits HISTORY 98-05-13_2228_185331-66_198d.sdfits HISTORY 98-05-13_2237_185915-66_199d.sdfits HISTORY 98-05-25_1711_182559-66_193e.sdfits HISTORY 98-05-25_1720_183143-66_194e.sdfits HISTORY 98-05-25_1729_183727-66_195e.sdfits HISTORY 98-05-25_1738_184311-66_196e.sdfits HISTORY 98-05-25_1747_184855-66_197e.sdfits HISTORY 98-05-25_1756_185439-66_198e.sdfits HISTORY 98-05-25_1806_190024-66_199e.sdfits HISTORY 98-05-25_1815_190608-66_200e.sdfits HISTORY 98-05-25_1824_191152-66_201e.sdfits HISTORY 98-05-25_1833_191736-66_202e.sdfits HISTORY 98-05-25_1842_192320-66_203e.sdfits HISTORY 98-05-25_1851_192905-66_204e.sdfits HISTORY 98-05-25_1901_193449-66_205e.sdfits HISTORY 98-05-25_1910_194033-66_206e.sdfits HISTORY 98-05-25_1919_194617-66_207e.sdfits HISTORY Original FITS filename "1904-66_PCO.continuum.fits". HISTORY Noise level of continuum map: 62 mJy (RMS) astropy-astropy-201cddb/astropy/wcs/tests/data/maps/1904-66_QSC.hdr000066400000000000000000000217601507226315300247500ustar00rootroot00000000000000SIMPLE = T BITPIX = -32 / IEEE (big-endian) 32-bit floating point data NAXIS = 2 NAXIS1 = 192 NAXIS2 = 192 BUNIT = 'JY/BEAM ' CTYPE1 = 'RA---QSC' CRPIX1 = -2.583408175994E+02 CDELT1 = -6.666666666667E-02 CRVAL1 = 0.000000000000E+00 CTYPE2 = 'DEC--QSC' CRPIX2 = -8.258194421088E+00 CDELT2 = 6.666666666667E-02 CRVAL2 = -9.000000000000E+01 LONPOLE = 1.800000000000E+02 / Native longitude of celestial pole LATPOLE = 0.000000000000E+00 / Native latitude of celestial pole EQUINOX = 2.000000000000E+03 / Equinox of equatorial coordinates BMAJ = 2.399999936422E-01 / Beam major axis in degrees BMIN = 2.399999936422E-01 / Beam minor axis in degrees BPA = 0.000000000000E+00 / Beam position angle in degrees RESTFRQ = 1.420405750000E+09 / Line rest frequency, Hz HISTORY Parkes Multibeam continuum map HISTORY Formed on Mon 2004/02/09 02:28:25 GMT by "pksgridzilla" which was HISTORY compiled on Feb 9 2004 12:08:02 (local time) within HISTORY AIPS++ version 19.405.00 dated . HISTORY Polarization mode: A and B aggregated HISTORY Gridding parameters: HISTORY Method: WGTMED HISTORY Clip fraction: 0.000 HISTORY Tsys weighting: applied HISTORY Beam weight order: 1 HISTORY Beam FWHM: 14.4 arcmin HISTORY Beam normalization: applied HISTORY Smoothing kernel type: TOP-HAT HISTORY Kernel FWHM: 12.0 arcmin HISTORY Cutoff radius: 6.0 arcmin HISTORY Beam RSS cutoff: 0.0 HISTORY Input data sets: HISTORY 97-10-09_0356_193558-66_206a.sdfits HISTORY 97-10-12_0142_182123-66_193a.sdfits HISTORY 97-10-12_0151_182707-66_194a.sdfits HISTORY 97-10-12_0200_183252-66_195a.sdfits HISTORY 97-11-07_0510_183836-66_196a.sdfits HISTORY 97-11-07_0519_184420-66_197a.sdfits HISTORY 97-11-07_0528_185004-66_198a.sdfits HISTORY 97-11-07_0537_185548-66_199a.sdfits HISTORY 97-11-07_0546_190132-66_200a.sdfits HISTORY 97-11-07_0556_190717-66_201a.sdfits HISTORY 97-11-07_0645_191301-66_202a.sdfits HISTORY 97-11-07_0654_191845-66_203a.sdfits HISTORY 97-11-07_0703_192429-66_204a.sdfits HISTORY 97-11-07_0712_193013-66_205a.sdfits HISTORY 97-11-07_0724_194142-66_207a.sdfits HISTORY 97-11-18_0256_193815-66_206c.sdfits HISTORY 97-11-18_0306_194359-66_207c.sdfits HISTORY 97-11-19_0447_182341-66_193c.sdfits HISTORY 97-11-19_0456_182925-66_194c.sdfits HISTORY 97-11-19_0507_190350-66_200c.sdfits HISTORY 97-11-19_0516_190934-66_201c.sdfits HISTORY 97-11-19_0525_191519-66_202c.sdfits HISTORY 97-11-19_0534_192103-66_203c.sdfits HISTORY 97-11-19_0544_192647-66_204c.sdfits HISTORY 97-11-19_0553_193231-66_205c.sdfits HISTORY 97-11-19_0602_183509-66_195c.sdfits HISTORY 97-11-19_0612_184053-66_196c.sdfits HISTORY 97-11-19_0622_184638-66_197c.sdfits HISTORY 97-11-19_0631_185222-66_198c.sdfits HISTORY 97-11-19_0640_185806-66_199c.sdfits HISTORY 98-03-24_2107_193706-66_206b.sdfits HISTORY 98-03-24_2116_194251-66_207b.sdfits HISTORY 98-03-25_2020_190826-66_201b.sdfits HISTORY 98-03-25_2029_191410-66_202b.sdfits HISTORY 98-03-25_2038_191954-66_203b.sdfits HISTORY 98-03-25_2047_192538-66_204b.sdfits HISTORY 98-03-25_2056_193122-66_205b.sdfits HISTORY 98-03-26_2048_190459-66_200d.sdfits HISTORY 98-03-27_2034_191627-66_202d.sdfits HISTORY 98-03-27_2043_192212-66_203d.sdfits HISTORY 98-03-27_2052_192756-66_204d.sdfits HISTORY 98-03-27_2102_193340-66_205d.sdfits HISTORY 98-03-27_2111_193924-66_206d.sdfits HISTORY 98-03-27_2120_194508-66_207d.sdfits HISTORY 98-03-27_2130_191043-66_201d.sdfits HISTORY 98-05-10_2123_182232-66_193b.sdfits HISTORY 98-05-10_2133_182816-66_194b.sdfits HISTORY 98-05-10_2142_183400-66_195b.sdfits HISTORY 98-05-10_2151_183945-66_196b.sdfits HISTORY 98-05-10_2200_184529-66_197b.sdfits HISTORY 98-05-10_2209_185113-66_198b.sdfits HISTORY 98-05-10_2219_185657-66_199b.sdfits HISTORY 98-05-10_2228_190241-66_200b.sdfits HISTORY 98-05-13_2132_182450-66_193d.sdfits HISTORY 98-05-13_2151_183034-66_194d.sdfits HISTORY 98-05-13_2200_183618-66_195d.sdfits HISTORY 98-05-13_2210_184202-66_196d.sdfits HISTORY 98-05-13_2219_184746-66_197d.sdfits HISTORY 98-05-13_2228_185331-66_198d.sdfits HISTORY 98-05-13_2237_185915-66_199d.sdfits HISTORY 98-05-25_1711_182559-66_193e.sdfits HISTORY 98-05-25_1720_183143-66_194e.sdfits HISTORY 98-05-25_1729_183727-66_195e.sdfits HISTORY 98-05-25_1738_184311-66_196e.sdfits HISTORY 98-05-25_1747_184855-66_197e.sdfits HISTORY 98-05-25_1756_185439-66_198e.sdfits HISTORY 98-05-25_1806_190024-66_199e.sdfits HISTORY 98-05-25_1815_190608-66_200e.sdfits HISTORY 98-05-25_1824_191152-66_201e.sdfits HISTORY 98-05-25_1833_191736-66_202e.sdfits HISTORY 98-05-25_1842_192320-66_203e.sdfits HISTORY 98-05-25_1851_192905-66_204e.sdfits HISTORY 98-05-25_1901_193449-66_205e.sdfits HISTORY 98-05-25_1910_194033-66_206e.sdfits HISTORY 98-05-25_1919_194617-66_207e.sdfits HISTORY Original FITS filename "1904-66_QSC.continuum.fits". HISTORY Noise level of continuum map: 61 mJy (RMS) astropy-astropy-201cddb/astropy/wcs/tests/data/maps/1904-66_SFL.hdr000066400000000000000000000217601507226315300247460ustar00rootroot00000000000000SIMPLE = T BITPIX = -32 / IEEE (big-endian) 32-bit floating point data NAXIS = 2 NAXIS1 = 192 NAXIS2 = 192 BUNIT = 'JY/BEAM ' CTYPE1 = 'RA---SFL' CRPIX1 = -2.463483086237E+02 CDELT1 = -6.666666666667E-02 CRVAL1 = 0.000000000000E+00 CTYPE2 = 'DEC--SFL' CRPIX2 = 7.527038199745E+00 CDELT2 = 6.666666666667E-02 CRVAL2 = -9.000000000000E+01 LONPOLE = 1.800000000000E+02 / Native longitude of celestial pole LATPOLE = 0.000000000000E+00 / Native latitude of celestial pole EQUINOX = 2.000000000000E+03 / Equinox of equatorial coordinates BMAJ = 2.399999936422E-01 / Beam major axis in degrees BMIN = 2.399999936422E-01 / Beam minor axis in degrees BPA = 0.000000000000E+00 / Beam position angle in degrees RESTFRQ = 1.420405750000E+09 / Line rest frequency, Hz HISTORY Parkes Multibeam continuum map HISTORY Formed on Mon 2004/02/09 01:56:37 GMT by "pksgridzilla" which was HISTORY compiled on Feb 9 2004 12:08:02 (local time) within HISTORY AIPS++ version 19.405.00 dated . HISTORY Polarization mode: A and B aggregated HISTORY Gridding parameters: HISTORY Method: WGTMED HISTORY Clip fraction: 0.000 HISTORY Tsys weighting: applied HISTORY Beam weight order: 1 HISTORY Beam FWHM: 14.4 arcmin HISTORY Beam normalization: applied HISTORY Smoothing kernel type: TOP-HAT HISTORY Kernel FWHM: 12.0 arcmin HISTORY Cutoff radius: 6.0 arcmin HISTORY Beam RSS cutoff: 0.0 HISTORY Input data sets: HISTORY 97-10-09_0356_193558-66_206a.sdfits HISTORY 97-10-12_0142_182123-66_193a.sdfits HISTORY 97-10-12_0151_182707-66_194a.sdfits HISTORY 97-10-12_0200_183252-66_195a.sdfits HISTORY 97-11-07_0510_183836-66_196a.sdfits HISTORY 97-11-07_0519_184420-66_197a.sdfits HISTORY 97-11-07_0528_185004-66_198a.sdfits HISTORY 97-11-07_0537_185548-66_199a.sdfits HISTORY 97-11-07_0546_190132-66_200a.sdfits HISTORY 97-11-07_0556_190717-66_201a.sdfits HISTORY 97-11-07_0645_191301-66_202a.sdfits HISTORY 97-11-07_0654_191845-66_203a.sdfits HISTORY 97-11-07_0703_192429-66_204a.sdfits HISTORY 97-11-07_0712_193013-66_205a.sdfits HISTORY 97-11-07_0724_194142-66_207a.sdfits HISTORY 97-11-18_0256_193815-66_206c.sdfits HISTORY 97-11-18_0306_194359-66_207c.sdfits HISTORY 97-11-19_0447_182341-66_193c.sdfits HISTORY 97-11-19_0456_182925-66_194c.sdfits HISTORY 97-11-19_0507_190350-66_200c.sdfits HISTORY 97-11-19_0516_190934-66_201c.sdfits HISTORY 97-11-19_0525_191519-66_202c.sdfits HISTORY 97-11-19_0534_192103-66_203c.sdfits HISTORY 97-11-19_0544_192647-66_204c.sdfits HISTORY 97-11-19_0553_193231-66_205c.sdfits HISTORY 97-11-19_0602_183509-66_195c.sdfits HISTORY 97-11-19_0612_184053-66_196c.sdfits HISTORY 97-11-19_0622_184638-66_197c.sdfits HISTORY 97-11-19_0631_185222-66_198c.sdfits HISTORY 97-11-19_0640_185806-66_199c.sdfits HISTORY 98-03-24_2107_193706-66_206b.sdfits HISTORY 98-03-24_2116_194251-66_207b.sdfits HISTORY 98-03-25_2020_190826-66_201b.sdfits HISTORY 98-03-25_2029_191410-66_202b.sdfits HISTORY 98-03-25_2038_191954-66_203b.sdfits HISTORY 98-03-25_2047_192538-66_204b.sdfits HISTORY 98-03-25_2056_193122-66_205b.sdfits HISTORY 98-03-26_2048_190459-66_200d.sdfits HISTORY 98-03-27_2034_191627-66_202d.sdfits HISTORY 98-03-27_2043_192212-66_203d.sdfits HISTORY 98-03-27_2052_192756-66_204d.sdfits HISTORY 98-03-27_2102_193340-66_205d.sdfits HISTORY 98-03-27_2111_193924-66_206d.sdfits HISTORY 98-03-27_2120_194508-66_207d.sdfits HISTORY 98-03-27_2130_191043-66_201d.sdfits HISTORY 98-05-10_2123_182232-66_193b.sdfits HISTORY 98-05-10_2133_182816-66_194b.sdfits HISTORY 98-05-10_2142_183400-66_195b.sdfits HISTORY 98-05-10_2151_183945-66_196b.sdfits HISTORY 98-05-10_2200_184529-66_197b.sdfits HISTORY 98-05-10_2209_185113-66_198b.sdfits HISTORY 98-05-10_2219_185657-66_199b.sdfits HISTORY 98-05-10_2228_190241-66_200b.sdfits HISTORY 98-05-13_2132_182450-66_193d.sdfits HISTORY 98-05-13_2151_183034-66_194d.sdfits HISTORY 98-05-13_2200_183618-66_195d.sdfits HISTORY 98-05-13_2210_184202-66_196d.sdfits HISTORY 98-05-13_2219_184746-66_197d.sdfits HISTORY 98-05-13_2228_185331-66_198d.sdfits HISTORY 98-05-13_2237_185915-66_199d.sdfits HISTORY 98-05-25_1711_182559-66_193e.sdfits HISTORY 98-05-25_1720_183143-66_194e.sdfits HISTORY 98-05-25_1729_183727-66_195e.sdfits HISTORY 98-05-25_1738_184311-66_196e.sdfits HISTORY 98-05-25_1747_184855-66_197e.sdfits HISTORY 98-05-25_1756_185439-66_198e.sdfits HISTORY 98-05-25_1806_190024-66_199e.sdfits HISTORY 98-05-25_1815_190608-66_200e.sdfits HISTORY 98-05-25_1824_191152-66_201e.sdfits HISTORY 98-05-25_1833_191736-66_202e.sdfits HISTORY 98-05-25_1842_192320-66_203e.sdfits HISTORY 98-05-25_1851_192905-66_204e.sdfits HISTORY 98-05-25_1901_193449-66_205e.sdfits HISTORY 98-05-25_1910_194033-66_206e.sdfits HISTORY 98-05-25_1919_194617-66_207e.sdfits HISTORY Original FITS filename "1904-66_SFL.continuum.fits". HISTORY Noise level of continuum map: 61 mJy (RMS) astropy-astropy-201cddb/astropy/wcs/tests/data/maps/1904-66_SIN.hdr000066400000000000000000000222201507226315300247430ustar00rootroot00000000000000SIMPLE = T BITPIX = -32 / IEEE (big-endian) 32-bit floating point data NAXIS = 2 NAXIS1 = 192 NAXIS2 = 192 BUNIT = 'JY/BEAM ' CTYPE1 = 'RA---SIN' CRPIX1 = -2.371895431541E+02 CDELT1 = -6.666666666667E-02 CRVAL1 = 0.000000000000E+00 CTYPE2 = 'DEC--SIN' CRPIX2 = 7.688571124876E+00 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_1 = 0.000000000000E+00 / Projection parameter 1 PV2_2 = 0.000000000000E+00 / Projection parameter 2 EQUINOX = 2.000000000000E+03 / Equinox of equatorial coordinates BMAJ = 2.399999936422E-01 / Beam major axis in degrees BMIN = 2.399999936422E-01 / Beam minor axis in degrees BPA = 0.000000000000E+00 / Beam position angle in degrees RESTFRQ = 1.420405750000E+09 / Line rest frequency, Hz HISTORY Parkes Multibeam continuum map HISTORY Formed on Mon 2004/02/09 01:30:25 GMT by "pksgridzilla" which was HISTORY compiled on Feb 9 2004 12:08:02 (local time) within HISTORY AIPS++ version 19.405.00 dated . HISTORY Polarization mode: A and B aggregated HISTORY Gridding parameters: HISTORY Method: WGTMED HISTORY Clip fraction: 0.000 HISTORY Tsys weighting: applied HISTORY Beam weight order: 1 HISTORY Beam FWHM: 14.4 arcmin HISTORY Beam normalization: applied HISTORY Smoothing kernel type: TOP-HAT HISTORY Kernel FWHM: 12.0 arcmin HISTORY Cutoff radius: 6.0 arcmin HISTORY Beam RSS cutoff: 0.0 HISTORY Input data sets: HISTORY 97-10-09_0356_193558-66_206a.sdfits HISTORY 97-10-12_0142_182123-66_193a.sdfits HISTORY 97-10-12_0151_182707-66_194a.sdfits HISTORY 97-10-12_0200_183252-66_195a.sdfits HISTORY 97-11-07_0510_183836-66_196a.sdfits HISTORY 97-11-07_0519_184420-66_197a.sdfits HISTORY 97-11-07_0528_185004-66_198a.sdfits HISTORY 97-11-07_0537_185548-66_199a.sdfits HISTORY 97-11-07_0546_190132-66_200a.sdfits HISTORY 97-11-07_0556_190717-66_201a.sdfits HISTORY 97-11-07_0645_191301-66_202a.sdfits HISTORY 97-11-07_0654_191845-66_203a.sdfits HISTORY 97-11-07_0703_192429-66_204a.sdfits HISTORY 97-11-07_0712_193013-66_205a.sdfits HISTORY 97-11-07_0724_194142-66_207a.sdfits HISTORY 97-11-18_0256_193815-66_206c.sdfits HISTORY 97-11-18_0306_194359-66_207c.sdfits HISTORY 97-11-19_0447_182341-66_193c.sdfits HISTORY 97-11-19_0456_182925-66_194c.sdfits HISTORY 97-11-19_0507_190350-66_200c.sdfits HISTORY 97-11-19_0516_190934-66_201c.sdfits HISTORY 97-11-19_0525_191519-66_202c.sdfits HISTORY 97-11-19_0534_192103-66_203c.sdfits HISTORY 97-11-19_0544_192647-66_204c.sdfits HISTORY 97-11-19_0553_193231-66_205c.sdfits HISTORY 97-11-19_0602_183509-66_195c.sdfits HISTORY 97-11-19_0612_184053-66_196c.sdfits HISTORY 97-11-19_0622_184638-66_197c.sdfits HISTORY 97-11-19_0631_185222-66_198c.sdfits HISTORY 97-11-19_0640_185806-66_199c.sdfits HISTORY 98-03-24_2107_193706-66_206b.sdfits HISTORY 98-03-24_2116_194251-66_207b.sdfits HISTORY 98-03-25_2020_190826-66_201b.sdfits HISTORY 98-03-25_2029_191410-66_202b.sdfits HISTORY 98-03-25_2038_191954-66_203b.sdfits HISTORY 98-03-25_2047_192538-66_204b.sdfits HISTORY 98-03-25_2056_193122-66_205b.sdfits HISTORY 98-03-26_2048_190459-66_200d.sdfits HISTORY 98-03-27_2034_191627-66_202d.sdfits HISTORY 98-03-27_2043_192212-66_203d.sdfits HISTORY 98-03-27_2052_192756-66_204d.sdfits HISTORY 98-03-27_2102_193340-66_205d.sdfits HISTORY 98-03-27_2111_193924-66_206d.sdfits HISTORY 98-03-27_2120_194508-66_207d.sdfits HISTORY 98-03-27_2130_191043-66_201d.sdfits HISTORY 98-05-10_2123_182232-66_193b.sdfits HISTORY 98-05-10_2133_182816-66_194b.sdfits HISTORY 98-05-10_2142_183400-66_195b.sdfits HISTORY 98-05-10_2151_183945-66_196b.sdfits HISTORY 98-05-10_2200_184529-66_197b.sdfits HISTORY 98-05-10_2209_185113-66_198b.sdfits HISTORY 98-05-10_2219_185657-66_199b.sdfits HISTORY 98-05-10_2228_190241-66_200b.sdfits HISTORY 98-05-13_2132_182450-66_193d.sdfits HISTORY 98-05-13_2151_183034-66_194d.sdfits HISTORY 98-05-13_2200_183618-66_195d.sdfits HISTORY 98-05-13_2210_184202-66_196d.sdfits HISTORY 98-05-13_2219_184746-66_197d.sdfits HISTORY 98-05-13_2228_185331-66_198d.sdfits HISTORY 98-05-13_2237_185915-66_199d.sdfits HISTORY 98-05-25_1711_182559-66_193e.sdfits HISTORY 98-05-25_1720_183143-66_194e.sdfits HISTORY 98-05-25_1729_183727-66_195e.sdfits HISTORY 98-05-25_1738_184311-66_196e.sdfits HISTORY 98-05-25_1747_184855-66_197e.sdfits HISTORY 98-05-25_1756_185439-66_198e.sdfits HISTORY 98-05-25_1806_190024-66_199e.sdfits HISTORY 98-05-25_1815_190608-66_200e.sdfits HISTORY 98-05-25_1824_191152-66_201e.sdfits HISTORY 98-05-25_1833_191736-66_202e.sdfits HISTORY 98-05-25_1842_192320-66_203e.sdfits HISTORY 98-05-25_1851_192905-66_204e.sdfits HISTORY 98-05-25_1901_193449-66_205e.sdfits HISTORY 98-05-25_1910_194033-66_206e.sdfits HISTORY 98-05-25_1919_194617-66_207e.sdfits HISTORY Original FITS filename "1904-66_SIN.continuum.fits". HISTORY Noise level of continuum map: 61 mJy (RMS) astropy-astropy-201cddb/astropy/wcs/tests/data/maps/1904-66_STG.hdr000066400000000000000000000217601507226315300247570ustar00rootroot00000000000000SIMPLE = T BITPIX = -32 / IEEE (big-endian) 32-bit floating point data NAXIS = 2 NAXIS1 = 192 NAXIS2 = 192 BUNIT = 'JY/BEAM ' CTYPE1 = 'RA---STG' CRPIX1 = -2.519459909290E+02 CDELT1 = -6.666666666667E-02 CRVAL1 = 0.000000000000E+00 CTYPE2 = 'DEC--STG' CRPIX2 = 3.744942537739E+00 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 EQUINOX = 2.000000000000E+03 / Equinox of equatorial coordinates BMAJ = 2.399999936422E-01 / Beam major axis in degrees BMIN = 2.399999936422E-01 / Beam minor axis in degrees BPA = 0.000000000000E+00 / Beam position angle in degrees RESTFRQ = 1.420405750000E+09 / Line rest frequency, Hz HISTORY Parkes Multibeam continuum map HISTORY Formed on Mon 2004/02/09 01:26:55 GMT by "pksgridzilla" which was HISTORY compiled on Feb 9 2004 12:08:02 (local time) within HISTORY AIPS++ version 19.405.00 dated . HISTORY Polarization mode: A and B aggregated HISTORY Gridding parameters: HISTORY Method: WGTMED HISTORY Clip fraction: 0.000 HISTORY Tsys weighting: applied HISTORY Beam weight order: 1 HISTORY Beam FWHM: 14.4 arcmin HISTORY Beam normalization: applied HISTORY Smoothing kernel type: TOP-HAT HISTORY Kernel FWHM: 12.0 arcmin HISTORY Cutoff radius: 6.0 arcmin HISTORY Beam RSS cutoff: 0.0 HISTORY Input data sets: HISTORY 97-10-09_0356_193558-66_206a.sdfits HISTORY 97-10-12_0142_182123-66_193a.sdfits HISTORY 97-10-12_0151_182707-66_194a.sdfits HISTORY 97-10-12_0200_183252-66_195a.sdfits HISTORY 97-11-07_0510_183836-66_196a.sdfits HISTORY 97-11-07_0519_184420-66_197a.sdfits HISTORY 97-11-07_0528_185004-66_198a.sdfits HISTORY 97-11-07_0537_185548-66_199a.sdfits HISTORY 97-11-07_0546_190132-66_200a.sdfits HISTORY 97-11-07_0556_190717-66_201a.sdfits HISTORY 97-11-07_0645_191301-66_202a.sdfits HISTORY 97-11-07_0654_191845-66_203a.sdfits HISTORY 97-11-07_0703_192429-66_204a.sdfits HISTORY 97-11-07_0712_193013-66_205a.sdfits HISTORY 97-11-07_0724_194142-66_207a.sdfits HISTORY 97-11-18_0256_193815-66_206c.sdfits HISTORY 97-11-18_0306_194359-66_207c.sdfits HISTORY 97-11-19_0447_182341-66_193c.sdfits HISTORY 97-11-19_0456_182925-66_194c.sdfits HISTORY 97-11-19_0507_190350-66_200c.sdfits HISTORY 97-11-19_0516_190934-66_201c.sdfits HISTORY 97-11-19_0525_191519-66_202c.sdfits HISTORY 97-11-19_0534_192103-66_203c.sdfits HISTORY 97-11-19_0544_192647-66_204c.sdfits HISTORY 97-11-19_0553_193231-66_205c.sdfits HISTORY 97-11-19_0602_183509-66_195c.sdfits HISTORY 97-11-19_0612_184053-66_196c.sdfits HISTORY 97-11-19_0622_184638-66_197c.sdfits HISTORY 97-11-19_0631_185222-66_198c.sdfits HISTORY 97-11-19_0640_185806-66_199c.sdfits HISTORY 98-03-24_2107_193706-66_206b.sdfits HISTORY 98-03-24_2116_194251-66_207b.sdfits HISTORY 98-03-25_2020_190826-66_201b.sdfits HISTORY 98-03-25_2029_191410-66_202b.sdfits HISTORY 98-03-25_2038_191954-66_203b.sdfits HISTORY 98-03-25_2047_192538-66_204b.sdfits HISTORY 98-03-25_2056_193122-66_205b.sdfits HISTORY 98-03-26_2048_190459-66_200d.sdfits HISTORY 98-03-27_2034_191627-66_202d.sdfits HISTORY 98-03-27_2043_192212-66_203d.sdfits HISTORY 98-03-27_2052_192756-66_204d.sdfits HISTORY 98-03-27_2102_193340-66_205d.sdfits HISTORY 98-03-27_2111_193924-66_206d.sdfits HISTORY 98-03-27_2120_194508-66_207d.sdfits HISTORY 98-03-27_2130_191043-66_201d.sdfits HISTORY 98-05-10_2123_182232-66_193b.sdfits HISTORY 98-05-10_2133_182816-66_194b.sdfits HISTORY 98-05-10_2142_183400-66_195b.sdfits HISTORY 98-05-10_2151_183945-66_196b.sdfits HISTORY 98-05-10_2200_184529-66_197b.sdfits HISTORY 98-05-10_2209_185113-66_198b.sdfits HISTORY 98-05-10_2219_185657-66_199b.sdfits HISTORY 98-05-10_2228_190241-66_200b.sdfits HISTORY 98-05-13_2132_182450-66_193d.sdfits HISTORY 98-05-13_2151_183034-66_194d.sdfits HISTORY 98-05-13_2200_183618-66_195d.sdfits HISTORY 98-05-13_2210_184202-66_196d.sdfits HISTORY 98-05-13_2219_184746-66_197d.sdfits HISTORY 98-05-13_2228_185331-66_198d.sdfits HISTORY 98-05-13_2237_185915-66_199d.sdfits HISTORY 98-05-25_1711_182559-66_193e.sdfits HISTORY 98-05-25_1720_183143-66_194e.sdfits HISTORY 98-05-25_1729_183727-66_195e.sdfits HISTORY 98-05-25_1738_184311-66_196e.sdfits HISTORY 98-05-25_1747_184855-66_197e.sdfits HISTORY 98-05-25_1756_185439-66_198e.sdfits HISTORY 98-05-25_1806_190024-66_199e.sdfits HISTORY 98-05-25_1815_190608-66_200e.sdfits HISTORY 98-05-25_1824_191152-66_201e.sdfits HISTORY 98-05-25_1833_191736-66_202e.sdfits HISTORY 98-05-25_1842_192320-66_203e.sdfits HISTORY 98-05-25_1851_192905-66_204e.sdfits HISTORY 98-05-25_1901_193449-66_205e.sdfits HISTORY 98-05-25_1910_194033-66_206e.sdfits HISTORY 98-05-25_1919_194617-66_207e.sdfits HISTORY Original FITS filename "1904-66_STG.continuum.fits". HISTORY Noise level of continuum map: 61 mJy (RMS) astropy-astropy-201cddb/astropy/wcs/tests/data/maps/1904-66_SZP.hdr000066400000000000000000000223401507226315300247710ustar00rootroot00000000000000SIMPLE = T BITPIX = -32 / IEEE (big-endian) 32-bit floating point data NAXIS = 2 NAXIS1 = 192 NAXIS2 = 192 BUNIT = 'JY/BEAM ' CTYPE1 = 'RA---SZP' CRPIX1 = -2.478656972779E+02 CDELT1 = -6.666666666667E-02 CRVAL1 = 0.000000000000E+00 CTYPE2 = 'DEC--SZP' CRPIX2 = -2.262051956373E+01 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_1 = 2.000000000000E+00 / Projection parameter 1 PV2_2 = 1.800000000000E+02 / Projection parameter 2 PV2_3 = 6.000000000000E+01 / Projection parameter 3 EQUINOX = 2.000000000000E+03 / Equinox of equatorial coordinates BMAJ = 2.399999936422E-01 / Beam major axis in degrees BMIN = 2.399999936422E-01 / Beam minor axis in degrees BPA = 0.000000000000E+00 / Beam position angle in degrees RESTFRQ = 1.420405750000E+09 / Line rest frequency, Hz HISTORY Parkes Multibeam continuum map HISTORY Formed on Mon 2004/02/09 01:20:19 GMT by "pksgridzilla" which was HISTORY compiled on Feb 9 2004 12:08:02 (local time) within HISTORY AIPS++ version 19.405.00 dated . HISTORY Polarization mode: A and B aggregated HISTORY Gridding parameters: HISTORY Method: WGTMED HISTORY Clip fraction: 0.000 HISTORY Tsys weighting: applied HISTORY Beam weight order: 1 HISTORY Beam FWHM: 14.4 arcmin HISTORY Beam normalization: applied HISTORY Smoothing kernel type: TOP-HAT HISTORY Kernel FWHM: 12.0 arcmin HISTORY Cutoff radius: 6.0 arcmin HISTORY Beam RSS cutoff: 0.0 HISTORY Input data sets: HISTORY 97-10-09_0356_193558-66_206a.sdfits HISTORY 97-10-12_0142_182123-66_193a.sdfits HISTORY 97-10-12_0151_182707-66_194a.sdfits HISTORY 97-10-12_0200_183252-66_195a.sdfits HISTORY 97-11-07_0510_183836-66_196a.sdfits HISTORY 97-11-07_0519_184420-66_197a.sdfits HISTORY 97-11-07_0528_185004-66_198a.sdfits HISTORY 97-11-07_0537_185548-66_199a.sdfits HISTORY 97-11-07_0546_190132-66_200a.sdfits HISTORY 97-11-07_0556_190717-66_201a.sdfits HISTORY 97-11-07_0645_191301-66_202a.sdfits HISTORY 97-11-07_0654_191845-66_203a.sdfits HISTORY 97-11-07_0703_192429-66_204a.sdfits HISTORY 97-11-07_0712_193013-66_205a.sdfits HISTORY 97-11-07_0724_194142-66_207a.sdfits HISTORY 97-11-18_0256_193815-66_206c.sdfits HISTORY 97-11-18_0306_194359-66_207c.sdfits HISTORY 97-11-19_0447_182341-66_193c.sdfits HISTORY 97-11-19_0456_182925-66_194c.sdfits HISTORY 97-11-19_0507_190350-66_200c.sdfits HISTORY 97-11-19_0516_190934-66_201c.sdfits HISTORY 97-11-19_0525_191519-66_202c.sdfits HISTORY 97-11-19_0534_192103-66_203c.sdfits HISTORY 97-11-19_0544_192647-66_204c.sdfits HISTORY 97-11-19_0553_193231-66_205c.sdfits HISTORY 97-11-19_0602_183509-66_195c.sdfits HISTORY 97-11-19_0612_184053-66_196c.sdfits HISTORY 97-11-19_0622_184638-66_197c.sdfits HISTORY 97-11-19_0631_185222-66_198c.sdfits HISTORY 97-11-19_0640_185806-66_199c.sdfits HISTORY 98-03-24_2107_193706-66_206b.sdfits HISTORY 98-03-24_2116_194251-66_207b.sdfits HISTORY 98-03-25_2020_190826-66_201b.sdfits HISTORY 98-03-25_2029_191410-66_202b.sdfits HISTORY 98-03-25_2038_191954-66_203b.sdfits HISTORY 98-03-25_2047_192538-66_204b.sdfits HISTORY 98-03-25_2056_193122-66_205b.sdfits HISTORY 98-03-26_2048_190459-66_200d.sdfits HISTORY 98-03-27_2034_191627-66_202d.sdfits HISTORY 98-03-27_2043_192212-66_203d.sdfits HISTORY 98-03-27_2052_192756-66_204d.sdfits HISTORY 98-03-27_2102_193340-66_205d.sdfits HISTORY 98-03-27_2111_193924-66_206d.sdfits HISTORY 98-03-27_2120_194508-66_207d.sdfits HISTORY 98-03-27_2130_191043-66_201d.sdfits HISTORY 98-05-10_2123_182232-66_193b.sdfits HISTORY 98-05-10_2133_182816-66_194b.sdfits HISTORY 98-05-10_2142_183400-66_195b.sdfits HISTORY 98-05-10_2151_183945-66_196b.sdfits HISTORY 98-05-10_2200_184529-66_197b.sdfits HISTORY 98-05-10_2209_185113-66_198b.sdfits HISTORY 98-05-10_2219_185657-66_199b.sdfits HISTORY 98-05-10_2228_190241-66_200b.sdfits HISTORY 98-05-13_2132_182450-66_193d.sdfits HISTORY 98-05-13_2151_183034-66_194d.sdfits HISTORY 98-05-13_2200_183618-66_195d.sdfits HISTORY 98-05-13_2210_184202-66_196d.sdfits HISTORY 98-05-13_2219_184746-66_197d.sdfits HISTORY 98-05-13_2228_185331-66_198d.sdfits HISTORY 98-05-13_2237_185915-66_199d.sdfits HISTORY 98-05-25_1711_182559-66_193e.sdfits HISTORY 98-05-25_1720_183143-66_194e.sdfits HISTORY 98-05-25_1729_183727-66_195e.sdfits HISTORY 98-05-25_1738_184311-66_196e.sdfits HISTORY 98-05-25_1747_184855-66_197e.sdfits HISTORY 98-05-25_1756_185439-66_198e.sdfits HISTORY 98-05-25_1806_190024-66_199e.sdfits HISTORY 98-05-25_1815_190608-66_200e.sdfits HISTORY 98-05-25_1824_191152-66_201e.sdfits HISTORY 98-05-25_1833_191736-66_202e.sdfits HISTORY 98-05-25_1842_192320-66_203e.sdfits HISTORY 98-05-25_1851_192905-66_204e.sdfits HISTORY 98-05-25_1901_193449-66_205e.sdfits HISTORY 98-05-25_1910_194033-66_206e.sdfits HISTORY 98-05-25_1919_194617-66_207e.sdfits HISTORY Original FITS filename "1904-66_SZP.continuum.fits". HISTORY Noise level of continuum map: 61 mJy (RMS) astropy-astropy-201cddb/astropy/wcs/tests/data/maps/1904-66_TAN.hdr000066400000000000000000000217601507226315300247440ustar00rootroot00000000000000SIMPLE = T BITPIX = -32 / IEEE (big-endian) 32-bit floating point data NAXIS = 2 NAXIS1 = 192 NAXIS2 = 192 BUNIT = 'JY/BEAM ' CTYPE1 = 'RA---TAN' CRPIX1 = -2.680658087122E+02 CDELT1 = -6.666666666667E-02 CRVAL1 = 0.000000000000E+00 CTYPE2 = 'DEC--TAN' CRPIX2 = -5.630437201085E-01 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 EQUINOX = 2.000000000000E+03 / Equinox of equatorial coordinates BMAJ = 2.399999936422E-01 / Beam major axis in degrees BMIN = 2.399999936422E-01 / Beam minor axis in degrees BPA = 0.000000000000E+00 / Beam position angle in degrees RESTFRQ = 1.420405750000E+09 / Line rest frequency, Hz HISTORY Parkes Multibeam continuum map HISTORY Formed on Mon 2004/02/09 01:23:37 GMT by "pksgridzilla" which was HISTORY compiled on Feb 9 2004 12:08:02 (local time) within HISTORY AIPS++ version 19.405.00 dated . HISTORY Polarization mode: A and B aggregated HISTORY Gridding parameters: HISTORY Method: WGTMED HISTORY Clip fraction: 0.000 HISTORY Tsys weighting: applied HISTORY Beam weight order: 1 HISTORY Beam FWHM: 14.4 arcmin HISTORY Beam normalization: applied HISTORY Smoothing kernel type: TOP-HAT HISTORY Kernel FWHM: 12.0 arcmin HISTORY Cutoff radius: 6.0 arcmin HISTORY Beam RSS cutoff: 0.0 HISTORY Input data sets: HISTORY 97-10-09_0356_193558-66_206a.sdfits HISTORY 97-10-12_0142_182123-66_193a.sdfits HISTORY 97-10-12_0151_182707-66_194a.sdfits HISTORY 97-10-12_0200_183252-66_195a.sdfits HISTORY 97-11-07_0510_183836-66_196a.sdfits HISTORY 97-11-07_0519_184420-66_197a.sdfits HISTORY 97-11-07_0528_185004-66_198a.sdfits HISTORY 97-11-07_0537_185548-66_199a.sdfits HISTORY 97-11-07_0546_190132-66_200a.sdfits HISTORY 97-11-07_0556_190717-66_201a.sdfits HISTORY 97-11-07_0645_191301-66_202a.sdfits HISTORY 97-11-07_0654_191845-66_203a.sdfits HISTORY 97-11-07_0703_192429-66_204a.sdfits HISTORY 97-11-07_0712_193013-66_205a.sdfits HISTORY 97-11-07_0724_194142-66_207a.sdfits HISTORY 97-11-18_0256_193815-66_206c.sdfits HISTORY 97-11-18_0306_194359-66_207c.sdfits HISTORY 97-11-19_0447_182341-66_193c.sdfits HISTORY 97-11-19_0456_182925-66_194c.sdfits HISTORY 97-11-19_0507_190350-66_200c.sdfits HISTORY 97-11-19_0516_190934-66_201c.sdfits HISTORY 97-11-19_0525_191519-66_202c.sdfits HISTORY 97-11-19_0534_192103-66_203c.sdfits HISTORY 97-11-19_0544_192647-66_204c.sdfits HISTORY 97-11-19_0553_193231-66_205c.sdfits HISTORY 97-11-19_0602_183509-66_195c.sdfits HISTORY 97-11-19_0612_184053-66_196c.sdfits HISTORY 97-11-19_0622_184638-66_197c.sdfits HISTORY 97-11-19_0631_185222-66_198c.sdfits HISTORY 97-11-19_0640_185806-66_199c.sdfits HISTORY 98-03-24_2107_193706-66_206b.sdfits HISTORY 98-03-24_2116_194251-66_207b.sdfits HISTORY 98-03-25_2020_190826-66_201b.sdfits HISTORY 98-03-25_2029_191410-66_202b.sdfits HISTORY 98-03-25_2038_191954-66_203b.sdfits HISTORY 98-03-25_2047_192538-66_204b.sdfits HISTORY 98-03-25_2056_193122-66_205b.sdfits HISTORY 98-03-26_2048_190459-66_200d.sdfits HISTORY 98-03-27_2034_191627-66_202d.sdfits HISTORY 98-03-27_2043_192212-66_203d.sdfits HISTORY 98-03-27_2052_192756-66_204d.sdfits HISTORY 98-03-27_2102_193340-66_205d.sdfits HISTORY 98-03-27_2111_193924-66_206d.sdfits HISTORY 98-03-27_2120_194508-66_207d.sdfits HISTORY 98-03-27_2130_191043-66_201d.sdfits HISTORY 98-05-10_2123_182232-66_193b.sdfits HISTORY 98-05-10_2133_182816-66_194b.sdfits HISTORY 98-05-10_2142_183400-66_195b.sdfits HISTORY 98-05-10_2151_183945-66_196b.sdfits HISTORY 98-05-10_2200_184529-66_197b.sdfits HISTORY 98-05-10_2209_185113-66_198b.sdfits HISTORY 98-05-10_2219_185657-66_199b.sdfits HISTORY 98-05-10_2228_190241-66_200b.sdfits HISTORY 98-05-13_2132_182450-66_193d.sdfits HISTORY 98-05-13_2151_183034-66_194d.sdfits HISTORY 98-05-13_2200_183618-66_195d.sdfits HISTORY 98-05-13_2210_184202-66_196d.sdfits HISTORY 98-05-13_2219_184746-66_197d.sdfits HISTORY 98-05-13_2228_185331-66_198d.sdfits HISTORY 98-05-13_2237_185915-66_199d.sdfits HISTORY 98-05-25_1711_182559-66_193e.sdfits HISTORY 98-05-25_1720_183143-66_194e.sdfits HISTORY 98-05-25_1729_183727-66_195e.sdfits HISTORY 98-05-25_1738_184311-66_196e.sdfits HISTORY 98-05-25_1747_184855-66_197e.sdfits HISTORY 98-05-25_1756_185439-66_198e.sdfits HISTORY 98-05-25_1806_190024-66_199e.sdfits HISTORY 98-05-25_1815_190608-66_200e.sdfits HISTORY 98-05-25_1824_191152-66_201e.sdfits HISTORY 98-05-25_1833_191736-66_202e.sdfits HISTORY 98-05-25_1842_192320-66_203e.sdfits HISTORY 98-05-25_1851_192905-66_204e.sdfits HISTORY 98-05-25_1901_193449-66_205e.sdfits HISTORY 98-05-25_1910_194033-66_206e.sdfits HISTORY 98-05-25_1919_194617-66_207e.sdfits HISTORY Original FITS filename "1904-66_TAN.continuum.fits". HISTORY Noise level of continuum map: 59 mJy (RMS) astropy-astropy-201cddb/astropy/wcs/tests/data/maps/1904-66_TSC.hdr000066400000000000000000000217601507226315300247530ustar00rootroot00000000000000SIMPLE = T BITPIX = -32 / IEEE (big-endian) 32-bit floating point data NAXIS = 2 NAXIS1 = 192 NAXIS2 = 192 BUNIT = 'JY/BEAM ' CTYPE1 = 'RA---TSC' CRPIX1 = -1.897220156818E+02 CDELT1 = -6.666666666667E-02 CRVAL1 = 0.000000000000E+00 CTYPE2 = 'DEC--TSC' CRPIX2 = 2.037416464676E+01 CDELT2 = 6.666666666667E-02 CRVAL2 = -9.000000000000E+01 LONPOLE = 1.800000000000E+02 / Native longitude of celestial pole LATPOLE = 0.000000000000E+00 / Native latitude of celestial pole EQUINOX = 2.000000000000E+03 / Equinox of equatorial coordinates BMAJ = 2.399999936422E-01 / Beam major axis in degrees BMIN = 2.399999936422E-01 / Beam minor axis in degrees BPA = 0.000000000000E+00 / Beam position angle in degrees RESTFRQ = 1.420405750000E+09 / Line rest frequency, Hz HISTORY Parkes Multibeam continuum map HISTORY Formed on Mon 2004/02/09 02:23:02 GMT by "pksgridzilla" which was HISTORY compiled on Feb 9 2004 12:08:02 (local time) within HISTORY AIPS++ version 19.405.00 dated . HISTORY Polarization mode: A and B aggregated HISTORY Gridding parameters: HISTORY Method: WGTMED HISTORY Clip fraction: 0.000 HISTORY Tsys weighting: applied HISTORY Beam weight order: 1 HISTORY Beam FWHM: 14.4 arcmin HISTORY Beam normalization: applied HISTORY Smoothing kernel type: TOP-HAT HISTORY Kernel FWHM: 12.0 arcmin HISTORY Cutoff radius: 6.0 arcmin HISTORY Beam RSS cutoff: 0.0 HISTORY Input data sets: HISTORY 97-10-09_0356_193558-66_206a.sdfits HISTORY 97-10-12_0142_182123-66_193a.sdfits HISTORY 97-10-12_0151_182707-66_194a.sdfits HISTORY 97-10-12_0200_183252-66_195a.sdfits HISTORY 97-11-07_0510_183836-66_196a.sdfits HISTORY 97-11-07_0519_184420-66_197a.sdfits HISTORY 97-11-07_0528_185004-66_198a.sdfits HISTORY 97-11-07_0537_185548-66_199a.sdfits HISTORY 97-11-07_0546_190132-66_200a.sdfits HISTORY 97-11-07_0556_190717-66_201a.sdfits HISTORY 97-11-07_0645_191301-66_202a.sdfits HISTORY 97-11-07_0654_191845-66_203a.sdfits HISTORY 97-11-07_0703_192429-66_204a.sdfits HISTORY 97-11-07_0712_193013-66_205a.sdfits HISTORY 97-11-07_0724_194142-66_207a.sdfits HISTORY 97-11-18_0256_193815-66_206c.sdfits HISTORY 97-11-18_0306_194359-66_207c.sdfits HISTORY 97-11-19_0447_182341-66_193c.sdfits HISTORY 97-11-19_0456_182925-66_194c.sdfits HISTORY 97-11-19_0507_190350-66_200c.sdfits HISTORY 97-11-19_0516_190934-66_201c.sdfits HISTORY 97-11-19_0525_191519-66_202c.sdfits HISTORY 97-11-19_0534_192103-66_203c.sdfits HISTORY 97-11-19_0544_192647-66_204c.sdfits HISTORY 97-11-19_0553_193231-66_205c.sdfits HISTORY 97-11-19_0602_183509-66_195c.sdfits HISTORY 97-11-19_0612_184053-66_196c.sdfits HISTORY 97-11-19_0622_184638-66_197c.sdfits HISTORY 97-11-19_0631_185222-66_198c.sdfits HISTORY 97-11-19_0640_185806-66_199c.sdfits HISTORY 98-03-24_2107_193706-66_206b.sdfits HISTORY 98-03-24_2116_194251-66_207b.sdfits HISTORY 98-03-25_2020_190826-66_201b.sdfits HISTORY 98-03-25_2029_191410-66_202b.sdfits HISTORY 98-03-25_2038_191954-66_203b.sdfits HISTORY 98-03-25_2047_192538-66_204b.sdfits HISTORY 98-03-25_2056_193122-66_205b.sdfits HISTORY 98-03-26_2048_190459-66_200d.sdfits HISTORY 98-03-27_2034_191627-66_202d.sdfits HISTORY 98-03-27_2043_192212-66_203d.sdfits HISTORY 98-03-27_2052_192756-66_204d.sdfits HISTORY 98-03-27_2102_193340-66_205d.sdfits HISTORY 98-03-27_2111_193924-66_206d.sdfits HISTORY 98-03-27_2120_194508-66_207d.sdfits HISTORY 98-03-27_2130_191043-66_201d.sdfits HISTORY 98-05-10_2123_182232-66_193b.sdfits HISTORY 98-05-10_2133_182816-66_194b.sdfits HISTORY 98-05-10_2142_183400-66_195b.sdfits HISTORY 98-05-10_2151_183945-66_196b.sdfits HISTORY 98-05-10_2200_184529-66_197b.sdfits HISTORY 98-05-10_2209_185113-66_198b.sdfits HISTORY 98-05-10_2219_185657-66_199b.sdfits HISTORY 98-05-10_2228_190241-66_200b.sdfits HISTORY 98-05-13_2132_182450-66_193d.sdfits HISTORY 98-05-13_2151_183034-66_194d.sdfits HISTORY 98-05-13_2200_183618-66_195d.sdfits HISTORY 98-05-13_2210_184202-66_196d.sdfits HISTORY 98-05-13_2219_184746-66_197d.sdfits HISTORY 98-05-13_2228_185331-66_198d.sdfits HISTORY 98-05-13_2237_185915-66_199d.sdfits HISTORY 98-05-25_1711_182559-66_193e.sdfits HISTORY 98-05-25_1720_183143-66_194e.sdfits HISTORY 98-05-25_1729_183727-66_195e.sdfits HISTORY 98-05-25_1738_184311-66_196e.sdfits HISTORY 98-05-25_1747_184855-66_197e.sdfits HISTORY 98-05-25_1756_185439-66_198e.sdfits HISTORY 98-05-25_1806_190024-66_199e.sdfits HISTORY 98-05-25_1815_190608-66_200e.sdfits HISTORY 98-05-25_1824_191152-66_201e.sdfits HISTORY 98-05-25_1833_191736-66_202e.sdfits HISTORY 98-05-25_1842_192320-66_203e.sdfits HISTORY 98-05-25_1851_192905-66_204e.sdfits HISTORY 98-05-25_1901_193449-66_205e.sdfits HISTORY 98-05-25_1910_194033-66_206e.sdfits HISTORY 98-05-25_1919_194617-66_207e.sdfits HISTORY Original FITS filename "1904-66_TSC.continuum.fits". HISTORY Noise level of continuum map: 61 mJy (RMS) astropy-astropy-201cddb/astropy/wcs/tests/data/maps/1904-66_ZEA.hdr000066400000000000000000000217601507226315300247410ustar00rootroot00000000000000SIMPLE = T BITPIX = -32 / IEEE (big-endian) 32-bit floating point data NAXIS = 2 NAXIS1 = 192 NAXIS2 = 192 BUNIT = 'JY/BEAM ' CTYPE1 = 'RA---ZEA' CRPIX1 = -2.444880690361E+02 CDELT1 = -6.666666666667E-02 CRVAL1 = 0.000000000000E+00 CTYPE2 = 'DEC--ZEA' CRPIX2 = 5.738055949994E+00 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 EQUINOX = 2.000000000000E+03 / Equinox of equatorial coordinates BMAJ = 2.399999936422E-01 / Beam major axis in degrees BMIN = 2.399999936422E-01 / Beam minor axis in degrees BPA = 0.000000000000E+00 / Beam position angle in degrees RESTFRQ = 1.420405750000E+09 / Line rest frequency, Hz HISTORY Parkes Multibeam continuum map HISTORY Formed on Mon 2004/02/09 01:40:52 GMT by "pksgridzilla" which was HISTORY compiled on Feb 9 2004 12:08:02 (local time) within HISTORY AIPS++ version 19.405.00 dated . HISTORY Polarization mode: A and B aggregated HISTORY Gridding parameters: HISTORY Method: WGTMED HISTORY Clip fraction: 0.000 HISTORY Tsys weighting: applied HISTORY Beam weight order: 1 HISTORY Beam FWHM: 14.4 arcmin HISTORY Beam normalization: applied HISTORY Smoothing kernel type: TOP-HAT HISTORY Kernel FWHM: 12.0 arcmin HISTORY Cutoff radius: 6.0 arcmin HISTORY Beam RSS cutoff: 0.0 HISTORY Input data sets: HISTORY 97-10-09_0356_193558-66_206a.sdfits HISTORY 97-10-12_0142_182123-66_193a.sdfits HISTORY 97-10-12_0151_182707-66_194a.sdfits HISTORY 97-10-12_0200_183252-66_195a.sdfits HISTORY 97-11-07_0510_183836-66_196a.sdfits HISTORY 97-11-07_0519_184420-66_197a.sdfits HISTORY 97-11-07_0528_185004-66_198a.sdfits HISTORY 97-11-07_0537_185548-66_199a.sdfits HISTORY 97-11-07_0546_190132-66_200a.sdfits HISTORY 97-11-07_0556_190717-66_201a.sdfits HISTORY 97-11-07_0645_191301-66_202a.sdfits HISTORY 97-11-07_0654_191845-66_203a.sdfits HISTORY 97-11-07_0703_192429-66_204a.sdfits HISTORY 97-11-07_0712_193013-66_205a.sdfits HISTORY 97-11-07_0724_194142-66_207a.sdfits HISTORY 97-11-18_0256_193815-66_206c.sdfits HISTORY 97-11-18_0306_194359-66_207c.sdfits HISTORY 97-11-19_0447_182341-66_193c.sdfits HISTORY 97-11-19_0456_182925-66_194c.sdfits HISTORY 97-11-19_0507_190350-66_200c.sdfits HISTORY 97-11-19_0516_190934-66_201c.sdfits HISTORY 97-11-19_0525_191519-66_202c.sdfits HISTORY 97-11-19_0534_192103-66_203c.sdfits HISTORY 97-11-19_0544_192647-66_204c.sdfits HISTORY 97-11-19_0553_193231-66_205c.sdfits HISTORY 97-11-19_0602_183509-66_195c.sdfits HISTORY 97-11-19_0612_184053-66_196c.sdfits HISTORY 97-11-19_0622_184638-66_197c.sdfits HISTORY 97-11-19_0631_185222-66_198c.sdfits HISTORY 97-11-19_0640_185806-66_199c.sdfits HISTORY 98-03-24_2107_193706-66_206b.sdfits HISTORY 98-03-24_2116_194251-66_207b.sdfits HISTORY 98-03-25_2020_190826-66_201b.sdfits HISTORY 98-03-25_2029_191410-66_202b.sdfits HISTORY 98-03-25_2038_191954-66_203b.sdfits HISTORY 98-03-25_2047_192538-66_204b.sdfits HISTORY 98-03-25_2056_193122-66_205b.sdfits HISTORY 98-03-26_2048_190459-66_200d.sdfits HISTORY 98-03-27_2034_191627-66_202d.sdfits HISTORY 98-03-27_2043_192212-66_203d.sdfits HISTORY 98-03-27_2052_192756-66_204d.sdfits HISTORY 98-03-27_2102_193340-66_205d.sdfits HISTORY 98-03-27_2111_193924-66_206d.sdfits HISTORY 98-03-27_2120_194508-66_207d.sdfits HISTORY 98-03-27_2130_191043-66_201d.sdfits HISTORY 98-05-10_2123_182232-66_193b.sdfits HISTORY 98-05-10_2133_182816-66_194b.sdfits HISTORY 98-05-10_2142_183400-66_195b.sdfits HISTORY 98-05-10_2151_183945-66_196b.sdfits HISTORY 98-05-10_2200_184529-66_197b.sdfits HISTORY 98-05-10_2209_185113-66_198b.sdfits HISTORY 98-05-10_2219_185657-66_199b.sdfits HISTORY 98-05-10_2228_190241-66_200b.sdfits HISTORY 98-05-13_2132_182450-66_193d.sdfits HISTORY 98-05-13_2151_183034-66_194d.sdfits HISTORY 98-05-13_2200_183618-66_195d.sdfits HISTORY 98-05-13_2210_184202-66_196d.sdfits HISTORY 98-05-13_2219_184746-66_197d.sdfits HISTORY 98-05-13_2228_185331-66_198d.sdfits HISTORY 98-05-13_2237_185915-66_199d.sdfits HISTORY 98-05-25_1711_182559-66_193e.sdfits HISTORY 98-05-25_1720_183143-66_194e.sdfits HISTORY 98-05-25_1729_183727-66_195e.sdfits HISTORY 98-05-25_1738_184311-66_196e.sdfits HISTORY 98-05-25_1747_184855-66_197e.sdfits HISTORY 98-05-25_1756_185439-66_198e.sdfits HISTORY 98-05-25_1806_190024-66_199e.sdfits HISTORY 98-05-25_1815_190608-66_200e.sdfits HISTORY 98-05-25_1824_191152-66_201e.sdfits HISTORY 98-05-25_1833_191736-66_202e.sdfits HISTORY 98-05-25_1842_192320-66_203e.sdfits HISTORY 98-05-25_1851_192905-66_204e.sdfits HISTORY 98-05-25_1901_193449-66_205e.sdfits HISTORY 98-05-25_1910_194033-66_206e.sdfits HISTORY 98-05-25_1919_194617-66_207e.sdfits HISTORY Original FITS filename "1904-66_ZEA.continuum.fits". HISTORY Noise level of continuum map: 61 mJy (RMS) astropy-astropy-201cddb/astropy/wcs/tests/data/maps/1904-66_ZPN.hdr000066400000000000000000000250601507226315300247660ustar00rootroot00000000000000SIMPLE = T BITPIX = -32 / IEEE (big-endian) 32-bit floating point data NAXIS = 2 NAXIS1 = 192 NAXIS2 = 192 BUNIT = 'JY/BEAM ' CTYPE1 = 'RA---ZPN' CRPIX1 = -1.832937255632E+02 CDELT1 = -6.666666666667E-02 CRVAL1 = 0.000000000000E+00 CTYPE2 = 'DEC--ZPN' CRPIX2 = 2.209211120575E+01 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 = 5.000000000000E-02 / Projection parameter 0 PV2_1 = 9.750000000000E-01 / Projection parameter 1 PV2_2 = -8.070000000000E-01 / Projection parameter 2 PV2_3 = 3.370000000000E-01 / Projection parameter 3 PV2_4 = -6.500000000000E-02 / Projection parameter 4 PV2_5 = 1.000000000000E-02 / Projection parameter 5 PV2_6 = 3.000000000000E-03 / Projection parameter 6 PV2_7 = -1.000000000000E-03 / Projection parameter 7 PV2_8 = 0.000000000000E+00 / Projection parameter 8 PV2_9 = 0.000000000000E+00 / Projection parameter 9 PV2_10 = 0.000000000000E+00 / Projection parameter 10 PV2_11 = 0.000000000000E+00 / Projection parameter 11 PV2_12 = 0.000000000000E+00 / Projection parameter 12 PV2_13 = 0.000000000000E+00 / Projection parameter 13 PV2_14 = 0.000000000000E+00 / Projection parameter 14 PV2_15 = 0.000000000000E+00 / Projection parameter 15 PV2_16 = 0.000000000000E+00 / Projection parameter 16 PV2_17 = 0.000000000000E+00 / Projection parameter 17 PV2_18 = 0.000000000000E+00 / Projection parameter 18 PV2_19 = 0.000000000000E+00 / Projection parameter 19 EQUINOX = 2.000000000000E+03 / Equinox of equatorial coordinates BMAJ = 2.399999936422E-01 / Beam major axis in degrees BMIN = 2.399999936422E-01 / Beam minor axis in degrees BPA = 0.000000000000E+00 / Beam position angle in degrees RESTFRQ = 1.420405750000E+09 / Line rest frequency, Hz HISTORY Parkes Multibeam continuum map HISTORY Formed on Mon 2004/02/09 01:38:20 GMT by "pksgridzilla" which was HISTORY compiled on Feb 9 2004 12:08:02 (local time) within HISTORY AIPS++ version 19.405.00 dated . HISTORY Polarization mode: A and B aggregated HISTORY Gridding parameters: HISTORY Method: WGTMED HISTORY Clip fraction: 0.000 HISTORY Tsys weighting: applied HISTORY Beam weight order: 1 HISTORY Beam FWHM: 14.4 arcmin HISTORY Beam normalization: applied HISTORY Smoothing kernel type: TOP-HAT HISTORY Kernel FWHM: 12.0 arcmin HISTORY Cutoff radius: 6.0 arcmin HISTORY Beam RSS cutoff: 0.0 HISTORY Input data sets: HISTORY 97-10-09_0356_193558-66_206a.sdfits HISTORY 97-10-12_0142_182123-66_193a.sdfits HISTORY 97-10-12_0151_182707-66_194a.sdfits HISTORY 97-10-12_0200_183252-66_195a.sdfits HISTORY 97-11-07_0510_183836-66_196a.sdfits HISTORY 97-11-07_0519_184420-66_197a.sdfits HISTORY 97-11-07_0528_185004-66_198a.sdfits HISTORY 97-11-07_0537_185548-66_199a.sdfits HISTORY 97-11-07_0546_190132-66_200a.sdfits HISTORY 97-11-07_0556_190717-66_201a.sdfits HISTORY 97-11-07_0645_191301-66_202a.sdfits HISTORY 97-11-07_0654_191845-66_203a.sdfits HISTORY 97-11-07_0703_192429-66_204a.sdfits HISTORY 97-11-07_0712_193013-66_205a.sdfits HISTORY 97-11-07_0724_194142-66_207a.sdfits HISTORY 97-11-18_0256_193815-66_206c.sdfits HISTORY 97-11-18_0306_194359-66_207c.sdfits HISTORY 97-11-19_0447_182341-66_193c.sdfits HISTORY 97-11-19_0456_182925-66_194c.sdfits HISTORY 97-11-19_0507_190350-66_200c.sdfits HISTORY 97-11-19_0516_190934-66_201c.sdfits HISTORY 97-11-19_0525_191519-66_202c.sdfits HISTORY 97-11-19_0534_192103-66_203c.sdfits HISTORY 97-11-19_0544_192647-66_204c.sdfits HISTORY 97-11-19_0553_193231-66_205c.sdfits HISTORY 97-11-19_0602_183509-66_195c.sdfits HISTORY 97-11-19_0612_184053-66_196c.sdfits HISTORY 97-11-19_0622_184638-66_197c.sdfits HISTORY 97-11-19_0631_185222-66_198c.sdfits HISTORY 97-11-19_0640_185806-66_199c.sdfits HISTORY 98-03-24_2107_193706-66_206b.sdfits HISTORY 98-03-24_2116_194251-66_207b.sdfits HISTORY 98-03-25_2020_190826-66_201b.sdfits HISTORY 98-03-25_2029_191410-66_202b.sdfits HISTORY 98-03-25_2038_191954-66_203b.sdfits HISTORY 98-03-25_2047_192538-66_204b.sdfits HISTORY 98-03-25_2056_193122-66_205b.sdfits HISTORY 98-03-26_2048_190459-66_200d.sdfits HISTORY 98-03-27_2034_191627-66_202d.sdfits HISTORY 98-03-27_2043_192212-66_203d.sdfits HISTORY 98-03-27_2052_192756-66_204d.sdfits HISTORY 98-03-27_2102_193340-66_205d.sdfits HISTORY 98-03-27_2111_193924-66_206d.sdfits HISTORY 98-03-27_2120_194508-66_207d.sdfits HISTORY 98-03-27_2130_191043-66_201d.sdfits HISTORY 98-05-10_2123_182232-66_193b.sdfits HISTORY 98-05-10_2133_182816-66_194b.sdfits HISTORY 98-05-10_2142_183400-66_195b.sdfits HISTORY 98-05-10_2151_183945-66_196b.sdfits HISTORY 98-05-10_2200_184529-66_197b.sdfits HISTORY 98-05-10_2209_185113-66_198b.sdfits HISTORY 98-05-10_2219_185657-66_199b.sdfits HISTORY 98-05-10_2228_190241-66_200b.sdfits HISTORY 98-05-13_2132_182450-66_193d.sdfits HISTORY 98-05-13_2151_183034-66_194d.sdfits HISTORY 98-05-13_2200_183618-66_195d.sdfits HISTORY 98-05-13_2210_184202-66_196d.sdfits HISTORY 98-05-13_2219_184746-66_197d.sdfits HISTORY 98-05-13_2228_185331-66_198d.sdfits HISTORY 98-05-13_2237_185915-66_199d.sdfits HISTORY 98-05-25_1711_182559-66_193e.sdfits HISTORY 98-05-25_1720_183143-66_194e.sdfits HISTORY 98-05-25_1729_183727-66_195e.sdfits HISTORY 98-05-25_1738_184311-66_196e.sdfits HISTORY 98-05-25_1747_184855-66_197e.sdfits HISTORY 98-05-25_1756_185439-66_198e.sdfits HISTORY 98-05-25_1806_190024-66_199e.sdfits HISTORY 98-05-25_1815_190608-66_200e.sdfits HISTORY 98-05-25_1824_191152-66_201e.sdfits HISTORY 98-05-25_1833_191736-66_202e.sdfits HISTORY 98-05-25_1842_192320-66_203e.sdfits HISTORY 98-05-25_1851_192905-66_204e.sdfits HISTORY 98-05-25_1901_193449-66_205e.sdfits HISTORY 98-05-25_1910_194033-66_206e.sdfits HISTORY 98-05-25_1919_194617-66_207e.sdfits HISTORY Original FITS filename "1904-66_ZPN.continuum.fits". HISTORY Noise level of continuum map: 61 mJy (RMS) astropy-astropy-201cddb/astropy/wcs/tests/data/nonstandard_units.hdr000066400000000000000000000024031507226315300262020ustar00rootroot00000000000000CD1_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 ENDastropy-astropy-201cddb/astropy/wcs/tests/data/outside_sky.hdr000066400000000000000000000026771507226315300250240ustar00rootroot00000000000000SIMPLE = T / BITPIX = -32 / NAXIS = 2 / NAXIS1 = 2048 / NAXIS2 = 2048 / EXTEND = T / BSCALE = 1.00000000000E+00 / BZERO = 0.00000000000E+00 / CDELT1 = -8.19629704013E-02 / CRPIX1 = 1.02500000000E+03 / CRVAL1 = 79.95701 CTYPE1 = 'RA---SIN' / CDELT2 = 8.19629704013E-02 / CRPIX2 = 1.02500000000E+03 / CRVAL2 = -45.779 CTYPE2 = 'DEC--SIN' / EPOCH = 2.00000000000E+03 / PV2_1 = -0.755124458581295 PV2_2 = 0.209028857410973 astropy-astropy-201cddb/astropy/wcs/tests/data/sip-broken.hdr000066400000000000000000000625001507226315300245220ustar00rootroot00000000000000XTENSION= '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 astropy-astropy-201cddb/astropy/wcs/tests/data/sip.fits000066400000000000000000000132001507226315300234250ustar00rootroot00000000000000SIMPLE = T / conforms to FITS standard BITPIX = 8 / array data type NAXIS = 0 / number of array dimensions WCSAXES = 2 / Number of coordinate axes CRPIX1 = 128.0 / Pixel coordinate of reference point CRPIX2 = 128.0 / Pixel coordinate of reference point PC1_1 = 0.000249756880272 / Coordinate transformation matrix element PC1_2 = 0.000230177809744 / Coordinate transformation matrix element PC2_1 = 0.000230428519265 / Coordinate transformation matrix element PC2_2 = -0.000249965770577 / Coordinate transformation matrix element CDELT1 = 1 / [deg] Coordinate increment at reference point CDELT2 = 1 / [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-SIP' / Right ascension, gnomonic projection CTYPE2 = 'DEC--TAN-SIP' / Declination, gnomonic projection CRVAL1 = 202.482322805 / [deg] Coordinate value at reference point CRVAL2 = 47.17511893 / [deg] Coordinate value at reference point LONPOLE = 180 / [deg] Native longitude of celestial pole LATPOLE = 47.17511893 / [deg] Native latitude of celestial pole RESTFRQ = 0 / [Hz] Line rest frequency RESTWAV = 0 / [Hz] Line rest wavelength CRDER1 = 4.02509762361E-05 / [deg] Random error in coordinate CRDER2 = 3.42746131953E-05 / [deg] Random error in coordinate RADESYS = 'ICRS' / Equatorial coordinate system EQUINOX = 2000 / [yr] Equinox of equatorial coordinates BP_0_1 = -1.6588E-05 BP_0_2 = -2.3424E-05 A_3_0 = -1.4172E-07 B_3_0 = -2.0249E-08 BP_3_0 = 2.0482E-08 B_1_2 = -5.7813E-09 B_1_1 = -2.4386E-05 B_2_1 = -1.6583E-07 B_2_0 = 2.1197E-06 A_ORDER = 3 B_0_3 = -1.6168E-07 B_0_2 = 2.31E-05 BP_0_3 = 1.651E-07 B_ORDER = 3 BP_ORDER= 3 BP_1_2 = 3.8917E-09 AP_ORDER= 3 AP_3_0 = 1.4492E-07 A_1_1 = 2.1886E-05 BP_2_0 = -2.151E-06 A_1_2 = -1.6847E-07 AP_2_1 = 6.709E-09 AP_2_0 = 2.4146E-05 A_0_2 = 2.9656E-06 A_0_3 = 3.7746E-09 BP_1_1 = 2.4753E-05 BP_1_0 = -2.6783E-06 A_2_0 = -2.3863E-05 A_2_1 = -8.561E-09 AP_1_0 = -1.4897E-05 AP_1_1 = -2.225E-05 AP_1_2 = 1.7195E-07 BP_2_1 = 1.7E-07 AP_0_1 = -6.4275E-07 AP_0_3 = -3.582E-09 AP_0_2 = -2.9425E-06 END astropy-astropy-201cddb/astropy/wcs/tests/data/sip2.fits000066400000000000000000000132001507226315300235070ustar00rootroot00000000000000SIMPLE = T / conforms to FITS standard BITPIX = 8 / array data type NAXIS = 0 / number of array dimensions WCSAXES = 2 / Number of coordinate axes CRPIX1 = 128.0 / Pixel coordinate of reference point CRPIX2 = 128.0 / Pixel coordinate of reference point PC1_1 = 0.000249756880272 / Coordinate transformation matrix element PC1_2 = 0.000230177809744 / Coordinate transformation matrix element PC2_1 = 0.000230428519265 / Coordinate transformation matrix element PC2_2 = -0.000249965770577 / Coordinate transformation matrix element CDELT1 = 1 / [deg] Coordinate increment at reference point CDELT2 = 1 / [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-SIP' / Right ascension, gnomonic projection CTYPE2 = 'DEC--TAN-SIP' / Declination, gnomonic projection CRVAL1 = 202.482322805 / [deg] Coordinate value at reference point CRVAL2 = 47.17511893 / [deg] Coordinate value at reference point LONPOLE = 180 / [deg] Native longitude of celestial pole LATPOLE = 47.17511893 / [deg] Native latitude of celestial pole RESTFRQ = 0 / [Hz] Line rest frequency RESTWAV = 0 / [Hz] Line rest wavelength CRDER1 = 4.02509762361E-05 / [deg] Random error in coordinate CRDER2 = 3.42746131953E-05 / [deg] Random error in coordinate RADESYS = 'ICRS' / Equatorial coordinate system EQUINOX = 2000 / [yr] Equinox of equatorial coordinates A_3_0 = -1.4172E-07 B_3_0 = -2.0249E-08 B_1_2 = -5.7813E-09 B_1_1 = -2.4386E-05 B_2_1 = -1.6583E-07 B_2_0 = 2.1197E-06 A_ORDER = 3 B_0_3 = -1.6168E-07 B_0_2 = 2.31E-05 B_ORDER = 3 A_1_1 = 2.1886E-05 A_1_2 = -1.6847E-07 A_0_2 = 2.9656E-06 A_0_3 = 3.7746E-09 A_2_0 = -2.3863E-05 A_2_1 = -8.561E-09 END astropy-astropy-201cddb/astropy/wcs/tests/data/siponly.hdr000066400000000000000000000702001507226315300241420ustar00rootroot00000000000000SIMPLE = 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 astropy-astropy-201cddb/astropy/wcs/tests/data/spectra/000077500000000000000000000000001507226315300234105ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/wcs/tests/data/spectra/orion-freq-1.hdr000066400000000000000000000733001507226315300263310ustar00rootroot00000000000000SIMPLE = T / file does conform to FITS standard BITPIX = -32 / number of bits per data pixel NAXIS = 1 / number of data axes NAXIS1 = 4096 / length of data axis 1 EXTEND = T / FITS dataset may contain extensions COMMENT FITS (Flexible Image Transport System) format is defined in 'AstronomyCOMMENT and Astrophysics', volume 376, page 359; bibcode: 2001A&A...376..359H COMMENT COMMENT This FITS file contains an example spectral WCS header constructed by COMMENT Mark Calabretta (ATNF) and Dirk Petry (ESO) based on an observation COMMENT of the Orion Kleinmann-Low nebula made by Andrew Walsh (JCU) and COMMENT Sven Thorwirth (MPIfR) using the Mopra radio telescope. COMMENT COMMENT The 110GHz 13CO 1-0 spectrum in this file is linear in frequency, as COMMENT observed, it being the Fourier transform of a lag spectrum produced COMMENT by a correlating spectrometer. COMMENT COMMENT The reference pixel has been placed deliberately well outside the COMMENT the spectrum in order to test spectral-WCS-interpreting software. COMMENT COMMENT Spectral representations are: COMMENT Frequency (default) ...frequency-like COMMENT E: Photon energy ...frequency-like COMMENT N: Wave number ...frequency-like COMMENT R: Radio velocity ...frequency-like COMMENT W: Wavelength ...wavelength-like COMMENT O: Optical velocity ...wavelength-like COMMENT Z: Redshift ...wavelength-like COMMENT V: Relativistic velocity ...velocity-like COMMENT B: Relativistic beta ...velocity-like COMMENT COMMENT The Mopra radio telescope is operated by the Australia Telescope COMMENT National Facility. COMMENT COMMENT Author: Mark Calabretta, Australia Telescope National Facility COMMENT http://www.atnf.csiro.au/~mcalabre/index.html COMMENT 2009-04-22 COMMENT ---------------------------------------------------------------------- COMMENT OBJECT = 'Orion-KL' / Orion Kleinmann-Low nebula MOLECULE= '13CO ' / Carbon(13) monoxide TRANSITI= '1-0 ' / 1-0 transition DATE-OBS= '2006-07-09T20:29:00' / Date of observation TELESCOP= 'ATNF Mopra' / 22m mm-wave telescope OBSERVER= 'Walsh/Thorwirth' / Observers BUNIT = 'K ' / Brightness units, Kelvin COMMENT COMMENT ------------------------------------------------------------ Frequency COMMENT CRPIX1 = 32768.0 / Pixel coordinate of reference point CTYPE1 = 'FREQ ' / Linear frequency axis (FFT of lag spectrum) CRVAL1 = 102.1189414E+9 / [Hz] Frequency of reference channel CDELT1 = -2.695372970E+5 / [Hz] Channel spacing (lower sideband) CUNIT1 = 'Hz ' / Units of coordinate increment and value COMMENT RESTFRQ = 110201353000.0 / [Hz] 13CO line rest frequency RESTWAV = 0.00272040633 / [m] 13CO line rest wavelength SPECSYS = 'LSRK ' / Reference frame of spectral coordinates SSYSOBS = 'TOPOCENT' / Reference frame of observation VELOSYS = 0.0 / [m/s] Bary-topo velocity towards the source SSYSSRC = 'LSRK ' / Reference frame of source redshift ZSOURCE = 0.0000 / Redshift of the source COMMENT CRPIX2 = 1 CDELT2 = 1.0 CTYPE2 = 'RA ' CRVAL2 = 83.81042 / [deg] (05h35m14.5s) CUNIT2 = 'deg ' COMMENT CRPIX3 = 1 CDELT3 = 1.0 CTYPE3 = 'DEC ' CRVAL3 = -5.375222 / [deg] (-05:22:30.8) CUNIT3 = 'deg ' COMMENT RADESYS = 'FK5 ' / FK5 (IAU 1984) equatorial coordinates EQUINOX = 2000.0 / Equinox J2000.0 COMMENT CRPIX4 = 1 CDELT4 = 1.0 CTYPE4 = 'STOKES ' CRVAL4 = 1 / Stokes I (total intensity) COMMENT COMMENT -------------------------------------------------------- Photon energy COMMENT CRPIX1E = 32768.0 / Pixel coordinate of reference point CTYPE1E = 'ENER ' / Photon energy, linear frequency axis CRVAL1E = 4.223303869E-4 / [eV] Photon energy of reference channel CDELT1E = -1.114717695E-9 / [eV] Channel spacing CUNIT1E = 'eV ' / Units of coordinate increment and value COMMENT RESTFRQE= 110201353000.0 / [Hz] 13CO line rest frequency RESTWAVE= 0.00272040633 / [m] 13CO line rest wavelength SPECSYSE= 'LSRK ' / Reference frame of spectral coordinates SSYSOBSE= 'TOPOCENT' / Reference frame of observation VELOSYSE= 0.0 / [m/s] Bary-topo velocity towards the source SSYSSRCE= 'LSRK ' / Reference frame of source redshift ZSOURCEE= 0.0000 / Redshift of the source COMMENT CRPIX2E = 1 CDELT2E = 1.0 CTYPE2E = 'RA ' CRVAL2E = 83.81042 / [deg] (05h35m14.5s) CUNIT2E = 'deg ' COMMENT CRPIX3E = 1 CDELT3E = 1.0 CTYPE3E = 'DEC ' CRVAL3E = -5.375222 / [deg] (-05:22:30.8) CUNIT3E = 'deg ' COMMENT RADESYSE= 'FK5 ' / FK5 (IAU 1984) equatorial coordinates EQUINOXE= 2000.0 / Equinox J2000.0 COMMENT CRPIX4E = 1 CDELT4E = 1.0 CTYPE4E = 'STOKES ' CRVAL4E = 1 / Stokes I (total intensity) COMMENT COMMENT ---------------------------------------------------------- Wave number COMMENT N: Wave number COMMENT CRPIX1N = 32768.0 / Pixel coordinate of reference point CTYPE1N = 'WAVN ' / Wave number, linear frequency axis CRVAL1N = 3.406321229E+2 / [/m] Wave number of reference channel CDELT1N = -8.990796460E-4 / [/m] Channel spacing CUNIT1N = '/m ' / Units of coordinate increment and value COMMENT RESTFRQN= 110201353000.0 / [Hz] 13CO line rest frequency RESTWAVN= 0.00272040633 / [m] 13CO line rest wavelength SPECSYSN= 'LSRK ' / Reference frame of spectral coordinates SSYSOBSN= 'TOPOCENT' / Reference frame of observation VELOSYSN= 0.0 / [m/s] Bary-topo velocity towards the source SSYSSRCN= 'LSRK ' / Reference frame of source redshift ZSOURCEN= 0.0000 / Redshift of the source COMMENT CRPIX2N = 1 CDELT2N = 1.0 CTYPE2N = 'RA ' CRVAL2N = 83.81042 / [deg] (05h35m14.5s) CUNIT2N = 'deg ' COMMENT CRPIX3N = 1 CDELT3N = 1.0 CTYPE3N = 'DEC ' CRVAL3N = -5.375222 / [deg] (-05:22:30.8) CUNIT3N = 'deg ' COMMENT RADESYSN= 'FK5 ' / FK5 (IAU 1984) equatorial coordinates EQUINOXN= 2000.0 / Equinox J2000.0 COMMENT CRPIX4N = 1 CDELT4N = 1.0 CTYPE4N = 'STOKES ' CRVAL4N = 1 / Stokes I (total intensity) COMMENT COMMENT ------------------------------------------------------- Radio velocity COMMENT N: Wave number COMMENT R: Radio velocity COMMENT CRPIX1R = 32768.0 / Pixel coordinate of reference point CTYPE1R = 'VRAD ' / Radio velocity, linear frequency axis CRVAL1R = 2.198744369E+7 / [m/s] Radio velocity of reference channel CDELT1R = 7.332509683E+2 / [m/s] Channel spacing CUNIT1R = 'm/s ' / Units of coordinate increment and value COMMENT RESTFRQR= 110201353000.0 / [Hz] 13CO line rest frequency RESTWAVR= 0.00272040633 / [m] 13CO line rest wavelength SPECSYSR= 'LSRK ' / Reference frame of spectral coordinates SSYSOBSR= 'TOPOCENT' / Reference frame of observation VELOSYSR= 0.0 / [m/s] Bary-topo velocity towards the source SSYSSRCR= 'LSRK ' / Reference frame of source redshift ZSOURCER= 0.0000 / Redshift of the source COMMENT CRPIX2R = 1 CDELT2R = 1.0 CTYPE2R = 'RA ' CRVAL2R = 83.81042 / [deg] (05h35m14.5s) CUNIT2R = 'deg ' COMMENT CRPIX3R = 1 CDELT3R = 1.0 CTYPE3R = 'DEC ' CRVAL3R = -5.375222 / [deg] (-05:22:30.8) CUNIT3R = 'deg ' COMMENT RADESYSR= 'FK5 ' / FK5 (IAU 1984) equatorial coordinates EQUINOXR= 2000.0 / Equinox J2000.0 COMMENT CRPIX4R = 1 CDELT4R = 1.0 CTYPE4R = 'STOKES ' CRVAL4R = 1 / Stokes I (total intensity) COMMENT COMMENT ----------------------------------------------------------- Wavelength COMMENT N: Wave number COMMENT R: Radio velocity COMMENT W: Wavelength COMMENT CRPIX1W = 32768.0 / Pixel coordinate of reference point CTYPE1W = 'WAVE-F2W' / Wavelength in vacuuo, non-linear axis CRVAL1W = 2.935718427E-3 / [m] Wavelength of reference channel CDELT1W = 7.748666397E-9 / [m] Channel spacing CUNIT1W = 'm ' / Units of coordinate increment and value COMMENT SPECSYSW= 'LSRK ' / Reference frame of spectral coordinates SSYSOBSW= 'TOPOCENT' / Reference frame of observation VELOSYSW= 0.0 / [m/s] Bary-topo velocity towards the source SSYSSRCW= 'LSRK ' / Reference frame of source redshift ZSOURCEW= 0.0000 / Redshift of the source COMMENT CRPIX2W = 1 CDELT2W = 1.0 CTYPE2W = 'RA ' CRVAL2W = 83.81042 / [deg] (05h35m14.5s) CUNIT2W = 'deg ' COMMENT CRPIX3W = 1 CDELT3W = 1.0 CTYPE3W = 'DEC ' CRVAL3W = -5.375222 / [deg] (-05:22:30.8) CUNIT3W = 'deg ' COMMENT RADESYSW= 'FK5 ' / FK5 (IAU 1984) equatorial coordinates EQUINOXW= 2000.0 / Equinox J2000.0 COMMENT CRPIX4W = 1 CDELT4W = 1.0 CTYPE4W = 'STOKES ' CRVAL4W = 1 / Stokes I (total intensity) COMMENT COMMENT ----------------------------------------------------- Optical velocity COMMENT CRPIX1O = 32768.0 / Pixel coordinate of reference point CTYPE1O = 'VOPT-F2W' / Optical velocity, non-linear axis CRVAL1O = 2.372768470E+7 / [m/s] Optical velocity of reference channel CDELT1O = 8.539135209E+2 / [m/s] Channel spacing CUNIT1O = 'm/s ' / Units of coordinate increment and value COMMENT RESTFRQO= 110201353000.0 / [Hz] 13CO line rest frequency RESTWAVO= 0.00272040633 / [m] 13CO line rest wavelength SPECSYSO= 'LSRK ' / Reference frame of spectral coordinates SSYSOBSO= 'TOPOCENT' / Reference frame of observation VELOSYSO= 0.0 / [m/s] Bary-topo velocity towards the source SSYSSRCO= 'LSRK ' / Reference frame of source redshift ZSOURCEO= 0.0000 / Redshift of the source COMMENT CRPIX2O = 1 CDELT2O = 1.0 CTYPE2O = 'RA ' CRVAL2O = 83.81042 / [deg] (05h35m14.5s) CUNIT2O = 'deg ' COMMENT CRPIX3O = 1 CDELT3O = 1.0 CTYPE3O = 'DEC ' CRVAL3O = -5.375222 / [deg] (-05:22:30.8) CUNIT3O = 'deg ' COMMENT RADESYSO= 'FK5 ' / FK5 (IAU 1984) equatorial coordinates EQUINOXO= 2000.0 / Equinox J2000.0 COMMENT CRPIX4O = 1 CDELT4O = 1.0 CTYPE4O = 'STOKES ' CRVAL4O = 1 / Stokes I (total intensity) COMMENT COMMENT ------------------------------------------------------------- Redshift COMMENT N: Wave number COMMENT R: Radio velocity COMMENT W: Wavelength COMMENT O: Optical velocity COMMENT Z: Redshift COMMENT CRPIX1Z = 32768.0 / Pixel coordinate of reference point CTYPE1Z = 'ZOPT-F2W' / Redshift, non-linear axis CRVAL1Z = 7.914703679E-2 / [] Redshift of reference channel CDELT1Z = 2.848348910E-6 / [] Channel spacing COMMENT RESTFRQZ= 110201353000.0 / [Hz] 13CO line rest frequency RESTWAVZ= 0.00272040633 / [m] 13CO line rest wavelength SPECSYSZ= 'LSRK ' / Reference frame of spectral coordinates SSYSOBSZ= 'TOPOCENT' / Reference frame of observation VELOSYSZ= 0.0 / [m/s] Bary-topo velocity towards the source SSYSSRCZ= 'LSRK ' / Reference frame of source redshift ZSOURCEZ= 0.0000 / Redshift of the source COMMENT CRPIX2Z = 1 CDELT2Z = 1.0 CTYPE2Z = 'RA ' CRVAL2Z = 83.81042 / [deg] (05h35m14.5s) CUNIT2Z = 'deg ' COMMENT CRPIX3Z = 1 CDELT3Z = 1.0 CTYPE3Z = 'DEC ' CRVAL3Z = -5.375222 / [deg] (-05:22:30.8) CUNIT3Z = 'deg ' COMMENT RADESYSZ= 'FK5 ' / FK5 (IAU 1984) equatorial coordinates EQUINOXZ= 2000.0 / Equinox J2000.0 COMMENT CRPIX4Z = 1 CDELT4Z = 1.0 CTYPE4Z = 'STOKES ' CRVAL4Z = 1 / Stokes I (total intensity) COMMENT COMMENT ------------------------------------------------ Relativistic velocity COMMENT CRPIX1V = 32768.0 / Pixel coordinate of reference point CTYPE1V = 'VELO-F2V' / Relativistic velocity, non-linear axis CRVAL1V = 2.279141418E+7 / [m/s] Velocity of reference channel CDELT1V = 7.867122599E+2 / [m/s] Channel spacing CUNIT1V = 'm/s ' / Units of coordinate increment and value COMMENT RESTFRQV= 110201353000.0 / [Hz] 13CO line rest frequency RESTWAVV= 0.00272040633 / [m] 13CO line rest wavelength SPECSYSV= 'LSRK ' / Reference frame of spectral coordinates SSYSOBSV= 'TOPOCENT' / Reference frame of observation VELOSYSV= 0.0 / [m/s] Bary-topo velocity towards the source SSYSSRCV= 'LSRK ' / Reference frame of source redshift ZSOURCEV= 0.0000 / Redshift of the source COMMENT CRPIX2V = 1 CDELT2V = 1.0 CTYPE2V = 'RA ' CRVAL2V = 83.81042 / [deg] (05h35m14.5s) CUNIT2V = 'deg ' COMMENT CRPIX3V = 1 CDELT3V = 1.0 CTYPE3V = 'DEC ' CRVAL3V = -5.375222 / [deg] (-05:22:30.8) CUNIT3V = 'deg ' COMMENT RADESYSV= 'FK5 ' / FK5 (IAU 1984) equatorial coordinates EQUINOXV= 2000.0 / Equinox J2000.0 COMMENT CRPIX4V = 1 CDELT4V = 1.0 CTYPE4V = 'STOKES ' CRVAL4V = 1 / Stokes I (total intensity) COMMENT COMMENT ---------------------------------------------- Relativistic beta (v/c) COMMENT CRPIX1B = 32768.0 / Pixel coordinate of reference point CTYPE1B = 'BETA-F2V' / Relativistic beta (v/c), non-linear axis CRVAL1B = 7.602397448E-2 / [] Relativistic beta of reference channel CDELT1B = 2.624189632E-6 / [] Channel spacing COMMENT RESTFRQB= 110201353000.0 / [Hz] 13CO line rest frequency RESTWAVB= 0.00272040633 / [m] 13CO line rest wavelength SPECSYSB= 'LSRK ' / Reference frame of spectral coordinates SSYSOBSB= 'TOPOCENT' / Reference frame of observation VELOSYSB= 0.0 / [m/s] Bary-topo velocity towards the source SSYSSRCB= 'LSRK ' / Reference frame of source redshift ZSOURCEB= 0.0000 / Redshift of the source COMMENT CRPIX2B = 1 CDELT2B = 1.0 CTYPE2B = 'RA ' CRVAL2B = 83.81042 / [deg] (05h35m14.5s) CUNIT2B = 'deg ' COMMENT CRPIX3B = 1 CDELT3B = 1.0 CTYPE3B = 'DEC ' CRVAL3B = -5.375222 / [deg] (-05:22:30.8) CUNIT3B = 'deg ' COMMENT RADESYSB= 'FK5 ' / FK5 (IAU 1984) equatorial coordinates EQUINOXB= 2000.0 / Equinox J2000.0 COMMENT CRPIX4B = 1 CDELT4B = 1.0 CTYPE4B = 'STOKES ' CRVAL4B = 1 / Stokes I (total intensity) COMMENT HISTORY fimgcreate 1.0b at 2009-04-22T04:27:55 DATE = '2009-04-22T04:27:55' / file creation date (YYYY-MM-DDThh:mm:ss UT) astropy-astropy-201cddb/astropy/wcs/tests/data/spectra/orion-freq-4.hdr000066400000000000000000000736601507226315300263450ustar00rootroot00000000000000SIMPLE = T / file does conform to FITS standard BITPIX = -32 / number of bits per data pixel NAXIS = 4 / number of data axes NAXIS1 = 4096 / length of data axis 1 NAXIS2 = 1 / length of data axis 2 NAXIS3 = 1 / length of data axis 3 NAXIS4 = 1 / length of data axis 4 EXTEND = T / FITS dataset may contain extensions COMMENT FITS (Flexible Image Transport System) format is defined in 'AstronomyCOMMENT and Astrophysics', volume 376, page 359; bibcode: 2001A&A...376..359H COMMENT COMMENT This FITS file contains an example spectral WCS header constructed by COMMENT Mark Calabretta (ATNF) and Dirk Petry (ESO) based on an observation COMMENT of the Orion Kleinmann-Low nebula made by Andrew Walsh (JCU) and COMMENT Sven Thorwirth (MPIfR) using the Mopra radio telescope. COMMENT COMMENT The 110GHz 13CO 1-0 spectrum in this file is linear in frequency, as COMMENT observed, it being the Fourier transform of a lag spectrum produced COMMENT by a correlating spectrometer. COMMENT COMMENT The reference pixel has been placed deliberately well outside the COMMENT the spectrum in order to test spectral-WCS-interpreting software. COMMENT COMMENT Spectral representations are: COMMENT Frequency (default) ...frequency-like COMMENT E: Photon energy ...frequency-like COMMENT N: Wave number ...frequency-like COMMENT R: Radio velocity ...frequency-like COMMENT W: Wavelength ...wavelength-like COMMENT O: Optical velocity ...wavelength-like COMMENT Z: Redshift ...wavelength-like COMMENT V: Relativistic velocity ...velocity-like COMMENT B: Relativistic beta ...velocity-like COMMENT COMMENT The Mopra radio telescope is operated by the Australia Telescope COMMENT National Facility. COMMENT COMMENT Author: Mark Calabretta, Australia Telescope National Facility COMMENT http://www.atnf.csiro.au/~mcalabre/index.html COMMENT 2009-04-22 COMMENT ---------------------------------------------------------------------- COMMENT OBJECT = 'Orion-KL' / Orion Kleinmann-Low nebula MOLECULE= '13CO ' / Carbon(13) monoxide TRANSITI= '1-0 ' / 1-0 transition DATE-OBS= '2006-07-09T20:29:00' / Date of observation TELESCOP= 'ATNF Mopra' / 22m mm-wave telescope OBSERVER= 'Walsh/Thorwirth' / Observers BUNIT = 'K ' / Brightness units, Kelvin COMMENT COMMENT ------------------------------------------------------------ Frequency COMMENT CRPIX1 = 32768.0 / Pixel coordinate of reference point CTYPE1 = 'FREQ ' / Linear frequency axis (FFT of lag spectrum) CRVAL1 = 102.1189414E+9 / [Hz] Frequency of reference channel CDELT1 = -2.695372970E+5 / [Hz] Channel spacing (lower sideband) CUNIT1 = 'Hz ' / Units of coordinate increment and value COMMENT RESTFRQ = 110201353000.0 / [Hz] 13CO line rest frequency RESTWAV = 0.00272040633 / [m] 13CO line rest wavelength SPECSYS = 'LSRK ' / Reference frame of spectral coordinates SSYSOBS = 'TOPOCENT' / Reference frame of observation VELOSYS = 0.0 / [m/s] Bary-topo velocity towards the source SSYSSRC = 'LSRK ' / Reference frame of source redshift ZSOURCE = 0.0000 / Redshift of the source COMMENT CRPIX2 = 1 CDELT2 = 1.0 CTYPE2 = 'RA ' CRVAL2 = 83.81042 / [deg] (05h35m14.5s) CUNIT2 = 'deg ' COMMENT CRPIX3 = 1 CDELT3 = 1.0 CTYPE3 = 'DEC ' CRVAL3 = -5.375222 / [deg] (-05:22:30.8) CUNIT3 = 'deg ' COMMENT RADESYS = 'FK5 ' / FK5 (IAU 1984) equatorial coordinates EQUINOX = 2000.0 / Equinox J2000.0 COMMENT CRPIX4 = 1 CDELT4 = 1.0 CTYPE4 = 'STOKES ' CRVAL4 = 1 / Stokes I (total intensity) COMMENT COMMENT -------------------------------------------------------- Photon energy COMMENT CRPIX1E = 32768.0 / Pixel coordinate of reference point CTYPE1E = 'ENER ' / Photon energy, linear frequency axis CRVAL1E = 4.223303869E-4 / [eV] Photon energy of reference channel CDELT1E = -1.114717695E-9 / [eV] Channel spacing CUNIT1E = 'eV ' / Units of coordinate increment and value COMMENT RESTFRQE= 110201353000.0 / [Hz] 13CO line rest frequency RESTWAVE= 0.00272040633 / [m] 13CO line rest wavelength SPECSYSE= 'LSRK ' / Reference frame of spectral coordinates SSYSOBSE= 'TOPOCENT' / Reference frame of observation VELOSYSE= 0.0 / [m/s] Bary-topo velocity towards the source SSYSSRCE= 'LSRK ' / Reference frame of source redshift ZSOURCEE= 0.0000 / Redshift of the source COMMENT CRPIX2E = 1 CDELT2E = 1.0 CTYPE2E = 'RA ' CRVAL2E = 83.81042 / [deg] (05h35m14.5s) CUNIT2E = 'deg ' COMMENT CRPIX3E = 1 CDELT3E = 1.0 CTYPE3E = 'DEC ' CRVAL3E = -5.375222 / [deg] (-05:22:30.8) CUNIT3E = 'deg ' COMMENT RADESYSE= 'FK5 ' / FK5 (IAU 1984) equatorial coordinates EQUINOXE= 2000.0 / Equinox J2000.0 COMMENT CRPIX4E = 1 CDELT4E = 1.0 CTYPE4E = 'STOKES ' CRVAL4E = 1 / Stokes I (total intensity) COMMENT COMMENT ---------------------------------------------------------- Wave number COMMENT N: Wave number COMMENT CRPIX1N = 32768.0 / Pixel coordinate of reference point CTYPE1N = 'WAVN ' / Wave number, linear frequency axis CRVAL1N = 3.406321229E+2 / [/m] Wave number of reference channel CDELT1N = -8.990796460E-4 / [/m] Channel spacing CUNIT1N = '/m ' / Units of coordinate increment and value COMMENT RESTFRQN= 110201353000.0 / [Hz] 13CO line rest frequency RESTWAVN= 0.00272040633 / [m] 13CO line rest wavelength SPECSYSN= 'LSRK ' / Reference frame of spectral coordinates SSYSOBSN= 'TOPOCENT' / Reference frame of observation VELOSYSN= 0.0 / [m/s] Bary-topo velocity towards the source SSYSSRCN= 'LSRK ' / Reference frame of source redshift ZSOURCEN= 0.0000 / Redshift of the source COMMENT CRPIX2N = 1 CDELT2N = 1.0 CTYPE2N = 'RA ' CRVAL2N = 83.81042 / [deg] (05h35m14.5s) CUNIT2N = 'deg ' COMMENT CRPIX3N = 1 CDELT3N = 1.0 CTYPE3N = 'DEC ' CRVAL3N = -5.375222 / [deg] (-05:22:30.8) CUNIT3N = 'deg ' COMMENT RADESYSN= 'FK5 ' / FK5 (IAU 1984) equatorial coordinates EQUINOXN= 2000.0 / Equinox J2000.0 COMMENT CRPIX4N = 1 CDELT4N = 1.0 CTYPE4N = 'STOKES ' CRVAL4N = 1 / Stokes I (total intensity) COMMENT COMMENT ------------------------------------------------------- Radio velocity COMMENT N: Wave number COMMENT R: Radio velocity COMMENT CRPIX1R = 32768.0 / Pixel coordinate of reference point CTYPE1R = 'VRAD ' / Radio velocity, linear frequency axis CRVAL1R = 2.198744369E+7 / [m/s] Radio velocity of reference channel CDELT1R = 7.332509683E+2 / [m/s] Channel spacing CUNIT1R = 'm/s ' / Units of coordinate increment and value COMMENT RESTFRQR= 110201353000.0 / [Hz] 13CO line rest frequency RESTWAVR= 0.00272040633 / [m] 13CO line rest wavelength SPECSYSR= 'LSRK ' / Reference frame of spectral coordinates SSYSOBSR= 'TOPOCENT' / Reference frame of observation VELOSYSR= 0.0 / [m/s] Bary-topo velocity towards the source SSYSSRCR= 'LSRK ' / Reference frame of source redshift ZSOURCER= 0.0000 / Redshift of the source COMMENT CRPIX2R = 1 CDELT2R = 1.0 CTYPE2R = 'RA ' CRVAL2R = 83.81042 / [deg] (05h35m14.5s) CUNIT2R = 'deg ' COMMENT CRPIX3R = 1 CDELT3R = 1.0 CTYPE3R = 'DEC ' CRVAL3R = -5.375222 / [deg] (-05:22:30.8) CUNIT3R = 'deg ' COMMENT RADESYSR= 'FK5 ' / FK5 (IAU 1984) equatorial coordinates EQUINOXR= 2000.0 / Equinox J2000.0 COMMENT CRPIX4R = 1 CDELT4R = 1.0 CTYPE4R = 'STOKES ' CRVAL4R = 1 / Stokes I (total intensity) COMMENT COMMENT ----------------------------------------------------------- Wavelength COMMENT N: Wave number COMMENT R: Radio velocity COMMENT W: Wavelength COMMENT CRPIX1W = 32768.0 / Pixel coordinate of reference point CTYPE1W = 'WAVE-F2W' / Wavelength in vacuuo, non-linear axis CRVAL1W = 2.935718427E-3 / [m] Wavelength of reference channel CDELT1W = 7.748666397E-9 / [m] Channel spacing CUNIT1W = 'm ' / Units of coordinate increment and value COMMENT SPECSYSW= 'LSRK ' / Reference frame of spectral coordinates SSYSOBSW= 'TOPOCENT' / Reference frame of observation VELOSYSW= 0.0 / [m/s] Bary-topo velocity towards the source SSYSSRCW= 'LSRK ' / Reference frame of source redshift ZSOURCEW= 0.0000 / Redshift of the source COMMENT CRPIX2W = 1 CDELT2W = 1.0 CTYPE2W = 'RA ' CRVAL2W = 83.81042 / [deg] (05h35m14.5s) CUNIT2W = 'deg ' COMMENT CRPIX3W = 1 CDELT3W = 1.0 CTYPE3W = 'DEC ' CRVAL3W = -5.375222 / [deg] (-05:22:30.8) CUNIT3W = 'deg ' COMMENT RADESYSW= 'FK5 ' / FK5 (IAU 1984) equatorial coordinates EQUINOXW= 2000.0 / Equinox J2000.0 COMMENT CRPIX4W = 1 CDELT4W = 1.0 CTYPE4W = 'STOKES ' CRVAL4W = 1 / Stokes I (total intensity) COMMENT COMMENT ----------------------------------------------------- Optical velocity COMMENT CRPIX1O = 32768.0 / Pixel coordinate of reference point CTYPE1O = 'VOPT-F2W' / Optical velocity, non-linear axis CRVAL1O = 2.372768470E+7 / [m/s] Optical velocity of reference channel CDELT1O = 8.539135209E+2 / [m/s] Channel spacing CUNIT1O = 'm/s ' / Units of coordinate increment and value COMMENT RESTFRQO= 110201353000.0 / [Hz] 13CO line rest frequency RESTWAVO= 0.00272040633 / [m] 13CO line rest wavelength SPECSYSO= 'LSRK ' / Reference frame of spectral coordinates SSYSOBSO= 'TOPOCENT' / Reference frame of observation VELOSYSO= 0.0 / [m/s] Bary-topo velocity towards the source SSYSSRCO= 'LSRK ' / Reference frame of source redshift ZSOURCEO= 0.0000 / Redshift of the source COMMENT CRPIX2O = 1 CDELT2O = 1.0 CTYPE2O = 'RA ' CRVAL2O = 83.81042 / [deg] (05h35m14.5s) CUNIT2O = 'deg ' COMMENT CRPIX3O = 1 CDELT3O = 1.0 CTYPE3O = 'DEC ' CRVAL3O = -5.375222 / [deg] (-05:22:30.8) CUNIT3O = 'deg ' COMMENT RADESYSO= 'FK5 ' / FK5 (IAU 1984) equatorial coordinates EQUINOXO= 2000.0 / Equinox J2000.0 COMMENT CRPIX4O = 1 CDELT4O = 1.0 CTYPE4O = 'STOKES ' CRVAL4O = 1 / Stokes I (total intensity) COMMENT COMMENT ------------------------------------------------------------- Redshift COMMENT N: Wave number COMMENT R: Radio velocity COMMENT W: Wavelength COMMENT O: Optical velocity COMMENT Z: Redshift COMMENT CRPIX1Z = 32768.0 / Pixel coordinate of reference point CTYPE1Z = 'ZOPT-F2W' / Redshift, non-linear axis CRVAL1Z = 7.914703679E-2 / [] Redshift of reference channel CDELT1Z = 2.848348910E-6 / [] Channel spacing COMMENT RESTFRQZ= 110201353000.0 / [Hz] 13CO line rest frequency RESTWAVZ= 0.00272040633 / [m] 13CO line rest wavelength SPECSYSZ= 'LSRK ' / Reference frame of spectral coordinates SSYSOBSZ= 'TOPOCENT' / Reference frame of observation VELOSYSZ= 0.0 / [m/s] Bary-topo velocity towards the source SSYSSRCZ= 'LSRK ' / Reference frame of source redshift ZSOURCEZ= 0.0000 / Redshift of the source COMMENT CRPIX2Z = 1 CDELT2Z = 1.0 CTYPE2Z = 'RA ' CRVAL2Z = 83.81042 / [deg] (05h35m14.5s) CUNIT2Z = 'deg ' COMMENT CRPIX3Z = 1 CDELT3Z = 1.0 CTYPE3Z = 'DEC ' CRVAL3Z = -5.375222 / [deg] (-05:22:30.8) CUNIT3Z = 'deg ' COMMENT RADESYSZ= 'FK5 ' / FK5 (IAU 1984) equatorial coordinates EQUINOXZ= 2000.0 / Equinox J2000.0 COMMENT CRPIX4Z = 1 CDELT4Z = 1.0 CTYPE4Z = 'STOKES ' CRVAL4Z = 1 / Stokes I (total intensity) COMMENT COMMENT ------------------------------------------------ Relativistic velocity COMMENT CRPIX1V = 32768.0 / Pixel coordinate of reference point CTYPE1V = 'VELO-F2V' / Relativistic velocity, non-linear axis CRVAL1V = 2.279141418E+7 / [m/s] Velocity of reference channel CDELT1V = 7.867122599E+2 / [m/s] Channel spacing CUNIT1V = 'm/s ' / Units of coordinate increment and value COMMENT RESTFRQV= 110201353000.0 / [Hz] 13CO line rest frequency RESTWAVV= 0.00272040633 / [m] 13CO line rest wavelength SPECSYSV= 'LSRK ' / Reference frame of spectral coordinates SSYSOBSV= 'TOPOCENT' / Reference frame of observation VELOSYSV= 0.0 / [m/s] Bary-topo velocity towards the source SSYSSRCV= 'LSRK ' / Reference frame of source redshift ZSOURCEV= 0.0000 / Redshift of the source COMMENT CRPIX2V = 1 CDELT2V = 1.0 CTYPE2V = 'RA ' CRVAL2V = 83.81042 / [deg] (05h35m14.5s) CUNIT2V = 'deg ' COMMENT CRPIX3V = 1 CDELT3V = 1.0 CTYPE3V = 'DEC ' CRVAL3V = -5.375222 / [deg] (-05:22:30.8) CUNIT3V = 'deg ' COMMENT RADESYSV= 'FK5 ' / FK5 (IAU 1984) equatorial coordinates EQUINOXV= 2000.0 / Equinox J2000.0 COMMENT CRPIX4V = 1 CDELT4V = 1.0 CTYPE4V = 'STOKES ' CRVAL4V = 1 / Stokes I (total intensity) COMMENT COMMENT ---------------------------------------------- Relativistic beta (v/c) COMMENT CRPIX1B = 32768.0 / Pixel coordinate of reference point CTYPE1B = 'BETA-F2V' / Relativistic beta (v/c), non-linear axis CRVAL1B = 7.602397448E-2 / [] Relativistic beta of reference channel CDELT1B = 2.624189632E-6 / [] Channel spacing COMMENT RESTFRQB= 110201353000.0 / [Hz] 13CO line rest frequency RESTWAVB= 0.00272040633 / [m] 13CO line rest wavelength SPECSYSB= 'LSRK ' / Reference frame of spectral coordinates SSYSOBSB= 'TOPOCENT' / Reference frame of observation VELOSYSB= 0.0 / [m/s] Bary-topo velocity towards the source SSYSSRCB= 'LSRK ' / Reference frame of source redshift ZSOURCEB= 0.0000 / Redshift of the source COMMENT CRPIX2B = 1 CDELT2B = 1.0 CTYPE2B = 'RA ' CRVAL2B = 83.81042 / [deg] (05h35m14.5s) CUNIT2B = 'deg ' COMMENT CRPIX3B = 1 CDELT3B = 1.0 CTYPE3B = 'DEC ' CRVAL3B = -5.375222 / [deg] (-05:22:30.8) CUNIT3B = 'deg ' COMMENT RADESYSB= 'FK5 ' / FK5 (IAU 1984) equatorial coordinates EQUINOXB= 2000.0 / Equinox J2000.0 COMMENT CRPIX4B = 1 CDELT4B = 1.0 CTYPE4B = 'STOKES ' CRVAL4B = 1 / Stokes I (total intensity) COMMENT HISTORY fimgcreate 1.0b at 2009-04-22T04:28:02 DATE = '2009-04-22T04:28:02' / file creation date (YYYY-MM-DDThh:mm:ss UT) astropy-astropy-201cddb/astropy/wcs/tests/data/spectra/orion-velo-1.hdr000066400000000000000000000717601507226315300263510ustar00rootroot00000000000000SIMPLE = T / file does conform to FITS standard BITPIX = -32 / number of bits per data pixel NAXIS = 1 / number of data axes NAXIS1 = 4096 / length of data axis 1 EXTEND = T / FITS dataset may contain extensions COMMENT FITS (Flexible Image Transport System) format is defined in 'AstronomyCOMMENT and Astrophysics', volume 376, page 359; bibcode: 2001A&A...376..359H COMMENT COMMENT This FITS file contains an example spectral WCS header constructed by COMMENT Mark Calabretta (ATNF) and Dirk Petry (ESO) based on an observation COMMENT of the Orion Kleinmann-Low nebula made by Andrew Walsh (JCU) and COMMENT Sven Thorwirth (MPIfR) using the Mopra radio telescope. COMMENT COMMENT The 110GHz 13CO 1-0 spectrum in this file is linear in relativistic COMMENT velocity having been regridded from a linear frequency axis, as COMMENT observed. COMMENT COMMENT The reference pixel has been placed deliberately well outside the COMMENT the spectrum in order to test spectral-WCS-interpreting software. COMMENT COMMENT Spectral representations are: COMMENT F: Frequency ...frequency-like COMMENT E: Photon energy ...frequency-like COMMENT N: Wave number ...frequency-like COMMENT R: Radio velocity ...frequency-like COMMENT W: Wavelength ...wavelength-like COMMENT O: Optical velocity ...wavelength-like COMMENT Z: Redshift ...wavelength-like COMMENT Relativistic velocity (default) ...velocity-like COMMENT B: Relativistic beta ...velocity-like COMMENT COMMENT The Mopra radio telescope is operated by the Australia Telescope COMMENT National Facility. COMMENT COMMENT Author: Mark Calabretta, Australia Telescope National Facility COMMENT http://www.atnf.csiro.au/~mcalabre/index.html COMMENT 2009-04-22 COMMENT ---------------------------------------------------------------------- COMMENT OBJECT = 'Orion-KL' / Orion Kleinmann-Low nebula MOLECULE= '13CO ' / Carbon(13) monoxide TRANSITI= '1-0 ' / 1-0 transition DATE-OBS= '2006-07-09T20:29:00' / Date of observation TELESCOP= 'ATNF Mopra' / 22m mm-wave telescope OBSERVER= 'Walsh/Thorwirth' / Observers BUNIT = 'K ' / Brightness units, Kelvin COMMENT COMMENT ------------------------------------------------------------ Frequency COMMENT CRPIX1F = 32768.0 / Pixel coordinate of reference point CTYPE1F = 'FREQ-V2F' / Frequency, non-linear axis CRVAL1F = 102.4071237E+9 / [Hz] Frequency of reference channel CDELT1F = -2.513721996E+5 / [Hz] Channel spacing CUNIT1F = 'Hz ' / Units of coordinate increment and value COMMENT RESTFRQF= 110201353000.0 / [Hz] 13CO line rest frequency RESTWAVF= 0.00272040633 / [m] 13CO line rest wavelength SPECSYSF= 'LSRK ' / Reference frame of spectral coordinates SSYSOBSF= 'TOPOCENT' / Reference frame of observation VELOSYSF= 0.0 / [m/s] Bary-topo velocity towards the source SSYSSRCF= 'LSRK ' / Reference frame of source redshift ZSOURCEF= 0.0000 / Redshift of the source COMMENT CRPIX2F = 1 CDELT2F = 1.0 CTYPE2F = 'RA ' CRVAL2F = 83.81042 / [deg] (05h35m14.5s) CUNIT2F = 'deg ' COMMENT CRPIX3F = 1 CDELT3F = 1.0 CTYPE3F = 'DEC ' CRVAL3F = -5.375222 / [deg] (-05:22:30.8) CUNIT3F = 'deg ' COMMENT RADESYSF= 'FK5 ' / FK5 (IAU 1984) equatorial coordinates EQUINOXF= 2000.0 / Equinox J2000.0 COMMENT CRPIX4F = 1 CDELT4F = 1.0 CTYPE4F = 'STOKES ' CRVAL4F = 1 / Stokes I (total intensity) COMMENT COMMENT -------------------------------------------------------- Photon energy COMMENT CRPIX1E = 32768.0 / Pixel coordinate of reference point CTYPE1E = 'ENER-V2F' / Photon energy, non-linear axis CRVAL1E = 4.235222141E-4 / [eV] Photon energy of reference channel CDELT1E = -1.039592821E-9 / [eV] Channel spacing CUNIT1E = 'eV ' / Units of coordinate increment and value COMMENT RESTFRQE= 110201353000.0 / [Hz] 13CO line rest frequency RESTWAVE= 0.00272040633 / [m] 13CO line rest wavelength SPECSYSE= 'LSRK ' / Reference frame of spectral coordinates SSYSOBSE= 'TOPOCENT' / Reference frame of observation VELOSYSE= 0.0 / [m/s] Bary-topo velocity towards the source SSYSSRCE= 'LSRK ' / Reference frame of source redshift ZSOURCEE= 0.0000 / Redshift of the source COMMENT CRPIX2E = 1 CDELT2E = 1.0 CTYPE2E = 'RA ' CRVAL2E = 83.81042 / [deg] (05h35m14.5s) CUNIT2E = 'deg ' COMMENT CRPIX3E = 1 CDELT3E = 1.0 CTYPE3E = 'DEC ' CRVAL3E = -5.375222 / [deg] (-05:22:30.8) CUNIT3E = 'deg ' COMMENT RADESYSE= 'FK5 ' / FK5 (IAU 1984) equatorial coordinates EQUINOXE= 2000.0 / Equinox J2000.0 COMMENT CRPIX4E = 1 CDELT4E = 1.0 CTYPE4E = 'STOKES ' CRVAL4E = 1 / Stokes I (total intensity) COMMENT COMMENT ---------------------------------------------------------- Wave number COMMENT CRPIX1N = 32768.0 / Pixel coordinate of reference point CTYPE1N = 'WAVN-V2F' / Wave number, non-linear axis CRVAL1N = 3.415933955E+2 / [/m] Wave number of reference channel CDELT1N = -8.384874032E-4 / [/m] Channel spacing CUNIT1N = '/m ' / Units of coordinate increment and value COMMENT RESTFRQN= 110201353000.0 / [Hz] 13CO line rest frequency RESTWAVN= 0.00272040633 / [m] 13CO line rest wavelength SPECSYSN= 'LSRK ' / Reference frame of spectral coordinates SSYSOBSN= 'TOPOCENT' / Reference frame of observation VELOSYSN= 0.0 / [m/s] Bary-topo velocity towards the source SSYSSRCN= 'LSRK ' / Reference frame of source redshift ZSOURCEN= 0.0000 / Redshift of the source COMMENT CRPIX2N = 1 CDELT2N = 1.0 CTYPE2N = 'RA ' CRVAL2N = 83.81042 / [deg] (05h35m14.5s) CUNIT2N = 'deg ' COMMENT CRPIX3N = 1 CDELT3N = 1.0 CTYPE3N = 'DEC ' CRVAL3N = -5.375222 / [deg] (-05:22:30.8) CUNIT3N = 'deg ' COMMENT RADESYSN= 'FK5 ' / FK5 (IAU 1984) equatorial coordinates EQUINOXN= 2000.0 / Equinox J2000.0 COMMENT CRPIX4N = 1 CDELT4N = 1.0 CTYPE4N = 'STOKES ' CRVAL4N = 1 / Stokes I (total intensity) COMMENT COMMENT ------------------------------------------------------- Radio velocity COMMENT CRPIX1R = 32768.0 / Pixel coordinate of reference point CTYPE1R = 'VRAD-V2F' / Radio velocity, non-linear axis CRVAL1R = 2.120347082E+7 / [m/s] Radio velocity of reference channel CDELT1R = 6.838345224E+2 / [m/s] Channel spacing CUNIT1R = 'm/s ' / Units of coordinate increment and value COMMENT RESTFRQR= 110201353000.0 / [Hz] 13CO line rest frequency RESTWAVR= 0.00272040633 / [m] 13CO line rest wavelength SPECSYSR= 'LSRK ' / Reference frame of spectral coordinates SSYSOBSR= 'TOPOCENT' / Reference frame of observation VELOSYSR= 0.0 / [m/s] Bary-topo velocity towards the source SSYSSRCR= 'LSRK ' / Reference frame of source redshift ZSOURCER= 0.0000 / Redshift of the source COMMENT CRPIX2R = 1 CDELT2R = 1.0 CTYPE2R = 'RA ' CRVAL2R = 83.81042 / [deg] (05h35m14.5s) CUNIT2R = 'deg ' COMMENT CRPIX3R = 1 CDELT3R = 1.0 CTYPE3R = 'DEC ' CRVAL3R = -5.375222 / [deg] (-05:22:30.8) CUNIT3R = 'deg ' COMMENT RADESYSR= 'FK5 ' / FK5 (IAU 1984) equatorial coordinates EQUINOXR= 2000.0 / Equinox J2000.0 COMMENT CRPIX4R = 1 CDELT4R = 1.0 CTYPE4R = 'STOKES ' CRVAL4R = 1 / Stokes I (total intensity) COMMENT COMMENT ----------------------------------------------------------- Wavelength COMMENT CRPIX1W = 32768.0 / Pixel coordinate of reference point CTYPE1W = 'WAVE-V2W' / Wavelength in vacuuo, linear axis CRVAL1W = 2.927457068E-3 / [m] Wavelength of reference channel CDELT1W = 7.185841143E-9 / [m] Channel spacing CUNIT1W = 'm ' / Units of coordinate increment and value COMMENT RESTFRQW= 110201353000.0 / [Hz] 13CO line rest frequency RESTWAVW= 0.00272040633 / [m] 13CO line rest wavelength SPECSYSW= 'LSRK ' / Reference frame of spectral coordinates SSYSOBSW= 'TOPOCENT' / Reference frame of observation VELOSYSW= 0.0 / [m/s] Bary-topo velocity towards the source SSYSSRCW= 'LSRK ' / Reference frame of source redshift ZSOURCEW= 0.0000 / Redshift of the source COMMENT CRPIX2W = 1 CDELT2W = 1.0 CTYPE2W = 'RA ' CRVAL2W = 83.81042 / [deg] (05h35m14.5s) CUNIT2W = 'deg ' COMMENT CRPIX3W = 1 CDELT3W = 1.0 CTYPE3W = 'DEC ' CRVAL3W = -5.375222 / [deg] (-05:22:30.8) CUNIT3W = 'deg ' COMMENT RADESYSW= 'FK5 ' / FK5 (IAU 1984) equatorial coordinates EQUINOXW= 2000.0 / Equinox J2000.0 COMMENT CRPIX4W = 1 CDELT4W = 1.0 CTYPE4W = 'STOKES ' CRVAL4W = 1 / Stokes I (total intensity) COMMENT COMMENT ----------------------------------------------------- Optical velocity COMMENT CRPIX1O = 32768.0 / Pixel coordinate of reference point CTYPE1O = 'VOPT-V2W' / Optical velocity, linear axis CRVAL1O = 2.281727178E+7 / [m/s] Optical velocity of reference channel CDELT1O = 7.918894164E+2 / [m/s] Channel spacing CUNIT1O = 'm/s ' / Units of coordinate increment and value COMMENT RESTFRQO= 110201353000.0 / [Hz] 13CO line rest frequency RESTWAVO= 0.00272040633 / [m] 13CO line rest wavelength SPECSYSO= 'LSRK ' / Reference frame of spectral coordinates SSYSOBSO= 'TOPOCENT' / Reference frame of observation VELOSYSO= 0.0 / [m/s] Bary-topo velocity towards the source SSYSSRCO= 'LSRK ' / Reference frame of source redshift ZSOURCEO= 0.0000 / Redshift of the source COMMENT CRPIX2O = 1 CDELT2O = 1.0 CTYPE2O = 'RA ' CRVAL2O = 83.81042 / [deg] (05h35m14.5s) CUNIT2O = 'deg ' COMMENT CRPIX3O = 1 CDELT3O = 1.0 CTYPE3O = 'DEC ' CRVAL3O = -5.375222 / [deg] (-05:22:30.8) CUNIT3O = 'deg ' COMMENT RADESYSO= 'FK5 ' / FK5 (IAU 1984) equatorial coordinates EQUINOXO= 2000.0 / Equinox J2000.0 COMMENT CRPIX4O = 1 CDELT4O = 1.0 CTYPE4O = 'STOKES ' CRVAL4O = 1 / Stokes I (total intensity) COMMENT COMMENT ------------------------------------------------------------- Redshift COMMENT CRPIX1Z = 32768.0 / Pixel coordinate of reference point CTYPE1Z = 'ZOPT-V2W' / Redshift, linear axis CRVAL1Z = 7.611022615E-2 / [] Redshift of reference channel CDELT1Z = 2.641458767E-6 / [] Channel spacing COMMENT RESTFRQZ= 110201353000.0 / [Hz] 13CO line rest frequency RESTWAVZ= 0.00272040633 / [m] 13CO line rest wavelength SPECSYSZ= 'LSRK ' / Reference frame of spectral coordinates SSYSOBSZ= 'TOPOCENT' / Reference frame of observation VELOSYSZ= 0.0 / [m/s] Bary-topo velocity towards the source SSYSSRCZ= 'LSRK ' / Reference frame of source redshift ZSOURCEZ= 0.0000 / Redshift of the source COMMENT CRPIX2Z = 1 CDELT2Z = 1.0 CTYPE2Z = 'RA ' CRVAL2Z = 83.81042 / [deg] (05h35m14.5s) CUNIT2Z = 'deg ' COMMENT CRPIX3Z = 1 CDELT3Z = 1.0 CTYPE3Z = 'DEC ' CRVAL3Z = -5.375222 / [deg] (-05:22:30.8) CUNIT3Z = 'deg ' COMMENT RADESYSZ= 'FK5 ' / FK5 (IAU 1984) equatorial coordinates EQUINOXZ= 2000.0 / Equinox J2000.0 COMMENT CRPIX4Z = 1 CDELT4Z = 1.0 CTYPE4Z = 'STOKES ' CRVAL4Z = 1 / Stokes I (total intensity) COMMENT COMMENT ------------------------------------------------ Relativistic velocity COMMENT CRPIX1 = 32768.0 / Pixel coordinate of reference point CTYPE1 = 'VELO ' / Relativistic velocity, non-linear axis CRVAL1 = 2.195128874E+7 / [m/s] Velocity of reference channel CDELT1 = 7.319359645E+2 / [m/s] Channel spacing CUNIT1 = 'm/s ' / Units of coordinate increment and value COMMENT RESTFRQ = 110201353000.0 / [Hz] 13CO line rest frequency RESTWAV = 0.00272040633 / [m] 13CO line rest wavelength SPECSYS = 'LSRK ' / Reference frame of spectral coordinates SSYSOBS = 'TOPOCENT' / Reference frame of observation VELOSYS = 0.0 / [m/s] Bary-topo velocity towards the source SSYSSRC = 'LSRK ' / Reference frame of source redshift ZSOURCE = 0.0000 / Redshift of the source COMMENT CRPIX2 = 1 CDELT2 = 1.0 CTYPE2 = 'RA ' CRVAL2 = 83.81042 / [deg] (05h35m14.5s) CUNIT2 = 'deg ' COMMENT CRPIX3 = 1 CDELT3 = 1.0 CTYPE3 = 'DEC ' CRVAL3 = -5.375222 / [deg] (-05:22:30.8) CUNIT3 = 'deg ' COMMENT RADESYS = 'FK5 ' / FK5 (IAU 1984) equatorial coordinates EQUINOX = 2000.0 / Equinox J2000.0 COMMENT CRPIX4 = 1 CDELT4 = 1.0 CTYPE4 = 'STOKES ' CRVAL4 = 1 / Stokes I (total intensity) COMMENT COMMENT ---------------------------------------------- Relativistic beta (v/c) COMMENT CRPIX1B = 32768.0 / Pixel coordinate of reference point CTYPE1B = 'BETA ' / Relativistic beta (v/c), non-linear axis CRVAL1B = 7.322161766E-2 / [] Relativistic beta of reference channel CDELT1B = 2.441475578E-6 / [] Channel spacing COMMENT RESTFRQB= 110201353000.0 / [Hz] 13CO line rest frequency RESTWAVB= 0.00272040633 / [m] 13CO line rest wavelength SPECSYSB= 'LSRK ' / Reference frame of spectral coordinates SSYSOBSB= 'TOPOCENT' / Reference frame of observation VELOSYSB= 0.0 / [m/s] Bary-topo velocity towards the source SSYSSRCB= 'LSRK ' / Reference frame of source redshift ZSOURCEB= 0.0000 / Redshift of the source COMMENT CRPIX2B = 1 CDELT2B = 1.0 CTYPE2B = 'RA ' CRVAL2B = 83.81042 / [deg] (05h35m14.5s) CUNIT2B = 'deg ' COMMENT CRPIX3B = 1 CDELT3B = 1.0 CTYPE3B = 'DEC ' CRVAL3B = -5.375222 / [deg] (-05:22:30.8) CUNIT3B = 'deg ' COMMENT RADESYSB= 'FK5 ' / FK5 (IAU 1984) equatorial coordinates EQUINOXB= 2000.0 / Equinox J2000.0 COMMENT CRPIX4B = 1 CDELT4B = 1.0 CTYPE4B = 'STOKES ' CRVAL4B = 1 / Stokes I (total intensity) COMMENT HISTORY fimgcreate 1.0b at 2009-04-22T04:28:25 DATE = '2009-04-22T04:28:25' / file creation date (YYYY-MM-DDThh:mm:ss UT) astropy-astropy-201cddb/astropy/wcs/tests/data/spectra/orion-velo-4.hdr000066400000000000000000000723401507226315300263470ustar00rootroot00000000000000SIMPLE = T / file does conform to FITS standard BITPIX = -32 / number of bits per data pixel NAXIS = 4 / number of data axes NAXIS1 = 4096 / length of data axis 1 NAXIS2 = 1 / length of data axis 2 NAXIS3 = 1 / length of data axis 3 NAXIS4 = 1 / length of data axis 4 EXTEND = T / FITS dataset may contain extensions COMMENT FITS (Flexible Image Transport System) format is defined in 'AstronomyCOMMENT and Astrophysics', volume 376, page 359; bibcode: 2001A&A...376..359H COMMENT COMMENT This FITS file contains an example spectral WCS header constructed by COMMENT Mark Calabretta (ATNF) and Dirk Petry (ESO) based on an observation COMMENT of the Orion Kleinmann-Low nebula made by Andrew Walsh (JCU) and COMMENT Sven Thorwirth (MPIfR) using the Mopra radio telescope. COMMENT COMMENT The 110GHz 13CO 1-0 spectrum in this file is linear in relativistic COMMENT velocity having been regridded from a linear frequency axis, as COMMENT observed. COMMENT COMMENT The reference pixel has been placed deliberately well outside the COMMENT the spectrum in order to test spectral-WCS-interpreting software. COMMENT COMMENT Spectral representations are: COMMENT F: Frequency ...frequency-like COMMENT E: Photon energy ...frequency-like COMMENT N: Wave number ...frequency-like COMMENT R: Radio velocity ...frequency-like COMMENT W: Wavelength ...wavelength-like COMMENT O: Optical velocity ...wavelength-like COMMENT Z: Redshift ...wavelength-like COMMENT Relativistic velocity (default) ...velocity-like COMMENT B: Relativistic beta ...velocity-like COMMENT COMMENT The Mopra radio telescope is operated by the Australia Telescope COMMENT National Facility. COMMENT COMMENT Author: Mark Calabretta, Australia Telescope National Facility COMMENT http://www.atnf.csiro.au/~mcalabre/index.html COMMENT 2009-04-22 COMMENT ---------------------------------------------------------------------- COMMENT OBJECT = 'Orion-KL' / Orion Kleinmann-Low nebula MOLECULE= '13CO ' / Carbon(13) monoxide TRANSITI= '1-0 ' / 1-0 transition DATE-OBS= '2006-07-09T20:29:00' / Date of observation TELESCOP= 'ATNF Mopra' / 22m mm-wave telescope OBSERVER= 'Walsh/Thorwirth' / Observers BUNIT = 'K ' / Brightness units, Kelvin COMMENT COMMENT ------------------------------------------------------------ Frequency COMMENT CRPIX1F = 32768.0 / Pixel coordinate of reference point CTYPE1F = 'FREQ-V2F' / Frequency, non-linear axis CRVAL1F = 102.4071237E+9 / [Hz] Frequency of reference channel CDELT1F = -2.513721996E+5 / [Hz] Channel spacing CUNIT1F = 'Hz ' / Units of coordinate increment and value COMMENT RESTFRQF= 110201353000.0 / [Hz] 13CO line rest frequency RESTWAVF= 0.00272040633 / [m] 13CO line rest wavelength SPECSYSF= 'LSRK ' / Reference frame of spectral coordinates SSYSOBSF= 'TOPOCENT' / Reference frame of observation VELOSYSF= 0.0 / [m/s] Bary-topo velocity towards the source SSYSSRCF= 'LSRK ' / Reference frame of source redshift ZSOURCEF= 0.0000 / Redshift of the source COMMENT CRPIX2F = 1 CDELT2F = 1.0 CTYPE2F = 'RA ' CRVAL2F = 83.81042 / [deg] (05h35m14.5s) CUNIT2F = 'deg ' COMMENT CRPIX3F = 1 CDELT3F = 1.0 CTYPE3F = 'DEC ' CRVAL3F = -5.375222 / [deg] (-05:22:30.8) CUNIT3F = 'deg ' COMMENT RADESYSF= 'FK5 ' / FK5 (IAU 1984) equatorial coordinates EQUINOXF= 2000.0 / Equinox J2000.0 COMMENT CRPIX4F = 1 CDELT4F = 1.0 CTYPE4F = 'STOKES ' CRVAL4F = 1 / Stokes I (total intensity) COMMENT COMMENT -------------------------------------------------------- Photon energy COMMENT CRPIX1E = 32768.0 / Pixel coordinate of reference point CTYPE1E = 'ENER-V2F' / Photon energy, non-linear axis CRVAL1E = 4.235222141E-4 / [eV] Photon energy of reference channel CDELT1E = -1.039592821E-9 / [eV] Channel spacing CUNIT1E = 'eV ' / Units of coordinate increment and value COMMENT RESTFRQE= 110201353000.0 / [Hz] 13CO line rest frequency RESTWAVE= 0.00272040633 / [m] 13CO line rest wavelength SPECSYSE= 'LSRK ' / Reference frame of spectral coordinates SSYSOBSE= 'TOPOCENT' / Reference frame of observation VELOSYSE= 0.0 / [m/s] Bary-topo velocity towards the source SSYSSRCE= 'LSRK ' / Reference frame of source redshift ZSOURCEE= 0.0000 / Redshift of the source COMMENT CRPIX2E = 1 CDELT2E = 1.0 CTYPE2E = 'RA ' CRVAL2E = 83.81042 / [deg] (05h35m14.5s) CUNIT2E = 'deg ' COMMENT CRPIX3E = 1 CDELT3E = 1.0 CTYPE3E = 'DEC ' CRVAL3E = -5.375222 / [deg] (-05:22:30.8) CUNIT3E = 'deg ' COMMENT RADESYSE= 'FK5 ' / FK5 (IAU 1984) equatorial coordinates EQUINOXE= 2000.0 / Equinox J2000.0 COMMENT CRPIX4E = 1 CDELT4E = 1.0 CTYPE4E = 'STOKES ' CRVAL4E = 1 / Stokes I (total intensity) COMMENT COMMENT ---------------------------------------------------------- Wave number COMMENT CRPIX1N = 32768.0 / Pixel coordinate of reference point CTYPE1N = 'WAVN-V2F' / Wave number, non-linear axis CRVAL1N = 3.415933955E+2 / [/m] Wave number of reference channel CDELT1N = -8.384874032E-4 / [/m] Channel spacing CUNIT1N = '/m ' / Units of coordinate increment and value COMMENT RESTFRQN= 110201353000.0 / [Hz] 13CO line rest frequency RESTWAVN= 0.00272040633 / [m] 13CO line rest wavelength SPECSYSN= 'LSRK ' / Reference frame of spectral coordinates SSYSOBSN= 'TOPOCENT' / Reference frame of observation VELOSYSN= 0.0 / [m/s] Bary-topo velocity towards the source SSYSSRCN= 'LSRK ' / Reference frame of source redshift ZSOURCEN= 0.0000 / Redshift of the source COMMENT CRPIX2N = 1 CDELT2N = 1.0 CTYPE2N = 'RA ' CRVAL2N = 83.81042 / [deg] (05h35m14.5s) CUNIT2N = 'deg ' COMMENT CRPIX3N = 1 CDELT3N = 1.0 CTYPE3N = 'DEC ' CRVAL3N = -5.375222 / [deg] (-05:22:30.8) CUNIT3N = 'deg ' COMMENT RADESYSN= 'FK5 ' / FK5 (IAU 1984) equatorial coordinates EQUINOXN= 2000.0 / Equinox J2000.0 COMMENT CRPIX4N = 1 CDELT4N = 1.0 CTYPE4N = 'STOKES ' CRVAL4N = 1 / Stokes I (total intensity) COMMENT COMMENT ------------------------------------------------------- Radio velocity COMMENT CRPIX1R = 32768.0 / Pixel coordinate of reference point CTYPE1R = 'VRAD-V2F' / Radio velocity, non-linear axis CRVAL1R = 2.120347082E+7 / [m/s] Radio velocity of reference channel CDELT1R = 6.838345224E+2 / [m/s] Channel spacing CUNIT1R = 'm/s ' / Units of coordinate increment and value COMMENT RESTFRQR= 110201353000.0 / [Hz] 13CO line rest frequency RESTWAVR= 0.00272040633 / [m] 13CO line rest wavelength SPECSYSR= 'LSRK ' / Reference frame of spectral coordinates SSYSOBSR= 'TOPOCENT' / Reference frame of observation VELOSYSR= 0.0 / [m/s] Bary-topo velocity towards the source SSYSSRCR= 'LSRK ' / Reference frame of source redshift ZSOURCER= 0.0000 / Redshift of the source COMMENT CRPIX2R = 1 CDELT2R = 1.0 CTYPE2R = 'RA ' CRVAL2R = 83.81042 / [deg] (05h35m14.5s) CUNIT2R = 'deg ' COMMENT CRPIX3R = 1 CDELT3R = 1.0 CTYPE3R = 'DEC ' CRVAL3R = -5.375222 / [deg] (-05:22:30.8) CUNIT3R = 'deg ' COMMENT RADESYSR= 'FK5 ' / FK5 (IAU 1984) equatorial coordinates EQUINOXR= 2000.0 / Equinox J2000.0 COMMENT CRPIX4R = 1 CDELT4R = 1.0 CTYPE4R = 'STOKES ' CRVAL4R = 1 / Stokes I (total intensity) COMMENT COMMENT ----------------------------------------------------------- Wavelength COMMENT CRPIX1W = 32768.0 / Pixel coordinate of reference point CTYPE1W = 'WAVE-V2W' / Wavelength in vacuuo, linear axis CRVAL1W = 2.927457068E-3 / [m] Wavelength of reference channel CDELT1W = 7.185841143E-9 / [m] Channel spacing CUNIT1W = 'm ' / Units of coordinate increment and value COMMENT RESTFRQW= 110201353000.0 / [Hz] 13CO line rest frequency RESTWAVW= 0.00272040633 / [m] 13CO line rest wavelength SPECSYSW= 'LSRK ' / Reference frame of spectral coordinates SSYSOBSW= 'TOPOCENT' / Reference frame of observation VELOSYSW= 0.0 / [m/s] Bary-topo velocity towards the source SSYSSRCW= 'LSRK ' / Reference frame of source redshift ZSOURCEW= 0.0000 / Redshift of the source COMMENT CRPIX2W = 1 CDELT2W = 1.0 CTYPE2W = 'RA ' CRVAL2W = 83.81042 / [deg] (05h35m14.5s) CUNIT2W = 'deg ' COMMENT CRPIX3W = 1 CDELT3W = 1.0 CTYPE3W = 'DEC ' CRVAL3W = -5.375222 / [deg] (-05:22:30.8) CUNIT3W = 'deg ' COMMENT RADESYSW= 'FK5 ' / FK5 (IAU 1984) equatorial coordinates EQUINOXW= 2000.0 / Equinox J2000.0 COMMENT CRPIX4W = 1 CDELT4W = 1.0 CTYPE4W = 'STOKES ' CRVAL4W = 1 / Stokes I (total intensity) COMMENT COMMENT ----------------------------------------------------- Optical velocity COMMENT CRPIX1O = 32768.0 / Pixel coordinate of reference point CTYPE1O = 'VOPT-V2W' / Optical velocity, linear axis CRVAL1O = 2.281727178E+7 / [m/s] Optical velocity of reference channel CDELT1O = 7.918894164E+2 / [m/s] Channel spacing CUNIT1O = 'm/s ' / Units of coordinate increment and value COMMENT RESTFRQO= 110201353000.0 / [Hz] 13CO line rest frequency RESTWAVO= 0.00272040633 / [m] 13CO line rest wavelength SPECSYSO= 'LSRK ' / Reference frame of spectral coordinates SSYSOBSO= 'TOPOCENT' / Reference frame of observation VELOSYSO= 0.0 / [m/s] Bary-topo velocity towards the source SSYSSRCO= 'LSRK ' / Reference frame of source redshift ZSOURCEO= 0.0000 / Redshift of the source COMMENT CRPIX2O = 1 CDELT2O = 1.0 CTYPE2O = 'RA ' CRVAL2O = 83.81042 / [deg] (05h35m14.5s) CUNIT2O = 'deg ' COMMENT CRPIX3O = 1 CDELT3O = 1.0 CTYPE3O = 'DEC ' CRVAL3O = -5.375222 / [deg] (-05:22:30.8) CUNIT3O = 'deg ' COMMENT RADESYSO= 'FK5 ' / FK5 (IAU 1984) equatorial coordinates EQUINOXO= 2000.0 / Equinox J2000.0 COMMENT CRPIX4O = 1 CDELT4O = 1.0 CTYPE4O = 'STOKES ' CRVAL4O = 1 / Stokes I (total intensity) COMMENT COMMENT ------------------------------------------------------------- Redshift COMMENT CRPIX1Z = 32768.0 / Pixel coordinate of reference point CTYPE1Z = 'ZOPT-V2W' / Redshift, linear axis CRVAL1Z = 7.611022615E-2 / [] Redshift of reference channel CDELT1Z = 2.641458767E-6 / [] Channel spacing COMMENT RESTFRQZ= 110201353000.0 / [Hz] 13CO line rest frequency RESTWAVZ= 0.00272040633 / [m] 13CO line rest wavelength SPECSYSZ= 'LSRK ' / Reference frame of spectral coordinates SSYSOBSZ= 'TOPOCENT' / Reference frame of observation VELOSYSZ= 0.0 / [m/s] Bary-topo velocity towards the source SSYSSRCZ= 'LSRK ' / Reference frame of source redshift ZSOURCEZ= 0.0000 / Redshift of the source COMMENT CRPIX2Z = 1 CDELT2Z = 1.0 CTYPE2Z = 'RA ' CRVAL2Z = 83.81042 / [deg] (05h35m14.5s) CUNIT2Z = 'deg ' COMMENT CRPIX3Z = 1 CDELT3Z = 1.0 CTYPE3Z = 'DEC ' CRVAL3Z = -5.375222 / [deg] (-05:22:30.8) CUNIT3Z = 'deg ' COMMENT RADESYSZ= 'FK5 ' / FK5 (IAU 1984) equatorial coordinates EQUINOXZ= 2000.0 / Equinox J2000.0 COMMENT CRPIX4Z = 1 CDELT4Z = 1.0 CTYPE4Z = 'STOKES ' CRVAL4Z = 1 / Stokes I (total intensity) COMMENT COMMENT ------------------------------------------------ Relativistic velocity COMMENT CRPIX1 = 32768.0 / Pixel coordinate of reference point CTYPE1 = 'VELO ' / Relativistic velocity, non-linear axis CRVAL1 = 2.195128874E+7 / [m/s] Velocity of reference channel CDELT1 = 7.319359645E+2 / [m/s] Channel spacing CUNIT1 = 'm/s ' / Units of coordinate increment and value COMMENT RESTFRQ = 110201353000.0 / [Hz] 13CO line rest frequency RESTWAV = 0.00272040633 / [m] 13CO line rest wavelength SPECSYS = 'LSRK ' / Reference frame of spectral coordinates SSYSOBS = 'TOPOCENT' / Reference frame of observation VELOSYS = 0.0 / [m/s] Bary-topo velocity towards the source SSYSSRC = 'LSRK ' / Reference frame of source redshift ZSOURCE = 0.0000 / Redshift of the source COMMENT CRPIX2 = 1 CDELT2 = 1.0 CTYPE2 = 'RA ' CRVAL2 = 83.81042 / [deg] (05h35m14.5s) CUNIT2 = 'deg ' COMMENT CRPIX3 = 1 CDELT3 = 1.0 CTYPE3 = 'DEC ' CRVAL3 = -5.375222 / [deg] (-05:22:30.8) CUNIT3 = 'deg ' COMMENT RADESYS = 'FK5 ' / FK5 (IAU 1984) equatorial coordinates EQUINOX = 2000.0 / Equinox J2000.0 COMMENT CRPIX4 = 1 CDELT4 = 1.0 CTYPE4 = 'STOKES ' CRVAL4 = 1 / Stokes I (total intensity) COMMENT COMMENT ---------------------------------------------- Relativistic beta (v/c) COMMENT CRPIX1B = 32768.0 / Pixel coordinate of reference point CTYPE1B = 'BETA ' / Relativistic beta (v/c), non-linear axis CRVAL1B = 7.322161766E-2 / [] Relativistic beta of reference channel CDELT1B = 2.441475578E-6 / [] Channel spacing COMMENT RESTFRQB= 110201353000.0 / [Hz] 13CO line rest frequency RESTWAVB= 0.00272040633 / [m] 13CO line rest wavelength SPECSYSB= 'LSRK ' / Reference frame of spectral coordinates SSYSOBSB= 'TOPOCENT' / Reference frame of observation VELOSYSB= 0.0 / [m/s] Bary-topo velocity towards the source SSYSSRCB= 'LSRK ' / Reference frame of source redshift ZSOURCEB= 0.0000 / Redshift of the source COMMENT CRPIX2B = 1 CDELT2B = 1.0 CTYPE2B = 'RA ' CRVAL2B = 83.81042 / [deg] (05h35m14.5s) CUNIT2B = 'deg ' COMMENT CRPIX3B = 1 CDELT3B = 1.0 CTYPE3B = 'DEC ' CRVAL3B = -5.375222 / [deg] (-05:22:30.8) CUNIT3B = 'deg ' COMMENT RADESYSB= 'FK5 ' / FK5 (IAU 1984) equatorial coordinates EQUINOXB= 2000.0 / Equinox J2000.0 COMMENT CRPIX4B = 1 CDELT4B = 1.0 CTYPE4B = 'STOKES ' CRVAL4B = 1 / Stokes I (total intensity) COMMENT HISTORY fimgcreate 1.0b at 2009-04-22T04:28:33 DATE = '2009-04-22T04:28:33' / file creation date (YYYY-MM-DDThh:mm:ss UT) astropy-astropy-201cddb/astropy/wcs/tests/data/spectra/orion-wave-1.hdr000066400000000000000000000716401507226315300263430ustar00rootroot00000000000000SIMPLE = T / file does conform to FITS standard BITPIX = -32 / number of bits per data pixel NAXIS = 1 / number of data axes NAXIS1 = 4096 / length of data axis 1 EXTEND = T / FITS dataset may contain extensions COMMENT FITS (Flexible Image Transport System) format is defined in 'AstronomyCOMMENT and Astrophysics', volume 376, page 359; bibcode: 2001A&A...376..359H COMMENT COMMENT This FITS file contains an example spectral WCS header constructed by COMMENT Mark Calabretta (ATNF) and Dirk Petry (ESO) based on an observation COMMENT of the Orion Kleinmann-Low nebula made by Andrew Walsh (JCU) and COMMENT Sven Thorwirth (MPIfR) using the Mopra radio telescope. COMMENT COMMENT The 110GHz 13CO 1-0 spectrum in this file is linear in wavelength, COMMENT having been regridded from a linear frequency axis, as observed. COMMENT COMMENT The reference pixel has been placed deliberately well outside the COMMENT the spectrum in order to test spectral-WCS-interpreting software. COMMENT COMMENT Spectral representations are: COMMENT F: Frequency ...frequency-like COMMENT E: Photon energy ...frequency-like COMMENT N: Wave number ...frequency-like COMMENT R: Radio velocity ...frequency-like COMMENT Wavelength (default) ...wavelength-like COMMENT O: Optical velocity ...wavelength-like COMMENT Z: Redshift ...wavelength-like COMMENT V: Relativistic velocity ...velocity-like COMMENT B: Relativistic beta ...velocity-like COMMENT COMMENT The Mopra radio telescope is operated by the Australia Telescope COMMENT National Facility. COMMENT COMMENT Author: Mark Calabretta, Australia Telescope National Facility COMMENT http://www.atnf.csiro.au/~mcalabre/index.html COMMENT 2009-04-22 COMMENT ---------------------------------------------------------------------- COMMENT OBJECT = 'Orion-KL' / Orion Kleinmann-Low nebula MOLECULE= '13CO ' / Carbon(13) monoxide TRANSITI= '1-0 ' / 1-0 transition DATE-OBS= '2006-07-09T20:29:00' / Date of observation TELESCOP= 'ATNF Mopra' / 22m mm-wave telescope OBSERVER= 'Walsh/Thorwirth' / Observers BUNIT = 'K ' / Brightness units, Kelvin COMMENT COMMENT ------------------------------------------------------------ Frequency COMMENT CRPIX1F = 32768.0 / Pixel coordinate of reference point CTYPE1F = 'FREQ-W2F' / Frequency, non-linear axis CRVAL1F = 102.6940613E+9 / [Hz] Frequency of reference channel CDELT1F = -2.332330873E+5 / [Hz] Channel spacing CUNIT1F = 'Hz ' / Units of coordinate increment and value COMMENT RESTFRQF= 110201353000.0 / [Hz] 13CO line rest frequency RESTWAVF= 0.00272040633 / [m] 13CO line rest wavelength SPECSYSF= 'LSRK ' / Reference frame of spectral coordinates SSYSOBSF= 'TOPOCENT' / Reference frame of observation VELOSYSF= 0.0 / [m/s] Bary-topo velocity towards the source SSYSSRCF= 'LSRK ' / Reference frame of source redshift ZSOURCEF= 0.0000 / Redshift of the source COMMENT CRPIX2F = 1 CDELT2F = 1.0 CTYPE2F = 'RA ' CRVAL2F = 83.81042 / [deg] (05h35m14.5s) CUNIT2F = 'deg ' COMMENT CRPIX3F = 1 CDELT3F = 1.0 CTYPE3F = 'DEC ' CRVAL3F = -5.375222 / [deg] (-05:22:30.8) CUNIT3F = 'deg ' COMMENT RADESYSF= 'FK5 ' / FK5 (IAU 1984) equatorial coordinates EQUINOXF= 2000.0 / Equinox J2000.0 COMMENT CRPIX4F = 1 CDELT4F = 1.0 CTYPE4F = 'STOKES ' CRVAL4F = 1 / Stokes I (total intensity) COMMENT COMMENT -------------------------------------------------------- Photon energy COMMENT CRPIX1E = 32768.0 / Pixel coordinate of reference point CTYPE1E = 'ENER-W2F' / Photon energy, non-linear axis CRVAL1E = 4.247088937E-4 / [eV] Photon energy of reference channel CDELT1E = -0.9645754124E-9 / [eV] Channel spacing CUNIT1E = 'eV ' / Units of coordinate increment and value COMMENT RESTFRQE= 110201353000.0 / [Hz] 13CO line rest frequency RESTWAVE= 0.00272040633 / [m] 13CO line rest wavelength SPECSYSE= 'LSRK ' / Reference frame of spectral coordinates SSYSOBSE= 'TOPOCENT' / Reference frame of observation VELOSYSE= 0.0 / [m/s] Bary-topo velocity towards the source SSYSSRCE= 'LSRK ' / Reference frame of source redshift ZSOURCEE= 0.0000 / Redshift of the source COMMENT CRPIX2E = 1 CDELT2E = 1.0 CTYPE2E = 'RA ' CRVAL2E = 83.81042 / [deg] (05h35m14.5s) CUNIT2E = 'deg ' COMMENT CRPIX3E = 1 CDELT3E = 1.0 CTYPE3E = 'DEC ' CRVAL3E = -5.375222 / [deg] (-05:22:30.8) CUNIT3E = 'deg ' COMMENT RADESYSE= 'FK5 ' / FK5 (IAU 1984) equatorial coordinates EQUINOXE= 2000.0 / Equinox J2000.0 COMMENT CRPIX4E = 1 CDELT4E = 1.0 CTYPE4E = 'STOKES ' CRVAL4E = 1 / Stokes I (total intensity) COMMENT COMMENT ---------------------------------------------------------- Wave number COMMENT CRPIX1N = 32768.0 / Pixel coordinate of reference point CTYPE1N = 'WAVN-W2F' / Wave number, non-linear axis CRVAL1N = 3.425505162E+2 / [/m] Wave number of reference channel CDELT1N = -7.779818375E-4 / [/m] Channel spacing CUNIT1N = '/m ' / Units of coordinate increment and value COMMENT RESTFRQN= 110201353000.0 / [Hz] 13CO line rest frequency RESTWAVN= 0.00272040633 / [m] 13CO line rest wavelength SPECSYSN= 'LSRK ' / Reference frame of spectral coordinates SSYSOBSN= 'TOPOCENT' / Reference frame of observation VELOSYSN= 0.0 / [m/s] Bary-topo velocity towards the source SSYSSRCN= 'LSRK ' / Reference frame of source redshift ZSOURCEN= 0.0000 / Redshift of the source COMMENT CRPIX2N = 1 CDELT2N = 1.0 CTYPE2N = 'RA ' CRVAL2N = 83.81042 / [deg] (05h35m14.5s) CUNIT2N = 'deg ' COMMENT CRPIX3N = 1 CDELT3N = 1.0 CTYPE3N = 'DEC ' CRVAL3N = -5.375222 / [deg] (-05:22:30.8) CUNIT3N = 'deg ' COMMENT RADESYSN= 'FK5 ' / FK5 (IAU 1984) equatorial coordinates EQUINOXN= 2000.0 / Equinox J2000.0 COMMENT CRPIX4N = 1 CDELT4N = 1.0 CTYPE4N = 'STOKES ' CRVAL4N = 1 / Stokes I (total intensity) COMMENT COMMENT ------------------------------------------------------- Radio velocity COMMENT CRPIX1R = 32768.0 / Pixel coordinate of reference point CTYPE1R = 'VRAD-W2F' / Radio velocity, non-linear axis CRVAL1R = 2.042288396E+7 / [m/s] Radio velocity of reference channel CDELT1R = 6.344887666E+2 / [m/s] Channel spacing CUNIT1R = 'm/s ' / Units of coordinate increment and value COMMENT RESTFRQR= 110201353000.0 / [Hz] 13CO line rest frequency RESTWAVR= 0.00272040633 / [m] 13CO line rest wavelength SPECSYSR= 'LSRK ' / Reference frame of spectral coordinates SSYSOBSR= 'TOPOCENT' / Reference frame of observation VELOSYSR= 0.0 / [m/s] Bary-topo velocity towards the source SSYSSRCR= 'LSRK ' / Reference frame of source redshift ZSOURCER= 0.0000 / Redshift of the source COMMENT CRPIX2R = 1 CDELT2R = 1.0 CTYPE2R = 'RA ' CRVAL2R = 83.81042 / [deg] (05h35m14.5s) CUNIT2R = 'deg ' COMMENT CRPIX3R = 1 CDELT3R = 1.0 CTYPE3R = 'DEC ' CRVAL3R = -5.375222 / [deg] (-05:22:30.8) CUNIT3R = 'deg ' COMMENT RADESYSR= 'FK5 ' / FK5 (IAU 1984) equatorial coordinates EQUINOXR= 2000.0 / Equinox J2000.0 COMMENT CRPIX4R = 1 CDELT4R = 1.0 CTYPE4R = 'STOKES ' CRVAL4R = 1 / Stokes I (total intensity) COMMENT COMMENT ----------------------------------------------------------- Wavelength COMMENT CRPIX1 = 32768.0 / Pixel coordinate of reference point CTYPE1 = 'WAVE ' / Wavelength in vacuuo, linear axis CRVAL1 = 2.919277457E-3 / [m] Wavelength of reference channel CDELT1 = 6.630101933E-9 / [m] Channel spacing CUNIT1 = 'm ' / Units of coordinate increment and value COMMENT RESTFRQ = 110201353000.0 / [Hz] 13CO line rest frequency RESTWAV = 0.00272040633 / [m] 13CO line rest wavelength SPECSYS = 'LSRK ' / Reference frame of spectral coordinates SSYSOBS = 'TOPOCENT' / Reference frame of observation VELOSYS = 0.0 / [m/s] Bary-topo velocity towards the source SSYSSRC = 'LSRK ' / Reference frame of source redshift ZSOURCE = 0.0000 / Redshift of the source COMMENT CRPIX2 = 1 CDELT2 = 1.0 CTYPE2 = 'RA ' CRVAL2 = 83.81042 / [deg] (05h35m14.5s) CUNIT2 = 'deg ' COMMENT CRPIX3 = 1 CDELT3 = 1.0 CTYPE3 = 'DEC ' CRVAL3 = -5.375222 / [deg] (-05:22:30.8) CUNIT3 = 'deg ' COMMENT RADESYS = 'FK5 ' / FK5 (IAU 1984) equatorial coordinates EQUINOX = 2000.0 / Equinox J2000.0 COMMENT CRPIX4 = 1 CDELT4 = 1.0 CTYPE4 = 'STOKES ' CRVAL4 = 1 / Stokes I (total intensity) COMMENT COMMENT ----------------------------------------------------- Optical velocity COMMENT CRPIX1O = 32768.0 / Pixel coordinate of reference point CTYPE1O = 'VOPT ' / Optical velocity, linear axis CRVAL1O = 2.191586755E+7 / [m/s] Optical velocity of reference channel CDELT1O = 7.306462036E+2 / [m/s] Channel spacing CUNIT1O = 'm/s ' / Units of coordinate increment and value COMMENT RESTFRQO= 110201353000.0 / [Hz] 13CO line rest frequency RESTWAVO= 0.00272040633 / [m] 13CO line rest wavelength SPECSYSO= 'LSRK ' / Reference frame of spectral coordinates SSYSOBSO= 'TOPOCENT' / Reference frame of observation VELOSYSO= 0.0 / [m/s] Bary-topo velocity towards the source SSYSSRCO= 'LSRK ' / Reference frame of source redshift ZSOURCEO= 0.0000 / Redshift of the source COMMENT CRPIX2O = 1 CDELT2O = 1.0 CTYPE2O = 'RA ' CRVAL2O = 83.81042 / [deg] (05h35m14.5s) CUNIT2O = 'deg ' COMMENT CRPIX3O = 1 CDELT3O = 1.0 CTYPE3O = 'DEC ' CRVAL3O = -5.375222 / [deg] (-05:22:30.8) CUNIT3O = 'deg ' COMMENT RADESYSO= 'FK5 ' / FK5 (IAU 1984) equatorial coordinates EQUINOXO= 2000.0 / Equinox J2000.0 COMMENT CRPIX4O = 1 CDELT4O = 1.0 CTYPE4O = 'STOKES ' CRVAL4O = 1 / Stokes I (total intensity) COMMENT COMMENT ------------------------------------------------------------- Redshift COMMENT CRPIX1Z = 32768.0 / Pixel coordinate of reference point CTYPE1Z = 'ZOPT ' / Redshift, linear axis CRVAL1Z = 7.310346531E-2 / [] Redshift of reference channel CDELT1Z = 2.437173398E-6 / [] Channel spacing COMMENT RESTFRQZ= 110201353000.0 / [Hz] 13CO line rest frequency RESTWAVZ= 0.00272040633 / [m] 13CO line rest wavelength SPECSYSZ= 'LSRK ' / Reference frame of spectral coordinates SSYSOBSZ= 'TOPOCENT' / Reference frame of observation VELOSYSZ= 0.0 / [m/s] Bary-topo velocity towards the source SSYSSRCZ= 'LSRK ' / Reference frame of source redshift ZSOURCEZ= 0.0000 / Redshift of the source COMMENT CRPIX2Z = 1 CDELT2Z = 1.0 CTYPE2Z = 'RA ' CRVAL2Z = 83.81042 / [deg] (05h35m14.5s) CUNIT2Z = 'deg ' COMMENT CRPIX3Z = 1 CDELT3Z = 1.0 CTYPE3Z = 'DEC ' CRVAL3Z = -5.375222 / [deg] (-05:22:30.8) CUNIT3Z = 'deg ' COMMENT RADESYSZ= 'FK5 ' / FK5 (IAU 1984) equatorial coordinates EQUINOXZ= 2000.0 / Equinox J2000.0 COMMENT CRPIX4Z = 1 CDELT4Z = 1.0 CTYPE4Z = 'STOKES ' CRVAL4Z = 1 / Stokes I (total intensity) COMMENT COMMENT ------------------------------------------------ Relativistic velocity COMMENT CRPIX1V = 32768.0 / Pixel coordinate of reference point CTYPE1V = 'VELO-W2V' / Relativistic velocity, non-linear axis CRVAL1V = 2.111679434E+7 / [m/s] Velocity of reference channel CDELT1V = 6.774939349E+2 / [m/s] Channel spacing CUNIT1V = 'm/s ' / Units of coordinate increment and value COMMENT RESTFRQV= 110201353000.0 / [Hz] 13CO line rest frequency RESTWAVV= 0.00272040633 / [m] 13CO line rest wavelength SPECSYSV= 'LSRK ' / Reference frame of spectral coordinates SSYSOBSV= 'TOPOCENT' / Reference frame of observation VELOSYSV= 0.0 / [m/s] Bary-topo velocity towards the source SSYSSRCV= 'LSRK ' / Reference frame of source redshift ZSOURCEV= 0.0000 / Redshift of the source COMMENT CRPIX2V = 1 CDELT2V = 1.0 CTYPE2V = 'RA ' CRVAL2V = 83.81042 / [deg] (05h35m14.5s) CUNIT2V = 'deg ' COMMENT CRPIX3V = 1 CDELT3V = 1.0 CTYPE3V = 'DEC ' CRVAL3V = -5.375222 / [deg] (-05:22:30.8) CUNIT3V = 'deg ' COMMENT RADESYSV= 'FK5 ' / FK5 (IAU 1984) equatorial coordinates EQUINOXV= 2000.0 / Equinox J2000.0 COMMENT CRPIX4V = 1 CDELT4V = 1.0 CTYPE4V = 'STOKES ' CRVAL4V = 1 / Stokes I (total intensity) COMMENT COMMENT ---------------------------------------------- Relativistic beta (v/c) COMMENT CRPIX1B = 32768.0 / Pixel coordinate of reference point CTYPE1B = 'BETA-W2V' / Relativistic beta (v/c), non-linear axis CRVAL1B = 7.043804396E-2 / [] Relativistic beta of reference channel CDELT1B = 2.259876514E-6 / [] Channel spacing COMMENT RESTFRQB= 110201353000.0 / [Hz] 13CO line rest frequency RESTWAVB= 0.00272040633 / [m] 13CO line rest wavelength SPECSYSB= 'LSRK ' / Reference frame of spectral coordinates SSYSOBSB= 'TOPOCENT' / Reference frame of observation VELOSYSB= 0.0 / [m/s] Bary-topo velocity towards the source SSYSSRCB= 'LSRK ' / Reference frame of source redshift ZSOURCEB= 0.0000 / Redshift of the source COMMENT CRPIX2B = 1 CDELT2B = 1.0 CTYPE2B = 'RA ' CRVAL2B = 83.81042 / [deg] (05h35m14.5s) CUNIT2B = 'deg ' COMMENT CRPIX3B = 1 CDELT3B = 1.0 CTYPE3B = 'DEC ' CRVAL3B = -5.375222 / [deg] (-05:22:30.8) CUNIT3B = 'deg ' COMMENT RADESYSB= 'FK5 ' / FK5 (IAU 1984) equatorial coordinates EQUINOXB= 2000.0 / Equinox J2000.0 COMMENT CRPIX4B = 1 CDELT4B = 1.0 CTYPE4B = 'STOKES ' CRVAL4B = 1 / Stokes I (total intensity) COMMENT HISTORY fimgcreate 1.0b at 2009-04-22T04:28:10 DATE = '2009-04-22T04:28:10' / file creation date (YYYY-MM-DDThh:mm:ss UT) astropy-astropy-201cddb/astropy/wcs/tests/data/spectra/orion-wave-4.hdr000066400000000000000000000722201507226315300263410ustar00rootroot00000000000000SIMPLE = T / file does conform to FITS standard BITPIX = -32 / number of bits per data pixel NAXIS = 4 / number of data axes NAXIS1 = 4096 / length of data axis 1 NAXIS2 = 1 / length of data axis 2 NAXIS3 = 1 / length of data axis 3 NAXIS4 = 1 / length of data axis 4 EXTEND = T / FITS dataset may contain extensions COMMENT FITS (Flexible Image Transport System) format is defined in 'AstronomyCOMMENT and Astrophysics', volume 376, page 359; bibcode: 2001A&A...376..359H COMMENT COMMENT This FITS file contains an example spectral WCS header constructed by COMMENT Mark Calabretta (ATNF) and Dirk Petry (ESO) based on an observation COMMENT of the Orion Kleinmann-Low nebula made by Andrew Walsh (JCU) and COMMENT Sven Thorwirth (MPIfR) using the Mopra radio telescope. COMMENT COMMENT The 110GHz 13CO 1-0 spectrum in this file is linear in wavelength, COMMENT having been regridded from a linear frequency axis, as observed. COMMENT COMMENT The reference pixel has been placed deliberately well outside the COMMENT the spectrum in order to test spectral-WCS-interpreting software. COMMENT COMMENT Spectral representations are: COMMENT F: Frequency ...frequency-like COMMENT E: Photon energy ...frequency-like COMMENT N: Wave number ...frequency-like COMMENT R: Radio velocity ...frequency-like COMMENT Wavelength (default) ...wavelength-like COMMENT O: Optical velocity ...wavelength-like COMMENT Z: Redshift ...wavelength-like COMMENT V: Relativistic velocity ...velocity-like COMMENT B: Relativistic beta ...velocity-like COMMENT COMMENT The Mopra radio telescope is operated by the Australia Telescope COMMENT National Facility. COMMENT COMMENT Author: Mark Calabretta, Australia Telescope National Facility COMMENT http://www.atnf.csiro.au/~mcalabre/index.html COMMENT 2009-04-22 COMMENT ---------------------------------------------------------------------- COMMENT OBJECT = 'Orion-KL' / Orion Kleinmann-Low nebula MOLECULE= '13CO ' / Carbon(13) monoxide TRANSITI= '1-0 ' / 1-0 transition DATE-OBS= '2006-07-09T20:29:00' / Date of observation TELESCOP= 'ATNF Mopra' / 22m mm-wave telescope OBSERVER= 'Walsh/Thorwirth' / Observers BUNIT = 'K ' / Brightness units, Kelvin COMMENT COMMENT ------------------------------------------------------------ Frequency COMMENT CRPIX1F = 32768.0 / Pixel coordinate of reference point CTYPE1F = 'FREQ-W2F' / Frequency, non-linear axis CRVAL1F = 102.6940613E+9 / [Hz] Frequency of reference channel CDELT1F = -2.332330873E+5 / [Hz] Channel spacing CUNIT1F = 'Hz ' / Units of coordinate increment and value COMMENT RESTFRQF= 110201353000.0 / [Hz] 13CO line rest frequency RESTWAVF= 0.00272040633 / [m] 13CO line rest wavelength SPECSYSF= 'LSRK ' / Reference frame of spectral coordinates SSYSOBSF= 'TOPOCENT' / Reference frame of observation VELOSYSF= 0.0 / [m/s] Bary-topo velocity towards the source SSYSSRCF= 'LSRK ' / Reference frame of source redshift ZSOURCEF= 0.0000 / Redshift of the source COMMENT CRPIX2F = 1 CDELT2F = 1.0 CTYPE2F = 'RA ' CRVAL2F = 83.81042 / [deg] (05h35m14.5s) CUNIT2F = 'deg ' COMMENT CRPIX3F = 1 CDELT3F = 1.0 CTYPE3F = 'DEC ' CRVAL3F = -5.375222 / [deg] (-05:22:30.8) CUNIT3F = 'deg ' COMMENT RADESYSF= 'FK5 ' / FK5 (IAU 1984) equatorial coordinates EQUINOXF= 2000.0 / Equinox J2000.0 COMMENT CRPIX4F = 1 CDELT4F = 1.0 CTYPE4F = 'STOKES ' CRVAL4F = 1 / Stokes I (total intensity) COMMENT COMMENT -------------------------------------------------------- Photon energy COMMENT CRPIX1E = 32768.0 / Pixel coordinate of reference point CTYPE1E = 'ENER-W2F' / Photon energy, non-linear axis CRVAL1E = 4.247088937E-4 / [eV] Photon energy of reference channel CDELT1E = -0.9645754124E-9 / [eV] Channel spacing CUNIT1E = 'eV ' / Units of coordinate increment and value COMMENT RESTFRQE= 110201353000.0 / [Hz] 13CO line rest frequency RESTWAVE= 0.00272040633 / [m] 13CO line rest wavelength SPECSYSE= 'LSRK ' / Reference frame of spectral coordinates SSYSOBSE= 'TOPOCENT' / Reference frame of observation VELOSYSE= 0.0 / [m/s] Bary-topo velocity towards the source SSYSSRCE= 'LSRK ' / Reference frame of source redshift ZSOURCEE= 0.0000 / Redshift of the source COMMENT CRPIX2E = 1 CDELT2E = 1.0 CTYPE2E = 'RA ' CRVAL2E = 83.81042 / [deg] (05h35m14.5s) CUNIT2E = 'deg ' COMMENT CRPIX3E = 1 CDELT3E = 1.0 CTYPE3E = 'DEC ' CRVAL3E = -5.375222 / [deg] (-05:22:30.8) CUNIT3E = 'deg ' COMMENT RADESYSE= 'FK5 ' / FK5 (IAU 1984) equatorial coordinates EQUINOXE= 2000.0 / Equinox J2000.0 COMMENT CRPIX4E = 1 CDELT4E = 1.0 CTYPE4E = 'STOKES ' CRVAL4E = 1 / Stokes I (total intensity) COMMENT COMMENT ---------------------------------------------------------- Wave number COMMENT CRPIX1N = 32768.0 / Pixel coordinate of reference point CTYPE1N = 'WAVN-W2F' / Wave number, non-linear axis CRVAL1N = 3.425505162E+2 / [/m] Wave number of reference channel CDELT1N = -7.779818375E-4 / [/m] Channel spacing CUNIT1N = '/m ' / Units of coordinate increment and value COMMENT RESTFRQN= 110201353000.0 / [Hz] 13CO line rest frequency RESTWAVN= 0.00272040633 / [m] 13CO line rest wavelength SPECSYSN= 'LSRK ' / Reference frame of spectral coordinates SSYSOBSN= 'TOPOCENT' / Reference frame of observation VELOSYSN= 0.0 / [m/s] Bary-topo velocity towards the source SSYSSRCN= 'LSRK ' / Reference frame of source redshift ZSOURCEN= 0.0000 / Redshift of the source COMMENT CRPIX2N = 1 CDELT2N = 1.0 CTYPE2N = 'RA ' CRVAL2N = 83.81042 / [deg] (05h35m14.5s) CUNIT2N = 'deg ' COMMENT CRPIX3N = 1 CDELT3N = 1.0 CTYPE3N = 'DEC ' CRVAL3N = -5.375222 / [deg] (-05:22:30.8) CUNIT3N = 'deg ' COMMENT RADESYSN= 'FK5 ' / FK5 (IAU 1984) equatorial coordinates EQUINOXN= 2000.0 / Equinox J2000.0 COMMENT CRPIX4N = 1 CDELT4N = 1.0 CTYPE4N = 'STOKES ' CRVAL4N = 1 / Stokes I (total intensity) COMMENT COMMENT ------------------------------------------------------- Radio velocity COMMENT CRPIX1R = 32768.0 / Pixel coordinate of reference point CTYPE1R = 'VRAD-W2F' / Radio velocity, non-linear axis CRVAL1R = 2.042288396E+7 / [m/s] Radio velocity of reference channel CDELT1R = 6.344887666E+2 / [m/s] Channel spacing CUNIT1R = 'm/s ' / Units of coordinate increment and value COMMENT RESTFRQR= 110201353000.0 / [Hz] 13CO line rest frequency RESTWAVR= 0.00272040633 / [m] 13CO line rest wavelength SPECSYSR= 'LSRK ' / Reference frame of spectral coordinates SSYSOBSR= 'TOPOCENT' / Reference frame of observation VELOSYSR= 0.0 / [m/s] Bary-topo velocity towards the source SSYSSRCR= 'LSRK ' / Reference frame of source redshift ZSOURCER= 0.0000 / Redshift of the source COMMENT CRPIX2R = 1 CDELT2R = 1.0 CTYPE2R = 'RA ' CRVAL2R = 83.81042 / [deg] (05h35m14.5s) CUNIT2R = 'deg ' COMMENT CRPIX3R = 1 CDELT3R = 1.0 CTYPE3R = 'DEC ' CRVAL3R = -5.375222 / [deg] (-05:22:30.8) CUNIT3R = 'deg ' COMMENT RADESYSR= 'FK5 ' / FK5 (IAU 1984) equatorial coordinates EQUINOXR= 2000.0 / Equinox J2000.0 COMMENT CRPIX4R = 1 CDELT4R = 1.0 CTYPE4R = 'STOKES ' CRVAL4R = 1 / Stokes I (total intensity) COMMENT COMMENT ----------------------------------------------------------- Wavelength COMMENT CRPIX1 = 32768.0 / Pixel coordinate of reference point CTYPE1 = 'WAVE ' / Wavelength in vacuuo, linear axis CRVAL1 = 2.919277457E-3 / [m] Wavelength of reference channel CDELT1 = 6.630101933E-9 / [m] Channel spacing CUNIT1 = 'm ' / Units of coordinate increment and value COMMENT RESTFRQ = 110201353000.0 / [Hz] 13CO line rest frequency RESTWAV = 0.00272040633 / [m] 13CO line rest wavelength SPECSYS = 'LSRK ' / Reference frame of spectral coordinates SSYSOBS = 'TOPOCENT' / Reference frame of observation VELOSYS = 0.0 / [m/s] Bary-topo velocity towards the source SSYSSRC = 'LSRK ' / Reference frame of source redshift ZSOURCE = 0.0000 / Redshift of the source COMMENT CRPIX2 = 1 CDELT2 = 1.0 CTYPE2 = 'RA ' CRVAL2 = 83.81042 / [deg] (05h35m14.5s) CUNIT2 = 'deg ' COMMENT CRPIX3 = 1 CDELT3 = 1.0 CTYPE3 = 'DEC ' CRVAL3 = -5.375222 / [deg] (-05:22:30.8) CUNIT3 = 'deg ' COMMENT RADESYS = 'FK5 ' / FK5 (IAU 1984) equatorial coordinates EQUINOX = 2000.0 / Equinox J2000.0 COMMENT CRPIX4 = 1 CDELT4 = 1.0 CTYPE4 = 'STOKES ' CRVAL4 = 1 / Stokes I (total intensity) COMMENT COMMENT ----------------------------------------------------- Optical velocity COMMENT CRPIX1O = 32768.0 / Pixel coordinate of reference point CTYPE1O = 'VOPT ' / Optical velocity, linear axis CRVAL1O = 2.191586755E+7 / [m/s] Optical velocity of reference channel CDELT1O = 7.306462036E+2 / [m/s] Channel spacing CUNIT1O = 'm/s ' / Units of coordinate increment and value COMMENT RESTFRQO= 110201353000.0 / [Hz] 13CO line rest frequency RESTWAVO= 0.00272040633 / [m] 13CO line rest wavelength SPECSYSO= 'LSRK ' / Reference frame of spectral coordinates SSYSOBSO= 'TOPOCENT' / Reference frame of observation VELOSYSO= 0.0 / [m/s] Bary-topo velocity towards the source SSYSSRCO= 'LSRK ' / Reference frame of source redshift ZSOURCEO= 0.0000 / Redshift of the source COMMENT CRPIX2O = 1 CDELT2O = 1.0 CTYPE2O = 'RA ' CRVAL2O = 83.81042 / [deg] (05h35m14.5s) CUNIT2O = 'deg ' COMMENT CRPIX3O = 1 CDELT3O = 1.0 CTYPE3O = 'DEC ' CRVAL3O = -5.375222 / [deg] (-05:22:30.8) CUNIT3O = 'deg ' COMMENT RADESYSO= 'FK5 ' / FK5 (IAU 1984) equatorial coordinates EQUINOXO= 2000.0 / Equinox J2000.0 COMMENT CRPIX4O = 1 CDELT4O = 1.0 CTYPE4O = 'STOKES ' CRVAL4O = 1 / Stokes I (total intensity) COMMENT COMMENT ------------------------------------------------------------- Redshift COMMENT CRPIX1Z = 32768.0 / Pixel coordinate of reference point CTYPE1Z = 'ZOPT ' / Redshift, linear axis CRVAL1Z = 7.310346531E-2 / [] Redshift of reference channel CDELT1Z = 2.437173398E-6 / [] Channel spacing COMMENT RESTFRQZ= 110201353000.0 / [Hz] 13CO line rest frequency RESTWAVZ= 0.00272040633 / [m] 13CO line rest wavelength SPECSYSZ= 'LSRK ' / Reference frame of spectral coordinates SSYSOBSZ= 'TOPOCENT' / Reference frame of observation VELOSYSZ= 0.0 / [m/s] Bary-topo velocity towards the source SSYSSRCZ= 'LSRK ' / Reference frame of source redshift ZSOURCEZ= 0.0000 / Redshift of the source COMMENT CRPIX2Z = 1 CDELT2Z = 1.0 CTYPE2Z = 'RA ' CRVAL2Z = 83.81042 / [deg] (05h35m14.5s) CUNIT2Z = 'deg ' COMMENT CRPIX3Z = 1 CDELT3Z = 1.0 CTYPE3Z = 'DEC ' CRVAL3Z = -5.375222 / [deg] (-05:22:30.8) CUNIT3Z = 'deg ' COMMENT RADESYSZ= 'FK5 ' / FK5 (IAU 1984) equatorial coordinates EQUINOXZ= 2000.0 / Equinox J2000.0 COMMENT CRPIX4Z = 1 CDELT4Z = 1.0 CTYPE4Z = 'STOKES ' CRVAL4Z = 1 / Stokes I (total intensity) COMMENT COMMENT ------------------------------------------------ Relativistic velocity COMMENT CRPIX1V = 32768.0 / Pixel coordinate of reference point CTYPE1V = 'VELO-W2V' / Relativistic velocity, non-linear axis CRVAL1V = 2.111679434E+7 / [m/s] Velocity of reference channel CDELT1V = 6.774939349E+2 / [m/s] Channel spacing CUNIT1V = 'm/s ' / Units of coordinate increment and value COMMENT RESTFRQV= 110201353000.0 / [Hz] 13CO line rest frequency RESTWAVV= 0.00272040633 / [m] 13CO line rest wavelength SPECSYSV= 'LSRK ' / Reference frame of spectral coordinates SSYSOBSV= 'TOPOCENT' / Reference frame of observation VELOSYSV= 0.0 / [m/s] Bary-topo velocity towards the source SSYSSRCV= 'LSRK ' / Reference frame of source redshift ZSOURCEV= 0.0000 / Redshift of the source COMMENT CRPIX2V = 1 CDELT2V = 1.0 CTYPE2V = 'RA ' CRVAL2V = 83.81042 / [deg] (05h35m14.5s) CUNIT2V = 'deg ' COMMENT CRPIX3V = 1 CDELT3V = 1.0 CTYPE3V = 'DEC ' CRVAL3V = -5.375222 / [deg] (-05:22:30.8) CUNIT3V = 'deg ' COMMENT RADESYSV= 'FK5 ' / FK5 (IAU 1984) equatorial coordinates EQUINOXV= 2000.0 / Equinox J2000.0 COMMENT CRPIX4V = 1 CDELT4V = 1.0 CTYPE4V = 'STOKES ' CRVAL4V = 1 / Stokes I (total intensity) COMMENT COMMENT ---------------------------------------------- Relativistic beta (v/c) COMMENT CRPIX1B = 32768.0 / Pixel coordinate of reference point CTYPE1B = 'BETA-W2V' / Relativistic beta (v/c), non-linear axis CRVAL1B = 7.043804396E-2 / [] Relativistic beta of reference channel CDELT1B = 2.259876514E-6 / [] Channel spacing COMMENT RESTFRQB= 110201353000.0 / [Hz] 13CO line rest frequency RESTWAVB= 0.00272040633 / [m] 13CO line rest wavelength SPECSYSB= 'LSRK ' / Reference frame of spectral coordinates SSYSOBSB= 'TOPOCENT' / Reference frame of observation VELOSYSB= 0.0 / [m/s] Bary-topo velocity towards the source SSYSSRCB= 'LSRK ' / Reference frame of source redshift ZSOURCEB= 0.0000 / Redshift of the source COMMENT CRPIX2B = 1 CDELT2B = 1.0 CTYPE2B = 'RA ' CRVAL2B = 83.81042 / [deg] (05h35m14.5s) CUNIT2B = 'deg ' COMMENT CRPIX3B = 1 CDELT3B = 1.0 CTYPE3B = 'DEC ' CRVAL3B = -5.375222 / [deg] (-05:22:30.8) CUNIT3B = 'deg ' COMMENT RADESYSB= 'FK5 ' / FK5 (IAU 1984) equatorial coordinates EQUINOXB= 2000.0 / Equinox J2000.0 COMMENT CRPIX4B = 1 CDELT4B = 1.0 CTYPE4B = 'STOKES ' CRVAL4B = 1 / Stokes I (total intensity) COMMENT HISTORY fimgcreate 1.0b at 2009-04-22T04:28:18 DATE = '2009-04-22T04:28:18' / file creation date (YYYY-MM-DDThh:mm:ss UT) astropy-astropy-201cddb/astropy/wcs/tests/data/sub-segfault.hdr000066400000000000000000000037021507226315300250510ustar00rootroot00000000000000WCSAXES = 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-astropy-astropy-201cddb/astropy/wcs/tests/data/tab-time-last-axis.fits000066400000000000000000000341001507226315300262410ustar00rootroot00000000000000SIMPLE = T / conforms to FITS standard BITPIX = -64 / array data type NAXIS = 3 / number of array dimensions NAXIS1 = 1 NAXIS2 = 1 NAXIS3 = 1 EXTEND = T WCSAXES = 3 / Number of coordinate axes WCSNAME = 'TEST 3D FRAME WITH TIME' CRPIX1 = 32.5 / Pixel coordinate of reference point CRPIX2 = 16.5 / Pixel coordinate of reference point PC1_1 = 5.9533058133115E-06 / Coordinate transformation matrix element PC1_2 = 1.2909957973842E-05 / Coordinate transformation matrix element PC2_1 = -1.2644950182756E-05 / Coordinate transformation matrix element PC2_2 = 5.0222580309289E-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' / Right ascension, gnomonic projection CTYPE2 = 'DEC--TAN' / Declination, gnomonic projection CRVAL1 = 5.6305039831305 / [deg] Coordinate value at reference point CRVAL2 = -72.050216354325 / [deg] Coordinate value at reference point LONPOLE = 180.0 / [deg] Native longitude of celestial pole LATPOLE = -72.050216354325 / [deg] Native latitude of celestial pole MJDREF = 0.0 / [d] MJD of fiducial time RADESYS = 'ICRS' / Equatorial coordinate system SIPMXERR= 6.55090871151496E-19 / Max diff from GWCS (equiv pix). CTYPE3 = 'TIME-TAB' CUNIT3 = 's ' PS3_0 = 'WCS-TABLE' PV3_1 = 1 PS3_1 = 'coordinates' PV3_3 = 1 CRVAL3 = 1 CRPIX3 = 1.0 PC3_3 = 1.0 CDELT3 = 1.0 COMMENT FITS WCS created by approximating a gWCS END ?đXTENSION= 'BINTABLE' / binary table extension BITPIX = 8 / array data type NAXIS = 2 / number of array dimensions NAXIS1 = 1024 / 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 = '128D ' TDIM1 = '(1,128) ' EXTNAME = 'WCS-TABLE' / extension name EXTVER = 1 / extension value END ?đ@@@@@@@ @"@$@&@(@*@,@.@0@1@2@3@4@5@6@7@8@9@:@;@<@=@>@?@@@@€@A@A€@B@B€@C@C€@D@D€@E@E€@F@F€@G@G€@H@H€@I@I€@J@J€@K@K€@L@L€@M@M€@N@N€@O@O€@P@P@@P€@PĀ@Q@Q@@Q€@QĀ@R@R@@R€@RĀ@S@S@@S€@SĀ@T@T@@T€@TĀ@U@U@@U€@UĀ@V@V@@V€@VĀ@W@W@@W€@WĀ@X@X@@X€@XĀ@Y@Y@@Y€@YĀ@Z@Z@@Z€@ZĀ@[@[@@[€@[Ā@\@\@@\€@\Ā@]@]@@]€@]Ā@^@^@@^€@^Ā@_@_@@_€@_Āastropy-astropy-201cddb/astropy/wcs/tests/data/too_many_pv.hdr000066400000000000000000001034001507226315300247760ustar00rootroot00000000000000SIMPLE = 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 astropy-astropy-201cddb/astropy/wcs/tests/data/tpvonly.hdr000066400000000000000000000625001507226315300241640ustar00rootroot00000000000000SIMPLE = 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 astropy-astropy-201cddb/astropy/wcs/tests/data/unit.hdr000066400000000000000000000055001507226315300234250ustar00rootroot00000000000000SIMPLE = 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 astropy-astropy-201cddb/astropy/wcs/tests/data/validate.5.0.txt000066400000000000000000000011351507226315300246020ustar00rootroot00000000000000HDU 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''. astropy-astropy-201cddb/astropy/wcs/tests/data/validate.5.13.txt000066400000000000000000000011271507226315300246670ustar00rootroot00000000000000HDU 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''. astropy-astropy-201cddb/astropy/wcs/tests/data/validate.6.txt000066400000000000000000000011361507226315300244460ustar00rootroot00000000000000HDU 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''. astropy-astropy-201cddb/astropy/wcs/tests/data/validate.7.4.txt000066400000000000000000000013331507226315300246100ustar00rootroot00000000000000HDU 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''. astropy-astropy-201cddb/astropy/wcs/tests/data/validate.7.6.txt000066400000000000000000000012601507226315300246110ustar00rootroot00000000000000HDU 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''.astropy-astropy-201cddb/astropy/wcs/tests/data/validate.fits000066400000000000000000001166001507226315300244330ustar00rootroot00000000000000SIMPLE = 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 astropy-astropy-201cddb/astropy/wcs/tests/data/validate.txt000066400000000000000000000011231507226315300242760ustar00rootroot00000000000000HDU 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''. astropy-astropy-201cddb/astropy/wcs/tests/data/zpn-hole.hdr000066400000000000000000000055001507226315300242020ustar00rootroot00000000000000NAXIS = 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 astropy-astropy-201cddb/astropy/wcs/tests/helper.py000066400000000000000000000073131507226315300226730ustar00rootroot00000000000000# 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 astropy-astropy-201cddb/astropy/wcs/tests/test_auxprm.py000066400000000000000000000137211507226315300237670ustar00rootroot00000000000000# 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 astropy-astropy-201cddb/astropy/wcs/tests/test_celprm.py000066400000000000000000000053121507226315300237320ustar00rootroot00000000000000# 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) astropy-astropy-201cddb/astropy/wcs/tests/test_pickle.py000066400000000000000000000115251507226315300237220ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst import os import pickle import numpy as np import pytest from numpy.testing import assert_array_almost_equal 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 = pickle.dumps(wcs1) pickle.loads(s) def test_dist(): 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(): 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) 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_sip2(): 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_pkg_data_contents( os.path.join("data", "outside_sky.hdr"), encoding="binary" ) wcs1 = wcs.WCS(header) 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) class Sub(wcs.WCS): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.foo = 42 def test_subclass(): wcs1 = Sub() wcs1.foo = 45 s = pickle.dumps(wcs1) wcs2 = pickle.loads(s) assert isinstance(wcs2, Sub) 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" astropy-astropy-201cddb/astropy/wcs/tests/test_prjprm.py000066400000000000000000000125051507226315300237640ustar00rootroot00000000000000# 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) astropy-astropy-201cddb/astropy/wcs/tests/test_profiling.py000066400000000000000000000050121507226315300244360ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst import os import numpy as np import pytest 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 # 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") ] # 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! def test_read_map_files(): # how many map files we expect to see n_map_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}" ) @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) with NumpyRNGContext(123456789): x = np.random.rand(2**12, wcsobj.wcs.naxis) wcsobj.wcs_pix2world(x, 1) wcsobj.wcs_world2pix(x, 1) hdr_spec_file_list = [ os.path.basename(fname) for fname in get_pkg_data_filenames("data/spectra", pattern="*.hdr") ] def test_read_spec_files(): # how many spec files expected n_spec_files = 6 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. @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) astropy-astropy-201cddb/astropy/wcs/tests/test_tab.py000066400000000000000000000066261507226315300232270ustar00rootroot00000000000000# 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 import _wcs from .helper import SimModelTAB _WCSLIB_VER = Version(_wcs.__version__) 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( _WCSLIB_VER < 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( _WCSLIB_VER < 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( _WCSLIB_VER < Version("7.8"), reason="Requires WCSLIB >= 7.8 for swapping -TAB axes to work.", ) @pytest.mark.xfail( Version("7.8") <= _WCSLIB_VER < 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) astropy-astropy-201cddb/astropy/wcs/tests/test_tabprm.py000066400000000000000000000057701507226315300237450ustar00rootroot00000000000000# 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) astropy-astropy-201cddb/astropy/wcs/tests/test_utils.py000066400000000000000000001632151507226315300236170ustar00rootroot00000000000000# 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 import _wcs 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, 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 with pytest.warns(AstropyUserWarning, match="No observer defined on WCS"): 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) with pytest.warns(AstropyUserWarning, match="No observer defined on WCS"): 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 with pytest.warns(AstropyUserWarning, match="No observer defined on WCS"): 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(_wcs.__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" astropy-astropy-201cddb/astropy/wcs/tests/test_wcs.py000066400000000000000000001772121507226315300232550ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst import io import os from contextlib import nullcontext from datetime import datetime from multiprocessing.pool import ThreadPool import numpy as np import pytest from numpy.testing import ( assert_allclose, assert_array_almost_equal, assert_array_almost_equal_nulp, assert_array_equal, ) from packaging.version import Version 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 import _wcs _WCSLIB_VER = Version(_wcs.__version__) # NOTE: User can choose to use system wcslib instead of bundled. def ctx_for_v71_dateref_warnings(): if _WCSLIB_VER >= Version("7.1") and _WCSLIB_VER < 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 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")) 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 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_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) class TestSpectra: def setup_method(self): self._file_list = list(get_pkg_data_filenames("data/spectra", pattern="*.hdr")) 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 = 6 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 _WCSLIB_VER >= 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) assert len(all_wcs) == 9 def test_fixes(): """ From github issue #36 """ header = get_pkg_data_contents("data/nonstandard_units.hdr", encoding="binary") with ( pytest.raises(wcs.InvalidTransformError), pytest.warns(wcs.FITSFixedWarning) as w, ): wcs.WCS(header, translate_units="dhs") if Version("7.4") <= _WCSLIB_VER < Version("7.6"): assert len(w) == 3 assert "'datfix' made the change 'Success'." in str(w.pop().message) else: assert len(w) == 2 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) # 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) 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))) 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) # might as well monitor for changing behavior if Version("7.4") <= _WCSLIB_VER < 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) # Catch #2791 ww.wcs_pix2world(pixels[..., 0], pixels[..., 1], 0, ra_dec_order=True) # assuming that the data of sip2.fits doesn't change answer = np.array([[0.00024976, 0.00023018], [0.00023043, -0.00024997]]) 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], ] ) 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) def test_dict_init(): """ Test that WCS can be initialized with a dict-like object """ # 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) 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 _WCSLIB_VER >= Version("7.1"): hdr["DATEREF"] = "1858-11-17" if _WCSLIB_VER >= 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(): """ 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(): """ 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") <= _WCSLIB_VER < 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 _WCSLIB_VER >= Version("7.3"): # fmt: off hdrstr += ( "MJDREF = 0.0 / [d] MJD of fiducial time ", ) # fmt: on elif _WCSLIB_VER >= 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 _WCSLIB_VER >= Version("7.1") else 8 if _WCSLIB_VER < Version("7.1"): nrec = 8 elif _WCSLIB_VER < 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 _WCSLIB_VER >= Version("7.6"): filename = "data/validate.7.6.txt" elif _WCSLIB_VER >= Version("7.4"): filename = "data/validate.7.4.txt" elif _WCSLIB_VER >= Version("6.0"): filename = "data/validate.6.txt" elif _WCSLIB_VER >= Version("5.13"): filename = "data/validate.5.13.txt" elif _WCSLIB_VER >= 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( fname=None, ext=0, tolerance=1.0e-4, origin=0, random_npts=25000, adaptive=False, maxiter=20, detect_divergence=True, ): """Test all_world2pix, iterative inverse of all_pix2world""" # Open test FITS file: if fname is None: 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(random_npts, 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=adaptive, maxiter=maxiter, detect_divergence=detect_divergence, ) 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) 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_unit_normalization(): """ 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(): """ 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) ra, dec = w.wcs_pix2world(110, 110, 0) assert np.isnan(ra) assert np.isnan(dec) 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( _wcs.__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( _wcs.__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 _WCSLIB_VER >= 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 _WCSLIB_VER >= 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 _WCSLIB_VER < 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 _WCSLIB_VER >= 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( _WCSLIB_VER < 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( _WCSLIB_VER < 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], ) 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 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 with ThreadPool(8) as pool: results = pool.map(round_trip_transform, (pixel,) * 8) for pixel2 in results: assert_allclose(pixel, pixel2, atol=1e-7) astropy-astropy-201cddb/astropy/wcs/tests/test_wcsprm.py000066400000000000000000000702651507226315300237740ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst import gc import locale import re import numpy as np import pytest 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 == " " w.alt = "X" assert w.alt == "X" del w.alt assert w.alt == " " def test_alt_invalid1(): w = _wcs.Wcsprm() with pytest.raises(ValueError): w.alt = "$" def test_alt_invalid2(): w = _wcs.Wcsprm() with pytest.raises(ValueError): w.alt = " " def test_axis_types(): w = _wcs.Wcsprm() assert_array_equal(w.axis_types, [0, 0]) def test_cd(): w = _wcs.Wcsprm() w.cd = [[1, 0], [0, 1]] 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() is False def test_cd_missing(): w = _wcs.Wcsprm() assert w.has_cd() is False with pytest.raises(AttributeError): w.cd def test_cd_missing2(): w = _wcs.Wcsprm() w.cd = [[1, 0], [0, 1]] assert w.has_cd() is True del w.cd assert w.has_cd() is False with pytest.raises(AttributeError): w.cd def test_cd_invalid(): 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() assert_array_equal(w.cdelt, [1, 1]) w.cdelt = [42, 54] assert_array_equal(w.cdelt, [42, 54]) def test_cdelt_delete(): w = _wcs.Wcsprm() with pytest.raises(TypeError): del w.cdelt def test_cel_offset(): w = _wcs.Wcsprm() assert w.cel_offset is False w.cel_offset = "foo" assert w.cel_offset is True w.cel_offset = 0 assert w.cel_offset is False 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() assert w.celfix() == -1 def test_cname(): w = _wcs.Wcsprm() # Test that this works as an iterator for x in w.cname: assert x == "" assert list(w.cname) == ["", ""] w.cname = [b"foo", "bar"] assert list(w.cname) == ["foo", "bar"] def test_cname_invalid(): w = _wcs.Wcsprm() with pytest.raises(TypeError): w.cname = [42, 54] def test_colax(): w = _wcs.Wcsprm() assert w.colax.dtype == np.intc assert_array_equal(w.colax, [0, 0]) w.colax = [42, 54] assert_array_equal(w.colax, [42, 54]) 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() 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 def test_colnum_invalid(): w = _wcs.Wcsprm() with pytest.raises(TypeError): w.colnum = "foo" def test_crder(): 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.crota = [1, 0] 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() is False def test_crota_missing(): w = _wcs.Wcsprm() assert w.has_crota() is False with pytest.raises(AttributeError): w.crota def test_crota_missing2(): w = _wcs.Wcsprm() w.crota = [1, 0] assert w.has_crota() is True del w.crota assert w.has_crota() is False with pytest.raises(AttributeError): w.crota def test_crpix(): 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 == float assert_array_equal(w.crval, [0, 0]) w.crval = [42, 54] assert_array_equal(w.crval, [42, 54]) w.crval[0] = 0 assert_array_equal(w.crval, [0, 54]) def test_csyer(): 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) == ["", ""] 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 == "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) == ["foo", "bar"] assert w.lat == -1 assert w.lng == -1 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() assert w.cubeface == -1 w.cubeface = 0 with pytest.raises(OverflowError): w.cubeface = -1 def test_cunit(): 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 def test_cunit_invalid(): w = _wcs.Wcsprm() with pytest.warns(u.UnitsWarning, match="foo") as warns: w.cunit[0] = "foo" assert len(warns) == 1 def test_cunit_invalid2(): 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() 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 == "" # TODO: When dateavg is verified, check that it works def test_dateobs(): w = _wcs.Wcsprm() assert w.dateobs == "" # TODO: When dateavg is verified, check that it works def test_datfix(): w = _wcs.Wcsprm() w.dateobs = "31/12/99" assert w.datfix() == 0 assert w.dateobs == "1999-12-31" assert w.mjdobs == 51543.0 def test_equinox(): 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() 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._wcs.__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 = "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._wcs.__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 = "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._wcs.__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() assert len(w.get_ps()) == 0 def test_get_pv(): # TODO: We need some data with PVi_ma keywords w = _wcs.Wcsprm() assert len(w.get_pv()) == 0 def test_imgpix_matrix(): w = _wcs.Wcsprm() with pytest.raises(AssertionError): w.imgpix_matrix 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() def test_lat(): w = _wcs.Wcsprm() assert w.lat == -1 def test_lat_set(): w = _wcs.Wcsprm() with pytest.raises(AttributeError): w.lat = 0 def test_latpole(): w = _wcs.Wcsprm() assert w.latpole == 90.0 w.latpole = 45.0 assert w.latpole == 45.0 del w.latpole assert w.latpole == 90.0 def test_lattyp(): w = _wcs.Wcsprm() assert w.lattyp == " " def test_lattyp_set(): w = _wcs.Wcsprm() with pytest.raises(AttributeError): w.lattyp = 0 def test_lng(): w = _wcs.Wcsprm() assert w.lng == -1 def test_lng_set(): w = _wcs.Wcsprm() with pytest.raises(AttributeError): w.lng = 0 def test_lngtyp(): w = _wcs.Wcsprm() assert w.lngtyp == " " def test_lngtyp_set(): w = _wcs.Wcsprm() with pytest.raises(AttributeError): w.lngtyp = 0 def test_lonpole(): w = _wcs.Wcsprm() assert np.isnan(w.lonpole) w.lonpole = 45.0 assert w.lonpole == 45.0 del w.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() assert np.isnan(w.mjdavg) w.mjdavg = 45.0 assert w.mjdavg == 45.0 del w.mjdavg assert np.isnan(w.mjdavg) def test_mjdobs(): w = _wcs.Wcsprm() assert np.isnan(w.mjdobs) w.mjdobs = 45.0 assert w.mjdobs == 45.0 del w.mjdobs assert np.isnan(w.mjdobs) def test_name(): w = _wcs.Wcsprm() assert w.name == "" w.name = "foo" assert w.name == "foo" def test_naxis(): w = _wcs.Wcsprm() assert w.naxis == 2 def test_naxis_set(): w = _wcs.Wcsprm() with pytest.raises(AttributeError): w.naxis = 4 def test_obsgeo(): w = _wcs.Wcsprm() assert np.all(np.isnan(w.obsgeo)) 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() assert w.has_pc() assert_array_equal(w.pc, [[1, 0], [0, 1]]) w.cd = [[1, 0], [0, 1]] assert not w.has_pc() del w.cd assert w.has_pc() assert_array_equal(w.pc, [[1, 0], [0, 1]]) w.pc = w.pc def test_pc_missing(): w = _wcs.Wcsprm() w.cd = [[1, 0], [0, 1]] assert not w.has_pc() with pytest.raises(AttributeError): w.pc def test_phi0(): w = _wcs.Wcsprm() assert np.isnan(w.phi0) w.phi0 = 42.0 assert w.phi0 == 42.0 del w.phi0 assert np.isnan(w.phi0) def test_piximg_matrix(): w = _wcs.Wcsprm() with pytest.raises(AssertionError): w.piximg_matrix def test_piximg_matrix2(): w = _wcs.Wcsprm() with pytest.raises(AttributeError): w.piximg_matrix = None 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() 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 == "" w.radesys = "foo" assert w.radesys == "foo" def test_restfrq(): 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() assert w.restwav == 0.0 w.restwav = np.nan assert np.isnan(w.restwav) del w.restwav def test_set_ps(): 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.set_ps([(0, 0, "param1")] * 16) def test_set_pv(): 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.0)] * 16) def test_spcfix(): # TODO: We need some data with broken spectral headers here to # really test 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() assert w.spec == -1 def test_spec_set(): w = _wcs.Wcsprm() with pytest.raises(AttributeError): w.spec = 0 def test_specsys(): w = _wcs.Wcsprm() assert w.specsys == "" w.specsys = "foo" assert w.specsys == "foo" def test_sptr(): # TODO: Write me pass def test_ssysobs(): w = _wcs.Wcsprm() assert w.ssysobs == "" w.ssysobs = "foo" assert w.ssysobs == "foo" def test_ssyssrc(): w = _wcs.Wcsprm() assert w.ssyssrc == "" w.ssyssrc = "foo" assert w.ssyssrc == "foo" def test_tab(): w = _wcs.Wcsprm() assert len(w.tab) == 0 # TODO: Inject some headers that have tables and test def test_theta0(): w = _wcs.Wcsprm() assert np.isnan(w.theta0) w.theta0 = 42.0 assert w.theta0 == 42.0 del w.theta0 assert np.isnan(w.theta0) def test_toheader(): w = _wcs.Wcsprm() assert isinstance(w.to_header(), str) def test_velangl(): w = _wcs.Wcsprm() assert np.isnan(w.velangl) w.velangl = 42.0 assert w.velangl == 42.0 del w.velangl assert np.isnan(w.velangl) def test_velosys(): w = _wcs.Wcsprm() assert np.isnan(w.velosys) w.velosys = 42.0 assert w.velosys == 42.0 del w.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() assert np.isnan(w.zsource) w.zsource = 42.0 assert w.zsource == 42.0 del w.zsource assert np.isnan(w.zsource) def test_cd_3d(): 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_pkg_data_contents("data/3d_cd.hdr", encoding="binary") w = _wcs.Wcsprm(header) pc = w.get_pc() try: pc[0, 0] = 42 except (RuntimeError, ValueError): pass else: raise AssertionError() def test_detailed_err(): w = _wcs.Wcsprm() w.pc = [[0, 0], [0, 0]] with pytest.raises(_wcs.SingularMatrixError): w.set() def test_header_parse(): from astropy.io import fits 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(): try: 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" ) 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._wcs.__version__) >= Version("7.3"): fix_ref["datfix"] = ( "Set DATEREF to '1858-11-17' from MJDREF.\n" + fix_ref["datfix"] ) elif Version(wcs._wcs.__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.__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") astropy-astropy-201cddb/astropy/wcs/tests/test_wtbarr.py000066400000000000000000000027731507226315300237610ustar00rootroot00000000000000# 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: ") astropy-astropy-201cddb/astropy/wcs/utils.py000066400000000000000000001335011507226315300214110ustar00rootroot00000000000000# 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) astropy-astropy-201cddb/astropy/wcs/wcs.py000066400000000000000000004204341507226315300210510ustar00rootroot00000000000000# 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 re import textwrap import uuid import warnings # THIRD-PARTY import numpy as np from packaging.version import Version # LOCAL 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 _wcs, docstrings # Mix-in class that provides the APE 14 API from .wcsapi.fitswcs import FITSWCSAPIMixin, SlicedFITSWCS __all__ = [ "WCS", "Auxprm", "Celprm", "DistortionLookupTable", "FITSFixedWarning", "InconsistentAxisTypesError", "InvalidCoordinateError", "InvalidPrjParametersError", "InvalidSubimageSpecificationError", "InvalidTabularParametersError", "InvalidTransformError", "NoConvergence", "NoSolutionError", "NoWcsKeywordsFoundError", "NonseparableSubimageCoordinateSystemError", "Prjprm", "SingularMatrixError", "Sip", "Tabprm", "WCSBase", "WcsError", "Wcsprm", "Wtbarr", "find_all_wcs", "validate", ] __doctest_skip__ = ["WCS.all_world2pix"] if _wcs is not None: if Version(_wcs.__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 _wcs._sanity_check(): raise RuntimeError( "astropy.wcs did not pass its sanity check for your build on your platform." ) _WCSSUB_TIME_SUPPORT = Version(_wcs.__version__) >= Version("7.8") _WCS_TPD_WARN_LT71 = Version(_wcs.__version__) < Version("7.1") _WCS_TPD_WARN_LT74 = Version(_wcs.__version__) < Version("7.4") WCSBase = _wcs._Wcs DistortionLookupTable = _wcs.DistortionLookupTable Sip = _wcs.Sip Wcsprm = _wcs.Wcsprm Auxprm = _wcs.Auxprm Celprm = _wcs.Celprm Prjprm = _wcs.Prjprm Tabprm = _wcs.Tabprm Wtbarr = _wcs.Wtbarr WcsError = _wcs.WcsError SingularMatrixError = _wcs.SingularMatrixError InconsistentAxisTypesError = _wcs.InconsistentAxisTypesError InvalidTransformError = _wcs.InvalidTransformError InvalidCoordinateError = _wcs.InvalidCoordinateError NoSolutionError = _wcs.NoSolutionError InvalidSubimageSpecificationError = _wcs.InvalidSubimageSpecificationError NonseparableSubimageCoordinateSystemError = ( _wcs.NonseparableSubimageCoordinateSystemError ) NoWcsKeywordsFoundError = _wcs.NoWcsKeywordsFoundError InvalidTabularParametersError = _wcs.InvalidTabularParametersError InvalidPrjParametersError = _wcs.InvalidPrjParametersError # Copy all the constants from the C extension into this module's namespace for key, val in _wcs.__dict__.items(): if key.startswith(("WCSSUB_", "WCSHDR_", "WCSHDO_", "WCSCOMPARE_", "PRJ_")): locals()[key] = val __all__.append(key) # noqa: PYI056 # 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) _wcs.set_wtbarr_fitsio_callback(_load_tab_bintable) else: WCSBase = object Wcsprm = object DistortionLookupTable = object Sip = object Tabprm = object Wtbarr = object WcsError = None SingularMatrixError = None InconsistentAxisTypesError = None InvalidTransformError = None InvalidCoordinateError = None NoSolutionError = None InvalidSubimageSpecificationError = None NonseparableSubimageCoordinateSystemError = None NoWcsKeywordsFoundError = None InvalidTabularParametersError = None _WCSSUB_TIME_SUPPORT = False _WCS_TPD_WARN_LT71 = False _WCS_TPD_WARN_LT74 = False # 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 else: raise ValueError( "keysel must be a list of 'image', 'binary' and/or 'pixel'" ) else: keysel_flags = -1 return keysel_flags 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. """ 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`, `~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 : `~astropy.io.fits.HDUList`, optional It is needed when header keywords point to a `distortion paper`_ lookup table stored in a different extension. 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 provided if *header* is also provided. minerr : float, optional The minimum value a distortion correction must have in order to be applied. If the value of ``CQERRja`` is smaller than *minerr*, the corresponding distortion is not applied. 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. - `int`: a bit field selecting specific extensions to accept. See :ref:`astropy:relaxread` for details. naxis : int or sequence, optional Extracts specific coordinate axes using :meth:`~astropy.wcs.Wcsprm.sub`. If a header is provided, and *naxis* is not ``None``, *naxis* will be passed to :meth:`~astropy.wcs.Wcsprm.sub` in order to select specific axes from the header. See :meth:`~astropy.wcs.Wcsprm.sub` for more details about this parameter. 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 function is called). To use binary table image array or pixel list keywords, *keysel* must be set. Each element in the list should be one of the following strings: - 'image': Image header keywords - 'binary': Binary table image array keywords - 'pixel': Pixel list keywords Keywords such as ``EQUIna`` or ``RFRQna`` that are common to binary table image arrays and pixel lists (including ``WCSNna`` and ``TWCSna``) are selected by both 'binary' and 'pixel'. colsel : sequence of int, optional A sequence of table column numbers used to restrict the WCS transformations considered to only those pertaining to the specified columns. If `None`, there is no restriction. fix : bool, optional 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`. Raises ------ MemoryError Memory allocation failed. ValueError Invalid key. KeyError Key not found in FITS header. ValueError Lookup table distortion present in the header but *fobj* was not provided. Notes ----- 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=True, naxis=None, keysel=None, colsel=None, fix=True, translate_units="", _do_set=True, ): close_fds = [] # these parameters are stored to be used when unpickling a WCS object: self._init_kwargs = { "keysel": copy.copy(keysel), "colsel": copy.copy(colsel), } if header is None: if naxis is None: naxis = 2 wcsprm = _wcs.Wcsprm(header=None, key=key, relax=relax, naxis=naxis) self.naxis = wcsprm.naxis # Set some reasonable defaults. det2im = (None, None) cpdis = (None, None) sip = None else: keysel_flags = _parse_keysel(keysel) 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" ) fobj = fits.open(header) close_fds.append(fobj) header = fobj[0].header 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: 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: 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 = _wcs.Wcsprm( header=tmp_header_bytes, key=key, relax=relax, keysel=keysel_flags, colsel=colsel, warnings=False, hdulist=fobj, ) if naxis is not None: try: tmp_wcsprm = tmp_wcsprm.sub(naxis) except ValueError: pass est_naxis = tmp_wcsprm.naxis if tmp_wcsprm.naxis else 2 except _wcs.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 = _wcs.Wcsprm( header=header_bytes, key=key, relax=relax, keysel=keysel_flags, colsel=colsel, hdulist=fobj, ) except _wcs.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, hdulist=fobj, ) else: raise if naxis is not None: wcsprm = wcsprm.sub(naxis) self.naxis = wcsprm.naxis if wcsprm.naxis != 2 and ( det2im[0] or det2im[1] or cpdis[0] or cpdis[1] or sip ): raise ValueError( 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. """ ) 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, ) 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), ) new_copy.__dict__.update(self.__dict__) return new_copy def __deepcopy__(self, memo): from copy import deepcopy new_copy = self.__class__() 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): """ Return a shallow copy of the object. 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) def deepcopy(self): """ Return a deep copy of the object. Convenience method so user doesn't have to import the :mod:`copy` stdlib module. """ return copy.deepcopy(self) def sub(self, axes=None): copy = self.deepcopy() # 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 _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. A footprint is defined as the positions of the corners of the image on the sky after all available distortions have been applied. Parameters ---------- 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 axes is not None: naxis1, naxis2 = axes else: 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) if naxis1 is None or naxis2 is 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, ) if undistort: return self.all_pix2world(corners, 1) else: return self.wcs_pix2world(corners, 1) def _read_det2im_kw(self, header, fobj, err=0.0): """ Create a `distortion paper`_ type lookup table for detector to image plane correction. """ if fobj is None: return (None, None) if not isinstance(fobj, fits.HDUList): return (None, None) try: 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"] 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) cpdis = DistortionLookupTable(d2im_data, crpix, crval, cdelt) if axiscorr == 1: return (cpdis, None) 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 `distortion paper`_ type lookup table to the given `~astropy.io.fits.HDUList`. """ if self.det2im1 is None and self.det2im2 is None: return dist = "D2IMDIS" d_kw = "D2IM" 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["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) write_d2i(1, self.det2im1) write_d2i(2, self.det2im2) def _read_distortion_kw(self, header, fobj, dist="CPDIS", err=0.0): """ Reads `distortion paper`_ table-lookup keywords and data, and returns a 2-tuple of `~astropy.wcs.DistortionLookupTable` objects. If no `distortion paper`_ keywords are found, ``(None, None)`` is returned. """ if isinstance(header, (str, bytes)): return (None, None) if dist == "CPDIS": d_kw = "DP" err_kw = "CPERR" else: d_kw = "DQ" err_kw = "CQERR" tables = {} for i in range(1, self.naxis + 1): 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() 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() 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["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: warnings.warn( "Polynomial distortion is not implemented.\n", AstropyUserWarning, ) else: tables[i] = None if not tables: return (None, None) else: return (tables.get(1), tables.get(2)) def _write_distortion_kw(self, hdulist, dist="CPDIS"): """ 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" else: d_kw = "DQ" def write_dist(num, cpdis): if cpdis is None: return 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["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 _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, (str, bytes)): # TODO: Parse SIP from a string without pyfits around return None 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" ) 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): key = f"A_{i}_{j}" if key in header: a[i, j] = header[key] del header[key] m = int(header["B_ORDER"]) 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" ) else: a = None b = None 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" ) 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): key = f"AP_{i}_{j}" if key in header: ap[i, j] = header[key] del header[key] m = int(header["BP_ORDER"]) 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" ) else: ap = None bp = None if a is None and b is None and ap is None and bp is None: return None 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(f"CRPIX1{wcskey}") crpix2 = header.get(f"CRPIX2{wcskey}") return Sip(a, b, ap, bp, (crpix1, crpix2)) def _write_sip_kw(self): """ Write out SIP keywords. Returns a dictionary of key-value pairs. """ if self.sip is None: return {} keywords = {} def write_array(name, a): if a is None: return size = a.shape[0] 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[f"{name}_{i:d}_{j:d}"] = a[i, j], comment 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": raise ValueError( "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" ) if self.wcs.naxis == 2: if self.wcs.lng == 0 and self.wcs.lat == 1: return sky elif self.wcs.lng == 1 and self.wcs.lat == 0: # Reverse the order of the columns 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" ) 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" ) 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": raise ValueError( "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" ) if self.wcs.naxis == 2: if self.wcs.lng == 0 and self.wcs.lat == 1: return sky elif self.wcs.lng == 1 and self.wcs.lat == 0: # Reverse the order of the columns return sky[:, ::-1] else: raise ValueError( "WCS does not have longitude and latitude celestial " "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" ) 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, ra_dec_order=False): """ A helper function to support reading either a pair of arrays or a single Nx2 array. """ def _return_list_of_arrays(axes, origin): if any(x.size == 0 for x in axes): return axes try: 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": result = self._normalize_sky(result) return result if len(args) == 2: try: xy, origin = args xy = np.asarray(xy) origin = int(origin) except Exception: raise TypeError( "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( 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__ = f""" Transforms pixel coordinates to world coordinates. Performs all of the following in series: - Detector to image plane correction (if present in the FITS file) - `SIP`_ distortion correction (if present in the FITS file) - `distortion paper`_ table-lookup correction (if present in the FITS file) - `wcslib`_ "core" WCS transformation 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)} Returns ------- {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 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. Raises ------ 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. """ 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__ = f""" Transforms pixel coordinates to world coordinates by doing only the basic `wcslib`_ transformation. 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`, or `~astropy.wcs.WCS.pix2foc`. 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)} Returns ------- {docstrings.RETURNS("world coordinates, in degrees", 8)} Raises ------ 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. Notes ----- The order of the axes for the result 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. """ 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) 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__ = f""" Transforms world coordinates to pixel coordinates, using only the basic `wcslib`_ WCS transformation. No `SIP`_ or `distortion paper`_ table lookup transformation is applied. 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)} 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. Raises ------ 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. """ def pix2foc(self, *args): return self._array_converter(self._pix2foc, None, *args) pix2foc.__doc__ = f""" Convert pixel coordinates to focal plane coordinates using the `SIP`_ polynomial distortion convention and `distortion paper`_ table-lookup correction. The output is in absolute pixel coordinates, not relative to ``CRPIX``. Parameters ---------- {docstrings.TWO_OR_MORE_ARGS("2", 8)} Returns ------- {docstrings.RETURNS("focal coordinates", 8)} Raises ------ MemoryError Memory allocation failed. ValueError Invalid coordinate transformation parameters. """ 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 `distortion paper`_ table-lookup correction. The output is in absolute pixel coordinates, not relative to ``CRPIX``. Parameters ---------- {docstrings.TWO_OR_MORE_ARGS("2", 8)} Returns ------- {docstrings.RETURNS("focal coordinates", 8)} Raises ------ MemoryError Memory allocation failed. ValueError Invalid coordinate transformation parameters. """ def det2im(self, *args): return self._array_converter(self._det2im, None, *args) det2im.__doc__ = f""" Convert detector coordinates to image plane coordinates using `distortion paper`_ table-lookup correction. The output is in absolute pixel coordinates, not relative to ``CRPIX``. Parameters ---------- {docstrings.TWO_OR_MORE_ARGS("2", 8)} Returns ------- {docstrings.RETURNS("pixel coordinates", 8)} Raises ------ MemoryError Memory allocation failed. ValueError Invalid coordinate transformation parameters. """ def sip_pix2foc(self, *args): if self.sip is None: if len(args) == 2: return args[0] elif len(args) == 3: return args[:2] else: raise TypeError("Wrong number of arguments") 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. 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 ---------- {docstrings.TWO_OR_MORE_ARGS("2", 8)} Returns ------- {docstrings.RETURNS("focal coordinates", 8)} Raises ------ MemoryError Memory allocation failed. ValueError Invalid coordinate transformation parameters. """ def sip_foc2pix(self, *args): if self.sip is None: if len(args) == 2: return args[0] elif len(args) == 3: return args[:2] else: raise TypeError("Wrong number of arguments") 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. 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 ---------- {docstrings.TWO_OR_MORE_ARGS("2", 8)} Returns ------- {docstrings.RETURNS("pixel coordinates", 8)} Raises ------ MemoryError Memory allocation failed. ValueError Invalid coordinate transformation parameters. """ 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 """ 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. See `to_header` for some warnings about the output produced. Parameters ---------- relax : bool or int, optional Degree of permissiveness: - `False` (default): Write all extensions that are considered to be safe and recommended. - `True`: Write all recognized informal extensions of the WCS standard. - `int`: a bit field selecting specific extensions to 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` """ header = self.to_header(relax=relax, key=key) hdu = fits.PrimaryHDU(header=header) hdulist = fits.HDUList(hdu) self._write_det2im(hdulist) self._write_distortion_kw(hdulist) return hdulist 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 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` (default): Write all extensions that are considered to be safe and recommended. - `True`: Write all recognized informal extensions of the WCS standard. - `int`: a bit field selecting specific extensions to 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 ------- header : `astropy.io.fits.Header` Notes ----- The output header will almost certainly differ from the input in a number of respects: 1. The output header only contains WCS-related keywords. In particular, it does not contain syntactically-required keywords such as ``SIMPLE``, ``NAXIS``, ``BITPIX``, or ``END``. 2. Deprecated (e.g. ``CROTAn``) or non-standard usage will be translated to standard (this is partially dependent on whether ``fix`` was applied). 3. Quantities will be converted to the units used internally, basically SI with the addition of degrees. 4. Floating-point quantities may be given to a different decimal precision. 5. Elements of the ``PCi_j`` 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. 6. Additional keywords such as ``WCSAXES``, ``CUNITia``, ``LONPOLEa`` and ``LATPOLEa`` may appear. 7. The original keycomments will be lost, although `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" {_wcs.__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 {_wcs.__version__}).", AstropyWarning, ) else: header = fits.Header() 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=None): """ Identical to `to_header`, but returns a string containing the header cards. """ return str(self.to_header(relax)) 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 : str, optional Output file name - default is ``'footprint.reg'`` 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 """ 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. """ description = ["WCS Keywords\n", f"Number of WCS axes: {self.naxis!r}"] sfmt = " : " + "".join([f"{{{i}}} " for i in range(self.naxis)]) keywords = ["CTYPE", "CRVAL", "CRPIX"] values = [[repr(v) for v in self.wcs.ctype], 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))}") return "\n".join(description) def get_axis_types(self): """ Similar to `self.wcsprm.axis_types ` but provides the information in a more Python-friendly format. Returns ------- result : list of dict Returns a list of dictionaries, one for each axis, each containing attributes about the type of that axis. Each dictionary has the following keys: - 'coordinate_type': - None: Non-specific coordinate type. - 'stokes': Stokes coordinate. - 'celestial': Celestial coordinate (including ``CUBEFACE``). - 'spectral': Spectral coordinate. - 'scale': - 'linear': Linear axis. - 'quantized': Quantized axis (``STOKES``, ``CUBEFACE``). - 'non-linear celestial': Non-linear celestial axis. - 'non-linear spectral': Non-linear spectral axis. - 'logarithmic': Logarithmic axis. - 'tabular': Tabular axis. - 'group' - Group number, e.g. lookup table number - 'number' - For celestial axes: - 0: Longitude coordinate. - 1: Latitude coordinate. - 2: ``CUBEFACE`` number. - For lookup tables: - the axis number in a multidimensional table. ``CTYPEia`` in ``"4-3"`` form with unrecognized algorithm code will generate an error. """ if self.wcs is None: raise AttributeError("This WCS object does not have a wcsprm object.") 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", } result = [] for axis_type in self.wcs.axis_types: subresult = {} coordinate_type = (axis_type // 1000) % 10 subresult["coordinate_type"] = coordinate_type_map[coordinate_type] scale = (axis_type // 100) % 10 subresult["scale"] = scale_map[scale] group = (axis_type // 10) % 10 subresult["group"] = group number = axis_type % 10 subresult["number"] = number result.append(subresult) return result 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) dct = self.__dict__.copy() 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 {_wcs.__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 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) buffer = io.BytesIO(fits_data) hdulist = fits.open(buffer) 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=True, keysel=None, fix=True, translate_units="", _do_set=True ): """ Find all the WCS transformations in the given header. Parameters ---------- 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. - `int`: a bit field selecting specific extensions to accept. See :ref:`astropy:relaxread` for details. 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 function is called). To use binary table image array or pixel list keywords, *keysel* must be set. Each element in the list should be one of the following strings: - 'image': Image header keywords - 'binary': Binary table image array keywords - 'pixel': Pixel list keywords Keywords such as ``EQUIna`` or ``RFRQna`` that are common to binary table image arrays and pixel lists (including ``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` """ if isinstance(header, (str, bytes)): header_string = header elif isinstance(header, fits.Header): header_string = header.tostring() else: raise TypeError("header must be a string or astropy.io.fits.Header object") keysel_flags = _parse_keysel(keysel) if isinstance(header_string, str): header_bytes = header_string.encode("ascii") else: header_bytes = header_string wcsprms = _wcs.find_all_wcs(header_bytes, relax, keysel_flags) result = [] for wcsprm in wcsprms: 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=_wcs.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=_wcs.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 astropy-astropy-201cddb/astropy/wcs/wcsapi/000077500000000000000000000000001507226315300211625ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/wcs/wcsapi/__init__.py000066400000000000000000000002161507226315300232720ustar00rootroot00000000000000from .high_level_api import * from .high_level_wcs_wrapper import * from .low_level_api import * from .utils import * from .wrappers import * astropy-astropy-201cddb/astropy/wcs/wcsapi/conftest.py000066400000000000000000000104651507226315300233670ustar00rootroot00000000000000import 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() astropy-astropy-201cddb/astropy/wcs/wcsapi/data/000077500000000000000000000000001507226315300220735ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/wcs/wcsapi/data/ucds.txt000066400000000000000000000156101507226315300235750ustar00rootroot00000000000000# 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 astropy-astropy-201cddb/astropy/wcs/wcsapi/fitswcs.py000066400000000000000000001014251507226315300232210ustar00rootroot00000000000000# 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 from astropy import units as u from astropy.constants import c 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 = 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 * 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(), None)) 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 to degrees kwargs["unit"] = ( u.Unit(self.wcs.cunit[self.wcs.lng]), u.Unit(self.wcs.cunit[self.wcs.lat]), ) classes["celestial"] = (SkyCoord, (), kwargs) components[self.wcs.lng] = ("celestial", 0, "spherical.lon.degree") components[self.wcs.lat] = ("celestial", 1, "spherical.lat.degree") # 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. 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. if ( observer is None or spectralcoord.observer is None or spectralcoord.target is None ): if observer is None: msg = "No observer defined on WCS" elif spectralcoord.observer is None: msg = "No observer defined on SpectralCoord" else: msg = "No target defined on SpectralCoord" warnings.warn( f"{msg}, SpectralCoord " "will be converted without any velocity " "frame change", AstropyUserWarning, ) return spectralcoord.to_value(u.m) / self.wcs.restwav - 1.0 else: return ( spectralcoord.with_observer_stationary_relative_to( observer ).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) if ( observer is None or spectralcoord.observer is None or spectralcoord.target is None ): if observer is None: msg = "No observer defined on WCS" elif spectralcoord.observer is None: msg = "No observer defined on SpectralCoord" else: msg = "No target defined on SpectralCoord" warnings.warn( f"{msg}, SpectralCoord " "will be converted without any velocity " "frame change", AstropyUserWarning, ) return spectralcoord.to_value(u.m / u.s, doppler_equiv) / C_SI else: return ( spectralcoord.with_observer_stationary_relative_to( observer ).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. if ( observer is None or spectralcoord.observer is None or spectralcoord.target is None ): if observer is None: msg = "No observer defined on WCS" elif spectralcoord.observer is None: msg = "No observer defined on SpectralCoord" else: msg = "No target defined on SpectralCoord" warnings.warn( f"{msg}, SpectralCoord " "will be converted without any velocity " "frame change", AstropyUserWarning, ) return spectralcoord.to_value(**kwargs) else: return spectralcoord.with_observer_stationary_relative_to( observer ).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 astropy-astropy-201cddb/astropy/wcs/wcsapi/high_level_api.py000066400000000000000000000307511507226315300245010ustar00rootroot00000000000000import abc import numbers from collections import OrderedDict, defaultdict import numpy as np from .utils import deserialize_class __all__ = [ "BaseHighLevelWCS", "HighLevelWCSMixin", "high_level_objects_to_values", "values_to_high_level_objects", ] 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(_toindex(self.world_to_pixel(*world_objects)[::-1]).tolist()) def high_level_objects_to_values(*world_objects, low_level_wcs): """ Convert the input high level object to low level values. This function uses the information in ``wcs.world_axis_object_classes`` and ``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: object High level coordinate objects. low_level_wcs: `.BaseLowLevelWCS` The WCS object to use to interpret the coordinates. """ # 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 low_level_wcs.serialized_classes: 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: 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, low_level_wcs): """ Convert low level values into high level objects. This function uses the information in ``wcs.world_axis_object_classes`` and ``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: object Low level, "values" representations of the world coordinates. low_level_wcs: `.BaseLowLevelWCS` The WCS object to use to interpret the coordinates. """ # 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: 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 low_level_wcs.serialized_classes: 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): world_values = high_level_objects_to_values( *world_objects, 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) return pixel_values def pixel_to_world(self, *pixel_arrays): # Compute the world coordinate values world_values = self.low_level_wcs.pixel_to_world_values(*pixel_arrays) if self.low_level_wcs.world_n_dim == 1: world_values = (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 astropy-astropy-201cddb/astropy/wcs/wcsapi/high_level_wcs_wrapper.py000066400000000000000000000044341507226315300262630ustar00rootroot00000000000000from .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)}" astropy-astropy-201cddb/astropy/wcs/wcsapi/low_level_api.py000066400000000000000000000370761507226315300243720ustar00rootroot00000000000000import 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:'." ) astropy-astropy-201cddb/astropy/wcs/wcsapi/tests/000077500000000000000000000000001507226315300223245ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/wcs/wcsapi/tests/__init__.py000066400000000000000000000000001507226315300244230ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/wcs/wcsapi/tests/data/000077500000000000000000000000001507226315300232355ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/wcs/wcsapi/tests/data/example_4d_tab.fits000066400000000000000000000341001507226315300267720ustar00rootroot00000000000000SIMPLE = 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Ž{@<ĖĖĖĖĖÍastropy-astropy-201cddb/astropy/wcs/wcsapi/tests/test_fitswcs.py000066400000000000000000001412161507226315300254240ustar00rootroot00000000000000# 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 from itertools import product 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.wcs._wcs import __version__ as wcsver from astropy.wcs.wcs import WCS, FITSFixedWarning, NoConvergence, Sip from astropy.wcs.wcsapi.fitswcs import VELOCITY_FRAMES, custom_ctype_to_ucd_mapping ############################################################################### # 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) assert wcs.world_axis_object_components == [ ("celestial", 0, "spherical.lon.degree"), ("celestial", 1, "spherical.lat.degree"), ] 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]], ) assert len(wcs.world_axis_object_components) == 3 assert wcs.world_axis_object_components[0] == ( "celestial", 1, "spherical.lat.degree", ) assert wcs.world_axis_object_components[1][:2] == ("spectral", 0) assert wcs.world_axis_object_components[2] == ( "celestial", 0, "spherical.lon.degree", ) 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 with pytest.warns(AstropyUserWarning, match="No observer defined on WCS"): 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 with pytest.warns(AstropyUserWarning, match="No observer defined on WCS"): x, y, z = wcs.world_to_pixel(spec, coord) assert_allclose(x, 29.0) assert_allclose(y, 39.0) assert_allclose(z, 44.0) with pytest.warns(AstropyUserWarning, match="No observer defined on WCS"): 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 with pytest.warns(AstropyUserWarning, match="No observer defined on WCS"): 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. assert len(wcs.world_axis_object_components) == 3 assert wcs.world_axis_object_components[0] == ( "celestial", 1, "spherical.lat.degree", ) assert wcs.world_axis_object_components[1][:2] == ("spectral", 0) assert wcs.world_axis_object_components[2] == ( "celestial", 0, "spherical.lon.degree", ) 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 components[0] == ("celestial", 1, "spherical.lat.degree") assert components[1] == ("celestial", 0, "spherical.lon.degree") 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(wcsver) >= 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() assert wcs.world_axis_object_components == [ ("celestial", 0, "spherical.lon.degree"), ("celestial", 1, "spherical.lat.degree"), ] 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 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", "observer"), product(["ZOPT", "BETA", "VELO", "VRAD", "VOPT"], [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) if observer: pix = wcs.world_to_pixel(skycoord, spectralcoord) else: with pytest.warns(AstropyUserWarning, match="No observer defined on WCS"): 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): 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", "observer"), product(["ZOPT", "BETA", "VELO", "VRAD", "VOPT"], [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: expected_message = "No target defined on SpectralCoord" else: expected_message = "No observer defined on WCS" with pytest.warns(AstropyUserWarning, match=expected_message): 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: expected_message = "No observer defined on SpectralCoord" else: expected_message = "No observer defined on WCS" with pytest.warns(AstropyUserWarning, match=expected_message): 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) astropy-astropy-201cddb/astropy/wcs/wcsapi/tests/test_high_level_api.py000066400000000000000000000177311507226315300267050ustar00rootroot00000000000000import 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 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) astropy-astropy-201cddb/astropy/wcs/wcsapi/tests/test_high_level_wcs_wrapper.py000066400000000000000000000040721507226315300304620ustar00rootroot00000000000000import 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) astropy-astropy-201cddb/astropy/wcs/wcsapi/tests/test_low_level_api.py000066400000000000000000000013421507226315300265560ustar00rootroot00000000000000import 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"]) astropy-astropy-201cddb/astropy/wcs/wcsapi/tests/test_utils.py000066400000000000000000000027031507226315300250770ustar00rootroot00000000000000import 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() astropy-astropy-201cddb/astropy/wcs/wcsapi/utils.py000066400000000000000000000121501507226315300226730ustar00rootroot00000000000000# 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()]) astropy-astropy-201cddb/astropy/wcs/wcsapi/wrappers/000077500000000000000000000000001507226315300230255ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/wcs/wcsapi/wrappers/__init__.py000066400000000000000000000000731507226315300251360ustar00rootroot00000000000000from .base import BaseWCSWrapper from .sliced_wcs import * astropy-astropy-201cddb/astropy/wcs/wcsapi/wrappers/base.py000066400000000000000000000037361507226315300243220ustar00rootroot00000000000000import 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) astropy-astropy-201cddb/astropy/wcs/wcsapi/wrappers/sliced_wcs.py000066400000000000000000000272241507226315300255250ustar00rootroot00000000000000import 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] astropy-astropy-201cddb/astropy/wcs/wcsapi/wrappers/tests/000077500000000000000000000000001507226315300241675ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/wcs/wcsapi/wrappers/tests/__init__.py000066400000000000000000000000001507226315300262660ustar00rootroot00000000000000astropy-astropy-201cddb/astropy/wcs/wcsapi/wrappers/tests/test_sliced_wcs.py000066400000000000000000001034471507226315300277300ustar00rootroot00000000000000import 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.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]], ) assert len(wcs.world_axis_object_components) == 3 assert wcs.world_axis_object_components[0] == ( "celestial", 1, "spherical.lat.degree", ) assert wcs.world_axis_object_components[1][:2] == ("spectral", 0) assert wcs.world_axis_object_components[2] == ( "celestial", 0, "spherical.lon.degree", ) 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]]) assert wcs.world_axis_object_components == [ ("celestial", 1, "spherical.lat.degree"), ("celestial", 0, "spherical.lon.degree"), ] 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]], ) assert len(wcs.world_axis_object_components) == 3 assert wcs.world_axis_object_components[0] == ( "celestial", 1, "spherical.lat.degree", ) assert wcs.world_axis_object_components[1][:2] == ("spectral", 0) assert wcs.world_axis_object_components[2] == ( "celestial", 0, "spherical.lon.degree", ) 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]] ) assert len(wcs.world_axis_object_components) == 3 assert wcs.world_axis_object_components[0] == ( "celestial", 1, "spherical.lat.degree", ) assert wcs.world_axis_object_components[1][:2] == ("spectral", 0) assert wcs.world_axis_object_components[2] == ( "celestial", 0, "spherical.lon.degree", ) 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]], ) assert len(wcs.world_axis_object_components) == 3 assert wcs.world_axis_object_components[0] == ( "celestial", 1, "spherical.lat.degree", ) assert wcs.world_axis_object_components[1][:2] == ("spectral", 0) assert wcs.world_axis_object_components[2] == ( "celestial", 0, "spherical.lon.degree", ) 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]], ) assert len(wcs.world_axis_object_components) == 3 assert wcs.world_axis_object_components[0] == ( "celestial", 1, "spherical.lat.degree", ) assert wcs.world_axis_object_components[1][:2] == ("spectral", 0) assert wcs.world_axis_object_components[2] == ( "celestial", 0, "spherical.lon.degree", ) 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]], ) assert len(wcs.world_axis_object_components) == 3 assert wcs.world_axis_object_components[0] == ( "celestial", 1, "spherical.lat.degree", ) assert wcs.world_axis_object_components[1][:2] == ("spectral", 0) assert wcs.world_axis_object_components[2] == ( "celestial", 0, "spherical.lon.degree", ) 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]], ) assert wcs.world_axis_object_components == [ ("celestial", 1, "spherical.lat.degree"), ("world", 0, "value"), ("celestial", 0, "spherical.lon.degree"), ] 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") 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, "world_axis_object_components": [ ("celestial", 1, "spherical.lat.degree"), ("celestial", 0, "spherical.lon.degree"), ], }, ) 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") 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, "world_axis_object_components": [ ("celestial", 1, "spherical.lat.degree"), ("celestial", 0, "spherical.lon.degree"), ], }, ) 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 wao_components[0] == ("celestial", 0, "spherical.lon.degree") assert wao_components[1] == ("celestial", 1, "spherical.lat.degree") 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) astropy-astropy-201cddb/astropy/wcs/wcslint.py000066400000000000000000000010151507226315300217260ustar00rootroot00000000000000# 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" ) ) parser.add_argument("filename", nargs=1, help="Path to FITS file to check") args = parser.parse_args(args) print(wcs.validate(args.filename[0])) astropy-astropy-201cddb/asv.ci.conf.json000066400000000000000000000006001507226315300203750ustar00rootroot00000000000000 { "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" } astropy-astropy-201cddb/cextern/000077500000000000000000000000001507226315300170475ustar00rootroot00000000000000astropy-astropy-201cddb/cextern/.gitignore000066400000000000000000000000051507226315300210320ustar00rootroot00000000000000!*.c astropy-astropy-201cddb/cextern/README.rst000066400000000000000000000004701507226315300205370ustar00rootroot00000000000000External Packages/Libraries =========================== This directory contains C libraries included with Astropy. Note that only C 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. astropy-astropy-201cddb/cextern/cfitsio/000077500000000000000000000000001507226315300205075ustar00rootroot00000000000000astropy-astropy-201cddb/cextern/cfitsio/ChangeLog000066400000000000000000006615661507226315300223050ustar00rootroot00000000000000 Log of Changes Made to CFITSIO 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 astropy-astropy-201cddb/cextern/cfitsio/README.rst000066400000000000000000000003521507226315300221760ustar00rootroot00000000000000This 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. astropy-astropy-201cddb/cextern/cfitsio/lib/000077500000000000000000000000001507226315300212555ustar00rootroot00000000000000astropy-astropy-201cddb/cextern/cfitsio/lib/fits_hcompress.c000066400000000000000000001337521507226315300244640ustar00rootroot00000000000000/* ######################################################################### 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 http://www.stsci.edu/software/hcompress.html This source file is a concatination of the following sources files in the original distribution htrans.c digitize.c encode.c qwrite.c doencode.c bit_output.c qtree_encode.c The following modifications have been made to the original code: - commented out redundant "include" statements - added the noutchar global variable - changed all the 'extern' declarations to 'static', since all the routines are in the same source file - changed the first parameter in encode (and in lower level routines from a file stream to a char array - modifid the encode routine to return the size of the compressed array of bytes - changed calls to printf and perror to call the CFITSIO ffpmsg routine - modified the mywrite routine, and lower level byte writing routines, to copy the output bytes to a char array, instead of writing them to a file stream - replace "exit" statements with "return" statements - changed the function declarations to the more modern ANSI C style ############################################################################ */ #include #include #include #include #include "fitsio2.h" static long noutchar; static long noutmax; static int htrans(int a[],int nx,int ny); static void digitize(int a[], int nx, int ny, int scale); static int encode(char *outfile, long *nlen, int a[], int nx, int ny, int scale); static void shuffle(int a[], int n, int n2, int tmp[]); static int htrans64(LONGLONG a[],int nx,int ny); static void digitize64(LONGLONG a[], int nx, int ny, int scale); static int encode64(char *outfile, long *nlen, LONGLONG a[], int nx, int ny, int scale); static void shuffle64(LONGLONG a[], int n, int n2, LONGLONG tmp[]); static void writeint(char *outfile, int a); static void writelonglong(char *outfile, LONGLONG a); static int doencode(char *outfile, int a[], int nx, int ny, unsigned char nbitplanes[3]); static int doencode64(char *outfile, LONGLONG a[], int nx, int ny, unsigned char nbitplanes[3]); static int qwrite(char *file, char buffer[], int n); static int qtree_encode(char *outfile, int a[], int n, int nqx, int nqy, int nbitplanes); static int qtree_encode64(char *outfile, LONGLONG a[], int n, int nqx, int nqy, int nbitplanes); static void start_outputing_bits(void); static void done_outputing_bits(char *outfile); static void output_nbits(char *outfile, int bits, int n); static void qtree_onebit(int a[], int n, int nx, int ny, unsigned char b[], int bit); static void qtree_onebit64(LONGLONG a[], int n, int nx, int ny, unsigned char b[], int bit); static void qtree_reduce(unsigned char a[], int n, int nx, int ny, unsigned char b[]); static int bufcopy(unsigned char a[], int n, unsigned char buffer[], int *b, int bmax); 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) */ 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 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 a - input image array nx - size of X axis of image ny - size of Y axis of image scale - quantization scale factor. Larger values results in more (lossy) compression scale = 0 does lossless compression output - pre-allocated array to hold the output compressed stream of bytes nbyts - input value = size of the output buffer; returned value = size of the compressed byte stream, in bytes 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); /* H-transform */ stat = htrans(a, nx, ny); if (stat) { *status = stat; return(*status); } /* digitize */ digitize(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 = encode(output, nbytes, a, nx, ny, scale); FFUNLOCK; *status = stat; return(*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 a - input image array nx - size of X axis of image ny - size of Y axis of image scale - quantization scale factor. Larger values results in more (lossy) compression scale = 0 does lossless compression output - pre-allocated array to hold the output compressed stream of bytes nbyts - size of the compressed byte stream, in bytes 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); /* H-transform */ stat = htrans64(a, nx, ny); if (stat) { *status = stat; return(*status); } /* digitize */ 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); } /* Copyright (c) 1993 Association of Universities for Research * in Astronomy. All rights reserved. Produced under National * Aeronautics and Space Administration Contract No. NAS5-26555. */ /* htrans.c H-transform of NX x NY integer image * * Programmer: R. White Date: 11 May 1992 */ /* ######################################################################### */ static int htrans(int a[],int nx,int ny) { int nmax, log2n, h0, hx, hy, hc, nxtop, nytop, i, j, k; int oddx, oddy; int shift, mask, mask2, prnd, prnd2, nrnd2; 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<> shift; hx = (a[s10+1] + a[s10] - a[s00+1] - a[s00]) >> shift; hy = (a[s10+1] - a[s10] + a[s00+1] - a[s00]) >> shift; hc = (a[s10+1] - a[s10] - a[s00+1] + a[s00]) >> shift; /* * Throw away the 2 bottom bits of h0, bottom bit of hx,hy. * To get rounding to be same for positive and negative * numbers, nrnd2 = prnd2 - 1. */ a[s10+1] = hc; a[s10 ] = ( (hx>=0) ? (hx+prnd) : hx ) & mask ; a[s00+1] = ( (hy>=0) ? (hy+prnd) : hy ) & mask ; a[s00 ] = ( (h0>=0) ? (h0+prnd2) : (h0+nrnd2) ) & mask2; 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[s10] + a[s00]) << (1-shift); hx = (a[s10] - a[s00]) << (1-shift); a[s10 ] = ( (hx>=0) ? (hx+prnd) : hx ) & mask ; a[s00 ] = ( (h0>=0) ? (h0+prnd2) : (h0+nrnd2) ) & mask2; s00 += 1; s10 += 1; } } if (oddx) { /* * do last row if column length is odd * s10, s10+1 are off edge */ s00 = i*ny; for (j = 0; j=0) ? (hy+prnd) : hy ) & mask ; a[s00 ] = ( (h0>=0) ? (h0+prnd2) : (h0+nrnd2) ) & mask2; 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] << (2-shift); a[s00 ] = ( (h0>=0) ? (h0+prnd2) : (h0+nrnd2) ) & mask2; } } /* * now shuffle in each dimension to group coefficients by order */ for (i = 0; i>1; nytop = (nytop+1)>>1; /* * divisor doubles after first reduction */ shift = 1; /* * masks, rounding values double after each iteration */ mask = mask2; prnd = prnd2; mask2 = mask2 << 1; prnd2 = prnd2 << 1; nrnd2 = prnd2 - 1; } free(tmp); return(0); } /* ######################################################################### */ static int htrans64(LONGLONG a[],int nx,int ny) { int nmax, log2n, nxtop, nytop, i, j, k; int oddx, oddy; int shift; int s10, s00; LONGLONG h0, hx, hy, hc, prnd, prnd2, nrnd2, mask, mask2; 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<> shift; hx = (a[s10+1] + a[s10] - a[s00+1] - a[s00]) >> shift; hy = (a[s10+1] - a[s10] + a[s00+1] - a[s00]) >> shift; hc = (a[s10+1] - a[s10] - a[s00+1] + a[s00]) >> shift; /* * Throw away the 2 bottom bits of h0, bottom bit of hx,hy. * To get rounding to be same for positive and negative * numbers, nrnd2 = prnd2 - 1. */ a[s10+1] = hc; a[s10 ] = ( (hx>=0) ? (hx+prnd) : hx ) & mask ; a[s00+1] = ( (hy>=0) ? (hy+prnd) : hy ) & mask ; a[s00 ] = ( (h0>=0) ? (h0+prnd2) : (h0+nrnd2) ) & mask2; 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[s10] + a[s00]) << (1-shift); hx = (a[s10] - a[s00]) << (1-shift); a[s10 ] = ( (hx>=0) ? (hx+prnd) : hx ) & mask ; a[s00 ] = ( (h0>=0) ? (h0+prnd2) : (h0+nrnd2) ) & mask2; s00 += 1; s10 += 1; } } if (oddx) { /* * do last row if column length is odd * s10, s10+1 are off edge */ s00 = i*ny; for (j = 0; j=0) ? (hy+prnd) : hy ) & mask ; a[s00 ] = ( (h0>=0) ? (h0+prnd2) : (h0+nrnd2) ) & mask2; 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] << (2-shift); a[s00 ] = ( (h0>=0) ? (h0+prnd2) : (h0+nrnd2) ) & mask2; } } /* * now shuffle in each dimension to group coefficients by order */ for (i = 0; i>1; nytop = (nytop+1)>>1; /* * divisor doubles after first reduction */ shift = 1; /* * masks, rounding values double after each iteration */ mask = mask2; prnd = prnd2; mask2 = mask2 << 1; prnd2 = prnd2 << 1; nrnd2 = prnd2 - 1; } free(tmp); return(0); } /* ######################################################################### */ static void shuffle(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 *p1, *p2, *pt; /* * copy odd elements to tmp */ pt = tmp; p1 = &a[n2]; for (i=1; i < n; i += 2) { *pt = *p1; pt += 1; p1 += (n2+n2); } /* * compress even elements into first half of A */ p1 = &a[n2]; p2 = &a[n2+n2]; for (i=2; i0) ? (*p+d) : (*p-d))/scale; } /* ######################################################################### */ static void digitize64(LONGLONG a[], int nx, int ny, int scale) { LONGLONG d, *p, scale64; /* * round to multiple of scale */ if (scale <= 1) return; d=(scale+1)/2-1; scale64 = scale; /* use a 64-bit int for efficiency in the big loop */ for (p=a; p <= &a[nx*ny-1]; p++) *p = ((*p>0) ? (*p+d) : (*p-d))/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. */ /* encode.c encode H-transform and write to outfile * * Programmer: R. White Date: 2 February 1994 */ static char code_magic[2] = { (char)0xDD, (char)0x99 }; /* ######################################################################### */ static int encode(char *outfile, long *nlength, int a[], int nx, int ny, int scale) { /* FILE *outfile; - change outfile to a char array */ /* long * nlength returned length (in bytes) of the encoded array) int a[]; input H-transform array (nx,ny) int nx,ny; size of H-transform array int scale; scale factor for digitization */ int nel, nx2, ny2, i, j, k, q, vmax[3], nsign, bits_to_go; unsigned char nbitplanes[3]; unsigned char *signbits; int stat; noutchar = 0; /* initialize the number of compressed bytes that have been written */ nel = nx*ny; /* * write magic value */ qwrite(outfile, code_magic, sizeof(code_magic)); writeint(outfile, nx); /* size of image */ writeint(outfile, ny); writeint(outfile, scale); /* scale factor for digitization */ /* * write first value of A (sum of all pixels -- the only value * which does not compress well) */ writelonglong(outfile, (LONGLONG) a[0]); a[0] = 0; /* * allocate array for sign bits and save values, 8 per byte (initialize to all zeros) */ signbits = (unsigned char *) calloc(1, (nel+7)/8); if (signbits == (unsigned char *) NULL) { ffpmsg("encode: insufficient memory"); return(DATA_COMPRESSION_ERR); } nsign = 0; bits_to_go = 8; /* signbits[0] = 0; */ for (i=0; i 0) { /* * positive element, put zero at end of buffer */ signbits[nsign] <<= 1; bits_to_go -= 1; } else if (a[i] < 0) { /* * negative element, shift in a one */ signbits[nsign] <<= 1; signbits[nsign] |= 1; bits_to_go -= 1; /* * replace a by absolute value */ a[i] = -a[i]; } if (bits_to_go == 0) { /* * filled up this byte, go to the next one */ bits_to_go = 8; nsign += 1; /* signbits[nsign] = 0; */ } } if (bits_to_go != 8) { /* * some bits in last element * move bits in last byte to bottom and increment nsign */ signbits[nsign] <<= bits_to_go; nsign += 1; } /* * calculate number of bit planes for 3 quadrants * * quadrant 0=bottom left, 1=bottom right or top left, 2=top right, */ for (q=0; q<3; q++) { vmax[q] = 0; } /* * get maximum absolute value in each quadrant */ 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; } } /* * now calculate number of bits for each quadrant */ /* 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 (q = 0; q < 3; q++) { nbitplanes[q] = (int) (log((float) (vmax[q]+1))/log(2.0)+0.5); if ( (vmax[q]+1) > (1< 0) { if ( 0 == qwrite(outfile, (char *) signbits, nsign)) { free(signbits); *nlength = noutchar; ffpmsg("encode: output buffer too small"); return(DATA_COMPRESSION_ERR); } } free(signbits); *nlength = noutchar; if (noutchar >= noutmax) { ffpmsg("encode: output buffer too small"); return(DATA_COMPRESSION_ERR); } return(stat); } /* ######################################################################### */ static int encode64(char *outfile, long *nlength, LONGLONG a[], int nx, int ny, int scale) { /* FILE *outfile; - change outfile to a char array */ /* long * nlength returned length (in bytes) of the encoded array) LONGLONG a[]; input H-transform array (nx,ny) int nx,ny; size of H-transform array int scale; scale factor for digitization */ int nel, nx2, ny2, i, j, k, q, nsign, bits_to_go; LONGLONG vmax[3]; unsigned char nbitplanes[3]; unsigned char *signbits; int stat; noutchar = 0; /* initialize the number of compressed bytes that have been written */ nel = nx*ny; /* * write magic value */ qwrite(outfile, code_magic, sizeof(code_magic)); writeint(outfile, nx); /* size of image */ writeint(outfile, ny); writeint(outfile, scale); /* scale factor for digitization */ /* * write first value of A (sum of all pixels -- the only value * which does not compress well) */ writelonglong(outfile, a[0]); a[0] = 0; /* * allocate array for sign bits and save values, 8 per byte */ signbits = (unsigned char *) calloc(1, (nel+7)/8); if (signbits == (unsigned char *) NULL) { ffpmsg("encode64: insufficient memory"); return(DATA_COMPRESSION_ERR); } nsign = 0; bits_to_go = 8; /* signbits[0] = 0; */ for (i=0; i 0) { /* * positive element, put zero at end of buffer */ signbits[nsign] <<= 1; bits_to_go -= 1; } else if (a[i] < 0) { /* * negative element, shift in a one */ signbits[nsign] <<= 1; signbits[nsign] |= 1; bits_to_go -= 1; /* * replace a by absolute value */ a[i] = -a[i]; } if (bits_to_go == 0) { /* * filled up this byte, go to the next one */ bits_to_go = 8; nsign += 1; /* signbits[nsign] = 0; */ } } if (bits_to_go != 8) { /* * some bits in last element * move bits in last byte to bottom and increment nsign */ signbits[nsign] <<= bits_to_go; nsign += 1; } /* * calculate number of bit planes for 3 quadrants * * quadrant 0=bottom left, 1=bottom right or top left, 2=top right, */ for (q=0; q<3; q++) { vmax[q] = 0; } /* * get maximum absolute value in each quadrant */ 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; } } /* * now calculate number of bits for each quadrant */ /* 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 (q = 0; q < 3; q++) { nbitplanes[q] = log((float) (vmax[q]+1))/log(2.0)+0.5; if ( (vmax[q]+1) > (((LONGLONG) 1)< 0) { if ( 0 == qwrite(outfile, (char *) signbits, nsign)) { free(signbits); *nlength = noutchar; ffpmsg("encode: output buffer too small"); return(DATA_COMPRESSION_ERR); } } free(signbits); *nlength = noutchar; if (noutchar >= noutmax) { ffpmsg("encode64: output buffer too small"); return(DATA_COMPRESSION_ERR); } 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. */ /* qwrite.c Write binary data * * Programmer: R. White Date: 11 March 1991 */ /* ######################################################################### */ static void writeint(char *outfile, int a) { int i; unsigned char b[4]; /* Write integer A one byte at a time to outfile. * * This is portable from Vax to Sun since it eliminates the * need for byte-swapping. */ for (i=3; i>=0; i--) { b[i] = a & 0x000000ff; a >>= 8; } for (i=0; i<4; i++) qwrite(outfile, (char *) &b[i],1); } /* ######################################################################### */ static void writelonglong(char *outfile, LONGLONG a) { int i; unsigned char b[8]; /* Write integer A one byte at a time to outfile. * * This is portable from Vax to Sun since it eliminates the * need for byte-swapping. */ for (i=7; i>=0; i--) { b[i] = (unsigned char) (a & 0x000000ff); a >>= 8; } for (i=0; i<8; i++) qwrite(outfile, (char *) &b[i],1); } /* ######################################################################### */ static int 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 */ if (noutchar + n > noutmax) return(0); /* buffer overflow */ memcpy(&file[noutchar], buffer, n); noutchar += n; return(n); } /* ######################################################################### */ /* ######################################################################### */ /* Copyright (c) 1993 Association of Universities for Research * in Astronomy. All rights reserved. Produced under National * Aeronautics and Space Administration Contract No. NAS5-26555. */ /* doencode.c Encode 2-D array and write stream of characters on outfile * * This version assumes that A is positive. * * Programmer: R. White Date: 7 May 1991 */ /* ######################################################################### */ static int doencode(char *outfile, int a[], int nx, int ny, unsigned char nbitplanes[3]) { /* char *outfile; output data stream int a[]; Array of values to encode int nx,ny; Array dimensions [nx][ny] unsigned char nbitplanes[3]; Number of bit planes in quadrants */ int nx2, ny2, stat; nx2 = (nx+1)/2; ny2 = (ny+1)/2; /* * Initialize bit output */ start_outputing_bits(); /* * write out the bit planes for each quadrant */ stat = qtree_encode(outfile, &a[0], ny, nx2, ny2, nbitplanes[0]); if (!stat) stat = qtree_encode(outfile, &a[ny2], ny, nx2, ny/2, nbitplanes[1]); if (!stat) stat = qtree_encode(outfile, &a[ny*nx2], ny, nx/2, ny2, nbitplanes[1]); if (!stat) stat = qtree_encode(outfile, &a[ny*nx2+ny2], ny, nx/2, ny/2, nbitplanes[2]); /* * Add zero as an EOF symbol */ output_nybble(outfile, 0); done_outputing_bits(outfile); return(stat); } /* ######################################################################### */ static int doencode64(char *outfile, LONGLONG a[], int nx, int ny, unsigned char nbitplanes[3]) { /* char *outfile; output data stream LONGLONG a[]; Array of values to encode int nx,ny; Array dimensions [nx][ny] unsigned char nbitplanes[3]; Number of bit planes in quadrants */ int nx2, ny2, stat; nx2 = (nx+1)/2; ny2 = (ny+1)/2; /* * Initialize bit output */ start_outputing_bits(); /* * write out the bit planes for each quadrant */ stat = qtree_encode64(outfile, &a[0], ny, nx2, ny2, nbitplanes[0]); if (!stat) stat = qtree_encode64(outfile, &a[ny2], ny, nx2, ny/2, nbitplanes[1]); if (!stat) stat = qtree_encode64(outfile, &a[ny*nx2], ny, nx/2, ny2, nbitplanes[1]); if (!stat) stat = qtree_encode64(outfile, &a[ny*nx2+ny2], ny, nx/2, ny/2, nbitplanes[2]); /* * Add zero as an EOF symbol */ output_nybble(outfile, 0); done_outputing_bits(outfile); 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. */ /* BIT OUTPUT ROUTINES */ static LONGLONG bitcount; /* THE BIT BUFFER */ static int buffer2; /* Bits buffered for output */ static int bits_to_go2; /* Number of bits free in buffer */ /* ######################################################################### */ /* INITIALIZE FOR BIT OUTPUT */ static void start_outputing_bits(void) { buffer2 = 0; /* Buffer is empty to start */ bits_to_go2 = 8; /* with */ bitcount = 0; } /* ######################################################################### */ /* OUTPUT N BITS (N must be <= 8) */ static void output_nbits(char *outfile, int bits, int n) { /* AND mask for the right-most n bits */ static int mask[9] = {0, 1, 3, 7, 15, 31, 63, 127, 255}; /* * insert bits at end of buffer */ buffer2 <<= n; /* buffer2 |= ( bits & ((1<>(-bits_to_go2)) & 0xff); if (noutchar < noutmax) noutchar++; bits_to_go2 += 8; } bitcount += n; } /* ######################################################################### */ /* OUTPUT a 4 bit nybble */ static void output_nybble(char *outfile, int bits) { /* * 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 */ outfile[noutchar] = ((buffer2>>(-bits_to_go2)) & 0xff); 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 */ int ii, jj, kk = 0, shift; 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; } } /* 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++; } } bitcount += (8 * (ii - 1)); /* write out last odd nybble, if present */ if (kk != n) output_nybble(outfile, (int) array[n - 1]); return; } /* ######################################################################### */ /* FLUSH OUT THE LAST BITS */ static void done_outputing_bits(char *outfile) { if(bits_to_go2 < 8) { /* putc(buffer2<nqy) ? nqx : nqy; log2n = (int) (log((float) nqmax)/log(2.0)+0.5); if (nqmax > (1<= 0; bit--) { /* * initial bit buffer */ b = 0; bitbuffer = 0; bits_to_go3 = 0; /* * on first pass copy A to scratch array */ qtree_onebit(a,n,nqx,nqy,scratch,bit); nx = (nqx+1)>>1; ny = (nqy+1)>>1; /* * copy non-zero values to output buffer, which will be written * in reverse order */ if (bufcopy(scratch,nx*ny,buffer,&b,bmax)) { /* * quadtree is expanding data, * change warning code and just fill buffer with bit-map */ write_bdirect(outfile,a,n,nqx,nqy,scratch,bit); goto bitplane_done; } /* * do log2n reductions */ for (k = 1; k>1; ny = (ny+1)>>1; if (bufcopy(scratch,nx*ny,buffer,&b,bmax)) { write_bdirect(outfile,a,n,nqx,nqy,scratch,bit); goto bitplane_done; } } /* * OK, we've got the code in buffer * Write quadtree warning code, then write buffer in reverse order */ output_nybble(outfile,0xF); if (b==0) { if (bits_to_go3>0) { /* * put out the last few bits */ output_nbits(outfile, bitbuffer & ((1<0) { /* * put out the last few bits */ output_nbits(outfile, bitbuffer & ((1<=0; i--) { output_nbits(outfile,buffer[i],8); } } bitplane_done: ; } free(buffer); free(scratch); return(0); } /* ######################################################################### */ static int qtree_encode64(char *outfile, LONGLONG a[], int n, int nqx, int nqy, int nbitplanes) { /* LONGLONG a[]; int n; physical dimension of row in a int nqx; length of row int nqy; length of column (<=n) int nbitplanes; number of bit planes to output */ int log2n, i, k, bit, b, nqmax, nqx2, nqy2, nx, ny; int bmax; /* this potentially needs to be made a 64-bit int to support large arrays */ unsigned char *scratch, *buffer; /* * 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--) { /* * initial bit buffer */ b = 0; bitbuffer = 0; bits_to_go3 = 0; /* * on first pass copy A to scratch array */ qtree_onebit64(a,n,nqx,nqy,scratch,bit); nx = (nqx+1)>>1; ny = (nqy+1)>>1; /* * copy non-zero values to output buffer, which will be written * in reverse order */ if (bufcopy(scratch,nx*ny,buffer,&b,bmax)) { /* * quadtree is expanding data, * change warning code and just fill buffer with bit-map */ write_bdirect64(outfile,a,n,nqx,nqy,scratch,bit); goto bitplane_done; } /* * do log2n reductions */ for (k = 1; k>1; ny = (ny+1)>>1; if (bufcopy(scratch,nx*ny,buffer,&b,bmax)) { write_bdirect64(outfile,a,n,nqx,nqy,scratch,bit); goto bitplane_done; } } /* * OK, we've got the code in buffer * Write quadtree warning code, then write buffer in reverse order */ output_nybble(outfile,0xF); if (b==0) { if (bits_to_go3>0) { /* * put out the last few bits */ output_nbits(outfile, bitbuffer & ((1<0) { /* * put out the last few bits */ output_nbits(outfile, bitbuffer & ((1<=0; i--) { output_nbits(outfile,buffer[i],8); } } bitplane_done: ; } free(buffer); free(scratch); return(0); } /* ######################################################################### */ /* * copy non-zero codes from array to buffer */ static int bufcopy(unsigned char a[], int n, unsigned char buffer[], int *b, int bmax) { int i; for (i = 0; i < n; i++) { if (a[i] != 0) { /* * add Huffman code for a[i] to buffer */ bitbuffer |= code[a[i]] << bits_to_go3; bits_to_go3 += ncode[a[i]]; if (bits_to_go3 >= 8) { buffer[*b] = bitbuffer & 0xFF; *b += 1; /* * return warning code if we fill buffer */ if (*b >= bmax) return(1); bitbuffer >>= 8; bits_to_go3 -= 8; } } } return(0); } /* ######################################################################### */ /* * Do first quadtree reduction step on bit BIT of array A. * Results put into B. * */ static void qtree_onebit(int a[], int n, int nx, int ny, unsigned char b[], int bit) { int i, j, k; int b0, b1, b2, b3; int s10, s00; /* * use selected bit to get amount to shift */ b0 = 1<> bit; k += 1; s00 += 2; s10 += 2; } if (j < ny) { /* * row size is odd, do last element in row * s00+1,s10+1 are off edge */ b[k] = ( ((a[s10 ]<<1) & b1) | ((a[s00 ]<<3) & b3) ) >> 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> bit; k += 1; s00 += 2; } if (j < ny) { /* * both row and column size are odd, do corner element * s00+1, s10, s10+1 are off edge */ b[k] = ( ((a[s00 ]<<3) & b3) ) >> bit; k += 1; } } } /* ######################################################################### */ /* * Do first quadtree reduction step on bit BIT of array A. * Results put into B. * */ static void qtree_onebit64(LONGLONG a[], int n, int nx, int ny, unsigned char b[], int bit) { int i, j, k; LONGLONG b0, b1, b2, b3; int s10, s00; /* * use selected bit to get amount to shift */ b0 = ((LONGLONG) 1)<> bit); k += 1; s00 += 2; s10 += 2; } if (j < ny) { /* * row size is odd, do last element in row * s00+1,s10+1 are off edge */ b[k] = (unsigned char) (( ((a[s10 ]<<1) & b1) | ((a[s00 ]<<3) & b3) ) >> 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> bit); k += 1; s00 += 2; } if (j < ny) { /* * both row and column size are odd, do corner element * s00+1, s10, s10+1 are off edge */ b[k] = (unsigned char) (( ((a[s00 ]<<3) & b3) ) >> bit); k += 1; } } } /* ######################################################################### */ /* * do one quadtree reduction step on array a * results put into b (which may be the same as a) */ static void qtree_reduce(unsigned char a[], int n, int nx, int ny, unsigned char b[]) { int i, j, k; int s10, s00; k = 0; /* k is index of b[i/2,j/2] */ for (i = 0; i #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 *nx, int *ny, int *scale); static int decode64(unsigned char *infile, LONGLONG *a, 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 *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, 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 *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, 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 *nx, int *ny, int *scale) /* char *infile; input file int *a; address of output array [nx][ny] int *nx,*ny; size of output array 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 */ /* 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 *nx, int *ny, int *scale) /* char *infile; input file LONGLONG *a; address of output array [nx][ny] int *nx,*ny; size of output array 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 */ /* 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) { 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) { 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 ); } astropy-astropy-201cddb/cextern/cfitsio/lib/fitsio2.h000066400000000000000000000006551507226315300230130ustar00rootroot00000000000000#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); astropy-astropy-201cddb/cextern/cfitsio/lib/pliocomp.c000066400000000000000000000156331507226315300232530ustar00rootroot00000000000000/* 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_ */ astropy-astropy-201cddb/cextern/cfitsio/lib/quantize.c000066400000000000000000003503111507226315300232640ustar00rootroot00000000000000/* 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 astropy-astropy-201cddb/cextern/cfitsio/lib/ricecomp.c000066400000000000000000001056171507226315300232340ustar00rootroot00000000000000/* 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. */ /*----------------------------------------------------------*/ /* */ /* START OF SOURCE FILE ORIGINALLY CALLED rcomp.c */ /* */ /*----------------------------------------------------------*/ /* @(#) rcomp.c 1.5 99/03/01 12:40:27 */ /* rcomp.c Compress image line using * (1) Difference of adjacent pixels * (2) Rice algorithm coding * * Returns number of bytes written to code buffer or * -1 on failure */ #include #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 */ Buffer_t *current; /* current position in buffer */ Buffer_t *end; /* end of buffer */ } Buffer; #define putcbuf(c,mf) ((*(mf->current)++ = c), 0) #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 fits_rcomp(int a[], /* input array */ int nx, /* number of input pixels */ unsigned char *c, /* output buffer */ int clen, /* max length of output */ int nblock) /* coding block size */ { Buffer bufmem, *buffer = &bufmem; /* int bsize; */ int i, j, thisblock; int lastpix, nextpix, pdiff; int v, fs, fsmask, top, fsmax, fsbits, bbits; int lbitbuffer, lbits_to_go; unsigned int psum; double pixelsum, dpsum; unsigned int *diff; /* * Original size of each pixel (bsize, bytes) and coding block * size (nblock, pixels) * Could make bsize a parameter to allow more efficient * compression of short & byte images. */ /* bsize = 4; */ /* nblock = 32; now an input parameter*/ /* * From bsize derive: * FSBITS = # bits required to store FS * FSMAX = maximum value for FS * BBITS = bits/pixel for direct coding */ /* switch (bsize) { case 1: fsbits = 3; fsmax = 6; break; case 2: fsbits = 4; fsmax = 14; break; case 4: fsbits = 5; fsmax = 25; break; default: ffpmsg("rdecomp: bsize must be 1, 2, or 4 bytes"); return(-1); } */ /* move out of switch block, to tweak performance */ fsbits = 5; fsmax = 25; bbits = 1<start = c; buffer->current = c; buffer->end = c+clen; buffer->bits_to_go = 8; /* * array for differences mapped to non-negative values */ diff = (unsigned int *) malloc(nblock*sizeof(unsigned int)); if (diff == (unsigned int *) NULL) { ffpmsg("fits_rcomp: insufficient memory"); return(-1); } /* * Code in blocks of nblock pixels */ start_outputing_bits(buffer); /* write out first int value to the first 4 bytes of the buffer */ if (output_nbits(buffer, a[0], 32) == EOF) { ffpmsg("rice_encode: end of buffer"); free(diff); return(-1); } lastpix = a[0]; /* the first difference will always be zero */ thisblock = nblock; for (i=0; i> 1; for (fs = 0; psum>0; fs++) psum >>= 1; /* * write the codes * fsbits ID bits used to indicate split level */ if (fs >= fsmax) { /* 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) { ffpmsg("rice_encode: end of buffer"); free(diff); return(-1); } for (j=0; jbitbuffer; lbits_to_go = buffer->bits_to_go; for (j=0; j> fs; /* * top is coded by top zeros + 1 */ if (lbits_to_go >= top+1) { lbitbuffer <<= top+1; lbitbuffer |= 1; lbits_to_go -= top+1; } else { lbitbuffer <<= lbits_to_go; putcbuf(lbitbuffer & 0xff,buffer); for (top -= lbits_to_go; top>=8; top -= 8) { putcbuf(0, buffer); } lbitbuffer = 1; lbits_to_go = 7-top; } /* * bottom FS bits are written without coding * code is output_nbits, moved into this routine to reduce overheads * This code potentially breaks if FS>24, so I am limiting * FS to 24 by choice of FSMAX above. */ if (fs > 0) { lbitbuffer <<= fs; lbitbuffer |= v & fsmask; lbits_to_go -= fs; while (lbits_to_go <= 0) { putcbuf((lbitbuffer>>(-lbits_to_go)) & 0xff,buffer); lbits_to_go += 8; } } } /* check if overflowed output buffer */ if (buffer->current > buffer->end) { ffpmsg("rice_encode: end of buffer"); free(diff); return(-1); } buffer->bitbuffer = lbitbuffer; buffer->bits_to_go = lbits_to_go; } } done_outputing_bits(buffer); free(diff); /* * return number of bytes used */ return(buffer->current - buffer->start); } /*---------------------------------------------------------------------------*/ int fits_rcomp_short( short a[], /* input array */ int nx, /* number of input pixels */ unsigned char *c, /* output buffer */ int clen, /* max length of output */ int nblock) /* coding block size */ { Buffer bufmem, *buffer = &bufmem; /* int bsize; */ int i, j, thisblock; /* NOTE: in principle, the following 2 variable could be declared as 'short' but in fact the code runs faster (on 32-bit Linux at least) as 'int' */ int lastpix, nextpix; /* int pdiff; */ short pdiff; int v, fs, fsmask, top, fsmax, fsbits, bbits; int lbitbuffer, lbits_to_go; /* unsigned int psum; */ unsigned short psum; double pixelsum, dpsum; unsigned int *diff; /* * Original size of each pixel (bsize, bytes) and coding block * size (nblock, pixels) * Could make bsize a parameter to allow more efficient * compression of short & byte images. */ /* bsize = 2; */ /* nblock = 32; now an input parameter */ /* * From bsize derive: * FSBITS = # bits required to store FS * FSMAX = maximum value for FS * BBITS = bits/pixel for direct coding */ /* switch (bsize) { case 1: fsbits = 3; fsmax = 6; break; case 2: fsbits = 4; fsmax = 14; break; case 4: fsbits = 5; fsmax = 25; break; default: ffpmsg("rdecomp: bsize must be 1, 2, or 4 bytes"); return(-1); } */ /* move these out of switch block to further tweak performance */ fsbits = 4; fsmax = 14; bbits = 1<start = c; buffer->current = c; buffer->end = c+clen; buffer->bits_to_go = 8; /* * array for differences mapped to non-negative values */ diff = (unsigned int *) malloc(nblock*sizeof(unsigned int)); if (diff == (unsigned int *) NULL) { ffpmsg("fits_rcomp: insufficient memory"); return(-1); } /* * Code in blocks of nblock pixels */ start_outputing_bits(buffer); /* write out first short value to the first 2 bytes of the buffer */ if (output_nbits(buffer, a[0], 16) == EOF) { ffpmsg("rice_encode: end of buffer"); free(diff); return(-1); } lastpix = a[0]; /* the first difference will always be zero */ thisblock = nblock; for (i=0; i> 1; */ psum = ((unsigned short) dpsum ) >> 1; for (fs = 0; psum>0; fs++) psum >>= 1; /* * write the codes * 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) { ffpmsg("rice_encode: end of buffer"); free(diff); return(-1); } for (j=0; jbitbuffer; lbits_to_go = buffer->bits_to_go; for (j=0; j> fs; /* * top is coded by top zeros + 1 */ if (lbits_to_go >= top+1) { lbitbuffer <<= top+1; lbitbuffer |= 1; lbits_to_go -= top+1; } else { lbitbuffer <<= lbits_to_go; putcbuf(lbitbuffer & 0xff,buffer); for (top -= lbits_to_go; top>=8; top -= 8) { putcbuf(0, buffer); } lbitbuffer = 1; lbits_to_go = 7-top; } /* * bottom FS bits are written without coding * code is output_nbits, moved into this routine to reduce overheads * This code potentially breaks if FS>24, so I am limiting * FS to 24 by choice of FSMAX above. */ if (fs > 0) { lbitbuffer <<= fs; lbitbuffer |= v & fsmask; lbits_to_go -= fs; while (lbits_to_go <= 0) { putcbuf((lbitbuffer>>(-lbits_to_go)) & 0xff,buffer); lbits_to_go += 8; } } } /* check if overflowed output buffer */ if (buffer->current > buffer->end) { ffpmsg("rice_encode: end of buffer"); free(diff); return(-1); } buffer->bitbuffer = lbitbuffer; buffer->bits_to_go = lbits_to_go; } } done_outputing_bits(buffer); free(diff); /* * return number of bytes used */ return(buffer->current - buffer->start); } /*---------------------------------------------------------------------------*/ int fits_rcomp_byte( signed char a[], /* input array */ int nx, /* number of input pixels */ unsigned char *c, /* output buffer */ int clen, /* max length of output */ int nblock) /* coding block size */ { Buffer bufmem, *buffer = &bufmem; /* int bsize; */ int i, j, thisblock; /* NOTE: in principle, the following 2 variable could be declared as 'short' but in fact the code runs faster (on 32-bit Linux at least) as 'int' */ int lastpix, nextpix; /* int pdiff; */ signed char pdiff; int v, fs, fsmask, top, fsmax, fsbits, bbits; int lbitbuffer, lbits_to_go; /* unsigned int psum; */ unsigned char psum; double pixelsum, dpsum; unsigned int *diff; /* * Original size of each pixel (bsize, bytes) and coding block * size (nblock, pixels) * Could make bsize a parameter to allow more efficient * compression of short & byte images. */ /* bsize = 1; */ /* nblock = 32; now an input parameter */ /* * From bsize derive: * FSBITS = # bits required to store FS * FSMAX = maximum value for FS * BBITS = bits/pixel for direct coding */ /* switch (bsize) { case 1: fsbits = 3; fsmax = 6; break; case 2: fsbits = 4; fsmax = 14; break; case 4: fsbits = 5; fsmax = 25; break; default: ffpmsg("rdecomp: bsize must be 1, 2, or 4 bytes"); return(-1); } */ /* move these out of switch block to further tweak performance */ fsbits = 3; fsmax = 6; bbits = 1<start = c; buffer->current = c; buffer->end = c+clen; buffer->bits_to_go = 8; /* * array for differences mapped to non-negative values */ diff = (unsigned int *) malloc(nblock*sizeof(unsigned int)); if (diff == (unsigned int *) NULL) { ffpmsg("fits_rcomp: insufficient memory"); return(-1); } /* * Code in blocks of nblock pixels */ start_outputing_bits(buffer); /* write out first byte value to the first byte of the buffer */ if (output_nbits(buffer, a[0], 8) == EOF) { ffpmsg("rice_encode: end of buffer"); free(diff); return(-1); } lastpix = a[0]; /* the first difference will always be zero */ thisblock = nblock; for (i=0; i> 1; */ psum = ((unsigned char) dpsum ) >> 1; for (fs = 0; psum>0; fs++) psum >>= 1; /* * write the codes * fsbits ID bits used to indicate split level */ if (fs >= fsmax) { /* 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) { ffpmsg("rice_encode: end of buffer"); free(diff); return(-1); } for (j=0; jbitbuffer; lbits_to_go = buffer->bits_to_go; for (j=0; j> fs; /* * top is coded by top zeros + 1 */ if (lbits_to_go >= top+1) { lbitbuffer <<= top+1; lbitbuffer |= 1; lbits_to_go -= top+1; } else { lbitbuffer <<= lbits_to_go; putcbuf(lbitbuffer & 0xff,buffer); for (top -= lbits_to_go; top>=8; top -= 8) { putcbuf(0, buffer); } lbitbuffer = 1; lbits_to_go = 7-top; } /* * bottom FS bits are written without coding * code is output_nbits, moved into this routine to reduce overheads * This code potentially breaks if FS>24, so I am limiting * FS to 24 by choice of FSMAX above. */ if (fs > 0) { lbitbuffer <<= fs; lbitbuffer |= v & fsmask; lbits_to_go -= fs; while (lbits_to_go <= 0) { putcbuf((lbitbuffer>>(-lbits_to_go)) & 0xff,buffer); lbits_to_go += 8; } } } /* check if overflowed output buffer */ if (buffer->current > buffer->end) { ffpmsg("rice_encode: end of buffer"); free(diff); return(-1); } buffer->bitbuffer = lbitbuffer; buffer->bits_to_go = lbits_to_go; } } done_outputing_bits(buffer); free(diff); /* * return number of bytes used */ return(buffer->current - buffer->start); } /*---------------------------------------------------------------------------*/ /* bit_output.c * * Bit output routines * Procedures return zero on success, EOF on end-of-buffer * * Programmer: R. White Date: 20 July 1998 */ /* Initialize for bit output */ static void start_outputing_bits(Buffer *buffer) { /* * Buffer is empty to start with */ buffer->bitbuffer = 0; buffer->bits_to_go = 8; } /*---------------------------------------------------------------------------*/ /* Output N bits (N must be <= 32) */ static int output_nbits(Buffer *buffer, int bits, int n) { /* local copies */ int lbitbuffer; int lbits_to_go; /* AND mask for the right-most n bits */ static unsigned int mask[33] = {0, 0x1, 0x3, 0x7, 0xf, 0x1f, 0x3f, 0x7f, 0xff, 0x1ff, 0x3ff, 0x7ff, 0xfff, 0x1fff, 0x3fff, 0x7fff, 0xffff, 0x1ffff, 0x3ffff, 0x7ffff, 0xfffff, 0x1fffff, 0x3fffff, 0x7fffff, 0xffffff, 0x1ffffff, 0x3ffffff, 0x7ffffff, 0xfffffff, 0x1fffffff, 0x3fffffff, 0x7fffffff, 0xffffffff}; /* * insert bits at end of bitbuffer */ lbitbuffer = buffer->bitbuffer; lbits_to_go = buffer->bits_to_go; if (lbits_to_go+n > 32) { /* * special case for large n: put out the top lbits_to_go bits first * note that 0 < lbits_to_go <= 8 */ lbitbuffer <<= lbits_to_go; /* lbitbuffer |= (bits>>(n-lbits_to_go)) & ((1<>(n-lbits_to_go)) & *(mask+lbits_to_go); putcbuf(lbitbuffer & 0xff,buffer); n -= lbits_to_go; lbits_to_go = 8; } lbitbuffer <<= n; /* lbitbuffer |= ( bits & ((1<>(-lbits_to_go)) & 0xff,buffer); lbits_to_go += 8; } buffer->bitbuffer = lbitbuffer; buffer->bits_to_go = lbits_to_go; return(0); } /*---------------------------------------------------------------------------*/ /* Flush out the last bits */ static int done_outputing_bits(Buffer *buffer) { if(buffer->bits_to_go < 8) { putcbuf(buffer->bitbuffer<bits_to_go,buffer); /* if (putcbuf(buffer->bitbuffer<bits_to_go,buffer) == EOF) return(EOF); */ } return(0); } /*---------------------------------------------------------------------------*/ /*----------------------------------------------------------*/ /* */ /* START OF SOURCE FILE ORIGINALLY CALLED rdecomp.c */ /* */ /*----------------------------------------------------------*/ /* @(#) rdecomp.c 1.4 99/03/01 12:38:41 */ /* rdecomp.c Decompress image line using * (1) Difference of adjacent pixels * (2) Rice algorithm coding * * Returns 0 on success or 1 on failure */ /* moved these 'includes' to the beginning of the file (WDP) #include #include */ /*---------------------------------------------------------------------------*/ /* this routine used to be called 'rdecomp' (WDP) */ int fits_rdecomp (unsigned char *c, /* input buffer */ int clen, /* length of input */ unsigned int array[], /* output array */ int nx, /* number of output pixels */ int nblock) /* coding block size */ { /* int bsize; */ int i, k, imax; int nbits, nzero, fs; unsigned char *cend, bytevalue; unsigned int b, diff, lastpix; int fsmax, fsbits, bbits; extern const int nonzero_count[]; /* * Original size of each pixel (bsize, bytes) and coding block * size (nblock, pixels) * Could make bsize a parameter to allow more efficient * compression of short & byte images. */ /* bsize = 4; */ /* nblock = 32; now an input parameter */ /* * From bsize derive: * FSBITS = # bits required to store FS * FSMAX = maximum value for FS * BBITS = bits/pixel for direct coding */ /* switch (bsize) { case 1: fsbits = 3; fsmax = 6; break; case 2: fsbits = 4; fsmax = 14; break; case 4: fsbits = 5; fsmax = 25; break; default: ffpmsg("rdecomp: bsize must be 1, 2, or 4 bytes"); return 1; } */ /* move out of switch block, to tweak performance */ fsbits = 5; fsmax = 25; bbits = 1<> nbits) - 1; b &= (1< nx) imax = nx; if (fs<0) { /* low-entropy case, all zero differences */ for ( ; i= 0; k -= 8) { b = *c++; diff |= b<0) { b = *c++; diff |= b>>(-k); b &= (1<>1; } else { diff = ~(diff>>1); } array[i] = diff+lastpix; lastpix = array[i]; } } else { /* normal case, Rice coding */ for ( ; i>nbits); b &= (1<>1; } else { diff = ~(diff>>1); } array[i] = diff+lastpix; lastpix = array[i]; } } if (c > cend) { ffpmsg("decompression error: hit end of compressed byte stream"); return 1; } } if (c < cend) { ffpmsg("decompression warning: unused bytes at end of compressed buffer"); } return 0; } /*---------------------------------------------------------------------------*/ /* this routine used to be called 'rdecomp' (WDP) */ 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 */ int nblock) /* coding block size */ { int i, imax; /* int bsize; */ int k; int nbits, nzero, fs; unsigned char *cend, bytevalue; unsigned int b, diff, lastpix; int fsmax, fsbits, bbits; extern const int nonzero_count[]; /* * Original size of each pixel (bsize, bytes) and coding block * size (nblock, pixels) * Could make bsize a parameter to allow more efficient * compression of short & byte images. */ /* bsize = 2; */ /* nblock = 32; now an input parameter */ /* * From bsize derive: * FSBITS = # bits required to store FS * FSMAX = maximum value for FS * BBITS = bits/pixel for direct coding */ /* switch (bsize) { case 1: fsbits = 3; fsmax = 6; break; case 2: fsbits = 4; fsmax = 14; break; case 4: fsbits = 5; fsmax = 25; break; default: ffpmsg("rdecomp: bsize must be 1, 2, or 4 bytes"); return 1; } */ /* move out of switch block, to tweak performance */ fsbits = 4; fsmax = 14; bbits = 1<> nbits) - 1; b &= (1< nx) imax = nx; if (fs<0) { /* low-entropy case, all zero differences */ for ( ; i= 0; k -= 8) { b = *c++; diff |= b<0) { b = *c++; diff |= b>>(-k); b &= (1<>1; } else { diff = ~(diff>>1); } array[i] = diff+lastpix; lastpix = array[i]; } } else { /* normal case, Rice coding */ for ( ; i>nbits); b &= (1<>1; } else { diff = ~(diff>>1); } array[i] = diff+lastpix; lastpix = array[i]; } } if (c > cend) { ffpmsg("decompression error: hit end of compressed byte stream"); return 1; } } if (c < cend) { ffpmsg("decompression warning: unused bytes at end of compressed buffer"); } return 0; } /*---------------------------------------------------------------------------*/ /* this routine used to be called 'rdecomp' (WDP) */ 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 */ int nblock) /* coding block size */ { int i, imax; /* int bsize; */ int k; int nbits, nzero, fs; unsigned char *cend; unsigned int b, diff, lastpix; int fsmax, fsbits, bbits; extern const int nonzero_count[]; /* * Original size of each pixel (bsize, bytes) and coding block * size (nblock, pixels) * Could make bsize a parameter to allow more efficient * compression of short & byte images. */ /* bsize = 1; */ /* nblock = 32; now an input parameter */ /* * From bsize derive: * FSBITS = # bits required to store FS * FSMAX = maximum value for FS * BBITS = bits/pixel for direct coding */ /* switch (bsize) { case 1: fsbits = 3; fsmax = 6; break; case 2: fsbits = 4; fsmax = 14; break; case 4: fsbits = 5; fsmax = 25; break; default: ffpmsg("rdecomp: bsize must be 1, 2, or 4 bytes"); return 1; } */ /* move out of switch block, to tweak performance */ fsbits = 3; fsmax = 6; bbits = 1<> nbits) - 1; b &= (1< nx) imax = nx; if (fs<0) { /* low-entropy case, all zero differences */ for ( ; i= 0; k -= 8) { b = *c++; diff |= b<0) { b = *c++; diff |= b>>(-k); b &= (1<>1; } else { diff = ~(diff>>1); } array[i] = diff+lastpix; lastpix = array[i]; } } else { /* normal case, Rice coding */ for ( ; i>nbits); b &= (1<>1; } else { diff = ~(diff>>1); } array[i] = diff+lastpix; lastpix = array[i]; } } if (c > cend) { ffpmsg("decompression error: hit end of compressed byte stream"); return 1; } } if (c < cend) { ffpmsg("decompression warning: unused bytes at end of compressed buffer"); } return 0; } astropy-astropy-201cddb/cextern/cfitsio/licenses/000077500000000000000000000000001507226315300223145ustar00rootroot00000000000000astropy-astropy-201cddb/cextern/cfitsio/licenses/License.txt000066400000000000000000000026021507226315300244370ustar00rootroot00000000000000Copyright (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. astropy-astropy-201cddb/cextern/expat/000077500000000000000000000000001507226315300201705ustar00rootroot00000000000000astropy-astropy-201cddb/cextern/expat/AUTHORS000066400000000000000000000002161507226315300212370ustar00rootroot00000000000000Expat is brought to you by: Clark Cooper Fred L. Drake, Jr. Greg Stein James Clark Karl Waclawek Rhodri James Sebastian Pipping Steven Solie astropy-astropy-201cddb/cextern/expat/COPYING000066400000000000000000000021701507226315300212230ustar00rootroot00000000000000Copyright (c) 1998-2000 Thai Open Source Software Center Ltd and Clark Cooper Copyright (c) 2001-2022 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. astropy-astropy-201cddb/cextern/expat/Changes000066400000000000000000001764471507226315300215060ustar00rootroot00000000000000NOTE: We are looking for help with a few things: https://github.com/libexpat/libexpat/labels/help%20wanted If you can help, please get in touch. Thanks! 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 curruption 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: Dong-hee 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 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 some character constants to be ASCII encoded. - Minor cleanups of the test harness. - 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. - Build fixes for various platforms: HP-UX, Tru64, Solaris 9: patch #1437840, bug #1196180. All Unix: #1554618 (refreshed config.sub/config.guess). #1490371, #1613457: support both, DESTDIR and INSTALL_ROOT, without relying on GNU-Make specific features. #1647805: Patched configure.in to work better with Intel compiler. - Fixes to Makefile.in to have make check work correctly: bugs #1408143, #1535603, #1536684. - Added Open Watcom support: patch #1523242. Release 2.0.0 Wed Jan 11 2006 - We no longer use the "check" library for C unit testing; we always use the (partial) internal implementation of the API. - Report XML_NS setting via XML_GetFeatureList(). - Fixed headers for use from C++. - XML_GetCurrentLineNumber() and XML_GetCurrentColumnNumber() now return unsigned integers. - Added XML_LARGE_SIZE switch to enable 64-bit integers for 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. Release 1.95.8 Fri Jul 23 2004 - Major new feature: suspend/resume. Handlers can now request that a parse be suspended for later resumption or aborted altogether. See "Temporarily Stopping Parsing" in the 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. Release 1.95.7 Mon Oct 20 2003 - Fixed enum XML_Status issue (reported on SourceForge many times), so compilers that are properly picky will be happy. - Introduced an XMLCALL macro to control the calling convention used by the Expat API; this macro should be used to annotate prototypes and definitions of callback implementations in code compiled with a calling convention other than the default convention for the host platform. - 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. - 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. - Enhanced the regression test suite. - Man page improvements: includes SF issue #632146. Release 1.95.5 Fri Sep 6 2002 - Added XML_UseForeignDTD() for improved SAX2 support. - Added XML_GetFeatureList(). - Defined XML_Bool type and the values XML_TRUE and XML_FALSE. - Use an incomplete struct instead of a void* for the parser (may not retain). - Fixed UTF-8 decoding bug that caused legal UTF-8 to be rejected. - Finally fixed bug where default handler would report DTD events that were already handled by another handler. Initial patch contributed by Darryl Miles. - Removed unnecessary DllMain() function that caused static linking into a DLL to be difficult. - Added VC++ projects for building static libraries. - 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. Release 1.95.4 Fri Jul 12 2002 - Added support for VMS, contributed by Craig Berry. See vms/README.vms for more information. - Added Mac OS (classic) support, with a makefile for MPW, 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. - 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 - Re-implemented section 5.1 from XML 1.0 spec: 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 version of the library; the DLLs are named libexpatw.dll. - Changed the name of the Windows DLLs from expat.dll to libexpat.dll; this fixes SF bug #432456. - Added the XML_ParserReset() API function. - Fixed XML_SetReturnNSTriplet() to work for element names. - 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 have been fixed, especially in the area of build support. Release 1.95.2 Fri Jul 27 2001 - More changes to make MSVC happy with the build; add a single workspace to support both the library and xmlwf application. - Added a Windows installer for Windows users; includes xmlwf.exe. - Added compile-time constants that can be used to determine the Expat version - Removed a lot of GNU-specific dependencies to aide portability among the various Unix flavors. - Fix the UTF-8 BOM bug. - Cleaned up warning messages for several compilers. - Added the -Wall, -Wstrict-prototypes options for GCC. Release 1.95.1 Sun Oct 22 15:11:36 EDT 2000 - Changes to get expat to build under Microsoft compiler - Removed all aborts and instead return an UNEXPECTED_STATE error. - Fixed a bug where a stray '%' in an entity value would cause an abort. - Defined XML_SetEndNamespaceDeclHandler. Thanks to Darryl Miles for finding this oversight. - Changed default patterns in lib/Makefile.in to fit non-GNU makes Thanks to robin@unrated.net for reporting and providing an account to test on. - The reference had the wrong label for XML_SetStartNamespaceDecl. Reported by an anonymous user. Release 1.95.0 Fri Sep 29 2000 - XML_ParserCreate_MM Allows you to set a memory management suite to replace the standard malloc,realloc, and free. - XML_SetReturnNSTriplet If you turn this feature on when namespace processing is in effect, then qualified, prefixed element and attribute names are returned as "uri|name|prefix" where '|' is whatever separator character is used in namespace processing. - Merged in features from perl-expat o XML_SetElementDeclHandler o XML_SetAttlistDeclHandler o XML_SetXmlDeclHandler o XML_SetEntityDeclHandler o StartDoctypeDeclHandler takes 3 additional parameters: sysid, pubid, has_internal_subset o Many paired handler setters (like XML_SetElementHandler) now have corresponding individual handler setters o XML_GetInputContext for getting the input context of the current parse position. - Added reference material - Packaged into a distribution that builds a sharable library astropy-astropy-201cddb/cextern/expat/README.md000066400000000000000000000211321507226315300214460ustar00rootroot00000000000000[![Run Linux Travis CI tasks](https://github.com/libexpat/libexpat/actions/workflows/linux.yml/badge.svg)](https://github.com/libexpat/libexpat/actions/workflows/linux.yml) [![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/github/libexpat/libexpat?svg=true)](https://ci.appveyor.com/project/libexpat/libexpat) [![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) # Expat, Release 2.5.0 This is Expat, a C library for parsing XML, 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 compilers: - GNU GCC >=4.5 - LLVM Clang >=3.5 - Microsoft Visual Studio >=15.0/2017 (rolling `${today} minus 5 years`) 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 two ways of using libexpat with CMake: ### a) 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.0) # or 3.10, see below project(hello VERSION 1.0.0) find_package(EXPAT 2.2.8 MODULE REQUIRED) add_executable(hello hello.c ) # a) for CMake >=3.10 (see CMake's FindEXPAT docs) target_link_libraries(hello PUBLIC EXPAT::EXPAT) # b) for CMake >=3.0 target_include_directories(hello PRIVATE ${EXPAT_INCLUDE_DIRS}) target_link_libraries(hello PUBLIC ${EXPAT_LIBRARIES}) ``` ### b) 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.0) 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) ``` ## 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 {} +` 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 ossfuzz 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 ``` astropy-astropy-201cddb/cextern/expat/README.txt000066400000000000000000000002301507226315300216610ustar00rootroot00000000000000Note: astropy only requires the expat library, and hence in this bundled version, we removed all other files except the required license and changelog. astropy-astropy-201cddb/cextern/expat/expat_config.h000066400000000000000000000077241507226315300230210ustar00rootroot00000000000000/* expat_config.h. Generated from expat_config.h.in by configure. */ /* expat_config.h.in. Generated from configure.ac by autoheader. */ #ifndef EXPAT_CONFIG_H #define EXPAT_CONFIG_H 1 /* Define if building universal (internal helper macro) */ /* #undef AC_APPLE_UNIVERSAL_BUILD */ /* 1234 = LILENDIAN, 4321 = BIGENDIAN */ #define BYTEORDER 1234 /* Define to 1 if you have the `arc4random' function. */ /* #undef HAVE_ARC4RANDOM */ /* Define to 1 if you have the `arc4random_buf' function. */ #define HAVE_ARC4RANDOM_BUF 1 /* Define to 1 if you have the header file. */ #define HAVE_DLFCN_H 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 HAVE_GETPAGESIZE 1 /* Define to 1 if you have the `getrandom' function. */ #define HAVE_GETRANDOM 1 /* Define to 1 if you have the header file. */ #define HAVE_INTTYPES_H 1 /* Define to 1 if you have the `bsd' library (-lbsd). */ /* #undef HAVE_LIBBSD */ /* 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 /* Define to 1 if you have the header file. */ #define HAVE_STRINGS_H 1 /* Define to 1 if you have the header file. */ #define HAVE_STRING_H 1 /* Define to 1 if you have `syscall' and `SYS_getrandom'. */ #define HAVE_SYSCALL_GETRANDOM 1 /* 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 /* Define to 1 if you have the header file. */ #define HAVE_UNISTD_H 1 /* Define to the sub-directory where libtool stores uninstalled libraries. */ #define LT_OBJDIR ".libs/" /* 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 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.5.0" /* 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.5.0" /* Define to 1 if all of the C90 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 /* Version number of package */ #define VERSION "2.5.0" /* 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. */ #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 1 /* Define to make XML Namespaces functionality available. */ #define XML_NS 1 /* Define to empty if `const' does not conform to ANSI C. */ /* #undef const */ /* Define to `long int' if does not define. */ /* #undef off_t */ /* Define to `unsigned int' if does not define. */ /* #undef size_t */ #endif // ndef EXPAT_CONFIG_H astropy-astropy-201cddb/cextern/expat/expat_config.h.in000066400000000000000000000072551507226315300234250ustar00rootroot00000000000000/* expat_config.h.in. Generated from configure.ac by autoheader. */ #ifndef EXPAT_CONFIG_H #define EXPAT_CONFIG_H 1 /* Define if building universal (internal helper macro) */ #undef AC_APPLE_UNIVERSAL_BUILD /* 1234 = LILENDIAN, 4321 = BIGENDIAN */ #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 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 `getrandom' function. */ #undef HAVE_GETRANDOM /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H /* Define to 1 if you have the `bsd' library (-lbsd). */ #undef HAVE_LIBBSD /* 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_STDIO_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 `syscall' and `SYS_getrandom'. */ #undef HAVE_SYSCALL_GETRANDOM /* Define to 1 if you have the header file. */ #undef HAVE_SYS_PARAM_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 sub-directory where libtool stores uninstalled libraries. */ #undef LT_OBJDIR /* Name of package */ #undef PACKAGE /* 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 home page for this package. */ #undef PACKAGE_URL /* Define to the version of this package. */ #undef PACKAGE_VERSION /* Define to 1 if all of the C90 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. */ #undef STDC_HEADERS /* Version number of package */ #undef VERSION /* 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. */ #undef XML_CONTEXT_BYTES /* Define to include code reading entropy from `/dev/urandom'. */ #undef XML_DEV_URANDOM /* Define to make parameter entity parsing functionality available. */ #undef XML_DTD /* Define to make XML Namespaces functionality available. */ #undef XML_NS /* Define to empty if `const' does not conform to ANSI C. */ #undef const /* Define to `long int' if does not define. */ #undef off_t /* Define to `unsigned int' if does not define. */ #undef size_t #endif // ndef EXPAT_CONFIG_H astropy-astropy-201cddb/cextern/expat/lib/000077500000000000000000000000001507226315300207365ustar00rootroot00000000000000astropy-astropy-201cddb/cextern/expat/lib/Makefile.am000066400000000000000000000052101507226315300227700ustar00rootroot00000000000000# # __ __ _ # ___\ \/ /_ __ __ _| |_ # / _ \\ /| '_ \ / _` | __| # | __// \| |_) | (_| | |_ # \___/_/\_\ .__/ \__,_|\__| # |_| XML parser # # Copyright (c) 2017-2022 Sebastian Pipping # Copyright (c) 2017 Tomasz Kłoczko # Copyright (c) 2019 David Loffredo # 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_HEADERS = \ ../expat_config.h \ expat.h \ expat_external.h lib_LTLIBRARIES = libexpat.la noinst_LTLIBRARIES = libexpatinternal.la libexpat_la_LDFLAGS = \ @AM_LDFLAGS@ \ @LIBM@ \ -no-undefined \ -version-info @LIBCURRENT@:@LIBREVISION@:@LIBAGE@ libexpat_la_SOURCES = # This layer of indirection allows # the test suite to access internal symbols # despite compiling with -fvisibility=hidden libexpatinternal_la_SOURCES = \ xmlparse.c \ xmltok.c \ xmlrole.c libexpat_la_LIBADD = libexpatinternal.la doc_DATA = \ ../AUTHORS \ ../Changes install-data-hook: cd "$(DESTDIR)$(docdir)" && $(am__mv) Changes changelog uninstall-local: $(RM) "$(DESTDIR)$(docdir)/changelog" EXTRA_DIST = \ ascii.h \ asciitab.h \ expat_external.h \ expat.h \ iasciitab.h \ internal.h \ latin1tab.h \ libexpat.def.cmake \ nametab.h \ siphash.h \ utf8tab.h \ winconfig.h \ xmlrole.h \ xmltok.h \ xmltok_impl.c \ xmltok_impl.h \ xmltok_ns.c astropy-astropy-201cddb/cextern/expat/lib/Makefile.in000066400000000000000000000655101507226315300230120ustar00rootroot00000000000000# Makefile.in generated by automake 1.16.5 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2021 Free Software Foundation, Inc. # This Makefile.in 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. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ # # __ __ _ # ___\ \/ /_ __ __ _| |_ # / _ \\ /| '_ \ / _` | __| # | __// \| |_) | (_| | |_ # \___/_/\_\ .__/ \__,_|\__| # |_| XML parser # # Copyright (c) 2017-2022 Sebastian Pipping # Copyright (c) 2017 Tomasz Kłoczko # Copyright (c) 2019 David Loffredo # 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. VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = lib ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/acinclude.m4 \ $(top_srcdir)/conftools/ax-require-defined.m4 \ $(top_srcdir)/conftools/ax-check-compile-flag.m4 \ $(top_srcdir)/conftools/ax-check-link-flag.m4 \ $(top_srcdir)/conftools/ax-append-flag.m4 \ $(top_srcdir)/conftools/ax-append-compile-flags.m4 \ $(top_srcdir)/conftools/ax-append-link-flags.m4 \ $(top_srcdir)/conftools/expatcfg-compiler-supports-visibility.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(include_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/expat_config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(docdir)" \ "$(DESTDIR)$(includedir)" LTLIBRARIES = $(lib_LTLIBRARIES) $(noinst_LTLIBRARIES) libexpat_la_DEPENDENCIES = libexpatinternal.la am_libexpat_la_OBJECTS = libexpat_la_OBJECTS = $(am_libexpat_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = libexpat_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(libexpat_la_LDFLAGS) $(LDFLAGS) -o $@ libexpatinternal_la_LIBADD = am_libexpatinternal_la_OBJECTS = xmlparse.lo xmltok.lo xmlrole.lo libexpatinternal_la_OBJECTS = $(am_libexpatinternal_la_OBJECTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/conftools/depcomp am__maybe_remake_depfiles = depfiles am__depfiles_remade = ./$(DEPDIR)/xmlparse.Plo ./$(DEPDIR)/xmlrole.Plo \ ./$(DEPDIR)/xmltok.Plo am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libexpat_la_SOURCES) $(libexpatinternal_la_SOURCES) DIST_SOURCES = $(libexpat_la_SOURCES) $(libexpatinternal_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac DATA = $(doc_DATA) HEADERS = $(include_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` am__DIST_COMMON = $(srcdir)/Makefile.in \ $(top_srcdir)/conftools/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_CFLAGS = @AM_CFLAGS@ AM_CPPFLAGS = @AM_CPPFLAGS@ AM_CXXFLAGS = @AM_CXXFLAGS@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AM_LDFLAGS = @AM_LDFLAGS@ AR = @AR@ AS = @AS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CMAKE_SHARED_LIBRARY_PREFIX = @CMAKE_SHARED_LIBRARY_PREFIX@ CPPFLAGS = @CPPFLAGS@ CSCOPE = @CSCOPE@ CTAGS = @CTAGS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DOCBOOK_TO_MAN = @DOCBOOK_TO_MAN@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ ETAGS = @ETAGS@ EXEEXT = @EXEEXT@ EXPAT_ATTR_INFO = @EXPAT_ATTR_INFO@ EXPAT_CHAR_TYPE = @EXPAT_CHAR_TYPE@ EXPAT_CONTEXT_BYTES = @EXPAT_CONTEXT_BYTES@ EXPAT_DTD = @EXPAT_DTD@ EXPAT_LARGE_SIZE = @EXPAT_LARGE_SIZE@ EXPAT_MIN_SIZE = @EXPAT_MIN_SIZE@ EXPAT_NS = @EXPAT_NS@ FGREP = @FGREP@ FILECMD = @FILECMD@ FILEMAP = @FILEMAP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBAGE = @LIBAGE@ LIBCURRENT = @LIBCURRENT@ LIBDIR_BASENAME = @LIBDIR_BASENAME@ LIBM = @LIBM@ LIBOBJS = @LIBOBJS@ LIBREVISION = @LIBREVISION@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SO_MAJOR = @SO_MAJOR@ SO_MINOR = @SO_MINOR@ SO_PATCH = @SO_PATCH@ STRIP = @STRIP@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ ac_cv_sizeof_void_p = @ac_cv_sizeof_void_p@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ include_HEADERS = \ ../expat_config.h \ expat.h \ expat_external.h lib_LTLIBRARIES = libexpat.la noinst_LTLIBRARIES = libexpatinternal.la libexpat_la_LDFLAGS = \ @AM_LDFLAGS@ \ @LIBM@ \ -no-undefined \ -version-info @LIBCURRENT@:@LIBREVISION@:@LIBAGE@ libexpat_la_SOURCES = # This layer of indirection allows # the test suite to access internal symbols # despite compiling with -fvisibility=hidden libexpatinternal_la_SOURCES = \ xmlparse.c \ xmltok.c \ xmlrole.c libexpat_la_LIBADD = libexpatinternal.la doc_DATA = \ ../AUTHORS \ ../Changes EXTRA_DIST = \ ascii.h \ asciitab.h \ expat_external.h \ expat.h \ iasciitab.h \ internal.h \ latin1tab.h \ libexpat.def.cmake \ nametab.h \ siphash.h \ utf8tab.h \ winconfig.h \ xmlrole.h \ xmltok.h \ xmltok_impl.c \ xmltok_impl.h \ xmltok_ns.c all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu lib/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu lib/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-libLTLIBRARIES: $(lib_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ } uninstall-libLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ done clean-libLTLIBRARIES: -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) @list='$(lib_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libexpat.la: $(libexpat_la_OBJECTS) $(libexpat_la_DEPENDENCIES) $(EXTRA_libexpat_la_DEPENDENCIES) $(AM_V_CCLD)$(libexpat_la_LINK) -rpath $(libdir) $(libexpat_la_OBJECTS) $(libexpat_la_LIBADD) $(LIBS) libexpatinternal.la: $(libexpatinternal_la_OBJECTS) $(libexpatinternal_la_DEPENDENCIES) $(EXTRA_libexpatinternal_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(libexpatinternal_la_OBJECTS) $(libexpatinternal_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xmlparse.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xmlrole.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xmltok.Plo@am__quote@ # am--include-marker $(am__depfiles_remade): @$(MKDIR_P) $(@D) @echo '# dummy' >$@-t && $(am__mv) $@-t $@ am--depfiles: $(am__depfiles_remade) .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-docDATA: $(doc_DATA) @$(NORMAL_INSTALL) @list='$(doc_DATA)'; test -n "$(docdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(docdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(docdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(docdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(docdir)" || exit $$?; \ done uninstall-docDATA: @$(NORMAL_UNINSTALL) @list='$(doc_DATA)'; test -n "$(docdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(docdir)'; $(am__uninstall_files_from_dir) install-includeHEADERS: $(include_HEADERS) @$(NORMAL_INSTALL) @list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(includedir)'"; \ $(MKDIR_P) "$(DESTDIR)$(includedir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(includedir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(includedir)" || exit $$?; \ done uninstall-includeHEADERS: @$(NORMAL_UNINSTALL) @list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(includedir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(DATA) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(docdir)" "$(DESTDIR)$(includedir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ clean-noinstLTLIBRARIES mostlyclean-am distclean: distclean-am -rm -f ./$(DEPDIR)/xmlparse.Plo -rm -f ./$(DEPDIR)/xmlrole.Plo -rm -f ./$(DEPDIR)/xmltok.Plo -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-docDATA install-includeHEADERS @$(NORMAL_INSTALL) $(MAKE) $(AM_MAKEFLAGS) install-data-hook install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-libLTLIBRARIES install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f ./$(DEPDIR)/xmlparse.Plo -rm -f ./$(DEPDIR)/xmlrole.Plo -rm -f ./$(DEPDIR)/xmltok.Plo -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-docDATA uninstall-includeHEADERS \ uninstall-libLTLIBRARIES uninstall-local .MAKE: install-am install-data-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ clean-generic clean-libLTLIBRARIES clean-libtool \ clean-noinstLTLIBRARIES cscopelist-am ctags ctags-am distclean \ distclean-compile distclean-generic distclean-libtool \ distclean-tags distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am \ install-data-hook install-docDATA install-dvi install-dvi-am \ install-exec install-exec-am install-html install-html-am \ install-includeHEADERS install-info install-info-am \ install-libLTLIBRARIES install-man install-pdf install-pdf-am \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am uninstall-docDATA \ uninstall-includeHEADERS uninstall-libLTLIBRARIES \ uninstall-local .PRECIOUS: Makefile install-data-hook: cd "$(DESTDIR)$(docdir)" && $(am__mv) Changes changelog uninstall-local: $(RM) "$(DESTDIR)$(docdir)/changelog" # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: astropy-astropy-201cddb/cextern/expat/lib/ascii.h000066400000000000000000000071421507226315300222030ustar00rootroot00000000000000/* __ __ _ ___\ \/ /_ __ __ _| |_ / _ \\ /| '_ \ / _` | __| | __// \| |_) | (_| | |_ \___/_/\_\ .__/ \__,_|\__| |_| 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 #define ASCII_B 0x42 #define ASCII_C 0x43 #define ASCII_D 0x44 #define ASCII_E 0x45 #define ASCII_F 0x46 #define ASCII_G 0x47 #define ASCII_H 0x48 #define ASCII_I 0x49 #define ASCII_J 0x4A #define ASCII_K 0x4B #define ASCII_L 0x4C #define ASCII_M 0x4D #define ASCII_N 0x4E #define ASCII_O 0x4F #define ASCII_P 0x50 #define ASCII_Q 0x51 #define ASCII_R 0x52 #define ASCII_S 0x53 #define ASCII_T 0x54 #define ASCII_U 0x55 #define ASCII_V 0x56 #define ASCII_W 0x57 #define ASCII_X 0x58 #define ASCII_Y 0x59 #define ASCII_Z 0x5A #define ASCII_a 0x61 #define ASCII_b 0x62 #define ASCII_c 0x63 #define ASCII_d 0x64 #define ASCII_e 0x65 #define ASCII_f 0x66 #define ASCII_g 0x67 #define ASCII_h 0x68 #define ASCII_i 0x69 #define ASCII_j 0x6A #define ASCII_k 0x6B #define ASCII_l 0x6C #define ASCII_m 0x6D #define ASCII_n 0x6E #define ASCII_o 0x6F #define ASCII_p 0x70 #define ASCII_q 0x71 #define ASCII_r 0x72 #define ASCII_s 0x73 #define ASCII_t 0x74 #define ASCII_u 0x75 #define ASCII_v 0x76 #define ASCII_w 0x77 #define ASCII_x 0x78 #define ASCII_y 0x79 #define ASCII_z 0x7A #define ASCII_0 0x30 #define ASCII_1 0x31 #define ASCII_2 0x32 #define ASCII_3 0x33 #define ASCII_4 0x34 #define ASCII_5 0x35 #define ASCII_6 0x36 #define ASCII_7 0x37 #define ASCII_8 0x38 #define ASCII_9 0x39 #define ASCII_TAB 0x09 #define ASCII_SPACE 0x20 #define ASCII_EXCL 0x21 #define ASCII_QUOT 0x22 #define ASCII_AMP 0x26 #define ASCII_APOS 0x27 #define ASCII_MINUS 0x2D #define ASCII_PERIOD 0x2E #define ASCII_COLON 0x3A #define ASCII_SEMI 0x3B #define ASCII_LT 0x3C #define ASCII_EQUALS 0x3D #define ASCII_GT 0x3E #define ASCII_LSQB 0x5B #define ASCII_RSQB 0x5D #define ASCII_UNDERSCORE 0x5F #define ASCII_LPAREN 0x28 #define ASCII_RPAREN 0x29 #define ASCII_FF 0x0C #define ASCII_SLASH 0x2F #define ASCII_HASH 0x23 #define ASCII_PIPE 0x7C #define ASCII_COMMA 0x2C astropy-astropy-201cddb/cextern/expat/lib/asciitab.h000066400000000000000000000067001507226315300226710ustar00rootroot00000000000000/* __ __ _ ___\ \/ /_ __ __ _| |_ / _ \\ /| '_ \ / _` | __| | __// \| |_) | (_| | |_ \___/_/\_\ .__/ \__,_|\__| |_| 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, astropy-astropy-201cddb/cextern/expat/lib/expat.h000066400000000000000000001250171507226315300222360ustar00rootroot00000000000000/* __ __ _ ___\ \/ /_ __ __ _| |_ / _ \\ /| '_ \ / _` | __| | __// \| |_) | (_| | |_ \___/_/\_\ .__/ \__,_|\__| |_| 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-2022 Sebastian Pipping Copyright (c) 2016 Cristian Rodríguez Copyright (c) 2016 Thomas Beutlich Copyright (c) 2017 Rhodri James Copyright (c) 2022 Thijs Schreijer 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 #include #include "expat_external.h" #ifdef __cplusplus extern "C" { #endif 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) /* The XML_Status enum gives the possible return values for several API functions. The preprocessor #defines are included so this stanza can be added to code that still needs to support older versions of Expat 1.95.x: #ifndef XML_STATUS_OK #define XML_STATUS_OK 1 #define XML_STATUS_ERROR 0 #endif Otherwise, the #define hackery is quite ugly and would have been dropped. */ 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, /* Added in 1.95.7. */ XML_ERROR_UNBOUND_PREFIX, /* Added in 1.95.8. */ 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, /* Added in 2.0. */ XML_ERROR_RESERVED_PREFIX_XML, XML_ERROR_RESERVED_PREFIX_XMLNS, 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 }; 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 }; /* If type == 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 == XML_CTYPE_MIXED, then quant will be NONE or REP and numchildren will contain number of elements that may be mixed in and children point to an array of XML_Content cells that will be all of XML_CTYPE_NAME type with no quantification. If type == XML_CTYPE_NAME, then the name points to the name, and the numchildren field will be zero and children will be NULL. The quant fields indicates any quantifiers placed on the name. CHOICE and SEQ will have name NULL, the number of children in numchildren and children will point, recursively, to an array of XML_Content cells. The EMPTY, ANY, and MIXED types will only occur at top level. */ 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; }; /* This is called for an element declaration. See above for 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); XMLPARSEAPI(void) 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 generate multiple calls to this handler. The "default" parameter may be NULL in the case of the "#IMPLIED" or "#REQUIRED" keyword. The "isrequired" parameter will be true and the default 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); XMLPARSEAPI(void) 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 parameter will be NULL for text declarations. The encoding parameter may be NULL for XML declarations. The standalone parameter will be -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. */ 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); 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; /* Constructs a new parser; encoding is the encoding specified by the external protocol or NULL if there is none specified. */ XMLPARSEAPI(XML_Parser) XML_ParserCreate(const XML_Char *encoding); /* Constructs a new parser and namespace processor. Element type names and attribute names that belong to a namespace will be expanded; unprefixed attribute names are never expanded; unprefixed element type names are expanded only if there is a default namespace. The expanded name is the concatenation of the namespace URI, the namespace separator character, and the local part of the name. If the namespace separator is '\0' then the namespace URI 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 namespace processing as described above. The character pointed at will serve as the namespace separator. All further memory operations used for the created parser will come from the given suite. */ XMLPARSEAPI(XML_Parser) 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 disproportionately high, such as when a large number of small documnents 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. Added in Expat 1.95.3. */ XMLPARSEAPI(XML_Bool) 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); /* s is not 0 terminated. */ 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); /* data is 0 terminated */ 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); /* This is called for any characters in the XML document for which there is no applicable handler. This includes both characters that are part of markup which is of a kind that is not reported (comments, markup declarations), or characters that are part of a construct which could be reported but for which no handler has been supplied. The characters are passed exactly as they were 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. */ 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); /* 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); /* This is called for entity declarations. The is_parameter_entity argument will be non-zero if the entity is a parameter entity, zero otherwise. For internal entities (), value will be non-NULL and systemId, publicID, and notationName will be NULL. 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. For external entities, value will be NULL and systemId will be non-NULL. The publicId argument will be NULL unless a public identifier was provided. The notationName argument will have a non-NULL value only for unparsed entity declarations. 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); XMLPARSEAPI(void) XML_SetEntityDeclHandler(XML_Parser parser, XML_EntityDeclHandler handler); /* OBSOLETE -- OBSOLETE -- OBSOLETE 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. The base argument is whatever was set by XML_SetBase. The 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); /* 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); /* When namespace processing is enabled, these are called once for each namespace declaration. The call to the start and end element handlers occur between the calls to the start and end namespace 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_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 have standalone="yes". If this handler returns XML_STATUS_ERROR, then processing will not continue, and the parser will return a XML_ERROR_NOT_STANDALONE error. If parameter entity parsing is enabled, then in addition to the conditions above this handler will only be called if the referenced entity was actually read. */ 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 application can parse it immediately or later using XML_ExternalEntityParserCreate. The parser argument is the parser parsing the entity containing the reference; it can be passed as the parser argument to XML_ExternalEntityParserCreate. The systemId argument is the system identifier as specified in the entity declaration; it will not be NULL. The base argument is the system identifier that should be used as the base for resolving systemId if systemId was relative; this is set by XML_SetBase; it may be NULL. The publicId argument is the public identifier as specified in the entity declaration, or NULL if none was specified; the whitespace in the public identifier will have been normalized as required by the XML spec. The context argument specifies the parsing context in the format expected by the context argument to XML_ExternalEntityParserCreate; context 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. The handler should return XML_STATUS_ERROR if processing should not continue because of a fatal error in the handling of the external entity. In this case the calling parser will return an XML_ERROR_EXTERNAL_ENTITY_HANDLING error. 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); /* 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) An internal entity reference is read, but not expanded, because XML_SetDefaultHandler has been called. 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 */ 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 to the parser. The map[b] member gives information about byte sequences whose first byte is b. If map[b] is c where c is >= 0, then b by itself encodes the Unicode scalar value c. If map[b] is -1, then the byte sequence is malformed. If map[b] is -n, where n >= 2, then b is the first byte of an n-byte sequence that encodes a single Unicode scalar value. The data member will be passed as the first argument to the convert function. The convert function is used to convert multibyte sequences; s will point to a n-byte sequence where map[(unsigned char)*s] == -n. The convert function must return the Unicode scalar value represented by this byte sequence or -1 if the byte sequence is malformed. The convert function may be NULL if the encoding is a single-byte encoding, that is if map[b] >= -1 for all bytes b. When the parser is finished with the encoding, then if release is not NULL, it will call release passing it the data member; once release has been called, the convert function will not be called again. Expat places certain restrictions on the encodings that are supported using this mechanism. 1. Every ASCII character that can appear in a well-formed XML document, other than the characters $@\^`{}~ must be represented by a single byte, and that byte must be the same byte that represents that character in ASCII. 2. No character may require more than 4 bytes to encode. 3. All characters encoded must have Unicode scalar values <= 0xFFFF, (i.e., characters that would be encoded by surrogates in UTF-16 are not allowed). Note that this restriction doesn't apply to the built-in support for UTF-8 and UTF-16. 4. No Unicode character may be encoded by more than one distinct sequence of bytes. */ typedef struct { int map[256]; 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. The encodingHandlerData argument is that which was passed as the second argument to XML_SetUnknownEncodingHandler. The name argument gives the name of the encoding as specified in the encoding declaration. If the callback can provide information about the encoding, it must fill in the XML_Encoding structure, and return XML_STATUS_OK. Otherwise it must return XML_STATUS_ERROR. If info does not describe a suitable encoding, then the parser will return an XML_ERROR_UNKNOWN_ENCODING error. */ 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_EndElementHandler end); XMLPARSEAPI(void) XML_SetStartElementHandler(XML_Parser parser, XML_StartElementHandler handler); XMLPARSEAPI(void) XML_SetEndElementHandler(XML_Parser parser, XML_EndElementHandler handler); XMLPARSEAPI(void) XML_SetCharacterDataHandler(XML_Parser parser, XML_CharacterDataHandler handler); XMLPARSEAPI(void) XML_SetProcessingInstructionHandler(XML_Parser parser, XML_ProcessingInstructionHandler handler); XMLPARSEAPI(void) XML_SetCommentHandler(XML_Parser parser, XML_CommentHandler handler); XMLPARSEAPI(void) XML_SetCdataSectionHandler(XML_Parser parser, XML_StartCdataSectionHandler start, XML_EndCdataSectionHandler end); XMLPARSEAPI(void) XML_SetStartCdataSectionHandler(XML_Parser parser, XML_StartCdataSectionHandler start); XMLPARSEAPI(void) XML_SetEndCdataSectionHandler(XML_Parser parser, XML_EndCdataSectionHandler end); /* This sets the default handler and also inhibits expansion of internal entities. These entity references will be passed to the default handler, or to the skipped entity handler, if one is set. */ XMLPARSEAPI(void) 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); XMLPARSEAPI(void) XML_SetDoctypeDeclHandler(XML_Parser parser, XML_StartDoctypeDeclHandler start, XML_EndDoctypeDeclHandler end); XMLPARSEAPI(void) XML_SetStartDoctypeDeclHandler(XML_Parser parser, XML_StartDoctypeDeclHandler start); XMLPARSEAPI(void) 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); XMLPARSEAPI(void) XML_SetNamespaceDeclHandler(XML_Parser parser, XML_StartNamespaceDeclHandler start, XML_EndNamespaceDeclHandler end); XMLPARSEAPI(void) XML_SetStartNamespaceDeclHandler(XML_Parser parser, XML_StartNamespaceDeclHandler start); XMLPARSEAPI(void) XML_SetEndNamespaceDeclHandler(XML_Parser parser, XML_EndNamespaceDeclHandler end); XMLPARSEAPI(void) XML_SetNotStandaloneHandler(XML_Parser parser, XML_NotStandaloneHandler handler); XMLPARSEAPI(void) XML_SetExternalEntityRefHandler(XML_Parser parser, XML_ExternalEntityRefHandler handler); /* If a non-NULL value for arg is specified here, then it will be passed as the first argument to the external entity ref handler instead of the parser object. */ XMLPARSEAPI(void) XML_SetExternalEntityRefHandlerArg(XML_Parser parser, void *arg); XMLPARSEAPI(void) XML_SetSkippedEntityHandler(XML_Parser parser, XML_SkippedEntityHandler handler); XMLPARSEAPI(void) XML_SetUnknownEncodingHandler(XML_Parser parser, XML_UnknownEncodingHandler handler, void *encodingHandlerData); /* 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. */ XMLPARSEAPI(void) XML_DefaultCurrent(XML_Parser parser); /* If do_nst is non-zero, and namespace processing is in effect, and a name has a prefix (i.e. an explicit namespace qualifier) then that name is returned as a triplet in a single string separated by the separator character specified when the parser was created: URI + sep + local_name + sep + prefix. If do_nst is zero, then namespace information is returned in the default manner (URI + sep + local_name) whether or not the name has a prefix. Note: Calling XML_SetReturnNSTriplet after XML_Parse or XML_ParseBuffer has no effect. */ XMLPARSEAPI(void) XML_SetReturnNSTriplet(XML_Parser parser, int do_nst); /* This value is passed as the userData argument to callbacks. */ 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)) /* This is equivalent to supplying an encoding argument to XML_ParserCreate. On success XML_SetEncoding returns non-zero, zero otherwise. Note: Calling XML_SetEncoding after XML_Parse or XML_ParseBuffer has no effect and returns XML_STATUS_ERROR. */ XMLPARSEAPI(enum XML_Status) XML_SetEncoding(XML_Parser parser, const XML_Char *encoding); /* If this function is called, then the parser will be passed as the first argument to callbacks instead of userData. The userData will still be accessible using XML_GetUserData. */ XMLPARSEAPI(void) XML_UseParserAsHandlerArg(XML_Parser parser); /* If useDTD == XML_TRUE is passed to this function, then the parser will assume that there is an external subset, even if none is specified in the document. In such a case the parser will call the externalEntityRefHandler with a value of NULL for the systemId argument (the publicId and context arguments will be NULL as well). 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. Note: If this function is called, then this must be done before the first call to XML_Parse or XML_ParseBuffer, since it will have no effect after that. Returns XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING. Note: If the document does not have a DOCTYPE declaration at all, then startDoctypeDeclHandler and endDoctypeDeclHandler will not 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 base argument to the XML_ExternalEntityRefHandler, XML_NotationDeclHandler and XML_UnparsedEntityDeclHandler. The base argument will be copied. Returns XML_STATUS_ERROR if out of memory, XML_STATUS_OK otherwise. */ XMLPARSEAPI(enum XML_Status) XML_SetBase(XML_Parser parser, const XML_Char *base); XMLPARSEAPI(const XML_Char *) 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 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 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). Though the return values for these functions has always been described as a Boolean value, the implementation, at least for the 1.95.x series, has always returned exactly one of the XML_Status values. */ XMLPARSEAPI(enum XML_Status) XML_Parse(XML_Parser parser, const char *s, int len, int isFinal); XMLPARSEAPI(void *) XML_GetBuffer(XML_Parser parser, int len); XMLPARSEAPI(enum XML_Status) XML_ParseBuffer(XML_Parser parser, int len, int isFinal); /* Stops parsing, causing XML_Parse() or XML_ParseBuffer() to return. Must be called from within a call-back handler, except when aborting (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(), 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: - 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. 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 externalEntityRefHandler() returns. It is up to the implementation of 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(). */ XMLPARSEAPI(enum XML_Status) 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. *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. */ XMLPARSEAPI(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; /* Returns status of parser with respect to being initialized, parsing, finished, or suspended and processing the final buffer. XXX XML_Parse() and XML_ParseBuffer() should return XML_ParsingStatus, XXX with XML_FINISHED_OK or XML_FINISHED_ERROR replacing XML_FINISHED */ XMLPARSEAPI(void) XML_GetParsingStatus(XML_Parser parser, XML_ParsingStatus *status); /* Creates an XML_Parser object that can parse an external general entity; context is a '\0'-terminated string specifying the parse context; encoding is a '\0'-terminated string giving the name of the externally specified encoding, or NULL if there is no externally specified encoding. The context string consists of a sequence of tokens separated by formfeeds (\f); a token consisting of a name specifies that the general entity of the name is open; a token of the form prefix=uri specifies the namespace for a particular prefix; a token of the form =uri specifies the default namespace. This can be called at any point after the first call to an ExternalEntityRefHandler so longer as the parser has not yet been freed. The new parser is completely independent and may safely be used in a separate thread. The handlers and userData are initialized from the parser argument. Returns NULL if out of memory. Otherwise returns a new XML_Parser object. */ XMLPARSEAPI(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 }; /* Controls parsing of parameter entities (including the external DTD subset). If parsing of parameter entities is enabled, then references to external parameter entities (including the external DTD subset) will be passed to the handler set with XML_SetExternalEntityRefHandler. The context passed will be 0. Unlike external general entities, external parameter entities can only be parsed synchronously. If the external parameter entity is to be parsed, it must be parsed during the call to the external entity ref handler: the complete sequence of XML_ExternalEntityParserCreate, XML_Parse/XML_ParseBuffer and XML_ParserFree calls must be made during this call. After XML_ExternalEntityParserCreate has been called to create the parser for the external parameter entity (context must be 0 for this call), it is illegal to make any calls on the old parser until XML_ParserFree has been called on the newly created parser. If the library has been compiled without support for parameter entity parsing (ie without XML_DTD being defined), then XML_SetParamEntityParsing will return 0 if parsing of parameter 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. */ XMLPARSEAPI(enum XML_Error) XML_GetErrorCode(XML_Parser parser); /* These functions return information about the current parse location. They may be called from any callback called to report some parse event; in this case the location is the location of the first of the sequence of characters that generated the event. When called from callbacks generated by declarations in the document prologue, the location identified isn't as neatly defined, but will 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); XMLPARSEAPI(XML_Index) XML_GetCurrentByteIndex(XML_Parser parser); /* Return the number of bytes in the current event. Returns 0 if the event is in an internal entity. */ XMLPARSEAPI(int) XML_GetCurrentByteCount(XML_Parser parser); /* If XML_CONTEXT_BYTES is defined, 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 returns a NULL pointer. Also returns a NULL pointer if a parse isn't active. NOTE: The character pointer returned should not be used outside the handler that makes the call. */ XMLPARSEAPI(const char *) 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 /* Frees the content model passed to the element declaration handler */ XMLPARSEAPI(void) 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) XML_MemFree(XML_Parser parser, void *ptr); /* Frees memory used by the parser. */ XMLPARSEAPI(void) XML_ParserFree(XML_Parser parser); /* Returns a string describing the error. */ XMLPARSEAPI(const XML_LChar *) XML_ErrorString(enum XML_Error code); /* Return a string containing the version number of this expat */ XMLPARSEAPI(const XML_LChar *) XML_ExpatVersion(void); typedef struct { int major; int minor; int micro; } XML_Expat_Version; /* Return an XML_Expat_Version structure containing numeric version number information for this version of expat. */ XMLPARSEAPI(XML_Expat_Version) XML_ExpatVersionInfo(void); /* Added in Expat 1.95.5. */ 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, 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 /* Additional features must be added to the end of this enum. */ }; typedef struct { enum XML_FeatureEnum feature; const XML_LChar *name; long int value; } XML_Feature; XMLPARSEAPI(const XML_Feature *) XML_GetFeatureList(void); #ifdef XML_DTD /* Added in Expat 2.4.0. */ XMLPARSEAPI(XML_Bool) XML_SetBillionLaughsAttackProtectionMaximumAmplification( XML_Parser parser, float maximumAmplificationFactor); /* Added in Expat 2.4.0. */ XMLPARSEAPI(XML_Bool) XML_SetBillionLaughsAttackProtectionActivationThreshold( XML_Parser parser, unsigned long long activationThresholdBytes); #endif /* Expat follows the semantic versioning convention. See http://semver.org. */ #define XML_MAJOR_VERSION 2 #define XML_MINOR_VERSION 5 #define XML_MICRO_VERSION 0 #ifdef __cplusplus } #endif #endif /* not Expat_INCLUDED */ astropy-astropy-201cddb/cextern/expat/lib/expat_external.h000066400000000000000000000136151507226315300241400ustar00rootroot00000000000000/* __ __ _ ___\ \/ /_ __ __ _| |_ / _ \\ /| '_ \ / _` | __| | __// \| |_) | (_| | |_ \___/_/\_\ .__/ \__,_|\__| |_| 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 /* External API definitions */ /* 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 achieve some different behavior, but doing so it not recommended or tested frequently. XMLCALL - The calling convention to use for all calls across the "library boundary." This will default to cdecl, and try really hard to tell the compiler that's what we want. XMLIMPORT - Whatever magic is needed to note that a function is to be imported from a dynamically loaded library (.dll, .so, or .sl, depending on your platform). The XMLCALL macro was added in Expat 1.95.7. The only one which is expected to be directly useful in client code is XMLCALL. Note that on at least some Unix versions, the Expat library must be 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 /* 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 do so. If this is the case for your platform, please file a bug report with information on how to identify your platform via the C pre-processor and how to specify the same calling convention as the platform's malloc() implementation. */ # define XMLCALL # endif #endif /* not defined XMLCALL */ #if ! defined(XML_STATIC) && ! defined(XMLIMPORT) # ifndef XML_BUILDING_EXPAT /* using Expat from an application */ # if defined(_MSC_EXTENSIONS) && ! defined(__BEOS__) && ! defined(__CYGWIN__) # define XMLIMPORT __declspec(dllimport) # endif # endif #endif /* not defined XML_STATIC */ #ifndef XML_ENABLE_VISIBILITY # define XML_ENABLE_VISIBILITY 0 #endif #if ! defined(XMLIMPORT) && XML_ENABLE_VISIBILITY # define XMLIMPORT __attribute__((visibility("default"))) #endif /* 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 # 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 typedef unsigned short XML_Char; typedef char XML_LChar; # endif /* XML_UNICODE_WCHAR_T */ #else /* Information is UTF-8 encoded. */ typedef char XML_Char; typedef char XML_LChar; #endif /* XML_UNICODE */ #ifdef XML_LARGE_SIZE /* Use large integers for file/stream positions. */ typedef long long XML_Index; typedef unsigned long long XML_Size; #else typedef long XML_Index; typedef unsigned long XML_Size; #endif /* XML_LARGE_SIZE */ #ifdef __cplusplus } #endif #endif /* not Expat_External_INCLUDED */ astropy-astropy-201cddb/cextern/expat/lib/iasciitab.h000066400000000000000000000070061507226315300230420ustar00rootroot00000000000000/* __ __ _ ___\ \/ /_ __ __ _| |_ / _ \\ /| '_ \ / _` | __| | __// \| |_) | (_| | |_ \___/_/\_\ .__/ \__,_|\__| |_| 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, astropy-astropy-201cddb/cextern/expat/lib/internal.h000066400000000000000000000135131507226315300227260ustar00rootroot00000000000000/* internal.h Internal definitions used by Expat. This is not needed to compile client code. The following calling convention macros are defined for frequently called functions: FASTCALL - Used for those internal functions that have a simple body and a low number of arguments and local variables. PTRCALL - Used for functions called though function pointers. PTRFASTCALL - Like PTRCALL, but for low number of arguments. inline - Used for selected internal functions for which inlining may improve performance on some platforms. 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-2022 Sebastian Pipping Copyright (c) 2018 Yury Gribov Copyright (c) 2019 David Loffredo 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__) /* We'll use this version by default only where we know it helps. regparm() generates warnings on Solaris boxes. See SF bug #692878. Instability reported with egcs on a RedHat Linux 7.3. Let's comment out: #define FASTCALL __attribute__((stdcall, regparm(3))) and let's try this: */ # define FASTCALL __attribute__((regparm(3))) # define PTRFASTCALL __attribute__((regparm(3))) #endif /* Using __fastcall seems to have an unexpected negative effect under MS VC++, especially for function pointers, so we won't use it for now on that platform. It may be reconsidered for a future release if it can be made more effective. Likely reason: __fastcall on Windows is like stdcall, therefore the compiler cannot perform stack optimizations for call clusters. */ /* Make sure all of these are defined if they aren't already. */ #ifndef FASTCALL # define FASTCALL #endif #ifndef PTRCALL # define PTRCALL #endif #ifndef PTRFASTCALL # define PTRFASTCALL #endif #ifndef XML_MIN_SIZE # if ! defined(__cplusplus) && ! defined(inline) # ifdef __GNUC__ # define inline __inline # endif /* __GNUC__ */ # endif #endif /* XML_MIN_SIZE */ #ifdef __cplusplus # define inline inline #else # ifndef inline # define inline # endif #endif #include // ULONG_MAX #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 # 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" # 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 /* 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_DTD) unsigned long long testingAccountingGetCountBytesDirect(XML_Parser parser); unsigned long long testingAccountingGetCountBytesIndirect(XML_Parser parser); const char *unsignedCharToPrintable(unsigned char c); #endif #ifdef __cplusplus } #endif astropy-astropy-201cddb/cextern/expat/lib/latin1tab.h000066400000000000000000000067651507226315300230040ustar00rootroot00000000000000/* __ __ _ ___\ \/ /_ __ __ _| |_ / _ \\ /| '_ \ / _` | __| | __// \| |_) | (_| | |_ \___/_/\_\ .__/ \__,_|\__| |_| 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, astropy-astropy-201cddb/cextern/expat/lib/nametab.h000066400000000000000000000215241507226315300225220ustar00rootroot00000000000000/* __ __ _ ___\ \/ /_ __ __ _| |_ / _ \\ /| '_ \ / _` | __| | __// \| |_) | (_| | |_ \___/_/\_\ .__/ \__,_|\__| |_| 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, }; 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, }; 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, }; astropy-astropy-201cddb/cextern/expat/lib/siphash.h000066400000000000000000000314241507226315300225520ustar00rootroot00000000000000/* ========================================================================== * 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 */ astropy-astropy-201cddb/cextern/expat/lib/utf8tab.h000066400000000000000000000067021507226315300224710ustar00rootroot00000000000000/* __ __ _ ___\ \/ /_ __ __ _| |_ / _ \\ /| '_ \ / _` | __| | __// \| |_) | (_| | |_ \___/_/\_\ .__/ \__,_|\__| |_| 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, astropy-astropy-201cddb/cextern/expat/lib/winconfig.h000066400000000000000000000036131507226315300230750ustar00rootroot00000000000000/* __ __ _ ___\ \/ /_ __ __ _| |_ / _ \\ /| '_ \ / _` | __| | __// \| |_) | (_| | |_ \___/_/\_\ .__/ \__,_|\__| |_| XML parser Copyright (c) 2000 Clark Cooper Copyright (c) 2002 Greg Stein Copyright (c) 2005 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. */ #ifndef WINCONFIG_H #define WINCONFIG_H #define WIN32_LEAN_AND_MEAN #include #undef WIN32_LEAN_AND_MEAN #include #include #endif /* ndef WINCONFIG_H */ astropy-astropy-201cddb/cextern/expat/lib/xmlparse.c000066400000000000000000010435421507226315300227460ustar00rootroot00000000000000/* 5ab094ffadd6edfc94c3eee53af44a86951f9f1f0933ada3114bbce2bfb02c99 (2.5.0+) __ __ _ ___\ \/ /_ __ __ _| |_ / _ \\ /| '_ \ / _` | __| | __// \| |_) | (_| | |_ \___/_/\_\ .__/ \__,_|\__| |_| 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-2022 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 Dong-hee Na Copyright (c) 2022 Samanta Navarro Copyright (c) 2022 Jeffrey Walton Copyright (c) 2022 Jann Horn 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 #if ! defined(_GNU_SOURCE) # define _GNU_SOURCE 1 /* syscall prototype */ #endif #ifdef _WIN32 /* force stdlib to define rand_s() */ # if ! defined(_CRT_RAND_S) # define _CRT_RAND_S # endif #endif #include #include /* memset(), memcpy() */ #include #include /* UINT_MAX */ #include /* fprintf */ #include /* getenv, rand_s */ #include /* uintptr_t */ #include /* isnan */ #ifdef _WIN32 # define getpid GetCurrentProcessId #else # include /* gettimeofday() */ # include /* getpid() */ # include /* getpid() */ # include /* O_RDONLY */ # include #endif #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 (arc4random_buf): HAVE_ARC4RANDOM_BUF, \ * BSD / macOS (including <10.7) (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 # 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) typedef char ICHAR; #endif #ifndef XML_NS # 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 #else # 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)) /* Do safe (NULL-aware) pointer arithmetic */ #define EXPAT_SAFE_PTR_DIFF(p, q) (((p) && (q)) ? ((p) - (q)) : 0) #include "internal.h" #include "xmltok.h" #include "xmlrole.h" typedef const XML_Char *KEY; typedef struct { KEY name; } NAMED; typedef struct { NAMED **v; unsigned char power; size_t size; size_t used; const XML_Memory_Handling_Suite *mem; } HASH_TABLE; static size_t keylen(KEY s); 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, since we can calculate a second hash value cheaply by taking those bits of the first hash value that were discarded (masked out) when the table index was calculated: index = hash & mask, where mask = table->size - 1. 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) \ ((((hash) & ~(mask)) >> ((power)-1)) & ((mask) >> 2)) #define PROBE_STEP(hash, mask, power) \ ((unsigned char)((SECOND_HASH(hash, mask, power)) | 1)) typedef struct { NAMED **p; NAMED **end; } HASH_TABLE_ITER; #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 #define INIT_BLOCK_SIZE 1024 #define INIT_BUFFER_SIZE 1024 #define EXPAND_SPARE 24 typedef struct binding { struct prefix *prefix; struct binding *nextTagBinding; struct binding *prevPrefixBinding; const struct attribute_id *attId; XML_Char *uri; int uriLen; int uriAlloc; } BINDING; typedef struct prefix { const XML_Char *name; BINDING *binding; } PREFIX; typedef struct { const XML_Char *str; const XML_Char *localPart; const XML_Char *prefix; int strLen; int uriLen; int prefixLen; } TAG_NAME; /* TAG represents an open element. 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' 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 TAG objects in a free list. */ typedef struct tag { 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 */ 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 */ const XML_Char *systemId; const XML_Char *base; const XML_Char *publicId; const XML_Char *notation; XML_Bool open; 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; } CONTENT_SCAFFOLD; #define INIT_SCAFFOLD_ELEMENTS 32 typedef struct block { struct block *next; int size; XML_Char s[1]; } BLOCK; typedef struct { BLOCK *blocks; BLOCK *freeBlocks; const XML_Char *end; XML_Char *ptr; XML_Char *start; const XML_Memory_Handling_Suite *mem; } STRING_POOL; /* The XML_Char before the name is used to determine whether an attribute has been specified. */ typedef struct attribute_id { XML_Char *name; PREFIX *prefix; XML_Bool maybeTokenized; XML_Bool xmlns; } ATTRIBUTE_ID; typedef struct { const ATTRIBUTE_ID *id; XML_Bool isCdata; const XML_Char *value; } DEFAULT_ATTRIBUTE; typedef struct { unsigned long version; unsigned long hash; const XML_Char *uriName; } NS_ATT; typedef struct { const XML_Char *name; PREFIX *prefix; const ATTRIBUTE_ID *idAtt; int nDefaultAtts; int allocDefaultAtts; DEFAULT_ATTRIBUTE *defaultAtts; } ELEMENT_TYPE; typedef struct { HASH_TABLE generalEntities; HASH_TABLE elementTypes; HASH_TABLE attributeIds; HASH_TABLE prefixes; STRING_POOL pool; STRING_POOL entityValuePool; /* false once a parameter entity reference has been skipped */ XML_Bool keepProcessing; /* true once an internal or external PE reference has been encountered; this includes the reference to an external subset */ XML_Bool hasParamEntityRefs; XML_Bool standalone; #ifdef XML_DTD /* indicates if external PE has been read */ XML_Bool paramEntityRead; HASH_TABLE paramEntities; #endif /* XML_DTD */ PREFIX defaultPrefix; /* === scaffolding for building content model === */ XML_Bool in_eldecl; CONTENT_SCAFFOLD *scaffold; unsigned contentStringLen; unsigned scaffSize; unsigned scaffCount; int scaffLevel; int *scaffIndex; } DTD; typedef struct open_internal_entity { const char *internalEventPtr; const char *internalEventEndPtr; struct open_internal_entity *next; ENTITY *entity; int startTagLevel; XML_Bool betweenDecl; /* WFC: PE Between Declarations */ } OPEN_INTERNAL_ENTITY; 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 */ }; #ifdef XML_DTD typedef unsigned long long XmlBigCount; typedef struct accounting { XmlBigCount countBytesDirect; XmlBigCount countBytesIndirect; int debugLevel; float maximumAmplificationFactor; // >=1.0 unsigned long long activationThresholdBytes; } ACCOUNTING; typedef struct entity_stats { unsigned int countEverOpened; unsigned int currentDepth; unsigned int maximumDepthSeen; int debugLevel; } ENTITY_STATS; #endif /* XML_DTD */ typedef enum XML_Error PTRCALL Processor(XML_Parser parser, const char *start, const char *end, const char **endPtr); static Processor prologProcessor; static Processor prologInitProcessor; static Processor contentProcessor; static Processor cdataSectionProcessor; #ifdef XML_DTD static Processor ignoreSectionProcessor; static Processor externalParEntProcessor; static Processor externalParEntInitProcessor; static Processor entityValueProcessor; static Processor entityValueInitProcessor; #endif /* XML_DTD */ static Processor epilogProcessor; static Processor errorProcessor; static Processor externalEntityInitProcessor; static Processor externalEntityInitProcessor2; 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, XML_Bool allowClosingDoctype, enum XML_Account account); 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, enum XML_Account account); static enum XML_Error doCdataSection(XML_Parser parser, const ENCODING *, 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); #endif /* XML_DTD */ static void freeBindings(XML_Parser parser, BINDING *bindings); static enum XML_Error storeAtts(XML_Parser parser, const ENCODING *, const char *s, 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 *, 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 *, enum XML_Account account); static enum XML_Error appendAttributeValue(XML_Parser parser, const ENCODING *, XML_Bool isCdata, const char *, const char *, STRING_POOL *, enum XML_Account account); 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, enum XML_Account account); 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 void FASTCALL normalizePublicId(XML_Char *s); static DTD *dtdCreate(const XML_Memory_Handling_Suite *ms); /* do not call if m_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(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd, const XML_Memory_Handling_Suite *ms); static int copyEntityTable(XML_Parser oldParser, HASH_TABLE *, STRING_POOL *, const HASH_TABLE *); static NAMED *lookup(XML_Parser parser, 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 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 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_Char *copyString(const XML_Char *s, const XML_Memory_Handling_Suite *memsuite); 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); static void parserInit(XML_Parser parser, const XML_Char *encodingName); #ifdef XML_DTD 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); static XML_Parser getRootParserOf(XML_Parser parser, unsigned int *outLevelDiff); #endif /* XML_DTD */ 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)) struct XML_ParserStruct { /* The first member must be m_userData so that the XML_GetUserData macro works. */ void *m_userData; void *m_handlerArg; char *m_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 m_buffer */ const char *m_bufferLim; XML_Index m_parseEndByteIndex; const char *m_parseEndPtr; XML_Char *m_dataBuf; XML_Char *m_dataBufEnd; XML_StartElementHandler m_startElementHandler; XML_EndElementHandler m_endElementHandler; XML_CharacterDataHandler m_characterDataHandler; XML_ProcessingInstructionHandler m_processingInstructionHandler; XML_CommentHandler m_commentHandler; XML_StartCdataSectionHandler m_startCdataSectionHandler; XML_EndCdataSectionHandler m_endCdataSectionHandler; XML_DefaultHandler m_defaultHandler; XML_StartDoctypeDeclHandler m_startDoctypeDeclHandler; XML_EndDoctypeDeclHandler m_endDoctypeDeclHandler; XML_UnparsedEntityDeclHandler m_unparsedEntityDeclHandler; XML_NotationDeclHandler m_notationDeclHandler; XML_StartNamespaceDeclHandler m_startNamespaceDeclHandler; XML_EndNamespaceDeclHandler m_endNamespaceDeclHandler; XML_NotStandaloneHandler m_notStandaloneHandler; XML_ExternalEntityRefHandler m_externalEntityRefHandler; XML_Parser m_externalEntityRefHandlerArg; XML_SkippedEntityHandler m_skippedEntityHandler; XML_UnknownEncodingHandler m_unknownEncodingHandler; XML_ElementDeclHandler m_elementDeclHandler; XML_AttlistDeclHandler m_attlistDeclHandler; XML_EntityDeclHandler m_entityDeclHandler; XML_XmlDeclHandler m_xmlDeclHandler; const ENCODING *m_encoding; INIT_ENCODING m_initEncoding; const ENCODING *m_internalEncoding; const XML_Char *m_protocolEncodingName; XML_Bool m_ns; XML_Bool m_ns_triplets; void *m_unknownEncodingMem; void *m_unknownEncodingData; void *m_unknownEncodingHandlerData; void(XMLCALL *m_unknownEncodingRelease)(void *); PROLOG_STATE m_prologState; Processor *m_processor; enum XML_Error m_errorCode; const char *m_eventPtr; const char *m_eventEndPtr; const char *m_positionPtr; OPEN_INTERNAL_ENTITY *m_openInternalEntities; OPEN_INTERNAL_ENTITY *m_freeInternalEntities; XML_Bool m_defaultExpandInternalEntities; int m_tagLevel; ENTITY *m_declEntity; const XML_Char *m_doctypeName; const XML_Char *m_doctypeSysid; const XML_Char *m_doctypePubid; const XML_Char *m_declAttributeType; const XML_Char *m_declNotationName; const XML_Char *m_declNotationPublicId; ELEMENT_TYPE *m_declElementType; ATTRIBUTE_ID *m_declAttributeId; XML_Bool m_declAttributeIsCdata; XML_Bool m_declAttributeIsId; DTD *m_dtd; const XML_Char *m_curBase; TAG *m_tagStack; TAG *m_freeTagList; BINDING *m_inheritedBindings; BINDING *m_freeBindingList; int m_attsSize; int m_nSpecifiedAtts; int m_idAttIndex; ATTRIBUTE *m_atts; 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; char *m_groupConnector; unsigned int m_groupSize; XML_Char m_namespaceSeparator; XML_Parser m_parentParser; XML_ParsingStatus m_parsingStatus; #ifdef XML_DTD XML_Bool m_isParamEntity; XML_Bool m_useForeignDTD; enum XML_ParamEntityParsing m_paramEntityParsing; #endif unsigned long m_hash_secret_salt; #ifdef XML_DTD ACCOUNTING m_accounting; ENTITY_STATS m_entity_stats; #endif }; #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))) XML_Parser XMLCALL 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] = {nsSep, 0}; return XML_ParserCreate_MM(encodingName, NULL, tmp); } // "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'}; /* 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; const int bytesWrittenMore = # if defined(HAVE_GETRANDOM) getrandom(currentTarget, bytesToWrite, getrandomFlags); # else 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) { if (parser->m_parentParser != NULL) return get_hash_secret_salt(parser->m_parentParser); return parser->m_hash_secret_salt; } 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 */ return setContext(parser, implicitContext); } 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); } static XML_Parser parserCreate(const XML_Char *encodingName, const XML_Memory_Handling_Suite *memsuite, const XML_Char *nameSep, DTD *dtd) { XML_Parser parser; if (memsuite) { XML_Memory_Handling_Suite *mtemp; parser = memsuite->malloc_fcn(sizeof(struct XML_ParserStruct)); if (parser != NULL) { 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 { XML_Memory_Handling_Suite *mtemp; parser = (XML_Parser)malloc(sizeof(struct XML_ParserStruct)); if (parser != NULL) { mtemp = (XML_Memory_Handling_Suite *)&(parser->m_mem); mtemp->malloc_fcn = malloc; mtemp->realloc_fcn = realloc; mtemp->free_fcn = free; } } if (! parser) return parser; parser->m_buffer = NULL; parser->m_bufferLim = NULL; parser->m_attsSize = INIT_ATTS_SIZE; parser->m_atts = (ATTRIBUTE *)MALLOC(parser, parser->m_attsSize * sizeof(ATTRIBUTE)); if (parser->m_atts == NULL) { FREE(parser, parser); return NULL; } #ifdef XML_ATTR_INFO parser->m_attInfo = (XML_AttrInfo *)MALLOC( parser, parser->m_attsSize * sizeof(XML_AttrInfo)); if (parser->m_attInfo == NULL) { FREE(parser, parser->m_atts); FREE(parser, parser); return NULL; } #endif parser->m_dataBuf = (XML_Char *)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; } parser->m_dataBufEnd = parser->m_dataBuf + INIT_DATA_BUF_SIZE; if (dtd) parser->m_dtd = dtd; else { parser->m_dtd = dtdCreate(&parser->m_mem); 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; } } parser->m_freeBindingList = NULL; parser->m_freeTagList = NULL; parser->m_freeInternalEntities = NULL; parser->m_groupSize = 0; parser->m_groupConnector = NULL; parser->m_unknownEncodingHandler = NULL; parser->m_unknownEncodingHandlerData = NULL; parser->m_namespaceSeparator = ASCII_EXCL; parser->m_ns = XML_FALSE; parser->m_ns_triplets = XML_FALSE; parser->m_nsAtts = NULL; parser->m_nsAttsVersion = 0; parser->m_nsAttsPower = 0; parser->m_protocolEncodingName = NULL; poolInit(&parser->m_tempPool, &(parser->m_mem)); poolInit(&parser->m_temp2Pool, &(parser->m_mem)); parserInit(parser, encodingName); 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) { 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) { parser->m_processor = prologInitProcessor; XmlPrologStateInit(&parser->m_prologState); if (encodingName != NULL) { parser->m_protocolEncodingName = copyString(encodingName, &(parser->m_mem)); } 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_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_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_parentParser = NULL; parser->m_parsingStatus.parsing = XML_INITIALIZED; #ifdef XML_DTD 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; #ifdef XML_DTD 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 m_freeBindingList */ static void FASTCALL moveToFreeBindingList(XML_Parser parser, BINDING *bindings) { while (bindings) { BINDING *b = bindings; bindings = bindings->nextTagBinding; b->nextTagBinding = parser->m_freeBindingList; parser->m_freeBindingList = b; } } XML_Bool XMLCALL XML_ParserReset(XML_Parser parser, const XML_Char *encodingName) { TAG *tStk; OPEN_INTERNAL_ENTITY *openEntityList; if (parser == NULL) return XML_FALSE; if (parser->m_parentParser) return XML_FALSE; /* move m_tagStack to m_freeTagList */ tStk = parser->m_tagStack; while (tStk) { TAG *tag = tStk; tStk = tStk->parent; tag->parent = parser->m_freeTagList; moveToFreeBindingList(parser, tag->bindings); tag->bindings = NULL; parser->m_freeTagList = tag; } /* move m_openInternalEntities to m_freeInternalEntities */ openEntityList = parser->m_openInternalEntities; while (openEntityList) { OPEN_INTERNAL_ENTITY *openEntity = openEntityList; openEntityList = openEntity->next; openEntity->next = parser->m_freeInternalEntities; parser->m_freeInternalEntities = 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(parser->m_dtd, &parser->m_mem); return XML_TRUE; } enum XML_Status XMLCALL 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 (parser->m_parsingStatus.parsing == XML_PARSING || parser->m_parsingStatus.parsing == XML_SUSPENDED) return XML_STATUS_ERROR; /* Get rid of any previous encoding name */ FREE(parser, (void *)parser->m_protocolEncodingName); if (encodingName == NULL) /* No new encoding name */ parser->m_protocolEncodingName = NULL; else { /* Copy the new encoding name into allocated memory */ parser->m_protocolEncodingName = copyString(encodingName, &(parser->m_mem)); 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_Parser parser = oldParser; DTD *newDtd = NULL; 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; int oldInEntityValue; #endif 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; /* 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 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; #ifdef XML_DTD if (! context) newDtd = oldDtd; #endif /* XML_DTD */ /* Note that the magical uses of the pre-processor to make field access look more like C++ require that `parser' be overwritten here. This makes this function more painful to follow than it would be otherwise. */ if (parser->m_ns) { XML_Char tmp[2] = {parser->m_namespaceSeparator, 0}; parser = parserCreate(encodingName, &parser->m_mem, tmp, newDtd); } else { parser = parserCreate(encodingName, &parser->m_mem, NULL, newDtd); } if (! parser) return NULL; 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) parser->m_handlerArg = parser->m_userData; else parser->m_handlerArg = parser; if (oldExternalEntityRefHandlerArg != 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_parentParser = oldParser; #ifdef XML_DTD parser->m_paramEntityParsing = oldParamEntityParsing; parser->m_prologState.inEntityValue = oldInEntityValue; if (context) { #endif /* XML_DTD */ if (! dtdCopy(oldParser, parser->m_dtd, oldDtd, &parser->m_mem) || ! setContext(parser, context)) { XML_ParserFree(parser); return NULL; } parser->m_processor = externalEntityInitProcessor; #ifdef XML_DTD } 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. */ 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) { for (;;) { BINDING *b = bindings; if (! b) break; bindings = b->nextTagBinding; FREE(parser, b->uri); FREE(parser, b); } } void XMLCALL XML_ParserFree(XML_Parser parser) { TAG *tagList; OPEN_INTERNAL_ENTITY *entityList; if (parser == NULL) return; /* free m_tagStack and m_freeTagList */ tagList = parser->m_tagStack; for (;;) { TAG *p; if (tagList == NULL) { if (parser->m_freeTagList == NULL) break; tagList = parser->m_freeTagList; parser->m_freeTagList = NULL; } p = tagList; tagList = tagList->parent; FREE(parser, p->buf); destroyBindings(p->bindings, parser); FREE(parser, p); } /* free m_openInternalEntities and m_freeInternalEntities */ entityList = parser->m_openInternalEntities; for (;;) { OPEN_INTERNAL_ENTITY *openEntity; if (entityList == NULL) { if (parser->m_freeInternalEntities == NULL) break; entityList = parser->m_freeInternalEntities; parser->m_freeInternalEntities = 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 (! parser->m_isParamEntity && parser->m_dtd) #else if (parser->m_dtd) #endif /* XML_DTD */ dtdDestroy(parser->m_dtd, (XML_Bool)! parser->m_parentParser, &parser->m_mem); FREE(parser, (void *)parser->m_atts); #ifdef XML_ATTR_INFO FREE(parser, (void *)parser->m_attInfo); #endif FREE(parser, parser->m_groupConnector); FREE(parser, 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) { if (parser != NULL) parser->m_handlerArg = parser; } enum XML_Error XMLCALL 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 (parser->m_parsingStatus.parsing == XML_PARSING || parser->m_parsingStatus.parsing == XML_SUSPENDED) return XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING; 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) { if (parser == NULL) return; /* block after XML_Parse()/XML_ParseBuffer() has been called */ if (parser->m_parsingStatus.parsing == XML_PARSING || parser->m_parsingStatus.parsing == XML_SUSPENDED) return; parser->m_ns_triplets = do_nst ? XML_TRUE : XML_FALSE; } void XMLCALL 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 parser->m_userData = p; } enum XML_Status XMLCALL XML_SetBase(XML_Parser parser, const XML_Char *p) { if (parser == NULL) return XML_STATUS_ERROR; if (p) { p = poolCopyString(&parser->m_dtd->pool, p); if (! p) return XML_STATUS_ERROR; parser->m_curBase = p; } else parser->m_curBase = NULL; return XML_STATUS_OK; } 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) { if (parser == NULL) return -1; return parser->m_nSpecifiedAtts; } int XMLCALL 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) { if (parser == NULL) return; parser->m_startElementHandler = start; parser->m_endElementHandler = end; } void XMLCALL 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) { if (parser != NULL) parser->m_endElementHandler = end; } void XMLCALL XML_SetCharacterDataHandler(XML_Parser parser, XML_CharacterDataHandler handler) { if (parser != NULL) parser->m_characterDataHandler = handler; } void XMLCALL XML_SetProcessingInstructionHandler(XML_Parser parser, XML_ProcessingInstructionHandler handler) { if (parser != NULL) parser->m_processingInstructionHandler = handler; } void XMLCALL 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) { if (parser == NULL) return; parser->m_startCdataSectionHandler = start; parser->m_endCdataSectionHandler = end; } void XMLCALL XML_SetStartCdataSectionHandler(XML_Parser parser, XML_StartCdataSectionHandler start) { if (parser != NULL) parser->m_startCdataSectionHandler = start; } void XMLCALL XML_SetEndCdataSectionHandler(XML_Parser parser, XML_EndCdataSectionHandler end) { if (parser != NULL) parser->m_endCdataSectionHandler = end; } void XMLCALL 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) { 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) { if (parser == NULL) return; parser->m_startDoctypeDeclHandler = start; parser->m_endDoctypeDeclHandler = end; } void XMLCALL XML_SetStartDoctypeDeclHandler(XML_Parser parser, XML_StartDoctypeDeclHandler start) { if (parser != NULL) parser->m_startDoctypeDeclHandler = start; } void XMLCALL 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) { if (parser != NULL) parser->m_unparsedEntityDeclHandler = handler; } void XMLCALL 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) { if (parser == NULL) return; parser->m_startNamespaceDeclHandler = start; parser->m_endNamespaceDeclHandler = end; } void XMLCALL XML_SetStartNamespaceDeclHandler(XML_Parser parser, XML_StartNamespaceDeclHandler start) { if (parser != NULL) parser->m_startNamespaceDeclHandler = start; } void XMLCALL XML_SetEndNamespaceDeclHandler(XML_Parser parser, XML_EndNamespaceDeclHandler end) { if (parser != NULL) parser->m_endNamespaceDeclHandler = end; } void XMLCALL XML_SetNotStandaloneHandler(XML_Parser parser, XML_NotStandaloneHandler handler) { if (parser != NULL) parser->m_notStandaloneHandler = handler; } void XMLCALL XML_SetExternalEntityRefHandler(XML_Parser parser, XML_ExternalEntityRefHandler handler) { if (parser != NULL) parser->m_externalEntityRefHandler = handler; } void XMLCALL XML_SetExternalEntityRefHandlerArg(XML_Parser parser, void *arg) { if (parser == NULL) return; if (arg) parser->m_externalEntityRefHandlerArg = (XML_Parser)arg; else parser->m_externalEntityRefHandlerArg = parser; } void XMLCALL XML_SetSkippedEntityHandler(XML_Parser parser, XML_SkippedEntityHandler handler) { if (parser != NULL) parser->m_skippedEntityHandler = handler; } void XMLCALL XML_SetUnknownEncodingHandler(XML_Parser parser, 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) { if (parser != NULL) parser->m_elementDeclHandler = eldecl; } void XMLCALL 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) { if (parser != NULL) parser->m_entityDeclHandler = handler; } void XMLCALL 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) { if (parser == NULL) return 0; /* block after XML_Parse()/XML_ParseBuffer() has been called */ if (parser->m_parsingStatus.parsing == XML_PARSING || parser->m_parsingStatus.parsing == XML_SUSPENDED) return 0; #ifdef XML_DTD 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; if (parser->m_parentParser) return XML_SetHashSalt(parser->m_parentParser, hash_salt); /* block after XML_Parse()/XML_ParseBuffer() has been called */ if (parser->m_parsingStatus.parsing == XML_PARSING || parser->m_parsingStatus.parsing == XML_SUSPENDED) return 0; parser->m_hash_secret_salt = hash_salt; return 1; } enum XML_Status XMLCALL 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: parser->m_errorCode = XML_ERROR_SUSPENDED; return XML_STATUS_ERROR; case XML_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: parser->m_parsingStatus.parsing = XML_PARSING; } if (len == 0) { parser->m_parsingStatus.finalBuffer = (XML_Bool)isFinal; if (! isFinal) return XML_STATUS_OK; parser->m_positionPtr = parser->m_bufferPtr; parser->m_parseEndPtr = parser->m_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. */ parser->m_errorCode = parser->m_processor(parser, parser->m_bufferPtr, parser->m_parseEndPtr, &parser->m_bufferPtr); if (parser->m_errorCode == XML_ERROR_NONE) { switch (parser->m_parsingStatus.parsing) { case XML_SUSPENDED: /* It is hard to be certain, but it seems that this case * cannot occur. This code is cleaning up a previous parse * with no new data (since len == 0). Changing the parsing * state requires getting to execute a handler function, and * there doesn't seem to be an opportunity for that while in * this circumstance. * * Given the uncertainty, we retain the code but exclude it * from coverage tests. * * LCOV_EXCL_START */ XmlUpdatePosition(parser->m_encoding, parser->m_positionPtr, parser->m_bufferPtr, &parser->m_position); parser->m_positionPtr = parser->m_bufferPtr; return XML_STATUS_SUSPENDED; /* LCOV_EXCL_STOP */ case XML_INITIALIZED: case XML_PARSING: parser->m_parsingStatus.parsing = XML_FINISHED; /* fall through */ default: return XML_STATUS_OK; } } parser->m_eventEndPtr = parser->m_eventPtr; parser->m_processor = errorProcessor; return XML_STATUS_ERROR; } #ifndef XML_CONTEXT_BYTES else if (parser->m_bufferPtr == parser->m_bufferEnd) { const char *end; int nLeftOver; 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; } parser->m_parseEndByteIndex += len; parser->m_positionPtr = s; parser->m_parsingStatus.finalBuffer = (XML_Bool)isFinal; parser->m_errorCode = parser->m_processor(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: if (isFinal) { parser->m_parsingStatus.parsing = XML_FINISHED; return XML_STATUS_OK; } /* fall through */ default: result = XML_STATUS_OK; } } XmlUpdatePosition(parser->m_encoding, parser->m_positionPtr, end, &parser->m_position); nLeftOver = s + len - end; if (nLeftOver) { if (parser->m_buffer == NULL || nLeftOver > parser->m_bufferLim - parser->m_buffer) { /* avoid _signed_ integer overflow */ char *temp = NULL; const int bytesToAllocate = (int)((unsigned)len * 2U); if (bytesToAllocate > 0) { temp = (char *)REALLOC(parser, parser->m_buffer, bytesToAllocate); } if (temp == NULL) { parser->m_errorCode = XML_ERROR_NO_MEMORY; parser->m_eventPtr = parser->m_eventEndPtr = NULL; parser->m_processor = errorProcessor; return XML_STATUS_ERROR; } parser->m_buffer = temp; parser->m_bufferLim = parser->m_buffer + bytesToAllocate; } memcpy(parser->m_buffer, end, nLeftOver); } 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); } } } enum XML_Status XMLCALL XML_ParseBuffer(XML_Parser parser, int len, int isFinal) { const char *start; enum XML_Status result = XML_STATUS_OK; if (parser == NULL) return XML_STATUS_ERROR; switch (parser->m_parsingStatus.parsing) { case XML_SUSPENDED: parser->m_errorCode = XML_ERROR_SUSPENDED; return XML_STATUS_ERROR; case XML_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: parser->m_parsingStatus.parsing = XML_PARSING; } 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; parser->m_errorCode = parser->m_processor( parser, start, parser->m_parseEndPtr, &parser->m_bufferPtr); 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: if (isFinal) { parser->m_parsingStatus.parsing = XML_FINISHED; return result; } default:; /* should not happen */ } } 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) { 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: parser->m_errorCode = XML_ERROR_SUSPENDED; return NULL; case XML_FINISHED: parser->m_errorCode = XML_ERROR_FINISHED; return NULL; default:; } if (len > EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_bufferEnd)) { #ifdef XML_CONTEXT_BYTES int keep; #endif /* defined XML_CONTEXT_BYTES */ /* 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; } #ifdef XML_CONTEXT_BYTES 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 <= EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_buffer)) { #ifdef XML_CONTEXT_BYTES 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 if (parser->m_buffer && parser->m_bufferPtr) { 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 /* not defined XML_CONTEXT_BYTES */ } else { char *newBuf; int bufferSize = (int)EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_bufferPtr); if (bufferSize == 0) bufferSize = INIT_BUFFER_SIZE; do { /* 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; } newBuf = (char *)MALLOC(parser, bufferSize); if (newBuf == 0) { parser->m_errorCode = XML_ERROR_NO_MEMORY; return NULL; } parser->m_bufferLim = newBuf + bufferSize; #ifdef XML_CONTEXT_BYTES if (parser->m_bufferPtr) { memcpy(newBuf, &parser->m_bufferPtr[-keep], EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr) + keep); FREE(parser, 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 (parser->m_bufferPtr) { memcpy(newBuf, parser->m_bufferPtr, EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr)); FREE(parser, 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; } parser->m_bufferPtr = parser->m_buffer = newBuf; #endif /* not defined XML_CONTEXT_BYTES */ } parser->m_eventPtr = parser->m_eventEndPtr = NULL; parser->m_positionPtr = NULL; } return parser->m_bufferEnd; } enum XML_Status XMLCALL XML_StopParser(XML_Parser parser, XML_Bool resumable) { if (parser == NULL) return XML_STATUS_ERROR; switch (parser->m_parsingStatus.parsing) { case XML_SUSPENDED: if (resumable) { parser->m_errorCode = XML_ERROR_SUSPENDED; return XML_STATUS_ERROR; } parser->m_parsingStatus.parsing = XML_FINISHED; break; case XML_FINISHED: parser->m_errorCode = XML_ERROR_FINISHED; return XML_STATUS_ERROR; default: if (resumable) { #ifdef XML_DTD if (parser->m_isParamEntity) { parser->m_errorCode = XML_ERROR_SUSPEND_PE; return XML_STATUS_ERROR; } #endif parser->m_parsingStatus.parsing = XML_SUSPENDED; } else parser->m_parsingStatus.parsing = XML_FINISHED; } return XML_STATUS_OK; } enum XML_Status XMLCALL XML_ResumeParser(XML_Parser parser) { enum XML_Status result = XML_STATUS_OK; 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; } parser->m_parsingStatus.parsing = XML_PARSING; parser->m_errorCode = parser->m_processor( parser, parser->m_bufferPtr, parser->m_parseEndPtr, &parser->m_bufferPtr); 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: if (parser->m_parsingStatus.finalBuffer) { parser->m_parsingStatus.parsing = XML_FINISHED; return result; } default:; } } 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) { if (parser == NULL) return; assert(status != NULL); *status = parser->m_parsingStatus; } enum XML_Error XMLCALL 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 (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 (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 (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; } #else (void)parser; (void)offset; (void)size; #endif /* defined XML_CONTEXT_BYTES */ return (const char *)0; } XML_Size XMLCALL 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 parser->m_position.lineNumber + 1; } XML_Size XMLCALL 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 parser->m_position.columnNumber; } void XMLCALL XML_FreeContentModel(XML_Parser parser, XML_Content *model) { if (parser != NULL) FREE(parser, model); } void *XMLCALL XML_MemMalloc(XML_Parser parser, size_t size) { if (parser == NULL) return NULL; return MALLOC(parser, size); } void *XMLCALL XML_MemRealloc(XML_Parser parser, void *ptr, size_t size) { if (parser == NULL) return NULL; return REALLOC(parser, ptr, size); } void XMLCALL XML_MemFree(XML_Parser parser, void *ptr) { if (parser != NULL) FREE(parser, ptr); } void XMLCALL 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, 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"); } return NULL; } 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 a macro, then rescan for more expansions. Thus, we use V2 to expand the version macros, then CPP will expand the resulting V1() macro 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) return V2(XML_MAJOR_VERSION, XML_MINOR_VERSION, XML_MICRO_VERSION); #undef V1 #undef V2 } XML_Expat_Version XMLCALL XML_ExpatVersionInfo(void) { XML_Expat_Version version; version.major = XML_MAJOR_VERSION; version.minor = XML_MINOR_VERSION; version.micro = XML_MICRO_VERSION; return version; } 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)}, #ifdef XML_UNICODE {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}, #endif #ifdef XML_DTD {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}, #endif #ifdef XML_MIN_SIZE {XML_FEATURE_MIN_SIZE, XML_L("XML_MIN_SIZE"), 0}, #endif #ifdef XML_NS {XML_FEATURE_NS, XML_L("XML_NS"), 0}, #endif #ifdef XML_LARGE_SIZE {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 #ifdef XML_DTD /* Added in Expat 2.4.0. */ {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}, #endif {XML_FEATURE_END, NULL, 0}}; return features; } #ifdef XML_DTD 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; } #endif /* XML_DTD */ /* 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 = parser->m_tagStack; while (tag) { int bufSize; int nameLen = sizeof(XML_Char) * (tag->name.strLen + 1); size_t rawNameLen; char *rawNameBuf = tag->buf + nameLen; /* 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 size of tag->buf is a multiple of sizeof(XML_Char). */ 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 + (int)rawNameLen; if (bufSize > tag->bufEnd - tag->buf) { char *temp = (char *)REALLOC(parser, tag->buf, bufSize); if (temp == NULL) return XML_FALSE; /* if tag->name.str points to tag->buf (only when namespace processing is off) then we have to update it */ if (tag->name.str == (XML_Char *)tag->buf) tag->name.str = (XML_Char *)temp; /* if tag->name.localPart is set (when namespace processing is on) 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->buf = temp; tag->bufEnd = temp + bufSize; rawNameBuf = temp + nameLen; } memcpy(rawNameBuf, tag->rawName, tag->rawNameLength); tag->rawName = rawNameBuf; tag = tag->parent; } return XML_TRUE; } 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, parser->m_encoding, start, end, endPtr, (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_ACCOUNT_DIRECT); if (result == XML_ERROR_NONE) { 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) { enum XML_Error result = initializeEncoding(parser); if (result != XML_ERROR_NONE) return result; 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) { const char *next = start; /* XmlContentTok doesn't always set the last arg */ int tok = XmlContentTok(parser->m_encoding, start, end, &next); switch (tok) { case XML_TOK_BOM: #ifdef XML_DTD if (! accountingDiffTolerated(parser, tok, start, next, __LINE__, XML_ACCOUNT_DIRECT)) { accountingOnAbort(parser); return XML_ERROR_AMPLIFICATION_LIMIT_BREACH; } #endif /* XML_DTD */ /* 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 && ! parser->m_parsingStatus.finalBuffer) { *endPtr = next; return XML_ERROR_NONE; } start = next; break; case XML_TOK_PARTIAL: if (! parser->m_parsingStatus.finalBuffer) { *endPtr = start; return XML_ERROR_NONE; } parser->m_eventPtr = start; return XML_ERROR_UNCLOSED_TOKEN; case XML_TOK_PARTIAL_CHAR: if (! parser->m_parsingStatus.finalBuffer) { *endPtr = start; return XML_ERROR_NONE; } parser->m_eventPtr = start; return XML_ERROR_PARTIAL_CHAR; } 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) { int tok; const char *next = start; /* XmlContentTok doesn't always set the last arg */ 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 (parser->m_parsingStatus.parsing) { case XML_SUSPENDED: *endPtr = next; return XML_ERROR_NONE; case XML_FINISHED: return XML_ERROR_ABORTED; default: start = next; } } break; case XML_TOK_PARTIAL: if (! parser->m_parsingStatus.finalBuffer) { *endPtr = start; return XML_ERROR_NONE; } return XML_ERROR_UNCLOSED_TOKEN; case XML_TOK_PARTIAL_CHAR: if (! parser->m_parsingStatus.finalBuffer) { *endPtr = start; return XML_ERROR_NONE; } return XML_ERROR_PARTIAL_CHAR; } 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, parser->m_encoding, start, end, endPtr, (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_ACCOUNT_ENTITY_EXPANSION); if (result == XML_ERROR_NONE) { 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, enum XML_Account account) { /* save one level of indirection */ DTD *const dtd = parser->m_dtd; const char **eventPP; const char **eventEndPP; 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); #ifdef XML_DTD 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: if (haveMore) { *nextPtr = s; return XML_ERROR_NONE; } *eventEndPP = end; if (parser->m_characterDataHandler) { XML_Char c = 0xA; 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? */ if (startTagLevel == 0) return XML_ERROR_NO_ELEMENTS; if (parser->m_tagLevel != startTagLevel) return XML_ERROR_ASYNC_ENTITY; *nextPtr = end; return XML_ERROR_NONE; case XML_TOK_NONE: if (haveMore) { *nextPtr = s; return XML_ERROR_NONE; } if (startTagLevel > 0) { if (parser->m_tagLevel != startTagLevel) return XML_ERROR_ASYNC_ENTITY; *nextPtr = s; return XML_ERROR_NONE; } return XML_ERROR_NO_ELEMENTS; case XML_TOK_INVALID: *eventPP = next; return XML_ERROR_INVALID_TOKEN; case XML_TOK_PARTIAL: if (haveMore) { *nextPtr = s; return XML_ERROR_NONE; } return XML_ERROR_UNCLOSED_TOKEN; case XML_TOK_PARTIAL_CHAR: if (haveMore) { *nextPtr = s; 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) { #ifdef XML_DTD /* 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_DTD */ 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; } result = processInternalEntity(parser, entity, XML_FALSE); 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; 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 (parser->m_freeTagList) { tag = parser->m_freeTagList; parser->m_freeTagList = parser->m_freeTagList->parent; } else { tag = (TAG *)MALLOC(parser, sizeof(TAG)); if (! tag) return XML_ERROR_NO_MEMORY; tag->buf = (char *)MALLOC(parser, INIT_TAG_BUF_SIZE); if (! tag->buf) { FREE(parser, 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; } bufSize = (int)(tag->bufEnd - tag->buf) << 1; { char *temp = (char *)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), 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(&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_processor = epilogProcessor; else return epilogProcessor(parser, next, end, nextPtr); } break; case XML_TOK_END_TAG: if (parser->m_tagLevel == startTagLevel) return XML_ERROR_ASYNC_ENTITY; else { int len; const char *rawName; 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; } 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 (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 */ 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 (parser->m_ns_triplets && prefix) { *uri++ = parser->m_namespaceSeparator; while (*prefix) *uri++ = *prefix++; } *uri = XML_T('\0'); } 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 (parser->m_endNamespaceDeclHandler) parser->m_endNamespaceDeclHandler(parser->m_handlerArg, b->prefix->name); tag->bindings = tag->bindings->nextTagBinding; b->nextTagBinding = parser->m_freeBindingList; parser->m_freeBindingList = b; b->prefix->binding = b->prevPrefixBinding; } if ((parser->m_tagLevel == 0) && (parser->m_parsingStatus.parsing != XML_FINISHED)) { if (parser->m_parsingStatus.parsing == XML_SUSPENDED) parser->m_processor = epilogProcessor; else 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 (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 (parser->m_characterDataHandler) { XML_Char c = 0xA; 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 (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; case XML_TOK_TRAILING_RSQB: if (haveMore) { *nextPtr = s; return XML_ERROR_NONE; } if (parser->m_characterDataHandler) { if (MUST_CONVERT(enc, s)) { 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, (XML_Char *)s, (int)((XML_Char *)end - (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? */ if (startTagLevel == 0) { *eventPP = end; return XML_ERROR_NO_ELEMENTS; } 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 = 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(parser->m_handlerArg, (XML_Char *)s, (int)((XML_Char *)next - (XML_Char *)s)); } else if (parser->m_defaultHandler) reportDefault(parser, enc, s, next); } break; case XML_TOK_PI: if (! reportProcessingInstruction(parser, enc, s, next)) return XML_ERROR_NO_MEMORY; break; case XML_TOK_COMMENT: if (! reportComment(parser, enc, s, next)) return XML_ERROR_NO_MEMORY; break; default: /* 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 (parser->m_parsingStatus.parsing) { case XML_SUSPENDED: *nextPtr = next; return XML_ERROR_NONE; case XML_FINISHED: return XML_ERROR_ABORTED; default:; } } /* 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 - check attributes for well-formedness - generate namespace aware attribute names (URI, prefix) - build list of attributes for startElementHandler - default attributes - process namespace declarations (check and report them) - 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, 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 */ int attIndex = 0; int prefixLen; int i; int n; XML_Char *uri; int nPrefixes = 0; BINDING *binding; const XML_Char *localPart; /* lookup the element type name */ elementType = (ELEMENT_TYPE *)lookup(parser, &dtd->elementTypes, tagNamePtr->str, 0); if (! elementType) { const XML_Char *name = poolCopyString(&dtd->pool, tagNamePtr->str); if (! name) return XML_ERROR_NO_MEMORY; elementType = (ELEMENT_TYPE *)lookup(parser, &dtd->elementTypes, name, sizeof(ELEMENT_TYPE)); if (! elementType) return XML_ERROR_NO_MEMORY; 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, 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; #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_t)(-1) / sizeof(ATTRIBUTE)) { parser->m_attsSize = oldAttsSize; return XML_ERROR_NO_MEMORY; } #endif temp = (ATTRIBUTE *)REALLOC(parser, (void *)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_t)(-1) / sizeof(XML_AttrInfo)) { parser->m_attsSize = oldAttsSize; return XML_ERROR_NO_MEMORY; } # endif temp2 = (XML_AttrInfo *)REALLOC(parser, (void *)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, parser->m_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, 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 == 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 (! parser->m_atts[i].normalized) { enum XML_Error result; XML_Bool isCdata = XML_TRUE; /* figure out whether declared as other than CDATA */ if (attId->maybeTokenized) { int j; for (j = 0; j < nDefaultAtts; j++) { if (attId == elementType->defaultAtts[j].id) { isCdata = elementType->defaultAtts[j].isCdata; break; } } } /* normalize the attribute value */ 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(&parser->m_tempPool); poolFinish(&parser->m_tempPool); } else { /* the value did not need normalizing */ 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(&parser->m_tempPool); } /* handle prefixed attribute names */ if (attId->prefix) { if (attId->xmlns) { /* deal with namespace declarations here */ enum XML_Error result = addBinding(parser, attId->prefix, attId, appAtts[attIndex], bindingsPtr); if (result) return result; --attIndex; } else { /* deal with other prefixed names later */ attIndex++; nPrefixes++; (attId->name)[-1] = 2; } } else attIndex++; } /* set-up for XML_GetSpecifiedAttributeCount and XML_GetIdAttributeIndex */ parser->m_nSpecifiedAtts = attIndex; if (elementType->idAtt && (elementType->idAtt->name)[-1]) { for (i = 0; i < attIndex; i += 2) if (appAtts[i] == elementType->idAtt->name) { parser->m_idAttIndex = i; break; } } 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->prefix) { if (da->id->xmlns) { enum XML_Error result = addBinding(parser, da->id->prefix, da->id, da->value, bindingsPtr); if (result) return result; } else { (da->id->name)[-1] = 2; nPrefixes++; appAtts[attIndex++] = da->id->name; appAtts[attIndex++] = da->value; } } else { (da->id->name)[-1] = 1; appAtts[attIndex++] = da->id->name; appAtts[attIndex++] = da->value; } } } appAtts[attIndex] = 0; /* expand prefixed attribute names, check for duplicates, and clear flags that say whether attributes were specified */ i = 0; if (nPrefixes) { 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) >> 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 >> 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_t)(-1) / sizeof(NS_ATT)) { /* Restore actual size of memory in m_nsAtts */ parser->m_nsAttsPower = oldNsAttsPower; return XML_ERROR_NO_MEMORY; } #endif temp = (NS_ATT *)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; } parser->m_nsAtts = temp; version = 0; /* force re-initialization of m_nsAtts hash table */ } /* 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;) parser->m_nsAtts[--j].version = 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 */ ATTRIBUTE_ID *id; const BINDING *b; 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) return XML_ERROR_UNBOUND_PREFIX; for (j = 0; j < b->uriLen; j++) { const XML_Char c = b->uri[j]; if (! poolAppendChar(&parser->m_tempPool, c)) return XML_ERROR_NO_MEMORY; } sip24_update(&sip_state, b->uri, b->uriLen * sizeof(XML_Char)); while (*s++ != XML_T(ASCII_COLON)) ; 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; } while (*s++); uriHash = (unsigned long)sip24_final(&sip_state); { /* Check hash table for duplicate of expanded name (uriName). 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 (parser->m_nsAtts[j].version == version) { /* for speed we compare stored hash values first */ 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++) ; if (*s1 == 0) return XML_ERROR_DUPLICATE_ATTRIBUTE; } if (! step) step = PROBE_STEP(uriHash, mask, parser->m_nsAttsPower); j < step ? (j += nsAttsSize - step) : (j -= step); } } 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(&parser->m_tempPool, *s)) return XML_ERROR_NO_MEMORY; } while (*s++); } /* store expanded name in attribute list */ s = poolStart(&parser->m_tempPool); poolFinish(&parser->m_tempPool); appAtts[i] = s; /* fill empty slot with new version, uriName and hash value */ parser->m_nsAtts[j].version = version; parser->m_nsAtts[j].hash = uriHash; parser->m_nsAtts[j].uriName = s; if (! --nPrefixes) { i += 2; break; } } else /* not prefixed */ ((XML_Char *)s)[-1] = 0; /* clear flag */ } } /* clear flags for the remaining attributes */ for (; i < attIndex; i += 2) ((XML_Char *)(appAtts[i]))[-1] = 0; for (binding = *bindingsPtr; binding; binding = binding->nextTagBinding) binding->attId->name[-1] = 0; if (! parser->m_ns) return XML_ERROR_NONE; /* expand the element type name */ if (elementType->prefix) { binding = elementType->prefix->binding; if (! binding) return XML_ERROR_UNBOUND_PREFIX; localPart = tagNamePtr->str; while (*localPart++ != XML_T(ASCII_COLON)) ; } else if (dtd->defaultPrefix.binding) { binding = dtd->defaultPrefix.binding; localPart = tagNamePtr->str; } else return XML_ERROR_NONE; prefixLen = 0; if (parser->m_ns_triplets && binding->prefix->name) { for (; 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 */ /* 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; /* 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_t)(-1) / sizeof(XML_Char)) { return XML_ERROR_NO_MEMORY; } #endif uri = (XML_Char *)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 = parser->m_tagStack; p; p = p->parent) if (p->name.str == binding->uri) p->name.str = uri; FREE(parser, binding->uri); binding->uri = uri; } /* 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 = 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) { // "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; /* empty URI is only valid for default namespace per XML NS 1.0 (not 1.1) */ if (*uri == XML_T('\0') && prefix->name) return XML_ERROR_UNDECLARING_PREFIX; 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) && prefix->name[5] == XML_T('\0')) return XML_ERROR_RESERVED_PREFIX_XMLNS; if (prefix->name[3] == XML_T('\0')) mustBeXML = XML_TRUE; } for (len = 0; uri[len]; len++) { if (isXML && (len > xmlLen || uri[len] != xmlNamespace[len])) isXML = XML_FALSE; 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; if (mustBeXML != isXML) return mustBeXML ? XML_ERROR_RESERVED_PREFIX_XML : XML_ERROR_RESERVED_NAMESPACE_URI; if (isXMLNS) return XML_ERROR_RESERVED_NAMESPACE_URI; if (parser->m_namespaceSeparator) len++; if (parser->m_freeBindingList) { b = parser->m_freeBindingList; if (len > b->uriAlloc) { /* 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_t)(-1) / sizeof(XML_Char)) { return XML_ERROR_NO_MEMORY; } #endif XML_Char *temp = (XML_Char *)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; } parser->m_freeBindingList = b->nextTagBinding; } else { b = (BINDING *)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_t)(-1) / sizeof(XML_Char)) { return XML_ERROR_NO_MEMORY; } #endif b->uri = (XML_Char *)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 (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 == &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 && parser->m_startNamespaceDeclHandler) parser->m_startNamespaceDeclHandler(parser->m_handlerArg, prefix->name, prefix->binding ? uri : 0); return XML_ERROR_NONE; } /* The idea here is to avoid using stack for each CDATA section when 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, 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 (parser->m_parentParser) { /* we are parsing an external entity */ parser->m_processor = externalEntityContentProcessor; return externalEntityContentProcessor(parser, start, end, endPtr); } else { parser->m_processor = contentProcessor; return contentProcessor(parser, start, end, endPtr); } } return result; } /* startPtr gets set to non-null if the section is closed, and to null if 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, enum XML_Account account) { const char *s = *startPtr; const char **eventPP; const char **eventEndPP; if (enc == parser->m_encoding) { eventPP = &parser->m_eventPtr; *eventPP = s; eventEndPP = &parser->m_eventEndPtr; } else { eventPP = &(parser->m_openInternalEntities->internalEventPtr); eventEndPP = &(parser->m_openInternalEntities->internalEventEndPtr); } *eventPP = s; *startPtr = NULL; for (;;) { const char *next = s; /* in case of XML_TOK_NONE or XML_TOK_PARTIAL */ int tok = XmlCdataSectionTok(enc, s, end, &next); #ifdef XML_DTD 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 (parser->m_endCdataSectionHandler) parser->m_endCdataSectionHandler(parser->m_handlerArg); /* BEGIN disabled code */ /* see comment under XML_TOK_CDATA_SECT_OPEN */ 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 (parser->m_parsingStatus.parsing == XML_FINISHED) return XML_ERROR_ABORTED; else return XML_ERROR_NONE; case XML_TOK_DATA_NEWLINE: if (parser->m_characterDataHandler) { XML_Char c = 0xA; 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 = 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(parser->m_handlerArg, (XML_Char *)s, (int)((XML_Char *)next - (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; case XML_TOK_PARTIAL_CHAR: if (haveMore) { *nextPtr = s; return XML_ERROR_NONE; } return XML_ERROR_PARTIAL_CHAR; case XML_TOK_PARTIAL: case XML_TOK_NONE: if (haveMore) { *nextPtr = s; return XML_ERROR_NONE; } 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 (parser->m_parsingStatus.parsing) { case XML_SUSPENDED: *nextPtr = next; return XML_ERROR_NONE; case XML_FINISHED: return XML_ERROR_ABORTED; default:; } } /* not reached */ } #ifdef XML_DTD /* The idea here is to avoid using stack for each IGNORE section when 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, parser->m_encoding, &start, end, endPtr, (XML_Bool)! parser->m_parsingStatus.finalBuffer); if (result != XML_ERROR_NONE) return result; if (start) { parser->m_processor = prologProcessor; return prologProcessor(parser, start, end, endPtr); } return result; } /* startPtr gets set to non-null is the section is closed, and to null 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 = *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 == parser->m_encoding) { eventPP = &parser->m_eventPtr; *eventPP = s; 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); # ifdef XML_DTD 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 (parser->m_defaultHandler) reportDefault(parser, enc, s, next); *startPtr = next; *nextPtr = next; if (parser->m_parsingStatus.parsing == XML_FINISHED) return XML_ERROR_ABORTED; else return XML_ERROR_NONE; case XML_TOK_INVALID: *eventPP = next; return XML_ERROR_INVALID_TOKEN; case XML_TOK_PARTIAL_CHAR: if (haveMore) { *nextPtr = s; return XML_ERROR_NONE; } return XML_ERROR_PARTIAL_CHAR; case XML_TOK_PARTIAL: case XML_TOK_NONE: if (haveMore) { *nextPtr = s; return XML_ERROR_NONE; } 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 */ } #endif /* XML_DTD */ static enum XML_Error initializeEncoding(XML_Parser parser) { const char *s; #ifdef XML_UNICODE char encodingBuf[128]; /* See comments about `protocolEncodingName` in parserInit() */ if (! parser->m_protocolEncodingName) s = NULL; else { int i; for (i = 0; parser->m_protocolEncodingName[i]; i++) { if (i == sizeof(encodingBuf) - 1 || (parser->m_protocolEncodingName[i] & ~0x7f) != 0) { encodingBuf[0] = '\0'; break; } encodingBuf[i] = (char)parser->m_protocolEncodingName[i]; } encodingBuf[i] = '\0'; s = encodingBuf; } #else s = parser->m_protocolEncodingName; #endif if ((parser->m_ns ? XmlInitEncodingNS : XmlInitEncoding)( &parser->m_initEncoding, &parser->m_encoding, s)) return XML_ERROR_NONE; return handleUnknownEncoding(parser, parser->m_protocolEncodingName); } static enum XML_Error 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 = NULL; const XML_Char *storedversion = NULL; int standalone = -1; #ifdef XML_DTD 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) { parser->m_dtd->standalone = XML_TRUE; #ifdef XML_DTD if (parser->m_paramEntityParsing == XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE) parser->m_paramEntityParsing = XML_PARAM_ENTITY_PARSING_NEVER; #endif /* XML_DTD */ } if (parser->m_xmlDeclHandler) { if (encodingName != NULL) { 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(&parser->m_temp2Pool, parser->m_encoding, version, versionend - parser->m_encoding->minBytesPerChar); if (! storedversion) return XML_ERROR_NO_MEMORY; } 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) { /* 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; } parser->m_encoding = newEncoding; } else if (encodingName) { enum XML_Error result; if (! storedEncName) { storedEncName = poolStoreString( &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(&parser->m_temp2Pool); if (result == XML_ERROR_UNKNOWN_ENCODING) parser->m_eventPtr = encodingName; return result; } } if (storedEncName || storedversion) poolClear(&parser->m_temp2Pool); return XML_ERROR_NONE; } static enum XML_Error handleUnknownEncoding(XML_Parser parser, const XML_Char *encodingName) { if (parser->m_unknownEncodingHandler) { XML_Encoding info; int i; for (i = 0; i < 256; i++) info.map[i] = -1; info.convert = NULL; info.data = NULL; info.release = NULL; if (parser->m_unknownEncodingHandler(parser->m_unknownEncodingHandlerData, encodingName, &info)) { ENCODING *enc; parser->m_unknownEncodingMem = MALLOC(parser, XmlSizeOfUnknownEncoding()); if (! parser->m_unknownEncodingMem) { if (info.release) info.release(info.data); return XML_ERROR_NO_MEMORY; } enc = (parser->m_ns ? XmlInitUnknownEncodingNS : XmlInitUnknownEncoding)( parser->m_unknownEncodingMem, info.map, info.convert, info.data); if (enc) { parser->m_unknownEncodingData = info.data; parser->m_unknownEncodingRelease = info.release; parser->m_encoding = enc; return XML_ERROR_NONE; } } if (info.release != NULL) info.release(info.data); } return XML_ERROR_UNKNOWN_ENCODING; } static enum XML_Error PTRCALL 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; 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) { 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 */ parser->m_dtd->paramEntityRead = XML_TRUE; if (parser->m_prologState.inEntityValue) { parser->m_processor = entityValueInitProcessor; return entityValueInitProcessor(parser, s, end, nextPtr); } 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) { int tok; const char *start = s; const char *next = start; parser->m_eventPtr = start; 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 (! parser->m_parsingStatus.finalBuffer && tok != XML_TOK_INVALID) { *nextPtr = s; return XML_ERROR_NONE; } switch (tok) { case XML_TOK_INVALID: return XML_ERROR_INVALID_TOKEN; case XML_TOK_PARTIAL: return XML_ERROR_UNCLOSED_TOKEN; case XML_TOK_PARTIAL_CHAR: return XML_ERROR_PARTIAL_CHAR; case XML_TOK_NONE: /* start == end */ default: break; } /* found end of entity value - can store it now */ return storeEntityValue(parser, parser->m_encoding, s, end, XML_ACCOUNT_DIRECT); } else if (tok == XML_TOK_XML_DECL) { enum XML_Error result; result = processXmlDecl(parser, 0, start, next); if (result != XML_ERROR_NONE) return result; /* 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; *nextPtr = next; /* stop scanning for text declaration - we found one */ 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 */ else if (tok == XML_TOK_BOM && next == end && ! parser->m_parsingStatus.finalBuffer) { # ifdef XML_DTD 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; } /* 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) { const char *next = s; int tok; tok = XmlPrologTok(parser->m_encoding, s, end, &next); if (tok <= 0) { if (! parser->m_parsingStatus.finalBuffer && tok != XML_TOK_INVALID) { *nextPtr = s; return XML_ERROR_NONE; } switch (tok) { case XML_TOK_INVALID: return XML_ERROR_INVALID_TOKEN; case XML_TOK_PARTIAL: return XML_ERROR_UNCLOSED_TOKEN; case XML_TOK_PARTIAL_CHAR: return XML_ERROR_PARTIAL_CHAR; 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, 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(parser->m_encoding, s, end, &next); } 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) { const char *start = s; const char *next = s; 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 (! parser->m_parsingStatus.finalBuffer && tok != XML_TOK_INVALID) { *nextPtr = s; return XML_ERROR_NONE; } switch (tok) { case XML_TOK_INVALID: return XML_ERROR_INVALID_TOKEN; case XML_TOK_PARTIAL: return XML_ERROR_UNCLOSED_TOKEN; case XML_TOK_PARTIAL_CHAR: return XML_ERROR_PARTIAL_CHAR; case XML_TOK_NONE: /* start == end */ default: break; } /* found end of entity value - can store it now */ return storeEntityValue(parser, enc, s, end, XML_ACCOUNT_DIRECT); } start = next; } } #endif /* XML_DTD */ static enum XML_Error PTRCALL prologProcessor(XML_Parser parser, const char *s, const char *end, const char **nextPtr) { const char *next = s; 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, XML_Bool allowClosingDoctype, enum XML_Account account) { #ifdef XML_DTD 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'}; #ifndef XML_DTD UNUSED_P(account); #endif /* save one level of indirection */ DTD *const dtd = parser->m_dtd; const char **eventPP; const char **eventEndPP; enum XML_Content_Quant quant; 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 (;;) { int role; XML_Bool handleDefault = XML_TRUE; *eventPP = s; *eventEndPP = next; if (tok <= 0) { if (haveMore && tok != XML_TOK_INVALID) { *nextPtr = s; return XML_ERROR_NONE; } switch (tok) { case XML_TOK_INVALID: *eventPP = next; return XML_ERROR_INVALID_TOKEN; case XML_TOK_PARTIAL: 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 != parser->m_encoding && ! parser->m_openInternalEntities->betweenDecl) { *nextPtr = s; return XML_ERROR_NONE; } /* WFC: PE Between Declarations - must check that PE contains complete markup, not only for external PEs, but also for internal PEs if the reference occurs between declarations. */ 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; return XML_ERROR_NONE; } #endif /* XML_DTD */ return XML_ERROR_NO_ELEMENTS; default: tok = -tok; next = end; break; } } role = XmlTokenRole(&parser->m_prologState, tok, s, next, enc); #ifdef XML_DTD switch (role) { case XML_ROLE_INSTANCE_START: // bytes accounted in contentProcessor case XML_ROLE_XML_DECL: // bytes accounted in processXmlDecl case XML_ROLE_TEXT_DECL: // bytes accounted in processXmlDecl 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 (parser->m_startDoctypeDeclHandler) { parser->m_doctypeName = poolStoreString(&parser->m_tempPool, enc, s, next); if (! parser->m_doctypeName) return XML_ERROR_NO_MEMORY; poolFinish(&parser->m_tempPool); parser->m_doctypePubid = NULL; handleDefault = XML_FALSE; } parser->m_doctypeSysid = NULL; /* always initialize to NULL */ break; case XML_ROLE_DOCTYPE_INTERNAL_SUBSET: 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 = parser->m_encoding; handleDefault = XML_FALSE; } break; #endif /* XML_DTD */ case XML_ROLE_DOCTYPE_PUBLIC_ID: #ifdef XML_DTD 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 (parser->m_startDoctypeDeclHandler) { XML_Char *pubId; if (! XmlIsPublicId(enc, s, next, eventPP)) return XML_ERROR_PUBLICID; pubId = poolStoreString(&parser->m_tempPool, enc, s + enc->minBytesPerChar, next - enc->minBytesPerChar); if (! pubId) return XML_ERROR_NO_MEMORY; 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)) return XML_ERROR_PUBLICID; alreadyChecked: 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); parser->m_declEntity->publicId = tem; poolFinish(&dtd->pool); /* 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 (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; } /* 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 (parser->m_doctypeSysid || parser->m_useForeignDTD) { XML_Bool hadParamEntityRefs = dtd->hasParamEntityRefs; dtd->hasParamEntityRefs = XML_TRUE; 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 (! 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 && 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 (! parser->m_doctypeSysid) dtd->hasParamEntityRefs = hadParamEntityRefs; /* end of DTD - no need to update dtd->keepProcessing */ } parser->m_useForeignDTD = XML_FALSE; } #endif /* XML_DTD */ if (parser->m_endDoctypeDeclHandler) { parser->m_endDoctypeDeclHandler(parser->m_handlerArg); handleDefault = XML_FALSE; } break; case XML_ROLE_INSTANCE_START: #ifdef XML_DTD /* if there is no DOCTYPE declaration then now is the last chance to read the foreign DTD */ if (parser->m_useForeignDTD) { XML_Bool hadParamEntityRefs = dtd->hasParamEntityRefs; dtd->hasParamEntityRefs = XML_TRUE; 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 = parser->m_curBase; dtd->paramEntityRead = XML_FALSE; 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 && 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 dtd->hasParamEntityRefs = hadParamEntityRefs; /* end of DTD - no need to update dtd->keepProcessing */ } } #endif /* XML_DTD */ parser->m_processor = contentProcessor; return contentProcessor(parser, s, end, nextPtr); case XML_ROLE_ATTLIST_ELEMENT_NAME: parser->m_declElementType = getElementType(parser, enc, s, next); if (! parser->m_declElementType) return XML_ERROR_NO_MEMORY; goto checkAttListDeclHandler; case XML_ROLE_ATTRIBUTE_NAME: parser->m_declAttributeId = getAttributeId(parser, enc, s, next); if (! parser->m_declAttributeId) return XML_ERROR_NO_MEMORY; parser->m_declAttributeIsCdata = XML_FALSE; parser->m_declAttributeType = NULL; parser->m_declAttributeIsId = XML_FALSE; goto checkAttListDeclHandler; case XML_ROLE_ATTRIBUTE_TYPE_CDATA: parser->m_declAttributeIsCdata = XML_TRUE; parser->m_declAttributeType = atypeCDATA; goto checkAttListDeclHandler; case XML_ROLE_ATTRIBUTE_TYPE_ID: parser->m_declAttributeIsId = XML_TRUE; parser->m_declAttributeType = atypeID; goto checkAttListDeclHandler; case XML_ROLE_ATTRIBUTE_TYPE_IDREF: parser->m_declAttributeType = atypeIDREF; goto checkAttListDeclHandler; case XML_ROLE_ATTRIBUTE_TYPE_IDREFS: parser->m_declAttributeType = atypeIDREFS; goto checkAttListDeclHandler; case XML_ROLE_ATTRIBUTE_TYPE_ENTITY: parser->m_declAttributeType = atypeENTITY; goto checkAttListDeclHandler; case XML_ROLE_ATTRIBUTE_TYPE_ENTITIES: parser->m_declAttributeType = atypeENTITIES; goto checkAttListDeclHandler; case XML_ROLE_ATTRIBUTE_TYPE_NMTOKEN: parser->m_declAttributeType = atypeNMTOKEN; goto checkAttListDeclHandler; case XML_ROLE_ATTRIBUTE_TYPE_NMTOKENS: parser->m_declAttributeType = atypeNMTOKENS; checkAttListDeclHandler: 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 && parser->m_attlistDeclHandler) { const XML_Char *prefix; if (parser->m_declAttributeType) { prefix = enumValueSep; } else { prefix = (role == XML_ROLE_ATTRIBUTE_NOTATION_VALUE ? notationPrefix : enumValueStart); } if (! poolAppendString(&parser->m_tempPool, prefix)) return XML_ERROR_NO_MEMORY; if (! poolAppend(&parser->m_tempPool, enc, s, next)) return XML_ERROR_NO_MEMORY; 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(parser->m_declElementType, parser->m_declAttributeId, parser->m_declAttributeIsCdata, parser->m_declAttributeIsId, 0, parser)) return XML_ERROR_NO_MEMORY; 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(&parser->m_tempPool, XML_T(ASCII_RPAREN)) || ! poolAppendChar(&parser->m_tempPool, XML_T('\0'))) return XML_ERROR_NO_MEMORY; parser->m_declAttributeType = parser->m_tempPool.start; poolFinish(&parser->m_tempPool); } *eventEndPP = s; 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, 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( parser->m_declElementType, parser->m_declAttributeId, parser->m_declAttributeIsCdata, XML_FALSE, attVal, parser)) return XML_ERROR_NO_MEMORY; 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(&parser->m_tempPool, XML_T(ASCII_RPAREN)) || ! poolAppendChar(&parser->m_tempPool, XML_T('\0'))) return XML_ERROR_NO_MEMORY; parser->m_declAttributeType = parser->m_tempPool.start; poolFinish(&parser->m_tempPool); } *eventEndPP = s; 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, 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 (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; } } else poolDiscard(&dtd->entityValuePool); if (result != XML_ERROR_NONE) return result; } break; case XML_ROLE_DOCTYPE_SYSTEM_ID: #ifdef XML_DTD parser->m_useForeignDTD = XML_FALSE; #endif /* XML_DTD */ dtd->hasParamEntityRefs = XML_TRUE; 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(&parser->m_tempPool); handleDefault = XML_FALSE; } #ifdef XML_DTD else /* 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 #ifdef XML_DTD && ! parser->m_paramEntityParsing #endif /* XML_DTD */ && parser->m_notStandaloneHandler && ! parser->m_notStandaloneHandler(parser->m_handlerArg)) return XML_ERROR_NOT_STANDALONE; #ifndef XML_DTD break; #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; parser->m_declEntity->publicId = NULL; } #endif /* XML_DTD */ /* fall through */ case XML_ROLE_ENTITY_SYSTEM_ID: 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; parser->m_declEntity->base = parser->m_curBase; poolFinish(&dtd->pool); /* 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 && parser->m_declEntity && parser->m_entityDeclHandler) { *eventEndPP = s; 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 && 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 (parser->m_unparsedEntityDeclHandler) { *eventEndPP = s; 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 (parser->m_entityDeclHandler) { *eventEndPP = s; 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)) { 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); 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; case XML_ROLE_PARAM_ENTITY_NAME: #ifdef XML_DTD 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->paramEntities, name, sizeof(ENTITY)); if (! parser->m_declEntity) return XML_ERROR_NO_MEMORY; if (parser->m_declEntity->name != name) { poolDiscard(&dtd->pool); parser->m_declEntity = NULL; } else { poolFinish(&dtd->pool); 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" */ 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; } #else /* not XML_DTD */ parser->m_declEntity = NULL; #endif /* XML_DTD */ break; case XML_ROLE_NOTATION_NAME: 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(&parser->m_tempPool); handleDefault = XML_FALSE; } break; case XML_ROLE_NOTATION_PUBLIC_ID: if (! XmlIsPublicId(enc, s, next, eventPP)) return XML_ERROR_PUBLICID; if (parser ->m_declNotationName) { /* means m_notationDeclHandler != NULL */ XML_Char *tem = poolStoreString(&parser->m_tempPool, enc, s + enc->minBytesPerChar, next - enc->minBytesPerChar); if (! tem) return XML_ERROR_NO_MEMORY; normalizePublicId(tem); parser->m_declNotationPublicId = tem; poolFinish(&parser->m_tempPool); handleDefault = XML_FALSE; } break; case XML_ROLE_NOTATION_SYSTEM_ID: 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; parser->m_notationDeclHandler( parser->m_handlerArg, parser->m_declNotationName, parser->m_curBase, systemId, parser->m_declNotationPublicId); handleDefault = XML_FALSE; } poolClear(&parser->m_tempPool); break; case XML_ROLE_NOTATION_NO_SYSTEM_ID: if (parser->m_declNotationPublicId && parser->m_notationDeclHandler) { *eventEndPP = s; parser->m_notationDeclHandler( parser->m_handlerArg, parser->m_declNotationName, parser->m_curBase, 0, parser->m_declNotationPublicId); handleDefault = XML_FALSE; } 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. */ return XML_ERROR_PARAM_ENTITY_REF; case XML_TOK_XML_DECL: return XML_ERROR_MISPLACED_XML_PI; default: return XML_ERROR_SYNTAX; } #ifdef XML_DTD 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; #endif /* XML_DTD */ case XML_ROLE_GROUP_OPEN: 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 = (char *)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) { /* 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_t)(-1) / sizeof(int)) { return XML_ERROR_NO_MEMORY; } #endif int *const new_scaff_index = (int *)REALLOC( parser, dtd->scaffIndex, parser->m_groupSize * sizeof(int)); if (new_scaff_index == NULL) return XML_ERROR_NO_MEMORY; dtd->scaffIndex = new_scaff_index; } } else { parser->m_groupConnector = (char *)MALLOC(parser, parser->m_groupSize = 32); if (! parser->m_groupConnector) { parser->m_groupSize = 0; return XML_ERROR_NO_MEMORY; } } } 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 (parser->m_elementDeclHandler) handleDefault = XML_FALSE; } break; case XML_ROLE_GROUP_SEQUENCE: if (parser->m_groupConnector[parser->m_prologState.level] == ASCII_PIPE) return XML_ERROR_SYNTAX; 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 (parser->m_groupConnector[parser->m_prologState.level] == ASCII_COMMA) return XML_ERROR_SYNTAX; if (dtd->in_eldecl && ! parser->m_groupConnector[parser->m_prologState.level] && (dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel - 1]].type != XML_CTYPE_MIXED)) { dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel - 1]].type = XML_CTYPE_CHOICE; if (parser->m_elementDeclHandler) handleDefault = XML_FALSE; } 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 (! 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) return XML_ERROR_NO_MEMORY; 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 (parser->m_prologState.documentEntity && (dtd->standalone ? ! parser->m_openInternalEntities : ! dtd->hasParamEntityRefs)) { if (! entity) return XML_ERROR_UNDEFINED_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) && parser->m_skippedEntityHandler) { parser->m_skippedEntityHandler(parser->m_handlerArg, name, 1); handleDefault = XML_FALSE; } break; } if (entity->open) 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); if (result != XML_ERROR_NONE) return result; handleDefault = XML_FALSE; break; } if (parser->m_externalEntityRefHandler) { dtd->paramEntityRead = XML_FALSE; entity->open = XML_TRUE; 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) { dtd->keepProcessing = dtd->standalone; break; } } else { dtd->keepProcessing = dtd->standalone; break; } } #endif /* XML_DTD */ if (! dtd->standalone && parser->m_notStandaloneHandler && ! parser->m_notStandaloneHandler(parser->m_handlerArg)) return XML_ERROR_NOT_STANDALONE; break; /* Element declaration stuff */ case XML_ROLE_ELEMENT_NAME: 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; dtd->in_eldecl = XML_TRUE; handleDefault = XML_FALSE; } break; case XML_ROLE_CONTENT_ANY: case XML_ROLE_CONTENT_EMPTY: if (dtd->in_eldecl) { if (parser->m_elementDeclHandler) { XML_Content *content = (XML_Content *)MALLOC(parser, 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); *eventEndPP = s; parser->m_elementDeclHandler( parser->m_handlerArg, parser->m_declElementType->name, content); handleDefault = XML_FALSE; } dtd->in_eldecl = XML_FALSE; } break; case XML_ROLE_CONTENT_PCDATA: if (dtd->in_eldecl) { dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel - 1]].type = XML_CTYPE_MIXED; if (parser->m_elementDeclHandler) handleDefault = XML_FALSE; } break; case XML_ROLE_CONTENT_ELEMENT: quant = XML_CQUANT_NONE; goto elementContent; case XML_ROLE_CONTENT_ELEMENT_OPT: quant = XML_CQUANT_OPT; goto elementContent; case XML_ROLE_CONTENT_ELEMENT_REP: quant = XML_CQUANT_REP; goto elementContent; case XML_ROLE_CONTENT_ELEMENT_PLUS: quant = XML_CQUANT_PLUS; elementContent: if (dtd->in_eldecl) { ELEMENT_TYPE *el; const XML_Char *name; 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) return XML_ERROR_NO_MEMORY; name = el->name; dtd->scaffold[myindex].name = name; nameLen = 0; for (; 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; case XML_ROLE_GROUP_CLOSE: quant = XML_CQUANT_NONE; goto closeGroup; case XML_ROLE_GROUP_CLOSE_OPT: quant = XML_CQUANT_OPT; goto closeGroup; case XML_ROLE_GROUP_CLOSE_REP: quant = XML_CQUANT_REP; goto closeGroup; case XML_ROLE_GROUP_CLOSE_PLUS: quant = XML_CQUANT_PLUS; closeGroup: if (dtd->in_eldecl) { if (parser->m_elementDeclHandler) handleDefault = XML_FALSE; dtd->scaffLevel--; dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel]].quant = quant; if (dtd->scaffLevel == 0) { if (! handleDefault) { XML_Content *model = build_model(parser); if (! model) return XML_ERROR_NO_MEMORY; *eventEndPP = s; parser->m_elementDeclHandler( parser->m_handlerArg, parser->m_declElementType->name, model); } dtd->in_eldecl = XML_FALSE; dtd->contentStringLen = 0; } } break; /* End element declaration stuff */ case XML_ROLE_PI: 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)) return XML_ERROR_NO_MEMORY; handleDefault = XML_FALSE; break; case XML_ROLE_NONE: switch (tok) { case XML_TOK_BOM: handleDefault = XML_FALSE; break; } break; case XML_ROLE_DOCTYPE_NONE: if (parser->m_startDoctypeDeclHandler) handleDefault = XML_FALSE; break; case XML_ROLE_ENTITY_NONE: if (dtd->keepProcessing && parser->m_entityDeclHandler) handleDefault = XML_FALSE; break; case XML_ROLE_NOTATION_NONE: if (parser->m_notationDeclHandler) handleDefault = XML_FALSE; break; case XML_ROLE_ATTLIST_NONE: if (dtd->keepProcessing && parser->m_attlistDeclHandler) handleDefault = XML_FALSE; break; case XML_ROLE_ELEMENT_NONE: if (parser->m_elementDeclHandler) handleDefault = XML_FALSE; break; } /* end of big switch */ if (handleDefault && parser->m_defaultHandler) reportDefault(parser, enc, s, next); switch (parser->m_parsingStatus.parsing) { case XML_SUSPENDED: *nextPtr = next; return XML_ERROR_NONE; case XML_FINISHED: return XML_ERROR_ABORTED; default: s = next; tok = XmlPrologTok(enc, s, end, &next); } } /* not reached */ } static enum XML_Error PTRCALL 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(parser->m_encoding, s, end, &next); #ifdef XML_DTD 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 (parser->m_defaultHandler) { reportDefault(parser, parser->m_encoding, s, next); if (parser->m_parsingStatus.parsing == XML_FINISHED) return XML_ERROR_ABORTED; } *nextPtr = next; return XML_ERROR_NONE; case XML_TOK_NONE: *nextPtr = s; return XML_ERROR_NONE; case XML_TOK_PROLOG_S: if (parser->m_defaultHandler) reportDefault(parser, parser->m_encoding, s, next); break; case XML_TOK_PI: if (! reportProcessingInstruction(parser, parser->m_encoding, s, next)) return XML_ERROR_NO_MEMORY; break; case XML_TOK_COMMENT: if (! reportComment(parser, parser->m_encoding, s, next)) return XML_ERROR_NO_MEMORY; break; case XML_TOK_INVALID: parser->m_eventPtr = next; return XML_ERROR_INVALID_TOKEN; case XML_TOK_PARTIAL: if (! parser->m_parsingStatus.finalBuffer) { *nextPtr = s; return XML_ERROR_NONE; } return XML_ERROR_UNCLOSED_TOKEN; case XML_TOK_PARTIAL_CHAR: if (! parser->m_parsingStatus.finalBuffer) { *nextPtr = s; return XML_ERROR_NONE; } return XML_ERROR_PARTIAL_CHAR; default: return XML_ERROR_JUNK_AFTER_DOC_ELEMENT; } parser->m_eventPtr = s = next; switch (parser->m_parsingStatus.parsing) { case XML_SUSPENDED: *nextPtr = next; return XML_ERROR_NONE; case XML_FINISHED: return XML_ERROR_ABORTED; default:; } } } 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 (parser->m_freeInternalEntities) { openEntity = parser->m_freeInternalEntities; parser->m_freeInternalEntities = openEntity->next; } else { openEntity = (OPEN_INTERNAL_ENTITY *)MALLOC(parser, sizeof(OPEN_INTERNAL_ENTITY)); if (! openEntity) return XML_ERROR_NO_MEMORY; } entity->open = XML_TRUE; #ifdef XML_DTD entityTrackingOnOpen(parser, entity, __LINE__); #endif entity->processed = 0; openEntity->next = parser->m_openInternalEntities; parser->m_openInternalEntities = openEntity; openEntity->entity = entity; openEntity->startTagLevel = parser->m_tagLevel; openEntity->betweenDecl = betweenDecl; openEntity->internalEventPtr = NULL; openEntity->internalEventEndPtr = NULL; textStart = (const char *)entity->textPtr; textEnd = (const char *)(entity->textPtr + entity->textLen); /* Set a safe default value in case 'next' does not get set */ next = textStart; #ifdef XML_DTD 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 #endif /* XML_DTD */ result = doContent(parser, parser->m_tagLevel, parser->m_internalEncoding, textStart, textEnd, &next, XML_FALSE, XML_ACCOUNT_ENTITY_EXPANSION); if (result == XML_ERROR_NONE) { if (textEnd != next && parser->m_parsingStatus.parsing == XML_SUSPENDED) { entity->processed = (int)(next - textStart); parser->m_processor = internalEntityProcessor; } else { #ifdef XML_DTD entityTrackingOnClose(parser, entity, __LINE__); #endif /* XML_DTD */ entity->open = XML_FALSE; parser->m_openInternalEntities = openEntity->next; /* put openEntity back in list of free instances */ openEntity->next = parser->m_freeInternalEntities; parser->m_freeInternalEntities = openEntity; } } return result; } static enum XML_Error PTRCALL internalEntityProcessor(XML_Parser parser, const char *s, const char *end, const char **nextPtr) { ENTITY *entity; const char *textStart, *textEnd; const char *next; enum XML_Error result; OPEN_INTERNAL_ENTITY *openEntity = parser->m_openInternalEntities; if (! openEntity) return XML_ERROR_UNEXPECTED_STATE; entity = openEntity->entity; 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; #ifdef XML_DTD 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_TRUE, XML_ACCOUNT_ENTITY_EXPANSION); } else #endif /* XML_DTD */ result = doContent(parser, openEntity->startTagLevel, parser->m_internalEncoding, textStart, textEnd, &next, XML_FALSE, XML_ACCOUNT_ENTITY_EXPANSION); if (result != XML_ERROR_NONE) return result; if (textEnd != next && parser->m_parsingStatus.parsing == XML_SUSPENDED) { entity->processed = (int)(next - (const char *)entity->textPtr); return result; } #ifdef XML_DTD entityTrackingOnClose(parser, entity, __LINE__); #endif entity->open = XML_FALSE; parser->m_openInternalEntities = openEntity->next; /* put openEntity back in list of free instances */ openEntity->next = parser->m_freeInternalEntities; parser->m_freeInternalEntities = openEntity; // If there are more open entities we want to stop right here and have the // upcoming call to XML_ResumeParser continue with entity content, or it would // be ignored altogether. if (parser->m_openInternalEntities != NULL && parser->m_parsingStatus.parsing == XML_SUSPENDED) { return XML_ERROR_NONE; } #ifdef XML_DTD if (entity->is_param) { int tok; parser->m_processor = prologProcessor; 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); } else #endif /* XML_DTD */ { parser->m_processor = contentProcessor; /* see externalEntityContentProcessor vs contentProcessor */ result = doContent(parser, parser->m_parentParser ? 1 : 0, parser->m_encoding, s, end, nextPtr, (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_ACCOUNT_DIRECT); if (result == XML_ERROR_NONE) { if (! storeRawNames(parser)) return XML_ERROR_NO_MEMORY; } return result; } } static enum XML_Error PTRCALL 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_Account account) { enum XML_Error result = appendAttributeValue(parser, enc, isCdata, ptr, end, pool, account); if (result) return result; if (! isCdata && poolLength(pool) && poolLastChar(pool) == 0x20) poolChop(pool); 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, enum XML_Account account) { DTD *const dtd = parser->m_dtd; /* save one level of indirection */ #ifndef XML_DTD UNUSED_P(account); #endif for (;;) { const char *next = ptr; /* XmlAttributeValueTok doesn't always set the last arg */ int tok = XmlAttributeValueTok(enc, ptr, end, &next); #ifdef XML_DTD if (! accountingDiffTolerated(parser, tok, ptr, next, __LINE__, account)) { accountingOnAbort(parser); return XML_ERROR_AMPLIFICATION_LIMIT_BREACH; } #endif switch (tok) { case XML_TOK_NONE: return XML_ERROR_NONE; case XML_TOK_INVALID: if (enc == parser->m_encoding) parser->m_eventPtr = next; return XML_ERROR_INVALID_TOKEN; case XML_TOK_PARTIAL: 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 == parser->m_encoding) parser->m_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); /* 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)) return XML_ERROR_NO_MEMORY; break; case XML_TOK_TRAILING_CR: next = ptr + enc->minBytesPerChar; /* fall through */ case XML_TOK_ATTRIBUTE_VALUE_S: case XML_TOK_DATA_NEWLINE: if (! isCdata && (poolLength(pool) == 0 || poolLastChar(pool) == 0x20)) break; 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) { #ifdef XML_DTD /* 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_DTD */ if (! poolAppendChar(pool, ch)) return XML_ERROR_NO_MEMORY; 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 parser->m_prologState.documentEntity && #endif /* XML_DTD */ (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 */ } 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; const XML_Char *textEnd = entity->textPtr + entity->textLen; entity->open = XML_TRUE; #ifdef XML_DTD entityTrackingOnOpen(parser, entity, __LINE__); #endif result = appendAttributeValue(parser, parser->m_internalEncoding, isCdata, (const char *)entity->textPtr, (const char *)textEnd, pool, XML_ACCOUNT_ENTITY_EXPANSION); #ifdef XML_DTD entityTrackingOnClose(parser, entity, __LINE__); #endif entity->open = XML_FALSE; if (result) return result; } } break; default: /* 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 */ } static enum XML_Error storeEntityValue(XML_Parser parser, const ENCODING *enc, const char *entityTextPtr, const char *entityTextEnd, enum XML_Account account) { 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 = 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)) return XML_ERROR_NO_MEMORY; } for (;;) { const char *next = entityTextPtr; /* XmlEntityValueTok doesn't always set the last arg */ int tok = XmlEntityValueTok(enc, entityTextPtr, entityTextEnd, &next); #ifdef XML_DTD if (! accountingDiffTolerated(parser, tok, entityTextPtr, next, __LINE__, account)) { accountingOnAbort(parser); result = XML_ERROR_AMPLIFICATION_LIMIT_BREACH; goto endEntityValue; } #endif switch (tok) { case XML_TOK_PARAM_ENTITY_REF: #ifdef XML_DTD if (parser->m_isParamEntity || enc != parser->m_encoding) { const XML_Char *name; ENTITY *entity; name = poolStoreString(&parser->m_tempPool, enc, entityTextPtr + enc->minBytesPerChar, next - enc->minBytesPerChar); if (! name) { result = XML_ERROR_NO_MEMORY; goto endEntityValue; } 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 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 == parser->m_encoding) parser->m_eventPtr = entityTextPtr; result = XML_ERROR_RECURSIVE_ENTITY_REF; goto endEntityValue; } if (entity->systemId) { if (parser->m_externalEntityRefHandler) { dtd->paramEntityRead = XML_FALSE; entity->open = XML_TRUE; 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) dtd->keepProcessing = dtd->standalone; } else dtd->keepProcessing = dtd->standalone; } else { entity->open = XML_TRUE; entityTrackingOnOpen(parser, entity, __LINE__); result = storeEntityValue( parser, parser->m_internalEncoding, (const char *)entity->textPtr, (const char *)(entity->textPtr + entity->textLen), XML_ACCOUNT_ENTITY_EXPANSION); entityTrackingOnClose(parser, entity, __LINE__); entity->open = XML_FALSE; if (result) goto endEntityValue; } break; } #endif /* XML_DTD */ /* In the internal subset, PE references are not legal within markup declarations, e.g entity values in this case. */ parser->m_eventPtr = entityTextPtr; result = XML_ERROR_PARAM_ENTITY_REF; goto endEntityValue; case XML_TOK_NONE: result = XML_ERROR_NONE; goto endEntityValue; case XML_TOK_ENTITY_REF: case XML_TOK_DATA_CHARS: if (! poolAppend(pool, enc, entityTextPtr, next)) { result = XML_ERROR_NO_MEMORY; goto endEntityValue; } break; case XML_TOK_TRAILING_CR: next = entityTextPtr + enc->minBytesPerChar; /* fall through */ case XML_TOK_DATA_NEWLINE: 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 == 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; } *(pool->ptr)++ = buf[i]; } } break; case XML_TOK_PARTIAL: if (enc == parser->m_encoding) parser->m_eventPtr = entityTextPtr; result = XML_ERROR_INVALID_TOKEN; goto endEntityValue; case XML_TOK_INVALID: if (enc == parser->m_encoding) parser->m_eventPtr = next; result = XML_ERROR_INVALID_TOKEN; goto endEntityValue; default: /* 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 parser->m_prologState.inEntityValue = oldInEntityValue; #endif /* XML_DTD */ return result; } static void FASTCALL normalizeLines(XML_Char *s) { XML_Char *p; for (;; s++) { if (*s == XML_T('\0')) return; if (*s == 0xD) break; } p = s; do { if (*s == 0xD) { *p++ = 0xA; if (*++s == 0xA) s++; } else *p++ = *s++; } while (*s); *p = XML_T('\0'); } static int reportProcessingInstruction(XML_Parser parser, const ENCODING *enc, const char *start, const char *end) { const XML_Char *target; XML_Char *data; const char *tem; 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(&parser->m_tempPool, enc, start, tem); if (! target) return 0; poolFinish(&parser->m_tempPool); data = poolStoreString(&parser->m_tempPool, enc, XmlSkipS(enc, tem), end - enc->minBytesPerChar * 2); if (! data) return 0; normalizeLines(data); 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) { XML_Char *data; if (! parser->m_commentHandler) { if (parser->m_defaultHandler) reportDefault(parser, enc, start, end); return 1; } data = poolStoreString(&parser->m_tempPool, enc, start + enc->minBytesPerChar * 4, end - enc->minBytesPerChar * 3); if (! data) return 0; normalizeLines(data); 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) { if (MUST_CONVERT(enc, s)) { enum XML_Convert_Result convert_res; const char **eventPP; const char **eventEndPP; 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 *)parser->m_dataBuf; convert_res = XmlConvert(enc, &s, end, &dataPtr, (ICHAR *)parser->m_dataBufEnd); *eventEndPP = s; parser->m_defaultHandler(parser->m_handlerArg, parser->m_dataBuf, (int)(dataPtr - (ICHAR *)parser->m_dataBuf)); *eventPP = s; } while ((convert_res != XML_CONVERT_COMPLETED) && (convert_res != XML_CONVERT_INPUT_INCOMPLETE)); } else parser->m_defaultHandler(parser->m_handlerArg, (XML_Char *)s, (int)((XML_Char *)end - (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) { DEFAULT_ATTRIBUTE *att; if (value || isId) { /* The handling of default attributes gets messed up if we have a default which duplicates a non-default. */ int i; for (i = 0; i < type->nDefaultAtts; i++) if (attId == type->defaultAtts[i].id) return 1; 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( parser, type->allocDefaultAtts * sizeof(DEFAULT_ATTRIBUTE)); if (! type->defaultAtts) { type->allocDefaultAtts = 0; return 0; } } else { DEFAULT_ATTRIBUTE *temp; /* Detect and prevent integer overflow */ if (type->allocDefaultAtts > INT_MAX / 2) { return 0; } int count = type->allocDefaultAtts * 2; /* 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_t)(-1) / sizeof(DEFAULT_ATTRIBUTE)) { return 0; } #endif temp = (DEFAULT_ATTRIBUTE *)REALLOC(parser, type->defaultAtts, (count * sizeof(DEFAULT_ATTRIBUTE))); if (temp == NULL) return 0; type->allocDefaultAtts = count; type->defaultAtts = temp; } } att = type->defaultAtts + type->nDefaultAtts; att->id = attId; att->value = value; att->isCdata = isCdata; if (! isCdata) attId->maybeTokenized = XML_TRUE; type->nDefaultAtts += 1; return 1; } static int 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)) return 0; } if (! poolAppendChar(&dtd->pool, XML_T('\0'))) return 0; prefix = (PREFIX *)lookup(parser, &dtd->prefixes, poolStart(&dtd->pool), sizeof(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 = parser->m_dtd; /* save one level of indirection */ ATTRIBUTE_ID *id; const XML_Char *name; if (! poolAppendChar(&dtd->pool, XML_T('\0'))) return NULL; name = poolStoreString(&dtd->pool, enc, start, end); if (! name) return NULL; /* skip quotation mark - its storage will be re-used (like in name[-1]) */ ++name; 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 (! 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))) { if (name[5] == XML_T('\0')) id->prefix = &dtd->defaultPrefix; else id->prefix = (PREFIX *)lookup(parser, &dtd->prefixes, name + 6, sizeof(PREFIX)); id->xmlns = XML_TRUE; } 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])) return NULL; } 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; if (id->prefix->name == poolStart(&dtd->pool)) poolFinish(&dtd->pool); else poolDiscard(&dtd->pool); break; } } } } return id; } #define CONTEXT_SEP XML_T(ASCII_FF) static const XML_Char * 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(&parser->m_tempPool, XML_T(ASCII_EQUALS))) return NULL; len = dtd->defaultPrefix.binding->uriLen; if (parser->m_namespaceSeparator) len--; 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; } hashTableIterInit(&iter, &(dtd->prefixes)); for (;;) { int i; int len; const XML_Char *s; PREFIX *prefix = (PREFIX *)hashTableIterNext(&iter); if (! prefix) break; 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(&parser->m_tempPool, *s)) return NULL; if (! poolAppendChar(&parser->m_tempPool, XML_T(ASCII_EQUALS))) return NULL; len = prefix->binding->uriLen; if (parser->m_namespaceSeparator) len--; for (i = 0; i < len; 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) break; if (! e->open) continue; if (needSep && ! poolAppendChar(&parser->m_tempPool, CONTEXT_SEP)) return NULL; for (s = e->name; *s; s++) if (! poolAppendChar(&parser->m_tempPool, *s)) return 0; needSep = XML_TRUE; } if (! poolAppendChar(&parser->m_tempPool, XML_T('\0'))) return NULL; return parser->m_tempPool.start; } static XML_Bool setContext(XML_Parser parser, const XML_Char *context) { 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(&parser->m_tempPool, XML_T('\0'))) return XML_FALSE; 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(&parser->m_tempPool); } else if (*s == XML_T(ASCII_EQUALS)) { PREFIX *prefix; if (poolLength(&parser->m_tempPool) == 0) prefix = &dtd->defaultPrefix; else { if (! poolAppendChar(&parser->m_tempPool, XML_T('\0'))) return XML_FALSE; prefix = (PREFIX *)lookup(parser, &dtd->prefixes, poolStart(&parser->m_tempPool), sizeof(PREFIX)); if (! prefix) return XML_FALSE; if (prefix->name == poolStart(&parser->m_tempPool)) { prefix->name = poolCopyString(&dtd->pool, prefix->name); if (! prefix->name) return XML_FALSE; } poolDiscard(&parser->m_tempPool); } for (context = s + 1; *context != CONTEXT_SEP && *context != XML_T('\0'); context++) if (! poolAppendChar(&parser->m_tempPool, *context)) return XML_FALSE; if (! poolAppendChar(&parser->m_tempPool, XML_T('\0'))) return XML_FALSE; if (addBinding(parser, prefix, NULL, poolStart(&parser->m_tempPool), &parser->m_inheritedBindings) != XML_ERROR_NONE) return XML_FALSE; poolDiscard(&parser->m_tempPool); if (*context != XML_T('\0')) ++context; s = context; } else { if (! poolAppendChar(&parser->m_tempPool, *s)) return XML_FALSE; s++; } } return XML_TRUE; } static void FASTCALL normalizePublicId(XML_Char *publicId) { XML_Char *p = publicId; XML_Char *s; for (s = publicId; *s; s++) { switch (*s) { case 0x20: case 0xD: case 0xA: if (p != publicId && p[-1] != 0x20) *p++ = 0x20; break; default: *p++ = *s; } } if (p != publicId && p[-1] == 0x20) --p; *p = XML_T('\0'); } static DTD * dtdCreate(const XML_Memory_Handling_Suite *ms) { DTD *p = ms->malloc_fcn(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); #ifdef XML_DTD p->paramEntityRead = XML_FALSE; hashTableInit(&(p->paramEntities), ms); #endif /* XML_DTD */ p->defaultPrefix.name = NULL; p->defaultPrefix.binding = NULL; p->in_eldecl = XML_FALSE; p->scaffIndex = NULL; p->scaffold = NULL; p->scaffLevel = 0; p->scaffSize = 0; p->scaffCount = 0; p->contentStringLen = 0; p->keepProcessing = XML_TRUE; p->hasParamEntityRefs = XML_FALSE; p->standalone = XML_FALSE; return p; } static void dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms) { HASH_TABLE_ITER iter; hashTableIterInit(&iter, &(p->elementTypes)); for (;;) { ELEMENT_TYPE *e = (ELEMENT_TYPE *)hashTableIterNext(&iter); if (! e) break; if (e->allocDefaultAtts != 0) ms->free_fcn(e->defaultAtts); } hashTableClear(&(p->generalEntities)); #ifdef XML_DTD p->paramEntityRead = XML_FALSE; hashTableClear(&(p->paramEntities)); #endif /* XML_DTD */ hashTableClear(&(p->elementTypes)); hashTableClear(&(p->attributeIds)); hashTableClear(&(p->prefixes)); poolClear(&(p->pool)); poolClear(&(p->entityValuePool)); p->defaultPrefix.name = NULL; p->defaultPrefix.binding = NULL; p->in_eldecl = XML_FALSE; ms->free_fcn(p->scaffIndex); p->scaffIndex = NULL; ms->free_fcn(p->scaffold); p->scaffold = NULL; p->scaffLevel = 0; p->scaffSize = 0; p->scaffCount = 0; p->contentStringLen = 0; p->keepProcessing = XML_TRUE; p->hasParamEntityRefs = XML_FALSE; p->standalone = XML_FALSE; } static void dtdDestroy(DTD *p, XML_Bool isDocEntity, const XML_Memory_Handling_Suite *ms) { HASH_TABLE_ITER iter; hashTableIterInit(&iter, &(p->elementTypes)); for (;;) { ELEMENT_TYPE *e = (ELEMENT_TYPE *)hashTableIterNext(&iter); if (! e) break; if (e->allocDefaultAtts != 0) ms->free_fcn(e->defaultAtts); } hashTableDestroy(&(p->generalEntities)); #ifdef XML_DTD hashTableDestroy(&(p->paramEntities)); #endif /* XML_DTD */ hashTableDestroy(&(p->elementTypes)); hashTableDestroy(&(p->attributeIds)); hashTableDestroy(&(p->prefixes)); poolDestroy(&(p->pool)); poolDestroy(&(p->entityValuePool)); if (isDocEntity) { ms->free_fcn(p->scaffIndex); ms->free_fcn(p->scaffold); } ms->free_fcn(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(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd, const XML_Memory_Handling_Suite *ms) { HASH_TABLE_ITER iter; /* Copy the prefix table. */ hashTableIterInit(&iter, &(oldDtd->prefixes)); for (;;) { const XML_Char *name; const PREFIX *oldP = (PREFIX *)hashTableIterNext(&iter); if (! oldP) break; name = poolCopyString(&(newDtd->pool), oldP->name); if (! name) return 0; if (! lookup(oldParser, &(newDtd->prefixes), name, sizeof(PREFIX))) return 0; } hashTableIterInit(&iter, &(oldDtd->attributeIds)); /* Copy the attribute id table. */ for (;;) { ATTRIBUTE_ID *newA; const XML_Char *name; const ATTRIBUTE_ID *oldA = (ATTRIBUTE_ID *)hashTableIterNext(&iter); if (! oldA) break; /* Remember to allocate the scratch byte before the name. */ if (! poolAppendChar(&(newDtd->pool), XML_T('\0'))) return 0; name = poolCopyString(&(newDtd->pool), oldA->name); if (! name) return 0; ++name; newA = (ATTRIBUTE_ID *)lookup(oldParser, &(newDtd->attributeIds), name, sizeof(ATTRIBUTE_ID)); if (! newA) return 0; newA->maybeTokenized = oldA->maybeTokenized; if (oldA->prefix) { newA->xmlns = oldA->xmlns; if (oldA->prefix == &oldDtd->defaultPrefix) newA->prefix = &newDtd->defaultPrefix; else newA->prefix = (PREFIX *)lookup(oldParser, &(newDtd->prefixes), oldA->prefix->name, 0); } } /* Copy the element type table. */ hashTableIterInit(&iter, &(oldDtd->elementTypes)); for (;;) { int i; ELEMENT_TYPE *newE; const XML_Char *name; const ELEMENT_TYPE *oldE = (ELEMENT_TYPE *)hashTableIterNext(&iter); if (! oldE) break; name = poolCopyString(&(newDtd->pool), oldE->name); if (! name) return 0; newE = (ELEMENT_TYPE *)lookup(oldParser, &(newDtd->elementTypes), name, sizeof(ELEMENT_TYPE)); if (! newE) return 0; if (oldE->nDefaultAtts) { newE->defaultAtts = ms->malloc_fcn(oldE->nDefaultAtts * sizeof(DEFAULT_ATTRIBUTE)); if (! newE->defaultAtts) { return 0; } } if (oldE->idAtt) 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(oldParser, &(newDtd->prefixes), oldE->prefix->name, 0); for (i = 0; i < newE->nDefaultAtts; i++) { 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) return 0; } else newE->defaultAtts[i].value = NULL; } } /* Copy the entity tables. */ if (! copyEntityTable(oldParser, &(newDtd->generalEntities), &(newDtd->pool), &(oldDtd->generalEntities))) return 0; #ifdef XML_DTD if (! copyEntityTable(oldParser, &(newDtd->paramEntities), &(newDtd->pool), &(oldDtd->paramEntities))) return 0; newDtd->paramEntityRead = oldDtd->paramEntityRead; #endif /* XML_DTD */ newDtd->keepProcessing = oldDtd->keepProcessing; newDtd->hasParamEntityRefs = oldDtd->hasParamEntityRefs; newDtd->standalone = oldDtd->standalone; /* Don't want deep copying for scaffolding */ newDtd->in_eldecl = oldDtd->in_eldecl; newDtd->scaffold = oldDtd->scaffold; newDtd->contentStringLen = oldDtd->contentStringLen; newDtd->scaffSize = oldDtd->scaffSize; newDtd->scaffLevel = oldDtd->scaffLevel; newDtd->scaffIndex = oldDtd->scaffIndex; return 1; } /* End dtdCopy */ static int 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; hashTableIterInit(&iter, oldTable); for (;;) { ENTITY *newE; const XML_Char *name; const ENTITY *oldE = (ENTITY *)hashTableIterNext(&iter); if (! oldE) break; name = poolCopyString(newPool, oldE->name); if (! name) return 0; 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) return 0; newE->systemId = tem; if (oldE->base) { if (oldE->base == cachedOldBase) newE->base = cachedNewBase; else { cachedOldBase = oldE->base; tem = poolCopyString(newPool, cachedOldBase); if (! tem) return 0; cachedNewBase = newE->base = tem; } } if (oldE->publicId) { tem = poolCopyString(newPool, oldE->publicId); if (! tem) return 0; newE->publicId = 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) return 0; newE->notation = tem; } newE->is_param = oldE->is_param; newE->is_internal = oldE->is_internal; } return 1; } #define INIT_POWER 6 static XML_Bool FASTCALL 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(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(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize) { size_t i; if (table->size == 0) { size_t tsize; 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 = table->mem->malloc_fcn(tsize); if (! table->v) { table->size = 0; return NULL; } memset(table->v, 0, tsize); 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) step = PROBE_STEP(h, mask, table->power); i < step ? (i += table->size - step) : (i -= step); } 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_t)(-1) / sizeof(NAMED *)) { return NULL; } size_t tsize = newSize * sizeof(NAMED *); NAMED **newV = table->mem->malloc_fcn(tsize); if (! newV) return NULL; memset(newV, 0, tsize); for (i = 0; i < table->size; i++) if (table->v[i]) { unsigned long newHash = hash(parser, table->v[i]->name); size_t j = newHash & newMask; step = 0; while (newV[j]) { 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); table->v = newV; table->power = newPower; table->size = newSize; i = h & newMask; step = 0; while (table->v[i]) { if (! step) step = PROBE_STEP(h, newMask, newPower); i < step ? (i += newSize - step) : (i -= step); } } } table->v[i] = table->mem->malloc_fcn(createSize); if (! table->v[i]) return NULL; memset(table->v[i], 0, createSize); table->v[i]->name = name; (table->used)++; return table->v[i]; } static void FASTCALL hashTableClear(HASH_TABLE *table) { size_t i; for (i = 0; i < table->size; i++) { table->mem->free_fcn(table->v[i]); table->v[i] = NULL; } table->used = 0; } static void FASTCALL 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); } static void FASTCALL hashTableInit(HASH_TABLE *p, const XML_Memory_Handling_Suite *ms) { p->power = 0; p->size = 0; p->used = 0; p->v = NULL; p->mem = ms; } static void FASTCALL hashTableIterInit(HASH_TABLE_ITER *iter, const HASH_TABLE *table) { iter->p = table->v; iter->end = iter->p ? iter->p + table->size : NULL; } static NAMED *FASTCALL hashTableIterNext(HASH_TABLE_ITER *iter) { while (iter->p != iter->end) { NAMED *tem = *(iter->p)++; if (tem) return tem; } return NULL; } static void FASTCALL poolInit(STRING_POOL *pool, const XML_Memory_Handling_Suite *ms) { pool->blocks = NULL; pool->freeBlocks = NULL; pool->start = NULL; pool->ptr = NULL; pool->end = NULL; pool->mem = ms; } static void FASTCALL poolClear(STRING_POOL *pool) { if (! pool->freeBlocks) pool->freeBlocks = pool->blocks; else { BLOCK *p = pool->blocks; while (p) { BLOCK *tem = p->next; p->next = pool->freeBlocks; pool->freeBlocks = p; p = tem; } } pool->blocks = NULL; pool->start = NULL; pool->ptr = NULL; pool->end = NULL; } static void FASTCALL poolDestroy(STRING_POOL *pool) { BLOCK *p = pool->blocks; while (p) { BLOCK *tem = p->next; pool->mem->free_fcn(p); p = tem; } p = pool->freeBlocks; while (p) { BLOCK *tem = p->next; pool->mem->free_fcn(p); p = tem; } } static XML_Char * poolAppend(STRING_POOL *pool, const ENCODING *enc, const char *ptr, const char *end) { if (! pool->ptr && ! poolGrow(pool)) return NULL; for (;;) { const enum XML_Convert_Result convert_res = XmlConvert( enc, &ptr, end, (ICHAR **)&(pool->ptr), (ICHAR *)pool->end); if ((convert_res == XML_CONVERT_COMPLETED) || (convert_res == XML_CONVERT_INPUT_INCOMPLETE)) break; if (! poolGrow(pool)) return NULL; } return pool->start; } static const XML_Char *FASTCALL poolCopyString(STRING_POOL *pool, const XML_Char *s) { do { if (! poolAppendChar(pool, *s)) return NULL; } while (*s++); s = pool->start; poolFinish(pool); return s; } static const XML_Char * 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)) return NULL; } s = pool->start; poolFinish(pool); return s; } static const XML_Char *FASTCALL poolAppendString(STRING_POOL *pool, const XML_Char *s) { while (*s) { if (! poolAppendChar(pool, *s)) return NULL; s++; } return pool->start; } static XML_Char * 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)) 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) { if (pool->freeBlocks) { if (pool->start == 0) { pool->blocks = pool->freeBlocks; pool->freeBlocks = pool->freeBlocks->next; pool->blocks->next = NULL; pool->start = pool->blocks->s; pool->end = pool->start + pool->blocks->size; pool->ptr = pool->start; return XML_TRUE; } if (pool->end - pool->start < pool->freeBlocks->size) { BLOCK *tem = pool->freeBlocks->next; pool->freeBlocks->next = pool->blocks; pool->blocks = pool->freeBlocks; pool->freeBlocks = tem; memcpy(pool->blocks->s, pool->start, (pool->end - pool->start) * sizeof(XML_Char)); pool->ptr = pool->blocks->s + (pool->ptr - pool->start); pool->start = pool->blocks->s; pool->end = pool->start + pool->blocks->size; return XML_TRUE; } } if (pool->blocks && pool->start == pool->blocks->s) { 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 = (BLOCK *)pool->mem->realloc_fcn(pool->blocks, (unsigned)bytesToAllocate); if (temp == NULL) return XML_FALSE; pool->blocks = temp; pool->blocks->size = blockSize; pool->ptr = pool->blocks->s + offsetInsideBlock; pool->start = pool->blocks->s; pool->end = pool->start + blockSize; } 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 { /* Detect overflow, avoiding _signed_ overflow undefined behavior */ if ((int)((unsigned)blockSize * 2U) < 0) { return XML_FALSE; } blockSize *= 2; } bytesToAllocate = poolBytesToAllocateFor(blockSize); if (bytesToAllocate == 0) return XML_FALSE; tem = pool->mem->malloc_fcn(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)); pool->ptr = tem->s + (pool->ptr - pool->start); pool->start = tem->s; pool->end = tem->s + blockSize; } return XML_TRUE; } static int FASTCALL 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(parser, parser->m_groupSize * sizeof(int)); if (! dtd->scaffIndex) return -1; dtd->scaffIndex[0] = 0; } if (dtd->scaffCount >= dtd->scaffSize) { CONTENT_SCAFFOLD *temp; if (dtd->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_t)(-1) / 2u / sizeof(CONTENT_SCAFFOLD)) { return -1; } #endif temp = (CONTENT_SCAFFOLD *)REALLOC( parser, dtd->scaffold, dtd->scaffSize * 2 * sizeof(CONTENT_SCAFFOLD)); if (temp == NULL) return -1; dtd->scaffSize *= 2; } else { temp = (CONTENT_SCAFFOLD *)MALLOC(parser, INIT_SCAFFOLD_ELEMENTS * sizeof(CONTENT_SCAFFOLD)); if (temp == NULL) return -1; dtd->scaffSize = INIT_SCAFFOLD_ELEMENTS; } dtd->scaffold = temp; } next = dtd->scaffCount++; me = &dtd->scaffold[next]; if (dtd->scaffLevel) { CONTENT_SCAFFOLD *parent = &dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel - 1]]; if (parent->lastchild) { dtd->scaffold[parent->lastchild].nextsib = next; } if (! parent->childcnt) parent->firstchild = next; parent->lastchild = next; parent->childcnt++; } me->firstchild = me->lastchild = me->childcnt = me->nextsib = 0; return next; } 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_t)(-1) / sizeof(XML_Content)) { return NULL; } if (dtd->contentStringLen > (size_t)(-1) / sizeof(XML_Char)) { return NULL; } #endif if (dtd->scaffCount * sizeof(XML_Content) > (size_t)(-1) - dtd->contentStringLen * sizeof(XML_Char)) { return NULL; } const size_t allocsize = (dtd->scaffCount * sizeof(XML_Content) + (dtd->contentStringLen * sizeof(XML_Char))); ret = (XML_Content *)MALLOC(parser, allocsize); if (! ret) return NULL; /* 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; } } return ret; } static ELEMENT_TYPE * 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) return NULL; 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)) return NULL; } return ret; } static XML_Char * copyString(const XML_Char *s, const XML_Memory_Handling_Suite *memsuite) { 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 = memsuite->malloc_fcn(charsRequired * sizeof(XML_Char)); if (result == NULL) return NULL; /* Copy the original into place */ memcpy(result, s, charsRequired * sizeof(XML_Char)); return result; } #ifdef XML_DTD static float accountingGetCurrentAmplification(XML_Parser rootParser) { const XmlBigCount countBytesOutput = rootParser->m_accounting.countBytesDirect + rootParser->m_accounting.countBytesIndirect; const float amplificationFactor = rootParser->m_accounting.countBytesDirect ? (countBytesOutput / (float)(rootParser->m_accounting.countBytesDirect)) : 1.0f; 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 < 1) { 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|%d, 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 >= 3) || (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 >= 2) { 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 < 1) return; # if defined(XML_UNICODE) const char *const entityName = "[..]"; # else const char *const entityName = entity->name; # endif fprintf( stderr, "expat: Entities(%p): Count %9d, depth %2d/%2d %*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, (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--; } 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; } 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"; default: assert(0); /* never gets here */ return "dead code"; } assert(0); /* never gets here */ } #endif /* XML_DTD */ 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 = (char *)value; unsigned long debugLevel = strtoul(value, &afterValue, 10); if ((errno != 0) || (afterValue[0] != '\0')) { errno = 0; return defaultDebugLevel; } return debugLevel; } astropy-astropy-201cddb/cextern/expat/lib/xmlrole.c000066400000000000000000001052241507226315300225700ustar00rootroot00000000000000/* __ __ _ ___\ \/ /_ __ __ _| |_ / _ \\ /| '_ \ / _` | __| | __// \| |_) | (_| | |_ \___/_/\_\ .__/ \__,_|\__| |_| 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-2021 Sebastian Pipping Copyright (c) 2017 Rhodri James Copyright (c) 2019 David Loffredo Copyright (c) 2021 Dong-hee 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 #include #ifdef _WIN32 # include "winconfig.h" #endif #include "expat_external.h" #include "internal.h" #include "xmlrole.h" #include "ascii.h" /* Doesn't check: that ,| are not mixed in a model group content of literals */ 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'}; #endif 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'}; #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'}; #ifndef MIN_BYTES_PER_CHAR # define MIN_BYTES_PER_CHAR(enc) ((enc)->minBytesPerChar) #endif #ifdef XML_DTD # define setTopLevel(state) \ ((state)->handler \ = ((state)->documentEntity ? internalSubset : externalSubset1)) #else /* not XML_DTD */ # 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, 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, #ifdef XML_DTD externalSubset0, externalSubset1, condSect0, condSect1, condSect2, #endif /* XML_DTD */ 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) { switch (tok) { case XML_TOK_PROLOG_S: state->handler = prolog1; return XML_ROLE_NONE; case XML_TOK_XML_DECL: state->handler = prolog1; return XML_ROLE_XML_DECL; case XML_TOK_PI: state->handler = prolog1; return XML_ROLE_PI; case XML_TOK_COMMENT: state->handler = prolog1; return XML_ROLE_COMMENT; 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)) break; state->handler = doctype0; return XML_ROLE_DOCTYPE_NONE; case XML_TOK_INSTANCE_START: state->handler = error; return XML_ROLE_INSTANCE_START; } return common(state, tok); } static int PTRCALL 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; case XML_TOK_PI: return XML_ROLE_PI; case XML_TOK_COMMENT: return XML_ROLE_COMMENT; case XML_TOK_BOM: /* 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)) break; state->handler = doctype0; return XML_ROLE_DOCTYPE_NONE; case XML_TOK_INSTANCE_START: state->handler = error; return XML_ROLE_INSTANCE_START; } return common(state, tok); } static int PTRCALL 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; case XML_TOK_PI: return XML_ROLE_PI; case XML_TOK_COMMENT: return XML_ROLE_COMMENT; case XML_TOK_INSTANCE_START: state->handler = error; return XML_ROLE_INSTANCE_START; } return common(state, tok); } static int PTRCALL 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; case XML_TOK_NAME: case XML_TOK_PREFIXED_NAME: state->handler = doctype1; return XML_ROLE_DOCTYPE_NAME; } return common(state, tok); } static int PTRCALL 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; case XML_TOK_OPEN_BRACKET: state->handler = internalSubset; return XML_ROLE_DOCTYPE_INTERNAL_SUBSET; case XML_TOK_DECL_CLOSE: state->handler = prolog2; return XML_ROLE_DOCTYPE_CLOSE; case XML_TOK_NAME: if (XmlNameMatchesAscii(enc, ptr, end, KW_SYSTEM)) { state->handler = doctype3; return XML_ROLE_DOCTYPE_NONE; } if (XmlNameMatchesAscii(enc, ptr, end, KW_PUBLIC)) { state->handler = doctype2; return XML_ROLE_DOCTYPE_NONE; } break; } return common(state, tok); } static int PTRCALL 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; case XML_TOK_LITERAL: state->handler = doctype3; return XML_ROLE_DOCTYPE_PUBLIC_ID; } return common(state, tok); } static int PTRCALL 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; case XML_TOK_LITERAL: state->handler = doctype4; return XML_ROLE_DOCTYPE_SYSTEM_ID; } return common(state, tok); } static int PTRCALL 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; case XML_TOK_OPEN_BRACKET: state->handler = internalSubset; return XML_ROLE_DOCTYPE_INTERNAL_SUBSET; case XML_TOK_DECL_CLOSE: state->handler = prolog2; return XML_ROLE_DOCTYPE_CLOSE; } return common(state, tok); } static int PTRCALL 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; case XML_TOK_DECL_CLOSE: state->handler = prolog2; return XML_ROLE_DOCTYPE_CLOSE; } return common(state, tok); } static int PTRCALL 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, KW_ENTITY)) { state->handler = entity0; return XML_ROLE_ENTITY_NONE; } 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, KW_ELEMENT)) { state->handler = element0; return XML_ROLE_ELEMENT_NONE; } if (XmlNameMatchesAscii(enc, ptr + 2 * MIN_BYTES_PER_CHAR(enc), end, KW_NOTATION)) { state->handler = notation0; return XML_ROLE_NOTATION_NONE; } break; case XML_TOK_PI: return XML_ROLE_PI; case XML_TOK_COMMENT: return XML_ROLE_COMMENT; case XML_TOK_PARAM_ENTITY_REF: return XML_ROLE_PARAM_ENTITY_REF; case XML_TOK_CLOSE_BRACKET: state->handler = doctype5; return XML_ROLE_DOCTYPE_NONE; case XML_TOK_NONE: return XML_ROLE_NONE; } return common(state, tok); } #ifdef XML_DTD static int PTRCALL 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; return externalSubset1(state, tok, ptr, end, enc); } static int PTRCALL 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; return XML_ROLE_NONE; case XML_TOK_COND_SECT_CLOSE: if (state->includeLevel == 0) break; state->includeLevel -= 1; return XML_ROLE_NONE; case XML_TOK_PROLOG_S: return XML_ROLE_NONE; case XML_TOK_CLOSE_BRACKET: break; case XML_TOK_NONE: if (state->includeLevel) break; return XML_ROLE_NONE; default: return internalSubset(state, tok, ptr, end, enc); } return common(state, tok); } #endif /* XML_DTD */ static int PTRCALL 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; case XML_TOK_PERCENT: state->handler = entity1; return XML_ROLE_ENTITY_NONE; case XML_TOK_NAME: state->handler = entity2; return XML_ROLE_GENERAL_ENTITY_NAME; } return common(state, tok); } static int PTRCALL 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; case XML_TOK_NAME: state->handler = entity7; return XML_ROLE_PARAM_ENTITY_NAME; } return common(state, tok); } static int PTRCALL 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; case XML_TOK_NAME: if (XmlNameMatchesAscii(enc, ptr, end, KW_SYSTEM)) { state->handler = entity4; return XML_ROLE_ENTITY_NONE; } if (XmlNameMatchesAscii(enc, ptr, end, KW_PUBLIC)) { state->handler = entity3; return XML_ROLE_ENTITY_NONE; } break; case XML_TOK_LITERAL: state->handler = declClose; state->role_none = XML_ROLE_ENTITY_NONE; return XML_ROLE_ENTITY_VALUE; } return common(state, tok); } static int PTRCALL 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; case XML_TOK_LITERAL: state->handler = entity4; return XML_ROLE_ENTITY_PUBLIC_ID; } return common(state, tok); } static int PTRCALL 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; case XML_TOK_LITERAL: state->handler = entity5; return XML_ROLE_ENTITY_SYSTEM_ID; } return common(state, tok); } static int PTRCALL 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; case XML_TOK_DECL_CLOSE: setTopLevel(state); return XML_ROLE_ENTITY_COMPLETE; case XML_TOK_NAME: if (XmlNameMatchesAscii(enc, ptr, end, KW_NDATA)) { state->handler = entity6; return XML_ROLE_ENTITY_NONE; } break; } return common(state, tok); } static int PTRCALL 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; case XML_TOK_NAME: state->handler = declClose; state->role_none = XML_ROLE_ENTITY_NONE; return XML_ROLE_ENTITY_NOTATION_NAME; } return common(state, tok); } static int PTRCALL 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; case XML_TOK_NAME: if (XmlNameMatchesAscii(enc, ptr, end, KW_SYSTEM)) { state->handler = entity9; return XML_ROLE_ENTITY_NONE; } if (XmlNameMatchesAscii(enc, ptr, end, KW_PUBLIC)) { state->handler = entity8; return XML_ROLE_ENTITY_NONE; } break; case XML_TOK_LITERAL: state->handler = declClose; state->role_none = XML_ROLE_ENTITY_NONE; return XML_ROLE_ENTITY_VALUE; } return common(state, tok); } static int PTRCALL 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; case XML_TOK_LITERAL: state->handler = entity9; return XML_ROLE_ENTITY_PUBLIC_ID; } return common(state, tok); } static int PTRCALL 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; case XML_TOK_LITERAL: state->handler = entity10; return XML_ROLE_ENTITY_SYSTEM_ID; } return common(state, tok); } static int PTRCALL 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; case XML_TOK_DECL_CLOSE: setTopLevel(state); return XML_ROLE_ENTITY_COMPLETE; } return common(state, tok); } static int PTRCALL 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; case XML_TOK_NAME: state->handler = notation1; return XML_ROLE_NOTATION_NAME; } return common(state, tok); } static int PTRCALL 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; case XML_TOK_NAME: if (XmlNameMatchesAscii(enc, ptr, end, KW_SYSTEM)) { state->handler = notation3; return XML_ROLE_NOTATION_NONE; } if (XmlNameMatchesAscii(enc, ptr, end, KW_PUBLIC)) { state->handler = notation2; return XML_ROLE_NOTATION_NONE; } break; } return common(state, tok); } static int PTRCALL 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; case XML_TOK_LITERAL: state->handler = notation4; return XML_ROLE_NOTATION_PUBLIC_ID; } return common(state, tok); } static int PTRCALL 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; case XML_TOK_LITERAL: state->handler = declClose; state->role_none = XML_ROLE_NOTATION_NONE; return XML_ROLE_NOTATION_SYSTEM_ID; } return common(state, tok); } static int PTRCALL 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; case XML_TOK_LITERAL: state->handler = declClose; state->role_none = XML_ROLE_NOTATION_NONE; return XML_ROLE_NOTATION_SYSTEM_ID; case XML_TOK_DECL_CLOSE: setTopLevel(state); return XML_ROLE_NOTATION_NO_SYSTEM_ID; } return common(state, tok); } static int PTRCALL 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; case XML_TOK_NAME: case XML_TOK_PREFIXED_NAME: state->handler = attlist1; return XML_ROLE_ATTLIST_ELEMENT_NAME; } return common(state, tok); } static int PTRCALL 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; case XML_TOK_DECL_CLOSE: setTopLevel(state); return XML_ROLE_ATTLIST_NONE; case XML_TOK_NAME: case XML_TOK_PREFIXED_NAME: state->handler = attlist2; return XML_ROLE_ATTRIBUTE_NAME; } return common(state, tok); } static int PTRCALL 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; } } if (XmlNameMatchesAscii(enc, ptr, end, KW_NOTATION)) { state->handler = attlist5; return XML_ROLE_ATTLIST_NONE; } break; case XML_TOK_OPEN_PAREN: state->handler = attlist3; return XML_ROLE_ATTLIST_NONE; } return common(state, tok); } static int PTRCALL 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; case XML_TOK_NMTOKEN: case XML_TOK_NAME: case XML_TOK_PREFIXED_NAME: state->handler = attlist4; return XML_ROLE_ATTRIBUTE_ENUM_VALUE; } return common(state, tok); } static int PTRCALL 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; case XML_TOK_CLOSE_PAREN: state->handler = attlist8; return XML_ROLE_ATTLIST_NONE; case XML_TOK_OR: state->handler = attlist3; return XML_ROLE_ATTLIST_NONE; } return common(state, tok); } static int PTRCALL 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; case XML_TOK_OPEN_PAREN: state->handler = attlist6; return XML_ROLE_ATTLIST_NONE; } return common(state, tok); } static int PTRCALL 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; case XML_TOK_NAME: state->handler = attlist7; return XML_ROLE_ATTRIBUTE_NOTATION_VALUE; } return common(state, tok); } static int PTRCALL 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; case XML_TOK_CLOSE_PAREN: state->handler = attlist8; return XML_ROLE_ATTLIST_NONE; case XML_TOK_OR: state->handler = attlist6; return XML_ROLE_ATTLIST_NONE; } return common(state, tok); } /* default value */ static int PTRCALL 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, KW_IMPLIED)) { state->handler = attlist1; return XML_ROLE_IMPLIED_ATTRIBUTE_VALUE; } 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, KW_FIXED)) { state->handler = attlist9; return XML_ROLE_ATTLIST_NONE; } break; case XML_TOK_LITERAL: state->handler = attlist1; return XML_ROLE_DEFAULT_ATTRIBUTE_VALUE; } return common(state, tok); } static int PTRCALL 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; case XML_TOK_LITERAL: state->handler = attlist1; return XML_ROLE_FIXED_ATTRIBUTE_VALUE; } return common(state, tok); } static int PTRCALL 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; case XML_TOK_NAME: case XML_TOK_PREFIXED_NAME: state->handler = element1; return XML_ROLE_ELEMENT_NAME; } return common(state, tok); } static int PTRCALL 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; case XML_TOK_NAME: if (XmlNameMatchesAscii(enc, ptr, end, KW_EMPTY)) { state->handler = declClose; state->role_none = XML_ROLE_ELEMENT_NONE; return XML_ROLE_CONTENT_EMPTY; } if (XmlNameMatchesAscii(enc, ptr, end, KW_ANY)) { state->handler = declClose; state->role_none = XML_ROLE_ELEMENT_NONE; return XML_ROLE_CONTENT_ANY; } break; case XML_TOK_OPEN_PAREN: state->handler = element2; state->level = 1; return XML_ROLE_GROUP_OPEN; } return common(state, tok); } static int PTRCALL 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, KW_PCDATA)) { state->handler = element3; return XML_ROLE_CONTENT_PCDATA; } break; case XML_TOK_OPEN_PAREN: state->level = 2; state->handler = element6; return XML_ROLE_GROUP_OPEN; case XML_TOK_NAME: case XML_TOK_PREFIXED_NAME: state->handler = element7; return XML_ROLE_CONTENT_ELEMENT; case XML_TOK_NAME_QUESTION: state->handler = element7; return XML_ROLE_CONTENT_ELEMENT_OPT; case XML_TOK_NAME_ASTERISK: state->handler = element7; return XML_ROLE_CONTENT_ELEMENT_REP; case XML_TOK_NAME_PLUS: state->handler = element7; return XML_ROLE_CONTENT_ELEMENT_PLUS; } return common(state, tok); } static int PTRCALL 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; case XML_TOK_CLOSE_PAREN: state->handler = declClose; state->role_none = XML_ROLE_ELEMENT_NONE; return XML_ROLE_GROUP_CLOSE; case XML_TOK_CLOSE_PAREN_ASTERISK: state->handler = declClose; state->role_none = XML_ROLE_ELEMENT_NONE; return XML_ROLE_GROUP_CLOSE_REP; case XML_TOK_OR: state->handler = element4; return XML_ROLE_ELEMENT_NONE; } return common(state, tok); } static int PTRCALL 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; case XML_TOK_NAME: case XML_TOK_PREFIXED_NAME: state->handler = element5; return XML_ROLE_CONTENT_ELEMENT; } return common(state, tok); } static int PTRCALL 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; case XML_TOK_CLOSE_PAREN_ASTERISK: state->handler = declClose; state->role_none = XML_ROLE_ELEMENT_NONE; return XML_ROLE_GROUP_CLOSE_REP; case XML_TOK_OR: state->handler = element4; return XML_ROLE_ELEMENT_NONE; } return common(state, tok); } static int PTRCALL 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; case XML_TOK_OPEN_PAREN: state->level += 1; return XML_ROLE_GROUP_OPEN; case XML_TOK_NAME: case XML_TOK_PREFIXED_NAME: state->handler = element7; return XML_ROLE_CONTENT_ELEMENT; case XML_TOK_NAME_QUESTION: state->handler = element7; return XML_ROLE_CONTENT_ELEMENT_OPT; case XML_TOK_NAME_ASTERISK: state->handler = element7; return XML_ROLE_CONTENT_ELEMENT_REP; case XML_TOK_NAME_PLUS: state->handler = element7; return XML_ROLE_CONTENT_ELEMENT_PLUS; } return common(state, tok); } static int PTRCALL 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; case XML_TOK_CLOSE_PAREN: state->level -= 1; if (state->level == 0) { state->handler = declClose; state->role_none = XML_ROLE_ELEMENT_NONE; } return XML_ROLE_GROUP_CLOSE; case XML_TOK_CLOSE_PAREN_ASTERISK: state->level -= 1; if (state->level == 0) { state->handler = declClose; state->role_none = XML_ROLE_ELEMENT_NONE; } return XML_ROLE_GROUP_CLOSE_REP; case XML_TOK_CLOSE_PAREN_QUESTION: state->level -= 1; if (state->level == 0) { state->handler = declClose; state->role_none = XML_ROLE_ELEMENT_NONE; } return XML_ROLE_GROUP_CLOSE_OPT; case XML_TOK_CLOSE_PAREN_PLUS: state->level -= 1; if (state->level == 0) { state->handler = declClose; state->role_none = XML_ROLE_ELEMENT_NONE; } return XML_ROLE_GROUP_CLOSE_PLUS; case XML_TOK_COMMA: state->handler = element6; return XML_ROLE_GROUP_SEQUENCE; case XML_TOK_OR: state->handler = element6; return XML_ROLE_GROUP_CHOICE; } return common(state, tok); } #ifdef XML_DTD static int PTRCALL 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; case XML_TOK_NAME: if (XmlNameMatchesAscii(enc, ptr, end, KW_INCLUDE)) { state->handler = condSect1; return XML_ROLE_NONE; } if (XmlNameMatchesAscii(enc, ptr, end, KW_IGNORE)) { state->handler = condSect2; return XML_ROLE_NONE; } break; } return common(state, tok); } static int PTRCALL 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; case XML_TOK_OPEN_BRACKET: state->handler = externalSubset1; state->includeLevel += 1; return XML_ROLE_NONE; } return common(state, tok); } static int PTRCALL 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; case XML_TOK_OPEN_BRACKET: state->handler = externalSubset1; return XML_ROLE_IGNORE_SECT; } return common(state, tok); } #endif /* XML_DTD */ static int PTRCALL 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; case XML_TOK_DECL_CLOSE: setTopLevel(state); return state->role_none; } 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) { 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) { #ifdef XML_DTD 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) { state->handler = prolog0; #ifdef XML_DTD state->documentEntity = 1; state->includeLevel = 0; state->inEntityValue = 0; #endif /* XML_DTD */ } #ifdef XML_DTD void XmlPrologStateInitExternalEntity(PROLOG_STATE *state) { state->handler = externalSubset0; state->documentEntity = 0; state->includeLevel = 0; } #endif /* XML_DTD */ astropy-astropy-201cddb/cextern/expat/lib/xmlrole.h000066400000000000000000000110751507226315300225750ustar00rootroot00000000000000/* __ __ _ ___\ \/ /_ __ __ _| |_ / _ \\ /| '_ \ / _` | __| | __// \| |_) | (_| | |_ \___/_/\_\ .__/ \__,_|\__| |_| 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 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 #include "xmltok.h" #ifdef __cplusplus extern "C" { #endif enum { XML_ROLE_ERROR = -1, XML_ROLE_NONE = 0, XML_ROLE_XML_DECL, XML_ROLE_INSTANCE_START, XML_ROLE_DOCTYPE_NONE, XML_ROLE_DOCTYPE_NAME, XML_ROLE_DOCTYPE_SYSTEM_ID, XML_ROLE_DOCTYPE_PUBLIC_ID, XML_ROLE_DOCTYPE_INTERNAL_SUBSET, XML_ROLE_DOCTYPE_CLOSE, XML_ROLE_GENERAL_ENTITY_NAME, XML_ROLE_PARAM_ENTITY_NAME, XML_ROLE_ENTITY_NONE, XML_ROLE_ENTITY_VALUE, XML_ROLE_ENTITY_SYSTEM_ID, XML_ROLE_ENTITY_PUBLIC_ID, XML_ROLE_ENTITY_COMPLETE, XML_ROLE_ENTITY_NOTATION_NAME, XML_ROLE_NOTATION_NONE, XML_ROLE_NOTATION_NAME, XML_ROLE_NOTATION_SYSTEM_ID, XML_ROLE_NOTATION_NO_SYSTEM_ID, XML_ROLE_NOTATION_PUBLIC_ID, XML_ROLE_ATTRIBUTE_NAME, XML_ROLE_ATTRIBUTE_TYPE_CDATA, XML_ROLE_ATTRIBUTE_TYPE_ID, XML_ROLE_ATTRIBUTE_TYPE_IDREF, XML_ROLE_ATTRIBUTE_TYPE_IDREFS, XML_ROLE_ATTRIBUTE_TYPE_ENTITY, XML_ROLE_ATTRIBUTE_TYPE_ENTITIES, XML_ROLE_ATTRIBUTE_TYPE_NMTOKEN, XML_ROLE_ATTRIBUTE_TYPE_NMTOKENS, XML_ROLE_ATTRIBUTE_ENUM_VALUE, XML_ROLE_ATTRIBUTE_NOTATION_VALUE, XML_ROLE_ATTLIST_NONE, XML_ROLE_ATTLIST_ELEMENT_NAME, XML_ROLE_IMPLIED_ATTRIBUTE_VALUE, XML_ROLE_REQUIRED_ATTRIBUTE_VALUE, XML_ROLE_DEFAULT_ATTRIBUTE_VALUE, XML_ROLE_FIXED_ATTRIBUTE_VALUE, XML_ROLE_ELEMENT_NONE, XML_ROLE_ELEMENT_NAME, XML_ROLE_CONTENT_ANY, XML_ROLE_CONTENT_EMPTY, XML_ROLE_CONTENT_PCDATA, XML_ROLE_GROUP_OPEN, XML_ROLE_GROUP_CLOSE, XML_ROLE_GROUP_CLOSE_REP, XML_ROLE_GROUP_CLOSE_OPT, XML_ROLE_GROUP_CLOSE_PLUS, XML_ROLE_GROUP_CHOICE, XML_ROLE_GROUP_SEQUENCE, XML_ROLE_CONTENT_ELEMENT, XML_ROLE_CONTENT_ELEMENT_REP, XML_ROLE_CONTENT_ELEMENT_OPT, XML_ROLE_CONTENT_ELEMENT_PLUS, XML_ROLE_PI, XML_ROLE_COMMENT, #ifdef XML_DTD XML_ROLE_TEXT_DECL, XML_ROLE_IGNORE_SECT, XML_ROLE_INNER_PARAM_ENTITY_REF, #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); unsigned level; int role_none; #ifdef XML_DTD unsigned includeLevel; int documentEntity; int inEntityValue; #endif /* XML_DTD */ } PROLOG_STATE; void XmlPrologStateInit(PROLOG_STATE *); #ifdef XML_DTD void XmlPrologStateInitExternalEntity(PROLOG_STATE *); #endif /* XML_DTD */ #define XmlTokenRole(state, tok, ptr, end, enc) \ (((state)->handler)(state, tok, ptr, end, enc)) #ifdef __cplusplus } #endif #endif /* not XmlRole_INCLUDED */ astropy-astropy-201cddb/cextern/expat/lib/xmltok.c000066400000000000000000001525121507226315300224260ustar00rootroot00000000000000/* __ __ _ ___\ \/ /_ __ __ _| |_ / _ \\ /| '_ \ / _` | __| | __// \| |_) | (_| | |_ \___/_/\_\ .__/ \__,_|\__| |_| 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-2022 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 Dong-hee Na 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. */ #include #include #include /* memcpy */ #include #ifdef _WIN32 # include "winconfig.h" #endif #include "expat_external.h" #include "internal.h" #include "xmltok.h" #include "nametab.h" #ifdef XML_DTD # define IGNORE_SECTION_TOK_VTABLE , PREFIX(ignoreSectionTok) #else # 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(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)] & (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)] \ & (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)] \ & (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/ with the additional restriction of not allowing the Unicode code points 0xFFFF and 0xFFFE (sequences EF,BF,BF and EF,BF,BE). Implementation details: (A & 0x80) == 0 means A < 0x80 and (A & 0xC0) == 0xC0 means A > 0xBF */ #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))) static int PTRFASTCALL 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) { UNUSED_P(enc); return UTF8_GET_NAMING2(namePages, (const unsigned char *)p); } static int PTRFASTCALL 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) { UNUSED_P(enc); return UTF8_GET_NAMING2(nmstrtPages, (const unsigned char *)p); } static int PTRFASTCALL 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) { UNUSED_P(enc); return UTF8_INVALID2((const unsigned char *)p); } static int PTRFASTCALL 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) { UNUSED_P(enc); return UTF8_INVALID4((const unsigned char *)p); } 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); #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 *); }; #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, #else # 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 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); #include "xmltok_impl.h" #include "ascii.h" #ifdef XML_MIN_SIZE # define sb_isNameMin isNever # define sb_isNmstrtMin isNever #endif #ifdef XML_MIN_SIZE # define MINBPC(enc) ((enc)->minBytesPerChar) #else /* minimum bytes per character */ # define MINBPC(enc) 1 #endif #define SB_BYTE_TYPE(enc, p) \ (((struct normal_encoding *)(enc))->type[(unsigned char)*(p)]) #ifdef XML_MIN_SIZE static int PTRFASTCALL 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)) #else # 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)) static int PTRFASTCALL sb_byteToAscii(const ENCODING *enc, const char *p) { UNUSED_P(enc); return *p; } #else # 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)) #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)) #else # 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)) static int PTRCALL 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)) #endif #define PREFIX(ident) normal_##ident #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 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 }; 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; } } *fromLimRef = fromLim; } 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]) { 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: 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 (toLim - to < 2) { res = XML_CONVERT_OUTPUT_EXHAUSTED; goto after; } 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_)}; #endif 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_)}; #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_)}; #endif 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_)}; 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) return XML_CONVERT_COMPLETED; c = (unsigned char)**fromP; if (c & 0x80) { if (toLim - *toP < 2) return XML_CONVERT_OUTPUT_EXHAUSTED; *(*toP)++ = (char)((c >> 6) | UTF8_cval2); *(*toP)++ = (char)((c & 0x3f) | 0x80); (*fromP)++; } else { if (*toP == toLim) return XML_CONVERT_OUTPUT_EXHAUSTED; *(*toP)++ = *(*fromP)++; } } } 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_) NULL_VTABLE}; #endif 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_) 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_) NULL_VTABLE}; #endif 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_) NULL_VTABLE}; static int PTRFASTCALL unicode_byte_type(char hi, char lo) { switch ((unsigned char)hi) { /* 0xD800-0xDBFF first 16-bit code unit or high surrogate (W1) */ case 0xD8: case 0xD9: case 0xDA: case 0xDB: return BT_LEAD4; /* 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: /* noncharacter-FFFF */ case 0xFE: /* noncharacter-FFFE */ return BT_NONXML; } break; } return BT_NONASCII; } #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 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(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(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) { return LITTLE2_BYTE_TYPE(enc, p); } static int PTRFASTCALL 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) { UNUSED_P(enc); return LITTLE2_CHAR_MATCHES(p, c); } static int PTRFASTCALL 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) { UNUSED_P(enc); return LITTLE2_IS_NMSTRT_CHAR_MINBPC(p); } # 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 /* 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(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_) NULL_VTABLE}; #endif static const struct normal_encoding little2_encoding = {{VTABLE, 2, 0, #if BYTEORDER == 1234 1 #else 0 #endif }, { #define BT_COLON BT_NMSTRT #include "asciitab.h" #undef BT_COLON #include "latin1tab.h" }, STANDARD_VTABLE(little2_) NULL_VTABLE}; #if BYTEORDER != 4321 # 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_) NULL_VTABLE}; # 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_) 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(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(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) { return BIG2_BYTE_TYPE(enc, p); } static int PTRFASTCALL 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) { UNUSED_P(enc); return BIG2_CHAR_MATCHES(p, c); } static int PTRFASTCALL 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) { UNUSED_P(enc); return BIG2_IS_NMSTRT_CHAR_MINBPC(p); } # 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 /* 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(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_) NULL_VTABLE}; #endif static const struct normal_encoding big2_encoding = {{VTABLE, 2, 0, #if BYTEORDER == 4321 1 #else 0 #endif }, { #define BT_COLON BT_NMSTRT #include "asciitab.h" #undef BT_COLON #include "latin1tab.h" }, STANDARD_VTABLE(big2_) NULL_VTABLE}; #if BYTEORDER != 1234 # 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_) NULL_VTABLE}; # 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_) NULL_VTABLE}; #endif #undef PREFIX static int FASTCALL 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) /* 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) break; } return 1; } static void PTRCALL 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) { char buf[1]; char *p = buf; XmlUtf8Convert(enc, &ptr, end, &p, p + 1); if (p == buf) return -1; else return buf[0]; } static int FASTCALL isSpace(int c) { switch (c) { case 0x20: case 0xD: case 0xA: case 0x9: return 1; } return 0; } /* Return 1 if there's just optional white space or there's an S 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) { int c; char open; if (ptr == end) { *namePtr = NULL; return 1; } if (! isSpace(toAscii(enc, ptr, end))) { *nextTokPtr = ptr; return 0; } do { ptr += enc->minBytesPerChar; } while (isSpace(toAscii(enc, ptr, end))); if (ptr == end) { *namePtr = NULL; return 1; } *namePtr = ptr; for (;;) { c = toAscii(enc, ptr, end); if (c == -1) { *nextTokPtr = ptr; return 0; } if (c == ASCII_EQUALS) { *nameEndPtr = ptr; break; } if (isSpace(c)) { *nameEndPtr = ptr; do { ptr += enc->minBytesPerChar; } while (isSpace(c = toAscii(enc, ptr, end))); if (c != ASCII_EQUALS) { *nextTokPtr = ptr; return 0; } break; } ptr += enc->minBytesPerChar; } if (ptr == *namePtr) { *nextTokPtr = ptr; return 0; } ptr += enc->minBytesPerChar; c = toAscii(enc, ptr, end); while (isSpace(c)) { ptr += enc->minBytesPerChar; c = toAscii(enc, ptr, end); } if (c != ASCII_QUOT && c != ASCII_APOS) { *nextTokPtr = ptr; return 0; } open = (char)c; ptr += enc->minBytesPerChar; *valPtr = ptr; for (;; ptr += enc->minBytesPerChar) { 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) { *nextTokPtr = ptr; return 0; } } *nextTokPtr = ptr + enc->minBytesPerChar; 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_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_yes[] = {ASCII_y, ASCII_e, ASCII_s, '\0'}; static const char KW_no[] = {ASCII_n, ASCII_o, '\0'}; static int 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) { 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) { *badPtr = ptr; return 0; } if (! XmlNameMatchesAscii(enc, name, nameEnd, KW_version)) { if (! isGeneralTextEntity) { *badPtr = name; return 0; } } else { if (versionPtr) *versionPtr = val; if (versionEndPtr) *versionEndPtr = ptr; if (! parsePseudoAttribute(enc, ptr, end, &name, &nameEnd, &val, &ptr)) { *badPtr = ptr; return 0; } if (! name) { if (isGeneralTextEntity) { /* a TextDecl must have an EncodingDecl */ *badPtr = ptr; return 0; } return 1; } } 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)) { *badPtr = val; return 0; } if (encodingName) *encodingName = val; if (encoding) *encoding = encodingFinder(enc, val, ptr - enc->minBytesPerChar); if (! parsePseudoAttribute(enc, ptr, end, &name, &nameEnd, &val, &ptr)) { *badPtr = ptr; return 0; } if (! name) return 1; } if (! XmlNameMatchesAscii(enc, name, nameEnd, KW_standalone) || isGeneralTextEntity) { *badPtr = name; return 0; } if (XmlNameMatchesAscii(enc, val, ptr - enc->minBytesPerChar, KW_yes)) { if (standalone) *standalone = 1; } else if (XmlNameMatchesAscii(enc, val, ptr - enc->minBytesPerChar, KW_no)) { if (standalone) *standalone = 0; } else { *badPtr = val; return 0; } while (isSpace(toAscii(enc, ptr, end))) ptr += enc->minBytesPerChar; if (ptr != end) { *badPtr = ptr; return 0; } return 1; } static int FASTCALL checkCharRefNumber(int result) { switch (result >> 8) { 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) return -1; break; case 0xFF: if (result == 0xFFFE || result == 0xFFFF) return -1; break; } return result; } int FASTCALL XmlUtf8Encode(int c, char *buf) { enum { /* minN is minimum legal resulting value for N byte sequence */ min2 = 0x80, min3 = 0x800, min4 = 0x10000 }; if (c < 0) return 0; /* LCOV_EXCL_LINE: this case is always eliminated beforehand */ if (c < min2) { buf[0] = (char)(c | UTF8_cval1); return 1; } if (c < min3) { buf[0] = (char)((c >> 6) | UTF8_cval2); buf[1] = (char)((c & 0x3f) | 0x80); return 2; } if (c < min4) { buf[0] = (char)((c >> 12) | UTF8_cval3); buf[1] = (char)(((c >> 6) & 0x3f) | 0x80); buf[2] = (char)((c & 0x3f) | 0x80); return 3; } if (c < 0x110000) { buf[0] = (char)((c >> 18) | UTF8_cval4); buf[1] = (char)(((c >> 12) & 0x3f) | 0x80); buf[2] = (char)(((c >> 6) & 0x3f) | 0x80); buf[3] = (char)((c & 0x3f) | 0x80); return 4; } return 0; /* LCOV_EXCL_LINE: this case too is eliminated before calling */ } int FASTCALL XmlUtf16Encode(int charNum, unsigned short *buf) { if (charNum < 0) return 0; if (charNum < 0x10000) { buf[0] = (unsigned short)charNum; return 1; } if (charNum < 0x110000) { charNum -= 0x10000; buf[0] = (unsigned short)((charNum >> 10) + 0xD800); buf[1] = (unsigned short)((charNum & 0x3FF) + 0xDC00); return 2; } return 0; } struct unknown_encoding { struct normal_encoding normal; CONVERTER convert; void *userData; unsigned short utf16[256]; char utf8[256][4]; }; #define AS_UNKNOWN_ENCODING(enc) ((const struct unknown_encoding *)(enc)) int XmlSizeOfUnknownEncoding(void) { return sizeof(struct unknown_encoding); } static int PTRFASTCALL 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) return 0; return UCS2_GET_NAMING(namePages, c >> 8, c & 0xFF); } static int PTRFASTCALL 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) return 0; return UCS2_GET_NAMING(nmstrtPages, c >> 8, c & 0xFF); } static int PTRFASTCALL 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 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) 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) return XML_CONVERT_OUTPUT_EXHAUSTED; utf8 = buf; *fromP += (AS_NORMAL_ENCODING(enc)->type[(unsigned char)**fromP] - (BT_LEAD2 - 2)); } else { if (n > toLim - *toP) return XML_CONVERT_OUTPUT_EXHAUSTED; (*fromP)++; } memcpy(*toP, utf8, n); *toP += n; } } 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) { unsigned short c = uenc->utf16[(unsigned char)**fromP]; if (c == 0) { c = (unsigned short)uenc->convert(uenc->userData, *fromP); *fromP += (AS_NORMAL_ENCODING(enc)->type[(unsigned char)**fromP] - (BT_LEAD2 - 2)); } 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) { int i; struct unknown_encoding *e = (struct unknown_encoding *)mem; 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) return 0; for (i = 0; i < 256; i++) { int c = table[i]; if (c == -1) { e->normal.type[i] = BT_MALFORM; /* This shouldn't really get used. */ e->utf16[i] = 0xFFFF; e->utf8[i][0] = 1; e->utf8[i][1] = 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) { if (latin1_encoding.type[c] != BT_OTHER && 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) { 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 { if (c > 0xFFFF) return 0; if (UCS2_GET_NAMING(nmstrtPages, c >> 8, c & 0xff)) e->normal.type[i] = BT_NMSTRT; else if (UCS2_GET_NAMING(namePages, c >> 8, c & 0xff)) e->normal.type[i] = BT_NAME; else e->normal.type[i] = BT_OTHER; e->utf8[i][0] = (char)XmlUtf8Encode(c, e->utf8[i] + 1); e->utf16[i] = (unsigned short)c; } } e->userData = userData; e->convert = convert; if (convert) { e->normal.isName2 = unknown_isName; e->normal.isName3 = unknown_isName; e->normal.isName4 = unknown_isName; e->normal.isNmstrt2 = unknown_isNmstrt; e->normal.isNmstrt3 = unknown_isNmstrt; e->normal.isNmstrt4 = unknown_isNmstrt; e->normal.isInvalid2 = unknown_isInvalid; e->normal.isInvalid3 = unknown_isInvalid; e->normal.isInvalid4 = unknown_isInvalid; } e->normal.enc.utf8Convert = unknown_toUtf8; e->normal.enc.utf16Convert = unknown_toUtf16; return &(e->normal.enc); } /* If this enumeration is changed, getEncodingIndex and encodings must also be changed. */ enum { UNKNOWN_ENC = -1, ISO_8859_1_ENC = 0, US_ASCII_ENC, UTF_8_ENC, UTF_16_ENC, UTF_16BE_ENC, UTF_16LE_ENC, /* must match encodingNames up to here */ 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 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, }; int i; if (name == NULL) return NO_ENC; for (i = 0; i < (int)(sizeof(encodingNames) / sizeof(encodingNames[0])); i++) if (streqci(name, encodingNames[i])) return i; return UNKNOWN_ENC; } /* For binary compatibility, we store the index of the encoding specified at initialization in the isUtf16 member. */ #define INIT_ENC_INDEX(enc) ((int)(enc)->initEnc.isUtf16) #define SET_INIT_ENC_INDEX(enc, i) ((enc)->initEnc.isUtf16 = (char)i) /* This is what detects the encoding. encodingTable maps from encoding indices to encodings; INIT_ENC_INDEX(enc) is the index of the external (protocol) specified encoding; state is XML_CONTENT_STATE if we're parsing an external text entity, and 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) { const ENCODING **encPtr; if (ptr >= end) return XML_TOK_NONE; encPtr = enc->encPtr; if (ptr + 1 == end) { /* only a single byte available for auto-detection */ #ifndef XML_DTD /* FIXME */ /* a well-formed document entity must have more than one byte */ if (state != XML_CONTENT_STATE) return XML_TOK_PARTIAL; #endif /* so we're parsing an external text entity... */ /* if UTF-16 was externally specified, then we need at least 2 bytes */ switch (INIT_ENC_INDEX(enc)) { case UTF_16_ENC: case UTF_16LE_ENC: case UTF_16BE_ENC: return XML_TOK_PARTIAL; } switch ((unsigned char)*ptr) { 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) break; /* fall through */ case 0x00: case 0x3C: return XML_TOK_PARTIAL; } } 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) break; *nextTokPtr = ptr + 2; *encPtr = encodingTable[UTF_16BE_ENC]; return XML_TOK_BOM; /* 00 3C is handled in the default case */ case 0x3C00: if ((INIT_ENC_INDEX(enc) == UTF_16BE_ENC || INIT_ENC_INDEX(enc) == UTF_16_ENC) && state == XML_CONTENT_STATE) break; *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) break; *nextTokPtr = ptr + 2; *encPtr = encodingTable[UTF_16LE_ENC]; return XML_TOK_BOM; case 0xEFBB: /* Maybe a UTF-8 BOM (EF BB BF) */ /* If there's an explicitly specified (external) encoding of ISO-8859-1 or some flavour of UTF-16 and this is an external text entity, don't look for the BOM, because it might be a legal data. */ 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) break; } if (ptr + 2 == end) return XML_TOK_PARTIAL; if ((unsigned char)ptr[2] == 0xBF) { *nextTokPtr = ptr + 3; *encPtr = encodingTable[UTF_8_ENC]; return XML_TOK_BOM; } break; default: if (ptr[0] == '\0') { /* 0 isn't a legal data character. Furthermore a document entity can only start with ASCII characters. So the only way this can fail to be big-endian UTF-16 if it it's an external parsed general entity that's labelled as UTF-16LE. */ if (state == XML_CONTENT_STATE && INIT_ENC_INDEX(enc) == UTF_16LE_ENC) break; *encPtr = encodingTable[UTF_16BE_ENC]; return XmlTok(*encPtr, state, ptr, end, nextTokPtr); } else if (ptr[1] == '\0') { /* We could recover here in the case: - parsing an external entity - second byte is 0 - no externally specified encoding - no encoding declaration by assuming UTF-16LE. But we don't, because this would mean when presented just with a single byte, we couldn't reliably determine whether we needed further bytes. */ if (state == XML_CONTENT_STATE) break; *encPtr = encodingTable[UTF_16LE_ENC]; return XmlTok(*encPtr, state, ptr, end, nextTokPtr); } break; } } *encPtr = encodingTable[INIT_ENC_INDEX(enc)]; return XmlTok(*encPtr, state, ptr, end, nextTokPtr); } #define NS(x) x #define ns(x) x #define XML_TOK_NS_C #include "xmltok_ns.c" #undef XML_TOK_NS_C #undef NS #undef ns #ifdef XML_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 # undef NS # undef ns ENCODING * XmlInitUnknownEncodingNS(void *mem, int *table, CONVERTER convert, void *userData) { ENCODING *enc = XmlInitUnknownEncoding(mem, table, convert, userData); if (enc) ((struct normal_encoding *)enc)->type[ASCII_COLON] = BT_COLON; return enc; } #endif /* XML_NS */ astropy-astropy-201cddb/cextern/expat/lib/xmltok.h000066400000000000000000000310461507226315300224310ustar00rootroot00000000000000/* __ __ _ ___\ \/ /_ __ __ _| |_ / _ \\ /| '_ \ / _` | __| | __// \| |_) | (_| | |_ \___/_/\_\ .__/ \__,_|\__| |_| 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-2017 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 #ifdef __cplusplus extern "C" { #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 */ /* 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 /* 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 */ /* 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 */ /* 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 /* 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 /* The following token is returned only by XmlAttributeValueTok */ #define XML_TOK_ATTRIBUTE_VALUE_S 39 /* The following token is returned only by XmlCdataSectionTok */ #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 #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 */ #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 /* The size of the buffer passed to XmlUtf8Encode must be at least this. */ #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 typedef struct position { /* first line and first column are 0 not 1 */ XML_Size lineNumber; XML_Size columnNumber; } POSITION; typedef struct { const char *name; const char *valuePtr; const char *valueEnd; char normalized; } ATTRIBUTE; struct encoding; typedef struct encoding ENCODING; 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 *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); 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; }; /* Scan the string starting at ptr until the end of the next complete token, but do not scan past eptr. Return an integer giving the type of token. Return XML_TOK_NONE when ptr == eptr; nextTokPtr will not be set. Return XML_TOK_PARTIAL when the string does not contain a complete token; nextTokPtr will not be set. Return XML_TOK_INVALID when the string does not start a valid token; nextTokPtr will be set to point to the character which made the token invalid. Otherwise the string starts with a valid token; nextTokPtr will be set to point to the character following the end of that token. Each data character counts as a single token, but adjacent data characters may be returned together. Similarly for characters in the prolog outside literals, comments and processing instructions. */ #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 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) #ifdef XML_DTD # define XmlIgnoreSectionTok(enc, ptr, end, nextTokPtr) \ XmlTok(enc, XML_IGNORE_SECTION_STATE, ptr, end, nextTokPtr) #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 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 XmlNameMatchesAscii(enc, ptr1, end1, ptr2) \ (((enc)->nameMatchesAscii)(enc, ptr1, end1, ptr2)) #define XmlNameLength(enc, ptr) (((enc)->nameLength)(enc, ptr)) #define XmlSkipS(enc, ptr) (((enc)->skipS)(enc, ptr)) #define XmlGetAttributes(enc, ptr, attsMax, atts) \ (((enc)->getAtts)(enc, ptr, attsMax, atts)) #define XmlCharRefNumber(enc, ptr) (((enc)->charRefNumber)(enc, ptr)) #define XmlPredefinedEntityName(enc, ptr, end) \ (((enc)->predefinedEntityName)(enc, ptr, end)) #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 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)) 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, const char **encodingNamePtr, const ENCODING **namedEncodingPtr, int *standalonePtr); int XmlInitEncoding(INIT_ENCODING *, const ENCODING **, 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); ENCODING *XmlInitUnknownEncoding(void *mem, 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, const char **encodingNamePtr, const ENCODING **namedEncodingPtr, int *standalonePtr); int XmlInitEncodingNS(INIT_ENCODING *, const ENCODING **, const char *name); const ENCODING *XmlGetUtf8InternalEncodingNS(void); const ENCODING *XmlGetUtf16InternalEncodingNS(void); ENCODING *XmlInitUnknownEncodingNS(void *mem, int *table, CONVERTER convert, void *userData); #ifdef __cplusplus } #endif #endif /* not XmlTok_INCLUDED */ astropy-astropy-201cddb/cextern/expat/lib/xmltok_impl.c000066400000000000000000001507261507226315300234540ustar00rootroot00000000000000/* 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. */ #ifdef XML_TOK_IMPL_C # 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_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_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_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_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) # 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: *nextTokPtr = ptr; return XML_TOK_INVALID; } /* fall through */ case BT_S: case BT_CR: case BT_LF: *nextTokPtr = ptr; return XML_TOK_DECL_OPEN; case BT_NMSTRT: case BT_HEX: ptr += MINBPC(enc); break; default: *nextTokPtr = ptr; return XML_TOK_INVALID; } } return XML_TOK_PARTIAL; } static int PTRCALL 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) return 1; switch (BYTE_TO_ASCII(enc, ptr)) { case ASCII_x: break; case ASCII_X: upper = 1; break; default: return 1; } ptr += MINBPC(enc); switch (BYTE_TO_ASCII(enc, ptr)) { case ASCII_m: break; case ASCII_M: upper = 1; break; default: return 1; } ptr += MINBPC(enc); switch (BYTE_TO_ASCII(enc, ptr)) { case ASCII_l: break; case ASCII_L: upper = 1; break; default: return 1; } if (upper) return 0; *tokPtr = XML_TOK_XML_DECL; return 1; } /* ptr points to character following "= end) return XML_TOK_NONE; if (MINBPC(enc) > 1) { size_t n = end - ptr; if (n & (MINBPC(enc) - 1)) { n &= ~(MINBPC(enc) - 1); if (n == 0) return XML_TOK_PARTIAL; end = ptr + n; } } switch (BYTE_TYPE(enc, ptr)) { case BT_RSQB: ptr += MINBPC(enc); REQUIRE_CHAR(enc, ptr, end); if (! CHAR_MATCHES(enc, ptr, ASCII_RSQB)) break; ptr += MINBPC(enc); REQUIRE_CHAR(enc, ptr, end); if (! CHAR_MATCHES(enc, ptr, ASCII_GT)) { ptr -= MINBPC(enc); break; } *nextTokPtr = ptr + MINBPC(enc); return XML_TOK_CDATA_SECT_CLOSE; case BT_CR: ptr += MINBPC(enc); REQUIRE_CHAR(enc, ptr, end); if (BYTE_TYPE(enc, ptr) == BT_LF) ptr += MINBPC(enc); *nextTokPtr = ptr; return XML_TOK_DATA_NEWLINE; case BT_LF: *nextTokPtr = ptr + MINBPC(enc); return XML_TOK_DATA_NEWLINE; INVALID_CASES(ptr, nextTokPtr) default: ptr += MINBPC(enc); break; } 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 case BT_NONXML: case BT_MALFORM: case BT_TRAIL: case BT_CR: case BT_LF: case BT_RSQB: *nextTokPtr = ptr; return XML_TOK_DATA_CHARS; default: ptr += MINBPC(enc); break; } } *nextTokPtr = ptr; return XML_TOK_DATA_CHARS; } /* ptr points to character following "= end) return XML_TOK_NONE; if (MINBPC(enc) > 1) { size_t n = end - ptr; if (n & (MINBPC(enc) - 1)) { n &= ~(MINBPC(enc) - 1); if (n == 0) return XML_TOK_PARTIAL; end = ptr + n; } } switch (BYTE_TYPE(enc, ptr)) { case BT_LT: return PREFIX(scanLt)(enc, ptr + MINBPC(enc), end, nextTokPtr); case BT_AMP: return PREFIX(scanRef)(enc, ptr + MINBPC(enc), end, nextTokPtr); case BT_CR: ptr += MINBPC(enc); if (! HAS_CHAR(enc, ptr, end)) return XML_TOK_TRAILING_CR; if (BYTE_TYPE(enc, ptr) == BT_LF) ptr += MINBPC(enc); *nextTokPtr = ptr; return XML_TOK_DATA_NEWLINE; case BT_LF: *nextTokPtr = ptr + MINBPC(enc); return XML_TOK_DATA_NEWLINE; case BT_RSQB: ptr += MINBPC(enc); if (! HAS_CHAR(enc, ptr, end)) return XML_TOK_TRAILING_RSQB; if (! CHAR_MATCHES(enc, ptr, ASCII_RSQB)) break; ptr += MINBPC(enc); if (! HAS_CHAR(enc, ptr, end)) return XML_TOK_TRAILING_RSQB; if (! CHAR_MATCHES(enc, ptr, ASCII_GT)) { ptr -= MINBPC(enc); break; } *nextTokPtr = ptr; return XML_TOK_INVALID; INVALID_CASES(ptr, nextTokPtr) default: ptr += MINBPC(enc); break; } 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 case BT_RSQB: 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: case BT_LT: case BT_NONXML: case BT_MALFORM: case BT_TRAIL: case BT_CR: case BT_LF: *nextTokPtr = ptr; return XML_TOK_DATA_CHARS; default: ptr += MINBPC(enc); break; } } *nextTokPtr = ptr; return XML_TOK_DATA_CHARS; } /* ptr points to character following "%" */ static int PTRCALL PREFIX(scanPercent)(const ENCODING *enc, const char *ptr, const char *end, 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: *nextTokPtr = ptr; return XML_TOK_PERCENT; default: *nextTokPtr = ptr; return XML_TOK_INVALID; } while (HAS_CHAR(enc, ptr, end)) { switch (BYTE_TYPE(enc, ptr)) { CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) case BT_SEMI: *nextTokPtr = ptr + MINBPC(enc); return XML_TOK_PARAM_ENTITY_REF; default: *nextTokPtr = ptr; return XML_TOK_INVALID; } } return XML_TOK_PARTIAL; } static int PTRCALL PREFIX(scanPoundName)(const ENCODING *enc, const char *ptr, const char *end, const char **nextTokPtr) { REQUIRE_CHAR(enc, ptr, end); switch (BYTE_TYPE(enc, ptr)) { CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr) default: *nextTokPtr = ptr; return XML_TOK_INVALID; } 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: *nextTokPtr = ptr; return XML_TOK_POUND_NAME; default: *nextTokPtr = ptr; return XML_TOK_INVALID; } } return -XML_TOK_POUND_NAME; } static int PTRCALL 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) case BT_QUOT: case BT_APOS: ptr += MINBPC(enc); if (t != open) break; 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: return XML_TOK_LITERAL; default: return XML_TOK_INVALID; } default: ptr += MINBPC(enc); break; } } return XML_TOK_PARTIAL; } static int PTRCALL PREFIX(prologTok)(const ENCODING *enc, const char *ptr, const char *end, const char **nextTokPtr) { int tok; if (ptr >= end) return XML_TOK_NONE; if (MINBPC(enc) > 1) { size_t n = end - ptr; if (n & (MINBPC(enc) - 1)) { n &= ~(MINBPC(enc) - 1); if (n == 0) return XML_TOK_PARTIAL; end = ptr + n; } } switch (BYTE_TYPE(enc, ptr)) { case BT_QUOT: 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); 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; /* indicate that this might be part of a CR/LF pair */ return -XML_TOK_PROLOG_S; } /* fall through */ case BT_S: case BT_LF: for (;;) { ptr += MINBPC(enc); if (! HAS_CHAR(enc, ptr, end)) break; switch (BYTE_TYPE(enc, ptr)) { case BT_S: case BT_LF: break; case BT_CR: /* don't split CR/LF pair */ if (ptr + MINBPC(enc) != end) break; /* fall through */ default: *nextTokPtr = ptr; return XML_TOK_PROLOG_S; } } *nextTokPtr = ptr; return XML_TOK_PROLOG_S; case BT_PERCNT: return PREFIX(scanPercent)(enc, ptr + MINBPC(enc), end, nextTokPtr); case BT_COMMA: *nextTokPtr = ptr + MINBPC(enc); return XML_TOK_COMMA; case BT_LSQB: *nextTokPtr = ptr + MINBPC(enc); return XML_TOK_OPEN_BRACKET; case BT_RSQB: ptr += MINBPC(enc); if (! HAS_CHAR(enc, ptr, end)) return -XML_TOK_CLOSE_BRACKET; if (CHAR_MATCHES(enc, ptr, ASCII_RSQB)) { REQUIRE_CHARS(enc, ptr, end, 2); if (CHAR_MATCHES(enc, ptr + MINBPC(enc), ASCII_GT)) { *nextTokPtr = ptr + 2 * MINBPC(enc); return XML_TOK_COND_SECT_CLOSE; } } *nextTokPtr = ptr; return XML_TOK_CLOSE_BRACKET; case BT_LPAR: *nextTokPtr = ptr + MINBPC(enc); return XML_TOK_OPEN_PAREN; case BT_RPAR: ptr += MINBPC(enc); if (! HAS_CHAR(enc, ptr, end)) return -XML_TOK_CLOSE_PAREN; switch (BYTE_TYPE(enc, ptr)) { case BT_AST: *nextTokPtr = ptr + MINBPC(enc); return XML_TOK_CLOSE_PAREN_ASTERISK; case BT_QUEST: *nextTokPtr = ptr + MINBPC(enc); return XML_TOK_CLOSE_PAREN_QUESTION; 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_RPAR: *nextTokPtr = ptr; return XML_TOK_CLOSE_PAREN; } *nextTokPtr = ptr; return XML_TOK_INVALID; case BT_VERBAR: *nextTokPtr = ptr + MINBPC(enc); return XML_TOK_OR; case BT_GT: *nextTokPtr = ptr + MINBPC(enc); 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_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 case BT_NMSTRT: case BT_HEX: tok = XML_TOK_NAME; ptr += MINBPC(enc); break; case BT_DIGIT: case BT_NAME: case BT_MINUS: # ifdef XML_NS case BT_COLON: # endif tok = XML_TOK_NMTOKEN; ptr += MINBPC(enc); break; case BT_NONASCII: if (IS_NMSTRT_CHAR_MINBPC(enc, ptr)) { ptr += MINBPC(enc); tok = XML_TOK_NAME; break; } if (IS_NAME_CHAR_MINBPC(enc, ptr)) { ptr += MINBPC(enc); tok = XML_TOK_NMTOKEN; break; } /* fall through */ default: *nextTokPtr = ptr; return XML_TOK_INVALID; } 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: *nextTokPtr = ptr; return tok; # ifdef XML_NS case BT_COLON: ptr += MINBPC(enc); switch (tok) { case XML_TOK_NAME: REQUIRE_CHAR(enc, ptr, end); tok = XML_TOK_PREFIXED_NAME; switch (BYTE_TYPE(enc, ptr)) { CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) default: tok = XML_TOK_NMTOKEN; break; } break; case XML_TOK_PREFIXED_NAME: tok = XML_TOK_NMTOKEN; break; } break; # endif case BT_PLUS: 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) { *nextTokPtr = ptr; return XML_TOK_INVALID; } *nextTokPtr = ptr + MINBPC(enc); return XML_TOK_NAME_ASTERISK; case BT_QUEST: if (tok == XML_TOK_NMTOKEN) { *nextTokPtr = ptr; return XML_TOK_INVALID; } *nextTokPtr = ptr + MINBPC(enc); return XML_TOK_NAME_QUESTION; default: *nextTokPtr = ptr; return XML_TOK_INVALID; } } return -tok; } static int PTRCALL PREFIX(attributeValueTok)(const ENCODING *enc, const char *ptr, const char *end, const char **nextTokPtr) { const char *start; 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 (HAS_CHAR(enc, ptr, end)) { switch (BYTE_TYPE(enc, ptr)) { # 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); *nextTokPtr = ptr; return XML_TOK_DATA_CHARS; case BT_LT: /* this is for inside entity references */ *nextTokPtr = ptr; return XML_TOK_INVALID; case BT_LF: if (ptr == start) { *nextTokPtr = ptr + MINBPC(enc); return XML_TOK_DATA_NEWLINE; } *nextTokPtr = ptr; return XML_TOK_DATA_CHARS; case BT_CR: if (ptr == start) { ptr += MINBPC(enc); if (! HAS_CHAR(enc, ptr, end)) return XML_TOK_TRAILING_CR; if (BYTE_TYPE(enc, ptr) == BT_LF) ptr += MINBPC(enc); *nextTokPtr = ptr; return XML_TOK_DATA_NEWLINE; } *nextTokPtr = ptr; return XML_TOK_DATA_CHARS; case BT_S: if (ptr == start) { *nextTokPtr = ptr + MINBPC(enc); return XML_TOK_ATTRIBUTE_VALUE_S; } *nextTokPtr = ptr; return XML_TOK_DATA_CHARS; default: ptr += MINBPC(enc); break; } } *nextTokPtr = ptr; return XML_TOK_DATA_CHARS; } static int PTRCALL PREFIX(entityValueTok)(const ENCODING *enc, const char *ptr, const char *end, const char **nextTokPtr) { const char *start; 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 (HAS_CHAR(enc, ptr, end)) { switch (BYTE_TYPE(enc, ptr)) { # 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); *nextTokPtr = ptr; return XML_TOK_DATA_CHARS; case BT_PERCNT: if (ptr == start) { int tok = PREFIX(scanPercent)(enc, ptr + MINBPC(enc), end, nextTokPtr); return (tok == XML_TOK_PERCENT) ? XML_TOK_INVALID : tok; } *nextTokPtr = ptr; return XML_TOK_DATA_CHARS; case BT_LF: if (ptr == start) { *nextTokPtr = ptr + MINBPC(enc); return XML_TOK_DATA_NEWLINE; } *nextTokPtr = ptr; return XML_TOK_DATA_CHARS; case BT_CR: if (ptr == start) { ptr += MINBPC(enc); if (! HAS_CHAR(enc, ptr, end)) return XML_TOK_TRAILING_CR; if (BYTE_TYPE(enc, ptr) == BT_LF) ptr += MINBPC(enc); *nextTokPtr = ptr; return XML_TOK_DATA_NEWLINE; } *nextTokPtr = ptr; return XML_TOK_DATA_CHARS; default: ptr += MINBPC(enc); break; } } *nextTokPtr = ptr; return XML_TOK_DATA_CHARS; } # ifdef XML_DTD static int PTRCALL 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; if (n & (MINBPC(enc) - 1)) { n &= ~(MINBPC(enc) - 1); end = ptr + n; } } while (HAS_CHAR(enc, ptr, end)) { switch (BYTE_TYPE(enc, ptr)) { INVALID_CASES(ptr, nextTokPtr) case BT_LT: ptr += MINBPC(enc); REQUIRE_CHAR(enc, ptr, end); if (CHAR_MATCHES(enc, ptr, ASCII_EXCL)) { ptr += MINBPC(enc); REQUIRE_CHAR(enc, ptr, end); if (CHAR_MATCHES(enc, ptr, ASCII_LSQB)) { ++level; ptr += MINBPC(enc); } } break; case BT_RSQB: ptr += MINBPC(enc); REQUIRE_CHAR(enc, ptr, end); if (CHAR_MATCHES(enc, ptr, ASCII_RSQB)) { ptr += MINBPC(enc); REQUIRE_CHAR(enc, ptr, end); if (CHAR_MATCHES(enc, ptr, ASCII_GT)) { ptr += MINBPC(enc); if (level == 0) { *nextTokPtr = ptr; return XML_TOK_IGNORE_SECT; } --level; } } break; default: ptr += MINBPC(enc); break; } } return XML_TOK_PARTIAL; } # endif /* XML_DTD */ static int PTRCALL PREFIX(isPublicId)(const ENCODING *enc, const char *ptr, const char *end, const char **badPtr) { ptr += MINBPC(enc); end -= MINBPC(enc); for (; HAS_CHAR(enc, ptr, end); ptr += MINBPC(enc)) { switch (BYTE_TYPE(enc, ptr)) { case BT_DIGIT: case BT_HEX: case BT_MINUS: case BT_APOS: case BT_LPAR: case BT_RPAR: case BT_PLUS: case BT_COMMA: case BT_SOL: case BT_EQUALS: case BT_QUEST: case BT_CR: case BT_LF: case BT_SEMI: case BT_EXCL: case BT_AST: case BT_PERCNT: case BT_NUM: # ifdef XML_NS case BT_COLON: # endif break; case BT_S: if (CHAR_MATCHES(enc, ptr, ASCII_TAB)) { *badPtr = ptr; return 0; } break; case BT_NAME: case BT_NMSTRT: if (! (BYTE_TO_ASCII(enc, ptr) & ~0x7f)) break; /* fall through */ default: switch (BYTE_TO_ASCII(enc, ptr)) { case 0x24: /* $ */ case 0x40: /* @ */ break; default: *badPtr = ptr; return 0; } break; } } return 1; } /* This must only be called for a well-formed start-tag or empty element tag. Returns the number of attributes. Pointers to the first attsMax attributes are stored in atts. */ static int PTRCALL 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; initialization just to shut up compilers */ 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: /* 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 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) { state = other; if (nAtts < attsMax) atts[nAtts].valueEnd = ptr; nAtts++; } break; case BT_APOS: if (state != inValue) { if (nAtts < attsMax) atts[nAtts].valuePtr = ptr + MINBPC(enc); state = inValue; open = BT_APOS; } else if (open == BT_APOS) { state = other; if (nAtts < attsMax) atts[nAtts].valueEnd = ptr; nAtts++; } break; case BT_AMP: if (nAtts < attsMax) atts[nAtts].normalized = 0; break; case BT_S: if (state == inName) state = other; 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: /* This case ensures that the first attribute name is counted Apart from that we could just change state on the quote. */ if (state == inName) state = other; else if (state == inValue && nAtts < attsMax) atts[nAtts].normalized = 0; break; case BT_GT: case BT_SOL: if (state != inValue) return nAtts; break; default: break; } } /* not reached */ } static int PTRFASTCALL PREFIX(charRefNumber)(const ENCODING *enc, const char *ptr) { int result = 0; /* skip &# */ UNUSED_P(enc); ptr += 2 * MINBPC(enc); if (CHAR_MATCHES(enc, ptr, ASCII_x)) { 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: 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: 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: result <<= 4; result += 10 + (c - ASCII_a); break; } if (result >= 0x110000) return -1; } } else { for (; ! CHAR_MATCHES(enc, ptr, ASCII_SEMI); ptr += MINBPC(enc)) { int c = BYTE_TO_ASCII(enc, ptr); result *= 10; result += (c - ASCII_0); if (result >= 0x110000) return -1; } } return checkCharRefNumber(result); } static int PTRCALL PREFIX(predefinedEntityName)(const ENCODING *enc, const char *ptr, 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)) { case ASCII_l: return ASCII_LT; case ASCII_g: return ASCII_GT; } } break; case 3: if (CHAR_MATCHES(enc, ptr, ASCII_a)) { ptr += MINBPC(enc); if (CHAR_MATCHES(enc, ptr, ASCII_m)) { ptr += MINBPC(enc); if (CHAR_MATCHES(enc, ptr, ASCII_p)) return ASCII_AMP; } } break; case 4: switch (BYTE_TO_ASCII(enc, ptr)) { case ASCII_q: ptr += MINBPC(enc); if (CHAR_MATCHES(enc, ptr, ASCII_u)) { ptr += MINBPC(enc); if (CHAR_MATCHES(enc, ptr, ASCII_o)) { ptr += MINBPC(enc); if (CHAR_MATCHES(enc, ptr, ASCII_t)) return ASCII_QUOT; } } break; case ASCII_a: ptr += MINBPC(enc); if (CHAR_MATCHES(enc, ptr, ASCII_p)) { ptr += MINBPC(enc); if (CHAR_MATCHES(enc, ptr, ASCII_o)) { ptr += MINBPC(enc); if (CHAR_MATCHES(enc, ptr, ASCII_s)) return ASCII_APOS; } } break; } } return 0; } static int PTRCALL PREFIX(nameMatchesAscii)(const ENCODING *enc, const char *ptr1, const char *end1, const char *ptr2) { UNUSED_P(enc); for (; *ptr2; ptr1 += MINBPC(enc), 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) { const char *start = ptr; for (;;) { switch (BYTE_TYPE(enc, ptr)) { # 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 case BT_COLON: # endif case BT_HEX: case BT_DIGIT: case BT_NAME: case BT_MINUS: ptr += MINBPC(enc); break; default: return (int)(ptr - start); } } } static const char *PTRFASTCALL PREFIX(skipS)(const ENCODING *enc, const char *ptr) { for (;;) { switch (BYTE_TYPE(enc, ptr)) { case BT_LF: case BT_CR: case BT_S: ptr += MINBPC(enc); break; default: return ptr; } } } static void PTRCALL 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; /* 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 = 0; pos->lineNumber++; ptr += MINBPC(enc); break; case BT_CR: pos->lineNumber++; ptr += MINBPC(enc); if (HAS_CHAR(enc, ptr, end) && BYTE_TYPE(enc, ptr) == BT_LF) ptr += MINBPC(enc); pos->columnNumber = 0; break; default: ptr += MINBPC(enc); pos->columnNumber++; break; } } } # 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 */ astropy-astropy-201cddb/cextern/expat/lib/xmltok_impl.h000066400000000000000000000065461507226315300234610ustar00rootroot00000000000000/* __ __ _ ___\ \/ /_ __ __ _| |_ / _ \\ /| '_ \ / _` | __| | __// \| |_) | (_| | |_ \___/_/\_\ .__/ \__,_|\__| |_| 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, /* 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, /* 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 astropy-astropy-201cddb/cextern/expat/lib/xmltok_ns.c000066400000000000000000000110401507226315300231140ustar00rootroot00000000000000/* 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. */ #ifdef XML_TOK_NS_C const ENCODING * NS(XmlGetUtf8InternalEncoding)(void) { return &ns(internal_utf8_encoding).enc; } const ENCODING * NS(XmlGetUtf16InternalEncoding)(void) { # if BYTEORDER == 1234 return &ns(internal_little2_encoding).enc; # elif BYTEORDER == 4321 return &ns(internal_big2_encoding).enc; # else const short n = 1; 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 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); } 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); } int NS(XmlInitEncoding)(INIT_ENCODING *p, const ENCODING **encPtr, const char *name) { int i = getEncodingIndex(name); if (i == UNKNOWN_ENC) return 0; SET_INIT_ENC_INDEX(p, i); p->initEnc.scanners[XML_PROLOG_STATE] = NS(initScanProlog); p->initEnc.scanners[XML_CONTENT_STATE] = NS(initScanContent); p->initEnc.updatePosition = initUpdatePosition; p->encPtr = encPtr; *encPtr = &(p->initEnc); return 1; } static const ENCODING * 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); if (ptr != end) return 0; *p = 0; if (streqci(buf, KW_UTF_16) && enc->minBytesPerChar == 2) return enc; i = getEncodingIndex(buf); if (i == UNKNOWN_ENC) return 0; return NS(encodings)[i]; } 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); } #endif /* XML_TOK_NS_C */ astropy-astropy-201cddb/cextern/trim_cfitsio.sh000077500000000000000000000037021507226315300221030ustar00rootroot00000000000000#!/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/README rm -f cfitsio/INSTALL rm -f cfitsio/configure rm -f cfitsio/install-sh rm -f cfitsio/docs/*.tex rm -f cfitsio/docs/*.ps rm -f cfitsio/docs/*.pdf rm -f cfitsio/docs/*.doc rm -f cfitsio/docs/*.toc rm -f cfitsio/docs/*.odt rm -rf cfitsio/[^L]*.* rm -rf cfitsio/utilities 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 astropy-astropy-201cddb/cextern/trim_wcslib.sh000077500000000000000000000004251507226315300217250ustar00rootroot00000000000000#!/bin/sh # This script should be run every time wcslib is updated. # This removes extra large files from wcslib that aren't needed. rm -rf wcslib/C/test rm -rf wcslib/doxygen rm -rf wcslib/Fortran rm -rf wcslib/html rm -rf wcslib/pgsbox rm -rf wcslib/utils rm wcslib/*.pdf astropy-astropy-201cddb/cextern/wcslib/000077500000000000000000000000001507226315300203325ustar00rootroot00000000000000astropy-astropy-201cddb/cextern/wcslib/C/000077500000000000000000000000001507226315300205145ustar00rootroot00000000000000astropy-astropy-201cddb/cextern/wcslib/C/GNUmakefile000066400000000000000000000434161507226315300225760ustar00rootroot00000000000000#----------------------------------------------------------------------------- # GNU makefile for building WCSLIB 8.4 and its test suite. # # Summary of the main targets # --------------------------- # build: Build the library. # # clean: Delete intermediate object files. # # cleaner: clean, and also delete the test executables. # # cleanest (distclean or realclean): cleaner, and also delete the object # 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 # "PASS" on success. Use # # make MODE=interactive check # # to run them interactively with full diagnostic output. To skip # graphical tests even if PGPLOT is available, use # # make CHECK=nopgplot check # # tests: Compile the test programs (but don't run them). # # Notes: # 1) If you need to make changes then preferably modify ../makedefs.in # instead and re-run configure. # # Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. # http://www.atnf.csiro.au/people/Mark.Calabretta # $Id: GNUmakefile,v 8.4 2024/10/28 13:56:16 mcalabre Exp $ #----------------------------------------------------------------------------- # Get configure settings. SUBDIR := C include ../makedefs FLEXMODS := $(patsubst %.l,%.c,$(wildcard *.l)) MODULES := $(sort \ $(patsubst %.c,%.o, \ $(filter-out getwcstab.c,$(wildcard *.c)) $(FLEXMODS))) ifeq "$(WCSTRIG)" "MACRO" CPPFLAGS += -DWCSTRIG_MACRO MODULES := $(filter-out wcstrig.o, $(MODULES)) else ifeq "$(WCSTRIG)" "NATIVE" MODULES := $(filter-out wcstrig.o, $(MODULES)) endif endif LIBLOCK := lib.lock # For building the sharable library. PICLIB := libwcs-PIC.a CPPFLAGS += -I. -I.. vpath %.c test vpath %.h .. vpath %.in .. # For building and exercising the test suite # ------------------------------------------ # Test programs that don't require CFITSIO or PGPLOT... 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 := tdisiter tspcaips tspcspxe tspctrne twcs_locale # Test programs that require CFITSIO (they don't need PGPLOT). 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 PGSBOX (and therefore PGPLOT). TEST_B := tpih2 # 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) # 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 # Do we have PGPLOT? DO_PGPLOT := 0 ifneq "$(CHECK)" "nopgplot" 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. 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 #-------------- ifeq "$(FLEX)" "flex" %.c : %.l -@ echo '' -@ $(RM) $@ $(FLEX) $(FLFLAGS) -t $< | sed -e 's/^[ ]*#/#/' > $@ else %.c : %.l -@ echo '' -@ $(RM) $@ cp flexed/$@ . endif $(WCSLIB)(%.o) : %.c -@ echo '' $(CC) $(CPPFLAGS) $(CFLAGS) -c $< @ if [ ! -f $(LIBLOCK) ] ; then \ echo $(AR) r$(ARFLAGS) $(WCSLIB) $% ; \ $(AR) r$(ARFLAGS) $(WCSLIB) $% ; \ $(RM) $% ; \ fi $(PICLIB)(%.o) : $(WCSLIB)(%.o) -@ echo '' $(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) $@ $(CPP) $(CPPFLAGS) $(CFLAGS) $< > $@ # Print out include file dependencies. %.d : %.c -@ echo '' -@ $(CPP) $(CPPFLAGS) $(CFLAGS) $< | \ sed -n -e 's|^# 1 "\([^/].*\.h\)".*|\1|p' | \ sed -e 's|.*/||' | \ sort -u %.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) @ if [ '$(MODE)' = interactive -o '$(VALGRIND)' ] ; then \ printf 'Press to run $<: ' ; \ read DUMMY ; \ fi ; \ if [ '$(VALGRIND)' ] ; then \ if [ '$<' = tunits ] ; then \ $(VALGRIND) ./$< < test/units_test ; \ else \ $(VALGRIND) ./$< ; \ fi ; \ else \ if [ '$(filter $<, $(TEST_N) $(TEST_C))' ] ; then \ if [ '$<' = tunits ] ; then \ if [ '$(MODE)' = interactive ] ; then \ ./$< < test/units_test 2>&1 | tee $<.out ; \ else \ ./$< < test/units_test > $<.out 2>&1 ; \ fi ; \ else \ if [ '$(MODE)' = interactive ] ; then \ ./$< < /dev/null 2>&1 | tee $<.out ; \ else \ ./$< < /dev/null > $<.out 2>&1 ; \ fi ; \ fi ; \ 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_$<.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 -2 $<.out ; \ fi ; \ echo 'PASS: Output agrees with C/test/$<.out' ; \ echo 'PASS: C/$<' >> test_results ; \ else \ if [ '$(MODE)' != interactive ] ; then \ cat $<.out ; \ fi ; \ echo '' ; \ echo 'FAIL: Output disagrees with C/test/$<.out' ; \ echo 'FAIL: C/$<' >> test_results ; \ fi ; \ elif [ '$(MODE)' != interactive ] ; then \ cat $<.out ; \ echo 'FAIL: C/$<' >> test_results ; \ fi ; \ elif [ '$(MODE)' = interactive ] ; then \ ./$< ; \ else \ if [ '$<' = tcel2 ] ; then \ echo N | ./$< ; \ else \ ./$< < /dev/null 2>&1 ; \ fi ; \ fi ; \ fi -@ echo '' # Static and static pattern rules #-------------------------------- .PHONY : build check clean cleaner cleanest distclean install lib realclean \ run_% test tests uninstall build : lib lib : $(FLEXMODS) -@ echo '' -@ echo 'Building WCSLIB C library...' @ $(MAKE) --no-print-directory $(WCSLIB) $(WCSLIB) : $(LIBLOCK) $(MODULES:%=$(WCSLIB)(%)) -@ echo '' @ 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 '' -@ $(RM) -r tmp mkdir tmp && \ cd tmp && \ trap 'cd .. ; $(RM) -r tmp' 0 1 2 3 15 ; \ $(AR) x ../$(PICLIB) && \ $(SHRLD) -o $@ *.o $(LDFLAGS) $(LIBS) && \ mv $@ .. $(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 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 $(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 755 $(SHRLIB) $(LIBDIR) ; \ if [ -h "$(LIBDIR)/$(SONAME)" ] ; then \ $(RM) $(LIBDIR)/$(SONAME) ; \ fi ; \ $(LN_S) $(SHRLIB) $(LIBDIR)/$(SONAME) ; \ if [ "$(SHRLN)" != "" ] ; then \ if [ -h "$(LIBDIR)/$(SHRLN)" ] ; then \ $(RM) $(LIBDIR)/$(SHRLN) ; \ fi ; \ $(LN_S) $(SONAME) $(LIBDIR)/$(SHRLN) ; \ fi ; \ fi - if [ ! -d "$(INCDIR)" ] ; then \ $(INSTALL) -d -m 775 $(INCDIR) ; \ fi $(INSTALL) -m 444 *.h $(INCDIR) - $(RM) $(INCLINK) $(LN_S) $(notdir $(INCDIR)) $(INCLINK) uninstall : - cd $(LIBDIR) && $(RM) $(WCSLIB) $(SHRLN) $(SONAME) $(SHRLIB) - $(RM) $(INCDIR) - $(RM) $(INCLINK) 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_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 - $(RM) ../wcsconfig.h ../wcsconfig_tests.h - $(RM) fitshdr.c wcsbth.c wcspih.c wcsulex.c wcsutrn.c - $(RM) $(PICLIB) libwcs-*.a libwcs.so.* libwcs.*.dylib 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 $< $(LD) -o $@ $@.o $(LDFLAGS) $(PGSBOXLIB) $(PGPLOTLIB) $(WCSLIB) \ $(FLIBS) $(LIBS) -@ $(RM) $@.o tpih2_cfitsio : test/tpih2.c $(PGSBOXLIB) $(WCSLIB) -@ echo '' $(CC) -DDO_CFITSIO $(CPPFLAGS) -I../pgsbox $(PGPLOTINC) \ $(CFITSIOINC) $(CFITSIO_CFLAGS) -c -o $@.o $< $(LD) -o $@ $@.o $(LDFLAGS) $(PGSBOXLIB) $(PGPLOTLIB) \ $(CFITSIOLIB) $(WCSLIB) $(FLIBS) $(LIBS) -@ $(RM) $@.o # POSIX threads test programs. $(TEST_T) : %_pthread : test/%_pthread.c $(WCSLIB) -@ echo '' $(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) $(CFITSIO_CFLAGS) $(CFITSIOINC) -c $< $(PGSBOXLIB) : -@ echo '' $(MAKE) -C ../pgsbox $(notdir $@) ../utils/tofits : ../utils/tofits.c $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ $< GNUmakefile : ../makedefs ; ../makedefs ../wcsconfig.h ../wcsconfig_tests.h : makedefs.in wcsconfig.h.in \ wcsconfig_tests.h.in ../config.status -@ $(RM) ../wcsconfig.h ../wcsconfig_tests.h cd .. && ./config.status show :: -@ 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)(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 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 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 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) : wcsmath.h wcsutil.h $(WCSLIB)(wcsutrn.o) : wcserr.h wcsunits.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 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 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 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 ttab1 : tab.h ttab2 : tab.h ttab3 : prj.h tab.h tunits : wcserr.h wcsunits.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 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 : 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 astropy-astropy-201cddb/cextern/wcslib/C/cel.c000066400000000000000000000333371507226315300214340ustar00rootroot00000000000000/*============================================================================ WCSLIB 8.4 - an implementation of the FITS WCS standard. Copyright (C) 1995-2024, 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/people/Mark.Calabretta $Id: cel.c,v 8.4 2024/10/28 13:56:16 mcalabre Exp $ *===========================================================================*/ #include #include #include #include "wcserr.h" #include "wcsmath.h" #include "wcsprintf.h" #include "wcstrig.h" #include "sph.h" #include "cel.h" // Map status return value to message. const char *cel_errmsg[] = { "Success", "Null celprm pointer passed", "Invalid projection parameters", "Invalid coordinate transformation parameters", "Ill-conditioned coordinate transformation parameters", "One or more of the (x,y) coordinates were invalid", "One or more of the (lng,lat) coordinates were invalid"}; // 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; // Convenience macro for invoking wcserr_set(). #define CEL_ERRMSG(status) WCSERR_SET(status), cel_errmsg[status] //---------------------------------------------------------------------------- int celini(struct celprm *cel) { if (cel == 0x0) return CELERR_NULL_POINTER; cel->offset = 0; cel->phi0 = UNDEFINED; cel->theta0 = UNDEFINED; cel->ref[0] = 0.0; cel->ref[1] = 0.0; cel->ref[2] = UNDEFINED; cel->ref[3] = +90.0; for (int k = 0; k < 5; cel->euler[k++] = 0.0); cel->latpreq = -1; cel->isolat = 0; cel->err = 0x0; cel->flag = 0; return cel_prjerr[prjini(&(cel->prj))]; } //---------------------------------------------------------------------------- int celfree(struct celprm *cel) { if (cel == 0x0) return CELERR_NULL_POINTER; 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; } // 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 celenq(const struct celprm *cel, int enquiry) { // 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; // Parameters supplied. wcsprintf(" flag: %d\n", cel->flag); wcsprintf(" offset: %d\n", cel->offset); if (undefined(cel->phi0)) { wcsprintf(" phi0: UNDEFINED\n"); } else { wcsprintf(" phi0: %9f\n", cel->phi0); } if (undefined(cel->theta0)) { wcsprintf(" theta0: UNDEFINED\n"); } else { wcsprintf(" theta0: %9f\n", cel->theta0); } wcsprintf(" ref:"); for (int i = 0; i < 4; i++) { wcsprintf(" %#- 11.5g", cel->ref[i]); } wcsprintf("\n"); wcsprintf(" prj: (see below)\n"); // Derived values. wcsprintf(" euler:"); for (int i = 0; i < 5; i++) { wcsprintf(" %#- 11.5g", cel->euler[i]); } wcsprintf("\n"); wcsprintf(" latpreq: %d", cel->latpreq); if (cel->latpreq == 0) { wcsprintf(" (not required)\n"); } else if (cel->latpreq == 1) { wcsprintf(" (disambiguation)\n"); } else if (cel->latpreq == 2) { wcsprintf(" (specification)\n"); } else { wcsprintf(" (UNDEFINED)\n"); } 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)); return 0; } //---------------------------------------------------------------------------- int celperr(const struct celprm *cel, const char *prefix) { 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; if (cel == 0x0) return CELERR_NULL_POINTER; if (cel->flag == -CELSET) return 0; struct wcserr **err = &(cel->err); // 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. celprj->phi0 = UNDEFINED; celprj->theta0 = UNDEFINED; } celprj->flag = 0; int status; if ((status = prjset(celprj))) { return wcserr_set(CEL_ERRMSG(cel_prjerr[status])); } // Defaults set by the projection routines. if (undefined(cel->phi0)) { cel->phi0 = celprj->phi0; } if (undefined(cel->theta0)) { cel->theta0 = celprj->theta0; } else if (fabs(cel->theta0) > 90.0) { if (fabs(cel->theta0) > 90.0 + tol) { return wcserr_set(WCSERR_SET(CELERR_BAD_COORD_TRANS), "Invalid coordinate transformation parameters: theta0 > 90"); } if (cel->theta0 > 90.0) { cel->theta0 = 90.0; } else { cel->theta0 = -90.0; } } 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? if (undefined(phip) || phip == 999.0) { phip = (lat0 < cel->theta0) ? 180.0 : 0.0; phip += cel->phi0; if (phip < -180.0) { phip += 360.0; } else if (phip > 180.0) { phip -= 360.0; } cel->ref[2] = phip; } // Compute celestial coordinates of the native pole. cel->latpreq = 0; if (cel->theta0 == 90.0) { // Fiducial point at the native pole. lngp = lng0; latp = lat0; } else { // 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; u = cel->theta0; v = 90.0 - lat0; } else { sincosd(phip - cel->phi0, &sphip, &cphip); 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), "Invalid coordinate description:\n" "lat0 == 0 is required for |phip - phi0| = 90 and theta0 == 0"); } // latp determined solely by LATPOLEa in this case. cel->latpreq = 2; if (latp > 90.0) { latp = 90.0; } else if (latp < -90.0) { latp = -90.0; } // Avert a spurious compiler warning. u = v = 0.0; } else { double slz = slat0/z; if (fabs(slz) > 1.0) { if ((fabs(slz) - 1.0) < tol) { if (slz > 0.0) { slz = 1.0; } else { slz = -1.0; } } else { return wcserr_set(WCSERR_SET(CELERR_BAD_COORD_TRANS), "Invalid coordinate description:\n|lat0| <= %.3f is required " "for these values of phip, phi0, and theta0", asind(z)); } } u = atan2d(y,x); v = acosd(slz); } } if (cel->latpreq == 0) { double latp1 = u + v; if (latp1 > 180.0) { latp1 -= 360.0; } else if (latp1 < -180.0) { latp1 += 360.0; } double latp2 = u - v; if (latp2 > 180.0) { latp2 -= 360.0; } else if (latp2 < -180.0) { latp2 += 360.0; } if (fabs(latp1) < 90.0+tol && fabs(latp2) < 90.0+tol) { // There are two valid solutions for latp. cel->latpreq = 1; } if (fabs(latp-latp1) < fabs(latp-latp2)) { if (fabs(latp1) < 90.0+tol) { latp = latp1; } else { latp = latp2; } } else { if (fabs(latp2) < 90.0+tol) { latp = latp2; } else { latp = latp1; } } // Account for rounding error. if (fabs(latp) < 90.0+tol) { if (latp > 90.0) { latp = 90.0; } else if (latp < -90.0) { latp = -90.0; } } } double z = cosd(latp)*clat0; if (fabs(z) < tol) { if (fabs(clat0) < tol) { // Celestial pole at the fiducial point. lngp = lng0; } else if (latp > 0.0) { // Celestial north pole at the native pole. lngp = lng0 + phip - cel->phi0 - 180.0; } else { // Celestial south pole at the native pole. lngp = lng0 - phip + cel->phi0; } } else { 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). 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. if (lng0 >= 0.0) { if (lngp < 0.0) { lngp += 360.0; } else if (lngp > 360.0) { lngp -= 360.0; } } else { if (lngp > 0.0) { lngp -= 360.0; } else if (lngp < -360.0) { lngp += 360.0; } } } // Reset LATPOLEa. cel->ref[3] = latp; // 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); // 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( 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"; // Initialize. if (cel == 0x0) return CELERR_NULL_POINTER; struct wcserr **err = &(cel->err); int status = 0; if (abs(cel->flag) != CELSET) { if ((status = celset(cel))) return status; } // 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 (istat) { status = wcserr_set(CEL_ERRMSG(cel_prjerr[istat])); if (status != CELERR_BAD_PIX) { return status; } } } int nphi = (ny > 0) ? (nx*ny) : nx; // Compute celestial coordinates. sphx2s(cel->euler, nphi, 0, 1, sll, phi, theta, lng, lat); return status; } //---------------------------------------------------------------------------- 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"; // Initialize. if (cel == 0x0) return CELERR_NULL_POINTER; struct wcserr **err = &(cel->err); int status = 0; if (abs(cel->flag) != CELSET) { if ((status = celset(cel))) return status; } // 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. nphi = nlng; ntheta = nlat; } else { nphi = (nlat > 0) ? (nlng*nlat) : nlng; ntheta = 0; } // 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 (istat) { status = wcserr_set(CEL_ERRMSG(cel_prjerr[istat])); if (status != CELERR_BAD_WORLD) { return status; } } } return status; } astropy-astropy-201cddb/cextern/wcslib/C/cel.h000066400000000000000000000511721507226315300214360ustar00rootroot00000000000000/*============================================================================ WCSLIB 8.4 - an implementation of the FITS WCS standard. Copyright (C) 1995-2024, 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/people/Mark.Calabretta $Id: cel.h,v 8.4 2024/10/28 13:56:16 mcalabre Exp $ *============================================================================= * * WCSLIB 8.4 - 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 * --------------------------- * 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, 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 * needs to be set up by celset() but it need not be called explicitly - refer * to the explanation of celprm::flag. * * celx2s() and cels2x() implement the WCS celestial coordinate * transformations. In fact, they are high level driver routines for the lower * level spherical coordinate rotation and projection routines described in * sph.h and prj.h. * * * celini() - Default constructor for the celprm struct * ---------------------------------------------------- * 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. * * Function return value: * int Status return value: * 0: Success. * 1: Null celprm pointer passed. * * * celfree() - Destructor for the celprm struct * -------------------------------------------- * celfree() frees any memory that may have been allocated to store an error * message in the celprm struct. * * Given: * cel struct celprm* * Celestial transformation parameters. * * Function return value: * int Status return value: * 0: Success. * 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 * intended for diagnostic purposes. * * Given: * cel const struct celprm* * Celestial transformation parameters. * * Function return value: * int Status return value: * 0: Success. * 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 * it. * * 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* * Celestial transformation parameters. * * Function return value: * int Status return value: * 0: Success. * 1: Null celprm pointer passed. * 2: Invalid projection parameters. * 3: Invalid coordinate transformation parameters. * 4: Ill-conditioned coordinate transformation * parameters. * * For returns > 1, a detailed error message is set in * celprm::err if enabled, see wcserr_enable(). * * * celx2s() - Pixel-to-world celestial transformation * -------------------------------------------------- * celx2s() transforms (x,y) coordinates in the plane of projection to * celestial coordinates (lng,lat). * * Given and returned: * cel struct celprm* * Celestial transformation parameters. * * Given: * nx,ny int Vector lengths. * * sxy,sll int Vector strides. * * x,y const double[] * Projected coordinates in pseudo "degrees". * * Returned: * phi,theta double[] Longitude and latitude (phi,theta) in the native * coordinate system of the projection [deg]. * * lng,lat double[] Celestial longitude and latitude (lng,lat) of the * projected point [deg]. * * stat int[] Status return value for each vector element: * 0: Success. * 1: Invalid value of (x,y). * * Function return value: * int Status return value: * 0: Success. * 1: Null celprm pointer passed. * 2: Invalid projection parameters. * 3: Invalid coordinate transformation parameters. * 4: Ill-conditioned coordinate transformation * parameters. * 5: One or more of the (x,y) coordinates were * invalid, as indicated by the stat vector. * * For returns > 1, a detailed error message is set in * celprm::err if enabled, see wcserr_enable(). * * * cels2x() - World-to-pixel celestial transformation * -------------------------------------------------- * cels2x() transforms celestial coordinates (lng,lat) to (x,y) coordinates in * the plane of projection. * * Given and returned: * cel struct celprm* * Celestial transformation parameters. * * Given: * nlng,nlat int Vector lengths. * * sll,sxy int Vector strides. * * lng,lat const double[] * Celestial longitude and latitude (lng,lat) of the * projected point [deg]. * * Returned: * phi,theta double[] Longitude and latitude (phi,theta) in the native * coordinate system of the projection [deg]. * * x,y double[] Projected coordinates in pseudo "degrees". * * stat int[] Status return value for each vector element: * 0: Success. * 1: Invalid value of (lng,lat). * * Function return value: * int Status return value: * 0: Success. * 1: Null celprm pointer passed. * 2: Invalid projection parameters. * 3: Invalid coordinate transformation parameters. * 4: Ill-conditioned coordinate transformation * parameters. * 6: One or more of the (lng,lat) coordinates were * invalid, as indicated by the stat vector. * * For returns > 1, a detailed error message is set in * celprm::err if enabled, see wcserr_enable(). * * * celprm struct - Celestial transformation parameters * --------------------------------------------------- * The celprm struct 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. * * Returned celprm struct members must not be modified by the user. * * int flag * (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, * - celprm::theta0, * - celprm::ref[4], * - celprm::prj: * - prjprm::code, * - prjprm::r0, * - prjprm::pv[], * - prjprm::phi0, * - prjprm::theta0. * * This signals the initialization routine, celset(), to recompute the * returned members of the celprm struct. celset() will reset flag to * indicate that this has been done. * * int offset * (Given) If true (non-zero), an offset will be applied to (x,y) to * force (x,y) = (0,0) at the fiducial point, (phi_0,theta_0). * Default is 0 (false). * * double phi0 * (Given) The native longitude, phi_0 [deg], and ... * * double theta0 * (Given) ... the native latitude, theta_0 [deg], of the fiducial point, * i.e. the point whose celestial coordinates are given in * celprm::ref[1:2]. If undefined (set to a magic value by prjini()) the * initialization routine, celset(), will set this to a projection-specific * default. * * double ref[4] * (Given) The first pair of values should be set to the celestial * longitude and latitude of the fiducial point [deg] - 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 [deg], and latitude, theta_p [deg], 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 phi_0 (see above) if the celestial latitude of the * fiducial point of the projection is greater than or equal to the native * latitude, otherwise phi_0 + 180 [deg]. (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 UNDEFINED (from * wcsmath.h) 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. * * struct prjprm prj * (Given and returned) Projection parameters described in the prologue to * prj.h. * * double euler[5] * (Returned) Euler angles and associated intermediaries derived from the * coordinate reference values. The first three values are the Z-, X-, and * Z'-Euler angles [deg], and the remaining two are the cosine and sine of * the X-Euler angle. * * int latpreq * (Returned) 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. * * int isolat * (Returned) True if the spherical rotation preserves the magnitude of the * latitude, which occurs iff 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. * * struct wcserr *err * (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. * *===========================================================================*/ #ifndef WCSLIB_CEL #define WCSLIB_CEL #include "prj.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. }; 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 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. #define CELLEN (sizeof(struct celprm)/sizeof(int)) 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, const double x[], const double y[], double phi[], double theta[], double lng[], double lat[], 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[]); // Deprecated. #define celini_errmsg cel_errmsg #define celprt_errmsg cel_errmsg #define celset_errmsg cel_errmsg #define celx2s_errmsg cel_errmsg #define cels2x_errmsg cel_errmsg #ifdef __cplusplus } #endif #endif // WCSLIB_CEL astropy-astropy-201cddb/cextern/wcslib/C/dis.c000066400000000000000000002723611507226315300214520ustar00rootroot00000000000000/*============================================================================ WCSLIB 8.4 - an implementation of the FITS WCS standard. Copyright (C) 1995-2024, 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/people/Mark.Calabretta $Id: dis.c,v 8.4 2024/10/28 13:56:16 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; } astropy-astropy-201cddb/cextern/wcslib/C/dis.h000066400000000000000000001457141507226315300214600ustar00rootroot00000000000000/*============================================================================ WCSLIB 8.4 - an implementation of the FITS WCS standard. Copyright (C) 1995-2024, 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/people/Mark.Calabretta $Id: dis.h,v 8.4 2024/10/28 13:56:16 mcalabre Exp $ *============================================================================= * * WCSLIB 8.4 - 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/people/Mark.Calabretta * * 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 * found at the registry of FITS conventions: * = 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: * 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 astropy-astropy-201cddb/cextern/wcslib/C/fitshdr.h000066400000000000000000000430371507226315300223370ustar00rootroot00000000000000/*============================================================================ WCSLIB 8.4 - an implementation of the FITS WCS standard. Copyright (C) 1995-2024, 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/people/Mark.Calabretta $Id: fitshdr.h,v 8.4 2024/10/28 13:56:16 mcalabre Exp $ *============================================================================= * * WCSLIB 8.4 - 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 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 * = "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 * * 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() * to handle the remainder. * * * fitshdr() - FITS header parser routine * -------------------------------------- * fitshdr() parses a character array containing a FITS header, extracting * all keywords and their values into an array of fitskey structs. * * Given: * header const char [] * Character array containing the (entire) FITS header, * for example, as might be obtained conveniently via the * CFITSIO routine fits_hdr2str(). * * Each header "keyrecord" (formerly "card image") * consists of exactly 80 7-bit ASCII printing characters * in the range 0x20 to 0x7e (which excludes NUL, BS, * TAB, LF, FF and CR) especially noting that the * keyrecords are NOT null-terminated. * * nkeyrec int Number of keyrecords in header[]. * * nkeyids int Number of entries in keyids[]. * * Given and returned: * keyids struct fitskeyid [] * While all keywords are extracted from the header, * 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::idx are returned by * fitshdr(). All matched keywords will have their * fitskey::keyno member negated. * * Returned: * nreject int* Number of header keyrecords rejected for syntax * errors. * * keys struct fitskey** * Pointer to an array of nkeyrec fitskey structs * containing all keywords and keyvalues extracted from * the header. * * Memory for the array is allocated by fitshdr() and * this must be freed by the user. See wcsdealloc(). * * Function return value: * int Status return value: * 0: Success. * 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 * NOST 100-2.0, noting the following points in particular: * * a: Sect. 5.1.2.1 specifies that keywords be left-justified in columns * 1-8, blank-filled with no embedded spaces, composed only of the * ASCII characters ABCDEFGHJKLMNOPQRSTUVWXYZ0123456789-_ * * fitshdr() accepts any characters in columns 1-8 but flags keywords * that do not conform to standard syntax. * * b: Sect. 5.1.2.2 defines the "value indicator" as the characters "= " * occurring in columns 9 and 10. If these are absent then the * keyword has no value and columns 9-80 may contain any ASCII text * (but see note 2 for CONTINUE keyrecords). This is copied to the * comment member of the fitskey struct. * * c: Sect. 5.1.2.3 states that a keyword may have a null (undefined) * value if the value/comment field, columns 11-80, consists entirely * of spaces, possibly followed by a comment. * * d: Sect. 5.1.1 states that trailing blanks in a string keyvalue are * not significant and the parser always removes them. A string * containing nothing but blanks will be replaced with a single * blank. * * Sect. 5.2.1 also states that a quote character (') in a string * value is to be represented by two successive quote characters and * the parser removes the repeated quote. * * e: The parser recognizes free-format character (NOST 100-2.0, * Sect. 5.2.1), integer (Sect. 5.2.3), and floating-point values * (Sect. 5.2.4) for all keywords. * * 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 * 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). * * g: END not followed by 77 blanks is not considered to be a legitimate * end keyrecord. * * 2: The parser supports a generalization of the OGIP Long String Keyvalue * Convention (v1.0) whereby strings may be continued onto successive * header keyrecords. A keyrecord contains a segment of a continued * string if and only if * * a: it contains the pseudo-keyword CONTINUE, * * b: columns 9 and 10 are both blank, * * c: columns 11 to 80 contain what would be considered a valid string * keyvalue, including optional keycomment, if column 9 had contained * '=', * * d: the previous keyrecord contained either a valid string keyvalue or * a valid CONTINUE keyrecord. * * If any of these conditions is violated, the keyrecord is considered in * isolation. * * Syntax errors in keycomments in a continued string are treated more * permissively than usual; the '/' delimiter may be omitted provided that * parsing of the string keyvalue is not compromised. However, the * FITSHDR_COMMENT status bit will be set for the keyrecord (see * fitskey::status). * * As for normal strings, trailing blanks in a continued string are not * significant. * * In the OGIP convention "the '&' character is used as the last non-blank * character of the string to indicate that the string is (probably) * continued on the following keyword". This additional syntax is not * required by fitshdr(), but if '&' does occur as the last non-blank * character of a continued string keyvalue then it will be removed, along * with any trailing blanks. However, blanks that occur before the '&' * will be preserved. * * * fitskeyid struct - Keyword indexing * ----------------------------------- * fitshdr() uses the fitskeyid struct to return indexing information for * specified keywords. The struct contains three members, the first of which, * fitskeyid::name, must be set by the user with the remainder returned by * fitshdr(). * * char name[12]: * (Given) Name of the required keyword. This is to be set by the user; * the '.' character may be used for wildcarding. Trailing blanks will be * replaced with nulls. * * int count: * (Returned) The number of matches found for the keyword. * * int idx[2]: * (Returned) Indices into keys[], the array of fitskey structs returned by * fitshdr(). Note that these are 0-relative array indices, not keyrecord * numbers. * * If the keyword is found in the header the first index will be set to the * array index of its first occurrence, otherwise it will be set to -1. * * If multiples of the keyword are found, the second index will be set to * the array index of its last occurrence, otherwise it will be set to -1. * * * fitskey struct - Keyword/value information * ------------------------------------------ * fitshdr() returns an array of fitskey structs, each of which contains the * result of parsing one FITS header keyrecord. All members of the fitskey * struct are returned by fitshdr(), none are given by the user. * * int keyno * (Returned) Keyrecord number (1-relative) in the array passed as input to * fitshdr(). This will be negated if the keyword matched any specified in * the keyids[] index. * * int keyid * (Returned) Index into the first entry in keyids[] with which the * keyrecord matches, else -1. * * int status * (Returned) Status flag bit-vector for the header keyrecord employing the * following bit masks defined as preprocessor macros: * * - FITSHDR_KEYWORD: Illegal keyword syntax. * - FITSHDR_KEYVALUE: Illegal keyvalue syntax. * - FITSHDR_COMMENT: Illegal keycomment syntax. * - FITSHDR_KEYREC: Illegal keyrecord, e.g. an END keyrecord with * trailing text. * - FITSHDR_TRAILER: Keyrecord following a valid END keyrecord. * * The header keyrecord is syntactically correct if no bits are set. * * char keyword[12] * (Returned) Keyword name, null-filled for keywords of less than eight * characters (trailing blanks replaced by nulls). * * Use * = sprintf(dst, "%.8s", keyword) * * to copy it to a character array with null-termination, or * = sprintf(dst, "%8.8s", keyword) * * to blank-fill to eight characters followed by null-termination. * * int type * (Returned) Keyvalue data type: * - 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). * - 4: Very long integer (see below). * - 5: Floating point (stored as double). * - 6: Integer complex (stored as double[2]). * - 7: Floating point complex (stored as double[2]). * - 8: String. * - 8+10*n: Continued string (described below and in fitshdr() note 2). * * A negative type indicates that a syntax error was encountered when * attempting to parse a keyvalue of the particular type. * * Comments on particular data types: * - 64-bit signed integers lie in the range * = (-9223372036854775808 <= int64 < -2147483648) || = (+2147483647 < int64 <= +9223372036854775807) * * A native 64-bit data type may be defined via preprocessor macro * WCSLIB_INT64 defined in wcsconfig.h, e.g. as 'long long int'; this * will be typedef'd to 'int64' here. If WCSLIB_INT64 is not set, then * int64 is typedef'd to int[3] instead and fitskey::keyvalue is to be * computed as * = ((keyvalue.k[2]) * 1000000000 + = keyvalue.k[1]) * 1000000000 + = keyvalue.k[0] * * and may reported via * = if (keyvalue.k[2]) { = printf("%d%09d%09d", keyvalue.k[2], abs(keyvalue.k[1]), = abs(keyvalue.k[0])); = } else { = printf("%d%09d", keyvalue.k[1], abs(keyvalue.k[0])); = } * * where keyvalue.k[0] and keyvalue.k[1] range from -999999999 to * +999999999. * * - Very long integers, up to 70 decimal digits in length, are encoded * in keyvalue.l as an array of int[8], each of which stores 9 decimal * digits. fitskey::keyvalue is to be computed as * = (((((((keyvalue.l[7]) * 1000000000 + = keyvalue.l[6]) * 1000000000 + = keyvalue.l[5]) * 1000000000 + = keyvalue.l[4]) * 1000000000 + = keyvalue.l[3]) * 1000000000 + = keyvalue.l[2]) * 1000000000 + = keyvalue.l[1]) * 1000000000 + = keyvalue.l[0] * * - Continued strings are not reconstructed, they remain split over * successive fitskey structs in the keys[] array returned by * fitshdr(). fitskey::keyvalue data type, 8 + 10n, indicates the * segment number, n, in the continuation. * * int padding * (An unused variable inserted for alignment purposes only.) * * union keyvalue * (Returned) A union comprised of * * - fitskey::i, * - fitskey::k, * - fitskey::l, * - fitskey::f, * - fitskey::c, * - fitskey::s, * * used by the fitskey struct to contain the value associated with a * keyword. * * int i * (Returned) Logical (fitskey::type == 1) and 32-bit signed integer * (fitskey::type == 2) data types in the fitskey::keyvalue union. * * int64 k * (Returned) 64-bit signed integer (fitskey::type == 3) data type in the * fitskey::keyvalue union. * * int l[8] * (Returned) Very long integer (fitskey::type == 4) data type in the * fitskey::keyvalue union. * * double f * (Returned) Floating point (fitskey::type == 5) data type in the * fitskey::keyvalue union. * * double c[2] * (Returned) Integer and floating point complex (fitskey::type == 6 || 7) * data types in the fitskey::keyvalue union. * * char s[72] * (Returned) Null-terminated string (fitskey::type == 8) data type in the * fitskey::keyvalue union. * * int ulen * (Returned) Where a keycomment contains a units string in the standard * form, e.g. [m/s], the ulen member indicates its length, inclusive of * square brackets. Otherwise ulen is zero. * * char comment[84] * (Returned) Keycomment, i.e. comment associated with the keyword or, for * keyrecords rejected because of syntax errors, the compete keyrecord * itself with null-termination. * * Comments are null-terminated with trailing spaces removed. Leading * spaces are also removed from keycomments (i.e. those immediately * following the '/' character), but not from COMMENT or HISTORY keyrecords * or keyrecords without a value indicator ("= " in columns 9-80). * * * Global variable: const char *fitshdr_errmsg[] - Status return messages * ---------------------------------------------------------------------- * Error messages to match the status value returned from each function. * *===========================================================================*/ #ifndef WCSLIB_FITSHDR #define WCSLIB_FITSHDR #include "wcsconfig.h" #ifdef __cplusplus extern "C" { #endif #define FITSHDR_KEYWORD 0x01 #define FITSHDR_KEYVALUE 0x02 #define FITSHDR_COMMENT 0x04 #define FITSHDR_KEYREC 0x08 #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 typedef int int64[3]; #endif // 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. }; // 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 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.) 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. }; // Size of the fitskey struct in int units, used by the Fortran wrappers. #define KEYLEN (sizeof(struct fitskey)/sizeof(int)) int fitshdr(const char header[], int nkeyrec, int nkeyids, struct fitskeyid keyids[], int *nreject, struct fitskey **keys); #ifdef __cplusplus } #endif #endif // WCSLIB_FITSHDR astropy-astropy-201cddb/cextern/wcslib/C/fitshdr.l000066400000000000000000000336041507226315300223420ustar00rootroot00000000000000/*============================================================================ WCSLIB 8.4 - an implementation of the FITS WCS standard. Copyright (C) 1995-2024, 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/people/Mark.Calabretta $Id: fitshdr.l,v 8.4 2024/10/28 13:56:16 mcalabre Exp $ *============================================================================= * * fitshdr.l is a Flex description file containing a lexical scanner * definition for extracting keywords and keyvalues from a FITS header. * * It requires Flex v2.5.4 or later. * * Refer to fitshdr.h for a description of the user interface and operating * notes. * *===========================================================================*/ /* 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] KW1 {KEYCHR}{1}" "{7} KW2 {KEYCHR}{2}" "{6} KW3 {KEYCHR}{3}" "{5} KW4 {KEYCHR}{4}" "{4} KW5 {KEYCHR}{5}" "{3} KW6 {KEYCHR}{6}" "{2} KW7 {KEYCHR}{7}" "{1} KW8 {KEYCHR}{8} KEYWORD ({KW1}|{KW2}|{KW3}|{KW4}|{KW5}|{KW6}|{KW7}|{KW8}) /* Keyvalue data types. */ LOGICAL [TF] 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]+)([eEdD][+-]?[0-9]+)? ICOMPLX \(" "*{INTEGER}" "*," "*{INTEGER}" "*\) FCOMPLX \(" "*{FLOAT}" "*," "*{FLOAT}" "*\) STRING '([^']|'')*' /* Characters forming standard unit strings (jwBIQX are not used). */ UNITSTR \[[-+*/^(). 0-9a-zA-Z]+\] /* Exclusive start states. */ %x VALUE INLINE UNITS COMMENT ERROR FLUSH %{ #include #include #include #include #include #include "fitshdr.h" #include "wcsutil.h" // 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 (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 void nullfill(char cptr[], int len); // 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"}; %} %% char ctmp[72]; if (keys == 0x0) { return FITSHDRERR_NULL_POINTER; } // 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 FITSHDRERR_MEMORY; } // 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 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. strncpy(kptr->keyword, yytext, 8); yyless(0); blank = 1; BEGIN(COMMENT); } ^(COMMENT|HISTORY|" "{8}) { strncpy(kptr->keyword, yytext, 8); BEGIN(COMMENT); } ^END" "{77} { strncpy(kptr->keyword, yytext, 8); end = 1; BEGIN(FLUSH); } ^END" "{5}=" "+ { // Illegal END keyrecord. strncpy(kptr->keyword, yytext, 8); kptr->status |= FITSHDR_KEYREC; BEGIN(VALUE); } ^END" "{5} { // Illegal END keyrecord. strncpy(kptr->keyword, yytext, 8); kptr->status |= FITSHDR_KEYREC; BEGIN(COMMENT); } ^{KEYWORD}=" "+ { strncpy(kptr->keyword, yytext, 8); BEGIN(VALUE); } ^CONTINUE" "+{STRING} { // Continued string keyvalue. strncpy(kptr->keyword, yytext, 8); if (keyno > 0 && (kptr-1)->type%10 == 8) { // Put back the string keyvalue. int k; for (k = 10; yytext[k] != '\''; k++); yyless(k); continuation = 1; BEGIN(VALUE); } else { // Not a valid continuation. yyless(8); BEGIN(COMMENT); } } ^{KEYWORD} { // Keyword without value. strncpy(kptr->keyword, yytext, 8); BEGIN(COMMENT); } ^.{8}=" "+ { // Illegal keyword, carry on regardless. strncpy(kptr->keyword, yytext, 8); kptr->status |= FITSHDR_KEYWORD; BEGIN(VALUE); } ^.{8} { // Illegal keyword, carry on regardless. strncpy(kptr->keyword, yytext, 8); kptr->status |= FITSHDR_KEYWORD; BEGIN(COMMENT); } " "*/\/ { // Null keyvalue. BEGIN(INLINE); } {LOGICAL} { // Logical keyvalue. kptr->type = 1; kptr->keyvalue.i = (*yytext == 'T'); BEGIN(INLINE); } {INT32} { // 32-bit signed integer keyvalue. kptr->type = 2; if (sscanf(yytext, "%d", &(kptr->keyvalue.i)) < 1) { kptr->status |= FITSHDR_KEYVALUE; BEGIN(ERROR); } BEGIN(INLINE); } {INT64} { // 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. kptr->type = 2; if (sscanf(yytext, "%d", &(kptr->keyvalue.i)) < 1) { kptr->status |= FITSHDR_KEYVALUE; BEGIN(ERROR); } } else { // 64-bit signed integer. kptr->type = 3; #ifdef WCSLIB_INT64 // 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]. kptr->keyvalue.k[2] = 0; 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 (*yytext == '-') { kptr->keyvalue.k[0] *= -1; } #endif } BEGIN(INLINE); } {INTVL} { // Very long integer keyvalue (and 19-digit int64). kptr->type = 4; strcpy(ctmp, yytext); int j, k = yyleng; for (j = 0; j < 8; j++) { // 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 (*yytext == '-') { kptr->keyvalue.l[j] = -abs(kptr->keyvalue.l[j]); } if (k == 0) break; ctmp[k] = '\0'; } // 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 && kptr->keyvalue.l[0] >= -854775808) { kptr->type = 3; #ifdef WCSLIB_INT64 // Native 64-bit integer is available. kptr->keyvalue.l[2] = 0; if (sscanf(yytext, int64fmt, &(kptr->keyvalue.k)) < 1) { kptr->status |= FITSHDR_KEYVALUE; BEGIN(ERROR); } #endif } BEGIN(INLINE); } {FLOAT} { // Float keyvalue. kptr->type = 5; if (wcsutil_str2double(yytext, &(kptr->keyvalue.f))) { kptr->status |= FITSHDR_KEYVALUE; BEGIN(ERROR); } BEGIN(INLINE); } {ICOMPLX} { // Integer complex keyvalue. kptr->type = 6; if (sscanf(yytext, "(%lf,%lf)", kptr->keyvalue.c, kptr->keyvalue.c+1) < 2) { kptr->status |= FITSHDR_KEYVALUE; BEGIN(ERROR); } BEGIN(INLINE); } {FCOMPLX} { // 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, kptr->keyvalue.c)) { kptr->status |= FITSHDR_KEYVALUE; BEGIN(ERROR); } for (cptr = ctmp, k++; yytext[k] != ')'; cptr++, k++) { *cptr = yytext[k]; } *cptr = '\0'; if (wcsutil_str2double(ctmp, kptr->keyvalue.c+1)) { kptr->status |= FITSHDR_KEYVALUE; BEGIN(ERROR); } BEGIN(INLINE); } {STRING} { // String keyvalue. kptr->type = 8; char *cptr = kptr->keyvalue.s; strcpy(cptr, yytext+1); // Squeeze out repeated quotes. int k = 0; for (int j = 0; j < 72; j++) { if (k < j) { cptr[k] = cptr[j]; } if (cptr[j] == '\0') { if (k) cptr[k-1] = '\0'; break; } else if (cptr[j] == '\'' && cptr[j+1] == '\'') { j++; } k++; } if (*cptr) { // Retain the initial blank in all-blank strings. nullfill(cptr+1, 71); } else { nullfill(cptr, 72); } BEGIN(INLINE); } . { kptr->status |= FITSHDR_KEYVALUE; BEGIN(ERROR); } " "*$ { BEGIN(FLUSH); } " "*\/" "*$ { BEGIN(FLUSH); } " "*\/" "* { BEGIN(UNITS); } " " { kptr->status |= FITSHDR_COMMENT; BEGIN(ERROR); } . { // Keyvalue parsing must now also be suspect. kptr->status |= FITSHDR_COMMENT; kptr->type = 0; BEGIN(ERROR); } {UNITSTR} { kptr->ulen = yyleng; yymore(); BEGIN(COMMENT); } . { yymore(); BEGIN(COMMENT); } .* { strcpy(kptr->comment, yytext); nullfill(kptr->comment, 84); BEGIN(FLUSH); } .* { if (!continuation) kptr->type = -abs(kptr->type); sprintf(kptr->comment, "%.80s", yyextra->hdr-80); kptr->comment[80] = '\0'; nullfill(kptr->comment+80, 4); BEGIN(FLUSH); } .*\n { // Discard the rest of the input line. kptr->keyno = ++keyno; // Null-fill the keyword. kptr->keyword[8] = '\0'; nullfill(kptr->keyword, 12); // Do indexing. iptr = keyids; kptr->keyid = -1; 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++) { if (*cptr != '.' && *cptr != kptr->keyword[k]) break; } if (k == 8) { // Found a match. iptr->count++; if (iptr->idx[0] == -1) { iptr->idx[0] = keyno-1; } else { iptr->idx[1] = keyno-1; } kptr->keyno = -abs(kptr->keyno); if (kptr->keyid < 0) kptr->keyid = j; } } // Deal with continued strings. if (continuation) { // Tidy up the previous string keyvalue. if ((kptr-1)->type == 8) (kptr-1)->type += 10; 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. if (end && (end++ > 1) && !blank) { kptr->status |= FITSHDR_TRAILER; } if (kptr->status) (*nreject)++; kptr++; blank = 0; continuation = 0; BEGIN(INITIAL); } <> { // End-of-input. return 0; } %% /*---------------------------------------------------------------------------- * 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) { // Propagate the terminating null to the end of the string. int j; for (j = 0; j < len; j++) { if (cptr[j] == '\0') { for (int k = j+1; k < len; k++) { cptr[k] = '\0'; } break; } } // Remove trailing blanks. for (int k = j-1; k >= 0; k--) { if (cptr[k] != ' ') break; cptr[k] = '\0'; } return; } astropy-astropy-201cddb/cextern/wcslib/C/flexed/000077500000000000000000000000001507226315300217635ustar00rootroot00000000000000astropy-astropy-201cddb/cextern/wcslib/C/flexed/README000066400000000000000000000004441507226315300226450ustar00rootroot00000000000000This 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. astropy-astropy-201cddb/cextern/wcslib/C/flexed/fitshdr.c000066400000000000000000020434211507226315300236000ustar00rootroot00000000000000#line 2 "fitshdr.c" #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 #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 #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 #endif #ifdef yyrestart #define fitshdrrestart_ALREADY_DEFINED #else #define yyrestart fitshdrrestart #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 #endif #ifdef yyfree #define fitshdrfree_ALREADY_DEFINED #else #define yyfree fitshdrfree #endif /* First, we deal with platform-specific or compiler-specific issues. */ /* begin standard C headers. */ #include #include #include #include /* end standard C headers. */ /* flex integer type definitions */ #ifndef FLEXINT_H #define FLEXINT_H /* C99 systems have . Non-C99 systems may or may not. */ #if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, * if you want the limit (max/min) macros for int types. */ #ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS 1 #endif #include typedef int8_t flex_int8_t; typedef uint8_t flex_uint8_t; typedef int16_t flex_int16_t; typedef uint16_t flex_uint16_t; typedef int32_t flex_int32_t; typedef uint32_t flex_uint32_t; #else typedef signed char flex_int8_t; typedef short int flex_int16_t; typedef int flex_int32_t; typedef unsigned char flex_uint8_t; typedef unsigned short int flex_uint16_t; typedef unsigned int flex_uint32_t; /* Limits of integral types. */ #ifndef INT8_MIN #define INT8_MIN (-128) #endif #ifndef INT16_MIN #define INT16_MIN (-32767-1) #endif #ifndef INT32_MIN #define INT32_MIN (-2147483647-1) #endif #ifndef INT8_MAX #define INT8_MAX (127) #endif #ifndef INT16_MAX #define INT16_MAX (32767) #endif #ifndef INT32_MAX #define INT32_MAX (2147483647) #endif #ifndef UINT8_MAX #define UINT8_MAX (255U) #endif #ifndef UINT16_MAX #define UINT16_MAX (65535U) #endif #ifndef UINT32_MAX #define UINT32_MAX (4294967295U) #endif #ifndef SIZE_MAX #define SIZE_MAX (~(size_t)0) #endif #endif /* ! C99 */ #endif /* ! FLEXINT_H */ /* begin standard C++ headers. */ /* TODO: this is always defined, so inline it */ #define yyconst const #if defined(__GNUC__) && __GNUC__ >= 3 #define yynoreturn __attribute__((__noreturn__)) #else #define yynoreturn #endif /* Returned upon end-of-file. */ #define YY_NULL 0 /* 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) ((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 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 ((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 yyrestart( yyin , yyscanner ) #define YY_END_OF_BUFFER_CHAR 0 /* Size of default input buffer. */ #ifndef YY_BUF_SIZE #ifdef __ia64__ /* On IA-64, the buffer size is 16k, not 8k. * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. * Ditto for the __ia64__ case accordingly. */ #define YY_BUF_SIZE 32768 #else #define YY_BUF_SIZE 16384 #endif /* __ia64__ */ #endif /* The state buf must be large enough to hold one state per character in the main buffer. */ #define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) #ifndef YY_TYPEDEF_YY_BUFFER_STATE #define YY_TYPEDEF_YY_BUFFER_STATE typedef struct yy_buffer_state *YY_BUFFER_STATE; #endif #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 yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ *yy_cp = yyg->yy_hold_char; \ YY_RESTORE_YY_MORE_OFFSET \ 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, yyg->yytext_ptr , yyscanner ) #ifndef YY_STRUCT_YY_BUFFER_STATE #define YY_STRUCT_YY_BUFFER_STATE struct yy_buffer_state { FILE *yy_input_file; char *yy_ch_buf; /* input buffer */ char *yy_buf_pos; /* current position in input buffer */ /* Size of input buffer in bytes, not including room for EOB * characters. */ int yy_buf_size; /* Number of characters read into yy_ch_buf, not including EOB * characters. */ int yy_n_chars; /* Whether we "own" the buffer - i.e., we know we created it, * and can realloc() it to grow it, and should free() it to * delete it. */ int yy_is_our_buffer; /* Whether this is an "interactive" input source; if so, and * if we're using stdio for input, then we want to use getc() * instead of fread(), to make sure we stop fetching input after * each newline. */ int yy_is_interactive; /* Whether we're considered to be at the beginning of a line. * If so, '^' rules will be active on the next match, otherwise * not. */ int yy_at_bol; 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. */ int yy_fill_buffer; int yy_buffer_status; #define YY_BUFFER_NEW 0 #define YY_BUFFER_NORMAL 1 /* When an EOF's been seen but there's still some text to process * then we mark the buffer as YY_EOF_PENDING, to indicate that we * shouldn't try reading from the input source any more. We might * still have a bunch of tokens to match, though, because of * possible backing-up. * * When we actually see the EOF, we change the status to "new" * (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 */ /* 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 ( 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 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 ){ \ yyensure_buffer_stack (yyscanner); \ YY_CURRENT_BUFFER_LVALUE = \ 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 ){\ yyensure_buffer_stack (yyscanner); \ YY_CURRENT_BUFFER_LVALUE = \ 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(yyscanner) (/*CONSTCOND*/1) #define YY_SKIP_YYWRAP typedef flex_uint8_t YY_CHAR; typedef int yy_state_type; #define yytext_ptr yytext_r static const flex_int16_t yy_nxt[][128] = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 15, 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, 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, 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, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16 }, { 15, 17, 17, 17, 17, 17, 17, 17, 17, 17, 16, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 18, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 19, 17, 17, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 17, 17, 17, 17, 17, 17, 17, 19, 19, 20, 19, 21, 19, 19, 22, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 17, 17, 17, 17, 19, 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 }, { 15, 23, 23, 23, 23, 23, 23, 23, 23, 23, 16, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 24, 23, 23, 23, 23, 23, 23, 25, 26, 23, 23, 27, 23, 27, 28, 29, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 32, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 32, 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, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23 }, { 15, 23, 23, 23, 23, 23, 23, 23, 23, 23, 16, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 24, 23, 23, 23, 23, 23, 23, 25, 26, 23, 23, 27, 23, 27, 28, 29, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 32, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 32, 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, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23 }, { 15, 33, 33, 33, 33, 33, 33, 33, 33, 33, 34, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 35, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 36, 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 }, { 15, 33, 33, 33, 33, 33, 33, 33, 33, 33, 34, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 35, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 36, 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 }, { 15, 37, 37, 37, 37, 37, 37, 37, 37, 37, 16, 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, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 38, 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 }, { 15, 37, 37, 37, 37, 37, 37, 37, 37, 37, 16, 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, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 38, 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 }, { 15, 39, 39, 39, 39, 39, 39, 39, 39, 39, 16, 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, 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, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39 }, { 15, 39, 39, 39, 39, 39, 39, 39, 39, 39, 16, 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, 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, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39 }, { 15, 40, 40, 40, 40, 40, 40, 40, 40, 40, 16, 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, 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 }, { 15, 40, 40, 40, 40, 40, 40, 40, 40, 40, 16, 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, 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 }, { 15, 41, 41, 41, 41, 41, 41, 41, 41, 41, 42, 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, 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 }, { 15, 41, 41, 41, 41, 41, 41, 41, 41, 41, 42, 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, 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 }, { -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15 }, { 15, -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, -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, -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, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16 }, { 15, 43, 43, 43, 43, 43, 43, 43, 43, 43, -17, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 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, 43, 43, 43, 43, 43, 43, 43, 43, 43, -18, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 44, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 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, 43, 43, 43, 43, 43, 43, 43, 43, 43, -19, 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, 46, 43, 43, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 43, 43, 43, 43, 43, 43, 43, 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, 46, 43, 43, 43, 43, 43, 43, 43, 43, 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, 43, 43, 43, 43, 43, 43, 43, 43, 43, -20, 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, 46, 43, 43, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 43, 43, 43, 43, 43, 43, 43, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 47, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 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, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43 }, { 15, 43, 43, 43, 43, 43, 43, 43, 43, 43, -21, 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, 46, 43, 43, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 43, 43, 43, 43, 43, 43, 43, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 48, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 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, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43 }, { 15, 43, 43, 43, 43, 43, 43, 43, 43, 43, -22, 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, 46, 43, 43, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 43, 43, 43, 43, 43, 43, 43, 46, 46, 46, 46, 46, 46, 46, 46, 49, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 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, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43 }, { 15, -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, -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, -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, -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, -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, -23, -23 }, { 15, -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, 50, -24, -24, -24, -24, -24, -24, -24, -24, -24, -24, -24, -24, -24, -24, 51, -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, -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, -24, -24, -24, -24, -24, -24, -24, -24, -24, -24 }, { 15, 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, 53, 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 }, { 15, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, 54, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, 55, -26, 55, 56, -26, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26 }, { 15, -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, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, 58, -27, 59, 60, 60, 60, 60, 60, 60, 60, 60, 60, -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, -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, -27, -27, -27, -27, -27, -27, -27, -27 }, { 15, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28 }, { 15, -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, -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, -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, -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 }, { 15, -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, -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, 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, 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 }, { 15, -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, -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, 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, 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 }, { 15, -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, -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, -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, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32 }, { 15, -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, -33, -33, -33, -33, -33, -33, -33, -33, -33 }, { 15, -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 }, { 15, -35, -35, -35, -35, -35, -35, -35, -35, -35, 67, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, 68, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, 69, -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, -35, -35 }, { 15, -36, -36, -36, -36, -36, -36, -36, -36, -36, 70, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, 71, -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, -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 }, { 15, -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, -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 }, { 15, -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, 72, -38, -38, -38, -38, -38, -38, -38, 72, 72, 72, 72, -38, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, -38, -38, -38, -38, -38, -38, -38, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, -38, -38, -38, 72, -38, -38, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, -38, -38, -38, -38, -38 }, { 15, 73, 73, 73, 73, 73, 73, 73, 73, 73, -39, 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, 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 }, { 15, 74, 74, 74, 74, 74, 74, 74, 74, 74, -40, 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, 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 }, { 15, 75, 75, 75, 75, 75, 75, 75, 75, 75, 76, 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, 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 }, { 15, -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, -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, -42, -42, -42, -42, -42 }, { 15, 77, 77, 77, 77, 77, 77, 77, 77, 77, -43, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77 }, { 15, 77, 77, 77, 77, 77, 77, 77, 77, 77, -44, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 78, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77 }, { 15, 77, 77, 77, 77, 77, 77, 77, 77, 77, -45, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 79, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77 }, { 15, 77, 77, 77, 77, 77, 77, 77, 77, 77, -46, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 80, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 81, 77, 77, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 77, 77, 77, 77, 77, 77, 77, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 77, 77, 77, 77, 81, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77 }, { 15, 77, 77, 77, 77, 77, 77, 77, 77, 77, -47, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 80, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 81, 77, 77, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 77, 77, 77, 77, 77, 77, 77, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 82, 83, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 77, 77, 77, 77, 81, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77 }, { 15, 77, 77, 77, 77, 77, 77, 77, 77, 77, -48, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 80, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 81, 77, 77, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 77, 77, 77, 77, 77, 77, 77, 81, 81, 81, 84, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 77, 77, 77, 77, 81, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77 }, { 15, 77, 77, 77, 77, 77, 77, 77, 77, 77, -49, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 80, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 81, 77, 77, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 77, 77, 77, 77, 77, 77, 77, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 85, 81, 81, 81, 81, 81, 81, 81, 77, 77, 77, 77, 81, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77 }, { 15, -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, -50, -50, -50, -50, -50, 51, -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, -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 }, { 15, -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, -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 }, { 15, 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, 53, 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 }, { 15, -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, 52, -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 }, { 15, -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, 55, -54, 55, 56, -54, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, -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, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54 }, { 15, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -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, -55, 57, 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 }, { 15, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56 }, { 15, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -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, 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, 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 }, { 15, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58 }, { 15, -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, 62, -59, 63, 64, 64, 64, 64, 64, 64, 64, 64, 64, -59, -59, -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, 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 }, { 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, -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, 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, 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 }, { 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, -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, 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, 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 }, { 15, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -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, 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, 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 }, { 15, -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, 62, -63, 93, 94, 94, 94, 94, 94, 94, 94, 94, 94, -63, -63, -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, 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 }, { 15, -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, 62, -64, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, -64, -64, -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, 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 }, { 15, -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, 96, -65, 96, -65, -65, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, -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 }, { 15, -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, 62, -66, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, -66, -66, -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, 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 }, { 15, -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, -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 }, { 15, -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, 69, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68 }, { 15, -69, -69, -69, -69, -69, -69, -69, -69, -69, 70, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, 71, -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, -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 }, { 15, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70 }, { 15, -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, -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, -71, -71 }, { 15, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, 72, -72, -72, -72, -72, -72, -72, -72, 72, 72, 72, 72, -72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, -72, -72, -72, -72, -72, -72, -72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, -72, -72, 99, 72, -72, -72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, -72, -72, -72, -72, -72 }, { 15, 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, 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, 73, 73, 73, 73, 73, 73, 73, 73, 73 }, { 15, 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, 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 }, { 15, 75, 75, 75, 75, 75, 75, 75, 75, 75, 76, 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, 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 }, { 15, -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, -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 }, { 15, 100, 100, 100, 100, 100, 100, 100, 100, 100, -77, 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, 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 }, { 15, 100, 100, 100, 100, 100, 100, 100, 100, 100, -78, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 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, 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 }, { 15, 100, 100, 100, 100, 100, 100, 100, 100, 100, -79, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 102, 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, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100 }, { 15, 100, 100, 100, 100, 100, 100, 100, 100, 100, -80, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 103, 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, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100 }, { 15, 100, 100, 100, 100, 100, 100, 100, 100, 100, -81, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 104, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 105, 100, 100, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 100, 100, 100, 100, 100, 100, 100, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 100, 100, 100, 100, 105, 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 }, { 15, 100, 100, 100, 100, 100, 100, 100, 100, 100, -82, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 104, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 105, 100, 100, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 100, 100, 100, 100, 100, 100, 100, 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, 100, 100, 100, 100, 105, 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 }, { 15, 100, 100, 100, 100, 100, 100, 100, 100, 100, -83, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 104, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 105, 100, 100, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 100, 100, 100, 100, 100, 100, 100, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 107, 105, 105, 105, 105, 105, 105, 100, 100, 100, 100, 105, 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 }, { 15, 100, 100, 100, 100, 100, 100, 100, 100, 100, -84, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 108, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 105, 100, 100, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 100, 100, 100, 100, 100, 100, 100, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 100, 100, 100, 100, 105, 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 }, { 15, 100, 100, 100, 100, 100, 100, 100, 100, 100, -85, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 104, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 105, 100, 100, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 100, 100, 100, 100, 100, 100, 100, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 109, 105, 105, 105, 105, 105, 105, 100, 100, 100, 100, 105, 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 }, { 15, -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, 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, 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, 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 }, { 15, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -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, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87 }, { 15, -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, 112, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, 113, -88, 113, 114, -88, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, -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, -88, -88, -88, -88 }, { 15, -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, 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, 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, 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 }, { 15, -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, 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, 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, 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 }, { 15, -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, 117, -91, 117, -91, -91, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, -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 }, { 15, -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, 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, 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 }, { 15, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -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, 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, 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 }, { 15, -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, 62, -94, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, -94, -94, -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, 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 }, { 15, -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, 62, -95, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, -95, -95, -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, 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 }, { 15, -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, 97, 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, -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 }, { 15, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97 }, { 15, -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, 62, -98, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, -98, -98, -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, 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 }, { 15, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99 }, { 15, 124, 124, 124, 124, 124, 124, 124, 124, 124, -100, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124 }, { 15, 124, 124, 124, 124, 124, 124, 124, 124, 124, -101, 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, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124 }, { 15, 124, 124, 124, 124, 124, 124, 124, 124, 124, -102, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 126, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124 }, { 15, 124, 124, 124, 124, 124, 124, 124, 124, 124, -103, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 127, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124 }, { 15, 124, 124, 124, 124, 124, 124, 124, 124, 124, -104, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 128, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124 }, { 15, 124, 124, 124, 124, 124, 124, 124, 124, 124, -105, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 129, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 130, 124, 124, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 124, 124, 124, 124, 124, 124, 124, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 124, 124, 124, 124, 130, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124 }, { 15, 124, 124, 124, 124, 124, 124, 124, 124, 124, -106, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 129, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 130, 124, 124, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 124, 124, 124, 124, 124, 124, 124, 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, 124, 124, 124, 124, 130, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124 }, { 15, 124, 124, 124, 124, 124, 124, 124, 124, 124, -107, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 129, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 130, 124, 124, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 124, 124, 124, 124, 124, 124, 124, 130, 130, 130, 130, 130, 130, 130, 130, 132, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 124, 124, 124, 124, 130, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124 }, { 15, 124, 124, 124, 124, 124, 124, 124, 124, 124, -108, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 133, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124 }, { 15, 124, 124, 124, 124, 124, 124, 124, 124, 124, -109, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 129, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 130, 124, 124, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 124, 124, 124, 124, 124, 124, 124, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 134, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 124, 124, 124, 124, 130, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124 }, { 15, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -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, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110 }, { 15, -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, 135, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, 136, -111, 136, 114, -111, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, -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 }, { 15, -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, 113, -112, 113, 114, -112, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, -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, -112, -112, -112, -112, -112, -112, -112, -112 }, { 15, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -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, -113, 115, 115, 115, 115, 115, 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, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113 }, { 15, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114 }, { 15, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, 139, -115, -115, -115, -115, -115, -115, -115, -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, 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, 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 }, { 15, -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, 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, 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, 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 }, { 15, -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, 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, -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 }, { 15, -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, 110, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, 111, -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 }, { 15, -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, 62, -119, 144, 145, 145, 145, 145, 145, 145, 145, 145, 145, -119, -119, -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, 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 }, { 15, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -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, 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, 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 }, { 15, -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, 62, -121, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, -121, -121, -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, 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 }, { 15, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -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, 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, 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 }, { 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, 62, -123, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, -123, -123, -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, 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 }, { 15, 150, 150, 150, 150, 150, 150, 150, 150, 150, -124, 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, 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, 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 }, { 15, 150, 150, 150, 150, 150, 150, 150, 150, 150, -125, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 151, 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, 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, 150, 150, 150, 150, 150 }, { 15, 150, 150, 150, 150, 150, 150, 150, 150, 150, -126, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 152, 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, 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, 150, 150, 150, 150, 150 }, { 15, 150, 150, 150, 150, 150, 150, 150, 150, 150, -127, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 153, 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, 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, 150, 150, 150, 150, 150 }, { 15, 150, 150, 150, 150, 150, 150, 150, 150, 150, -128, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 154, 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, 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, 150, 150, 150, 150, 150 }, { 15, 150, 150, 150, 150, 150, 150, 150, 150, 150, -129, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 155, 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, 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, 150, 150, 150, 150, 150 }, { 15, 150, 150, 150, 150, 150, 150, 150, 150, 150, -130, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 156, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 157, 150, 150, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 150, 150, 150, 150, 150, 150, 150, 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, 150, 150, 150, 150, 157, 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 }, { 15, 150, 150, 150, 150, 150, 150, 150, 150, 150, -131, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 156, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 157, 150, 150, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 150, 150, 150, 150, 150, 150, 150, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 158, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 150, 150, 150, 150, 157, 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 }, { 15, 150, 150, 150, 150, 150, 150, 150, 150, 150, -132, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 156, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 157, 150, 150, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 150, 150, 150, 150, 150, 150, 150, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 159, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 150, 150, 150, 150, 157, 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 }, { 15, 150, 150, 150, 150, 150, 150, 150, 150, 150, -133, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 160, 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, 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, 150, 150, 150, 150, 150 }, { 15, 150, 150, 150, 150, 150, 150, 150, 150, 150, -134, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 156, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 157, 150, 150, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 150, 150, 150, 150, 150, 150, 150, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 161, 157, 157, 157, 157, 157, 157, 157, 157, 150, 150, 150, 150, 157, 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 }, { 15, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -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, 114, -135, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135 }, { 15, -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, 114, -136, 137, 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, -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 }, { 15, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -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, 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, 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 }, { 15, -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, 162, -138, -138, -138, -138, -138, -138, -138, -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, 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, 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 }, { 15, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -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, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139 }, { 15, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140 }, { 15, -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, 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, 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, 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 }, { 15, -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, 139, -142, -142, -142, -142, -142, -142, -142, -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, 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, 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 }, { 15, -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, 166, -143, 166, -143, -143, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, -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 }, { 15, -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, 62, -144, 168, 169, 169, 169, 169, 169, 169, 169, 169, 169, -144, -144, -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, 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 }, { 15, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -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, 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, 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 }, { 15, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -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, 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, 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 }, { 15, -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, 62, -147, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, -147, -147, -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, 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 }, { 15, -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, 62, -148, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, -148, -148, -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, 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 }, { 15, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -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, 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, 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 }, { 15, 175, 175, 175, 175, 175, 175, 175, 175, 175, -150, 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, 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 }, { 15, 175, 175, 175, 175, 175, 175, 175, 175, 175, -151, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 176, 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, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175 }, { 15, 175, 175, 175, 175, 175, 175, 175, 175, 175, -152, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 177, 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, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175 }, { 15, 175, 175, 175, 175, 175, 175, 175, 175, 175, -153, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 178, 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, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175 }, { 15, 175, 175, 175, 175, 175, 175, 175, 175, 175, -154, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 179, 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, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175 }, { 15, 175, 175, 175, 175, 175, 175, 175, 175, 175, -155, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 180, 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, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175 }, { 15, 175, 175, 175, 175, 175, 175, 175, 175, 175, -156, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 181, 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, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175 }, { 15, 175, 175, 175, 175, 175, 175, 175, 175, 175, -157, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 182, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 183, 175, 175, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 175, 175, 175, 175, 175, 175, 175, 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, 175, 175, 175, 175, 183, 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 }, { 15, 175, 175, 175, 175, 175, 175, 175, 175, 175, -158, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 182, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 183, 175, 175, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 175, 175, 175, 175, 175, 175, 175, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 184, 183, 183, 183, 183, 183, 183, 175, 175, 175, 175, 183, 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 }, { 15, 175, 175, 175, 175, 175, 175, 175, 175, 175, -159, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 182, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 183, 175, 175, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 175, 175, 175, 175, 175, 175, 175, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 185, 183, 183, 183, 183, 183, 175, 175, 175, 175, 183, 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 }, { 15, 175, 175, 175, 175, 175, 175, 175, 175, 175, -160, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 186, 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, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175 }, { 15, 175, 175, 175, 175, 175, 175, 175, 175, 175, -161, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 182, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 183, 175, 175, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 175, 175, 175, 175, 175, 175, 175, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 184, 183, 175, 175, 175, 175, 183, 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 }, { 15, -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, 163, -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, -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 }, { 15, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163 }, { 15, -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, 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, 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, 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 }, { 15, -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, 162, -165, -165, -165, -165, -165, -165, -165, -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, 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, 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 }, { 15, -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, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, -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 }, { 15, -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, 162, -167, -167, -167, -167, -167, -167, -167, -167, 163, -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, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167 }, { 15, -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, 62, -168, 187, 188, 188, 188, 188, 188, 188, 188, 188, 188, -168, -168, -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, 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 }, { 15, -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, 62, -169, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, -169, -169, -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, 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 }, { 15, -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, 62, -170, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, -170, -170, -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, 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 }, { 15, -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, 62, -171, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, -171, -171, -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, 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 }, { 15, -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, 62, -172, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, -172, -172, -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, 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 }, { 15, -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, 62, -173, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, -173, -173, -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, 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 }, { 15, -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, 62, -174, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, -174, -174, -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, 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 }, { 15, 195, 195, 195, 195, 195, 195, 195, 195, 195, -175, 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, 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 }, { 15, 195, 195, 195, 195, 195, 195, 195, 195, 195, -176, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 196, 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, 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 }, { 15, 195, 195, 195, 195, 195, 195, 195, 195, 195, -177, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 197, 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, 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 }, { 15, 195, 195, 195, 195, 195, 195, 195, 195, 195, -178, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 197, 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, 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 }, { 15, 195, 195, 195, 195, 195, 195, 195, 195, 195, -179, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 197, 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, 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 }, { 15, 195, 195, 195, 195, 195, 195, 195, 195, 195, -180, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 197, 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, 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 }, { 15, 195, 195, 195, 195, 195, 195, 195, 195, 195, -181, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 197, 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, 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 }, { 15, 195, 195, 195, 195, 195, 195, 195, 195, 195, -182, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 197, 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, 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 }, { 15, 195, 195, 195, 195, 195, 195, 195, 195, 195, -183, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 198, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 197, 195, 195, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 195, 195, 195, 195, 195, 195, 195, 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, 195, 195, 195, 195, 197, 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 }, { 15, 195, 195, 195, 195, 195, 195, 195, 195, 195, -184, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 198, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 197, 195, 195, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 195, 195, 195, 195, 195, 195, 195, 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, 195, 195, 195, 195, 197, 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 }, { 15, 195, 195, 195, 195, 195, 195, 195, 195, 195, -185, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 198, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 197, 195, 195, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 195, 195, 195, 195, 195, 195, 195, 197, 197, 197, 197, 199, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 195, 195, 195, 195, 197, 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 }, { 15, 195, 195, 195, 195, 195, 195, 195, 195, 195, -186, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 200, 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, 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 }, { 15, -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, 62, -187, 201, 202, 202, 202, 202, 202, 202, 202, 202, 202, -187, -187, -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, 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 }, { 15, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -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, 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, 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 }, { 15, -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, 62, -189, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, -189, -189, -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, 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 }, { 15, -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, 62, -190, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, -190, -190, -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, 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 }, { 15, -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, -191, 62, -191, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, -191, -191, -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, 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 }, { 15, -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, 62, -192, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, -192, -192, -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, 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 }, { 15, -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, 62, -193, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, -193, -193, -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, 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 }, { 15, -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, 62, -194, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, -194, -194, -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, 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 }, { 15, -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, 210, -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 }, { 15, -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, 211, -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, 210, -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, -196, -196, -196, -196, -196, -196 }, { 15, -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, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, 212, -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, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197 }, { 15, -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, 212, -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 }, { 15, -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, 213, -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, 212, -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 }, { 15, -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, 214, -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, 215, -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 }, { 15, -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, 62, -201, 216, 217, 217, 217, 217, 217, 217, 217, 217, 217, -201, -201, -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, 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 }, { 15, -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, 62, -202, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, -202, -202, -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, 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 }, { 15, -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, 62, -203, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, -203, -203, -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, 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 }, { 15, -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, 62, -204, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, -204, -204, -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, 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 }, { 15, -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, 62, -205, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, -205, -205, -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, 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 }, { 15, -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, 62, -206, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, -206, -206, -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, 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 }, { 15, -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, 62, -207, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, -207, -207, -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, 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 }, { 15, -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, 62, -208, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, -208, -208, -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, 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 }, { 15, -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, 62, -209, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, -209, -209, -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, 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 }, { 15, -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, 226, -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, -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 }, { 15, -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, 227, -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, -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 }, { 15, -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, 228, -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, -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 }, { 15, -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, 229, -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, -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 }, { 15, -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, 230, -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, -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 }, { 15, -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, 231, -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, -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 }, { 15, -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, 62, -216, 232, 233, 233, 233, 233, 233, 233, 233, 233, 233, -216, -216, -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, 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 }, { 15, -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, 62, -217, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, -217, -217, -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, 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 }, { 15, -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, 62, -218, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, -218, -218, -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, 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 }, { 15, -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, 62, -219, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, -219, -219, -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, 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 }, { 15, -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, 62, -220, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, -220, -220, -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, 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 }, { 15, -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, 62, -221, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, -221, -221, -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, 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 }, { 15, -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, 62, -222, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, -222, -222, -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, 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 }, { 15, -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, 62, -223, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, -223, -223, -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, 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 }, { 15, -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, 62, -224, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, -224, -224, -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, 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 }, { 15, -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, 62, -225, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, -225, -225, -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, 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 }, { 15, -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, -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, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226 }, { 15, -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, 243, -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, -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, -227 }, { 15, -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, -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, -228, -228, -228, -228, -228, -228, -228, -228, -228 }, { 15, -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, 213, -229, -229, -229, -229, -229, -229, 244, -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 }, { 15, -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, 245, -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, -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 }, { 15, -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, -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, -231, -231, -231, -231, -231, -231, -231, -231, -231 }, { 15, -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, 62, -232, 246, 247, 247, 247, 247, 247, 247, 247, 247, 247, -232, -232, -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, 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 }, { 15, -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, 62, -233, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, -233, -233, -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, 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 }, { 15, -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, 62, -234, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, -234, -234, -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, 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 }, { 15, -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, -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, 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, 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 }, { 15, -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, 62, -236, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, -236, -236, -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, 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 }, { 15, -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, 62, -237, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, -237, -237, -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, 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 }, { 15, -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, 62, -238, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, -238, -238, -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, 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 }, { 15, -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, 62, -239, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, -239, -239, -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, 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 }, { 15, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -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, 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, 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 }, { 15, -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, 62, -241, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, -241, -241, -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, 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 }, { 15, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -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, 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, 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 }, { 15, -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, 258, -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, -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 }, { 15, 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, 260, 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, 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 }, { 15, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, 261, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245 }, { 15, -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, 62, -246, 262, 263, 263, 263, 263, 263, 263, 263, 263, 263, -246, -246, -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, 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 }, { 15, -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, 62, -247, 264, 264, 264, 264, 264, 264, 264, 264, 264, 264, -247, -247, -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, 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 }, { 15, -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, 62, -248, 265, 265, 265, 265, 265, 265, 265, 265, 265, 265, -248, -248, -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, 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 }, { 15, -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, 62, -249, 266, 266, 266, 266, 266, 266, 266, 266, 266, 266, -249, -249, -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, 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 }, { 15, -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, 62, -250, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, -250, -250, -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, 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 }, { 15, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -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, 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, 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 }, { 15, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -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, 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, 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 }, { 15, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -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, 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, 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 }, { 15, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -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, 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, 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 }, { 15, -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, 62, -255, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, -255, -255, -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, 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 }, { 15, -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, 62, -256, 273, 273, 273, 273, 273, 273, 273, 273, 273, 273, -256, -256, -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, 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 }, { 15, -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, 62, -257, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, -257, -257, -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, 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 }, { 15, -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, 275, -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, -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 }, { 15, 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, 260, 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, 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 }, { 15, -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, 259, -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, -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 }, { 15, -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, 276, -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, -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 }, { 15, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -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, 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, 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 }, { 15, -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, 62, -263, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, -263, -263, -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, 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 }, { 15, -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, 62, -264, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, -264, -264, -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, 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 }, { 15, -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, 62, -265, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, -265, -265, -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, 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 }, { 15, -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, 62, -266, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, -266, -266, -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, 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 }, { 15, -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, 62, -267, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, -267, -267, -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, 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 }, { 15, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -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, 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, 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 }, { 15, -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, 62, -269, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, -269, -269, -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, 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 }, { 15, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -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, 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, 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 }, { 15, -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, 62, -271, 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, -271, -271, -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, 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 }, { 15, -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, 62, -272, 288, 288, 288, 288, 288, 288, 288, 288, 288, 288, -272, -272, -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, 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 }, { 15, -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, 62, -273, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, -273, -273, -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, 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 }, { 15, -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, 62, -274, 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, -274, -274, -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, 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 }, { 15, -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, 291, -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, -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 }, { 15, -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, 292, -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, -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 }, { 15, -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, 62, -277, 293, 294, 294, 294, 294, 294, 294, 294, 294, 294, -277, -277, -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, 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 }, { 15, -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, 62, -278, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, -278, -278, -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, 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 }, { 15, -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, 62, -279, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, -279, -279, -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, 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 }, { 15, -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, 62, -280, 297, 297, 297, 297, 297, 297, 297, 297, 297, 297, -280, -280, -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, 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 }, { 15, -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, 62, -281, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, -281, -281, -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, 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 }, { 15, -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, 62, -282, 299, 299, 299, 299, 299, 299, 299, 299, 299, 299, -282, -282, -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, 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 }, { 15, -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, -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, 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, 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 }, { 15, -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, 62, -284, 301, 301, 301, 301, 301, 301, 301, 301, 301, 301, -284, -284, -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, 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 }, { 15, -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, 62, -285, 302, 302, 302, 302, 302, 302, 302, 302, 302, 302, -285, -285, -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, 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 }, { 15, -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, 62, -286, 303, 303, 303, 303, 303, 303, 303, 303, 303, 303, -286, -286, -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, 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 }, { 15, -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, 62, -287, 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, -287, -287, -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, 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 }, { 15, -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, 62, -288, 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, -288, -288, -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, 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 }, { 15, -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, 62, -289, 306, 306, 306, 306, 306, 306, 306, 306, 306, 306, -289, -289, -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, 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 }, { 15, -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, 62, -290, 307, 307, 307, 307, 307, 307, 307, 307, 307, 307, -290, -290, -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, 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 }, { 15, -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, 308, -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, -291 }, { 15, -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, 309, -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, -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 }, { 15, -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, 62, -293, 310, 311, 311, 311, 311, 311, 311, 311, 311, 311, -293, -293, -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, 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 }, { 15, -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, 62, -294, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, -294, -294, -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, 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 }, { 15, -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, 62, -295, 313, 313, 313, 313, 313, 313, 313, 313, 313, 313, -295, -295, -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, 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 }, { 15, -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, 62, -296, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, -296, -296, -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, 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 }, { 15, -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, 62, -297, 315, 315, 315, 315, 315, 315, 315, 315, 315, 315, -297, -297, -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, 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 }, { 15, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -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, 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, 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 }, { 15, -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, 62, -299, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, -299, -299, -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, 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 }, { 15, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -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, 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, 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 }, { 15, -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, 62, -301, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, -301, -301, -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, 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 }, { 15, -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, 62, -302, 320, 320, 320, 320, 320, 320, 320, 320, 320, 320, -302, -302, -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, 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 }, { 15, -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, 62, -303, 321, 321, 321, 321, 321, 321, 321, 321, 321, 321, -303, -303, -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, 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 }, { 15, -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, 62, -304, 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, -304, -304, -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, 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 }, { 15, -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, 62, -305, 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, -305, -305, -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, 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 }, { 15, -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, 62, -306, 324, 324, 324, 324, 324, 324, 324, 324, 324, 324, -306, -306, -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, 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 }, { 15, -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, 62, -307, 325, 325, 325, 325, 325, 325, 325, 325, 325, 325, -307, -307, -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, 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 }, { 15, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, 326, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308 }, { 15, -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, 327, -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, -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, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309 }, { 15, -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, 62, -310, 328, 329, 329, 329, 329, 329, 329, 329, 329, 329, -310, -310, -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, 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 }, { 15, -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, 62, -311, 330, 330, 330, 330, 330, 330, 330, 330, 330, 330, -311, -311, -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, 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 }, { 15, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -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, 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, 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 }, { 15, -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, 62, -313, 332, 332, 332, 332, 332, 332, 332, 332, 332, 332, -313, -313, -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, 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 }, { 15, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -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, 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, 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 }, { 15, -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, 62, -315, 334, 334, 334, 334, 334, 334, 334, 334, 334, 334, -315, -315, -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, 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 }, { 15, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -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, 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, 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 }, { 15, -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, 62, -317, 336, 336, 336, 336, 336, 336, 336, 336, 336, 336, -317, -317, -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, 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 }, { 15, -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, 62, -318, 337, 337, 337, 337, 337, 337, 337, 337, 337, 337, -318, -318, -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, 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 }, { 15, -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, 62, -319, 338, 338, 338, 338, 338, 338, 338, 338, 338, 338, -319, -319, -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, 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 }, { 15, -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, 62, -320, 339, 339, 339, 339, 339, 339, 339, 339, 339, 339, -320, -320, -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, 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 }, { 15, -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, 62, -321, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, -321, -321, -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, 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 }, { 15, -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, 62, -322, 341, 341, 341, 341, 341, 341, 341, 341, 341, 341, -322, -322, -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, 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 }, { 15, -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, 62, -323, 342, 342, 342, 342, 342, 342, 342, 342, 342, 342, -323, -323, -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, 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 }, { 15, -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, 62, -324, 343, 343, 343, 343, 343, 343, 343, 343, 343, 343, -324, -324, -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, 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 }, { 15, -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, -325, 62, -325, 344, 344, 344, 344, 344, 344, 344, 344, 344, 344, -325, -325, -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, 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 }, { 15, -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, 345, -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, -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 }, { 15, -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, 346, -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, -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, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327 }, { 15, -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, 62, -328, 347, 348, 348, 348, 348, 348, 348, 348, 348, 348, -328, -328, -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, 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 }, { 15, -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, -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, 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, 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 }, { 15, -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, 62, -330, 350, 350, 350, 350, 350, 350, 350, 350, 350, 350, -330, -330, -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, 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 }, { 15, -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, 62, -331, 351, 351, 351, 351, 351, 351, 351, 351, 351, 351, -331, -331, -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, 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 }, { 15, -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, 62, -332, 352, 352, 352, 352, 352, 352, 352, 352, 352, 352, -332, -332, -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, 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 }, { 15, -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, 62, -333, 353, 353, 353, 353, 353, 353, 353, 353, 353, 353, -333, -333, -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, 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 }, { 15, -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, 62, -334, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, -334, -334, -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, 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 }, { 15, -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, 62, -335, 355, 355, 355, 355, 355, 355, 355, 355, 355, 355, -335, -335, -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, 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 }, { 15, -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, 62, -336, 356, 356, 356, 356, 356, 356, 356, 356, 356, 356, -336, -336, -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, 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 }, { 15, -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, 62, -337, 357, 357, 357, 357, 357, 357, 357, 357, 357, 357, -337, -337, -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, 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 }, { 15, -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, 62, -338, 358, 358, 358, 358, 358, 358, 358, 358, 358, 358, -338, -338, -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, 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 }, { 15, -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, 62, -339, 359, 359, 359, 359, 359, 359, 359, 359, 359, 359, -339, -339, -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, 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 }, { 15, -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, 62, -340, 360, 360, 360, 360, 360, 360, 360, 360, 360, 360, -340, -340, -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, 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 }, { 15, -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, -341, 62, -341, 361, 361, 361, 361, 361, 361, 361, 361, 361, 361, -341, -341, -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, 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 }, { 15, -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, 62, -342, 362, 362, 362, 362, 362, 362, 362, 362, 362, 362, -342, -342, -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, 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 }, { 15, -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, 62, -343, 363, 363, 363, 363, 363, 363, 363, 363, 363, 363, -343, -343, -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, 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 }, { 15, -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, 62, -344, 364, 364, 364, 364, 364, 364, 364, 364, 364, 364, -344, -344, -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, 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 }, { 15, -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, 365, -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, -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, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345 }, { 15, -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, 366, -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, -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 }, { 15, -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, 62, -347, 367, 368, 368, 368, 368, 368, 368, 368, 368, 368, -347, -347, -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, 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 }, { 15, -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, 62, -348, 369, 369, 369, 369, 369, 369, 369, 369, 369, 369, -348, -348, -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, 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 }, { 15, -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, 62, -349, 370, 370, 370, 370, 370, 370, 370, 370, 370, 370, -349, -349, -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, 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 }, { 15, -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, 62, -350, 371, 371, 371, 371, 371, 371, 371, 371, 371, 371, -350, -350, -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, 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 }, { 15, -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, 62, -351, 372, 372, 372, 372, 372, 372, 372, 372, 372, 372, -351, -351, -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, 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 }, { 15, -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, 62, -352, 373, 373, 373, 373, 373, 373, 373, 373, 373, 373, -352, -352, -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, 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 }, { 15, -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, 62, -353, 374, 374, 374, 374, 374, 374, 374, 374, 374, 374, -353, -353, -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, 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 }, { 15, -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, 62, -354, 375, 375, 375, 375, 375, 375, 375, 375, 375, 375, -354, -354, -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, 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 }, { 15, -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, 62, -355, 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, -355, -355, -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, 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 }, { 15, -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, 62, -356, 377, 377, 377, 377, 377, 377, 377, 377, 377, 377, -356, -356, -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, 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 }, { 15, -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, 62, -357, 378, 378, 378, 378, 378, 378, 378, 378, 378, 378, -357, -357, -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, 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 }, { 15, -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, 62, -358, 379, 379, 379, 379, 379, 379, 379, 379, 379, 379, -358, -358, -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, 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 }, { 15, -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, 62, -359, 380, 380, 380, 380, 380, 380, 380, 380, 380, 380, -359, -359, -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, 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 }, { 15, -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, 62, -360, 381, 381, 381, 381, 381, 381, 381, 381, 381, 381, -360, -360, -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, 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 }, { 15, -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, 62, -361, 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, -361, -361, -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, 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 }, { 15, -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, 62, -362, 383, 383, 383, 383, 383, 383, 383, 383, 383, 383, -362, -362, -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, 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 }, { 15, -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, 62, -363, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, -363, -363, -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, 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 }, { 15, -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, 62, -364, 385, 385, 385, 385, 385, 385, 385, 385, 385, 385, -364, -364, -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, 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 }, { 15, -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, 386, -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, -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 }, { 15, -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, 387, -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, -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 }, { 15, -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, 62, -367, 388, 389, 389, 389, 389, 389, 389, 389, 389, 389, -367, -367, -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, 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 }, { 15, -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, 62, -368, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, -368, -368, -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, 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 }, { 15, -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, 62, -369, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, -369, -369, -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, 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 }, { 15, -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, 62, -370, 392, 392, 392, 392, 392, 392, 392, 392, 392, 392, -370, -370, -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, 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 }, { 15, -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, 62, -371, 393, 393, 393, 393, 393, 393, 393, 393, 393, 393, -371, -371, -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, 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 }, { 15, -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, 62, -372, 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, -372, -372, -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, 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 }, { 15, -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, 62, -373, 395, 395, 395, 395, 395, 395, 395, 395, 395, 395, -373, -373, -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, 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 }, { 15, -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, 62, -374, 396, 396, 396, 396, 396, 396, 396, 396, 396, 396, -374, -374, -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, 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 }, { 15, -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, 62, -375, 397, 397, 397, 397, 397, 397, 397, 397, 397, 397, -375, -375, -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, 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 }, { 15, -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, 62, -376, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, -376, -376, -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, 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 }, { 15, -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, 62, -377, 399, 399, 399, 399, 399, 399, 399, 399, 399, 399, -377, -377, -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, 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 }, { 15, -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, 62, -378, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, -378, -378, -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, 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 }, { 15, -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, 62, -379, 401, 401, 401, 401, 401, 401, 401, 401, 401, 401, -379, -379, -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, 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 }, { 15, -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, 62, -380, 402, 402, 402, 402, 402, 402, 402, 402, 402, 402, -380, -380, -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, 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 }, { 15, -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, 62, -381, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, -381, -381, -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, 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 }, { 15, -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, -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, 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, 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 }, { 15, -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, 62, -383, 405, 405, 405, 405, 405, 405, 405, 405, 405, 405, -383, -383, -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, 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 }, { 15, -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, 62, -384, 406, 406, 406, 406, 406, 406, 406, 406, 406, 406, -384, -384, -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, 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 }, { 15, -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, 62, -385, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, -385, -385, -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, 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 }, { 15, -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, 408, -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, -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, -386 }, { 15, -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, 409, -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, -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 }, { 15, -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, -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, 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, 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 }, { 15, -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, -389, -389, 62, -389, 412, 412, 412, 412, 412, 412, 412, 412, 412, 412, -389, -389, -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, 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 }, { 15, -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, 62, -390, 413, 413, 413, 413, 413, 413, 413, 413, 413, 413, -390, -390, -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, 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 }, { 15, -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, 62, -391, 414, 414, 414, 414, 414, 414, 414, 414, 414, 414, -391, -391, -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, 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 }, { 15, -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, 62, -392, 415, 415, 415, 415, 415, 415, 415, 415, 415, 415, -392, -392, -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, 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 }, { 15, -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, 62, -393, 416, 416, 416, 416, 416, 416, 416, 416, 416, 416, -393, -393, -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, 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 }, { 15, -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, 62, -394, 417, 417, 417, 417, 417, 417, 417, 417, 417, 417, -394, -394, -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, 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 }, { 15, -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, 62, -395, 418, 418, 418, 418, 418, 418, 418, 418, 418, 418, -395, -395, -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, 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 }, { 15, -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, 62, -396, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, -396, -396, -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, 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 }, { 15, -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, 62, -397, 420, 420, 420, 420, 420, 420, 420, 420, 420, 420, -397, -397, -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, 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 }, { 15, -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, -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, 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, 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 }, { 15, -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, 62, -399, 422, 422, 422, 422, 422, 422, 422, 422, 422, 422, -399, -399, -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, 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 }, { 15, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -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, 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, 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 }, { 15, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -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, 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, 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 }, { 15, -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, -402, -402, 62, -402, 425, 425, 425, 425, 425, 425, 425, 425, 425, 425, -402, -402, -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, 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 }, { 15, -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, 62, -403, 426, 426, 426, 426, 426, 426, 426, 426, 426, 426, -403, -403, -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, 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 }, { 15, -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, 62, -404, 427, 427, 427, 427, 427, 427, 427, 427, 427, 427, -404, -404, -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, 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 }, { 15, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -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, 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, 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 }, { 15, -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, 62, -406, 429, 429, 429, 429, 429, 429, 429, 429, 429, 429, -406, -406, -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, 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 }, { 15, -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, 62, -407, 430, 430, 430, 430, 430, 430, 430, 430, 430, 430, -407, -407, -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, 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 }, { 15, -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, 431, -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, -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 }, { 15, -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, 432, -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 }, { 15, -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, 62, -410, 410, 411, 411, 411, 411, 411, 411, 411, 411, 411, -410, -410, -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, 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 }, { 15, -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, 62, -411, 412, 412, 412, 412, 412, 412, 412, 412, 412, 412, -411, -411, -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, 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 }, { 15, -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, 62, -412, 413, 413, 413, 413, 413, 413, 413, 413, 413, 413, -412, -412, -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, 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 }, { 15, -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, 62, -413, 414, 414, 414, 414, 414, 414, 414, 414, 414, 414, -413, -413, -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, 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 }, { 15, -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, 62, -414, 415, 415, 415, 415, 415, 415, 415, 415, 415, 415, -414, -414, -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, 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 }, { 15, -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, 62, -415, 416, 416, 416, 416, 416, 416, 416, 416, 416, 416, -415, -415, -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, 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 }, { 15, -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, -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, 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, 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 }, { 15, -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, 62, -417, 418, 418, 418, 418, 418, 418, 418, 418, 418, 418, -417, -417, -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, 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 }, { 15, -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, 62, -418, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, -418, -418, -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, 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 }, { 15, -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, 62, -419, 420, 420, 420, 420, 420, 420, 420, 420, 420, 420, -419, -419, -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, 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 }, { 15, -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, 62, -420, 421, 421, 421, 421, 421, 421, 421, 421, 421, 421, -420, -420, -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, 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 }, { 15, -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, 62, -421, 422, 422, 422, 422, 422, 422, 422, 422, 422, 422, -421, -421, -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, 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 }, { 15, -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, 62, -422, 423, 423, 423, 423, 423, 423, 423, 423, 423, 423, -422, -422, -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, 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 }, { 15, -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, 62, -423, 424, 424, 424, 424, 424, 424, 424, 424, 424, 424, -423, -423, -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, 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 }, { 15, -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, -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, 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, 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 }, { 15, -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, 62, -425, 426, 426, 426, 426, 426, 426, 426, 426, 426, 426, -425, -425, -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, 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 }, { 15, -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, 62, -426, 427, 427, 427, 427, 427, 427, 427, 427, 427, 427, -426, -426, -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, 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 }, { 15, -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, 62, -427, 428, 428, 428, 428, 428, 428, 428, 428, 428, 428, -427, -427, -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, 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 }, { 15, -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, 62, -428, 429, 429, 429, 429, 429, 429, 429, 429, 429, 429, -428, -428, -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, 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 }, { 15, -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, 62, -429, 430, 430, 430, 430, 430, 430, 430, 430, 430, 430, -429, -429, -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, 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 }, { 15, -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, 62, -430, 430, 430, 430, 430, 430, 430, 430, 430, 430, 430, -430, -430, -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, 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 }, { 15, -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, 433, -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, -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 }, { 15, -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, 434, -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, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432 }, { 15, -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, 435, -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, -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 }, { 15, -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, 436, -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, -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 }, { 15, -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, 437, -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, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435 }, { 15, -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, 438, -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 }, { 15, -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, 439, -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, -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 }, { 15, -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, 440, -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, -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 }, { 15, -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, 441, -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, -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 }, { 15, -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, 442, -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, -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, -440 }, { 15, -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, 443, -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, -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 }, { 15, -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, 444, -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, -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, -442 }, { 15, -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, 445, -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, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443 }, { 15, -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, 446, -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, -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 }, { 15, -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, 447, -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, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445 }, { 15, -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, 448, -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, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446 }, { 15, -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, 449, -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, -447, -447, -447, -447, -447, -447, -447, -447, -447, -447, -447, -447, -447, -447 }, { 15, -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, 450, -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, -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 }, { 15, -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, 451, -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, -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 }, { 15, -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, 452, -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, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450 }, { 15, -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, 453, -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, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451 }, { 15, -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, 454, -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, -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 }, { 15, -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, 455, -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, -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 }, { 15, -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, 456, -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, -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 }, { 15, -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, 457, -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, -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 }, { 15, -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, 458, -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, -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 }, { 15, -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, 459, -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, -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, -457 }, { 15, -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, 460, -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, -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 }, { 15, -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, 461, -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, -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, -459 }, { 15, -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, 462, -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, -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, -460 }, { 15, -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, 463, -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, -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 }, { 15, -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, 464, -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, -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 }, { 15, -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, 465, -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, -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 }, { 15, -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, 466, -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, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464 }, { 15, -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, 467, -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, -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 }, { 15, -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, 468, -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, -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 }, { 15, -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, 469, -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, -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 }, { 15, -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, 470, -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, -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 }, { 15, -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, 471, -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, -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 }, { 15, -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, 472, -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, -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 }, { 15, -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, 473, -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, -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 }, { 15, -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, 474, -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, -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 }, { 15, -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, 475, -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, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473 }, { 15, -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, 476, -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, -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, -474 }, { 15, -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, 477, -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, -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 }, { 15, -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, 478, -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, -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 }, { 15, -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, 479, -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, -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 }, { 15, -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, 480, -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, -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 }, { 15, -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, 481, -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, -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, -479 }, { 15, -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, 482, -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, -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, -480 }, { 15, -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, 483, -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, -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 }, { 15, -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, 484, -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, -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 }, { 15, -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, 485, -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, -483, -483, -483, -483, -483, -483, -483, -483, -483, -483, -483, -483, -483 }, { 15, -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, 486, -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, -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 }, { 15, -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, 487, -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, -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 }, { 15, -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, 488, -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, -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, -486 }, { 15, -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, 489, -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 }, { 15, -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, 490, -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, -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 }, { 15, -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, 491, -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 }, { 15, -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, 492, -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, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490 }, { 15, -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, 493, -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, -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 }, { 15, -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, 494, -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, -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, -492, -492, -492, -492, -492 }, { 15, -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, 495, -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 }, { 15, -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, 496, -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, -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 }, { 15, -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, 497, -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 }, { 15, -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, 498, -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, -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, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496 }, { 15, -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, 499, -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, -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, -497 }, { 15, -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, 500, -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, -498, -498, -498, -498, -498, -498, -498, -498, -498, -498, -498, -498, -498, -498, -498, -498, -498 }, { 15, -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, 501, -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 }, { 15, -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, 502, -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 }, { 15, -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, 503, -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, -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 }, { 15, -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, 504, -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, -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 }, { 15, -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, 505, -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, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503 }, { 15, -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, 506, -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, -504, -504, -504, -504, -504, -504, -504, -504, -504, -504, -504, -504, -504, -504 }, { 15, -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, 507, -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, -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 }, { 15, -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, 508, -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, -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 }, { 15, -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, 509, -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, -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 }, { 15, -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, 510, -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 }, { 15, -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, -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, -509 }, { 15, -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, 512, -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, -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, -510, -510, -510, -510, -510, -510, -510 }, { 15, -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, 513, -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, -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 }, { 15, -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, 514, -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, -512, -512, -512, -512, -512, -512, -512 }, { 15, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -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, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, -513 }, { 15, -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, 516, -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, -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, -514, -514, -514, -514, -514, -514, -514 }, { 15, -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, 517, -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, -515, -515, -515, -515, -515, -515, -515, -515, -515, -515, -515, -515, -515, -515, -515, -515, -515 }, { 15, -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, 518, -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 }, { 15, -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, 519, -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, -517, -517, -517, -517, -517, -517 }, { 15, -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, 520, -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, -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, -518, -518, -518, -518, -518, -518, -518, -518, -518, -518, -518, -518, -518, -518, -518, -518, -518 }, { 15, -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, 521, -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 }, { 15, -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, 522, -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 }, { 15, -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, 523, -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, -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, -521, -521, -521, -521, -521, -521, -521, -521, -521, -521, -521, -521, -521, -521, -521, -521, -521 }, { 15, -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, 524, -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, -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, -522, -522, -522, -522, -522, -522, -522, -522, -522, -522, -522, -522, -522, -522, -522, -522, -522 }, { 15, -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, 525, -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, -523, -523, -523, -523, -523, -523, -523, -523, -523, -523, -523, -523, -523, -523, -523, -523, -523, -523 }, { 15, -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, 526, -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, -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, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524 }, { 15, -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, 527, -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, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525 }, { 15, -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, 528, -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, -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, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526 }, { 15, -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, 529, -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, -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, -527, -527, -527, -527, -527, -527, -527, -527, -527, -527, -527, -527, -527, -527, -527, -527, -527 }, { 15, -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, 530, -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, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528 }, { 15, -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, 531, -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, -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 }, { 15, -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, 532, -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, -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 }, { 15, -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, 533, -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, -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 }, { 15, -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, 534, -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, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532 }, { 15, -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, 535, -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, -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 }, { 15, -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, 536, -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, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534 }, { 15, -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, 537, -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, -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 }, { 15, -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, 538, -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, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536 }, { 15, -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, 539, -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, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537 }, { 15, -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, 540, -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 }, { 15, -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, 541, -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, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539 }, { 15, -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, 542, -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, -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, -540, -540, -540, -540, -540, -540, -540 }, { 15, -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, 543, -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, -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 }, { 15, -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, 544, -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 }, { 15, -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, 545, -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, -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 }, { 15, -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, 546, -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, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544 }, { 15, -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, 547, -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, -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 }, { 15, -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, 548, -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 }, { 15, -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, 549, -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 }, { 15, -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, 550, -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 }, { 15, -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 }, { 15, -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, -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 }, } ; 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; \ yyg->yytext_ptr -= yyg->yy_more_len; \ yyleng = (int) (yy_cp - yyg->yytext_ptr); \ yyg->yy_hold_char = *yy_cp; \ *yy_cp = '\0'; \ 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, 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[551] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 28, 29, 29, 0, 0, 32, 31, 31, 31, 31, 31, 31, 31, 20, 20, 20, 20, 20, 20, 11, 13, 13, 12, 25, 21, 24, 23, 27, 27, 28, 29, 31, 30, 0, 0, 0, 0, 0, 0, 0, 0, 11, 0, 19, 0, 0, 0, 0, 0, 13, 13, 16, 16, 13, 13, 0, 13, 21, 0, 23, 22, 23, 0, 28, 29, 0, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 13, 13, 13, 0, 16, 13, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 13, 13, 13, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 13, 13, 13, 13, 13, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 0, 0, 0, 0, 13, 13, 13, 13, 13, 13, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 13, 13, 13, 13, 13, 13, 13, 13, 10, 2, 8, 8, 8, 5, 13, 13, 13, 13, 13, 13, 13, 13, 13, 0, 0, 0, 0, 0, 0, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 9, 0, 6, 0, 0, 4, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 0, 0, 0, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 0, 0, 7, 0, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 0, 0, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 0, 0, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 0, 0, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 0, 0, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 0, 0, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 0, 0, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 0, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 0, 0, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3 } ; 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, 43, 43, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 74, 75, 0, 77, 77, 77, 77, 77, 77, 77, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 74, 75, 0, 100, 100, 100, 100, 100, 100, 100, 100, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 259, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 259, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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() (yyg->yy_more_flag = 1) #define YY_MORE_ADJ yyg->yy_more_len #define YY_RESTORE_YY_MORE_OFFSET #line 1 "fitshdr.l" /*============================================================================ WCSLIB 8.4 - an implementation of the FITS WCS standard. Copyright (C) 1995-2024, 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/people/Mark.Calabretta $Id: fitshdr.c,v 8.4 2024/10/28 13:56:17 mcalabre Exp $ *============================================================================= * * fitshdr.l is a Flex description file containing a lexical scanner * definition for extracting keywords and keyvalues from a FITS header. * * It requires Flex v2.5.4 or later. * * Refer to fitshdr.h for a description of the user interface and operating * notes. * *===========================================================================*/ /* Options. */ #define YY_NO_INPUT 1 /* Keywords. */ /* Keyvalue data types. */ /* Characters forming standard unit strings (jwBIQX are not used). */ /* Exclusive start states. */ #line 76 "fitshdr.l" #include #include #include #include #include #include "fitshdr.h" #include "wcsutil.h" // 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 (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 void nullfill(char cptr[], int len); // 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 10327 "fitshdr.c" #line 10328 "fitshdr.c" #define INITIAL 0 #define VALUE 1 #define INLINE 2 #define UNITS 3 #define COMMENT 4 #define ERROR 5 #define FLUSH 6 #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 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); 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 #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 128 "fitshdr.l" #line 130 "fitshdr.l" char ctmp[72]; if (keys == 0x0) { return FITSHDRERR_NULL_POINTER; } // 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 FITSHDRERR_MEMORY; } // 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 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); #line 10637 "fitshdr.c" while ( /*CONSTCOND*/1 ) /* loops until end-of-file is reached */ { yyg->yy_more_len = 0; if ( yyg->yy_more_flag ) { yyg->yy_more_len = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr); yyg->yy_more_flag = 0; } 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 187 "fitshdr.l" { // A completely blank keyrecord. strncpy(kptr->keyword, yytext, 8); yyless(0); blank = 1; BEGIN(COMMENT); } YY_BREAK case 2: YY_RULE_SETUP #line 195 "fitshdr.l" { strncpy(kptr->keyword, yytext, 8); BEGIN(COMMENT); } YY_BREAK case 3: YY_RULE_SETUP #line 200 "fitshdr.l" { strncpy(kptr->keyword, yytext, 8); end = 1; BEGIN(FLUSH); } YY_BREAK case 4: YY_RULE_SETUP #line 206 "fitshdr.l" { // Illegal END keyrecord. strncpy(kptr->keyword, yytext, 8); kptr->status |= FITSHDR_KEYREC; BEGIN(VALUE); } YY_BREAK case 5: YY_RULE_SETUP #line 213 "fitshdr.l" { // Illegal END keyrecord. strncpy(kptr->keyword, yytext, 8); kptr->status |= FITSHDR_KEYREC; BEGIN(COMMENT); } YY_BREAK case 6: YY_RULE_SETUP #line 220 "fitshdr.l" { strncpy(kptr->keyword, yytext, 8); BEGIN(VALUE); } YY_BREAK case 7: /* rule 7 can match eol */ YY_RULE_SETUP #line 225 "fitshdr.l" { // Continued string keyvalue. strncpy(kptr->keyword, yytext, 8); if (keyno > 0 && (kptr-1)->type%10 == 8) { // Put back the string keyvalue. int k; for (k = 10; yytext[k] != '\''; k++); yyless(k); continuation = 1; BEGIN(VALUE); } else { // Not a valid continuation. yyless(8); BEGIN(COMMENT); } } YY_BREAK case 8: YY_RULE_SETUP #line 244 "fitshdr.l" { // Keyword without value. strncpy(kptr->keyword, yytext, 8); BEGIN(COMMENT); } YY_BREAK case 9: YY_RULE_SETUP #line 250 "fitshdr.l" { // Illegal keyword, carry on regardless. strncpy(kptr->keyword, yytext, 8); kptr->status |= FITSHDR_KEYWORD; BEGIN(VALUE); } YY_BREAK case 10: YY_RULE_SETUP #line 257 "fitshdr.l" { // Illegal keyword, carry on regardless. strncpy(kptr->keyword, yytext, 8); kptr->status |= FITSHDR_KEYWORD; BEGIN(COMMENT); } YY_BREAK case 11: *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 264 "fitshdr.l" { // Null keyvalue. BEGIN(INLINE); } YY_BREAK case 12: YY_RULE_SETUP #line 269 "fitshdr.l" { // Logical keyvalue. kptr->type = 1; kptr->keyvalue.i = (*yytext == 'T'); BEGIN(INLINE); } YY_BREAK case 13: YY_RULE_SETUP #line 276 "fitshdr.l" { // 32-bit signed integer keyvalue. kptr->type = 2; if (sscanf(yytext, "%d", &(kptr->keyvalue.i)) < 1) { kptr->status |= FITSHDR_KEYVALUE; BEGIN(ERROR); } BEGIN(INLINE); } YY_BREAK case 14: YY_RULE_SETUP #line 287 "fitshdr.l" { // 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. kptr->type = 2; if (sscanf(yytext, "%d", &(kptr->keyvalue.i)) < 1) { kptr->status |= FITSHDR_KEYVALUE; BEGIN(ERROR); } } else { // 64-bit signed integer. kptr->type = 3; #ifdef WCSLIB_INT64 // 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]. kptr->keyvalue.k[2] = 0; 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 (*yytext == '-') { kptr->keyvalue.k[0] *= -1; } #endif } BEGIN(INLINE); } YY_BREAK case 15: YY_RULE_SETUP #line 329 "fitshdr.l" { // Very long integer keyvalue (and 19-digit int64). kptr->type = 4; strcpy(ctmp, yytext); int j, k = yyleng; for (j = 0; j < 8; j++) { // 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 (*yytext == '-') { kptr->keyvalue.l[j] = -abs(kptr->keyvalue.l[j]); } if (k == 0) break; ctmp[k] = '\0'; } // 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 && kptr->keyvalue.l[0] >= -854775808) { kptr->type = 3; #ifdef WCSLIB_INT64 // Native 64-bit integer is available. kptr->keyvalue.l[2] = 0; if (sscanf(yytext, int64fmt, &(kptr->keyvalue.k)) < 1) { kptr->status |= FITSHDR_KEYVALUE; BEGIN(ERROR); } #endif } BEGIN(INLINE); } YY_BREAK case 16: YY_RULE_SETUP #line 370 "fitshdr.l" { // Float keyvalue. kptr->type = 5; if (wcsutil_str2double(yytext, &(kptr->keyvalue.f))) { kptr->status |= FITSHDR_KEYVALUE; BEGIN(ERROR); } BEGIN(INLINE); } YY_BREAK case 17: YY_RULE_SETUP #line 381 "fitshdr.l" { // Integer complex keyvalue. kptr->type = 6; if (sscanf(yytext, "(%lf,%lf)", kptr->keyvalue.c, kptr->keyvalue.c+1) < 2) { kptr->status |= FITSHDR_KEYVALUE; BEGIN(ERROR); } BEGIN(INLINE); } YY_BREAK case 18: YY_RULE_SETUP #line 393 "fitshdr.l" { // 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, kptr->keyvalue.c)) { kptr->status |= FITSHDR_KEYVALUE; BEGIN(ERROR); } for (cptr = ctmp, k++; yytext[k] != ')'; cptr++, k++) { *cptr = yytext[k]; } *cptr = '\0'; if (wcsutil_str2double(ctmp, kptr->keyvalue.c+1)) { kptr->status |= FITSHDR_KEYVALUE; BEGIN(ERROR); } BEGIN(INLINE); } YY_BREAK case 19: /* rule 19 can match eol */ YY_RULE_SETUP #line 422 "fitshdr.l" { // String keyvalue. kptr->type = 8; char *cptr = kptr->keyvalue.s; strcpy(cptr, yytext+1); // Squeeze out repeated quotes. int k = 0; for (int j = 0; j < 72; j++) { if (k < j) { cptr[k] = cptr[j]; } if (cptr[j] == '\0') { if (k) cptr[k-1] = '\0'; break; } else if (cptr[j] == '\'' && cptr[j+1] == '\'') { j++; } k++; } if (*cptr) { // Retain the initial blank in all-blank strings. nullfill(cptr+1, 71); } else { nullfill(cptr, 72); } BEGIN(INLINE); } YY_BREAK case 20: YY_RULE_SETUP #line 455 "fitshdr.l" { kptr->status |= FITSHDR_KEYVALUE; BEGIN(ERROR); } YY_BREAK case 21: *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 460 "fitshdr.l" { BEGIN(FLUSH); } YY_BREAK case 22: *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 464 "fitshdr.l" { BEGIN(FLUSH); } YY_BREAK case 23: YY_RULE_SETUP #line 468 "fitshdr.l" { BEGIN(UNITS); } YY_BREAK case 24: YY_RULE_SETUP #line 472 "fitshdr.l" { kptr->status |= FITSHDR_COMMENT; BEGIN(ERROR); } YY_BREAK case 25: YY_RULE_SETUP #line 477 "fitshdr.l" { // Keyvalue parsing must now also be suspect. kptr->status |= FITSHDR_COMMENT; kptr->type = 0; BEGIN(ERROR); } YY_BREAK case 26: YY_RULE_SETUP #line 484 "fitshdr.l" { kptr->ulen = yyleng; yymore(); BEGIN(COMMENT); } YY_BREAK case 27: YY_RULE_SETUP #line 490 "fitshdr.l" { yymore(); BEGIN(COMMENT); } YY_BREAK case 28: YY_RULE_SETUP #line 495 "fitshdr.l" { strcpy(kptr->comment, yytext); nullfill(kptr->comment, 84); BEGIN(FLUSH); } YY_BREAK case 29: YY_RULE_SETUP #line 501 "fitshdr.l" { if (!continuation) kptr->type = -abs(kptr->type); sprintf(kptr->comment, "%.80s", yyextra->hdr-80); kptr->comment[80] = '\0'; nullfill(kptr->comment+80, 4); BEGIN(FLUSH); } YY_BREAK case 30: /* rule 30 can match eol */ YY_RULE_SETUP #line 511 "fitshdr.l" { // Discard the rest of the input line. kptr->keyno = ++keyno; // Null-fill the keyword. kptr->keyword[8] = '\0'; nullfill(kptr->keyword, 12); // Do indexing. iptr = keyids; kptr->keyid = -1; 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++) { if (*cptr != '.' && *cptr != kptr->keyword[k]) break; } if (k == 8) { // Found a match. iptr->count++; if (iptr->idx[0] == -1) { iptr->idx[0] = keyno-1; } else { iptr->idx[1] = keyno-1; } kptr->keyno = -abs(kptr->keyno); if (kptr->keyid < 0) kptr->keyid = j; } } // Deal with continued strings. if (continuation) { // Tidy up the previous string keyvalue. if ((kptr-1)->type == 8) (kptr-1)->type += 10; 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. if (end && (end++ > 1) && !blank) { kptr->status |= FITSHDR_TRAILER; } if (kptr->status) (*nreject)++; kptr++; blank = 0; continuation = 0; BEGIN(INITIAL); } YY_BREAK case YY_STATE_EOF(INITIAL): case YY_STATE_EOF(VALUE): case YY_STATE_EOF(INLINE): case YY_STATE_EOF(UNITS): case YY_STATE_EOF(COMMENT): case YY_STATE_EOF(ERROR): case YY_STATE_EOF(FLUSH): #line 568 "fitshdr.l" { // End-of-input. return 0; } YY_BREAK case 31: YY_RULE_SETUP #line 573 "fitshdr.l" ECHO; YY_BREAK #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 - yyg->yytext_ptr) - 1; /* Undo the effects of YY_DO_BEFORE_ACTION. */ *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 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. */ 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; } /* Note that here we test for yy_c_buf_p "<=" to the position * of the first EOB in the buffer, since yy_c_buf_p will * already have been incremented past the NUL character * (since all states make transitions on EOB to the * end-of-buffer state). Contrast this with the test * in input(). */ 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; yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state( yyscanner ); /* Okay, we're now positioned to make the NUL * transition. We couldn't have * yy_get_previous_state() go ahead and do it * for us because it doesn't know how to deal * with the possibility of jamming (and we don't * want to build jamming into it because then it * will run more slowly). */ yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner); yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; if ( yy_next_state ) { /* Consume the NUL. */ yy_cp = ++yyg->yy_c_buf_p; yy_current_state = yy_next_state; goto yy_match; } else { yy_cp = yyg->yy_c_buf_p; goto yy_find_action; } } else switch ( yy_get_next_buffer( yyscanner ) ) { case EOB_ACT_END_OF_FILE: { yyg->yy_did_buffer_switch_on_eof = 0; if ( yywrap( yyscanner ) ) { /* Note: because we've taken care in * yy_get_next_buffer() to have 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. */ yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ; yy_act = YY_STATE_EOF(YY_START); goto do_action; } else { if ( ! yyg->yy_did_buffer_switch_on_eof ) YY_NEW_FILE; } break; } case EOB_ACT_CONTINUE_SCAN: yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state( yyscanner ); yy_cp = yyg->yy_c_buf_p; yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; goto yy_match; case EOB_ACT_LAST_MATCH: yyg->yy_c_buf_p = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars]; yy_current_state = yy_get_previous_state( yyscanner ); yy_cp = yyg->yy_c_buf_p; yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; goto yy_find_action; } break; } default: YY_FATAL_ERROR( "fatal flex scanner internal error--no action found" ); } /* end of action switch */ } /* end of scanning one token */ } /* end of user's declarations */ } /* end of yylex */ /* yy_get_next_buffer - try to read in a new buffer * * Returns a code representing an action: * EOB_ACT_LAST_MATCH - * EOB_ACT_CONTINUE_SCAN - continue scanning from current position * EOB_ACT_END_OF_FILE - end of file */ static int yy_get_next_buffer (yyscan_t yyscanner) { 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 ( 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 ( 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. */ return EOB_ACT_END_OF_FILE; } else { /* We matched some text prior to the EOB, first * process it. */ return EOB_ACT_LAST_MATCH; } } /* Try to read more data. */ /* First move last chars to start of buffer. */ number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr - 1); for ( i = 0; i < number_to_move; ++i ) *(dest++) = *(source++); if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) /* don't do the read, it's not guaranteed to return an EOF, * just force an EOF */ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0; else { int num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; while ( num_to_read <= 0 ) { /* Not enough room in the buffer - grow it. */ /* just a shorter name for the current buffer */ YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE; int yy_c_buf_p_offset = (int) (yyg->yy_c_buf_p - b->yy_ch_buf); if ( b->yy_is_our_buffer ) { int new_size = b->yy_buf_size * 2; if ( new_size <= 0 ) b->yy_buf_size += b->yy_buf_size / 8; else b->yy_buf_size *= 2; b->yy_ch_buf = (char *) /* Include room in for 2 EOB chars. */ 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 = NULL; if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "fatal error - scanner input buffer overflow" ); 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; } if ( num_to_read > YY_READ_BUF_SIZE ) num_to_read = YY_READ_BUF_SIZE; /* Read in more data. */ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), yyg->yy_n_chars, num_to_read ); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } if ( yyg->yy_n_chars == 0 ) { if ( number_to_move == YY_MORE_ADJ ) { ret_val = EOB_ACT_END_OF_FILE; yyrestart( yyin , yyscanner); } else { ret_val = EOB_ACT_LAST_MATCH; YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_EOF_PENDING; } } else ret_val = EOB_ACT_CONTINUE_SCAN; 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. */ 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); } 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; 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 (yyscan_t yyscanner) { 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 = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp ) { if ( *yy_cp ) { yy_current_state = yy_nxt[yy_current_state][YY_SC_TO_UI(*yy_cp)]; } else yy_current_state = yy_NUL_trans[yy_current_state]; if ( yy_accept[yy_current_state] ) { yyg->yy_last_accepting_state = yy_current_state; yyg->yy_last_accepting_cpos = yy_cp; } } return yy_current_state; } /* yy_try_NUL_trans - try to make a transition on the NUL character * * synopsis * next_state = yy_try_NUL_trans( current_state ); */ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner) { 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); if ( ! yy_is_jam ) { if ( yy_accept[yy_current_state] ) { 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 (yyscan_t yyscanner) #else static int input (yyscan_t yyscanner) #endif { int c; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; *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 ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) /* This was really a NUL. */ *yyg->yy_c_buf_p = '\0'; else { /* need more input */ int offset = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr); ++yyg->yy_c_buf_p; switch ( yy_get_next_buffer( yyscanner ) ) { case EOB_ACT_LAST_MATCH: /* This happens because yy_g_n_b() * sees that we've accumulated a * token and flags that we need to * try matching the token before * proceeding. But for input(), * there's no matching to consider. * So convert the EOB_ACT_LAST_MATCH * to EOB_ACT_END_OF_FILE. */ /* Reset buffer status. */ yyrestart( yyin , yyscanner); /*FALLTHROUGH*/ case EOB_ACT_END_OF_FILE: { if ( yywrap( yyscanner ) ) return 0; if ( ! yyg->yy_did_buffer_switch_on_eof ) YY_NEW_FILE; #ifdef __cplusplus return yyinput(yyscanner); #else return input(yyscanner); #endif } case EOB_ACT_CONTINUE_SCAN: yyg->yy_c_buf_p = yyg->yytext_ptr + offset; break; } } } 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'); return c; } #endif /* ifndef YY_NO_INPUT */ /** 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 yyrestart (FILE * input_file , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if ( ! YY_CURRENT_BUFFER ){ yyensure_buffer_stack (yyscanner); YY_CURRENT_BUFFER_LVALUE = yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); } 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 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 * yypop_buffer_state(); * yypush_buffer_state(new_buffer); */ yyensure_buffer_stack (yyscanner); if ( YY_CURRENT_BUFFER == new_buffer ) return; if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ *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; yy_load_buffer_state( yyscanner ); /* We don't actually know whether we did this switch during * 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. */ yyg->yy_did_buffer_switch_on_eof = 1; } static void yy_load_buffer_state (yyscan_t yyscanner) { 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 yy_create_buffer (FILE * file, int size , yyscan_t yyscanner) { YY_BUFFER_STATE b; b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); if ( ! b ) 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 *) yyalloc( (yy_size_t) (b->yy_buf_size + 2) , yyscanner ); if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); b->yy_is_our_buffer = 1; yy_init_buffer( b, file , yyscanner); return b; } /** Destroy the buffer. * @param b a buffer created with yy_create_buffer() * @param yyscanner The scanner object. */ void yy_delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if ( ! b ) return; if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; if ( b->yy_is_our_buffer ) yyfree( (void *) b->yy_ch_buf , yyscanner ); 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 yyrestart() or at EOF. */ static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner) { int oerrno = errno; 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 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){ b->yy_bs_lineno = 1; b->yy_bs_column = 0; } b->yy_is_interactive = 0; errno = oerrno; } /** 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 yy_flush_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if ( ! b ) return; b->yy_n_chars = 0; /* We always need two end-of-buffer characters. The first causes * a transition to the end-of-buffer state. The second causes * a jam in that state. */ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; b->yy_buf_pos = &b->yy_ch_buf[0]; b->yy_at_bol = 1; b->yy_buffer_status = YY_BUFFER_NEW; if ( b == YY_CURRENT_BUFFER ) 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 yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (new_buffer == NULL) return; yyensure_buffer_stack(yyscanner); /* This block is copied from yy_switch_to_buffer. */ if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ *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) yyg->yy_buffer_stack_top++; YY_CURRENT_BUFFER_LVALUE = new_buffer; /* 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 yypop_buffer_state (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (!YY_CURRENT_BUFFER) return; yy_delete_buffer(YY_CURRENT_BUFFER , yyscanner); YY_CURRENT_BUFFER_LVALUE = NULL; if (yyg->yy_buffer_stack_top > 0) --yyg->yy_buffer_stack_top; if (YY_CURRENT_BUFFER) { 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 yyensure_buffer_stack (yyscan_t yyscanner) { 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; /* 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*) , 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 (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){ /* Increase the buffer to prepare for a possible push. */ yy_size_t grow_size = 8 /* arbitrary grow size */; 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*) , yyscanner); if ( ! yyg->yy_buffer_stack ) YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); /* zero only the new slots.*/ 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 * @param yyscanner The scanner object. * @return the newly allocated buffer state object. */ YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size , yyscan_t yyscanner) { YY_BUFFER_STATE b; if ( size < 2 || 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 NULL; b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); 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 = 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; yy_switch_to_buffer( b , yyscanner ); return b; } /** 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 * yy_scan_bytes() instead. */ YY_BUFFER_STATE yy_scan_string (const char * yystr , yyscan_t yyscanner) { return yy_scan_bytes( yystr, (int) strlen(yystr) , yyscanner); } /** 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 yy_scan_bytes (const char * yybytes, int _yybytes_len , yyscan_t yyscanner) { YY_BUFFER_STATE b; char *buf; yy_size_t n; int i; /* Get memory for full buffer, including space for trailing EOB's. */ n = (yy_size_t) (_yybytes_len + 2); buf = (char *) yyalloc( n , yyscanner ); if ( ! buf ) 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 = yy_scan_buffer( buf, n , yyscanner); if ( ! b ) 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. */ b->yy_is_our_buffer = 1; return b; } #ifndef YY_EXIT_FAILURE #define YY_EXIT_FAILURE 2 #endif static void yynoreturn yy_fatal_error (const char* msg , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; (void)yyg; fprintf( stderr, "%s\n", msg ); exit( YY_EXIT_FAILURE ); } /* Redefine yyless() so it works in section 3 code. */ #undef yyless #define yyless(n) \ do \ { \ /* Undo effects of setting up yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(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 yyget_column (yyscan_t yyscanner) { 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 *yyget_in (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyin; } /** Get the output stream. * @param yyscanner The scanner object. */ FILE *yyget_out (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyout; } /** Get the length of the current token. * @param yyscanner The scanner object. */ int yyget_leng (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyleng; } /** Get the current token. * @param yyscanner The scanner object. */ 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) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yyextra = user_defined ; } /** Set the current 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 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. * @param yyscanner The scanner object. * @see yy_switch_to_buffer */ 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) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yy_flex_debug; } void yyset_debug (int _bdebug , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yy_flex_debug = _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) { 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 ); } /* 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 ) { 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. */ 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 yyin = stdin; yyout = stdout; #else yyin = NULL; yyout = NULL; #endif /* For future reference: Set errno on error, since we are called by * yylex_init() */ return 0; } /* 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){ yy_delete_buffer( YY_CURRENT_BUFFER , yyscanner ); YY_CURRENT_BUFFER_LVALUE = NULL; yypop_buffer_state(yyscanner); } /* Destroy the stack itself. */ 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 * yylex() is called, initialization will occur. */ yy_init_globals( yyscanner); /* Destroy the main struct (reentrant only). */ yyfree ( yyscanner , yyscanner ); yyscanner = NULL; return 0; } /* * Internal utility routines. */ #ifndef yytext_ptr static void yy_flex_strncpy (char* s1, const char * s2, int n , yyscan_t yyscanner) { 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 (const char * s , yyscan_t yyscanner) { int n; for ( n = 0; s[n]; ++n ) ; return n; } #endif void *yyalloc (yy_size_t size , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; (void)yyg; return malloc(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 * because both ANSI C and C++ allow castless assignment from * any pointer type to void*, and deal with argument conversions * as though doing an assignment. */ return realloc(ptr, size); } void yyfree (void * ptr , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; (void)yyg; free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ } #define YYTABLES_NAME "yytables" #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) { // Propagate the terminating null to the end of the string. int j; for (j = 0; j < len; j++) { if (cptr[j] == '\0') { for (int k = j+1; k < len; k++) { cptr[k] = '\0'; } break; } } // Remove trailing blanks. for (int k = j-1; k >= 0; k--) { if (cptr[k] != ' ') break; cptr[k] = '\0'; } return; } astropy-astropy-201cddb/cextern/wcslib/C/flexed/wcsbth.c000066400000000000000000052565401507226315300234420ustar00rootroot00000000000000#line 2 "wcsbth.c" #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 #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 #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 #endif #ifdef yyrestart #define wcsbthrestart_ALREADY_DEFINED #else #define yyrestart wcsbthrestart #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 #endif #ifdef yyfree #define wcsbthfree_ALREADY_DEFINED #else #define yyfree wcsbthfree #endif /* First, we deal with platform-specific or compiler-specific issues. */ /* begin standard C headers. */ #include #include #include #include /* end standard C headers. */ /* flex integer type definitions */ #ifndef FLEXINT_H #define FLEXINT_H /* C99 systems have . Non-C99 systems may or may not. */ #if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, * if you want the limit (max/min) macros for int types. */ #ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS 1 #endif #include typedef int8_t flex_int8_t; typedef uint8_t flex_uint8_t; typedef int16_t flex_int16_t; typedef uint16_t flex_uint16_t; typedef int32_t flex_int32_t; typedef uint32_t flex_uint32_t; #else typedef signed char flex_int8_t; typedef short int flex_int16_t; typedef int flex_int32_t; typedef unsigned char flex_uint8_t; typedef unsigned short int flex_uint16_t; typedef unsigned int flex_uint32_t; /* Limits of integral types. */ #ifndef INT8_MIN #define INT8_MIN (-128) #endif #ifndef INT16_MIN #define INT16_MIN (-32767-1) #endif #ifndef INT32_MIN #define INT32_MIN (-2147483647-1) #endif #ifndef INT8_MAX #define INT8_MAX (127) #endif #ifndef INT16_MAX #define INT16_MAX (32767) #endif #ifndef INT32_MAX #define INT32_MAX (2147483647) #endif #ifndef UINT8_MAX #define UINT8_MAX (255U) #endif #ifndef UINT16_MAX #define UINT16_MAX (65535U) #endif #ifndef UINT32_MAX #define UINT32_MAX (4294967295U) #endif #ifndef SIZE_MAX #define SIZE_MAX (~(size_t)0) #endif #endif /* ! C99 */ #endif /* ! FLEXINT_H */ /* begin standard C++ headers. */ /* TODO: this is always defined, so inline it */ #define yyconst const #if defined(__GNUC__) && __GNUC__ >= 3 #define yynoreturn __attribute__((__noreturn__)) #else #define yynoreturn #endif /* Returned upon end-of-file. */ #define YY_NULL 0 /* 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) ((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 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 ((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 yyrestart( yyin , yyscanner ) #define YY_END_OF_BUFFER_CHAR 0 /* Size of default input buffer. */ #ifndef YY_BUF_SIZE #ifdef __ia64__ /* On IA-64, the buffer size is 16k, not 8k. * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. * Ditto for the __ia64__ case accordingly. */ #define YY_BUF_SIZE 32768 #else #define YY_BUF_SIZE 16384 #endif /* __ia64__ */ #endif /* The state buf must be large enough to hold one state per character in the main buffer. */ #define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) #ifndef YY_TYPEDEF_YY_BUFFER_STATE #define YY_TYPEDEF_YY_BUFFER_STATE typedef struct yy_buffer_state *YY_BUFFER_STATE; #endif #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 yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ *yy_cp = yyg->yy_hold_char; \ YY_RESTORE_YY_MORE_OFFSET \ 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, yyg->yytext_ptr , yyscanner ) #ifndef YY_STRUCT_YY_BUFFER_STATE #define YY_STRUCT_YY_BUFFER_STATE struct yy_buffer_state { FILE *yy_input_file; char *yy_ch_buf; /* input buffer */ char *yy_buf_pos; /* current position in input buffer */ /* Size of input buffer in bytes, not including room for EOB * characters. */ int yy_buf_size; /* Number of characters read into yy_ch_buf, not including EOB * characters. */ int yy_n_chars; /* Whether we "own" the buffer - i.e., we know we created it, * and can realloc() it to grow it, and should free() it to * delete it. */ int yy_is_our_buffer; /* Whether this is an "interactive" input source; if so, and * if we're using stdio for input, then we want to use getc() * instead of fread(), to make sure we stop fetching input after * each newline. */ int yy_is_interactive; /* Whether we're considered to be at the beginning of a line. * If so, '^' rules will be active on the next match, otherwise * not. */ int yy_at_bol; 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. */ int yy_fill_buffer; int yy_buffer_status; #define YY_BUFFER_NEW 0 #define YY_BUFFER_NORMAL 1 /* When an EOF's been seen but there's still some text to process * then we mark the buffer as YY_EOF_PENDING, to indicate that we * shouldn't try reading from the input source any more. We might * still have a bunch of tokens to match, though, because of * possible backing-up. * * When we actually see the EOF, we change the status to "new" * (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 */ /* 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 ( 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 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 ){ \ yyensure_buffer_stack (yyscanner); \ YY_CURRENT_BUFFER_LVALUE = \ 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 ){\ yyensure_buffer_stack (yyscanner); \ YY_CURRENT_BUFFER_LVALUE = \ 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(yyscanner) (/*CONSTCOND*/1) #define YY_SKIP_YYWRAP typedef flex_uint8_t YY_CHAR; typedef int yy_state_type; #define yytext_ptr yytext_r static const flex_int16_t yy_nxt[][128] = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 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, 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 }, { 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, 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 }, { 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, 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 }, { 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, 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 }, { 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, 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 }, { 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, 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 }, { 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, 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 }, { 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 }, { 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 }, { 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, 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, 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, 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, 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, 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, 70, 70, 70, 70, 70, 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, 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, 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, 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, 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, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 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, 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, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112 }, { 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, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112 }, { 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 }, { 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 }, { 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, 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 }, { 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, 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 }, { 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, 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 }, { 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, 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 }, { 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, 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 }, { 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, 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 }, { 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 }, { 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 }, { 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, 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 }, { 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, 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 }, { 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, 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 }, { 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, 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 }, { 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, 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 }, { 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, 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 }, { 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, 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 }, { 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, 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 }, { 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, 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, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 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, 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, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132 }, { 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, 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 }, { 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, 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 }, { 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, 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 }, { 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, 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 }, { 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, 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 }, { 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, 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 }, { 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 }, { 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 }, { 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, 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, 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, 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 }, { 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 }, { 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, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 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, 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, 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 }, { 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, 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 }, { 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, 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, 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, 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, -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, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -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, -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, -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 }, { 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, 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, -72, -72, -72, -72, -72, -72, -72, -72 }, { 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, -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, -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 }, { 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, -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, 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 }, { 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, 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 }, { 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, 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, -76, -76, -76, -76, -76, -76, -76, -76 }, { 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, -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, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77 }, { 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, -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, -78, -78, -78, -78, -78, -78, -78, -78 }, { 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, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79 }, { 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, -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 }, { 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, 195, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81 }, { 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, 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, -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 }, { 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, 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, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83 }, { 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, -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 }, { 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, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -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, -85, -85, -85, -85, -85, -85, -85, -85 }, { 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, -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, -86, -86, -86, -86, -86, -86, -86, -86 }, { 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, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87 }, { 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, -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 }, { 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, -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, 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, -89, -89, -89, -89, -89, -89, -89, -89 }, { 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, -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, -90, -90, -90, -90, -90, -90, -90, -90 }, { 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, -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, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91 }, { 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, 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 }, { 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, 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, -93, -93, -93, -93, -93, -93, -93, -93 }, { 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, -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 }, { 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, 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 }, { 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, -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 }, { 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, 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 }, { 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, -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 }, { 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, 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 }, { 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, -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, -101, -101, -101, -101, -101, -101, -101, -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 }, { 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, -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, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -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, 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, -103, -103, -103, -103, -103, -103, -103, -103 }, { 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, -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 }, { 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 }, { 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, -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 }, { 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, 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 }, { 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, -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, -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, 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 }, { 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, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -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, -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, 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 }, { 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, -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, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112 }, { 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, 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, -113, -113, -113, -113, -113, -113, -113, -113 }, { 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, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114 }, { 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 }, { 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, -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, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116 }, { 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, 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 }, { 69, -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, 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, 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 }, { 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, -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 }, { 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, -120, -120, -120, -120, -120, -120, -120, -120 }, { 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, -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, -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 }, { 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, 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 }, { 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, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123 }, { 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, 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 }, { 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, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125 }, { 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, 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, -126, -126, -126, -126, -126, -126, -126, -126 }, { 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, -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 }, { 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, -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 }, { 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, -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 }, { 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, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130 }, { 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, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131 }, { 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, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132 }, { 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, 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, -133, -133, -133, -133, -133, -133, -133, -133 }, { 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, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134 }, { 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 }, { 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, -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, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136 }, { 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, 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, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137 }, { 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, -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 }, { 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, 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, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -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, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -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, -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, -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 }, { 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, -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, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142 }, { 69, -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, 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, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143 }, { 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, -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, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -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, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -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, -146, -146, -146, -146, -146, -146, -146, -146 }, { 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, -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 }, { 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, -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, -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 }, { 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, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -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, -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, -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 }, { 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, -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 }, { 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, 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 }, { 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, -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 }, { 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, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 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, 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 }, { 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, -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, -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 }, { 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, 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, 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, 308, 308, 308, 308, 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, 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 }, { 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, -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 }, { 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 }, { 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, -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, -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 }, { 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, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316 }, { 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, -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, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164 }, { 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, 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, -165, -165, -165, -165, -165, -165, -165, -165 }, { 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, 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, -166, -166, -166, -166, -166, -166, -166, -166 }, { 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, -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, 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, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167 }, { 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, -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, -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 }, { 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, -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, -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, -169, -169, -169, -169 }, { 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, -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, 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, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170 }, { 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, -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, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171 }, { 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, -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, -172, -172, -172, -172, -172, -172, -172, -172 }, { 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, -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 }, { 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, -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, 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, -174, -174, -174, -174, -174, -174, -174, -174 }, { 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, -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, -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 }, { 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, 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, -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 }, { 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, 338, -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 }, { 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, 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, -178, -178, -178, -178, -178, -178, -178, -178 }, { 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, -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, -179, -179, -179, -179, -179, -179, -179, -179 }, { 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, -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, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180 }, { 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, -181, 346, -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 }, { 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, -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, -182, -182, -182, -182, -182, -182, -182, -182 }, { 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, -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, 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, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183 }, { 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, -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, -184, -184, -184, -184, -184, -184, -184, -184 }, { 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, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185 }, { 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, -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, -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, -186, -186, -186, -186, -186, -186, -186, -186 }, { 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, -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 }, { 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, -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, -188, -188, -188, -188, -188, -188, -188, -188 }, { 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, -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, -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, -189, -189, -189, -189, -189, -189, -189, -189 }, { 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, -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, -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 }, { 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, -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 }, { 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, -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, -192, -192, -192, -192, -192, -192, -192, -192 }, { 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, -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, -193, -193, -193, -193, -193, -193, -193, -193 }, { 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, -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, -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 }, { 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, -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 }, { 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, -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, -196, -196, -196, -196, -196, -196, -196, -196 }, { 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, -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, -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 }, { 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, -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 }, { 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, -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, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199 }, { 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, -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 }, { 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, -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, -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 }, { 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, -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, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202 }, { 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, -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, 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, -203, -203, -203, -203, -203, -203, -203, -203 }, { 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, -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, -204, -204, -204, -204, -204, -204, -204, -204 }, { 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, -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, -205, -205, -205, -205, -205, -205, -205, -205 }, { 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, -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, -206, -206, -206, -206, -206, -206, -206, -206 }, { 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, -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, -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 }, { 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, -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, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208 }, { 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, -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, 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 }, { 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, -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, -210, -210, -210, -210, -210, -210, -210, -210 }, { 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, -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, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211 }, { 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, -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, 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 }, { 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, -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, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213 }, { 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, -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, 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, -214, -214, -214, -214, -214, -214, -214, -214 }, { 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, -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 }, { 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, -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, -216, -216, -216, -216, -216, -216, -216, -216 }, { 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, -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, -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 }, { 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, -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 }, { 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, -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, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219 }, { 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, -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, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220 }, { 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, -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, -221, -221, -221, -221, -221, -221, -221, -221 }, { 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, -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, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222 }, { 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, -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, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223 }, { 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, -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, -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 }, { 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, -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 }, { 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, 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, -226, -226, -226, -226, -226, -226, -226, -226 }, { 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, -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 }, { 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, -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, -228 }, { 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, 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 }, { 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, 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, -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, -230, -230, -230, -230, -230, -230, -230 }, { 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 }, { 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, -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 }, { 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, 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, -233, -233, -233, -233, -233, -233, -233, -233 }, { 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, -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 }, { 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, 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, -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, -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 }, { 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, 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, -236, -236, -236, -236, -236, -236, -236, -236 }, { 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, 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, -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, -237, -237, -237, -237, -237 }, { 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 }, { 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, 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, -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, -239, -239, -239 }, { 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, 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, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240 }, { 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, 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, 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 }, { 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, 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, -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 }, { 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, 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, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243 }, { 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, -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, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244 }, { 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, -245, -245, 435, 436, 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, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245 }, { 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, -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 }, { 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, 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 }, { 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, -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, 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 }, { 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, -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, -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 }, { 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, -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 }, { 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 }, { 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 }, { 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, 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 }, { 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 }, { 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 }, { 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, -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 }, { 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, -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, -257, -257, -257 }, { 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 }, { 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, -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, -259, -259, -259, -259, -259, -259 }, { 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, -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 }, { 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, -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 }, { 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, 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 }, { 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, -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 }, { 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, 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 }, { 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, 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, -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 }, { 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, 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, -266, -266, -266, -266, -266, -266, -266, -266 }, { 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, -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 }, { 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, -268, -268, -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 }, { 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, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269 }, { 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, -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, -270, -270, -270, -270, -270, -270, -270, -270 }, { 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, -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 }, { 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, 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 }, { 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, -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 }, { 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, -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 }, { 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, -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 }, { 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, -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 }, { 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, 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 }, { 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, 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, -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 }, { 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, 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 }, { 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, -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 }, { 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, -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 }, { 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, -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 }, { 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, -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, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283 }, { 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, -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 }, { 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, -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 }, { 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, -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 }, { 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, 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 }, { 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, -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 }, { 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, 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 }, { 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, -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 }, { 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, -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 }, { 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, -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 }, { 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, 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 }, { 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, -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 }, { 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, 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 }, { 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, -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 }, { 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, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297 }, { 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, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 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, -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, -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 }, { 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 }, { 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, -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, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301 }, { 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, 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 }, { 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, 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 }, { 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, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513 }, { 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, -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, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305 }, { 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, 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 }, { 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, 308, 308, 308, 308, 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, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 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, -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, -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, -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, -309 }, { 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, 308, 308, 308, 308, 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, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 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, 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 }, { 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, -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, -313, -313, -313, -313, -313, -313, -313, -313 }, { 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 }, { 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, -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 }, { 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, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316 }, { 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, -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 }, { 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, 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, -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 }, { 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, 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, -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 }, { 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, -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, -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, -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 }, { 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, 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, -322, -322, -322, -322, -322, -322, -322, -322 }, { 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, -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, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323 }, { 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, -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, -324, -324, -324, -324, -324, -324, -324, -324 }, { 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, -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, -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 }, { 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, -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, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326 }, { 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, -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, 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, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327 }, { 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, -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, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328 }, { 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, -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, -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, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329 }, { 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, -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 }, { 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, -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, -331, -331, -331, -331, -331, -331, -331, -331 }, { 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, -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, 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 }, { 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, -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, -333, -333, -333, -333, -333, -333, -333, -333 }, { 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, -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 }, { 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, -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, -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 }, { 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, -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, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336 }, { 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, -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, 543, -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, -337, -337, -337 }, { 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, -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, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338 }, { 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, -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, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339 }, { 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, -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, -340, -340, -340, -340, -340, -340, -340, -340 }, { 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, -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, 548, -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 }, { 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, -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, -342, -342, -342, -342, -342, -342, -342, -342 }, { 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, -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, -343, -343, -343, -343, -343, -343, -343, -343 }, { 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, -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 }, { 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, -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, -345, -345, -345, -345, -345, -345, -345, -345 }, { 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, -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, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346 }, { 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, -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 }, { 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, 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, -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 }, { 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, 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, -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 }, { 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, -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 }, { 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, -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, 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, -351, -351, -351, -351, -351, -351, -351, -351 }, { 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, -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, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352 }, { 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, 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, -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 }, { 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, -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 }, { 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, -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, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355 }, { 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, -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, -356, -356, -356, -356, -356, -356, -356, -356 }, { 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, -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, 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, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357 }, { 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, -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, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358 }, { 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, -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, -359, -359, -359, -359, -359, -359, -359, -359 }, { 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, -360, -360, -360, -360, -360, -360, -360, -360, -360, -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, -360, -360, -360, -360, -360, -360, -360, -360 }, { 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, -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 }, { 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, 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, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362 }, { 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, -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, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363 }, { 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, -364, -364, -364, -364, -364, -364, -364, -364, -364, -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 }, { 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, -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 }, { 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, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366 }, { 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, -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, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367 }, { 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, -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, 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 }, { 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, -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 }, { 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, -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, 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, -370, -370, -370, -370, -370, -370, -370, -370 }, { 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, -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, -371, -371, -371, -371, -371, -371, -371, -371 }, { 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, -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, 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 }, { 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, -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, -373, -373, -373, -373, -373, -373, -373, -373 }, { 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, -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, 586, -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 }, { 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, -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, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375 }, { 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, -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, 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, -376, -376, -376, -376, -376, -376, -376, -376 }, { 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, -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 }, { 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, 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, -378, -378, -378, -378, -378, -378, -378, -378 }, { 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, -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, -379, -379, -379, -379, -379, -379, -379, -379 }, { 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, -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, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380 }, { 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, -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, 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 }, { 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, -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, -382, -382, -382, -382, -382, -382, -382, -382 }, { 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, 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, -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 }, { 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, -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 }, { 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, -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, -385, -385, -385, -385, -385, -385, -385, -385 }, { 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, -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, -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, -386, -386 }, { 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, -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, -387, -387, -387, -387, -387 }, { 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, -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, -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, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388 }, { 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, -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, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389 }, { 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, -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 }, { 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, 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, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391 }, { 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, -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, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392 }, { 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, 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, -393, -393, -393, -393, -393, -393, -393, -393 }, { 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, -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 }, { 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, 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, -395, -395, -395, -395, -395, -395, -395, -395 }, { 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, -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, 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, -396, -396, -396, -396, -396, -396, -396, -396 }, { 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, -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, 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, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397 }, { 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, -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, -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, -398, -398, -398, -398, -398, -398, -398, -398 }, { 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, 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, -399, -399, -399, -399, -399, -399, -399, -399 }, { 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, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -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, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400 }, { 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, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -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, -401, -401, -401, -401, -401, -401, -401, -401 }, { 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, -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, -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, -402, -402 }, { 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, -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 }, { 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, -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, -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 }, { 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, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405 }, { 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, -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, -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, -406, -406, -406, -406 }, { 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, -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 }, { 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, -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, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408 }, { 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, -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 }, { 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, -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 }, { 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, 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 }, { 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, -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 }, { 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, -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 }, { 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, -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, -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 }, { 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, -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, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415 }, { 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, -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, -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, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416 }, { 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, -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, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417 }, { 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, 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, -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, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418 }, { 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, 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 }, { 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, -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 }, { 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, -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, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421 }, { 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, -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, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422 }, { 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, -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 }, { 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, -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, -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, -424, -424, -424, -424 }, { 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 }, { 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, -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 }, { 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, -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 }, { 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, -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 }, { 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, -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 }, { 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, -430, -430, -430, -430, -430, -430, -430, -430 }, { 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, -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 }, { 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 }, { 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, 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, -433, -433, -433, -433, -433, -433, -433, -433 }, { 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, -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 }, { 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, 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 }, { 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, 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, -436, -436, -436, -436, -436, -436, -436, -436 }, { 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, -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 }, { 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, -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 }, { 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, 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 }, { 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, -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, -440, -440, -440, -440, -440, -440, -440, -440 }, { 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, -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 }, { 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 }, { 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, -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, -443, -443, -443, -443, -443, -443, -443, -443 }, { 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, -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 }, { 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 }, { 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, -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, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446 }, { 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, -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, -447, -447, -447, -447, -447, -447, -447 }, { 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, -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, -448, -448, -448, -448, -448, -448, -448, -448, -448, -448, -448, -448, -448, -448, -448, -448, -448, -448, -448, -448 }, { 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, -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, -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 }, { 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, -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, -450 }, { 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, -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 }, { 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, -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, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452 }, { 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, -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, -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 }, { 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, -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 }, { 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, -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, -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 }, { 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, -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 }, { 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, -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 }, { 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, -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 }, { 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, 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 }, { 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, -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 }, { 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, 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 }, { 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, 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 }, { 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, 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, -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 }, { 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, 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 }, { 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, 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, -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 }, { 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, 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, -466, -466, -466, -466, -466, -466, -466, -466 }, { 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, 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, 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 }, { 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, 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, -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 }, { 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, 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 }, { 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, -470, -470, -470, -470, -470, -470, -470, -470 }, { 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, -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 }, { 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, 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, -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 }, { 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, 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, -473, -473, -473, -473, -473, -473, -473, -473 }, { 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 }, { 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, -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, -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 }, { 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, 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, -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 }, { 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, -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 }, { 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 }, { 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, -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 }, { 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, 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, -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 }, { 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, -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 }, { 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 }, { 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, -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, -483, -483, -483, -483, -483, -483, -483, -483 }, { 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, -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 }, { 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, -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 }, { 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, 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, -486, -486, -486, -486, -486, -486, -486, -486 }, { 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, -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 }, { 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, -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, -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, -488 }, { 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, -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 }, { 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, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490 }, { 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, -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 }, { 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, 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, 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 }, { 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, -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 }, { 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, -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, -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 }, { 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, -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 }, { 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, -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, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496 }, { 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, -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, -497, -497, -497, -497, -497, -497, -497, -497, -497 }, { 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, 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 }, { 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, -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, -499 }, { 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, -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 }, { 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, -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, -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 }, { 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, 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 }, { 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, -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, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503 }, { 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, -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 }, { 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, -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, 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 }, { 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, -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, -506, -506, -506, -506, -506, -506, -506, -506 }, { 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, 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, -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 }, { 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, 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 }, { 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, 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 }, { 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, -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, -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 }, { 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, 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 }, { 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, 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 }, { 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, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513 }, { 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, -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, -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 }, { 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, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513 }, { 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, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716 }, { 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, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513 }, { 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, -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, -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, -518, -518, -518, -518, -518, -518, -518, -518, -518, -518 }, { 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, 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 }, { 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 }, { 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, -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, -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, -521, -521, -521, -521, -521, -521, -521, -521, -521, -521 }, { 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, -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, -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, -522, -522, -522, -522, -522, -522, -522, -522, -522, -522 }, { 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, -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, -523, -523, -523, -523, -523, -523, -523, -523, -523, -523, -523, -523, -523, -523, -523, -523, -523, -523, -523, -523, -523 }, { 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, -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, -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, -524, -524, -524, -524, -524, -524, -524, -524 }, { 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, -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, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525 }, { 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, -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, -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, -526, -526, -526, -526, -526, -526, -526, -526 }, { 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, -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, -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, -527, -527, -527, -527, -527, -527, -527, -527, -527, -527 }, { 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, 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, -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 }, { 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, -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, -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, -529, -529, -529, -529, -529, -529, -529, -529 }, { 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, -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, 729, -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 }, { 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, -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, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531 }, { 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, 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, -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 }, { 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, -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, 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, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533 }, { 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, -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 }, { 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, -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, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535 }, { 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, 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, -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 }, { 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, -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 }, { 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, -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, -538, -538, -538, -538, -538, -538, -538, -538 }, { 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, -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 }, { 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, -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, -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, -540, -540, -540, -540, -540, -540, -540, -540 }, { 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, -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 }, { 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, -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, 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, -542, -542, -542, -542, -542, -542, -542, -542 }, { 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, -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 }, { 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, -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 }, { 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, -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, -545, -545, -545, -545, -545, -545, -545, -545 }, { 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, -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, 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, -546, -546, -546, -546, -546, -546, -546, -546 }, { 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, -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 }, { 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, 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, -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 }, { 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, -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, 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, -549, -549, -549, -549, -549, -549, -549, -549 }, { 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, -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 }, { 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, -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, 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, -551, -551, -551, -551, -551, -551, -551, -551 }, { 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, -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 }, { 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, -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, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -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, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553 }, { 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, -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, -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, -554, -554, -554, -554, -554, -554, -554, -554 }, { 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, -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, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555 }, { 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, 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, -556, -556, -556, -556, -556, -556, -556, -556 }, { 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, -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 }, { 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, -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 }, { 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, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -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 }, { 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, -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 }, { 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, -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, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561 }, { 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, -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, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562 }, { 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, -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, 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, -563, -563, -563, -563, -563, -563, -563, -563 }, { 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, -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, 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 }, { 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, 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, -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 }, { 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, -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, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566 }, { 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, -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 }, { 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, -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, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568 }, { 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, -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, 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, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569 }, { 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, 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, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570 }, { 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, -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, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571 }, { 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, -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 }, { 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, -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, -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 }, { 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, -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, -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 }, { 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, -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, -575, -575, -575, -575, -575, -575, -575, -575 }, { 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, -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, 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, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576 }, { 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, -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 }, { 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, -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 }, { 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, -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, -579, -579, -579, -579, -579, -579, -579, -579 }, { 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, -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 }, { 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, -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 }, { 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, -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 }, { 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, -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 }, { 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, -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 }, { 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, -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, -585, -585, -585, -585, -585, -585, -585, -585 }, { 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, -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, -586, -586, -586, -586, -586, -586, -586, -586 }, { 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, -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 }, { 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, -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, -588, -588, -588, -588, -588, -588, -588, -588 }, { 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, -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 }, { 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, -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, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590 }, { 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, -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 }, { 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, -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, -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 }, { 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, -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, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593 }, { 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, -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 }, { 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, -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, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595 }, { 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, 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, -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 }, { 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, -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, 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, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597 }, { 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, -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, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598 }, { 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, -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, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599 }, { 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, -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, 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, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600 }, { 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, -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, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601 }, { 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, -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, 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 }, { 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, -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, -603, -603, -603, -603, -603, -603, -603, -603 }, { 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, -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, -604, -604, -604, -604, -604, -604, -604, -604 }, { 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, -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 }, { 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, -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, -606, -606, -606, -606, -606, -606, -606, -606 }, { 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, -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, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607 }, { 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, -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 }, { 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, -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, -609, -609, -609, -609, -609, -609, -609, -609 }, { 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, -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 }, { 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, -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 }, { 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, -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, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612 }, { 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, -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, -613, -613, -613, -613, -613, -613, -613, -613 }, { 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, -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, -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 }, { 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, -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, 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, -615, -615, -615, -615, -615, -615, -615, -615 }, { 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, -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 }, { 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, -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 }, { 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, -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, 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, -618, -618, -618, -618, -618, -618, -618, -618 }, { 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, 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, -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 }, { 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, -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 }, { 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, -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 }, { 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, -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, -622, -622, -622, -622, -622, -622, -622, -622 }, { 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, -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, -623, -623, -623, -623, -623, -623, -623, -623 }, { 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, -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, -624 }, { 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, -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 }, { 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, -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, -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, -626 }, { 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, -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 }, { 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, -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, -628, -628, -628, -628, -628 }, { 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, -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, -629, -629, -629, -629, -629, -629, -629, -629 }, { 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, -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 }, { 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 }, { 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 }, { 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, 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, -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 }, { 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, 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 }, { 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, 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 }, { 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, 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, -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 }, { 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, 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 }, { 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, -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 }, { 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, 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 }, { 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, -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, -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 }, { 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 }, { 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 }, { 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, -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 }, { 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, -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, 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 }, { 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 }, { 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, 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, -646, -646, -646, -646, -646, -646, -646, -646 }, { 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, 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, 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 }, { 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 }, { 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, -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 }, { 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, 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, -650, -650, -650, -650, -650, -650, -650, -650 }, { 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, -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 }, { 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, -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, -652, -652, -652, -652, -652, -652, -652 }, { 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, -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, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653 }, { 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, -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, 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 }, { 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, -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 }, { 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, 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, -656, -656, -656, -656, -656, -656, -656, -656 }, { 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, -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 }, { 69, -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, 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 }, { 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, -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, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659 }, { 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, -660, -660, -660, -660, -660, -660, -660, -660 }, { 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, -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 }, { 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 }, { 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, 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, -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 }, { 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, 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 }, { 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, 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 }, { 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, -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 }, { 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, -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, 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 }, { 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, -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 }, { 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, -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, -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, -669 }, { 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, -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, -670, -670, -670, -670, -670, -670, -670, -670 }, { 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, -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 }, { 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 }, { 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, 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, -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 }, { 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 }, { 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 }, { 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, 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, -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, -676, -676, -676, -676, -676, -676, -676, -676, -676, -676, -676, -676, -676, -676, -676, -676, -676 }, { 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, 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 }, { 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 }, { 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, 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 }, { 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, -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, -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, -680 }, { 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 }, { 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 }, { 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, 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, -683, -683, -683, -683, -683, -683, -683, -683 }, { 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, -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, -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 }, { 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, 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, -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 }, { 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, 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, -686, -686, -686, -686, -686, -686, -686, -686 }, { 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, 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 }, { 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, -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, -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 }, { 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, 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 }, { 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, -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 }, { 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, 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 }, { 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 }, { 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, 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, -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, -693, -693, -693, -693, -693, -693, -693, -693, -693, -693, -693, -693, -693, -693, -693, -693, -693 }, { 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 }, { 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, -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, -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 }, { 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, 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, -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 }, { 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, 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 }, { 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 }, { 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, 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, -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, -699, -699, -699, -699, -699, -699, -699, -699, -699, -699, -699, -699, -699, -699, -699, -699, -699 }, { 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, -700, -700, -700, -700, -700, -700, -700, -700 }, { 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, -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 }, { 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, 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 }, { 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, 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, -703, -703, -703, -703, -703, -703, -703, -703 }, { 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, -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, -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, -704 }, { 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 }, { 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, -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, -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, -706, -706, -706, -706, -706, -706, -706, -706, -706, -706, -706, -706, -706, -706, -706, -706, -706, -706, -706, -706, -706, -706, -706, -706 }, { 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, -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, -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, -707, -707, -707, -707, -707, -707, -707, -707, -707, -707 }, { 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, -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, -708, -708, -708, -708, -708, -708, -708, -708, -708, -708, -708, -708, -708, -708, -708, -708, -708, -708 }, { 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, -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, -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 }, { 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, -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, -710, -710, -710, -710, -710, -710, -710, -710, -710, -710, -710, -710, -710, -710, -710, -710, -710, -710 }, { 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, -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, -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, -711, -711, -711, -711, -711, -711, -711, -711, -711, -711 }, { 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, -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, -712, -712, -712, -712, -712, -712, -712, -712, -712, -712, -712, -712, -712, -712, -712, -712, -712, -712 }, { 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, -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, -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, -713, -713, -713, -713, -713, -713, -713, -713, -713, -713, -713, -713, -713, -713, -713, -713, -713, -713, -713, -713, -713, -713, -713, -713 }, { 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, 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 }, { 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, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 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, 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, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716 }, { 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, -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, -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, -717, -717, -717, -717, -717, -717, -717, -717, -717, -717 }, { 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, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, 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, 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 }, { 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 }, { 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, -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, -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, -721, -721, -721, -721, -721, -721, -721, -721, -721, -721 }, { 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 }, { 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 }, { 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, -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 }, { 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, -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, -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, -725, -725, -725, -725 }, { 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, -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, -726 }, { 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, -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, -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, -727, -727, -727, -727 }, { 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, -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, -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, -728, -728, -728, -728 }, { 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, -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, -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, -729, -729 }, { 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, -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 }, { 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, -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 }, { 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, -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, -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, -732, -732, -732, -732, -732, -732, -732, -732, -732, -732 }, { 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, -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, -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, -733, -733 }, { 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, -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, -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, -734, -734, -734, -734 }, { 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, -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, 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, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735 }, { 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, -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, 947, -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 }, { 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, -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 }, { 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, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -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, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738 }, { 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, -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, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739 }, { 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, -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 }, { 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, -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, -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, -741, -741, -741, -741, -741, -741, -741, -741, -741, -741 }, { 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, -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, -742, -742, -742, -742, -742, -742, -742, -742, -742, -742, -742, -742, -742, -742, -742, -742, -742, -742, -742, -742 }, { 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, -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, -743, -743, -743 }, { 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, -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, -744, -744, -744, -744, -744 }, { 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, -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, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745 }, { 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, -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, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746 }, { 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, -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 }, { 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, -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, -748, -748, -748, -748, -748, -748, -748, -748, -748, -748, -748, -748, -748, -748, -748, -748, -748, -748, -748, -748, -748 }, { 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, -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, -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 }, { 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, -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 }, { 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, -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 }, { 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, -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, -752, -752, -752, -752, -752 }, { 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, -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, -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, -753 }, { 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, -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, 954, -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 }, { 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, -755, -755, -755, -755, -755, -755, -755, -755, -755, -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 }, { 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, 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, -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 }, { 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, -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 }, { 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, -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, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758 }, { 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, 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, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759 }, { 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, -760, -760, -760, -760, -760, -760, -760, -760 }, { 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, -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, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761 }, { 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, -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, 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 }, { 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, -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, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763 }, { 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, -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 }, { 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, 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, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765 }, { 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, -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, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766 }, { 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, -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 }, { 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, -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, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768 }, { 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, -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, -769, -769, -769, -769, -769, -769, -769, -769 }, { 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, -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 }, { 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, -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, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771 }, { 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, 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, -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 }, { 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, 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, -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 }, { 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, -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 }, { 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, -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, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775 }, { 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, 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, -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 }, { 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, -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 }, { 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, -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 }, { 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, 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, -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 }, { 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, -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, -780, -780, -780, -780, -780, -780, -780, -780, -780, -780, -780, -780, -780, -780, -780, -780, -780, -780, -780, -780, -780 }, { 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, -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, -781, -781, -781, -781, -781, -781 }, { 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, -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, -782, -782, -782, -782, -782, -782, -782, -782, -782, -782, -782, -782, -782, -782, -782, -782, -782, -782, -782 }, { 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, 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, -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 }, { 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, -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, -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 }, { 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, -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 }, { 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, -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, -786, -786, -786, -786, -786, -786, -786, -786 }, { 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, -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, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787 }, { 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, -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, -788, -788, -788, -788, -788, -788, -788, -788 }, { 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, -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, -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 }, { 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, -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, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790 }, { 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, -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, -791, -791, -791, -791, -791, -791, -791, -791 }, { 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, -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, -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 }, { 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, -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, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793 }, { 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, -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 }, { 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, -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 }, { 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, -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, -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, -796 }, { 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, -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, -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, -797 }, { 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, -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, -798 }, { 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, -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, -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 }, { 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, -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, -800, -800, -800, -800 }, { 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, -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, -801, -801, -801, -801 }, { 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, -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, -802, -802, -802, -802, -802, -802, -802, -802, -802, -802, -802, -802, -802, -802, -802, -802, -802, -802, -802, -802 }, { 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, -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, -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 }, { 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, -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, -804, -804, -804, -804, -804, -804, -804, -804, -804, -804, -804 }, { 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, -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, -805, -805, -805, -805, -805, -805, -805, -805 }, { 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, -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, -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 }, { 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, -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, -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 }, { 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, -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, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808 }, { 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, -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 }, { 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, -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, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810 }, { 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, -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, -811, -811, -811, -811, -811, -811, -811, -811 }, { 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, -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, -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 }, { 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, -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, -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 }, { 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, -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 }, { 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, -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, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815 }, { 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, -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, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816 }, { 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, -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, -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, -817, -817, -817, -817 }, { 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, -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, -818, -818, -818, -818, -818, -818, -818, -818 }, { 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, 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, -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 }, { 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, -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, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820 }, { 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, 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, -821, -821, -821, -821, -821, -821, -821, -821 }, { 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, -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, -822, -822, -822, -822, -822, -822, -822, -822 }, { 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, 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, -823, -823, -823, -823, -823, -823, -823, -823 }, { 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, 1014, -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 }, { 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 }, { 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, -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, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826 }, { 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, 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, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827 }, { 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 }, { 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, 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 }, { 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, 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, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830 }, { 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, -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, -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 }, { 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, -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 }, { 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, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833 }, { 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 }, { 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, -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 }, { 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, 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, -836, -836, -836, -836, -836, -836, -836, -836 }, { 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, 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 }, { 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, -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, -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 }, { 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, -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 }, { 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, -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, -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 }, { 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, -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 }, { 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 }, { 69, -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, 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, -843, -843, -843, -843, -843, -843, -843, -843, -843, -843, -843, -843, -843, -843, -843, -843, -843, -843 }, { 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 }, { 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, 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 }, { 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, 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, -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 }, { 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, 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 }, { 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 }, { 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, 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 }, { 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, -850, -850, -850, -850, -850, -850, -850, -850 }, { 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, 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 }, { 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, 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, -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 }, { 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, 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 }, { 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, 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, -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 }, { 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 }, { 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, 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, -856, -856, -856, -856, -856, -856, -856, -856 }, { 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, 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 }, { 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 }, { 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, 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, -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 }, { 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, -860, -860, -860, -860, -860, -860, -860, -860 }, { 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 }, { 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 }, { 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, 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, -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 }, { 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 }, { 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, 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, -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 }, { 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, 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, -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 }, { 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, -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, -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 }, { 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 }, { 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, 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, -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 }, { 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, -870, -870, -870, -870, -870, -870, -870, -870 }, { 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, 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, -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 }, { 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, 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, -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 }, { 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, 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, -873, -873, -873, -873, -873, -873, -873, -873 }, { 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, -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 }, { 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, -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 }, { 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, -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 }, { 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, -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 }, { 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, -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 }, { 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, -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 }, { 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, 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, -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 }, { 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, 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, -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 }, { 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 }, { 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, 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, -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 }, { 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 }, { 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 }, { 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, -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, -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 }, { 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, -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, -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 }, { 69, -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, 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, -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 }, { 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, 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 }, { 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, -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, -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 }, { 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 }, { 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 }, { 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, 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, -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 }, { 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 }, { 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 }, { 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, 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, -896, -896, -896, -896, -896, -896, -896, -896 }, { 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, 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, -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 }, { 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 }, { 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, 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 }, { 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, -900, -900, -900, -900, -900, -900, -900, -900 }, { 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, 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, -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 }, { 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, 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, -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 }, { 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, 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, -903, -903, -903, -903, -903, -903, -903, -903 }, { 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, 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, -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 }, { 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 }, { 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, 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, -906, -906, -906, -906, -906, -906, -906, -906 }, { 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, 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 }, { 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, 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, -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 }, { 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, 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, -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 }, { 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, -910, -910, -910, -910, -910, -910, -910, -910 }, { 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 }, { 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 }, { 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, 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, -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 }, { 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, -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, -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 }, { 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, 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, 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 }, { 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, 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, -916, -916, -916, -916, -916, -916, -916, -916 }, { 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, 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, -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 }, { 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 }, { 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, 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, -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 }, { 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, 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, -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 }, { 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 }, { 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, -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 }, { 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, -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 }, { 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, -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 }, { 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, -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 }, { 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, -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 }, { 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, -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 }, { 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, -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 }, { 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, -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 }, { 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, -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 }, { 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, -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 }, { 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, -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, -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, -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, 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, -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, -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, -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, -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, -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, -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, 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, -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, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 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, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, 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, -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, -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, -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, 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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, 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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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.4 - an implementation of the FITS WCS standard. Copyright (C) 1995-2024, 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/people/Mark.Calabretta $Id: wcsbth.c,v 8.4 2024/10/28 13:56:17 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 { if (!(relax & WCSHDR_CNAMn)) vptr = 0x0; keyname = "iCRDEn"; BEGIN(iCCCCn); } } YY_BREAK case 77: #line 966 "wcsbth.l" case 78: YY_RULE_SETUP #line 966 "wcsbth.l" { valtype = FLOAT; vptr = &(wcstem.crder); if (yyleng == 4) { keyname = "TCRDna"; BEGIN(TCCCna); } else { 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); 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); sscanf(yytext, "%d", &i); if (yyleng == 4) { keyname = "iCSYna"; BEGIN(iCCCna); } else { if (!(relax & WCSHDR_CNAMn)) vptr = 0x0; keyname = "iCSYEn"; BEGIN(iCCCCn); } } YY_BREAK case 82: #line 1006 "wcsbth.l" case 83: YY_RULE_SETUP #line 1006 "wcsbth.l" { valtype = FLOAT; vptr = &(wcstem.csyer); if (yyleng == 4) { keyname = "TCSYna"; BEGIN(TCCCna); } else { if (!(relax & WCSHDR_CNAMn)) vptr = 0x0; keyname = "TCSYEn"; BEGIN(TCCCCn); } } YY_BREAK case 84: YY_RULE_SETUP #line 1020 "wcsbth.l" { valtype = FLOAT; vptr = &(wcstem.czphs); keyname = "CZPHSia"; BEGIN(CCCCCia); } YY_BREAK case 85: #line 1029 "wcsbth.l" case 86: YY_RULE_SETUP #line 1029 "wcsbth.l" { 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 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 1060 "wcsbth.l" { valtype = FLOAT; vptr = &(wcstem.cperi); keyname = "CPERIia"; BEGIN(CCCCCia); } YY_BREAK case 90: #line 1069 "wcsbth.l" case 91: YY_RULE_SETUP #line 1069 "wcsbth.l" { valtype = FLOAT; vptr = &(wcstem.cperi); sscanf(yytext, "%d", &i); if (yyleng == 4) { keyname = "iCPRna"; BEGIN(iCCCna); } else { if (!(relax & WCSHDR_CNAMn)) vptr = 0x0; keyname = "iCPERn"; BEGIN(iCCCCn); } } YY_BREAK case 92: #line 1086 "wcsbth.l" case 93: YY_RULE_SETUP #line 1086 "wcsbth.l" { valtype = FLOAT; vptr = &(wcstem.cperi); if (yyleng == 4) { keyname = "TCPRna"; BEGIN(TCCCna); } else { if (!(relax & WCSHDR_CNAMn)) vptr = 0x0; keyname = "TCPERn"; BEGIN(TCCCCn); } } YY_BREAK case 94: #line 1101 "wcsbth.l" case 95: #line 1102 "wcsbth.l" case 96: YY_RULE_SETUP #line 1102 "wcsbth.l" { valtype = STRING; vptr = wcstem.wcsname; if (yyleng == 7) { keyname = "WCSNAMEa"; imherit = 0; BEGIN(CCCCCCCa); } else { if (*yytext == 'W') { keyname = "WCSNna"; } else { keyname = "TWCSna"; } BEGIN(CCCCna); } } YY_BREAK case 97: YY_RULE_SETUP #line 1121 "wcsbth.l" { valtype = STRING; vptr = wcstem.timesys; 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; if (yyleng == 8) { if (ctrl < -10) keep = keyrec; keyname = "TREFPOS"; BEGIN(CCCCCCCC); } else { keyname = "TRPOSn"; BEGIN(CCCCCn); } } YY_BREAK case 100: #line 1145 "wcsbth.l" case 101: YY_RULE_SETUP #line 1145 "wcsbth.l" { valtype = STRING; vptr = wcstem.trefdir; if (yyleng == 8) { if (ctrl < -10) keep = keyrec; keyname = "TREFDIR"; BEGIN(CCCCCCCC); } else { keyname = "TRDIRn"; BEGIN(CCCCCn); } } YY_BREAK case 102: YY_RULE_SETUP #line 1159 "wcsbth.l" { valtype = STRING; vptr = wcstem.plephem; keyname = "PLEPHEM"; BEGIN(CCCCCCCC); } YY_BREAK case 103: YY_RULE_SETUP #line 1167 "wcsbth.l" { 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; 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 106: #line 1194 "wcsbth.l" case 107: YY_RULE_SETUP #line 1194 "wcsbth.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 108: #line 1212 "wcsbth.l" case 109: YY_RULE_SETUP #line 1212 "wcsbth.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 110: #line 1231 "wcsbth.l" case 111: YY_RULE_SETUP #line 1231 "wcsbth.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 112: #line 1249 "wcsbth.l" case 113: YY_RULE_SETUP #line 1249 "wcsbth.l" { 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); } else { BEGIN(DISCARD); } } YY_BREAK case 114: #line 1268 "wcsbth.l" case 115: YY_RULE_SETUP #line 1268 "wcsbth.l" { 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); } else { BEGIN(DISCARD); } } YY_BREAK case 116: #line 1288 "wcsbth.l" case 117: YY_RULE_SETUP #line 1288 "wcsbth.l" { 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); } } YY_BREAK case 118: YY_RULE_SETUP #line 1306 "wcsbth.l" { valtype = FLOAT; vptr = &(wcstem.timeoffs); keyname = "TIMEOFFS"; BEGIN(CCCCCCCC); } YY_BREAK case 119: YY_RULE_SETUP #line 1314 "wcsbth.l" { valtype = STRING; vptr = wcstem.dateobs; if (ctrl < -10) keep = keyrec; keyname = "DATE-OBS"; imherit = 0; BEGIN(CCCCCCCC); } YY_BREAK case 120: #line 1325 "wcsbth.l" case 121: #line 1326 "wcsbth.l" case 122: YY_RULE_SETUP #line 1326 "wcsbth.l" { 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); } } YY_BREAK case 123: YY_RULE_SETUP #line 1345 "wcsbth.l" { valtype = STRING; vptr = wcstem.datebeg; if (ctrl < -10) keep = keyrec; keyname = "DATE-BEG"; BEGIN(CCCCCCCC); } YY_BREAK case 124: #line 1355 "wcsbth.l" case 125: YY_RULE_SETUP #line 1355 "wcsbth.l" { valtype = STRING; vptr = wcstem.dateavg; if (yyleng == 8) { if (ctrl < -10) keep = keyrec; keyname = "DATE-AVG"; BEGIN(CCCCCCCC); } else { keyname = "DAVGn"; BEGIN(CCCCn); } } YY_BREAK case 126: YY_RULE_SETUP #line 1369 "wcsbth.l" { valtype = STRING; vptr = wcstem.dateend; if (ctrl < -10) keep = keyrec; keyname = "DATE-END"; BEGIN(CCCCCCCC); } YY_BREAK case 127: #line 1379 "wcsbth.l" case 128: YY_RULE_SETUP #line 1379 "wcsbth.l" { 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); } } YY_BREAK case 129: YY_RULE_SETUP #line 1394 "wcsbth.l" { valtype = FLOAT; vptr = &(wcstem.mjdbeg); if (ctrl < -10) keep = keyrec; keyname = "MJD-BEG"; BEGIN(CCCCCCCC); } YY_BREAK case 130: #line 1404 "wcsbth.l" case 131: YY_RULE_SETUP #line 1404 "wcsbth.l" { valtype = FLOAT; vptr = &(wcstem.mjdavg); if (yyleng == 8) { if (ctrl < -10) keep = keyrec; keyname = "MJD-AVG"; BEGIN(CCCCCCCC); } else { keyname = "MJDAn"; BEGIN(CCCCn); } } YY_BREAK case 132: YY_RULE_SETUP #line 1418 "wcsbth.l" { valtype = FLOAT; vptr = &(wcstem.mjdend); if (ctrl < -10) keep = keyrec; keyname = "MJD-END"; BEGIN(CCCCCCCC); } YY_BREAK case 133: YY_RULE_SETUP #line 1427 "wcsbth.l" { valtype = FLOAT; vptr = &(wcstem.jepoch); if (ctrl < -10) keep = keyrec; keyname = "JEPOCH"; BEGIN(CCCCCCCC); } YY_BREAK case 134: YY_RULE_SETUP #line 1436 "wcsbth.l" { valtype = FLOAT; vptr = &(wcstem.bepoch); if (ctrl < -10) keep = keyrec; keyname = "BEPOCH"; BEGIN(CCCCCCCC); } YY_BREAK case 135: YY_RULE_SETUP #line 1445 "wcsbth.l" { valtype = FLOAT; vptr = &(wcstem.tstart); if (ctrl < -10) keep = keyrec; keyname = "TSTART"; BEGIN(CCCCCCCC); } YY_BREAK case 136: YY_RULE_SETUP #line 1454 "wcsbth.l" { valtype = FLOAT; vptr = &(wcstem.tstop); if (ctrl < -10) keep = keyrec; keyname = "TSTOP"; BEGIN(CCCCCCCC); } YY_BREAK case 137: YY_RULE_SETUP #line 1463 "wcsbth.l" { valtype = FLOAT; vptr = &(wcstem.xposure); if (ctrl < -10) keep = keyrec; keyname = "XPOSURE"; BEGIN(CCCCCCCC); } YY_BREAK case 138: YY_RULE_SETUP #line 1472 "wcsbth.l" { valtype = FLOAT; vptr = &(wcstem.telapse); if (ctrl < -10) keep = keyrec; keyname = "TELAPSE"; BEGIN(CCCCCCCC); } YY_BREAK case 139: YY_RULE_SETUP #line 1481 "wcsbth.l" { valtype = FLOAT; vptr = &(wcstem.timsyer); if (ctrl < -10) keep = keyrec; keyname = "TIMSYER"; BEGIN(CCCCCCCC); } YY_BREAK case 140: YY_RULE_SETUP #line 1490 "wcsbth.l" { valtype = FLOAT; vptr = &(wcstem.timrder); if (ctrl < -10) keep = keyrec; keyname = "TIMRDER"; BEGIN(CCCCCCCC); } YY_BREAK case 141: YY_RULE_SETUP #line 1499 "wcsbth.l" { valtype = FLOAT; vptr = &(wcstem.timedel); if (ctrl < -10) keep = keyrec; keyname = "TIMEDEL"; BEGIN(CCCCCCCC); } YY_BREAK case 142: YY_RULE_SETUP #line 1508 "wcsbth.l" { valtype = FLOAT; vptr = &(wcstem.timepixr); chekval = wcsbth_timepixr; if (ctrl < -10) keep = keyrec; keyname = "TIMEPIXR"; BEGIN(CCCCCCCC); } YY_BREAK case 143: #line 1519 "wcsbth.l" case 144: YY_RULE_SETUP #line 1519 "wcsbth.l" { valtype = FLOAT; vptr = wcstem.obsgeo; if (yyleng == 8) { if (ctrl < -10) keep = keyrec; keyname = "OBSGEO-X"; BEGIN(CCCCCCCC); } else { keyname = "OBSGXn"; BEGIN(CCCCCn); } } YY_BREAK case 145: #line 1534 "wcsbth.l" case 146: YY_RULE_SETUP #line 1534 "wcsbth.l" { valtype = FLOAT; vptr = wcstem.obsgeo + 1; if (yyleng == 8) { if (ctrl < -10) keep = keyrec; keyname = "OBSGEO-Y"; BEGIN(CCCCCCCC); } else { keyname = "OBSGYn"; BEGIN(CCCCCn); } } YY_BREAK case 147: #line 1549 "wcsbth.l" case 148: YY_RULE_SETUP #line 1549 "wcsbth.l" { valtype = FLOAT; vptr = wcstem.obsgeo + 2; if (yyleng == 8) { if (ctrl < -10) keep = keyrec; keyname = "OBSGEO-Z"; BEGIN(CCCCCCCC); } else { keyname = "OBSGZn"; BEGIN(CCCCCn); } } YY_BREAK case 149: YY_RULE_SETUP #line 1563 "wcsbth.l" { valtype = FLOAT; vptr = wcstem.obsgeo + 3; if (ctrl < -10) keep = keyrec; keyname = "OBSGEO-L"; BEGIN(CCCCCCCC); } YY_BREAK case 150: #line 1573 "wcsbth.l" case 151: #line 1574 "wcsbth.l" case 152: YY_RULE_SETUP #line 1574 "wcsbth.l" { valtype = STRING; 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); } } YY_BREAK case 153: YY_RULE_SETUP #line 1593 "wcsbth.l" { valtype = FLOAT; 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); } else { BEGIN(DISCARD); } } YY_BREAK case 157: YY_RULE_SETUP #line 1623 "wcsbth.l" { valtype = FLOAT; vptr = wcstem.obsgeo + 5; if (ctrl < -10) keep = keyrec; keyname = "OBSGEO-H"; BEGIN(CCCCCCCC); } YY_BREAK case 158: #line 1633 "wcsbth.l" case 159: #line 1634 "wcsbth.l" case 160: YY_RULE_SETUP #line 1634 "wcsbth.l" { 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); } } YY_BREAK case 161: YY_RULE_SETUP #line 1653 "wcsbth.l" { valtype = STRING; vptr = wcstem.obsorbit; keyname = "OBSORBIT"; BEGIN(CCCCCCCC); } YY_BREAK case 162: #line 1662 "wcsbth.l" case 163: YY_RULE_SETUP #line 1662 "wcsbth.l" { valtype = STRING; vptr = wcstem.radesys; if (yyleng == 7) { keyname = "RADESYSa"; imherit = 0; BEGIN(CCCCCCCa); } else { keyname = "RADEna"; BEGIN(CCCCna); } } YY_BREAK case 164: YY_RULE_SETUP #line 1676 "wcsbth.l" { 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); } } YY_BREAK case 165: YY_RULE_SETUP #line 1696 "wcsbth.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 = 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); } } YY_BREAK case 166: #line 1724 "wcsbth.l" case 167: YY_RULE_SETUP #line 1724 "wcsbth.l" { valtype = FLOAT; vptr = &(wcstem.equinox); if (yyleng == 7) { keyname = "EQUINOXa"; imherit = 0; BEGIN(CCCCCCCa); } else { keyname = "EQUIna"; BEGIN(CCCCna); } } YY_BREAK case 168: #line 1739 "wcsbth.l" case 169: YY_RULE_SETUP #line 1739 "wcsbth.l" { valtype = STRING; vptr = wcstem.specsys; if (yyleng == 7) { keyname = "SPECSYSa"; BEGIN(CCCCCCCa); } else { keyname = "SPECna"; BEGIN(CCCCna); } } YY_BREAK case 170: #line 1753 "wcsbth.l" case 171: YY_RULE_SETUP #line 1753 "wcsbth.l" { valtype = STRING; vptr = wcstem.ssysobs; if (yyleng == 7) { keyname = "SSYSOBSa"; BEGIN(CCCCCCCa); } else { keyname = "SOBSna"; BEGIN(CCCCna); } } YY_BREAK case 172: #line 1767 "wcsbth.l" case 173: YY_RULE_SETUP #line 1767 "wcsbth.l" { valtype = FLOAT; vptr = &(wcstem.velosys); if (yyleng == 7) { keyname = "VELOSYSa"; BEGIN(CCCCCCCa); } else { keyname = "VSYSna"; BEGIN(CCCCna); } } YY_BREAK case 174: YY_RULE_SETUP #line 1780 "wcsbth.l" { 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); } else { BEGIN(DISCARD); } } YY_BREAK case 175: #line 1801 "wcsbth.l" case 176: #line 1802 "wcsbth.l" case 177: YY_RULE_SETUP #line 1802 "wcsbth.l" { 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); } else { // Pretend we don't recognize it. BEGIN(DISCARD); } } YY_BREAK case 178: #line 1823 "wcsbth.l" case 179: YY_RULE_SETUP #line 1823 "wcsbth.l" { valtype = FLOAT; vptr = &(wcstem.zsource); if (yyleng == 7) { keyname = "ZSOURCEa"; BEGIN(CCCCCCCa); } else { keyname = "ZSOUna"; BEGIN(CCCCna); } } YY_BREAK case 180: #line 1837 "wcsbth.l" case 181: YY_RULE_SETUP #line 1837 "wcsbth.l" { valtype = STRING; vptr = wcstem.ssyssrc; if (yyleng == 7) { keyname = "SSYSSRCa"; BEGIN(CCCCCCCa); } else { keyname = "SSRCna"; BEGIN(CCCCna); } } YY_BREAK case 182: #line 1851 "wcsbth.l" case 183: YY_RULE_SETUP #line 1851 "wcsbth.l" { valtype = FLOAT; vptr = &(wcstem.velangl); if (yyleng == 7) { keyname = "VELANGLa"; BEGIN(CCCCCCCa); } else { keyname = "VANGna"; BEGIN(CCCCna); } } YY_BREAK case 184: YY_RULE_SETUP #line 1864 "wcsbth.l" { valtype = FLOAT; auxprm = 1; vptr = &(auxtem.rsun_ref); keyname = "RSUN_REF"; BEGIN(CCCCCCCC); } YY_BREAK case 185: YY_RULE_SETUP #line 1873 "wcsbth.l" { valtype = FLOAT; auxprm = 1; vptr = &(auxtem.dsun_obs); keyname = "DSUN_OBS"; BEGIN(CCCCCCCC); } YY_BREAK case 186: YY_RULE_SETUP #line 1882 "wcsbth.l" { valtype = FLOAT; auxprm = 1; vptr = &(auxtem.crln_obs); keyname = "CRLN_OBS"; BEGIN(CCCCCCCC); } YY_BREAK case 187: YY_RULE_SETUP #line 1891 "wcsbth.l" { valtype = FLOAT; auxprm = 1; vptr = &(auxtem.hgln_obs); keyname = "HGLN_OBS"; BEGIN(CCCCCCCC); } YY_BREAK case 188: #line 1901 "wcsbth.l" case 189: YY_RULE_SETUP #line 1901 "wcsbth.l" { valtype = FLOAT; auxprm = 1; vptr = &(auxtem.hglt_obs); keyname = "HGLT_OBS"; BEGIN(CCCCCCCC); } YY_BREAK case 190: YY_RULE_SETUP #line 1910 "wcsbth.l" { valtype = FLOAT; auxprm = 1; vptr = &(auxtem.a_radius); 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 192: YY_RULE_SETUP #line 1928 "wcsbth.l" { valtype = FLOAT; auxprm = 1; vptr = &(auxtem.c_radius); keyname = "C_RADIUS"; BEGIN(CCCCCCCC); } YY_BREAK case 193: YY_RULE_SETUP #line 1937 "wcsbth.l" { valtype = FLOAT; auxprm = 1; vptr = &(auxtem.blon_obs); keyname = "BLON_OBS"; BEGIN(CCCCCCCC); } YY_BREAK case 194: YY_RULE_SETUP #line 1946 "wcsbth.l" { valtype = FLOAT; auxprm = 1; vptr = &(auxtem.blat_obs); keyname = "BLAT_OBS"; BEGIN(CCCCCCCC); } YY_BREAK case 195: YY_RULE_SETUP #line 1955 "wcsbth.l" { valtype = FLOAT; auxprm = 1; vptr = &(auxtem.bdis_obs); keyname = "BDIS_OBS"; BEGIN(CCCCCCCC); } YY_BREAK case 196: YY_RULE_SETUP #line 1964 "wcsbth.l" { if (yyextra->nkeyrec) { yyextra->nkeyrec = 0; errmsg = "keyrecords following the END keyrecord were ignored"; BEGIN(ERROR); } else { BEGIN(DISCARD); } } YY_BREAK case 197: YY_RULE_SETUP #line 1974 "wcsbth.l" { BEGIN(DISCARD); } YY_BREAK case 198: #line 1979 "wcsbth.l" case 199: YY_RULE_SETUP #line 1979 "wcsbth.l" { 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", keyname); BEGIN(ERROR); } else { // Pretend we don't recognize it. BEGIN(DISCARD); } } YY_BREAK case 200: #line 1998 "wcsbth.l" case 201: YY_RULE_SETUP #line 1998 "wcsbth.l" { if (relax & WCSHDR_ALLIMG) { 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); } } YY_BREAK 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 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 { BEGIN(DISCARD); } } YY_BREAK case 212: #line 2079 "wcsbth.l" case 213: #line 2080 "wcsbth.l" case 214: #line 2081 "wcsbth.l" case 215: YY_RULE_SETUP #line 2081 "wcsbth.l" { if (vptr && (relax & WCSHDR_LONGKEY)) { WCSBTH_PUTBACK; BEGIN((YY_START == iCCCCn) ? iCCCna : TCCCna); } else if (relax & WCSHDR_reject) { errmsg = errtxt; if (!vptr) { sprintf(errmsg, "%s keyword is non-standard", keyname); } else { 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 216: #line 2103 "wcsbth.l" case 217: YY_RULE_SETUP #line 2103 "wcsbth.l" { BEGIN(DISCARD); } YY_BREAK 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 2112 "wcsbth.l" { 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 224: #line 2120 "wcsbth.l" case 225: YY_RULE_SETUP #line 2120 "wcsbth.l" { BEGIN(DISCARD); } YY_BREAK 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 2204 "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 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 2232 "wcsbth.l" { if (relax & WCSHDR_ALLIMG) { 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. BEGIN(DISCARD); } } YY_BREAK case 271: YY_RULE_SETUP #line 2251 "wcsbth.l" { // 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, "deprecated image-header keyword %s in binary table", keyname); BEGIN(ERROR); } else { // Pretend we don't recognize it. BEGIN(DISCARD); } } YY_BREAK case 272: YY_RULE_SETUP #line 2285 "wcsbth.l" { BEGIN(DISCARD); } YY_BREAK case 273: #line 2290 "wcsbth.l" case 274: #line 2291 "wcsbth.l" case 275: YY_RULE_SETUP #line 2291 "wcsbth.l" { sscanf(yytext, "%d%c", &n, &a); keytype = BIMGARR; BEGIN(VALUE); } YY_BREAK 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 2302 "wcsbth.l" { if (relax & WCSHDR_LONGKEY) { WCSBTH_PUTBACK; BEGIN(TCn_ka); } else if (relax & WCSHDR_reject) { errmsg = errtxt; sprintf(errmsg, "%s keyword is non-standard", keyname); BEGIN(ERROR); } else { // Pretend we don't recognize it. BEGIN(DISCARD); } } YY_BREAK case 282: YY_RULE_SETUP #line 2318 "wcsbth.l" { BEGIN(DISCARD); } YY_BREAK 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 2327 "wcsbth.l" { 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 289: #line 2336 "wcsbth.l" case 290: #line 2337 "wcsbth.l" case 291: #line 2338 "wcsbth.l" case 292: YY_RULE_SETUP #line 2338 "wcsbth.l" { sscanf(yytext, "%d_%d", &n, &k); a = ' '; i = wcsbth_colax(*wcs, &alts, n, a); j = wcsbth_colax(*wcs, &alts, k, a); keytype = PIXLIST; BEGIN(VALUE); } YY_BREAK case 293: YY_RULE_SETUP #line 2347 "wcsbth.l" { BEGIN(DISCARD); } YY_BREAK case 294: #line 2352 "wcsbth.l" case 295: #line 2353 "wcsbth.l" case 296: YY_RULE_SETUP #line 2353 "wcsbth.l" { 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 = errtxt; sprintf(errmsg, "deprecated image-header keyword %s in binary table", keyname); BEGIN(ERROR); } else { // Pretend we don't recognize it. BEGIN(DISCARD); } } YY_BREAK case 297: YY_RULE_SETUP #line 2387 "wcsbth.l" { if (relax & WCSHDR_ALLIMG) { yyless(0); BEGIN(CCCCCia); } else { // Let it go. BEGIN(DISCARD); } } YY_BREAK 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 2402 "wcsbth.l" { WCSBTH_PUTBACK; BEGIN((YY_START == iCROTn) ? iCCCna : TCCCna); } YY_BREAK case 304: #line 2408 "wcsbth.l" case 305: #line 2409 "wcsbth.l" case 306: #line 2410 "wcsbth.l" case 307: YY_RULE_SETUP #line 2410 "wcsbth.l" { if (relax & WCSHDR_CROTAia) { WCSBTH_PUTBACK; BEGIN((YY_START == iCROTn) ? iCCCna : TCCCna); } else if (relax & WCSHDR_reject) { errmsg = errtxt; 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 308: #line 2428 "wcsbth.l" case 309: YY_RULE_SETUP #line 2428 "wcsbth.l" { BEGIN(DISCARD); } YY_BREAK case 310: #line 2433 "wcsbth.l" case 311: YY_RULE_SETUP #line 2433 "wcsbth.l" { // Image-header keyword. if (imherit || (relax & (WCSHDR_AUXIMG | WCSHDR_ALLIMG))) { if (YY_START == CCCCCCCa) { sscanf(yytext, "%c", &a); } else { a = 0; unput(yytext[0]); } keytype = IMGAUX; 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 312: YY_RULE_SETUP #line 2457 "wcsbth.l" { 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 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 2475 "wcsbth.l" { sscanf(yytext, "%d%c", &n, &a); keytype = BINTAB; BEGIN(VALUE); } YY_BREAK case 318: YY_RULE_SETUP #line 2481 "wcsbth.l" { sscanf(yytext, "%d", &n); a = ' '; keytype = BINTAB; BEGIN(VALUE); } YY_BREAK case 319: #line 2489 "wcsbth.l" case 320: YY_RULE_SETUP #line 2489 "wcsbth.l" { BEGIN(DISCARD); } YY_BREAK 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 2499 "wcsbth.l" { sscanf(yytext, "%d", &n); a = 0; keytype = BINTAB; BEGIN(VALUE); } YY_BREAK case 328: #line 2507 "wcsbth.l" case 329: YY_RULE_SETUP #line 2507 "wcsbth.l" { BEGIN(DISCARD); } YY_BREAK case 330: #line 2512 "wcsbth.l" case 331: #line 2513 "wcsbth.l" case 332: #line 2514 "wcsbth.l" case 333: YY_RULE_SETUP #line 2514 "wcsbth.l" { // Image-header keyword. if (relax & WCSHDR_ALLIMG) { 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", keyname); BEGIN(ERROR); } else { // Pretend we don't recognize it. BEGIN(DISCARD); } } YY_BREAK 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 2553 "wcsbth.l" { if (relax & WCSHDR_ALLIMG) { 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); } } YY_BREAK 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 2627 "wcsbth.l" { BEGIN(DISCARD); } YY_BREAK 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 2642 "wcsbth.l" { if (relax & WCSHDR_LONGKEY) { WCSBTH_PUTBACK; BEGIN((YY_START == iCCn_ma) ? iCn_ma : TCn_ma); } else if (relax & WCSHDR_reject) { errmsg = errtxt; sprintf(errmsg, "the %s keyword is non-standard", keyname); BEGIN(ERROR); } else { // Pretend we don't recognize it. BEGIN(DISCARD); } } YY_BREAK case 388: #line 2659 "wcsbth.l" case 389: YY_RULE_SETUP #line 2659 "wcsbth.l" { BEGIN(DISCARD); } YY_BREAK 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 2674 "wcsbth.l" { 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 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 2688 "wcsbth.l" { // 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 410: #line 2698 "wcsbth.l" case 411: YY_RULE_SETUP #line 2698 "wcsbth.l" { BEGIN(DISCARD); } YY_BREAK case 412: YY_RULE_SETUP #line 2702 "wcsbth.l" { if (relax & WCSHDR_PROJPn) { sscanf(yytext, "%d", &m); i = 0; a = ' '; keytype = IMGAXIS; BEGIN(VALUE); } else if (relax & WCSHDR_reject) { 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 { BEGIN(DISCARD); } } YY_BREAK case 415: YY_RULE_SETUP #line 2731 "wcsbth.l" { BEGIN(DISCARD); } YY_BREAK case 416: YY_RULE_SETUP #line 2735 "wcsbth.l" { // Do checks on i, j, m, n, k. if (!(keytype & keysel)) { // Selection by keyword type. BEGIN(DISCARD); } else if (exclude[n] || exclude[k]) { // 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. return WCSHDRERR_BAD_COLUMN; } else { BEGIN(DISCARD); } } else if (i > 99 || j > 99 || m > 99 || n > 999 || k > 999) { if (relax & WCSHDR_reject) { errmsg = errtxt; if (i > 99 || j > 99) { sprintf(errmsg, "axis number exceeds 99"); } else if (m > 99) { sprintf(errmsg, "parameter number exceeds 99"); } else if (n > 999 || k > 999) { sprintf(errmsg, "column number exceeds 999"); } BEGIN(ERROR); } else { // 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. // 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", 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 { // Read the keyvalue. sscanf(yytext, "%d", &inttmp); BEGIN(COMMENT); } } YY_BREAK case 419: YY_RULE_SETUP #line 2814 "wcsbth.l" { errmsg = "an integer value was expected"; BEGIN(ERROR); } YY_BREAK case 420: YY_RULE_SETUP #line 2819 "wcsbth.l" { if (ipass == 1) { BEGIN(COMMENT); } else { // Read the keyvalue. wcsutil_str2double(yytext, &dbltmp); if (chekval && chekval(dbltmp)) { errmsg = "invalid keyvalue"; BEGIN(ERROR); } else { BEGIN(COMMENT); } } } YY_BREAK case 421: YY_RULE_SETUP #line 2836 "wcsbth.l" { errmsg = "a floating-point value was expected"; BEGIN(ERROR); } YY_BREAK case 422: YY_RULE_SETUP #line 2841 "wcsbth.l" { if (ipass == 1) { BEGIN(COMMENT); } else { // Read the keyvalue as integer and fractional parts. wcsutil_str2double2(yytext, dbl2tmp); 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 { // Copy the keyvalue minus the quotes. strncpy(strtmp, yytext+1, yyleng-2); strtmp[yyleng-2] = '\0'; // Strip off trailing blanks. for (int jx = yyleng-3; jx >= 0; jx--) { if (strtmp[jx] != ' ') { break; } strtmp[jx] = '\0'; } // Squeeze out repeated quotes. int ix = 0; for (int jx = 0; jx < 72; jx++) { if (ix < jx) { strtmp[ix] = strtmp[jx]; } if (strtmp[jx] == '\0') { break; } else if (strtmp[jx] == '\'' && strtmp[jx+1] == '\'') { jx++; } ix++; } BEGIN(COMMENT); } } YY_BREAK case 425: YY_RULE_SETUP #line 2895 "wcsbth.l" { errmsg = "a string value was expected"; BEGIN(ERROR); } YY_BREAK 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 2900 "wcsbth.l" { if (ipass == 1) { // Do first-pass bookkeeping. wcsbth_pass1(keytype, i, j, n, k, a, ptype, &alts); BEGIN(FLUSH); } 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))) { 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); } 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); } } } if (ipass == npass) { if (gotone) { nvalid++; if (ctrl == 4) { wcsfprintf(stderr, "%.80s\n Accepted (%d) as a valid WCS keyrecord.\n", keyrec, nvalid); } BEGIN(FLUSH); } else { errmsg = "syntactically valid WCS keyrecord has no effect"; BEGIN(ERROR); } } else { BEGIN(FLUSH); } } else { BEGIN(FLUSH); } } YY_BREAK 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 3017 "wcsbth.l" { errmsg = "invalid keyvalue"; BEGIN(ERROR); } YY_BREAK 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 3022 "wcsbth.l" { errmsg = "invalid keyvalue"; BEGIN(ERROR); } YY_BREAK 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 3027 "wcsbth.l" { errmsg = "invalid keyvalue or malformed keycomment"; BEGIN(ERROR); } YY_BREAK 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 3032 "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 3037 "wcsbth.l" { if (ipass == npass) { if (ctrl < 0) { // Preserve discards. keep = keyrec; } else if (2 < ctrl) { nother++; wcsfprintf(stderr, "%.80s\n Not a recognized WCS keyword.\n", keyrec); } } BEGIN(FLUSH); } YY_BREAK 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 3052 "wcsbth.l" { if (ipass == npass) { (*nreject)++; if (ctrl%10 == -1) { keep = keyrec; } if (1 < abs(ctrl%10)) { wcsfprintf(stderr, "%.80s\n Rejected (%d), %s.\n", keyrec, *nreject, errmsg); } } BEGIN(FLUSH); } YY_BREAK case 433: /* rule 433 can match eol */ YY_RULE_SETUP #line 3068 "wcsbth.l" { 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 = ' '; chekval = 0x0; special = 0x0; BEGIN(INITIAL); } YY_BREAK case YY_STATE_EOF(INITIAL): case YY_STATE_EOF(CCCCCia): case YY_STATE_EOF(iCCCna): case YY_STATE_EOF(iCCCCn): case YY_STATE_EOF(TCCCna): case YY_STATE_EOF(TCCCCn): case YY_STATE_EOF(CCi_ja): case YY_STATE_EOF(ijCCna): case YY_STATE_EOF(TCn_ka): case YY_STATE_EOF(TCCn_ka): case YY_STATE_EOF(CROTAi): case YY_STATE_EOF(iCROTn): case YY_STATE_EOF(TCROTn): case YY_STATE_EOF(CCi_ma): case YY_STATE_EOF(iCn_ma): case YY_STATE_EOF(iCCn_ma): case YY_STATE_EOF(TCn_ma): case YY_STATE_EOF(TCCn_ma): case YY_STATE_EOF(PROJPm): case YY_STATE_EOF(CCCCCCCC): case YY_STATE_EOF(CCCCCCCa): case YY_STATE_EOF(CCCCna): case YY_STATE_EOF(CCCCCna): case YY_STATE_EOF(CCCCn): 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 3100 "wcsbth.l" { // End-of-input. if (ipass == 1) { int status; if ((status = wcsbth_init1(&alts, naux, nwcs, wcs)) || (*nwcs == 0 && ctrl == 0)) { return status; } if (2 < abs(ctrl%10)) { if (*nwcs == 1) { if (strcmp(wcs[0]->wcsname, "DEFAULTS") != 0) { wcsfprintf(stderr, "Found one coordinate representation.\n"); } } else { wcsfprintf(stderr, "Found %d coordinate representations.\n", *nwcs); } } if (alts.imgherit) npass = 3; } if (ipass++ < npass) { yyextra->hdr = header; yyextra->nkeyrec = nkeyrec; keyrec = header; *nreject = 0; imherit = 1; i = j = 0; k = n = 0; m = 0; a = ' '; keytype = 0; valtype = -1; vptr = 0x0; altlin = 0; ptype = ' '; chekval = 0x0; special = 0x0; yyrestart(yyin, yyscanner); } else { if (ctrl < 0) { *hptr = '\0'; } else if (ctrl == 1) { 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 434: YY_RULE_SETUP #line 3168 "wcsbth.l" ECHO; YY_BREAK #line 30467 "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 - yyg->yytext_ptr) - 1; /* Undo the effects of YY_DO_BEFORE_ACTION. */ *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 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. */ 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; } /* Note that here we test for yy_c_buf_p "<=" to the position * of the first EOB in the buffer, since yy_c_buf_p will * already have been incremented past the NUL character * (since all states make transitions on EOB to the * end-of-buffer state). Contrast this with the test * in input(). */ 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; yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state( yyscanner ); /* Okay, we're now positioned to make the NUL * transition. We couldn't have * yy_get_previous_state() go ahead and do it * for us because it doesn't know how to deal * with the possibility of jamming (and we don't * want to build jamming into it because then it * will run more slowly). */ yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner); yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; if ( yy_next_state ) { /* Consume the NUL. */ yy_cp = ++yyg->yy_c_buf_p; yy_current_state = yy_next_state; goto yy_match; } else { yy_cp = yyg->yy_c_buf_p; goto yy_find_action; } } else switch ( yy_get_next_buffer( yyscanner ) ) { case EOB_ACT_END_OF_FILE: { yyg->yy_did_buffer_switch_on_eof = 0; if ( yywrap( yyscanner ) ) { /* Note: because we've taken care in * yy_get_next_buffer() to have 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. */ yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ; yy_act = YY_STATE_EOF(YY_START); goto do_action; } else { if ( ! yyg->yy_did_buffer_switch_on_eof ) YY_NEW_FILE; } break; } case EOB_ACT_CONTINUE_SCAN: yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state( yyscanner ); yy_cp = yyg->yy_c_buf_p; yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; goto yy_match; case EOB_ACT_LAST_MATCH: yyg->yy_c_buf_p = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars]; yy_current_state = yy_get_previous_state( yyscanner ); yy_cp = yyg->yy_c_buf_p; yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; goto yy_find_action; } break; } default: YY_FATAL_ERROR( "fatal flex scanner internal error--no action found" ); } /* end of action switch */ } /* end of scanning one token */ } /* end of user's declarations */ } /* end of yylex */ /* yy_get_next_buffer - try to read in a new buffer * * Returns a code representing an action: * EOB_ACT_LAST_MATCH - * EOB_ACT_CONTINUE_SCAN - continue scanning from current position * EOB_ACT_END_OF_FILE - end of file */ static int yy_get_next_buffer (yyscan_t yyscanner) { 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 ( 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 ( 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. */ return EOB_ACT_END_OF_FILE; } else { /* We matched some text prior to the EOB, first * process it. */ return EOB_ACT_LAST_MATCH; } } /* Try to read more data. */ /* First move last chars to start of buffer. */ number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr - 1); for ( i = 0; i < number_to_move; ++i ) *(dest++) = *(source++); if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) /* don't do the read, it's not guaranteed to return an EOF, * just force an EOF */ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0; else { int num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; while ( num_to_read <= 0 ) { /* Not enough room in the buffer - grow it. */ /* just a shorter name for the current buffer */ YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE; int yy_c_buf_p_offset = (int) (yyg->yy_c_buf_p - b->yy_ch_buf); if ( b->yy_is_our_buffer ) { int new_size = b->yy_buf_size * 2; if ( new_size <= 0 ) b->yy_buf_size += b->yy_buf_size / 8; else b->yy_buf_size *= 2; b->yy_ch_buf = (char *) /* Include room in for 2 EOB chars. */ 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 = NULL; if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "fatal error - scanner input buffer overflow" ); 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; } if ( num_to_read > YY_READ_BUF_SIZE ) num_to_read = YY_READ_BUF_SIZE; /* Read in more data. */ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), yyg->yy_n_chars, num_to_read ); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } if ( yyg->yy_n_chars == 0 ) { if ( number_to_move == YY_MORE_ADJ ) { ret_val = EOB_ACT_END_OF_FILE; yyrestart( yyin , yyscanner); } else { ret_val = EOB_ACT_LAST_MATCH; YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_EOF_PENDING; } } else ret_val = EOB_ACT_CONTINUE_SCAN; 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. */ 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); } 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; 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 (yyscan_t yyscanner) { 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 = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp ) { if ( *yy_cp ) { yy_current_state = yy_nxt[yy_current_state][YY_SC_TO_UI(*yy_cp)]; } else yy_current_state = yy_NUL_trans[yy_current_state]; if ( yy_accept[yy_current_state] ) { yyg->yy_last_accepting_state = yy_current_state; yyg->yy_last_accepting_cpos = yy_cp; } } return yy_current_state; } /* yy_try_NUL_trans - try to make a transition on the NUL character * * synopsis * next_state = yy_try_NUL_trans( current_state ); */ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner) { 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); if ( ! yy_is_jam ) { if ( yy_accept[yy_current_state] ) { 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 static void yyunput (int c, char * yy_bp , yyscan_t yyscanner) { char *yy_cp; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; 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. */ 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]; char *source = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]; while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) *--dest = *--source; yy_cp += (int) (dest - source); yy_bp += (int) (dest - source); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = 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" ); } *--yy_cp = (char) c; 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 (yyscan_t yyscanner) #else static int input (yyscan_t yyscanner) #endif { int c; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; *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 ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) /* This was really a NUL. */ *yyg->yy_c_buf_p = '\0'; else { /* need more input */ int offset = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr); ++yyg->yy_c_buf_p; switch ( yy_get_next_buffer( yyscanner ) ) { case EOB_ACT_LAST_MATCH: /* This happens because yy_g_n_b() * sees that we've accumulated a * token and flags that we need to * try matching the token before * proceeding. But for input(), * there's no matching to consider. * So convert the EOB_ACT_LAST_MATCH * to EOB_ACT_END_OF_FILE. */ /* Reset buffer status. */ yyrestart( yyin , yyscanner); /*FALLTHROUGH*/ case EOB_ACT_END_OF_FILE: { if ( yywrap( yyscanner ) ) return 0; if ( ! yyg->yy_did_buffer_switch_on_eof ) YY_NEW_FILE; #ifdef __cplusplus return yyinput(yyscanner); #else return input(yyscanner); #endif } case EOB_ACT_CONTINUE_SCAN: yyg->yy_c_buf_p = yyg->yytext_ptr + offset; break; } } } 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'); return c; } #endif /* ifndef YY_NO_INPUT */ /** 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 yyrestart (FILE * input_file , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if ( ! YY_CURRENT_BUFFER ){ yyensure_buffer_stack (yyscanner); YY_CURRENT_BUFFER_LVALUE = yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); } 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 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 * yypop_buffer_state(); * yypush_buffer_state(new_buffer); */ yyensure_buffer_stack (yyscanner); if ( YY_CURRENT_BUFFER == new_buffer ) return; if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ *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; yy_load_buffer_state( yyscanner ); /* We don't actually know whether we did this switch during * 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. */ yyg->yy_did_buffer_switch_on_eof = 1; } static void yy_load_buffer_state (yyscan_t yyscanner) { 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 yy_create_buffer (FILE * file, int size , yyscan_t yyscanner) { YY_BUFFER_STATE b; b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); if ( ! b ) 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 *) yyalloc( (yy_size_t) (b->yy_buf_size + 2) , yyscanner ); if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); b->yy_is_our_buffer = 1; yy_init_buffer( b, file , yyscanner); return b; } /** Destroy the buffer. * @param b a buffer created with yy_create_buffer() * @param yyscanner The scanner object. */ void yy_delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if ( ! b ) return; if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; if ( b->yy_is_our_buffer ) yyfree( (void *) b->yy_ch_buf , yyscanner ); 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 yyrestart() or at EOF. */ static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner) { int oerrno = errno; 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 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){ b->yy_bs_lineno = 1; b->yy_bs_column = 0; } b->yy_is_interactive = 0; errno = oerrno; } /** 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 yy_flush_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if ( ! b ) return; b->yy_n_chars = 0; /* We always need two end-of-buffer characters. The first causes * a transition to the end-of-buffer state. The second causes * a jam in that state. */ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; b->yy_buf_pos = &b->yy_ch_buf[0]; b->yy_at_bol = 1; b->yy_buffer_status = YY_BUFFER_NEW; if ( b == YY_CURRENT_BUFFER ) 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 yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (new_buffer == NULL) return; yyensure_buffer_stack(yyscanner); /* This block is copied from yy_switch_to_buffer. */ if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ *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) yyg->yy_buffer_stack_top++; YY_CURRENT_BUFFER_LVALUE = new_buffer; /* 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 yypop_buffer_state (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (!YY_CURRENT_BUFFER) return; yy_delete_buffer(YY_CURRENT_BUFFER , yyscanner); YY_CURRENT_BUFFER_LVALUE = NULL; if (yyg->yy_buffer_stack_top > 0) --yyg->yy_buffer_stack_top; if (YY_CURRENT_BUFFER) { 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 yyensure_buffer_stack (yyscan_t yyscanner) { 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; /* 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*) , 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 (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){ /* Increase the buffer to prepare for a possible push. */ yy_size_t grow_size = 8 /* arbitrary grow size */; 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*) , yyscanner); if ( ! yyg->yy_buffer_stack ) YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); /* zero only the new slots.*/ 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 * @param yyscanner The scanner object. * @return the newly allocated buffer state object. */ YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size , yyscan_t yyscanner) { YY_BUFFER_STATE b; if ( size < 2 || 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 NULL; b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); 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 = 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; yy_switch_to_buffer( b , yyscanner ); return b; } /** 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 * yy_scan_bytes() instead. */ YY_BUFFER_STATE yy_scan_string (const char * yystr , yyscan_t yyscanner) { return yy_scan_bytes( yystr, (int) strlen(yystr) , yyscanner); } /** 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 yy_scan_bytes (const char * yybytes, int _yybytes_len , yyscan_t yyscanner) { YY_BUFFER_STATE b; char *buf; yy_size_t n; int i; /* Get memory for full buffer, including space for trailing EOB's. */ n = (yy_size_t) (_yybytes_len + 2); buf = (char *) yyalloc( n , yyscanner ); if ( ! buf ) 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 = yy_scan_buffer( buf, n , yyscanner); if ( ! b ) 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. */ b->yy_is_our_buffer = 1; return b; } #ifndef YY_EXIT_FAILURE #define YY_EXIT_FAILURE 2 #endif static void yynoreturn yy_fatal_error (const char* msg , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; (void)yyg; fprintf( stderr, "%s\n", msg ); exit( YY_EXIT_FAILURE ); } /* Redefine yyless() so it works in section 3 code. */ #undef yyless #define yyless(n) \ do \ { \ /* Undo effects of setting up yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(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 yyget_column (yyscan_t yyscanner) { 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 *yyget_in (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyin; } /** Get the output stream. * @param yyscanner The scanner object. */ FILE *yyget_out (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyout; } /** Get the length of the current token. * @param yyscanner The scanner object. */ int yyget_leng (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyleng; } /** Get the current token. * @param yyscanner The scanner object. */ 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) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yyextra = user_defined ; } /** Set the current 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 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. * @param yyscanner The scanner object. * @see yy_switch_to_buffer */ 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) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yy_flex_debug; } void yyset_debug (int _bdebug , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yy_flex_debug = _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) { 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 ); } /* 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 ) { 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. */ 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 yyin = stdin; yyout = stdout; #else yyin = NULL; yyout = NULL; #endif /* For future reference: Set errno on error, since we are called by * yylex_init() */ return 0; } /* 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){ yy_delete_buffer( YY_CURRENT_BUFFER , yyscanner ); YY_CURRENT_BUFFER_LVALUE = NULL; yypop_buffer_state(yyscanner); } /* Destroy the stack itself. */ 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 * yylex() is called, initialization will occur. */ yy_init_globals( yyscanner); /* Destroy the main struct (reentrant only). */ yyfree ( yyscanner , yyscanner ); yyscanner = NULL; return 0; } /* * Internal utility routines. */ #ifndef yytext_ptr static void yy_flex_strncpy (char* s1, const char * s2, int n , yyscan_t yyscanner) { 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 (const char * s , yyscan_t yyscanner) { int n; for ( n = 0; s[n]; ++n ) ; return n; } #endif void *yyalloc (yy_size_t size , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; (void)yyg; return malloc(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 * because both ANSI C and C++ allow castless assignment from * any pointer type to void*, and deal with argument conversions * as though doing an assignment. */ return realloc(ptr, size); } void yyfree (void * ptr , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; (void)yyg; free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ } #define YYTABLES_NAME "yytables" #line 3168 "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: * * 1) Count the number of coordinate axes in each of the 27 possible alternate * image-header coordinate representations. Also count the number of PVi_ma * and PSi_ma keywords in each representation. * * 2) Determine the number of binary table columns that have an image array * with a coordinate representation (up to 999), and count the number of * coordinate axes in each of the 27 possible alternates. Also count the * number of iVn_ma and iSn_ma keywords in each representation. * * 3) Determine the number of alternate pixel list coordinate representations * (up to 27) and the table columns associated with each. Also count the * number of TVn_ma and TSn_ma keywords in each representation. * * In the first pass alts->arridx[icol][27] is used to determine the number of * axes in each of 27 possible image-header coordinate descriptions (icol == 0) * and each of the 27 possible coordinate representations for an image array in * each column. * * The elements of alts->pixlist[icol] are used as bit arrays to flag which of * the 27 possible pixel list coordinate representations are associated with * each table column. *---------------------------------------------------------------------------*/ int wcsbth_pass1( int keytype, int i, int j, int n, int k, char a, char ptype, struct wcsbth_alts *alts) { if (a == 0) { // Keywords such as DATE-OBS go along for the ride. return 0; } int ncol = alts->ncol; // 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. if (n || k) { // The header is mangled, assume the worst. ncol = 999; } } if (!(alts->arridx = calloc((1 + ncol)*27, sizeof(short int))) || !(alts->npv = calloc((1 + ncol)*27, sizeof(unsigned char))) || !(alts->nps = calloc((1 + ncol)*27, sizeof(unsigned char))) || !(alts->pixlist = calloc((1 + ncol), sizeof(unsigned int)))) { if (alts->arridx) free(alts->arridx); if (alts->npv) free(alts->npv); if (alts->nps) free(alts->nps); if (alts->pixlist) free(alts->pixlist); 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. ncol = 999; if (!(alts->arridx = realloc(alts->arridx, 27*(1 + ncol)*sizeof(short int))) || !(alts->npv = realloc(alts->npv, 27*(1 + ncol)*sizeof(unsigned char))) || !(alts->nps = realloc(alts->nps, 27*(1 + ncol)*sizeof(unsigned char))) || !(alts->pixlist = realloc(alts->pixlist, (1 + ncol)*sizeof(unsigned int)))) { if (alts->arridx) free(alts->arridx); if (alts->npv) free(alts->npv); if (alts->nps) free(alts->nps); if (alts->pixlist) free(alts->pixlist); return WCSHDRERR_MEMORY; } // 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; alts->pixlist[icol] = 0; } } alts->ncol = ncol; } 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. if (keytype & IMGHEAD || keytype & BIMGARR) { // 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. alts->arridx[n][ialt] = -1; } } else { // Record the maximum axis number found. if (alts->arridx[n][ialt] < i) { alts->arridx[n][ialt] = i; } if (alts->arridx[n][ialt] < j) { alts->arridx[n][ialt] = j; } } if (ptype == 'v') { alts->npv[n][ialt]++; } else if (ptype == 's') { alts->nps[n][ialt]++; } } // 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) { int mask = 1 << ialt; // n > 0 for PIXLIST keytypes. alts->pixlist[n] |= mask; if (k) alts->pixlist[k] |= mask; // Used as a flag over all columns. alts->pixlist[0] |= mask; if (ptype == 'v') { alts->pixnpv[ialt]++; } else if (ptype == 's') { alts->pixnps[ialt]++; } } return 0; } /*---------------------------------------------------------------------------- * Perform initializations at the end of the first pass: * * 1) Determine the required number of wcsprm structs, allocate memory for * an array of them and initialize each one. *---------------------------------------------------------------------------*/ int wcsbth_init1( struct wcsbth_alts *alts, int naux, int *nwcs, struct wcsprm **wcs) { int status = 0; if (alts->arridx == 0x0) { *nwcs = 0; return 0; } // 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 (int icol = 1; icol <= ncol; icol++) { if (alts->pixlist[icol] & mask) { alts->pixidx[ialt]++; } } } } // Find the total number of coordinate representations. *nwcs = 0; alts->imgherit = 0; int inherit[27]; for (int ialt = 0; ialt < 27; ialt++) { inherit[ialt] = 0; for (int icol = 1; icol <= ncol; icol++) { if (alts->arridx[icol][ialt] < 0) { // 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. alts->arridx[icol][ialt] = alts->arridx[0][ialt]; } else { alts->arridx[icol][ialt] = 0; } } if (alts->arridx[icol][ialt]) { if (alts->arridx[0][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. alts->arridx[icol][ialt] = alts->arridx[0][ialt]; } } (*nwcs)++; } } // 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. alts->arridx[0][ialt] = 0; alts->imgherit = 1; } else if (alts->arridx[0][ialt] > 0) { (*nwcs)++; } } // We need a struct for every "a" found in a PIXLIST keyword. if (alts->pixidx[ialt]) { (*nwcs)++; } } if (*nwcs) { // Allocate memory for the required number of wcsprm structs. if (!(*wcs = calloc(*nwcs, sizeof(struct wcsprm)))) { return WCSHDRERR_MEMORY; } // Initialize each wcsprm struct. struct wcsprm *wcsp = *wcs; *nwcs = 0; 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; 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. if (ialt) { wcsp->alt[0] = 'A' + ialt - 1; } // 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. alts->arridx[icol][ialt] = (*nwcs)++; wcsp++; } else { // Signal that this column has no WCS for this "a". alts->arridx[icol][ialt] = -1; } } } for (int ialt = 0; ialt < 27; ialt++) { if (alts->pixidx[ialt]) { // Pixel lists representations. wcsp->flag = -1; 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. if (ialt) { wcsp->alt[0] = 'A' + ialt - 1; } // 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[ialt] = (*nwcs)++; wcsp++; } else { // Signal that this column is not a pixel list axis for this "a". alts->pixidx[ialt] = -1; } } } return status; } /*---------------------------------------------------------------------------- * Return a pointer to the next wcsprm struct for a particular column number * and alternate. *---------------------------------------------------------------------------*/ struct wcsprm *wcsbth_idx( struct wcsprm *wcs, struct wcsbth_alts *alts, int keytype, int n, char a) { const char as[] = " ABCDEFGHIJKLMNOPQRSTUVWXYZ"; if (!wcs) return 0x0; 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. 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. 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. if (iwcs >= 0) break; // Start from scratch for the next alts->ialt. alts->icol = 0; } if (keytype & (IMGAUX | PIXLIST)) { iwcs = alts->pixidx[alts->ialt]; } } return (iwcs >= 0) ? (wcs + iwcs) : 0x0; } /*---------------------------------------------------------------------------- * Return the axis number associated with the specified column number in a * particular pixel list coordinate representation. *---------------------------------------------------------------------------*/ int wcsbth_colax( struct wcsprm *wcs, struct wcsbth_alts *alts, int n, char a) { if (!wcs) return 0; struct wcsprm *wcsp = wcs; if (a != ' ') { wcsp += alts->pixidx[a-'A'+1]; } for (int ix = 0; ix < wcsp->naxis; ix++) { if (wcsp->colax[ix] == n) { return ++ix; } } return 0; } /*---------------------------------------------------------------------------- * Interpret the JDREF, JDREFI, and JDREFF keywords. *---------------------------------------------------------------------------*/ 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) { // Set the fractional part of MJDREF from JDREFF. if (undefined(mjdref[1])) { mjdref[1] = *jdreff; } return 0; } /*---------------------------------------------------------------------------- * Interpret EPOCHa keywords. *---------------------------------------------------------------------------*/ int wcsbth_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 wcsbth_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 wcsbth_timepixr(double timepixr) { return (timepixr < 0.0 || 1.0 < timepixr); } /*---------------------------------------------------------------------------- * Tie up loose ends. *---------------------------------------------------------------------------*/ int wcsbth_final( struct wcsbth_alts *alts, int *nwcs, struct wcsprm **wcs) { 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 (int ialt = 0; ialt < *nwcs; ialt++) { // Interpret -TAB header keywords. int status; if ((status = wcstab(*wcs+ialt))) { wcsvfree(nwcs, wcs); return status; } } return 0; } astropy-astropy-201cddb/cextern/wcslib/C/flexed/wcspih.c000066400000000000000000043154601507226315300234410ustar00rootroot00000000000000#line 2 "wcspih.c" #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 #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 #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 #endif #ifdef yyrestart #define wcspihrestart_ALREADY_DEFINED #else #define yyrestart wcspihrestart #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 #endif #ifdef yyfree #define wcspihfree_ALREADY_DEFINED #else #define yyfree wcspihfree #endif /* First, we deal with platform-specific or compiler-specific issues. */ /* begin standard C headers. */ #include #include #include #include /* end standard C headers. */ /* flex integer type definitions */ #ifndef FLEXINT_H #define FLEXINT_H /* C99 systems have . Non-C99 systems may or may not. */ #if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, * if you want the limit (max/min) macros for int types. */ #ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS 1 #endif #include typedef int8_t flex_int8_t; typedef uint8_t flex_uint8_t; typedef int16_t flex_int16_t; typedef uint16_t flex_uint16_t; typedef int32_t flex_int32_t; typedef uint32_t flex_uint32_t; #else typedef signed char flex_int8_t; typedef short int flex_int16_t; typedef int flex_int32_t; typedef unsigned char flex_uint8_t; typedef unsigned short int flex_uint16_t; typedef unsigned int flex_uint32_t; /* Limits of integral types. */ #ifndef INT8_MIN #define INT8_MIN (-128) #endif #ifndef INT16_MIN #define INT16_MIN (-32767-1) #endif #ifndef INT32_MIN #define INT32_MIN (-2147483647-1) #endif #ifndef INT8_MAX #define INT8_MAX (127) #endif #ifndef INT16_MAX #define INT16_MAX (32767) #endif #ifndef INT32_MAX #define INT32_MAX (2147483647) #endif #ifndef UINT8_MAX #define UINT8_MAX (255U) #endif #ifndef UINT16_MAX #define UINT16_MAX (65535U) #endif #ifndef UINT32_MAX #define UINT32_MAX (4294967295U) #endif #ifndef SIZE_MAX #define SIZE_MAX (~(size_t)0) #endif #endif /* ! C99 */ #endif /* ! FLEXINT_H */ /* begin standard C++ headers. */ /* TODO: this is always defined, so inline it */ #define yyconst const #if defined(__GNUC__) && __GNUC__ >= 3 #define yynoreturn __attribute__((__noreturn__)) #else #define yynoreturn #endif /* Returned upon end-of-file. */ #define YY_NULL 0 /* 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) ((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 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 ((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 yyrestart( yyin , yyscanner ) #define YY_END_OF_BUFFER_CHAR 0 /* Size of default input buffer. */ #ifndef YY_BUF_SIZE #ifdef __ia64__ /* On IA-64, the buffer size is 16k, not 8k. * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. * Ditto for the __ia64__ case accordingly. */ #define YY_BUF_SIZE 32768 #else #define YY_BUF_SIZE 16384 #endif /* __ia64__ */ #endif /* The state buf must be large enough to hold one state per character in the main buffer. */ #define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) #ifndef YY_TYPEDEF_YY_BUFFER_STATE #define YY_TYPEDEF_YY_BUFFER_STATE typedef struct yy_buffer_state *YY_BUFFER_STATE; #endif #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 yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ *yy_cp = yyg->yy_hold_char; \ YY_RESTORE_YY_MORE_OFFSET \ 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, yyg->yytext_ptr , yyscanner ) #ifndef YY_STRUCT_YY_BUFFER_STATE #define YY_STRUCT_YY_BUFFER_STATE struct yy_buffer_state { FILE *yy_input_file; char *yy_ch_buf; /* input buffer */ char *yy_buf_pos; /* current position in input buffer */ /* Size of input buffer in bytes, not including room for EOB * characters. */ int yy_buf_size; /* Number of characters read into yy_ch_buf, not including EOB * characters. */ int yy_n_chars; /* Whether we "own" the buffer - i.e., we know we created it, * and can realloc() it to grow it, and should free() it to * delete it. */ int yy_is_our_buffer; /* Whether this is an "interactive" input source; if so, and * if we're using stdio for input, then we want to use getc() * instead of fread(), to make sure we stop fetching input after * each newline. */ int yy_is_interactive; /* Whether we're considered to be at the beginning of a line. * If so, '^' rules will be active on the next match, otherwise * not. */ int yy_at_bol; 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. */ int yy_fill_buffer; int yy_buffer_status; #define YY_BUFFER_NEW 0 #define YY_BUFFER_NORMAL 1 /* When an EOF's been seen but there's still some text to process * then we mark the buffer as YY_EOF_PENDING, to indicate that we * shouldn't try reading from the input source any more. We might * still have a bunch of tokens to match, though, because of * possible backing-up. * * When we actually see the EOF, we change the status to "new" * (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 */ /* 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 ( 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 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 ){ \ yyensure_buffer_stack (yyscanner); \ YY_CURRENT_BUFFER_LVALUE = \ 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 ){\ yyensure_buffer_stack (yyscanner); \ YY_CURRENT_BUFFER_LVALUE = \ 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(yyscanner) (/*CONSTCOND*/1) #define YY_SKIP_YYWRAP typedef flex_uint8_t YY_CHAR; typedef int yy_state_type; #define yytext_ptr yytext_r static const flex_int16_t yy_nxt[][128] = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 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, 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, 57, 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 }, { 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 }, { 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 }, { 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, 81, 81, 81, 81, 81, 81, 81, 81 }, { 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, 81, 81, 81, 81, 81, 81, 81, 81 }, { 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, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84 }, { 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, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84 }, { 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 }, { 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 }, { 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, 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, 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, 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, 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, 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, 93, 93, 93, 93, 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, 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, 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, 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, 97, 97, 97, 97, 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, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 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, 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, 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, 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, 107, 107, 107, 107, 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 }, { 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 }, { 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 }, { 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 }, { 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, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 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, 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, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 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, 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, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -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, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57 }, { 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, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -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 }, { 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, -59, -59, -59, -59, -59, -59, -59, -59, -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 }, { 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, -60, -60, -60, -60, -60, -60, -60, -60 }, { 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, 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, -61, -61, -61, -61, -61, -61, -61, -61 }, { 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, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -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, -62, -62, -62, -62, -62, -62, -62, -62 }, { 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, -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, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63 }, { 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, -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, -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 }, { 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, 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, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65 }, { 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, -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, -66, -66, -66, -66, -66, -66, -66, -66 }, { 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, -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, -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 }, { 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, 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, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68 }, { 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, -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, -69, -69, -69, -69, -69, -69, -69, -69 }, { 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, -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, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70 }, { 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, -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, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71 }, { 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, 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, -72, -72, -72, -72, -72, -72, -72, -72 }, { 55, -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, -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, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73 }, { 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, -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, -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, -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, -75, -75, -75, -75, -75, -75, -75, -75 }, { 55, -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, -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, -76, -76, -76, -76, -76, -76, -76, -76 }, { 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, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -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, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -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, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -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 }, { 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, -80, -80, -80, -80, -80, -80, -80, -80 }, { 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, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81 }, { 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, 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 }, { 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, -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, -83, -83, -83, -83, -83, -83, -83, -83 }, { 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, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84 }, { 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, 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 }, { 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, 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, -86, -86, -86, -86, -86, -86, -86, -86 }, { 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, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -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, -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, -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 }, { 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, 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 }, { 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, -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, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90 }, { 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, -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, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91 }, { 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, -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 }, { 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, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -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, -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, 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 }, { 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, -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, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95 }, { 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, 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, -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 }, { 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, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -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, -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, -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 }, { 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, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -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, -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, -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, -100, -100, -100, -100, -100, -100, -100, -100 }, { 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, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -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, -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, -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 }, { 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, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -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, -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, -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 }, { 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, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -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, -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, -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 }, { 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, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -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, -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, 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, -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 }, { 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, -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 }, { 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, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -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, -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, 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, -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 }, { 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, -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 }, { 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, 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 }, { 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, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -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, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, 236, -115, 237, 237, 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, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115 }, { 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, 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, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116 }, { 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, 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, 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 }, { 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, -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, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 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, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 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, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120 }, { 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, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 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, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122 }, { 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, 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, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, -123, -123, -123, -123, -123 }, { 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, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -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, -125, -125, -125, -125, -125, -125, -125, -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, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125 }, { 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, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -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, -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, 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, -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 }, { 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, -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, -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 }, { 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, -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 }, { 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 }, { 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 }, { 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 }, { 55, 258, 258, 258, 258, 258, 258, 258, 258, 258, 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, 258, 258, 258, 258, 258, 258, 258, 258, 258, 258, 258, 258, 258, 258, 258, 258, 258, 258 }, { 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, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262 }, { 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, 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 }, { 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, -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, -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 }, { 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, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268 }, { 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, -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, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138 }, { 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 }, { 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, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140 }, { 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, 272, -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 }, { 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, -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, 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 }, { 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, 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, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143 }, { 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, -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, -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 }, { 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, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -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, -145, -145, -145, -145, -145, -145, -145, -145 }, { 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, 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, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146 }, { 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, -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 }, { 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, 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, -148, -148, -148, -148, -148, -148, -148, -148 }, { 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, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149 }, { 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, -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, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150 }, { 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, -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 }, { 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, -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 }, { 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, 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, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153 }, { 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, -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, -154, -154, -154, -154, -154, -154, -154, -154 }, { 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, -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, 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, -155, -155, -155, -155, -155, -155, -155, -155 }, { 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, -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, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156 }, { 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, -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, 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, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157 }, { 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, -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, -158, -158, -158, -158, -158, -158, -158, -158 }, { 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, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -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, -159, -159, -159, -159, -159, -159, -159, -159 }, { 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, -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 }, { 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, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161 }, { 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, -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, -162, -162, -162, -162, -162, -162, -162, -162 }, { 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, 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, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163 }, { 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, -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 }, { 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, -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, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165 }, { 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, -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, 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, -166, -166, -166, -166, -166, -166, -166, -166 }, { 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, -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 }, { 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, 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, -168, -168, -168, -168, -168, -168, -168, -168 }, { 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, -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, -169, -169, -169, -169, -169, -169, -169, -169 }, { 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, -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, 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, -170, -170, -170, -170, -170, -170, -170, -170 }, { 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, -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, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171 }, { 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, -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 }, { 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, -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, 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, -173, -173, -173, -173, -173, -173, -173, -173 }, { 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, -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, -174, -174, -174, -174, -174, -174, -174, -174 }, { 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, -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, -175, -175, -175, -175, -175, -175, -175, -175, -175 }, { 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, -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, -176, -176, -176, -176, -176, -176, -176, -176 }, { 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, 320, -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 }, { 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, -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, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178 }, { 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, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179 }, { 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, -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, -180, -180, -180, -180, -180, -180 }, { 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, 322, -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 }, { 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, -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, 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, -182, -182, -182, -182, -182, -182, -182, -182 }, { 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, -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, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183 }, { 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, -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 }, { 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, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -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, -185, -185, -185, -185, -185, -185, -185, -185 }, { 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, -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, -186, -186, -186, -186, 327, -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, -186, -186, -186, -186, -186, -186, -186, -186, -186 }, { 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, -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, -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 }, { 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, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188 }, { 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, -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, -189, -189, -189, -189, -189, -189, -189, -189 }, { 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, -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, -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 }, { 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, -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, 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, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191 }, { 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, -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, 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, -192, -192, -192, -192, -192, -192, -192, -192 }, { 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, -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, 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, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193 }, { 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, 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, -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 }, { 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, -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, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195 }, { 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, -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, 338, -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 }, { 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, -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, -197, -197, -197 }, { 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, 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 }, { 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, 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 }, { 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, -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 }, { 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, 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 }, { 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, -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 }, { 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, 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, -203, -203, -203, -203, -203, -203, -203, -203 }, { 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, -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 }, { 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, -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 }, { 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, 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, -206, -206, -206, -206, -206, -206, -206, -206 }, { 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, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207 }, { 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, -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 }, { 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, 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 }, { 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, -210, -210, -210, -210, -210, -210, -210, -210 }, { 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, -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 }, { 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 }, { 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, -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, -213, -213, -213, -213, -213, -213, -213, -213 }, { 55, -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, 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, 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 }, { 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, -215, -215, -215, 369, -215, -215, 373, 373, 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 }, { 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, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216 }, { 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, 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, 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 }, { 55, -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, 378, 379, 379, 379, 379, 379, 379, 379, 379, 379, -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 }, { 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, 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 }, { 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, -220, -220, -220, -220, -220, -220, -220, -220 }, { 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, -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 }, { 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, 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, -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 }, { 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, 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, -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 }, { 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, -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 }, { 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, -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 }, { 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, 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, -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, -226, -226, -226, -226, -226, -226 }, { 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, -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, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227 }, { 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, -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, -228, -228, -228, -228, -228, -228, -228, -228, -228 }, { 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, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229 }, { 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, -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 }, { 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, 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, -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 }, { 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 }, { 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, -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, -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 }, { 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, -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 }, { 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, -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 }, { 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, -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 }, { 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, 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 }, { 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, -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 }, { 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, -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 }, { 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, -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 }, { 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, -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 }, { 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, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 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, -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, -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 }, { 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, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 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, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245 }, { 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, 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 }, { 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, -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 }, { 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, -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 }, { 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, 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 }, { 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, 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 }, { 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, -251, -251, -251, -251, -251, -251, -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 }, { 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, 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 }, { 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, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253 }, { 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 }, { 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, -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 }, { 55, 400, 400, 400, 400, 400, 400, 400, 400, 400, 255, 400, 400, 400, 400, 400, 400, 400, 400, 400, 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, 400, 400, 400, 400, 400, 400, 400, 400 }, { 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 }, { 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 }, { 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, -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, -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, -259, -259 }, { 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, 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, 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, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 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, 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 }, { 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, -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 }, { 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, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 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, 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 }, { 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, 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 }, { 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, -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, -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 }, { 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, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268 }, { 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, -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, -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 }, { 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 }, { 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, -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 }, { 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, -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, 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, -272, -272, -272, -272, -272, -272, -272, -272 }, { 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, -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, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273 }, { 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, -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, -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 }, { 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, -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, -275, -275, -275, -275, -275, -275, -275, -275 }, { 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, -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 }, { 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, -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, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277 }, { 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, -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, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278 }, { 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, -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, 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, -279, -279, -279, -279, -279, -279, -279, -279 }, { 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, -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 }, { 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, -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, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281 }, { 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, -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, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282 }, { 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, -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, -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, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283 }, { 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, -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 }, { 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, -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, -285, -285, -285, -285, -285, -285, -285, -285 }, { 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, -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, 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, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286 }, { 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, -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 }, { 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, -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, 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, -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 }, { 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, -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, -289, -289, -289, -289, -289, -289, -289, -289 }, { 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, 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, -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 }, { 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, -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, 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, -291, -291, -291, -291, -291, -291, -291, -291 }, { 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, -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, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292 }, { 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, -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, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293 }, { 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, -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, 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, -294, -294, -294, -294, -294, -294, -294, -294 }, { 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, -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, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295 }, { 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, 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, -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 }, { 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, -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 }, { 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, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -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, -298, -298, -298, -298, -298, -298, -298, -298 }, { 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, -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, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299 }, { 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, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300 }, { 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, -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 }, { 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, 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, -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 }, { 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, -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, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303 }, { 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, -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, 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, -304 }, { 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, -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 }, { 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, 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, -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 }, { 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, -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 }, { 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, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -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, -308, -308, -308, -308, -308, -308, -308, -308 }, { 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, -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, -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, -309, -309, -309, -309, -309, -309, -309, -309 }, { 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, -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 }, { 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, -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, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311 }, { 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, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -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, -312, -312, -312, -312, -312, -312, -312, -312 }, { 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, -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, 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, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313 }, { 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, 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, -314, -314, -314, -314, -314, -314, -314, -314 }, { 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, -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, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315 }, { 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, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -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, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316 }, { 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, -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, 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, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317 }, { 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, -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, 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, -318, -318, -318, -318, -318, -318, -318, -318 }, { 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, 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, -319, -319, -319, -319, -319, -319, -319, -319 }, { 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, -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, -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 }, { 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, -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, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321 }, { 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, 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, -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 }, { 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, -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, 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, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323 }, { 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, -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 }, { 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, -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, -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 }, { 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, -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, -326, -326, -326, -326, -326, -326, -326, -326 }, { 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, -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, -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 }, { 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, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328 }, { 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, -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, 479, -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, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329 }, { 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, -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, -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, 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, -331, -331, -331, -331, -331, -331, -331, -331 }, { 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, -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, -332, -332, -332, -332, -332, -332, -332, -332 }, { 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, 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, -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 }, { 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, -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, -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, -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, -335, -335, -335, -335, -335, -335, -335, -335 }, { 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, -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, 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, -336, -336, -336, -336, -336, -336, -336, -336 }, { 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, -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, -337, 491, -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 }, { 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, -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, 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, -338, -338, -338, -338, -338, -338, -338, -338 }, { 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, 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, -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 }, { 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, -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 }, { 55, -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, 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 }, { 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 }, { 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, 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, -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, -343, -343, -343, -343 }, { 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, -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 }, { 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, -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, -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 }, { 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, 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, -346, -346, -346, -346, -346, -346, -346, -346 }, { 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, 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 }, { 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, -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 }, { 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, 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 }, { 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, 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, -350, -350, -350, -350, -350, -350, -350, -350 }, { 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, -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 }, { 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, 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 }, { 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, 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, -353, -353, -353, -353, -353, -353, -353, -353 }, { 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, -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 }, { 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 }, { 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, 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, -356, -356, -356, -356, -356, -356, -356, -356 }, { 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, 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 }, { 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, -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 }, { 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, 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 }, { 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, -360, -360, -360, -360, -360, -360, -360, -360 }, { 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, -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, -361, -361, -361, -361, -361, -361, -361 }, { 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, -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, -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 }, { 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, -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, -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 }, { 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, -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, -364, -364, -364, -364, -364 }, { 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, -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, -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 }, { 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, -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, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366 }, { 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, -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 }, { 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 }, { 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, -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, -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 }, { 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, 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, -370, -370, -370, -370, -370, -370, -370, -370 }, { 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, 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, 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 }, { 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, -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, -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 }, { 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, -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, -373, -373, -373, -373, -373, -373, -373, -373 }, { 55, -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, 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, -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 }, { 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 }, { 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, -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, -376, -376, -376, -376, -376, -376, -376, -376 }, { 55, -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, 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, -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 }, { 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 }, { 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, 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 }, { 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, -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, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380 }, { 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, -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, -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 }, { 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, -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, -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, -382, -382, -382, -382 }, { 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, -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, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383 }, { 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, -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, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384 }, { 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, -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, -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 }, { 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, 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, -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, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386 }, { 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, 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, -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 }, { 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, 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, -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, -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 }, { 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, 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, -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, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389 }, { 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, -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, 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 }, { 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, -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, -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 }, { 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, -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 }, { 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, 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 }, { 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, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, 396, 396, 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, -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 }, { 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, -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, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396 }, { 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, 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 }, { 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, -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, -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, -398, -398, -398, -398, -398, -398, -398, -398, -398 }, { 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, -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, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399 }, { 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, 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 }, { 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, -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, -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 }, { 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 }, { 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 }, { 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 }, { 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, -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, -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, -406, -406 }, { 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 }, { 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, 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 }, { 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, -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, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410 }, { 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, 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, 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, -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, -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 }, { 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, -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, -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 }, { 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, -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, -415, -415, -415, -415, -415, -415, -415, -415 }, { 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, -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, -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 }, { 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, -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, -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 }, { 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, -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, -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 }, { 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, -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 }, { 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, -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, -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 }, { 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, -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 }, { 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, -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 }, { 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, -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, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423 }, { 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, -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, -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 }, { 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, -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, -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 }, { 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, -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, -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 }, { 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, -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, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427 }, { 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, -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, -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 }, { 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, -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, -429, -429, -429, -429, -429, -429, -429, -429 }, { 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, -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, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430 }, { 55, -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, -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, -431, -431, -431, -431, -431, -431, -431, -431 }, { 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, -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, -432, -432, -432, -432, -432, -432, -432, -432 }, { 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, -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, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433 }, { 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, -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, -434, -434, -434, -434, -434, -434, -434, -434 }, { 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, -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 }, { 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, -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, -436, -436, -436, -436, -436, -436, -436, -436 }, { 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, -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, -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 }, { 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, -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, -438, -438, -438, -438, -438, -438, -438, -438 }, { 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, -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, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439 }, { 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, -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, 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, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -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, -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 }, { 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, -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 }, { 55, -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, -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, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443 }, { 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, -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 }, { 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, -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 }, { 55, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, 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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, -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, 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, -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, -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, -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, -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, -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, -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, 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, 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, -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, -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, -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, -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, -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, -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, -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, -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, 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, -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, -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, -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, -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, -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, 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, -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, -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, 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, -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, -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, 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, -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, -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, -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.4 - an implementation of the FITS WCS standard. Copyright (C) 1995-2024, 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/people/Mark.Calabretta $Id: wcspih.c,v 8.4 2024/10/28 13:56:17 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 97: YY_RULE_SETUP #line 1177 "wcspih.l" { valtype = RECORD; distype = SEQUENT; vptr = &(distem.dp); npptr = ndq; keyname = "DQia"; BEGIN(CCia); } YY_BREAK case 98: YY_RULE_SETUP #line 1187 "wcspih.l" { valtype = FLOAT; distype = PRIOR; vptr = &(distem.maxdis); keyname = "CPERRja"; BEGIN(CCCCCia); } YY_BREAK case 99: YY_RULE_SETUP #line 1196 "wcspih.l" { valtype = FLOAT; distype = SEQUENT; vptr = &(distem.maxdis); keyname = "CQERRia"; BEGIN(CCCCCia); } YY_BREAK case 100: YY_RULE_SETUP #line 1205 "wcspih.l" { valtype = FLOAT; distype = PRIOR; vptr = &(distem.totdis); keyname = "DVERRa"; BEGIN(CCCCCCCa); } YY_BREAK case 101: YY_RULE_SETUP #line 1214 "wcspih.l" { // SIP: axis 1 polynomial degree (not stored). valtype = INTEGER; distype = PRIOR; vptr = 0x0; i = 1; a = ' '; keyname = "A_ORDER"; BEGIN(VALUE); } YY_BREAK case 102: YY_RULE_SETUP #line 1227 "wcspih.l" { // SIP: axis 2 polynomial degree (not stored). valtype = INTEGER; distype = PRIOR; vptr = 0x0; i = 2; a = ' '; keyname = "B_ORDER"; BEGIN(VALUE); } YY_BREAK case 103: YY_RULE_SETUP #line 1240 "wcspih.l" { // 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 104: YY_RULE_SETUP #line 1253 "wcspih.l" { // 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 105: YY_RULE_SETUP #line 1266 "wcspih.l" { // SIP: axis 1 maximum distortion. valtype = FLOAT; distype = PRIOR; vptr = &(distem.maxdis); i = 1; a = ' '; keyname = "A_DMAX"; BEGIN(VALUE); } YY_BREAK case 106: YY_RULE_SETUP #line 1279 "wcspih.l" { // SIP: axis 2 maximum distortion. valtype = FLOAT; distype = PRIOR; vptr = &(distem.maxdis); i = 2; a = ' '; keyname = "B_DMAX"; BEGIN(VALUE); } YY_BREAK case 107: YY_RULE_SETUP #line 1292 "wcspih.l" { // SIP: axis 1 polynomial coefficient. i = 1; sipflag = 2; keyname = "A_p_q"; BEGIN(SIP2); } YY_BREAK case 108: YY_RULE_SETUP #line 1301 "wcspih.l" { // SIP: axis 2 polynomial coefficient. i = 2; sipflag = 2; keyname = "B_p_q"; BEGIN(SIP2); } YY_BREAK case 109: YY_RULE_SETUP #line 1310 "wcspih.l" { // SIP: axis 1 inverse polynomial coefficient. i = 1; sipflag = 3; keyname = "AP_p_q"; BEGIN(SIP3); } YY_BREAK case 110: YY_RULE_SETUP #line 1319 "wcspih.l" { // SIP: axis 2 inverse polynomial coefficient. i = 2; sipflag = 3; keyname = "BP_p_q"; BEGIN(SIP3); } YY_BREAK case 111: YY_RULE_SETUP #line 1328 "wcspih.l" { // DSS: LLH corner pixel coordinate 1. valtype = FLOAT; distype = SEQUENT; vptr = dsstmp; dssflag = 1; distran = DSS; keyname = "CNPIX1"; BEGIN(VALUE); } YY_BREAK case 112: YY_RULE_SETUP #line 1340 "wcspih.l" { // DSS: LLH corner pixel coordinate 2. valtype = FLOAT; distype = SEQUENT; vptr = dsstmp+1; dssflag = 1; distran = DSS; keyname = "CNPIX1"; BEGIN(VALUE); } YY_BREAK case 113: YY_RULE_SETUP #line 1352 "wcspih.l" { // 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 114: YY_RULE_SETUP #line 1364 "wcspih.l" { // 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 115: YY_RULE_SETUP #line 1376 "wcspih.l" { // DSS: pixel x-dimension in micron. valtype = FLOAT; distype = SEQUENT; vptr = dsstmp+4; dssflag = 1; distran = DSS; keyname = "XPIXELSZ"; BEGIN(VALUE); } YY_BREAK case 116: YY_RULE_SETUP #line 1388 "wcspih.l" { // DSS: pixel y-dimension in micron. valtype = FLOAT; distype = SEQUENT; vptr = dsstmp+5; dssflag = 1; distran = DSS; keyname = "YPIXELSZ"; BEGIN(VALUE); } YY_BREAK case 117: YY_RULE_SETUP #line 1400 "wcspih.l" { // DSS: plate centre, right ascension - hours. valtype = FLOAT; distype = SEQUENT; vptr = dsstmp+6; dssflag = 1; distran = DSS; keyname = "PLTRAH"; BEGIN(VALUE); } YY_BREAK case 118: YY_RULE_SETUP #line 1412 "wcspih.l" { // DSS: plate centre, right ascension - minutes. valtype = FLOAT; distype = SEQUENT; vptr = dsstmp+7; dssflag = 1; distran = DSS; keyname = "PLTRAM"; BEGIN(VALUE); } YY_BREAK case 119: YY_RULE_SETUP #line 1424 "wcspih.l" { // DSS: plate centre, right ascension - seconds. valtype = FLOAT; distype = SEQUENT; vptr = dsstmp+8; dssflag = 1; distran = DSS; 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; keyname = "PLTDECSN"; BEGIN(PLTDECSN); } YY_BREAK case 121: YY_RULE_SETUP #line 1448 "wcspih.l" { // DSS: plate centre, declination - degrees. valtype = FLOAT; distype = SEQUENT; vptr = dsstmp+10; dssflag = 1; distran = DSS; keyname = "PLTDECD"; BEGIN(VALUE); } YY_BREAK case 122: YY_RULE_SETUP #line 1460 "wcspih.l" { // DSS: plate centre, declination - arcmin. valtype = FLOAT; distype = SEQUENT; vptr = dsstmp+11; dssflag = 1; distran = DSS; keyname = "PLTDECM"; BEGIN(VALUE); } YY_BREAK case 123: YY_RULE_SETUP #line 1472 "wcspih.l" { // DSS: plate centre, declination - arcsec. valtype = FLOAT; distype = SEQUENT; vptr = dsstmp+12; dssflag = 1; distran = DSS; keyname = "PLTDECS"; BEGIN(VALUE); } YY_BREAK case 124: YY_RULE_SETUP #line 1484 "wcspih.l" { // 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 125: YY_RULE_SETUP #line 1496 "wcspih.l" { // DSS: axis 1 polynomial coefficient. i = 1; dssflag = 3; keyname = "AMDXm"; BEGIN(DSSAMDXY); } YY_BREAK case 126: YY_RULE_SETUP #line 1505 "wcspih.l" { // DSS: axis 2 polynomial coefficient. i = 2; dssflag = 3; keyname = "AMDYm"; BEGIN(DSSAMDXY); } YY_BREAK case 127: YY_RULE_SETUP #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; distype = SEQUENT; vptr = wat[i-1] + 68*(m-1); a = ' '; distran = WAT; keyname = "WATi_m"; BEGIN(VALUE); } YY_BREAK case 128: YY_RULE_SETUP #line 1531 "wcspih.l" { if (yyextra->nkeyrec) { yyextra->nkeyrec = 0; errmsg = "keyrecords following the END keyrecord were ignored"; BEGIN(ERROR); } else { BEGIN(DISCARD); } } YY_BREAK case 129: YY_RULE_SETUP #line 1541 "wcspih.l" { BEGIN(DISCARD); } YY_BREAK case 130: #line 1546 "wcspih.l" case 131: #line 1547 "wcspih.l" case 132: #line 1548 "wcspih.l" case 133: YY_RULE_SETUP #line 1548 "wcspih.l" { sscanf(yytext, "%d%c", &i, &a); BEGIN(VALUE); } YY_BREAK 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 1559 "wcspih.l" { 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 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 1580 "wcspih.l" { // 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 150: YY_RULE_SETUP #line 1587 "wcspih.l" { // Let it go. BEGIN(DISCARD); } YY_BREAK case 151: YY_RULE_SETUP #line 1592 "wcspih.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 { // 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 = "indices in parameterized keywords must not have " "leading zeroes"; BEGIN(ERROR); } else { // Pretend we don't recognize it. BEGIN(DISCARD); } } YY_BREAK 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 1661 "wcspih.l" { // 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 = 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 198: YY_RULE_SETUP #line 1705 "wcspih.l" { BEGIN(DISCARD); } YY_BREAK case 199: #line 1710 "wcspih.l" case 200: YY_RULE_SETUP #line 1710 "wcspih.l" { if (YY_START == CCCCCCCa) { sscanf(yytext, "%c", &a); } else { unput(yytext[0]); a = 0; } BEGIN(VALUE); } YY_BREAK case 201: YY_RULE_SETUP #line 1721 "wcspih.l" { 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 202: #line 1736 "wcspih.l" case 203: #line 1737 "wcspih.l" case 204: #line 1738 "wcspih.l" case 205: YY_RULE_SETUP #line 1738 "wcspih.l" { sscanf(yytext, "%d_%d%c", &i, &m, &a); BEGIN(VALUE); } YY_BREAK 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 1763 "wcspih.l" { 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 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 1789 "wcspih.l" { // 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 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 1805 "wcspih.l" { errmsg = errtxt; sprintf(errmsg, "%s keyword must use an underscore, not a dash", keyname); BEGIN(ERROR); } YY_BREAK case 247: YY_RULE_SETUP #line 1812 "wcspih.l" { BEGIN(DISCARD); } YY_BREAK case 248: #line 1817 "wcspih.l" case 249: #line 1818 "wcspih.l" case 250: YY_RULE_SETUP #line 1818 "wcspih.l" { 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); } } YY_BREAK case 251: YY_RULE_SETUP #line 1840 "wcspih.l" { yyless(0); BEGIN(CCCCCia); } YY_BREAK case 252: YY_RULE_SETUP #line 1845 "wcspih.l" { if (relax & WCSHDR_PROJPn) { sscanf(yytext, "%d", &m); i = 0; a = ' '; BEGIN(VALUE); } else if (relax & WCSHDR_reject) { errmsg = "the PROJPn keyword is deprecated, use PVi_ma"; BEGIN(ERROR); } else { BEGIN(DISCARD); } } YY_BREAK case 253: #line 1862 "wcspih.l" case 254: YY_RULE_SETUP #line 1862 "wcspih.l" { if (relax & (WCSHDR_PROJPn | WCSHDR_reject)) { errmsg = "invalid PROJPn keyword"; BEGIN(ERROR); } else { BEGIN(DISCARD); } } YY_BREAK case 255: YY_RULE_SETUP #line 1872 "wcspih.l" { BEGIN(DISCARD); } YY_BREAK case 256: #line 1877 "wcspih.l" case 257: YY_RULE_SETUP #line 1877 "wcspih.l" { // SIP keywords. valtype = FLOAT; distype = PRIOR; vptr = &(distem.dp); npptr = ndp; a = ' '; distran = SIP; sscanf(yytext, "%d_%d", &p, &q); BEGIN(VALUE); } YY_BREAK case 258: #line 1892 "wcspih.l" case 259: YY_RULE_SETUP #line 1892 "wcspih.l" { BEGIN(DISCARD); } YY_BREAK case 260: #line 1897 "wcspih.l" case 261: YY_RULE_SETUP #line 1897 "wcspih.l" { // DSS keywords. valtype = FLOAT; distype = SEQUENT; vptr = &(distem.dp); npptr = ndq; a = ' '; distran = DSS; sscanf(yytext, "%d", &m); BEGIN(VALUE); } YY_BREAK case 262: YY_RULE_SETUP #line 1911 "wcspih.l" { BEGIN(DISCARD); } YY_BREAK case 263: /* rule 263 can match eol */ YY_RULE_SETUP #line 1915 "wcspih.l" { // 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 264: YY_RULE_SETUP #line 1926 "wcspih.l" { BEGIN(DISCARD); } YY_BREAK case 265: YY_RULE_SETUP #line 1930 "wcspih.l" { // Do checks on i, j & m. if (99 < i || 99 < j || 99 < m) { if (relax & WCSHDR_reject) { if (99 < i || 99 < j) { errmsg = "axis number exceeds 99"; } else if (m > 99) { errmsg = "parameter number exceeds 99"; } BEGIN(ERROR); } else { // Pretend we don't recognize it. BEGIN(DISCARD); } } else { 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 if (valtype == RECORD) { BEGIN(RECORD_VAL); } else { errmsg = errtxt; sprintf(errmsg, "internal parser ERROR, bad data type: %d", valtype); BEGIN(ERROR); } } } YY_BREAK case 266: YY_RULE_SETUP #line 1966 "wcspih.l" { errmsg = "invalid KEYWORD = VALUE syntax"; BEGIN(ERROR); } YY_BREAK case 267: YY_RULE_SETUP #line 1971 "wcspih.l" { if (ipass == 1) { BEGIN(COMMENT); } else { // Read the keyvalue. sscanf(yytext, "%d", &inttmp); 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 { // Read the keyvalue. wcsutil_str2double(yytext, &dbltmp); if (chekval && chekval(dbltmp)) { errmsg = "invalid keyvalue"; BEGIN(ERROR); } else { BEGIN(COMMENT); } } } YY_BREAK case 270: YY_RULE_SETUP #line 2005 "wcspih.l" { errmsg = "a floating-point value was expected"; BEGIN(ERROR); } YY_BREAK case 271: YY_RULE_SETUP #line 2010 "wcspih.l" { if (ipass == 1) { BEGIN(COMMENT); } else { // Read the keyvalue as integer and fractional parts. wcsutil_str2double2(yytext, dbl2tmp); 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); } else { // Copy the keyvalue minus the quotes. strncpy(strtmp, yytext+1, yyleng-2); strtmp[yyleng-2] = '\0'; // Strip off trailing blanks. for (int jx = yyleng-3; jx >= 0; jx--) { if (strtmp[jx] != ' ') { break; } strtmp[jx] = '\0'; } // Squeeze out repeated quotes. int ix = 0; for (int jx = 0; jx < 72; jx++) { if (ix < jx) { strtmp[ix] = strtmp[jx]; } if (strtmp[jx] == '\0') { break; } else if (strtmp[jx] == '\'' && strtmp[jx+1] == '\'') { jx++; } ix++; } BEGIN(COMMENT); } } YY_BREAK case 274: YY_RULE_SETUP #line 2064 "wcspih.l" { errmsg = "a string value was expected"; BEGIN(ERROR); } YY_BREAK case 275: /* rule 275 can match eol */ YY_RULE_SETUP #line 2069 "wcspih.l" { if (ipass == 1) { BEGIN(COMMENT); } else { yyless(1); BEGIN(RECFIELD); } } YY_BREAK case 276: YY_RULE_SETUP #line 2080 "wcspih.l" { errmsg = "a record was expected"; BEGIN(ERROR); } YY_BREAK case 277: YY_RULE_SETUP #line 2085 "wcspih.l" { strncpy(strtmp, yytext, 72); strtmp[72] = '\0'; BEGIN(RECCOLON); } YY_BREAK case 278: YY_RULE_SETUP #line 2091 "wcspih.l" { errmsg = "invalid record field"; BEGIN(ERROR); } YY_BREAK case 279: YY_RULE_SETUP #line 2096 "wcspih.l" { BEGIN(RECVALUE); } YY_BREAK case 280: YY_RULE_SETUP #line 2100 "wcspih.l" { errmsg = "invalid record syntax"; BEGIN(ERROR); } YY_BREAK case 281: YY_RULE_SETUP #line 2105 "wcspih.l" { rectype = 0; sscanf(yytext, "%d", &inttmp); BEGIN(RECEND); } YY_BREAK case 282: YY_RULE_SETUP #line 2111 "wcspih.l" { rectype = 1; wcsutil_str2double(yytext, &dbltmp); BEGIN(RECEND); } YY_BREAK case 283: YY_RULE_SETUP #line 2117 "wcspih.l" { errmsg = "invalid record value"; BEGIN(ERROR); } YY_BREAK case 284: YY_RULE_SETUP #line 2122 "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 2126 "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; 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); } } 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; } } } } 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); } 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 (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(FLUSH); } else { errmsg = "syntactically valid WCS keyrecord has no effect"; BEGIN(ERROR); } } else { BEGIN(FLUSH); } } YY_BREAK 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 2326 "wcspih.l" { errmsg = "invalid keyvalue"; BEGIN(ERROR); } YY_BREAK 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 2331 "wcspih.l" { errmsg = "invalid keyvalue"; BEGIN(ERROR); } YY_BREAK 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 2336 "wcspih.l" { errmsg = "invalid keyvalue or malformed keycomment"; BEGIN(ERROR); } YY_BREAK 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 2341 "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 2346 "wcspih.l" { if (ipass == npass) { if (ctrl < 0) { // Preserve discards. keep = keyrec; } else if (2 < ctrl) { nother++; wcsfprintf(stderr, "%.80s\n Not a recognized WCS keyword.\n", keyrec); } } BEGIN(FLUSH); } YY_BREAK 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 2361 "wcspih.l" { if (ipass == npass) { (*nreject)++; if (ctrl%10 == -1) { // Preserve rejects. keep = keyrec; } if (1 < abs(ctrl%10)) { wcsfprintf(stderr, "%.80s\n Rejected (%d), %s.\n", keyrec, *nreject, errmsg); } } BEGIN(FLUSH); } YY_BREAK case 292: /* rule 292 can match eol */ YY_RULE_SETUP #line 2378 "wcspih.l" { if (ipass == npass && keep) { if (hptr < keep) { strncpy(hptr, keep, 80); } hptr += 80; } 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; 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(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 2412 "wcspih.l" { // 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 (2 < abs(ctrl%10)) { if (*nwcs == 1) { if (strcmp(wcs[0]->wcsname, "DEFAULTS") != 0) { wcsfprintf(stderr, "Found one coordinate representation.\n"); } } else { wcsfprintf(stderr, "Found %d coordinate representations.\n", *nwcs); } } 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; 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, yyscanner); } else { if (ctrl < 0) { *hptr = '\0'; } else if (ctrl == 1) { 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"); } status = wcspih_final(ndp, ndq, distran, dsstmp, wat, nwcs, wcs); free(watstr); return status; } } YY_BREAK case 293: YY_RULE_SETUP #line 2486 "wcspih.l" ECHO; YY_BREAK #line 24922 "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 - yyg->yytext_ptr) - 1; /* Undo the effects of YY_DO_BEFORE_ACTION. */ *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 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. */ 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; } /* Note that here we test for yy_c_buf_p "<=" to the position * of the first EOB in the buffer, since yy_c_buf_p will * already have been incremented past the NUL character * (since all states make transitions on EOB to the * end-of-buffer state). Contrast this with the test * in input(). */ 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; yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state( yyscanner ); /* Okay, we're now positioned to make the NUL * transition. We couldn't have * yy_get_previous_state() go ahead and do it * for us because it doesn't know how to deal * with the possibility of jamming (and we don't * want to build jamming into it because then it * will run more slowly). */ yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner); yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; if ( yy_next_state ) { /* Consume the NUL. */ yy_cp = ++yyg->yy_c_buf_p; yy_current_state = yy_next_state; goto yy_match; } else { yy_cp = yyg->yy_c_buf_p; goto yy_find_action; } } else switch ( yy_get_next_buffer( yyscanner ) ) { case EOB_ACT_END_OF_FILE: { yyg->yy_did_buffer_switch_on_eof = 0; if ( yywrap( yyscanner ) ) { /* Note: because we've taken care in * yy_get_next_buffer() to have 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. */ yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ; yy_act = YY_STATE_EOF(YY_START); goto do_action; } else { if ( ! yyg->yy_did_buffer_switch_on_eof ) YY_NEW_FILE; } break; } case EOB_ACT_CONTINUE_SCAN: yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state( yyscanner ); yy_cp = yyg->yy_c_buf_p; yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; goto yy_match; case EOB_ACT_LAST_MATCH: yyg->yy_c_buf_p = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars]; yy_current_state = yy_get_previous_state( yyscanner ); yy_cp = yyg->yy_c_buf_p; yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; goto yy_find_action; } break; } default: YY_FATAL_ERROR( "fatal flex scanner internal error--no action found" ); } /* end of action switch */ } /* end of scanning one token */ } /* end of user's declarations */ } /* end of yylex */ /* yy_get_next_buffer - try to read in a new buffer * * Returns a code representing an action: * EOB_ACT_LAST_MATCH - * EOB_ACT_CONTINUE_SCAN - continue scanning from current position * EOB_ACT_END_OF_FILE - end of file */ static int yy_get_next_buffer (yyscan_t yyscanner) { 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 ( 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 ( 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. */ return EOB_ACT_END_OF_FILE; } else { /* We matched some text prior to the EOB, first * process it. */ return EOB_ACT_LAST_MATCH; } } /* Try to read more data. */ /* First move last chars to start of buffer. */ number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr - 1); for ( i = 0; i < number_to_move; ++i ) *(dest++) = *(source++); if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) /* don't do the read, it's not guaranteed to return an EOF, * just force an EOF */ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0; else { int num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; while ( num_to_read <= 0 ) { /* Not enough room in the buffer - grow it. */ /* just a shorter name for the current buffer */ YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE; int yy_c_buf_p_offset = (int) (yyg->yy_c_buf_p - b->yy_ch_buf); if ( b->yy_is_our_buffer ) { int new_size = b->yy_buf_size * 2; if ( new_size <= 0 ) b->yy_buf_size += b->yy_buf_size / 8; else b->yy_buf_size *= 2; b->yy_ch_buf = (char *) /* Include room in for 2 EOB chars. */ 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 = NULL; if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "fatal error - scanner input buffer overflow" ); 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; } if ( num_to_read > YY_READ_BUF_SIZE ) num_to_read = YY_READ_BUF_SIZE; /* Read in more data. */ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), yyg->yy_n_chars, num_to_read ); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } if ( yyg->yy_n_chars == 0 ) { if ( number_to_move == YY_MORE_ADJ ) { ret_val = EOB_ACT_END_OF_FILE; yyrestart( yyin , yyscanner); } else { ret_val = EOB_ACT_LAST_MATCH; YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_EOF_PENDING; } } else ret_val = EOB_ACT_CONTINUE_SCAN; 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. */ 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); } 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; 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 (yyscan_t yyscanner) { 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 = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp ) { if ( *yy_cp ) { yy_current_state = yy_nxt[yy_current_state][YY_SC_TO_UI(*yy_cp)]; } else yy_current_state = yy_NUL_trans[yy_current_state]; if ( yy_accept[yy_current_state] ) { yyg->yy_last_accepting_state = yy_current_state; yyg->yy_last_accepting_cpos = yy_cp; } } return yy_current_state; } /* yy_try_NUL_trans - try to make a transition on the NUL character * * synopsis * next_state = yy_try_NUL_trans( current_state ); */ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner) { 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); if ( ! yy_is_jam ) { if ( yy_accept[yy_current_state] ) { 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 static void yyunput (int c, char * yy_bp , yyscan_t yyscanner) { char *yy_cp; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; 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. */ 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]; char *source = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]; while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) *--dest = *--source; yy_cp += (int) (dest - source); yy_bp += (int) (dest - source); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = 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" ); } *--yy_cp = (char) c; 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 (yyscan_t yyscanner) #else static int input (yyscan_t yyscanner) #endif { int c; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; *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 ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) /* This was really a NUL. */ *yyg->yy_c_buf_p = '\0'; else { /* need more input */ int offset = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr); ++yyg->yy_c_buf_p; switch ( yy_get_next_buffer( yyscanner ) ) { case EOB_ACT_LAST_MATCH: /* This happens because yy_g_n_b() * sees that we've accumulated a * token and flags that we need to * try matching the token before * proceeding. But for input(), * there's no matching to consider. * So convert the EOB_ACT_LAST_MATCH * to EOB_ACT_END_OF_FILE. */ /* Reset buffer status. */ yyrestart( yyin , yyscanner); /*FALLTHROUGH*/ case EOB_ACT_END_OF_FILE: { if ( yywrap( yyscanner ) ) return 0; if ( ! yyg->yy_did_buffer_switch_on_eof ) YY_NEW_FILE; #ifdef __cplusplus return yyinput(yyscanner); #else return input(yyscanner); #endif } case EOB_ACT_CONTINUE_SCAN: yyg->yy_c_buf_p = yyg->yytext_ptr + offset; break; } } } 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'); return c; } #endif /* ifndef YY_NO_INPUT */ /** 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 yyrestart (FILE * input_file , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if ( ! YY_CURRENT_BUFFER ){ yyensure_buffer_stack (yyscanner); YY_CURRENT_BUFFER_LVALUE = yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); } 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 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 * yypop_buffer_state(); * yypush_buffer_state(new_buffer); */ yyensure_buffer_stack (yyscanner); if ( YY_CURRENT_BUFFER == new_buffer ) return; if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ *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; yy_load_buffer_state( yyscanner ); /* We don't actually know whether we did this switch during * 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. */ yyg->yy_did_buffer_switch_on_eof = 1; } static void yy_load_buffer_state (yyscan_t yyscanner) { 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 yy_create_buffer (FILE * file, int size , yyscan_t yyscanner) { YY_BUFFER_STATE b; b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); if ( ! b ) 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 *) yyalloc( (yy_size_t) (b->yy_buf_size + 2) , yyscanner ); if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); b->yy_is_our_buffer = 1; yy_init_buffer( b, file , yyscanner); return b; } /** Destroy the buffer. * @param b a buffer created with yy_create_buffer() * @param yyscanner The scanner object. */ void yy_delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if ( ! b ) return; if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; if ( b->yy_is_our_buffer ) yyfree( (void *) b->yy_ch_buf , yyscanner ); 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 yyrestart() or at EOF. */ static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner) { int oerrno = errno; 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 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){ b->yy_bs_lineno = 1; b->yy_bs_column = 0; } b->yy_is_interactive = 0; errno = oerrno; } /** 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 yy_flush_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if ( ! b ) return; b->yy_n_chars = 0; /* We always need two end-of-buffer characters. The first causes * a transition to the end-of-buffer state. The second causes * a jam in that state. */ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; b->yy_buf_pos = &b->yy_ch_buf[0]; b->yy_at_bol = 1; b->yy_buffer_status = YY_BUFFER_NEW; if ( b == YY_CURRENT_BUFFER ) 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 yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (new_buffer == NULL) return; yyensure_buffer_stack(yyscanner); /* This block is copied from yy_switch_to_buffer. */ if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ *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) yyg->yy_buffer_stack_top++; YY_CURRENT_BUFFER_LVALUE = new_buffer; /* 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 yypop_buffer_state (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (!YY_CURRENT_BUFFER) return; yy_delete_buffer(YY_CURRENT_BUFFER , yyscanner); YY_CURRENT_BUFFER_LVALUE = NULL; if (yyg->yy_buffer_stack_top > 0) --yyg->yy_buffer_stack_top; if (YY_CURRENT_BUFFER) { 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 yyensure_buffer_stack (yyscan_t yyscanner) { 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; /* 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*) , 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 (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){ /* Increase the buffer to prepare for a possible push. */ yy_size_t grow_size = 8 /* arbitrary grow size */; 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*) , yyscanner); if ( ! yyg->yy_buffer_stack ) YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); /* zero only the new slots.*/ 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 * @param yyscanner The scanner object. * @return the newly allocated buffer state object. */ YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size , yyscan_t yyscanner) { YY_BUFFER_STATE b; if ( size < 2 || 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 NULL; b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); 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 = 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; yy_switch_to_buffer( b , yyscanner ); return b; } /** 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 * yy_scan_bytes() instead. */ YY_BUFFER_STATE yy_scan_string (const char * yystr , yyscan_t yyscanner) { return yy_scan_bytes( yystr, (int) strlen(yystr) , yyscanner); } /** 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 yy_scan_bytes (const char * yybytes, int _yybytes_len , yyscan_t yyscanner) { YY_BUFFER_STATE b; char *buf; yy_size_t n; int i; /* Get memory for full buffer, including space for trailing EOB's. */ n = (yy_size_t) (_yybytes_len + 2); buf = (char *) yyalloc( n , yyscanner ); if ( ! buf ) 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 = yy_scan_buffer( buf, n , yyscanner); if ( ! b ) 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. */ b->yy_is_our_buffer = 1; return b; } #ifndef YY_EXIT_FAILURE #define YY_EXIT_FAILURE 2 #endif static void yynoreturn yy_fatal_error (const char* msg , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; (void)yyg; fprintf( stderr, "%s\n", msg ); exit( YY_EXIT_FAILURE ); } /* Redefine yyless() so it works in section 3 code. */ #undef yyless #define yyless(n) \ do \ { \ /* Undo effects of setting up yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(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 yyget_column (yyscan_t yyscanner) { 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 *yyget_in (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyin; } /** Get the output stream. * @param yyscanner The scanner object. */ FILE *yyget_out (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyout; } /** Get the length of the current token. * @param yyscanner The scanner object. */ int yyget_leng (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyleng; } /** Get the current token. * @param yyscanner The scanner object. */ 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) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yyextra = user_defined ; } /** Set the current 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 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. * @param yyscanner The scanner object. * @see yy_switch_to_buffer */ 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) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yy_flex_debug; } void yyset_debug (int _bdebug , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yy_flex_debug = _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) { 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 ); } /* 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 ) { 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. */ 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 yyin = stdin; yyout = stdout; #else yyin = NULL; yyout = NULL; #endif /* For future reference: Set errno on error, since we are called by * yylex_init() */ return 0; } /* 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){ yy_delete_buffer( YY_CURRENT_BUFFER , yyscanner ); YY_CURRENT_BUFFER_LVALUE = NULL; yypop_buffer_state(yyscanner); } /* Destroy the stack itself. */ 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 * yylex() is called, initialization will occur. */ yy_init_globals( yyscanner); /* Destroy the main struct (reentrant only). */ yyfree ( yyscanner , yyscanner ); yyscanner = NULL; return 0; } /* * Internal utility routines. */ #ifndef yytext_ptr static void yy_flex_strncpy (char* s1, const char * s2, int n , yyscan_t yyscanner) { 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 (const char * s , yyscan_t yyscanner) { int n; for ( n = 0; s[n]; ++n ) ; return n; } #endif void *yyalloc (yy_size_t size , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; (void)yyg; return malloc(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 * because both ANSI C and C++ allow castless assignment from * any pointer type to void*, and deal with argument conversions * as though doing an assignment. */ return realloc(ptr, size); } void yyfree (void * ptr , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; (void)yyg; free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ } #define YYTABLES_NAME "yytables" #line 2486 "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, 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; } int ialt = 0; if (a != ' ') { ialt = a - 'A' + 1; } int *ip = alts + ialt; if (*ip < naxis) { *ip = naxis; } // i or j can be greater than naxis. if (*ip < i) { *ip = i; } if (*ip < j) { *ip = j; } // Type of distortions present. dpq[ialt] |= distype; // Count PVi_ma, PSi_ma, DPja, or DQia keywords. if (npptr) { npptr[ialt]++; } } /*---------------------------------------------------------------------------- * Allocate memory for an array of the required number of wcsprm structs and * initialize each of them. *---------------------------------------------------------------------------*/ 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 status = 0; // Find the number of coordinate descriptions. *nwcs = 0; for (int ialt = 0; ialt < 27; ialt++) { if (alts[ialt]) (*nwcs)++; } 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))) == 0x0) { return WCSHDRERR_MEMORY; } 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. struct wcsprm *wcsp = *wcs; *nwcs = 0; for (int ialt = 0; ialt < 27; ialt++) { if (alts[ialt]) { wcsp->flag = -1; 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. if (ialt) { wcsp->alt[0] = 'A' + ialt - 1; } // 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; } } // 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 ndp[], int ndq[], int distran, double dsstmp[], char *wat[], int *nwcs, struct wcsprm **wcs) { 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++; } // 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; } astropy-astropy-201cddb/cextern/wcslib/C/flexed/wcsulex.c000066400000000000000000014170111507226315300236260ustar00rootroot00000000000000#line 2 "wcsulex.c" #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 #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 #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 #endif #ifdef yyrestart #define wcsulexrestart_ALREADY_DEFINED #else #define yyrestart wcsulexrestart #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 #endif #ifdef yyfree #define wcsulexfree_ALREADY_DEFINED #else #define yyfree wcsulexfree #endif /* First, we deal with platform-specific or compiler-specific issues. */ /* begin standard C headers. */ #include #include #include #include /* end standard C headers. */ /* flex integer type definitions */ #ifndef FLEXINT_H #define FLEXINT_H /* C99 systems have . Non-C99 systems may or may not. */ #if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, * if you want the limit (max/min) macros for int types. */ #ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS 1 #endif #include typedef int8_t flex_int8_t; typedef uint8_t flex_uint8_t; typedef int16_t flex_int16_t; typedef uint16_t flex_uint16_t; typedef int32_t flex_int32_t; typedef uint32_t flex_uint32_t; #else typedef signed char flex_int8_t; typedef short int flex_int16_t; typedef int flex_int32_t; typedef unsigned char flex_uint8_t; typedef unsigned short int flex_uint16_t; typedef unsigned int flex_uint32_t; /* Limits of integral types. */ #ifndef INT8_MIN #define INT8_MIN (-128) #endif #ifndef INT16_MIN #define INT16_MIN (-32767-1) #endif #ifndef INT32_MIN #define INT32_MIN (-2147483647-1) #endif #ifndef INT8_MAX #define INT8_MAX (127) #endif #ifndef INT16_MAX #define INT16_MAX (32767) #endif #ifndef INT32_MAX #define INT32_MAX (2147483647) #endif #ifndef UINT8_MAX #define UINT8_MAX (255U) #endif #ifndef UINT16_MAX #define UINT16_MAX (65535U) #endif #ifndef UINT32_MAX #define UINT32_MAX (4294967295U) #endif #ifndef SIZE_MAX #define SIZE_MAX (~(size_t)0) #endif #endif /* ! C99 */ #endif /* ! FLEXINT_H */ /* begin standard C++ headers. */ /* TODO: this is always defined, so inline it */ #define yyconst const #if defined(__GNUC__) && __GNUC__ >= 3 #define yynoreturn __attribute__((__noreturn__)) #else #define yynoreturn #endif /* Returned upon end-of-file. */ #define YY_NULL 0 /* 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) ((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 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 ((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 yyrestart( yyin , yyscanner ) #define YY_END_OF_BUFFER_CHAR 0 /* Size of default input buffer. */ #ifndef YY_BUF_SIZE #ifdef __ia64__ /* On IA-64, the buffer size is 16k, not 8k. * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. * Ditto for the __ia64__ case accordingly. */ #define YY_BUF_SIZE 32768 #else #define YY_BUF_SIZE 16384 #endif /* __ia64__ */ #endif /* The state buf must be large enough to hold one state per character in the main buffer. */ #define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) #ifndef YY_TYPEDEF_YY_BUFFER_STATE #define YY_TYPEDEF_YY_BUFFER_STATE typedef struct yy_buffer_state *YY_BUFFER_STATE; #endif #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 yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ *yy_cp = yyg->yy_hold_char; \ YY_RESTORE_YY_MORE_OFFSET \ 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, yyg->yytext_ptr , yyscanner ) #ifndef YY_STRUCT_YY_BUFFER_STATE #define YY_STRUCT_YY_BUFFER_STATE struct yy_buffer_state { FILE *yy_input_file; char *yy_ch_buf; /* input buffer */ char *yy_buf_pos; /* current position in input buffer */ /* Size of input buffer in bytes, not including room for EOB * characters. */ int yy_buf_size; /* Number of characters read into yy_ch_buf, not including EOB * characters. */ int yy_n_chars; /* Whether we "own" the buffer - i.e., we know we created it, * and can realloc() it to grow it, and should free() it to * delete it. */ int yy_is_our_buffer; /* Whether this is an "interactive" input source; if so, and * if we're using stdio for input, then we want to use getc() * instead of fread(), to make sure we stop fetching input after * each newline. */ int yy_is_interactive; /* Whether we're considered to be at the beginning of a line. * If so, '^' rules will be active on the next match, otherwise * not. */ int yy_at_bol; 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. */ int yy_fill_buffer; int yy_buffer_status; #define YY_BUFFER_NEW 0 #define YY_BUFFER_NORMAL 1 /* When an EOF's been seen but there's still some text to process * then we mark the buffer as YY_EOF_PENDING, to indicate that we * shouldn't try reading from the input source any more. We might * still have a bunch of tokens to match, though, because of * possible backing-up. * * When we actually see the EOF, we change the status to "new" * (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 */ /* 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 ( 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 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 ){ \ yyensure_buffer_stack (yyscanner); \ YY_CURRENT_BUFFER_LVALUE = \ 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 ){\ yyensure_buffer_stack (yyscanner); \ YY_CURRENT_BUFFER_LVALUE = \ 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(yyscanner) (/*CONSTCOND*/1) #define YY_SKIP_YYWRAP typedef flex_uint8_t YY_CHAR; typedef int yy_state_type; #define yytext_ptr yytext_r static const flex_int16_t yy_nxt[][128] = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 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, 16, 14, 14, 14, 14, 14, 14, 14, 17, 14, 18, 14, 14, 14, 18, 19, 14, 14, 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, 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, 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, 52, 14, 14, 14, 14, 14, 14, 14, 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, 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, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 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, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 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, 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, 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, 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, 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, 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, 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, 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, 115, 115, 115, 115, 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, 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, 115, 115, 115, 115, 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, 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, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 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, 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, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 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, -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, -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, -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, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13 }, { 13, -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, -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, -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, -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, -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, -14, -14 }, { 13, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15 }, { 13, -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, 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, -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, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16 }, { 13, -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, -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, -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, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17 }, { 13, -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, -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, -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, -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, -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, -18, -18 }, { 13, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19 }, { 13, -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, -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, -20, -20, -20, -20, -20, -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, 128, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20 }, { 13, -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, -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, -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, -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, 129, -21, -21, -21, -21, -21, -21 }, { 13, -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, -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, -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, -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, -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, -22, -22 }, { 13, -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, -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, -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, -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, -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, -23, -23 }, { 13, -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, -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, 130, 131, 132, -24, -24, 132, 133, 134, -24, 135, 130, -24, -24, 132, 136, 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 }, { 13, -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, -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, -25, -25, -25, -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 }, { 13, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, 149, -26, -26, -26, -26, -26 }, { 13, -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, -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, -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, -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, 150, -27, -27, -27, -27, -27, -27 }, { 13, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28 }, { 13, -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, -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, -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, -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 }, { 13, -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, -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, 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, 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 }, { 13, -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, -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, -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, -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, 153, -31, -31, -31, -31, -31, -31 }, { 13, -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, -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, -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, -32, -32, -32, -32, -32, 154, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32 }, { 13, -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, 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, 136, 145, -33, 146, 147, -33, -33, -33, -33, -33, 148, -33, -33, -33, -33, -33, -33 }, { 13, -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, 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 }, { 13, -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, -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 }, { 13, -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, -36, -36, -36, -36, -36, -36, -36, -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 }, { 13, -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, -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, 159, -37, -37, -37, 160, -37, -37, -37, 161, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, 129, -37, -37, -37, -37, -37, -37 }, { 13, -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, -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 }, { 13, -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, -39, -39, -39, -39, -39, -39, -39, -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 }, { 13, -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, -40, -40, -40, -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, 168, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40 }, { 13, -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, 130, -41, 132, -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 }, { 13, -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, -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 }, { 13, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -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, 149, 169, -43, -43, -43, -43, -43, -43 }, { 13, -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, 130, -44, 132, -44, -44, 132, 133, 134, -44, 135, 130, -44, -44, 132, 136, 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 }, { 13, -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, -45, -45, -45, -45, -45, -45, -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 }, { 13, -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, -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, 176, -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 }, { 13, -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, -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, 177, -47, 178, 162, -47, -47, -47, -47, -47, -47, -47, -47, -47, -47, -47, -47, -47 }, { 13, -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, -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 }, { 13, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -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 }, { 13, -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, -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, -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, 180, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50 }, { 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, -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, 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 }, { 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, 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, -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 }, { 13, -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, -53, -53, -53, -53, -53, -53, -53, -53, -53 }, { 13, -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, -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, -54 }, { 13, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55 }, { 13, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -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, 168, -56, -56, -56, -56, -56, 185, -56, -56, -56, -56, -56, -56, -56 }, { 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, -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, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 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, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 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, -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, -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, -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 }, { 13, -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, -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 }, { 13, -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, -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 }, { 13, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62 }, { 13, -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, -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 }, { 13, -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, -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, -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 }, { 13, -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, -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 }, { 13, -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, -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, -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 }, { 13, -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, -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 }, { 13, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68 }, { 13, -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, -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 }, { 13, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70 }, { 13, -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, -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 }, { 13, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72 }, { 13, -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, -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, -73, -73, -73, -73, -73, -73, -73, -73, -73 }, { 13, -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, -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 }, { 13, -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, -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 }, { 13, -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, -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 }, { 13, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77 }, { 13, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78 }, { 13, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79 }, { 13, -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, -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, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81 }, { 13, -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, 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, 191, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82 }, { 13, -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, -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, 192, -83, -83, -83, -83, -83, -83 }, { 13, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84 }, { 13, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85 }, { 13, -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, -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 }, { 13, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87 }, { 13, -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, -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, 193, -88, -88, -88, -88, -88 }, { 13, -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, -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, 194, -89, -89, -89, -89, -89, -89 }, { 13, -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, -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, -90, -90, -90 }, { 13, -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, -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 }, { 13, -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, -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 }, { 13, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -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, -93, -93, -93, -93, -93, -93, -93 }, { 13, -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, -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, 197, -94, -94, -94, -94, -94, -94 }, { 13, -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, -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, 198, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95 }, { 13, -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, -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 }, { 13, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97 }, { 13, -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, -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, -98, -98, -98, -98, -98, -98, -98, -98 }, { 13, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -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 }, { 13, -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, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, 202, -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 }, { 13, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -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 }, { 13, -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, 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 }, { 13, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -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, 212, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103 }, { 13, -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, -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, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104 }, { 13, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105 }, { 13, -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, -106, -106, -106, -106, -106, -106, -106, -106, -106, 213, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, 214, 215, -106, -106, -106, -106, -106, -106 }, { 13, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -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 }, { 13, -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, -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, 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 }, { 13, -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, -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, -109, -109, -109, -109, -109, -109, -109 }, { 13, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -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 }, { 13, -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, -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, 225, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111 }, { 13, -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, -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, -112, -112, -112 }, { 13, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -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 }, { 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, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -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 }, { 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, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -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, -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, -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, -116, -116, -116, -116, -116, -116, -116, -116 }, { 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, 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, -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 }, { 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, 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, -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 }, { 13, -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, 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, -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 }, { 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, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120 }, { 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, 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, -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, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121 }, { 13, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -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, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122 }, { 13, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -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, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124 }, { 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, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 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, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, 126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126 }, { 13, -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, -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 }, { 13, -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, -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 }, { 13, -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, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, 247, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129 }, { 13, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130 }, { 13, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -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 }, { 13, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132 }, { 13, -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, -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 }, { 13, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -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 }, { 13, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -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 }, { 13, -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, -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, 249, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136 }, { 13, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -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 }, { 13, -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, -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, 132, -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 }, { 13, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139 }, { 13, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -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, 248, -140, -140, -140, -140, -140, -140 }, { 13, -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, -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, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141 }, { 13, -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, -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, -142, -142, -142, -142, -142, -142, -142 }, { 13, -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, -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, 132, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, 132, -143, -143, -143, -143, -143, -143, -143 }, { 13, -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, -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 }, { 13, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -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, -145, -145, -145, -145 }, { 13, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -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, -146, -146, -146, -146 }, { 13, -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, -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, 130, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147 }, { 13, -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, -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 }, { 13, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149 }, { 13, -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, -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, -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 }, { 13, -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, -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 }, { 13, -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, -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 }, { 13, -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, -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 }, { 13, -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, -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, 127, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154 }, { 13, -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, -155, -155, -155, -155, -155, -155, -155, -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, -155, -155, -155, -155, -155, -155, -155 }, { 13, -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, -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, 165, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156 }, { 13, -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, -157, -157, -157, -157, -157, -157, -157, -157, 254, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, 252, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157 }, { 13, -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, -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, 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 }, { 13, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, 256, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159 }, { 13, -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, -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, -160, -160, -160, -160, -160, -160, -160, -160 }, { 13, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -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 }, { 13, -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, -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, -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 }, { 13, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -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, -163, -163, -163, -163, -163, -163, -163, -163 }, { 13, -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, -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, 249, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, 260, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164 }, { 13, -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, -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 }, { 13, -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, 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, 148, -166, -166, -166, -166, -166, -166 }, { 13, -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, -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, -167, -167, -167, -167 }, { 13, -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, -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, -168, -168, -168, -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 }, { 13, -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, -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, -169, -169, -169, -169, -169, -169, -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 }, { 13, -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, -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, -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 }, { 13, -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, -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, 153, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171 }, { 13, -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, -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, 249, -172, -172, -172, 162, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172 }, { 13, -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, -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, -173, -173, -173, -173, -173, -173, -173, -173 }, { 13, -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, -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, 261, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174 }, { 13, -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, -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, 262, -175, -175, -175, -175, -175, -175, -175 }, { 13, -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, -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, 162, -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 }, { 13, -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, -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, 263, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177 }, { 13, -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, -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, 264, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178 }, { 13, -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, -179, -179, -179, -179, 265, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179 }, { 13, -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, -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, 266, -180, -180, -180, -180, -180, -180, -180 }, { 13, -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, -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 }, { 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, -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 }, { 13, -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, -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 }, { 13, -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, -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, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -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 }, { 13, -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, -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, -186, -186, -186, -186, -186, -186 }, { 13, -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, -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, 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, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 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, -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, -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, -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 }, { 13, -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, -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, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190 }, { 13, -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, -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, -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 }, { 13, -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, -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, -192, 273, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192 }, { 13, -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, -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, -193 }, { 13, -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, -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 }, { 13, -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, -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, 274, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195 }, { 13, -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, -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, -196, -196, -196, -196, -196, -196, -196 }, { 13, -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, -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, -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 }, { 13, -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, -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, 275, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198 }, { 13, -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, -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 }, { 13, -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, -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 }, { 13, -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, -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, 277, -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 }, { 13, -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, -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, -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 }, { 13, -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, -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, 279, -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 }, { 13, -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, -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, 280, -204, -204, -204, -204, -204, 281, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204 }, { 13, -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, -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 }, { 13, -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, -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, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206 }, { 13, -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, -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, 283, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207 }, { 13, -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, -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 }, { 13, -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, -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 }, { 13, -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, -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, 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 }, { 13, -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, -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 }, { 13, -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, -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, 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 }, { 13, -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, -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 }, { 13, -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, -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 }, { 13, -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, -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, 286, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215 }, { 13, -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, -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, 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 }, { 13, -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, -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, -217, -217, -217, -217, -217, -217, -217, -217, -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 }, { 13, -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, -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, 290, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218 }, { 13, -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, -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 }, { 13, -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, -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, 291, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220 }, { 13, -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, -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, 292, -221, -221, -221, -221, -221, -221, -221 }, { 13, -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, -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, -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 }, { 13, -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, -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 }, { 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, -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, -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 }, { 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, -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 }, { 13, -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, -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, -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, -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, -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, -227, -227 }, { 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, 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, 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 }, { 13, -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, 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, -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 }, { 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, 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, -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 }, { 13, -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, -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 }, { 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, 242, -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, -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 }, { 13, -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, 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, -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 }, { 13, -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, 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, -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 }, { 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, -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, -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, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235 }, { 13, -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, -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, -236, -236, -236 }, { 13, -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, -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 }, { 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, 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, -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 }, { 13, -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, -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 }, { 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 }, { 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, 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, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241 }, { 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, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242 }, { 13, -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, -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 }, { 13, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -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, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 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, -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, 306, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246 }, { 13, -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, -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, 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 }, { 13, -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, -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, -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 }, { 13, -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, -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, -249, -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 }, { 13, -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, -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, 308, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250 }, { 13, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -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 }, { 13, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -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 }, { 13, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -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 }, { 13, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -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, -254, -254, -254, -254, -254, -254, -254, -254 }, { 13, -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, -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, 310, -255, -255, -255, -255, -255, 311, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255 }, { 13, -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, -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, 150, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256 }, { 13, -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, -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, -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 }, { 13, -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, -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 }, { 13, -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, -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, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, 165, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259 }, { 13, -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, -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, -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 }, { 13, -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, -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, 313, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261 }, { 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, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -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 }, { 13, -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, 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, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263 }, { 13, -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, -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, -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 }, { 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, -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 }, { 13, -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, 319, -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 }, { 13, -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, -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, -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 }, { 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, 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, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268 }, { 13, -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, -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 }, { 13, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270 }, { 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 }, { 13, -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, -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, 324, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272 }, { 13, -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, -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, 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 }, { 13, -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, -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, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274 }, { 13, -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, -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, -275, -275, -275, -275, -275, -275, -275, -275, -275 }, { 13, -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, -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 }, { 13, -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, -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, 326, -277, -277, -277, -277, -277, 327, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277 }, { 13, -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, -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, 328, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278 }, { 13, -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, -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 }, { 13, -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, -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 }, { 13, -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, -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, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281 }, { 13, -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, -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, -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 }, { 13, -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, -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, -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, 331, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283 }, { 13, -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, -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 }, { 13, -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, -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 }, { 13, -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, -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, -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 }, { 13, -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, -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 }, { 13, -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, -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 }, { 13, -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, -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 }, { 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, -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, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290 }, { 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, -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 }, { 13, -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, -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, 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 }, { 13, -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, -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 }, { 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, -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 }, { 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, -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 }, { 13, -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, -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 }, { 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, 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, -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 }, { 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, 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, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298 }, { 13, -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, -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, -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 }, { 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, 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, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300 }, { 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, 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, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301 }, { 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, 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, -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 }, { 13, -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, -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 }, { 13, -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, -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 }, { 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, 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, -305, -305 }, { 13, -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, -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, 341, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306 }, { 13, -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, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -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 }, { 13, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, 133, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308 }, { 13, -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, -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, -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, -309 }, { 13, -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, -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, 342, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310 }, { 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, -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 }, { 13, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -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 }, { 13, -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, -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 }, { 13, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -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 }, { 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, -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 }, { 13, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -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, -316, -316, -316, -316, -316, -316, -316, -316 }, { 13, -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, -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, -317, -317 }, { 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, 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, -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 }, { 13, -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, -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, 165, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319 }, { 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, 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, -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 }, { 13, -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, -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 }, { 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, 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, -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 }, { 13, -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, -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 }, { 13, -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, -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, 350, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324 }, { 13, -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, -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, -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 }, { 13, -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, -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, 351, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326 }, { 13, -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, -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, -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, -327 }, { 13, -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, -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 }, { 13, -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, -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, -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, -329, -329, -329, -329, -329, -329, -329 }, { 13, -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, -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 }, { 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, -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 }, { 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, -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, -332, -332, -332, -332, -332, -332, 353, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332 }, { 13, -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, -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, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333 }, { 13, -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, -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, 355, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334 }, { 13, -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, -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, 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 }, { 13, -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, -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, 357, -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 }, { 13, -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, -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, -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 }, { 13, -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, -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, 358, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338 }, { 13, -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, 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 }, { 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, 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, -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 }, { 13, -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, -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, -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 }, { 13, -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, -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, 153, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342 }, { 13, -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, -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, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343 }, { 13, -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, -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, 165, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344 }, { 13, -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, -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, -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, -345, -345, -345, -345, -345, -345, -345, -345 }, { 13, -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, -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 }, { 13, -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, -347, -347, -347, -347, -347, -347, -347, -347, 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 }, { 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, 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, -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 }, { 13, -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, -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 }, { 13, -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, -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, 364, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350 }, { 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, -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 }, { 13, -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, -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 }, { 13, -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, -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 }, { 13, -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, -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 }, { 13, -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, -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, -355, -355, -355 }, { 13, -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, -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, 369, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356 }, { 13, -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, -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, -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 }, { 13, -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, -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 }, { 13, -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, -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 }, { 13, -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, -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, -360, -360, -360, -360, -360 }, { 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, 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, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361 }, { 13, -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, -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, -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 }, { 13, -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, -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, 127, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363 }, { 13, -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, -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, 372, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364 }, { 13, -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, -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, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365 }, { 13, -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, -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 }, { 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 ( 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; \ yyg->yytext_ptr -= yyg->yy_more_len; \ yyleng = (int) (yy_cp - yyg->yytext_ptr); \ yyg->yy_hold_char = *yy_cp; \ *yy_cp = '\0'; \ 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 { flex_int32_t yy_verify; flex_int32_t yy_nxt; }; static const flex_int16_t yy_accept[375] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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, 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 const yy_state_type yy_NUL_trans[375] = { 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, 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, 0, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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() (yyg->yy_more_flag = 1) #define YY_MORE_ADJ yyg->yy_more_len #define YY_RESTORE_YY_MORE_OFFSET #line 1 "wcsulex.l" /*============================================================================ WCSLIB 8.4 - an implementation of the FITS WCS standard. Copyright (C) 1995-2024, 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/people/Mark.Calabretta $Id: wcsulex.c,v 8.4 2024/10/28 13:56:17 mcalabre Exp $ *============================================================================= * * wcsulex.l is a Flex description file containing the definition of a * recursive, multi-buffered lexical scanner and parser for FITS units * specifications. * * It requires Flex v2.5.4 or later. * * Refer to wcsunits.h for a description of the user interface and operating * notes. * *===========================================================================*/ /* Options. */ #define YY_NO_INPUT 1 /* Exponents. */ /* Metric prefixes. */ /* Basic and derived SI units. */ /* Additional recognized units: all metric prefixes allowed. */ /* Additional recognized units: only super-metric prefixes allowed. */ /* Additional recognized units: only sub-metric prefixes allowed. */ /* Additional recognized units for which NO metric prefixes are allowed. */ /* All additional recognized units. */ /* Exclusive start states. */ #line 85 "wcsulex.l" #include #include #include #include #include "wcserr.h" #include "wcsmath.h" #include "wcsunits.h" #include "wcsutil.h" // 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); // Internal helper functions. static YY_DECL; #line 7229 "wcsulex.c" #line 7230 "wcsulex.c" #define INITIAL 0 #define PAREN 1 #define PREFIX 2 #define UNITS 3 #define EXPON 4 #define FLUSH 5 #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 wcsulex_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 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[]); // 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; } double expon = 1.0; double factor = 1.0; 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(yyextra->abort_jmp_env)) { return wcserr_set(WCSERR_SET(UNITSERR_PARSER_ERROR), "Internal units parser error parsing '%s'", unitstr); } BEGIN(INITIAL); #ifdef DEBUG fprintf(stderr, "\n%s ->\n", unitstr); #endif #line 7529 "wcsulex.c" while ( /*CONSTCOND*/1 ) /* loops until end-of-file is reached */ { yyg->yy_more_len = 0; if ( yyg->yy_more_flag ) { yyg->yy_more_len = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr); yyg->yy_more_flag = 0; } 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 164 "wcsulex.l" { // Pretend initial whitespace doesn't exist. yy_set_bol(1); } YY_BREAK case 2: YY_RULE_SETUP #line 169 "wcsulex.l" { if (bracket++) { BEGIN(FLUSH); } else { yy_set_bol(1); } } YY_BREAK case 3: YY_RULE_SETUP #line 177 "wcsulex.l" { status = wcserr_set(WCSERR_SET(UNITSERR_BAD_NUM_MULTIPLIER), "Invalid exponent in '%s'", unitstr); BEGIN(FLUSH); } YY_BREAK case 4: YY_RULE_SETUP #line 183 "wcsulex.l" { factor = 10.0; BEGIN(EXPON); } YY_BREAK case 5: YY_RULE_SETUP #line 188 "wcsulex.l" { *func = 1; unput('('); BEGIN(PAREN); } YY_BREAK case 6: YY_RULE_SETUP #line 194 "wcsulex.l" { *func = 2; unput('('); BEGIN(PAREN); } YY_BREAK case 7: YY_RULE_SETUP #line 200 "wcsulex.l" { *func = 3; unput('('); BEGIN(PAREN); } YY_BREAK case 8: YY_RULE_SETUP #line 206 "wcsulex.l" { // Leading binary multiply. status = wcserr_set(WCSERR_SET(UNITSERR_DANGLING_BINOP), "Dangling binary operator in '%s'", unitstr); BEGIN(FLUSH); } YY_BREAK case 9: YY_RULE_SETUP #line 213 "wcsulex.l" // Discard whitespace in INITIAL context. YY_BREAK case 10: YY_RULE_SETUP #line 215 "wcsulex.l" { expon /= 2.0; unput('('); BEGIN(PAREN); } YY_BREAK case 11: YY_RULE_SETUP #line 221 "wcsulex.l" { // Gather terms in parentheses. yyless(0); BEGIN(PAREN); } YY_BREAK case 12: YY_RULE_SETUP #line 227 "wcsulex.l" { if (operator++) { BEGIN(FLUSH); } } YY_BREAK case 13: #line 234 "wcsulex.l" case 14: YY_RULE_SETUP #line 234 "wcsulex.l" { if (operator++) { BEGIN(FLUSH); } else { expon *= -1.0; } } YY_BREAK case 15: YY_RULE_SETUP #line 242 "wcsulex.l" { operator = 0; yyless(0); BEGIN(UNITS); } YY_BREAK case 16: #line 249 "wcsulex.l" case 17: #line 250 "wcsulex.l" case 18: YY_RULE_SETUP #line 250 "wcsulex.l" { operator = 0; yyless(0); BEGIN(PREFIX); } YY_BREAK case 19: YY_RULE_SETUP #line 256 "wcsulex.l" { bracket = !bracket; BEGIN(FLUSH); } YY_BREAK case 20: YY_RULE_SETUP #line 261 "wcsulex.l" { status = wcserr_set(WCSERR_SET(UNITSERR_BAD_INITIAL_SYMBOL), "Invalid symbol in INITIAL context in '%s'", unitstr); BEGIN(FLUSH); } YY_BREAK case 21: YY_RULE_SETUP #line 267 "wcsulex.l" { paren++; operator = 0; yymore(); } YY_BREAK case 22: YY_RULE_SETUP #line 273 "wcsulex.l" { paren--; if (paren) { // Not balanced yet. yymore(); } else { // 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); YY_BUFFER_STATE buf = YY_CURRENT_BUFFER; yy_switch_to_buffer(buf, yyscanner); if (func_r) { status = wcserr_set(WCSERR_SET(UNITSERR_FUNCTION_CONTEXT), "Function in invalid context in '%s'", unitstr); } if (status) { BEGIN(FLUSH); } else { factor *= factor_r; BEGIN(EXPON); } } } YY_BREAK case 23: /* rule 23 can match eol */ YY_RULE_SETUP #line 304 "wcsulex.l" { yymore(); } YY_BREAK case 24: YY_RULE_SETUP #line 308 "wcsulex.l" { factor = 1e-1; BEGIN(UNITS); } YY_BREAK case 25: YY_RULE_SETUP #line 313 "wcsulex.l" { factor = 1e-2; BEGIN(UNITS); } YY_BREAK case 26: YY_RULE_SETUP #line 318 "wcsulex.l" { factor = 1e-3; BEGIN(UNITS); } YY_BREAK case 27: YY_RULE_SETUP #line 323 "wcsulex.l" { factor = 1e-6; BEGIN(UNITS); } YY_BREAK case 28: YY_RULE_SETUP #line 328 "wcsulex.l" { factor = 1e-9; BEGIN(UNITS); } YY_BREAK case 29: YY_RULE_SETUP #line 333 "wcsulex.l" { factor = 1e-12; BEGIN(UNITS); } YY_BREAK case 30: YY_RULE_SETUP #line 338 "wcsulex.l" { factor = 1e-15; BEGIN(UNITS); } YY_BREAK case 31: YY_RULE_SETUP #line 343 "wcsulex.l" { factor = 1e-18; BEGIN(UNITS); } YY_BREAK case 32: YY_RULE_SETUP #line 348 "wcsulex.l" { factor = 1e-21; BEGIN(UNITS); } YY_BREAK case 33: YY_RULE_SETUP #line 353 "wcsulex.l" { factor = 1e-24; BEGIN(UNITS); } YY_BREAK case 34: YY_RULE_SETUP #line 358 "wcsulex.l" { factor = 1e+1; BEGIN(UNITS); } YY_BREAK case 35: YY_RULE_SETUP #line 363 "wcsulex.l" { factor = 1e+2; BEGIN(UNITS); } YY_BREAK case 36: YY_RULE_SETUP #line 368 "wcsulex.l" { factor = 1e+3; BEGIN(UNITS); } YY_BREAK case 37: YY_RULE_SETUP #line 373 "wcsulex.l" { factor = 1e+6; BEGIN(UNITS); } YY_BREAK case 38: YY_RULE_SETUP #line 378 "wcsulex.l" { factor = 1e+9; BEGIN(UNITS); } YY_BREAK case 39: YY_RULE_SETUP #line 383 "wcsulex.l" { factor = 1e+12; BEGIN(UNITS); } YY_BREAK case 40: YY_RULE_SETUP #line 388 "wcsulex.l" { factor = 1e+15; BEGIN(UNITS); } YY_BREAK case 41: YY_RULE_SETUP #line 393 "wcsulex.l" { factor = 1e+18; BEGIN(UNITS); } YY_BREAK case 42: YY_RULE_SETUP #line 398 "wcsulex.l" { factor = 1e+21; BEGIN(UNITS); } YY_BREAK case 43: YY_RULE_SETUP #line 403 "wcsulex.l" { factor = 1e+24; BEGIN(UNITS); } YY_BREAK case 44: YY_RULE_SETUP #line 408 "wcsulex.l" { // Internal parser error. status = wcserr_set(WCSERR_SET(UNITSERR_PARSER_ERROR), "Internal units parser error parsing '%s'", unitstr); BEGIN(FLUSH); } YY_BREAK case 45: YY_RULE_SETUP #line 415 "wcsulex.l" { // Ampere. types[WCSUNITS_CHARGE] += 1.0; types[WCSUNITS_TIME] -= 1.0; BEGIN(EXPON); } YY_BREAK case 46: YY_RULE_SETUP #line 422 "wcsulex.l" { // Julian year (annum). factor *= 31557600.0; types[WCSUNITS_TIME] += 1.0; BEGIN(EXPON); } YY_BREAK case 47: YY_RULE_SETUP #line 429 "wcsulex.l" { // Analogue-to-digital converter units. types[WCSUNITS_COUNT] += 1.0; BEGIN(EXPON); } YY_BREAK case 48: YY_RULE_SETUP #line 435 "wcsulex.l" { // Angstrom. factor *= 1e-10; types[WCSUNITS_LENGTH] += 1.0; BEGIN(EXPON); } YY_BREAK case 49: YY_RULE_SETUP #line 442 "wcsulex.l" { // Minute of arc. factor /= 60.0; types[WCSUNITS_PLANE_ANGLE] += 1.0; BEGIN(EXPON); } YY_BREAK case 50: YY_RULE_SETUP #line 449 "wcsulex.l" { // Second of arc. factor /= 3600.0; types[WCSUNITS_PLANE_ANGLE] += 1.0; BEGIN(EXPON); } YY_BREAK case 51: YY_RULE_SETUP #line 456 "wcsulex.l" { // Astronomical unit. factor *= 1.49598e+11; types[WCSUNITS_LENGTH] += 1.0; BEGIN(EXPON); } YY_BREAK case 52: YY_RULE_SETUP #line 463 "wcsulex.l" { // Barn. factor *= 1e-28; types[WCSUNITS_LENGTH] += 2.0; BEGIN(EXPON); } YY_BREAK case 53: YY_RULE_SETUP #line 470 "wcsulex.l" { // Beam, as in Jy/beam. types[WCSUNITS_BEAM] += 1.0; BEGIN(EXPON); } YY_BREAK case 54: YY_RULE_SETUP #line 476 "wcsulex.l" { // Bin (e.g. histogram). types[WCSUNITS_BIN] += 1.0; BEGIN(EXPON); } YY_BREAK case 55: YY_RULE_SETUP #line 482 "wcsulex.l" { // Bit. types[WCSUNITS_BIT] += 1.0; BEGIN(EXPON); } YY_BREAK case 56: YY_RULE_SETUP #line 488 "wcsulex.l" { // Byte. factor *= 8.0; types[WCSUNITS_BIT] += 1.0; BEGIN(EXPON); } YY_BREAK case 57: YY_RULE_SETUP #line 495 "wcsulex.l" { // Coulomb. types[WCSUNITS_CHARGE] += 1.0; BEGIN(EXPON); } YY_BREAK case 58: YY_RULE_SETUP #line 501 "wcsulex.l" { // Candela. types[WCSUNITS_LUMINTEN] += 1.0; BEGIN(EXPON); } YY_BREAK case 59: YY_RULE_SETUP #line 507 "wcsulex.l" { // Channel. types[WCSUNITS_BIN] += 1.0; BEGIN(EXPON); } YY_BREAK case 60: YY_RULE_SETUP #line 513 "wcsulex.l" { // Count. types[WCSUNITS_COUNT] += 1.0; BEGIN(EXPON); } YY_BREAK case 61: YY_RULE_SETUP #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. factor *= 1e-29 / 3.0; types[WCSUNITS_CHARGE] += 1.0; types[WCSUNITS_LENGTH] += 1.0; BEGIN(EXPON); } YY_BREAK case 63: YY_RULE_SETUP #line 534 "wcsulex.l" { // Day. factor *= 86400.0; types[WCSUNITS_TIME] += 1.0; BEGIN(EXPON); } YY_BREAK case 64: YY_RULE_SETUP #line 541 "wcsulex.l" { // Degree. types[WCSUNITS_PLANE_ANGLE] += 1.0; BEGIN(EXPON); } YY_BREAK case 65: YY_RULE_SETUP #line 547 "wcsulex.l" { // Erg. factor *= 1e-7; types[WCSUNITS_MASS] += 1.0; types[WCSUNITS_LENGTH] += 2.0; types[WCSUNITS_TIME] -= 2.0; BEGIN(EXPON); } YY_BREAK case 66: YY_RULE_SETUP #line 556 "wcsulex.l" { // Electron volt. factor *= 1.6021765e-19; types[WCSUNITS_MASS] += 1.0; types[WCSUNITS_LENGTH] += 2.0; types[WCSUNITS_TIME] -= 2.0; BEGIN(EXPON); } YY_BREAK case 67: YY_RULE_SETUP #line 565 "wcsulex.l" { // Farad. types[WCSUNITS_MASS] -= 1.0; types[WCSUNITS_LENGTH] -= 2.0; types[WCSUNITS_TIME] += 3.0; types[WCSUNITS_CHARGE] += 2.0; BEGIN(EXPON); } YY_BREAK case 68: YY_RULE_SETUP #line 574 "wcsulex.l" { // Gauss. factor *= 1e-4; types[WCSUNITS_MASS] += 1.0; types[WCSUNITS_TIME] += 1.0; types[WCSUNITS_CHARGE] -= 1.0; BEGIN(EXPON); } YY_BREAK case 69: YY_RULE_SETUP #line 583 "wcsulex.l" { // Gram. factor *= 1e-3; types[WCSUNITS_MASS] += 1.0; BEGIN(EXPON); } YY_BREAK case 70: YY_RULE_SETUP #line 590 "wcsulex.l" { // Henry. types[WCSUNITS_MASS] += 1.0; types[WCSUNITS_LENGTH] += 2.0; types[WCSUNITS_TIME] += 2.0; types[WCSUNITS_CHARGE] -= 2.0; BEGIN(EXPON); } YY_BREAK case 71: YY_RULE_SETUP #line 599 "wcsulex.l" { // Hour. factor *= 3600.0; types[WCSUNITS_TIME] += 1.0; BEGIN(EXPON); } YY_BREAK case 72: YY_RULE_SETUP #line 606 "wcsulex.l" { // Hertz. types[WCSUNITS_TIME] -= 1.0; BEGIN(EXPON); } YY_BREAK case 73: YY_RULE_SETUP #line 612 "wcsulex.l" { // Joule. types[WCSUNITS_MASS] += 1.0; types[WCSUNITS_LENGTH] += 2.0; types[WCSUNITS_TIME] -= 2.0; BEGIN(EXPON); } YY_BREAK case 74: YY_RULE_SETUP #line 620 "wcsulex.l" { // Jansky. factor *= 1e-26; types[WCSUNITS_MASS] += 1.0; types[WCSUNITS_TIME] -= 2.0; BEGIN(EXPON); } YY_BREAK case 75: YY_RULE_SETUP #line 628 "wcsulex.l" { // Kelvin. types[WCSUNITS_TEMPERATURE] += 1.0; BEGIN(EXPON); } YY_BREAK case 76: YY_RULE_SETUP #line 634 "wcsulex.l" { // Lumen. types[WCSUNITS_LUMINTEN] += 1.0; types[WCSUNITS_SOLID_ANGLE] += 1.0; BEGIN(EXPON); } YY_BREAK case 77: YY_RULE_SETUP #line 641 "wcsulex.l" { // Lux. types[WCSUNITS_LUMINTEN] += 1.0; types[WCSUNITS_SOLID_ANGLE] += 1.0; types[WCSUNITS_LENGTH] -= 2.0; BEGIN(EXPON); } YY_BREAK case 78: YY_RULE_SETUP #line 649 "wcsulex.l" { // Light year. factor *= 2.99792458e8 * 31557600.0; types[WCSUNITS_LENGTH] += 1.0; BEGIN(EXPON); } YY_BREAK case 79: YY_RULE_SETUP #line 656 "wcsulex.l" { // Metre. types[WCSUNITS_LENGTH] += 1.0; BEGIN(EXPON); } YY_BREAK case 80: YY_RULE_SETUP #line 662 "wcsulex.l" { // Stellar magnitude. types[WCSUNITS_MAGNITUDE] += 1.0; BEGIN(EXPON); } YY_BREAK case 81: YY_RULE_SETUP #line 668 "wcsulex.l" { // Milli-arcsec. factor /= 3600e+3; types[WCSUNITS_PLANE_ANGLE] += 1.0; BEGIN(EXPON); } YY_BREAK case 82: YY_RULE_SETUP #line 675 "wcsulex.l" { // Minute. factor *= 60.0; types[WCSUNITS_TIME] += 1.0; BEGIN(EXPON); } YY_BREAK case 83: YY_RULE_SETUP #line 682 "wcsulex.l" { // Mole. types[WCSUNITS_MOLE] += 1.0; BEGIN(EXPON); } YY_BREAK case 84: YY_RULE_SETUP #line 688 "wcsulex.l" { // Newton. types[WCSUNITS_MASS] += 1.0; types[WCSUNITS_LENGTH] += 1.0; types[WCSUNITS_TIME] -= 2.0; BEGIN(EXPON); } YY_BREAK case 85: YY_RULE_SETUP #line 696 "wcsulex.l" { // Ohm. types[WCSUNITS_MASS] += 1.0; types[WCSUNITS_LENGTH] += 2.0; types[WCSUNITS_TIME] -= 1.0; types[WCSUNITS_CHARGE] -= 2.0; BEGIN(EXPON); } YY_BREAK case 86: YY_RULE_SETUP #line 705 "wcsulex.l" { // Pascal. types[WCSUNITS_MASS] += 1.0; types[WCSUNITS_LENGTH] -= 1.0; types[WCSUNITS_TIME] -= 2.0; BEGIN(EXPON); } YY_BREAK case 87: YY_RULE_SETUP #line 713 "wcsulex.l" { // Parsec. factor *= 3.0857e16; types[WCSUNITS_LENGTH] += 1.0; BEGIN(EXPON); } YY_BREAK case 88: YY_RULE_SETUP #line 720 "wcsulex.l" { // Photon. types[WCSUNITS_COUNT] += 1.0; BEGIN(EXPON); } YY_BREAK case 89: YY_RULE_SETUP #line 726 "wcsulex.l" { // Pixel. types[WCSUNITS_PIXEL] += 1.0; BEGIN(EXPON); } YY_BREAK case 90: YY_RULE_SETUP #line 732 "wcsulex.l" { // Rayleigh. factor *= 1e10 / (4.0 * PI); types[WCSUNITS_LENGTH] -= 2.0; types[WCSUNITS_TIME] -= 1.0; types[WCSUNITS_SOLID_ANGLE] -= 1.0; BEGIN(EXPON); } YY_BREAK case 91: YY_RULE_SETUP #line 741 "wcsulex.l" { // Radian. factor *= 180.0 / PI; types[WCSUNITS_PLANE_ANGLE] += 1.0; BEGIN(EXPON); } YY_BREAK case 92: YY_RULE_SETUP #line 748 "wcsulex.l" { // Rydberg. factor *= 13.605692 * 1.6021765e-19; types[WCSUNITS_MASS] += 1.0; types[WCSUNITS_LENGTH] += 2.0; types[WCSUNITS_TIME] -= 2.0; BEGIN(EXPON); } YY_BREAK case 93: YY_RULE_SETUP #line 757 "wcsulex.l" { // Siemen. types[WCSUNITS_MASS] -= 1.0; types[WCSUNITS_LENGTH] -= 2.0; types[WCSUNITS_TIME] += 1.0; types[WCSUNITS_CHARGE] += 2.0; BEGIN(EXPON); } YY_BREAK case 94: YY_RULE_SETUP #line 766 "wcsulex.l" { // Second. types[WCSUNITS_TIME] += 1.0; BEGIN(EXPON); } YY_BREAK case 95: YY_RULE_SETUP #line 772 "wcsulex.l" { // Solar luminosity. factor *= 3.8268e26; types[WCSUNITS_MASS] += 1.0; types[WCSUNITS_LENGTH] += 2.0; types[WCSUNITS_TIME] -= 3.0; BEGIN(EXPON); } YY_BREAK case 96: YY_RULE_SETUP #line 781 "wcsulex.l" { // Solar mass. factor *= 1.9891e30; types[WCSUNITS_MASS] += 1.0; BEGIN(EXPON); } YY_BREAK case 97: YY_RULE_SETUP #line 788 "wcsulex.l" { // Solar radius. factor *= 6.9599e8; types[WCSUNITS_LENGTH] += 1.0; BEGIN(EXPON); } YY_BREAK case 98: YY_RULE_SETUP #line 795 "wcsulex.l" { // Steradian. types[WCSUNITS_SOLID_ANGLE] += 1.0; BEGIN(EXPON); } YY_BREAK case 99: YY_RULE_SETUP #line 801 "wcsulex.l" { // Sun (with respect to). types[WCSUNITS_SOLRATIO] += 1.0; BEGIN(EXPON); } YY_BREAK case 100: YY_RULE_SETUP #line 807 "wcsulex.l" { // Tesla. types[WCSUNITS_MASS] += 1.0; types[WCSUNITS_TIME] += 1.0; types[WCSUNITS_CHARGE] -= 1.0; BEGIN(EXPON); } YY_BREAK 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 822 "wcsulex.l" { // Unified atomic mass unit. factor *= 1.6605387e-27; types[WCSUNITS_MASS] += 1.0; BEGIN(EXPON); } YY_BREAK case 103: YY_RULE_SETUP #line 829 "wcsulex.l" { // Volt. types[WCSUNITS_MASS] += 1.0; types[WCSUNITS_LENGTH] += 1.0; types[WCSUNITS_TIME] -= 2.0; types[WCSUNITS_CHARGE] -= 1.0; BEGIN(EXPON); } YY_BREAK case 104: YY_RULE_SETUP #line 838 "wcsulex.l" { // Voxel. types[WCSUNITS_VOXEL] += 1.0; BEGIN(EXPON); } YY_BREAK case 105: YY_RULE_SETUP #line 844 "wcsulex.l" { // Watt. types[WCSUNITS_MASS] += 1.0; types[WCSUNITS_LENGTH] += 2.0; types[WCSUNITS_TIME] -= 3.0; BEGIN(EXPON); } YY_BREAK case 106: YY_RULE_SETUP #line 852 "wcsulex.l" { // Weber. types[WCSUNITS_MASS] += 1.0; types[WCSUNITS_LENGTH] += 2.0; types[WCSUNITS_TIME] += 1.0; types[WCSUNITS_CHARGE] -= 1.0; BEGIN(EXPON); } YY_BREAK case 107: YY_RULE_SETUP #line 861 "wcsulex.l" { // Internal parser error. status = wcserr_set(WCSERR_SET(UNITSERR_PARSER_ERROR), "Internal units parser error parsing '%s'", unitstr); BEGIN(FLUSH); } YY_BREAK case 108: YY_RULE_SETUP #line 868 "wcsulex.l" { // Exponentiation. if (operator++) { BEGIN(FLUSH); } } YY_BREAK case 109: YY_RULE_SETUP #line 875 "wcsulex.l" { int i; sscanf(yytext, " %d", &i); expon *= (double)i; add(&factor, types, &expon, scale, units); operator = 0; BEGIN(INITIAL); } YY_BREAK case 110: YY_RULE_SETUP #line 884 "wcsulex.l" { int i; sscanf(yytext, " (%d)", &i); expon *= (double)i; add(&factor, types, &expon, scale, units); operator = 0; BEGIN(INITIAL); } YY_BREAK case 111: YY_RULE_SETUP #line 893 "wcsulex.l" { 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 112: YY_RULE_SETUP #line 902 "wcsulex.l" { 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 113: YY_RULE_SETUP #line 913 "wcsulex.l" { // Multiply. if (operator++) { BEGIN(FLUSH); } else { add(&factor, types, &expon, scale, units); BEGIN(INITIAL); } } YY_BREAK case 114: YY_RULE_SETUP #line 923 "wcsulex.l" { // Multiply. if (operator) { BEGIN(FLUSH); } else { add(&factor, types, &expon, scale, units); unput('('); BEGIN(INITIAL); } } YY_BREAK case 115: YY_RULE_SETUP #line 934 "wcsulex.l" { // Multiply. if (operator) { BEGIN(FLUSH); } else { add(&factor, types, &expon, scale, units); BEGIN(INITIAL); } } YY_BREAK case 116: YY_RULE_SETUP #line 944 "wcsulex.l" { // Divide. if (operator++) { BEGIN(FLUSH); } else { add(&factor, types, &expon, scale, units); expon = -1.0; BEGIN(INITIAL); } } YY_BREAK case 117: YY_RULE_SETUP #line 955 "wcsulex.l" { add(&factor, types, &expon, scale, units); bracket = !bracket; BEGIN(FLUSH); } YY_BREAK case 118: YY_RULE_SETUP #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 119: YY_RULE_SETUP #line 967 "wcsulex.l" { // Discard any remaining input. } YY_BREAK case YY_STATE_EOF(INITIAL): case YY_STATE_EOF(PAREN): case YY_STATE_EOF(PREFIX): case YY_STATE_EOF(UNITS): case YY_STATE_EOF(EXPON): case YY_STATE_EOF(FLUSH): #line 971 "wcsulex.l" { // End-of-string. if (YY_START == EXPON) { add(&factor, types, &expon, scale, units); } if (bracket) { status = wcserr_set(WCSERR_SET(UNITSERR_UNBAL_BRACKET), "Unbalanced bracket in '%s'", unitstr); } else if (paren) { status = wcserr_set(WCSERR_SET(UNITSERR_UNBAL_PAREN), "Unbalanced parenthesis in '%s'", unitstr); } else if (operator == 1) { status = wcserr_set(WCSERR_SET(UNITSERR_DANGLING_BINOP), "Dangling binary operator in '%s'", unitstr); } else if (operator) { status = wcserr_set(WCSERR_SET(UNITSERR_CONSEC_BINOPS), "Consecutive binary operators in '%s'", unitstr); #ifdef DEBUG } else { fprintf(stderr, "EOS\n"); #endif } if (status) { for (int i = 0; i < WCSUNITS_NTYPE; i++) { units[i] = 0.0; *scale = 0.0; } } return status; } YY_BREAK case 120: YY_RULE_SETUP #line 1005 "wcsulex.l" ECHO; YY_BREAK #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 - yyg->yytext_ptr) - 1; /* Undo the effects of YY_DO_BEFORE_ACTION. */ *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 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. */ 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; } /* Note that here we test for yy_c_buf_p "<=" to the position * of the first EOB in the buffer, since yy_c_buf_p will * already have been incremented past the NUL character * (since all states make transitions on EOB to the * end-of-buffer state). Contrast this with the test * in input(). */ 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; yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state( yyscanner ); /* Okay, we're now positioned to make the NUL * transition. We couldn't have * yy_get_previous_state() go ahead and do it * for us because it doesn't know how to deal * with the possibility of jamming (and we don't * want to build jamming into it because then it * will run more slowly). */ yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner); yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; if ( yy_next_state ) { /* Consume the NUL. */ yy_cp = ++yyg->yy_c_buf_p; yy_current_state = yy_next_state; goto yy_match; } else { yy_cp = yyg->yy_c_buf_p; goto yy_find_action; } } else switch ( yy_get_next_buffer( yyscanner ) ) { case EOB_ACT_END_OF_FILE: { yyg->yy_did_buffer_switch_on_eof = 0; if ( yywrap( yyscanner ) ) { /* Note: because we've taken care in * yy_get_next_buffer() to have 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. */ yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ; yy_act = YY_STATE_EOF(YY_START); goto do_action; } else { if ( ! yyg->yy_did_buffer_switch_on_eof ) YY_NEW_FILE; } break; } case EOB_ACT_CONTINUE_SCAN: yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state( yyscanner ); yy_cp = yyg->yy_c_buf_p; yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; goto yy_match; case EOB_ACT_LAST_MATCH: yyg->yy_c_buf_p = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars]; yy_current_state = yy_get_previous_state( yyscanner ); yy_cp = yyg->yy_c_buf_p; yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; goto yy_find_action; } break; } default: YY_FATAL_ERROR( "fatal flex scanner internal error--no action found" ); } /* end of action switch */ } /* end of scanning one token */ } /* end of user's declarations */ } /* end of yylex */ /* yy_get_next_buffer - try to read in a new buffer * * Returns a code representing an action: * EOB_ACT_LAST_MATCH - * EOB_ACT_CONTINUE_SCAN - continue scanning from current position * EOB_ACT_END_OF_FILE - end of file */ static int yy_get_next_buffer (yyscan_t yyscanner) { 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 ( 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 ( 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. */ return EOB_ACT_END_OF_FILE; } else { /* We matched some text prior to the EOB, first * process it. */ return EOB_ACT_LAST_MATCH; } } /* Try to read more data. */ /* First move last chars to start of buffer. */ number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr - 1); for ( i = 0; i < number_to_move; ++i ) *(dest++) = *(source++); if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) /* don't do the read, it's not guaranteed to return an EOF, * just force an EOF */ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0; else { int num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; while ( num_to_read <= 0 ) { /* Not enough room in the buffer - grow it. */ /* just a shorter name for the current buffer */ YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE; int yy_c_buf_p_offset = (int) (yyg->yy_c_buf_p - b->yy_ch_buf); if ( b->yy_is_our_buffer ) { int new_size = b->yy_buf_size * 2; if ( new_size <= 0 ) b->yy_buf_size += b->yy_buf_size / 8; else b->yy_buf_size *= 2; b->yy_ch_buf = (char *) /* Include room in for 2 EOB chars. */ 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 = NULL; if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "fatal error - scanner input buffer overflow" ); 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; } if ( num_to_read > YY_READ_BUF_SIZE ) num_to_read = YY_READ_BUF_SIZE; /* Read in more data. */ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), yyg->yy_n_chars, num_to_read ); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } if ( yyg->yy_n_chars == 0 ) { if ( number_to_move == YY_MORE_ADJ ) { ret_val = EOB_ACT_END_OF_FILE; yyrestart( yyin , yyscanner); } else { ret_val = EOB_ACT_LAST_MATCH; YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_EOF_PENDING; } } else ret_val = EOB_ACT_CONTINUE_SCAN; 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. */ 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); } 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; 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 (yyscan_t yyscanner) { 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 = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp ) { if ( *yy_cp ) { yy_current_state = yy_nxt[yy_current_state][YY_SC_TO_UI(*yy_cp)]; } else yy_current_state = yy_NUL_trans[yy_current_state]; if ( yy_accept[yy_current_state] ) { yyg->yy_last_accepting_state = yy_current_state; yyg->yy_last_accepting_cpos = yy_cp; } } return yy_current_state; } /* yy_try_NUL_trans - try to make a transition on the NUL character * * synopsis * next_state = yy_try_NUL_trans( current_state ); */ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner) { 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); if ( ! yy_is_jam ) { if ( yy_accept[yy_current_state] ) { 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 static void yyunput (int c, char * yy_bp , yyscan_t yyscanner) { char *yy_cp; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; 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. */ 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]; char *source = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]; while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) *--dest = *--source; yy_cp += (int) (dest - source); yy_bp += (int) (dest - source); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = 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" ); } *--yy_cp = (char) c; 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 (yyscan_t yyscanner) #else static int input (yyscan_t yyscanner) #endif { int c; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; *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 ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) /* This was really a NUL. */ *yyg->yy_c_buf_p = '\0'; else { /* need more input */ int offset = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr); ++yyg->yy_c_buf_p; switch ( yy_get_next_buffer( yyscanner ) ) { case EOB_ACT_LAST_MATCH: /* This happens because yy_g_n_b() * sees that we've accumulated a * token and flags that we need to * try matching the token before * proceeding. But for input(), * there's no matching to consider. * So convert the EOB_ACT_LAST_MATCH * to EOB_ACT_END_OF_FILE. */ /* Reset buffer status. */ yyrestart( yyin , yyscanner); /*FALLTHROUGH*/ case EOB_ACT_END_OF_FILE: { if ( yywrap( yyscanner ) ) return 0; if ( ! yyg->yy_did_buffer_switch_on_eof ) YY_NEW_FILE; #ifdef __cplusplus return yyinput(yyscanner); #else return input(yyscanner); #endif } case EOB_ACT_CONTINUE_SCAN: yyg->yy_c_buf_p = yyg->yytext_ptr + offset; break; } } } 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'); return c; } #endif /* ifndef YY_NO_INPUT */ /** 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 yyrestart (FILE * input_file , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if ( ! YY_CURRENT_BUFFER ){ yyensure_buffer_stack (yyscanner); YY_CURRENT_BUFFER_LVALUE = yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); } 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 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 * yypop_buffer_state(); * yypush_buffer_state(new_buffer); */ yyensure_buffer_stack (yyscanner); if ( YY_CURRENT_BUFFER == new_buffer ) return; if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ *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; yy_load_buffer_state( yyscanner ); /* We don't actually know whether we did this switch during * 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. */ yyg->yy_did_buffer_switch_on_eof = 1; } static void yy_load_buffer_state (yyscan_t yyscanner) { 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 yy_create_buffer (FILE * file, int size , yyscan_t yyscanner) { YY_BUFFER_STATE b; b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); if ( ! b ) 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 *) yyalloc( (yy_size_t) (b->yy_buf_size + 2) , yyscanner ); if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); b->yy_is_our_buffer = 1; yy_init_buffer( b, file , yyscanner); return b; } /** Destroy the buffer. * @param b a buffer created with yy_create_buffer() * @param yyscanner The scanner object. */ void yy_delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if ( ! b ) return; if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; if ( b->yy_is_our_buffer ) yyfree( (void *) b->yy_ch_buf , yyscanner ); 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 yyrestart() or at EOF. */ static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner) { int oerrno = errno; 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 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){ b->yy_bs_lineno = 1; b->yy_bs_column = 0; } b->yy_is_interactive = 0; errno = oerrno; } /** 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 yy_flush_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if ( ! b ) return; b->yy_n_chars = 0; /* We always need two end-of-buffer characters. The first causes * a transition to the end-of-buffer state. The second causes * a jam in that state. */ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; b->yy_buf_pos = &b->yy_ch_buf[0]; b->yy_at_bol = 1; b->yy_buffer_status = YY_BUFFER_NEW; if ( b == YY_CURRENT_BUFFER ) 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 yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (new_buffer == NULL) return; yyensure_buffer_stack(yyscanner); /* This block is copied from yy_switch_to_buffer. */ if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ *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) yyg->yy_buffer_stack_top++; YY_CURRENT_BUFFER_LVALUE = new_buffer; /* 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 yypop_buffer_state (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (!YY_CURRENT_BUFFER) return; yy_delete_buffer(YY_CURRENT_BUFFER , yyscanner); YY_CURRENT_BUFFER_LVALUE = NULL; if (yyg->yy_buffer_stack_top > 0) --yyg->yy_buffer_stack_top; if (YY_CURRENT_BUFFER) { 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 yyensure_buffer_stack (yyscan_t yyscanner) { 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; /* 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*) , 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 (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){ /* Increase the buffer to prepare for a possible push. */ yy_size_t grow_size = 8 /* arbitrary grow size */; 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*) , yyscanner); if ( ! yyg->yy_buffer_stack ) YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); /* zero only the new slots.*/ 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 * @param yyscanner The scanner object. * @return the newly allocated buffer state object. */ YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size , yyscan_t yyscanner) { YY_BUFFER_STATE b; if ( size < 2 || 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 NULL; b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); 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 = 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; yy_switch_to_buffer( b , yyscanner ); return b; } /** 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 * yy_scan_bytes() instead. */ YY_BUFFER_STATE yy_scan_string (const char * yystr , yyscan_t yyscanner) { return yy_scan_bytes( yystr, (int) strlen(yystr) , yyscanner); } /** 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 yy_scan_bytes (const char * yybytes, int _yybytes_len , yyscan_t yyscanner) { YY_BUFFER_STATE b; char *buf; yy_size_t n; int i; /* Get memory for full buffer, including space for trailing EOB's. */ n = (yy_size_t) (_yybytes_len + 2); buf = (char *) yyalloc( n , yyscanner ); if ( ! buf ) 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 = yy_scan_buffer( buf, n , yyscanner); if ( ! b ) 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. */ b->yy_is_our_buffer = 1; return b; } #ifndef YY_EXIT_FAILURE #define YY_EXIT_FAILURE 2 #endif static void yynoreturn yy_fatal_error (const char* msg , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; (void)yyg; fprintf( stderr, "%s\n", msg ); exit( YY_EXIT_FAILURE ); } /* Redefine yyless() so it works in section 3 code. */ #undef yyless #define yyless(n) \ do \ { \ /* Undo effects of setting up yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(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 yyget_column (yyscan_t yyscanner) { 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 *yyget_in (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyin; } /** Get the output stream. * @param yyscanner The scanner object. */ FILE *yyget_out (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyout; } /** Get the length of the current token. * @param yyscanner The scanner object. */ int yyget_leng (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyleng; } /** Get the current token. * @param yyscanner The scanner object. */ 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) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yyextra = user_defined ; } /** Set the current 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 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. * @param yyscanner The scanner object. * @see yy_switch_to_buffer */ 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) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yy_flex_debug; } void yyset_debug (int _bdebug , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yy_flex_debug = _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) { 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 ); } /* 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 ) { 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. */ 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 yyin = stdin; yyout = stdout; #else yyin = NULL; yyout = NULL; #endif /* For future reference: Set errno on error, since we are called by * yylex_init() */ return 0; } /* 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){ yy_delete_buffer( YY_CURRENT_BUFFER , yyscanner ); YY_CURRENT_BUFFER_LVALUE = NULL; yypop_buffer_state(yyscanner); } /* Destroy the stack itself. */ 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 * yylex() is called, initialization will occur. */ yy_init_globals( yyscanner); /* Destroy the main struct (reentrant only). */ yyfree ( yyscanner , yyscanner ); yyscanner = NULL; return 0; } /* * Internal utility routines. */ #ifndef yytext_ptr static void yy_flex_strncpy (char* s1, const char * s2, int n , yyscan_t yyscanner) { 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 (const char * s , yyscan_t yyscanner) { int n; for ( n = 0; s[n]; ++n ) ; return n; } #endif void *yyalloc (yy_size_t size , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; (void)yyg; return malloc(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 * because both ANSI C and C++ allow castless assignment from * any pointer type to void*, and deal with argument conversions * as though doing an assignment. */ return realloc(ptr, size); } void yyfree (void * ptr , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; (void)yyg; free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ } #define YYTABLES_NAME "yytables" #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; } /*---------------------------------------------------------------------------- * Accumulate a term in a units specification and reset work variables. *---------------------------------------------------------------------------*/ void add( double *factor, double types[], double *expon, double *scale, double units[]) { *scale *= pow(*factor, *expon); for (int i = 0; i < WCSUNITS_NTYPE; i++) { units[i] += *expon * types[i]; types[i] = 0.0; } *expon = 1.0; *factor = 1.0; return; } astropy-astropy-201cddb/cextern/wcslib/C/flexed/wcsutrn.c000066400000000000000000007455221507226315300236530ustar00rootroot00000000000000#line 2 "wcsutrn.c" #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 #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 #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 #endif #ifdef yyrestart #define wcsutrnrestart_ALREADY_DEFINED #else #define yyrestart wcsutrnrestart #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 #endif #ifdef yyfree #define wcsutrnfree_ALREADY_DEFINED #else #define yyfree wcsutrnfree #endif /* First, we deal with platform-specific or compiler-specific issues. */ /* begin standard C headers. */ #include #include #include #include /* end standard C headers. */ /* flex integer type definitions */ #ifndef FLEXINT_H #define FLEXINT_H /* C99 systems have . Non-C99 systems may or may not. */ #if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, * if you want the limit (max/min) macros for int types. */ #ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS 1 #endif #include typedef int8_t flex_int8_t; typedef uint8_t flex_uint8_t; typedef int16_t flex_int16_t; typedef uint16_t flex_uint16_t; typedef int32_t flex_int32_t; typedef uint32_t flex_uint32_t; #else typedef signed char flex_int8_t; typedef short int flex_int16_t; typedef int flex_int32_t; typedef unsigned char flex_uint8_t; typedef unsigned short int flex_uint16_t; typedef unsigned int flex_uint32_t; /* Limits of integral types. */ #ifndef INT8_MIN #define INT8_MIN (-128) #endif #ifndef INT16_MIN #define INT16_MIN (-32767-1) #endif #ifndef INT32_MIN #define INT32_MIN (-2147483647-1) #endif #ifndef INT8_MAX #define INT8_MAX (127) #endif #ifndef INT16_MAX #define INT16_MAX (32767) #endif #ifndef INT32_MAX #define INT32_MAX (2147483647) #endif #ifndef UINT8_MAX #define UINT8_MAX (255U) #endif #ifndef UINT16_MAX #define UINT16_MAX (65535U) #endif #ifndef UINT32_MAX #define UINT32_MAX (4294967295U) #endif #ifndef SIZE_MAX #define SIZE_MAX (~(size_t)0) #endif #endif /* ! C99 */ #endif /* ! FLEXINT_H */ /* begin standard C++ headers. */ /* TODO: this is always defined, so inline it */ #define yyconst const #if defined(__GNUC__) && __GNUC__ >= 3 #define yynoreturn __attribute__((__noreturn__)) #else #define yynoreturn #endif /* Returned upon end-of-file. */ #define YY_NULL 0 /* 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) ((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 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 ((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 yyrestart( yyin , yyscanner ) #define YY_END_OF_BUFFER_CHAR 0 /* Size of default input buffer. */ #ifndef YY_BUF_SIZE #ifdef __ia64__ /* On IA-64, the buffer size is 16k, not 8k. * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. * Ditto for the __ia64__ case accordingly. */ #define YY_BUF_SIZE 32768 #else #define YY_BUF_SIZE 16384 #endif /* __ia64__ */ #endif /* The state buf must be large enough to hold one state per character in the main buffer. */ #define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) #ifndef YY_TYPEDEF_YY_BUFFER_STATE #define YY_TYPEDEF_YY_BUFFER_STATE typedef struct yy_buffer_state *YY_BUFFER_STATE; #endif #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 yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ *yy_cp = yyg->yy_hold_char; \ YY_RESTORE_YY_MORE_OFFSET \ 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, yyg->yytext_ptr , yyscanner ) #ifndef YY_STRUCT_YY_BUFFER_STATE #define YY_STRUCT_YY_BUFFER_STATE struct yy_buffer_state { FILE *yy_input_file; char *yy_ch_buf; /* input buffer */ char *yy_buf_pos; /* current position in input buffer */ /* Size of input buffer in bytes, not including room for EOB * characters. */ int yy_buf_size; /* Number of characters read into yy_ch_buf, not including EOB * characters. */ int yy_n_chars; /* Whether we "own" the buffer - i.e., we know we created it, * and can realloc() it to grow it, and should free() it to * delete it. */ int yy_is_our_buffer; /* Whether this is an "interactive" input source; if so, and * if we're using stdio for input, then we want to use getc() * instead of fread(), to make sure we stop fetching input after * each newline. */ int yy_is_interactive; /* Whether we're considered to be at the beginning of a line. * If so, '^' rules will be active on the next match, otherwise * not. */ int yy_at_bol; 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. */ int yy_fill_buffer; int yy_buffer_status; #define YY_BUFFER_NEW 0 #define YY_BUFFER_NORMAL 1 /* When an EOF's been seen but there's still some text to process * then we mark the buffer as YY_EOF_PENDING, to indicate that we * shouldn't try reading from the input source any more. We might * still have a bunch of tokens to match, though, because of * possible backing-up. * * When we actually see the EOF, we change the status to "new" * (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 */ /* 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 ( 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 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 ){ \ yyensure_buffer_stack (yyscanner); \ YY_CURRENT_BUFFER_LVALUE = \ 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 ){\ yyensure_buffer_stack (yyscanner); \ YY_CURRENT_BUFFER_LVALUE = \ 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(yyscanner) (/*CONSTCOND*/1) #define YY_SKIP_YYWRAP typedef flex_uint8_t YY_CHAR; typedef int yy_state_type; #define yytext_ptr yytext_r static const flex_int16_t yy_nxt[][128] = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 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, 9, 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, 10, 11, 12, 13, 12, 12, 14, 15, 12, 16, 17, 12, 18, 12, 19, 20, 12, 21, 22, 12, 12, 23, 12, 12, 24, 12, 8, 8, 8, 8, 8, 8, 25, 12, 12, 26, 12, 12, 12, 27, 12, 12, 28, 12, 29, 12, 12, 30, 12, 31, 32, 12, 12, 33, 12, 12, 34, 12, 8, 8, 8, 8, 8 }, { 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, 35, 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, 10, 11, 12, 13, 12, 12, 14, 15, 12, 16, 17, 12, 18, 12, 19, 20, 12, 21, 22, 12, 12, 23, 12, 12, 24, 12, 36, 8, 8, 8, 8, 8, 25, 12, 12, 26, 12, 12, 12, 27, 12, 12, 28, 12, 29, 12, 12, 30, 12, 31, 32, 12, 12, 33, 12, 12, 34, 12, 8, 8, 8, 8, 8 }, { 7, 37, 37, 37, 37, 37, 37, 37, 37, 37, 38, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 39, 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, 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, 37, 37, 37, 37, 37, 37, 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, 37, 37, 37, 37, 37 }, { 7, 37, 37, 37, 37, 37, 37, 37, 37, 37, 38, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 39, 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, 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, 37, 37, 37, 37, 37, 37, 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, 37, 37, 37, 37, 37 }, { 7, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 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, 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 }, { 7, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 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, 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 }, { -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, -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 }, { 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 }, { 7, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, 42, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9 }, { 7, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 44, 43, 43, 43, 43, 43, 43, 43, 43, -10, -10, -10, -10, -10, -10, 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 }, { 7, -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, -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, 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, 47, 43, -11, -11, -11, -11, -11 }, { 7, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -12, -12, -12, -12, -12, -12, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -12, -12, -12, -12, -12 }, { 7, -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, -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, 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, 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 }, { 7, -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, -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, -14, -14, -14, -14, -14, -14, -14, -14, -14, -14, -14, -14, -14, -14, 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, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -14, -14, -14, -14, -14 }, { 7, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -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, 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 }, { 7, -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, -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, 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, 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, 43, 43, 43, -16, -16, -16, -16, -16 }, { 7, -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, -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, 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, 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 }, { 7, -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, -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, -18, -18, -18, -18, -18, -18, -18, -18, -18, -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, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -18, -18, -18, -18, -18 }, { 7, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, 43, 43, 43, 43, 43, 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, 62, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -19, -19, -19, -19, -19 }, { 7, -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, -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, 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, 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, -20, -20, -20, -20, -20 }, { 7, -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, -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, 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, 43, -21, -21, -21, -21, -21, -21, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -21, -21, -21, -21, -21 }, { 7, -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, -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, -22, -22, -22, -22, -22, -22, -22, -22, -22, -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, 43, 43, 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 }, { 7, -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, -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, -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, 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, 69, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -23, -23, -23, -23, -23 }, { 7, -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, -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, 70, 43, 43, 43, 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, 43, 43, 43, -24, -24, -24, -24, -24 }, { 7, -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, -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, -25, -25, -25, -25, -25, -25, -25, -25, -25, -25, -25, -25, -25, -25, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 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, 72, 43, 43, 43, 73, 43, 43, 43, 43, 43, 43, 43, 43, -25, -25, -25, -25, -25 }, { 7, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -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, 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 }, { 7, -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, -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, -27, -27, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -27, -27, -27, -27, -27, -27, 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, 53, -27, -27, -27, -27, -27 }, { 7, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, 43, 43, 43, 43, 43, 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, 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 }, { 7, -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, -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, 43, 43, 43, 43, 43, 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, 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 }, { 7, -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, -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, 43, 43, 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, 65, 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 }, { 7, -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, -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, -31, -31, 43, 43, 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, -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 }, { 7, -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, -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, 43, 43, 43, 43, 43, 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, 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 }, { 7, -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, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -33, -33, -33, -33, -33, -33, 43, 43, 43, 43, 43, 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 }, { 7, -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, 43, 43, 43, 43, 43, 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, -34, 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 }, { 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, 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, 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 }, { 7, -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, -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, -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 }, { 7, -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, -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 }, { 7, -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, -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, -38, -38, -38, -38, -38, -38, -38, -38, -38 }, { 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, 83, 83, 83, 83, 83 }, { 7, -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, 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, 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, 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, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87 }, { 7, -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, -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, -42, -42, -42, -42, -42 }, { 7, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -43, -43, -43, -43, -43, -43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -43, -43, -43, -43, -43 }, { 7, -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, 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, 43, -44, -44, -44, -44, -44, -44, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -44, -44, -44, -44, -44 }, { 7, -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, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, -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, 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 }, { 7, -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, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -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, 43, 43, 43, 43, 43, 43, 43, -46, -46, -46, -46, -46 }, { 7, -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, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 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, 91, 43, 43, 43, 43, 43, 43, -47, -47, -47, -47, -47 }, { 7, -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, -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, 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, 43, 43, 43, -48, -48, -48, -48, -48 }, { 7, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -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, -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 }, { 7, -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, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -50, -50, -50, -50, -50, -50, 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 }, { 7, -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, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 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 }, { 7, -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, 43, 43, 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, -52, -52, -52, -52, -52, 43, 43, 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, -52, -52, -52, -52 }, { 7, -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, 43, 43, 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, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -53, -53, -53, -53, -53 }, { 7, -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, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -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, -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 }, { 7, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -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, 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, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -55, -55, -55, -55, -55 }, { 7, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -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, 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 }, { 7, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -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, 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, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -57, -57, -57, -57, -57 }, { 7, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -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, 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 }, { 7, -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, -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, 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, 43, 43, 43, -59, -59, -59, -59, -59 }, { 7, -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, 43, 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, -60, -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 }, { 7, -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, 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, -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, 43, 43, 43, -61, -61, -61, -61, -61 }, { 7, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -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, 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 }, { 7, -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, 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, 43, 43, 43, 43, 43, 43, 43, 43, -63, -63, -63, -63, -63 }, { 7, -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, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -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, 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, 43, 43, 43, -64, -64, -64, -64, -64 }, { 7, -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, 43, 43, 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, 105, 43, 43, 43, 43, 43, 43, 43, -65, -65, -65, -65, -65 }, { 7, -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, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -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, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -66, -66, -66, -66, -66 }, { 7, -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, 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, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -67, -67, -67, -67, -67 }, { 7, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -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, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -68, -68, -68, -68, -68 }, { 7, -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, 43, 43, 43, 43, 43, 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, 109, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -69, -69, -69, -69, -69 }, { 7, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -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, 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 }, { 7, -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, 43, 43, 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, -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 }, { 7, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, 43, 43, 43, 43, 43, 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, -72, -72, -72, -72, -72 }, { 7, -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, -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, 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 }, { 7, -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, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 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, 43, 43, 43, 43, 43, 113, 43, -74, -74, -74, -74, -74 }, { 7, -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, 43, 43, 43, 43, 43, 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, -75, -75, -75, -75, -75 }, { 7, -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, 43, 43, 43, 43, 43, 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, 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 }, { 7, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -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, -77, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 116, 43, 43, -77, -77, -77, -77, -77 }, { 7, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -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, 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 }, { 7, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -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 }, { 7, -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, 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, 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, -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, -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 }, { 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, -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, -83, -83, -83, -83, -83, -83, -83, -83, -83, -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, 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, 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 }, { 7, -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, 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 }, { 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, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87 }, { 7, -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, 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, 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 }, { 7, -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, 43, 43, 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, -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 }, { 7, -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, -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, -90, -90, -90, -90, -90, -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, -90, -90, -90, -90, -90 }, { 7, -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, 43, 43, 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, -91, -91, -91, -91, -91, 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 }, { 7, -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, 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, -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, 43, 43, 43, -92, -92, -92, -92, -92 }, { 7, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -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, 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, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -93, -93, -93, -93, -93 }, { 7, -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, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 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, 127, 43, 43, 43, 43, 43, 43, 43, 43, -94, -94, -94, -94, -94 }, { 7, -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, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 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, 43, 43, 43, -95, -95, -95, -95, -95 }, { 7, -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, 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, 43, 43, 43, -96, -96, -96, -96, -96 }, { 7, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -97, -97, -97, -97, -97, -97, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -97, -97, -97, -97, -97 }, { 7, -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, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 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, 129, 43, 43, 43, 43, -98, -98, -98, -98, -98 }, { 7, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -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, 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, 43, 43, 43, -99, -99, -99, -99, -99 }, { 7, -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, 43, 43, 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, 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 }, { 7, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -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, -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 }, { 7, -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, 43, 43, 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, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -102, -102, -102, -102, -102 }, { 7, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -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, 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, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -103, -103, -103, -103, -103 }, { 7, -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, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, 43, 43, 43, 43, 133, 43, 43, 43, 43, 43, 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, 43, 43, 43, -104, -104, -104, -104, -104 }, { 7, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -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, 134, 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 }, { 7, -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, 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, -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, 43, 43, 43, -106, -106, -106, -106, -106 }, { 7, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -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, 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, 43, 43, 43, 43, 43, 43, 43, 43, -107, -107, -107, -107, -107 }, { 7, -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, 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, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -108, -108, -108, -108, -108 }, { 7, -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, 43, 43, 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, -109, -109, -109, -109, -109, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 138, 43, 43, 43, 43, 43, 43, -109, -109, -109, -109, -109 }, { 7, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -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, 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, 43, 43, 43, 43, 43, 43, 43, 43, 43, -110, -110, -110, -110, -110 }, { 7, -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, 43, 43, 43, 43, 43, 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, 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 }, { 7, -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, -112, -112, 43, 43, 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, -112, -112, -112, -112, -112, 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 }, { 7, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -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, 43, -113, -113, -113, -113, -113, -113, 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 }, { 7, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -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, -114, -114, -114, -114, -114, -114, 43, 43, 43, 43, 43, 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 }, { 7, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, 43, 43, 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, -115, -115, -115, -115, -115, 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 }, { 7, -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, 43, 43, 43, 43, 43, 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, -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 }, { 7, -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, 43, 43, 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, 148, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -117, -117, -117, -117, -117 }, { 7, -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, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 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, 149, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -118, -118, -118, -118, -118 }, { 7, -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, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 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, 150, 43, 43, 43, 43, 43, 43, 43, 43, -119, -119, -119, -119, -119 }, { 7, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -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, 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, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -120, -120, -120, -120, -120 }, { 7, -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, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -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, 43, -121, -121, -121, -121, -121, -121, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -121, -121, -121, -121, -121 }, { 7, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -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, 43, 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, 43, 43, 43, 43, 43, 153, 43, 43, 43, 43, 43, 43, -122, -122, -122, -122, -122 }, { 7, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, 43, 43, 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, -123, 43, 43, 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 }, { 7, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -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, 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, 43, 43, 43, -124, -124, -124, -124, -124 }, { 7, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -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, 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, 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 }, { 7, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -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, 43, 43, 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 }, { 7, -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, 43, 43, 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, 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 }, { 7, -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, 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, -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 }, { 7, -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, 43, 43, 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, 157, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -129, -129, -129, -129, -129 }, { 7, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -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, 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, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -130, -130, -130, -130, -130 }, { 7, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -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, 159, 43, 43, 43, 43, 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, 43, 43, 43, -131, -131, -131, -131, -131 }, { 7, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -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, 43, 43, 43, 43, 43, 43, 43, 43, -132, -132, -132, -132, -132 }, { 7, -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, 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, -133, -133, -133, -133, -133, -133, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -133, -133, -133, -133, -133 }, { 7, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -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, -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, -134, -134, -134, -134, -134 }, { 7, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -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, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -135, -135, -135, -135, -135 }, { 7, -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, 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, 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 }, { 7, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -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, 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, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -137, -137, -137, -137, -137 }, { 7, -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, 43, 43, 43, 43, 43, 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, 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 }, { 7, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -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, 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, 43, 43, 43, 43, 43, 43, 43, 43, 43, -139, -139, -139, -139, -139 }, { 7, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -140, -140, -140, -140, -140, -140, 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 }, { 7, -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, 43, 43, 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, -141, -141, -141, -141, -141, 43, 43, 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 }, { 7, -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, 43, 43, 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, 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 }, { 7, -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, 43, 43, 43, 43, 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, -143, 43, 43, 43, 43, 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 }, { 7, -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, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 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, -144, -144, -144, -144, -144 }, { 7, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -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, 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 }, { 7, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -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, 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, 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 }, { 7, -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, 43, 43, 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, 174, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -147, -147, -147, -147, -147 }, { 7, -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, 43, 43, 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, -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 }, { 7, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -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, -149, -149, -149, -149, -149, -149, 43, 43, 43, 43, 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 }, { 7, -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, -150, -150, -150, -150, -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, 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, 71, 43, 43, 43, 43, 43, 43, 43, -150, -150, -150, -150, -150 }, { 7, -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, 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, -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, 43, 43, 43, -151, -151, -151, -151, -151 }, { 7, -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, 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, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -152, -152, -152, -152, -152 }, { 7, -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, 43, 43, 43, 43, 43, 43, 43, 43, 43, 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, 179, 43, 43, 43, 43, 43, 43, 43, 43, -153, -153, -153, -153, -153 }, { 7, -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, 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, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -154, -154, -154, -154, -154 }, { 7, -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, 43, 43, 43, 43, 43, 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, -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 }, { 7, -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, 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, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -156, -156, -156, -156, -156 }, { 7, -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, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -157, -157, -157, -157, -157, -157, 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 }, { 7, -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, 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, 43, 43, 43, -158, -158, -158, -158, -158 }, { 7, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -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, 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, 43, 43, 43, -159, -159, -159, -159, -159 }, { 7, -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, 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, 43, 43, 43, 43, 43, 43, 43, 43, 43, -160, -160, -160, -160, -160 }, { 7, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, 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, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -161, -161, -161, -161, -161 }, { 7, -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, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -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, 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 }, { 7, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -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, 189, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -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, 43, 43, 43, -163, -163, -163, -163, -163 }, { 7, -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, 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, 43, 43, 43, 43, 43, 43, 43, 43, -164, -164, -164, -164, -164 }, { 7, -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, 43, 43, 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, -165, -165, -165, -165, -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, 43, 43, -165, -165, -165, -165, -165 }, { 7, -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, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 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, 43, 43, 43, 43, 43, 43, 43, 43, -166, -166, -166, -166, -166 }, { 7, -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, 43, 43, 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, -167, -167, -167, -167, -167, 43, 43, 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, -167, -167, -167, -167 }, { 7, -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, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, 43, 43, 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, -168, -168, -168, -168, -168, 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 }, { 7, -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, -169, -169, -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, 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 }, { 7, -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, -170, -170, -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, 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, 43, 43, 43, -170, -170, -170, -170, -170 }, { 7, -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, -171, -171, -171, -171, -171, -171, 43, 43, 43, 43, 43, 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, -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 }, { 7, -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, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 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 }, { 7, -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, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 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, 196, 43, 43, 43, 43, 43, 43, 43, -173, -173, -173, -173, -173 }, { 7, -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, -174, -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, -174, -174, -174, -174, -174, -174, 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 }, { 7, -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, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 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, 197, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -175, -175, -175, -175, -175 }, { 7, -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, 43, 43, 43, 43, 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, -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 }, { 7, -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, 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, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -177, -177, -177, -177, -177 }, { 7, -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, 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, 43, 43, 43, 43, 43, 43, 43, 43, -178, -178, -178, -178, -178 }, { 7, -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, 43, 43, 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, -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 }, { 7, -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, 43, 43, 43, 43, 43, 43, 43, 43, 43, 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, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -180, -180, -180, -180, -180 }, { 7, -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, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 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, 43, 43, 43, 43, 203, 43, 43, 43, 43, 43, 43, 43, -181, -181, -181, -181, -181 }, { 7, -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, 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, 43, 43, 43, 43, 43, 43, 43, 43, -182, -182, -182, -182, -182 }, { 7, -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, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 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, 205, 43, 43, 43, 43, 43, 43, 43, -183, -183, -183, -183, -183 }, { 7, -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, 43, 43, 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, -184, -184, -184, -184, -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, -184, -184, -184, -184, -184 }, { 7, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, 43, 43, 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, -185, -185, -185, -185, -185, 43, 43, 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, -185, -185, -185, -185 }, { 7, -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, -186, -186, -186, -186, -186, -186, -186, -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, 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, 43, 43, 43, -186, -186, -186, -186, -186 }, { 7, -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, 43, 43, 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, -187, -187, -187, -187, -187, 43, 43, 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, -187, -187, -187, -187 }, { 7, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 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, 207, 43, 43, 43, 43, 43, 43, 43, -188, -188, -188, -188, -188 }, { 7, -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, -189, -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, 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, 43, 43, 43, -189, -189, -189, -189, -189 }, { 7, -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, -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, 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, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -190, -190, -190, -190, -190 }, { 7, -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, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -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, 43, 43, -191, -191, -191, -191, -191, -191, 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 }, { 7, -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, -192, -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, -192, -192, -192, -192, -192, -192, 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 }, { 7, -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, -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, -193, -193, -193, -193, -193, -193, 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 }, { 7, -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, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 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, 211, 43, 43, 43, 43, 43, 43, 43, -194, -194, -194, -194, -194 }, { 7, -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, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -195, -195, -195, -195, -195, -195, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -195, -195, -195, -195, -195 }, { 7, -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, -196, -196, -196, -196, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -196, -196, -196, -196, -196, -196, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -196, -196, -196, -196, -196 }, { 7, -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, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -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, -197, -197, -197, -197, -197, -197, 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 }, { 7, -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, 43, 43, 43, 43, 43, 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, 43, 43, 43, 43, 43, 43, 213, 43, 43, 43, 43, 43, 43, 43, -198, -198, -198, -198, -198 }, { 7, -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, 43, 43, 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, -199, -199, -199, -199, -199, 43, 43, 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, -199, -199, -199, -199 }, { 7, -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, 43, 43, 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, -200, -200, -200, -200, -200, 43, 43, 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, -200, -200, -200, -200 }, { 7, -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, -201, -201, -201, -201, -201, -201, -201, -201, -201, -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, 43, 43, 43, 43, -201, -201, -201, -201, -201, -201, 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 }, { 7, -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, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -202, -202, -202, -202, -202, -202, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 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 ( 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 37 #define YY_END_OF_BUFFER 38 /* 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[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, 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, 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, 31, 11, 11, 18, 18, 24, 24, 26, 27, 31, 11, 26, 27, 31, 4, 4 } ; 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, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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 "wcsutrn.l" /*============================================================================ WCSLIB 8.4 - an implementation of the FITS WCS standard. Copyright (C) 1995-2024, 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/people/Mark.Calabretta $Id: wcsutrn.c,v 8.4 2024/10/28 13:56:17 mcalabre Exp $ *============================================================================= * * wcsutrn.l is a Flex description file containing the definition of a lexical * scanner that translates non-standard FITS units specifications. * * It requires Flex v2.5.4 or later. * * Refer to wcsunits.h for a description of the user interface and operating * notes. * *===========================================================================*/ /* Options. */ #define YY_NO_INPUT 1 /* Exclusive start states. */ #line 49 "wcsutrn.l" #include #include #include #include #include "wcserr.h" #include "wcsunits.h" // 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); // Internal helper functions. static YY_DECL; #line 4449 "wcsutrn.c" #line 4450 "wcsutrn.c" #define INITIAL 0 #define NEXT 1 #define FLUSH 2 #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 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; }; /* 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 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_delete_buffer(YY_CURRENT_BUFFER, yyscanner); yy_scan_string(unitstr, yyscanner); *unitstr = '\0'; // 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"); } BEGIN(INITIAL); #ifdef DEBUG fprintf(stderr, "\n%s ->\n", unitstr); #endif #line 4728 "wcsutrn.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 ) ++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 1: YY_RULE_SETUP #line 107 "wcsutrn.l" { // Looks like a keycomment. strcat(unitstr, "["); bracket = 1; } YY_BREAK case 2: YY_RULE_SETUP #line 113 "wcsutrn.l" // Discard leading whitespace. YY_BREAK case 3: /* rule 3 can match eol */ YY_RULE_SETUP #line 115 "wcsutrn.l" { // Non-alphabetic character. strcat(unitstr, yytext); if (bracket && *yytext == ']') { BEGIN(FLUSH); } } YY_BREAK case 4: YY_RULE_SETUP #line 123 "wcsutrn.l" { strcpy(orig, yytext); strcpy(subs, "Angstrom"); BEGIN(NEXT); } YY_BREAK case 5: YY_RULE_SETUP #line 129 "wcsutrn.l" { strcpy(orig, yytext); strcpy(subs, "arcmin"); BEGIN(NEXT); } YY_BREAK case 6: YY_RULE_SETUP #line 135 "wcsutrn.l" { strcpy(orig, yytext); strcpy(subs, "arcsec"); BEGIN(NEXT); } YY_BREAK case 7: YY_RULE_SETUP #line 141 "wcsutrn.l" { strcpy(orig, yytext); strcpy(subs, "beam"); BEGIN(NEXT); } YY_BREAK case 8: YY_RULE_SETUP #line 147 "wcsutrn.l" { strcpy(orig, yytext); strcpy(subs, "byte"); BEGIN(NEXT); } YY_BREAK case 9: YY_RULE_SETUP #line 153 "wcsutrn.l" { strcpy(orig, yytext); strcpy(subs, "d"); BEGIN(NEXT); } YY_BREAK case 10: YY_RULE_SETUP #line 159 "wcsutrn.l" { unsafe = 1; strcpy(orig, yytext); strcpy(subs, (ctrl & 4) ? "d" : "D"); BEGIN(NEXT); } YY_BREAK case 11: YY_RULE_SETUP #line 166 "wcsutrn.l" { strcpy(orig, yytext); strcpy(subs, "deg"); BEGIN(NEXT); } YY_BREAK case 12: YY_RULE_SETUP #line 172 "wcsutrn.l" { strcpy(orig, yytext); strcpy(subs, "GHz"); BEGIN(NEXT); } YY_BREAK case 13: YY_RULE_SETUP #line 178 "wcsutrn.l" { strcpy(orig, yytext); strcpy(subs, "h"); BEGIN(NEXT); } YY_BREAK case 14: YY_RULE_SETUP #line 184 "wcsutrn.l" { unsafe = 1; strcpy(orig, yytext); strcpy(subs, (ctrl & 2) ? "h" : "H"); BEGIN(NEXT); } YY_BREAK case 15: YY_RULE_SETUP #line 191 "wcsutrn.l" { strcpy(orig, yytext); strcpy(subs, "Hz"); BEGIN(NEXT); } YY_BREAK case 16: YY_RULE_SETUP #line 197 "wcsutrn.l" { strcpy(orig, yytext); strcpy(subs, "kHz"); BEGIN(NEXT); } YY_BREAK case 17: YY_RULE_SETUP #line 203 "wcsutrn.l" { strcpy(orig, yytext); strcpy(subs, "Jy"); BEGIN(NEXT); } YY_BREAK case 18: YY_RULE_SETUP #line 209 "wcsutrn.l" { strcpy(orig, yytext); strcpy(subs, "K"); BEGIN(NEXT); } YY_BREAK case 19: YY_RULE_SETUP #line 215 "wcsutrn.l" { strcpy(orig, yytext); strcpy(subs, "km"); BEGIN(NEXT); } YY_BREAK case 20: YY_RULE_SETUP #line 221 "wcsutrn.l" { strcpy(orig, yytext); strcpy(subs, "m"); BEGIN(NEXT); } YY_BREAK case 21: YY_RULE_SETUP #line 227 "wcsutrn.l" { strcpy(orig, yytext); strcpy(subs, "min"); BEGIN(NEXT); } YY_BREAK case 22: YY_RULE_SETUP #line 233 "wcsutrn.l" { strcpy(orig, yytext); strcpy(subs, "MHz"); BEGIN(NEXT); } YY_BREAK case 23: YY_RULE_SETUP #line 239 "wcsutrn.l" { strcpy(orig, yytext); strcpy(subs, "ohm"); BEGIN(NEXT); } YY_BREAK case 24: YY_RULE_SETUP #line 245 "wcsutrn.l" { strcpy(orig, yytext); strcpy(subs, "Pa"); BEGIN(NEXT); } YY_BREAK case 25: YY_RULE_SETUP #line 251 "wcsutrn.l" { strcpy(orig, yytext); strcpy(subs, "pixel"); BEGIN(NEXT); } YY_BREAK case 26: YY_RULE_SETUP #line 257 "wcsutrn.l" { strcpy(orig, yytext); strcpy(subs, "rad"); BEGIN(NEXT); } YY_BREAK case 27: YY_RULE_SETUP #line 263 "wcsutrn.l" { strcpy(orig, yytext); strcpy(subs, "s"); BEGIN(NEXT); } YY_BREAK case 28: YY_RULE_SETUP #line 269 "wcsutrn.l" { unsafe = 1; strcpy(orig, yytext); strcpy(subs, (ctrl & 1) ? "s" : "S"); BEGIN(NEXT); } YY_BREAK case 29: YY_RULE_SETUP #line 276 "wcsutrn.l" { strcpy(orig, yytext); strcpy(subs, "V"); BEGIN(NEXT); } YY_BREAK case 30: YY_RULE_SETUP #line 282 "wcsutrn.l" { strcpy(orig, yytext); strcpy(subs, "yr"); BEGIN(NEXT); } YY_BREAK case 31: YY_RULE_SETUP #line 288 "wcsutrn.l" { // Not a recognized alias. strcpy(orig, yytext); strcpy(subs, orig); BEGIN(NEXT); } YY_BREAK case 32: YY_RULE_SETUP #line 295 "wcsutrn.l" { // Reject the alias match. strcat(orig, yytext); strcpy(subs, orig); } YY_BREAK case 33: /* rule 33 can match eol */ YY_RULE_SETUP #line 301 "wcsutrn.l" { // Discard separating whitespace. unput(yytext[yyleng-1]); } YY_BREAK case 34: YY_RULE_SETUP #line 306 "wcsutrn.l" { // Compress separating whitespace. strcat(unitstr, subs); strcat(unitstr, " "); if (strcmp(orig, subs)) status = 0; unput(yytext[yyleng-1]); *subs = '\0'; BEGIN(INITIAL); } YY_BREAK case 35: YY_RULE_SETUP #line 316 "wcsutrn.l" { // Copy anything else unchanged. strcat(unitstr, subs); if (strcmp(orig, subs)) status = 0; unput(*yytext); *subs = '\0'; BEGIN(INITIAL); } YY_BREAK case 36: YY_RULE_SETUP #line 325 "wcsutrn.l" { // 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 330 "wcsutrn.l" { // End-of-string. if (*subs) { strcat(unitstr, subs); if (strcmp(orig, subs)) status = 0; } if (unsafe) { return wcserr_set(WCSERR_SET(UNITSERR_UNSAFE_TRANS), "Unsafe unit translation in '%s'", unitstr); } return status; } YY_BREAK case 37: YY_RULE_SETUP #line 344 "wcsutrn.l" ECHO; YY_BREAK #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 - yyg->yytext_ptr) - 1; /* Undo the effects of YY_DO_BEFORE_ACTION. */ *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 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. */ 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; } /* Note that here we test for yy_c_buf_p "<=" to the position * of the first EOB in the buffer, since yy_c_buf_p will * already have been incremented past the NUL character * (since all states make transitions on EOB to the * end-of-buffer state). Contrast this with the test * in input(). */ 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; yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state( yyscanner ); /* Okay, we're now positioned to make the NUL * transition. We couldn't have * yy_get_previous_state() go ahead and do it * for us because it doesn't know how to deal * with the possibility of jamming (and we don't * want to build jamming into it because then it * will run more slowly). */ yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner); yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; if ( yy_next_state ) { /* Consume the NUL. */ yy_cp = ++yyg->yy_c_buf_p; yy_current_state = yy_next_state; goto yy_match; } else { yy_cp = yyg->yy_c_buf_p; goto yy_find_action; } } else switch ( yy_get_next_buffer( yyscanner ) ) { case EOB_ACT_END_OF_FILE: { yyg->yy_did_buffer_switch_on_eof = 0; if ( yywrap( yyscanner ) ) { /* Note: because we've taken care in * yy_get_next_buffer() to have 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. */ yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ; yy_act = YY_STATE_EOF(YY_START); goto do_action; } else { if ( ! yyg->yy_did_buffer_switch_on_eof ) YY_NEW_FILE; } break; } case EOB_ACT_CONTINUE_SCAN: yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state( yyscanner ); yy_cp = yyg->yy_c_buf_p; yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; goto yy_match; case EOB_ACT_LAST_MATCH: yyg->yy_c_buf_p = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars]; yy_current_state = yy_get_previous_state( yyscanner ); yy_cp = yyg->yy_c_buf_p; yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; goto yy_find_action; } break; } default: YY_FATAL_ERROR( "fatal flex scanner internal error--no action found" ); } /* end of action switch */ } /* end of scanning one token */ } /* end of user's declarations */ } /* end of yylex */ /* yy_get_next_buffer - try to read in a new buffer * * Returns a code representing an action: * EOB_ACT_LAST_MATCH - * EOB_ACT_CONTINUE_SCAN - continue scanning from current position * EOB_ACT_END_OF_FILE - end of file */ static int yy_get_next_buffer (yyscan_t yyscanner) { 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 ( 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 ( 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. */ return EOB_ACT_END_OF_FILE; } else { /* We matched some text prior to the EOB, first * process it. */ return EOB_ACT_LAST_MATCH; } } /* Try to read more data. */ /* First move last chars to start of buffer. */ number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr - 1); for ( i = 0; i < number_to_move; ++i ) *(dest++) = *(source++); if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) /* don't do the read, it's not guaranteed to return an EOF, * just force an EOF */ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0; else { int num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; while ( num_to_read <= 0 ) { /* Not enough room in the buffer - grow it. */ /* just a shorter name for the current buffer */ YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE; int yy_c_buf_p_offset = (int) (yyg->yy_c_buf_p - b->yy_ch_buf); if ( b->yy_is_our_buffer ) { int new_size = b->yy_buf_size * 2; if ( new_size <= 0 ) b->yy_buf_size += b->yy_buf_size / 8; else b->yy_buf_size *= 2; b->yy_ch_buf = (char *) /* Include room in for 2 EOB chars. */ 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 = NULL; if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "fatal error - scanner input buffer overflow" ); 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; } if ( num_to_read > YY_READ_BUF_SIZE ) num_to_read = YY_READ_BUF_SIZE; /* Read in more data. */ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), yyg->yy_n_chars, num_to_read ); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } if ( yyg->yy_n_chars == 0 ) { if ( number_to_move == YY_MORE_ADJ ) { ret_val = EOB_ACT_END_OF_FILE; yyrestart( yyin , yyscanner); } else { ret_val = EOB_ACT_LAST_MATCH; YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_EOF_PENDING; } } else ret_val = EOB_ACT_CONTINUE_SCAN; 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. */ 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); } 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; 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 (yyscan_t yyscanner) { 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 = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp ) { if ( *yy_cp ) { yy_current_state = yy_nxt[yy_current_state][YY_SC_TO_UI(*yy_cp)]; } else yy_current_state = yy_NUL_trans[yy_current_state]; } return yy_current_state; } /* yy_try_NUL_trans - try to make a transition on the NUL character * * synopsis * next_state = yy_try_NUL_trans( current_state ); */ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner) { 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; } #ifndef YY_NO_UNPUT static void yyunput (int c, char * yy_bp , yyscan_t yyscanner) { char *yy_cp; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; 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. */ 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]; char *source = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]; while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) *--dest = *--source; yy_cp += (int) (dest - source); yy_bp += (int) (dest - source); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = 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" ); } *--yy_cp = (char) c; 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 (yyscan_t yyscanner) #else static int input (yyscan_t yyscanner) #endif { int c; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; *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 ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) /* This was really a NUL. */ *yyg->yy_c_buf_p = '\0'; else { /* need more input */ int offset = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr); ++yyg->yy_c_buf_p; switch ( yy_get_next_buffer( yyscanner ) ) { case EOB_ACT_LAST_MATCH: /* This happens because yy_g_n_b() * sees that we've accumulated a * token and flags that we need to * try matching the token before * proceeding. But for input(), * there's no matching to consider. * So convert the EOB_ACT_LAST_MATCH * to EOB_ACT_END_OF_FILE. */ /* Reset buffer status. */ yyrestart( yyin , yyscanner); /*FALLTHROUGH*/ case EOB_ACT_END_OF_FILE: { if ( yywrap( yyscanner ) ) return 0; if ( ! yyg->yy_did_buffer_switch_on_eof ) YY_NEW_FILE; #ifdef __cplusplus return yyinput(yyscanner); #else return input(yyscanner); #endif } case EOB_ACT_CONTINUE_SCAN: yyg->yy_c_buf_p = yyg->yytext_ptr + offset; break; } } } 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'); return c; } #endif /* ifndef YY_NO_INPUT */ /** 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 yyrestart (FILE * input_file , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if ( ! YY_CURRENT_BUFFER ){ yyensure_buffer_stack (yyscanner); YY_CURRENT_BUFFER_LVALUE = yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); } 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 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 * yypop_buffer_state(); * yypush_buffer_state(new_buffer); */ yyensure_buffer_stack (yyscanner); if ( YY_CURRENT_BUFFER == new_buffer ) return; if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ *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; yy_load_buffer_state( yyscanner ); /* We don't actually know whether we did this switch during * 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. */ yyg->yy_did_buffer_switch_on_eof = 1; } static void yy_load_buffer_state (yyscan_t yyscanner) { 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 yy_create_buffer (FILE * file, int size , yyscan_t yyscanner) { YY_BUFFER_STATE b; b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); if ( ! b ) 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 *) yyalloc( (yy_size_t) (b->yy_buf_size + 2) , yyscanner ); if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); b->yy_is_our_buffer = 1; yy_init_buffer( b, file , yyscanner); return b; } /** Destroy the buffer. * @param b a buffer created with yy_create_buffer() * @param yyscanner The scanner object. */ void yy_delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if ( ! b ) return; if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; if ( b->yy_is_our_buffer ) yyfree( (void *) b->yy_ch_buf , yyscanner ); 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 yyrestart() or at EOF. */ static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner) { int oerrno = errno; 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 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){ b->yy_bs_lineno = 1; b->yy_bs_column = 0; } b->yy_is_interactive = 0; errno = oerrno; } /** 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 yy_flush_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if ( ! b ) return; b->yy_n_chars = 0; /* We always need two end-of-buffer characters. The first causes * a transition to the end-of-buffer state. The second causes * a jam in that state. */ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; b->yy_buf_pos = &b->yy_ch_buf[0]; b->yy_at_bol = 1; b->yy_buffer_status = YY_BUFFER_NEW; if ( b == YY_CURRENT_BUFFER ) 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 yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (new_buffer == NULL) return; yyensure_buffer_stack(yyscanner); /* This block is copied from yy_switch_to_buffer. */ if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ *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) yyg->yy_buffer_stack_top++; YY_CURRENT_BUFFER_LVALUE = new_buffer; /* 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 yypop_buffer_state (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (!YY_CURRENT_BUFFER) return; yy_delete_buffer(YY_CURRENT_BUFFER , yyscanner); YY_CURRENT_BUFFER_LVALUE = NULL; if (yyg->yy_buffer_stack_top > 0) --yyg->yy_buffer_stack_top; if (YY_CURRENT_BUFFER) { 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 yyensure_buffer_stack (yyscan_t yyscanner) { 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; /* 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*) , 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 (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){ /* Increase the buffer to prepare for a possible push. */ yy_size_t grow_size = 8 /* arbitrary grow size */; 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*) , yyscanner); if ( ! yyg->yy_buffer_stack ) YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); /* zero only the new slots.*/ 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 * @param yyscanner The scanner object. * @return the newly allocated buffer state object. */ YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size , yyscan_t yyscanner) { YY_BUFFER_STATE b; if ( size < 2 || 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 NULL; b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); 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 = 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; yy_switch_to_buffer( b , yyscanner ); return b; } /** 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 * yy_scan_bytes() instead. */ YY_BUFFER_STATE yy_scan_string (const char * yystr , yyscan_t yyscanner) { return yy_scan_bytes( yystr, (int) strlen(yystr) , yyscanner); } /** 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 yy_scan_bytes (const char * yybytes, int _yybytes_len , yyscan_t yyscanner) { YY_BUFFER_STATE b; char *buf; yy_size_t n; int i; /* Get memory for full buffer, including space for trailing EOB's. */ n = (yy_size_t) (_yybytes_len + 2); buf = (char *) yyalloc( n , yyscanner ); if ( ! buf ) 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 = yy_scan_buffer( buf, n , yyscanner); if ( ! b ) 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. */ b->yy_is_our_buffer = 1; return b; } #ifndef YY_EXIT_FAILURE #define YY_EXIT_FAILURE 2 #endif static void yynoreturn yy_fatal_error (const char* msg , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; (void)yyg; fprintf( stderr, "%s\n", msg ); exit( YY_EXIT_FAILURE ); } /* Redefine yyless() so it works in section 3 code. */ #undef yyless #define yyless(n) \ do \ { \ /* Undo effects of setting up yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(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 yyget_column (yyscan_t yyscanner) { 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 *yyget_in (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyin; } /** Get the output stream. * @param yyscanner The scanner object. */ FILE *yyget_out (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyout; } /** Get the length of the current token. * @param yyscanner The scanner object. */ int yyget_leng (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyleng; } /** Get the current token. * @param yyscanner The scanner object. */ 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) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yyextra = user_defined ; } /** Set the current 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 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. * @param yyscanner The scanner object. * @see yy_switch_to_buffer */ 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) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yy_flex_debug; } void yyset_debug (int _bdebug , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yy_flex_debug = _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) { 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 ); } /* 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 ) { 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. */ 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 yyin = stdin; yyout = stdout; #else yyin = NULL; yyout = NULL; #endif /* For future reference: Set errno on error, since we are called by * yylex_init() */ return 0; } /* 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){ yy_delete_buffer( YY_CURRENT_BUFFER , yyscanner ); YY_CURRENT_BUFFER_LVALUE = NULL; yypop_buffer_state(yyscanner); } /* Destroy the stack itself. */ 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 * yylex() is called, initialization will occur. */ yy_init_globals( yyscanner); /* Destroy the main struct (reentrant only). */ yyfree ( yyscanner , yyscanner ); yyscanner = NULL; return 0; } /* * Internal utility routines. */ #ifndef yytext_ptr static void yy_flex_strncpy (char* s1, const char * s2, int n , yyscan_t yyscanner) { 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 (const char * s , yyscan_t yyscanner) { int n; for ( n = 0; s[n]; ++n ) ; return n; } #endif void *yyalloc (yy_size_t size , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; (void)yyg; return malloc(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 * because both ANSI C and C++ allow castless assignment from * any pointer type to void*, and deal with argument conversions * as though doing an assignment. */ return realloc(ptr, size); } void yyfree (void * ptr , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; (void)yyg; free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ } #define YYTABLES_NAME "yytables" #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; } astropy-astropy-201cddb/cextern/wcslib/C/getwcstab.c000066400000000000000000000076461507226315300226600ustar00rootroot00000000000000/*============================================================================ WCSLIB 8.4 - an implementation of the FITS WCS standard. Copyright (C) 1995-2024, 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/people/Mark.Calabretta $Id: getwcstab.c,v 8.4 2024/10/28 13:56:16 mcalabre Exp $ *===========================================================================*/ #include #include #include #include "getwcstab.h" //---------------------------------------------------------------------------- int fits_read_wcstab( fitsfile *fptr, int nwtb, wtbarr *wtb, int *status) { int anynul, colnum, hdunum, iwtb, m, naxis, nostat; long *naxes = 0, nelem; wtbarr *wtbp; if (*status) return *status; if (fptr == 0) { return (*status = NULL_INPUT_PTR); } if (nwtb == 0) return 0; // 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. fits_get_hdu_num(fptr, &hdunum); wtbp = wtb; for (iwtb = 0; iwtb < nwtb; iwtb++, wtbp++) { // 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. if (fits_get_colnum(fptr, CASEINSEN, (char *)(wtbp->ttype), &colnum, status)) { goto cleanup; } // Get the array dimensions and check for consistency. if (wtbp->ndim < 1) { *status = NEG_AXIS; goto cleanup; } if (!(naxes = calloc(wtbp->ndim, sizeof(long)))) { *status = MEMORY_ALLOCATION; goto cleanup; } if (fits_read_tdim(fptr, colnum, wtbp->ndim, &naxis, naxes, status)) { goto cleanup; } if (naxis != wtbp->ndim) { if (wtbp->kind == 'c' && wtbp->ndim == 2) { // Allow TDIMn to be omitted for degenerate coordinate arrays. naxis = 2; naxes[1] = naxes[0]; naxes[0] = 1; } else { *status = BAD_TDIM; goto cleanup; } } if (wtbp->kind == 'c') { // 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. if ((nelem = naxes[0]) != *(wtbp->dimlen)) { // N.B. coordinate array precedes the index vectors. *status = BAD_TDIM; goto cleanup; } } free(naxes); naxes = 0; // 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. if (fits_read_col_dbl(fptr, colnum, wtbp->row, 1L, nelem, 0.0, *wtbp->arrayp, &anynul, status)) { goto cleanup; } } cleanup: // Move back to the starting HDU. nostat = 0; fits_movabs_hdu(fptr, hdunum, 0, &nostat); // Release allocated memory. if (naxes) free(naxes); if (*status) { wtbp = wtb; for (iwtb = 0; iwtb < nwtb; iwtb++, wtbp++) { if (*wtbp->arrayp) free(*wtbp->arrayp); } } return *status; } astropy-astropy-201cddb/cextern/wcslib/C/getwcstab.h000066400000000000000000000161611507226315300226550ustar00rootroot00000000000000/*============================================================================ WCSLIB 8.4 - an implementation of the FITS WCS standard. Copyright (C) 1995-2024, 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/people/Mark.Calabretta $Id: getwcstab.h,v 8.4 2024/10/28 13:56:16 mcalabre Exp $ *============================================================================= * * WCSLIB 8.4 - 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 * 'TAB' coordinates, is provided for CFITSIO programmers. It has been * incorporated into CFITSIO as of v3.006 with the definitions in this file, * getwcstab.h, moved into fitsio.h. * * fits_read_wcstab() is not included in the WCSLIB object library but the * source code is presented here as it may be useful for programmers using an * older version of CFITSIO than 3.006, or as a programming template for * non-CFITSIO programmers. * * * fits_read_wcstab() - FITS 'TAB' table reading routine * ---------------------------------------------------- * fits_read_wcstab() extracts arrays from a binary table required in * constructing 'TAB' coordinates. * * Given: * fptr fitsfile * * Pointer to the file handle returned, for example, by * the fits_open_file() routine in CFITSIO. * * nwtb int Number of arrays to be read from the binary table(s). * * Given and returned: * wtb wtbarr * Address of the first element of an array of wtbarr * typedefs. This wtbarr typedef is defined to match the * wtbarr struct defined in WCSLIB. An array of such * structs returned by the WCSLIB function wcstab() as * discussed in the notes below. * * Returned: * status int * CFITSIO status value. * * Function return value: * int CFITSIO status value. * * Notes: * 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 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. * * Therefore, declarations within WCSLIB look like * = struct wtbarr *w; * * while within CFITSIO they are simply * = 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. * * 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 * -------------- * The wtbarr typedef is defined as a struct containing the following members: * * int i * Image axis number. * * int m * Array axis number for index vectors. * * int kind * Character identifying the array type: * - c: coordinate array, * - i: index vector. * * char extnam[72] * EXTNAME identifying the binary table extension. * * int extver * EXTVER identifying the binary table extension. * * int extlev * EXTLEV identifying the binary table extension. * * char ttype[72] * TTYPEn identifying the column of the binary table that contains the * array. * * long row * Table row number. * * int ndim * Expected dimensionality of the array. * * int *dimlen * Address of the first element of an array of int of length ndim into * which the array axis lengths are to be written. * * double **arrayp * Pointer to an array of double which is to be allocated by the user * and into which the array is to be written. * *===========================================================================*/ #ifndef WCSLIB_GETWCSTAB #define WCSLIB_GETWCSTAB #ifdef __cplusplus extern "C" { #endif #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. } wtbarr; int fits_read_wcstab(fitsfile *fptr, int nwtb, wtbarr *wtb, int *status); #ifdef __cplusplus } #endif #endif // WCSLIB_GETWCSTAB astropy-astropy-201cddb/cextern/wcslib/C/lin.c000066400000000000000000001001121507226315300214350ustar00rootroot00000000000000/*============================================================================ WCSLIB 8.4 - an implementation of the FITS WCS standard. Copyright (C) 1995-2024, 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/people/Mark.Calabretta $Id: lin.c,v 8.4 2024/10/28 13:56:16 mcalabre Exp $ *===========================================================================*/ #include #include #include #include #include "wcserr.h" #include "wcsprintf.h" #include "lin.h" #include "dis.h" // Map status return value to message. const char *lin_errmsg[] = { "Success", "Null linprm pointer passed", "Memory allocation failed", "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(int alloc, int naxis, struct linprm *lin) { return lininit(alloc, naxis, lin, -1); } //---------------------------------------------------------------------------- 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. if (lin->flag == -1) { lin->err = 0x0; } struct wcserr **err = &(lin->err); wcserr_clear(err); // Initialize memory management. if (lin->flag == -1 || lin->m_flag != LINSET) { 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), "naxis must not be negative (got %d)", naxis); } // Allocate memory for arrays if required. if (alloc || lin->crpix == 0x0 || lin->pc == 0x0 || lin->cdelt == 0x0) { // Was sufficient allocated previously? if (lin->m_flag == LINSET && lin->m_naxis < naxis) { // No, free it. linfree(lin); } if (alloc || lin->crpix == 0x0) { if (lin->m_crpix) { // In case the caller fiddled with it. lin->crpix = lin->m_crpix; } else { if ((lin->crpix = calloc(naxis, sizeof(double))) == 0x0) { return wcserr_set(LIN_ERRMSG(LINERR_MEMORY)); } lin->m_flag = LINSET; lin->m_naxis = naxis; lin->m_crpix = lin->crpix; } } if (alloc || lin->pc == 0x0) { if (lin->m_pc) { // In case the caller fiddled with it. lin->pc = lin->m_pc; } else { if ((lin->pc = calloc(naxis*naxis, sizeof(double))) == 0x0) { linfree(lin); return wcserr_set(LIN_ERRMSG(LINERR_MEMORY)); } lin->m_flag = LINSET; lin->m_naxis = naxis; lin->m_pc = lin->pc; } } if (alloc || lin->cdelt == 0x0) { if (lin->m_cdelt) { // In case the caller fiddled with it. lin->cdelt = lin->m_cdelt; } else { if ((lin->cdelt = calloc(naxis, sizeof(double))) == 0x0) { linfree(lin); return wcserr_set(LIN_ERRMSG(LINERR_MEMORY)); } lin->m_flag = LINSET; lin->m_naxis = naxis; lin->m_cdelt = lin->cdelt; } } } // 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 = 0; lin->unity = 0; lin->affine = 0; lin->simple = 0; lin->naxis = naxis; // CRPIXja defaults to 0.0. for (int j = 0; j < naxis; j++) { lin->crpix[j] = 0.0; } // 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 { *pc = 0.0; } pc++; } } // CDELTia defaults to 1.0. for (int i = 0; i < naxis; i++) { lin->cdelt[i] = 1.0; } lin->flag = 0; return 0; } //---------------------------------------------------------------------------- int lindis(int sequence, struct linprm *lin, struct disprm *dis) { return lindist(sequence, lin, dis, -1); } //---------------------------------------------------------------------------- int lindist(int sequence, struct linprm *lin, struct disprm *dis, int ndpmax) { static const char *function = "lindist"; 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; struct wcserr **err = &(lindst->err); int naxis = linsrc->naxis; if (naxis < 1) { return wcserr_set(WCSERR_SET(LINERR_MEMORY), "naxis must be positive (got %d)", naxis); } int status = lininit(alloc, naxis, lindst, 0); if (status) { return status; } 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 (int i = 0; i < naxis; i++) { for (int j = 0; j < naxis; j++) { *(dstp++) = *(srcp++); } } srcp = linsrc->cdelt; dstp = lindst->cdelt; for (int i = 0; i < naxis; i++) { *(dstp++) = *(srcp++); } 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; } 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; } 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) { // 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->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_disseq) { disfree(lin->m_disseq); free(lin->m_disseq); } } // Allocated unconditionally by linset(). if (lin->piximg) free(lin->piximg); if (lin->imgpix) free(lin->imgpix); } 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); // Total size of allocated memory, in bytes. sizes[1] = 0; 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) { // 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 (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 (int j = 0; j < lin->naxis; j++) { wcsprintf(" %#- 11.5g", lin->crpix[j]); } wcsprintf("\n"); int k = 0; WCSPRINTF_PTR(" pc: ", lin->pc, "\n"); for (int i = 0; i < lin->naxis; i++) { wcsprintf(" pc[%d][]:", i); for (int j = 0; j < lin->naxis; j++) { wcsprintf(" %#- 11.5g", lin->pc[k++]); } wcsprintf("\n"); } WCSPRINTF_PTR(" cdelt: ", lin->cdelt, "\n"); wcsprintf(" "); for (int i = 0; i < lin->naxis; i++) { wcsprintf(" %#- 11.5g", lin->cdelt[i]); } wcsprintf("\n"); 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 { int k = 0; for (int i = 0; i < lin->naxis; i++) { wcsprintf("piximg[%d][]:", i); for (int j = 0; j < lin->naxis; j++) { wcsprintf(" %#- 11.5g", lin->piximg[k++]); } wcsprintf("\n"); } } if (lin->imgpix == 0x0) { wcsprintf(" imgpix: (nil)\n"); } else { int k = 0; for (int i = 0; i < lin->naxis; i++) { wcsprintf("imgpix[%d][]:", i); 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, ""); if (lin->m_crpix == lin->crpix) wcsprintf(" (= crpix)"); wcsprintf("\n"); WCSPRINTF_PTR(" m_pc: ", lin->m_pc, ""); if (lin->m_pc == lin->pc) wcsprintf(" (= pc)"); wcsprintf("\n"); 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(struct linprm *lin) { static const char *function = "linset"; if (lin == 0x0) return LINERR_NULL_POINTER; if (lin->flag == -LINSET) return 0; struct wcserr **err = &(lin->err); int naxis = lin->naxis; // Check for a unit matrix. lin->unity = 1; 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; break; } } else { if (*(pc++) != 0.0) { lin->unity = 0; break; } } } } if (lin->unity) { 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->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 (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(naxis*naxis, sizeof(double))) == 0x0) { return wcserr_set(LIN_ERRMSG(LINERR_MEMORY)); } if ((lin->imgpix = calloc(naxis*naxis, sizeof(double))) == 0x0) { free(lin->piximg); return wcserr_set(LIN_ERRMSG(LINERR_MEMORY)); } lin->i_naxis = naxis; } // 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. int status = matinv(naxis, lin->piximg, lin->imgpix); if (status) { return wcserr_set(LIN_ERRMSG(status)); } } // 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( struct linprm *lin, int ncoord, int nelem, const double pixcrd[], double imgcrd[]) { static const char *function = "linp2x"; // Initialize. if (lin == 0x0) return LINERR_NULL_POINTER; struct wcserr **err = &(lin->err); if (abs(lin->flag) != LINSET) { int status = linset(lin); if (status) { return status; } } int naxis = lin->naxis; // Convert pixel coordinates to intermediate world coordinates. const double *pix = pixcrd; double *img = imgcrd; 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 += 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) { img[i] += *piximg * temp; } } pix += nelemn; img += nelem; } } 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->dispre) { int status = disp2x(lin->dispre, pix, tmp); if (status) { return wcserr_set(LIN_ERRMSG(lin_diserr[status])); } } else { memcpy(tmp, pix, ndbl); } if (lin->unity) { for (int i = 0; i < naxis; i++) { img[i] = tmp[i] - lin->crpix[i]; } } 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++) { img[i] += *(piximg++) * tmp[j]; } } } 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( struct linprm *lin, int ncoord, int nelem, const double imgcrd[], double pixcrd[]) { static const char *function = "linx2p"; int status = 0; // Initialize. if (lin == 0x0) return LINERR_NULL_POINTER; struct wcserr **err = &(lin->err); if (abs(lin->flag) != LINSET) { if ((status = linset(lin))) { return status; } } int naxis = lin->naxis; // Convert intermediate world coordinates to pixel coordinates. const double *img = imgcrd; double *pix = pixcrd; 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]; } img += nelemn; pix += nelemn; } } 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 (int j = 0; j < naxis; j++) { *pix = 0.0; for (int i = 0; i < naxis; i++) { *pix += *imgpix * img[i]; imgpix++; } *(pix++) += lin->crpix[j]; } 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))) { wcserr_set(LIN_ERRMSG(lin_diserr[status])); goto cleanup; } 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++) { pix[j] += *(imgpix++) * tmp[i]; } } } if (lin->dispre) { memcpy(tmp, pix, ndbl); if ((status = disx2p(lin->dispre, tmp, pix))) { wcserr_set(LIN_ERRMSG(lin_diserr[status])); goto cleanup; } } img += nelem; pix += nelem; } cleanup: free(tmp); } return status; } //---------------------------------------------------------------------------- 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) { 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; } 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; } int *lxm = calloc(n, sizeof(int)); if (lxm == 0x0) { free(mxl); return LINERR_MEMORY; } double *rowmax = calloc(n, sizeof(double)); if (rowmax == 0x0) { free(mxl); free(lxm); return LINERR_MEMORY; } double *lu = calloc(n*n, sizeof(double)); if (lu == 0x0) { free(mxl); free(lxm); free(rowmax); return LINERR_MEMORY; } // 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 (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. if (rowmax[i] == 0.0) { free(mxl); free(lxm); free(rowmax); free(lu); return LINERR_SINGULAR_MTX; } } // 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 (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; } } if (pivot > k) { // 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. double dtemp = rowmax[pivot]; rowmax[pivot] = rowmax[k]; rowmax[k] = dtemp; // Record the interchange for later use. int itemp = mxl[pivot]; mxl[pivot] = mxl[k]; mxl[k] = itemp; } // Gaussian elimination. for (int i = k+1; i < n; i++) { int ik = i*n + k; // Nothing to do if lu[ik] is zero. if (lu[ik] != 0.0) { // Save the scaling factor. lu[ik] /= lu[k*n+k]; // Subtract rows. for (int j = k+1; j < n; j++) { lu[i*n+j] -= lu[ik]*lu[k*n+j]; } } } } // 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. ij = 0; for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++, ij++) { inv[ij] = 0.0; } } for (int k = 0; k < n; k++) { inv[lxm[k]*n+k] = 1.0; // 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 (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); return 0; } astropy-astropy-201cddb/cextern/wcslib/C/lin.h000066400000000000000000000767331507226315300214670ustar00rootroot00000000000000/*============================================================================ WCSLIB 8.4 - an implementation of the FITS WCS standard. Copyright (C) 1995-2024, 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/people/Mark.Calabretta $Id: lin.h,v 8.4 2024/10/28 13:56:16 mcalabre Exp $ *============================================================================= * * WCSLIB 8.4 - 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 * --------------------------- * Routines in this suite apply the linear transformation 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) * * 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 * needs to be set up by linset() but need not be called explicitly - refer to * the explanation of linprm::flag. * * 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() 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. * * * 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 lininit() will actually be used to allocate memory. * * Given: * alloc int If true, allocate memory unconditionally for arrays in * the linprm 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: * lin struct linprm* * Linear transformation parameters. Note that, in order * to initialize memory management linprm::flag should be * set to -1 when lin 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 linprm pointer passed. * 2: Memory allocation failed. * * For returns > 1, a detailed error message is set in * 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 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. * * Given: * alloc int If true, allocate memory for the crpix, pc, and cdelt * 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. * * linsrc const struct linprm* * Struct to copy from. * * Given and returned: * lindst struct linprm* * Struct to copy to. linprm::flag should be set to -1 * if lindst was not previously initialized (memory leaks * may result if it was previously initialized). * * Function return value: * int Status return value: * 0: Success. * 1: Null linprm pointer passed. * 2: Memory allocation failed. * * For returns > 1, a detailed error message is set in * linprm::err if enabled, see wcserr_enable(). * * * linfree() - Destructor for the linprm struct * -------------------------------------------- * 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 lininit(). * * Given: * lin struct linprm* * Linear transformation parameters. * * Function return value: * int Status return value: * 0: Success. * 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 * intended for diagnostic purposes. * * Given: * lin const struct linprm* * Linear transformation parameters. * * Function return value: * int Status return value: * 0: Success. * 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 * linprm::imgpix arrays and sets up the linprm struct according to information * supplied within it - refer to the explanation of linprm::flag. * * Note that this routine need not be called directly; it will be invoked by * 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. * * Function return value: * int Status return value: * 0: Success. * 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(). * * * linp2x() - Pixel-to-world linear transformation * ----------------------------------------------- * linp2x() transforms pixel coordinates to intermediate world coordinates. * * Given and returned: * lin struct linprm* * Linear transformation parameters. * * Given: * ncoord, * nelem int The number of coordinates, each of vector length nelem * but containing lin.naxis coordinate elements. * * pixcrd const double[ncoord][nelem] * Array of pixel coordinates. * * Returned: * imgcrd double[ncoord][nelem] * Array of intermediate world coordinates. * * Function return value: * int Status return value: * 0: Success. * 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 * ----------------------------------------------- * linx2p() transforms intermediate world coordinates to pixel coordinates. * * Given and returned: * lin struct linprm* * Linear transformation parameters. * * Given: * ncoord, * nelem int The number of coordinates, each of vector length nelem * but containing lin.naxis coordinate elements. * * imgcrd const double[ncoord][nelem] * Array of intermediate world coordinates. * * Returned: * pixcrd double[ncoord][nelem] * Array of pixel coordinates. * * int Status return value: * 0: Success. * 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 * ------------------------------------------------ * The linprm struct contains all of the information required to perform a * linear transformation. It consists of certain members that must be set by * the user ("given") and others that are set by the WCSLIB routines * ("returned"). * * int flag * (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::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 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. * * int naxis * (Given or returned) Number of pixel and world coordinate elements. * * 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. * * double *crpix * (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 * = struct linprm lin; = lin.pc = {PC1_1, PC1_2, PC2_1, PC2_2}; * * This may be constructed conveniently from a 2-D array via * = double m[2][2] = {{PC1_1, PC1_2}, = {PC2_1, PC2_2}}; * * which is equivalent to * = double m[2][2]; = m[0][0] = PC1_1; = m[0][1] = PC1_2; = m[1][0] = PC2_1; = m[1][1] = PC2_2; * * The storage order for this 2-D array is the same as for the 1-D array, * whence * = lin.pc = *m; * * would be legitimate. * * double *cdelt * (Given) Pointer to the first element of an array of double containing * the coordinate increments, CDELTia. * * 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 * product of the CDELTia diagonal matrix and the PCi_ja matrix. * * double *imgpix * (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 * contains detailed information about the error, see wcserr_enable(). * * double *dummy * (For internal use only.) * int m_flag * (For internal use only.) * int m_naxis * (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.) * struct disprm *m_dispre * (For internal use only.) * struct disprm *m_disseq * (For internal use only.) * * * Global variable: const char *lin_errmsg[] - Status return messages * ------------------------------------------------------------------ * Error messages to match the status value returned from each function. * *===========================================================================*/ #ifndef WCSLIB_LIN #define WCSLIB_LIN #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_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. 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 internal use. //-------------------------------------------------------------------------- double *dummy; // The remainder are for memory management. int m_flag, m_naxis; double *m_crpix, *m_pc, *m_cdelt; struct disprm *m_dispre, *m_disseq; }; // 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[], double imgcrd[]); 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. #define linini_errmsg lin_errmsg #define lincpy_errmsg lin_errmsg #define linfree_errmsg lin_errmsg #define linprt_errmsg lin_errmsg #define linset_errmsg lin_errmsg #define linp2x_errmsg lin_errmsg #define linx2p_errmsg lin_errmsg #ifdef __cplusplus } #endif #endif // WCSLIB_LIN astropy-astropy-201cddb/cextern/wcslib/C/log.c000066400000000000000000000046451507226315300214520ustar00rootroot00000000000000/*============================================================================ WCSLIB 8.4 - an implementation of the FITS WCS standard. Copyright (C) 1995-2024, 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/people/Mark.Calabretta $Id: log.c,v 8.4 2024/10/28 13:56:16 mcalabre Exp $ *===========================================================================*/ #include #include "log.h" // Map status return value to message. const char *log_errmsg[] = { "Success", "", "Invalid log-coordinate reference value", "One or more of the x coordinates were invalid", "One or more of the world coordinates were invalid"}; //---------------------------------------------------------------------------- int logx2s( double crval, int nx, int sx, int slogc, const double x[], double logc[], int stat[]) { if (crval <= 0.0) { return LOGERR_BAD_LOG_REF_VAL; } 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; } return 0; } //---------------------------------------------------------------------------- int logs2x( double crval, int nlogc, int slogc, int sx, const double logc[], double x[], int stat[]) { if (crval <= 0.0) { return LOGERR_BAD_LOG_REF_VAL; } 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; } else { status = LOGERR_BAD_WORLD; *statp = 1; } } return status; } astropy-astropy-201cddb/cextern/wcslib/C/log.h000066400000000000000000000130371507226315300214520ustar00rootroot00000000000000/*============================================================================ WCSLIB 8.4 - an implementation of the FITS WCS standard. Copyright (C) 1995-2024, 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/people/Mark.Calabretta $Id: log.h,v 8.4 2024/10/28 13:56:16 mcalabre Exp $ *============================================================================= * * WCSLIB 8.4 - 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 (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 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. * * Argument checking: * ------------------ * The input log-coordinate values are only checked for values that would * result in floating point exceptions and the same is true for the * log-coordinate reference value. * * Accuracy: * --------- * No warranty is given for the accuracy of these routines (refer to the * copyright notice); intending users must satisfy for themselves their * adequacy for the intended purpose. However, closure effectively to within * double precision rounding error was demonstrated by test routine tlog.c * which accompanies this software. * * * logx2s() - Transform to logarithmic coordinates * ----------------------------------------------- * logx2s() transforms intermediate world coordinates to logarithmic * coordinates. * * Given and returned: * crval double Log-coordinate reference value (CRVALia). * * Given: * nx int Vector length. * * sx int Vector stride. * * slogc int Vector stride. * * x const double[] * Intermediate world coordinates, in SI units. * * Returned: * logc double[] Logarithmic coordinates, in SI units. * * stat int[] Status return value status for each vector element: * 0: Success. * * Function return value: * int Status return value: * 0: Success. * 2: Invalid log-coordinate reference value. * * * logs2x() - Transform logarithmic coordinates * -------------------------------------------- * logs2x() transforms logarithmic world coordinates to intermediate world * coordinates. * * Given and returned: * crval double Log-coordinate reference value (CRVALia). * * Given: * nlogc int Vector length. * * slogc int Vector stride. * * sx int Vector stride. * * logc const double[] * Logarithmic coordinates, in SI units. * * Returned: * x double[] Intermediate world coordinates, in SI units. * * stat int[] Status return value status for each vector element: * 0: Success. * 1: Invalid value of logc. * * Function return value: * int Status return value: * 0: Success. * 2: Invalid log-coordinate reference value. * 4: One or more of the world-coordinate values * are incorrect, as indicated by the stat vector. * * * Global variable: const char *log_errmsg[] - Status return messages * ------------------------------------------------------------------ * Error messages to match the status value returned from each function. * *===========================================================================*/ #ifndef WCSLIB_LOG #define WCSLIB_LOG #ifdef __cplusplus extern "C" { #endif 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. }; int logx2s(double crval, int nx, int sx, int slogc, const double x[], double logc[], int stat[]); int logs2x(double crval, int nlogc, int slogc, int sx, const double logc[], double x[], int stat[]); #ifdef __cplusplus } #endif #endif // WCSLIB_LOG astropy-astropy-201cddb/cextern/wcslib/C/prj.c000066400000000000000000005721641507226315300214720ustar00rootroot00000000000000/*============================================================================ WCSLIB 8.4 - an implementation of the FITS WCS standard. Copyright (C) 1995-2024, 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/people/Mark.Calabretta $Id: prj.c,v 8.4 2024/10/28 13:56:16 mcalabre Exp $ *===========================================================================*/ #include #include #include #include #include "wcserr.h" #include "wcsmath.h" #include "wcsprintf.h" #include "wcstrig.h" #include "wcsutil.h" #include "prj.h" // Projection categories. const int ZENITHAL = 1; const int CYLINDRICAL = 2; const int PSEUDOCYLINDRICAL = 3; const int CONVENTIONAL = 4; const int CONIC = 5; const int POLYCONIC = 6; const int QUADCUBE = 7; const int HEALPIX = 8; const char prj_categories[9][32] = {"undefined", "zenithal", "cylindrical", "pseudocylindrical", "conventional", "conic", "polyconic", "quadcube", "HEALPix"}; // 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", "XPH"}; // Map status return value to message. const char *prj_errmsg[] = { "Success", "Null prjprm pointer passed", "Invalid projection parameters", "One or more of the (x,y) coordinates were invalid", "One or more of the (phi,theta) coordinates were invalid"}; 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); #define PRJERR_BAD_PIX_SET(function) \ wcserr_set(&(prj->err), PRJERR_BAD_PIX, function, __FILE__, __LINE__, \ "One or more of the (x, y) coordinates were invalid for %s projection", \ prj->name); #define PRJERR_BAD_WORLD_SET(function) \ wcserr_set(&(prj->err), PRJERR_BAD_WORLD, function, __FILE__, __LINE__, \ "One or more of the (lat, lng) coordinates were invalid for " \ "%s projection", prj->name); #define copysign(X, Y) ((Y) < 0.0 ? -fabs(X) : fabs(X)) /*============================================================================ * 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. * * prjx2s invokes the specific deprojection routine based on the pointer-to- * function stored in the prjprm struct. * * prjs2x invokes the specific projection routine based on the pointer-to- * function stored in the prjprm struct. * *---------------------------------------------------------------------------*/ int prjini(struct prjprm *prj) { if (prj == 0x0) return PRJERR_NULL_POINTER; strcpy(prj->code, " "); prj->pv[0] = 0.0; prj->pv[1] = UNDEFINED; prj->pv[2] = UNDEFINED; prj->pv[3] = UNDEFINED; for (int k = 4; k < PVN; prj->pv[k++] = 0.0); prj->r0 = 0.0; prj->phi0 = UNDEFINED; prj->theta0 = UNDEFINED; prj->bounds = 7; strcpy(prj->name, "undefined"); for (int k = 9; k < 40; prj->name[k++] = '\0'); prj->category = 0; prj->pvrange = 0; prj->simplezen = 0; prj->equiareal = 0; prj->conformal = 0; prj->global = 0; prj->divergent = 0; prj->x0 = 0.0; prj->y0 = 0.0; prj->err = 0x0; prj->padding = 0x0; 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(struct prjprm *prj) { if (prj == 0x0) return PRJERR_NULL_POINTER; wcserr_clear(&(prj->err)); return 0; } //---------------------------------------------------------------------------- int prjsize(const struct prjprm *prj, int sizes[2]) { 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; } //---------------------------------------------------------------------------- int prjenq(const struct prjprm *prj, int enquiry) { // 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(" r0: %9f\n", prj->r0); wcsprintf(" pv:"); if (prj->pvrange) { int n = (prj->pvrange)%100; if (prj->pvrange/100) { wcsprintf(" (0)"); } else { wcsprintf(" %#- 11.5g", prj->pv[0]); n--; } for (int i = 1; i <= n; i++) { if (i%5 == 1) { wcsprintf("\n "); } if (undefined(prj->pv[i])) { wcsprintf(" UNDEFINED "); } else { wcsprintf(" %#- 11.5g", prj->pv[i]); } } wcsprintf("\n"); } else { wcsprintf(" (not used)\n"); } if (undefined(prj->phi0)) { wcsprintf(" phi0: UNDEFINED\n"); } else { wcsprintf(" phi0: %9f\n", prj->phi0); } if (undefined(prj->theta0)) { wcsprintf(" theta0: UNDEFINED\n"); } else { wcsprintf(" theta0: %9f\n", prj->theta0); } wcsprintf(" bounds: %d\n", prj->bounds); // Derived values. wcsprintf("\n"); wcsprintf(" name: \"%s\"\n", prj->name); wcsprintf(" category: %d (%s)\n", prj->category, prj_categories[prj->category]); wcsprintf(" pvrange: %d\n", prj->pvrange); wcsprintf(" simplezen: %d\n", prj->simplezen); wcsprintf(" equiareal: %d\n", prj->equiareal); wcsprintf(" conformal: %d\n", prj->conformal); wcsprintf(" global: %d\n", prj->global); wcsprintf(" divergent: %d\n", prj->divergent); 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 (int i = 0; i < 5; i++) { wcsprintf(" %#- 11.5g", prj->w[i]); } wcsprintf("\n "); 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((void (*)(void))prj->prjx2s, hext)); wcsprintf(" prjs2x: %s\n", 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 prjbchk( double tol, int nphi, int ntheta, int spt, double phi[], double theta[], int stat[]) { 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; } } 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; if (prj->flag < 0) return 0; struct wcserr **err = &(prj->err); // 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) { status = szpset(prj); } else if (strcmp(prj->code, "TAN") == 0) { status = tanset(prj); } else if (strcmp(prj->code, "STG") == 0) { status = stgset(prj); } else if (strcmp(prj->code, "SIN") == 0) { status = sinset(prj); } else if (strcmp(prj->code, "ARC") == 0) { status = arcset(prj); } else if (strcmp(prj->code, "ZPN") == 0) { status = zpnset(prj); } else if (strcmp(prj->code, "ZEA") == 0) { status = zeaset(prj); } else if (strcmp(prj->code, "AIR") == 0) { status = airset(prj); } else if (strcmp(prj->code, "CYP") == 0) { status = cypset(prj); } else if (strcmp(prj->code, "CEA") == 0) { status = ceaset(prj); } else if (strcmp(prj->code, "CAR") == 0) { status = carset(prj); } else if (strcmp(prj->code, "MER") == 0) { status = merset(prj); } else if (strcmp(prj->code, "SFL") == 0) { status = sflset(prj); } else if (strcmp(prj->code, "PAR") == 0) { status = parset(prj); } else if (strcmp(prj->code, "MOL") == 0) { status = molset(prj); } else if (strcmp(prj->code, "AIT") == 0) { status = aitset(prj); } else if (strcmp(prj->code, "COP") == 0) { status = copset(prj); } else if (strcmp(prj->code, "COE") == 0) { status = coeset(prj); } else if (strcmp(prj->code, "COD") == 0) { status = codset(prj); } else if (strcmp(prj->code, "COO") == 0) { status = cooset(prj); } else if (strcmp(prj->code, "BON") == 0) { status = bonset(prj); } else if (strcmp(prj->code, "PCO") == 0) { status = pcoset(prj); } else if (strcmp(prj->code, "TSC") == 0) { status = tscset(prj); } else if (strcmp(prj->code, "CSC") == 0) { status = cscset(prj); } else if (strcmp(prj->code, "QSC") == 0) { 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. status = wcserr_set(WCSERR_SET(PRJERR_BAD_PARAM), "Unrecognized projection code '%s'", prj->code); } return status; } //---------------------------------------------------------------------------- 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[]) { // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; 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( 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) < 100) { if ((status = prjset(prj))) return status; } return prj->prjs2x(prj, nphi, ntheta, spt, sxy, phi, theta, x, y, stat); } /*============================================================================ * Internal helper routine used by the *set() routines - not intended for * outside use. It forces (x,y) = (0,0) at (phi0,theta0). *---------------------------------------------------------------------------*/ static int prjoff( struct prjprm *prj, const double phi0, const double theta0) { 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. 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"); } prj->x0 = x0; prj->y0 = y0; } return 0; } /*============================================================================ * AZP: zenithal/azimuthal perspective projection. * * Given: * prj->pv[1] Distance parameter, mu in units of r0. * prj->pv[2] Tilt angle, gamma in degrees. * * Given and/or returned: * prj->r0 Reset to 180/pi if 0. * prj->phi0 Reset to 0.0 if undefined. * prj->theta0 Reset to 90.0 if undefined. * * Returned: * prj->flag AZP * prj->code "AZP" * prj->x0 Offset in x. * prj->y0 Offset in y. * prj->w[0] r0*(mu+1) * prj->w[1] tan(gamma) * prj->w[2] sec(gamma) * prj->w[3] cos(gamma) * prj->w[4] sin(gamma) * prj->w[5] asin(-1/mu) for |mu| >= 1, -90 otherwise * prj->w[6] mu*cos(gamma) * prj->w[7] 1 if |mu*cos(gamma)| < 1, 0 otherwise * prj->prjx2s Pointer to azpx2s(). * prj->prjs2x Pointer to azps2x(). *===========================================================================*/ int azpset(struct prjprm *prj) { if (prj == 0x0) return PRJERR_NULL_POINTER; if (prj->flag == -AZP) return 0; strcpy(prj->code, "AZP"); if (undefined(prj->pv[1])) prj->pv[1] = 0.0; if (undefined(prj->pv[2])) prj->pv[2] = 0.0; if (prj->r0 == 0.0) prj->r0 = R2D; strcpy(prj->name, "zenithal/azimuthal perspective"); prj->category = ZENITHAL; prj->pvrange = 102; prj->simplezen = prj->pv[2] == 0.0; prj->equiareal = 0; prj->conformal = 0; prj->global = 0; prj->divergent = prj->pv[1] <= 1.0; prj->w[0] = prj->r0*(prj->pv[1] + 1.0); if (prj->w[0] == 0.0) { return PRJERR_BAD_PARAM_SET("azpset"); } prj->w[3] = cosd(prj->pv[2]); if (prj->w[3] == 0.0) { return PRJERR_BAD_PARAM_SET("azpset"); } prj->w[2] = 1.0/prj->w[3]; prj->w[4] = sind(prj->pv[2]); prj->w[1] = prj->w[4] / prj->w[3]; if (fabs(prj->pv[1]) > 1.0) { prj->w[5] = asind(-1.0/prj->pv[1]); } else { prj->w[5] = -90.0; } prj->w[6] = prj->pv[1] * prj->w[3]; prj->w[7] = (fabs(prj->w[6]) < 1.0) ? 1.0 : 0.0; prj->prjx2s = azpx2s; prj->prjs2x = azps2x; prj->flag = (prj->flag == 1) ? -AZP : AZP; return prjoff(prj, 0.0, 90.0); } //---------------------------------------------------------------------------- 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[]) { const double tol = 1.0e-13; // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; int status; if (abs(prj->flag) != AZP) { if ((status = azpset(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; 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 (int iy = 0; iy < my; iy++) { *phip = xj; phip += rowlen; } } // Do y dependence. const double *yp = y; int *statp = stat; phip = phi; thetap = theta; for (int iy = 0; iy < ny; iy++, yp += sxy) { double yj = *yp + prj->y0; double yc = yj*prj->w[3]; double yc2 = yc*yc; double q = prj->w[0] + yj*prj->w[4]; for (int ix = 0; ix < mx; ix++, phip += spt, thetap += spt, statp++) { double xj = *phip; double r = sqrt(xj*xj + yc2); if (r == 0.0) { *phip = 0.0; *thetap = 90.0; *statp = 0; } else { *phip = atan2d(xj, -yc); 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; if (!status) status = PRJERR_BAD_PIX_SET("azpx2s"); continue; } t = copysign(90.0, t); } else { t = asind(t); } 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; } } } // 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( 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) != AZP) { if ((status = azpset(prj))) return status; } int mphi, mtheta; if (ntheta > 0) { mphi = nphi; mtheta = ntheta; } else { mphi = 1; mtheta = 1; ntheta = nphi; } status = 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 sinphi, cosphi; sincosd(*phip, &sinphi, &cosphi); double *xp = x + rowoff; double *yp = y + rowoff; for (int itheta = 0; itheta < mtheta; itheta++) { *xp = sinphi; *yp = cosphi; 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, costhe; sincosd(*thetap, &sinthe, &costhe); 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; if (!status) status = PRJERR_BAD_WORLD_SET("azps2x"); } else { double r = prj->w[0]*costhe/t; // Bounds checking. int istat = 0; if (prj->bounds&1) { if (*thetap < prj->w[5]) { // Overlap. istat = 1; if (!status) status = PRJERR_BAD_WORLD_SET("azps2x"); } else if (prj->w[7] > 0.0) { // Divergence. t = prj->pv[1] / sqrt(1.0 + s*s); if (fabs(t) <= 1.0) { s = atand(-s); t = asind(t); double a = s - t; double b = s + t + 180.0; if (a > 90.0) a -= 360.0; if (b > 90.0) b -= 360.0; if (*thetap < ((a > b) ? a : b)) { istat = 1; if (!status) status = PRJERR_BAD_WORLD_SET("azps2x"); } } } } *xp = r*(*xp) - prj->x0; *yp = -r*(*yp)*prj->w[2] - prj->y0; *statp = istat; } } } return status; } /*============================================================================ * SZP: slant zenithal perspective projection. * * Given: * prj->pv[1] Distance of the point of projection from the centre of the * generating sphere, mu in units of r0. * prj->pv[2] Native longitude, phi_c, and ... * prj->pv[3] Native latitude, theta_c, on the planewards side of the * intersection of the line through the point of projection * and the centre of the generating sphere, phi_c in degrees. * * Given and/or returned: * prj->r0 Reset to 180/pi if 0. * prj->phi0 Reset to 0.0 if undefined. * prj->theta0 Reset to 90.0 if undefined. * * Returned: * prj->flag SZP * prj->code "SZP" * prj->x0 Fiducial offset in x. * prj->y0 Fiducial offset in y. * prj->w[0] 1/r0 * prj->w[1] xp = -mu*cos(theta_c)*sin(phi_c) * prj->w[2] yp = mu*cos(theta_c)*cos(phi_c) * prj->w[3] zp = mu*sin(theta_c) + 1 * prj->w[4] r0*xp * prj->w[5] r0*yp * prj->w[6] r0*zp * prj->w[7] (zp - 1)^2 * prj->w[8] asin(1-zp) if |1 - zp| < 1, -90 otherwise * prj->prjx2s Pointer to szpx2s(). * prj->prjs2x Pointer to szps2x(). *===========================================================================*/ int szpset(struct prjprm *prj) { if (prj == 0x0) return PRJERR_NULL_POINTER; if (prj->flag == -SZP) return 0; strcpy(prj->code, "SZP"); if (undefined(prj->pv[1])) prj->pv[1] = 0.0; if (undefined(prj->pv[2])) prj->pv[2] = 0.0; if (undefined(prj->pv[3])) prj->pv[3] = 90.0; if (prj->r0 == 0.0) prj->r0 = R2D; strcpy(prj->name, "slant zenithal perspective"); prj->category = ZENITHAL; prj->pvrange = 103; prj->simplezen = prj->pv[3] == 90.0; prj->equiareal = 0; prj->conformal = 0; prj->global = 0; prj->divergent = prj->pv[1] <= 1.0; prj->w[0] = 1.0/prj->r0; prj->w[3] = prj->pv[1] * sind(prj->pv[3]) + 1.0; if (prj->w[3] == 0.0) { return PRJERR_BAD_PARAM_SET("szpset"); } prj->w[1] = -prj->pv[1] * cosd(prj->pv[3]) * sind(prj->pv[2]); prj->w[2] = prj->pv[1] * cosd(prj->pv[3]) * cosd(prj->pv[2]); prj->w[4] = prj->r0 * prj->w[1]; prj->w[5] = prj->r0 * prj->w[2]; prj->w[6] = prj->r0 * prj->w[3]; prj->w[7] = (prj->w[3] - 1.0) * prj->w[3] - 1.0; if (fabs(prj->w[3] - 1.0) < 1.0) { prj->w[8] = asind(1.0 - prj->w[3]); } else { prj->w[8] = -90.0; } prj->prjx2s = szpx2s; prj->prjs2x = szps2x; prj->flag = (prj->flag == 1) ? -SZP : SZP; return prjoff(prj, 0.0, 90.0); } //---------------------------------------------------------------------------- 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[]) { const double tol = 1.0e-13; // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; int status; if (abs(prj->flag) != SZP) { if ((status = szpset(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[0]; 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[0]; for (int ix = 0; ix < mx; ix++, phip += spt, thetap += spt, statp++) { double xr = *phip; double r2 = xr*xr + yr*yr; 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. z = r2/2.0; *thetap = 90.0 - R2D*sqrt(r2/(1.0 + xy)); } else { 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. if (d < 0.0) { *phip = 0.0; *thetap = 0.0; *statp = 1; if (!status) status = PRJERR_BAD_PIX_SET("szpx2s"); continue; } d = sqrt(d); // 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; } else { sinthe = (sinth1 < sinth2) ? sinth1 : sinth2; } } if (sinthe < -1.0) { if (sinthe+1.0 > -tol) { sinthe = -1.0; } } if (sinthe > 1.0 || sinthe < -1.0) { *phip = 0.0; *thetap = 0.0; *statp = 1; if (!status) status = PRJERR_BAD_PIX_SET("szpx2s"); continue; } *thetap = asind(sinthe); z = 1.0 - sinthe; } *phip = atan2d(xr - x1*z, -(yr - y1*z)); *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( 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) != SZP) { if ((status = szpset(prj))) return status; } int mphi, mtheta; if (ntheta > 0) { mphi = nphi; mtheta = ntheta; } else { mphi = 1; mtheta = 1; ntheta = nphi; } status = 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 sinphi, cosphi; sincosd(*phip, &sinphi, &cosphi); double *xp = x + rowoff; double *yp = y + rowoff; for (int itheta = 0; itheta < mtheta; itheta++) { *xp = sinphi; *yp = cosphi; 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 s = 1.0 - sind(*thetap); double t = prj->w[3] - s; if (t == 0.0) { for (int iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy, statp++) { *xp = 0.0; *yp = 0.0; *statp = 1; } if (!status) status = PRJERR_BAD_WORLD_SET("szps2x"); } else { 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. istat = 1; if (!status) status = PRJERR_BAD_WORLD_SET("szps2x"); } else if (fabs(prj->pv[1]) > 1.0) { // 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); double a = s - t; double b = s + t + 180.0; if (a > 90.0) a -= 360.0; if (b > 90.0) b -= 360.0; if (*thetap < ((a > b) ? a : b)) { istat = 1; if (!status) status = PRJERR_BAD_WORLD_SET("szps2x"); } } } } *xp = r*(*xp) - u; *yp = -r*(*yp) - v; *statp = istat; } } } return status; } /*============================================================================ * TAN: gnomonic 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 90.0 if undefined. * * Returned: * prj->flag TAN * prj->code "TAN" * prj->x0 Fiducial offset in x. * prj->y0 Fiducial offset in y. * prj->prjx2s Pointer to tanx2s(). * prj->prjs2x Pointer to tans2x(). *===========================================================================*/ int tanset(struct prjprm *prj) { if (prj == 0x0) return PRJERR_NULL_POINTER; if (prj->flag == -TAN) return 0; strcpy(prj->code, "TAN"); if (prj->r0 == 0.0) prj->r0 = R2D; strcpy(prj->name, "gnomonic"); prj->category = ZENITHAL; prj->pvrange = 0; prj->simplezen = 1; prj->equiareal = 0; prj->conformal = 0; prj->global = 0; prj->divergent = 1; prj->prjx2s = tanx2s; prj->prjs2x = tans2x; prj->flag = (prj->flag == 1) ? -TAN : TAN; return prjoff(prj, 0.0, 90.0); } //---------------------------------------------------------------------------- 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[]) { // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; int status; if (abs(prj->flag) != TAN) { if ((status = tanset(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 xj = *xp + prj->x0; double *phip = phi + rowoff; for (int iy = 0; iy < my; iy++) { *phip = xj; 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 yj = *yp + prj->y0; double yj2 = yj*yj; for (int ix = 0; ix < mx; ix++, phip += spt, thetap += spt, statp++) { double xj = *phip; double r = sqrt(xj*xj + yj2); if (r == 0.0) { *phip = 0.0; } else { *phip = atan2d(xj, -yj); } *thetap = atan2d(prj->r0, r); *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("tanx2s"); } return status; } //---------------------------------------------------------------------------- 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. if (prj == 0x0) return PRJERR_NULL_POINTER; int status; if (abs(prj->flag) != TAN) { if ((status = tanset(prj))) return status; } int mphi, mtheta; if (ntheta > 0) { mphi = nphi; mtheta = ntheta; } else { mphi = 1; mtheta = 1; ntheta = nphi; } status = 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 sinphi, cosphi; sincosd(*phip, &sinphi, &cosphi); double *xp = x + rowoff; double *yp = y + rowoff; for (int itheta = 0; itheta < mtheta; itheta++) { *xp = sinphi; *yp = cosphi; 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 s = sind(*thetap); if (s == 0.0) { for (int iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy, statp++) { *xp = 0.0; *yp = 0.0; *statp = 1; } if (!status) status = PRJERR_BAD_WORLD_SET("tans2x"); } else { double r = prj->r0*cosd(*thetap)/s; // Bounds checking. int istat = 0; if (prj->bounds&1) { if (s < 0.0) { istat = 1; if (!status) status = PRJERR_BAD_WORLD_SET("tans2x"); } } for (int iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy, statp++) { *xp = r*(*xp) - prj->x0; *yp = -r*(*yp) - prj->y0; *statp = istat; } } } return status; } /*============================================================================ * STG: stereographic 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 90.0 if undefined. * * Returned: * prj->flag STG * prj->code "STG" * prj->x0 Fiducial offset in x. * prj->y0 Fiducial offset in y. * prj->w[0] 2*r0 * prj->w[1] 1/(2*r0) * prj->prjx2s Pointer to stgx2s(). * prj->prjs2x Pointer to stgs2x(). *===========================================================================*/ int stgset(struct prjprm *prj) { if (prj == 0x0) return PRJERR_NULL_POINTER; if (prj->flag == -STG) return 0; strcpy(prj->code, "STG"); strcpy(prj->name, "stereographic"); prj->category = ZENITHAL; prj->pvrange = 0; prj->simplezen = 1; prj->equiareal = 0; prj->conformal = 1; prj->global = 0; prj->divergent = 1; if (prj->r0 == 0.0) { prj->r0 = R2D; prj->w[0] = 360.0/PI; prj->w[1] = PI/360.0; } else { prj->w[0] = 2.0*prj->r0; prj->w[1] = 1.0/prj->w[0]; } prj->prjx2s = stgx2s; prj->prjs2x = stgs2x; prj->flag = (prj->flag == 1) ? -STG : STG; return prjoff(prj, 0.0, 90.0); } //---------------------------------------------------------------------------- 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[]) { // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; int status; if (abs(prj->flag) != STG) { if ((status = stgset(prj))) return status; } int mx, my; if (ny > 0) { mx = nx; my = ny; } else { mx = 1; my = 1; ny = nx; } // 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. 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 (int ix = 0; ix < mx; ix++, phip += spt, thetap += spt, statp++) { double xj = *phip; double r = sqrt(xj*xj + yj2); if (r == 0.0) { *phip = 0.0; } else { *phip = atan2d(xj, -yj); } *thetap = 90.0 - 2.0*atand(r*prj->w[1]); *statp = 0; } } return 0; } //---------------------------------------------------------------------------- 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[]) { // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; int status; if (abs(prj->flag) != STG) { if ((status = stgset(prj))) return status; } int mphi, mtheta; if (ntheta > 0) { mphi = nphi; mtheta = ntheta; } else { mphi = 1; mtheta = 1; ntheta = nphi; } status = 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 sinphi, cosphi; sincosd(*phip, &sinphi, &cosphi); double *xp = x + rowoff; double *yp = y + rowoff; for (int itheta = 0; itheta < mtheta; itheta++) { *xp = sinphi; *yp = cosphi; 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 s = 1.0 + sind(*thetap); if (s == 0.0) { for (int iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy, statp++) { *xp = 0.0; *yp = 0.0; *statp = 1; } if (!status) status = PRJERR_BAD_WORLD_SET("stgs2x"); } else { double r = prj->w[0]*cosd(*thetap)/s; for (int iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy, statp++) { *xp = r*(*xp) - prj->x0; *yp = -r*(*yp) - prj->y0; *statp = 0; } } } return status; } /*============================================================================ * SIN: orthographic/synthesis projection. * * Given: * prj->pv[1:2] Obliqueness parameters, xi and eta. * * Given and/or returned: * prj->r0 Reset to 180/pi if 0. * prj->phi0 Reset to 0.0 if undefined. * prj->theta0 Reset to 90.0 if undefined. * * Returned: * prj->flag SIN * prj->code "SIN" * prj->x0 Fiducial offset in x. * prj->y0 Fiducial offset in y. * prj->w[0] 1/r0 * prj->w[1] xi**2 + eta**2 * prj->w[2] xi**2 + eta**2 + 1 * prj->w[3] xi**2 + eta**2 - 1 * prj->prjx2s Pointer to sinx2s(). * prj->prjs2x Pointer to sins2x(). *===========================================================================*/ int sinset(struct prjprm *prj) { if (prj == 0x0) return PRJERR_NULL_POINTER; if (prj->flag == -SIN) return 0; strcpy(prj->code, "SIN"); if (undefined(prj->pv[1])) prj->pv[1] = 0.0; if (undefined(prj->pv[2])) prj->pv[2] = 0.0; if (prj->r0 == 0.0) prj->r0 = R2D; strcpy(prj->name, "orthographic/synthesis"); prj->category = ZENITHAL; prj->pvrange = 102; prj->simplezen = (prj->pv[1] == 0.0 && prj->pv[2] == 0.0); prj->equiareal = 0; prj->conformal = 0; prj->global = 0; prj->divergent = 0; prj->w[0] = 1.0/prj->r0; prj->w[1] = prj->pv[1]*prj->pv[1] + prj->pv[2]*prj->pv[2]; prj->w[2] = prj->w[1] + 1.0; prj->w[3] = prj->w[1] - 1.0; prj->prjx2s = sinx2s; prj->prjs2x = sins2x; prj->flag = (prj->flag == 1) ? -SIN : SIN; return prjoff(prj, 0.0, 90.0); } //---------------------------------------------------------------------------- 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[]) { const double tol = 1.0e-13; // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; int status; if (abs(prj->flag) != SIN) { if ((status = sinset(prj))) return status; } double xi = prj->pv[1]; double eta = prj->pv[2]; 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 x0 = (*xp + prj->x0)*prj->w[0]; double *phip = phi + rowoff; for (int iy = 0; iy < my; iy++) { *phip = x0; 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 y0 = (*yp + prj->y0)*prj->w[0]; double y02 = y0*y0; 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. if (r2 != 0.0) { *phip = atan2d(x0, -y0); } else { *phip = 0.0; } if (r2 < 0.5) { *thetap = acosd(sqrt(r2)); } else if (r2 <= 1.0) { *thetap = asind(sqrt(1.0 - r2)); } else { *statp = 1; if (!status) status = PRJERR_BAD_PIX_SET("sinx2s") continue; } } else { // "Synthesis" projection. double xy = x0*xi + y0*eta; double z; if (r2 < 1.0e-10) { // Use small angle formula. z = r2/2.0; *thetap = 90.0 - R2D*sqrt(r2/(1.0 + xy)); } else { 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. if (d < 0.0) { *phip = 0.0; *thetap = 0.0; *statp = 1; if (!status) status = PRJERR_BAD_PIX_SET("sinx2s") continue; } d = sqrt(d); // 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; } else { sinthe = (sinth1 < sinth2) ? sinth1 : sinth2; } } if (sinthe < -1.0) { if (sinthe+1.0 > -tol) { sinthe = -1.0; } } if (sinthe > 1.0 || sinthe < -1.0) { *phip = 0.0; *thetap = 0.0; *statp = 1; if (!status) status = PRJERR_BAD_PIX_SET("sinx2s") continue; } *thetap = asind(sinthe); z = 1.0 - sinthe; } double x1 = -y0 + eta*z; double y1 = x0 - xi*z; if (x1 == 0.0 && y1 == 0.0) { *phip = 0.0; } else { *phip = atan2d(y1,x1); } } *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( 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) != SIN) { if ((status = sinset(prj))) return status; } int mphi, mtheta; if (ntheta > 0) { mphi = nphi; mtheta = ntheta; } else { mphi = 1; mtheta = 1; ntheta = nphi; } status = 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 sinphi, cosphi; sincosd(*phip, &sinphi, &cosphi); double *xp = x + rowoff; double *yp = y + rowoff; for (int itheta = 0; itheta < mtheta; itheta++) { *xp = sinphi; *yp = cosphi; 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 costhe, z; double t = (90.0 - fabs(*thetap))*D2R; if (t < 1.0e-5) { if (*thetap > 0.0) { z = t*t/2.0; } else { z = 2.0 - t*t/2.0; } costhe = t; } else { z = 1.0 - sind(*thetap); costhe = cosd(*thetap); } double r = prj->r0*costhe; if (prj->w[1] == 0.0) { // Orthographic projection. int istat = 0; if (prj->bounds&1) { if (*thetap < 0.0) { istat = 1; if (!status) status = PRJERR_BAD_WORLD_SET("sins2x"); } } for (int iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy, statp++) { *xp = r*(*xp) - prj->x0; *yp = -r*(*yp) - prj->y0; *statp = istat; } } else { // "Synthesis" projection. z *= prj->r0; double z1 = prj->pv[1]*z - prj->x0; double z2 = prj->pv[2]*z - prj->y0; 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; if (!status) status = PRJERR_BAD_WORLD_SET("sins2x"); } } *xp = r*(*xp) + z1; *yp = -r*(*yp) + z2; *statp = istat; } } } return status; } /*============================================================================ * ARC: zenithal/azimuthal equidistant 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 90.0 if undefined. * * Returned: * prj->flag ARC * prj->code "ARC" * prj->x0 Fiducial offset in x. * prj->y0 Fiducial offset in y. * prj->w[0] r0*(pi/180) * prj->w[1] (180/pi)/r0 * prj->prjx2s Pointer to arcx2s(). * prj->prjs2x Pointer to arcs2x(). *===========================================================================*/ int arcset(struct prjprm *prj) { if (prj == 0x0) return PRJERR_NULL_POINTER; if (prj->flag == -ARC) return 0; strcpy(prj->code, "ARC"); strcpy(prj->name, "zenithal/azimuthal equidistant"); prj->category = ZENITHAL; prj->pvrange = 0; prj->simplezen = 1; prj->equiareal = 0; 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] = 1.0/prj->w[0]; } prj->prjx2s = arcx2s; prj->prjs2x = arcs2x; prj->flag = (prj->flag == 1) ? -ARC : ARC; return prjoff(prj, 0.0, 90.0); } //---------------------------------------------------------------------------- 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[]) { // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; int status; if (abs(prj->flag) != ARC) { if ((status = arcset(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 xj = *xp + prj->x0; double *phip = phi + rowoff; for (int iy = 0; iy < my; iy++) { *phip = xj; 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 yj = *yp + prj->y0; double yj2 = yj*yj; for (int ix = 0; ix < mx; ix++, phip += spt, thetap += spt, statp++) { double xj = *phip; double r = sqrt(xj*xj + yj2); if (r == 0.0) { *phip = 0.0; *thetap = 90.0; } else { *phip = atan2d(xj, -yj); *thetap = 90.0 - r*prj->w[1]; } *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("arcx2s"); } return status; } //---------------------------------------------------------------------------- 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. if (prj == 0x0) return PRJERR_NULL_POINTER; int status; if (abs(prj->flag) != ARC) { if ((status = arcset(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; for (int iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { double sinphi, cosphi; sincosd(*phip, &sinphi, &cosphi); double *xp = x + rowoff; double *yp = y + rowoff; for (int itheta = 0; itheta < mtheta; itheta++) { *xp = sinphi; *yp = cosphi; 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 r = prj->w[0]*(90.0 - *thetap); for (int iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy, statp++) { *xp = r*(*xp) - prj->x0; *yp = -r*(*yp) - prj->y0; *statp = 0; } } return 0; } /*============================================================================ * ZPN: zenithal/azimuthal polynomial projection. * * Given: * prj->pv[] Polynomial coefficients. * * Given and/or returned: * prj->r0 Reset to 180/pi if 0. * prj->phi0 Reset to 0.0 if undefined. * prj->theta0 Reset to 90.0 if undefined. * * Returned: * prj->flag ZPN * prj->code "ZPN" * prj->x0 Fiducial offset in x. * prj->y0 Fiducial offset in y. * prj->n Degree of the polynomial, N. * prj->w[0] Co-latitude of the first point of inflection, radian. * prj->w[1] Radius of the first point of inflection (N > 1), radian. * prj->prjx2s Pointer to zpnx2s(). * prj->prjs2x Pointer to zpns2x(). *===========================================================================*/ int zpnset(struct prjprm *prj) { const double tol = 1.0e-13; if (prj == 0x0) return PRJERR_NULL_POINTER; if (prj->flag == -ZPN) return 0; strcpy(prj->code, "ZPN"); if (undefined(prj->pv[1])) prj->pv[1] = 0.0; if (undefined(prj->pv[2])) prj->pv[2] = 0.0; if (undefined(prj->pv[3])) prj->pv[3] = 0.0; if (prj->r0 == 0.0) prj->r0 = R2D; strcpy(prj->name, "zenithal/azimuthal polynomial"); prj->category = ZENITHAL; prj->pvrange = 30; prj->simplezen = 1; prj->equiareal = 0; prj->conformal = 0; prj->global = 0; prj->divergent = 0; // 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"); } prj->n = k; if (k < 2) { // No point of inflection. prj->w[0] = PI; } else { // 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. int j; double zd, zd1, zd2; zd1 = 0.0; for (j = 0; j < 180; j++) { zd2 = j*D2R; d2 = 0.0; for (int m = k; m > 0; m--) { d2 = d2*zd2 + m*prj->pv[m]; } if (d2 <= 0.0) break; zd1 = zd2; d1 = d2; } if (j == 180) { // No negative derivative -> no point of inflection. zd = PI; prj->global = 1; } else { // Find where the derivative is zero. for (j = 1; j <= 10; j++) { zd = zd1 - d1*(zd2-zd1)/(d2-d1); d = 0.0; for (int m = k; m > 0; m--) { d = d*zd + m*prj->pv[m]; } if (fabs(d) < tol) break; if (d < 0.0) { zd2 = zd; d2 = d; } else { zd1 = zd; d1 = d; } } } double r = 0.0; for (int m = k; m >= 0; m--) { r = r*zd + prj->pv[m]; } prj->w[0] = zd; prj->w[1] = r; } prj->prjx2s = zpnx2s; prj->prjs2x = zpns2x; prj->flag = (prj->flag == 1) ? -ZPN : ZPN; return prjoff(prj, 0.0, 90.0); } //---------------------------------------------------------------------------- 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[]) { const double tol = 1.0e-13; // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; int status; if (abs(prj->flag) != ZPN) { if ((status = zpnset(prj))) return status; } int k = prj->n; 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 xj = *xp + prj->x0; double *phip = phi + rowoff; for (int iy = 0; iy < my; iy++) { *phip = xj; 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 yj = *yp + prj->y0; double yj2 = yj*yj; for (int ix = 0; ix < mx; ix++, phip += spt, thetap += spt, statp++) { double xj = *phip; 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. return PRJERR_BAD_PARAM_SET("zpnx2s"); } else if (k == 1) { // Linear. zd = (r - prj->pv[0])/prj->pv[1]; } else if (k == 2) { // Quadratic. double a = prj->pv[2]; double b = prj->pv[1]; double c = prj->pv[0] - r; double d = b*b - 4.0*a*c; if (d < 0.0) { *thetap = 0.0; *statp = 1; if (!status) status = PRJERR_BAD_PIX_SET("zpnx2s"); continue; } d = sqrt(d); // 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; if (!status) status = PRJERR_BAD_PIX_SET("zpnx2s"); continue; } zd = 0.0; } else if (zd > PI) { if (zd > PI+tol) { *thetap = 0.0; *statp = 1; if (!status) status = PRJERR_BAD_PIX_SET("zpnx2s"); continue; } zd = PI; } } else { // 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; if (!status) status = PRJERR_BAD_PIX_SET("zpnx2s"); continue; } zd = zd1; } else if (r > r2) { if (r > r2+tol) { *thetap = 0.0; *statp = 1; if (!status) status = PRJERR_BAD_PIX_SET("zpnx2s"); continue; } zd = zd2; } else { // 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) { lambda = 0.9; } zd = zd2 - lambda*(zd2 - zd1); double rt = 0.0; for (int m = k; m >= 0; m--) { rt = (rt * zd) + prj->pv[m]; } if (rt < r) { if (r-rt < tol) break; r1 = rt; zd1 = zd; } else { if (rt-r < tol) break; r2 = rt; zd2 = zd; } if (fabs(zd2-zd1) < tol) break; } } } *thetap = 90.0 - zd*R2D; *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( 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) != ZPN) { if ((status = zpnset(prj))) return status; } int mphi, mtheta; if (ntheta > 0) { mphi = nphi; mtheta = ntheta; } else { mphi = 1; mtheta = 1; ntheta = nphi; } status = 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 sinphi, cosphi; sincosd(*phip, &sinphi, &cosphi); double *xp = x + rowoff; double *yp = y + rowoff; for (int itheta = 0; itheta < mtheta; itheta++) { *xp = sinphi; *yp = cosphi; 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 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; // 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 (int iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy, statp++) { *xp = r*(*xp) - prj->x0; *yp = -r*(*yp) - prj->y0; *statp = istat; } } return status; } /*============================================================================ * ZEA: zenithal/azimuthal equal area 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 90.0 if undefined. * * Returned: * prj->flag ZEA * prj->code "ZEA" * prj->x0 Fiducial offset in x. * prj->y0 Fiducial offset in y. * prj->w[0] 2*r0 * prj->w[1] 1/(2*r0) * prj->prjx2s Pointer to zeax2s(). * prj->prjs2x Pointer to zeas2x(). *===========================================================================*/ int zeaset(struct prjprm *prj) { if (prj == 0x0) return PRJERR_NULL_POINTER; if (prj->flag == -ZEA) return 0; strcpy(prj->code, "ZEA"); strcpy(prj->name, "zenithal/azimuthal equal area"); prj->category = ZENITHAL; prj->pvrange = 0; prj->simplezen = 1; prj->equiareal = 1; prj->conformal = 0; prj->global = 1; prj->divergent = 0; if (prj->r0 == 0.0) { prj->r0 = R2D; prj->w[0] = 360.0/PI; prj->w[1] = PI/360.0; } else { prj->w[0] = 2.0*prj->r0; prj->w[1] = 1.0/prj->w[0]; } prj->prjx2s = zeax2s; prj->prjs2x = zeas2x; prj->flag = (prj->flag == 1) ? -ZEA : ZEA; return prjoff(prj, 0.0, 90.0); } //---------------------------------------------------------------------------- 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[]) { const double tol = 1.0e-12; // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; int status; if (abs(prj->flag) != ZEA) { if ((status = zeaset(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 xj = *xp + prj->x0; double *phip = phi + rowoff; for (int iy = 0; iy < my; iy++) { *phip = xj; 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 yj = *yp + prj->y0; double yj2 = yj*yj; for (int ix = 0; ix < mx; ix++, phip += spt, thetap += spt, statp++) { double xj = *phip; double r = sqrt(xj*xj + yj2); if (r == 0.0) { *phip = 0.0; } else { *phip = atan2d(xj, -yj); } 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; if (!status) status = PRJERR_BAD_PIX_SET("zeax2s"); continue; } } else { *thetap = 90.0 - 2.0*asind(s); } *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( 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) != ZEA) { if ((status = zeaset(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; for (int iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { double sinphi, cosphi; sincosd(*phip, &sinphi, &cosphi); double *xp = x + rowoff; double *yp = y + rowoff; for (int itheta = 0; itheta < mtheta; itheta++) { *xp = sinphi; *yp = cosphi; 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 r = prj->w[0]*sind((90.0 - *thetap)/2.0); for (int iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy, statp++) { *xp = r*(*xp) - prj->x0; *yp = -r*(*yp) - prj->y0; *statp = 0; } } return 0; } /*============================================================================ * AIR: Airy's projection. * * Given: * prj->pv[1] Latitude theta_b within which the error is minimized, in * degrees. * * Given and/or returned: * prj->r0 Reset to 180/pi if 0. * prj->phi0 Reset to 0.0 if undefined. * prj->theta0 Reset to 90.0 if undefined. * * Returned: * prj->flag AIR * prj->code "AIR" * prj->x0 Fiducial offset in x. * prj->y0 Fiducial offset in y. * prj->w[0] 2*r0 * prj->w[1] ln(cos(xi_b))/tan(xi_b)**2, where xi_b = (90-theta_b)/2 * prj->w[2] 1/2 - prj->w[1] * prj->w[3] 2*r0*prj->w[2] * prj->w[4] tol, cutoff for using small angle approximation, in * radians. * prj->w[5] prj->w[2]*tol * prj->w[6] (180/pi)/prj->w[2] * prj->prjx2s Pointer to airx2s(). * prj->prjs2x Pointer to airs2x(). *===========================================================================*/ int airset(struct prjprm *prj) { const double tol = 1.0e-4; if (prj == 0x0) return PRJERR_NULL_POINTER; if (prj->flag == -AIR) return 0; strcpy(prj->code, "AIR"); if (undefined(prj->pv[1])) prj->pv[1] = 90.0; if (prj->r0 == 0.0) prj->r0 = R2D; strcpy(prj->name, "Airy's zenithal"); prj->category = ZENITHAL; prj->pvrange = 101; prj->simplezen = 1; prj->equiareal = 0; prj->conformal = 0; prj->global = 0; prj->divergent = 1; prj->w[0] = 2.0*prj->r0; if (prj->pv[1] == 90.0) { prj->w[1] = -0.5; prj->w[2] = 1.0; } else if (prj->pv[1] > -90.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 { return PRJERR_BAD_PARAM_SET("airset"); } prj->w[3] = prj->w[0] * prj->w[2]; prj->w[4] = tol; prj->w[5] = prj->w[2]*tol; prj->w[6] = R2D/prj->w[2]; prj->prjx2s = airx2s; prj->prjs2x = airs2x; prj->flag = (prj->flag == 1) ? -AIR : AIR; return prjoff(prj, 0.0, 90.0); } //---------------------------------------------------------------------------- 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[]) { const double tol = 1.0e-12; // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; int status; if (abs(prj->flag) != AIR) { if ((status = airset(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 xj = *xp + prj->x0; double *phip = phi + rowoff; for (int iy = 0; iy < my; iy++) { *phip = xj; 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 yj = *yp + prj->y0; double yj2 = yj*yj; for (int ix = 0; ix < mx; ix++, phip += spt, thetap += spt, statp++) { double xj = *phip; 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. 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; double tanxi = sqrt(1.0-x2*x2)/x2; r2 = -(log(x2)/tanxi + prj->w[1]*tanxi); if (r2 >= r) break; x1 = x2; r1 = r2; } if (k == 30) { *thetap = 0.0; *statp = 1; if (!status) status = PRJERR_BAD_PIX_SET("airx2s"); continue; } double cosxi; for (k = 0; k < 100; k++) { // Weighted division of the interval. double lambda = (r2-r)/(r2-r1); if (lambda < 0.1) { lambda = 0.1; } else if (lambda > 0.9) { lambda = 0.9; } cosxi = x2 - lambda*(x2-x1); 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; r1 = rt; x1 = cosxi; } else { if (rt-r < tol) break; r2 = rt; x2 = cosxi; } } if (k == 100) { *thetap = 0.0; *statp = 1; if (!status) status = PRJERR_BAD_PIX_SET("airx2s"); continue; } xi = acosd(cosxi); } *thetap = 90.0 - 2.0*xi; *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( 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) != AIR) { if ((status = airset(prj))) return status; } int mphi, mtheta; if (ntheta > 0) { mphi = nphi; mtheta = ntheta; } else { mphi = 1; mtheta = 1; ntheta = nphi; } status = 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 sinphi, cosphi; sincosd(*phip, &sinphi, &cosphi); double *xp = x + rowoff; double *yp = y + rowoff; for (int itheta = 0; itheta < mtheta; itheta++) { *xp = sinphi; *yp = cosphi; 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) { int istat = 0; double r; if (*thetap == 90.0) { r = 0.0; } else if (*thetap > -90.0) { double xi = D2R*(90.0 - *thetap)/2.0; if (xi < prj->w[4]) { r = xi*prj->w[3]; } else { 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 { r = 0.0; istat = 1; if (!status) status = PRJERR_BAD_WORLD_SET("airs2x"); } for (int iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy, statp++) { *xp = r*(*xp) - prj->x0; *yp = -r*(*yp) - prj->y0; *statp = istat; } } return status; } /*============================================================================ * CYP: cylindrical perspective projection. * * Given: * prj->pv[1] Distance of point of projection from the centre of the * generating sphere, mu, in units of r0. * prj->pv[2] Radius of the cylinder of projection, lambda, in units of * r0. * * 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 CYP * prj->code "CYP" * prj->x0 Fiducial offset in x. * prj->y0 Fiducial offset in y. * prj->w[0] r0*lambda*(pi/180) * prj->w[1] (180/pi)/(r0*lambda) * prj->w[2] r0*(mu + lambda) * prj->w[3] 1/(r0*(mu + lambda)) * prj->prjx2s Pointer to cypx2s(). * prj->prjs2x Pointer to cyps2x(). *===========================================================================*/ int cypset(struct prjprm *prj) { if (prj == 0x0) return PRJERR_NULL_POINTER; if (prj->flag == -CYP) return 0; strcpy(prj->code, "CYP"); if (undefined(prj->pv[1])) prj->pv[1] = 1.0; if (undefined(prj->pv[2])) prj->pv[2] = 1.0; strcpy(prj->name, "cylindrical perspective"); prj->category = CYLINDRICAL; prj->pvrange = 102; prj->simplezen = 0; prj->equiareal = 0; prj->conformal = 0; prj->global = prj->pv[1] < -1.0 || 0.0 < prj->pv[1]; prj->divergent = !prj->global; if (prj->r0 == 0.0) { prj->r0 = R2D; prj->w[0] = prj->pv[2]; if (prj->w[0] == 0.0) { return PRJERR_BAD_PARAM_SET("cypset"); } prj->w[1] = 1.0/prj->w[0]; prj->w[2] = R2D*(prj->pv[1] + prj->pv[2]); if (prj->w[2] == 0.0) { return PRJERR_BAD_PARAM_SET("cypset"); } prj->w[3] = 1.0/prj->w[2]; } else { prj->w[0] = prj->r0*prj->pv[2]*D2R; if (prj->w[0] == 0.0) { return PRJERR_BAD_PARAM_SET("cypset"); } prj->w[1] = 1.0/prj->w[0]; prj->w[2] = prj->r0*(prj->pv[1] + prj->pv[2]); if (prj->w[2] == 0.0) { return PRJERR_BAD_PARAM_SET("cypset"); } prj->w[3] = 1.0/prj->w[2]; } prj->prjx2s = cypx2s; prj->prjs2x = cyps2x; prj->flag = (prj->flag == 1) ? -CYP : CYP; return prjoff(prj, 0.0, 0.0); } //---------------------------------------------------------------------------- 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[]) { // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; int status; if (abs(prj->flag) != CYP) { if ((status = cypset(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 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. 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 (int ix = 0; ix < mx; ix++, thetap += spt, statp++) { *thetap = t; *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("cypx2s"); } return status; } //---------------------------------------------------------------------------- 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. if (prj == 0x0) return PRJERR_NULL_POINTER; int status; if (abs(prj->flag) != CYP) { if ((status = cypset(prj))) return status; } int mphi, mtheta; if (ntheta > 0) { mphi = nphi; mtheta = ntheta; } else { mphi = 1; mtheta = 1; ntheta = nphi; } status = 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 xi = prj->w[0]*(*phip) - prj->x0; double *xp = x + rowoff; for (int itheta = 0; itheta < mtheta; itheta++) { *xp = xi; xp += rowlen; } } // 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); int istat = 0; if (eta == 0.0) { istat = 1; if (!status) status = PRJERR_BAD_WORLD_SET("cyps2x"); } else { eta = prj->w[2]*sind(*thetap)/eta; } eta -= prj->y0; for (int iphi = 0; iphi < mphi; iphi++, yp += sxy, statp++) { *yp = eta; *statp = istat; } } return status; } /*============================================================================ * CEA: cylindrical equal area projection. * * Given: * prj->pv[1] Square of the cosine of the latitude at which the * projection is conformal, lambda. * * 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 CEA * prj->code "CEA" * prj->x0 Fiducial offset in x. * prj->y0 Fiducial offset in y. * prj->w[0] r0*(pi/180) * prj->w[1] (180/pi)/r0 * prj->w[2] r0/lambda * prj->w[3] lambda/r0 * prj->prjx2s Pointer to ceax2s(). * prj->prjs2x Pointer to ceas2x(). *===========================================================================*/ int ceaset(struct prjprm *prj) { if (prj == 0x0) return PRJERR_NULL_POINTER; if (prj->flag == -CEA) return 0; strcpy(prj->code, "CEA"); if (undefined(prj->pv[1])) prj->pv[1] = 1.0; strcpy(prj->name, "cylindrical equal area"); prj->category = CYLINDRICAL; prj->pvrange = 101; 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; if (prj->pv[1] <= 0.0 || prj->pv[1] > 1.0) { return PRJERR_BAD_PARAM_SET("ceaset"); } prj->w[2] = prj->r0/prj->pv[1]; prj->w[3] = prj->pv[1]/prj->r0; } else { prj->w[0] = prj->r0*D2R; prj->w[1] = R2D/prj->r0; if (prj->pv[1] <= 0.0 || prj->pv[1] > 1.0) { return PRJERR_BAD_PARAM_SET("ceaset"); } prj->w[2] = prj->r0/prj->pv[1]; prj->w[3] = prj->pv[1]/prj->r0; } prj->prjx2s = ceax2s; prj->prjs2x = ceas2x; prj->flag = (prj->flag == 1) ? -CEA : CEA; return prjoff(prj, 0.0, 0.0); } //---------------------------------------------------------------------------- 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[]) { const double tol = 1.0e-13; // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; int status; if (abs(prj->flag) != CEA) { if ((status = ceaset(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 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. 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); int istat = 0; if (fabs(s) > 1.0) { if (fabs(s) > 1.0+tol) { s = 0.0; istat = 1; if (!status) status = PRJERR_BAD_PIX_SET("ceax2s"); } else { s = copysign(90.0, s); } } else { s = asind(s); } for (int ix = 0; ix < mx; ix++, thetap += spt, statp++) { *thetap = s; *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( 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) != CEA) { if ((status = ceaset(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; for (int iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { double xi = prj->w[0]*(*phip) - prj->x0; double *xp = x + rowoff; for (int itheta = 0; itheta < mtheta; itheta++) { *xp = xi; xp += rowlen; } } // 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 (int iphi = 0; iphi < mphi; iphi++, yp += sxy, statp++) { *yp = eta; *statp = 0; } } return 0; } /*============================================================================ * CAR: Plate carree 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 CAR * prj->code "CAR" * prj->x0 Fiducial offset in x. * prj->y0 Fiducial offset in y. * prj->w[0] r0*(pi/180) * prj->w[1] (180/pi)/r0 * prj->prjx2s Pointer to carx2s(). * prj->prjs2x Pointer to cars2x(). *===========================================================================*/ int carset(struct prjprm *prj) { if (prj == 0x0) return PRJERR_NULL_POINTER; if (prj->flag == -CAR) return 0; strcpy(prj->code, "CAR"); strcpy(prj->name, "plate caree"); prj->category = CYLINDRICAL; prj->pvrange = 0; prj->simplezen = 0; prj->equiareal = 0; 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] = 1.0/prj->w[0]; } prj->prjx2s = carx2s; prj->prjs2x = cars2x; prj->flag = (prj->flag == 1) ? -CAR : CAR; return prjoff(prj, 0.0, 0.0); } //---------------------------------------------------------------------------- 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[]) { // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; int status; if (abs(prj->flag) != CAR) { if ((status = carset(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 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. 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 (int ix = 0; ix < mx; ix++, thetap += spt, statp++) { *thetap = t; *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("carx2s"); } return status; } //---------------------------------------------------------------------------- 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. if (prj == 0x0) return PRJERR_NULL_POINTER; int status; if (abs(prj->flag) != CAR) { if ((status = carset(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; for (int iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { double xi = prj->w[0]*(*phip) - prj->x0; double *xp = x + rowoff; for (int itheta = 0; itheta < mtheta; itheta++) { *xp = xi; xp += rowlen; } } // 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 (int iphi = 0; iphi < mphi; iphi++, yp += sxy, statp++) { *yp = eta; *statp = 0; } } return 0; } /*============================================================================ * MER: Mercator's 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 MER * prj->code "MER" * prj->x0 Fiducial offset in x. * prj->y0 Fiducial offset in y. * prj->w[0] r0*(pi/180) * prj->w[1] (180/pi)/r0 * prj->prjx2s Pointer to merx2s(). * prj->prjs2x Pointer to mers2x(). *===========================================================================*/ int merset(struct prjprm *prj) { if (prj == 0x0) return PRJERR_NULL_POINTER; if (prj->flag == -MER) return 0; strcpy(prj->code, "MER"); strcpy(prj->name, "Mercator's"); prj->category = CYLINDRICAL; prj->pvrange = 0; prj->simplezen = 0; prj->equiareal = 0; prj->conformal = 1; prj->global = 0; prj->divergent = 1; 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] = 1.0/prj->w[0]; } prj->prjx2s = merx2s; prj->prjs2x = mers2x; prj->flag = (prj->flag == 1) ? -MER : MER; return prjoff(prj, 0.0, 0.0); } //---------------------------------------------------------------------------- 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[]) { // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; int status; if (abs(prj->flag) != MER) { if ((status = merset(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 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. 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 (int ix = 0; ix < mx; ix++, thetap += spt, statp++) { *thetap = t; *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("merx2s"); } return status; } //---------------------------------------------------------------------------- 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. if (prj == 0x0) return PRJERR_NULL_POINTER; int status; if (abs(prj->flag) != MER) { if ((status = merset(prj))) return status; } int mphi, mtheta; if (ntheta > 0) { mphi = nphi; mtheta = ntheta; } else { mphi = 1; mtheta = 1; ntheta = nphi; } status = 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 xi = prj->w[0]*(*phip) - prj->x0; double *xp = x + rowoff; for (int itheta = 0; itheta < mtheta; itheta++) { *xp = xi; xp += rowlen; } } // 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; if (!status) status = PRJERR_BAD_WORLD_SET("mers2x"); } else { eta = prj->r0*log(tand((*thetap+90.0)/2.0)) - prj->y0; } for (int iphi = 0; iphi < mphi; iphi++, yp += sxy, statp++) { *yp = eta; *statp = istat; } } return status; } /*============================================================================ * SFL: Sanson-Flamsteed ("global sinusoid") 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 SFL * prj->code "SFL" * prj->x0 Fiducial offset in x. * prj->y0 Fiducial offset in y. * prj->w[0] r0*(pi/180) * prj->w[1] (180/pi)/r0 * prj->prjx2s Pointer to sflx2s(). * prj->prjs2x Pointer to sfls2x(). *===========================================================================*/ int sflset(struct prjprm *prj) { if (prj == 0x0) return PRJERR_NULL_POINTER; if (prj->flag == -SFL) return 0; strcpy(prj->code, "SFL"); strcpy(prj->name, "Sanson-Flamsteed"); prj->category = PSEUDOCYLINDRICAL; 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] = 1.0/prj->w[0]; } prj->prjx2s = sflx2s; prj->prjs2x = sfls2x; prj->flag = (prj->flag == 1) ? -SFL : SFL; return prjoff(prj, 0.0, 0.0); } //---------------------------------------------------------------------------- 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[]) { // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; int status; if (abs(prj->flag) != SFL) { if ((status = sflset(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 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. 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); int istat = 0; if (s == 0.0) { istat = 1; if (!status) status = PRJERR_BAD_PIX_SET("sflx2s"); } else { s = 1.0/s; } double t = prj->w[1]*yj; for (int ix = 0; ix < mx; ix++, phip += spt, thetap += spt, statp++) { *phip *= s; *thetap = t; *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( 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) != SFL) { if ((status = sflset(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; for (int iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { double xi = prj->w[0]*(*phip); double *xp = x + rowoff; for (int itheta = 0; itheta < mtheta; itheta++) { *xp = xi; xp += 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 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; } } return 0; } /*============================================================================ * PAR: parabolic 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 PAR * prj->code "PAR" * prj->x0 Fiducial offset in x. * prj->y0 Fiducial offset in y. * prj->w[0] r0*(pi/180) * prj->w[1] (180/pi)/r0 * prj->w[2] pi*r0 * prj->w[3] 1/(pi*r0) * prj->prjx2s Pointer to parx2s(). * prj->prjs2x Pointer to pars2x(). *===========================================================================*/ int parset(struct prjprm *prj) { if (prj == 0x0) return PRJERR_NULL_POINTER; if (prj->flag == -PAR) return 0; strcpy(prj->code, "PAR"); strcpy(prj->name, "parabolic"); prj->category = PSEUDOCYLINDRICAL; 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; prj->w[2] = 180.0; prj->w[3] = 1.0/prj->w[2]; } else { prj->w[0] = prj->r0*D2R; prj->w[1] = 1.0/prj->w[0]; prj->w[2] = PI*prj->r0; prj->w[3] = 1.0/prj->w[2]; } prj->prjx2s = parx2s; prj->prjs2x = pars2x; prj->flag = (prj->flag == 1) ? -PAR : PAR; return prjoff(prj, 0.0, 0.0); } //---------------------------------------------------------------------------- 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[]) { const double tol = 1.0e-13; // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; int status; if (abs(prj->flag) != PAR) { if ((status = parset(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 xj = *xp + prj->x0; double s = prj->w[1]*xj; double t = fabs(xj) - tol; double *phip = phi + rowoff; double *thetap = theta + rowoff; for (int iy = 0; iy < my; iy++) { *phip = s; *thetap = t; phip += rowlen; thetap += 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 r = prj->w[3]*(*yp + prj->y0); int istat = 0; double s, t; if (r > 1.0 || r < -1.0) { s = 0.0; t = 0.0; istat = 1; if (!status) status = PRJERR_BAD_PIX_SET("parx2s"); } else { s = 1.0 - 4.0*r*r; if (s == 0.0) { // Deferred test. istat = -1; } else { s = 1.0/s; } t = 3.0*asind(r); } for (int ix = 0; ix < mx; ix++, phip += spt, thetap += spt, statp++) { if (istat < 0) { if (*thetap < 0.0) { *statp = 0; } else { *statp = 1; if (!status) status = PRJERR_BAD_PIX_SET("parx2s"); } } else { *statp = istat; } *phip *= s; *thetap = t; } } // 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( 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) != PAR) { if ((status = parset(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; for (int iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { double xi = prj->w[0]*(*phip); double *xp = x + rowoff; for (int itheta = 0; itheta < mtheta; itheta++) { *xp = xi; xp += 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 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; } } return 0; } /*============================================================================ * MOL: Mollweide's 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 MOL * prj->code "MOL" * prj->x0 Fiducial offset in x. * prj->y0 Fiducial offset in y. * prj->w[0] sqrt(2)*r0 * prj->w[1] sqrt(2)*r0/90 * prj->w[2] 1/(sqrt(2)*r0) * prj->w[3] 90/r0 * prj->prjx2s Pointer to molx2s(). * prj->prjs2x Pointer to mols2x(). *===========================================================================*/ int molset(struct prjprm *prj) { if (prj == 0x0) return PRJERR_NULL_POINTER; if (prj->flag == -MOL) return 0; strcpy(prj->code, "MOL"); if (prj->r0 == 0.0) prj->r0 = R2D; strcpy(prj->name, "Mollweide's"); prj->category = PSEUDOCYLINDRICAL; prj->pvrange = 0; prj->simplezen = 0; prj->equiareal = 1; prj->conformal = 0; prj->global = 1; prj->divergent = 0; prj->w[0] = SQRT2*prj->r0; prj->w[1] = prj->w[0]/90.0; prj->w[2] = 1.0/prj->w[0]; prj->w[3] = 90.0/prj->r0; prj->w[4] = 2.0/PI; prj->prjx2s = molx2s; prj->prjs2x = mols2x; prj->flag = (prj->flag == 1) ? -MOL : MOL; return prjoff(prj, 0.0, 0.0); } //---------------------------------------------------------------------------- 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[]) { const double tol = 1.0e-12; // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; int status; if (abs(prj->flag) != MOL) { if ((status = molset(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 xj = *xp + prj->x0; double s = prj->w[3]*xj; double t = fabs(xj) - tol; double *phip = phi + rowoff; double *thetap = theta + rowoff; for (int iy = 0; iy < my; iy++) { *phip = s; *thetap = t; phip += rowlen; thetap += 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 yj = *yp + prj->y0; double y0 = yj/prj->r0; double r = 2.0 - y0*y0; 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. istat = -1; } r = 0.0; s = 0.0; } else { r = sqrt(r); s = 1.0/r; } double z = yj*prj->w[2]; if (fabs(z) > 1.0) { if (fabs(z) > 1.0+tol) { z = 0.0; istat = 1; if (!status) status = PRJERR_BAD_PIX_SET("molx2s"); } else { z = copysign(1.0, z) + y0*r/PI; } } else { z = asin(z)*prj->w[4] + y0*r/PI; } if (fabs(z) > 1.0) { if (fabs(z) > 1.0+tol) { z = 0.0; istat = 1; if (!status) status = PRJERR_BAD_PIX_SET("molx2s"); } else { z = copysign(1.0, z); } } double t = asind(z); for (int ix = 0; ix < mx; ix++, phip += spt, thetap += spt, statp++) { if (istat < 0) { if (*thetap < 0.0) { *statp = 0; } else { *statp = 1; if (!status) status = PRJERR_BAD_PIX_SET("molx2s"); } } else { *statp = istat; } *phip *= s; *thetap = t; } } // 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( struct prjprm *prj, int nphi, int ntheta, int spt, int sxy, const double phi[], const double theta[], double x[], double y[], int stat[]) { const double tol = 1.0e-13; // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; int status; if (abs(prj->flag) != MOL) { if ((status = molset(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; for (int iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { double xi = prj->w[1]*(*phip); double *xp = x + rowoff; for (int itheta = 0; itheta < mtheta; itheta++) { *xp = xi; xp += 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 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 { 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; } else { if (resid < tol) break; v1 = v; } v = (v0 + v1)/2.0; } double gamma = v/2.0; xi = cos(gamma); eta = prj->w[0]*sin(gamma); } eta -= prj->y0; for (int iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy, statp++) { *xp = xi*(*xp) - prj->x0; *yp = eta; *statp = 0; } } return 0; } /*============================================================================ * AIT: Hammer-Aitoff 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 AIT * prj->code "AIT" * prj->x0 Fiducial offset in x. * prj->y0 Fiducial offset in y. * prj->w[0] 2*r0**2 * prj->w[1] 1/(2*r0)**2 * prj->w[2] 1/(4*r0)**2 * prj->w[3] 1/(2*r0) * prj->prjx2s Pointer to aitx2s(). * prj->prjs2x Pointer to aits2x(). *===========================================================================*/ int aitset(struct prjprm *prj) { if (prj == 0x0) return PRJERR_NULL_POINTER; if (prj->flag == -AIT) return 0; strcpy(prj->code, "AIT"); if (prj->r0 == 0.0) prj->r0 = R2D; strcpy(prj->name, "Hammer-Aitoff"); prj->category = CONVENTIONAL; prj->pvrange = 0; prj->simplezen = 0; prj->equiareal = 1; prj->conformal = 0; prj->global = 1; prj->divergent = 0; prj->w[0] = 2.0*prj->r0*prj->r0; prj->w[1] = 1.0/(2.0*prj->w[0]); prj->w[2] = prj->w[1]/4.0; prj->w[3] = 1.0/(2.0*prj->r0); prj->prjx2s = aitx2s; prj->prjs2x = aits2x; prj->flag = (prj->flag == 1) ? -AIT : AIT; return prjoff(prj, 0.0, 0.0); } //---------------------------------------------------------------------------- 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[]) { const double tol = 1.0e-13; // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; int status; if (abs(prj->flag) != AIT) { if ((status = aitset(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 xj = *xp + prj->x0; double s = 1.0 - xj*xj*prj->w[2]; double t = xj*prj->w[3]; double *phip = phi + rowoff; double *thetap = theta + rowoff; for (int iy = 0; iy < my; iy++) { *phip = s; *thetap = t; phip += rowlen; thetap += 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 yj = *yp + prj->y0; double yj2 = yj*yj*prj->w[1]; for (int ix = 0; ix < mx; ix++, phip += spt, thetap += spt, statp++) { double s = *phip - yj2; int istat = 0; if (s < 0.5) { if (s < 0.5-tol) { istat = 1; if (!status) status = PRJERR_BAD_PIX_SET("aitx2s"); } s = 0.5; } 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); } double t = z*yj/prj->r0; if (fabs(t) > 1.0) { if (fabs(t) > 1.0+tol) { istat = 1; if (!status) status = PRJERR_BAD_PIX_SET("aitx2s"); } t = copysign(90.0, t); } else { t = asind(t); } *thetap = t; *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( 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) != AIT) { if ((status = aitset(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; for (int iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { double w = (*phip)/2.0; double sinphi, cosphi; sincosd(w, &sinphi, &cosphi); double *xp = x + rowoff; double *yp = y + rowoff; for (int itheta = 0; itheta < mtheta; itheta++) { *xp = sinphi; *yp = cosphi; 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, costhe; sincosd(*thetap, &sinthe, &costhe); 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; } } return 0; } /*============================================================================ * COP: conic perspective projection. * * Given: * prj->pv[1] sigma = (theta2+theta1)/2 * prj->pv[2] delta = (theta2-theta1)/2, where theta1 and theta2 are the * latitudes of the standard parallels, in degrees. * * Given and/or returned: * prj->r0 Reset to 180/pi if 0. * prj->phi0 Reset to sigma if undefined. * prj->theta0 Reset to sigma if undefined. * * Returned: * prj->flag COP * prj->code "COP" * prj->x0 Fiducial offset in x. * prj->y0 Fiducial offset in y. * prj->w[0] C = sin(sigma) * prj->w[1] 1/C * prj->w[2] Y0 = r0*cos(delta)*cot(sigma) * prj->w[3] r0*cos(delta) * prj->w[4] 1/(r0*cos(delta) * prj->w[5] cot(sigma) * prj->prjx2s Pointer to copx2s(). * prj->prjs2x Pointer to cops2x(). *===========================================================================*/ int copset(struct prjprm *prj) { if (prj == 0x0) return PRJERR_NULL_POINTER; if (prj->flag == -COP) return 0; strcpy(prj->code, "COP"); if (undefined(prj->pv[1])) { return PRJERR_BAD_PARAM_SET("copset"); } 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; prj->equiareal = 0; prj->conformal = 0; prj->global = 0; prj->divergent = 1; prj->w[0] = sind(prj->pv[1]); if (prj->w[0] == 0.0) { return PRJERR_BAD_PARAM_SET("copset"); } prj->w[1] = 1.0/prj->w[0]; prj->w[3] = prj->r0*cosd(prj->pv[2]); if (prj->w[3] == 0.0) { return PRJERR_BAD_PARAM_SET("copset"); } prj->w[4] = 1.0/prj->w[3]; prj->w[5] = 1.0/tand(prj->pv[1]); prj->w[2] = prj->w[3]*prj->w[5]; prj->prjx2s = copx2s; prj->prjs2x = cops2x; prj->flag = (prj->flag == 1) ? -COP : COP; return prjoff(prj, 0.0, prj->pv[1]); } //---------------------------------------------------------------------------- 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[]) { // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; int status; if (abs(prj->flag) != COP) { if ((status = copset(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 xj = *xp + prj->x0; double *phip = phi + rowoff; for (int iy = 0; iy < my; iy++) { *phip = xj; 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 dy = prj->w[2] - (*yp + prj->y0); double dy2 = dy*dy; for (int ix = 0; ix < mx; ix++, phip += spt, thetap += spt, statp++) { double xj = *phip; 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); } *phip = alpha*prj->w[1]; *thetap = prj->pv[1] + atand(prj->w[5] - r*prj->w[4]); *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("copx2s"); } return status; } //---------------------------------------------------------------------------- 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[]) { // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; int status; if (abs(prj->flag) != COP) { if ((status = copset(prj))) return status; } int mphi, mtheta; if (ntheta > 0) { mphi = nphi; mtheta = ntheta; } else { mphi = 1; mtheta = 1; ntheta = nphi; } status = 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 alpha = prj->w[0]*(*phip); double sinalpha, cosalpha; sincosd(alpha, &sinalpha, &cosalpha); double *xp = x + rowoff; double *yp = y + rowoff; for (int itheta = 0; itheta < mtheta; itheta++) { *xp = sinalpha; *yp = cosalpha; xp += rowlen; yp += rowlen; } } // 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; // Bounds checking. if (prj->bounds&1) { if (r*prj->w[0] < 0.0) { istat = 1; if (!status) status = PRJERR_BAD_WORLD_SET("cops2x"); } } } for (int iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy, statp++) { *xp = r*(*xp) - prj->x0; *yp = -r*(*yp) - y0; *statp = istat; } } return status; } /*============================================================================ * COE: conic equal area projection. * * Given: * prj->pv[1] sigma = (theta2+theta1)/2 * prj->pv[2] delta = (theta2-theta1)/2, where theta1 and theta2 are the * latitudes of the standard parallels, in degrees. * * Given and/or returned: * prj->r0 Reset to 180/pi if 0. * prj->phi0 Reset to sigma if undefined. * prj->theta0 Reset to sigma if undefined. * * Returned: * prj->flag COE * prj->code "COE" * prj->x0 Fiducial offset in x. * prj->y0 Fiducial offset in y. * prj->w[0] C = (sin(theta1) + sin(theta2))/2 * prj->w[1] 1/C * prj->w[2] Y0 = chi*sqrt(psi - 2C*sind(sigma)) * prj->w[3] chi = r0/C * prj->w[4] psi = 1 + sin(theta1)*sin(theta2) * prj->w[5] 2C * prj->w[6] (1 + sin(theta1)*sin(theta2))*(r0/C)**2 * prj->w[7] C/(2*r0**2) * prj->w[8] chi*sqrt(psi + 2C) * prj->prjx2s Pointer to coex2s(). * prj->prjs2x Pointer to coes2x(). *===========================================================================*/ int coeset(struct prjprm *prj) { if (prj == 0x0) return PRJERR_NULL_POINTER; if (prj->flag == -COE) return 0; strcpy(prj->code, "COE"); if (undefined(prj->pv[1])) { return PRJERR_BAD_PARAM_SET("coeset"); } 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; prj->equiareal = 1; prj->conformal = 0; prj->global = 1; prj->divergent = 0; 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) { return PRJERR_BAD_PARAM_SET("coeset"); } prj->w[1] = 1.0/prj->w[0]; prj->w[3] = prj->r0/prj->w[0]; prj->w[4] = 1.0 + sind(theta1)*sind(theta2); prj->w[5] = 2.0*prj->w[0]; prj->w[6] = prj->w[3]*prj->w[3]*prj->w[4]; prj->w[7] = 1.0/(2.0*prj->r0*prj->w[3]); prj->w[8] = prj->w[3]*sqrt(prj->w[4] + prj->w[5]); prj->w[2] = prj->w[3]*sqrt(prj->w[4] - prj->w[5]*sind(prj->pv[1])); prj->prjx2s = coex2s; prj->prjs2x = coes2x; prj->flag = (prj->flag == 1) ? -COE : COE; return prjoff(prj, 0.0, prj->pv[1]); } //---------------------------------------------------------------------------- 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[]) { const double tol = 1.0e-12; // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; int status; if (abs(prj->flag) != COE) { if ((status = coeset(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 xj = *xp + prj->x0; double *phip = phi + rowoff; for (int iy = 0; iy < my; iy++) { *phip = xj; 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 dy = prj->w[2] - (*yp + prj->y0); double dy2 = dy*dy; for (int ix = 0; ix < mx; ix++, phip += spt, thetap += spt, statp++) { double xj = *phip; 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); } int istat = 0; double t; if (fabs(r - prj->w[8]) < tol) { t = -90.0; } else { double w = (prj->w[6] - r*r)*prj->w[7]; if (fabs(w) > 1.0) { if (fabs(w-1.0) < tol) { t = 90.0; } else if (fabs(w+1.0) < tol) { t = -90.0; } else { t = 0.0; istat = 1; if (!status) status = PRJERR_BAD_PIX_SET("coex2s"); } } else { t = asind(w); } } *phip = alpha*prj->w[1]; *thetap = t; *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( 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) != COE) { if ((status = coeset(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; for (int iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { double alpha = prj->w[0]*(*phip); double sinalpha, cosalpha; sincosd(alpha, &sinalpha, &cosalpha); double *xp = x + rowoff; double *yp = y + rowoff; for (int itheta = 0; itheta < mtheta; itheta++) { *xp = sinalpha; *yp = cosalpha; xp += rowlen; yp += rowlen; } } // 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 (int iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy, statp++) { *xp = r*(*xp) - prj->x0; *yp = -r*(*yp) - y0; *statp = 0; } } return 0; } /*============================================================================ * COD: conic equidistant projection. * * Given: * prj->pv[1] sigma = (theta2+theta1)/2 * prj->pv[2] delta = (theta2-theta1)/2, where theta1 and theta2 are the * latitudes of the standard parallels, in degrees. * * Given and/or returned: * prj->r0 Reset to 180/pi if 0. * prj->phi0 Reset to sigma if undefined. * prj->theta0 Reset to sigma if undefined. * * Returned: * prj->flag COD * prj->code "COD" * prj->x0 Fiducial offset in x. * prj->y0 Fiducial offset in y. * prj->w[0] C = r0*sin(sigma)*sin(delta)/delta * prj->w[1] 1/C * prj->w[2] Y0 = delta*cot(delta)*cot(sigma) * prj->w[3] Y0 + sigma * prj->prjx2s Pointer to codx2s(). * prj->prjs2x Pointer to cods2x(). *===========================================================================*/ int codset(struct prjprm *prj) { if (prj == 0x0) return PRJERR_NULL_POINTER; if (prj->flag == -COD) return 0; strcpy(prj->code, "COD"); if (undefined(prj->pv[1])) { return PRJERR_BAD_PARAM_SET("codset"); } 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; prj->equiareal = 0; prj->conformal = 0; prj->global = 1; prj->divergent = 0; if (prj->pv[2] == 0.0) { prj->w[0] = prj->r0*sind(prj->pv[1])*D2R; } else { prj->w[0] = prj->r0*sind(prj->pv[1])*sind(prj->pv[2])/prj->pv[2]; } if (prj->w[0] == 0.0) { return PRJERR_BAD_PARAM_SET("codset"); } prj->w[1] = 1.0/prj->w[0]; prj->w[2] = prj->r0*cosd(prj->pv[2])*cosd(prj->pv[1])/prj->w[0]; prj->w[3] = prj->w[2] + prj->pv[1]; prj->prjx2s = codx2s; prj->prjs2x = cods2x; prj->flag = (prj->flag == 1) ? -COD : COD; return prjoff(prj, 0.0, prj->pv[1]); } //---------------------------------------------------------------------------- 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[]) { // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; int status; if (abs(prj->flag) != COD) { if ((status = codset(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 xj = *xp + prj->x0; double *phip = phi + rowoff; for (int iy = 0; iy < my; iy++) { *phip = xj; 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 dy = prj->w[2] - (*yp + prj->y0); double dy2 = dy*dy; for (int ix = 0; ix < mx; ix++, phip += spt, thetap += spt, statp++) { double xj = *phip; 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); } *phip = alpha*prj->w[1]; *thetap = prj->w[3] - r; *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("codx2s"); } return status; } //---------------------------------------------------------------------------- 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[]) { // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; int status; if (abs(prj->flag) != COD) { if ((status = codset(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; for (int iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { double alpha = prj->w[0]*(*phip); double sinalpha, cosalpha; sincosd(alpha, &sinalpha, &cosalpha); double *xp = x + rowoff; double *yp = y + rowoff; for (int itheta = 0; itheta < mtheta; itheta++) { *xp = sinalpha; *yp = cosalpha; xp += rowlen; yp += rowlen; } } // 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; } } return 0; } /*============================================================================ * COO: conic orthomorphic projection. * * Given: * prj->pv[1] sigma = (theta2+theta1)/2 * prj->pv[2] delta = (theta2-theta1)/2, where theta1 and theta2 are the * latitudes of the standard parallels, in degrees. * * Given and/or returned: * prj->r0 Reset to 180/pi if 0. * prj->phi0 Reset to sigma if undefined. * prj->theta0 Reset to sigma if undefined. * * Returned: * prj->flag COO * prj->code "COO" * prj->x0 Fiducial offset in x. * prj->y0 Fiducial offset in y. * prj->w[0] C = ln(cos(theta2)/cos(theta1))/ln(tan(tau2)/tan(tau1)) * where tau1 = (90 - theta1)/2 * tau2 = (90 - theta2)/2 * prj->w[1] 1/C * prj->w[2] Y0 = psi*tan((90-sigma)/2)**C * prj->w[3] psi = (r0*cos(theta1)/C)/tan(tau1)**C * prj->w[4] 1/psi * prj->prjx2s Pointer to coox2s(). * prj->prjs2x Pointer to coos2x(). *===========================================================================*/ int cooset(struct prjprm *prj) { if (prj == 0x0) return PRJERR_NULL_POINTER; if (prj->flag == -COO) return 0; strcpy(prj->code, "COO"); if (undefined(prj->pv[1])) { return PRJERR_BAD_PARAM_SET("cooset"); } 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; prj->equiareal = 0; prj->conformal = 1; prj->global = 0; prj->divergent = 1; double theta1 = prj->pv[1] - prj->pv[2]; double theta2 = prj->pv[1] + prj->pv[2]; double tan1 = tand((90.0 - theta1)/2.0); double cos1 = cosd(theta1); if (theta1 == theta2) { prj->w[0] = sind(theta1); } else { 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) { return PRJERR_BAD_PARAM_SET("cooset"); } prj->w[1] = 1.0/prj->w[0]; prj->w[3] = prj->r0*(cos1/prj->w[0])/pow(tan1,prj->w[0]); if (prj->w[3] == 0.0) { return PRJERR_BAD_PARAM_SET("cooset"); } prj->w[2] = prj->w[3]*pow(tand((90.0 - prj->pv[1])/2.0),prj->w[0]); prj->w[4] = 1.0/prj->w[3]; prj->prjx2s = coox2s; prj->prjs2x = coos2x; prj->flag = (prj->flag == 1) ? -COO : COO; return prjoff(prj, 0.0, prj->pv[1]); } //---------------------------------------------------------------------------- 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[]) { // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; int status; if (abs(prj->flag) != COO) { if ((status = cooset(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 xj = *xp + prj->x0; double *phip = phi + rowoff; for (int iy = 0; iy < my; iy++) { *phip = xj; 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 dy = prj->w[2] - (*yp + prj->y0); double dy2 = dy*dy; for (int ix = 0; ix < mx; ix++, phip += spt, thetap += spt, statp++) { double xj = *phip; 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); } int istat = 0; double t; if (r == 0.0) { if (prj->w[0] < 0.0) { t = -90.0; } else { t = 0.0; istat = 1; if (!status) status = PRJERR_BAD_PIX_SET("coox2s"); } } else { t = 90.0 - 2.0*atand(pow(r*prj->w[4],prj->w[1])); } *phip = alpha*prj->w[1]; *thetap = t; *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( 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) != COO) { if ((status = cooset(prj))) return status; } int mphi, mtheta; if (ntheta > 0) { mphi = nphi; mtheta = ntheta; } else { mphi = 1; mtheta = 1; ntheta = nphi; } status = 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 alpha = prj->w[0]*(*phip); double sinalpha, cosalpha; sincosd(alpha, &sinalpha, &cosalpha); double *xp = x + rowoff; double *yp = y + rowoff; for (int itheta = 0; itheta < mtheta; itheta++) { *xp = sinalpha; *yp = cosalpha; xp += rowlen; yp += rowlen; } } // 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) { istat = 1; if (!status) status = PRJERR_BAD_WORLD_SET("coos2x"); } } else { r = prj->w[3]*pow(tand((90.0 - *thetap)/2.0),prj->w[0]); } for (int iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy, statp++) { *xp = r*(*xp) - prj->x0; *yp = -r*(*yp) - y0; *statp = istat; } } return status; } /*============================================================================ * BON: Bonne's projection. * * Given: * prj->pv[1] Bonne conformal latitude, theta1, in degrees. * * 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 BON * prj->code "BON" * prj->x0 Fiducial offset in x. * prj->y0 Fiducial offset in y. * prj->w[1] r0*pi/180 * prj->w[2] Y0 = r0*(cot(theta1) + theta1*pi/180) * prj->prjx2s Pointer to bonx2s(). * prj->prjs2x Pointer to bons2x(). *===========================================================================*/ int bonset(struct prjprm *prj) { if (prj == 0x0) return PRJERR_NULL_POINTER; if (prj->flag == -BON) return 0; strcpy(prj->code, "BON"); if (undefined(prj->pv[1])) { return PRJERR_BAD_PARAM_SET("bonset"); } if (prj->pv[1] == 0.0) { // Sanson-Flamsteed. return sflset(prj); } strcpy(prj->name, "Bonne's"); prj->category = POLYCONIC; prj->pvrange = 101; 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[1] = 1.0; prj->w[2] = prj->r0*cosd(prj->pv[1])/sind(prj->pv[1]) + prj->pv[1]; } else { prj->w[1] = prj->r0*D2R; prj->w[2] = prj->r0*(cosd(prj->pv[1])/sind(prj->pv[1]) + prj->pv[1]*D2R); } prj->prjx2s = bonx2s; prj->prjs2x = bons2x; prj->flag = (prj->flag == 1) ? -BON : BON; return prjoff(prj, 0.0, 0.0); } //---------------------------------------------------------------------------- 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[]) { // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; if (prj->pv[1] == 0.0) { // Sanson-Flamsteed. return sflx2s(prj, nx, ny, sxy, spt, x, y, phi, theta, stat); } int status; if (abs(prj->flag) != BON) { if ((status = bonset(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 xj = *xp + prj->x0; double *phip = phi + rowoff; for (int iy = 0; iy < my; iy++) { *phip = xj; 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 dy = prj->w[2] - (*yp + prj->y0); double dy2 = dy*dy; for (int ix = 0; ix < mx; ix++, phip += spt, thetap += spt, statp++) { double xj = *phip; 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); } double s; double t = (prj->w[2] - r)/prj->w[1]; double costhe = cosd(t); if (costhe == 0.0) { s = 0.0; } else { s = alpha*(r/prj->r0)/costhe; } *phip = s; *thetap = t; *statp = 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"); } return status; } //---------------------------------------------------------------------------- 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. if (prj == 0x0) return PRJERR_NULL_POINTER; if (prj->pv[1] == 0.0) { // Sanson-Flamsteed. return sfls2x(prj, nphi, ntheta, spt, sxy, phi, theta, x, y, stat); } int status; if (abs(prj->flag) != BON) { if ((status = bonset(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; for (int iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { double s = prj->r0*(*phip); double *xp = x + rowoff; for (int itheta = 0; itheta < mtheta; itheta++) { *xp = s; xp += rowlen; } } // 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; } } return 0; } /*============================================================================ * PCO: polyconic 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 PCO * prj->code "PCO" * prj->x0 Fiducial offset in x. * prj->y0 Fiducial offset in y. * prj->w[0] r0*(pi/180) * 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(struct prjprm *prj) { if (prj == 0x0) return PRJERR_NULL_POINTER; if (prj->flag == -PCO) return 0; strcpy(prj->code, "PCO"); strcpy(prj->name, "polyconic"); prj->category = POLYCONIC; prj->pvrange = 0; prj->simplezen = 0; prj->equiareal = 0; 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; prj->w[2] = 360.0/PI; } else { prj->w[0] = prj->r0*D2R; 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( 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) != PCO) { if ((status = pcoset(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 xj = *xp + prj->x0; double *phip = phi + rowoff; for (int iy = 0; iy < my; iy++) { *phip = xj; 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 yj = *yp + prj->y0; double w = fabs(yj*prj->w[1]); for (int ix = 0; ix < mx; ix++, phip += spt, thetap += spt, statp++) { double xj = *phip; if (w < tol) { *phip = xj*prj->w[1]; *thetap = 0.0; } else if (fabs(w-90.0) < tol) { *phip = 0.0; *thetap = copysign(90.0, yj); } else { 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); } 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); double f = xx + ymthe*(ymthe - prj->w[2]/tanthe); // 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; } } } double x1 = prj->r0 - ymthe*tanthe; double y1 = xj*tanthe; if (x1 == 0.0 && y1 == 0.0) { *phip = 0.0; } else { *phip = atan2d(y1, x1)/sind(the); } *thetap = the; } *statp = 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"); } return status; } //---------------------------------------------------------------------------- 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. if (prj == 0x0) return PRJERR_NULL_POINTER; int status; if (abs(prj->flag) != PCO) { if ((status = pcoset(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; 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. 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; *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; } } 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; } } } return 0; } /*============================================================================ * TSC: tangential spherical cube 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 TSC * prj->code "TSC" * prj->x0 Fiducial offset in x. * prj->y0 Fiducial offset in y. * prj->w[0] r0*(pi/4) * prj->w[1] (4/pi)/r0 * prj->prjx2s Pointer to tscx2s(). * prj->prjs2x Pointer to tscs2x(). *===========================================================================*/ int tscset(struct prjprm *prj) { if (prj == 0x0) return PRJERR_NULL_POINTER; if (prj->flag == -TSC) return 0; strcpy(prj->code, "TSC"); strcpy(prj->name, "tangential spherical cube"); prj->category = QUADCUBE; prj->pvrange = 0; prj->simplezen = 0; prj->equiareal = 0; prj->conformal = 0; prj->global = 1; prj->divergent = 0; if (prj->r0 == 0.0) { prj->r0 = R2D; prj->w[0] = 45.0; prj->w[1] = 1.0/45.0; } else { prj->w[0] = prj->r0*PI/4.0; prj->w[1] = 1.0/prj->w[0]; } prj->prjx2s = tscx2s; prj->prjs2x = tscs2x; prj->flag = (prj->flag == 1) ? -TSC : TSC; return prjoff(prj, 0.0, 0.0); } //---------------------------------------------------------------------------- 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[]) { // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; int status; if (abs(prj->flag) != TSC) { if ((status = tscset(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 xf = (*xp + prj->x0)*prj->w[1]; double *phip = phi + rowoff; for (int iy = 0; iy < my; iy++) { *phip = xf; 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 yf = (*yp + prj->y0)*prj->w[1]; for (int ix = 0; ix < mx; ix++, phip += spt, thetap += spt, statp++) { double xf = *phip; // Bounds checking. if (fabs(xf) <= 1.0) { if (fabs(yf) > 3.0) { *phip = 0.0; *thetap = 0.0; *statp = 1; if (!status) status = PRJERR_BAD_PIX_SET("tscx2s"); continue; } } else { if (fabs(xf) > 7.0 || fabs(yf) > 1.0) { *phip = 0.0; *thetap = 0.0; *statp = 1; if (!status) status = PRJERR_BAD_PIX_SET("tscx2s"); continue; } } // Map negative faces to the other side. if (xf < -1.0) xf += 8.0; // Determine the face. double l, m, n; if (xf > 5.0) { // 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 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 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 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 yf = yf + 2.0; n = -1.0/sqrt(1.0 + xf*xf + yf*yf); l = -n*yf; m = -n*xf; } else { // face = 1 l = 1.0/sqrt(1.0 + xf*xf + yf*yf); m = l*xf; n = l*yf; } if (l == 0.0 && m == 0.0) { *phip = 0.0; } else { *phip = atan2d(m, l); } *thetap = asind(n); *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( struct prjprm *prj, int nphi, int ntheta, int spt, int sxy, const double phi[], const double theta[], double x[], double y[], int stat[]) { const double tol = 1.0e-12; // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; int status; if (abs(prj->flag) != TSC) { if ((status = tscset(prj))) return status; } int mphi, mtheta; if (ntheta > 0) { mphi = nphi; mtheta = ntheta; } else { mphi = 1; mtheta = 1; ntheta = nphi; } status = 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 sinphi, cosphi; sincosd(*phip, &sinphi, &cosphi); double *xp = x + rowoff; double *yp = y + rowoff; for (int itheta = 0; itheta < mtheta; itheta++) { *xp = cosphi; *yp = sinphi; 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, costhe; sincosd(*thetap, &sinthe, &costhe); for (int iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy, statp++) { double l = costhe*(*xp); double m = costhe*(*yp); double n = sinthe; int face = 0; double zeta = n; if (l > zeta) { face = 1; zeta = l; } if (m > zeta) { face = 2; zeta = m; } if (-l > zeta) { face = 3; zeta = -l; } if (-m > zeta) { face = 4; zeta = -m; } if (-n > zeta) { face = 5; zeta = -n; } double xf, yf, x0, y0; switch (face) { case 1: xf = m/zeta; yf = n/zeta; x0 = 0.0; y0 = 0.0; break; case 2: xf = -l/zeta; yf = n/zeta; x0 = 2.0; y0 = 0.0; break; case 3: xf = -m/zeta; yf = n/zeta; x0 = 4.0; y0 = 0.0; break; case 4: xf = l/zeta; yf = n/zeta; x0 = 6.0; y0 = 0.0; break; case 5: xf = m/zeta; yf = l/zeta; x0 = 0.0; y0 = -2.0; break; default: // face == 0 xf = m/zeta; yf = -l/zeta; x0 = 0.0; y0 = 2.0; break; } int istat = 0; if (fabs(xf) > 1.0) { if (fabs(xf) > 1.0+tol) { istat = 1; if (!status) status = PRJERR_BAD_WORLD_SET("tscs2x"); } xf = copysign(1.0, xf); } if (fabs(yf) > 1.0) { if (fabs(yf) > 1.0+tol) { istat = 1; if (!status) status = PRJERR_BAD_WORLD_SET("tscs2x"); } yf = copysign(1.0, yf); } *xp = prj->w[0]*(xf + x0) - prj->x0; *yp = prj->w[0]*(yf + y0) - prj->y0; *statp = istat; } } return status; } /*============================================================================ * CSC: COBE quadrilateralized spherical cube 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 CSC * prj->code "CSC" * prj->x0 Fiducial offset in x. * prj->y0 Fiducial offset in y. * prj->w[0] r0*(pi/4) * prj->w[1] (4/pi)/r0 * prj->prjx2s Pointer to cscx2s(). * prj->prjs2x Pointer to cscs2x(). *===========================================================================*/ int cscset(struct prjprm *prj) { if (prj == 0x0) return PRJERR_NULL_POINTER; if (prj->flag == -CSC) return 0; strcpy(prj->code, "CSC"); strcpy(prj->name, "COBE quadrilateralized spherical cube"); prj->category = QUADCUBE; prj->pvrange = 0; prj->simplezen = 0; prj->equiareal = 0; prj->conformal = 0; prj->global = 1; prj->divergent = 0; if (prj->r0 == 0.0) { prj->r0 = R2D; prj->w[0] = 45.0; prj->w[1] = 1.0/45.0; } else { prj->w[0] = prj->r0*PI/4.0; prj->w[1] = 1.0/prj->w[0]; } prj->prjx2s = cscx2s; prj->prjs2x = cscs2x; prj->flag = (prj->flag == 1) ? -CSC : CSC; return prjoff(prj, 0.0, 0.0); } //---------------------------------------------------------------------------- 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[]) { const float p00 = -0.27292696f; const float p10 = -0.07629969f; const float p20 = -0.22797056f; const float p30 = 0.54852384f; const float p40 = -0.62930065f; const float p50 = 0.25795794f; const float p60 = 0.02584375f; const float p01 = -0.02819452f; const float p11 = -0.01471565f; const float p21 = 0.48051509f; const float p31 = -1.74114454f; const float p41 = 1.71547508f; const float p51 = -0.53022337f; const float p02 = 0.27058160f; const float p12 = -0.56800938f; const float p22 = 0.30803317f; const float p32 = 0.98938102f; const float p42 = -0.83180469f; const float p03 = -0.60441560f; const float p13 = 1.50880086f; const float p23 = -0.93678576f; const float p33 = 0.08693841f; const float p04 = 0.93412077f; const float p14 = -1.41601920f; const float p24 = 0.33887446f; const float p05 = -0.63915306f; const float p15 = 0.52032238f; const float p06 = 0.14381585f; // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; int status; if (abs(prj->flag) != CSC) { if ((status = cscset(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) { float xf = (float)((*xp + prj->x0)*prj->w[1]); double *phip = phi + rowoff; for (int iy = 0; iy < my; iy++) { *phip = xf; 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) { float yf = (float)((*yp + prj->y0)*prj->w[1]); for (int ix = 0; ix < mx; ix++, phip += spt, thetap += spt, statp++) { float xf = (float)(*phip); // Bounds checking. if (fabs((double)xf) <= 1.0) { if (fabs((double)yf) > 3.0) { *phip = 0.0; *thetap = 0.0; *statp = 1; if (!status) status = PRJERR_BAD_PIX_SET("cscx2s"); continue; } } else { if (fabs((double)xf) > 7.0 || fabs((double)yf) > 1.0) { *phip = 0.0; *thetap = 0.0; *statp = 1; if (!status) status = PRJERR_BAD_PIX_SET("cscx2s"); continue; } } // Map negative faces to the other side. if (xf < -1.0f) xf += 8.0f; // Determine the face. int face = 0; if (xf > 5.0f) { face = 4; xf = xf - 6.0f; } else if (xf > 3.0f) { face = 3; xf = xf - 4.0f; } else if (xf > 1.0f) { face = 2; xf = xf - 2.0f; } else if (yf > 1.0f) { face = 0; yf = yf - 2.0f; } else if (yf < -1.0f) { face = 5; yf = yf + 2.0f; } else { face = 1; } 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))))); z2 = p02 + xx*(p12 + xx*(p22 + xx*(p32 + xx*(p42)))); z3 = p03 + xx*(p13 + xx*(p23 + xx*(p33))); z4 = p04 + xx*(p14 + xx*(p24)); 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; z0 = p00 + yy*(p10 + yy*(p20 + yy*(p30 + yy*(p40 + yy*(p50 + yy*(p60)))))); z1 = p01 + yy*(p11 + yy*(p21 + yy*(p31 + yy*(p41 + yy*(p51))))); z2 = p02 + yy*(p12 + yy*(p22 + yy*(p32 + yy*(p42)))); z3 = p03 + yy*(p13 + yy*(p23 + yy*(p33))); z4 = p04 + yy*(p14 + yy*(p24)); 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; double l, m, n; double t = 1.0/sqrt((double)(chi*chi + psi*psi) + 1.0); switch (face) { case 1: l = t; m = chi*l; n = psi*l; break; case 2: m = t; l = -chi*m; n = psi*m; break; case 3: l = -t; m = chi*l; n = -psi*l; break; case 4: m = -t; l = -chi*m; n = -psi*m; break; case 5: n = -t; l = -psi*n; m = -chi*n; break; default: // face == 0 n = t; l = -psi*n; m = chi*n; break; } if (l == 0.0 && m == 0.0) { *phip = 0.0; } else { *phip = atan2d(m, l); } *thetap = asind(n); *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( struct prjprm *prj, int nphi, int ntheta, int spt, int sxy, const double phi[], const double theta[], double x[], double y[], int stat[]) { const double tol = 1.0e-7; const float gstar = 1.37484847732f; const float mm = 0.004869491981f; const float gamma = -0.13161671474f; const float omega1 = -0.159596235474f; const float d0 = 0.0759196200467f; const float d1 = -0.0217762490699f; const float c00 = 0.141189631152f; const float c10 = 0.0809701286525f; const float c01 = -0.281528535557f; const float c11 = 0.15384112876f; const float c20 = -0.178251207466f; const float c02 = 0.106959469314f; // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; int status; if (abs(prj->flag) != CSC) { if ((status = cscset(prj))) return status; } int mphi, mtheta; if (ntheta > 0) { mphi = nphi; mtheta = ntheta; } else { mphi = 1; mtheta = 1; ntheta = nphi; } status = 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 sinphi, cosphi; sincosd(*phip, &sinphi, &cosphi); double *xp = x + rowoff; double *yp = y + rowoff; for (int itheta = 0; itheta < mtheta; itheta++) { *xp = cosphi; *yp = sinphi; 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, costhe; sincosd(*thetap, &sinthe, &costhe); for (int iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy, statp++) { double l = costhe*(*xp); double m = costhe*(*yp); double n = sinthe; int face = 0; double zeta = n; if (l > zeta) { face = 1; zeta = l; } if (m > zeta) { face = 2; zeta = m; } if (-l > zeta) { face = 3; zeta = -l; } if (-m > zeta) { face = 4; zeta = -m; } if (-n > zeta) { face = 5; zeta = -n; } double eta, xi; float x0, y0; switch (face) { case 1: xi = m; eta = n; x0 = 0.0; y0 = 0.0; break; case 2: xi = -l; eta = n; x0 = 2.0; y0 = 0.0; break; case 3: xi = -m; eta = n; x0 = 4.0; y0 = 0.0; break; case 4: xi = l; eta = n; x0 = 6.0; y0 = 0.0; break; case 5: xi = m; eta = l; x0 = 0.0; y0 = -2.0; break; default: // face == 0 xi = m; eta = -l; x0 = 0.0; y0 = 2.0; break; } float chi = (float)( xi/zeta); float psi = (float)(eta/zeta); float chi2 = chi*chi; float psi2 = psi*psi; float chi2co = 1.0f - chi2; float psi2co = 1.0f - psi2; // 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)))); yf = psi*(psi2 + psi2co*(gstar + chi2*(gamma*psi2co + mm*psi2 + chi2co*(c00 + c10*psi2 + c01*chi2 + c11*chi2psi2 + c20*psi4 + c02*chi4)) + psi2*(omega1 - psi2co*(d0 + d1*psi2)))); int istat = 0; if (fabs((double)xf) > 1.0) { if (fabs((double)xf) > 1.0+tol) { istat = 1; if (!status) status = PRJERR_BAD_WORLD_SET("cscs2x"); } xf = (float)copysign(1.0, (double)xf); } if (fabs((double)yf) > 1.0) { if (fabs((double)yf) > 1.0+tol) { istat = 1; if (!status) status = PRJERR_BAD_WORLD_SET("cscs2x"); } yf = (float)copysign(1.0, (double)yf); } *xp = prj->w[0]*(xf + x0) - prj->x0; *yp = prj->w[0]*(yf + y0) - prj->y0; *statp = istat; } } return status; } /*============================================================================ * QSC: quadrilaterilized spherical cube 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 QSC * prj->code "QSC" * prj->x0 Fiducial offset in x. * prj->y0 Fiducial offset in y. * prj->w[0] r0*(pi/4) * prj->w[1] (4/pi)/r0 * prj->prjx2s Pointer to qscx2s(). * prj->prjs2x Pointer to qscs2x(). *===========================================================================*/ int qscset(struct prjprm *prj) { if (prj == 0x0) return PRJERR_NULL_POINTER; if (prj->flag == -QSC) return 0; strcpy(prj->code, "QSC"); strcpy(prj->name, "quadrilateralized spherical cube"); prj->category = QUADCUBE; 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] = 45.0; prj->w[1] = 1.0/45.0; } else { prj->w[0] = prj->r0*PI/4.0; prj->w[1] = 1.0/prj->w[0]; } prj->prjx2s = qscx2s; prj->prjs2x = qscs2x; prj->flag = (prj->flag == 1) ? -QSC : QSC; return prjoff(prj, 0.0, 0.0); } //---------------------------------------------------------------------------- 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[]) { const double tol = 1.0e-12; // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; int status; if (abs(prj->flag) != QSC) { if ((status = qscset(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 xf = (*xp + prj->x0)*prj->w[1]; double *phip = phi + rowoff; for (int iy = 0; iy < my; iy++) { *phip = xf; 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 yf = (*yp + prj->y0)*prj->w[1]; for (int ix = 0; ix < mx; ix++, phip += spt, thetap += spt, statp++) { double xf = *phip; // Bounds checking. if (fabs(xf) <= 1.0) { if (fabs(yf) > 3.0) { *phip = 0.0; *thetap = 0.0; *statp = 1; if (!status) status = PRJERR_BAD_PIX_SET("qscx2s"); continue; } } else { if (fabs(xf) > 7.0 || fabs(yf) > 1.0) { *phip = 0.0; *thetap = 0.0; *statp = 1; if (!status) status = PRJERR_BAD_PIX_SET("qscx2s"); continue; } } // Map negative faces to the other side. if (xf < -1.0) xf += 8.0; int face = 0; // Determine the face. if (xf > 5.0) { face = 4; xf -= 6.0; } else if (xf > 3.0) { face = 3; xf -= 4.0; } else if (xf > 1.0) { face = 2; xf -= 2.0; } else if (yf > 1.0) { face = 0; yf -= 2.0; } else if (yf < -1.0) { face = 5; yf += 2.0; } else { face = 1; } double omega, tau, w, zeta, zeco; int direct = (fabs(xf) > fabs(yf)); if (direct) { if (xf == 0.0) { omega = 0.0; tau = 1.0; zeta = 1.0; zeco = 0.0; } else { w = 15.0*yf/xf; omega = sind(w)/(cosd(w) - SQRT2INV); tau = 1.0 + omega*omega; zeco = xf*xf*(1.0 - 1.0/sqrt(1.0 + tau)); zeta = 1.0 - zeco; } } else { if (yf == 0.0) { omega = 0.0; tau = 1.0; zeta = 1.0; 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; zeco = yf*yf*(1.0 - 1.0/sqrt(1.0 + tau)); zeta = 1.0 - zeco; } } if (zeta < -1.0) { if (zeta < -1.0-tol) { *phip = 0.0; *thetap = 0.0; *statp = 1; if (!status) status = PRJERR_BAD_PIX_SET("qscx2s"); continue; } zeta = -1.0; zeco = 2.0; w = 0.0; } else { w = sqrt(zeco*(2.0-zeco)/tau); } double l, m, n; switch (face) { case 1: l = zeta; if (direct) { m = w; if (xf < 0.0) m = -m; n = m*omega; } else { n = w; if (yf < 0.0) n = -n; m = n*omega; } break; case 2: m = zeta; if (direct) { l = w; if (xf > 0.0) l = -l; n = -l*omega; } else { n = w; if (yf < 0.0) n = -n; l = -n*omega; } break; case 3: l = -zeta; if (direct) { m = w; if (xf > 0.0) m = -m; n = -m*omega; } else { n = w; if (yf < 0.0) n = -n; m = -n*omega; } break; case 4: m = -zeta; if (direct) { l = w; if (xf < 0.0) l = -l; n = l*omega; } else { n = w; if (yf < 0.0) n = -n; l = n*omega; } break; case 5: n = -zeta; if (direct) { m = w; if (xf < 0.0) m = -m; l = m*omega; } else { l = w; if (yf < 0.0) l = -l; m = l*omega; } break; default: // face == 0 n = zeta; if (direct) { m = w; if (xf < 0.0) m = -m; l = -m*omega; } else { l = w; if (yf > 0.0) l = -l; m = -l*omega; } break; } if (l == 0.0 && m == 0.0) { *phip = 0.0; } else { *phip = atan2d(m, l); } *thetap = asind(n); *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( struct prjprm *prj, int nphi, int ntheta, int spt, int sxy, const double phi[], const double theta[], double x[], double y[], int stat[]) { const double tol = 1.0e-12; // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; int status; if (abs(prj->flag) != QSC) { if ((status = qscset(prj))) return status; } int mphi, mtheta; if (ntheta > 0) { mphi = nphi; mtheta = ntheta; } else { mphi = 1; mtheta = 1; ntheta = nphi; } status = 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 sinphi, cosphi; sincosd(*phip, &sinphi, &cosphi); double *xp = x + rowoff; double *yp = y + rowoff; for (int itheta = 0; itheta < mtheta; itheta++) { *xp = cosphi; *yp = sinphi; 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, costhe; sincosd(*thetap, &sinthe, &costhe); 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; continue; } double l = costhe*(*xp); double m = costhe*(*yp); double n = sinthe; int face = 0; double zeta = n; if (l > zeta) { face = 1; zeta = l; } if (m > zeta) { face = 2; zeta = m; } if (-l > zeta) { face = 3; zeta = -l; } if (-m > zeta) { face = 4; zeta = -m; } if (-n > zeta) { face = 5; zeta = -n; } 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. double t = (*thetap)*D2R; double p = atan2(*yp, *xp); zeco = (p*p + t*t)/2.0; } x0 = 0.0; y0 = 0.0; break; case 2: xi = -l; eta = n; if (zeco < 1.0e-8) { // 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; y0 = 0.0; break; case 3: xi = -m; eta = n; if (zeco < 1.0e-8) { // Small angle formula. double t = (*thetap)*D2R; double p = atan2(*yp, *xp); p -= copysign(PI, p); zeco = (p*p + t*t)/2.0; } x0 = 4.0; y0 = 0.0; break; case 4: xi = l; eta = n; if (zeco < 1.0e-8) { // Small angle formula. double t = (*thetap)*D2R; double p = atan2(*yp, *xp) + PI/2.0; zeco = (p*p + t*t)/2.0; } x0 = 6; y0 = 0.0; break; case 5: xi = m; eta = l; if (zeco < 1.0e-8) { // Small angle formula. double t = (*thetap + 90.0)*D2R; zeco = t*t/2.0; } x0 = 0.0; y0 = -2; break; default: // face == 0 xi = m; eta = -l; if (zeco < 1.0e-8) { // Small angle formula. double t = (90.0 - *thetap)*D2R; zeco = t*t/2.0; } x0 = 0.0; y0 = 2.0; break; } 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; tau = 1.0 + omega*omega; xf = -sqrt(zeco/(1.0 - 1.0/sqrt(1.0+tau))); yf = (xf/15.0)*(atand(omega) - asind(omega/sqrt(tau+tau))); } else if (xi > fabs(eta)) { omega = eta/xi; tau = 1.0 + omega*omega; xf = sqrt(zeco/(1.0 - 1.0/sqrt(1.0+tau))); yf = (xf/15.0)*(atand(omega) - asind(omega/sqrt(tau+tau))); } else if (-eta >= fabs(xi)) { omega = xi/eta; tau = 1.0 + omega*omega; yf = -sqrt(zeco/(1.0 - 1.0/sqrt(1.0+tau))); xf = (yf/15.0)*(atand(omega) - asind(omega/sqrt(tau+tau))); } else if (eta >= fabs(xi)) { omega = xi/eta; tau = 1.0 + omega*omega; yf = sqrt(zeco/(1.0 - 1.0/sqrt(1.0+tau))); xf = (yf/15.0)*(atand(omega) - asind(omega/sqrt(tau+tau))); } } int istat = 0; if (fabs(xf) > 1.0) { if (fabs(xf) > 1.0+tol) { istat = 1; if (!status) status = PRJERR_BAD_WORLD_SET("qscs2x"); } xf = copysign(1.0, xf); } if (fabs(yf) > 1.0) { if (fabs(yf) > 1.0+tol) { istat = 1; if (!status) status = PRJERR_BAD_WORLD_SET("qscs2x"); } yf = copysign(1.0, yf); } *xp = prj->w[0]*(xf + x0) - prj->x0; *yp = prj->w[0]*(yf + y0) - prj->y0; *statp = istat; } } return status; } /*============================================================================ * HPX: HEALPix projection. * * Given: * prj->pv[1] H - the number of facets in longitude. * prj->pv[2] K - the number of facets in latitude * * 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 HPX * prj->code "HPX" * prj->x0 Fiducial offset in x. * prj->y0 Fiducial offset in y. * prj->m True if H is odd. * prj->n True if K is odd. * prj->w[0] r0*(pi/180) * prj->w[1] (180/pi)/r0 * prj->w[2] (K-1)/K * prj->w[3] 90*K/H * prj->w[4] (K+1)/2 * prj->w[5] 90*(K-1)/H * prj->w[6] 180/H * prj->w[7] H/360 * prj->w[8] r0*(pi/180)*(90*K/H) * prj->w[9] r0*(pi/180)*(180/H) * prj->prjx2s Pointer to hpxx2s(). * prj->prjs2x Pointer to hpxs2x(). *===========================================================================*/ int hpxset(struct prjprm *prj) { if (prj == 0x0) return PRJERR_NULL_POINTER; if (prj->flag == -HPX) return 0; strcpy(prj->code, "HPX"); if (undefined(prj->pv[1])) prj->pv[1] = 4.0; if (undefined(prj->pv[2])) prj->pv[2] = 3.0; strcpy(prj->name, "HEALPix"); prj->category = HEALPIX; prj->pvrange = 102; prj->simplezen = 0; prj->equiareal = 1; prj->conformal = 0; prj->global = 1; prj->divergent = 0; if (prj->pv[1] <= 0.0 || prj->pv[2] <= 0.0) { return PRJERR_BAD_PARAM_SET("hpxset"); } prj->m = ((int)(prj->pv[1]+0.5))%2; prj->n = ((int)(prj->pv[2]+0.5))%2; 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[2] = (prj->pv[2] - 1.0) / prj->pv[2]; prj->w[3] = 90.0 * prj->pv[2] / prj->pv[1]; prj->w[4] = (prj->pv[2] + 1.0) / 2.0; prj->w[5] = 90.0 * (prj->pv[2] - 1.0) / prj->pv[1]; prj->w[6] = 180.0 / prj->pv[1]; prj->w[7] = prj->pv[1] / 360.0; prj->w[8] = prj->w[3] * prj->w[0]; prj->w[9] = prj->w[6] * prj->w[0]; prj->prjx2s = hpxx2s; prj->prjs2x = hpxs2x; prj->flag = (prj->flag == 1) ? -HPX : HPX; return prjoff(prj, 0.0, 0.0); } //---------------------------------------------------------------------------- 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[]) { // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; int status; if (abs(prj->flag) != HPX) { if ((status = hpxset(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 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); 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; thetap += rowlen; } } // Do y dependence. const double *yp = y; double *phip = phi; double *thetap = theta; int *statp = stat; 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. double t = asind(yr/prj->w[3]); for (int ix = 0; ix < mx; ix++, phip += spt, thetap += spt, statp++) { *thetap = t; *statp = 0; } } else if (absy <= ylim) { // Polar regime. int offset = (prj->n || *yp > 0.0) ? 0 : 1; double sigma = prj->w[4] - absy / prj->w[6]; double s, t; if (sigma == 0.0) { s = 1e9; t = 90.0; } else { t = 1.0 - sigma*sigma/prj->pv[2]; if (t < -1.0) { s = 0.0; t = 0.0; istat = 1; if (!status) status = PRJERR_BAD_PIX_SET("hpxx2s"); } else { s = 1.0/sigma; t = asind(t); } } if (*yp < 0.0) t = -t; for (int ix = 0; ix < mx; ix++, phip += spt, thetap += spt, statp++) { if (offset) { // 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 { *thetap += prj->w[6]; } } // 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 (int ix = 0; ix < mx; ix++, phip += spt, thetap += spt, statp++) { *phip = 0.0; *thetap = 0.0; *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( 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) != HPX) { if ((status = hpxset(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; 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. 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); 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; 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); double eta; if (abssin <= prj->w[2]) { // Equatorial regime. eta = prj->w[8] * sinthe - prj->y0; for (int iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy, statp++) { *yp = eta; *statp = 0; } } else { // Polar regime. int offset = (prj->n || *thetap > 0.0) ? 0 : 1; 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 (int iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy, statp++) { if (offset) { // 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 { *yp += prj->w[9]; } } // Recall that y[] holds (phi - phi_c). *xp += *yp * xi; *yp = eta; *statp = 0; // Put the phi = 180 meridian in the expected place. if (180.0 < *xp) *xp = 360.0 - *xp; } } } 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; } astropy-astropy-201cddb/cextern/wcslib/C/prj.h000066400000000000000000001024271507226315300214660ustar00rootroot00000000000000/*============================================================================ WCSLIB 8.4 - an implementation of the FITS WCS standard. Copyright (C) 1995-2024, 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/people/Mark.Calabretta $Id: prj.h,v 8.4 2024/10/28 13:56:16 mcalabre Exp $ *============================================================================= * * WCSLIB 8.4 - 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 * --------------------------- * 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, 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 * values in the prjprm struct from parameters in it that were supplied by the * user. The struct always needs to be set by the projection's setup routine * but that need not be called explicitly - refer to the explanation of * prjprm::flag. * * Each map projection is implemented via separate functions for the spherical * projection, ???s2x(), and deprojection, ???x2s(). * * A set of driver routines, prjset(), prjx2s(), and prjs2x(), provides a * generic interface to the specific projection routines which they invoke * via pointers-to-functions stored in the prjprm struct. * * In summary, the routines are: * - prjini() Initialization routine for 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 * * - azpset(), azpx2s(), azps2x(): AZP (zenithal/azimuthal perspective) * - szpset(), szpx2s(), szps2x(): SZP (slant zenithal perspective) * - tanset(), tanx2s(), tans2x(): TAN (gnomonic) * - stgset(), stgx2s(), stgs2x(): STG (stereographic) * - sinset(), sinx2s(), sins2x(): SIN (orthographic/synthesis) * - arcset(), arcx2s(), arcs2x(): ARC (zenithal/azimuthal equidistant) * - zpnset(), zpnx2s(), zpns2x(): ZPN (zenithal/azimuthal polynomial) * - zeaset(), zeax2s(), zeas2x(): ZEA (zenithal/azimuthal equal area) * - airset(), airx2s(), airs2x(): AIR (Airy) * - cypset(), cypx2s(), cyps2x(): CYP (cylindrical perspective) * - ceaset(), ceax2s(), ceas2x(): CEA (cylindrical equal area) * - carset(), carx2s(), cars2x(): CAR (Plate carree) * - merset(), merx2s(), mers2x(): MER (Mercator) * - sflset(), sflx2s(), sfls2x(): SFL (Sanson-Flamsteed) * - parset(), parx2s(), pars2x(): PAR (parabolic) * - molset(), molx2s(), mols2x(): MOL (Mollweide) * - aitset(), aitx2s(), aits2x(): AIT (Hammer-Aitoff) * - copset(), copx2s(), cops2x(): COP (conic perspective) * - coeset(), coex2s(), coes2x(): COE (conic equal area) * - codset(), codx2s(), cods2x(): COD (conic equidistant) * - cooset(), coox2s(), coos2x(): COO (conic orthomorphic) * - bonset(), bonx2s(), bons2x(): BON (Bonne) * - pcoset(), pcox2s(), pcos2x(): PCO (polyconic) * - tscset(), tscx2s(), tscs2x(): TSC (tangential spherical cube) * - 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): * ---------------------------------------- * The values of phi and theta (the native longitude and latitude) normally lie * in the range [-180,180] for phi, and [-90,90] for theta. However, all * projection routines will accept any value of phi and will not normalize it. * * The projection routines do not explicitly check that theta lies within the * range [-90,90]. They do check for any value of theta that produces an * invalid argument to the projection equations (e.g. leading to division by * 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%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, 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: * --------- * No warranty is given for the accuracy of these routines (refer to the * copyright notice); intending users must satisfy for themselves their * adequacy for the intended purpose. However, closure to a precision of at * least 1E-10 degree of longitude and latitude has been verified for typical * projection parameters on the 1 degree graticule of native longitude and * latitude (to within 5 degrees of any latitude where the projection may * diverge). Refer to the tprj1.c and tprj2.c test routines that accompany * this software. * * * prjini() - Default constructor for the prjprm struct * ---------------------------------------------------- * 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. * * Function return value: * int Status return value: * 0: Success. * 1: Null prjprm pointer passed. * * * prjfree() - Destructor for the prjprm struct * -------------------------------------------- * prjfree() frees any memory that may have been allocated to store an error * message in the prjprm struct. * * Given: * prj struct prjprm* * Projection parameters. * * Function return value: * int Status return value: * 0: Success. * 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 * intended for diagnostic purposes. * * Given: * prj const struct prjprm* * Projection parameters. * * Function return value: * int Status return value: * 0: Success. * 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. * * 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. * Once prjset() has initialized the prjprm struct, prjx2s() and prjs2x() use * 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. * * Function return value: * int Status return value: * 0: Success. * 1: Null prjprm pointer passed. * 2: Invalid projection parameters. * * For returns > 1, a detailed error message is set in * prjprm::err if enabled, see wcserr_enable(). * * * prjx2s() - Generic Cartesian-to-spherical deprojection * ------------------------------------------------------ * Deproject Cartesian (x,y) coordinates in the plane of projection to native * spherical coordinates (phi,theta). * * The projection is that specified by prjprm::code. * * Given and returned: * prj struct prjprm* * Projection parameters. * * Given: * nx,ny int Vector lengths. * * sxy,spt int Vector strides. * * x,y const double[] * Projected coordinates. * * Returned: * phi,theta double[] Longitude and latitude (phi,theta) of the projected * point in native spherical coordinates [deg]. * * stat int[] Status value for each vector element: * 0: Success. * 1: Invalid value of (x,y). * * Function return value: * int Status return value: * 0: Success. * 1: Null prjprm pointer passed. * 2: Invalid projection parameters. * 3: One or more of the (x,y) coordinates were * invalid, as indicated by the stat vector. * * For returns > 1, a detailed error message is set in * prjprm::err if enabled, see wcserr_enable(). * * * prjs2x() - Generic spherical-to-Cartesian projection * ---------------------------------------------------- * Project native spherical coordinates (phi,theta) to Cartesian (x,y) * coordinates in the plane of projection. * * The projection is that specified by prjprm::code. * * Given and returned: * prj struct prjprm* * Projection parameters. * * Given: * nphi, * ntheta int Vector lengths. * * spt,sxy int Vector strides. * * phi,theta const double[] * Longitude and latitude (phi,theta) of the projected * point in native spherical coordinates [deg]. * * Returned: * x,y double[] Projected coordinates. * * stat int[] Status value for each vector element: * 0: Success. * 1: Invalid value of (phi,theta). * * Function return value: * int Status return value: * 0: Success. * 1: Null prjprm pointer passed. * 2: Invalid projection parameters. * 4: One or more of the (phi,theta) coordinates * were, invalid, as indicated by the stat vector. * * For returns > 1, a detailed error message is set in * prjprm::err if enabled, see wcserr_enable(). * * * ???set() - Specific setup routines for the prjprm struct * -------------------------------------------------------- * Set up a prjprm struct for a particular projection according to information * supplied within it. * * Given and returned: * prj struct prjprm* * Projection parameters. * * Function return value: * int Status return value: * 0: Success. * 1: Null prjprm pointer passed. * 2: Invalid projection parameters. * * For returns > 1, a detailed error message is set in * prjprm::err if enabled, see wcserr_enable(). * * * ???x2s() - Specific Cartesian-to-spherical deprojection routines * ---------------------------------------------------------------- * Transform (x,y) coordinates in the plane of projection to native spherical * coordinates (phi,theta). * * Given and returned: * prj struct prjprm* * Projection parameters. * * Given: * nx,ny int Vector lengths. * * sxy,spt int Vector strides. * * x,y const double[] * Projected coordinates. * * Returned: * phi,theta double[] Longitude and latitude of the projected point in * native spherical coordinates [deg]. * * stat int[] Status value for each vector element: * 0: Success. * 1: Invalid value of (x,y). * * Function return value: * int Status return value: * 0: Success. * 1: Null prjprm pointer passed. * 2: Invalid projection parameters. * 3: One or more of the (x,y) coordinates were * invalid, as indicated by the stat vector. * * For returns > 1, a detailed error message is set in * prjprm::err if enabled, see wcserr_enable(). * * * ???s2x() - Specific spherical-to-Cartesian projection routines *--------------------------------------------------------------- * Transform native spherical coordinates (phi,theta) to (x,y) coordinates in * the plane of projection. * * Given and returned: * prj struct prjprm* * Projection parameters. * * Given: * nphi, * ntheta int Vector lengths. * * spt,sxy int Vector strides. * * phi,theta const double[] * Longitude and latitude of the projected point in * native spherical coordinates [deg]. * * Returned: * x,y double[] Projected coordinates. * * stat int[] Status value for each vector element: * 0: Success. * 1: Invalid value of (phi,theta). * * Function return value: * int Status return value: * 0: Success. * 1: Null prjprm pointer passed. * 2: Invalid projection parameters. * 4: One or more of the (phi,theta) coordinates * were, invalid, as indicated by the stat vector. * * For returns > 1, a detailed error message is set in * prjprm::err if enabled, see wcserr_enable(). * * * prjprm struct - Projection parameters * ------------------------------------- * The prjprm struct contains all 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 * while others are for internal use only. * * int flag * (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, * - prjprm::pv[], * - prjprm::phi0, * - prjprm::theta0. * * This signals the initialization routine (prjset() or ???set()) to * recompute the returned members of the prjprm struct. flag will then be * reset to indicate that this has been done. * * Note that flag need not be reset when prjprm::bounds is changed. * * char code[4] * (Given) Three-letter projection code defined by the FITS standard. * * double r0 * (Given) 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 180/pi (the value for FITS WCS). * * double pv[30] * (Given) 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. * * Usage of the pv[] array as it applies to each projection is described in * the prologue to each trio of projection routines in prj.c. * * double phi0 * (Given) The native longitude, phi_0 [deg], and ... * double theta0 * (Given) ... the native latitude, theta_0 [deg], of the reference point, * i.e. the point (x,y) = (0,0). If undefined (set to a magic value by * prjini()) the initialization routine will set this to a * projection-specific default. * * int bounds * (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: * * char name[40] * (Returned) Long name of the projection. * * Provided for information only, not used by the projection routines. * * int category * (Returned) Projection category matching the value of the relevant global * variable: * * - ZENITHAL, * - CYLINDRICAL, * - PSEUDOCYLINDRICAL, * - CONVENTIONAL, * - CONIC, * - POLYCONIC, * - QUADCUBE, and * - HEALPIX. * * The category name may be identified via the prj_categories character * array, e.g. * = struct prjprm prj; = ... = printf("%s\n", prj_categories[prj.category]); * * Provided for information only, not used by the projection routines. * * int pvrange * (Returned) 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). * * Provided for information only, not used by the projection routines. * * int simplezen * (Returned) True if the projection is a radially-symmetric zenithal * projection. * * Provided for information only, not used by the projection routines. * * int equiareal * (Returned) True if the projection is equal area. * * Provided for information only, not used by the projection routines. * * int conformal * (Returned) True if the projection is conformal. * * Provided for information only, not used by the projection routines. * * int global * (Returned) True if the projection can represent the whole sphere in a * finite, non-overlapped mapping. * * Provided for information only, not used by the projection routines. * * int divergent * (Returned) True if the projection diverges in latitude. * * Provided for information only, not used by the projection routines. * * double x0 * (Returned) The offset in x, and ... * double y0 * (Returned) ... the offset in y used to force (x,y) = (0,0) at * (phi_0,theta_0). * * struct wcserr *err * (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] * (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 m, n * (For internal use only.) Intermediate integer values set by prjset() * (used only for the ZPN and HPX projections). * * int (*prjx2s)(PRJX2S_ARGS) * (For internal use only.) Pointer to the spherical projection ... * int (*prjs2x)(PRJ_ARGS) * (For internal use only.) ... and deprojection routines. * * * Global variable: const char *prj_errmsg[] - Status return messages * ------------------------------------------------------------------ * Error messages to match the status value returned from each function. * *===========================================================================*/ #ifndef WCSLIB_PROJ #define WCSLIB_PROJ #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. #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. }; extern const int CONIC, CONVENTIONAL, CYLINDRICAL, POLYCONIC, PSEUDOCYLINDRICAL, QUADCUBE, ZENITHAL, HEALPIX; extern const char prj_categories[9][32]; extern const int prj_ncode; extern const char prj_codes[28][4]; #ifdef PRJX2S_ARGS #undef PRJX2S_ARGS #endif #ifdef PRJS2X_ARGS #undef PRJS2X_ARGS #endif // 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. #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; // 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 - 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. }; // Size of the prjprm struct in int units, used by the Fortran wrappers. #define PRJLEN (sizeof(struct prjprm)/sizeof(int)) 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); int azpset(struct prjprm *prj); int azpx2s(PRJX2S_ARGS); int azps2x(PRJS2X_ARGS); int szpset(struct prjprm *prj); int szpx2s(PRJX2S_ARGS); int szps2x(PRJS2X_ARGS); int tanset(struct prjprm *prj); int tanx2s(PRJX2S_ARGS); int tans2x(PRJS2X_ARGS); int stgset(struct prjprm *prj); int stgx2s(PRJX2S_ARGS); int stgs2x(PRJS2X_ARGS); int sinset(struct prjprm *prj); int sinx2s(PRJX2S_ARGS); int sins2x(PRJS2X_ARGS); int arcset(struct prjprm *prj); int arcx2s(PRJX2S_ARGS); int arcs2x(PRJS2X_ARGS); int zpnset(struct prjprm *prj); int zpnx2s(PRJX2S_ARGS); int zpns2x(PRJS2X_ARGS); int zeaset(struct prjprm *prj); int zeax2s(PRJX2S_ARGS); int zeas2x(PRJS2X_ARGS); int airset(struct prjprm *prj); int airx2s(PRJX2S_ARGS); int airs2x(PRJS2X_ARGS); int cypset(struct prjprm *prj); int cypx2s(PRJX2S_ARGS); int cyps2x(PRJS2X_ARGS); int ceaset(struct prjprm *prj); int ceax2s(PRJX2S_ARGS); int ceas2x(PRJS2X_ARGS); int carset(struct prjprm *prj); int carx2s(PRJX2S_ARGS); int cars2x(PRJS2X_ARGS); int merset(struct prjprm *prj); int merx2s(PRJX2S_ARGS); int mers2x(PRJS2X_ARGS); int sflset(struct prjprm *prj); int sflx2s(PRJX2S_ARGS); int sfls2x(PRJS2X_ARGS); int parset(struct prjprm *prj); int parx2s(PRJX2S_ARGS); int pars2x(PRJS2X_ARGS); int molset(struct prjprm *prj); int molx2s(PRJX2S_ARGS); int mols2x(PRJS2X_ARGS); int aitset(struct prjprm *prj); int aitx2s(PRJX2S_ARGS); int aits2x(PRJS2X_ARGS); int copset(struct prjprm *prj); int copx2s(PRJX2S_ARGS); int cops2x(PRJS2X_ARGS); int coeset(struct prjprm *prj); int coex2s(PRJX2S_ARGS); int coes2x(PRJS2X_ARGS); int codset(struct prjprm *prj); int codx2s(PRJX2S_ARGS); int cods2x(PRJS2X_ARGS); int cooset(struct prjprm *prj); int coox2s(PRJX2S_ARGS); int coos2x(PRJS2X_ARGS); int bonset(struct prjprm *prj); int bonx2s(PRJX2S_ARGS); int bons2x(PRJS2X_ARGS); int pcoset(struct prjprm *prj); int pcox2s(PRJX2S_ARGS); int pcos2x(PRJS2X_ARGS); int tscset(struct prjprm *prj); int tscx2s(PRJX2S_ARGS); int tscs2x(PRJS2X_ARGS); int cscset(struct prjprm *prj); int cscx2s(PRJX2S_ARGS); int cscs2x(PRJS2X_ARGS); int qscset(struct prjprm *prj); int qscx2s(PRJX2S_ARGS); int qscs2x(PRJS2X_ARGS); 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. #define prjini_errmsg prj_errmsg #define prjprt_errmsg prj_errmsg #define prjset_errmsg prj_errmsg #define prjx2s_errmsg prj_errmsg #define prjs2x_errmsg prj_errmsg #ifdef __cplusplus } #endif #endif // WCSLIB_PROJ astropy-astropy-201cddb/cextern/wcslib/C/spc.c000066400000000000000000001110451507226315300214470ustar00rootroot00000000000000/*============================================================================ WCSLIB 8.4 - an implementation of the FITS WCS standard. Copyright (C) 1995-2024, 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/people/Mark.Calabretta $Id: spc.c,v 8.4 2024/10/28 13:56:16 mcalabre Exp $ *===========================================================================*/ #include #include #include #include #include "wcserr.h" #include "wcsmath.h" #include "wcsprintf.h" #include "wcstrig.h" #include "wcsutil.h" #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. const char *spc_errmsg[] = { "Success", "Null spcprm pointer passed", "Invalid spectral parameters", "One or more of x coordinates were invalid", "One or more of the spec coordinates were invalid"}; // 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) { if (spc == 0x0) return SPCERR_NULL_POINTER; memset(spc->type, 0, 8); strcpy(spc->type, " "); strcpy(spc->code, " "); spc->crval = UNDEFINED; spc->restfrq = 0.0; spc->restwav = 0.0; for (int k = 0; k < 7; k++) { spc->pv[k] = UNDEFINED; } for (int k = 0; k < 6; k++) { spc->w[k] = 0.0; } spc->isGrism = 0; spc->padding1 = 0; spc->err = 0x0; spc->padding2 = 0x0; spc->spxX2P = 0x0; spc->spxP2S = 0x0; spc->spxS2P = 0x0; spc->spxP2X = 0x0; spc->flag = 0; return 0; } //---------------------------------------------------------------------------- int spcfree(struct spcprm *spc) { if (spc == 0x0) return SPCERR_NULL_POINTER; 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; } // 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 spcenq(const struct spcprm *spc, int enquiry) { // 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.5g\n", spc->crval); } wcsprintf(" restfrq: %f\n", spc->restfrq); wcsprintf(" restwav: %f\n", spc->restwav); wcsprintf(" pv:"); if (spc->isGrism) { for (int i = 0; i < 5; i++) { if (undefined(spc->pv[i])) { wcsprintf(" UNDEFINED "); } else { wcsprintf(" %#- 11.5g", spc->pv[i]); } } wcsprintf("\n "); for (int i = 5; i < 7; i++) { if (undefined(spc->pv[i])) { wcsprintf(" UNDEFINED "); } else { wcsprintf(" %#- 11.5g", spc->pv[i]); } } wcsprintf("\n"); } else { wcsprintf(" (not used)\n"); } // Derived values. wcsprintf(" w:"); for (int i = 0; i < 3; i++) { wcsprintf(" %#- 11.5g", spc->w[i]); } if (spc->isGrism) { wcsprintf("\n "); for (int i = 3; i < 6; i++) { wcsprintf(" %#- 11.5g", spc->w[i]); } wcsprintf("\n"); } else { wcsprintf(" (remainder unused)\n"); } 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((void (*)(void))spc->spxX2P, hext)); wcsprintf(" spxP2S: %s\n", wcsutil_fptr2str((void (*)(void))spc->spxP2S, hext)); wcsprintf(" spxS2P: %s\n", wcsutil_fptr2str((void (*)(void))spc->spxS2P, hext)); wcsprintf(" spxP2X: %s\n", 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 0; } //---------------------------------------------------------------------------- int spcset(struct spcprm *spc) { static const char *function = "spcset"; if (spc == 0x0) return SPCERR_NULL_POINTER; if (spc->flag < 0) return 0; struct wcserr **err = &(spc->err); if (undefined(spc->crval)) { return wcserr_set(WCSERR_SET(SPCERR_BAD_SPEC_PARAMS), "Spectral crval is undefined"); } memset((spc->type)+4, 0, 4); spc->code[3] = '\0'; wcsutil_blank_fill(4, spc->type); wcsutil_blank_fill(3, spc->code); spc->w[0] = 0.0; // Analyse the spectral axis type. char ctype[9]; memset(ctype, 0, 9); memcpy(ctype, spc->type, 4); if (*(spc->code) != ' ') { sprintf(ctype+4, "-%s", spc->code); } 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. 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. restwav = 1.0; } if (restfrq == 0.0) { restfrq = C/restwav; } else { restwav = C/restfrq; } if (ptype == 'F') { spc->w[0] = restfrq; } else if (ptype != 'V') { spc->w[0] = restwav; } else { if (xtype == 'F') { spc->w[0] = restfrq; } else { spc->w[0] = restwav; } } } spc->w[1] = crvalX; spc->w[2] = dXdS; // Set pointers-to-functions for the linear part of the transformation. int flag = 0; if (ptype == 'F') { if (strcmp(spc->type, "FREQ") == 0) { // Frequency. flag = FREQ; spc->spxP2S = 0x0; spc->spxS2P = 0x0; } else if (strcmp(spc->type, "AFRQ") == 0) { // Angular frequency. flag = AFRQ; spc->spxP2S = freqafrq; spc->spxS2P = afrqfreq; } else if (strcmp(spc->type, "ENER") == 0) { // Photon energy. flag = ENER; spc->spxP2S = freqener; spc->spxS2P = enerfreq; } else if (strcmp(spc->type, "WAVN") == 0) { // Wave number. flag = WAVN; spc->spxP2S = freqwavn; spc->spxS2P = wavnfreq; } else if (strcmp(spc->type, "VRAD") == 0) { // Radio velocity. flag = VRAD; spc->spxP2S = freqvrad; spc->spxS2P = vradfreq; } } else if (ptype == 'W') { if (strcmp(spc->type, "WAVE") == 0) { // Vacuum wavelengths. flag = WAVE; spc->spxP2S = 0x0; spc->spxS2P = 0x0; } else if (strcmp(spc->type, "VOPT") == 0) { // Optical velocity. flag = VOPT; spc->spxP2S = wavevopt; spc->spxS2P = voptwave; } else if (strcmp(spc->type, "ZOPT") == 0) { // Redshift. flag = ZOPT; spc->spxP2S = wavezopt; spc->spxS2P = zoptwave; } } else if (ptype == 'A') { if (strcmp(spc->type, "AWAV") == 0) { // Air wavelengths. flag = AWAV; spc->spxP2S = 0x0; spc->spxS2P = 0x0; } } else if (ptype == 'V') { if (strcmp(spc->type, "VELO") == 0) { // Relativistic velocity. flag = VELO; spc->spxP2S = 0x0; spc->spxS2P = 0x0; } else if (strcmp(spc->type, "BETA") == 0) { // 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. spc->isGrism = 0; if (xtype == 'F') { // Axis is linear in frequency. if (ptype == 'F') { spc->spxX2P = 0x0; spc->spxP2X = 0x0; } else if (ptype == 'W') { spc->spxX2P = freqwave; spc->spxP2X = wavefreq; } else if (ptype == 'A') { spc->spxX2P = freqawav; spc->spxP2X = awavfreq; } else if (ptype == 'V') { spc->spxX2P = freqvelo; spc->spxP2X = velofreq; } flag += F2S; } else if (xtype == 'W' || xtype == 'w') { // Axis is linear in vacuum wavelengths. if (ptype == 'F') { spc->spxX2P = wavefreq; spc->spxP2X = freqwave; } else if (ptype == 'W') { spc->spxX2P = 0x0; spc->spxP2X = 0x0; } else if (ptype == 'A') { spc->spxX2P = waveawav; spc->spxP2X = awavwave; } else if (ptype == 'V') { spc->spxX2P = wavevelo; spc->spxP2X = velowave; } if (xtype == 'W') { flag += W2S; } else { // Grism in vacuum. spc->isGrism = 1; flag += GRI; } } else if (xtype == 'A' || xtype == 'a') { // Axis is linear in air wavelengths. if (ptype == 'F') { spc->spxX2P = awavfreq; spc->spxP2X = freqawav; } else if (ptype == 'W') { spc->spxX2P = awavwave; spc->spxP2X = waveawav; } else if (ptype == 'A') { spc->spxX2P = 0x0; spc->spxP2X = 0x0; } else if (ptype == 'V') { spc->spxX2P = awavvelo; spc->spxP2X = veloawav; } if (xtype == 'A') { flag += A2S; } else { // Grism in air. spc->isGrism = 2; flag += GRA; } } else if (xtype == 'V') { // Axis is linear in relativistic velocity. if (ptype == 'F') { spc->spxX2P = velofreq; spc->spxP2X = freqvelo; } else if (ptype == 'W') { spc->spxX2P = velowave; spc->spxP2X = wavevelo; } else if (ptype == 'A') { spc->spxX2P = veloawav; spc->spxP2X = awavvelo; } else if (ptype == 'V') { spc->spxX2P = 0x0; spc->spxP2X = 0x0; } flag += V2S; } // Check for grism axes. if (spc->isGrism) { // Axis is linear in "grism parameter"; work in wavelength. double lambda_r = crvalX; // 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; if (undefined(spc->pv[3])) spc->pv[3] = 1.0; if (undefined(spc->pv[4])) spc->pv[4] = 0.0; if (undefined(spc->pv[5])) spc->pv[5] = 0.0; if (undefined(spc->pv[6])) spc->pv[6] = 0.0; // 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]; double t = G*m/cosd(epsilon); double beta_r = asind(t*lambda_r - n_r*sind(alpha)); t -= dn_r*sind(alpha); spc->w[1] = -tand(theta); spc->w[2] *= t / (cosd(beta_r)*cosd(theta)*cosd(theta)); spc->w[3] = beta_r + theta; spc->w[4] = (n_r - dn_r*lambda_r)*sind(alpha); spc->w[5] = 1.0 / t; } spc->flag = (spc->flag == 1) ? -flag : flag; return 0; } //---------------------------------------------------------------------------- int spcx2s( struct spcprm *spc, int nx, int sx, int sspec, const double x[], double spec[], int stat[]) { static const char *function = "spcx2s"; // Initialize. if (spc == 0x0) return SPCERR_NULL_POINTER; struct wcserr **err = &(spc->err); int status = 0; if (abs(spc->flag) < 100) { if ((status = spcset(spc))) return status; } // 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; } // If X is the grism parameter then convert it to wavelength. if (spc->isGrism) { specp = spec; 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. if (spc->spxX2P) { int statX2P; if ((statX2P = spc->spxX2P(spc->w[0], nx, sspec, sspec, spec, spec, stat))) { if (statX2P == SPXERR_BAD_INSPEC_COORD) { status = SPCERR_BAD_X; } else if (statX2P == SPXERR_BAD_SPEC_PARAMS) { return wcserr_set(WCSERR_SET(SPCERR_BAD_SPEC_PARAMS), "Invalid spectral parameters: Frequency or wavelength is 0"); } else { 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. if (spc->spxP2S) { int statP2S; if ((statP2S = spc->spxP2S(spc->w[0], nx, sspec, sspec, spec, spec, stat))) { if (statP2S == SPXERR_BAD_INSPEC_COORD) { status = SPCERR_BAD_X; } else if (statP2S == SPXERR_BAD_SPEC_PARAMS) { return wcserr_set(WCSERR_SET(SPCERR_BAD_SPEC_PARAMS), "Invalid spectral parameters: Frequency or wavelength is 0"); } else { return wcserr_set(SPC_ERRMSG(spc_spxerr[statP2S])); } } } if (status) { wcserr_set(SPC_ERRMSG(status)); } return status; } //---------------------------------------------------------------------------- int spcs2x( struct spcprm *spc, int nspec, int sspec, int sx, const double spec[], double x[], int stat[]) { static const char *function = "spcs2x"; // Initialize. if (spc == 0x0) return SPCERR_NULL_POINTER; struct wcserr **err = &(spc->err); 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. 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; } else if (statS2P == SPXERR_BAD_SPEC_PARAMS) { return wcserr_set(WCSERR_SET(SPCERR_BAD_SPEC_PARAMS), "Invalid spectral parameters: Frequency or wavelength is 0"); } else { return wcserr_set(SPC_ERRMSG(spc_spxerr[statS2P])); } } } else { // 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; } } // 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; } else if (statP2X == SPXERR_BAD_SPEC_PARAMS) { return wcserr_set(WCSERR_SET(SPCERR_BAD_SPEC_PARAMS), "Invalid spectral parameters: Frequency or wavelength is 0"); } else { return wcserr_set(SPC_ERRMSG(spc_spxerr[statP2X])); } } } if (spc->isGrism) { // 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; double s = *xp/spc->w[5] - spc->w[4]; if (fabs(s) <= 1.0) { double beta = asind(s); *xp = tand(beta - spc->w[3]); } else { *statp = 1; } } } // 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]; } if (status) { wcserr_set(SPC_ERRMSG(status)); } return status; } //---------------------------------------------------------------------------- int spctyp( const char ctypei[9], char stype[], char scode[], char sname[], char units[], char *ptype, char *xtype, int *restreq) { return spctype( ctypei, stype, scode, sname, units, ptype, xtype, restreq, NULL); } // : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : int spctype( const char ctypei[9], char stype[], char scode[], char sname[], char units[], char *ptype, char *xtype, int *restreq, struct wcserr **err) { static const char *function = "spctype"; // 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. char ctype[9]; sprintf(ctype, "%-8.8s", ctypei); ctype[8] = '\0'; 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"); ptype_t = 'F'; } else if (strncmp(ctype, "AFRQ", 4) == 0) { strcpy(sname_t, "Angular frequency"); strcpy(units_t, "rad/s"); ptype_t = 'F'; } else if (strncmp(ctype, "ENER", 4) == 0) { strcpy(sname_t, "Photon energy"); strcpy(units_t, "J"); ptype_t = 'F'; } else if (strncmp(ctype, "WAVN", 4) == 0) { strcpy(sname_t, "Wavenumber"); strcpy(units_t, "/m"); ptype_t = 'F'; } else if (strncmp(ctype, "VRAD", 4) == 0) { strcpy(sname_t, "Radio velocity"); strcpy(units_t, "m/s"); ptype_t = 'F'; restreq_t = 1; } else if (strncmp(ctype, "WAVE", 4) == 0) { strcpy(sname_t, "Vacuum wavelength"); strcpy(units_t, "m"); ptype_t = 'W'; } else if (strncmp(ctype, "VOPT", 4) == 0) { strcpy(sname_t, "Optical velocity"); strcpy(units_t, "m/s"); ptype_t = 'W'; restreq_t = 1; } else if (strncmp(ctype, "ZOPT", 4) == 0) { strcpy(sname_t, "Redshift"); strcpy(units_t, ""); ptype_t = 'W'; restreq_t = 1; } else if (strncmp(ctype, "AWAV", 4) == 0) { strcpy(sname_t, "Air wavelength"); strcpy(units_t, "m"); ptype_t = 'A'; } else if (strncmp(ctype, "VELO", 4) == 0) { strcpy(sname_t, "Relativistic velocity"); strcpy(units_t, "m/s"); ptype_t = 'V'; } else if (strncmp(ctype, "BETA", 4) == 0) { strcpy(sname_t, "Velocity ratio (v/c)"); strcpy(units_t, ""); ptype_t = 'V'; } else { return wcserr_set(WCSERR_SET(SPCERR_BAD_SPEC_PARAMS), "Unknown spectral type '%s'", ctype); } // Determine X-type and validate the spectral algorithm code. char xtype_t; if ((xtype_t = ctype[5]) == ' ') { // 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); } xtype_t = ptype_t; } else if (ctype[4] != '-') { return wcserr_set(WCSERR_SET(SPCERR_BAD_SPEC_PARAMS), "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. } else if (xtype_t == 'G') { // 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... if (ctype[7] == 'I') { // ...in vacuum. xtype_t = 'w'; } else if (ctype[7] == 'A') { // ...in air. xtype_t = 'a'; } else { return wcserr_set(WCSERR_SET(SPCERR_BAD_SPEC_PARAMS), "Invalid spectral algorithm '%s'", xtype_t); } } else if (ctype[6] != '2') { // 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. 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. sprintf(ctype+4, " "); } // Rest freq/wavelength required for transformation between P and X? if (strchr("FWAwa", (int)xtype_t)) { if (ptype_t == 'V') { restreq_t += 2; } } else if (xtype_t == 'V') { if (strchr("FWAwa", (int)ptype_t)) { restreq_t += 2; } } else if (strchr("LT", (int)xtype_t) == 0) { // 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. if (stype) { memcpy(stype, ctype, 4); stype[4] = '\0'; } if (scode) strcpy(scode, ctype+5); if (sname) strcpy(sname, sname_t); if (units) strcpy(units, units_t); if (ptype) *ptype = ptype_t; if (xtype) *xtype = xtype_t; if (restreq) *restreq = restreq_t; return 0; } //---------------------------------------------------------------------------- int spcspx( const char ctypeS[9], double crvalS, double restfrq, double restwav, char *ptype, char *xtype, int *restreq, double *crvalX, double *dXdS) { return spcspxe(ctypeS, crvalS, restfrq, restwav, ptype, xtype, restreq, crvalX, dXdS, 0x0); } // : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : 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) { static const char *function = "spcspxe"; // Analyse the spectral axis code. char stype[5], scode[4]; int status; if ((status = spctype(ctypeS, stype, scode, 0x0, 0x0, ptype, xtype, restreq, err))) { return status; } 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? 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. char type[8]; strcpy(type, stype); struct spxprm spx; spx.err = (err ? *err : 0x0); if ((status = specx(type, crvalS, restfrq, restwav, &spx))) { status = spc_spxerr[status]; if (err) { if ((*err = spx.err)) { (*err)->status = status; } } else { wcserr_clear(&(spx.err)); } return status; } // 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; } else if (strcmp(stype, "AFRQ") == 0) { dPdS = spx.dfreqafrq; } else if (strcmp(stype, "ENER") == 0) { dPdS = spx.dfreqener; } else if (strcmp(stype, "WAVN") == 0) { dPdS = spx.dfreqwavn; } else if (strcmp(stype, "VRAD") == 0) { dPdS = spx.dfreqvrad; } if (*xtype == 'F') { *crvalX = spx.freq; dXdP = 1.0; } else if (*xtype == 'W' || *xtype == 'w') { *crvalX = spx.wave; dXdP = spx.dwavefreq; } else if (*xtype == 'A' || *xtype == 'a') { *crvalX = spx.awav; dXdP = spx.dawavfreq; } else if (*xtype == 'V') { *crvalX = spx.velo; dXdP = spx.dvelofreq; } } else if (*ptype == 'W' || *ptype == 'w') { if (strcmp(stype, "WAVE") == 0) { dPdS = 1.0; } else if (strcmp(stype, "VOPT") == 0) { dPdS = spx.dwavevopt; } else if (strcmp(stype, "ZOPT") == 0) { dPdS = spx.dwavezopt; } if (*xtype == 'F') { *crvalX = spx.freq; dXdP = spx.dfreqwave; } else if (*xtype == 'W' || *xtype == 'w') { *crvalX = spx.wave; dXdP = 1.0; } else if (*xtype == 'A' || *xtype == 'a') { *crvalX = spx.awav; dXdP = spx.dawavwave; } else if (*xtype == 'V') { *crvalX = spx.velo; dXdP = spx.dvelowave; } } else if (*ptype == 'A' || *ptype == 'a') { if (strcmp(stype, "AWAV") == 0) { dPdS = 1.0; } if (*xtype == 'F') { *crvalX = spx.freq; dXdP = spx.dfreqawav; } else if (*xtype == 'W' || *xtype == 'w') { *crvalX = spx.wave; dXdP = spx.dwaveawav; } else if (*xtype == 'A' || *xtype == 'a') { *crvalX = spx.awav; dXdP = 1.0; } else if (*xtype == 'V') { *crvalX = spx.velo; dXdP = spx.dveloawav; } } else if (*ptype == 'V') { if (strcmp(stype, "VELO") == 0) { dPdS = 1.0; } else if (strcmp(stype, "BETA") == 0) { dPdS = spx.dvelobeta; } if (*xtype == 'F') { *crvalX = spx.freq; dXdP = spx.dfreqvelo; } else if (*xtype == 'W' || *xtype == 'w') { *crvalX = spx.wave; dXdP = spx.dwavevelo; } else if (*xtype == 'A' || *xtype == 'a') { *crvalX = spx.awav; dXdP = spx.dawavvelo; } else if (*xtype == 'V') { *crvalX = spx.velo; dXdP = 1.0; } } *dXdS = dXdP * dPdS; return 0; } //---------------------------------------------------------------------------- int spcxps( const char ctypeS[9], double crvalX, double restfrq, double restwav, char *ptype, char *xtype, int *restreq, double *crvalS, double *dSdX) { return spcxpse(ctypeS, crvalX, restfrq, restwav, ptype, xtype, restreq, crvalS, dSdX, NULL); } // : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : 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) { static const char *function = "spcxpse"; // Compiler balm for premature return on error. *crvalS = 0.0; *dSdX = 0.0; // 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 (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? 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. char type[8]; if (*xtype == 'F') { strcpy(type, "FREQ"); } else if (*xtype == 'W' || *xtype == 'w') { strcpy(type, "WAVE"); } else if (*xtype == 'A' || *xtype == 'a') { strcpy(type, "AWAV"); } else if (*xtype == 'V') { strcpy(type, "VELO"); } struct spxprm spx; spx.err = (err ? *err : 0x0); if (specx(type, crvalX, restfrq, restwav, &spx)) { status = spc_spxerr[status]; if (err) { if ((*err = spx.err)) { (*err)->status = status; } } else { wcserr_clear(&(spx.err)); } return status; } // 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; } else if (*xtype == 'W' || *xtype == 'w') { dPdX = spx.dfreqwave; } else if (*xtype == 'A' || *xtype == 'a') { dPdX = spx.dfreqawav; } else if (*xtype == 'V') { dPdX = spx.dfreqvelo; } if (strcmp(stype, "FREQ") == 0) { *crvalS = spx.freq; dSdP = 1.0; } else if (strcmp(stype, "AFRQ") == 0) { *crvalS = spx.afrq; dSdP = spx.dafrqfreq; } else if (strcmp(stype, "ENER") == 0) { *crvalS = spx.ener; dSdP = spx.denerfreq; } else if (strcmp(stype, "WAVN") == 0) { *crvalS = spx.wavn; dSdP = spx.dwavnfreq; } else if (strcmp(stype, "VRAD") == 0) { *crvalS = spx.vrad; dSdP = spx.dvradfreq; } } else if (*ptype == 'W') { if (*xtype == 'F') { dPdX = spx.dwavefreq; } else if (*xtype == 'W' || *xtype == 'w') { dPdX = 1.0; } else if (*xtype == 'A' || *xtype == 'a') { dPdX = spx.dwaveawav; } else if (*xtype == 'V') { dPdX = spx.dwavevelo; } if (strcmp(stype, "WAVE") == 0) { *crvalS = spx.wave; dSdP = 1.0; } else if (strcmp(stype, "VOPT") == 0) { *crvalS = spx.vopt; dSdP = spx.dvoptwave; } else if (strcmp(stype, "ZOPT") == 0) { *crvalS = spx.zopt; dSdP = spx.dzoptwave; } } else if (*ptype == 'A') { if (*xtype == 'F') { dPdX = spx.dawavfreq; } else if (*xtype == 'W' || *xtype == 'w') { dPdX = spx.dawavwave; } else if (*xtype == 'A' || *xtype == 'a') { dPdX = 1.0; } else if (*xtype == 'V') { dPdX = spx.dawavvelo; } if (strcmp(stype, "AWAV") == 0) { *crvalS = spx.awav; dSdP = 1.0; } } else if (*ptype == 'V') { if (*xtype == 'F') { dPdX = spx.dvelofreq; } else if (*xtype == 'W' || *xtype == 'w') { dPdX = spx.dvelowave; } else if (*xtype == 'A' || *xtype == 'a') { dPdX = spx.dveloawav; } else if (*xtype == 'V') { dPdX = 1.0; } if (strcmp(stype, "VELO") == 0) { *crvalS = spx.velo; dSdP = 1.0; } else if (strcmp(stype, "BETA") == 0) { *crvalS = spx.beta; dSdP = spx.dbetavelo; } } *dSdX = dSdP * dPdX; return 0; } //---------------------------------------------------------------------------- int spctrn( const char ctypeS1[9], double crvalS1, double cdeltS1, double restfrq, double restwav, char ctypeS2[9], double *crvalS2, double *cdeltS2) { return spctrne(ctypeS1, crvalS1, cdeltS1, restfrq, restwav, ctypeS2, crvalS2, cdeltS2, NULL); } // : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : int spctrne( const char ctypeS1[9], double crvalS1, double cdeltS1, double restfrq, double restwav, char ctypeS2[9], double *crvalS2, double *cdeltS2, struct wcserr **err) { static const char *function = "spctrne"; // 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. char stype1[5], stype2[5]; strncpy(stype1, ctypeS1, 4); strncpy(stype2, ctypeS2, 4); stype1[4] = stype2[4] = '\0'; if ((strstr("VRAD VOPT ZOPT VELO BETA", stype1) != 0x0) == (strstr("VRAD VOPT ZOPT VELO BETA", stype2) != 0x0)) { restwav = 1.0; } } 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. 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. if (xtype1 == 'w') { strcpy(ctypeS2+5, "GRI"); } else if (xtype1 == 'a') { strcpy(ctypeS2+5, "GRA"); } else { ctypeS2[5] = xtype1; ctypeS2[6] = '2'; } } 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? if (xtype2 != xtype1) { return wcserr_set(WCSERR_SET(SPCERR_BAD_SPEC_PARAMS), "Incompatible X-types '%c' and '%c'", xtype1, xtype2); } if (ctypeS2[7] == '?') { if (ptype2 == xtype2) { strcpy(ctypeS2+4, " "); } else { ctypeS2[7] = ptype2; } } *cdeltS2 = dS2dX * dXdS1 * cdeltS1; return 0; } //---------------------------------------------------------------------------- int spcaips( const char ctypeA[9], int velref, char ctype[9], char specsys[9]) { const char *frames[] = {"LSRK", "BARYCENT", "TOPOCENT", "LSRD", "GEOCENTR", "SOURCE", "GALACTOC"}; // 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? 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. char *fcode; if (*(fcode = ctype+4)) { if (strcmp(fcode, "-LSR") == 0) { strcpy(specsys, "LSRK"); } else if (strcmp(fcode, "-HEL") == 0) { strcpy(specsys, "BARYCENT"); } else if (strcmp(fcode, "-OBS") == 0) { strcpy(specsys, "TOPOCENT"); } else { // Not a recognized AIPS spectral type. return SPCERR_NO_CHANGE; } *fcode = '\0'; status = 0; } // VELREF takes precedence if present. int ivf = velref%256; if (0 < ivf && ivf <= 7) { strcpy(specsys, frames[ivf-1]); status = 0; } else if (ivf) { status = SPCERR_BAD_SPEC_PARAMS; } if (strcmp(ctype, "VELO") == 0) { // Check that we found an AIPS-convention Doppler frame. if (*specsys) { // 'VELO' in AIPS means radio or optical depending on VELREF. ivf = velref/256; if (ivf == 0) { strcpy(ctype, "VOPT"); } else if (ivf == 1) { strcpy(ctype, "VRAD"); } else { status = SPCERR_BAD_SPEC_PARAMS; } } } 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). strcpy(ctype, "VOPT-F2W"); if (status < 0) status = 0; } } return status; } astropy-astropy-201cddb/cextern/wcslib/C/spc.h000066400000000000000000001223751507226315300214640ustar00rootroot00000000000000/*============================================================================ WCSLIB 8.4 - an implementation of the FITS WCS standard. Copyright (C) 1995-2024, 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/people/Mark.Calabretta $Id: spc.h,v 8.4 2024/10/28 13:56:16 mcalabre Exp $ *============================================================================= * * WCSLIB 8.4 - 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 * --------------------------- * 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, 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 * needs to be set up by spcset() but it need not be called explicitly - refer * to the explanation of spcprm::flag. * * spcx2s() and spcs2x() implement the WCS spectral coordinate transformations. * In fact, they are high level driver routines for the lower level spectral * coordinate transformation routines described in spx.h. * * A number of routines are provided to aid in analysing or synthesising sets * of FITS spectral axis keywords: * * - spctype() checks a spectral CTYPEia keyword for validity and returns * information derived from it. * * - Spectral keyword analysis routine spcspxe() computes the values of the * X-type spectral variables for the S-type variables supplied. * * - Spectral keyword synthesis routine, spcxpse(), computes the S-type * variables for the X-types supplied. * * - Given a set of spectral keywords, a translation routine, spctrne(), * produces the corresponding set for the specified spectral CTYPEia. * * - spcaips() translates AIPS-convention spectral CTYPEia and VELREF * keyvalues. * * Spectral variable types - S, P, and X: * -------------------------------------- * A few words of explanation are necessary regarding spectral variable types * in FITS. * * Every FITS spectral axis has three associated spectral variables: * * S-type: the spectral variable in which coordinates are to be * 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 * * W (Wavelength in vacuo): * - 'WAVE': wavelength * - 'VOPT': optical velocity * - 'ZOPT': redshift * * A (wavelength in Air): * - 'AWAV': wavelength in air * * 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 * they provide a first-order approximation to the S-type variable at * the reference point. * * Note that 'AFRQ', angular frequency, is additional to the variables * defined in WCS Paper III. * * P-type: the basic spectral variable (F, W, A, or V) with which the * S-type variable is associated (see list above). * * For non-grism axes, the P-type is encoded as the eighth character of * CTYPEia. * * X-type: the basic spectral variable (F, W, A, or V) for which the * spectral axis is linear, grisms excluded (see below). * * For non-grism axes, the X-type is encoded as the sixth character of * CTYPEia. * * Grisms: Grism axes have normal S-, and P-types but the axis is linear, * not in any spectral variable, but in a special "grism parameter". * The X-type spectral variable is either W or A for grisms in vacuo or * air respectively, but is encoded as 'w' or 'a' to indicate that an * additional transformation is required to convert to or from the * grism parameter. The spectral algorithm code for grisms also has a * special encoding in CTYPEia, either 'GRI' (in vacuo) or 'GRA' (in air). * * In the algorithm chain, the non-linear transformation occurs between the * X-type and the P-type variables; the transformation between P-type and * S-type variables is always linear. * * When the P-type and X-type variables are the same, the spectral axis is * linear in the S-type variable and the second four characters of CTYPEia * are blank. This can never happen for grism axes. * * As an example, correlating radio spectrometers always produce spectra that * are regularly gridded in frequency; a redshift scale on such a spectrum is * non-linear. The required value of CTYPEia would be 'ZOPT-F2W', where the * desired S-type is 'ZOPT' (redshift), the P-type is necessarily 'W' * (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 * floating point exceptions. In particular, negative frequencies and * wavelengths are allowed, as are velocities greater than the speed of * light. The same is true for the spectral parameters - rest frequency and * wavelength. * * Accuracy: * --------- * No warranty is given for the accuracy of these routines (refer to the * copyright notice); intending users must satisfy for themselves their * adequacy for the intended purpose. However, closure effectively to within * double precision rounding error was demonstrated by test routine tspc.c * which accompanies this software. * * * spcini() - Default constructor for the spcprm struct * ---------------------------------------------------- * 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. A memory leak may otherwise * result. * * Given and returned: * spc struct spcprm* * Spectral transformation parameters. * * Function return value: * int Status return value: * 0: Success. * 1: Null spcprm pointer passed. * * * spcfree() - Destructor for the spcprm struct * -------------------------------------------- * spcfree() frees any memory that may have been allocated to store an error * message in the spcprm struct. * * Given: * spc struct spcprm* * Spectral transformation parameters. * * Function return value: * int Status return value: * 0: Success. * 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 * intended for diagnostic purposes. * * Given: * spc const struct spcprm* * Spectral transformation parameters. * * Function return value: * int Status return value: * 0: Success. * 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 * it. * * Note that this routine need not be called directly; it will be invoked by * 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. * * Function return value: * int Status return value: * 0: Success. * 1: Null spcprm pointer passed. * 2: Invalid spectral parameters. * * For returns > 1, a detailed error message is set in * spcprm::err if enabled, see wcserr_enable(). * * * spcx2s() - Transform to spectral coordinates * -------------------------------------------- * spcx2s() transforms intermediate world coordinates to spectral coordinates. * * Given and returned: * spc struct spcprm* * Spectral transformation parameters. * * Given: * nx int Vector length. * * sx int Vector stride. * * sspec int Vector stride. * * x const double[] * Intermediate world coordinates, in SI units. * * Returned: * spec double[] Spectral coordinates, in SI units. * * stat int[] Status return value status for each vector element: * 0: Success. * 1: Invalid value of x. * * Function return value: * int Status return value: * 0: Success. * 1: Null spcprm pointer passed. * 2: Invalid spectral parameters. * 3: One or more of the x coordinates were invalid, * as indicated by the stat vector. * * For returns > 1, a detailed error message is set in * spcprm::err if enabled, see wcserr_enable(). * * * spcs2x() - Transform spectral coordinates * ----------------------------------------- * spcs2x() transforms spectral world coordinates to intermediate world * coordinates. * * Given and returned: * spc struct spcprm* * Spectral transformation parameters. * * Given: * nspec int Vector length. * * sspec int Vector stride. * * sx int Vector stride. * * spec const double[] * Spectral coordinates, in SI units. * * Returned: * x double[] Intermediate world coordinates, in SI units. * * stat int[] Status return value status for each vector element: * 0: Success. * 1: Invalid value of spec. * * Function return value: * int Status return value: * 0: Success. * 1: Null spcprm pointer passed. * 2: Invalid spectral parameters. * 4: One or more of the spec coordinates were * invalid, as indicated by the stat vector. * * For returns > 1, a detailed error message is set in * spcprm::err if enabled, see wcserr_enable(). * * * spctype() - Spectral CTYPEia keyword analysis * --------------------------------------------- * spctype() checks whether a CTYPEia keyvalue is a valid spectral axis type * and if so returns information derived from it relating to the associated S-, * P-, and X-type spectral variables (see explanation above). * * The return arguments are guaranteed not be modified if CTYPEia is not a * valid spectral type; zero-pointers may be specified for any that are not of * interest. * * A deprecated form of this function, spctyp(), lacks the wcserr** parameter. * * Given: * ctype const char[9] * The CTYPEia keyvalue, (eight characters with null * termination). * * Returned: * stype char[] The four-letter name of the S-type spectral variable * copied or translated from ctype. If a non-zero * pointer is given, the array must accomodate a null- * terminated string of length 5. * * scode char[] The three-letter spectral algorithm code copied or * translated from ctype. Logarithmic ('LOG') and * tabular ('TAB') codes are also recognized. If a * non-zero pointer is given, the array must accomodate a * null-terminated string of length 4. * * sname char[] Descriptive name of the S-type spectral variable. * If a non-zero pointer is given, the array must * accomodate a null-terminated string of length 22. * * units char[] SI units of the S-type spectral variable. If a * non-zero pointer is given, the array must accomodate a * null-terminated string of length 8. * * ptype char* Character code for the P-type spectral variable * derived from ctype, one of 'F', 'W', 'A', or 'V'. * * xtype char* Character code for the X-type spectral variable * derived from ctype, one of 'F', 'W', 'A', or 'V'. * Also, 'w' and 'a' are synonymous to 'W' and 'A' for * grisms in vacuo and air respectively. Set to 'L' or * 'T' for logarithmic ('LOG') and tabular ('TAB') axes. * * restreq int* Multivalued flag that indicates whether rest * frequency or wavelength is required to compute * spectral variables for this CTYPEia: * 0: Not required. * 1: Required for the conversion between S- and * P-types (e.g. 'ZOPT-F2W'). * 2: Required for the conversion between P- and * X-types (e.g. 'BETA-W2V'). * 3: Required for the conversion between S- and * P-types, and between P- and X-types, but not * between S- and X-types (this applies only for * 'VRAD-V2F', 'VOPT-V2W', and 'ZOPT-V2W'). * Thus the rest frequency or wavelength is required for * spectral coordinate computations (i.e. between S- and * X-types) only if restreq%3 != 0. * * err struct wcserr ** * 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: * 0: Success. * 2: Invalid spectral parameters (not a spectral * CTYPEia). * * * spcspxe() - Spectral keyword analysis * ------------------------------------ * spcspxe() analyses the CTYPEia and CRVALia FITS spectral axis keyword values * and returns information about the associated X-type spectral variable. * * A deprecated form of this function, spcspx(), lacks the wcserr** parameter. * * Given: * ctypeS const char[9] * Spectral axis type, i.e. the CTYPEia keyvalue, (eight * characters with null termination). For non-grism * axes, the character code for the P-type spectral * variable in the algorithm code (i.e. the eighth * character of CTYPEia) may be set to '?' (it will not * be reset). * * crvalS double Value of the S-type spectral variable at the reference * point, i.e. the CRVALia keyvalue, SI units. * * restfrq, * restwav double Rest frequency [Hz] and rest wavelength in vacuo [m], * only one of which need be given, the other should be * set to zero. * * Returned: * ptype char* Character code for the P-type spectral variable * derived from ctypeS, one of 'F', 'W', 'A', or 'V'. * * xtype char* Character code for the X-type spectral variable * derived from ctypeS, one of 'F', 'W', 'A', or 'V'. * Also, 'w' and 'a' are synonymous to 'W' and 'A' for * grisms in vacuo and air respectively; crvalX and dXdS * (see below) will conform to these. * * restreq int* Multivalued flag that indicates whether rest frequency * or wavelength is required to compute spectral * variables for this CTYPEia, as for spctype(). * * crvalX double* Value of the X-type spectral variable at the reference * point, SI units. * * dXdS double* The derivative, dX/dS, evaluated at the reference * point, SI units. Multiply the CDELTia keyvalue by * this to get the pixel spacing in the X-type spectral * coordinate. * * err struct wcserr ** * 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: * 0: Success. * 2: Invalid spectral parameters. * * * spcxpse() - Spectral keyword synthesis * ------------------------------------- * spcxpse(), for the spectral axis type specified and the value provided for * the X-type spectral variable at the reference point, deduces the value of * the FITS spectral axis keyword CRVALia and also the derivative dS/dX which * may be used to compute CDELTia. See above for an explanation of the S-, * P-, and X-type spectral variables. * * A deprecated form of this function, spcxps(), lacks the wcserr** parameter. * * Given: * ctypeS const char[9] * The required spectral axis type, i.e. the CTYPEia * keyvalue, (eight characters with null termination). * For non-grism axes, the character code for the P-type * spectral variable in the algorithm code (i.e. the * eighth character of CTYPEia) may be set to '?' (it * will not be reset). * * crvalX double Value of the X-type spectral variable at the reference * point (N.B. NOT the CRVALia keyvalue), SI units. * * restfrq, * restwav double Rest frequency [Hz] and rest wavelength in vacuo [m], * only one of which need be given, the other should be * set to zero. * * Returned: * ptype char* Character code for the P-type spectral variable * derived from ctypeS, one of 'F', 'W', 'A', or 'V'. * * xtype char* Character code for the X-type spectral variable * derived from ctypeS, one of 'F', 'W', 'A', or 'V'. * Also, 'w' and 'a' are synonymous to 'W' and 'A' for * grisms; crvalX and cdeltX must conform to these. * * restreq int* Multivalued flag that indicates whether rest frequency * or wavelength is required to compute spectral * variables for this CTYPEia, as for spctype(). * * crvalS double* Value of the S-type spectral variable at the reference * point (i.e. the appropriate CRVALia keyvalue), SI * units. * * dSdX double* The derivative, dS/dX, evaluated at the reference * point, SI units. Multiply this by the pixel spacing * in the X-type spectral coordinate to get the CDELTia * keyvalue. * * err struct wcserr ** * 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: * 0: Success. * 2: Invalid spectral parameters. * * * spctrne() - Spectral keyword translation * --------------------------------------- * spctrne() translates a set of FITS spectral axis keywords into the * corresponding set for the specified spectral axis type. For example, a * 'FREQ' axis may be translated into 'ZOPT-F2W' and vice versa. * * A deprecated form of this function, spctrn(), lacks the wcserr** parameter. * * Given: * ctypeS1 const char[9] * Spectral axis type, i.e. the CTYPEia keyvalue, (eight * characters with null termination). For non-grism * axes, the character code for the P-type spectral * variable in the algorithm code (i.e. the eighth * character of CTYPEia) may be set to '?' (it will not * be reset). * * crvalS1 double Value of the S-type spectral variable at the reference * point, i.e. the CRVALia keyvalue, SI units. * * cdeltS1 double Increment of the S-type spectral variable at the * reference point, SI units. * * restfrq, * restwav double Rest frequency [Hz] and rest wavelength in vacuo [m], * only one of which need be given, the other should be * set to zero. Neither are required if the translation * is between wave-characteristic types, or between * velocity-characteristic types. E.g., required for * 'FREQ' -> 'ZOPT-F2W', but not required for * 'VELO-F2V' -> 'ZOPT-F2W'. * * Given and returned: * ctypeS2 char[9] Required spectral axis type (eight characters with * null termination). The first four characters are * required to be given and are never modified. The * remaining four, the algorithm code, are completely * determined by, and must be consistent with, ctypeS1 * and the first four characters of ctypeS2. A non-zero * status value will be returned if they are inconsistent * (see below). However, if the final three characters * are specified as "???", or if just the eighth * character is specified as '?', the correct algorithm * code will be substituted (applies for grism axes as * well as non-grism). * * Returned: * crvalS2 double* Value of the new S-type spectral variable at the * reference point, i.e. the new CRVALia keyvalue, SI * units. * * cdeltS2 double* Increment of the new S-type spectral variable at the * reference point, i.e. the new CDELTia keyvalue, SI * units. * * err struct wcserr ** * 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: * 0: Success. * 2: Invalid spectral parameters. * * A status value of 2 will be returned if restfrq or * restwav are not specified when required, or if ctypeS1 * or ctypeS2 are self-inconsistent, or have different * spectral X-type variables. * * * spcaips() - Translate AIPS-convention spectral keywords * ------------------------------------------------------- * spcaips() translates AIPS-convention spectral CTYPEia and VELREF keyvalues. * * Given: * ctypeA const char[9] * CTYPEia keyvalue possibly containing an * AIPS-convention spectral code (eight characters, need * not be null-terminated). * * velref int AIPS-convention VELREF code. It has the following * integer values: * 1: LSR kinematic, originally described simply as * "LSR" without distinction between the kinematic * and dynamic definitions. * 2: Barycentric, originally described as "HEL" * meaning heliocentric. * 3: Topocentric, originally described as "OBS" * meaning geocentric but widely interpreted as * topocentric. * AIPS++ extensions to VELREF are also recognized: * 4: LSR dynamic. * 5: Geocentric. * 6: Source rest frame. * 7: Galactocentric. * * For an AIPS 'VELO' axis, a radio convention velocity * (VRAD) is denoted by adding 256 to VELREF, otherwise * an optical velocity (VOPT) is indicated (this is not * applicable to 'FREQ' or 'FELO' axes). Setting velref * to 0 or 256 chooses between optical and radio velocity * without specifying a Doppler frame, provided that a * frame is encoded in ctypeA. If not, i.e. for * ctypeA = 'VELO', ctype will be returned as 'VELO'. * * VELREF takes precedence over CTYPEia in defining the * Doppler frame, e.g. * = 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 * blanks in ctypeA will be replaced with nulls). * * specsys char[9] Doppler reference frame indicated by VELREF or else * by CTYPEia with value corresponding to the SPECSYS * keyvalue in the FITS WCS standard. May be returned * blank if neither specifies a Doppler frame, e.g. * ctypeA = 'FELO' and velref%256 == 0. * * Function return value: * int Status return value: * -1: No translation required (not an error). * 0: Success. * 2: Invalid value of VELREF. * * * spcprm struct - Spectral transformation parameters * -------------------------------------------------- * The spcprm struct contains information required to transform spectral * 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 while others are for * internal use only. * * int flag * (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, * - spcprm::crval, * - spcprm::restfrq, * - spcprm::restwav, * - spcprm::pv[]. * * This signals the initialization routine, spcset(), to recompute the * returned members of the spcprm struct. spcset() will reset flag to * indicate that this has been done. * * char type[8] * (Given) Four-letter spectral variable type, e.g "ZOPT" for * CTYPEia = 'ZOPT-F2W'. (Declared as char[8] for alignment reasons.) * * char code[4] * (Given) Three-letter spectral algorithm code, e.g "F2W" for * CTYPEia = 'ZOPT-F2W'. * * double crval * (Given) Reference value (CRVALia), SI units. * * double restfrq * (Given) The rest frequency [Hz], and ... * * double restwav * (Given) ... the rest wavelength in vacuo [m], only one of which need be * given, the other should be set to zero. Neither are required if the * X and S spectral variables are both wave-characteristic, or both * velocity-characteristic, types. * * double pv[7] * (Given) Grism parameters for 'GRI' and 'GRA' algorithm codes: * - 0: G, grating ruling density. * - 1: m, interference order. * - 2: alpha, angle of incidence [deg]. * - 3: n_r, refractive index at the reference wavelength, lambda_r. * - 4: n'_r, dn/dlambda at the reference wavelength, lambda_r (/m). * - 5: epsilon, grating tilt angle [deg]. * - 6: theta, detector tilt angle [deg]. * * The remaining members of the spcprm struct are maintained by spcset() and * must not be modified elsewhere: * * double w[6] * (Returned) Intermediate values: * - 0: Rest frequency or wavelength (SI). * - 1: The value of the X-type spectral variable at the reference point * (SI units). * - 2: dX/dS at the reference point (SI units). * The remainder are grism intermediates. * * int isGrism * (Returned) Grism coordinates? * - 0: no, * - 1: in vacuum, * - 2: in air. * * int padding1 * (An unused variable inserted for alignment purposes only.) * * struct wcserr *err * (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) * (For internal use only) The first and ... * int (*spxP2S)(SPX_ARGS) * (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) * (For internal use only) The first and ... * int (*spxP2X)(SPX_ARGS) * (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 * ------------------------------------------------------------------ * Error messages to match the status value returned from each function. * *===========================================================================*/ #ifndef WCSLIB_SPC #define WCSLIB_SPC #include "spx.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. }; 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 messaging, if enabled. //-------------------------------------------------------------------------- struct wcserr *err; //-------------------------------------------------------------------------- // 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. }; // Size of the spcprm struct in int units, used by the Fortran wrappers. #define SPCLEN (sizeof(struct spcprm)/sizeof(int)) 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, const double x[], double spec[], int stat[]); int spcs2x(struct spcprm *spc, int nspec, int sspec, int sx, const double spec[], double x[], int stat[]); 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[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[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[9], double crvalS1, double cdeltS1, double restfrq, double restwav, char ctypeS2[9], double *crvalS2, double *cdeltS2, struct wcserr **err); int spcaips(const char ctypeA[9], int velref, char ctype[9], char specsys[9]); // 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[9], char stype[], char scode[], char sname[], char units[], char *ptype, char *xtype, int *restreq); 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 astropy-astropy-201cddb/cextern/wcslib/C/sph.c000066400000000000000000000262041507226315300214560ustar00rootroot00000000000000/*============================================================================ WCSLIB 8.4 - an implementation of the FITS WCS standard. Copyright (C) 1995-2024, 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/people/Mark.Calabretta $Id: sph.c,v 8.4 2024/10/28 13:56:16 mcalabre Exp $ *===========================================================================*/ #include #include "wcstrig.h" #include "sph.h" #define copysign(X, Y) ((Y) < 0.0 ? -fabs(X) : fabs(X)) #define tol 1.0e-5 //---------------------------------------------------------------------------- int sphx2s( const double eul[5], int nphi, int ntheta, int spt, int sll, const double phi[], const double theta[], double lng[], double lat[]) { int mphi, mtheta; if (ntheta > 0) { mphi = nphi; mtheta = ntheta; } else { mphi = 1; mtheta = 1; ntheta = nphi; } // Check for special-case rotations. if (eul[4] == 0.0) { if (eul[1] == 0.0) { // 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. if (eul[0] >= 0.0) { if (*lngp < 0.0) *lngp += 360.0; } else { if (*lngp > 0.0) *lngp -= 360.0; } if (*lngp > 360.0) { *lngp -= 360.0; } else if (*lngp < -360.0) { *lngp += 360.0; } lngp += sll; latp += sll; } } } else { // 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. if (eul[0] >= 0.0) { if (*lngp < 0.0) *lngp += 360.0; } else { if (*lngp > 0.0) *lngp -= 360.0; } if (*lngp > 360.0) { *lngp -= 360.0; } else if (*lngp < -360.0) { *lngp += 360.0; } lngp += sll; latp += sll; } } } return 0; } // 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]; double *lngp = lng + rowoff; for (int itheta = 0; itheta < mtheta; itheta++) { *lngp = dphi; lngp += rowlen; } } // 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); 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. double x = sinthe4 - costhe3*cosphi; if (fabs(x) < tol) { // Rearrange formula to reduce roundoff errors. x = -cosd(*thetap + eul[1]) + costhe3*(1.0 - cosphi); } double dlng; double y = -costhe*sinphi; if (x != 0.0 || y != 0.0) { dlng = atan2d(y, x); } else { // Change of origin of longitude. if (eul[1] < 90.0) { dlng = dphi + 180.0; } else { dlng = -dphi; } } *lngp = eul[0] + dlng; // Normalize the celestial longitude. if (eul[0] >= 0.0) { if (*lngp < 0.0) *lngp += 360.0; } else { if (*lngp > 0.0) *lngp -= 360.0; } if (*lngp > 360.0) { *lngp -= 360.0; } else if (*lngp < -360.0) { *lngp += 360.0; } // 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 { double z = sinthe3 + costhe4*cosphi; if (fabs(z) > 0.99) { // Use an alternative formula for greater accuracy. *latp = copysign(acosd(sqrt(x*x+y*y)), z); } else { *latp = asind(z); } } } } return 0; } //---------------------------------------------------------------------------- int sphs2x( const double eul[5], int nlng, int nlat, int sll, int spt, const double lng[], const double lat[], double phi[], double theta[]) { int mlng, mlat; if (nlat > 0) { mlng = nlng; mlat = nlat; } else { mlng = 1; mlat = 1; nlat = nlng; } // Check for special-case rotations. if (eul[4] == 0.0) { if (eul[1] == 0.0) { // 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. if (*phip > 180.0) { *phip -= 360.0; } else if (*phip < -180.0) { *phip += 360.0; } phip += spt; thetap += spt; } } } else { // 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. if (*phip > 180.0) { *phip -= 360.0; } else if (*phip < -180.0) { *phip += 360.0; } phip += spt; thetap += spt; } } } return 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]; double *phip = phi + rowoff; for (int ilat = 0; ilat < mlat; ilat++) { *phip = dlng; phip += rowlen; } } // 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); 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. double x = sinlat4 - coslat3*coslng; if (fabs(x) < tol) { // Rearrange formula to reduce roundoff errors. x = -cosd(*latp+eul[1]) + coslat3*(1.0 - coslng); } double dphi; double y = -coslat*sinlng; if (x != 0.0 || y != 0.0) { dphi = atan2d(y, x); } else { // Change of origin of longitude. if (eul[1] < 90.0) { dphi = dlng - 180.0; } else { dphi = -dlng; } } *phip = fmod(eul[2] + dphi, 360.0); // Normalize the native longitude. if (*phip > 180.0) { *phip -= 360.0; } else if (*phip < -180.0) { *phip += 360.0; } // 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 { double z = sinlat3 + coslat4*coslng; if (fabs(z) > 0.99) { // Use an alternative formula for greater accuracy. *thetap = copysign(acosd(sqrt(x*x+y*y)), z); } else { *thetap = asind(z); } } } } return 0; } //---------------------------------------------------------------------------- int sphdpa( int nfield, double lng0, double lat0, const double lng[], const double lat[], double dist[], double pa[]) { // Set the Euler angles for the coordinate transformation. double eul[5]; 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. sphs2x(eul, nfield, 0, 1, 1, lng, lat, pa, dist); 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. pa[i] = -pa[i]; if (pa[i] < -180.0) pa[i] += 360.0; } return 0; } //---------------------------------------------------------------------------- int sphpad( int nfield, double lng0, double lat0, const double dist[], const double pa[], double lng[], double lat[]) { // Set the Euler angles for the coordinate transformation. double eul[5]; eul[0] = lng0; eul[1] = 90.0 - lat0; eul[2] = 0.0; eul[3] = cosd(eul[1]); eul[4] = sind(eul[1]); 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. lng[i] = -pa[i]; } // Transform field points to the old system. sphx2s(eul, nfield, 0, 1, 1, lng, lat, lng, lat); return 0; } astropy-astropy-201cddb/cextern/wcslib/C/sph.h000066400000000000000000000231241507226315300214610ustar00rootroot00000000000000/*============================================================================ WCSLIB 8.4 - an implementation of the FITS WCS standard. Copyright (C) 1995-2024, 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/people/Mark.Calabretta $Id: sph.h,v 8.4 2024/10/28 13:56:16 mcalabre Exp $ *============================================================================= * * WCSLIB 8.4 - 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 * --------------------------- * 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() * does the complementary operation - computes the coordinates of points offset * by the given angular distances and position angles from a given point on the * sky. * * * sphx2s() - Rotation in the pixel-to-world direction * --------------------------------------------------- * sphx2s() transforms native coordinates of a projection to celestial * coordinates. * * Given: * eul const double[5] * Euler angles for the transformation: * 0: Celestial longitude of the native pole [deg]. * 1: Celestial colatitude of the native pole, or * native colatitude of the celestial pole [deg]. * 2: Native longitude of the celestial pole [deg]. * 3: cos(eul[1]) * 4: sin(eul[1]) * * nphi, * ntheta int Vector lengths. * * spt,sxy int Vector strides. * * phi,theta const double[] * Longitude and latitude in the native coordinate * system of the projection [deg]. * * Returned: * lng,lat double[] Celestial longitude and latitude [deg]. These may * refer to the same storage as phi and theta * respectively. * * Function return value: * int Status return value: * 0: Success. * * * sphs2x() - Rotation in the world-to-pixel direction * --------------------------------------------------- * sphs2x() transforms celestial coordinates to the native coordinates of a * projection. * * Given: * eul const double[5] * Euler angles for the transformation: * 0: Celestial longitude of the native pole [deg]. * 1: Celestial colatitude of the native pole, or * native colatitude of the celestial pole [deg]. * 2: Native longitude of the celestial pole [deg]. * 3: cos(eul[1]) * 4: sin(eul[1]) * * nlng,nlat int Vector lengths. * * sll,spt int Vector strides. * * lng,lat const double[] * Celestial longitude and latitude [deg]. * * Returned: * phi,theta double[] Longitude and latitude in the native coordinate system * of the projection [deg]. These may refer to the same * storage as lng and lat respectively. * * Function return value: * int Status return value: * 0: Success. * * * sphdpa() - Compute angular distance and position angle * ------------------------------------------------------ * sphdpa() computes the angular distance and generalized position angle (see * notes) from a "reference" point to a number of "field" points on the sphere. * The points must be specified consistently in any spherical coordinate * system. * * sphdpa() is complementary to sphpad(). * * Given: * nfield int The number of field points. * * lng0,lat0 double Spherical coordinates of the reference point [deg]. * * lng,lat const double[] * Spherical coordinates of the field points [deg]. * * Returned: * dist,pa double[] Angular distances and position angles [deg]. These * may refer to the same storage as lng and lat * respectively. * * Function return value: * int Status return value: * 0: Success. * * Notes: * 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 * --------------------------------------------------------- * sphpad() computes the coordinates of a set of points that are offset by the * specified angular distances and position angles from a given "reference" * point on the sky. The distances and position angles must be specified * consistently in any spherical coordinate system. * * sphpad() is complementary to sphdpa(). * * Given: * nfield int The number of field points. * * lng0,lat0 double Spherical coordinates of the reference point [deg]. * * dist,pa const double[] * Angular distances and position angles [deg]. * * Returned: * lng,lat double[] Spherical coordinates of the field points [deg]. * These may refer to the same storage as dist and pa * respectively. * * Function return value: * int Status return value: * 0: Success. * * Notes: * 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. * *===========================================================================*/ #ifndef WCSLIB_SPH #define WCSLIB_SPH #ifdef __cplusplus extern "C" { #endif int sphx2s(const double eul[5], int nphi, int ntheta, int spt, int sxy, const double phi[], const double theta[], double lng[], double lat[]); int sphs2x(const double eul[5], int nlng, int nlat, int sll , int spt, const double lng[], const double lat[], double phi[], double theta[]); int sphdpa(int nfield, double lng0, double lat0, const double lng[], const double lat[], double dist[], double pa[]); int sphpad(int nfield, double lng0, double lat0, const double dist[], const double pa[], double lng[], double lat[]); #ifdef __cplusplus } #endif #endif // WCSLIB_SPH astropy-astropy-201cddb/cextern/wcslib/C/spx.c000066400000000000000000000571321507226315300215020ustar00rootroot00000000000000/*============================================================================ WCSLIB 8.4 - an implementation of the FITS WCS standard. Copyright (C) 1995-2024, 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/people/Mark.Calabretta $Id: spx.c,v 8.4 2024/10/28 13:56:16 mcalabre Exp $ *===========================================================================*/ #include #include #include #include "wcserr.h" #include "wcsmath.h" #include "spx.h" // Map status return value to message. const char *spx_errmsg[] = { "Success", "Null spxprm pointer passed", "Invalid spectral parameters", "Invalid spectral variable", "One or more of the inspec coordinates were invalid"}; // Convenience macro for invoking wcserr_set(). #define SPX_ERRMSG(status) WCSERR_SET(status), spx_errmsg[status] #define C 2.99792458e8 #define h 6.6260755e-34 /*============================================================================ * Spectral cross conversions; given one spectral coordinate it computes all * the others, plus the required derivatives of each with respect to the * others. *===========================================================================*/ int specx( const char *type, double spec, double restfrq, double restwav, struct spxprm *spx) { static const char *function = "specx"; if (spx == 0x0) return SPXERR_NULL_POINTER; struct wcserr **err = &(spx->err); int haverest = 1; if (restfrq == 0.0) { if (restwav == 0.0) { // No line rest frequency supplied. haverest = 0; // Temporarily set a dummy value for conversions. spx->restwav = 1.0; } else { spx->restwav = restwav; } spx->restfrq = C/spx->restwav; } else { spx->restfrq = restfrq; spx->restwav = C/restfrq; } spx->err = 0x0; // Convert to frequency. spx->wavetype = 0; spx->velotype = 0; if (strcmp(type, "FREQ") == 0) { if (spec == 0.0) { return wcserr_set(WCSERR_SET(SPXERR_BAD_SPEC_VAR), "Invalid spectral variable: frequency == 0"); } spx->freq = spec; spx->wavetype = 1; } else if (strcmp(type, "AFRQ") == 0) { if (spec == 0.0) { return wcserr_set(WCSERR_SET(SPXERR_BAD_SPEC_VAR), "Invalid spectral variable: frequency == 0"); } spx->freq = spec/(2.0*PI); spx->wavetype = 1; } else if (strcmp(type, "ENER") == 0) { if (spec == 0.0) { return wcserr_set(WCSERR_SET(SPXERR_BAD_SPEC_VAR), "Invalid spectral variable: frequency == 0"); } spx->freq = spec/h; spx->wavetype = 1; } else if (strcmp(type, "WAVN") == 0) { if (spec == 0.0) { return wcserr_set(WCSERR_SET(SPXERR_BAD_SPEC_VAR), "Invalid spectral variable: frequency == 0"); } spx->freq = spec*C; spx->wavetype = 1; } else if (strcmp(type, "VRAD") == 0) { spx->freq = spx->restfrq*(1.0 - spec/C); spx->velotype = 1; } else if (strcmp(type, "WAVE") == 0) { if (spec == 0.0) { return wcserr_set(WCSERR_SET(SPXERR_BAD_SPEC_VAR), "Invalid spectral variable: frequency == 0"); } spx->freq = C/spec; spx->wavetype = 1; } else if (strcmp(type, "VOPT") == 0) { double s = 1.0 + spec/C; if (s == 0.0) { return wcserr_set(WCSERR_SET(SPXERR_BAD_SPEC_VAR), "Invalid spectral variable"); } spx->freq = spx->restfrq/s; spx->velotype = 1; } else if (strcmp(type, "ZOPT") == 0) { double s = 1.0 + spec; if (s == 0.0) { return wcserr_set(WCSERR_SET(SPXERR_BAD_SPEC_VAR), "Invalid spectral variable"); } spx->freq = spx->restfrq/s; spx->velotype = 1; } else if (strcmp(type, "AWAV") == 0) { if (spec == 0.0) { 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); n += 294.981e8 / (1.46e14 - s); n += 1.000064328; spx->freq = C/(spec*n); spx->wavetype = 1; } else if (strcmp(type, "VELO") == 0) { double beta = spec/C; if (fabs(beta) == 1.0) { return wcserr_set(WCSERR_SET(SPXERR_BAD_SPEC_VAR), "Invalid spectral variable"); } spx->freq = spx->restfrq*(1.0 - beta)/sqrt(1.0 - beta*beta); spx->velotype = 1; } else if (strcmp(type, "BETA") == 0) { if (fabs(spec) == 1.0) { return wcserr_set(WCSERR_SET(SPXERR_BAD_SPEC_VAR), "Invalid spectral variable"); } spx->freq = spx->restfrq*(1.0 - spec)/sqrt(1.0 - spec*spec); spx->velotype = 1; } else { // Unrecognized type. return wcserr_set(WCSERR_SET(SPXERR_BAD_SPEC_PARAMS), "Unrecognized spectral type '%s'", type); } // Convert frequency to the other spectral types. double n, s, t, u; n = 1.0; for (int k = 0; k < 4; k++) { s = n*spx->freq/C; s *= s; t = 0.41e14 - s; u = 1.46e14 - s; n = 1.000064328 + (2.554e8/t + 294.981e8/u); } double dwaveawav = n - 2.0*s*(2.554e8/(t*t) + 294.981e8/(u*u)); s = spx->freq/spx->restfrq; spx->ener = spx->freq*h; spx->afrq = spx->freq*(2.0*PI); spx->wavn = spx->freq/C; spx->vrad = C*(1.0 - s); spx->wave = C/spx->freq; spx->awav = spx->wave/n; spx->vopt = C*(1.0/s - 1.0); spx->zopt = spx->vopt/C; spx->velo = C*(1.0 - s*s)/(1.0 + s*s); spx->beta = spx->velo/C; // 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; spx->dfreqener = 1.0/h; spx->denerfreq = 1.0/spx->dfreqener; spx->dfreqwavn = C; spx->dwavnfreq = 1.0/spx->dfreqwavn; spx->dfreqvrad = -spx->restfrq/C; spx->dvradfreq = 1.0/spx->dfreqvrad; spx->dfreqwave = -spx->freq/spx->wave; spx->dwavefreq = 1.0/spx->dfreqwave; spx->dfreqawav = spx->dfreqwave * dwaveawav; spx->dawavfreq = 1.0/spx->dfreqawav; spx->dfreqvelo = -gamma*spx->restfrq/(C + spx->velo); spx->dvelofreq = 1.0/spx->dfreqvelo; spx->dwavevopt = spx->restwav/C; spx->dvoptwave = 1.0/spx->dwavevopt; spx->dwavezopt = spx->restwav; spx->dzoptwave = 1.0/spx->dwavezopt; spx->dwaveawav = dwaveawav; spx->dawavwave = 1.0/spx->dwaveawav; spx->dwavevelo = gamma*spx->restwav/(C - spx->velo); spx->dvelowave = 1.0/spx->dwavevelo; spx->dawavvelo = spx->dwavevelo/dwaveawav; spx->dveloawav = 1.0/spx->dawavvelo; spx->dvelobeta = C; spx->dbetavelo = 1.0/spx->dvelobeta; // Reset values if no line rest frequency was supplied. if (haverest) { spx->wavetype = 1; spx->velotype = 1; } else { spx->restfrq = 0.0; spx->restwav = 0.0; if (!spx->wavetype) { // Don't have wave characteristic types. spx->freq = 0.0; spx->afrq = 0.0; spx->ener = 0.0; spx->wavn = 0.0; spx->wave = 0.0; spx->awav = 0.0; spx->dfreqwave = 0.0; spx->dwavefreq = 0.0; spx->dfreqawav = 0.0; spx->dawavfreq = 0.0; spx->dwaveawav = 0.0; spx->dawavwave = 0.0; } else { // Don't have velocity types. spx->vrad = 0.0; spx->vopt = 0.0; spx->zopt = 0.0; spx->velo = 0.0; spx->beta = 0.0; } spx->dfreqvrad = 0.0; spx->dvradfreq = 0.0; spx->dfreqvelo = 0.0; spx->dvelofreq = 0.0; spx->dwavevopt = 0.0; spx->dvoptwave = 0.0; spx->dwavezopt = 0.0; spx->dzoptwave = 0.0; spx->dwavevelo = 0.0; spx->dvelowave = 0.0; spx->dawavvelo = 0.0; spx->dveloawav = 0.0; } 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( 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; for (int ifreq = 0; ifreq < nfreq; ifreq++) { if (*freqp != 0.0) { *wavep = C/(*freqp); *(statp++) = 0; } else { *(statp++) = 1; status = SPXERR_BAD_INSPEC_COORD; } freqp += sfreq; wavep += swave; } return status; } //---------------------------------------------------------------------------- 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; for (int iwave = 0; iwave < nwave; iwave++) { if (*wavep != 0.0) { *freqp = C/(*wavep); *(statp++) = 0; } else { *(statp++) = 1; status = SPXERR_BAD_INSPEC_COORD; } wavep += swave; freqp += sfreq; } return status; } /*============================================================================ * Conversions between frequency and air wavelength. *===========================================================================*/ 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; } return waveawav(dummy, nfreq, sawav, sawav, awav, awav, 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; } return wavefreq(dummy, nawav, sfreq, sfreq, freq, freq, stat); } /*============================================================================ * Conversions between frequency and relativistic velocity. *===========================================================================*/ int freqvelo( double restfrq, int nfreq, int sfreq, int svelo, const double freq[], double velo[], int stat[]) { 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; freqp += sfreq; velop += svelo; } return 0; } //---------------------------------------------------------------------------- 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; for (int ivelo = 0; ivelo < nvelo; ivelo++) { double s = C + *velop; if (s != 0.0) { *freqp = restfrq*sqrt((C - *velop)/s); *(statp++) = 0; } else { *(statp++) = 1; status = SPXERR_BAD_INSPEC_COORD; } velop += svelo; freqp += sfreq; } return status; } /*============================================================================ * Conversions between vacuum wavelength and air wavelength. *===========================================================================*/ 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; for (int iwave = 0; iwave < nwave; iwave++) { if (*wavep != 0.0) { 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); n += 1.000064328; } *awavp = (*wavep)/n; *(statp++) = 0; } else { *(statp++) = 1; status = SPXERR_BAD_INSPEC_COORD; } wavep += swave; awavp += sawav; } return status; } //---------------------------------------------------------------------------- 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; 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); n += 294.981e8 / (1.46e14 - s); n += 1.000064328; *wavep = (*awavp)*n; *(statp++) = 0; } else { *(statp++) = 1; status = SPXERR_BAD_INSPEC_COORD; } awavp += sawav; wavep += swave; } return status; } /*============================================================================ * Conversions between vacuum wavelength and relativistic velocity. *===========================================================================*/ int wavevelo( double restwav, int nwave, int swave, int svelo, const double wave[], double velo[], int stat[]) { 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; wavep += swave; velop += svelo; } return 0; } //---------------------------------------------------------------------------- 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; for (int ivelo = 0; ivelo < nvelo; ivelo++) { double s = C - *velop; if (s != 0.0) { *wavep = restwav*sqrt((C + *velop)/s); *(statp++) = 0; } else { *(statp++) = 1; status = SPXERR_BAD_INSPEC_COORD; } velop += svelo; wavep += swave; } return status; } /*============================================================================ * Conversions between air wavelength and relativistic velocity. *===========================================================================*/ 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; } return wavevelo(dummy, nawav, svelo, svelo, velo, velo, 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; } return waveawav(dummy, nvelo, sawav, sawav, awav, awav, stat); } /*============================================================================ * Conversions between frequency and angular frequency. *===========================================================================*/ int freqafrq( double dummy, int nfreq, int sfreq, int safrq, const double freq[], double afrq[], int stat[]) { // 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; freqp += sfreq; afrqp += safrq; } return 0; } //---------------------------------------------------------------------------- int afrqfreq( double dummy, int nafrq, int safrq, int sfreq, const double afrq[], double freq[], int stat[]) { // 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; afrqp += safrq; freqp += sfreq; } return 0; } /*============================================================================ * Conversions between frequency and energy. *===========================================================================*/ int freqener( double dummy, int nfreq, int sfreq, int sener, const double freq[], double ener[], int stat[]) { // 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; freqp += sfreq; enerp += sener; } return 0; } //---------------------------------------------------------------------------- int enerfreq( double dummy, int nener, int sener, int sfreq, const double ener[], double freq[], int stat[]) { // 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; enerp += sener; freqp += sfreq; } return 0; } /*============================================================================ * Conversions between frequency and wave number. *===========================================================================*/ int freqwavn( double dummy, int nfreq, int sfreq, int swavn, const double freq[], double wavn[], int stat[]) { // 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; freqp += sfreq; wavnp += swavn; } return 0; } //---------------------------------------------------------------------------- int wavnfreq( double dummy, int nwavn, int swavn, int sfreq, const double wavn[], double freq[], int stat[]) { // 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; wavnp += swavn; freqp += sfreq; } return 0; } /*============================================================================ * Conversions between frequency and radio velocity. *===========================================================================*/ int freqvrad( double restfrq, int nfreq, int sfreq, int svrad, const double freq[], double vrad[], int stat[]) { if (restfrq == 0.0) { return SPXERR_BAD_SPEC_PARAMS; } double r = C/restfrq; const double *freqp = freq; double *vradp = vrad; int *statp = stat; for (int ifreq = 0; ifreq < nfreq; ifreq++) { *vradp = r*(restfrq - *freqp); *(statp++) = 0; freqp += sfreq; vradp += svrad; } return 0; } //---------------------------------------------------------------------------- int vradfreq( double restfrq, int nvrad, int svrad, int sfreq, const double vrad[], double freq[], int stat[]) { double r = restfrq/C; 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; freqp += sfreq; } return 0; } /*============================================================================ * Conversions between vacuum wavelength and optical velocity. *===========================================================================*/ int wavevopt( double restwav, int nwave, int swave, int svopt, const double wave[], double vopt[], int stat[]) { if (restwav == 0.0) { return SPXERR_BAD_SPEC_PARAMS; } double r = C/restwav; 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; voptp += svopt; } return 0; } //---------------------------------------------------------------------------- int voptwave( double restwav, int nvopt, int svopt, int swave, const double vopt[], double wave[], int stat[]) { double r = restwav/C; 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; wavep += swave; } return 0; } /*============================================================================ * Conversions between vacuum wavelength and redshift. *===========================================================================*/ int wavezopt( double restwav, int nwave, int swave, int szopt, const double wave[], double zopt[], int stat[]) { if (restwav == 0.0) { return SPXERR_BAD_SPEC_PARAMS; } double r = 1.0/restwav; 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; zoptp += szopt; } return 0; } //---------------------------------------------------------------------------- int zoptwave( double restwav, int nzopt, int szopt, int swave, const double zopt[], double wave[], int stat[]) { 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; wavep += swave; } return 0; } /*============================================================================ * Conversions between relativistic velocity and beta (= v/c). *===========================================================================*/ int velobeta( double dummy, int nvelo, int svelo, int sbeta, const double velo[], double beta[], int stat[]) { // 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; velop += svelo; betap += sbeta; } return 0; } //---------------------------------------------------------------------------- int betavelo( double dummy, int nbeta, int sbeta, int svelo, const double beta[], double velo[], int stat[]) { // 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; betap += sbeta; velop += svelo; } return 0; } astropy-astropy-201cddb/cextern/wcslib/C/spx.h000066400000000000000000000527061507226315300215110ustar00rootroot00000000000000/*============================================================================ WCSLIB 8.4 - an implementation of the FITS WCS standard. Copyright (C) 1995-2024, 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/people/Mark.Calabretta $Id: spx.h,v 8.4 2024/10/28 13:56:16 mcalabre Exp $ *============================================================================= * * WCSLIB 8.4 - 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. * * Non-linear: * - freqwave() frequency -> vacuum wavelength * - wavefreq() vacuum wavelength -> frequency * * - freqawav() frequency -> air wavelength * - awavfreq() air wavelength -> frequency * * - freqvelo() frequency -> relativistic velocity * - velofreq() relativistic velocity -> frequency * * - waveawav() vacuum wavelength -> air wavelength * - awavwave() air wavelength -> vacuum wavelength * * - wavevelo() vacuum wavelength -> relativistic velocity * - velowave() relativistic velocity -> vacuum wavelength * * - awavvelo() air wavelength -> relativistic velocity * - veloawav() relativistic velocity -> air wavelength * * Linear: * - freqafrq() frequency -> angular frequency * - afrqfreq() angular frequency -> frequency * * - freqener() frequency -> energy * - enerfreq() energy -> frequency * * - freqwavn() frequency -> wave number * - wavnfreq() wave number -> frequency * * - freqvrad() frequency -> radio velocity * - vradfreq() radio velocity -> frequency * * - wavevopt() vacuum wavelength -> optical velocity * - voptwave() optical velocity -> vacuum wavelength * * - wavezopt() vacuum wavelength -> redshift * - zoptwave() redshift -> vacuum wavelength * * - velobeta() relativistic velocity -> beta (= v/c) * - betavelo() beta (= v/c) -> relativistic velocity * * These are the workhorse routines, to be used for fast transformations. * 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 * in floating point exceptions. In particular, negative frequencies and * wavelengths are allowed, as are velocities greater than the speed of * light. The same is true for the spectral parameters - rest frequency and * wavelength. * * Accuracy: * --------- * No warranty is given for the accuracy of these routines (refer to the * copyright notice); intending users must satisfy for themselves their * adequacy for the intended purpose. However, closure effectively to within * double precision rounding error was demonstrated by test routine tspec.c * which accompanies this software. * * * specx() - Spectral cross conversions (scalar) * --------------------------------------------- * Given one spectral variable specx() computes all the others, plus the * required derivatives of each with respect to the others. * * Given: * type const char* * The type of spectral variable given by spec, FREQ, * AFRQ, ENER, WAVN, VRAD, WAVE, VOPT, ZOPT, AWAV, VELO, * or BETA (case sensitive). * * spec double The spectral variable given, in SI units. * * restfrq, * restwav double Rest frequency [Hz] or rest wavelength in vacuo [m], * only one of which need be given. The other should be * set to zero. If both are zero, only a subset of the * spectral variables can be computed, the remainder are * set to zero. Specifically, given one of FREQ, AFRQ, * ENER, WAVN, WAVE, or AWAV the others can be computed * without knowledge of the rest frequency. Likewise, * VRAD, VOPT, ZOPT, VELO, and BETA. * * Given and returned: * specs struct spxprm* * Data structure containing all spectral variables and * their derivatives, in SI units. * * Function return value: * int Status return value: * 0: Success. * 1: Null spxprm pointer passed. * 2: Invalid spectral parameters. * 3: Invalid spectral variable. * * For returns > 1, a detailed error message is set in * spxprm::err if enabled, see wcserr_enable(). * * freqafrq(), afrqfreq(), freqener(), enerfreq(), freqwavn(), wavnfreq(), * freqwave(), wavefreq(), freqawav(), awavfreq(), waveawav(), awavwave(), * velobeta(), and betavelo() implement vector conversions between wave-like * or velocity-like spectral types (i.e. conversions that do not need the rest * 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. * * Given: * param double Ignored. * * nspec int Vector length. * * instep, * outstep int Vector strides. * * inspec const double[] * Input spectral variables, in SI units. * * Returned: * outspec double[] Output spectral variables, in SI units. * * stat int[] Status return value for each vector element: * 0: Success. * 1: Invalid value of inspec. * * Function return value: * int Status return value: * 0: Success. * 2: Invalid spectral parameters. * 4: One or more of the inspec coordinates were * invalid, as indicated by the stat vector. * * * freqvelo(), velofreq(), freqvrad(), and vradfreq() implement vector * conversions between frequency and velocity spectral types. They all have * the same API. * * * freqvelo() - Convert frequency to relativistic velocity (vector) * ---------------------------------------------------------------- * freqvelo() converts frequency to relativistic velocity. * * Given: * param double Rest frequency [Hz]. * * nspec int Vector length. * * instep, * outstep int Vector strides. * * inspec const double[] * Input spectral variables, in SI units. * * Returned: * outspec double[] Output spectral variables, in SI units. * * stat int[] Status return value for each vector element: * 0: Success. * 1: Invalid value of inspec. * * Function return value: * int Status return value: * 0: Success. * 2: Invalid spectral parameters. * 4: One or more of the inspec coordinates were * invalid, as indicated by the stat vector. * * * wavevelo(), velowave(), awavvelo(), veloawav(), wavevopt(), voptwave(), * wavezopt(), and zoptwave() implement vector conversions between wavelength * and velocity spectral types. They all have the same API. * * * wavevelo() - Conversions between wavelength and velocity types (vector) * ----------------------------------------------------------------------- * wavevelo() converts vacuum wavelength to relativistic velocity. * * Given: * param double Rest wavelength in vacuo [m]. * * nspec int Vector length. * * instep, * outstep int Vector strides. * * inspec const double[] * Input spectral variables, in SI units. * * Returned: * outspec double[] Output spectral variables, in SI units. * * stat int[] Status return value for each vector element: * 0: Success. * 1: Invalid value of inspec. * * Function return value: * int Status return value: * 0: Success. * 2: Invalid spectral parameters. * 4: One or more of the inspec coordinates were * invalid, as indicated by the stat vector. * * * spxprm struct - Spectral variables and their derivatives * -------------------------------------------------------- * The spxprm struct contains the value of all spectral variables and their * derivatives. It is used solely by specx() which constructs it from * information provided via its function arguments. * * This struct should be considered read-only, no members need ever be set nor * should ever be modified by the user. * * double restfrq * (Returned) Rest frequency [Hz]. * * double restwav * (Returned) Rest wavelength [m]. * * int wavetype * (Returned) True if wave types have been computed, and ... * * int velotype * (Returned) ... true if velocity types have been computed; types are * defined below. * * If one or other of spxprm::restfrq and spxprm::restwav is given * (non-zero) then all spectral variables may be computed. If both are * given, restfrq is used. If restfrq and restwav are both zero, only wave * characteristic xor velocity type spectral variables may be computed * depending on the variable given. These flags indicate what is * available. * * double freq * (Returned) Frequency [Hz] (wavetype). * * double afrq * (Returned) Angular frequency [rad/s] (wavetype). * * double ener * (Returned) Photon energy [J] (wavetype). * * double wavn * (Returned) Wave number [/m] (wavetype). * * double vrad * (Returned) Radio velocity [m/s] (velotype). * * double wave * (Returned) Vacuum wavelength [m] (wavetype). * * double vopt * (Returned) Optical velocity [m/s] (velotype). * * double zopt * (Returned) Redshift [dimensionless] (velotype). * * double awav * (Returned) Air wavelength [m] (wavetype). * * double velo * (Returned) Relativistic velocity [m/s] (velotype). * * double beta * (Returned) Relativistic beta [dimensionless] (velotype). * * double dfreqafrq * (Returned) Derivative of frequency with respect to angular frequency * [/rad] (constant, = 1 / 2*pi), and ... * double dafrqfreq * (Returned) ... vice versa [rad] (constant, = 2*pi, always available). * * double dfreqener * (Returned) Derivative of frequency with respect to photon energy * [/J/s] (constant, = 1/h), and ... * double denerfreq * (Returned) ... vice versa [Js] (constant, = h, Planck's constant, * always available). * * double dfreqwavn * (Returned) Derivative of frequency with respect to wave number [m/s] * (constant, = c, the speed of light in vacuo), and ... * double dwavnfreq * (Returned) ... vice versa [s/m] (constant, = 1/c, always available). * * double dfreqvrad * (Returned) Derivative of frequency with respect to radio velocity [/m], * and ... * double dvradfreq * (Returned) ... vice versa [m] (wavetype && velotype). * * double dfreqwave * (Returned) Derivative of frequency with respect to vacuum wavelength * [/m/s], and ... * double dwavefreq * (Returned) ... vice versa [m s] (wavetype). * * double dfreqawav * (Returned) Derivative of frequency with respect to air wavelength, * [/m/s], and ... * double dawavfreq * (Returned) ... vice versa [m s] (wavetype). * * double dfreqvelo * (Returned) Derivative of frequency with respect to relativistic * velocity [/m], and ... * double dvelofreq * (Returned) ... vice versa [m] (wavetype && velotype). * * double dwavevopt * (Returned) Derivative of vacuum wavelength with respect to optical * velocity [s], and ... * double dvoptwave * (Returned) ... vice versa [/s] (wavetype && velotype). * * double dwavezopt * (Returned) Derivative of vacuum wavelength with respect to redshift [m], * and ... * double dzoptwave * (Returned) ... vice versa [/m] (wavetype && velotype). * * double dwaveawav * (Returned) Derivative of vacuum wavelength with respect to air * wavelength [dimensionless], and ... * double dawavwave * (Returned) ... vice versa [dimensionless] (wavetype). * * double dwavevelo * (Returned) Derivative of vacuum wavelength with respect to relativistic * velocity [s], and ... * double dvelowave * (Returned) ... vice versa [/s] (wavetype && velotype). * * double dawavvelo * (Returned) Derivative of air wavelength with respect to relativistic * velocity [s], and ... * double dveloawav * (Returned) ... vice versa [/s] (wavetype && velotype). * * double dvelobeta * (Returned) Derivative of relativistic velocity with respect to * 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 * 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. * *===========================================================================*/ #ifndef WCSLIB_SPEC #define WCSLIB_SPEC #ifdef __cplusplus extern "C" { #endif 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. }; 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 //-------------------------------------------------------------------------- struct wcserr *err; // Private //-------------------------------------------------------------------------- void *padding; // (Dummy inserted for alignment purposes.) }; // 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. #define SPX_ARGS double param, int nspec, int instep, int outstep, \ const double inspec[], double outspec[], int stat[] int freqafrq(SPX_ARGS); int afrqfreq(SPX_ARGS); int freqener(SPX_ARGS); int enerfreq(SPX_ARGS); int freqwavn(SPX_ARGS); int wavnfreq(SPX_ARGS); int freqwave(SPX_ARGS); int wavefreq(SPX_ARGS); int freqawav(SPX_ARGS); int awavfreq(SPX_ARGS); int waveawav(SPX_ARGS); int awavwave(SPX_ARGS); int velobeta(SPX_ARGS); int betavelo(SPX_ARGS); int freqvelo(SPX_ARGS); int velofreq(SPX_ARGS); int freqvrad(SPX_ARGS); int vradfreq(SPX_ARGS); int wavevelo(SPX_ARGS); int velowave(SPX_ARGS); int awavvelo(SPX_ARGS); int veloawav(SPX_ARGS); int wavevopt(SPX_ARGS); int voptwave(SPX_ARGS); int wavezopt(SPX_ARGS); int zoptwave(SPX_ARGS); #ifdef __cplusplus } #endif #endif // WCSLIB_SPEC astropy-astropy-201cddb/cextern/wcslib/C/tab.c000066400000000000000000001277741507226315300214500ustar00rootroot00000000000000/*============================================================================ WCSLIB 8.4 - an implementation of the FITS WCS standard. Copyright (C) 1995-2024, 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/people/Mark.Calabretta $Id: tab.c,v 8.4 2024/10/28 13:56:16 mcalabre Exp $ *===========================================================================*/ #include #include #include #include #include "wcserr.h" #include "wcsmath.h" #include "wcsprintf.h" #include "wcsutil.h" #include "tab.h" // Map status return value to message. const char *tab_errmsg[] = { "Success", "Null tabprm pointer passed", "Memory allocation failed", "Invalid tabular parameters", "One or more of the x coordinates were invalid", "One or more of the world coordinates were invalid"}; 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"; if (tab == 0x0) return TABERR_NULL_POINTER; // Initialize error message handling. if (tab->flag == -1) { tab->err = 0x0; } struct wcserr **err = &(tab->err); wcserr_clear(err); if (M <= 0) { return wcserr_set(WCSERR_SET(TABERR_BAD_PARAMS), "M must be positive, got %d", M); } // Determine the total number of elements in the coordinate array. int N; if (K) { N = 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 " "non-negative, got %d", K[m]); } N *= K[m]; } } else { // Axis lengths as yet unknown. N = 0; } // 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; tab->m_K = 0x0; tab->m_map = 0x0; tab->m_crval = 0x0; tab->m_index = 0x0; tab->m_indxs = 0x0; tab->m_coord = 0x0; } else { // 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; } // Allocate memory for arrays if required. if (alloc || tab->K == 0x0 || tab->map == 0x0 || tab->crval == 0x0 || tab->index == 0x0 || tab->coord == 0x0) { // Was sufficient allocated previously? if (tab->m_flag == TABSET && (tab->m_M < M || tab->m_N < N)) { // No, free it. tabfree(tab); } if (alloc || tab->K == 0x0) { if (tab->m_K) { // In case the caller fiddled with it. tab->K = tab->m_K; } else { if (!(tab->K = calloc(M, sizeof(int)))) { return wcserr_set(TAB_ERRMSG(TABERR_MEMORY)); } tab->m_flag = TABSET; tab->m_M = M; tab->m_K = tab->K; } } if (alloc || tab->map == 0x0) { if (tab->m_map) { // In case the caller fiddled with it. tab->map = tab->m_map; } else { if (!(tab->map = calloc(M, sizeof(int)))) { return wcserr_set(TAB_ERRMSG(TABERR_MEMORY)); } tab->m_flag = TABSET; tab->m_M = M; tab->m_map = tab->map; } } if (alloc || tab->crval == 0x0) { if (tab->m_crval) { // In case the caller fiddled with it. tab->crval = tab->m_crval; } else { if (!(tab->crval = calloc(M, sizeof(double)))) { return wcserr_set(TAB_ERRMSG(TABERR_MEMORY)); } tab->m_flag = TABSET; tab->m_M = M; tab->m_crval = tab->crval; } } if (alloc || tab->index == 0x0) { if (tab->m_index) { // In case the caller fiddled with it. tab->index = tab->m_index; } else { if (!(tab->index = calloc(M, sizeof(double *)))) { return wcserr_set(TAB_ERRMSG(TABERR_MEMORY)); } tab->m_flag = TABSET; tab->m_M = M; tab->m_N = N; tab->m_index = tab->index; if (!(tab->m_indxs = calloc(M, sizeof(double *)))) { return wcserr_set(TAB_ERRMSG(TABERR_MEMORY)); } // Recall that calloc() initializes these pointers to zero. if (K) { 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)); } tab->m_indxs[m] = tab->index[m]; } } } } } if (alloc || tab->coord == 0x0) { if (tab->m_coord) { // In case the caller fiddled with it. tab->coord = tab->m_coord; } else if (N) { if (!(tab->coord = calloc(N, sizeof(double)))) { return wcserr_set(TAB_ERRMSG(TABERR_MEMORY)); } tab->m_flag = TABSET; tab->m_M = M; tab->m_N = N; tab->m_coord = tab->coord; } } } tab->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])) { // Table indexes are 1-relative. for (int k = 1; k <= K[m]; k++) { *(dp++) = k; } } } else { tab->K[m] = 0; } } // 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"; if (tab == 0x0) return TABERR_NULL_POINTER; struct wcserr **err = &(tab->err); if (tab->M == 0 || tab->K == 0x0) { // Should have been set by this time. return wcserr_set(WCSERR_SET(TABERR_MEMORY), "Null pointers in tabprm struct"); } 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 " "non-negative, got %d", M); } N *= tab->K[m]; } if (tab->m_M == 0) { tab->m_M = M; } else if (tab->m_M < M) { // Only possible if the user changed M. return wcserr_set(WCSERR_SET(TABERR_MEMORY), "tabprm struct inconsistent"); } if (tab->m_N == 0) { tab->m_N = N; } else if (tab->m_N < N) { // Only possible if the user changed K[]. return wcserr_set(WCSERR_SET(TABERR_MEMORY), "tabprm struct inconsistent"); } if (tab->m_K == 0x0) { if ((tab->m_K = tab->K)) { tab->m_flag = TABSET; } } if (tab->m_map == 0x0) { if ((tab->m_map = tab->map)) { tab->m_flag = TABSET; } } if (tab->m_crval == 0x0) { if ((tab->m_crval = tab->crval)) { tab->m_flag = TABSET; } } if (tab->m_index == 0x0) { if ((tab->m_index = tab->index)) { tab->m_flag = TABSET; } } 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; } } } if (tab->m_coord == 0x0 || tab->m_coord == (double *)0x1) { if ((tab->m_coord = tab->coord)) { tab->m_flag = TABSET; } } tab->flag = 0; return 0; } //---------------------------------------------------------------------------- int tabcpy(int alloc, const struct tabprm *tabsrc, struct tabprm *tabdst) { static const char *function = "tabcpy"; int status; if (tabsrc == 0x0) return TABERR_NULL_POINTER; if (tabdst == 0x0) return TABERR_NULL_POINTER; struct wcserr **err = &(tabdst->err); int M = tabsrc->M; if (M <= 0) { return wcserr_set(WCSERR_SET(TABERR_BAD_PARAMS), "M must be positive, got %d", M); } if ((status = tabini(alloc, M, tabsrc->K, tabdst))) { return status; } 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]; } double *dstp, *srcp; for (int m = 0; m < M; m++) { if ((srcp = tabsrc->index[m])) { dstp = tabdst->index[m]; 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 (int n = 0; n < N; n++) { *(dstp++) = *(srcp++); } return 0; } //---------------------------------------------------------------------------- int tabcmp( int dummy, double tol, const struct tabprm *tab1, const struct tabprm *tab2, int *equal) { // 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 (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(). if (tab->m_flag == TABSET) { if (tab->K == tab->m_K) tab->K = 0x0; if (tab->map == tab->m_map) tab->map = 0x0; if (tab->crval == tab->m_crval) tab->crval = 0x0; if (tab->index == tab->m_index) tab->index = 0x0; if (tab->coord == tab->m_coord) tab->coord = 0x0; if (tab->m_K) free(tab->m_K); if (tab->m_map) free(tab->m_map); if (tab->m_crval) free(tab->m_crval); if (tab->m_index) { for (int m = 0; m < tab->m_M; m++) { if (tab->m_indxs[m]) free(tab->m_indxs[m]); } free(tab->m_index); free(tab->m_indxs); } if (tab->m_coord) free(tab->m_coord); } // Free memory allocated by tabset(). if (tab->sense) free(tab->sense); if (tab->p0) free(tab->p0); if (tab->delta) free(tab->delta); if (tab->extrema) free(tab->extrema); } tab->m_flag = 0; tab->m_M = 0; tab->m_N = 0; tab->m_K = 0x0; tab->m_map = 0x0; tab->m_crval = 0x0; tab->m_index = 0x0; tab->m_indxs = 0x0; tab->m_coord = 0x0; tab->sense = 0x0; tab->p0 = 0x0; tab->delta = 0x0; tab->extrema = 0x0; tab->set_M = 0; 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]; double *dp; if (tab == 0x0) return TABERR_NULL_POINTER; 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. WCSPRINTF_PTR(" K: ", tab->K, "\n"); wcsprintf(" "); for (int m = 0; m < tab->M; m++) { wcsprintf("%6d", tab->K[m]); } wcsprintf("\n"); // ...map vector. WCSPRINTF_PTR(" map: ", tab->map, "\n"); wcsprintf(" "); for (int m = 0; m < tab->M; m++) { wcsprintf("%6d", tab->map[m]); } wcsprintf("\n"); // ...reference index value. WCSPRINTF_PTR(" crval: ", tab->crval, "\n"); wcsprintf(" "); for (int m = 0; m < tab->M; m++) { wcsprintf(" %#- 11.5g", tab->crval[m]); } wcsprintf("\n"); // ...index vectors. WCSPRINTF_PTR(" index: ", tab->index, "\n"); for (int m = 0; m < tab->M; m++) { wcsprintf(" index[%d]: ", m); WCSPRINTF_PTR("", tab->index[m], ""); if (tab->index[m]) { for (int k = 0; k < tab->K[m]; k++) { if (k%5 == 0) { wcsprintf("\n "); } wcsprintf(" %#- 11.5g", tab->index[m][k]); } } wcsprintf("\n"); } // ...coordinate array. WCSPRINTF_PTR(" coord: ", tab->coord, "\n"); dp = tab->coord; for (int n = 0; n < tab->nc; n++) { // Array index. int j = n; cp = text; 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 (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 (int m = 0; m < tab->M; m++) { wcsprintf("%6d", tab->sense[m]); } wcsprintf("\n"); } WCSPRINTF_PTR(" p0: ", tab->p0, "\n"); if (tab->p0) { wcsprintf(" "); for (int m = 0; m < tab->M; m++) { wcsprintf("%6d", tab->p0[m]); } wcsprintf("\n"); } WCSPRINTF_PTR(" delta: ", tab->delta, "\n"); if (tab->delta) { wcsprintf(" "); 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 (int n = 0; n < tab->nc/tab->K[0]; n++) { // Array index. int j = n; cp = text; *cp = '\0'; 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 (int m = 0; m < 2*tab->M; m++) { if (m == tab->M) wcsprintf("-> "); wcsprintf(" %#- 11.5g", *(dp++)); } wcsprintf("\n"); } // Error handling. WCSPRINTF_PTR(" err: ", tab->err, "\n"); if (tab->err) { wcserr_prt(tab->err, " "); } // 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); WCSPRINTF_PTR(" m_K: ", tab->m_K, ""); if (tab->m_K == tab->K) wcsprintf(" (= K)"); wcsprintf("\n"); WCSPRINTF_PTR(" m_map: ", tab->m_map, ""); if (tab->m_map == tab->map) wcsprintf(" (= map)"); wcsprintf("\n"); WCSPRINTF_PTR(" m_crval: ", tab->m_crval, ""); if (tab->m_crval == tab->crval) wcsprintf(" (= crval)"); wcsprintf("\n"); WCSPRINTF_PTR(" m_index: ", tab->m_index, ""); if (tab->m_index == tab->index) wcsprintf(" (= index)"); wcsprintf("\n"); 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); wcsprintf("\n"); } WCSPRINTF_PTR(" m_coord: ", tab->m_coord, ""); if (tab->m_coord == tab->coord) wcsprintf(" (= coord)"); wcsprintf("\n"); 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"; if (tab == 0x0) return TABERR_NULL_POINTER; if (tab->flag == -TABSET) return 0; struct wcserr **err = &(tab->err); // 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. if (!tab->K) { return wcserr_set(WCSERR_SET(TABERR_MEMORY), "Null pointers in tabprm struct"); } tab->nc = 1; 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. tab->nc *= tab->K[m]; } // Check that the map vector is sensible. if (!tab->map) { return wcserr_set(WCSERR_SET(TABERR_MEMORY), "Null pointers in tabprm struct"); } 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 " "non-negative, got %d", i); } } // 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 (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; } } if (tab->m_coord == (double *)0x1 && (tab->m_coord = tab->coord)) { tab->m_flag = TABSET; } // 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); 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)); } if (!(tab->p0 = calloc(M, sizeof(int)))) { free(tab->sense); return wcserr_set(TAB_ERRMSG(TABERR_MEMORY)); } if (!(tab->delta = calloc(M, sizeof(double)))) { free(tab->sense); free(tab->p0); return wcserr_set(TAB_ERRMSG(TABERR_MEMORY)); } int ne = (tab->nc / tab->K[0]) * 2 * M; if (!(tab->extrema = calloc(ne, sizeof(double)))) { free(tab->sense); free(tab->p0); free(tab->delta); return wcserr_set(TAB_ERRMSG(TABERR_MEMORY)); } tab->set_M = M; } // 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. tab->sense[m] = 1; } else { for (int k = 0; k < *Km-1; k++) { switch (tab->sense[m]) { case 0: if (Psi[k] < Psi[k+1]) { // Monotonic increasing. tab->sense[m] = 1; } else if (Psi[k] > Psi[k+1]) { // Monotonic decreasing. tab->sense[m] = -1; } break; case 1: if (Psi[k] > Psi[k+1]) { // Should be monotonic increasing. free(tab->sense); free(tab->p0); free(tab->delta); free(tab->extrema); return wcserr_set(WCSERR_SET(TABERR_BAD_PARAMS), "Invalid tabular parameters: Index vectors are not " "monotonically increasing"); } break; case -1: if (Psi[k] < Psi[k+1]) { // Should be monotonic decreasing. free(tab->sense); free(tab->p0); free(tab->delta); free(tab->extrema); return wcserr_set(WCSERR_SET(TABERR_BAD_PARAMS), "Invalid tabular parameters: Index vectors are not " "monotonically decreasing"); } break; } } } if (tab->sense[m] == 0) { free(tab->sense); free(tab->p0); free(tab->delta); free(tab->extrema); return wcserr_set(WCSERR_SET(TABERR_BAD_PARAMS), "Invalid tabular parameters: Index vectors are not monotonic"); } } } // 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. double dPsi; double *Psi = tab->index[0]; if (Psi == 0x0) { dPsi = 1.0; } else { dPsi = Psi[1] - Psi[0]; } double dval = *dcrd; if (dPsi != 0.0) { dval -= 0.5 * (*(dcrd+M) - *dcrd)/dPsi; } *(dmax+m) = *(dmin+m) = dval; } else { *(dmax+m) = *(dmin+m) = *dcrd; } } dcrd -= M; 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. double dPsi; double *Psi = tab->index[0]; if (Psi == 0x0) { dPsi = 1.0; } else { dPsi = Psi[i] - Psi[i-1]; } double dval = *dcrd; if (dPsi != 0.0) { dval += 0.5 * (*dcrd - *(dcrd-M))/dPsi; } if (*(dmax+m) < dval) *(dmax+m) = dval; if (*(dmin+m) > dval) *(dmin+m) = dval; } } } dmin += 2*M; dmax += 2*M; } tab->flag = (tab->flag == 1) ? -TABSET : TABSET; return 0; } //---------------------------------------------------------------------------- int tabx2s( struct tabprm *tab, int ncoord, int nelem, const double x[], double world[], int stat[]) { static const char *function = "tabx2s"; int status; if (tab == 0x0) return TABERR_NULL_POINTER; struct wcserr **err = &(tab->err); // Initialize if required. if (abs(tab->flag) != TABSET) { if ((status = tabset(tab))) return status; } // This is used a lot. int M = tab->M; status = 0; 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. 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. Psi--; if (*Km == 1) { // Index vector is degenerate. if (Psi[1]-0.5 <= psi_m && psi_m <= Psi[1]+0.5) { upsilon = psi_m; } else { *statp = 1; status = wcserr_set(TAB_ERRMSG(TABERR_BAD_X)); goto next; } } else { // Interpolate in the indexing vector. int k; if (tab->sense[m] == 1) { // Monotonic increasing index values. if (psi_m < Psi[1]) { if (Psi[1] - 0.5*(Psi[2]-Psi[1]) <= psi_m) { // Allow minor extrapolation. k = 1; } else { // Index is out of range. *statp = 1; status = wcserr_set(TAB_ERRMSG(TABERR_BAD_X)); goto next; } } else if (Psi[*Km] < psi_m) { if (psi_m <= Psi[*Km] + 0.5*(Psi[*Km]-Psi[*Km-1])) { // Allow minor extrapolation. k = *Km - 1; } else { // Index is out of range. *statp = 1; status = wcserr_set(TAB_ERRMSG(TABERR_BAD_X)); goto next; } } else { for (k = 1; k < *Km; k++) { if (psi_m < Psi[k]) { continue; } if (Psi[k] == psi_m && psi_m < Psi[k+1]) { break; } if (Psi[k] < psi_m && psi_m <= Psi[k+1]) { break; } } } } else { // Monotonic decreasing index values. if (psi_m > Psi[1]) { if (Psi[1] + 0.5*(Psi[1]-Psi[2]) >= psi_m) { // Allow minor extrapolation. k = 1; } else { // Index is out of range. *statp = 1; status = wcserr_set(TAB_ERRMSG(TABERR_BAD_X)); goto next; } } else if (psi_m < Psi[*Km]) { if (Psi[*Km] - 0.5*(Psi[*Km-1]-Psi[*Km]) <= psi_m) { // Allow minor extrapolation. k = *Km - 1; } else { // Index is out of range. *statp = 1; status = wcserr_set(TAB_ERRMSG(TABERR_BAD_X)); goto next; } } else { for (k = 1; k < *Km; k++) { if (psi_m > Psi[k]) { continue; } if (Psi[k] == psi_m && psi_m > Psi[k+1]) { break; } if (Psi[k] > psi_m && psi_m >= Psi[k+1]) { break; } } } } upsilon = k + (psi_m - Psi[k]) / (Psi[k+1] - Psi[k]); } } if (upsilon < 0.5 || upsilon > *Km + 0.5) { // 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. 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 (int m = 0; m < M; m++) { int i = tab->map[m]; *(wp+i) = 0.0; } // 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)) { if (tab->K[m] > 1) offset++; wgt *= tab->delta[m]; } else { wgt *= 1.0 - tab->delta[m]; } } if (wgt == 0.0) continue; // 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; } if (wgt == 1.0) break; } *statp = 0; next: xp += nelem; wp += nelem; statp++; } 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, int ncoord, int nelem, const double world[], double x[], int stat[]) { static const char *function = "tabs2x"; int status; if (tab == 0x0) return TABERR_NULL_POINTER; struct wcserr **err = &(tab->err); // Initialize if required. if (abs(tab->flag) != TABSET) { if ((status = tabset(tab))) return status; } // This is used a lot. int M = tab->M; double **tabcoord = 0x0; int nv = 0; if (M > 1) { nv = 1 << M; tabcoord = calloc(nv, sizeof(double *)); } status = 0; 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? if (edge || tabrow(tab, wp)) { // No, skip it. ic += tab->K[0]; if (1 < M) { tab->p0[1]++; edge = tabedge(tab); } // Because ic will be incremented when the loop is reentered. ic--; continue; } } if (M == 1) { // 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] <= 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] = (w - tab->coord[ic]) / (tab->coord[ic+1] - tab->coord[ic]); break; } } } else { // Multi-dimensional tables are harder. if (!edge) { // 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++; } tabcoord[iv] = tab->coord + offset*M; } if (tabvox(tab, wp, 0, tabcoord, 0x0) == 0) { // Found a solution. break; } } // Next voxel. tab->p0[0]++; edge = tabedge(tab); } } if (ic == tab->nc) { // Coordinate not found; allow minor extrapolation. if (M == 1) { // 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; double delta = (w - *dcrd) / (*(dcrd+1) - *dcrd); if (i == 0) { if (-0.5 <= delta && delta <= 0.0) { tab->p0[0] = 0; tab->delta[0] = delta; ic = 0; break; } } else { if (1.0 <= delta && delta <= 1.5) { tab->p0[0] = tab->K[0] - 1; tab->delta[0] = delta - 1.0; ic = 0; } } } } } else { // Multi-dimensional tables. // >>> TBD <<< } } if (ic == tab->nc) { // Coordinate not found. *statp = 1; status = wcserr_set(TAB_ERRMSG(TABERR_BAD_WORLD)); } else { // 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. *statp = 1; status = wcserr_set(TAB_ERRMSG(TABERR_BAD_WORLD)); } else { // Do inverse lookup of the index vector. double *Psi = tab->index[m]; double psi_m; if (Psi == 0x0) { // Default indexing. psi_m = upsilon; } else { // Decrement Psi and use 1-relative C array indexing to match the // 1-relative FITS indexing. Psi--; if (*Km == 1) { // Degenerate index vector. psi_m = Psi[1]; } else { int k = (int)(upsilon); psi_m = Psi[k]; if (k < *Km) { psi_m += (upsilon - k) * (Psi[k+1] - Psi[k]); } } } xp[tab->map[m]] = psi_m - tab->crval[m]; } } *statp = 0; } wp += nelem; xp += nelem; statp++; } if (tabcoord) free(tabcoord); return status; } /*---------------------------------------------------------------------------- * 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 = 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; 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; } } return edge; } /*---------------------------------------------------------------------------- * Quick test to see whether the world coordinate indicated by wp could lie * somewhere along (or near) the row of the image indexed by tabprm::p0. * Return 0 if so, 1 otherwise. * * tabprm::p0 selects a particular row of the image, p0[0] being ignored (i.e. * treated as zero). Adjacent rows that delimit a row of "voxels" are formed * by incrementing elements other than p0[0] in all binary combinations. N.B. * these are not the same as the voxels (pixels) that are indexed by, and * centred on, integral pixel coordinates in FITS. * * To see why it is necessary to examine the adjacent rows, consider the 2-D * case where the first world coordinate element is constant along each row. * If the first element of wp has value 0.5, and its value in the row indexed * by p0 has value 0, and in the next row it has value 1, then it is clear that * the solution lies in neither row but somewhere between them. Thus both rows * will be involved in finding the solution. * * tabprm::extrema is the address of the first element of a 1-D array that * records the minimum and maximum value of each element of the coordinate * vector in each row of the coordinate array, treated as though it were * defined as * * double extrema[K_M]...[K_2][2][M] * * The minimum is recorded in the first element of the compressed K_1 * dimension, then the maximum. *---------------------------------------------------------------------------*/ int tabrow(struct tabprm* tab, const double *wp) { const double tol = 1e-10; 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. if (iv & (1 << m)) { if (tab->K[m] > 1) offset++; } } // The K_1 dimension has length 2 (see prologue). offset *= 2; // Select the minimum on even numbered iterations, else the maximum. if (iv & 1) offset++; // The last dimension has length M (see prologue). offset *= M; // 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 (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. if (fabs(*cp - w) < tol) { eq |= (1 << m); } else if (*cp < w) { lt |= (1 << m); } else if (*cp > w) { gt |= (1 << m); } } // Have all bits been switched on? if ((lt | eq) == nv-1 && (gt | eq) == nv-1) { // A solution could lie within this row of voxels. return 0; } } // No solution in this row. return 1; } /*---------------------------------------------------------------------------- * Does the world coordinate indicated by wp lie within the voxel indexed by * tabprm::p0? If so, do a binary chop of the interior of the voxel to find * it and return 0, with tabprm::delta set to the solution. Else return 1. * * As in tabrow(), a "voxel" is formed by incrementing the elements of * tabprm::p0 in all binary combinations. Note that these are not the same as * the voxels (pixels) that are indexed by, and centred on, integral pixel * coordinates in FITS. * * tabvox() calls itself recursively. When called from outside, level, being * the level of recursion, should be given as zero. tabcoord is an array * holding the addresses of the coordinates for each corner of the voxel. * vox is the address of a work array (vox2) used during recursive calls to * 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 32. ----------------------------------------------------------------------------*/ int tabvox( struct tabprm* tab, const double *wp, int level, double **tabcoord, unsigned int *vox) { const double tol = 1e-10; int M = tab->M; // The number of corners in a voxel. unsigned int nv = 1 << M; 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. 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; if (iv & (1 << m)) { tab->delta[m] += dv; } } // 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 { wgt *= 1.0 - tab->delta[m]; } } if (wgt == 0.0) continue; // 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. 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. if (fabs(coord[m] - w) < tol) { et |= (1 << m); } else if (coord[m] < w) { lt |= (1 << m); } else if (coord[m] > w) { gt |= (1 << m); } } if (et == nv-1) { // 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? if ((lt | eq) == nv-1 && (gt | eq) == nv-1) { // Yes it could, but does it? // Is it time to stop the recursion? if (level == 31) { // We have a solution, squeeze out the last bit of juice. dv /= 2.0; 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 (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. if (tabvox(tab, wp, level+1, tabcoord, vox2) == 0) { return 0; } } } // No solution in this sub-voxel. return 1; } astropy-astropy-201cddb/cextern/wcslib/C/tab.h000066400000000000000000000673231507226315300214460ustar00rootroot00000000000000/*============================================================================ WCSLIB 8.4 - an implementation of the FITS WCS standard. Copyright (C) 1995-2024, 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/people/Mark.Calabretta $Id: tab.h,v 8.4 2024/10/28 13:56:16 mcalabre Exp $ *============================================================================= * * WCSLIB 8.4 - 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 * --------------------------- * 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, 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 * needs to be set up by tabset() but it need not be called explicitly - refer * to the explanation of tabprm::flag. * * tabx2s() and tabs2x() implement the WCS tabular coordinate transformations. * * Accuracy: * --------- * No warranty is given for the accuracy of these routines (refer to the * copyright notice); intending users must satisfy for themselves their * adequacy for the intended purpose. However, closure effectively to within * double precision rounding error was demonstrated by test routine ttab.c * which accompanies this software. * * * tabini() - Default constructor for the tabprm struct * ---------------------------------------------------- * tabini() allocates memory for arrays in a tabprm struct and sets all members * of the struct to default values. * * PLEASE NOTE: every tabprm struct should be initialized by tabini(), possibly * repeatedly. On the first invokation, and only the first invokation, the * flag member of the tabprm struct must be set to -1 to initialize memory * management, regardless of whether tabini() will actually be used to allocate * memory. * * Given: * alloc int If true, allocate memory unconditionally for arrays in * the tabprm 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.) * * M int The number of tabular coordinate axes. * * K const int[] * 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. M and K[] are used to * determine the length of the various tabprm arrays and * therefore the amount of memory to allocate for them. * Their values are copied into the tabprm struct. * * It is permissible to set K (i.e. the address of the * array) to zero which has the same effect as setting * each element of K[] to zero. In this case no memory * will be allocated for the index vectors or coordinate * array in the tabprm struct. These together with the * K vector must be set separately before calling * tabset(). * * Given and returned: * tab struct tabprm* * Tabular transformation parameters. Note that, in * order to initialize memory management tabprm::flag * should be set to -1 when tab is initialized for the * first time (memory leaks may result if it had already * been initialized). * * Function return value: * int Status return value: * 0: Success. * 1: Null tabprm pointer passed. * 2: Memory allocation failed. * 3: Invalid tabular parameters. * * For returns > 1, a detailed error message is set in * tabprm::err if enabled, see wcserr_enable(). * * * tabmem() - Acquire tabular memory * --------------------------------- * tabmem() takes control of memory allocated by the user for arrays in the * tabprm struct. * * Given and returned: * tab struct tabprm* * Tabular transformation parameters. * * Function return value: * int Status return value: * 0: Success. * 1: Null tabprm pointer passed. * 2: Memory allocation failed. * * For returns > 1, a detailed error message is set in * tabprm::err if enabled, see wcserr_enable(). * * * tabcpy() - Copy routine for the tabprm struct * --------------------------------------------- * tabcpy() does a deep copy of one tabprm struct to another, using tabini() to * allocate memory for its arrays if required. Only the "information to be * provided" part of the struct is copied; a call to tabset() is required to * set up the remainder. * * Given: * alloc int If true, allocate memory unconditionally for arrays in * the tabprm 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.) * * tabsrc const struct tabprm* * Struct to copy from. * * Given and returned: * tabdst struct tabprm* * Struct to copy to. tabprm::flag should be set to -1 * if tabdst was not previously initialized (memory leaks * may result if it was previously initialized). * * Function return value: * int Status return value: * 0: Success. * 1: Null tabprm pointer passed. * 2: Memory allocation failed. * * For returns > 1, a detailed error message is set in * tabprm::err (associated with tabdst) if enabled, see * 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(). * tabini() records the memory it allocates and tabfree() will only attempt to * free this. * * PLEASE NOTE: tabfree() must not be invoked on a tabprm struct that was not * initialized by tabini(). * * Returned: * tab struct tabprm* * Coordinate transformation parameters. * * Function return value: * int Status return value: * 0: Success. * 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 * intended for diagnostic purposes. * * Given: * tab const struct tabprm* * Tabular transformation parameters. * * Function return value: * int Status return value: * 0: Success. * 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 * the struct according to information supplied within it. * * Note that this routine need not be called directly; it will be invoked by * 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. * * Function return value: * int Status return value: * 0: Success. * 1: Null tabprm pointer passed. * 3: Invalid tabular parameters. * * For returns > 1, a detailed error message is set in * tabprm::err if enabled, see wcserr_enable(). * * * tabx2s() - Pixel-to-world transformation * ---------------------------------------- * tabx2s() transforms intermediate world coordinates to world coordinates * using coordinate lookup. * * Given and returned: * tab struct tabprm* * Tabular transformation parameters. * * Given: * ncoord, * nelem int The number of coordinates, each of vector length * nelem. * * x const double[ncoord][nelem] * Array of intermediate world coordinates, SI units. * * Returned: * world double[ncoord][nelem] * Array of world coordinates, in SI units. * * stat int[ncoord] * Status return value status for each coordinate: * 0: Success. * 1: Invalid intermediate world coordinate. * * Function return value: * int Status return value: * 0: Success. * 1: Null tabprm pointer passed. * 3: Invalid tabular parameters. * 4: One or more of the x coordinates were invalid, * as indicated by the stat vector. * * For returns > 1, a detailed error message is set in * tabprm::err if enabled, see wcserr_enable(). * * * tabs2x() - World-to-pixel transformation * ---------------------------------------- * tabs2x() transforms world coordinates to intermediate world coordinates. * * Given and returned: * tab struct tabprm* * Tabular transformation parameters. * * Given: * ncoord, * nelem int The number of coordinates, each of vector length * nelem. * world const double[ncoord][nelem] * Array of world coordinates, in SI units. * * Returned: * x double[ncoord][nelem] * Array of intermediate world coordinates, SI units. * stat int[ncoord] * Status return value status for each vector element: * 0: Success. * 1: Invalid world coordinate. * * Function return value: * int Status return value: * 0: Success. * 1: Null tabprm pointer passed. * 3: Invalid tabular parameters. * 5: One or more of the world coordinates were * invalid, as indicated by the stat vector. * * For returns > 1, a detailed error message is set in * tabprm::err if enabled, see wcserr_enable(). * * * tabprm struct - Tabular transformation parameters * ------------------------------------------------- * The tabprm struct contains information required to transform tabular * 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 while others are for * internal use only. * * int flag * (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), * - tabprm::map, * - tabprm::crval, * - tabprm::index, * - tabprm::coord. * * This signals the initialization routine, tabset(), to recompute the * returned members of the tabprm struct. tabset() will reset flag to * indicate that this has been done. * * PLEASE NOTE: flag should be set to -1 when tabini() is called for the * first time for a particular tabprm struct in order to initialize memory * management. It must ONLY be used on the first initialization otherwise * memory leaks may result. * * int M * (Given or returned) Number of tabular coordinate axes. * * 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. * * int *K * (Given or returned) Pointer to the first element of a vector of length * 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 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. * * int *map * (Given) Pointer to the first element of a vector of length 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, x[] and world[], in the argument * lists for tabx2s() and tabs2x(). * * When x[] and world[] contain the full complement of coordinate elements * in image-order, as will usually be the case, then map[m-1] == i-1 for * axis i in the N-dimensional image (1 <= i <= N). In terms of the FITS * keywords * * map[PVi_3a - 1] == i - 1. * * However, a different association may result if x[], for example, only * contains a (relevant) subset of intermediate world coordinate elements. * For example, if M == 1 for an image with N > 1, it is possible to fill * x[] with the relevant coordinate element with nelem set to 1. In this * case map[0] = 0 regardless of the value of i. * * double *crval * (Given) Pointer to the first element of a vector of length tabprm::M * whose elements contain the index value for the reference pixel for each * of the tabular coordinate axes. * * double **index * (Given) Pointer to the first element of a vector of length tabprm::M of * pointers to vectors of lengths (K_1, K_2,... K_M) of 0-relative indexes * (see tabprm::K). * * The address of any or all of these index vectors may be set to zero, * i.e. * = index[m] == 0; * * this is interpreted as default indexing, i.e. * = index[m][k] = k; * * double *coord * (Given) Pointer to the first element of the tabular coordinate array, * treated as though it were defined as * = double coord[K_M]...[K_2][K_1][M]; * * (see tabprm::K) i.e. with the M dimension varying fastest so that the * M elements of a coordinate vector are stored contiguously in memory. * * int nc * (Returned) Total number of coordinate vectors in the coordinate array * being the product K_1 * K_2 * ... * K_M (see tabprm::K). * * int padding * (An unused variable inserted for alignment purposes only.) * * int *sense * (Returned) Pointer to the first element of a vector of length tabprm::M * whose elements indicate whether the corresponding indexing vector is * monotonic increasing (+1), or decreasing (-1). * * int *p0 * (Returned) Pointer to the first element of a vector of length tabprm::M * of interpolated indices into the coordinate array such that Upsilon_m, * as defined in Paper III, is equal to (p0[m] + 1) + tabprm::delta[m]. * * double *delta * (Returned) Pointer to the first element of a vector of length tabprm::M * of interpolated indices into the coordinate array such that Upsilon_m, * as defined in Paper III, is equal to (tabprm::p0[m] + 1) + delta[m]. * * double *extrema * (Returned) Pointer to the first element of an array that records the * minimum and maximum value of each element of the coordinate vector in * each row of the coordinate array, treated as though it were defined as * = double extrema[K_M]...[K_2][2][M] * * (see 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, tabs2x(), to speed up table searches. * * struct wcserr *err * (Returned) If enabled, when an error status is returned, this struct * contains detailed information about the error, see wcserr_enable(). * * int m_flag * (For internal use only.) * int m_M * (For internal use only.) * int m_N * (For internal use only.) * int set_M * (For internal use only.) * int m_K * (For internal use only.) * int m_map * (For internal use only.) * int m_crval * (For internal use only.) * int m_index * (For internal use only.) * int m_indxs * (For internal use only.) * int m_coord * (For internal use only.) * * * Global variable: const char *tab_errmsg[] - Status return messages * ------------------------------------------------------------------ * Error messages to match the status value returned from each function. * *===========================================================================*/ #ifndef WCSLIB_TAB #define WCSLIB_TAB #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. }; 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 messaging, if enabled. //-------------------------------------------------------------------------- struct wcserr *err; //-------------------------------------------------------------------------- // 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. #define TABLEN (sizeof(struct tabprm)/sizeof(int)) int tabini(int alloc, int M, const int K[], struct tabprm *tab); 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[], double world[], int stat[]); int tabs2x(struct tabprm *tab, int ncoord, int nelem, const double world[], double x[], int stat[]); // Deprecated. #define tabini_errmsg tab_errmsg #define tabcpy_errmsg tab_errmsg #define tabfree_errmsg tab_errmsg #define tabprt_errmsg tab_errmsg #define tabset_errmsg tab_errmsg #define tabx2s_errmsg tab_errmsg #define tabs2x_errmsg tab_errmsg #ifdef __cplusplus } #endif #endif // WCSLIB_TAB astropy-astropy-201cddb/cextern/wcslib/C/wcs.c000066400000000000000000004140021507226315300214550ustar00rootroot00000000000000/*============================================================================ WCSLIB 8.4 - an implementation of the FITS WCS standard. Copyright (C) 1995-2024, 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/people/Mark.Calabretta $Id: wcs.c,v 8.4 2024/10/28 13:56:16 mcalabre Exp $ *===========================================================================*/ #include #include #include #include #include #include "wcserr.h" #include "wcsmath.h" #include "wcsprintf.h" #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" #include "sph.h" #include "cel.h" #include "tab.h" #include "wcs.h" // Maximum number of PVi_ma and PSi_ma keywords. int NPVMAX = 64; int NPSMAX = 8; // 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 type", "Invalid parameter value", "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", "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(). #define WCS_ERRMSG(status) WCSERR_SET(status), wcs_errmsg[status] #ifndef signbit #define signbit(X) ((X) < 0.0 ? 1 : 0) #endif //---------------------------------------------------------------------------- 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) { 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 status; // Check inputs. if (wcs == 0x0) return WCSERR_NULL_POINTER; 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; } wcserr_clear(&(wcs->lin.err)); wcserr_clear(&(wcs->cel.err)); wcserr_clear(&(wcs->spc.err)); // Initialize pointers. if (wcs->flag == -1 || wcs->m_flag != WCSSET) { if (wcs->flag == -1) { wcs->tab = 0x0; wcs->types = 0x0; wcs->lin.flag = -1; } // Initialize memory management. wcs->m_flag = 0; wcs->m_naxis = 0; wcs->m_crpix = 0x0; wcs->m_pc = 0x0; wcs->m_cdelt = 0x0; wcs->m_crval = 0x0; wcs->m_cunit = 0x0; wcs->m_ctype = 0x0; wcs->m_pv = 0x0; wcs->m_ps = 0x0; wcs->m_cd = 0x0; wcs->m_crota = 0x0; wcs->m_colax = 0x0; 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; } if (naxis < 0) { return wcserr_set(WCSERR_SET(WCSERR_MEMORY), "naxis must not be negative (got %d)", naxis); } // Allocate memory for arrays if required. if (alloc || wcs->crpix == 0x0 || wcs->pc == 0x0 || wcs->cdelt == 0x0 || wcs->crval == 0x0 || wcs->cunit == 0x0 || wcs->ctype == 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->czphs == 0x0 || wcs->cperi == 0x0) { // Was sufficient allocated previously? if (wcs->m_flag == WCSSET && (wcs->m_naxis < naxis || 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. wcs->crpix = wcs->m_crpix; } else { if ((wcs->crpix = calloc(naxis, sizeof(double))) == 0x0) { return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY)); } wcs->m_flag = WCSSET; wcs->m_naxis = naxis; wcs->m_crpix = wcs->crpix; } } if (alloc || wcs->pc == 0x0) { if (wcs->m_pc) { // In case the caller fiddled with it. wcs->pc = wcs->m_pc; } else { if ((wcs->pc = calloc(naxis*naxis, sizeof(double))) == 0x0) { wcsfree(wcs); return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY)); } wcs->m_flag = WCSSET; wcs->m_naxis = naxis; wcs->m_pc = wcs->pc; } } if (alloc || wcs->cdelt == 0x0) { if (wcs->m_cdelt) { // In case the caller fiddled with it. wcs->cdelt = wcs->m_cdelt; } else { if ((wcs->cdelt = calloc(naxis, sizeof(double))) == 0x0) { wcsfree(wcs); return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY)); } wcs->m_flag = WCSSET; wcs->m_naxis = naxis; wcs->m_cdelt = wcs->cdelt; } } if (alloc || wcs->crval == 0x0) { if (wcs->m_crval) { // In case the caller fiddled with it. wcs->crval = wcs->m_crval; } else { if ((wcs->crval = calloc(naxis, sizeof(double))) == 0x0) { wcsfree(wcs); return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY)); } wcs->m_flag = WCSSET; wcs->m_naxis = naxis; wcs->m_crval = wcs->crval; } } if (alloc || wcs->cunit == 0x0) { if (wcs->m_cunit) { // In case the caller fiddled with it. wcs->cunit = wcs->m_cunit; } else { if ((wcs->cunit = calloc(naxis, sizeof(char [72]))) == 0x0) { wcsfree(wcs); return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY)); } wcs->m_flag = WCSSET; wcs->m_naxis = naxis; wcs->m_cunit = wcs->cunit; } } if (alloc || wcs->ctype == 0x0) { if (wcs->m_ctype) { // In case the caller fiddled with it. wcs->ctype = wcs->m_ctype; } else { if ((wcs->ctype = calloc(naxis, sizeof(char [72]))) == 0x0) { wcsfree(wcs); return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY)); } wcs->m_flag = WCSSET; wcs->m_naxis = naxis; wcs->m_ctype = wcs->ctype; } } if (alloc || wcs->pv == 0x0) { if (wcs->m_pv) { // In case the caller fiddled with it. wcs->pv = wcs->m_pv; } else { if (npvmax) { if ((wcs->pv = calloc(npvmax, sizeof(struct pvcard))) == 0x0) { wcsfree(wcs); return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY)); } } else { wcs->pv = 0x0; } wcs->npvmax = npvmax; wcs->m_flag = WCSSET; wcs->m_naxis = naxis; wcs->m_pv = wcs->pv; } } if (alloc || wcs->ps == 0x0) { if (wcs->m_ps) { // In case the caller fiddled with it. wcs->ps = wcs->m_ps; } else { if (npsmax) { if ((wcs->ps = calloc(npsmax, sizeof(struct pscard))) == 0x0) { wcsfree(wcs); return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY)); } } else { wcs->ps = 0x0; } wcs->npsmax = npsmax; wcs->m_flag = WCSSET; wcs->m_naxis = naxis; wcs->m_ps = wcs->ps; } } if (alloc || wcs->cd == 0x0) { if (wcs->m_cd) { // In case the caller fiddled with it. wcs->cd = wcs->m_cd; } else { if ((wcs->cd = calloc(naxis*naxis, sizeof(double))) == 0x0) { wcsfree(wcs); return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY)); } wcs->m_flag = WCSSET; wcs->m_naxis = naxis; wcs->m_cd = wcs->cd; } } if (alloc || wcs->crota == 0x0) { if (wcs->m_crota) { // In case the caller fiddled with it. wcs->crota = wcs->m_crota; } else { if ((wcs->crota = calloc(naxis, sizeof(double))) == 0x0) { wcsfree(wcs); return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY)); } wcs->m_flag = WCSSET; wcs->m_naxis = naxis; wcs->m_crota = wcs->crota; } } if (alloc || wcs->colax == 0x0) { if (wcs->m_colax) { // In case the caller fiddled with it. wcs->colax = wcs->m_colax; } else { if ((wcs->colax = calloc(naxis, sizeof(int))) == 0x0) { wcsfree(wcs); return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY)); } wcs->m_flag = WCSSET; wcs->m_naxis = naxis; wcs->m_colax = wcs->colax; } } if (alloc || wcs->cname == 0x0) { if (wcs->m_cname) { // In case the caller fiddled with it. wcs->cname = wcs->m_cname; } else { if ((wcs->cname = calloc(naxis, sizeof(char [72]))) == 0x0) { wcsfree(wcs); return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY)); } wcs->m_flag = WCSSET; wcs->m_naxis = naxis; wcs->m_cname = wcs->cname; } } if (alloc || wcs->crder == 0x0) { if (wcs->m_crder) { // In case the caller fiddled with it. wcs->crder = wcs->m_crder; } else { if ((wcs->crder = calloc(naxis, sizeof(double))) == 0x0) { wcsfree(wcs); return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY)); } wcs->m_flag = WCSSET; wcs->m_naxis = naxis; wcs->m_crder = wcs->crder; } } if (alloc || wcs->csyer == 0x0) { if (wcs->m_csyer) { // In case the caller fiddled with it. wcs->csyer = wcs->m_csyer; } else { if ((wcs->csyer = calloc(naxis, sizeof(double))) == 0x0) { wcsfree(wcs); return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY)); } wcs->m_flag = WCSSET; wcs->m_naxis = naxis; 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->naxis = naxis; // 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 (int i = 0; i < naxis; i++) { wcs->crval[i] = 0.0; } // 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. wcs->lonpole = UNDEFINED; wcs->latpole = +90.0; // Set defaults for the spectral transformation parameters. wcs->restfrq = 0.0; wcs->restwav = 0.0; // Default parameter values. wcs->npv = 0; 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 (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. double *cd = wcs->cd; for (int i = 0; i < naxis; i++) { for (int j = 0; j < naxis; j++) { *(cd++) = 0.0; } } for (int i = 0; i < naxis; i++) { wcs->crota[i] = 0.0; } wcs->altlin = 0; wcs->velref = 0; // Defaults for auxiliary coordinate system information. memset(wcs->alt, 0, 4); wcs->alt[0] = ' '; wcs->colnum = 0; 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->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. 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, const struct wcsprm *wcssrc, int *nsub, int axes[], struct wcsprm *wcsdst) { static const char *function = "wcssub"; const char *pq = "PQ"; int status; if (wcssrc == 0x0) return WCSERR_NULL_POINTER; 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); } int dummy; if (nsub == 0x0) { nsub = &dummy; *nsub = naxis; } else if (*nsub == 0) { *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))) == 0x0) { free(itmp); return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY)); } for (int i = 0; i < naxis; i++) { axes[i] = i+1; } } // So that we don't try to free uninitialized pointers on cleanup. wcsdst->m_aux = 0x0; wcsdst->m_tab = 0x0; int msub = 0; for (int j = 0; j < *nsub; j++) { int axis = axes[j]; if (abs(axis) > 0x1000) { // Subimage extraction by type. int k = abs(axis) & 0xFF; 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 (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. char *c = ctypei + 8; while (c-- > ctypei) { if (*c == ' ') *c = '\0'; if (*c != '\0') break; } if ( strcmp(ctypei, "RA") == 0 || strcmp(ctypei+1, "LON") == 0 || strcmp(ctypei+2, "LN") == 0 || strncmp(ctypei, "RA---", 5) == 0 || strncmp(ctypei+1, "LON-", 4) == 0 || strncmp(ctypei+2, "LN-", 3) == 0) { if (!longitude) { continue; } } else if ( strcmp(ctypei, "DEC") == 0 || strcmp(ctypei+1, "LAT") == 0 || strcmp(ctypei+2, "LT") == 0 || strncmp(ctypei, "DEC--", 5) == 0 || strncmp(ctypei+1, "LAT-", 4) == 0 || strncmp(ctypei+2, "LT-", 3) == 0) { if (!latitude) { continue; } } else if (( strncmp(ctypei, "FREQ", 4) == 0 || strncmp(ctypei, "ENER", 4) == 0 || strncmp(ctypei, "WAVN", 4) == 0 || strncmp(ctypei, "VRAD", 4) == 0 || strncmp(ctypei, "WAVE", 4) == 0 || strncmp(ctypei, "VOPT", 4) == 0 || strncmp(ctypei, "ZOPT", 4) == 0 || strncmp(ctypei, "AWAV", 4) == 0 || strncmp(ctypei, "VELO", 4) == 0 || strncmp(ctypei, "BETA", 4) == 0) && (ctypei[4] == '\0' || ctypei[4] == '-')) { if (!spectral) { 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? int k; for (k = 0; k < msub; k++) { if (itmp[k] == i+1) { break; } } if (k == msub) itmp[msub++] = i+1; } } else if (0 < axis && axis <= naxis) { // Check that the requested axis has not already been added. int k; for (k = 0; k < msub; k++) { if (itmp[k] == axis) { break; } } if (k == msub) itmp[msub++] = axis; } else if (axis == 0) { // Graft on a new axis. itmp[msub++] = 0; } else { status = wcserr_set(WCS_ERRMSG(WCSERR_BAD_SUBIMAGE)); goto cleanup; } } if ((*nsub = msub) == 0) { // Zero out this struct. status = wcsinit(alloc, 0, wcsdst, 0, 0, 0); goto cleanup; } for (int i = 0; i < *nsub; i++) { axes[i] = itmp[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 (int i = 0; i < *nsub; i++) { if (axes[i] > 0) { map[axes[i]-1] = i+1; } } // 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; } // 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++; } } // 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++; } } // 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)); } // Also inits disdst. disdst->flag = -1; lindist(m+1, &(wcsdst->lin), disdst, ndpmax); } } if (status) { goto cleanup; } // Linear transformation. srcp = wcssrc->crpix; dstp = wcsdst->crpix; for (int j = 0; j < *nsub; j++, dstp++) { if (axes[j] > 0) { int k = axes[j] - 1; *dstp = *(srcp+k); } } srcp = wcssrc->pc; dstp = wcsdst->pc; 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 (int i = 0; i < *nsub; i++, dstp++) { if (axes[i] > 0) { int k = axes[i] - 1; *dstp = *(srcp+k); } } // Coordinate reference value. srcp = wcssrc->crval; dstp = wcsdst->crval; for (int i = 0; i < *nsub; i++, dstp++) { if (axes[i] > 0) { int k = axes[i] - 1; *dstp = *(srcp+k); } } // Coordinate units and type. for (int i = 0; i < *nsub; i++) { if (axes[i] > 0) { 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. wcsdst->lonpole = wcssrc->lonpole; wcsdst->latpole = wcssrc->latpole; wcsdst->restfrq = wcssrc->restfrq; wcsdst->restwav = wcssrc->restwav; // 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; 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[m]; wcsdst->ps[nps].i = map[i-1]; nps++; } } wcsdst->nps = nps; // 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; } } } } 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. strncpy(wcsdst->alt, wcssrc->alt, 4); wcsdst->colnum = wcssrc->colnum; for (int i = 0; i < *nsub; i++) { if (axes[i] > 0) { 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->wcsname, wcssrc->wcsname, 72); 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->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; 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 (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]) { wcsdst->ntab++; break; } } } if (wcsdst->ntab) { // 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)); goto cleanup; } wcsdst->m_tab = wcsdst->tab; } 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]) { tab->flag = -1; if ((status = tabcpy(1, wcssrc->tab + itab, tab))) { wcserr_set(WCS_ERRMSG(wcs_taberr[status])); goto cleanup; } // 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 (itmp) free(itmp); if (dealloc) { free(axes); } 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 wcscompare( int cmp, double tol, const struct wcsprm *wcs1, const struct wcsprm *wcs2, int *equal) { 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 { // 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; if (wcs->crval == wcs->m_crval) wcs->crval = 0x0; if (wcs->cunit == wcs->m_cunit) wcs->cunit = 0x0; if (wcs->ctype == wcs->m_ctype) wcs->ctype = 0x0; if (wcs->pv == wcs->m_pv) wcs->pv = 0x0; if (wcs->ps == wcs->m_ps) wcs->ps = 0x0; if (wcs->cd == wcs->m_cd) wcs->cd = 0x0; if (wcs->crota == wcs->m_crota) wcs->crota = 0x0; if (wcs->colax == wcs->m_colax) wcs->colax = 0x0; 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); if (wcs->m_crval) free(wcs->m_crval); if (wcs->m_cunit) free(wcs->m_cunit); if (wcs->m_ctype) free(wcs->m_ctype); if (wcs->m_pv) free(wcs->m_pv); if (wcs->m_ps) free(wcs->m_ps); if (wcs->m_cd) free(wcs->m_cd); if (wcs->m_crota) free(wcs->m_crota); if (wcs->m_colax) free(wcs->m_colax); 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); // 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 (int itab = 0; itab < wcs->ntab; itab++) { tabfree(wcs->m_tab + itab); } free(wcs->m_tab); } if (wcs->m_wtb) free(wcs->m_wtb); } // Allocated unconditionally by wcsset(). if (wcs->types) free(wcs->types); if (wcs->lin.crpix == wcs->m_crpix) wcs->lin.crpix = 0x0; if (wcs->lin.pc == wcs->m_pc) wcs->lin.pc = 0x0; if (wcs->lin.cdelt == wcs->m_cdelt) wcs->lin.cdelt = 0x0; } wcs->m_flag = 0; wcs->m_naxis = 0x0; wcs->m_crpix = 0x0; wcs->m_pc = 0x0; wcs->m_cdelt = 0x0; wcs->m_crval = 0x0; wcs->m_cunit = 0x0; wcs->m_ctype = 0x0; wcs->m_pv = 0x0; wcs->m_ps = 0x0; wcs->m_cd = 0x0; wcs->m_crota = 0x0; wcs->m_colax = 0x0; 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; wcs->nwtb = 0; wcs->m_wtb = 0x0; wcs->types = 0x0; linfree(&(wcs->lin)); celfree(&(wcs->cel)); spcfree(&(wcs->spc)); wcserr_clear(&(wcs->err)); wcs->flag = 0; return 0; } //---------------------------------------------------------------------------- int wcstrim(struct wcsprm *wcs) { if (wcs == 0x0) return WCSERR_NULL_POINTER; if (wcs->m_flag != WCSSET) { // Nothing to do. return 0; } 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 wcssize(const struct wcsprm *wcs, int sizes[2]) { 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 (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 (int i = 0; i < wcs->naxis; i++) { wcsprintf(" %#- 11.5g", wcs->crpix[i]); } wcsprintf("\n"); // ...linear transformation. int k = 0; WCSPRINTF_PTR(" pc: ", wcs->pc, "\n"); for (int i = 0; i < wcs->naxis; i++) { wcsprintf(" pc[%d][]:", i); for (int j = 0; j < wcs->naxis; j++) { wcsprintf(" %#- 11.5g", wcs->pc[k++]); } wcsprintf("\n"); } // ...coordinate increment at reference point. WCSPRINTF_PTR(" cdelt: ", wcs->cdelt, "\n"); wcsprintf(" "); for (int i = 0; i < wcs->naxis; i++) { wcsprintf(" %#- 11.5g", wcs->cdelt[i]); } wcsprintf("\n"); // ...coordinate value at reference point. WCSPRINTF_PTR(" crval: ", wcs->crval, "\n"); wcsprintf(" "); for (int i = 0; i < wcs->naxis; i++) { wcsprintf(" %#- 11.5g", wcs->crval[i]); } wcsprintf("\n"); // ...coordinate units and type. WCSPRINTF_PTR(" cunit: ", wcs->cunit, "\n"); for (int i = 0; i < wcs->naxis; i++) { wcsprintf(" \"%s\"\n", wcs->cunit[i]); } WCSPRINTF_PTR(" ctype: ", wcs->ctype, "\n"); for (int i = 0; i < wcs->naxis; i++) { wcsprintf(" \"%s\"\n", wcs->ctype[i]); } // ...celestial and spectral transformation parameters. if (undefined(wcs->lonpole)) { wcsprintf(" lonpole: UNDEFINED\n"); } else { wcsprintf(" lonpole: %9f\n", wcs->lonpole); } wcsprintf(" latpole: %9f\n", wcs->latpole); wcsprintf(" restfrq: %f\n", wcs->restfrq); wcsprintf(" restwav: %f\n", wcs->restwav); // ...parameter values. wcsprintf(" npv: %d\n", wcs->npv); wcsprintf(" npvmax: %d\n", wcs->npvmax); WCSPRINTF_PTR(" pv: ", wcs->pv, "\n"); 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 (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. k = 0; WCSPRINTF_PTR(" cd: ", wcs->cd, "\n"); if (wcs->cd) { for (int i = 0; i < wcs->naxis; i++) { wcsprintf(" cd[%d][]:", i); for (int j = 0; j < wcs->naxis; j++) { wcsprintf(" %#- 11.5g", wcs->cd[k++]); } wcsprintf("\n"); } } WCSPRINTF_PTR(" crota: ", wcs->crota, "\n"); if (wcs->crota) { wcsprintf(" "); for (int i = 0; i < wcs->naxis; i++) { wcsprintf(" %#- 11.5g", wcs->crota[i]); } wcsprintf("\n"); } wcsprintf(" altlin: %d\n", wcs->altlin); wcsprintf(" velref: %d\n", wcs->velref); // ...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 (int i = 0; i < wcs->naxis; i++) { wcsprintf(" %5d", wcs->colax[i]); } wcsprintf("\n"); } WCSPRINTF_PTR(" cname: ", wcs->cname, "\n"); if (wcs->cname) { for (int i = 0; i < wcs->naxis; i++) { if (wcs->cname[i][0] == '\0') { wcsprintf(" UNDEFINED\n"); } else { wcsprintf(" \"%s\"\n", wcs->cname[i]); } } } WCSPRINTF_PTR(" crder: ", wcs->crder, "\n"); if (wcs->crder) { wcsprintf(" "); for (int i = 0; i < wcs->naxis; i++) { if (undefined(wcs->crder[i])) { wcsprintf(" UNDEFINED"); } else { wcsprintf(" %#- 11.5g", wcs->crder[i]); } } wcsprintf("\n"); } WCSPRINTF_PTR(" csyer: ", wcs->csyer, "\n"); if (wcs->csyer) { wcsprintf(" "); for (int i = 0; i < wcs->naxis; i++) { if (undefined(wcs->csyer[i])) { wcsprintf(" UNDEFINED"); } else { wcsprintf(" %#- 11.5g", wcs->csyer[i]); } } wcsprintf("\n"); } 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"); } 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"); } 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(" %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); wcsprintf(" obsgeo: "); for (int k = 0; k < 3; k++) { if (undefined(wcs->obsgeo[k])) { wcsprintf(" UNDEFINED"); } else { wcsprintf(" %15.6f", wcs->obsgeo[k]); } } 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"); 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); WCSPRINTF_PTR(" tab: ", wcs->tab, ""); if (wcs->tab != 0x0) wcsprintf(" (see below)"); wcsprintf("\n"); wcsprintf(" nwtb: %d\n", wcs->nwtb); WCSPRINTF_PTR(" wtb: ", wcs->wtb, ""); if (wcs->wtb != 0x0) wcsprintf(" (see below)"); 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(" 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"); // 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, ""); if (wcs->m_crpix == wcs->crpix) wcsprintf(" (= crpix)"); wcsprintf("\n"); WCSPRINTF_PTR(" m_pc: ", wcs->m_pc, ""); if (wcs->m_pc == wcs->pc) wcsprintf(" (= pc)"); wcsprintf("\n"); WCSPRINTF_PTR(" m_cdelt: ", wcs->m_cdelt, ""); if (wcs->m_cdelt == wcs->cdelt) wcsprintf(" (= cdelt)"); wcsprintf("\n"); WCSPRINTF_PTR(" m_crval: ", wcs->m_crval, ""); if (wcs->m_crval == wcs->crval) wcsprintf(" (= crval)"); wcsprintf("\n"); WCSPRINTF_PTR(" m_cunit: ", wcs->m_cunit, ""); if (wcs->m_cunit == wcs->cunit) wcsprintf(" (= cunit)"); wcsprintf("\n"); WCSPRINTF_PTR(" m_ctype: ", wcs->m_ctype, ""); if (wcs->m_ctype == wcs->ctype) wcsprintf(" (= ctype)"); wcsprintf("\n"); WCSPRINTF_PTR(" m_pv: ", wcs->m_pv, ""); if (wcs->m_pv == wcs->pv) wcsprintf(" (= pv)"); wcsprintf("\n"); WCSPRINTF_PTR(" m_ps: ", wcs->m_ps, ""); if (wcs->m_ps == wcs->ps) wcsprintf(" (= ps)"); wcsprintf("\n"); WCSPRINTF_PTR(" m_cd: ", wcs->m_cd, ""); if (wcs->m_cd == wcs->cd) wcsprintf(" (= cd)"); wcsprintf("\n"); WCSPRINTF_PTR(" m_crota: ", wcs->m_crota, ""); if (wcs->m_crota == wcs->crota) wcsprintf(" (= crota)"); wcsprintf("\n"); wcsprintf("\n"); WCSPRINTF_PTR(" m_colax: ", wcs->m_colax, ""); if (wcs->m_colax == wcs->colax) wcsprintf(" (= colax)"); wcsprintf("\n"); WCSPRINTF_PTR(" m_cname: ", wcs->m_cname, ""); if (wcs->m_cname == wcs->cname) wcsprintf(" (= cname)"); wcsprintf("\n"); WCSPRINTF_PTR(" m_crder: ", wcs->m_crder, ""); if (wcs->m_crder == wcs->crder) wcsprintf(" (= crder)"); wcsprintf("\n"); 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"); WCSPRINTF_PTR(" m_wtb: ", wcs->m_wtb, ""); if (wcs->m_wtb == wcs->wtb) wcsprintf(" (= wtb)"); wcsprintf("\n"); // 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", iwtb); wcsprintf(" i: %d\n", wtbp->i); wcsprintf(" m: %d\n", wtbp->m); wcsprintf(" kind: %c\n", wtbp->kind); wcsprintf(" extnam: %s\n", wtbp->extnam); wcsprintf(" extver: %d\n", wtbp->extver); wcsprintf(" extlev: %d\n", wtbp->extlev); wcsprintf(" ttype: %s\n", wtbp->ttype); wcsprintf(" row: %ld\n", wtbp->row); wcsprintf(" ndim: %d\n", wtbp->ndim); WCSPRINTF_PTR(" dimlen: ", wtbp->dimlen, "\n"); WCSPRINTF_PTR(" arrayp: ", wtbp->arrayp, " -> "); WCSPRINTF_PTR("", *(wtbp->arrayp), "\n"); } } if (wcs->tab) { for (int itab = 0; itab < wcs->ntab; itab++) { wcsprintf("\n"); wcsprintf("tab[%d].*\n", itab); tabprt(wcs->tab + itab); } } // Linear transformation parameters. wcsprintf("\n"); wcsprintf(" lin.*\n"); linprt(&(wcs->lin)); // Celestial transformation parameters. wcsprintf("\n"); wcsprintf(" cel.*\n"); celprt(&(wcs->cel)); // Spectral transformation parameters. wcsprintf("\n"); wcsprintf(" spc.*\n"); spcprt(&(wcs->spc)); return 0; } //---------------------------------------------------------------------------- int wcsperr(const struct wcsprm *wcs, const char *prefix) { if (wcs == 0x0) return WCSERR_NULL_POINTER; 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 (int itab = 0; itab < wcs->ntab; itab++) { wcserr_prt((wcs->tab + itab)->err, prefix); } } } return 0; } //---------------------------------------------------------------------------- int wcsbchk(struct wcsprm *wcs, int bounds) { if (wcs == 0x0) return WCSERR_NULL_POINTER; 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; if (wcs->flag == -WCSSET) return 0; struct wcserr **err = &(wcs->err); // Determine axis types from CTYPEia. int status; if ((status = wcs_types(wcs))) { return status; } // 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? if (wcs->lng >= 0 && wcs->types[wcs->lng] == 2200) { struct celprm *wcscel = &(wcs->cel); celini(wcscel); // 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; // 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"); } // 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. if (m < 30) { wcsprj->pv[m] = wcs->pv[k].value; } } else if (i == wcs->lng) { // PVi_ma associated with longitude axis. switch (m) { case 0: wcscel->offset = (wcs->pv[k].value != 0.0); break; case 1: wcscel->phi0 = wcs->pv[k].value; break; case 2: wcscel->theta0 = wcs->pv[k].value; break; case 3: // If present, overrides LONPOLEa. wcscel->ref[2] = wcs->pv[k].value; break; case 4: // If present, overrides LATPOLEa. wcscel->ref[3] = wcs->pv[k].value; break; default: return wcserr_set(WCSERR_SET(WCSERR_BAD_COORD_TRANS), "PV%i_%i%s: Unrecognized coordinate transformation parameter", i+1, m, wcs->alt); break; } } } // Do simple alias translations. if (strncmp(wcs->ctype[wcs->lng]+5, "GLS", 3) == 0) { wcscel->offset = 1; wcscel->phi0 = 0.0; wcscel->theta0 = wcs->crval[wcs->lat]; strcpy(wcsprj->code, "SFL"); } else if (strncmp(wcs->ctype[wcs->lng]+5, "NCP", 3) == 0) { // 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"); } strcpy(wcsprj->code, "SIN"); wcsprj->pv[1] = 0.0; wcsprj->pv[2] = cosd(wcscel->ref[1])/sind(wcscel->ref[1]); } // Initialize the celestial transformation routines. wcsprj->r0 = 0.0; wcscel->flag = 0; if ((status = celset(wcscel))) { return wcserr_set(WCS_ERRMSG(wcs_celerr[status])); } // Update LONPOLE, LATPOLE, and PVi_ma keyvalues. wcs->lonpole = wcscel->ref[2]; wcs->latpole = wcscel->ref[3]; 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) { case 1: wcs->pv[k].value = wcscel->phi0; break; case 2: wcs->pv[k].value = wcscel->theta0; break; case 3: wcs->pv[k].value = wcscel->ref[2]; break; case 4: wcs->pv[k].value = wcscel->ref[3]; break; } } } } // 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))) { return status; } strcpy(wcsspc->type, stype); strcpy(wcsspc->code, scode); // CRVALia, RESTFRQa, and RESTWAVa keyvalues. wcsspc->crval = wcs->crval[wcs->spec]; wcsspc->restfrq = wcs->restfrq; wcsspc->restwav = wcs->restwav; // 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. if (m < 7) { wcsspc->pv[m] = wcs->pv[k].value; } } } // Initialize the spectral transformation routines. wcsspc->flag = 0; if ((status = spcset(wcsspc))) { return wcserr_set(WCS_ERRMSG(wcs_spcerr[status])); } } // 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. wcs->altlin &= 15; if (wcs->altlin > 1 && !(wcs->altlin & 1)) { 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); } 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. 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) { 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); } 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); *(pc + i*naxis + j) *= -lambda; *(pc + j*naxis + i) /= lambda; } } } 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(wcs_linerr[status])); } // 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); // 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; } // : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : static int wcs_types(struct wcsprm *wcs) { static const char *function = "wcs_types"; const int nalias = 6; const char aliases [6][4] = {"NCP", "GLS", "TPU", "TPV", "TNX", "ZPX"}; if (wcs == 0x0) return WCSERR_NULL_POINTER; struct wcserr **err = &(wcs->err); // 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; int naxis = wcs->naxis; if (wcs->types) free(wcs->types); if ((wcs->types = calloc(naxis, sizeof(int))) == 0x0) { return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY)); } 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). if (strlen(ctypei) == 12 && ctypei[8] == '-') { // Excise the "4-3-3" or "8-3"-form distortion code. ctypei[8] = '\0'; // 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? wcs->types[i] = 0; if (strcmp(ctypei+4, "-LOG") == 0) { // Logarithmic axis. wcs->types[i] = 400; } else if (strcmp(ctypei+4, "-TAB") == 0) { // Tabular axis. wcs->types[i] = 500; } if (wcs->types[i]) { // Could have -LOG or -TAB with celestial or spectral types. ctypei[4] = '\0'; // 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(). 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. if (!(strlen(ctypei) == 8 && ctypei[4] == '-')) { // Identify Stokes, celestial, spectral, and time types. if (strcmp(ctypei, "STOKES") == 0) { // STOKES axis. wcs->types[i] = 1100; } else if (strcmp(ctypei, "RA") == 0 || strcmp(ctypei+1, "LON") == 0 || strcmp(ctypei+2, "LN") == 0) { // 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. wcs->types[i] += 2001; if (wcs->lat < 0) { wcs->lat = i; strcpy(wcs->lattyp, ctypei); } } else if (strcmp(ctypei, "CUBEFACE") == 0) { // CUBEFACE axis. if (wcs->cubeface == -1) { wcs->types[i] = 2102; wcs->cubeface = i; } else { // 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. 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? char scode[4]; if (spctyp(ctypei, 0x0, scode, 0x0, 0x0, 0x0, 0x0, 0x0) == 0) { // Non-linear spectral axis found. wcs->types[i] = 3300; // 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)", wcs->spec+1, alt, i+1, alt); } wcs->spec = i; continue; } // 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. 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. wcs->types[i] = -1; return wcserr_set(WCSERR_SET(WCSERR_BAD_CTYPE), "Unrecognized projection code (%s in CTYPE%d%.1s)", ctypei+5, i+1, alt); } } // Parse the celestial axis type. wcs->types[i] = 2200; if (*pcode == '\0') { // The first of the two celestial axes. sprintf(pcode, "%.3s", ctypei+5); if (strncmp(ctypei, "RA--", 4) == 0) { wcs->lng = i; strcpy(wcs->lngtyp, "RA"); strcpy(wcs->lattyp, "DEC"); ndx = &wcs->lat; sprintf(requir, "DEC--%s", pcode); } else if (strncmp(ctypei, "DEC-", 4) == 0) { wcs->lat = i; strcpy(wcs->lngtyp, "RA"); strcpy(wcs->lattyp, "DEC"); ndx = &wcs->lng; sprintf(requir, "RA---%s", pcode); } else if (strncmp(ctypei+1, "LON", 3) == 0) { wcs->lng = i; sprintf(wcs->lngtyp, "%cLON", ctypei[0]); sprintf(wcs->lattyp, "%cLAT", ctypei[0]); ndx = &wcs->lat; sprintf(requir, "%s-%s", wcs->lattyp, pcode); } else if (strncmp(ctypei+1, "LAT", 3) == 0) { wcs->lat = i; sprintf(wcs->lngtyp, "%cLON", ctypei[0]); sprintf(wcs->lattyp, "%cLAT", ctypei[0]); ndx = &wcs->lng; sprintf(requir, "%s-%s", wcs->lngtyp, pcode); } else if (strncmp(ctypei+2, "LN", 2) == 0) { wcs->lng = i; sprintf(wcs->lngtyp, "%c%cLN", ctypei[0], ctypei[1]); sprintf(wcs->lattyp, "%c%cLT", ctypei[0], ctypei[1]); ndx = &wcs->lat; sprintf(requir, "%s-%s", wcs->lattyp, pcode); } else if (strncmp(ctypei+2, "LT", 2) == 0) { wcs->lat = i; sprintf(wcs->lngtyp, "%c%cLN", ctypei[0], ctypei[1]); sprintf(wcs->lattyp, "%c%cLT", ctypei[0], ctypei[1]); ndx = &wcs->lng; sprintf(requir, "%s-%s", wcs->lngtyp, pcode); } else { // Unrecognized celestial type. wcs->types[i] = -1; wcs->lng = -1; wcs->lat = -1; return wcserr_set(WCSERR_SET(WCSERR_BAD_CTYPE), "Unrecognized celestial type (%5s in CTYPE%d%.1s)", ctypei, i+1, alt); } if (wcs->lat >= 0) wcs->types[i]++; } else { // Looking for the complementary celestial axis. if (wcs->lat < 0) wcs->types[i]++; if (strncmp(ctypei, requir, 8) != 0) { // Inconsistent projection types. wcs->lng = -1; wcs->lat = -1; return wcserr_set(WCSERR_SET(WCSERR_BAD_CTYPE), "Inconsistent " "projection types (expected %s, got %s in CTYPE%d%.1s)", requir, ctypei, i+1, alt); } *ndx = i; requir[0] = '\0'; } } // Do we have a complementary pair of celestial axes? if (strcmp(requir, "")) { // Unmatched celestial axis. wcs->lng = -1; wcs->lat = -1; return wcserr_set(WCSERR_SET(WCSERR_BAD_CTYPE), "Unmatched celestial axes"); } // 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]; 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"); } wcs->types[i] += 10 * j; } } return 0; } // : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : static int time_type(const char *ctype) { // 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) { // 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; 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]); // Use types set by wcs_types(). char ctype[9], units[16]; switch (wcs->types[i]/1000) { case 2: // Celestial axis. strcpy(units, "deg"); break; case 3: // Spectral axis. strncpy(ctype, wcs->ctype[i], 8); ctype[8] = '\0'; spctyp(ctype, 0x0, 0x0, 0x0, units, 0x0, 0x0, 0x0); break; default: continue; } // Tabular axis, CDELTia and CRVALia relate to indices. if ((wcs->types[i]/100)%10 == 5) { continue; } if (wcs->cunit[i][0]) { double scale, offset, power; struct wcserr *uniterr; if (wcsunitse(wcs->cunit[i], units, &scale, &offset, &power, &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; } if (scale != 1.0) { wcs->cdelt[i] *= scale; wcs->crval[i] *= scale; if (wcs->cd) { for (int j = 0; j < naxis; j++) { *(wcs->cd + i*naxis + j) *= scale; } } strcpy(wcs->cunit[i], units); } } else { strcpy(wcs->cunit[i], units); } } 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, int ncoord, int nelem, const double pixcrd[], double imgcrd[], double phi[], double theta[], double world[], int stat[]) { static const char *function = "wcsp2s"; // Initialize if required. if (wcs == 0x0) return WCSERR_NULL_POINTER; struct wcserr **err = &(wcs->err); int status = 0; if (abs(wcs->flag) != WCSSET) { if ((status = wcsset(wcs))) return status; } // 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. int *istatp; if ((istatp = calloc(ncoord, sizeof(int))) == 0x0) { return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY)); } stat[0] = 0; wcsutil_setAli(ncoord, 1, stat); // 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. img = imgcrd + i; wrl = world + i; 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? if (wcs->cubeface != -1) { // 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. img = imgcrd; 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)); } else { *statp = 0; switch (face) { case 0: *(img+wcs->lat) += offset; break; case 1: break; case 2: *(img+i) += offset; break; case 3: *(img+i) += offset*2; break; case 4: *(img+i) += offset*3; break; case 5: *(img+wcs->lat) -= offset; break; default: *statp |= bits; status = wcserr_set(WCS_ERRMSG(WCSERR_BAD_PIX)); } } img += nelem; } } // Check for constant x and/or y. int iso_x = 0; int iso_y = 0; int nx = ncoord; int ny = 0; 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. 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 (iso_x && iso_y) { wcsutil_setAll(ncoord, nelem, world+i); wcsutil_setAll(ncoord, nelem, world+wcs->lat); wcsutil_setAll(ncoord, 1, phi); wcsutil_setAll(ncoord, 1, theta); wcsutil_setAli(ncoord, 1, istatp); } // 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) { // 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; } } int istat = 0; if (wcs->types[i] == 3300) { // Spectral coordinates. istat = spcx2s(&(wcs->spc), nx, nelem, nelem, imgcrd+i, world+i, istatp); if (istat) { status = wcserr_set(WCS_ERRMSG(wcs_spcerr[istat])); if (status != WCSERR_BAD_PIX) { goto cleanup; } } } else if (type == 4) { // Logarithmic coordinates. istat = logx2s(wcs->crval[i], nx, nelem, nelem, imgcrd+i, world+i, istatp); if (istat) { status = wcserr_set(WCS_ERRMSG(wcs_logerr[istat])); if (status != WCSERR_BAD_PIX) { goto cleanup; } } } // If x was constant, replicate values. if (iso_x) { wcsutil_setAll(ncoord, nelem, world+i); wcsutil_setAli(ncoord, 1, istatp); } // WCSERR_BAD_PIX stat[] vector accounting. if (istat) { wcsutil_setBit(ncoord, istatp, 1 << i, stat); } } } // Do tabular coordinates. for (int itab = 0; itab < wcs->ntab; itab++) { int istat = tabx2s(wcs->tab + itab, ncoord, nelem, imgcrd, world, istatp); if (istat) { status = wcserr_set(WCS_ERRMSG(wcs_taberr[istat])); if (status != WCSERR_BAD_PIX) { goto cleanup; } // 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); } } // Zero the unused world coordinate elements. for (int i = wcs->naxis; i < nelem; i++) { world[i] = 0.0; wcsutil_setAll(ncoord, nelem, world+i); } cleanup: free(istatp); return status; } //---------------------------------------------------------------------------- int wcss2p( struct wcsprm* wcs, int ncoord, int nelem, const double world[], double phi[], double theta[], double imgcrd[], double pixcrd[], int stat[]) { static const char *function = "wcss2p"; // Initialize if required. if (wcs == 0x0) return WCSERR_NULL_POINTER; struct wcserr **err = &(wcs->err); int status = 0; if (abs(wcs->flag) != WCSSET) { if ((status = wcsset(wcs))) return status; } // 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. int *istatp; if ((istatp = calloc(ncoord, sizeof(int))) == 0x0) { return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY)); } stat[0] = 0; wcsutil_setAli(ncoord, 1, stat); // 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. 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. 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. 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 (isolng && isolat) { wcsutil_setAll(ncoord, nelem, imgcrd+i); wcsutil_setAll(ncoord, nelem, imgcrd+wcs->lat); wcsutil_setAll(ncoord, 1, phi); wcsutil_setAll(ncoord, 1, theta); wcsutil_setAli(ncoord, 1, istatp); } // 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? if (wcs->cubeface != -1) { // 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. 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; } else if (*(img+wcs->lat) > 0.5*offset) { *(img+wcs->lat) -= offset; *(img+wcs->cubeface) = 0.0; } else if (*(img+i) > 2.5*offset) { *(img+i) -= 3.0*offset; *(img+wcs->cubeface) = 4.0; } else if (*(img+i) > 1.5*offset) { *(img+i) -= 2.0*offset; *(img+wcs->cubeface) = 3.0; } else if (*(img+i) > 0.5*offset) { *(img+i) -= offset; *(img+wcs->cubeface) = 2.0; } else { *(img+wcs->cubeface) = 1.0; } img += nelem; } } } else if (type == 3 || type == 4) { // 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; } } int istat = 0; if (wcs->types[i] == 3300) { // Spectral coordinates. istat = spcs2x(&(wcs->spc), nwrld, nelem, nelem, world+i, imgcrd+i, istatp); if (istat) { status = wcserr_set(WCS_ERRMSG(wcs_spcerr[istat])); if (status != WCSERR_BAD_WORLD) { goto cleanup; } } } else if (type == 4) { // Logarithmic coordinates. istat = logs2x(wcs->crval[i], nwrld, nelem, nelem, world+i, imgcrd+i, istatp); if (istat) { status = wcserr_set(WCS_ERRMSG(wcs_logerr[istat])); if (status != WCSERR_BAD_WORLD) { goto cleanup; } } } // If constant, replicate values. if (isospec) { wcsutil_setAll(ncoord, nelem, imgcrd+i); wcsutil_setAli(ncoord, 1, istatp); } // WCSERR_BAD_WORLD stat[] vector accounting. if (istat) { wcsutil_setBit(ncoord, istatp, 1 << i, stat); } } } // Do tabular coordinates. for (int itab = 0; itab < wcs->ntab; itab++) { int istat = tabs2x(wcs->tab + itab, ncoord, nelem, world, imgcrd, istatp); if (istat) { status = wcserr_set(WCS_ERRMSG(wcs_taberr[istat])); 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); } } // 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. 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: free(istatp); return status; } //---------------------------------------------------------------------------- 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[]) { static const char *function = "wcsmix"; const int niter = 60; const double tol = 1.0e-10; const double tol2 = 100.0*tol; // Initialize if required. if (wcs == 0x0) return WCSERR_NULL_POINTER; struct wcserr **err = &(wcs->err); int status; 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"); } double *worldlng = world + wcs->lng; double *worldlat = world + wcs->lat; // Check vspan. double span[2]; if (vspan[0] <= vspan[1]) { span[0] = vspan[0]; span[1] = vspan[1]; } else { // Swap them. span[0] = vspan[1]; span[1] = vspan[0]; } // 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. int nstep = viter; if (nstep < 5) { nstep = 5; } else if (nstep > 10) { nstep = 10; } // Given pixel element. double pixmix = pixcrd[mixpix]; // 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. if (mixcel == 1) { // Celestial longitude is given. // 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) { status = wcserr_set(WCS_ERRMSG(WCSERR_BAD_WORLD_COORD)); } return status; } double d0 = pixcrd[mixpix] - pixmix; double dabs = fabs(d0); if (dabs < tol) return 0; double lat1 = span[1]; *worldlat = lat1; if ((status = wcss2p(wcs, 1, 0, world, phi, theta, imgcrd, pixcrd, stat))) { if (status == WCSERR_BAD_WORLD) { status = wcserr_set(WCS_ERRMSG(WCSERR_BAD_WORLD_COORD)); } return status; } double d1 = pixcrd[mixpix] - pixmix; dabs = fabs(d1); if (dabs < tol) return 0; double lmin = lat1; double dmin = dabs; // Check for a crossing point. int crossed; double dx = 0.0; if (signbit(d0) != signbit(d1)) { crossed = 1; dx = d1; } else { crossed = 0; lat0 = span[1]; } for (int retry = 0; retry < 4; retry++) { // Refine the solution interval. while (lat0 > span[0]) { lat0 -= step; if (lat0 < span[0]) lat0 = span[0]; *worldlat = lat0; if ((status = wcss2p(wcs, 1, 0, world, phi, theta, imgcrd, pixcrd, stat))) { if (status == WCSERR_BAD_WORLD) { status = wcserr_set(WCS_ERRMSG(WCSERR_BAD_WORLD_COORD)); } return status; } d0 = pixcrd[mixpix] - pixmix; // Check for a solution. dabs = fabs(d0); if (dabs < tol) return 0; // Record the point of closest approach. if (dabs < dmin) { lmin = lat0; dmin = dabs; } // Check for a crossing point. if (signbit(d0) != signbit(d1)) { crossed = 2; dx = d0; break; } // Advance to the next subinterval. lat1 = lat0; d1 = d0; } if (crossed) { // 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; } double dlat = lat1 - lat0; double lat = lat0 + lambda*dlat; *worldlat = lat; if ((status = wcss2p(wcs, 1, 0, world, phi, theta, imgcrd, pixcrd, stat))) { if (status == WCSERR_BAD_WORLD) { status = wcserr_set(WCS_ERRMSG(WCSERR_BAD_WORLD_COORD)); } return status; } // 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. if (dabs < tol2) return 0; // Must be a discontinuity. break; } // Record the point of closest approach. if (dabs < dmin) { lmin = lat; dmin = dabs; } if (signbit(d0) == signbit(d)) { lat0 = lat; d0 = d; } else { lat1 = lat; d1 = d; } } // 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. if (lmin == span[0]) break; if (lmin == span[1]) break; double lat = lmin; lat0 = lat - step; if (lat0 < span[0]) lat0 = span[0]; lat1 = lat + step; if (lat1 > span[1]) lat1 = span[1]; *worldlat = lat0; if ((status = wcss2p(wcs, 1, 0, world, phi, theta, imgcrd, pixcrd, stat))) { if (status == WCSERR_BAD_WORLD) { status = wcserr_set(WCS_ERRMSG(WCSERR_BAD_WORLD_COORD)); } return status; } d0 = fabs(pixcrd[mixpix] - pixmix); double d = dmin; *worldlat = lat1; if ((status = wcss2p(wcs, 1, 0, world, phi, theta, imgcrd, pixcrd, stat))) { if (status == WCSERR_BAD_WORLD) { status = wcserr_set(WCS_ERRMSG(WCSERR_BAD_WORLD_COORD)); } return status; } d1 = fabs(pixcrd[mixpix] - pixmix); 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))) { if (status == WCSERR_BAD_WORLD) { status = wcserr_set(WCS_ERRMSG(WCSERR_BAD_WORLD_COORD)); } return status; } double d0m = fabs(pixcrd[mixpix] - pixmix); if (d0m < tol) return 0; double lat1m = (lat1 + lat)/2.0; *worldlat = lat1m; if ((status = wcss2p(wcs, 1, 0, world, phi, theta, imgcrd, pixcrd, stat))) { if (status == WCSERR_BAD_WORLD) { status = wcserr_set(WCS_ERRMSG(WCSERR_BAD_WORLD_COORD)); } return status; } double d1m = fabs(pixcrd[mixpix] - pixmix); if (d1m < tol) return 0; if (d0m < d && d0m <= d1m) { lat1 = lat; d1 = d; lat = lat0m; d = d0m; } else if (d1m < d) { lat0 = lat; d0 = d; lat = lat1m; d = d1m; } else { lat0 = lat0m; d0 = d0m; lat1 = lat1m; d1 = d1m; } } } } } else { // Celestial latitude is given. // 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) { status = wcserr_set(WCS_ERRMSG(WCSERR_BAD_WORLD_COORD)); } return status; } double d0 = pixcrd[mixpix] - pixmix; double dabs = fabs(d0); if (dabs < tol) return 0; double lng1 = span[1]; *worldlng = lng1; if ((status = wcss2p(wcs, 1, 0, world, phi, theta, imgcrd, pixcrd, stat))) { if (status == WCSERR_BAD_WORLD) { status = wcserr_set(WCS_ERRMSG(WCSERR_BAD_WORLD_COORD)); } return status; } double d1 = pixcrd[mixpix] - pixmix; dabs = fabs(d1); if (dabs < tol) return 0; double lmin = lng1; double dmin = dabs; // Check for a crossing point. int crossed; double dx = 0.0; if (signbit(d0) != signbit(d1)) { crossed = 1; dx = d1; } else { crossed = 0; lng0 = span[1]; } for (int retry = 0; retry < 4; retry++) { // Refine the solution interval. while (lng0 > span[0]) { lng0 -= step; if (lng0 < span[0]) lng0 = span[0]; *worldlng = lng0; if ((status = wcss2p(wcs, 1, 0, world, phi, theta, imgcrd, pixcrd, stat))) { if (status == WCSERR_BAD_WORLD) { status = wcserr_set(WCS_ERRMSG(WCSERR_BAD_WORLD_COORD)); } return status; } d0 = pixcrd[mixpix] - pixmix; // Check for a solution. dabs = fabs(d0); if (dabs < tol) return 0; // Record the point of closest approach. if (dabs < dmin) { lmin = lng0; dmin = dabs; } // Check for a crossing point. if (signbit(d0) != signbit(d1)) { crossed = 2; dx = d0; break; } // Advance to the next subinterval. lng1 = lng0; d1 = d0; } if (crossed) { // 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; } double dlng = lng1 - lng0; double lng = lng0 + lambda*dlng; *worldlng = lng; if ((status = wcss2p(wcs, 1, 0, world, phi, theta, imgcrd, pixcrd, stat))) { if (status == WCSERR_BAD_WORLD) { status = wcserr_set(WCS_ERRMSG(WCSERR_BAD_WORLD_COORD)); } return status; } // 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. if (dabs < tol2) return 0; // Must be a discontinuity. break; } // Record the point of closest approach. if (dabs < dmin) { lmin = lng; dmin = dabs; } if (signbit(d0) == signbit(d)) { lng0 = lng; d0 = d; } else { lng1 = lng; d1 = d; } } // 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. if (lmin == span[0]) break; if (lmin == span[1]) break; double lng = lmin; lng0 = lng - step; if (lng0 < span[0]) lng0 = span[0]; lng1 = lng + step; if (lng1 > span[1]) lng1 = span[1]; *worldlng = lng0; if ((status = wcss2p(wcs, 1, 0, world, phi, theta, imgcrd, pixcrd, stat))) { if (status == WCSERR_BAD_WORLD) { status = wcserr_set(WCS_ERRMSG(WCSERR_BAD_WORLD_COORD)); } return status; } d0 = fabs(pixcrd[mixpix] - pixmix); double d = dmin; *worldlng = lng1; if ((status = wcss2p(wcs, 1, 0, world, phi, theta, imgcrd, pixcrd, stat))) { if (status == WCSERR_BAD_WORLD) { status = wcserr_set(WCS_ERRMSG(WCSERR_BAD_WORLD_COORD)); } return status; } d1 = fabs(pixcrd[mixpix] - pixmix); 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))) { if (status == WCSERR_BAD_WORLD) { status = wcserr_set(WCS_ERRMSG(WCSERR_BAD_WORLD_COORD)); } return status; } double d0m = fabs(pixcrd[mixpix] - pixmix); if (d0m < tol) return 0; double lng1m = (lng1 + lng)/2.0; *worldlng = lng1m; if ((status = wcss2p(wcs, 1, 0, world, phi, theta, imgcrd, pixcrd, stat))) { if (status == WCSERR_BAD_WORLD) { status = wcserr_set(WCS_ERRMSG(WCSERR_BAD_WORLD_COORD)); } return status; } double d1m = fabs(pixcrd[mixpix] - pixmix); if (d1m < tol) return 0; if (d0m < d && d0m <= d1m) { lng1 = lng; d1 = d; lng = lng0m; d = d0m; } else if (d1m < d) { lng0 = lng; d0 = d; lng = lng1m; d = d1m; } else { lng0 = lng0m; d0 = d0m; lng1 = lng1m; d1 = d1m; } } } } } } // 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. *theta = -90.0; 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) { if (fabs(fmod(*worldlng-lng, 360.0)) > tol) continue; if (lat < span[0]) continue; if (lat > span[1]) continue; *worldlat = lat; } else { if (fabs(*worldlat-lat) > tol) continue; if (lng < span[0]) lng += 360.0; if (lng > span[1]) lng -= 360.0; if (lng < span[0]) continue; if (lng > span[1]) continue; *worldlng = lng; } // Is there a solution for the given pixel coordinate element? lng = *worldlng; lat = *worldlat; // 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))) { wcserr_clear(err); wcs->err = wcs0.err; if (status == WCSERR_BAD_WORLD) { status = wcserr_set(WCS_ERRMSG(WCSERR_BAD_WORLD_COORD)); } return status; } double d0 = pixcrd[mixpix] - pixmix; // Check for a solution. if (fabs(d0) < tol) { // Recall saved world coordinates. *worldlng = lng; *worldlat = lat; return 0; } // 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))) { wcserr_clear(err); wcs->err = wcs0.err; if (status == WCSERR_BAD_WORLD) { status = wcserr_set(WCS_ERRMSG(WCSERR_BAD_WORLD_COORD)); } return status; } d1 = pixcrd[mixpix] - pixmix; // Check for a solution. double dabs = fabs(d1); if (dabs < tol) { // Recall saved world coordinates. *worldlng = lng; *worldlat = lat; return 0; } // Is it a crossing interval? if (signbit(d0) != signbit(d1)) break; phi0 = phi1; 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; } double dphi = phi1 - phi0; *worldlng = phi0 + lambda*dphi; if ((status = wcss2p(&wcs0, 1, 0, world, phi, theta, imgcrd, pixcrd, stat))) { wcserr_clear(err); wcs->err = wcs0.err; if (status == WCSERR_BAD_WORLD) { status = wcserr_set(WCS_ERRMSG(WCSERR_BAD_WORLD_COORD)); } return status; } // Check for a solution. double d = pixcrd[mixpix] - pixmix; double dabs = fabs(d); if (dabs < tol || (dphi < tol && dabs < tol2)) { // Recall saved world coordinates. *worldlng = lng; *worldlat = lat; return 0; } if (signbit(d0) == signbit(d)) { phi0 = *worldlng; d0 = d; } else { phi1 = *worldlng; d1 = d; } } } // 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, int *i, char ctype[9]) { static const char *function = "wcssptr"; 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; } int j; if ((j = *i) < 0) { if ((j = wcs->spec) < 0) { // Look for a linear spectral axis. for (j = 0; j < wcs->naxis; j++) { if (wcs->types[j]/100 == 30) { break; } } if (j >= wcs->naxis) { // No spectral axis. return wcserr_set(WCSERR_SET(WCSERR_BAD_SUBIMAGE), "No spectral axis found"); } } *i = j; } // 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->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. 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; } astropy-astropy-201cddb/cextern/wcslib/C/wcs.h000066400000000000000000002777321507226315300215030ustar00rootroot00000000000000/*============================================================================ WCSLIB 8.4 - an implementation of the FITS WCS standard. Copyright (C) 1995-2024, 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/people/Mark.Calabretta $Id: wcs.h,v 8.4 2024/10/28 13:56:16 mcalabre Exp $ *============================================================================= * * WCSLIB 8.4 - 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 * --------------------------- * 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/people/Mark.Calabretta = = "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. * * A setup routine, wcsset(), computes intermediate values in the wcsprm struct * from parameters in it that were supplied by the user. The struct always * needs to be set up by wcsset() but this need not be called explicitly - * refer to the explanation of wcsprm::flag. * * wcsp2s() and wcss2p() implement the WCS world coordinate transformations. * In fact, they are high level driver routines for the WCS linear, * logarithmic, celestial, spectral and tabular transformation routines * described in lin.h, log.h, cel.h, spc.h and tab.h. * * Given either the celestial longitude or latitude plus an element of the * pixel coordinate a hybrid routine, wcsmix(), iteratively solves for the * unknown elements. * * 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: * --------------------- * The quadcube projections (TSC, CSC, QSC) may be represented in FITS in * either of two ways: * * a: The six faces may be laid out in one plane and numbered as follows: * = 0 = = 4 3 2 1 4 3 2 = = 5 * * Faces 2, 3 and 4 may appear on one side or the other (or both). The * world-to-pixel routines map faces 2, 3 and 4 to the left but the * pixel-to-world routines accept them on either side. * * b: The "COBE" convention in which the six faces are stored in a * three-dimensional structure using a CUBEFACE axis indexed from * 0 to 5 as above. * * These routines support both methods; wcsset() determines which is being * used by the presence or absence of a CUBEFACE axis in ctype[]. wcsp2s() * and wcss2p() translate the CUBEFACE axis representation to the single * plane representation understood by the lower-level WCSLIB projection * 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() 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 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. 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 * 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. This is used to * determine the length of the various wcsprm vectors and * matrices and therefore the amount of memory to * allocate for them. * * Given and returned: * wcs struct wcsprm* * Coordinate transformation parameters. * * Note that, in order to initialize memory management, * wcsprm::flag should be set to -1 when wcs is * 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. * 1: Null wcsprm pointer passed. * 2: Memory allocation failed. * * For returns > 1, a detailed error message is set in * wcsprm::err if enabled, see wcserr_enable(). * * * 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: * alloc int If true, allocate memory unconditionally for the * auxprm struct. * * 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 and returned: * wcs struct wcsprm* * Coordinate transformation parameters. * * Function return value: * 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 wcsinit() to allocate memory for its * arrays if required. Only the "information to be provided" part of the * 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 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 wcsinit() which produce a simple, unnamed, linear * axis with world coordinate equal to the pixel coordinate. These default * values can be changed afterwards, before invoking wcsset(). * * Given: * alloc int If true, allocate memory for the crpix, etc. 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. * * wcssrc const struct wcsprm* * Struct to extract from. * * Given and returned: * nsub int* * axes int[] Vector of length *nsub containing the image axis * numbers (1-relative) to extract. Order is * significant; axes[0] is the axis number of the input * image that corresponds to the first axis in the * subimage, etc. * * Use an axis number of 0 to create a new axis using * the defaults set by wcsinit(). They can be changed * later. * * nsub (the pointer) may be set to zero, and so also may * *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 (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 * following special preprocessor macro values: * * WCSSUB_LONGITUDE: Celestial longitude. * WCSSUB_LATITUDE: Celestial latitude. * 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 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 * that were extracted, or 0 for newly created axes. The * vector length must be sufficient to contain all axis * numbers. No checks are performed to verify that the * coordinate axes are consistent, this is done by * wcsset(). * * wcsdst struct wcsprm* * Struct describing the subimage. wcsprm::flag should * be set to -1 if wcsdst was not previously initialized * (memory leaks may result if it was previously * initialized). * * Function return value: * int Status return value: * 0: Success. * 1: Null wcsprm pointer passed. * 2: Memory allocation failed. * 12: Invalid subimage specification. * 13: Non-separable subimage coordinate system. * * For returns > 1, a detailed error message is set in * wcsprm::err if enabled, see wcserr_enable(). * * Notes: * 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. * * The codes may also be negated to extract all but the types specified, * for example * = *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. * * 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). * * * wcscompare() - Compare two wcsprm structs for equality * ------------------------------------------------------ * wcscompare() compares two wcsprm structs for equality. * * 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 * ---------------------------------------------------- * wcscopy() does a deep copy of one wcsprm struct to another. As of * WCSLIB 3.6, it is implemented as a preprocessor macro that invokes * wcssub() with the nsub and axes pointers both set to zero. * * * wcsfree() - Destructor for the wcsprm struct * -------------------------------------------- * 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 wcsinit(). * * Given and returned: * wcs struct wcsprm* * Coordinate transformation parameters. * * Function return value: * int Status return value: * 0: Success. * 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 * intended for diagnostic purposes. * * Given: * wcs const struct wcsprm* * Coordinate transformation parameters. * * Function return value: * int Status return value: * 0: Success. * 1: Null wcsprm pointer passed. * * * wcsperr() - Print error messages from a wcsprm struct * ----------------------------------------------------- * 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. * If there are no errors then nothing is printed. It uses wcserr_prt(), q.v. * * Given: * wcs const struct wcsprm* * 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 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 * it (refer to the description of the wcsprm struct). * * wcsset() recognizes the NCP projection and converts it to the equivalent SIN * projection and likewise translates GLS into SFL. It also translates the * AIPS spectral types ('FREQ-LSR', 'FELO-HEL', etc.), possibly changing the * input header keywords wcsprm::ctype and/or wcsprm::specsys if necessary. * * Note that this routine need not be called directly; it will be invoked by * 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. * * Function return value: * int Status return value: * 0: Success. * 1: Null wcsprm pointer passed. * 2: Memory allocation failed. * 3: Linear transformation matrix is singular. * 4: Inconsistent or unrecognized coordinate axis * types. * 5: Invalid parameter value. * 6: Invalid coordinate transformation parameters. * 7: Ill-conditioned coordinate transformation * parameters. * * 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 * ---------------------------------------- * wcsp2s() transforms pixel coordinates to world coordinates. * * Given and returned: * wcs struct wcsprm* * Coordinate transformation parameters. * * Given: * ncoord, * nelem int The number of coordinates, each of vector length * nelem but containing wcs.naxis coordinate elements. * Thus nelem must equal or exceed the value of the * NAXIS keyword unless ncoord == 1, in which case nelem * is not used. * * pixcrd const double[ncoord][nelem] * Array of pixel coordinates. * * Returned: * imgcrd double[ncoord][nelem] * Array of intermediate world coordinates. For * celestial axes, imgcrd[][wcs.lng] and * 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. For time axes, * imgcrd[][wcs.time] is the intermediate time * coordinate. * * phi,theta double[ncoord] * Longitude and latitude in the native coordinate system * of the projection [deg]. * * 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, 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 * element(s). * * Function return value: * int Status return value: * 0: Success. * 1: Null wcsprm pointer passed. * 2: Memory allocation failed. * 3: Linear transformation matrix is singular. * 4: Inconsistent or unrecognized coordinate axis * types. * 5: Invalid parameter value. * 6: Invalid coordinate transformation parameters. * 7: Ill-conditioned coordinate transformation * parameters. * 8: One or more of the pixel coordinates were * invalid, as indicated by the stat vector. * * For returns > 1, a detailed error message is set in * wcsprm::err if enabled, see wcserr_enable(). * * * wcss2p() - World-to-pixel transformation * ---------------------------------------- * wcss2p() transforms world coordinates to pixel coordinates. * * Given and returned: * wcs struct wcsprm* * Coordinate transformation parameters. * * Given: * ncoord, * nelem int The number of coordinates, each of vector length nelem * but containing wcs.naxis coordinate elements. Thus * nelem must equal or exceed the value of the NAXIS * keyword unless ncoord == 1, in which case nelem is not * used. * * world const 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, world[][wcs.spec] is the spectral coordinate, in * SI units. For time axes, world[][wcs.time] is the * time coordinate. * * Returned: * phi,theta double[ncoord] * Longitude and latitude in the native coordinate * system of the projection [deg]. * * imgcrd double[ncoord][nelem] * Array of intermediate world coordinates. For * celestial axes, imgcrd[][wcs.lng] and * imgcrd[][wcs.lat] are the projected x-, and * y-coordinates in pseudo "degrees". For quadcube * 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. For time axes, * imgcrd[][wcs.time] is the intermediate time * coordinate. * * pixcrd double[ncoord][nelem] * Array of pixel coordinates. * * stat int[ncoord] * Status return value for each coordinate: * 0: Success. * 1+: A bit mask indicating invalid world coordinate * element(s). * * Function return value: * int Status return value: * 0: Success. * 1: Null wcsprm pointer passed. * 2: Memory allocation failed. * 3: Linear transformation matrix is singular. * 4: Inconsistent or unrecognized coordinate axis * types. * 5: Invalid parameter value. * 6: Invalid coordinate transformation parameters. * 7: Ill-conditioned coordinate transformation * parameters. * 9: One or more of the world coordinates were * invalid, as indicated by the stat vector. * * For returns > 1, a detailed error message is set in * wcsprm::err if enabled, see wcserr_enable(). * * * wcsmix() - Hybrid coordinate transformation * ------------------------------------------- * wcsmix(), given either the celestial longitude or latitude plus an element * of the pixel coordinate, solves for the remaining elements by iterating on * the unknown celestial coordinate element using wcss2p(). Refer also to the * notes below. * * Given and returned: * wcs struct wcsprm* * Indices for the celestial coordinates obtained * by parsing the wcsprm::ctype[]. * * Given: * mixpix int Which element of the pixel coordinate is given. * * mixcel int Which element of the celestial coordinate is given: * 1: Celestial longitude is given in * world[wcs.lng], latitude returned in * world[wcs.lat]. * 2: Celestial latitude is given in * world[wcs.lat], longitude returned in * world[wcs.lng]. * * vspan const double[2] * Solution interval for the celestial coordinate [deg]. * The ordering of the two limits is irrelevant. * Longitude ranges may be specified with any convenient * normalization, for example [-120,+120] is the same as * [240,480], except that the solution will be returned * with the same normalization, i.e. lie within the * interval specified. * * vstep const double * Step size for solution search [deg]. If zero, a * sensible, although perhaps non-optimal default will be * used. * * viter int If a solution is not found then the step size will be * halved and the search recommenced. viter controls how * many times the step size is halved. The allowed range * is 5 - 10. * * Given and returned: * world double[naxis] * World coordinate elements. world[wcs.lng] and * world[wcs.lat] are the celestial longitude and * latitude [deg]. Which is given and which returned * depends on the value of mixcel. All other elements * are given. * * Returned: * phi,theta double[naxis] * Longitude and latitude in the native coordinate * system of the projection [deg]. * * imgcrd double[naxis] * Image coordinate elements. imgcrd[wcs.lng] and * imgcrd[wcs.lat] are the projected x-, and * y-coordinates in pseudo "degrees". * * Given and returned: * pixcrd double[naxis] * Pixel coordinate. The element indicated by mixpix is * given and the remaining elements are returned. * * Function return value: * int Status return value: * 0: Success. * 1: Null wcsprm pointer passed. * 2: Memory allocation failed. * 3: Linear transformation matrix is singular. * 4: Inconsistent or unrecognized coordinate axis * types. * 5: Invalid parameter value. * 6: Invalid coordinate transformation parameters. * 7: Ill-conditioned coordinate transformation * parameters. * 10: Invalid world coordinate. * 11: No solution found in the specified interval. * * For returns > 1, a detailed error message is set in * wcsprm::err if enabled, see wcserr_enable(). * * Notes: * 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 * ------------------------------------- * 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. * * i int* Index of the spectral axis (0-relative). If given < 0 * it will be set to the first spectral axis identified * from the ctype[] keyvalues in the wcsprm struct. * * ctype char[9] Desired spectral CTYPEia. Wildcarding may be used as * for the ctypeS2 argument to spctrn() as described in * the prologue of spc.h, i.e. if the final three * characters are specified as "???", or if just the * eighth character is specified as '?', the correct * algorithm code will be substituted and returned. * * Function return value: * int Status return value: * 0: Success. * 1: Null wcsprm pointer passed. * 2: Memory allocation failed. * 3: Linear transformation matrix is singular. * 4: Inconsistent or unrecognized coordinate axis * types. * 5: Invalid parameter value. * 6: Invalid coordinate transformation parameters. * 7: Ill-conditioned coordinate transformation * parameters. * 12: Invalid subimage specification (no spectral * axis). * * For returns > 1, a detailed error message is set in * 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"). * 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 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 * wcspih() does - refer to the prologue of wcshdr.h. As the final step, * wcsset() is invoked, either directly or indirectly, to set the derived * members of the wcsprm struct. wcsset() strips off trailing blanks in all * string members and null-fills the character array. * * int flag * (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, * - wcsprm::pc, * - wcsprm::cdelt, * - wcsprm::crval, * - wcsprm::cunit, * - wcsprm::ctype, * - wcsprm::lonpole, * - wcsprm::latpole, * - wcsprm::restfrq, * - wcsprm::restwav, * - wcsprm::npv, * - wcsprm::pv, * - wcsprm::nps, * - wcsprm::ps, * - wcsprm::cd, * - wcsprm::crota, * - wcsprm::altlin, * - wcsprm::ntab, * - wcsprm::nwtb, * - wcsprm::tab, * - wcsprm::wtb. * * This signals the initialization routine, wcsset(), to recompute the * 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 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. * * int naxis * (Given or returned) Number of pixel and world coordinate elements. * * 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. * * double *crpix * (Given) Address of the first element of an array of double containing * the coordinate reference pixel, CRPIXja. * * double *pc * (Given) Address of the first element of the PCi_ja (pixel coordinate) * transformation matrix. The expected order is * = struct wcsprm wcs; = wcs.pc = {PC1_1, PC1_2, PC2_1, PC2_2}; * * This may be constructed conveniently from a 2-D array via * = double m[2][2] = {{PC1_1, PC1_2}, = {PC2_1, PC2_2}}; * * which is equivalent to * = double m[2][2]; = m[0][0] = PC1_1; = m[0][1] = PC1_2; = m[1][0] = PC2_1; = m[1][1] = PC2_2; * * The storage order for this 2-D array is the same as for the 1-D array, * whence * = wcs.pc = *m; * * would be legitimate. * * double *cdelt * (Given) Address of the first element of an array of double containing * the coordinate increments, CDELTia. * * double *crval * (Given) Address of the first element of an array of double containing * the coordinate reference values, CRVALia. * * char (*cunit)[72] * (Given) Address of the first element of an array of char[72] containing * the CUNITia keyvalues which define the units of measurement of the * CRVALia, CDELTia, and CDi_ja keywords. * * As CUNITia is an optional header keyword, cunit[][72] may be left blank * but otherwise is expected to contain a standard units specification as * defined by WCS Paper I. Utility function wcsutrn(), described in * wcsunits.h, is available to translate commonly used non-standard units * specifications but this must be done as a separate step before invoking * wcsset(). * * For celestial axes, if cunit[][72] is not blank, wcsset() uses * wcsunits() to parse it and scale cdelt[], crval[], and cd[][*] to * degrees. It then resets cunit[][72] to "deg". * * For spectral axes, if cunit[][72] is not blank, wcsset() uses wcsunits() * to parse it and scale cdelt[], crval[], and cd[][*] to SI units. It * then resets cunit[][72] accordingly. * * wcsset() ignores cunit[][72] for other coordinate types; cunit[][72] may * be used to label coordinate values. * * These variables accomodate the longest allowed string-valued FITS * keyword, being limited to 68 characters, plus the null-terminating * character. * * char (*ctype)[72] * (Given) Address of the first element of an array of char[72] containing * the coordinate axis types, CTYPEia. * * The ctype[][72] keyword values must be in upper case and there must be * zero or one pair of matched celestial axis types, and zero or one * spectral axis. The ctype[][72] strings should be padded with blanks on * the right and null-terminated so that they are at least eight characters * in length. * * These variables accomodate the longest allowed string-valued FITS * keyword, being limited to 68 characters, plus the null-terminating * character. * * double lonpole * (Given and returned) The native longitude of the celestial pole, phi_p, * given by LONPOLEa [deg] or by PVi_2a [deg] attached to the longitude * axis which takes precedence if defined, and ... * double latpole * (Given and returned) ... the native latitude of the celestial pole, * 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 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 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 vacuo [m], only one of which need be * given, the other should be set to zero. * * int npv * (Given) The number of entries in the wcsprm::pv[] array. * * int npvmax * (Given or returned) The length of the wcsprm::pv[] array. * * 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) 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() * interprets these as required. * * Note that, if they were not given, wcsset() resets the entries for * PVi_1a, PVi_2a, PVi_3a, and PVi_4a for longitude axis i to match * phi_0 and theta_0 (the native longitude and latitude of the reference * point), LONPOLEa and LATPOLEa respectively. * * int nps * (Given) The number of entries in the wcsprm::ps[] array. * * int npsmax * (Given or returned) The length of the wcsprm::ps[] array. * * 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) 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() * interprets these as required (currently no PSi_ma keyvalues are * recognized). * * double *cd * (Given) For historical compatibility, the wcsprm struct supports two * alternate specifications of the linear transformation matrix, those * associated with the CDi_ja keywords, and ... * double *crota * (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 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 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 CROTAi which are to be ignored. * * - Bit 2: CROTAi is present. * * 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. * * CROTAi may not formally co-exist with PCi_ja. * * CROTAi and CDELTia may formally co-exist with CDi_ja but if so are to * be ignored. * * - 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 CDi_ja keyword is encountered: altlin |= 2; * * - 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, unless bit 3 is also set, * wcsset() resets wcsprm::cdelt to unity if CDi_ja is present (and no * PCi_ja). * * 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 * primary coordinate description, or one of the 26 upper-case letters, * A-Z. * * 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 * used to record the relevant column number. * * 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. * * 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. * * These variables accomodate 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::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. * * 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) 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. * * 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 mjdobs * (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). * * 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. 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. * * It is not necessary to reset the wcsprm struct (via wcsset()) when * wcsprm::specsys is changed. * * char ssysobs[72] * (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. * * 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), * 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. * * 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. * * int nwtb * (Given) See wcsprm::wtb. * * struct tabprm *tab * (Given) Address of the first element of an array of ntab tabprm structs * for which memory has been allocated. These are used to store tabular * transformation parameters. * * Although technically wcsprm::ntab and tab are "given", they will * normally be set by invoking wcstab(), whether directly or indirectly. * * The tabprm structs contain some members that must be supplied and others * that are derived. The information to be supplied comes primarily from * arrays stored in one or more FITS binary table extensions. These * arrays, referred to here as "wcstab arrays", are themselves located by * parameters stored in the FITS image header. * * struct wtbarr *wtb * (Given) Address of the first element of an array of nwtb wtbarr structs * for which memory has been allocated. These are used in extracting * wcstab arrays from a FITS binary table. * * Although technically wcsprm::nwtb and wtb are "given", they will * normally be set by invoking wcstab(), whether directly or indirectly. * * char lngtyp[8] * (Returned) Four-character WCS celestial longitude and ... * char lattyp[8] * (Returned) ... latitude axis types. e.g. "RA", "DEC", "GLON", "GLAT", * etc. extracted from 'RA--', 'DEC-', 'GLON', 'GLAT', etc. in the first * four characters of CTYPEia but with trailing dashes removed. (Declared * as char[8] for alignment reasons.) * * int lng * (Returned) Index for the longitude coordinate, and ... * int lat * (Returned) ... index for the latitude coordinate, and ... * int spec * (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 * the PCi_ja matrix does not transpose axes. * * int cubeface * (Returned) Index into the pixcrd[][] array for the CUBEFACE axis. This * 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. * * - First digit (i.e. 1000s): * - 0: Non-specific coordinate type. * - 1: Stokes coordinate. * - 2: Celestial coordinate (including CUBEFACE). * - 3: Spectral coordinate. * - 4: Time coordinate. * * - Second digit (i.e. 100s): * - 0: Linear axis. * - 1: Quantized axis (STOKES, CUBEFACE). * - 2: Non-linear celestial axis. * - 3: Non-linear spectral axis. * - 4: Logarithmic axis. * - 5: Tabular axis. * * - Third digit (i.e. 10s): * - 0: Group number, e.g. lookup table number, being an index into the * tabprm array (see above). * * - The fourth digit is used as a qualifier depending on the axis type. * * - For celestial axes: * - 0: Longitude coordinate. * - 1: Latitude coordinate. * - 2: CUBEFACE number. * * - For lookup tables: the axis number in a multidimensional table. * * CTYPEia in "4-3" form with unrecognized algorithm code will have its * type set to -1 and generate an error. * * struct linprm lin * (Returned) Linear transformation parameters (usage is described in the * prologue to lin.h). * * struct celprm cel * (Returned) Celestial transformation parameters (usage is described in * the prologue to cel.h). * * struct spcprm spc * (Returned) Spectral transformation parameters (usage is described in the * prologue to spc.h). * * struct wcserr *err * (Returned) If enabled, when an error status is returned, this struct * contains detailed information about the error, see wcserr_enable(). * * int m_flag * (For internal use only.) * int m_naxis * (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.) * double *m_crval * (For internal use only.) * char (*m_cunit)[72] * (For internal use only.) * char (*m_ctype)[72] * (For internal use only.) * struct pvcard *m_pv * (For internal use only.) * struct pscard *m_ps * (For internal use only.) * double *m_cd * (For internal use only.) * double *m_crota * (For internal use only.) * int *m_colax * (For internal use only.) * char (*m_cname)[72] * (For internal use only.) * double *m_crder * (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.) * * * pvcard struct - Store for PVi_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 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 PVi_ma keyword. * * double value * (Given) Parameter value. * * * pscard struct - Store for PSi_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 PSi_ma keyword. * * int m * (Given) Parameter number (non-negative), as in the FITS PSi_ma keyword. * * char value[72] * (Given) Parameter value. * * * 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. * * All members of this struct are to be set by the user. * * double rsun_ref * (Given, auxiliary) Reference radius of the Sun used in coordinate * calculations (m). * * double dsun_obs * (Given, auxiliary) Distance between the centre of the Sun and the * observer (m). * * double crln_obs * (Given, auxiliary) Carrington heliographic longitude of the observer * (deg). * * double hgln_obs * (Given, auxiliary) Stonyhurst heliographic longitude of the observer * (deg). * * double hglt_obs * (Given, auxiliary) Heliographic latitude (Carrington or Stonyhurst) of * the observer (deg). * * 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). * * 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). * * 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). * * 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). * * double blat_obs * Bodycentric latitude of the observer in the coordinate system fixed to * the planet or other solar system body (deg). * * double bdis_obs * Bodycentric distance of the observer (m). * * * Global variable: const char *wcs_errmsg[] - Status return messages * ------------------------------------------------------------------ * Error messages to match the status value returned from each function. * *===========================================================================*/ #ifndef WCSLIB_WCS #define WCSLIB_WCS #include "lin.h" #include "cel.h" #include "spc.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 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 pvcard { int i; // Axis number, as in PVi_ma (1-relative). int m; // Parameter number, ditto (0-relative). double value; // Parameter value. }; // 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. }; // 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; // 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; 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; // 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 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]; struct pvcard *m_pv; struct pscard *m_ps; double *m_cd, *m_crota; int *m_colax; char (*m_cname)[72]; 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. #define WCSLEN (sizeof(struct wcsprm)/sizeof(int)) int wcsnpv(int n); 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[], double imgcrd[], double phi[], double theta[], double world[], int stat[]); 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[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]); 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. #define wcsini_errmsg wcs_errmsg #define wcssub_errmsg wcs_errmsg #define wcscopy_errmsg wcs_errmsg #define wcsfree_errmsg wcs_errmsg #define wcsprt_errmsg wcs_errmsg #define wcsset_errmsg wcs_errmsg #define wcsp2s_errmsg wcs_errmsg #define wcss2p_errmsg wcs_errmsg #define wcsmix_errmsg wcs_errmsg #ifdef __cplusplus #undef wtbarr } #endif #endif // WCSLIB_WCS astropy-astropy-201cddb/cextern/wcslib/C/wcsbth.l000066400000000000000000002324151507226315300221720ustar00rootroot00000000000000/*============================================================================ WCSLIB 8.4 - an implementation of the FITS WCS standard. Copyright (C) 1995-2024, 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/people/Mark.Calabretta $Id: wcsbth.l,v 8.4 2024/10/28 13:56:16 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. */ %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. */ 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} I4 [1-9][0-9]{3} /* Alternate coordinate system identifier. */ ALT [ A-Z] /* Keyvalue data types. */ INTEGER [+-]?[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 %x CROTAi iCROTn TCROTn %x CCi_ma iCn_ma iCCn_ma TCn_ma TCCn_ma %x PROJPm %x CCCCCCCC CCCCCCCa %x CCCCna CCCCCna %x CCCCn CCCCCn %x VALUE INTEGER_VAL FLOAT_VAL FLOAT2_VAL STRING_VAL %x COMMENT DISCARD ERROR FLUSH %{ #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); %} %% 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); ^TFIELDS" = "" "*{INTEGER} { 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); } } ^WCSAXES{ALT}=" "" "*{INTEGER} { 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); } } } ^WCAX{I1}{ALT}" = "" "*{INTEGER} | ^WCAX{I2}{ALT}" = "" "*{INTEGER} | ^WCAX{I3}{ALT}"= "" "*{INTEGER} { 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); } } ^WCST{I1}{ALT}" = "" "*{STRING} | ^WCST{I2}{ALT}" = "" "*{STRING} | ^WCST{I3}{ALT}"= "" "*{STRING} { // 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. keyname = "WCSXna"; errmsg = "cross-references are not implemented"; BEGIN(ERROR); } ^CRPIX { valtype = FLOAT; vptr = &(wcstem.crpix); keyname = "CRPIXja"; BEGIN(CCCCCia); } ^{I1}CRP | ^{I1}CRPX { valtype = FLOAT; vptr = &(wcstem.crpix); sscanf(yytext, "%d", &i); if (yyleng == 4) { keyname = "jCRPna"; BEGIN(iCCCna); } else { keyname = "jCRPXn"; BEGIN(iCCCCn); } } ^TCRP | ^TCRPX { valtype = FLOAT; vptr = &(wcstem.crpix); if (yyleng == 4) { keyname = "TCRPna"; BEGIN(TCCCna); } else { keyname = "TCRPXn"; BEGIN(TCCCCn); } } ^PC { valtype = FLOAT; vptr = &(wcstem.pc); altlin = 1; keyname = "PCi_ja"; BEGIN(CCi_ja); } ^{I2}PC { valtype = FLOAT; vptr = &(wcstem.pc); altlin = 1; sscanf(yytext, "%1d%1d", &i, &j); keyname = "ijPCna"; BEGIN(ijCCna); } ^TP | ^TPC { valtype = FLOAT; vptr = &(wcstem.pc); altlin = 1; if (yyleng == 2) { keyname = "TPn_ka"; BEGIN(TCn_ka); } else { keyname = "TPCn_ka"; BEGIN(TCCn_ka); } } ^CD { valtype = FLOAT; vptr = &(wcstem.cd); altlin = 2; keyname = "CDi_ja"; BEGIN(CCi_ja); } ^{I2}CD { valtype = FLOAT; vptr = &(wcstem.cd); altlin = 2; sscanf(yytext, "%1d%1d", &i, &j); keyname = "ijCDna"; BEGIN(ijCCna); } ^TC | ^TCD { valtype = FLOAT; vptr = &(wcstem.cd); altlin = 2; if (yyleng == 2) { keyname = "TCn_ka"; BEGIN(TCn_ka); } else { keyname = "TCDn_ka"; BEGIN(TCCn_ka); } } ^CDELT { valtype = FLOAT; vptr = &(wcstem.cdelt); keyname = "CDELTia"; BEGIN(CCCCCia); } ^{I1}CDE | ^{I1}CDLT { valtype = FLOAT; vptr = &(wcstem.cdelt); sscanf(yytext, "%d", &i); if (yyleng == 4) { keyname = "iCDEna"; BEGIN(iCCCna); } else { keyname = "iCDLTn"; BEGIN(iCCCCn); } } ^TCDE | ^TCDLT { valtype = FLOAT; vptr = &(wcstem.cdelt); if (yyleng == 4) { keyname = "TCDEna"; BEGIN(TCCCna); } else { keyname = "TCDLTn"; BEGIN(TCCCCn); } } ^CROTA { valtype = FLOAT; vptr = &(wcstem.crota); altlin = 4; keyname = "CROTAi"; BEGIN(CROTAi); } ^{I1}CROT { valtype = FLOAT; vptr = &(wcstem.crota); altlin = 4; sscanf(yytext, "%d", &i); keyname = "iCROTn"; BEGIN(iCROTn); } ^TCROT { valtype = FLOAT; vptr = &(wcstem.crota); altlin = 4; keyname = "TCROTn"; BEGIN(TCROTn); } ^CUNIT { valtype = STRING; vptr = &(wcstem.cunit); keyname = "CUNITia"; BEGIN(CCCCCia); } ^{I1}CUN | ^{I1}CUNI { valtype = STRING; vptr = &(wcstem.cunit); sscanf(yytext, "%d", &i); if (yyleng == 4) { keyname = "iCUNna"; BEGIN(iCCCna); } else { keyname = "iCUNIn"; BEGIN(iCCCCn); } } ^TCUN | ^TCUNI { valtype = STRING; vptr = &(wcstem.cunit); if (yyleng == 4) { keyname = "TCUNna"; BEGIN(TCCCna); } else { keyname = "TCUNIn"; BEGIN(TCCCCn); } } ^CTYPE { valtype = STRING; vptr = &(wcstem.ctype); keyname = "CTYPEia"; BEGIN(CCCCCia); } ^{I1}CTY | ^{I1}CTYP { valtype = STRING; vptr = &(wcstem.ctype); sscanf(yytext, "%d", &i); if (yyleng == 4) { keyname = "iCTYna"; BEGIN(iCCCna); } else { keyname = "iCTYPn"; BEGIN(iCCCCn); } } ^TCTY | ^TCTYP { valtype = STRING; vptr = &(wcstem.ctype); if (yyleng == 4) { keyname = "TCTYna"; BEGIN(TCCCna); } else { keyname = "TCTYPn"; BEGIN(TCCCCn); } } ^CRVAL { valtype = FLOAT; vptr = &(wcstem.crval); keyname = "CRVALia"; BEGIN(CCCCCia); } ^{I1}CRV | ^{I1}CRVL { valtype = FLOAT; vptr = &(wcstem.crval); sscanf(yytext, "%d", &i); if (yyleng == 4) { keyname = "iCRVna"; BEGIN(iCCCna); } else { keyname = "iCRVLn"; BEGIN(iCCCCn); } } ^TCRV | ^TCRVL { valtype = FLOAT; vptr = &(wcstem.crval); if (yyleng == 4) { keyname = "TCRVna"; BEGIN(TCCCna); } else { keyname = "TCRVLn"; BEGIN(TCCCCn); } } ^LONPOLE | ^LONP { valtype = FLOAT; vptr = &(wcstem.lonpole); if (yyleng == 7) { keyname = "LONPOLEa"; imherit = 0; BEGIN(CCCCCCCa); } else { keyname = "LONPna"; BEGIN(CCCCna); } } ^LATPOLE | ^LATP { valtype = FLOAT; vptr = &(wcstem.latpole); if (yyleng == 7) { keyname = "LATPOLEa"; imherit = 0; BEGIN(CCCCCCCa); } else { keyname = "LATPna"; BEGIN(CCCCna); } } ^RESTFREQ | ^RESTFRQ | ^RFRQ { 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); } } ^RESTWAV | ^RWAV { valtype = FLOAT; vptr = &(wcstem.restwav); if (yyleng == 7) { keyname = "RESTWAVa"; BEGIN(CCCCCCCa); } else { keyname = "RWAVna"; BEGIN(CCCCna); } } ^PV { valtype = FLOAT; vptr = &(wcstem.pv); ptype = 'v'; keyname = "PVi_ma"; BEGIN(CCi_ma); } ^{I1}V | ^{I1}PV { 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); } } ^TV | ^TPV { valtype = FLOAT; vptr = &(wcstem.pv); ptype = 'v'; if (yyleng == 2) { keyname = "TVn_ma"; BEGIN(TCn_ma); } else { keyname = "TPVn_ma"; BEGIN(TCCn_ma); } } ^PROJP { valtype = FLOAT; vptr = &(wcstem.pv); ptype = 'v'; keyname = "PROJPm"; BEGIN(PROJPm); } ^PS { valtype = STRING; vptr = &(wcstem.ps); ptype = 's'; keyname = "PSi_ma"; BEGIN(CCi_ma); } ^{I1}S | ^{I1}PS { 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); } } ^TS | ^TPS { valtype = STRING; vptr = &(wcstem.ps); ptype = 's'; if (yyleng == 2) { keyname = "TSn_ma"; BEGIN(TCn_ma); } else { 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); keyname = "CNAMEia"; BEGIN(CCCCCia); } ^{I1}CNA | ^{I1}CNAM { 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); } } ^TCNA | ^TCNAM { valtype = STRING; vptr = &(wcstem.cname); if (yyleng == 4) { keyname = "TCNAna"; BEGIN(TCCCna); } else { if (!(relax & WCSHDR_CNAMn)) vptr = 0x0; keyname = "TCNAMn"; BEGIN(TCCCCn); } } ^CRDER { valtype = FLOAT; vptr = &(wcstem.crder); keyname = "CRDERia"; BEGIN(CCCCCia); } ^{I1}CRD | ^{I1}CRDE { valtype = FLOAT; vptr = &(wcstem.crder); sscanf(yytext, "%d", &i); if (yyleng == 4) { keyname = "iCRDna"; BEGIN(iCCCna); } else { if (!(relax & WCSHDR_CNAMn)) vptr = 0x0; keyname = "iCRDEn"; BEGIN(iCCCCn); } } ^TCRD | ^TCRDE { valtype = FLOAT; vptr = &(wcstem.crder); if (yyleng == 4) { keyname = "TCRDna"; BEGIN(TCCCna); } else { if (!(relax & WCSHDR_CNAMn)) vptr = 0x0; keyname = "TCRDEn"; BEGIN(TCCCCn); } } ^CSYER { valtype = FLOAT; vptr = &(wcstem.csyer); keyname = "CSYERia"; BEGIN(CCCCCia); } ^{I1}CSY | ^{I1}CSYE { valtype = FLOAT; vptr = &(wcstem.csyer); sscanf(yytext, "%d", &i); if (yyleng == 4) { keyname = "iCSYna"; BEGIN(iCCCna); } else { if (!(relax & WCSHDR_CNAMn)) vptr = 0x0; keyname = "iCSYEn"; BEGIN(iCCCCn); } } ^TCSY | ^TCSYE { valtype = FLOAT; vptr = &(wcstem.csyer); if (yyleng == 4) { keyname = "TCSYna"; BEGIN(TCCCna); } else { if (!(relax & WCSHDR_CNAMn)) vptr = 0x0; keyname = "TCSYEn"; BEGIN(TCCCCn); } } ^CZPHS { valtype = FLOAT; vptr = &(wcstem.czphs); keyname = "CZPHSia"; BEGIN(CCCCCia); } ^{I1}CZP | ^{I1}CZPH { 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); } } ^TCZP | ^TCZPH { valtype = FLOAT; vptr = &(wcstem.czphs); if (yyleng == 4) { keyname = "TCZPna"; BEGIN(TCCCna); } else { if (!(relax & WCSHDR_CNAMn)) vptr = 0x0; keyname = "TCZPHn"; BEGIN(TCCCCn); } } ^CPERI { valtype = FLOAT; vptr = &(wcstem.cperi); keyname = "CPERIia"; BEGIN(CCCCCia); } ^{I1}CPR | ^{I1}CPER { valtype = FLOAT; vptr = &(wcstem.cperi); sscanf(yytext, "%d", &i); if (yyleng == 4) { keyname = "iCPRna"; BEGIN(iCCCna); } else { if (!(relax & WCSHDR_CNAMn)) vptr = 0x0; keyname = "iCPERn"; BEGIN(iCCCCn); } } ^TCPR | ^TCPER { valtype = FLOAT; 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) { keyname = "WCSNAMEa"; imherit = 0; BEGIN(CCCCCCCa); } else { if (*yytext == 'W') { keyname = "WCSNna"; } else { keyname = "TWCSna"; } BEGIN(CCCCna); } } ^TIMESYS" " { valtype = STRING; vptr = wcstem.timesys; keyname = "TIMESYS"; BEGIN(CCCCCCCC); } ^TREFPOS" " | ^TRPOS { valtype = STRING; vptr = wcstem.trefpos; if (yyleng == 8) { if (ctrl < -10) keep = keyrec; keyname = "TREFPOS"; BEGIN(CCCCCCCC); } else { keyname = "TRPOSn"; BEGIN(CCCCCn); } } ^TREFDIR" " | ^TRDIR { valtype = STRING; vptr = wcstem.trefdir; if (yyleng == 8) { if (ctrl < -10) keep = keyrec; keyname = "TREFDIR"; BEGIN(CCCCCCCC); } else { keyname = "TRDIRn"; BEGIN(CCCCCn); } } ^PLEPHEM" " { valtype = STRING; vptr = wcstem.plephem; 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(DISCARD); } } ^MJDREF" " | ^MJD-REF" " { 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); } } ^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); } else { BEGIN(DISCARD); } } ^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); } } ^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); } else { BEGIN(DISCARD); } } ^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); } else { BEGIN(DISCARD); } } ^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.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) { keyname = "RADESYSa"; imherit = 0; BEGIN(CCCCCCCa); } else { keyname = "RADEna"; BEGIN(CCCCna); } } ^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.equinox); if (yyleng == 7) { keyname = "EQUINOXa"; imherit = 0; BEGIN(CCCCCCCa); } else { keyname = "EQUIna"; BEGIN(CCCCna); } } ^SPECSYS | ^SPEC { valtype = STRING; vptr = wcstem.specsys; if (yyleng == 7) { keyname = "SPECSYSa"; BEGIN(CCCCCCCa); } else { keyname = "SPECna"; BEGIN(CCCCna); } } ^SSYSOBS | ^SOBS { valtype = STRING; vptr = wcstem.ssysobs; 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 { keyname = "VSYSna"; BEGIN(CCCCna); } } ^VSOURCE{ALT} { 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); } else { BEGIN(DISCARD); } } ^VSOU{I1}{ALT}" " | ^VSOU{I2}{ALT}" " | ^VSOU{I3}{ALT} { 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); } else { // Pretend we don't recognize it. BEGIN(DISCARD); } } ^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.ssyssrc; if (yyleng == 7) { keyname = "SSYSSRCa"; BEGIN(CCCCCCCa); } else { keyname = "SSRCna"; BEGIN(CCCCna); } } ^VELANGL | ^VANG { valtype = FLOAT; vptr = &(wcstem.velangl); if (yyleng == 7) { 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} { 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} { 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", keyname); BEGIN(ERROR); } else { // Pretend we don't recognize it. BEGIN(DISCARD); } } 0{I1}{ALT} | 00{I1} { if (relax & WCSHDR_ALLIMG) { 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. BEGIN(DISCARD); } } {I1}" " | {I2}" " | {I3} | {I1}" " | {I2}" " | {I3} { 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 { BEGIN(DISCARD); } } {I1}[A-Z]" " | {I2}[A-Z] | {I1}[A-Z]" " | {I2}[A-Z] { if (vptr && (relax & WCSHDR_LONGKEY)) { WCSBTH_PUTBACK; BEGIN((YY_START == iCCCCn) ? iCCCna : TCCCna); } else if (relax & WCSHDR_reject) { errmsg = errtxt; if (!vptr) { sprintf(errmsg, "%s keyword is non-standard", keyname); } else { sprintf(errmsg, "%s keyword may not have an alternate version code", keyname); } BEGIN(ERROR); } else { // Pretend we don't recognize it. BEGIN(DISCARD); } } . | . { BEGIN(DISCARD); } {I1}{ALT}" " | {I2}{ALT}" " | {I3}{ALT} | {I1}{ALT}" " | {I2}{ALT}" " | {I3}{ALT} { 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); } . | . { BEGIN(DISCARD); } {I1}_{I1}{ALT}" " | {I1}_{I2}{ALT}" " | {I2}_{I1}{ALT}" " | {I2}_{I2}{ALT} { 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); } } 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, "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) { 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. BEGIN(DISCARD); } } {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, "deprecated image-header keyword %s in binary table", keyname); BEGIN(ERROR); } else { // Pretend we don't recognize it. BEGIN(DISCARD); } } . { BEGIN(DISCARD); } {I1}{ALT}" " | {I2}{ALT}" " | {I3}{ALT} { sscanf(yytext, "%d%c", &n, &a); keytype = BIMGARR; BEGIN(VALUE); } {I1}_{I1}{ALT}" " | {I1}_{I2}{ALT} | {I2}_{I1}{ALT} | {I1}_{I3} | {I2}_{I2} | {I3}_{I1} { if (relax & WCSHDR_LONGKEY) { WCSBTH_PUTBACK; BEGIN(TCn_ka); } else if (relax & WCSHDR_reject) { errmsg = errtxt; sprintf(errmsg, "%s keyword is non-standard", keyname); BEGIN(ERROR); } else { // Pretend we don't recognize it. BEGIN(DISCARD); } } . { BEGIN(DISCARD); } {I1}_{I1}{ALT}" " | {I1}_{I2}{ALT}" " | {I2}_{I1}{ALT}" " | {I1}_{I3}{ALT} | {I2}_{I2}{ALT} | {I3}_{I1}{ALT} { 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); } {I1}_{I4} | {I2}_{I3} | {I3}_{I2} | {I4}_{I1} { sscanf(yytext, "%d_%d", &n, &k); a = ' '; i = wcsbth_colax(*wcs, &alts, n, a); j = wcsbth_colax(*wcs, &alts, k, a); keytype = PIXLIST; BEGIN(VALUE); } . { BEGIN(DISCARD); } {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 = errtxt; sprintf(errmsg, "deprecated image-header keyword %s in binary table", keyname); BEGIN(ERROR); } else { // Pretend we don't recognize it. BEGIN(DISCARD); } } . { if (relax & WCSHDR_ALLIMG) { yyless(0); BEGIN(CCCCCia); } else { // Let it go. BEGIN(DISCARD); } } {I1}" " | {I2}" " | {I3} | {I1}" " | {I2}" " | {I3} { WCSBTH_PUTBACK; BEGIN((YY_START == iCROTn) ? iCCCna : TCCCna); } {I1}[A-Z]" " | {I2}[A-Z] | {I1}[A-Z]" " | {I2}[A-Z] { if (relax & WCSHDR_CROTAia) { WCSBTH_PUTBACK; BEGIN((YY_START == iCROTn) ? iCCCna : TCCCna); } else if (relax & WCSHDR_reject) { errmsg = errtxt; sprintf(errmsg, "%s keyword may not have an alternate version code", keyname); BEGIN(ERROR); } else { // Pretend we don't recognize it. BEGIN(DISCARD); } } . | . { BEGIN(DISCARD); } {ALT} | . { // Image-header keyword. if (imherit || (relax & (WCSHDR_AUXIMG | WCSHDR_ALLIMG))) { if (YY_START == CCCCCCCa) { sscanf(yytext, "%c", &a); } else { a = 0; unput(yytext[0]); } keytype = IMGAUX; 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); } } . { 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}" " | {I2}{ALT}" " | {I3}{ALT} | {I1}{ALT}" " | {I2}{ALT} { sscanf(yytext, "%d%c", &n, &a); keytype = BINTAB; BEGIN(VALUE); } {I3} { sscanf(yytext, "%d", &n); a = ' '; keytype = BINTAB; BEGIN(VALUE); } . | . { BEGIN(DISCARD); } {I1}" " | {I2}" " | {I3}" " | {I4} | {I1}" " | {I2}" " | {I3} { sscanf(yytext, "%d", &n); a = 0; keytype = BINTAB; BEGIN(VALUE); } . | . { BEGIN(DISCARD); } {I1}_{Z1}{ALT}" " | {I1}_{I2}{ALT}" " | {I2}_{Z1}{ALT}" " | {I2}_{I2}{ALT} { // Image-header keyword. if (relax & WCSHDR_ALLIMG) { 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", keyname); BEGIN(ERROR); } else { // Pretend we don't recognize it. BEGIN(DISCARD); } } 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) { 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. 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}_{Z1}{ALT}" " | {I1}_{I2}{ALT} | {I1}_{I3} | {I2}_{Z1}{ALT} | {I2}_{I2} | {I3}_{Z1} | {I1}_{Z1}{ALT}" " | {I1}_{I2}{ALT} | {I1}_{I3} | {I2}_{Z1}{ALT} | {I2}_{I2} | {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, "the %s keyword is non-standard", keyname); BEGIN(ERROR); } else { // Pretend we don't recognize it. BEGIN(DISCARD); } } . | . { BEGIN(DISCARD); } {I1}_{Z1}{ALT}" " | {I1}_{I2}{ALT}" " | {I1}_{I3}{ALT} | {I2}_{Z1}{ALT}" " | {I2}_{I2}{ALT} | {I3}_{Z1}{ALT} | {I1}_{Z1}{ALT}" " | {I1}_{I2}{ALT}" " | {I1}_{I3}{ALT} | {I2}_{Z1}{ALT}" " | {I2}_{I2}{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; BEGIN(VALUE); } {I1}_{I4} | {I2}_{I3} | {I3}_{I2} | {I4}_{Z1} | {I1}_{I4} | {I2}_{I3} | {I3}_{I2} | {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); keytype = (YY_START == iCn_ma) ? BIMGARR : PIXLIST; BEGIN(VALUE); } . | . { BEGIN(DISCARD); } {Z1}" " { if (relax & WCSHDR_PROJPn) { sscanf(yytext, "%d", &m); i = 0; a = ' '; keytype = IMGAXIS; BEGIN(VALUE); } else if (relax & WCSHDR_reject) { 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 { BEGIN(DISCARD); } } . { BEGIN(DISCARD); } =" "+ { // Do checks on i, j, m, n, k. if (!(keytype & keysel)) { // Selection by keyword type. BEGIN(DISCARD); } else if (exclude[n] || exclude[k]) { // 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. return WCSHDRERR_BAD_COLUMN; } else { BEGIN(DISCARD); } } else if (i > 99 || j > 99 || m > 99 || n > 999 || k > 999) { if (relax & WCSHDR_reject) { errmsg = errtxt; if (i > 99 || j > 99) { sprintf(errmsg, "axis number exceeds 99"); } else if (m > 99) { sprintf(errmsg, "parameter number exceeds 99"); } else if (n > 999 || k > 999) { sprintf(errmsg, "column number exceeds 999"); } BEGIN(ERROR); } else { // 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. // 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", valtype); BEGIN(ERROR); } } } . { errmsg = "invalid KEYWORD = VALUE syntax"; BEGIN(ERROR); } {INTEGER} { if (ipass == 1) { BEGIN(COMMENT); } else { // Read the keyvalue. sscanf(yytext, "%d", &inttmp); BEGIN(COMMENT); } } . { errmsg = "an integer value was expected"; BEGIN(ERROR); } {FLOAT} { if (ipass == 1) { BEGIN(COMMENT); } else { // 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); BEGIN(COMMENT); } } . { errmsg = "a floating-point value was expected"; BEGIN(ERROR); } {STRING} { if (ipass == 1) { BEGIN(COMMENT); } else { // Copy the keyvalue minus the quotes. strncpy(strtmp, yytext+1, yyleng-2); strtmp[yyleng-2] = '\0'; // Strip off trailing blanks. for (int jx = yyleng-3; jx >= 0; jx--) { if (strtmp[jx] != ' ') { break; } strtmp[jx] = '\0'; } // Squeeze out repeated quotes. int ix = 0; for (int jx = 0; jx < 72; jx++) { if (ix < jx) { strtmp[ix] = strtmp[jx]; } if (strtmp[jx] == '\0') { break; } else if (strtmp[jx] == '\'' && strtmp[jx+1] == '\'') { jx++; } ix++; } BEGIN(COMMENT); } } . { errmsg = "a string value was expected"; BEGIN(ERROR); } {INLINE}$ { if (ipass == 1) { // Do first-pass bookkeeping. wcsbth_pass1(keytype, i, j, n, k, a, ptype, &alts); BEGIN(FLUSH); } 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))) { 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); } 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); } } } if (ipass == npass) { if (gotone) { nvalid++; if (ctrl == 4) { wcsfprintf(stderr, "%.80s\n Accepted (%d) as a valid WCS keyrecord.\n", keyrec, nvalid); } BEGIN(FLUSH); } else { errmsg = "syntactically valid WCS keyrecord has no effect"; BEGIN(ERROR); } } else { BEGIN(FLUSH); } } else { BEGIN(FLUSH); } } .*" "*\/.*$ { errmsg = "invalid keyvalue"; BEGIN(ERROR); } [^ \/\n]*{INLINE}$ { errmsg = "invalid keyvalue"; BEGIN(ERROR); } " "+[^\/\n].*{INLINE}$ { errmsg = "invalid keyvalue or malformed keycomment"; BEGIN(ERROR); } .*$ { errmsg = "malformed keycomment"; BEGIN(ERROR); } .*$ { if (ipass == npass) { if (ctrl < 0) { // Preserve discards. keep = keyrec; } else if (2 < ctrl) { nother++; wcsfprintf(stderr, "%.80s\n Not a recognized WCS keyword.\n", keyrec); } } BEGIN(FLUSH); } .*$ { if (ipass == npass) { (*nreject)++; if (ctrl%10 == -1) { keep = keyrec; } if (1 < abs(ctrl%10)) { wcsfprintf(stderr, "%.80s\n Rejected (%d), %s.\n", keyrec, *nreject, errmsg); } } BEGIN(FLUSH); } .*\n { 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 = ' '; chekval = 0x0; special = 0x0; BEGIN(INITIAL); } <> { // End-of-input. if (ipass == 1) { int status; if ((status = wcsbth_init1(&alts, naux, nwcs, wcs)) || (*nwcs == 0 && ctrl == 0)) { return status; } if (2 < abs(ctrl%10)) { if (*nwcs == 1) { if (strcmp(wcs[0]->wcsname, "DEFAULTS") != 0) { wcsfprintf(stderr, "Found one coordinate representation.\n"); } } else { wcsfprintf(stderr, "Found %d coordinate representations.\n", *nwcs); } } if (alts.imgherit) npass = 3; } if (ipass++ < npass) { yyextra->hdr = header; yyextra->nkeyrec = nkeyrec; keyrec = header; *nreject = 0; imherit = 1; i = j = 0; k = n = 0; m = 0; a = ' '; keytype = 0; valtype = -1; vptr = 0x0; altlin = 0; ptype = ' '; chekval = 0x0; special = 0x0; yyrestart(yyin, yyscanner); } else { if (ctrl < 0) { *hptr = '\0'; } else if (ctrl == 1) { 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); } } %% /*---------------------------------------------------------------------------- * 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: * * 1) Count the number of coordinate axes in each of the 27 possible alternate * image-header coordinate representations. Also count the number of PVi_ma * and PSi_ma keywords in each representation. * * 2) Determine the number of binary table columns that have an image array * with a coordinate representation (up to 999), and count the number of * coordinate axes in each of the 27 possible alternates. Also count the * number of iVn_ma and iSn_ma keywords in each representation. * * 3) Determine the number of alternate pixel list coordinate representations * (up to 27) and the table columns associated with each. Also count the * number of TVn_ma and TSn_ma keywords in each representation. * * In the first pass alts->arridx[icol][27] is used to determine the number of * axes in each of 27 possible image-header coordinate descriptions (icol == 0) * and each of the 27 possible coordinate representations for an image array in * each column. * * The elements of alts->pixlist[icol] are used as bit arrays to flag which of * the 27 possible pixel list coordinate representations are associated with * each table column. *---------------------------------------------------------------------------*/ int wcsbth_pass1( int keytype, int i, int j, int n, int k, char a, char ptype, struct wcsbth_alts *alts) { if (a == 0) { // Keywords such as DATE-OBS go along for the ride. return 0; } int ncol = alts->ncol; // 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. if (n || k) { // The header is mangled, assume the worst. ncol = 999; } } if (!(alts->arridx = calloc((1 + ncol)*27, sizeof(short int))) || !(alts->npv = calloc((1 + ncol)*27, sizeof(unsigned char))) || !(alts->nps = calloc((1 + ncol)*27, sizeof(unsigned char))) || !(alts->pixlist = calloc((1 + ncol), sizeof(unsigned int)))) { if (alts->arridx) free(alts->arridx); if (alts->npv) free(alts->npv); if (alts->nps) free(alts->nps); if (alts->pixlist) free(alts->pixlist); 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. ncol = 999; if (!(alts->arridx = realloc(alts->arridx, 27*(1 + ncol)*sizeof(short int))) || !(alts->npv = realloc(alts->npv, 27*(1 + ncol)*sizeof(unsigned char))) || !(alts->nps = realloc(alts->nps, 27*(1 + ncol)*sizeof(unsigned char))) || !(alts->pixlist = realloc(alts->pixlist, (1 + ncol)*sizeof(unsigned int)))) { if (alts->arridx) free(alts->arridx); if (alts->npv) free(alts->npv); if (alts->nps) free(alts->nps); if (alts->pixlist) free(alts->pixlist); return WCSHDRERR_MEMORY; } // 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; alts->pixlist[icol] = 0; } } alts->ncol = ncol; } 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. if (keytype & IMGHEAD || keytype & BIMGARR) { // 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. alts->arridx[n][ialt] = -1; } } else { // Record the maximum axis number found. if (alts->arridx[n][ialt] < i) { alts->arridx[n][ialt] = i; } if (alts->arridx[n][ialt] < j) { alts->arridx[n][ialt] = j; } } if (ptype == 'v') { alts->npv[n][ialt]++; } else if (ptype == 's') { alts->nps[n][ialt]++; } } // 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) { int mask = 1 << ialt; // n > 0 for PIXLIST keytypes. alts->pixlist[n] |= mask; if (k) alts->pixlist[k] |= mask; // Used as a flag over all columns. alts->pixlist[0] |= mask; if (ptype == 'v') { alts->pixnpv[ialt]++; } else if (ptype == 's') { alts->pixnps[ialt]++; } } return 0; } /*---------------------------------------------------------------------------- * Perform initializations at the end of the first pass: * * 1) Determine the required number of wcsprm structs, allocate memory for * an array of them and initialize each one. *---------------------------------------------------------------------------*/ int wcsbth_init1( struct wcsbth_alts *alts, int naux, int *nwcs, struct wcsprm **wcs) { int status = 0; if (alts->arridx == 0x0) { *nwcs = 0; return 0; } // 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 (int icol = 1; icol <= ncol; icol++) { if (alts->pixlist[icol] & mask) { alts->pixidx[ialt]++; } } } } // Find the total number of coordinate representations. *nwcs = 0; alts->imgherit = 0; int inherit[27]; for (int ialt = 0; ialt < 27; ialt++) { inherit[ialt] = 0; for (int icol = 1; icol <= ncol; icol++) { if (alts->arridx[icol][ialt] < 0) { // 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. alts->arridx[icol][ialt] = alts->arridx[0][ialt]; } else { alts->arridx[icol][ialt] = 0; } } if (alts->arridx[icol][ialt]) { if (alts->arridx[0][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. alts->arridx[icol][ialt] = alts->arridx[0][ialt]; } } (*nwcs)++; } } // 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. alts->arridx[0][ialt] = 0; alts->imgherit = 1; } else if (alts->arridx[0][ialt] > 0) { (*nwcs)++; } } // We need a struct for every "a" found in a PIXLIST keyword. if (alts->pixidx[ialt]) { (*nwcs)++; } } if (*nwcs) { // Allocate memory for the required number of wcsprm structs. if (!(*wcs = calloc(*nwcs, sizeof(struct wcsprm)))) { return WCSHDRERR_MEMORY; } // Initialize each wcsprm struct. struct wcsprm *wcsp = *wcs; *nwcs = 0; 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; 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. if (ialt) { wcsp->alt[0] = 'A' + ialt - 1; } // 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. alts->arridx[icol][ialt] = (*nwcs)++; wcsp++; } else { // Signal that this column has no WCS for this "a". alts->arridx[icol][ialt] = -1; } } } for (int ialt = 0; ialt < 27; ialt++) { if (alts->pixidx[ialt]) { // Pixel lists representations. wcsp->flag = -1; 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. if (ialt) { wcsp->alt[0] = 'A' + ialt - 1; } // 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[ialt] = (*nwcs)++; wcsp++; } else { // Signal that this column is not a pixel list axis for this "a". alts->pixidx[ialt] = -1; } } } return status; } /*---------------------------------------------------------------------------- * Return a pointer to the next wcsprm struct for a particular column number * and alternate. *---------------------------------------------------------------------------*/ struct wcsprm *wcsbth_idx( struct wcsprm *wcs, struct wcsbth_alts *alts, int keytype, int n, char a) { const char as[] = " ABCDEFGHIJKLMNOPQRSTUVWXYZ"; if (!wcs) return 0x0; 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. 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. 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. if (iwcs >= 0) break; // Start from scratch for the next alts->ialt. alts->icol = 0; } if (keytype & (IMGAUX | PIXLIST)) { iwcs = alts->pixidx[alts->ialt]; } } return (iwcs >= 0) ? (wcs + iwcs) : 0x0; } /*---------------------------------------------------------------------------- * Return the axis number associated with the specified column number in a * particular pixel list coordinate representation. *---------------------------------------------------------------------------*/ int wcsbth_colax( struct wcsprm *wcs, struct wcsbth_alts *alts, int n, char a) { if (!wcs) return 0; struct wcsprm *wcsp = wcs; if (a != ' ') { wcsp += alts->pixidx[a-'A'+1]; } for (int ix = 0; ix < wcsp->naxis; ix++) { if (wcsp->colax[ix] == n) { return ++ix; } } return 0; } /*---------------------------------------------------------------------------- * Interpret the JDREF, JDREFI, and JDREFF keywords. *---------------------------------------------------------------------------*/ 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) { // Set the fractional part of MJDREF from JDREFF. if (undefined(mjdref[1])) { mjdref[1] = *jdreff; } return 0; } /*---------------------------------------------------------------------------- * Interpret EPOCHa keywords. *---------------------------------------------------------------------------*/ int wcsbth_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 wcsbth_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 wcsbth_timepixr(double timepixr) { return (timepixr < 0.0 || 1.0 < timepixr); } /*---------------------------------------------------------------------------- * Tie up loose ends. *---------------------------------------------------------------------------*/ int wcsbth_final( struct wcsbth_alts *alts, int *nwcs, struct wcsprm **wcs) { 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 (int ialt = 0; ialt < *nwcs; ialt++) { // Interpret -TAB header keywords. int status; if ((status = wcstab(*wcs+ialt))) { wcsvfree(nwcs, wcs); return status; } } return 0; } astropy-astropy-201cddb/cextern/wcslib/C/wcserr.c000066400000000000000000000107121507226315300221660ustar00rootroot00000000000000/*============================================================================ WCSLIB 8.4 - an implementation of the FITS WCS standard. Copyright (C) 1995-2024, 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. Module author: Michael Droettboom http://www.atnf.csiro.au/people/Mark.Calabretta $Id: wcserr.c,v 8.4 2024/10/28 13:56:16 mcalabre Exp $ *===========================================================================*/ #include #include #include #include #include "wcserr.h" #include "wcsprintf.h" static int wcserr_enabled = 0; //---------------------------------------------------------------------------- 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) { if (!wcserr_enabled) { wcsprintf("Error messaging is not enabled, use wcserr_enable().\n"); return 2; } if (err == 0x0) { return 0; } if (err->status) { if (prefix == 0x0) prefix = ""; if (err->status > 0) { wcsprintf("%sERROR %d in %s() at line %d of file %s:\n%s%s.\n", prefix, err->status, err->function, err->line_no, err->file, prefix, err->msg); } else { // 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); } } return 0; } //---------------------------------------------------------------------------- int wcserr_clear(struct wcserr **errp) { if (*errp) { if ((*errp)->msg) { free((*errp)->msg); } free(*errp); *errp = 0x0; } return 0; } //---------------------------------------------------------------------------- int wcserr_set( struct wcserr **errp, int status, const char *function, const char *file, int line_no, const char *format, ...) { int msglen; struct wcserr *err; va_list argp; if (!wcserr_enabled) return status; if (errp == 0x0) { return status; } err = *errp; if (status) { if (err == 0x0) { *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; // 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); msglen = vsnprintf(err->msg, msglen, format, argp); va_end(argp); if (msglen < 0) { wcserr_clear(errp); } } return status; } //---------------------------------------------------------------------------- int wcserr_copy(const struct wcserr *src, struct wcserr *dst) { size_t msglen; if (src == 0x0) { if (dst) { memset(dst, 0, sizeof(struct wcserr)); } return 0; } 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; } astropy-astropy-201cddb/cextern/wcslib/C/wcserr.h000066400000000000000000000231231507226315300221730ustar00rootroot00000000000000/*============================================================================ WCSLIB 8.4 - an implementation of the FITS WCS standard. Copyright (C) 1995-2024, 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. Module author: Michael Droettboom http://www.atnf.csiro.au/people/Mark.Calabretta $Id: wcserr.h,v 8.4 2024/10/28 13:56:16 mcalabre Exp $ *============================================================================= * * WCSLIB 8.4 - 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 * identifies the function, source file, and line number where the error * occurred. * * For example: * = struct prjprm prj; = wcserr_enable(1); = if (prjini(&prj)) { = // Print the error message to stderr. = wcsprintf_set(stderr); = wcserr_prt(prj.err, 0x0); = } * * A number of utility functions used in managing the wcserr struct are for * internal use only. 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! * * * wcserr struct - Error message handling * -------------------------------------- * The wcserr struct contains the numeric error code, a textual description of * the error, and information about the function, source file, and line number * where the error was generated. * * int status * Numeric status code associated with the error, the meaning of which * depends on the function that generated it. See the documentation for * the particular function. * * int line_no * Line number where the error occurred as given by the __LINE__ * preprocessor macro. * * const char *function * Name of the function where the error occurred. * * const char *file * Name of the source file where the error occurred as given by the * __FILE__ preprocessor macro. * * char *msg * Informative error message. * * * wcserr_enable() - Enable/disable error messaging * ------------------------------------------------ * wcserr_enable() enables or disables wcserr error messaging. By default it * is disabled. * * PLEASE NOTE: This function is not thread-safe. * * Given: * enable int If true (non-zero), enable error messaging, else * disable it. * * Function return value: * int Status return value: * 0: Error messaging is disabled. * 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. * It uses the wcsprintf() functions. * * Given: * err const struct wcserr* * The error object. If NULL, nothing is printed. * * prefix const char * * If non-NULL, each output line will be prefixed with * this string. * * Function return value: * int Status return value: * 0: Success. * 2: Error messaging is not enabled. * * * wcserr_clear() - Clear a wcserr struct * -------------------------------------- * wcserr_clear() clears (deletes) a wcserr struct. * * Given and returned: * err struct wcserr** * The error object. If NULL, nothing is done. Set to * NULL on return. * * Function return value: * int Status return value: * 0: Success. * * * wcserr_set() - Fill in the contents of an error object * ------------------------------------------------------ * INTERNAL USE ONLY. * * wcserr_set() fills a wcserr struct with information about an error. * * A convenience macro, WCSERR_SET, provides the source file and line number * information automatically. * * Given and returned: * err struct wcserr** * Error object. * * If err is NULL, returns the status code given without * setting an error message. * * If *err is NULL, allocates memory for a wcserr struct * (provided that status is non-zero). * * Given: * status int Numeric status code to set. If 0, then *err will be * deleted and *err will be returned as NULL. * * function const char * * Name of the function generating the error. This * must point to a constant string, i.e. in the * initialized read-only data section ("data") of the * executable. * * file const char * * Name of the source file generating the error. This * must point to a constant string, i.e. in the * initialized read-only data section ("data") of the * executable such as given by the __FILE__ preprocessor * macro. * * line_no int Line number in the source file generating the error * such as given by the __LINE__ preprocessor macro. * * format const char * * Format string of the error message. May contain * printf-style %-formatting codes. * * ... mixed The remaining variable arguments are applied (like * printf) to the format string to generate the error * message. * * Function return value: * int The status return code passed in. * * * wcserr_copy() - Copy an error object * ------------------------------------ * INTERNAL USE ONLY. * * wcserr_copy() copies one error object to another. Use of this function * should be avoided in general since the function, source file, and line * number information copied to the destination may lose its context. * * Given: * src const struct wcserr* * Source error object. If src is NULL, dst is cleared. * * Returned: * dst struct wcserr* * Destination error object. If NULL, no copy is made. * * Function return value: * int Numeric status code of the source error object. * * * WCSERR_SET() macro - Fill in the contents of an error object * ------------------------------------------------------------ * INTERNAL USE ONLY. * * WCSERR_SET() is a preprocessor macro that helps to fill in the argument list * of wcserr_set(). It takes status as an argument of its own and provides the * name of the source file and the line number at the point where invoked. It * assumes that the err and function arguments of wcserr_set() will be provided * by variables of the same names. * *===========================================================================*/ #ifndef WCSLIB_WCSERR #define WCSLIB_WCSERR #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; // Informative error message. }; // 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 ------------------------------------------------------- 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(). #define WCSERR_SET(status) err, status, function, __FILE__, __LINE__ #ifdef __cplusplus } #endif #endif // WSCLIB_WCSERR astropy-astropy-201cddb/cextern/wcslib/C/wcsfix.c000066400000000000000000001171141507226315300221700ustar00rootroot00000000000000/*============================================================================ WCSLIB 8.4 - an implementation of the FITS WCS standard. Copyright (C) 1995-2024, 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/people/Mark.Calabretta $Id: wcsfix.c,v 8.4 2024/10/28 13:56:16 mcalabre Exp $ *===========================================================================*/ #include #include #include #include #include "lin.h" #include "sph.h" #include "tab.h" #include "wcs.h" #include "wcserr.h" #include "wcsfix.h" #include "wcsmath.h" #include "wcstrig.h" #include "wcsunits.h" #include "wcsutil.h" #include "wtbarr.h" // Maximum number of coordinate axes that can be handled. #define NMAX 16 // Map status return value to message. const char *wcsfix_errmsg[] = { "Success", "Null wcsprm pointer passed", "Memory allocation failed", "Linear transformation matrix is singular", "Inconsistent or unrecognized coordinate axis types", "Invalid parameter value", "Invalid coordinate transformation parameters", "Ill-conditioned coordinate transformation parameters", "All of the corner pixel coordinates are invalid", "Could not determine reference pixel coordinate", "Could not determine reference pixel value"}; // 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[]) { int status = 0; if ((stat[CDFIX] = cdfix(wcs)) > 0) { status = 1; } if ((stat[DATFIX] = datfix(wcs)) > 0) { status = 1; } if ((stat[OBSFIX] = obsfix(0, wcs)) > 0) { status = 1; } if ((stat[UNITFIX] = unitfix(ctrl, wcs)) > 0) { status = 1; } if ((stat[SPCFIX] = spcfix(wcs)) > 0) { status = 1; } if ((stat[CELFIX] = celfix(wcs)) > 0) { status = 1; } if ((stat[CYLFIX] = cylfix(naxis, wcs)) > 0) { status = 1; } return status; } //---------------------------------------------------------------------------- int wcsfixi( int ctrl, const int naxis[], struct wcsprm *wcs, int stat[], struct wcserr info[]) { 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. // 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 (int ifix = CDFIX; ifix < NWCSFIX; ifix++) { // Clear (delete) wcs->err. wcserr_clear(&(wcs->err)); switch (ifix) { case CDFIX: stat[ifix] = cdfix(wcs); break; case DATFIX: stat[ifix] = datfix(wcs); break; case OBSFIX: stat[ifix] = obsfix(0, wcs); break; case UNITFIX: stat[ifix] = unitfix(ctrl, wcs); break; case SPCFIX: stat[ifix] = spcfix(wcs); break; case CELFIX: stat[ifix] = celfix(wcs); break; case CYLFIX: stat[ifix] = cylfix(naxis, wcs); break; default: continue; } if (stat[ifix] == FIXERR_NO_CHANGE) { // No change => no message. wcserr_copy(0x0, info+ifix); } 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 { wcserr_copy(0x0, info+ifix); } } else { // 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. wcserr_copy(wcs->err, &err); } } } // Restore the last error to occur. if (err.status) { wcserr_copy(&err, wcs->err); } else { wcserr_clear(&(wcs->err)); } return status; } //---------------------------------------------------------------------------- int cdfix(struct wcsprm *wcs) { 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. return FIXERR_NO_CHANGE; } 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? cd = wcs->cd + i; for (int k = 0; k < naxis; k++, cd += naxis) { if (*cd != 0.0) goto next; } cd = wcs->cd + i * (naxis + 1); *cd = 1.0; status = FIXERR_SUCCESS; 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, sec)) { return 1; } return 0; } static void write_date(char *buf, int hour, int minute, double sec) { 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"; // 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; 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); } char orig_date[72]; strncpy(orig_date, date, 72); 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; } } 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]; } 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); } } } } 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'; } } // 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 { // Not a valid date format. status = FIXERR_BAD_PARAM; sprintf(newline(&cp), "Invalid DATE%s format '%s'", dateid, date); continue; } if (year < 100) year += 1900; // Doesn't have a time. sprintf(date, "%.4d-%.2d-%.2d", year, month, day); } // 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; } 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 { 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); } } if (i == 1) { if (!undefined(wcs->jepoch)) { // Check consistency of JEPOCH. double jepoch = 2000.0 + (*wcsmjd - mjd2000) / djy; if (0.000002 < fabs(jepoch - wcs->jepoch)) { // Informational only, no error. sprintf(newline(&cp), "JEPOCH is inconsistent with DATE-OBS"); } } 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 { sprintf(newline(&cp), "Changed DATE%s from '%s' to '%s'", dateid, orig_date, date); } 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"); } if (havexyz == 0 || havelbh == 0) { return FIXERR_NO_CHANGE; } } if (havexyz == 0 && havelbh == 0) { return FIXERR_NO_CHANGE; } 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"); } // 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 status; } //---------------------------------------------------------------------------- int unitfix(int ctrl, struct wcsprm *wcs) { const char *function = "unitfix"; if (wcs == 0x0) return FIXERR_NULL_POINTER; 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) { // Chop off the trailing ", ". size_t msglen = strlen(msg) - 2; msg[msglen] = '\0'; wcserr_set(WCSERR_SET(FIXERR_UNITS_ALIAS), msg); status = FIXERR_SUCCESS; } return status; } //---------------------------------------------------------------------------- int spcfix(struct wcsprm *wcs) { static const char *function = "spcfix"; if (wcs == 0x0) return FIXERR_NULL_POINTER; 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? if (wcs->specsys[0] == '\0' && *specsys) { strncpy(wcs->specsys, specsys, 9); wcserr_set(WCSERR_SET(FIXERR_SPC_UPDATE), "Changed SPECSYS to '%s'", specsys); status = FIXERR_SUCCESS; } // 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... if (status == FIXERR_SUCCESS) { // ...and specsys was also. wcserr_set(WCSERR_SET(FIXERR_SPC_UPDATE), "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' (VELREF=%d)", i+1, wcs->ctype[i], ctype, wcs->velref); status = FIXERR_SUCCESS; } strncpy(wcs->ctype[i], ctype, 9); } // 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. return status; } else if (status == SPCERR_BAD_SPEC_PARAMS) { // 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); } } return FIXERR_NO_CHANGE; } //---------------------------------------------------------------------------- int celfix(struct wcsprm *wcs) { static const char *function = "celfix"; if (wcs == 0x0) return FIXERR_NULL_POINTER; struct wcserr **err = &(wcs->err); // 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? if (wcs->lat >= 0) { // 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_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; return wcserr_set(WCSFIX_ERRMSG(FIXERR_MEMORY)); } wcs->npvmax = wcs->npv + 2; wcs->m_flag = WCSSET; for (int k = 0; k < wcs->npv; k++) { wcs->pv[k] = wcs->m_pv[k]; } if (wcs->m_pv) free(wcs->m_pv); wcs->m_pv = wcs->pv; } else { return wcserr_set(WCSFIX_ERRMSG(FIXERR_MEMORY)); } } 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]; (wcs->npv)++; wcs->pv[wcs->npv].i = wcs->lat + 1; wcs->pv[wcs->npv].m = 2; wcs->pv[wcs->npv].value = wcsprj->pv[2]; (wcs->npv)++; return FIXERR_SUCCESS; } else if (strcmp(wcs->ctype[wcs->lat]+5, "GLS") == 0) { strcpy(wcs->ctype[wcs->lng]+5, "SFL"); 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 + 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; return wcserr_set(WCSFIX_ERRMSG(FIXERR_MEMORY)); } wcs->npvmax = wcs->npv + 3; wcs->m_flag = WCSSET; for (int k = 0; k < wcs->npv; k++) { wcs->pv[k] = wcs->m_pv[k]; } if (wcs->m_pv) free(wcs->m_pv); wcs->m_pv = wcs->pv; } else { return wcserr_set(WCSFIX_ERRMSG(FIXERR_MEMORY)); } } wcs->pv[wcs->npv].i = wcs->lng + 1; wcs->pv[wcs->npv].m = 0; wcs->pv[wcs->npv].value = 1.0; (wcs->npv)++; // 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; (wcs->npv)++; wcs->pv[wcs->npv].i = wcs->lng + 1; wcs->pv[wcs->npv].m = 2; wcs->pv[wcs->npv].value = wcs->crval[wcs->lat]; (wcs->npv)++; } return FIXERR_SUCCESS; } } return FIXERR_NO_CHANGE; } //---------------------------------------------------------------------------- int cylfix(const int naxis[], struct wcsprm *wcs) { static const char *function = "cylfix"; if (naxis == 0x0) return FIXERR_NO_CHANGE; if (wcs == 0x0) return FIXERR_NULL_POINTER; struct wcserr **err = &(wcs->err); // 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. 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. unsigned short ncnr = 1 << wcs->naxis; unsigned short indx[NMAX]; for (int k = 0; k < NMAX; k++) { indx[k] = 1 << k; } int stat[4]; double img[4][NMAX], phi[4], pix[4][NMAX], theta[4], world[4][NMAX]; 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 { *(pixj++) = 0.5; } } } if (!(status = wcsp2s(wcs, 4, NMAX, pix[0], img[0], phi, theta, world[0], stat))) { 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 fix_wcserr[status]; // Any changes needed? if (phimin >= -180.0 && phimax <= 180.0) return FIXERR_NO_CHANGE; // 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) { status = FIXERR_BAD_PARAM; } else { status = FIXERR_NO_REF_PIX_COORD; } return wcserr_set(WCSFIX_ERRMSG(status)); } 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(fix_linerr[status])); } // Compute celestial coordinates at the new reference pixel. if ((status = wcsp2s(wcs, 1, 0, pix[0], img[0], phi, theta, world[0], stat))) { return fix_wcserr[status]; } // 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]; wcs->crpix[wcs->lat] = pix[0][wcs->lat]; wcs->crval[wcs->lng] = world[0][wcs->lng]; 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; } astropy-astropy-201cddb/cextern/wcslib/C/wcsfix.h000066400000000000000000000723371507226315300222040ustar00rootroot00000000000000/*============================================================================ WCSLIB 8.4 - an implementation of the FITS WCS standard. Copyright (C) 1995-2024, 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/people/Mark.Calabretta $Id: wcsfix.h,v 8.4 2024/10/28 13:56:16 mcalabre Exp $ *============================================================================= * * WCSLIB 8.4 - 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 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: * ----------------------- * AIPS-convention celestial projection types, NCP and GLS, and spectral * types, 'FREQ-LSR', 'FELO-HEL', etc., set in CTYPEia are translated * on-the-fly by wcsset() but without modifying the relevant ctype[], pv[] or * specsys members of the wcsprm struct. That is, only the information * extracted from ctype[] is translated when wcsset() fills in wcsprm::cel * (celprm struct) or wcsprm::spc (spcprm struct). * * On the other hand, these routines do change the values of wcsprm::ctype[], * wcsprm::pv[], wcsprm::specsys and other wcsprm struct members as * appropriate to produce the same result as if the FITS header itself had * been translated. * * Auxiliary WCS header information not used directly by WCSLIB may also be * translated. For example, the older DATE-OBS date format (wcsprm::dateobs) * is recast to year-2000 standard form, and MJD-OBS (wcsprm::mjdobs) will be * deduced from it if not already set. * * Certain combinations of keyvalues that result in malformed coordinate * systems, as described in Sect. 7.3.4 of Paper I, may also be repaired. * These are handled by cylfix(). * * Non-standard keywords: * ---------------------- * 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 * particular, they do not identify or translate CD00i00j, PC00i00j, PROJPn, * EPOCH, VELREF or VSOURCEa keywords; this may be done by the FITS WCS * 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: * * - 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. 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'. * * - spcfix(): translate AIPS-convention spectral types, 'FREQ-LSR', * 'FELO-HEL', etc., in ctype[] as set from CTYPEia. * * - celfix(): translate AIPS-convention celestial projection types, NCP and * GLS, in ctype[] as set from CTYPEia. * * - cylfix(): fixes WCS keyvalues for malformed cylindrical projections that * suffer from the problem described in Sect. 7.3.4 of Paper I. * * * wcsfix() - Translate a non-standard WCS struct * ---------------------------------------------- * wcsfix() is identical to wcsfixi(), but lacks the info argument. * * * wcsfixi() - Translate a non-standard WCS struct * ----------------------------------------------- * 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 * unit strings as described in the usage notes to * wcsutrn(). * * naxis const int [] * Image axis lengths. If this array pointer is set to * zero then cylfix() will not be invoked. * * Given and returned: * wcs struct wcsprm* * Coordinate transformation parameters. * * Returned: * stat int [NWCSFIX] * Status returns from each of the functions. Use the * preprocessor macros NWCSFIX to dimension this vector * 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, 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: * 0: Success. * 1: One or more of the translation functions * returned an error. * * * 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 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. * * Function return value: * int Status return value: * -1: No change required (not an error). * 0: Success. * 1: Null wcsprm pointer passed. * * * 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). 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::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: * -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_DATE_FIX. * * Notes: * 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 * ---------------------------------------------- * 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(). * * Given and returned: * wcs struct wcsprm* * Coordinate transformation parameters. * * Function return value: * int Status return value: * -1: No change required (not an error). * 0: Success (an alias was applied). * 1: Null wcsprm pointer passed. * * 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 * --------------------------------------------------- * spcfix() translates AIPS-convention spectral coordinate types, * '{FREQ,FELO,VELO}-{LSR,HEL,OBS}' (e.g. 'FREQ-OBS', 'FELO-HEL', 'VELO-LSR') * set in wcsprm::ctype[], subject to VELREF set in wcsprm::velref. * * 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[] * and/or wcsprm::specsys may be changed. * * Function return value: * int Status return value: * -1: No change required (not an error). * 0: Success. * 1: Null wcsprm pointer passed. * 2: Memory allocation failed. * 3: Linear transformation matrix is singular. * 4: Inconsistent or unrecognized coordinate axis * types. * 5: Invalid parameter value. * 6: Invalid coordinate transformation parameters. * 7: Ill-conditioned coordinate transformation * parameters. * * 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 * --------------------------------------------------------------- * 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, 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* * Coordinate transformation parameters. wcsprm::ctype[] * and/or wcsprm::pv[] may be changed. * * Function return value: * int Status return value: * -1: No change required (not an error). * 0: Success. * 1: Null wcsprm pointer passed. * 2: Memory allocation failed. * 3: Linear transformation matrix is singular. * 4: Inconsistent or unrecognized coordinate axis * types. * 5: Invalid parameter value. * 6: Invalid coordinate transformation parameters. * 7: Ill-conditioned coordinate transformation * parameters. * * For returns > 1, a detailed error message is set in * wcsprm::err if enabled, see wcserr_enable(). * * * cylfix() - Fix malformed cylindrical projections * ------------------------------------------------ * 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. * * Given and returned: * wcs struct wcsprm* * Coordinate transformation parameters. * * Function return value: * int Status return value: * -1: No change required (not an error). * 0: Success. * 1: Null wcsprm pointer passed. * 2: Memory allocation failed. * 3: Linear transformation matrix is singular. * 4: Inconsistent or unrecognized coordinate axis * types. * 5: Invalid parameter value. * 6: Invalid coordinate transformation parameters. * 7: Ill-conditioned coordinate transformation * parameters. * 8: All of the corner pixel coordinates are invalid. * 9: Could not determine reference pixel coordinate. * 10: Could not determine reference pixel value. * * For returns > 1, a detailed error message is set in * 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. * *===========================================================================*/ #ifndef WCSLIB_WCSFIX #define WCSLIB_WCSFIX #include "wcs.h" #include "wcserr.h" #ifdef __cplusplus extern "C" { #endif #define CDFIX 0 #define DATFIX 1 #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_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[]); int wcsfixi(int ctrl, const int naxis[], struct wcsprm *wcs, int stat[], struct wcserr info[]); 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); 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 astropy-astropy-201cddb/cextern/wcslib/C/wcshdr.c000066400000000000000000002154561507226315300221670ustar00rootroot00000000000000/*============================================================================ WCSLIB 8.4 - an implementation of the FITS WCS standard. Copyright (C) 1995-2024, 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/people/Mark.Calabretta $Id: wcshdr.c,v 8.4 2024/10/28 13:56:16 mcalabre Exp $ *===========================================================================*/ #include #include #include #include #include #include "wcserr.h" #include "wcsmath.h" #include "wcsutil.h" #include "wcshdr.h" #include "wtbarr.h" #include "tab.h" #include "dis.h" #include "wcs.h" // Map status return value to message. const char *wcshdr_errmsg[] = { "Success", "Null wcsprm pointer passed", "Memory allocation failed", "Invalid column selection", "Fatal error returned by Flex parser", "Invalid tabular parameters"}; // 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; int *tabidx = 0x0; int status = 0; if (wcs == 0x0) return WCSHDRERR_NULL_POINTER; struct wcserr **err = &(wcs->err); // 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 (int j = 0; j < wcs->ntab; j++) { tabfree(wcs->m_tab + j); } free(wcs->m_tab); } } wcs->ntab = 0; wcs->nwtb = 0; wcs->wtb = 0x0; wcs->tab = 0x0; // 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)); } 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")) { tabax[i] = ntabax++; } else { tabax[i] = -1; } } if (ntabax == 0) { // No lookup tables. goto cleanup; } // 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))) && (PSi_1a = calloc(ntabax, sizeof(char[72]))) && (PSi_2a = calloc(ntabax, sizeof(char[72]))) && (PVi_3a = calloc(ntabax, sizeof(int))) && (tabidx = calloc(ntabax, sizeof(int))))) { status = wcserr_set(WCSHDR_ERRMSG(WCSHDRERR_MEMORY)); goto cleanup; } 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 (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. strcpy(PSi_0a[itabax], wcs->ps[ip].value); wcsutil_null_fill(72, PSi_0a[itabax]); break; case 1: // 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. strcpy(PSi_2a[itabax], wcs->ps[ip].value); wcsutil_null_fill(72, PSi_2a[itabax]); break; } } } 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. PVi_1a[itabax] = (int)(wcs->pv[ip].value + 0.5); break; case 2: // EXTLEVEL. PVi_2a[itabax] = (int)(wcs->pv[ip].value + 0.5); break; case 3: // Table axis number. PVi_3a[itabax] = (int)(wcs->pv[ip].value + 0.5); break; } } } // 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"); goto cleanup; } tabidx[itabax] = -1; 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] && PVi_2a[itabax] == PVi_2a[jtabax]) { tabidx[itabax] = tabidx[jtabax]; break; } } if (jtabax == itabax) { tabidx[itabax] = wcs->ntab; wcs->ntab++; } } if (!(wcs->tab = calloc(wcs->ntab, sizeof(struct tabprm)))) { status = wcserr_set(WCSHDR_ERRMSG(WCSHDRERR_MEMORY)); goto cleanup; } wcs->m_tab = wcs->tab; // 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. if (PVi_3a[itabax] > tabp->M) { tabp->M = PVi_3a[itabax]; } } for (int itab = 0; itab < wcs->ntab; itab++) { if ((status = tabini(1, wcs->tab[itab].M, 0, wcs->tab + itab))) { status = wcserr_set(WCSHDR_ERRMSG(wcshdr_taberr[status])); goto cleanup; } } // Copy parameters into the tabprm structs. for (int i = 0; i < naxis; i++) { int itabax; if ((itabax = tabax[i]) < 0) { // Not a -TAB axis. continue; } // 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 (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"); goto cleanup; } } } // 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. wcs->nwtb += wcs->ntab; // Allocate memory for structs to be returned. if (!(wcs->wtb = calloc(wcs->nwtb, sizeof(struct wtbarr)))) { wcs->nwtb = 0; status = wcserr_set(WCSHDR_ERRMSG(WCSHDRERR_MEMORY)); goto cleanup; } wcs->m_wtb = wcs->wtb; // 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. wtbp->i = itabax + 1; wtbp->m = PVi_3a[itabax]; wtbp->kind = 'c'; strcpy(wtbp->extnam, PSi_0a[itabax]); wtbp->extver = PVi_1a[itabax]; wtbp->extlev = PVi_2a[itabax]; strcpy(wtbp->ttype, PSi_1a[itabax]); wtbp->row = 1L; wtbp->ndim = wcs->tab[itab].M + 1; wtbp->dimlen = wcs->tab[itab].K; wtbp->arrayp = &(wcs->tab[itab].coord); // Signal for tabset() to take this memory. wcs->tab[itab].m_coord = (double *)0x1; wtbp++; getcrd = 0; } if (PSi_2a[itabax][0]) { // Index array. wtbp->i = itabax + 1; wtbp->m = PVi_3a[itabax]; wtbp->kind = 'i'; int m = wtbp->m - 1; strcpy(wtbp->extnam, PSi_0a[itabax]); wtbp->extver = PVi_1a[itabax]; wtbp->extlev = PVi_2a[itabax]; strcpy(wtbp->ttype, PSi_2a[itabax]); wtbp->row = 1L; wtbp->ndim = 1; wtbp->dimlen = wcs->tab[itab].K + m; wtbp->arrayp = wcs->tab[itab].index + m; // Signal for tabset() to take this memory. wcs->tab[itab].m_indxs[m] = (double *)0x1; wtbp++; } } } cleanup: if (tabax) free(tabax); if (tabidx) free(tabidx); if (PSi_0a) free(PSi_0a); if (PVi_1a) free(PVi_1a); if (PVi_2a) free(PVi_2a); if (PSi_1a) free(PSi_1a); if (PSi_2a) free(PSi_2a); if (PVi_3a) free(PVi_3a); if (status) { if (wcs->tab) free(wcs->tab); if (wcs->wtb) free(wcs->wtb); } return status; } //---------------------------------------------------------------------------- int wcsidx(int nwcs, struct wcsprm **wcs, int alts[27]) { for (int a = 0; a < 27; a++) { alts[a] = -1; } if (wcs == 0x0) { return WCSHDRERR_NULL_POINTER; } 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 { a = wcsp->alt[0] - 'A' + 1; } alts[a] = iwcs; } return 0; } //---------------------------------------------------------------------------- int wcsbdx(int nwcs, struct wcsprm **wcs, int type, short alts[1000][28]) { for (short *ip = alts[0]; ip < alts[0] + 28*1000; ip++) { *ip = -1; } for (int icol = 0; icol < 1000; icol++) { alts[icol][27] = 0; } if (wcs == 0x0) { return WCSHDRERR_NULL_POINTER; } struct wcsprm *wcsp = *wcs; for (int iwcs = 0; iwcs < nwcs; iwcs++, wcsp++) { int a; if (wcsp->alt[0] == ' ') { a = 0; } else { a = wcsp->alt[0] - 'A' + 1; } if (type) { // Pixel list. if (wcsp->colax[0]) { for (int i = 0; i < wcsp->naxis; i++) { alts[wcsp->colax[i]][a] = iwcs; alts[wcsp->colax[i]][27]++; } } else if (!wcsp->colnum) { alts[0][a] = iwcs; alts[0][27]++; } } else { // Binary table image array. if (wcsp->colnum) { alts[wcsp->colnum][a] = iwcs; alts[wcsp->colnum][27]++; } else if (!wcsp->colax[0]) { alts[0][a] = iwcs; alts[0][27]++; } } } return 0; } //---------------------------------------------------------------------------- int wcsvfree(int *nwcs, struct wcsprm **wcs) { int status = 0; if (wcs == 0x0) { return WCSHDRERR_NULL_POINTER; } struct wcsprm *wcsp = *wcs; for (int a = 0; a < *nwcs; a++, wcsp++) { status |= wcsfree(wcsp); } free(*wcs); *nwcs = 0; *wcs = 0x0; 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 ctrl, struct wcsprm *wcs, int *nkeyrec, char **header) // ::: CUBEFACE and STOKES handling? { static const char *function = "wcshdo"; 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; struct wcserr **err = &(wcs->err); 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. char alt = wcs->alt[0]; if (alt == ' ') alt = '\0'; int colnum = wcs->colnum; int *colax = wcs->colax; int primage = 0; int bintab = 0; int pixlist = 0; if (colnum) { bintab = 1; } else if (colax[0]) { pixlist = 1; } else { primage = 1; } // 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(ctrl, "WCSAXES", "WCAX", 0, 0x0, 0, 0, 0, alt, colnum, colax, keyvalue, "Number of coordinate axes", nkeyrec, header, &status); } // 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. 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, 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. 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(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 (int i = 0; i < naxis; i++) { if (wcs->cunit[i][0] == '\0') continue; sprintf(keyvalue, "'%s'", wcs->cunit[i]); 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); } // 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) { // 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 (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, "); } } 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)"); } else { switch (xtype) { case 'F': strcat(comment, " (linear in frequency)"); break; case 'V': strcat(comment, " (linear in velocity)"); break; case 'W': strcat(comment, " (linear in wavelength)"); break; } } } 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 (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(ctrl, "CRVAL", "CRV", WCSHDO_CRPXna, "CRVL", i+1, 0, 0, alt, colnum, colax, keyvalue, comment, nkeyrec, header, &status); } // 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: strcpy(comment, "[deg] Native longitude of the reference point"); break; case 2: strcpy(comment, "[deg] Native latitude of the reference point"); break; case 3: if (primage) { sprintf(keyword, "LONPOLE%c", alt); } else if (bintab) { sprintf(keyword, "LONP%d%c", colnum, alt); } else { sprintf(keyword, "LONP%d%c", colax[(wcs->pv[k]).i - 1], alt); } sprintf(comment, "[deg] alias for %s (has precedence)", keyword); break; case 4: if (primage) { sprintf(keyword, "LATPOLE%c", alt); } else if (bintab) { sprintf(keyword, "LATP%d%c", colnum, alt); } else { sprintf(keyword, "LATP%d%c", colax[(wcs->pv[k]).i - 1], alt); } sprintf(comment, "[deg] alias for %s (has precedence)", keyword); break; } } else if ((wcs->pv[k]).i == (wcs->lat + 1)) { sprintf(comment, "%s projection parameter", wcs->cel.prj.code); } else { strcpy(comment, "Coordinate transformation parameter"); } 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 (int k = 0; k < wcs->nps; k++) { sprintf(keyvalue, "'%s'", (wcs->ps[k]).value); 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); } // Celestial and spectral transformation parameters. if (!undefined(wcs->lonpole)) { 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, 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 (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 (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); } // - - - - - - - - - - - - - - - - - Auxiliary coordinate axis information. char timeunit[16]; sprintf(timeunit, "%.15s", wcs->timeunit[0] ? wcs->timeunit : "s"); // Coordinate axis title. if (wcs->cname) { for (int i = 0; i < naxis; i++) { if (wcs->cname[i][0] == '\0') continue; sprintf(keyvalue, "'%s'", wcs->cname[i]); 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. if (wcs->crder) { for (int i = 0; i < naxis; i++) { if (undefined(wcs->crder[i])) continue; 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(ctrl, "CRDER", "CRD", WCSHDO_CNAMna, "CRDE", i+1, 0, 0, alt, colnum, colax, keyvalue, comment, nkeyrec, header, &status); } } // Systematic error in coordinate. if (wcs->csyer) { for (int i = 0; i < naxis; i++) { if (undefined(wcs->csyer[i])) continue; 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(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); } } // - - - - - - - - - - - - - - - - - - - - - - - - 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(ctrl, "RADESYS", "RADE", 0, 0x0, 0, 0, 0, alt, colnum, colax, keyvalue, "Equatorial coordinate system", nkeyrec, header, &status); } // Equinox of equatorial coordinate system. if (!undefined(wcs->equinox)) { 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. if (wcs->specsys[0]) { sprintf(keyvalue, "'%s'", wcs->specsys); 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. if (wcs->ssysobs[0]) { sprintf(keyvalue, "'%s'", wcs->ssysobs); 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. if (!undefined(wcs->velosys)) { 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); } // 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(ctrl, "SSYSSRC", "SSRC", 0, 0x0, 0, 0, 0, alt, colnum, colax, keyvalue, "Reference frame of source redshift", 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); } // - - - - - - - - - - - - - - - - - - - - Additional auxiliary parameters. 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); } 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); } 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); } } 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); } } // - - - - - - - - - - - - - - - - - - - - - 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; } 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 { strcpy(keyword, "B_"); } int degree; int ncoeff = dis->iparm[j][I_TPDNCO]; for (degree = 0; degree <= 9; degree++) { if (ncoeff <= nTPD[degree]) break; } 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); } 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 { strcpy(keyword, "BP_"); } 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); } } } 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); } } 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); } } } } // 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, const char pikey[], const char tbkey[], int level, const char tlkey[], int i, int j, int m, char alt, int btcol, int plcol[], char keyvalue[], const char keycomment[], int *nkeyrec, char **header, int *status) { if (*status) return; // Reallocate memory in blocks of 2880 bytes. char *hptr; if ((*nkeyrec)%32 == 0) { int nbyte = ((*nkeyrec)/32 + 1) * 2880; if (!(hptr = realloc(*header, nbyte))) { *status = WCSHDRERR_MEMORY; return; } *header = hptr; } // Construct the keyword. char keyword[32]; if (alt == ' ') alt = '\0'; if (btcol) { // Binary table image array. if (i > 0 && j) { if (j > 0) { sprintf(keyword, "%d%d%s%d%c", i, j, tbkey, btcol, alt); } else { sprintf(keyword, "%d%s%d_%d%c", i, tbkey, btcol, m, alt); } } else if (i > 0) { sprintf(keyword, "%d%s%d%c", i, tbkey, btcol, alt); } else if (j > 0) { sprintf(keyword, "%d%s%d%c", j, tbkey, btcol, alt); } else { sprintf(keyword, "%s%d%c", tbkey, btcol, alt); } if ((strlen(keyword) < 8) && tlkey && (relax & level)) { // Use the long form. if (i > 0 && j) { if (j > 0) { sprintf(keyword, "%d%d%s%d%c", i, j, tlkey, btcol, alt); } else { sprintf(keyword, "%d%s%d_%d%c", i, tlkey, btcol, m, alt); } } else if (i > 0) { sprintf(keyword, "%d%s%d%c", i, tlkey, btcol, alt); } else if (j > 0) { sprintf(keyword, "%d%s%d%c", j, tlkey, btcol, alt); } else { sprintf(keyword, "%s%d%c", tlkey, btcol, alt); } } } else if (plcol && plcol[0]) { // Pixel list. if (i > 0 && j) { if (j > 0) { sprintf(keyword, "T%s%d_%d%c", tbkey, plcol[i-1], plcol[j-1], alt); } else { sprintf(keyword, "T%s%d_%d%c", tbkey, plcol[i-1], m, alt); } } else if (i > 0) { sprintf(keyword, "T%s%d%c", tbkey, plcol[i-1], alt); } else if (j > 0) { sprintf(keyword, "T%s%d%c", tbkey, plcol[j-1], alt); } else { sprintf(keyword, "%s%d%c", tbkey, plcol[0], alt); } if ((strlen(keyword) < 8) && tlkey && (relax & level)) { // 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); } else { sprintf(keyword, "T%s%d_%d%c", tlkey, plcol[i-1], m, alt); } } else if (i > 0) { sprintf(keyword, "T%s%d%c", tlkey, plcol[i-1], alt); } else if (j > 0) { sprintf(keyword, "T%s%d%c", tlkey, plcol[j-1], alt); } else { sprintf(keyword, "%s%d%c", tlkey, plcol[0], alt); } } } else { if (i > 0 && j) { if (j > 0) { sprintf(keyword, "%s%d_%d%c", pikey, i, j, alt); } else { sprintf(keyword, "%s%d_%d%c", pikey, i, m, alt); } } else if (i > 0) { sprintf(keyword, "%s%d%c", pikey, i, alt); } else if (j > 0) { sprintf(keyword, "%s%d%c", pikey, j, alt); } else { sprintf(keyword, "%s%c", pikey, alt); } } // 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'; } } 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. nc -= (nv - 20); } hptr = *header + (80 * ((*nkeyrec)++)); 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); } } astropy-astropy-201cddb/cextern/wcslib/C/wcshdr.h000066400000000000000000002017301507226315300221620ustar00rootroot00000000000000/*============================================================================ WCSLIB 8.4 - an implementation of the FITS WCS standard. Copyright (C) 1995-2024, 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/people/Mark.Calabretta $Id: wcshdr.h,v 8.4 2024/10/28 13:56:16 mcalabre Exp $ *============================================================================= * * WCSLIB 8.4 - 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. 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/people/Mark.Calabretta = = "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. * * Briefly, the anticipated sequence of operations is as follows: * * - 1: Open the FITS file and read the image or binary table header, e.g. * using CFITSIO routine fits_hdr2str(). * * - 2: Parse the header using wcspih() or wcsbth(); they will automatically * interpret 'TAB' header keywords using wcstab(). * * - 3: Allocate memory for, and read 'TAB' arrays from the binary table * 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 * freed by wcsfree(). * * - 4: Translate non-standard WCS usage using wcsfix(), see wcsfix.h. * * - 5: Initialize wcsprm struct(s) using wcsset() and calculate coordinates * using wcsp2s() and/or wcss2p(). Refer to the prologue of wcs.h for a * description of these and other high-level WCS coordinate * transformation routines. * * - 6: Clean up by freeing memory with wcsvfree(). * * In detail: * * - wcspih() is a high-level FITS WCS routine that parses an image header. It * returns an array of up to 27 wcsprm structs on each of which it invokes * wcstab(). * * - wcsbth() is the analogue of wcspih() for use with binary tables; it * handles image array and pixel list keywords. As an extension of the FITS * WCS standard, it also recognizes image header keywords which may be used * to provide default values via an inheritance mechanism. * * - wcstab() assists in filling in members of the wcsprm struct associated * with coordinate lookup tables ('TAB'). These are based on arrays stored * in a FITS binary table extension (BINTABLE) that are located by PVi_ma * keywords in the image header. * * - wcsidx() and wcsbdx() are utility routines that return the index for a * specified alternate coordinate descriptor in the array of wcsprm structs * returned by wcspih() or wcsbth(). * * - wcsvfree() deallocates memory for an array of wcsprm structs, such as * returned by wcspih() or wcsbth(). * * - wcshdo() writes out a wcsprm struct as a FITS header. * * * wcspih() - FITS WCS parser routine for image headers * ---------------------------------------------------- * 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, 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 * to 26 alternate representations. It returns this information as an array of * wcsprm structs. * * wcspih() invokes wcstab() on each of the wcsprm structs that it returns. * * 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, 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 * header from which to identify and construct the * coordinate representations, for example, as might be * obtained conveniently via the CFITSIO routine * fits_hdr2str(). * * Each header "keyrecord" (formerly "card image") * consists of exactly 80 7-bit ASCII printing characters * in the range 0x20 to 0x7e (which excludes NUL, BS, * TAB, LF, FF and CR) especially noting that the * keyrecords are NOT null-terminated. * * For negative values of ctrl (see below), header[] is * modified so that WCS keyrecords processed by wcspih() * are removed from it. * * Given: * nkeyrec int Number of keyrecords in header[]. * * relax int Degree of permissiveness: * 0: Recognize only FITS keywords defined by the * published WCS standard. * WCSHDR_all: Admit all recognized informal * extensions of the WCS standard. * Fine-grained control of the degree of permissiveness * is also possible as explained in wcsbth() note 5. * * ctrl int Error reporting and other control options for invalid * WCS and other header keyrecords: * 0: Do not report any rejected header keyrecords. * 1: Produce a one-line message stating the number * of WCS keyrecords rejected (nreject). * 2: Report each rejected keyrecord and the reason * why it was rejected. * 3: As above, but also report all non-WCS * keyrecords that were discarded, and the number * of coordinate representations (nwcs) found. * 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: 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 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 * complement of nkeyrec keyrecords and possibly not be * null-terminated. * * Returned: * nreject int* Number of WCS keywords rejected for syntax errors, * illegal values, etc. Keywords not recognized as WCS * keywords are simply ignored. Refer also to wcsbth() * note 5. * * nwcs int* Number of coordinate representations found. * * wcs struct wcsprm** * Pointer to an array of wcsprm structs containing up to * 27 coordinate representations. * * Memory for the array is allocated by wcspih() which * also invokes wcsini() for each struct to allocate * memory for internal arrays and initialize their * members to default values. Refer also to wcsbth() * note 8. Note that wcsset() is not invoked on these * structs. * * This allocated memory must be freed by the user, first * by invoking wcsfree() for each struct, and then by * freeing the array itself. A routine, wcsvfree(), is * provided to do this (see below). * * Function return value: * int Status return value: * 0: Success. * 1: Null wcsprm pointer passed. * 2: Memory allocation failed. * 4: Fatal error returned by Flex parser. * * Notes: * 1: Refer to wcsbth() notes 1, 2, 3, 5, 7, and 8. * * * wcsbth() - FITS WCS parser routine for binary table and image headers * --------------------------------------------------------------------- * wcsbth() is a high-level FITS WCS routine that parses a binary table header. * It handles image array and pixel list WCS keywords which may be present * together in one header. * * As an extension of the FITS WCS standard, wcsbth() also recognizes image * header keywords in a binary table header. These may be used to provide * default values via an inheritance mechanism discussed in note 5 (c.f. * WCSHDR_AUXIMG and WCSHDR_ALLIMG), or may instead result in wcsprm structs * that are not associated with any particular column. Thus wcsbth() can * handle primary image and image extension headers in addition to binary table * headers (it ignores NAXIS and does not rely on the presence of the TFIELDS * keyword). * * 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 * associated with each pixel coordinate element in a pixel list. wcsprm * structs that are not associated with any particular column, as may be * derived from image header keywords, have colnum == 0. * * Note 6 below discusses the number of wcsprm structs returned by wcsbth(), * and the circumstances in which image header keywords cause a struct to be * created. See also note 9 concerning the number of separate images that may * be stored in a pixel list. * * The API to wcsbth() is similar to that of wcspih() except for the addition * of extra arguments that may be used to restrict its operation. Like * wcspih(), wcsbth() invokes wcstab() on each of the wcsprm structs that it * returns. * * Given and returned: * header char[] Character array containing the (entire) FITS binary * table, primary image, or image extension header from * which to identify and construct the coordinate * representations, for example, as might be obtained * conveniently via the CFITSIO routine fits_hdr2str(). * * Each header "keyrecord" (formerly "card image") * consists of exactly 80 7-bit ASCII printing * characters in the range 0x20 to 0x7e (which excludes * NUL, BS, TAB, LF, FF and CR) especially noting that * the keyrecords are NOT null-terminated. * * For negative values of ctrl (see below), header[] is * modified so that WCS keyrecords processed by wcsbth() * are removed from it. * * Given: * nkeyrec int Number of keyrecords in header[]. * * relax int Degree of permissiveness: * 0: Recognize only FITS keywords defined by the * published WCS standard. * WCSHDR_all: Admit all recognized informal * extensions of the WCS standard. * Fine-grained control of the degree of permissiveness * is also possible, as explained in note 5 below. * * ctrl int Error reporting and other control options for invalid * WCS and other header keyrecords: * 0: Do not report any rejected header keyrecords. * 1: Produce a one-line message stating the number * of WCS keyrecords rejected (nreject). * 2: Report each rejected keyrecord and the reason * why it was rejected. * 3: As above, but also report all non-WCS * keyrecords that were discarded, and the number * of coordinate representations (nwcs) found. * 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[]: * -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. * -3: As above, and also report the number of * coordinate representations (nwcs) found. * -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 * complement of nkeyrec keyrecords and possibly not be * null-terminated. * * keysel int Vector of flag bits that may be used to restrict the * keyword types considered: * WCSHDR_IMGHEAD: Image header keywords. * WCSHDR_BIMGARR: Binary table image array. * WCSHDR_PIXLIST: Pixel list keywords. * If zero, there is no restriction. * * Keywords such as EQUIna or RFRQna that are common to * binary table image arrays and pixel lists (including * WCSNna and TWCSna, as explained in note 4 below) are * selected by both WCSHDR_BIMGARR and WCSHDR_PIXLIST. * Thus if inheritance via WCSHDR_ALLIMG is enabled as * discussed in note 5 and one of these shared keywords * is present, then WCSHDR_IMGHEAD and WCSHDR_PIXLIST * alone may be sufficient to cause the construction of * coordinate descriptions for binary table image arrays. * * colsel int* Pointer to an array of table column numbers used to * restrict the keywords considered by wcsbth(). * * A null pointer may be specified to indicate that there * is no restriction. Otherwise, the magnitude of * cols[0] specifies the length of the array: * cols[0] > 0: the columns are included, * cols[0] < 0: the columns are excluded. * * For the pixel list keywords TPn_ka and TCn_ka (and * TPCn_ka and TCDn_ka if WCSHDR_LONGKEY is enabled), it * is an error for one column to be selected but not the * other. This is unlike the situation with invalid * keyrecords, which are simply rejected, because the * error is not intrinsic to the header itself but * arises in the way that it is processed. * * Returned: * nreject int* Number of WCS keywords rejected for syntax errors, * illegal values, etc. Keywords not recognized as WCS * keywords are simply ignored, refer also to note 5 * below. * * nwcs int* Number of coordinate representations found. * * wcs struct wcsprm** * Pointer to an array of wcsprm structs containing up * to 27027 coordinate representations, refer to note 6 * below. * * Memory for the array is allocated by wcsbth() which * also invokes wcsini() for each struct to allocate * memory for internal arrays and initialize their * members to default values. Refer also to note 8 * below. Note that wcsset() is not invoked on these * structs. * * This allocated memory must be freed by the user, first * by invoking wcsfree() for each struct, and then by * freeing the array itself. A routine, wcsvfree(), is * provided to do this (see below). * * Function return value: * int Status return value: * 0: Success. * 1: Null wcsprm pointer passed. * 2: Memory allocation failed. * 3: Invalid column selection. * 4: Fatal error returned by Flex parser. * * Notes: * 1: wcspih() determines the number of coordinate axes independently for * each alternate coordinate representation (denoted by the "a" value in * keywords like CTYPEia) from the higher of * * a: NAXIS, * b: WCSAXESa, * c: 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. * * wcsbth() is similar except that it ignores the NAXIS keyword if given * an image header to process. * * The number of axes, which is returned as a member of the wcsprm * struct, may differ for different coordinate representations of the * same image. * * 2: wcspih() and wcsbth() enforce correct FITS "keyword = value" syntax * with regard to "= " occurring in columns 9 and 10. * * However, they do recognize free-format character (NOST 100-2.0, * Sect. 5.2.1), integer (Sect. 5.2.3), and floating-point values * (Sect. 5.2.4) for all keywords. * * 3: Where CROTAn, CDi_ja, and PCi_ja occur together in one header wcspih() * and wcsbth() treat them as described in the prologue to wcs.h. * * 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, 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 * flag bits to provide fine-grained control over what non-standard WCS * keywords to accept. The flag bits are subject to change in future and * should be set by using the preprocessor macros (see below) for the * purpose. * * - WCSHDR_none: Don't accept any extensions (not even those in the * errata). Treat non-conformant keywords in the same way as * non-WCS keywords in the header, i.e. simply ignore them. * * - WCSHDR_all: Accept all extensions recognized by the parser. * * - 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. 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. * * - 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_VELREFa: Accept VELREFa. * wcspih() always recognizes the AIPS-convention keywords, * CROTAn, EPOCH, and VELREF for the primary representation * (a = ' ') but alternates are non-standard. * * wcsbth() accepts EPOCHa and VELREFa only if WCSHDR_AUXIMG is * also enabled. * * - WCSHDR_CD00i00j: Accept CD00i00j (wcspih()). * - WCSHDR_PC00i00j: Accept PC00i00j (wcspih()). * - WCSHDR_PROJPn: Accept PROJPn (wcspih()). * These appeared in early drafts of WCS Paper I+II (before they * were split) and are equivalent to CDi_ja, PCi_ja, and PVi_ma * for the primary representation (a = ' '). PROJPn is * 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. * * wcsbth() accepts VSOURCEa only if WCSHDR_AUXIMG is also * enabled. * * - 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- * blank. Specifically * # jCRPXna TCRPXna : jCRPXn jCRPna TCRPXn TCRPna CRPIXja # - TPCn_ka : - ijPCna - TPn_ka PCi_ja # - TCDn_ka : - ijCDna - TCn_ka CDi_ja # iCDLTna TCDLTna : iCDLTn iCDEna TCDLTn TCDEna CDELTia # iCUNIna TCUNIna : iCUNIn iCUNna TCUNIn TCUNna CUNITia # iCTYPna TCTYPna : iCTYPn iCTYna TCTYPn TCTYna CTYPEia # iCRVLna TCRVLna : iCRVLn iCRVna TCRVLn TCRVna CRVALia # iPVn_ma TPVn_ma : - iVn_ma - TVn_ma PVi_ma # iPSn_ma TPSn_ma : - iSn_ma - TSn_ma PSi_ma * * where the primary and standard alternate forms together with * the image-header equivalent are shown rightwards of the colon. * * The long form of these keywords could be described as quasi- * standard. TPCn_ka, iPVn_ma, and TPVn_ma appeared by mistake * in the examples in WCS Paper II and subsequently these and * also TCDn_ka, iPSn_ma and TPSn_ma were legitimized by the * errata to the WCS papers. * * Strictly speaking, the other long forms are non-standard and * in fact have never appeared in any draft of the WCS papers nor * in the errata. However, as natural extensions of the primary * form they are unlikely to be written with any other intention. * Thus it should be safe to accept them provided, of course, * that the resulting keyword does not exceed the 8-character * limit. * * If WCSHDR_CNAMn is enabled then also accept * # 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, 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, 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 * provide a default value for all images. This default may be * overridden by the column-specific form of the keyword. * * For example, a keyword like EQUINOXa would apply to all image * arrays in a binary table, or all pixel list columns with * alternate representation "a" unless overridden by EQUIna. * * Specifically the keywords are: * # LONPOLEa for LONPna # 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 * 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 * * 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-END - # MJD-BEG - # MJD-AVG for MJDAn # 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 * * 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 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 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 * image arrays in a binary table (n.b. not pixel list). This * default may be overridden by the column-specific form of the * keyword. * * For example, a keyword like CRPIXja would apply to all image * arrays in a binary table with alternate representation "a" * unless overridden by jCRPna. * * Specifically the keywords are those listed above for * WCSHDR_AUXIMG plus * # WCSAXESa for WCAXna * * which defines the coordinate dimensionality, and the following * keywords that are parameterized by axis number: * # CRPIXja for jCRPna # PCi_ja for ijPCna # CDi_ja for ijCDna # CDELTia for iCDEna # CROTAi for iCROTn # CROTAia - ... Only if WCSHDR_CROTAia is set. # CUNITia for iCUNna # CTYPEia for iCTYna # 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. * * 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 (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 * auxiliary information. * * Note especially that at least one wcsprm struct will be * returned for each "a" found in one of the image header * keywords listed above: * * - If the image header keywords for "a" ARE NOT inherited by a * binary table, then the struct will not be associated with * any particular table column number and it is up to the user * to provide an association. * * - If the image header keywords for "a" ARE inherited by a * binary table image array, then those keywords are considered * to be "exhausted" and do not result in a separate wcsprm * struct. * * For example, to accept CD00i00j and PC00i00j and reject all other * extensions, use * = relax = WCSHDR_reject | WCSHDR_CD00i00j | WCSHDR_PC00i00j; * * The parser always treats EPOCH as subordinate to EQUINOXa if both are * present, and VSOURCEa is always subordinate to ZSOURCEa. * * Likewise, VELREF is subordinate to the formalism of WCS Paper III, see * spcaips(). * * Neither wcspih() nor wcsbth() currently recognize the AIPS-convention * keywords ALTRPIX or ALTRVAL which effectively define an alternative * representation for a spectral axis. * * 6: Depending on what flags have been set in its "relax" argument, * wcsbth() could return as many as 27027 wcsprm structs: * * - Up to 27 unattached representations derived from image header * keywords. * * - Up to 27 structs for each of up to 999 columns containing an image * arrays. * * - Up to 27 structs for a pixel list. * * Note that it is considered legitimate for a column to contain an image * array and also form part of a pixel list, and in particular that * wcsbth() does not check the TFORM keyword for a pixel list column to * check that it is scalar. * * In practice, of course, a realistic binary table header is unlikely to * contain more than a handful of images. * * In order for wcsbth() to create a wcsprm struct for a particular * coordinate representation, at least one WCS keyword that defines an * axis number must be present, either directly or by inheritance if * WCSHDR_ALLIMG is set. * * When the image header keywords for an alternate representation are * inherited by a binary table image array via WCSHDR_ALLIMG, those * keywords are considered to be "exhausted" and do not result in a * separate wcsprm struct. Otherwise they do. * * 7: Neither wcspih() nor wcsbth() check for duplicated keywords, in most * cases they accept the last encountered. * * 8: wcspih() and wcsbth() use wcsnpv() and wcsnps() (refer to the prologue * of wcs.h) to match the size of the pv[] and ps[] arrays in the wcsprm * structs to the number in the header. Consequently there are no unused * elements in the pv[] and ps[] arrays, indeed they will often be of * zero length. * * 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, for which the device "pixel" coordinates are stored in * separate scalar columns of the table. * * 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. * * 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 * --------------------------------------- * wcstab() assists in filling in the information in the wcsprm struct relating * to coordinate lookup tables. * * Tabular coordinates ('TAB') present certain difficulties in that the main * components of the lookup table - the multidimensional coordinate array plus * an index vector for each dimension - are stored in a FITS binary table * extension (BINTABLE). Information required to locate these arrays is stored * in PVi_ma and PSi_ma keywords in the image header. * * wcstab() parses the PVi_ma and PSi_ma keywords associated with each 'TAB' * 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 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 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 * wcsprm structs that they return. * * Given and returned: * wcs struct wcsprm* * Coordinate transformation parameters (see below). * * wcstab() sets ntab, tab, nwtb and wtb, allocating * memory for the tab and wtb arrays. This allocated * memory will be freed automatically by wcsfree(). * * Function return value: * int Status return value: * 0: Success. * 1: Null wcsprm pointer passed. * 2: Memory allocation failed. * 3: Invalid tabular parameters. * * For returns > 1, a detailed error message is set in * wcsprm::err if enabled, see wcserr_enable(). * * * wcsidx() - Index alternate coordinate representations * ----------------------------------------------------- * wcsidx() returns an array of 27 indices for the alternate coordinate * representations in the array of wcsprm structs returned by wcspih(). For * the array returned by wcsbth() it returns indices for the unattached * (colnum == 0) representations derived from image header keywords - use * wcsbdx() for those derived from binary table image arrays or pixel lists * keywords. * * Given: * nwcs int Number of coordinate representations in the array. * * wcs const struct wcsprm** * Pointer to an array of wcsprm structs returned by * wcspih() or wcsbth(). * * Returned: * alts int[27] Index of each alternate coordinate representation in * the array: alts[0] for the primary, alts[1] for 'A', * etc., set to -1 if not present. * * For example, if there was no 'P' representation then * = alts['P'-'A'+1] == -1; * * Otherwise, the address of its wcsprm struct would be * = wcs + alts['P'-'A'+1]; * * Function return value: * int Status return value: * 0: Success. * 1: Null wcsprm pointer passed. * * * wcsbdx() - Index alternate coordinate representions * --------------------------------------------------- * wcsbdx() returns an array of 999 x 27 indices for the alternate coordinate * representions for binary table image arrays xor pixel lists in the array of * wcsprm structs returned by wcsbth(). Use wcsidx() for the unattached * representations derived from image header keywords. * * Given: * nwcs int Number of coordinate representations in the array. * * wcs const struct wcsprm** * Pointer to an array of wcsprm structs returned by * wcsbth(). * * type int Select the type of coordinate representation: * 0: binary table image arrays, * 1: pixel lists. * * Returned: * alts short[1000][28] * Index of each alternate coordinate represention in the * array: alts[col][0] for the primary, alts[col][1] for * 'A', to alts[col][26] for 'Z', where col is the * 1-relative column number, and col == 0 is used for * unattached image headers. Set to -1 if not present. * * alts[col][27] counts the number of coordinate * representations of the chosen type for each column. * * For example, if there was no 'P' represention for * column 13 then * = alts[13]['P'-'A'+1] == -1; * * Otherwise, the address of its wcsprm struct would be * = wcs + alts[13]['P'-'A'+1]; * * Function return value: * int Status return value: * 0: Success. * 1: Null wcsprm pointer passed. * * * wcsvfree() - Free the array of wcsprm structs * --------------------------------------------- * wcsvfree() frees the memory allocated by wcspih() or wcsbth() for the array * of wcsprm structs, first invoking wcsfree() on each of the array members. * * Given and returned: * nwcs int* Number of coordinate representations found; set to 0 * on return. * * wcs struct wcsprm** * Pointer to the array of wcsprm structs; set to 0x0 on * return. * * Function return value: * int Status return value: * 0: Success. * 1: Null wcsprm pointer passed. * * * wcshdo() - Write out a wcsprm struct as a FITS header * ----------------------------------------------------- * wcshdo() translates a wcsprm struct into a FITS header. If the colnum * member of the struct is non-zero then a binary table image array header will * be produced. Otherwise, if the colax[] member of the struct is set non-zero * then a pixel list header will be produced. Otherwise, a primary image or * image extension header will be produced. * * If the struct was originally constructed from a header, e.g. by wcspih(), * the output header will almost certainly differ in a number of respects: * * - The output header only contains WCS-related keywords. In particular, it * does not contain syntactically-required keywords such as SIMPLE, NAXIS, * BITPIX, or END. * * - 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 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: * 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 * transformation parameters. Will be initialized if * necessary. * * Returned: * nkeyrec int* Number of FITS header keyrecords returned in the * "header" array. * * 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 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 * (*header)[0], the second at (*header)[80], etc. * * Function return value: * int Status return value (associated with wcs_errmsg[]): * 0: Success. * 1: Null wcsprm pointer passed. * 2: Memory allocation failed. * 3: Linear transformation matrix is singular. * 4: Inconsistent or unrecognized coordinate axis * types. * 5: Invalid parameter value. * 6: Invalid coordinate transformation parameters. * 7: Ill-conditioned coordinate transformation * parameters. * * For returns > 1, a detailed error message is set in * wcsprm::err if enabled, see wcserr_enable(). * * Notes: * 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_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_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 * * - 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 * * - 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. * * - WCSHDO_PVn_ma: WCS Paper I defined * * - 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 * * - 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. * * - 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 * * 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 * * 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 * * - iCNAna, iCRDna, and iCSYna for bintables and * - TCNAna, TCRDna, and TCSYna for pixel lists * * By analogy with the above, the long forms would be * * - 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. * * - 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 * --------------------------------------------------------------------- * Error messages to match the status value returned from each function. * Use wcs_errmsg[] for status returns from wcshdo(). * *===========================================================================*/ #ifndef WCSLIB_WCSHDR #define WCSLIB_WCSHDR #include "wcs.h" #ifdef __cplusplus extern "C" { #endif #define WCSHDR_none 0x00000000 #define WCSHDR_all 0x000FFFFF #define WCSHDR_reject 0x10000000 #define WCSHDR_strict 0x20000000 #define WCSHDR_CROTAia 0x00000001 #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 0x00100000 #define WCSHDR_BIMGARR 0x00200000 #define WCSHDR_PIXLIST 0x00400000 #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. }; int wcspih(char *header, int nkeyrec, int relax, int ctrl, int *nreject, int *nwcs, struct wcsprm **wcs); int wcsbth(char *header, int nkeyrec, int relax, int ctrl, int keysel, int *colsel, int *nreject, int *nwcs, struct wcsprm **wcs); int wcstab(struct wcsprm *wcs); int wcsidx(int nwcs, struct wcsprm **wcs, int alts[27]); int wcsbdx(int nwcs, struct wcsprm **wcs, int type, short alts[1000][28]); int wcsvfree(int *nwcs, struct wcsprm **wcs); int wcshdo(int ctrl, struct wcsprm *wcs, int *nkeyrec, char **header); #ifdef __cplusplus } #endif #endif // WCSLIB_WCSHDR astropy-astropy-201cddb/cextern/wcslib/C/wcslib.h000066400000000000000000000036621507226315300221570ustar00rootroot00000000000000/*============================================================================ WCSLIB 8.4 - an implementation of the FITS WCS standard. Copyright (C) 1995-2024, 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/people/Mark.Calabretta $Id: wcslib.h,v 8.4 2024/10/28 13:56:16 mcalabre Exp $ *============================================================================= * * WCSLIB 8.4 - 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 * ------------------- * This header file is provided purely for convenience. Use it to include all * of the separate WCSLIB headers. * *===========================================================================*/ #ifndef WCSLIB_WCSLIB #define WCSLIB_WCSLIB #include "cel.h" #include "dis.h" #include "fitshdr.h" #include "lin.h" #include "log.h" #include "prj.h" #include "spc.h" #include "sph.h" #include "spx.h" #include "tab.h" #include "wcs.h" #include "wcserr.h" #include "wcsfix.h" #include "wcshdr.h" #include "wcsmath.h" #include "wcsprintf.h" #include "wcstrig.h" #include "wcsunits.h" #include "wcsutil.h" #include "wtbarr.h" #endif // WCSLIB_WCSLIB astropy-astropy-201cddb/cextern/wcslib/C/wcsmath.h000066400000000000000000000036611507226315300223410ustar00rootroot00000000000000/*============================================================================ WCSLIB 8.4 - an implementation of the FITS WCS standard. Copyright (C) 1995-2024, 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/people/Mark.Calabretta $Id: wcsmath.h,v 8.4 2024/10/28 13:56:16 mcalabre Exp $ *============================================================================= * * WCSLIB 8.4 - 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. * *===========================================================================*/ #ifndef WCSLIB_WCSMATH #define WCSLIB_WCSMATH #ifdef PI #undef PI #endif #ifdef D2R #undef D2R #endif #ifdef R2D #undef R2D #endif #ifdef SQRT2 #undef SQRT2 #endif #ifdef SQRT2INV #undef SQRT2INV #endif #define PI 3.141592653589793238462643 #define D2R PI/180.0 #define R2D 180.0/PI #define SQRT2 1.4142135623730950488 #define SQRT2INV 1.0/SQRT2 #ifdef UNDEFINED #undef UNDEFINED #endif #define UNDEFINED 987654321.0e99 #define undefined(value) (value == UNDEFINED) #endif // WCSLIB_WCSMATH astropy-astropy-201cddb/cextern/wcslib/C/wcspih.l000066400000000000000000001772051507226315300222020ustar00rootroot00000000000000/*============================================================================ WCSLIB 8.4 - an implementation of the FITS WCS standard. Copyright (C) 1995-2024, 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/people/Mark.Calabretta $Id: wcspih.l,v 8.4 2024/10/28 13:56:16 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. */ %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. */ 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} I4 [1-9][0-9]{3} /* Alternate coordinate system identifier. */ ALT [ A-Z] /* Keyvalue data types. */ INTEGER [+-]?[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 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 "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); %} %% 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); ^NAXIS" = "" "*{INTEGER}{INLINE} { 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); } } } ^WCSAXES{ALT}=" "" "*{INTEGER} { 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); } } ^CRPIX { valtype = FLOAT; vptr = &(wcstem.crpix); keyname = "CRPIXja"; BEGIN(CCCCCia); } ^PC { valtype = FLOAT; vptr = &(wcstem.pc); altlin = 1; keyname = "PCi_ja"; BEGIN(CCi_ja); } ^CD { valtype = FLOAT; vptr = &(wcstem.cd); altlin = 2; keyname = "CDi_ja"; BEGIN(CCi_ja); } ^CDELT { valtype = FLOAT; vptr = &(wcstem.cdelt); keyname = "CDELTia"; BEGIN(CCCCCia); } ^CROTA { valtype = FLOAT; vptr = &(wcstem.crota); altlin = 4; keyname = "CROTAn"; BEGIN(CROTAi); } ^CUNIT { valtype = STRING; vptr = &(wcstem.cunit); keyname = "CUNITia"; BEGIN(CCCCCia); } ^CTYPE { valtype = STRING; vptr = &(wcstem.ctype); keyname = "CTYPEia"; BEGIN(CCCCCia); } ^CRVAL { valtype = FLOAT; vptr = &(wcstem.crval); keyname = "CRVALia"; BEGIN(CCCCCia); } ^LONPOLE { valtype = FLOAT; vptr = &(wcstem.lonpole); keyname = "LONPOLEa"; BEGIN(CCCCCCCa); } ^LATPOLE { valtype = FLOAT; vptr = &(wcstem.latpole); keyname = "LATPOLEa"; BEGIN(CCCCCCCa); } ^RESTFRQ { valtype = FLOAT; vptr = &(wcstem.restfrq); keyname = "RESTFRQa"; BEGIN(CCCCCCCa); } ^RESTFREQ { 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; vptr = &(wcstem.restwav); keyname = "RESTWAVa"; BEGIN(CCCCCCCa); } ^PV { valtype = FLOAT; vptr = &(wcstem.pv); npptr = npv; keyname = "PVi_ma"; BEGIN(CCi_ma); } ^PROJP { valtype = FLOAT; vptr = &(wcstem.pv); npptr = npv; keyname = "PROJPn"; BEGIN(PROJPn); } ^PS { valtype = STRING; 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; vptr = &(wcstem.cname); keyname = "CNAMEia"; BEGIN(CCCCCia); } ^CRDER { valtype = FLOAT; vptr = &(wcstem.crder); keyname = "CRDERia"; BEGIN(CCCCCia); } ^CSYER { valtype = FLOAT; vptr = &(wcstem.csyer); keyname = "CSYERia"; BEGIN(CCCCCia); } ^CZPHS { valtype = FLOAT; vptr = &(wcstem.czphs); keyname = "CZPHSia"; BEGIN(CCCCCia); } ^CPERI { valtype = FLOAT; vptr = &(wcstem.cperi); keyname = "CPERIia"; BEGIN(CCCCCia); } ^WCSNAME { valtype = STRING; vptr = wcstem.wcsname; keyname = "WCSNAMEa"; BEGIN(CCCCCCCa); } ^TIMESYS" " { valtype = STRING; vptr = wcstem.timesys; keyname = "TIMESYS"; BEGIN(CCCCCCCC); } ^TREFPOS" " { valtype = STRING; vptr = wcstem.trefpos; keyname = "TREFPOS"; BEGIN(CCCCCCCC); } ^TREFDIR" " { valtype = STRING; vptr = wcstem.trefdir; keyname = "TREFDIR"; BEGIN(CCCCCCCC); } ^PLEPHEM" " { valtype = STRING; vptr = wcstem.plephem; 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(DISCARD); } } ^MJDREF" " | ^MJD-REF" " { 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); } } ^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); } else { BEGIN(DISCARD); } } ^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); } } ^JDREF" " | ^JD-REF" " { 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); } } ^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 = "the JD-REFI keyword is non-standard"; BEGIN(ERROR); } else { BEGIN(DISCARD); } } ^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} | . { if (YY_START == CCCCCCCa) { sscanf(yytext, "%c", &a); } else { unput(yytext[0]); a = 0; } BEGIN(VALUE); } . { 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}_{Z1}{ALT}" " | {I1}_{I2}{ALT}" " | {I2}_{Z1}{ALT}" " | {I2}_{I2}{ALT} { sscanf(yytext, "%d_%d%c", &i, &m, &a); 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); } {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 = ' '; BEGIN(VALUE); } else if (relax & WCSHDR_reject) { 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 { BEGIN(DISCARD); } } . { 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 (99 < i || 99 < j || 99 < m) { if (relax & WCSHDR_reject) { if (99 < i || 99 < j) { errmsg = "axis number exceeds 99"; } else if (m > 99) { errmsg = "parameter number exceeds 99"; } BEGIN(ERROR); } else { // Pretend we don't recognize it. BEGIN(DISCARD); } } else { 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 if (valtype == RECORD) { BEGIN(RECORD_VAL); } else { errmsg = errtxt; sprintf(errmsg, "internal parser ERROR, bad data type: %d", valtype); BEGIN(ERROR); } } } . { errmsg = "invalid KEYWORD = VALUE syntax"; BEGIN(ERROR); } {INTEGER} { if (ipass == 1) { BEGIN(COMMENT); } else { // Read the keyvalue. sscanf(yytext, "%d", &inttmp); BEGIN(COMMENT); } } . { errmsg = "an integer value was expected"; BEGIN(ERROR); } {FLOAT} { if (ipass == 1) { BEGIN(COMMENT); } else { // 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); BEGIN(COMMENT); } } . { errmsg = "a floating-point value was expected"; BEGIN(ERROR); } {STRING} { if (ipass == 1) { BEGIN(COMMENT); } else { // Copy the keyvalue minus the quotes. strncpy(strtmp, yytext+1, yyleng-2); strtmp[yyleng-2] = '\0'; // Strip off trailing blanks. for (int jx = yyleng-3; jx >= 0; jx--) { if (strtmp[jx] != ' ') { break; } strtmp[jx] = '\0'; } // Squeeze out repeated quotes. int ix = 0; for (int jx = 0; jx < 72; jx++) { if (ix < jx) { strtmp[ix] = strtmp[jx]; } if (strtmp[jx] == '\0') { break; } else if (strtmp[jx] == '\'' && strtmp[jx+1] == '\'') { jx++; } ix++; } BEGIN(COMMENT); } } . { errmsg = "a string value was expected"; BEGIN(ERROR); } {RECORD} { if (ipass == 1) { BEGIN(COMMENT); } else { yyless(1); 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); } 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); } } 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; } } } } 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); } 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 (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(FLUSH); } else { errmsg = "syntactically valid WCS keyrecord has no effect"; BEGIN(ERROR); } } else { BEGIN(FLUSH); } } .*" "*\/.*$ { errmsg = "invalid keyvalue"; BEGIN(ERROR); } [^ \/\n]*{INLINE}$ { errmsg = "invalid keyvalue"; BEGIN(ERROR); } " "+[^\/\n].*{INLINE}$ { errmsg = "invalid keyvalue or malformed keycomment"; BEGIN(ERROR); } .*$ { errmsg = "malformed keycomment"; BEGIN(ERROR); } .*$ { if (ipass == npass) { if (ctrl < 0) { // Preserve discards. keep = keyrec; } else if (2 < ctrl) { nother++; wcsfprintf(stderr, "%.80s\n Not a recognized WCS keyword.\n", keyrec); } } BEGIN(FLUSH); } .*$ { if (ipass == npass) { (*nreject)++; if (ctrl%10 == -1) { // Preserve rejects. keep = keyrec; } if (1 < abs(ctrl%10)) { wcsfprintf(stderr, "%.80s\n Rejected (%d), %s.\n", keyrec, *nreject, errmsg); } } BEGIN(FLUSH); } .*\n { if (ipass == npass && keep) { if (hptr < keep) { strncpy(hptr, keep, 80); } hptr += 80; } 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; 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. 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 (2 < abs(ctrl%10)) { if (*nwcs == 1) { if (strcmp(wcs[0]->wcsname, "DEFAULTS") != 0) { wcsfprintf(stderr, "Found one coordinate representation.\n"); } } else { wcsfprintf(stderr, "Found %d coordinate representations.\n", *nwcs); } } 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; 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, yyscanner); } else { if (ctrl < 0) { *hptr = '\0'; } else if (ctrl == 1) { 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"); } status = wcspih_final(ndp, ndq, distran, dsstmp, wat, nwcs, wcs); free(watstr); return status; } } %% /*---------------------------------------------------------------------------- * 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, 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; } int ialt = 0; if (a != ' ') { ialt = a - 'A' + 1; } int *ip = alts + ialt; if (*ip < naxis) { *ip = naxis; } // i or j can be greater than naxis. if (*ip < i) { *ip = i; } if (*ip < j) { *ip = j; } // Type of distortions present. dpq[ialt] |= distype; // Count PVi_ma, PSi_ma, DPja, or DQia keywords. if (npptr) { npptr[ialt]++; } } /*---------------------------------------------------------------------------- * Allocate memory for an array of the required number of wcsprm structs and * initialize each of them. *---------------------------------------------------------------------------*/ 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 status = 0; // Find the number of coordinate descriptions. *nwcs = 0; for (int ialt = 0; ialt < 27; ialt++) { if (alts[ialt]) (*nwcs)++; } 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))) == 0x0) { return WCSHDRERR_MEMORY; } 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. struct wcsprm *wcsp = *wcs; *nwcs = 0; for (int ialt = 0; ialt < 27; ialt++) { if (alts[ialt]) { wcsp->flag = -1; 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. if (ialt) { wcsp->alt[0] = 'A' + ialt - 1; } // 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; } } // 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 ndp[], int ndq[], int distran, double dsstmp[], char *wat[], int *nwcs, struct wcsprm **wcs) { 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++; } // 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; } astropy-astropy-201cddb/cextern/wcslib/C/wcsprintf.c000066400000000000000000000103731507226315300227030ustar00rootroot00000000000000/*============================================================================ WCSLIB 8.4 - an implementation of the FITS WCS standard. Copyright (C) 1995-2024, 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/people/Mark.Calabretta $Id: wcsprintf.c,v 8.4 2024/10/28 13:56:16 mcalabre Exp $ *===========================================================================*/ #include #include #include #include "wcsprintf.h" static FILE *wcsprintf_file = 0x0; 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. wcsprintf_file = wcsout; if (wcsprintf_buff != 0x0) { // Release the buffer. free(wcsprintf_buff); wcsprintf_buff = 0x0; } } else { // Output to buffer. wcsprintf_file = 0x0; if (wcsprintf_buff == 0x0) { // Allocate a buffer. wcsprintf_buff = malloc(1024); if (wcsprintf_buff == NULL) { return 1; } wcsprintf_size = 1024; } // Reset pointer to the start of the buffer. wcsprintf_bufp = wcsprintf_buff; *wcsprintf_bufp = '\0'; } 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. wcsprintf_file = stdout; } 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; } 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; } nbytes = vsprintf(wcsprintf_bufp, format, arg_list); wcsprintf_bufp += nbytes; } va_end(arg_list); return nbytes; } astropy-astropy-201cddb/cextern/wcslib/C/wcsprintf.h000066400000000000000000000136461507226315300227160ustar00rootroot00000000000000/*============================================================================ WCSLIB 8.4 - an implementation of the FITS WCS standard. Copyright (C) 1995-2024, 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/people/Mark.Calabretta $Id: wcsprintf.h,v 8.4 2024/10/28 13:56:16 mcalabre Exp $ *============================================================================= * * WCSLIB 8.4 - 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 * --------------------------------- * 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 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 * of stdio library functions. * * ... mixed Argument list matching format, as per printf(3). * * Function return value: * int Number of bytes written. * * * 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, and for wcsfprintf() which is used by wcsbth() and * wcspih(). * * Given: * wcsout FILE* Pointer to an output stream that has been opened for * writing, e.g. by the fopen() stdio library function, * or one of the predefined stdio output streams - stdout * and stderr. If zero (NULL), output is written to an * internally-allocated string buffer, the address of * which may be obtained by wcsprintf_buf(). * * Function return value: * int Status return value: * 0: Success. * * * wcsprintf_buf() - Get the address of the internal string buffer * --------------------------------------------------------------- * wcsprintf_buf() returns the address of the internal string buffer created * when wcsprintf_set() is invoked with its FILE* argument set to zero. * * Function return value: * const char * * Address of the internal string buffer. The user may * free this buffer by calling wcsprintf_set() with a * valid FILE*, e.g. stdout. The free() stdlib library * function must NOT be invoked on this const pointer. * * * WCSPRINTF_PTR() macro - Print addresses in a consistent way * ----------------------------------------------------------- * WCSPRINTF_PTR() is a preprocessor macro used to print addresses in a * consistent way. * * On some systems the "%p" format descriptor renders a NULL pointer as the * string "0x0". On others, however, it produces "0" or even "(nil)". On * some systems a non-zero address is prefixed with "0x", on others, not. * * The WCSPRINTF_PTR() macro ensures that a NULL pointer is always rendered as * "0x0" and that non-zero addresses are prefixed with "0x" thus providing * consistency, for example, for comparing the output of test programs. * *===========================================================================*/ #ifndef WCSLIB_WCSPRINTF #define WCSLIB_WCSPRINTF #include #include #ifdef __cplusplus extern "C" { #endif #define WCSPRINTF_PTR(str1, ptr, str2) \ if (ptr) { \ 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 astropy-astropy-201cddb/cextern/wcslib/C/wcstrig.c000066400000000000000000000100441507226315300223410ustar00rootroot00000000000000/*============================================================================ WCSLIB 8.4 - an implementation of the FITS WCS standard. Copyright (C) 1995-2024, 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/people/Mark.Calabretta $Id: wcstrig.c,v 8.4 2024/10/28 13:56:16 mcalabre Exp $ *===========================================================================*/ #include #include #include "wcsmath.h" #include "wcstrig.h" double cosd(double angle) { int i; if (fmod(angle,90.0) == 0.0) { i = abs((int)floor(angle/90.0 + 0.5))%4; switch (i) { case 0: return 1.0; case 1: return 0.0; case 2: return -1.0; case 3: return 0.0; } } return cos(angle*D2R); } //---------------------------------------------------------------------------- double sind(double angle) { int i; if (fmod(angle,90.0) == 0.0) { i = abs((int)floor(angle/90.0 - 0.5))%4; switch (i) { case 0: return 1.0; case 1: return 0.0; case 2: return -1.0; case 3: return 0.0; } } return sin(angle*D2R); } //---------------------------------------------------------------------------- void sincosd(double angle, double *s, double *c) { int i; if (fmod(angle,90.0) == 0.0) { i = abs((int)floor(angle/90.0 + 0.5))%4; switch (i) { case 0: *s = 0.0; *c = 1.0; return; case 1: *s = (angle > 0.0) ? 1.0 : -1.0; *c = 0.0; return; case 2: *s = 0.0; *c = -1.0; return; case 3: *s = (angle > 0.0) ? -1.0 : 1.0; *c = 0.0; return; } } #ifdef HAVE_SINCOS sincos(angle*D2R, s, c); #else *s = sin(angle*D2R); *c = cos(angle*D2R); #endif return; } //---------------------------------------------------------------------------- double tand(double angle) { double resid; resid = fmod(angle,360.0); if (resid == 0.0 || fabs(resid) == 180.0) { return 0.0; } else if (resid == 45.0 || resid == 225.0) { return 1.0; } else if (resid == -135.0 || resid == -315.0) { return -1.0; } return tan(angle*D2R); } //---------------------------------------------------------------------------- double acosd(double v) { if (v >= 1.0) { if (v-1.0 < WCSTRIG_TOL) return 0.0; } else if (v == 0.0) { return 90.0; } else if (v <= -1.0) { if (v+1.0 > -WCSTRIG_TOL) return 180.0; } return acos(v)*R2D; } //---------------------------------------------------------------------------- double asind(double v) { if (v <= -1.0) { if (v+1.0 > -WCSTRIG_TOL) return -90.0; } else if (v == 0.0) { return 0.0; } else if (v >= 1.0) { if (v-1.0 < WCSTRIG_TOL) return 90.0; } return asin(v)*R2D; } //---------------------------------------------------------------------------- double atand(double v) { if (v == -1.0) { return -45.0; } else if (v == 0.0) { return 0.0; } else if (v == 1.0) { return 45.0; } return atan(v)*R2D; } //---------------------------------------------------------------------------- double atan2d(double y, double x) { if (y == 0.0) { if (x >= 0.0) { return 0.0; } else if (x < 0.0) { return 180.0; } } else if (x == 0.0) { if (y > 0.0) { return 90.0; } else if (y < 0.0) { return -90.0; } } return atan2(y,x)*R2D; } astropy-astropy-201cddb/cextern/wcslib/C/wcstrig.h000066400000000000000000000140521507226315300223510ustar00rootroot00000000000000/*============================================================================ WCSLIB 8.4 - an implementation of the FITS WCS standard. Copyright (C) 1995-2024, 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/people/Mark.Calabretta $Id: wcstrig.h,v 8.4 2024/10/28 13:56:16 mcalabre Exp $ *============================================================================= * * WCSLIB 8.4 - 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 * (some moreso than others) it is often desirable to use an angular measure * that provides an exact representation of the latitude of the north or south * pole. The WCSLIB routines use the following trigonometric functions that * take or return angles in degrees: * * - cosd() * - sind() * - tand() * - acosd() * - asind() * - atand() * - atan2d() * - sincosd() * * These "trigd" routines are expected to handle angles that are a multiple of * 90 degrees returning an exact result. Some C implementations provide these * as part of a system library and in such cases it may (or may not!) be * preferable to use them. WCSLIB provides wrappers on the standard trig * functions based on radian measure, adding tests for multiples of 90 degrees. * * However, wcstrig.h also provides the choice of using preprocessor macro * implementations of the trigd functions that don't test for multiples of * 90 degrees (compile with -DWCSTRIG_MACRO). These are typically 20% faster * but may lead to problems near the poles. * * * cosd() - Cosine of an angle in degrees * -------------------------------------- * cosd() returns the cosine of an angle given in degrees. * * Given: * angle double [deg]. * * Function return value: * double Cosine of the angle. * * * sind() - Sine of an angle in degrees * ------------------------------------ * sind() returns the sine of an angle given in degrees. * * Given: * angle double [deg]. * * Function return value: * double Sine of the angle. * * * sincosd() - Sine and cosine of an angle in degrees * -------------------------------------------------- * sincosd() returns the sine and cosine of an angle given in degrees. * * Given: * angle double [deg]. * * Returned: * sin *double Sine of the angle. * * cos *double Cosine of the angle. * * Function return value: * void * * * tand() - Tangent of an angle in degrees * --------------------------------------- * tand() returns the tangent of an angle given in degrees. * * Given: * angle double [deg]. * * Function return value: * double Tangent of the angle. * * * acosd() - Inverse cosine, returning angle in degrees * ---------------------------------------------------- * acosd() returns the inverse cosine in degrees. * * Given: * x double in the range [-1,1]. * * Function return value: * double Inverse cosine of x [deg]. * * * asind() - Inverse sine, returning angle in degrees * -------------------------------------------------- * asind() returns the inverse sine in degrees. * * Given: * y double in the range [-1,1]. * * Function return value: * double Inverse sine of y [deg]. * * * atand() - Inverse tangent, returning angle in degrees * ----------------------------------------------------- * atand() returns the inverse tangent in degrees. * * Given: * s double * * Function return value: * double Inverse tangent of s [deg]. * * * atan2d() - Polar angle of (x,y), in degrees * ------------------------------------------- * atan2d() returns the polar angle, beta, in degrees, of polar coordinates * (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. * * x double Cartesian x-coordinate. * * Function return value: * double Polar angle of (x,y) [deg]. * *===========================================================================*/ #ifndef WCSLIB_WCSTRIG #define WCSLIB_WCSTRIG #include #include "wcsconfig.h" #ifdef HAVE_SINCOS void sincos(double angle, double *sin, double *cos); #endif #ifdef __cplusplus extern "C" { #endif #ifdef WCSTRIG_MACRO // Macro implementation of the trigd functions. #include "wcsmath.h" #define cosd(X) cos((X)*D2R) #define sind(X) sin((X)*D2R) #define tand(X) tan((X)*D2R) #define acosd(X) acos(X)*R2D #define asind(X) asin(X)*R2D #define atand(X) atan(X)*R2D #define atan2d(Y,X) atan2(Y,X)*R2D #ifdef HAVE_SINCOS #define sincosd(X,S,C) sincos((X)*D2R,(S),(C)) #else #define sincosd(X,S,C) *(S) = sin((X)*D2R); *(C) = cos((X)*D2R); #endif #else // Use WCSLIB wrappers or native trigd functions. double cosd(double angle); double sind(double angle); void sincosd(double angle, double *sin, double *cos); double tand(double angle); double acosd(double x); double asind(double y); double atand(double s); double atan2d(double y, double x); // Domain tolerance for asin() and acos() functions. #define WCSTRIG_TOL 1e-10 #endif // WCSTRIG_MACRO #ifdef __cplusplus } #endif #endif // WCSLIB_WCSTRIG astropy-astropy-201cddb/cextern/wcslib/C/wcsulex.l000066400000000000000000000463461507226315300224000ustar00rootroot00000000000000/*============================================================================ WCSLIB 8.4 - an implementation of the FITS WCS standard. Copyright (C) 1995-2024, 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/people/Mark.Calabretta $Id: wcsulex.l,v 8.4 2024/10/28 13:56:16 mcalabre Exp $ *============================================================================= * * wcsulex.l is a Flex description file containing the definition of a * recursive, multi-buffered lexical scanner and parser for FITS units * specifications. * * It requires Flex v2.5.4 or later. * * Refer to wcsunits.h for a description of the user interface and operating * notes. * *===========================================================================*/ /* 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]* FRAC {INTEGER}"/"[1-9][0-9]* FLOAT [+-]?([0-9]+\.?[0-9]*|\.[0-9]+) /* Metric prefixes. */ SUB3 [munpfazy] SUBPREFIX [dc]|{SUB3} SUP3 [kMGTPEZY] SUPPREFIX da|h|{SUP3} PREFIX {SUBPREFIX}|{SUPPREFIX} /* Basic and derived SI units. */ BASIC m|s|g|rad|sr|K|A|mol|cd DERIVED Hz|J|W|V|N|Pa|C|[Oo]hm|S|F|Wb|T|H|lm|lx SI_UNIT {BASIC}|{DERIVED} /* Additional recognized units: all metric prefixes allowed. */ ADD_ALL eV|Jy|R|G|barn /* Additional recognized units: only super-metric prefixes allowed. */ ADD_SUP a|yr|pc|bit|[bB]yte /* Additional recognized units: only sub-metric prefixes allowed. */ ADD_SUB mag /* Additional recognized units for which NO metric prefixes are allowed. */ 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} /* All additional recognized units. */ ADD_UNIT {ADD_ALL}|{ADD_SUP}|{ADD_SUB}|{ADD_NONE} /* Exclusive start states. */ %x PAREN PREFIX UNITS EXPON FLUSH %{ #include #include #include #include #include "wcserr.h" #include "wcsmath.h" #include "wcsunits.h" #include "wcsutil.h" // 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); // Internal helper functions. static YY_DECL; %} %% static const char *function = "wcsulexe_scanner"; void add(double *factor, double types[], double *expon, double *scale, double units[]); // 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; } double expon = 1.0; double factor = 1.0; 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(yyextra->abort_jmp_env)) { return wcserr_set(WCSERR_SET(UNITSERR_PARSER_ERROR), "Internal units parser error parsing '%s'", unitstr); } BEGIN(INITIAL); #ifdef DEBUG fprintf(stderr, "\n%s ->\n", unitstr); #endif ^" "+ { // Pretend initial whitespace doesn't exist. yy_set_bol(1); } ^"[" { if (bracket++) { BEGIN(FLUSH); } else { yy_set_bol(1); } } ^10[0-9] { status = wcserr_set(WCSERR_SET(UNITSERR_BAD_NUM_MULTIPLIER), "Invalid exponent in '%s'", unitstr); BEGIN(FLUSH); } ^10 { factor = 10.0; BEGIN(EXPON); } ^log" "*"(" { *func = 1; unput('('); BEGIN(PAREN); } ^ln" "*"(" { *func = 2; unput('('); BEGIN(PAREN); } ^exp" "*"(" { *func = 3; unput('('); BEGIN(PAREN); } ^[*.] { // Leading binary multiply. status = wcserr_set(WCSERR_SET(UNITSERR_DANGLING_BINOP), "Dangling binary operator in '%s'", unitstr); BEGIN(FLUSH); } " "+ // Discard whitespace in INITIAL context. sqrt" "*"(" { expon /= 2.0; unput('('); BEGIN(PAREN); } "(" { // Gather terms in parentheses. yyless(0); BEGIN(PAREN); } [*.] { if (operator++) { BEGIN(FLUSH); } } ^1"/" | "/" { if (operator++) { BEGIN(FLUSH); } else { expon *= -1.0; } } {SI_UNIT}|{ADD_UNIT} { operator = 0; yyless(0); BEGIN(UNITS); } {PREFIX}({SI_UNIT}|{ADD_ALL}) | {SUPPREFIX}{ADD_SUP} | {SUBPREFIX}{ADD_SUB} { operator = 0; yyless(0); BEGIN(PREFIX); } "]" { bracket = !bracket; BEGIN(FLUSH); } . { status = wcserr_set(WCSERR_SET(UNITSERR_BAD_INITIAL_SYMBOL), "Invalid symbol in INITIAL context in '%s'", unitstr); BEGIN(FLUSH); } "(" { paren++; operator = 0; yymore(); } ")" { paren--; if (paren) { // Not balanced yet. yymore(); } else { // 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); YY_BUFFER_STATE buf = YY_CURRENT_BUFFER; yy_switch_to_buffer(buf, yyscanner); if (func_r) { status = wcserr_set(WCSERR_SET(UNITSERR_FUNCTION_CONTEXT), "Function in invalid context in '%s'", unitstr); } if (status) { BEGIN(FLUSH); } else { factor *= factor_r; BEGIN(EXPON); } } } [^()]+ { yymore(); } d { factor = 1e-1; BEGIN(UNITS); } c { factor = 1e-2; BEGIN(UNITS); } m { factor = 1e-3; BEGIN(UNITS); } u { factor = 1e-6; BEGIN(UNITS); } n { factor = 1e-9; BEGIN(UNITS); } p { factor = 1e-12; BEGIN(UNITS); } f { factor = 1e-15; BEGIN(UNITS); } a { factor = 1e-18; BEGIN(UNITS); } z { factor = 1e-21; BEGIN(UNITS); } y { factor = 1e-24; BEGIN(UNITS); } da { factor = 1e+1; BEGIN(UNITS); } h { factor = 1e+2; BEGIN(UNITS); } k { factor = 1e+3; BEGIN(UNITS); } M { factor = 1e+6; BEGIN(UNITS); } G { factor = 1e+9; BEGIN(UNITS); } T { factor = 1e+12; BEGIN(UNITS); } P { factor = 1e+15; BEGIN(UNITS); } E { factor = 1e+18; BEGIN(UNITS); } Z { factor = 1e+21; BEGIN(UNITS); } Y { factor = 1e+24; BEGIN(UNITS); } . { // Internal parser error. status = wcserr_set(WCSERR_SET(UNITSERR_PARSER_ERROR), "Internal units parser error parsing '%s'", unitstr); BEGIN(FLUSH); } A { // Ampere. types[WCSUNITS_CHARGE] += 1.0; types[WCSUNITS_TIME] -= 1.0; BEGIN(EXPON); } a|yr { // Julian year (annum). factor *= 31557600.0; types[WCSUNITS_TIME] += 1.0; BEGIN(EXPON); } adu { // Analogue-to-digital converter units. types[WCSUNITS_COUNT] += 1.0; BEGIN(EXPON); } [Aa]ngstrom { // Angstrom. factor *= 1e-10; types[WCSUNITS_LENGTH] += 1.0; BEGIN(EXPON); } arcmin { // Minute of arc. factor /= 60.0; types[WCSUNITS_PLANE_ANGLE] += 1.0; BEGIN(EXPON); } arcsec { // Second of arc. factor /= 3600.0; types[WCSUNITS_PLANE_ANGLE] += 1.0; BEGIN(EXPON); } AU { // Astronomical unit. factor *= 1.49598e+11; types[WCSUNITS_LENGTH] += 1.0; BEGIN(EXPON); } barn { // Barn. factor *= 1e-28; types[WCSUNITS_LENGTH] += 2.0; BEGIN(EXPON); } beam { // Beam, as in Jy/beam. types[WCSUNITS_BEAM] += 1.0; BEGIN(EXPON); } bin { // Bin (e.g. histogram). types[WCSUNITS_BIN] += 1.0; BEGIN(EXPON); } bit { // Bit. types[WCSUNITS_BIT] += 1.0; BEGIN(EXPON); } [bB]yte { // Byte. factor *= 8.0; types[WCSUNITS_BIT] += 1.0; BEGIN(EXPON); } C { // Coulomb. types[WCSUNITS_CHARGE] += 1.0; BEGIN(EXPON); } cd { // Candela. types[WCSUNITS_LUMINTEN] += 1.0; BEGIN(EXPON); } chan { // Channel. types[WCSUNITS_BIN] += 1.0; BEGIN(EXPON); } count|ct { // Count. types[WCSUNITS_COUNT] += 1.0; BEGIN(EXPON); } cy { // Julian century. factor *= 3155760000.0; types[WCSUNITS_TIME] += 1.0; BEGIN(EXPON); } D { // Debye. factor *= 1e-29 / 3.0; types[WCSUNITS_CHARGE] += 1.0; types[WCSUNITS_LENGTH] += 1.0; BEGIN(EXPON); } d { // Day. factor *= 86400.0; types[WCSUNITS_TIME] += 1.0; BEGIN(EXPON); } deg { // Degree. types[WCSUNITS_PLANE_ANGLE] += 1.0; BEGIN(EXPON); } erg { // Erg. factor *= 1e-7; types[WCSUNITS_MASS] += 1.0; types[WCSUNITS_LENGTH] += 2.0; types[WCSUNITS_TIME] -= 2.0; BEGIN(EXPON); } eV { // Electron volt. factor *= 1.6021765e-19; types[WCSUNITS_MASS] += 1.0; types[WCSUNITS_LENGTH] += 2.0; types[WCSUNITS_TIME] -= 2.0; BEGIN(EXPON); } F { // Farad. types[WCSUNITS_MASS] -= 1.0; types[WCSUNITS_LENGTH] -= 2.0; types[WCSUNITS_TIME] += 3.0; types[WCSUNITS_CHARGE] += 2.0; BEGIN(EXPON); } G { // Gauss. factor *= 1e-4; types[WCSUNITS_MASS] += 1.0; types[WCSUNITS_TIME] += 1.0; types[WCSUNITS_CHARGE] -= 1.0; BEGIN(EXPON); } g { // Gram. factor *= 1e-3; types[WCSUNITS_MASS] += 1.0; BEGIN(EXPON); } H { // Henry. types[WCSUNITS_MASS] += 1.0; types[WCSUNITS_LENGTH] += 2.0; types[WCSUNITS_TIME] += 2.0; types[WCSUNITS_CHARGE] -= 2.0; BEGIN(EXPON); } h { // Hour. factor *= 3600.0; types[WCSUNITS_TIME] += 1.0; BEGIN(EXPON); } Hz { // Hertz. types[WCSUNITS_TIME] -= 1.0; BEGIN(EXPON); } J { // Joule. types[WCSUNITS_MASS] += 1.0; types[WCSUNITS_LENGTH] += 2.0; types[WCSUNITS_TIME] -= 2.0; BEGIN(EXPON); } Jy { // Jansky. factor *= 1e-26; types[WCSUNITS_MASS] += 1.0; types[WCSUNITS_TIME] -= 2.0; BEGIN(EXPON); } K { // Kelvin. types[WCSUNITS_TEMPERATURE] += 1.0; BEGIN(EXPON); } lm { // Lumen. types[WCSUNITS_LUMINTEN] += 1.0; types[WCSUNITS_SOLID_ANGLE] += 1.0; BEGIN(EXPON); } lx { // Lux. types[WCSUNITS_LUMINTEN] += 1.0; types[WCSUNITS_SOLID_ANGLE] += 1.0; types[WCSUNITS_LENGTH] -= 2.0; BEGIN(EXPON); } lyr { // Light year. factor *= 2.99792458e8 * 31557600.0; types[WCSUNITS_LENGTH] += 1.0; BEGIN(EXPON); } m { // Metre. types[WCSUNITS_LENGTH] += 1.0; BEGIN(EXPON); } mag { // Stellar magnitude. types[WCSUNITS_MAGNITUDE] += 1.0; BEGIN(EXPON); } mas { // Milli-arcsec. factor /= 3600e+3; types[WCSUNITS_PLANE_ANGLE] += 1.0; BEGIN(EXPON); } min { // Minute. factor *= 60.0; types[WCSUNITS_TIME] += 1.0; BEGIN(EXPON); } mol { // Mole. types[WCSUNITS_MOLE] += 1.0; BEGIN(EXPON); } N { // Newton. types[WCSUNITS_MASS] += 1.0; types[WCSUNITS_LENGTH] += 1.0; types[WCSUNITS_TIME] -= 2.0; BEGIN(EXPON); } [Oo]hm { // Ohm. types[WCSUNITS_MASS] += 1.0; types[WCSUNITS_LENGTH] += 2.0; types[WCSUNITS_TIME] -= 1.0; types[WCSUNITS_CHARGE] -= 2.0; BEGIN(EXPON); } Pa { // Pascal. types[WCSUNITS_MASS] += 1.0; types[WCSUNITS_LENGTH] -= 1.0; types[WCSUNITS_TIME] -= 2.0; BEGIN(EXPON); } pc { // Parsec. factor *= 3.0857e16; types[WCSUNITS_LENGTH] += 1.0; BEGIN(EXPON); } photon|ph { // Photon. types[WCSUNITS_COUNT] += 1.0; BEGIN(EXPON); } pixel|pix { // Pixel. types[WCSUNITS_PIXEL] += 1.0; BEGIN(EXPON); } R { // Rayleigh. factor *= 1e10 / (4.0 * PI); types[WCSUNITS_LENGTH] -= 2.0; types[WCSUNITS_TIME] -= 1.0; types[WCSUNITS_SOLID_ANGLE] -= 1.0; BEGIN(EXPON); } rad { // Radian. factor *= 180.0 / PI; types[WCSUNITS_PLANE_ANGLE] += 1.0; BEGIN(EXPON); } Ry { // Rydberg. factor *= 13.605692 * 1.6021765e-19; types[WCSUNITS_MASS] += 1.0; types[WCSUNITS_LENGTH] += 2.0; types[WCSUNITS_TIME] -= 2.0; BEGIN(EXPON); } S { // Siemen. types[WCSUNITS_MASS] -= 1.0; types[WCSUNITS_LENGTH] -= 2.0; types[WCSUNITS_TIME] += 1.0; types[WCSUNITS_CHARGE] += 2.0; BEGIN(EXPON); } s { // Second. types[WCSUNITS_TIME] += 1.0; BEGIN(EXPON); } solLum { // Solar luminosity. factor *= 3.8268e26; types[WCSUNITS_MASS] += 1.0; types[WCSUNITS_LENGTH] += 2.0; types[WCSUNITS_TIME] -= 3.0; BEGIN(EXPON); } solMass { // Solar mass. factor *= 1.9891e30; types[WCSUNITS_MASS] += 1.0; BEGIN(EXPON); } solRad { // Solar radius. factor *= 6.9599e8; types[WCSUNITS_LENGTH] += 1.0; BEGIN(EXPON); } sr { // Steradian. types[WCSUNITS_SOLID_ANGLE] += 1.0; BEGIN(EXPON); } Sun { // Sun (with respect to). types[WCSUNITS_SOLRATIO] += 1.0; BEGIN(EXPON); } T { // 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. factor *= 1.6605387e-27; types[WCSUNITS_MASS] += 1.0; BEGIN(EXPON); } V { // Volt. types[WCSUNITS_MASS] += 1.0; types[WCSUNITS_LENGTH] += 1.0; types[WCSUNITS_TIME] -= 2.0; types[WCSUNITS_CHARGE] -= 1.0; BEGIN(EXPON); } voxel { // Voxel. types[WCSUNITS_VOXEL] += 1.0; BEGIN(EXPON); } W { // Watt. types[WCSUNITS_MASS] += 1.0; types[WCSUNITS_LENGTH] += 2.0; types[WCSUNITS_TIME] -= 3.0; BEGIN(EXPON); } Wb { // Weber. types[WCSUNITS_MASS] += 1.0; types[WCSUNITS_LENGTH] += 2.0; types[WCSUNITS_TIME] += 1.0; types[WCSUNITS_CHARGE] -= 1.0; BEGIN(EXPON); } . { // Internal parser error. status = wcserr_set(WCSERR_SET(UNITSERR_PARSER_ERROR), "Internal units parser error parsing '%s'", unitstr); BEGIN(FLUSH); } " "*("**"|^) { // Exponentiation. if (operator++) { BEGIN(FLUSH); } } " "*{INTEGER} { int i; sscanf(yytext, " %d", &i); expon *= (double)i; add(&factor, types, &expon, scale, units); operator = 0; BEGIN(INITIAL); } " "*"("" "*{INTEGER}" "*")" { int i; sscanf(yytext, " (%d)", &i); expon *= (double)i; add(&factor, types, &expon, scale, units); operator = 0; BEGIN(INITIAL); } " "*"("" "*{FRAC}" "*")" { int i, j; sscanf(yytext, " (%d/%d)", &i, &j); expon *= (double)i / (double)j; add(&factor, types, &expon, scale, units); operator = 0; BEGIN(INITIAL); } " "*"("" "*{FLOAT}" "*")" { 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); } " "*[.*]" "* { // Multiply. if (operator++) { BEGIN(FLUSH); } else { add(&factor, types, &expon, scale, units); BEGIN(INITIAL); } } " "*"(" { // Multiply. if (operator) { BEGIN(FLUSH); } else { add(&factor, types, &expon, scale, units); unput('('); BEGIN(INITIAL); } } " "+ { // Multiply. if (operator) { BEGIN(FLUSH); } else { add(&factor, types, &expon, scale, units); BEGIN(INITIAL); } } " "*"/"" "* { // Divide. if (operator++) { BEGIN(FLUSH); } else { add(&factor, types, &expon, scale, units); expon = -1.0; BEGIN(INITIAL); } } " "*"]" { add(&factor, types, &expon, scale, units); bracket = !bracket; BEGIN(FLUSH); } . { status = wcserr_set(WCSERR_SET(UNITSERR_BAD_EXPON_SYMBOL), "Invalid symbol in EXPON context in '%s'", unitstr); BEGIN(FLUSH); } .* { // Discard any remaining input. } <> { // End-of-string. if (YY_START == EXPON) { add(&factor, types, &expon, scale, units); } if (bracket) { status = wcserr_set(WCSERR_SET(UNITSERR_UNBAL_BRACKET), "Unbalanced bracket in '%s'", unitstr); } else if (paren) { status = wcserr_set(WCSERR_SET(UNITSERR_UNBAL_PAREN), "Unbalanced parenthesis in '%s'", unitstr); } else if (operator == 1) { status = wcserr_set(WCSERR_SET(UNITSERR_DANGLING_BINOP), "Dangling binary operator in '%s'", unitstr); } else if (operator) { status = wcserr_set(WCSERR_SET(UNITSERR_CONSEC_BINOPS), "Consecutive binary operators in '%s'", unitstr); #ifdef DEBUG } else { fprintf(stderr, "EOS\n"); #endif } if (status) { for (int i = 0; i < WCSUNITS_NTYPE; i++) { units[i] = 0.0; *scale = 0.0; } } return status; } %% /*---------------------------------------------------------------------------- * 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. *---------------------------------------------------------------------------*/ void add( double *factor, double types[], double *expon, double *scale, double units[]) { *scale *= pow(*factor, *expon); for (int i = 0; i < WCSUNITS_NTYPE; i++) { units[i] += *expon * types[i]; types[i] = 0.0; } *expon = 1.0; *factor = 1.0; return; } astropy-astropy-201cddb/cextern/wcslib/C/wcsunits.c000066400000000000000000000125111507226315300225370ustar00rootroot00000000000000/*============================================================================ WCSLIB 8.4 - an implementation of the FITS WCS standard. Copyright (C) 1995-2024, 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/people/Mark.Calabretta $Id: wcsunits.c,v 8.4 2024/10/28 13:56:16 mcalabre Exp $ *===========================================================================*/ #include #include "wcsunits.h" // Map status return value to message. const char *wcsunits_errmsg[] = { "Success", "Invalid numeric multiplier", "Dangling binary operator", "Invalid symbol in INITIAL context", "Function in invalid context", "Invalid symbol in EXPON context", "Unbalanced bracket", "Unbalanced parenthesis", "Consecutive binary operators", "Internal parser error", "Non-conformant unit specifications", "Non-conformant functions", "Potentially unsafe translation"}; // Unit types. const char *wcsunits_types[] = { "plane angle", "solid angle", "charge", "mole", "temperature", "luminous intensity", "mass", "length", "time", "beam", "bin", "bit", "count", "stellar magnitude", "pixel", "solar ratio", "voxel"}; const char *wcsunits_units[] = { "degree", "steradian", "Coulomb", "mole", "Kelvin", "candela", "kilogram", "metre", "second", "", "", "", "", "", "", "", ""}; const char *wcsunits_funcs[] = { "none", "log", "ln", "exp"}; //---------------------------------------------------------------------------- int wcsunits( const char have[], const char want[], double *scale, double *offset, double *power) { return wcsunitse(have, want, scale, offset, power, 0x0); } //---------------------------------------------------------------------------- int wcsunitse( const char have[], const char want[], double *scale, double *offset, double *power, struct wcserr **err) { static const char *function = "wcsunitse"; 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 (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'", wcsunits_types[i], have, want); } } switch (func1) { case 0: // No function. if (func2) { return wcserr_set(WCSERR_SET(UNITSERR_BAD_FUNCS), "Mismatched unit functions: have '%s' (%s), want '%s' (%s)", have, wcsunits_funcs[func1], want, wcsunits_funcs[func2]); } *scale = scale1 / scale2; break; case 1: // log(). if (func2 == 1) { // log(). *scale = 1.0; *offset = log10(scale1 / scale2); } else if (func2 == 2) { // ln(). *scale = log(10.0); *offset = log(scale1 / scale2); } else { return wcserr_set(WCSERR_SET(UNITSERR_BAD_FUNCS), "Mismatched unit functions: have '%s' (%s), want '%s' (%s)", have, wcsunits_funcs[func1], want, wcsunits_funcs[func2]); } break; case 2: // ln(). if (func2 == 1) { // log(). *scale = 1.0 / log(10.0); *offset = log(scale1 / scale2); } else if (func2 == 2) { // ln(). *scale = 1.0; *offset = log(scale1 / scale2); } else { return wcserr_set(WCSERR_SET(UNITSERR_BAD_FUNCS), "Mismatched unit functions: have '%s' (%s), want '%s' (%s)", have, wcsunits_funcs[func1], want, wcsunits_funcs[func2]); } break; case 3: // exp(). if (func2 != 3) { return wcserr_set(WCSERR_SET(UNITSERR_BAD_FUNCS), "Mismatched unit functions: have '%s' (%s), want '%s' (%s)", have, wcsunits_funcs[func1], want, wcsunits_funcs[func2]); } *scale = 1.0; *power = scale1 / scale2; break; default: // Internal parser error. return wcserr_set(WCSERR_SET(UNITSERR_PARSER_ERROR), "Internal units parser error"); } return 0; } //---------------------------------------------------------------------------- int wcsutrn(int ctrl, char unitstr[]) { return wcsutrne(ctrl, unitstr, 0x0); } //---------------------------------------------------------------------------- int wcsulex( const char unitstr[], int *func, double *scale, double units[WCSUNITS_NTYPE]) { return wcsulexe(unitstr, func, scale, units, 0x0); } astropy-astropy-201cddb/cextern/wcslib/C/wcsunits.h000066400000000000000000000415461507226315300225560ustar00rootroot00000000000000/*============================================================================ WCSLIB 8.4 - an implementation of the FITS WCS standard. Copyright (C) 1995-2024, 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/people/Mark.Calabretta $Id: wcsunits.h,v 8.4 2024/10/28 13:56:16 mcalabre Exp $ *============================================================================= * * WCSLIB 8.4 - 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 wcsunits routines * -------------------------------- * Routines in this suite deal with units specifications and conversions, as * described in * = "Representations of world coordinates in FITS", = Greisen, E.W., & Calabretta, M.R. 2002, A&A, 395, 1061 (WCS Paper I) * * The Flexible Image Transport System (FITS), a data format widely used in * astronomy for data interchange and archive, is described in * = "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 * * These routines perform basic units-related operations: * * - wcsunitse(): given two unit specifications, derive the conversion from * one to the other. * * - wcsutrne(): translates certain commonly used but non-standard unit * strings. It is intended to be called before wcsulexe() which only * handles standard FITS units specifications. * * - wcsulexe(): parses a standard FITS units specification of arbitrary * complexity, deriving the conversion to canonical units. * * * wcsunitse() - FITS units specification conversion * ------------------------------------------------- * wcsunitse() derives the conversion from one system of units to another. * * A deprecated form of this function, wcsunits(), lacks the wcserr** * parameter. * * Given: * have const char [] * FITS units specification to convert from (null- * terminated), with or without surrounding square * brackets (for inline specifications); text following * the closing bracket is ignored. * * want const char [] * FITS units specification to convert to (null- * terminated), with or without surrounding square * brackets (for inline specifications); text following * the closing bracket is ignored. * * Returned: * scale, * offset, * power double* Convert units using * = pow(scale*value + offset, power); * * Normally offset is zero except for log() or ln() * conversions, e.g. "log(MHz)" to "ln(Hz)". Likewise, * power is normally unity except for exp() conversions, * e.g. "exp(ms)" to "exp(/Hz)". Thus conversions * ordinarily consist of * = value *= scale; * * err struct wcserr ** * 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: * 0: Success. * 1-9: Status return from wcsulexe(). * 10: Non-conformant unit specifications. * 11: Non-conformant functions. * * scale is zeroed on return if an error occurs. * * * wcsutrne() - Translation of non-standard unit specifications * ------------------------------------------------------------ * wcsutrne() translates certain commonly used but non-standard unit strings, * e.g. "DEG", "MHZ", "KELVIN", that are not recognized by wcsulexe(), refer to * the notes below for a full list. Compounds are also recognized, e.g. * "JY/BEAM" and "KM/SEC/SEC". Extraneous embedded blanks are removed. * * A deprecated form of this function, wcsutrn(), lacks the wcserr** parameter. * * Given: * ctrl int 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 * bit-flag controls what to do in such cases: * 1: Translate "S" to "s". * 2: Translate "H" to "h". * 4: Translate "D" to "d". * Thus ctrl == 0 doesn't do any unsafe translations, * whereas ctrl == 7 does all of them. * * Given and returned: * unitstr char [] Null-terminated character array containing the units * specification to be translated. * * 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 '[' * will be stripped off, but text following the closing * bracket will be preserved without modification. * * err struct wcserr ** * 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: * -1: No change was made, other than stripping blanks * (not an error). * 0: Success. * 9: Internal parser error. * 12: Potentially unsafe translation, whether applied * or not (see notes). * * Notes: * 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 * -------------------------------------------- * wcsulexe() parses a standard FITS units specification of arbitrary * complexity, deriving the scale factor required to convert to canonical * units - basically SI with degrees and "dimensionless" additions such as * byte, pixel and count. * * A deprecated form of this function, wcsulex(), lacks the wcserr** parameter. * * Given: * unitstr const char [] * Null-terminated character array containing the units * specification, with or without surrounding square * brackets (for inline specifications); text following * the closing bracket is ignored. * * Returned: * func int* Special function type, see note 4: * 0: None * 1: log() ...base 10 * 2: ln() ...base e * 3: exp() * * scale double* Scale factor for the unit specification; multiply a * value expressed in the given units by this factor to * convert it to canonical units. * * units double[WCSUNITS_NTYPE] * A units specification is decomposed into powers of 16 * fundamental unit types: angle, mass, length, time, * count, pixel, etc. Preprocessor macro WCSUNITS_NTYPE * is defined to dimension this vector, and others such * WCSUNITS_PLANE_ANGLE, WCSUNITS_LENGTH, etc. to access * its elements. * * Corresponding character strings, wcsunits_types[] and * wcsunits_units[], are predefined to describe each * quantity and its canonical units. * * err struct wcserr ** * 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: * 0: Success. * 1: Invalid numeric multiplier. * 2: Dangling binary operator. * 3: Invalid symbol in INITIAL context. * 4: Function in invalid context. * 5: Invalid symbol in EXPON context. * 6: Unbalanced bracket. * 7: Unbalanced parenthesis. * 8: Consecutive binary operators. * 9: Internal parser error. * * scale and units[] are zeroed on return if an error * occurs. * * Notes: * 1: wcsulexe() 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. * * 2: Supported extensions: * - "angstrom" (OGIP usage) is allowed in addition to "Angstrom". * - "ohm" (OGIP usage) is allowed in addition to "Ohm". * - "Byte" (common usage) is allowed in addition to "byte". * * 3: Table 6 of WCS Paper I lists eleven units for which metric prefixes are * allowed. However, in this implementation only prefixes greater than * unity are allowed for "a" (annum), "yr" (year), "pc" (parsec), "bit", * and "byte", and only prefixes less than unity are allowed for "mag" * (stellar magnitude). * * Metric prefix "P" (peta) is specifically forbidden for "a" (annum) to * avoid confusion with "Pa" (Pascal, not peta-annum). Note that metric * prefixes are specifically disallowed for "h" (hour) and "d" (day) so * that "ph" (photons) cannot be interpreted as pico-hours, nor "cd" * (candela) as centi-days. * * 4: Function types log(), ln() and exp() may only occur at the start of the * units specification. The scale and units[] returned for these refers * to the string inside the function "argument", e.g. to "MHz" in log(MHz) * for which a scale of 1e6 will be returned. * * * Global variable: const char *wcsunits_errmsg[] - Status return messages * ----------------------------------------------------------------------- * Error messages to match the status value returned from each function. * * * Global variable: const char *wcsunits_types[] - Names of physical quantities * ---------------------------------------------------------------------------- * Names for physical quantities to match the units vector returned by * wcsulexe(): * - 0: plane angle * - 1: solid angle * - 2: charge * - 3: mole * - 4: temperature * - 5: luminous intensity * - 6: mass * - 7: length * - 8: time * - 9: beam * - 10: bin * - 11: bit * - 12: count * - 13: stellar magnitude * - 14: pixel * - 15: solar ratio * - 16: voxel * * * Global variable: const char *wcsunits_units[] - Names of units * -------------------------------------------------------------- * Names for the units (SI) to match the units vector returned by wcsulexe(): * - 0: degree * - 1: steradian * - 2: Coulomb * - 3: mole * - 4: Kelvin * - 5: candela * - 6: kilogram * - 7: metre * - 8: second * * The remainder are dimensionless. *===========================================================================*/ #ifndef WCSLIB_WCSUNITS #define WCSLIB_WCSUNITS #include "wcserr.h" #ifdef __cplusplus extern "C" { #endif 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. }; extern const char *wcsunits_types[]; extern const char *wcsunits_units[]; #define WCSUNITS_PLANE_ANGLE 0 #define WCSUNITS_SOLID_ANGLE 1 #define WCSUNITS_CHARGE 2 #define WCSUNITS_MOLE 3 #define WCSUNITS_TEMPERATURE 4 #define WCSUNITS_LUMINTEN 5 #define WCSUNITS_MASS 6 #define WCSUNITS_LENGTH 7 #define WCSUNITS_TIME 8 #define WCSUNITS_BEAM 9 #define WCSUNITS_BIN 10 #define WCSUNITS_BIT 11 #define WCSUNITS_COUNT 12 #define WCSUNITS_MAGNITUDE 13 #define WCSUNITS_PIXEL 14 #define WCSUNITS_SOLRATIO 15 #define WCSUNITS_VOXEL 16 #define WCSUNITS_NTYPE 17 int wcsunitse(const char have[], const char want[], double *scale, double *offset, double *power, struct wcserr **err); int wcsutrne(int ctrl, char unitstr[], struct wcserr **err); int wcsulexe(const char unitstr[], int *func, double *scale, double units[WCSUNITS_NTYPE], struct wcserr **err); // 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[WCSUNITS_NTYPE]); #ifdef __cplusplus } #endif #endif // WCSLIB_WCSUNITS astropy-astropy-201cddb/cextern/wcslib/C/wcsutil.c000066400000000000000000000264011507226315300223550ustar00rootroot00000000000000/*============================================================================ WCSLIB 8.4 - an implementation of the FITS WCS standard. Copyright (C) 1995-2024, 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/people/Mark.Calabretta $Id: wcsutil.c,v 8.4 2024/10/28 13:56:16 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[]) { if (n <= 0) return; 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[]) { if (n <= 0) return; if (c == 0x0) { return; } // Find the first NULL character. int j; for (j = 0; j < n; j++) { if (c[j] == '\0') { break; } } // Ensure null-termination. if (j == n) { j = n - 1; c[j] = '\0'; } // 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_all_ival(int nelem, int ival, const int iarr[]) { 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; double v0 = *first; for (const double *vp = first+nelem; vp < first + nvec*nelem; vp += nelem) { if (*vp != v0) return 0; } return 1; } //---------------------------------------------------------------------------- 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]) { 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; 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) { if (nvec <= 0 || nelem <= 0) return; 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) { if (bits == 0 || nelem <= 0) return; if (sel == 0x0) { // All elements selected. for (int *arrp = array; arrp < array + nelem; arrp++) { *arrp |= bits; } } else { // Some elements selected. for (int *arrp = array; arrp < array + nelem; arrp++) { if (*(sel++)) *arrp |= bits; } } } //---------------------------------------------------------------------------- char *wcsutil_fptr2str(void (*fptr)(void), char hext[19]) { // 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(fptr) - 1; le = -1; } char *t = hext; sprintf(t, "0x0"); t += 2; int gotone = 0; for (size_t i = 0; i < sizeof(fptr); i++) { // Skip leading zeroes. if (*p) gotone = 1; if (gotone) { sprintf(t, "%02x", *p); t += 2; } p += le; } 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) { struct lconv *locale_data = localeconv(); const char *decimal_point = locale_data->decimal_point; if (decimal_point[0] != '.' || decimal_point[1] != 0) { char *out = outbuf; size_t decimal_point_len = strlen(decimal_point); for ( ; *inbuf; inbuf++) { if (*inbuf == '.') { memcpy(out, decimal_point, decimal_point_len); out += decimal_point_len; } else { *out++ = *inbuf; } } *out = '\0'; return outbuf; } else { return inbuf; } } int wcsutil_str2double(const char *buf, double *value) { char ctmp[72]; return sscanf(wcsutil_dot_to_locale(buf, ctmp), "%lf", value) < 1; } int wcsutil_str2double2(const char *buf, double *value) { value[0] = 0.0; value[1] = 0.0; // 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]); 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; } 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++; } if (sscanf(wcsutil_dot_to_locale(ctmp, ltmp), "%lf", value+1) < 1) { return 1; } } return 0; } astropy-astropy-201cddb/cextern/wcslib/C/wcsutil.h000066400000000000000000000417241507226315300223670ustar00rootroot00000000000000/*============================================================================ WCSLIB 8.4 - an implementation of the FITS WCS standard. Copyright (C) 1995-2024, 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/people/Mark.Calabretta $Id: wcsutil.h,v 8.4 2024/10/28 13:56:16 mcalabre Exp $ *============================================================================= * * WCSLIB 8.4 - 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. 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 sub-string with blanks starting with * the terminating NULL character (if any). * * Given: * n int Length of the sub-string. * * Given and returned: * c char[] The character sub-string, which will not be * null-terminated on return. * * Function return value: * void * * * wcsutil_null_fill() - Fill a character string with NULLs * -------------------------------------------------------- * INTERNAL USE ONLY. * * 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. * * 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 (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. * * wcsutil_allEq() tests for equality of a particular element in a set of * vectors. * * Given: * nvec int The number of vectors. * * nelem int The length of each vector. * * first const double* * Pointer to the first element to test in the array. * The elements tested for equality are * = *first == *(first + nelem) = == *(first + nelem*2) = : = == *(first + nelem*(nvec-1)); * * The array might be dimensioned as * = double v[nvec][nelem]; * * Function return value: * int Status return value: * 0: Not all equal. * 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 * of type double. * * Given: * nvec int The number of vectors. * * nelem int The length of each vector. * * Given and returned: * first double* Pointer to the first element in the array, the value * of which is used to set the others * = *(first + nelem) = *first; = *(first + nelem*2) = *first; = : = *(first + nelem*(nvec-1)) = *first; * * The array might be dimensioned as * = double v[nvec][nelem]; * * Function return value: * void * * * wcsutil_setAli() - Set a particular vector element * -------------------------------------------------- * INTERNAL USE ONLY. * * wcsutil_setAli() sets the value of a particular element in a set of vectors * of type int. * * Given: * nvec int The number of vectors. * * nelem int The length of each vector. * * Given and returned: * first int* Pointer to the first element in the array, the value * of which is used to set the others * = *(first + nelem) = *first; = *(first + nelem*2) = *first; = : = *(first + nelem*(nvec-1)) = *first; * * The array might be dimensioned as * = int v[nvec][nelem]; * * Function return value: * void * * * wcsutil_setBit() - Set bits in selected elements of an array * ------------------------------------------------------------ * INTERNAL USE ONLY. * * wcsutil_setBit() sets bits in selected elements of an array. * * Given: * nelem int Number of elements in the array. * * sel const int* * Address of a selection array of length nelem. May * be specified as the null pointer in which case all * elements are selected. * * bits int Bit mask. * * Given and returned: * array int* Address of the array of length nelem. * * Function return value: * void * * * wcsutil_fptr2str() - Translate pointer-to-function to string * ------------------------------------------------------------ * INTERNAL USE ONLY. * * 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, 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 void(*)() Pointer to function. * * Returned: * 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'. * * Function return value: * char * The address of hext. * * * wcsutil_double2str() - Translate double to string ignoring the locale * --------------------------------------------------------------------- * 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. 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 * should only include a formatting directive and * 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. * * * wcsutil_str2double() - Translate string to a double, ignoring the locale * ------------------------------------------------------------------------ * INTERNAL USE ONLY. * * 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 * * 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(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 astropy-astropy-201cddb/cextern/wcslib/C/wcsutrn.l000066400000000000000000000160551507226315300224050ustar00rootroot00000000000000/*============================================================================ WCSLIB 8.4 - an implementation of the FITS WCS standard. Copyright (C) 1995-2024, 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/people/Mark.Calabretta $Id: wcsutrn.l,v 8.4 2024/10/28 13:56:16 mcalabre Exp $ *============================================================================= * * wcsutrn.l is a Flex description file containing the definition of a lexical * scanner that translates non-standard FITS units specifications. * * It requires Flex v2.5.4 or later. * * Refer to wcsunits.h for a description of the user interface and operating * notes. * *===========================================================================*/ /* 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 %{ #include #include #include #include #include "wcserr.h" #include "wcsunits.h" // 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); // Internal helper functions. static YY_DECL; %} %% 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_delete_buffer(YY_CURRENT_BUFFER, yyscanner); yy_scan_string(unitstr, yyscanner); *unitstr = '\0'; // 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"); } BEGIN(INITIAL); #ifdef DEBUG fprintf(stderr, "\n%s ->\n", unitstr); #endif ^" "*"[" { // Looks like a keycomment. strcat(unitstr, "["); bracket = 1; } " "+ // Discard leading whitespace. [^A-Za-z] { // Non-alphabetic character. strcat(unitstr, yytext); if (bracket && *yytext == ']') { BEGIN(FLUSH); } } Angstroms|angstroms? { strcpy(orig, yytext); strcpy(subs, "Angstrom"); BEGIN(NEXT); } arcmins|ARCMINS? { strcpy(orig, yytext); strcpy(subs, "arcmin"); BEGIN(NEXT); } arcsecs|ARCSECS? { strcpy(orig, yytext); strcpy(subs, "arcsec"); BEGIN(NEXT); } BEAM { strcpy(orig, yytext); strcpy(subs, "beam"); BEGIN(NEXT); } Byte { strcpy(orig, yytext); strcpy(subs, "byte"); BEGIN(NEXT); } days?|DAYS? { strcpy(orig, yytext); strcpy(subs, "d"); BEGIN(NEXT); } D { unsafe = 1; strcpy(orig, yytext); strcpy(subs, (ctrl & 4) ? "d" : "D"); BEGIN(NEXT); } degrees?|Deg|Degrees?|DEG|DEGREES? { strcpy(orig, yytext); strcpy(subs, "deg"); BEGIN(NEXT); } GHZ { strcpy(orig, yytext); strcpy(subs, "GHz"); BEGIN(NEXT); } hr|HR { strcpy(orig, yytext); strcpy(subs, "h"); BEGIN(NEXT); } H { unsafe = 1; strcpy(orig, yytext); strcpy(subs, (ctrl & 2) ? "h" : "H"); BEGIN(NEXT); } hz|HZ { strcpy(orig, yytext); strcpy(subs, "Hz"); BEGIN(NEXT); } KHZ { strcpy(orig, yytext); strcpy(subs, "kHz"); BEGIN(NEXT); } JY { strcpy(orig, yytext); strcpy(subs, "Jy"); BEGIN(NEXT); } [kK]elvins?|KELVINS? { strcpy(orig, yytext); strcpy(subs, "K"); BEGIN(NEXT); } KM { strcpy(orig, yytext); strcpy(subs, "km"); BEGIN(NEXT); } metres?|meters?|M|METRES?|METERS? { strcpy(orig, yytext); strcpy(subs, "m"); BEGIN(NEXT); } MIN { strcpy(orig, yytext); strcpy(subs, "min"); BEGIN(NEXT); } MHZ { strcpy(orig, yytext); strcpy(subs, "MHz"); BEGIN(NEXT); } Ohm { strcpy(orig, yytext); strcpy(subs, "ohm"); BEGIN(NEXT); } [pP]ascals?|PASCALS? { strcpy(orig, yytext); strcpy(subs, "Pa"); BEGIN(NEXT); } pixels|PIXELS? { strcpy(orig, yytext); strcpy(subs, "pixel"); BEGIN(NEXT); } radians?|RAD|RADIANS? { strcpy(orig, yytext); strcpy(subs, "rad"); BEGIN(NEXT); } sec|seconds?|SEC|SECONDS? { strcpy(orig, yytext); strcpy(subs, "s"); BEGIN(NEXT); } S { unsafe = 1; strcpy(orig, yytext); strcpy(subs, (ctrl & 1) ? "s" : "S"); BEGIN(NEXT); } [vV]olts?|VOLTS? { strcpy(orig, yytext); strcpy(subs, "V"); BEGIN(NEXT); } years?|YR|YEARS? { strcpy(orig, yytext); strcpy(subs, "yr"); BEGIN(NEXT); } [A-Za-z]+ { // Not a recognized alias. strcpy(orig, yytext); strcpy(subs, orig); BEGIN(NEXT); } [A-Za-z]+ { // Reject the alias match. strcat(orig, yytext); strcpy(subs, orig); } " "+[^A-Za-z] { // Discard separating whitespace. unput(yytext[yyleng-1]); } " "+[A-Za-z] { // Compress separating whitespace. strcat(unitstr, subs); strcat(unitstr, " "); if (strcmp(orig, subs)) status = 0; unput(yytext[yyleng-1]); *subs = '\0'; BEGIN(INITIAL); } . { // Copy anything else unchanged. strcat(unitstr, subs); if (strcmp(orig, subs)) status = 0; unput(*yytext); *subs = '\0'; BEGIN(INITIAL); } .* { // Copy out remaining input. strcat(unitstr, yytext); } <> { // End-of-string. if (*subs) { strcat(unitstr, subs); if (strcmp(orig, subs)) status = 0; } if (unsafe) { return wcserr_set(WCSERR_SET(UNITSERR_UNSAFE_TRANS), "Unsafe unit translation in '%s'", unitstr); } return status; } %% /*---------------------------------------------------------------------------- * 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; } astropy-astropy-201cddb/cextern/wcslib/C/wtbarr.h000066400000000000000000000104141507226315300221660ustar00rootroot00000000000000/*============================================================================ WCSLIB 8.4 - an implementation of the FITS WCS standard. Copyright (C) 1995-2024, 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/people/Mark.Calabretta $Id: wtbarr.h,v 8.4 2024/10/28 13:56:16 mcalabre Exp $ *============================================================================= * * WCSLIB 8.4 - 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 astropy-astropy-201cddb/cextern/wcslib/CHANGES000066400000000000000000003664071507226315300213450ustar00rootroot00000000000000WCSLIB 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) -------------------------------- * C library - Problems were caused for fitshdr(), wcsbth(), wcspih(), wcsulex(), datfix() and wcshdo(), by locales such as "fr_FR" in which commas are used to delimit the decimal part of floating point numbers. In particular, these locales adversely affect the behaviour of sscanf() and sprintf() when reading and writing FITS header keyvalues. Thread-safe fixes provided by Michael Droettboom. - Applied astropy patches from Michael Droettboom: attend to compiler warnings in prj.c, spc.c, tab.c, and wcsutil.c, including those for MS-Windows. WCSLIB version 4.13.4 (2012/04/02) ---------------------------------- * Installation - Relaxed the closure tolerance slightly in tlog.f as for tlog.c previously. WCSLIB version 4.13.3 (2012/03/26) ---------------------------------- * Installation - Relaxed the closure tolerance slightly in tlog.c for gcc 4.6.3 on Ubuntu 12.04 (reported by Ole Streicher). WCSLIB version 4.13.2 (2012/03/21) ---------------------------------- * Installation - Changes to configure.ac and the GNUmakefiles to put '-lm' strictly in the correct sequence when producing the sharable library (reported by Ole Streicher). WCSLIB version 4.13.1 (2012/03/15) ---------------------------------- * C library - Workaround in wcserr_set() for an unfortunate compiler segv from gcc 4.2.1 in MacOSX 10.7. WCSLIB version 4.13 (2012/03/14) -------------------------------- * C library - Allow naxis == 0 in wcsini() and linini() for a degenerate coordinate system as may be produced by wcssub(). In wcssub(), ensure that wcsini() is called for coordinate systems with naxis == 0. In wcssptr(), call spcfree() before spcini() to plug a memory leak. WCSLIB version 4.12 (2012/02/29) -------------------------------- * C library - In spctrne(), guard against restfrq == restwav == 0.0 when translating between two velocity-characteristic types, or between two wave-characteristic types (the translation is independent of restfrq and restwav, but a dummy value is needed for intermediate calculations). WCSLIB version 4.11 (2012/02/21) -------------------------------- * C library - Bug fix in spcset() for handling simple linear spectral axes. * Fortran wrappers - Bug fix in spctrne_() for handling ctypeS2. WCSLIB version 4.10 (2012/02/06) -------------------------------- * C library - datfix() and spcfix() now return informative messages when dates and AIPS-convention spectral axes are translated (changes contributed by Michael Droettboom). spcaips() now returns an error status for invalid values of VELREF. - wcssub() has been augmented with the ability to add new axes onto a wcsprm struct. WCSLIB version 4.9 (2012/01/24) ------------------------------- * C library - Fixes to wcsfixi() for collecting the messages properly in the info array (from Michael Droettboom). - Handle certain malformed date strings more gracefully in datfix(). - Make informative messages printed by wcserr_prt() a bit more informative. WCSLIB version 4.8.4 (2011/12/05) --------------------------------- * C library - Fixed the pseudo-random number generator in twcstab.c - gcc 4.6 with '-O2' baulked at testing for signed integer overflow. * Installation - In configure.ac, the Fortran compiler's libraries must be added to the link list when checking for the PGPLOT libraries since gcc is driving the linker. Likewise in C/GNUmakefile when linking test programs that use PGPLOT. - Use 'make CHECK=nopgplot check' to run only the non-graphical tests (even if PGPLOT is available). - After compiling and running the tests, 'make check' now summarizes the non-graphical test results and stops if any failed. 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 values. wcserr_prt() will now recognize and print them as such. Added wcserr_clear() to reset (clear) a wcserr struct. - Modified unitfix() to return an informative message if a units alias is applied, and wcsfixi() to allow such messages to be propagated through the info array (from Michael Droettboom). - Modified twcsfix.c to use wcserr diagnostics, in particular to report units alias translations. * Fortran wrappers - In wcsfix_(), interpret *naxis == 0 as meaning naxis == 0x0, thus causing cylfix() to be skipped. - Modified twcsfix.f to reflect changes made to twcsfix.c. WCSLIB version 4.8.2 (2011/10/04) --------------------------------- * Installation - Changes for Debian package generation contributed by Ole Streicher: - Corrections to 'configure' reported by 'lintian'. - Generate man pages for the utility programs and install them. WCSLIB version 4.8.1 (2011/09/19) --------------------------------- * Installation - Set SONAME in the sharable library in accordance with tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html (reported by Ole Streicher, Debian package maintainer). The sharable library will again be installed with full release number and with a symbolic link from SONAME pointing to it. If defined, SHRLN will point to SONAME. WCSLIB version 4.8 (2011/08/15) ------------------------------- * C library - New error diagnostics mechanism contributed by Michael Droettboom: Most functions that return a numeric status (error) code now also write a detailed error message to a wcserr struct attached to the passed-in *prm struct. See wcserr.h for more information. Functions that didn't have a *prm struct as an argument have no direct way to return an error message. Therefore, these functions now have duplicate versions with the suffix "e" that take an additional "struct wcserr *" parameter. These functions are: spcspx() -> spcspxe() spctrn() -> spctrne() spctyp() -> spctype() spcxps() -> spcxpse() wcsulex() -> wcsulexe() wcsunits() -> wcsunitse() wcsutrn() -> wcsutrne() A new function wcsfixi() was added which is identical to wcsfix() but in addition stores all of the detailed textual messages about the fixes that were performed. - In wcssub(), ensure that wcstab->m_tab has been initialized before trying to free it on status return 12 or 13 (reported by Hans Terlow). - Bug fixes: - In sphx2s() and sphs2x() for the case where |eul[1]| = 180.0. - For parsing AIPS-convention VELREF in wcsbth(). - In spcaips() for translating AIPS-convention CTYPEia without Doppler frame. - Non-graphical test programs now simply report "PASS" if they satisfy the reporting tolerance for closure residuals, etc. Their full output is reported otherwise. Run 'make MODE=interactive check' to revert to the previous behaviour of reporting the full output for tests that succeed. - Eliminated compiler warnings about type-punning of pointer-to- function. * Fortran wrappers Extensive modifications to track the new error handling mechanism in the C library. * Installation - configure now prefers gfortran over g77 if available. - Don't rely on "." being in the PATH if config.status needs to be run in the pgsbox and utils makefile (reported by Peter Teuben). WCSLIB version 4.7 (2011/02/07) ------------------------------- * C library - Bug fix in celset() for interpreting LATPOLEa when LONPOLEa = phi0. Crept in at version 4.4. - Fixed the bounds test on y in hpxx2s() (HEALPix projection) for unconventional values of H and K. In hpxx2s() and hpxs2x(), corrected the offset of the southern polar half-facets for even K. In hpxs2x(), put the phi = 180 meridian in the expected place. - Bug fixes in tabx2s() and tabs2x() for default indexes (reported by David Berry). In tabs2x(), if no solution is found then do minor extrapolation past the ends of each row (1-D case only). Sped up tabs2x() by about 50%. - New functions wcsprintf(), wcsprintf_set(), and wcsprintf_buf(), declared in wcsprintf.h, provide control over the disposition of output from celprt(), linprt(), prjprt(), spcprt(), tabprt(), and wcsprt() routines. Prompted by Michael Droettboom, with an initial implementation. * Fortran wrappers - In the various test programs, used EQUIVALENCEs to ensure that the CEL, LIN, PRJ, etc. arrays are aligned on a DOUBLE PRECISION boundary. * PGSBOX - Bug fix for the case where NG1 == 0 and GRID1(0) < 0, and likewise for NG2 and GRID2. * Utilities - In wcsware, added a '-w' option to convert world coordinates obtained from stdin to pixel coordinates using wcss2p(). Allow multiple sets of input coordinates with the '-x' and '-w' options and report the value of the intermediate world coordinates. * User manual - Fixed list formatting for function return values < 0 or > 9. - New section for wcsprintf() and related routines. * Installation - Changes prompted by Sébastien Fabbro for the Gentoo Linux package: a) autoconf updates, b) respect LDFLAGS when building the shared library, c) install documentation, d) recognise DESTDIR for doing a staged installation. - As of this release, the minor WCSLIB version number (second field) will be incremented if and only if a change is made that affects the library itself, not the documentation or utilities. The version number on the installed libraries and header files will omit the patch number (third field). WCSLIB version 4.6.3 (2010/11/24) --------------------------------- * C library - Bug fix in wcsbth() for handling the inheritance of image header keywords (uncovered by valgrind, reported by Jim Lewis). WCSLIB version 4.6.2 (2010/11/22) --------------------------------- * C library - Fixed a memory leak in wcsbth.l (reported by Jim Lewis). WCSLIB version 4.6.1 (2010/11/18) --------------------------------- * Fortran wrappers - Fixed typos in cel_f.c, celget[cdi] -> celgt[cdi]. WCSLIB version 4.6 (2010/11/16) ------------------------------- * C library - In wcsulex.l and wcsutrn.l, stdlib.h must be included explicitly before the redefinition of exit() - most versions of flex do include it upfront but some don't (reported by Peter Williams). * Fortran wrappers - Changes intended to avert nuisance compiler warnings that could potentially obscure warnings indicative of a genuine problem: - To stop messages about unused variables when the relevant compiler option is set, e.g. 'g77 -Wunused', the various *_ERRMSG arrays defined in the Fortran include files and (formerly) initialized therein via DATA statements, e.g. PRJ_ERRMSG in prj.inc, have now been placed into COMMON blocks with names such as PRJ_DATA, and are initialized via DATA statements in BLOCK DATA subprograms defined in separate files, e.g. prj_data.f. - To stop messages about subroutines being invoked with inconsistent argument lists if the relevant compiler option is not set, e.g. 'g77 -Wno-globals', the C wrapper functions that take 'void *' arguments now have separate forms for INTEGER, DOUBLE PRECISION, and CHARACTER arguments that simply invoke the generic function. Application code must be modified to take advantage of this. * User manual - In the section on the Fortran wrappers in the manual, warn about the need for the INTEGER array holding a data structure to be aligned on a DOUBLE PRECISION boundary. WCSLIB version 4.5.6 (2010/10/28) --------------------------------- * Installation - Fixed the search for CFITSIO and PGPLOT library and include directories. WCSLIB version 4.5.5 (2010/10/14) --------------------------------- * Installation - Build the PGSBOX sharable library. WCSLIB version 4.5.4 (2010/09/28) --------------------------------- * C library - In wcshdo(), according to the FITS standard, "Letters in the exponential form ('E' or 'D') shall be upper case" (reported by Michael Droettboom). WCSLIB version 4.5.3 (2010/09/23) --------------------------------- * Utilities - Various improvements to wcsgrid: correct the scaling set via cpgwnad(); label angles other than RA,Dec in decimal degrees; draw the projection boundary for projections other than zenithals. WCSLIB version 4.5.2 (2010/09/23) --------------------------------- * C library - Fixed the translation of GLS to SFL in wcsset() and celfix() when the reference longitude is non-zero - it introduces an offset in longitude in the normal way. (This undoes part of the change applied in version 4.4.) WCSLIB version 4.5.1 (2010/08/12) --------------------------------- * C library - New utility function, sphpad(), computes the coordinates of points offset by given angular distances and position angles from a given point on the sky (complementary to sphdpa()). * Fortran wrappers - New wrapper function: - SPHPAD for sphpad(). WCSLIB version 4.5 (2010/07/16) ------------------------------- * C library - Fixed the interpretation of VELREF when translating AIPS-convention spectral types. Such translation is now handled by a new special- purpose function, spcaips(). The wcsprm struct has been augmented with an entry for velref which is filled by wcspih() and wcsbth(). Previously, selection by VELREF of the radio or optical velocity convention for type VELO was not properly handled. * Fortran wrappers - New wrapper function: - SPCAIPS for spcaips(). - Changed spc.inc, spc_f.c, wcs.inc and wcs_f.c to track VELREF changes. - Declared functions external in the include files to avoid compiler warnings about unused variables (if the particular option is set). * Utilities - Added a '-q' option to fitshdr to quit after a specified number of HDUs. WCSLIB version 4.4.4 (2009/09/14) --------------------------------- * Installation - Added more configure options for controlling the build: --disable-fortran, --disable-utils, --without-cfitsio, and --without-pgplot. WCSLIB version 4.4.3 (2009/09/03) --------------------------------- * C library - Set wave number units to "/m" in spctyp(), was "1/m" which is not strictly legal and wasn't handled by wcsulex() (reported by Hans Terlow). Also fixed a number of units specifications in the prologue of spx.h to conform with Paper I usage. - In wcsulex(), allow unit strings like "1/m" in addition to "/m", provided that the superfluous "1" is the first non-blank character in the expression, or parenthesised sub-expression. - In wcssptr(), ensure that i is always reset if given < 0. * User manual - Augmented the list of FITS WCS and related software in the manual. WCSLIB version 4.4.2 (2009/08/13) --------------------------------- * C library - In sphx2s() and sphs2x(), handle the case where |eul[1]| = 180.0 separately for speed and accuracy. This change also fixes a rare and subtle bug in cels2x() that occurs when celprm::isolat is set and the magnitude of the first latitude in the lat[] vector exceeds 90 deg (reported by Hans Terlouw). * Installation - Fix relating to creation of symlinks when installing the libraries. WCSLIB version 4.4.1 (2009/08/11) --------------------------------- * Installation - Fixes for installation of the CHANGES file and for the creation of a symbolic link for the sharable library if one already exists. WCSLIB version 4.4 (2009/08/10) ------------------------------- * C library - Creation of WCSLIB user manual from the header file prologues using a special-purpose parser, doxextr, and sed scripts to generate input for doxygen. This required minor formatting changes to all prologues plus miscellaneous changes such as naming of arguments in function prototypes. - Bug fix in wcsset() that affected handling of PROJPn (deprecated) and PVi_ma attached to the longitude (not latitude) axis. Guard against long strings when copying the projection code. In wcs_types(), allow for early Paper IV distortion codes (e.g. "RA---TAN-SIP") when parsing CTYPEia. - Use sincos() whereever possible for a ~15% speedup (patches for cel.c, prj.c and sph.c supplied by Michael Droettboom). configure checks for, and uses it automatically if available. - Fixed the translation of GLS to SFL in wcsset() and celfix() when the reference longitude and latitude are non-zero. (In the AIPS convention, this simply translates the reference point, i.e. the map as a whole, to those coordinates without creating an oblique grid.) - Bug fix in prjoff(), a utility function used by the prj routines. It forces (x,y) = (0,0) at (phi_0,theta_0) when the latter are set by PVi_[012]a attached to the longitude (not latitude) axis. Rarely used in practice. - New utility function, sphdpa(), computes the distance and position angle from a point on the sphere to a set of field points. - In sphx2s() and sphx2s(), handle a simple change in origin of longitude using a short-cut calculation for speed and accuracy. Similarly in celset(), check whether phip == phi0 when computing latp and if so use a short-cut that ensures latp == 90.0 (exactly) if lat0 == theta0. The resulting spherical rotation then becomes a simple change in origin of longitude. In particular, these changes should assist PGSBOX in drawing grid lines of +/-180 longitude, to prevent flip-flopping between one and the other. - wcsbth() & wcspih(): resolved an inconsistency between the documentation and code by renamimg WCSHDR_VSOURCEa as WCSHDR_VSOURCE. - Flex code: moved declaration of helper functions out of global scope. - Fixed the call to wcss2p() in twcshdr (in a section of code not usually exercised). * Fortran wrappers - New wrapper functions: - WCSBTH for wcsbth(), - WCSBDX for wcsbdx(), - CDFIX for cdfix(), - SPHDPA for sphdpa(). - Updated WCSLEN (in wcs.inc) and added WCS_COLAX and WCS_VELANGL to match changes to wcsprm made in v4.3 with corresponding changes to the wrapper functions. Likewise updated TABLEN (in tab.inc) for changes to tabprm, and added CEL_LATPREQ for celprm. - Struct lengths (WCSLEN, PRJLEN, etc.) are now long enough to accomodate 64-bit machines. - Updated the flag bits for the RELAX argument in wcshdr.inc to reflect changes to wcshdr.h made in v4.3. Renamed WCSHDR_VSOURCEa to WCSHDR_VSOURCE for consistency with the C library. * PGSBOX - Improved grid labelling, particularly in minimizing the number of fields required in sexagesimal labels. * Utilities - New utility program: - wcsware extracts the WCS keywords for an image from the specified FITS file, constructs wcsprm structs for each coordinate representation found, and performs a variety of operations using them. - Old utility programs (first appeared in 4.3 but were not recorded): - HPXcvt reorganises HEALPix data into a 2-D FITS image with HPX coordinate system. - wcsgrid extracts the WCS keywords for an image from the specified FITS file and uses pgsbox() to plot a 2-D coordinate graticule for each alternate representation found. - fitshdr lists headers from a FITS file specified on the command line, or else on stdin, printing them as 80-character keyrecords without trailing blanks. * Installation - New configure options, --with-pgplotinc, --with-pgplotlib, --with-cfitsioinc and --with-cfitsiolib allow additional directories to be added to the library and include file search path. - Miscellaneous fixes and improvements to the installation process. - Generate a metadata file for pkg-config. - Added 'make MODE=interactive check' to run the test programs in interactive mode rather than batch. - Merged the separate CHANGES files for C, Fortran and PGSBOX into one (this), with a new section for utilities. WCSLIB version 4.3.3 (2009/04/30) --------------------------------- * C library - fitshdr.l, wcsbth.l, and wcspih.l: use setjmp/longjmp to preempt the call to exit() which is hard-coded in function yy_fatal_error() supplied by flex. - wcspih.l: if NAXIS is non-zero but there were no WCS keywords at all in the header then create a default WCS with blank alternate version. WCSLIB version 4.3.2 (2009/03/16) --------------------------------- * C library - utils/GNUmakefile: create BINDIR if necessary prior to installing utilities. WCSLIB version 4.3.1 (2008/09/08) --------------------------------- * Installation - Top-level GNUmakefile: install header files. WCSLIB version 4.3 (2007/12/27) ------------------------------- * C library - A new general WCS header parser wcsbth() handles binary table image arrays and pixel lists as well as image array headers. Added "colax" to the wcsprm struct to record the column numbers for each axis in a pixel list. - New function wcsbdx() is the analog of wcsidx() for the array of wcsprm structs returned by wcsbth(). - New function wcshdo() writes out a wcsprm struct as a FITS header. - Changes to wcspih(): - Bug fix, check for a == 0 (indication of a keyword that applies to all alternates) in internal helper function wcspih_naxes() (reported by Craig Markwardt). - Added a new ctrl option to remove valid WCS keyrecords except for those with a more general role, namely {DATE,MJD}-{OBS,AVG} and OBSGEO-{X,Y,Z} (suggested by Jim Lewis). - Added a rule for VELANGLa. Also added "velangl" to the wcsprm struct. - Do checks on the i, k & m keyword parameters in . - Fixed the test for repeated blanks in the NAXIS and WCSAXES patterns. - Fixed three rules to allow m == 0. - Reworked the implementation notes in the prologue. - The flex scanners, fitshdr.l, wcsbth.l, wcspih.l, wcsulex.l, and wcsutrn.l, invoke yylex_destroy() before returning to avoid a 16kiB memory leak. This was reported by several people, however it may be problematic depending on the version of flex used - version 2.5.9 or later is required. If this is not available, C sources pre- generated by flex 2.5.33 will be used. - In wcs.c, don't define the signbit macro if already defined (for MacOSX). - In wcs.h, documented wtbarr namespace issues in C++. - In wcsset(), always set wcsprm.cunit[i], if possible (primarily for use by wcshdo()). - In wcsfix.c, parenthesised a boolean expression that was otherwise incorrect. - Fixed an obscure floating point rounding error in celset() that appeared with -O2 optimization in gcc v3.3.5 (Linux). - prjset() now correctly propagates the status value returned by the specific projection-setting functions (reported by Bill Pence). - Bug fix in hpxx2s(), also added bounds checking. Minor efficiencies in carx2s() and merx2s(). - In the various functions that print the contents of the structs, use the "%p" printf conversion specifier to print addresses rather than casting the pointer to int and using "#x". The latter does not work on 64-bit machines where sizeof(int) != sizeof(void*). - Reorganized the various structs to get alignment on 64-bit machines. - All header file prologues now reference the README file for an overview of the library. - Miscellaneous portability fixes for 64-bit, MacOSX, OSF compiler, etc. - Elimination of compiler warnings, e.g. parenthesised assignments used as truth values (a favourite gcc gripe!), etc. - Process flex descriptions using a newer version of flex, primarily for MacOSX. However, the processed files are now only used when flex 2.5.9 or later is not available. - Removed WCSLIB 2.x backwards-compatibility measures from lin.h, prj.h, prj.c, and sph.h. * Fortran wrappers - (No substantive changes.) * PGSBOX - Miscellaneous improvements to PGSBOX. * General - Switched licensing to LGPL 3.0. - In comment text, replaced use of the obsolete term "card" with "keyrecord" which consists of a "keyword", "keyvalue", and "keycomment". * Installation - General improvements to the installation process: autoconf-related portability improvements, particularly relating to Fortran name mangling; makefile rules for building the shared library, for processing flex descriptions; don't rely on "." being in the PATH when running tests. WCSLIB version 4.2 (2005/09/23) ------------------------------- * C library - Brought the installation process under control of GNU autoconf, the top-level makefile now builds and tests everything, and the C library has a config.h in which WCS_INT64 is set. Added an INSTALL file. - Merged the FORTRAN, C and PGSBOX READMEs into one top-level README. - Extensions for -TAB coordinate handling: in tabx2s() and tabs2x(), allow extrapolation by half a cell at either end of the index and coordinate tables; fits_read_wcstab() (in getwcstab.{h,c}) allows TDIMn to be omitted for 1-D lookup tables for which it has the form '(1,K)', i.e. describing a degenerate 2-D array; wcsprt() now prints the wtbarr structs in wcsprm. - Bug fixes for -TAB coordinate handling: in tabx2s() and tabs2x() the incorrect indexing variable, m instead of i, was used for tab->crval[]; wcsp2s() and wcss2p() returned prematurely in the tabular coordinate loop; in wcstab(), removed an extraneous assignment to wtbp->kind for index arrays. - In wcsp2s() and wcss2p(), elements of the stat[] vector that had been set were being reset incorrectly to zero. The stat[] values are now set as flag bits for each coordinate element. - Added cdfix() to the wcsfix() suite to fix erroneously omitted CDi_ja cards. - PGSBOX is now compiled into a separate object library, and is installed alongside WCSLIB. - Eliminated several instances of non-ANSI C library functions and header files and some residual K&R C usage. The Sun C compiler complained about const int definitions of initializers used in variable declarations in some of the test programs; changed these to preprocessor macros. * Fortran wrappers - Fixed handling of 64-bit integer keyvalues in keyget_(). - Fixed output formatting of 64-bit integer keyvalues in tfitshdr.f. - Fixed minor syntax errors in twcsfix.f and tpih1.f reported by the Sun Fortran compiler. - The output of each test program now identifies the source file. * PGSBOX - (No substantive changes.) WCSLIB version 4.1 (2005/08/31) ------------------------------- * C library Summary of added functionality: - -TAB coordinate axes are now fully implemented in the WCSLIB driver functions (in wcs.{h,c}); multiple -TAB axes are supported. A new function, wcstab(), which is automatically invoked by wcspih(), parses -TAB-related header cards and sets up structs for a separate routine that reads the necessary arrays from a FITS binary table extension. An implementation of this routine in CFITSIO, fits_read_wcstab(), is provided. Note however that the interface of this function is experimental, and the code itself must be considered beta-release in WCSLIB 4.1. - Units specifications, either from CNAMEia or inline comments (with brackets), of arbitrary complexity are now fully implemented via a parser, wcsulex(), and converter, wcsunits(). This is invoked automatically by wcsset(). - Translators for non-standard WCS constructs are provided. These cover date formats, units specifications, defunct celestial projection types, AIPS spectral axis types, and the repair of malformed cylindrical coordinate systems. - wcspih() now has options to remove the WCS cards it has processed from the header and a new generic FITS header parser, fitshdr(), may be used to parse the remaining non-WCS cards. In addition to the more basic types, it handles 64-bit and 'very long' (70 digit) integer keyvalues, and also continued string keyvalues. It also does keyword matching and extracts units specifications in inline comments. - -LOG coordinates are now implemented independently of spectral coordinate types. Multiple -LOG axes are supported. - New function wcssptr() translates the spectral axis in a wcsprm struct to the required type. - The README file now gives an introduction to, and complete overview of, WCSLIB. It provides a point of entry to programming with WCSLIB. Complete descriptions and usage notes for all functions are contained in the header files. - The FORTRAN wrappers and test programs are now completely up-to-date with respect to the C implementation. - All code, including the FORTRAN wrappers, PGSBOX, and all test programs, now pass 'purify' without memory leaks, uninitialized memory reads, memory access violations, or other memory faults. Change notes: - Added options to wcspih() to remove WCS cards from the input header leaving only non-WCS cards behind. Modified test programs tpih1.c and tpih2.c to use CFITSIO optionally via preprocessor macro DO_CFITSIO. - New function wcstab() in wcshdr.{h,c} parses -TAB-related header cards and sets up structs for a separate routine that reads the necessary arrays from a FITS binary table extension. New test/demo program twcstab.c using header defined in wcstab.cards. - CFITSIO implementation, fits_read_wcstab() in getwcstab.{h,c}, of a function, independent of WCSLIB, for extracting arrays from a binary table as required in constructing -TAB coordinates. - New units specification parser, wcsulex() in wcsunits.h and wcsulex.l, and converter, wcsunits() in wcsunits.{h,c}. New test/demo program tunits.c. - New parser for non-standard units specifications, wcsutrn() in wcsunits.h and wcsutrn.l, also tested by tunits.c. - New functions datfix(), unitfix() (which applies wcsutrn()), celfix(), and spcfix() join cylfix() in wcsfix.{h,c} to translate various forms of non-standard or quasi-standard FITS WCS keyvalues in a wcsprm struct. wcsfix() applies all of these in sequence. New test/demo program twcsfix.c, with wcsfix() also now invoked by tpih1.c. - New generic FITS header parser, fitshdr() in fitshdr.{h,l}. New test/demo program tfitshdr.c uses wcs.cards with extra non-WCS cards added. - -LOG coordinates are now treated as a coordinate type separate from spectral coordinates, implemented via log.{h,c} and test program tlog.c. The logarithmic functions were removed from spx.{h,c}, and spc.c. - Extensive changes to wcs.{h,c} to support multiple -TAB and -LOG coordinate axes and units conversion. Substantially changed the test program, twcs.c, to test the more general functionality. - New function wcssptr() in wcs.{h,c} translates the spectral axis in a wcsprm struct. - Added DATE-AVG to wcsprm. Also ntab, tab, nwtb, and wtb required for -TAB implementation. Define struct wtbarr. - Added a types[] member to the wcsprm struct to identify axis coordinate types using a four-digit code. - Use memset() in wcsini() to null-fill character arrays in the wcsprm struct so that they don't appear to be padded with garbage when displayed by gdb. - Do alias translation for AIPS-convention spectral types in wcsset() using spctyp(). If wcsset() finds a CTYPEia in "4-3" form with an unrecognized algorithm code it now returns an error rather than assume that it's a linear axis. wcsset() now also resets lonpole and latpole to the values actually used. - Modified spctyp() to translate AIPS-convention spectral ctypes, and modified the argument list to return the parsed spectral type and algorithm code. The return arguments will not be modified if CTYPEia is not a valid spectral type; zero-pointers may be specified for any that are not of interest. Removed the external const variables, spc_codes and spc_ncode, as their function is now fulfilled by spctyp(). - Fixed a bug in spctrn() in resolving ctypeS2 wildcarding. - Added latpreq member to the celprm struct, set by celset() to indicate how LATPOLE is used. Augmented tcel2.c to report it. - New function tabmem() in tab.{h,c} takes control of user-allocated memory. - tabini() allows K == 0 and also K[m] == 0 to initialize partially the tabprm struct (for wcstab()). It now does fine-grained bookkeeping of memory allocation for the index arrays and allocates each individually. tabprm.index[] == 0x0 is recognized as default indexing in tabset(), tabx2s() and tabs2x(). - The *prt() functions report parameters to an extra decimal place. - tabprt() prints the array index for elements of the coordinate and index vectors. - Set the 0th element in all *_errmsg arrays to "Success". - Extracted string utility functions used by WCSLIB into wcsutil.{h,c}. - Removed support for K&R C. * Fortran wrappers - The FORTRAN wrappers and test programs are now completely up-to-date with respect to the C implementation. - New include files, wrappers, and test programs: fitshdr.inc, fitshdr_f.c, getwcstab.inc, getwcstab_f.c, log.inc, log_f.c, sph.inc, tab.inc, tab_f.c, tfitshdr.f, tlog.f, ttab1.f, ttab2.f, ttab3.f, tunits.f, twcsfix.f, twcstab.f, wcsfix.inc, wcsfix_f.c, wcsunits.inc, wcsunits_f.c. - Updates to reflect changes to the C library and test programs: cel.inc, cel_f.c, prj.inc, spc.inc, spc_f.c, spx.inc, spx_f.c, tlin.f, tpih1.f, tpih2.f, tprj1.f, tprj2.f, tspc.f, tsph.f, tspx.f, twcs.f, twcsmix.f, twcssub.f, wcs.inc, wcs_f.c, wcshdr.inc, wcshdr_f.c. - Added *_ERRMSG arrays containing status messages to all include files. - Removed support for K&R C. * PGSBOX - Fixed a subtle though benign memory fault identified by 'purify'. - Reset LATPOLE in the COE example in cpgtest.f when drawing the second (native) grid because it will have been set to a non-default value by wcsset() when the first grid was drawn; set wcs.flag to -1 before wcsinit() and call wcsfree() at the end. Similarly for pgtest.f. WCSLIB version 4.0 (2005/02/07) ------------------------------- * C library - Implemented tabular coordinates (-TAB). New files: tab.h and tab.c, and test programs ttab[123].c. These have not been incorporated into the higher-level (wcs.h) functions at this stage. - New spectral functions: spchek() checks a spectral algorithm code for legitimacy; from the spectral keywords given, spcspx() derives the corresponding CRVALi and CDELTi keywords for the underlying P-, and X-type spectral coordinates; spcxps() does the opposite; spctrn() combines spcspx() and spcxps() to translate one set of spectral keywords into another, e.g. 'FREQ' -> 'ZOPT-F2W'. - Implemented the HEALPix (HPX) projection in the prj functions. - Added a new function, wcsidx(), to return an array that indexes the alternate coordinate descriptions found by wcspih() (suggested by Bill Pence, NASA/Goddard). Modified tpih1.c to exercise it. - In wcsp2s() and wcss2p(), check that nelem equals or exceeds wcs.naxis; emphasised this in the usage notes for these functions in tab.h (suggested by Bill Pence, NASA/Goddard). - Moved the macros used for UNDEFINED values and the corresponding macro test function, undefined(), to wcsmath.h for general use. Previously, UNDEFINED values were only used internally, but they are now visible in some of the structs, particularly values of undefined auxiliary header cards in the wcsprm struct. - Remove const from the double args in the specx() prototype in spx.h to match the definition in spx.c (reported by Bryan Irby, NASA/Goddard). - Fixed the interaction between the FLAVOUR and PGPLOTLIB definitions in the C and FORTRAN Makefiles by introducing a separate variable, DO_PLOTS, to control whether to exercise test programs that require PGPLOT (reported by Bill Pence, NASA/Goddard). * Fortran wrappers - New wrapper defined in wcshdr_f.c: wcsidx_(). Modified test program tpih1.f to use it. * PGSBOX - (No substantive changes.) * General - Changed the copyright notice in all library routines from LGPL to GPL as recommended by the FSF (http://www.gnu.org/licenses/why-not- lgpl.html). * Installation - General improvements to the installation process: fixed the interaction between the FLAVOUR and PGPLOTLIB definitions in the Makefile by introducing a separate variable, DO_PLOTS, to control whether to exercise test programs that require PGPLOT (reported by Bill Pence, NASA/Goddard). Added an "install" target to the Makefile. WCSLIB version 3.6 (2004/08/25) ------------------------------- * C library - New service routine, wcssub() extracts the coordinate description for a subimage from a wcsprm struct. wcscopy() is now implemented as a preprocessor macro that invokes wcssub(). New test program, twcssub.c, tests wcssub(). - In wcspih(): 1) Fixed handling of string-valued keywords where the inline comment contains a single-quote character ('). 2) Fixed the address arithmetic for EPOCH and VELREF. 3) Translate VSOURCEa into ZSOURCEa if required. 4) Recognize SSYSSRCa. 5) Support free-format string keyvalues, as well as integer and floating-point keyvalues; documented this in the prologue of wcshdr.h. 6) Allow header cards without inline comments. 7) Fixed function prototyping in wcspih.l (i.e. ANSI and non-ANSI forms were potentially mixed). 8) Catch an unhandled newline character on the END card that was echoed to stdout. 9) In error messages, print "ERROR" (uppercase) - POSIX standard. - Modified wcs.cards to explain and test free-format keyvalues, and also augmented the inline comment on illegal WCS cards that are to be rejected, and WCS-like cards to be discarded. Added a header card with no inline comment. - Removed vsource from the wcsprm struct and added ssyssrc. - In wcsini(), fixed a problem with memory management for wcs.pv when NPVMAX is zero; likewise for wcs.ps and NPSMAX. - In wcsprt(), don't print auxiliary coordinate system information in arrays with zero address. - In wcss2p(), status == 9 (one or more invalid world coordinates) was not returned appropriately. - Renamed twcs1.c to twcs.c, and twcs2.c to twcsmix.c. - "Error status/code/number" is now referred to consistently as the "status return value". - Some vestiges of K&R C were removed: preprocessor definition of const, and K&R function prototypes. * Fortran wrappers - New wrapper defined in wcs_f.c: wcssub_(). New test program, twcssub.f. - Renamed twcs1.f to twcs.f, and twcs2.f to twcsmix.f. * PGSBOX - (No substantive changes.) * Installation - Worked over the C, FORTRAN, and PGSBOX makefiles, in particular to make them all consistent. WCSLIB version 3.5 (2004/06/28) ------------------------------- * C library - WCSLIB now provides a function, wcspih() implemented as a Flex description, that parses a FITS image header, either that of a primary HDU or an image extension. Given a character array containing the header it identifies and reads all WCS cards for the primary coordinate description and up to 26 alternate descriptions and returns this information as an array of wcsprm structs. A service routine, wcsvfree(), is provided to free the memory allocated by wcspih(). The relevant header file for these functions is wcshdr.h. Test programs, tpih1 and tpih2, are provided to verify wcspih. The first simply prints the contents of the structs using wcsprt(). The second uses cpgsbox() to draw coordinate graticules. A FITS WCS test header has been developed to provide input to these test programs. It is implemented as a list of card images, wcs.cards, one card per line, together with a program, tofits, that compiles these into a valid FITS file. tpih1 uses its own code to read this, whereas tpih2 uses the fits_hdr2str() function from CFITSIO. - Removed twcsprt, tpih exercises wcsprt() much more thoroughly than twcsprt ever did. Modified twcs1 to print the size of the various structs as twcsprt used to. - Although they are not used in any coordinate calculations, the wcsprm struct now provides data members for storing all of the auxiliary FITS WCS header cards defined in Papers I, II, and III, such as WCSNAMEa, EQUINOXa, and CNAMEia. Members are also provided for storing the alternate descriptor code (the "a" in CTYPEia), and the binary table column number. These are supported by the high level WCSLIB routines, wcsini(), wcscopy(), wcsfree(), and wcsprt(). Refer to wcs.h for details. - The number of PVi_ma cards for which wcsini() allocates memory is now set by a global variable, NPVMAX (previously a C-preprocessor macro). This defaults to 64 but may be changed by a new function, wcsnpv(). The wcsprm struct contains a new member, npvmax, that records the value of this number at the time the struct was initialized. This is in addition to npv which records the actual number of cards that were encountered. Similarly, NPSMAX (default 8) is used for the number of PSi_ma cards, and it may be changed via wcsnps(). The axis number, i, in the pvcard struct used for storing PVi_ma cards may now be set to 0 to indicate the latitude axis. - calloc() is now used in place of malloc() in allocating memory for arrays, and inclusion of malloc.h has been replaced with stdlib.h for all platforms. wcsfree() checks that wcs.flag != -1 before testing wcs.m_flag when freeing memory allocated by wcsini() in case the struct is uninitialized. Similarly for linfree(). - In prj.h, renamed C-preprocessor macros INI, PRT, SET, X2S and S2X to PRJINI, PRJPRT, PRJSET, PRJX2S and PRJS2X to reduce the likelihood of namespace clashes. Similarly in spc.h. Also, in prj.c, changed the name of helper routine offset() to prjoff() to reduce the likelihood of global namespace conflicts. - In line with bonx2s() and bons2x(), bonset() now recognizes the equatorial case of Bonne's projection as Sanson-Flamsteed, mainly so that the auxiliary information in the prjprm struct more accurately reflects the truth. Modified tcel2 to exercise this by using an equatorial Bonne projection in place of the Hammer-Aitov. - zpns2x() used prj.w[0] for bounds checking, though this had not been set by zpnset() for polynomials of degree N < 3. Consequently, bounds checking for N < 3 was unreliable (reported by David Berry, STARLINK). - Changed some variable names in tscs2x(), cscx2s(), cscs2x(), qscx2s(), and qscs2x() to match Paper II, and likewise changed some inequality tests in qscs2x() without changing the results. - Minor tidying up of output formatting in prjprt(). - Added the alternate version code to FITS WCS keywords mentioned in comments, e.g. CTYPEi changed to CTYPEia. * Fortran wrappers - New wrappers defined in wcshdr_f.c: wcspih_() and wcsvfree_(), and also a new service function, wcsvcopy_(). New test programs, TPIH1 and TPIH2, being analogues of tpih1 and tpih2. Removed TWCSPRT. - In wcs_f.c, new wrappers wcsnpv_() and wcsnps_(); modified wcsput_() and wcsget_() to handle new members of the wcsprm struct. Also modified wcsput_() to null-fill all char[] members of the wcsprm struct, and likewise wcsget_() to blank-fill them. - Modified wcs.inc to support changes to the wcsprm struct. * PGSBOX - In PGSBOX, increased the dimension of the WORLD and XY vectors from 2 to 9 to accomodate higher-dimensional coordinate representations of up to 9 elements. Similarly for pgwcsl(). The assumption (presently) is that the first two world, and pixel, coordinate elements are the relevant ones; the others are all set to zero when pgwcsl() initializes and otherwise ignored. Assigned some variables in DATA to stop compiler messages about uninitialized variables. - Generalized the Makefile, bringing it into line with the WCSLIB Makefile, and adding separate targets for compiling and running the test programs. The default target now simply compiles pgsbox.c and cpgsbox.c. A separate target compiles pgwcsl.c and inserts it into ../C/libwcs.a. WCSLIB version 3.4 (2004/02/11) ------------------------------- * C library - In aitx2s(), apply the boundary condition 0.5 <= Z^2 <= 1 given after Eq. (109) in WCS Paper II to detect outlying pixels. - Fixed several special-case bugs in celset(): 1) For theta_0 = 90, in substituting the default value for phi_p (LONPOLE), a) for the special case when delta_0 = 90, celset() provided the wrong value (180 instead of 0), b) celset() neglected to add phi_0 (normally 0). 2) For theta_0 != 90, a) for the special case when delta_0 = -90, celset() incorrectly computed delta_p (as theta_0 instead of -theta_0), b) for the special case when delta_p = +90 (or -90), celset() neglected to subtract (or add) phi_0 (normally 0). 3) For |delta_0| = 90, celset() incorrectly allowed the particular, invalid, value of phi_p (LONPOLE) that put the other pole at the fiducial point. 4) For theta_0 = 0, delta_0 = 0 LATPOLE determines delta_p completely. For LATPOLE > 90 celset() now sets delta_p to 90, and for LATPOLE < -90 it sets it to -90. - Additional refinements in celset(): 1) cel->ref[2] is normalized in the range [-180,180]. 2) Account for rounding error in the computation of delta_p. - sphx2s() and sphs2x() incorrectly handled the "change in the origin of longitude" special case that arises when delta_p = -90, in the even more restrictive case where |theta| = 90 also; it applied Eq. (3) instead of Eq. (4) of Paper II. - Added a new test program, tcel2.c, to exercise celset() more thoroughly. Renamed the original tcel.c to tcel1.c and modified the Makefile to suit. * Fortran wrappers - (No changes.) * PGSBOX - (No substantive changes.) WCSLIB version 3.3 (2003/10/21) ------------------------------- * C library - In celset(), the default value for phi_p (LONPOLE) is phi_p = phi_0 + ((delta_0 < theta_0) ? 180.0 : 0.0) Previously phi_0 (which is normally zero) was not added (reported by David Berry, STARLINK). - wcsprt() and linprt() now check that the structs have been initialized. - In wcsini(), when the wcsprm flag is -1 also set the linprm flag to -1 to force initialization of the memory managed by linset(). - wcsset() now explicitly initializes the celprm and spcprm structs via celini() and spcini(). - Fixed syntax errors in the macro definitions of linrev_errmsg and linfwd_errmsg. - In Makefile, added the -ansi option to gcc to force it to behave like a strict ANSI C compiler, specifically in setting the __STDC__ preprocessor macro. * Fortran wrappers - (No changes.) * PGSBOX - PGSBOX now recognizes status returns -1, -2, and -3 from NLFUNC for opcodes +2 and +1 which cause it to accept the returned (x,y) coordinates but not consider them as one end of a crossing segment for labelling world coordinate 1, 2, or both. - PGSBOX now takes care not to lose vertical tick marks (and hence labels) at the left or right edge of the frame. Likewise for horizontal tick marks at the top or bottom edge of the frame. - Tightened up the test in PGSBOX for cycles in angle to catch the case where the coordinate value spans a full 360 degrees. - PGSBOX will no longer accept frame crossings that are too oblique; floating point rounding errors may cause grid lines that should otherwise track along the frame to weave down it from side-to-side resulting in spurious crossing points. - Fixed a bug in pgwcsl_() for processing simple linear coordinates. - pgwcsl_() now returns error -2 if the latitude is outside -90 to +90 for opcodes +2 and +1. - Amended the translation of status return codes from WCSLIB in pgwcsl_(). - Provided a header file for pgwcsl_() (mainly for C++ usage). - Added extra test plots to PGTEST and cpgtest. - Added extra functionality to the Makefile. WCSLIB version 3.2 (2003/09/09) ------------------------------- * C library - Added the facility of setting the flag member of a wcsprm struct to -1 before calling wcsini() for the first time in order to initialize memory management. Likewise for linprm and linini(). - Renamed wcscpy() to wcscopy() to avoid a conflict with the Posix "wide character string" function of the same name (wchar.h). In particular, this is used by the GNU C++ string class. - The higher level functions (wcs, cel, spc) no longer return prematurely if some of the input coordinate values are invalid. - All functions now test whether a null pointer for the particular struct (wcsprm, celprm, etc.) has been passed to them. - Function return codes have been rationalized into a consistent set within each of the wcs, cel, lin, prj, spc, and spx suites of functions. Error messages to match these error codes are now encoded in a single character array, e.g. wcs_errmsg and prj_errmsg, instead of a separate array for each function. Macro definitions for the older character arrays (e.g. wcsini_errmsg) have been provided for backward compatibility. - Declared prj_stat as extern in prj.h. * Fortran wrappers - (No changes.) * PGSBOX - Added an ENTRY point, PGLBOX, that provides a simplified interface to PGSBOX for linear axes without having to specify an NLFUNC or the associated parameters. WCSLIB version 3.1 (2003/04/29) ------------------------------- * C library - Added "global" and "divergent" prjprm struct informational members to record whether the projection is capable of mapping the whole sphere, and whether it is divergent in latitude. - Function cylfix() provided to fix WCS FITS header cards for malformed cylindrical projections (c.f. Paper II, Sect. 7.3.4). - Added support for CUNITi cards to wcsprm (but not currently implemented). - Added macro implementations of the trigd functions to wcstrig.h, enabled if WCSTRIG_MACRO is defined. - Improved printing of the WCSLIB structs. - Added macro definitions for the lengths of the WCSLIB structs measured in sizeof(int) units (mainly for the FORTRAN wrappers). * Fortran wrappers - FORTRAN is now supported via a set of wrappers on the C library. Refer to the README file. * PGSBOX WCSLIB version 3.0 beta release (2003/04/01) -------------------------------------------- * C library - Fully vectorized function interfaces (C preprocessor macros are available to implement the scalar interfaces of the proj.c, sph.c, and lin.c routines from WCSLIB 2.x). - Implementation of Paper II, Sect. 2.5: User-specified (phi0, theta0). - Implementation of Paper III (excluding "-TAB"). - Memory management is now implemented in the upper-level (wcs.c) routines. - New extensible design should accomodate Paper IV (and any other) without further change to the function interfaces. * PGSBOX - Added a C wrapper function, cpgsbox(), and C test/demo program, cpgtest, that duplicates PGTEST and serves as a C coding template. - Added calendar date axes. - Sped up the max/min search - if only tickmarks are required there is no need to search the interior of the image. - Return margin widths in CACHE(,NC). - Fixed a buglet that caused ticks at the frame edges to be skipped. - Return error 3 if CACHE overflows. - Adapted PGWCSL for WCSLIB 3.x - it is now a C function (for interfacing to WCSLIB) with a FORTRAN-like interface (for PGSBOX). WCSLIB version 2.9 (2002/04/03) ------------------------------- * C library - Fixed a bug with alias translation in wcsset(). - Added a conditional compilation directive to lin.c for Apple's MacOSX. * Fortran library - Fixed CUBEFACE handling in WCSSET. WCSLIB version 2.8 (2001/11/16) ------------------------------- * C library - Added support for the SZP projection with szpset(), szpfwd() and szprev(), and generalized AZP with support for the tilt parameter, gamma. - Added phi0 to the prjprm struct, this is set by the projection initialization routines along with theta0. - Fixed a problem in wcsmix() caused by numerical imprecision that could cause it not to recognize when convergence was reached; break out of the loop early if a discontinuity is detected. - Clarified the usage of vspan in the prologue to wcsmix(). - Fixed comments relating to LATPOLE in the prologue to cel.c and tcel.c, and replaced references to LONGPOLE with LONPOLE. - Augmented the error reports in twcs2. - Modified projex() in tproj1 and prjplt() in tproj2 to make use of the information stored in the prjprm struct. * Fortran library - Added support for the SZP projection with SZPSET, SZPFWD and SZPREV, and generalized AZP with support for the tilt parameter, gamma. - Changed the call sequence to PRJSET to return PHI0 along with THETA0. - Fixed a problem in WCSMIX caused by numerical imprecision that could cause it not to recognize when convergence was reached; break out of the loop early if a discontinuity is detected. - Clarified the usage of VSPAN in the prologue to WCSMIX. - Fixed comments relating to LATPOLE in the prologue to CEL and TCEL, and replaced references to LONGPOLE with LONPOLE. - Augmented the error reports in TWCS2. - Modified PROJEX in TPROJ1 and PRJPLT in TPROJ2 to use the generic driver routines PRJSET, PRJFWD and PRJREV. PRJPLT also now uses the projection type encoded in PRJ(11). WCSLIB version 2.7 (2001/02/19) ------------------------------- * C library - Added generic driver routines prjset(), prjfwd() and prjrev(). These invoke specific projection routines via the pointer-to- function elements, prjfwd and prjrev, transferred to the prjprm struct from celprm. - Added code (3-letter projection code) and theta0 (reference latitude) elements to prjprm. - The projection code for the Sanson-Flamsteed projection is now SFL. The upper-level routines, wcsset(), wcsfwd(), and wcsrev(), recognize GLS as an alias for this. - wcsset() now recognizes 'xyLN/xyLT' axis pairs. - Two bugs in the translation from NCP to SIN in wcsfwd() and wcsrev() were fixed: (1) the projection parameter was computed incorrectly and (2) they did not honour prj->flag set to -1 to disable strict bounds checking. - A bug in wcsmix() was fixed - it was relying on the wcsprm struct to have been initialized beforehand. - The test programs now use the cpgplot interface to PGPLOT, the old tpgc.c and tpgf.f wrappers have been removed. * Fortran library - Added generic driver routines PRJSET, PRJFWD and PRJREV. These are keyed to specific projection routines via the value of PRJ(11) which now differs for each projection. - The projection code for the Sanson-Flamsteed projection is now SFL. The upper-level routines, WCSSET, WCSFWD, and WCSREV, recognize GLS as an alias for this. - WCSSET now recognizes 'xyLN/xyLT' axis pairs. - A bug in the translation from NCP to SIN in WCSFWD and WCSREV was fixed; they did not honour PRJ(11) set to -1 to disable strict bounds checking. - A bug in WCSMIX was fixed - it was relying on the WCS array to have been initialized beforehand. WCSLIB version 2.6 (2000/05/10) ------------------------------- * C library - Check for invalid (x,y) in zearev(). - In wcsmath.h, guard against prior definition of PI and other preprocessor variables. * Fortran library - Check for invalid (X,Y) in ZEAREV. - Declare COSD and SIND in WCSFWD and WCSREV, reported by Clive Page (cgp@star.le.ac.uk). WCSLIB version 2.5 (1999/12/14) ------------------------------- * C library - Added copyright notice to header files and prefixed include guard names with "WCSLIB_". - Fixed cube face handling in wcsfwd() and wcsrev() (reported by Doug Mink, CfA). Allow more general face layout in the inverse quadcube projections. - Fixed the problem in wcsmix() where it failed to find a valid solution when the solution point lay at a native pole of a projection in which the pole is represented as a finite interval. However, wcsmix() will only ever return one solution even when two or more valid solutions may exist. - wcsmix() now accepts viter in the range 5 - 10, the specified value will be pushed up or down into this range if necessary. - The projection routines for AZP, TAN, SIN, ZPN, and COP now return error 2 if (phi,theta) correspond to the overlapped (far) side of the projection. This strict bounds checking can be switched off by setting prj->flag to -1 (rather than 0) when the projections are initialized. - The upper level routines, wcsset(), wcsfwd(), wcsrev(), and wcsmix(), now recognize the NCP projection and convert it to the equivalent SIN projection. The lower level routines do not recognize NCP. - Extracted definitions of mathematical constants (PI etc.) from proj.h into wcsmath.h in order to avoid conflicts with their definition in math.h in some systems (such as Linux). - Describe the two alternate representations of the quadcube projections (i.e. faces laid out or stacked) in the prologue of wcs.c. * Fortran library - Fixed cube face handling in WCSFWD and WCSREV, reported by Doug Mink (dmink@cfa.harvard.edu). Allow more general face layout in the inverse quadcube projections. - Fixed the problem in WCSMIX where it failed to find a valid solution when the solution point lay at a native pole of a projection in which the pole is represented as a finite interval. However, WCSMIX will only ever return one solution even when two or more valid solutions may exist. - WCSMIX now accepts VITER in the range 5 - 10, the specified value will be pushed up or down into this range if necessary. - The projection routines for AZP, TAN, SIN, ZPN, and COP now return error 2 if (phi,theta) correspond to the overlapped (far) side of the projection. This strict bounds checking can be switched off by setting PRJ(11) to -1 (rather than 0) when the projections are initialized. - The upper level routines, WCSSET, WCSFWD, WCSREV, and WCSMIX, now recognize the NCP projection and convert it to the equivalent SIN projection. The lower level routines do not recognize NCP. - Describe the two alternate representations of the quadcube projections (i.e. faces laid out or stacked) in the prologue of wcs.f. WCSLIB version 2.4 (1996/09/23) ------------------------------- * C library - In sinrev(), cscrev(), qscrev(), and tscrev(), return error 2 if (x,y) do not lie within the perimeter of the projection. In sinrev(), stop the computation of phi for the "synthesis" projection being applied to the pure "orthographic" case (reported by David Berry, STARLINK). - (Internal change) Renamed variables l <-> m in the quadcube projections to accord with standard usage (and revised WCS draft paper). * Fortran library - In SINREV, CSCREV, QSCREV, and TSCREV, return error 2 if (X,Y) do not lie within the perimeter of the projection. In SINREV, stop the computation of PHI for the "synthesis" projection being applied to the pure "orthographic" case. Reported by David Berry (dsb@ast.man.ac.uk). - (Internal change) Renamed variables L <-> M in the quadcube projections to accord with standard usage (and revised WCS draft paper). - (Internal change) Stopped PRJ(11) doing double service in any projection. It is now set and tested for a specific magic value rather than simply being non-zero. WCSLIB version 2.3 (1996/06/24) ------------------------------- * C library - Fixed two bugs in zpnset(). The first led to an incorrect determination of the degree of the polynomial and would mainly have affected the efficiency of zpnrev(). The second affected the determination of the boundary of the projection but would only have been significant for projections with a point of inflection between 9 and 10 degrees of the pole. Reported by David Berry, STARLINK. - Replaced usage of alloca() in lin.c with malloc() and free() for portability as suggested by Klaus Banse, ESO (kbanse@eso.org). - Allow for C implementations that provide their own versions of cosd(), sind(), tand(), acosd(), asind(), atand(), and atan2d(). From Klaus Banse, ESO (kbanse@eso.org). - Implemented the CUBEFACE axis for quadcube projections. - Made all function prototypes const-correct. - Adapted the header files to C++ usage. - Added a new test program, twcs1, to verify closure of wcsfwd() and wcsrev(). The old twcs test program is now called twcs2. - Added external arrays of error messages indexed by function return value. For example, extern const char *wcsmix_errmsg[] for wcsmix(). Messages for the many proj.c functions are in prjfwd_errmsg[], etc. * Fortran library - Implemented the CUBEFACE axis for quadcube projections. - Added a new test program, TWCS1, to verify closure of WCSFWD and WCSREV. The old TWCS test program is now called TWCS2. WCSLIB version 2.2 (1996/01/18) ------------------------------- * C library - Amended the projection equations for the conics (COP, COD, COE, COO) and Bonne's projection (BON) to correctly handle southern hemisphere projections with PROJP1 < 0 (reported by Lindsay Davis, NOAO). Revised tproj1 and tproj2 to test such cases. * Fortran library - Amended the projection equations for the conics (COP, COD, COE, COO) and Bonne's projection (BON) to correctly handle southern hemisphere projections with PROJP1 < 0 (reported by Lindsay Davis, NOAO). Revised TPROJ1 and TPROJ2 to test such cases. - Increased the dimension of the WCS array from WCS(0:2) to WCS(0:3) to allow for future handling of the CUBEFACE keyword - WCS(3) will store an index to the CUBEFACE axis. This affects the call sequences of WCSSET, WCSFWD, WCSREV, and WCSMIX. WCSLIB version 2.1 (1995/11/17) ------------------------------- * C library The main change of interest to programmers is that of changed argument lists for wcsfwd() and wcsrev() as described below. - The WCS linear transformations are now implemented in WCSLIB, complete with matrix inverter. The new files are lin.c, lin.h, and test program tlin.c. - Given either the celestial longitude or latitude plus an element of the pixel coordinate a new routine, wcsmix(), solves for the remaining elements by iterating on the unknown celestial coordinate element using wcsfwd(). - The high level driver routines wcsfwd(), wcsrev(), and wcsmix() now apply the full WCS algorithm chain (except for pixel regularization table), including parsing the CTYPEn header cards and computing non- celestial elements of the world coordinate. This required a change to their argument lists which now more closely reflect the sequence of algorithms applied. A new routine, wcsset(), parses the CTYPEn. - The high level driver routines of WCSLIB 1.0 are available as intermediate level drivers celset(), celfwd(), and celrev(), but note that their argument lists have been extended to return native coordinates. The related struct is now called celprm instead of wcsprm. - The reference point for conic projections is now at the midpoint of the standard parallels. The FITS header cards PROJP1 and PROJP2 now give the half-sum (midpoint) and half-difference of the latitudes of the standard parallels; previously they gave the latitudes of the standard parallels themselves. The change is reflected in this release of WCSLIB. - A bug in celset() (formerly wcsset()) that misapplied WCS draft equations 7 has been fixed (thanks to Rick Ebert IPAC/JPL and Lindsey Davis, NOAO for reporting this). This affected the computation of Euler angles for the celestial coordinate transformation for those projections that have their reference point away from the native pole. In investigating this a deficiency with the formalism was discovered that led to the introduction of a LATPOLE FITS header card which may be used to disambiguate where CRVAL1, CRVAL2, and LONGPOLE do not uniquely determine the latitude of the native pole. The celprm struct (formerly wcsprm) has been extended to accomodate LATPOLE. - Default values of LONGPOLE and LATPOLE are now supported and their use is recommended where appropriate. - Numerical precision was being lost near the native poles in the SIN, AIR, and QSC projections and this has been recovered (reported by Lindsey Davis, NOAO). Floating underflows in CSC are now avoided. - Numerical precision was also lost in certain circumstances in the spherical coordinate transformation routines and this has been fixed. - The test programs have been enhanced in various ways and the library has been validated on an SGI machine using both 32-bit and 64-bit compilers. * Fortran library The main change of interest to programmers is that of changed call sequences for WCSFWD and WCSREV as described below. - The WCS linear transformations are now implemented in WCSLIB, complete with matrix inverter. The new files are lin.f and test program tlin.f. - Given either the celestial longitude or latitude plus an element of the pixel coordinate a new routine, WCSMIX, solves for the remaining elements by iterating on the unknown celestial coordinate element using WCSFWD. - The high level driver routines WCSFWD, WCSREV, and WCSMIX now apply the full WCS algorithm chain (except for pixel regularization table), including parsing the CTYPEn header cards and computing non- celestial elements of the world coordinate. This required a change to their call sequences which now more closely reflect the sequence of algorithms applied. A new routine, WCSSET, parses the CTYPEn. - The high level driver routines of WCSLIB 1.0 are available as intermediate level drivers CELSET, CELFWD, and CELREV, but note that their call sequences have been extended to return native coordinates. The related parameter array is now called CEL instead of WCS. - The reference point for conic projections is now at the midpoint of the standard parallels. The FITS header cards PROJP1 and PROJP2 now give the half-sum (midpoint) and half-difference of the latitudes of the standard parallels; previously they gave the latitudes of the standard parallels themselves. The change is reflected in this release of WCSLIB. - A bug in CELSET (formerly WCSSET) that misapplied WCS draft equations 7 has been fixed (thanks to Rick Ebert IPAC/JPL and Lindsey Davis, NOAO for reporting this). This affected the computation of Euler angles for the celestial coordinate transformation for those projections that have their reference point away from the native pole. In investigating this a deficiency with the formalism was discovered that led to the introduction of a LATPOLE FITS header card which may be used to disambiguate where CRVAL1, CRVAL2, and LONGPOLE do not uniquely determine the latitude of the native pole. The CEL parameter array (formerly WCS) has been extended to accomodate LATPOLE as CEL(4), and the flag variable is now CEL(5) (formerly WCS(4)). - Default values of LONGPOLE and LATPOLE are now supported and their use is recommended where appropriate. - Numerical precision was being lost near the native poles in the SIN, AIR, and QSC projections and this has been recovered (reported by Lindsey Davis, NOAO). Floating underflows in CSC are now avoided. - Numerical precision was also lost in certain circumstances in the spherical coordinate transformation routines and this has been fixed. - The test programs have been enhanced in various ways and the library has been validated on an SGI machine using both 32-bit and 64-bit compilers. WCSLIB version 1.0 (1995/01/31) ------------------------------- * C library Initial release. * Fortran library Initial release. ------------------------------------------------------------------------ $Id: CHANGES,v 8.4 2024/10/28 13:56:17 mcalabre Exp $ astropy-astropy-201cddb/cextern/wcslib/COPYING000066400000000000000000001045131507226315300213710ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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 . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . astropy-astropy-201cddb/cextern/wcslib/COPYING.LESSER000066400000000000000000000167251507226315300223740ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. astropy-astropy-201cddb/cextern/wcslib/GNUmakefile000066400000000000000000000156401507226315300224120ustar00rootroot00000000000000#----------------------------------------------------------------------------- # GNU makefile for building WCSLIB 8.4 # # Summary of the main targets # --------------------------- # all: Do 'make all' in each subdirectory (excluding ./doxygen). # check: Do 'make check' in each subdirectory (compile and run tests). # 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 # input and output, and intermediates produced in compiling the # programmers' manual. # distclean (or realclean): Recursively delete all platform-dependent files # generated during the build, preserving only the programmers' # manual and man pages (which are normally provided pre-built). # It is the one to use between builds for multiple platforms. # cleanest: Like distclean, but deletes everything that can be regenerated # from the source files, including the programmers' manual and # man pages, but excluding 'configure'. # show: Print the values of important variables used in this and the # other makefiles. # writable: Run chmod recursively to make all sources writable. # # Notes: # 1) If you need to make changes then preferably modify makedefs.in instead. # # 2) Refer also to the makefiles in subdirectories, particularly # C/GNUmakefile. # # Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. # http://www.atnf.csiro.au/people/Mark.Calabretta # $Id: GNUmakefile,v 8.4 2024/10/28 13:56:17 mcalabre Exp $ #----------------------------------------------------------------------------- # Get configure settings. SUBDIR := . include makedefs ifeq "$(CHECK)" "nopgplot" TSTDIRS := $(filter-out pgsbox,$(TSTDIRS)) endif .PHONY : build check chmod clean cleaner cleanest distclean install \ realclean show tests uninstall writable build : -@ for DIR in $(SUBDIRS) ; do \ echo '' ; \ $(TIMER) ; \ $(MAKE) -k -C $$DIR build ; \ done check tests :: show -@ echo '' -@ $(TIMER) @ for DIR in $(SUBDIRS) ; do \ echo '' ; \ $(MAKE) -i -C $$DIR cleaner build ; \ done -@ echo '' @ for DIR in $(TSTDIRS) ; do \ echo '' ; \ $(TIMER) ; \ $(MAKE) -k -C $$DIR $@ ; \ done check :: -@ echo '' -@ echo 'Summary of results for non-graphical tests' -@ echo '------------------------------------------' -@ cat ./*/test_results @ if grep 'FAIL:' ./*/test_results > /dev/null ; then \ exit 1 ; \ else \ exit 0 ; \ fi 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 775 $(DOCDIR) ; \ fi $(INSTALL) -m 444 CHANGES COPYING* README $(DOCDIR) $(INSTALL) -m 444 INSTALL THANKS VALIDATION $(DOCDIR) - if [ -h $(DOCLINK) ] ; then \ $(RM) $(DOCLINK) ; \ fi $(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 \ $(MAKE) -C $$DIR $@ ; \ done cleanest distclean realclean : for DIR in $(SUBDIRS) doxygen ; do \ $(MAKE) -C $$DIR $@ ; \ done - $(RM) *.log - $(RM) -r autom4te.cache autoscan.log - $(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 - $(RM) wcslib-*.tar.gz show :: -@ echo 'Subdirectories to be built...' -@ echo ' SUBDIRS := $(SUBDIRS)' -@ echo ' TSTDIRS := $(TSTDIRS)' -@ echo '' writable : chmod -R u+w . GNUmakefile : makedefs ; makedefs : makedefs.in config.status -@ echo '' -@ $(TIMER) ./config.status config.status : configure -@ echo '' -@ $(TIMER) -@ echo '' -@ echo "Environment variables that affect 'configure':" -@ 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 #----------------------------------------------------------------------------- # These are for code management. .PHONY : dist 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 rm -f $(WCSLIBPKG).tar.bz2 tar cf - -C .. -X wcslib.X $(WCSLIBPKG) | \ tar t | \ grep -v '/$$' | \ sort > wcslib.T rm -f wcslib.X tar cvf $(WCSLIBPKG).tar -C .. -T wcslib.T rm -f wcslib.T bzip2 $(WCSLIBPKG).tar chmod 444 $(WCSLIBPKG).tar.bz2 install_dist : scp -p $(WCSLIBPKG).tar.bz2 cal103@venice:/nfs/ftp/software/wcslib/ cp -fp $(WCSLIBPKG).tar.bz2 ~/public_html/WCS/ mv -f $(WCSLIBPKG).tar.bz2 ../wcslib-releases/ 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/ configure : configure.ac -@ echo '' -@ $(TIMER) autoconf -@ $(RM) configure~ # Code development settings exported for 'configure'. -include flavours astropy-astropy-201cddb/cextern/wcslib/INSTALL000066400000000000000000000314341507226315300213700ustar00rootroot00000000000000------------------------------------------------------------------------------ WCSLIB 8.4 and PGSBOX 8.4 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.4.tar.bz2 cd wcslib-8.4 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 the tar file, type bzcat wcslib-8.4.tar.bz2 | tar pvxf - cd wcslib-8.4 then if you do not need to specify any configuration options, simply run gmake This will run 'configure' to generate "makedefs" which is included by the top- level GNUmakefile and those in each subdirectory, and then build 'libwcs.a', 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 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 gmake check To install the object libraries and header files, do gmake install TWEAKING THE INSTALLATION DEFAULTS ---------------------------------- By default the library and header files are installed in the lib and include subdirectories of /usr/local/. To change this, or any other options, run configure separately before gmake: ./configure --prefix=/some/other/dir gmake Use ./configure --help to list configure's options. Useful options are --with-pgplotinc --with-pgplotlib --with-cfitsioinc --with-cfitsiolib Which allow additional directories to be added to the library and include file search path. Installation of WCSLIB differs a little from most packages in that all configurable makefile variables are defined in a single file, "makedefs", which configure generates from "makedefs.in". If you need to redefine any of the makefile variables you can modify makedefs, or preferably makedefs.in. The makefile will automatically detect this and re-run config.status to re-generate a new makedefs. configure also creates four header files: wcsconfig.h: Contains general purpose preprocessor definitions. It is included by the other wcsconfig header files. wcsconfig_f77.h: By common convention the WCSLIB Fortran wrappers have been written (in C) using function names in lower case with an underscore ("_") suffix. wcsconfig_f77.h defines a preprocessor macro, F77_FUNC(name,NAME), that may redefine these to suit different name mangling schemes used by some Fortran compilers. wcsconfig_tests.h: Contains C preprocessor definitions for compiling the test/demo programs. wcsconfig_utils.h: Contains C preprocessor macro definitions for compiling the utility programs provided with WCSLIB. If you do have trouble building the library please send me config.log. 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 8.4 2024/10/28 13:56:17 mcalabre Exp $ ============================================================================== Copyright 1994, 1995, 1996, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. This file is free documentation; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. Basic Installation ================== These are generic installation instructions. The `configure' shell script attempts to guess correct values for various system-dependent variables used during compilation. It uses those values to create a `Makefile' in each directory of the package. It may also create one or more `.h' files containing system-dependent definitions. Finally, it creates a shell script `config.status' that you can run in the future to recreate the current configuration, and a file `config.log' containing compiler output (useful mainly for debugging `configure'). It can also use an optional file (typically called `config.cache' and enabled with `--cache-file=config.cache' or simply `-C') that saves the results of its tests to speed up reconfiguring. (Caching is disabled by default to prevent problems with accidental use of stale cache files.) If you need to do unusual things to compile the package, please try to figure out how `configure' could check whether to do them, and mail diffs or instructions to the address given in the `README' so they can be considered for the next release. If you are using the cache, and at some point `config.cache' contains results you don't want to keep, you may remove or edit it. The file `configure.ac' (or `configure.in') is used to create `configure' by a program called `autoconf'. You only need `configure.ac' if you want to change it or regenerate `configure' using a newer version of `autoconf'. The simplest way to compile this package is: 1. `cd' to the directory containing the package's source code and type `./configure' to configure the package for your system. If you're using `csh' on an old version of System V, you might need to type `sh ./configure' instead to prevent `csh' from trying to execute `configure' itself. Running `configure' takes awhile. While running, it prints some messages telling which features it is checking for. 2. Type `make' to compile the package. 3. Optionally, type `make check' to run any self-tests that come with the package. 4. Type `make install' to install the programs and any data files and documentation. 5. You can remove the program binaries and object files from the source code directory by typing `make clean'. To also remove the files that `configure' created (so you can compile the package for a different kind of computer), type `make distclean'. There is also a `make maintainer-clean' target, but that is intended mainly for the package's developers. If you use it, you may have to get all sorts of other programs in order to regenerate files that came with the distribution. Compilers and Options ===================== Some systems require unusual options for compilation or linking that the `configure' script does not know about. Run `./configure --help' for details on some of the pertinent environment variables. You can give `configure' initial values for variables by setting them in the environment. You can do that on the command line like this: ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix *Note Defining Variables::, for more details. Compiling For Multiple Architectures ==================================== You can compile the package for more than one kind of computer at the same time, by placing the object files for each architecture in their own directory. To do this, you must use a version of `make' that supports the `VPATH' variable, such as GNU `make'. `cd' to the directory where you want the object files and executables to go and run the `configure' script. `configure' automatically checks for the source code in the directory that `configure' is in and in `..'. If you have to use a `make' that does not support the `VPATH' variable, you have to compile the package for one architecture at a time in the source code directory. After you have installed the package for one architecture, use `make distclean' before reconfiguring for another architecture. Installation Names ================== By default, `make install' will install the package's files in `/usr/local/bin', `/usr/local/man', etc. You can specify an installation prefix other than `/usr/local' by giving `configure' the option `--prefix=PATH'. You can specify separate installation prefixes for architecture-specific files and architecture-independent files. If you give `configure' the option `--exec-prefix=PATH', the package will use PATH as the prefix for installing programs and libraries. Documentation and other data files will still use the regular prefix. In addition, if you use an unusual directory layout you can give options like `--bindir=PATH' to specify different values for particular kinds of files. Run `configure --help' for a list of the directories you can set and what kinds of files go in them. If the package supports it, you can cause programs to be installed with an extra prefix or suffix on their names by giving `configure' the option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. Optional Features ================= Some packages pay attention to `--enable-FEATURE' options to `configure', where FEATURE indicates an optional part of the package. They may also pay attention to `--with-PACKAGE' options, where PACKAGE is something like `gnu-as' or `x' (for the X Window System). The `README' should mention any `--enable-' and `--with-' options that the package recognizes. For packages that use the X Window System, `configure' can usually find the X include and library files automatically, but if it doesn't, you can use the `configure' options `--x-includes=DIR' and `--x-libraries=DIR' to specify their locations. Specifying the System Type ========================== There may be some features `configure' cannot figure out automatically, but needs to determine by the type of machine the package will run on. Usually, assuming the package is built to be run on the _same_ architectures, `configure' can figure that out, but if it prints a message saying it cannot guess the machine type, give it the `--build=TYPE' option. TYPE can either be a short name for the system type, such as `sun4', or a canonical name which has the form: CPU-COMPANY-SYSTEM where SYSTEM can have one of these forms: OS KERNEL-OS See the file `config.sub' for the possible values of each field. If `config.sub' isn't included in this package, then this package doesn't need to know the machine type. If you are _building_ compiler tools for cross-compiling, you should use the `--target=TYPE' option to select the type of system they will produce code for. If you want to _use_ a cross compiler, that generates code for a platform different from the build platform, you should specify the "host" platform (i.e., that on which the generated programs will eventually be run) with `--host=TYPE'. Sharing Defaults ================ If you want to set default values for `configure' scripts to share, you can create a site shell script called `config.site' that gives default values for variables like `CC', `cache_file', and `prefix'. `configure' looks for `PREFIX/share/config.site' if it exists, then `PREFIX/etc/config.site' if it exists. Or, you can set the `CONFIG_SITE' environment variable to the location of the site script. A warning: not all `configure' scripts look for a site script. Defining Variables ================== Variables not defined in a site shell script can be set in the environment passed to `configure'. However, some packages may run configure again during the build, and the customized values of these variables may be lost. In order to avoid this problem, you should set them in the `configure' command line, using `VAR=value'. For example: ./configure CC=/usr/local2/bin/gcc will cause the specified gcc to be used as the C compiler (unless it is overridden in the site shell script). `configure' Invocation ====================== `configure' recognizes the following options to control how it operates. `--help' `-h' Print a summary of the options to `configure', and exit. `--version' `-V' Print the version of Autoconf used to generate the `configure' script, and exit. `--cache-file=FILE' Enable the cache: use and save the results of the tests in FILE, traditionally `config.cache'. FILE defaults to `/dev/null' to disable caching. `--config-cache' `-C' Alias for `--cache-file=config.cache'. `--quiet' `--silent' `-q' Do not print messages saying which checks are being made. To suppress all normal output, redirect it to `/dev/null' (any error messages will still be shown). `--srcdir=DIR' Look for the package's source code in directory DIR. Usually `configure' can determine that directory automatically. `configure' also accepts some other, not widely useful, options. Run `configure --help' for more details. astropy-astropy-201cddb/cextern/wcslib/README000066400000000000000000000033571507226315300212220ustar00rootroot00000000000000------------------------------------------------------------------------------ WCSLIB 8.4 and PGSBOX 8.4 ------------------------------------------------------------------------------ WCSLIB 8.4 - an implementation of the FITS WCS standard. Copyright (C) 1995-2024, 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/people/Mark.Calabretta $Id: README,v 8.4 2024/10/28 13:56:17 mcalabre Exp $ ------------------------------------------------------------------------------ Please refer to ./INSTALL ...Installation instructions. ./html/index.html ...The WCSLIB programmer's manual in HTML format. ./wcslib.pdf ...The WCSLIB programmer's manual in PDF format. ./CHANGES ...Log of changes made to WCSLIB. ./THANKS ...List of contributors to WCSLIB. ./VALIDATION ...List of platforms on which the installation procedures and test suite were exercised. ./COPYING ...A copy of the GNU General Public License, v3.0. ./COPYING.LESSER ...A copy of the Lesser GNU General Public License. astropy-astropy-201cddb/cextern/wcslib/THANKS000066400000000000000000000063541507226315300212550ustar00rootroot00000000000000I 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) 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) 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) Randall Wayth (Curtin U.) Benjamin Alan Weaver (LBL) Peter Weilbacher (AIP) Matthew Whiting (ATNF/CSIRO) Peter Williams (UCB, CfA) Daren Scot Wilson (NRAO) Tony Wong (ATNF/CSIRO) $Id: THANKS,v 8.4 2024/10/28 13:56:17 mcalabre Exp $ astropy-astropy-201cddb/cextern/wcslib/VALIDATION000066400000000000000000000675231507226315300217240ustar00rootroot00000000000000Platforms on which the installation procedures and test suite were exercised. 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) -------------------------------- * 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 * MacBook Pro (Intel Core 2 Duo) running MacOSX 10.7.3 (11D50) uname -r (Darwin kernel version): 11.3.0 gcc --version: i686-apple-darwin11-llvm-gcc-4.2 (GCC) 4.2.1 (Apple Inc. build 5658) (LLVM build 2336.1.00) gfortran --version: GNU Fortran (GCC) 4.6.1 (Non-graphics tests only.) WCSLIB version 4.13.1 (2012/03/15) ---------------------------------- * Dell Latitude D630 (Intel Centrino, i686) running Debian linux 5.0.9 (lenny) uname -r (kernel version): 2.6.32-bpo.5-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 * MacBook Pro (Intel Core 2 Duo) running MacOSX 10.7.3 (11D50) uname -r (Darwin kernel version): 11.3.0 gcc --version: i686-apple-darwin11-llvm-gcc-4.2 (GCC) 4.2.1 (Apple Inc. build 5658) (LLVM build 2336.1.00) gfortran --version: GNU Fortran (GCC) 4.6.1 (Non-graphics tests only.) WCSLIB version 4.10 (2012/02/06) -------------------------------- * Dell Latitude D630 (Intel Centrino, i686) running Debian linux 5.0.9 (lenny) uname -r (kernel version): 2.6.32-bpo.5-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.8 (2011/08/15) ------------------------------- * Dell Latitude D620 (Intel Centrino Duo, i686), Debian linux 4.0 (etch) uname -r (kernel version): 2.6.24-1-686 (32-bit) gcc --version: gcc (GCC) 4.1.2 20061115 (prerelease) (Debian 4.1.1-21) g77 --version: GNU Fortran (GCC) 3.4.6 (Debian 3.4.6-5) * Dell PowerEdge 2950 (Intel Xeon, 8 x X5460), Debian linux 5.0.8 (lenny) uname -r (kernel version): 2.6.26-2-amd64 (64-bit) 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 * 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 gfortran --version: GNU Fortran (Debian 4.4.5-8) 4.4.5 * Mac mini (Intel Core 2 Duo) running MacOSX 10.6.2 (10C540) uname -r (Darwin kernel version): 10.2.0 gcc --version: i686-apple-darwin10-gcc-4.2.1 (GCC) 4.2.1 (Apple Inc. build 5646) gfortran --version: GNU Fortran (GCC) 4.5.0 20100107 (experimental) * Enterprise 450 Model 2250 (Sparc, sun4u 64-bit), SunOS 5.9 (Solaris 9) uname -r (SunOS version): 5.9 gcc --version: gcc (GCC) 4.5.1 gfortran --version: GNU Fortran (GCC) 4.5.1 WCSLIB version 4.7 (2011/02/07) ------------------------------- * Dell Latitude D630 (Intel Centrino, i686) running Debian linux 4.0 (etch) uname -r (kernel version): 2.6.24-1-686 gcc --version: gcc (GCC) 4.1.2 20061115 (prerelease) (Debian 4.1.1-21) g77 --version: GNU Fortran (GCC) 3.4.6 (Debian 3.4.6-5) * Sun SunFire V20z (AMD Opteron, x86_64) running Debian linux 4.0 (etch) uname -r (kernel version): 2.6.18-6-amd64 gcc --version: gcc (GCC) 4.1.2 20061115 (prerelease) (Debian 4.1.1-21) g77 --version: GNU Fortran (GCC) 3.4.6 (Debian 3.4.6-5) * Enterprise 450 Model 2250 (Sparc, sun4u 64-bit), SunOS 5.9 (Solaris 9) uname -r (SunOS version): 5.9 gcc --version: gcc (GCC) 4.5.1 gfortran --version: GNU Fortran (GCC) 4.5.1 and cc -V: cc: Sun WorkShop 6 update 2 C 5.3 Patch 111679-14 2004/02/20 f77 -V: f77: Sun WorkShop 6 update 2 FORTRAN 77 5.3 Patch 111691-07 2004/04/23 * Mac Xserve (Quad-Core Intel Xeon) running MacOSX 10.6.5 (10H575) uname -r (Darwin kernel version): 10.5.0 gcc --version: 4.2.1 (Apple Inc. build 5664) gfortran --version: GNU Fortran (GCC) 4.5.0 20100107 (experimental) * Mac mini (Intel Core 2 Duo) running MacOSX 10.6.2 (10C540) uname -r (Darwin kernel version): 10.2.0 gcc --version: i686-apple-darwin10-gcc-4.2.1 (GCC) 4.2.1 (Apple Inc. build 5646) gfortran --version: GNU Fortran (GCC) 4.5.0 20100107 (experimental) * Mac mini (Intel Core Duo) running MacOSX 10.4.9 (8P2137) uname -r (Darwin kernel version): 8.9.1 gcc --version: gcc (GCC) 4.3.0 20070316 (experimental) g77 --version: GNU Fortran (GCC) 3.4.0 WCSLIB version 4.5 (2010/07/16) ------------------------------- * Dell Latitude D630 (Intel Centrino, i686) running Debian linux 4.0 (etch) uname -r (kernel version): 2.6.24-1-686 gcc --version: gcc (GCC) 4.1.2 20061115 (prerelease) (Debian 4.1.1-21) g77 --version: GNU Fortran (GCC) 3.4.6 (Debian 3.4.6-5) and gcc --version: gcc (GCC) 4.1.2 20061115 (prerelease) (Debian 4.1.1-21) ifort -V: Intel(R) Fortran Compiler for 32-bit applications, Version 8.1 Build 20041118Z Package ID: l_fc_pc_8.1.023 * Mac mini (Intel Core 2 Duo, i386) running MacOSX 10.6.2 (10C540) uname -r (Darwin kernel version): 10.2.0 gcc --version: i686-apple-darwin10-gcc-4.2.1 (GCC) 4.2.1 (Apple Inc. build 5646) gfortran --version: GNU Fortran (GCC) 4.5.0 20100107 (experimental) * Mac mini (Intel Core Duo, i386) running MacOSX 10.4.9 (8P2137) uname -r (Darwin kernel version): 8.9.1 gcc --version: gcc (GCC) 4.3.0 20070316 (experimental) g77 --version: GNU Fortran (GCC) 3.4.0 and gcc --version: gcc (GCC) 4.3.0 20070316 (experimental) gfortran --version: GNU Fortran (GCC) 4.3.0 20070316 (experimental) * Sun SunFire V20z (AMD Opteron, x86_64) running Debian linux 4.0 (etch) uname -r (kernel version): 2.6.18-6-amd64 gcc --version: gcc (GCC) 4.1.2 20061115 (prerelease) (Debian 4.1.1-21) g77 --version: GNU Fortran (GCC) 3.4.6 (Debian 3.4.6-5) and 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) * Sun Ultra-60 (Sparc, sun4u) running SunOS 5.6 (Solaris 2.6) uname -r (SunOS version): 5.6 gcc --version: 2.95.3 g77 --version: GNU Fortran 0.5.25 20010315 (release) and cc -V: cc: Sun WorkShop 6 update 2 C 5.3 Patch 111679-14 2004/02/20 f77 -V: f77: Sun WorkShop 6 update 2 FORTRAN 77 5.3 Patch 111691-07 2004/04/23 WCSLIB version 4.4 (2009/08/06) ------------------------------- * Dell Latitude D630 (Intel Centrino, i686) running Debian linux 4.0 (etch) uname -r (kernel version): 2.6.24-1-686 gcc --version: gcc (GCC) 4.1.2 20061115 (prerelease) (Debian 4.1.1-21) g77 --version: GNU Fortran (GCC) 3.4.6 (Debian 3.4.6-5) * Mac mini (Intel Core Duo, i386) running MacOSX 10.4.9 (8P2137) uname -r (Darwin kernel version): 8.9.1 gcc --version: gcc (GCC) 4.3.0 20070316 (experimental) g77 --version: GNU Fortran (GCC) 3.4.0 and gcc --version: gcc (GCC) 4.3.0 20070316 (experimental) gfortran --version: GNU Fortran (GCC) 4.3.0 20070316 (experimental) * Sun SunFire V20z (AMD Opteron, x86_64) running Debian linux 4.0 (etch) uname -r (kernel version): 2.6.18-6-amd64 gcc --version: gcc (GCC) 4.1.2 20061115 (prerelease) (Debian 4.1.1-21) g77 --version: GNU Fortran (GCC) 3.4.6 (Debian 3.4.6-5) and 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) * Sun SunBlade 1000 (Sparc, sun4u) running SunOS 5.8 (Solaris 2.8) uname -r (SunOS version): 5.8 gcc --version: 2.95.3 g77 --version: GNU Fortran 0.5.25 20010315 (release) and cc -V: cc: Sun WorkShop 6 update 2 C 5.3 Patch 111679-14 2004/02/20 f77 -V: f77: Sun WorkShop 6 update 2 FORTRAN 77 5.3 Patch 111691-07 2004/04/23 ------------------------------------------------------------------------------ $Id: VALIDATION,v 8.4 2024/10/28 13:56:17 mcalabre Exp $ astropy-astropy-201cddb/cextern/wcslib/config/000077500000000000000000000000001507226315300215775ustar00rootroot00000000000000astropy-astropy-201cddb/cextern/wcslib/config/ax_pthread.m4000066400000000000000000000540341507226315300241660ustar00rootroot00000000000000# =========================================================================== # 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 astropy-astropy-201cddb/cextern/wcslib/config/config.guess000066400000000000000000001262161507226315300241240ustar00rootroot00000000000000#! /bin/sh # Attempt to guess a canonical system name. # Copyright 1992-2018 Free Software Foundation, Inc. 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 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 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 Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # # Originally written by Per Bothner; maintained since 2000 by Ben Elliston. # # You can get the latest version of this script from: # 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,.*/,,'` usage="\ Usage: $0 [OPTION] Output the configuration name of the system \`$me' is run on. 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 Report bugs and patches to ." version="\ GNU config.guess ($timestamp) Originally written by Per Bothner. 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." 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 # 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. 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) 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 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 *:NetBSD:*:*) # NetBSD (nbsd) targets should (where applicable) match one or # 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 # 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=`(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 ;; 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) and ABI. case "$UNAME_MACHINE_ARCH" in earm*) os=netbsdelf ;; arm*|i386|m68k|ns32k|sh3*|sparc|vax) set_cc_for_build if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ELF__ 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 # 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 Debian*) release='-gnu' ;; *) 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}${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" 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" 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 ;; *: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) 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`" # Reset EXIT trap before exiting to avoid spurious non-zero exit code. exitcode=$? trap '' 0 exit $exitcode ;; 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 ;; s390x:SunOS:*:*) 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/[^.]*//'`" exit ;; sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) echo sparc-sun-solaris2"`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`" exit ;; i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) echo i386-pc-auroraux"$UNAME_RELEASE" exit ;; i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) 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/[^.]*//'`" 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) 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 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:*:[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 IBM_ARCH=rs6000 else IBM_ARCH=powerpc fi 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" fi echo "$IBM_ARCH"-ibm-aix"$IBM_REV" exit ;; *:AIX:*:*) echo rs6000-ibm-aix exit ;; 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 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 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 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 -q __LP64__ 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:*:*) 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 ;; 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 amd64) 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 exit ;; *:MINGW64*:*) echo "$UNAME_MACHINE"-pc-mingw64 exit ;; *:MINGW*:*) echo "$UNAME_MACHINE"-pc-mingw32 exit ;; *:MSYS*:*) echo "$UNAME_MACHINE"-pc-msys exit ;; i*:PW*:*) echo "$UNAME_MACHINE"-pc-pw32 exit ;; *:Interix*:*) case "$UNAME_MACHINE" in x86) echo i586-pc-interix"$UNAME_RELEASE" exit ;; authenticamd | genuineintel | EM64T) echo x86_64-unknown-interix"$UNAME_RELEASE" exit ;; IA64) echo ia64-unknown-interix"$UNAME_RELEASE" exit ;; esac ;; i*:UWIN*:*) echo "$UNAME_MACHINE"-pc-uwin exit ;; amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) echo x86_64-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-$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 "[:upper:]" "[:lower:]"``echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`-$LIBC" exit ;; *:Minix:*:*) echo "$UNAME_MACHINE"-unknown-minix exit ;; 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 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 -q ld.so.1 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:*:*) 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-"$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-"$LIBC"eabi else echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"eabihf fi fi exit ;; avr32*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; cris:Linux:*:*) echo "$UNAME_MACHINE"-axis-linux-"$LIBC" exit ;; crisv32:Linux:*:*) echo "$UNAME_MACHINE"-axis-linux-"$LIBC" exit ;; e2k:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; frv:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; hexagon:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; i*86:Linux:*:*) echo "$UNAME_MACHINE"-pc-linux-"$LIBC" exit ;; ia64:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; k1om:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; m32r*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; m68*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; mips:Linux:*:* | mips64:Linux:*:*) set_cc_for_build sed 's/^ //' << EOF > "$dummy.c" #undef CPU #undef ${UNAME_MACHINE} #undef ${UNAME_MACHINE}el #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) CPU=${UNAME_MACHINE}el #else #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) CPU=${UNAME_MACHINE} #else CPU= #endif #endif EOF eval "`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU'`" test "x$CPU" != x && { echo "$CPU-unknown-linux-$LIBC"; exit; } ;; 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-"$LIBC" exit ;; parisc64:Linux:*:* | hppa64:Linux:*:*) 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-"$LIBC" ;; PA8*) echo hppa2.0-unknown-linux-"$LIBC" ;; *) echo hppa-unknown-linux-"$LIBC" ;; esac exit ;; ppc64:Linux:*:*) echo powerpc64-unknown-linux-"$LIBC" exit ;; ppc:Linux:*:*) 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-"$LIBC" exit ;; sh64*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; sh*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; sparc:Linux:*:* | sparc64:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; tile*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; vax:Linux:*:*) echo "$UNAME_MACHINE"-dec-linux-"$LIBC" exit ;; x86_64:Linux:*:*) echo "$UNAME_MACHINE"-pc-linux-"$LIBC" exit ;; xtensa*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" 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.[02]*:*) echo i386-unknown-lynxos"$UNAME_RELEASE" exit ;; i*86:*DOS:*:*) echo "$UNAME_MACHINE"-pc-msdosdjgpp exit ;; 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" 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 i586. # Note: whatever this is, it MUST be the same as what config.sub # prints for the "djgpp" host, or else GDB configure will decide that # this is a cross-build. echo i586-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; } ;; NCR*:*:4.2:* | MPRAS*:*:4.2:*) OS_REL='.3' 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; } /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ && { echo i586-ncr-sysv4.3"$OS_REL"; 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.[02]*:*) 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 ;; 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" exit ;; SX-5:SUPER-UX:*:*) echo sx5-nec-superux"$UNAME_RELEASE" exit ;; SX-6:SUPER-UX:*:*) echo sx6-nec-superux"$UNAME_RELEASE" exit ;; SX-7:SUPER-UX:*:*) echo sx7-nec-superux"$UNAME_RELEASE" exit ;; SX-8:SUPER-UX:*:*) echo sx8-nec-superux"$UNAME_RELEASE" exit ;; SX-8R:SUPER-UX:*:*) 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" exit ;; *:Rhapsody:*:*) echo "$UNAME_MACHINE"-apple-rhapsody"$UNAME_RELEASE" exit ;; *:Darwin:*:*) UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown 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 UNAME_PROCESSOR=i386 UNAME_MACHINE=pc fi 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" exit ;; NSE-*:NONSTOP_KERNEL:*:*) echo nse-tandem-nsk"$UNAME_RELEASE" exit ;; 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 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. # shellcheck disable=SC2154 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 ;; i*86: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 "$0: unable to guess system type" >&2 case "$UNAME_MACHINE:$UNAME_SYSTEM" in mips:Linux | mips64:Linux) # If we got here on MIPS GNU/Linux, output extra information. cat >&2 <&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 'before-save-hook 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: astropy-astropy-201cddb/cextern/wcslib/config/config.sub000066400000000000000000001055571507226315300235740ustar00rootroot00000000000000#! /bin/sh # Configuration validation subroutine script. # Copyright 1992-2018 Free Software Foundation, Inc. timestamp='2018-07-25' # 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. # # You should have received a copy of the GNU General Public License # 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. This Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # Please send patches to . # # 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. # You can get the latest version of this script from: # 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 # 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 or ALIAS Canonicalize a configuration name. 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 Report bugs and patches to ." version="\ GNU config.sub ($timestamp) 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." 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 # Split fields of configuration type IFS="-" read -r field1 field2 field3 field4 <&2 exit 1 ;; *-*-*-*) basic_machine=$field1-$field2 os=$field3-$field4 ;; *-*-*) # 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 ;; *-*) # 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 ;; *) # 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 # 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 \ | 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 | 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 | c8051 | clipper | csky \ | d10v | d30v | dlx | dsp16xx \ | e2k | epiphany \ | fido | fr30 | frv | ft32 \ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ | hexagon \ | i370 | i860 | i960 | ia16 | ia64 \ | ip2k | iq2000 \ | k1om \ | le32 | le64 \ | lm32 \ | m32c | m32r | m32rle | m68000 | m68k | m88k \ | maxq | mb | microblaze | microblazeel | mcore | mep | metag \ | mips | mipsbe | mipseb | mipsel | mipsle \ | mips16 \ | mips64 | mips64el \ | mips64octeon | mips64octeonel \ | mips64orion | mips64orionel \ | mips64r5900 | mips64r5900el \ | mips64vr | mips64vrel \ | mips64vr4100 | mips64vr4100el \ | mips64vr4300 | mips64vr4300el \ | mips64vr5000 | mips64vr5000el \ | 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 \ | nfp \ | nios | nios2 | nios2eb | nios2el \ | ns16k | ns32k \ | open8 | or1k | or1knd | or32 \ | pdp10 | pj | pjl \ | powerpc | powerpc64 | powerpc64le | powerpcle \ | pru \ | pyramid \ | riscv | riscv32 | riscv64 \ | rl78 | rx \ | score \ | 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 \ | spu \ | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \ | ubicom32 \ | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \ | visium \ | wasm32 \ | x86 | xc16x | xstormy16 | xtensa \ | z8k | z80) basic_machine=$basic_machine-unknown ;; c54x) basic_machine=tic54x-unknown ;; c55x) basic_machine=tic55x-unknown ;; c6x) basic_machine=tic6x-unknown ;; leon|leon[3-9]) basic_machine=sparc-$basic_machine ;; m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip) basic_machine=$basic_machine-unknown os=${os:-none} ;; m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65) ;; 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 ;; xscaleel) basic_machine=armel-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 ;; # 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-* | arceb-* \ | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ | avr-* | avr32-* \ | ba-* \ | be32-* | be64-* \ | bfin-* | bs2000-* \ | c[123]* | c30-* | [cjt]90-* | c4x-* \ | c8051-* | clipper-* | craynv-* | csky-* | cydra-* \ | d10v-* | d30v-* | dlx-* \ | 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-* | 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-* | microblazeel-* \ | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ | mips16-* \ | mips64-* | mips64el-* \ | mips64octeon-* | mips64octeonel-* \ | mips64orion-* | mips64orionel-* \ | mips64r5900-* | mips64r5900el-* \ | mips64vr-* | mips64vrel-* \ | mips64vr4100-* | mips64vr4100el-* \ | mips64vr4300-* | mips64vr4300el-* \ | mips64vr5000-* | mips64vr5000el-* \ | 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-* \ | nfp-* \ | nios-* | nios2-* | nios2eb-* | nios2el-* \ | none-* | np1-* | ns16k-* | ns32k-* \ | open8-* \ | or1k*-* \ | orion-* \ | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \ | pru-* \ | pyramid-* \ | 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*-* \ | tahoe-* \ | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ | tile*-* \ | tron-* \ | ubicom32-* \ | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \ | vax-* \ | visium-* \ | wasm32-* \ | we32k-* \ | x86-* | x86_64-* | xc16x-* | xps100-* \ | xstormy16-* | xtensa*-* \ | ymp-* \ | z8k-* | z80-*) ;; # Recognize the basic CPU types without company name, with glob match. xtensa*) basic_machine=$basic_machine-unknown ;; # Recognize the various machine names and aliases which stand # for a CPU type and a company and sometimes even an OS. 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) basic_machine=m68000-att ;; 3b*) basic_machine=we32k-att ;; abacus) basic_machine=abacus-unknown ;; alliant | fx80) basic_machine=fx80-alliant ;; altos | altos3068) basic_machine=m68k-altos ;; amd64) basic_machine=x86_64-pc ;; amd64-*) basic_machine=x86_64-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; amiga | amiga-*) basic_machine=m68k-unknown ;; asmjs) basic_machine=asmjs-unknown ;; blackfin-*) basic_machine=bfin-`echo "$basic_machine" | sed 's/^[^-]*-//'` os=linux ;; bluegene*) basic_machine=powerpc-ibm os=cnk ;; c54x-*) basic_machine=tic54x-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; c55x-*) basic_machine=tic55x-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; c6x-*) basic_machine=tic6x-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; c90) basic_machine=c90-cray os=${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 ;; cr16 | cr16-*) basic_machine=cr16-unknown os=${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=${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 ;; dpx20 | dpx20-*) basic_machine=rs6000-bull os=${os:-bosx} ;; dpx2*) basic_machine=m68k-bull os=sysv3 ;; e500v[12]) basic_machine=powerpc-unknown os=$os"spe" ;; e500v[12]-*) basic_machine=powerpc-`echo "$basic_machine" | sed 's/^[^-]*-//'` os=$os"spe" ;; encore | umax | mmax) basic_machine=ns32k-encore ;; elxsi) basic_machine=elxsi-elxsi os=${os:-bsd} ;; fx2800) basic_machine=i860-alliant ;; genix) basic_machine=ns32k-ns ;; h3050r* | hiux*) basic_machine=hppa1.1-hitachi os=hiuxwe2 ;; hp300-*) basic_machine=m68k-hp ;; 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 ;; i370-ibm* | ibm*) basic_machine=i370-ibm ;; 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 ;; j90 | j90-cray) basic_machine=j90-cray os=${os:-unicos} ;; iris | iris4d) basic_machine=mips-sgi case $os in irix*) ;; *) os=irix4 ;; esac ;; leon-*|leon[3-9]-*) basic_machine=sparc-`echo "$basic_machine" | sed 's/-.*//'` ;; m68knommu-*) basic_machine=m68k-`echo "$basic_machine" | sed 's/^[^-]*-//'` os=linux ;; microblaze*) basic_machine=microblaze-xilinx ;; 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 ;; ms1-*) basic_machine=`echo "$basic_machine" | sed -e 's/ms1-/mt-/'` ;; news-3600 | risc-news) basic_machine=mips-sony os=newsos ;; next | m*-next) basic_machine=m68k-next case $os in nextstep* ) ;; ns2*) os=nextstep2 ;; *) os=nextstep3 ;; esac ;; np1) basic_machine=np1-gould ;; neo-tandem) basic_machine=neo-tandem ;; nse-tandem) basic_machine=nse-tandem ;; 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 ;; openrisc | openrisc-*) basic_machine=or32-unknown ;; pa-hitachi) basic_machine=hppa1.1-hitachi os=hiuxwe2 ;; parisc-*) basic_machine=hppa-`echo "$basic_machine" | sed 's/^[^-]*-//'` os=linux ;; 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 | ppcbe) basic_machine=powerpc-unknown ;; ppc-* | ppcbe-*) basic_machine=powerpc-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; ppcle | powerpclittle) 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) basic_machine=powerpc64le-unknown ;; ppc64le-* | powerpc64little-*) basic_machine=powerpc64le-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; ps2) basic_machine=i386-ibm ;; 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 ;; sb1) basic_machine=mipsisa64sb1-unknown ;; sb1el) basic_machine=mipsisa64sb1el-unknown ;; sde) basic_machine=mipsisa32-sde os=${os:-elf} ;; sequent) basic_machine=i386-sequent ;; sh5el) basic_machine=sh5le-unknown ;; simso-wrs) basic_machine=sparclite-wrs os=vxworks ;; spur) basic_machine=spur-unknown ;; st2000) basic_machine=m68k-tandem ;; strongarm-* | thumb-*) basic_machine=arm-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; sun2) basic_machine=m68000-sun ;; sun3 | sun3-*) basic_machine=m68k-sun ;; sun4) basic_machine=sparc-sun ;; sun386 | sun386i | roadrunner) basic_machine=i386-sun ;; tile*) basic_machine=$basic_machine-unknown os=linux-gnu ;; tx39) basic_machine=mipstx39-unknown ;; tx39el) basic_machine=mipstx39el-unknown ;; tower | tower-32) basic_machine=m68k-ncr ;; vpp*|vx|vx-*) basic_machine=f301-fujitsu ;; w65*) basic_machine=w65-wdc os=none ;; w89k-*) basic_machine=hppa1.1-winbond os=proelf ;; x64) basic_machine=x86_64-pc ;; xps | xps100) basic_machine=xps100-honeywell ;; xscale-* | xscalee[bl]-*) basic_machine=`echo "$basic_machine" | sed 's/^xscale/arm/'` ;; none) basic_machine=none-none os=${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 ;; pdp11) basic_machine=pdp11-dec ;; we32k) basic_machine=we32k-att ;; sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele) basic_machine=sh-unknown ;; 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 ;; *) 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. auroraux) os=auroraux ;; bluegene*) os=cnk ;; solaris1 | solaris1.*) os=`echo $os | sed -e 's|solaris1|sunos4|'` ;; solaris) os=solaris2 ;; unixware*) os=sysv4.2uw ;; gnu/linux*) os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` ;; # 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* | 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*) case $basic_machine in x86-* | i*86-*) ;; *) os=nto-$os ;; esac ;; hiux*) os=hiuxwe2 ;; nto-qnx*) ;; nto*) os=`echo $os | sed -e 's|nto|nto-qnx|'` ;; sim | xray | os68k* | v88r* \ | windows* | osx | abug | netware* | os9* \ | macos* | mpw* | magic* | mmixware* | mon960* | lnews*) ;; linux-dietlibc) os=linux-dietlibc ;; linux*) os=`echo $os | sed -e 's|linux|linux-gnu|'` ;; lynx*178) os=lynxos178 ;; lynx*5) os=lynxos5 ;; lynx*) os=lynxos ;; mac*) os=`echo "$os" | sed -e 's|mac|macos|'` ;; opened*) os=openedition ;; os400*) os=os400 ;; sunos5*) os=`echo "$os" | sed -e 's|sunos5|solaris2|'` ;; sunos6*) os=`echo "$os" | sed -e 's|sunos6|solaris3|'` ;; wince*) os=wince ;; 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 ;; *mint | mint[0-9]* | *MiNT | MiNT[0-9]*) os=mint ;; zvmoe) os=zvmoe ;; dicos*) os=dicos ;; 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 ;; nacl*) ;; ios) ;; none) ;; *-eabi) ;; *) 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 ;; c8051-*) os=elf ;; clipper-intergraph) os=clix ;; hexagon-*) os=elf ;; tic54x-*) os=coff ;; tic55x-*) os=coff ;; tic6x-*) 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 ;; m68*-cisco) os=aout ;; mep-*) os=elf ;; 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 ;; pru-*) os=elf ;; *-be) os=beos ;; *-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 ;; *-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 ;; *-wrs) os=vxworks ;; *) 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 ;; cnk*|-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 ;; clix*) vendor=intergraph ;; 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 'before-save-hook 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: astropy-astropy-201cddb/cextern/wcslib/config/install-sh000077500000000000000000000332551507226315300236130ustar00rootroot00000000000000#!/bin/sh # install - install a program, script, or datafile 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 # following copyright and license. # # Copyright (C) 1994 X Consortium # # 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 # X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN # AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- # TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Except as contained in this notice, the name of the X Consortium shall not # be used in advertising or otherwise to promote the sale, use or other deal- # ings in this Software without prior written authorization from the X Consor- # tium. # # # 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 # when there is no Makefile. # # This script is compatible with the BSD install script, but was written # 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-} 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= chmodcmd=$chmodprog chowncmd= mvcmd=$mvprog rmcmd="$rmprog -f" stripcmd= src= dst= dir_arg= dst_arg= copy_on_change=false no_target_directory= 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: --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 CMPPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG " while test $# -ne 0; do case $1 in -c) ;; -C) copy_on_change=true;; -d) dir_arg=true;; -g) chgrpcmd="$chgrpprog $2" shift;; --help) echo "$usage"; exit $?;; -m) mode=$2 case $mode in *' '* | *' '* | *' '* | *'*'* | *'?'* | *'['*) echo "$0: invalid mode: $mode" >&2 exit 1;; esac shift;; -o) chowncmd="$chownprog $2" shift;; -s) stripcmd=$stripprog;; -t) dst_arg=$2 # Protect names problematic for 'test' and other utilities. case $dst_arg in -* | [=\(\)!]) dst_arg=./$dst_arg;; esac shift;; -T) no_target_directory=true;; --version) echo "$0 $scriptversion"; exit $?;; --) shift break;; -*) echo "$0: invalid option: $1" >&2 exit 1;; *) break;; esac shift done 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 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 for src do # Protect names problematic for 'test' and other utilities. case $src in -* | [=\(\)!]) src=./$src;; esac if test -n "$dir_arg"; then dst=$src dstdir=$dst test -d "$dstdir" dstdir_status=$? else # 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 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 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 # 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 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 # 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 # 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 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-time-zone: "UTC" # time-stamp-end: "; # UTC" # End: astropy-astropy-201cddb/cextern/wcslib/configure000077500000000000000000010224611507226315300222470ustar00rootroot00000000000000#! /bin/sh # From configure.ac Revision: 8.4 . # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.71 for WCSLIB 8.4. # # Report bugs to . # # # 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. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh 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 # 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. 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 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 ;; 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 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; } 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 : 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 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 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 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 if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; 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 if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # 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 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 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 } # 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 # 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 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 -pR' 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='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi 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'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" test -n "$DJDIR" || exec 7<&0 &1 # 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. # ac_default_prefix=/usr/local ac_clean_files= ac_config_libobj_dir=. LIBOBJS= cross_compiling=no subdirs= MFLAGS= MAKEFLAGS= # Identity of this package. PACKAGE_NAME='WCSLIB' PACKAGE_TARNAME='wcslib-8.4' PACKAGE_VERSION='8.4' PACKAGE_STRING='WCSLIB 8.4' 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_STDIO_H # include #endif #ifdef HAVE_STDLIB_H # include #endif #ifdef HAVE_STRING_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_header_c_list= ac_subst_vars='LTLIBOBJS LIBOBJS EXTRA_CLEAN VALGRIND MODE FLAVOUR OBSTZ OBSLAT OBSLNG INSTDIR TSTDIRS SUBDIRS PGPLOTLIB PGPLOTINC XMKMF GETWCSTAB 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 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. # (The list follows the same order as the GNU Coding Standards.) bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datarootdir='${prefix}/share' 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}' infodir='${datarootdir}/info' htmldir='${docdir}' dvidir='${docdir}' pdfdir='${docdir}' psdir='${docdir}' libdir='${exec_prefix}/lib' localedir='${datarootdir}/locale' mandir='${datarootdir}/man' ac_prev= ac_dashdash= 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 case $ac_option in *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; *=) ac_optarg= ;; *) ac_optarg=yes ;; esac case $ac_dashdash$ac_option in --) ac_dashdash=yes ;; -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) ac_prev=datadir ;; -datadir=* | --datadir=* | --datadi=* | --datad=*) datadir=$ac_optarg ;; -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ | --dataroo | --dataro | --datar) ac_prev=datarootdir ;; -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) datarootdir=$ac_optarg ;; -disable-* | --disable-*) ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. 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 ;; -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) docdir=$ac_optarg ;; -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) ac_prev=dvidir ;; -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) dvidir=$ac_optarg ;; -enable-* | --enable-*) ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. 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- \ | --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 ;; -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) ac_prev=htmldir ;; -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ | --ht=*) htmldir=$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 ;; -localedir | --localedir | --localedi | --localed | --locale) ac_prev=localedir ;; -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) localedir=$ac_optarg ;; -localstatedir | --localstatedir | --localstatedi | --localstated \ | --localstate | --localstat | --localsta | --localst | --locals) ac_prev=localstatedir ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) 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 ;; -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) ac_prev=pdfdir ;; -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) pdfdir=$ac_optarg ;; -psdir | --psdir | --psdi | --psd | --ps) ac_prev=psdir ;; -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) psdir=$ac_optarg ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -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=* \ | --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_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. 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_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. 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. 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 ;; -*) 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. 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. printf "%s\n" "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && 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 done if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` 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 # 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 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 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' # 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 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 ac_pwd=`pwd` && test -n "$ac_pwd" && ac_ls_di=`ls -di .` && ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || as_fn_error $? "working directory cannot be determined" test "X$ac_ls_di" = "X$ac_pwd_ls_di" || 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 -- "$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 } /^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 test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." 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" || as_fn_error $? "$ac_msg" pwd)` # When building in place, set srcdir=. if test "$ac_abs_confdir" = "$ac_pwd"; then srcdir=. fi # Remove unnecessary trailing slashes from srcdir. # Double slashes in file names in object file debugging info # mess up M-x gdb in Emacs. case $srcdir in */) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; esac for ac_var in $ac_precious_vars; do eval ac_env_${ac_var}_set=\${${ac_var}+set} eval ac_env_${ac_var}_value=\$${ac_var} eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} eval ac_cv_env_${ac_var}_value=\$${ac_var} done # # 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 WCSLIB 8.4 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 \`..'] 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] --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.4] --htmldir=DIR html documentation [DOCDIR] --dvidir=DIR dvi documentation [DOCDIR] --pdfdir=DIR pdf documentation [DOCDIR] --psdir=DIR ps documentation [DOCDIR] _ACEOF cat <<\_ACEOF X features: --x-includes=DIR X include files are in DIR --x-libraries=DIR X library files are in DIR 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 8.4:";; 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 --without-pgplot eschew PGPLOT --with-pgplotlib=DIR directory containing pgplot library --with-pgplotinc=DIR directory containing pgplot header files --with-x use the X Window System 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 LIBS libraries to pass to the linker, e.g. -l 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 FFLAGS Fortran 77 compiler flags XMKMF Path to xmkmf, Makefile generator for X Window System 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 ac_status=$? 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" || { 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=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. 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/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix cd "$ac_dir" || { ac_status=$?; continue; } # 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 elif test -f "$ac_srcdir/configure"; then echo && $SHELL "$ac_srcdir/configure" --help=recursive else printf "%s\n" "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi || ac_status=$? cd "$ac_pwd" || { ac_status=$?; break; } done fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF WCSLIB configure 8.4 generated by GNU Autoconf 2.71 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 ## ------------------------ ## ## Autoconf initialization. ## ## ------------------------ ## # 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 if ac_fn_c_try_compile "$LINENO" then : cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main (void) { if (sizeof (($2))) return 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : 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 } # ac_fn_c_check_type # 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 } # ac_fn_c_check_header_compile # 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]; ; 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 : 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 # 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 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 # 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 /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $2 (); below. */ #include #undef $2 /* 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 (void) { return $2 (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : eval "$3=yes" else $as_nop eval "$3=no" fi 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 } # ac_fn_c_check_func # 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 () { 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 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; } && { ac_try='./conftest$ac_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=$? 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 ac_retval=$ac_status fi 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_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]; ; return 0; } _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. */ $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_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. */ $4 int main (void) { static int test_array [1 - 2 * !(($2) < 0)]; test_array [0] = 0; return test_array [0]; ; return 0; } _ACEOF 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.beam conftest.$ac_ext done else $as_nop ac_lo= ac_hi= 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 # 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. */ $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_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 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 static long int longval (void) { return $2; } static unsigned long int ulongval (void) { return $2; } #include #include int main (void) { 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 if ac_fn_c_try_run "$LINENO" then : echo >>conftest.val; read $3 &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_f77_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_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 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 8.4, which was generated by GNU Autoconf 2.71. Invocation command line was $ $0$ac_configure_args_raw _ACEOF exec 5>>config.log { 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` /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 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 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 # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -f -r conftest* confdefs.h printf "%s\n" "/* confdefs.h */" > confdefs.h # Predefined preprocessor variables. printf "%s\n" "#define PACKAGE_NAME \"$PACKAGE_NAME\"" >>confdefs.h printf "%s\n" "#define PACKAGE_TARNAME \"$PACKAGE_TARNAME\"" >>confdefs.h printf "%s\n" "#define PACKAGE_VERSION \"$PACKAGE_VERSION\"" >>confdefs.h printf "%s\n" "#define PACKAGE_STRING \"$PACKAGE_STRING\"" >>confdefs.h printf "%s\n" "#define PACKAGE_BUGREPORT \"$PACKAGE_BUGREPORT\"" >>confdefs.h printf "%s\n" "#define PACKAGE_URL \"$PACKAGE_URL\"" >>confdefs.h # 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_site_files="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" fi 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 { 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 # 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 #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; { 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 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]; /* 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 **, 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]); ' # 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); // 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) { 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 #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; 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 bool 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 = 0; float fnumber = 0; 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 *str && number && fnumber; } ' # 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); // Check restrict. if (test_restrict ("String literal") == 0) success = true; char *restrict newvar = "Another string"; // Check varargs. success &= 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[0] = argv[0][0]; dynamic_array[ni.number - 1] = 543; // work around unused variable warnings ok |= (!success || bignum == 0LL || ubignum == 0uLL || newvar[0] == '\''x'\'' || dynamic_array[ni.number - 1] != 543); ' # 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 // 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; // 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; } // 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; }; // Check UTF-8 literals. #define u8 syntax error! char const utf8_literal[] = u8"happens to be ASCII" "another string"; // 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 as_found=false done IFS=$as_save_IFS if $as_found then : else $as_nop as_fn_error $? "cannot find required auxiliary files:$ac_missing_aux_files" "$LINENO" 5 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. 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 # 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. ## ## -------------------- ## 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" "#define WCSLIB_VERSION $PACKAGE_VERSION" >>confdefs.h # Library version number, same as package version. LIBVER="$PACKAGE_VERSION" # Get the system type. # 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 { 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 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 fi { 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 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 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;} else # 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 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 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 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 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 # 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 { 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}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 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 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 { 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 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 { 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}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 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 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 { 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 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 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 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 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 { 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 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 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 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 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 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 { 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 CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi fi 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 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 cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF 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 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=$? 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= 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 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 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; } rm -f conftest.$ac_ext EXEEXT=$ac_cv_exeext ac_exeext=$EXEEXT cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { FILE *f = fopen ("conftest.out", "w"); return ferror (f) || fclose (f) != 0; ; return 0; } _ACEOF 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 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=$? 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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 printf "%s\n" "$cross_compiling" >&6; } 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. */ int main (void) { ; 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 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=$? 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 { { 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 conftest.$ac_cv_objext conftest.$ac_ext fi { 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. */ 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 { 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 if test $ac_compiler_gnu = yes; then GCC=yes else 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. */ int main (void) { ; return 0; } _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. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : else $as_nop ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF 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 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 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 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 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 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 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. */ #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 fi CPP=$ac_cv_prog_CPP else 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 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. */ #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 : 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 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 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 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 { 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 { 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 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 { 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}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 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 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 { 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 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 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 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 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 { 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 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 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 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 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 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 { 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 CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi fi 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 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 { 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. */ 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 { 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 if test $ac_compiler_gnu = yes; then GCC=yes else 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. */ int main (void) { ; return 0; } _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. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : else $as_nop ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF 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 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 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 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 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 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. */ int main (void) { #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 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 fi 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 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 # 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) ;; #( *) 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) ;; #( *) 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 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 # 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 # 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. */ /* 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 (void) { return floor (); ; return 0; } _ACEOF 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" fi # 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. */ /* 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 (void) { return cosd (); ; return 0; } _ACEOF 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 # 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 fi # 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 fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_int" >&5 printf "%s\n" "$ac_cv_sizeof_int" >&6; } printf "%s\n" "#define SIZEOF_INT $ac_cv_sizeof_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 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_int=0 fi fi fi { 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 { 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; } printf "%s\n" "#define SIZEOF_LONG_LONG_INT $ac_cv_sizeof_long_long_int" >>confdefs.h # 64-bit integer data type; use long long int preferentially since that # accords with "%lld" formatting used in fitshdr.l, e.g. # int size_t long int long long int # --- ------ -------- ------------- # gcc x86: 32 32 32 64 # gcc x86_64: 32 64 64 64 if test "x$ac_cv_sizeof_long_long_int" = x8; then printf "%s\n" "#define WCSLIB_INT64 long long int" >>confdefs.h elif test "x$ac_cv_sizeof_long_int" = x8; then printf "%s\n" "#define WCSLIB_INT64 long int" >>confdefs.h elif test "x$ac_cv_sizeof_int" = x8; then printf "%s\n" "#define WCSLIB_INT64 int" >>confdefs.h fi # Does printf() have the z modifier for size_t type? Important for 64-bit. { 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 : printf "%s\n" "#define MODZ \"\"" >>confdefs.h { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: assumed not" >&5 printf "%s\n" "assumed not" >&6; } else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default int main (void) { char buf[64]; if (sprintf(buf, "%zu", (size_t)1) != 1) return 1; else if (strcmp(buf, "1")) return 2; ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO" then : printf "%s\n" "#define MODZ \"z\"" >>confdefs.h { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else $as_nop printf "%s\n" "#define MODZ \"\"" >>confdefs.h { 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.beam conftest.$ac_ext fi # Starting values, may be augmented later. SUBDIRS="C" TSTDIRS="C" INSTDIR="C" # Ways of specifying the Fortran compiler, in order of precedence: # configure --enable-fortran= # F77= configure ...bash # # Ways of disabling Fortran: # configure --disable-fortran # configure --enable-fortran=no # F77=no configure ...bash # Check whether --enable-fortran was given. if test ${enable_fortran+y} then : enableval=$enable_fortran; fi # Check whether --enable-fortran was given. if test ${enable_fortran+y} then : enableval=$enable_fortran; fi if test "x$enable_fortran" != x -a "x$enable_fortran" != xyes ; then F77="$enable_fortran" fi if test "x$F77" = xno ; then F77= { 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 # Look for a Fortran compiler. 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 gfortran g77 f77 ifort xlf frt pgf77 fl32 af77 fort77 f90 \ xlf90 pgf90 epcf90 f95 fort xlf95 lf95 g95 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_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 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_F77="$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 fi fi F77=$ac_cv_prog_F77 if test -n "$F77"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $F77" >&5 printf "%s\n" "$F77" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -n "$F77" && break done fi if test -z "$F77"; then ac_ct_F77=$F77 for ac_prog in gfortran g77 f77 ifort xlf frt pgf77 fl32 af77 fort77 f90 \ xlf90 pgf90 epcf90 f95 fort xlf95 lf95 g95 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_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 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_F77="$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 fi ac_ct_F77=$ac_cv_prog_ac_ct_F77 if test -n "$ac_ct_F77"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_F77" >&5 printf "%s\n" "$ac_ct_F77" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -n "$ac_ct_F77" && break done if test "x$ac_ct_F77" = x; then F77="" 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 F77=$ac_ct_F77 fi fi # Provide some information about the compiler. 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 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 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 { 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 #endif end _ACEOF if ac_fn_f77_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_f77_compiler_gnu=$ac_compiler_gnu fi { 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+y} ac_save_FFLAGS=$FFLAGS FFLAGS= { 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 program main end _ACEOF if ac_fn_f77_try_compile "$LINENO" then : ac_cv_prog_f77_g=yes else $as_nop ac_cv_prog_f77_g=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_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 FFLAGS="-g -O2" else FFLAGS="-g" fi else if test "x$ac_cv_f77_compiler_gnu" = xyes; then FFLAGS="-O2" else FFLAGS= fi fi 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' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu fi if test "x$F77" = x; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: ------------------------------------------------------------------ Fortran compiler not found, will skip Fortran wrappers and PGSBOX. ------------------------------------------------------------------" >&5 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. printf "%s\n" "#define F77_FUNC(name,NAME) name ## _" >>confdefs.h else if test "x$ac_cv_f77_compiler_gnu" = xyes ; then if test "x$F77" = xg77 -o "x$F77" = xf77 ; then # Not recognized by gfortran. FFLAGS="$FFLAGS -Wno-globals" fi fi { 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' ac_compiler_gnu=$ac_cv_f77_compiler_gnu FFLAGS_save=$FFLAGS FFLAGS=-I. cat > conftest.$ac_ext <<_ACEOF program main end _ACEOF 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.beam 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 # Libraries required by the Fortran compiler itself (sets FLIBS). # Required by utilities and test programs written in C that link to # Fortran object modules such as pgsbox. 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 { 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 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 program main end _ACEOF # Compile and link our simple test program by passing a flag (argument # 1 to this macro) to the Fortran compiler in order to get # "verbose" output that we can then parse for the Fortran linker # flags. ac_save_FFLAGS=$FFLAGS FFLAGS="$FFLAGS $ac_verb" eval "set x $ac_link" shift 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 -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" # 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 # 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 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). *mGLOB_options_string*) ac_f77_v_output=`echo $ac_f77_v_output | sed 's/"-mGLOB[^"]*"/ /g'` ;; # Portland Group compiler has singly- or doubly-quoted -cmdline argument # Singly-quoted arguments were reported for versions 5.2-4 and 6.0-4. # 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"` ;; # 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*) ac_f77_v_output=`echo $ac_f77_v_output | sed 's/"//g'` ;; 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 ;; esac done done if test -z "$ac_cv_prog_f77_v"; then { 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 $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.beam conftest.$ac_ext fi { 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 program main end _ACEOF # Compile and link our simple test program by passing a flag (argument # 1 to this macro) to the Fortran compiler in order to get # "verbose" output that we can then parse for the Fortran linker # flags. ac_save_FFLAGS=$FFLAGS FFLAGS="$FFLAGS $ac_cv_prog_f77_v" eval "set x $ac_link" shift 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 -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" # 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 # 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 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). *mGLOB_options_string*) ac_f77_v_output=`echo $ac_f77_v_output | sed 's/"-mGLOB[^"]*"/ /g'` ;; # Portland Group compiler has singly- or doubly-quoted -cmdline argument # Singly-quoted arguments were reported for versions 5.2-4 and 6.0-4. # 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"` ;; # 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*) ac_f77_v_output=`echo $ac_f77_v_output | sed 's/"//g'` ;; esac ac_cv_f77_libs= # Save positional arguments (if any) ac_save_positional="$@" set X $ac_f77_v_output while test $# != 1; do shift ac_arg=$1 case $ac_arg in [\\/]*.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 break fi done 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 for ac_i in $ac_cv_f77_libs; do if test x"$ac_arg" = x"$ac_i"; then ac_exists=true break fi done 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" done 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 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 "" | -*);; *) ac_arg="$ac_arg$2" shift; shift set X $ac_arg "$@" ;; esac ;; -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 break fi done 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" fi 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 break fi done 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" ;; -mllvm) ${2+shift};; # Defend against 'clang -mllvm -loopopt=0'. # Ignore everything else. esac done # restore positional arguments set X $ac_save_positional; shift # We only consider "LD_RUN_PATH" on Solaris systems. If this is seen, # then we insist that the "run path" must be an absolute path (i.e. it # must begin with a "/"). case `(uname -sr) 2>/dev/null` in "SunOS 5"*) 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 for ac_link_opt in $ac_ld_run_path; do ac_cv_f77_libs="$ac_cv_f77_libs -Xlinker $ac_link_opt" done else ac_cv_f77_libs="$ac_cv_f77_libs $ac_ld_run_path" fi ;; esac fi # test "x$[]_AC_LANG_PREFIX[]LIBS" = "x" fi { 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" 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 # 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 { 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 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 # First, try linking without a dummy main: cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef F77_DUMMY_MAIN # ifdef __cplusplus extern "C" # endif int F77_DUMMY_MAIN() { return 1; } #endif int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_fortran_dummy_main=none else $as_nop ac_cv_fortran_dummy_main=unknown fi 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 confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define $ac_fortran_dm_var $ac_func #ifdef F77_DUMMY_MAIN # ifdef __cplusplus extern "C" # endif int F77_DUMMY_MAIN() { return 1; } #endif int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_fortran_dummy_main=$ac_func; break fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext done 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 ac_cv_f77_dummy_main=$ac_cv_fortran_dummy_main rm -rf conftest* LIBS=$ac_f77_dm_save_LIBS fi { 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 != none; then 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 printf "%s\n" "#define FC_DUMMY_MAIN_EQ_F77 1" >>confdefs.h fi fi 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' 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=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 { 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 subroutine foo_bar() return end _ACEOF if ac_fn_f77_try_compile "$LINENO" then : mv conftest.$ac_objext cfortran_test.$ac_objext ac_save_LIBS=$LIBS LIBS="cfortran_test.$ac_objext $LIBS $FLIBS" 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_success=no for ac_foobar in foobar FOOBAR; do for ac_underscore in "" "_"; do ac_func="$ac_foobar$ac_underscore" 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. */ char $ac_func (); #ifdef F77_DUMMY_MAIN # ifdef __cplusplus extern "C" # endif int F77_DUMMY_MAIN() { return 1; } #endif int main (void) { return $ac_func (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_success=yes; break 2 fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext done done 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 "$ac_success" = "yes"; then case $ac_foobar in foobar) ac_case=lower ac_foo_bar=foo_bar ;; FOOBAR) ac_case=upper ac_foo_bar=FOO_BAR ;; 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_success_extra=no for ac_extra in "" "_"; do ac_func="$ac_foo_bar$ac_underscore$ac_extra" 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. */ char $ac_func (); #ifdef F77_DUMMY_MAIN # ifdef __cplusplus extern "C" # endif int F77_DUMMY_MAIN() { return 1; } #endif int main (void) { return $ac_func (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_success_extra=yes; break fi 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' ac_link='$F77 -o conftest$ac_exeext $FFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' 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" 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" else ac_cv_f77_mangling="$ac_cv_f77_mangling, extra underscore" fi else ac_cv_f77_mangling="unknown" fi else ac_cv_f77_mangling="unknown" fi LIBS=$ac_save_LIBS 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.beam conftest.$ac_ext fi { 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' 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=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") printf "%s\n" "#define F77_FUNC(name,NAME) name" >>confdefs.h printf "%s\n" "#define F77_FUNC_(name,NAME) name" >>confdefs.h ;; "lower case, no underscore, extra underscore") printf "%s\n" "#define F77_FUNC(name,NAME) name" >>confdefs.h printf "%s\n" "#define F77_FUNC_(name,NAME) name ## _" >>confdefs.h ;; "lower case, underscore, no extra underscore") printf "%s\n" "#define F77_FUNC(name,NAME) name ## _" >>confdefs.h printf "%s\n" "#define F77_FUNC_(name,NAME) name ## _" >>confdefs.h ;; "lower case, underscore, extra underscore") printf "%s\n" "#define F77_FUNC(name,NAME) name ## _" >>confdefs.h printf "%s\n" "#define F77_FUNC_(name,NAME) name ## __" >>confdefs.h ;; "upper case, no underscore, no extra underscore") printf "%s\n" "#define F77_FUNC(name,NAME) NAME" >>confdefs.h printf "%s\n" "#define F77_FUNC_(name,NAME) NAME" >>confdefs.h ;; "upper case, no underscore, extra underscore") printf "%s\n" "#define F77_FUNC(name,NAME) NAME" >>confdefs.h printf "%s\n" "#define F77_FUNC_(name,NAME) NAME ## _" >>confdefs.h ;; "upper case, underscore, no extra underscore") printf "%s\n" "#define F77_FUNC(name,NAME) NAME ## _" >>confdefs.h printf "%s\n" "#define F77_FUNC_(name,NAME) NAME ## _" >>confdefs.h ;; "upper case, underscore, extra underscore") printf "%s\n" "#define F77_FUNC(name,NAME) NAME ## _" >>confdefs.h printf "%s\n" "#define F77_FUNC_(name,NAME) NAME ## __" >>confdefs.h ;; *) { 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 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$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" fi fi # System-dependent system libraries (for building the sharable library). #----------------------------------------------------------------------- # Darwin (contains stubs for long double). 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 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. */ char printf\$LDBLStub (); #ifdef F77_DUMMY_MAIN # ifdef __cplusplus extern "C" # endif int F77_DUMMY_MAIN() { return 1; } #endif int main (void) { return printf\$LDBLStub (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : eval "$as_ac_Lib=yes" else $as_nop eval "$as_ac_Lib=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 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 # 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 { 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 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_RANLIB="${ac_tool_prefix}ranlib" 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 RANLIB=$ac_cv_prog_RANLIB if test -n "$RANLIB"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5 printf "%s\n" "$RANLIB" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "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 { 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 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_RANLIB="ranlib" 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_RANLIB=$ac_cv_prog_ac_ct_RANLIB if test -n "$ac_ct_RANLIB"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5 printf "%s\n" "$ac_ct_RANLIB" >&6; } else { 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 RANLIB=":" 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 RANLIB=$ac_ct_RANLIB fi else RANLIB="$ac_cv_prog_RANLIB" fi # 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 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 fi fi # Installation utilities. { 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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else { 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), # 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. # 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+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 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[\\/]* | \ /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_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 # 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 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 IFS=$as_save_IFS rm -rf conftest.one conftest.two conftest.dir fi 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 # value for INSTALL within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the value is a relative name. INSTALL=$ac_install_sh fi fi { 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. test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' # 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 printf "%s\n" "$as_me: End of primary configuration. " >&6;} # The following are required to build utilities and test programs. # ---------------------------------------------------------------- { 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 else $as_nop headers=no fi done # 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 `printf "%s\n" "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF else $as_nop headers=no fi done 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 confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include /* for off_t */ #include #ifdef F77_DUMMY_MAIN # ifdef __cplusplus extern "C" # endif int F77_DUMMY_MAIN() { return 1; } #endif int main (void) { int (*fp) (FILE *, off_t, int) = fseeko; return fseeko (stdin, 0, 0) && fp (stdin, 0, 0); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_sys_largefile_source=no; break fi 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 /* for off_t */ #include #ifdef F77_DUMMY_MAIN # ifdef __cplusplus extern "C" # endif int F77_DUMMY_MAIN() { return 1; } #endif int main (void) { int (*fp) (FILE *, off_t, int) = fseeko; return fseeko (stdin, 0, 0) && fp (stdin, 0, 0); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_sys_largefile_source=1; break fi 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 { 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) ;; *) printf "%s\n" "#define _LARGEFILE_SOURCE $ac_cv_sys_largefile_source" >>confdefs.h ;; esac 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 printf "%s\n" "#define HAVE_FSEEKO 1" >>confdefs.h fi # Check whether --enable-largefile was given. if test ${enable_largefile+y} then : enableval=$enable_largefile; fi if test "$enable_largefile" != no; then { 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 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 << 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]; #ifdef F77_DUMMY_MAIN # ifdef __cplusplus extern "C" # endif int F77_DUMMY_MAIN() { return 1; } #endif int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : break fi rm -f core conftest.err conftest.$ac_objext conftest.beam CC="$CC -n32" if ac_fn_c_try_compile "$LINENO" then : ac_cv_sys_largefile_CC=' -n32'; break fi rm -f core conftest.err conftest.$ac_objext conftest.beam break done CC=$ac_save_CC rm -f conftest.$ac_ext fi fi { 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 { 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 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 << 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]; #ifdef F77_DUMMY_MAIN # ifdef __cplusplus extern "C" # endif int F77_DUMMY_MAIN() { return 1; } #endif int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_sys_file_offset_bits=no; break fi 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 /* 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 << 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]; #ifdef F77_DUMMY_MAIN # ifdef __cplusplus extern "C" # endif int F77_DUMMY_MAIN() { return 1; } #endif int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_sys_file_offset_bits=64; break fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ac_cv_sys_file_offset_bits=unknown break done fi { 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) ;; *) printf "%s\n" "#define _FILE_OFFSET_BITS $ac_cv_sys_file_offset_bits" >>confdefs.h ;; esac rm -rf conftest* if test $ac_cv_sys_file_offset_bits = unknown; then { 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 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 << 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]; #ifdef F77_DUMMY_MAIN # ifdef __cplusplus extern "C" # endif int F77_DUMMY_MAIN() { return 1; } #endif int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_sys_large_files=no; break fi 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 /* 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 << 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]; #ifdef F77_DUMMY_MAIN # ifdef __cplusplus extern "C" # endif int F77_DUMMY_MAIN() { return 1; } #endif int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_sys_large_files=1; break fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ac_cv_sys_large_files=unknown break done fi { 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) ;; *) printf "%s\n" "#define _LARGE_FILES $ac_cv_sys_large_files" >>confdefs.h ;; esac 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. LIBDIRS= # Check whether --with-cfitsio was given. if test ${with_cfitsio+y} then : withval=$with_cfitsio; fi if test "x$with_cfitsio" = xno ; then { 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+y} then : withval=$with_cfitsiolib; fi if test "x$with_cfitsiolib" != x ; then LIBDIRS="$LIBDIRS $with_cfitsiolib" fi # Check whether --with-cfitsioinc was given. if test ${with_cfitsioinc+y} then : withval=$with_cfitsioinc; fi if test "x$with_cfitsioinc" != x ; then CFITSIO_INCDIRS="$with_cfitsioinc" fi CFITSIO_INCDIRS="$CFITSIO_INCDIRS \ /usr/local/cfitsio/include \ /local/cfitsio/include" LIBDIRS="$LIBDIRS \ /usr/local/cfitsio/lib \ /local/cfitsio/lib" fi # Check whether --with-pgplot was given. if test ${with_pgplot+y} then : withval=$with_pgplot; fi if test "x$with_pgplot" = xno ; then { 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+y} then : withval=$with_pgplotlib; fi if test "x$with_pgplotlib" != x ; then LIBDIRS="$LIBDIRS $with_pgplotlib" fi # Check whether --with-pgplotinc was given. if test ${with_pgplotinc+y} then : withval=$with_pgplotinc; fi if test "x$with_pgplotinc" != x ; then PGPLOT_INCDIRS="$with_pgplotinc" fi PGPLOT_INCDIRS="$PGPLOT_INCDIRS \ /usr/local/pgplot/include \ /local/pgplot/include" LIBDIRS="$LIBDIRS \ /usr/local/pgplot/lib \ /local/pgplot/lib" fi if test "x$with_cfitsio" != xno -o \ "x$with_pgplot" != xno ; then LIBDIRS="$LIBDIRS \ /usr/local/lib \ /local/lib \ /opt/local/lib \ /opt/SUNWspro/lib \ /sw/lib" for LIBDIR in $LIBDIRS ; do 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 && 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 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 $as_nop continue fi done # Generic include directories. INCDIRS="/usr/local/include \ /local/include \ /opt/local/include \ /sw/include \ /local \ /usr/include" # CFITSIO. if test "x$with_cfitsio" != xno ; then # Search for CFITSIO. for INCDIR in $CFITSIO_INCDIRS $INCDIRS ; do 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 && 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 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=`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 && 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 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 { 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 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. */ char recv (); #ifdef F77_DUMMY_MAIN # ifdef __cplusplus extern "C" # endif int F77_DUMMY_MAIN() { return 1; } #endif int main (void) { return recv (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_socket_recv=yes else $as_nop ac_cv_lib_socket_recv=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_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 { 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 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. */ char ffopen (); #ifdef F77_DUMMY_MAIN # ifdef __cplusplus extern "C" # endif int F77_DUMMY_MAIN() { return 1; } #endif int main (void) { return ffopen (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_cfitsio_ffopen=yes else $as_nop ac_cv_lib_cfitsio_ffopen=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_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 { 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 { 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;} printf "%s\n" "#define HAVE_CFITSIO 1" >>confdefs.h # Check for fits_read_wcstab, present in CFITSIO 3.004beta and later. { 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 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. */ char fits_read_wcstab (); #ifdef F77_DUMMY_MAIN # ifdef __cplusplus extern "C" # endif int F77_DUMMY_MAIN() { return 1; } #endif int main (void) { return fits_read_wcstab (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_cfitsio_fits_read_wcstab=yes else $as_nop ac_cv_lib_cfitsio_fits_read_wcstab=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_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 $as_nop GETWCSTAB=getwcstab.o fi if test "x$GETWCSTAB" != x ; then { 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 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=`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 && 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 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=`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 && 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 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 done # FLIBS (found above via AC_F77_LIBRARY_LDFLAGS) only helps if PGPLOT was # built using the same Fortran compiler that we are using here. # PGPLOT compiled by the SUN Fortran compiler but linked with something # 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 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. */ char iand_ (); #ifdef F77_DUMMY_MAIN # ifdef __cplusplus extern "C" # endif int F77_DUMMY_MAIN() { return 1; } #endif int main (void) { return iand_ (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_M77_iand_=yes else $as_nop ac_cv_lib_M77_iand_=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_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 { 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 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. */ char f77_init (); #ifdef F77_DUMMY_MAIN # ifdef __cplusplus extern "C" # endif int F77_DUMMY_MAIN() { return 1; } #endif int main (void) { return f77_init (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_F77_f77_init=yes else $as_nop ac_cv_lib_F77_f77_init=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_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. { 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 confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef F77_DUMMY_MAIN # ifdef __cplusplus extern "C" # endif int F77_DUMMY_MAIN() { return 1; } #endif int main (void) { return main (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_frtbegin_main=yes else $as_nop ac_cv_lib_frtbegin_main=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_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 { 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 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. */ char gerror_ (); #ifdef F77_DUMMY_MAIN # ifdef __cplusplus extern "C" # endif int F77_DUMMY_MAIN() { return 1; } #endif int main (void) { return gerror_ (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_g2c_gerror_=yes else $as_nop ac_cv_lib_g2c_gerror_=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_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 fi if test "x$F77" != xgfortran; then # For PGPLOT compiled with gfortran but linked with something else. # Note that if gfortran itself is driving the linker it can be harmful # to add -lgfortran to the link list without also adding -lgfortranbegin. # Doing so stops gfortran from adding -lgfortranbegin which is needed to # resolve "main". { 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 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. */ char _gfortran_abort (); #ifdef F77_DUMMY_MAIN # ifdef __cplusplus extern "C" # endif int F77_DUMMY_MAIN() { return 1; } #endif int main (void) { return _gfortran_abort (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_gfortran__gfortran_abort=yes else $as_nop ac_cv_lib_gfortran__gfortran_abort=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_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. { 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+y} then : withval=$with_x; fi # $have_x is `yes', `no', `disabled', or empty when we do not yet know. if test "x$with_x" = xno; then # The user explicitly disabled X. have_x=disabled else case $x_includes,$x_libraries in #( *\'*) 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 # 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' incroot: @echo incroot='${INCROOT}' usrlibdir: @echo usrlibdir='${USRLIBDIR}' 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. 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 # Open Windows xmkmf reportedly sets LIBDIR instead of USRLIBDIR. for ac_extension in a so sl dylib la dll; do if test ! -f "$ac_im_usrlibdir/libX11.$ac_extension" && test -f "$ac_im_libdir/libX11.$ac_extension"; then ac_im_usrlibdir=$ac_im_libdir; break fi done # Screen out bogus values from the imake configuration. They are # bogus both because they are the default anyway, and because # using them would break gcc on systems where it needs fixed includes. case $ac_im_incroot in /usr/include) ac_x_includes= ;; *) test -f "$ac_im_incroot/X11/Xos.h" && ac_x_includes=$ac_im_incroot;; esac case $ac_im_usrlibdir in /usr/lib | /usr/lib64 | /lib | /lib64) ;; *) test -d "$ac_im_usrlibdir" && ac_x_libraries=$ac_im_usrlibdir ;; esac fi cd .. rm -f -r conftest.dir fi # 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 /usr/include /usr/local/include /usr/unsupported/include /usr/athena/include /usr/local/x11r5/include /usr/lpp/Xamples/include /usr/openwin/include /usr/openwin/share/include' 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 confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO" then : # We can compile using X headers with no special include directory. ac_x_includes= 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 break fi done fi rm -f conftest.err conftest.i conftest.$ac_ext fi # $ac_x_includes = no if test "$ac_x_libraries" = no; then # Check for the libraries. # See if we find them without any special options. # Don't add to $LIBS permanently. 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 : LIBS=$ac_save_LIBS # We can link X programs with no special library path. ac_x_libraries= 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 if test -r "$ac_dir/libX11.$ac_extension"; then ac_x_libraries=$ac_dir break 2 fi done done fi 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 | *\'*) : # Didn't find X, or a directory has "'" in its name. 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'" ;; esac fi ;; #( *) have_x=yes;; esac eval "$ac_cv_have_x" fi # $with_x != no if test "$have_x" != yes; then { 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. test "x$x_includes" = xNONE && x_includes=$ac_x_includes test "x$x_libraries" = xNONE && x_libraries=$ac_x_libraries # Update the cache value to reflect the command line values. ac_cv_have_x="have_x=yes\ ac_x_includes='$x_includes'\ ac_x_libraries='$x_libraries'" { 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 if test "x$ac_x_libraries" != x ; then # Not needed for systems that keep the X11 libraries in /usr/lib. LDFLAGS="$LDFLAGS -L$ac_x_libraries" fi PGPLOTLIB="-lX11 $PGPLOTLIB" fi # It is possible that other libraries may be required depending on what # graphics drivers were installed with PGPLOT. { 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 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. */ char deflate (); #ifdef F77_DUMMY_MAIN # ifdef __cplusplus extern "C" # endif int F77_DUMMY_MAIN() { return 1; } #endif int main (void) { return deflate (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_z_deflate=yes else $as_nop ac_cv_lib_z_deflate=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_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 { 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 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. */ char png_error (); #ifdef F77_DUMMY_MAIN # ifdef __cplusplus extern "C" # endif int F77_DUMMY_MAIN() { return 1; } #endif int main (void) { return png_error (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_png_png_error=yes else $as_nop ac_cv_lib_png_png_error=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_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 { 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 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. */ char pgbeg_ (); #ifdef F77_DUMMY_MAIN # ifdef __cplusplus extern "C" # endif int F77_DUMMY_MAIN() { return 1; } #endif int main (void) { return pgbeg_ (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_pgplot_pgbeg_=yes else $as_nop ac_cv_lib_pgplot_pgbeg_=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_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 { 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 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. */ char cpgbeg (); #ifdef F77_DUMMY_MAIN # ifdef __cplusplus extern "C" # endif int F77_DUMMY_MAIN() { return 1; } #endif int main (void) { return cpgbeg (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_cpgplot_cpgbeg=yes else $as_nop ac_cv_lib_cpgplot_cpgbeg=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_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 $as_nop PGPLOTLIB= fi # Only need the PGPLOT include file to build PGSBOX. if test "x$PGPLOTINC" != x; then SUBDIRS="$SUBDIRS pgsbox" INSTDIR="pgsbox" fi # Also need the PGPLOT library to build pgtest and cpgtest. if test "x$PGPLOTLIB" = x; then { 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 { 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 # Utilities are compiled last since they need the libraries. # Ways of disabling them: # configure --disable-utils # configure --enable-utils=no # Check whether --enable-utils was given. if test ${enable_utils+y} then : enableval=$enable_utils; fi 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" 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` { printf "%s\n" "$as_me:${as_lineno-$LINENO}: End of auxiliary configuration. " >&5 printf "%s\n" "$as_me: End of auxiliary configuration. " >&6;} # Set from the environment for code development. # Do it. { 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" 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, we kill variables containing newlines. # 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. ( 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=\ *) # `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 "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) | sed ' /^ac_cv_env_/b end t clear :clear 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 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 { 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 test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' 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=`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. 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 LTLIBOBJS=$ac_ltlibobjs : "${CONFIG_STATUS=./config.status}" ac_write_fail=0 ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" { 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. # 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} export SHELL _ASEOF cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh 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 # 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. 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 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 ;; 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 printf "%s\n" "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # 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 # 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 if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; 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 if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # 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 # 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 # 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 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 -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='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi # 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'" # Sed expression to map a string onto a valid variable name. 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 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 8.4, which was generated by GNU Autoconf 2.71. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ on `(hostname || uname -n) 2>/dev/null | sed 1q` " _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 || ac_write_fail=1 ac_cs_usage="\ \`$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 [OPTION]... [TAG]... -h, --help print this help, then exit -V, --version print version number and configuration settings, then exit --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 Configuration files: $config_files Configuration headers: $config_headers Report bugs to ." _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 8.4 configured by $0, generated by GNU Autoconf 2.71, with options \\"\$ac_cs_config\\" 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 || 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 ac_shift=shift ;; esac case $ac_option in # Handling of the options. -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) ac_cs_recheck=: ;; --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) 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 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 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 as_fn_error $? "ambiguous option: \`$1' Try \`$0 --help' for more information.";; --help | --hel | -h ) 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. -*) as_fn_error $? "unrecognized option: \`$1' Try \`$0 --help' for more information." ;; *) as_fn_append ac_config_targets " $1" ac_need_defaults=false ;; 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 || ac_write_fail=1 if \$ac_cs_recheck; then 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 "\$@" fi _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 printf "%s\n" "$ac_log" } >&5 _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Handling of arguments. for ac_config_target in $ac_config_targets do case $ac_config_target in "makedefs") CONFIG_FILES="$CONFIG_FILES makedefs" ;; "wcslib.pc") CONFIG_FILES="$CONFIG_FILES wcslib.pc" ;; "wcsconfig.h") CONFIG_HEADERS="$CONFIG_HEADERS wcsconfig.h" ;; "wcsconfig_f77.h") CONFIG_HEADERS="$CONFIG_HEADERS wcsconfig_f77.h" ;; "wcsconfig_tests.h") CONFIG_HEADERS="$CONFIG_HEADERS wcsconfig_tests.h" ;; "wcsconfig_utils.h") CONFIG_HEADERS="$CONFIG_HEADERS wcsconfig_utils.h" ;; *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; 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+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 # simply because there is no reason against having it here, and in addition, # creating and moving files from /tmp can sometimes cause problems. # Hook for its removal unless debugging. # Note that there is a small window in which the directory will not be cleaned: # after its creation but before its name has been assigned to `$tmp'. $debug || { tmp= ac_tmp= trap 'exit_status=$? : "${ac_tmp:=$tmp}" { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status ' 0 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 -d "$tmp" } || { tmp=./conf$$-$RANDOM (umask 077 && mkdir "$tmp") } || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 ac_tmp=$tmp # 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 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 . ./conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 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 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 = "" } { 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 } _ACAWK _ACEOF 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 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[ ]*=[ ]*/{ 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 || 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" 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*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac ac_save_IFS=$IFS IFS=: set x $ac_tag IFS=$ac_save_IFS shift ac_file=$1 shift case $ac_mode in :L) ac_source=$1;; :[FH]) ac_file_inputs= for ac_f do case $ac_f in -) 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 `:'. test -f "$ac_f" || case $ac_f in [\\/$]*) false;; *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; esac || as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; esac 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 '` printf "%s\n" "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' `' by configure.' if test x"$ac_file" != x-; then configure_input="$ac_file. $configure_input" { 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 >"$ac_tmp/stdin" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; esac ;; esac ac_dir=`$as_dirname -- "$ac_file" || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" 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"; as_fn_mkdir_p ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. 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/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix case $ac_mode in :F) # # CONFIG_FILE # case $INSTALL in [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; esac _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= ac_sed_dataroot=' /datarootdir/ { p q } /@datadir@/p /@docdir@/p /@infodir@/p /@localedir@/p /@mandir@/p' case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in *datarootdir*) ac_datarootdir_seen=yes;; *@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) { 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 || 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' ;; 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 || ac_write_fail=1 ac_sed_extra="$ac_vpsub $extrasub _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 :t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b 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 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&@abs_top_builddir@&$ac_abs_top_builddir&;t t s&@INSTALL@&$ac_INSTALL&;t t $ac_datarootdir_hack " 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' "$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 "$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 # if test x"$ac_file" != x-; then { 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 "$ac_tmp/config.h" "$ac_file" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 fi else 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 ;; esac done # for ac_tag as_fn_exit 0 _ACEOF 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. # 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 || 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 astropy-astropy-201cddb/cextern/wcslib/configure.ac000066400000000000000000000470121507226315300226240ustar00rootroot00000000000000#----------------------------------------------------------------------------- # Process this file with autoconf-2.53 or later to produce a configure script. #----------------------------------------------------------------------------- # Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. # http://www.atnf.csiro.au/people/Mark.Calabretta # $Id: configure.ac,v 8.4 2024/10/28 13:56:17 mcalabre Exp $ #----------------------------------------------------------------------------- AC_INIT([WCSLIB],[8.4],[mark@calabretta.id.au],[wcslib-8.4]) AC_PREREQ([2.71]) AC_REVISION([$Revision: 8.4 $]) AC_SUBST([PACKAGE_VERSION]) AC_DEFINE_UNQUOTED([WCSLIB_VERSION], [$PACKAGE_VERSION], [Define wcslib version]) # Library version number, same as package version. LIBVER="$PACKAGE_VERSION" AC_SUBST([LIBVER]) AC_CONFIG_SRCDIR([C/wcs.h]) AC_CONFIG_AUX_DIR([config]) # Get the system type. AC_CANONICAL_HOST ARCH="${host_cpu}-$host_os" AC_SUBST([ARCH]) # Look for Flex. 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 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 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 \ "x$ac_cv_c_const" = xno -o \ "x$ac_cv_type_size_t" = xno; then AC_MSG_ERROR([ ------------------------------------------------------- An ANSI standard C library is required to build WCSLIB. ERROR: WCSLIB configuration failure. -------------------------------------------------------], [1]) fi # Data types used in wcs.c (results not currently used). AC_TYPE_UINT16_T AC_TYPE_UINT32_T # 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 standard C header files it requires is missing or unusable. Please refer to the above log. ERROR: WCSLIB configuration failure. -------------------------------------------------------------------], [1]) fi # 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]) # System libraries that may be required by WCSLIB itself. # SunOS, extra maths functions. AC_CHECK_LIB([sunmath], [cosd], [LIBS="-lsunmath $LIBS"], [], []) # See if we can find sincos(). AC_CHECK_FUNCS([sincos]) # Check the size and availability of integer data types. AC_CHECK_SIZEOF([int]) AC_CHECK_SIZEOF([long int]) AC_CHECK_SIZEOF([long long int]) # 64-bit integer data type; use long long int preferentially since that # accords with "%lld" formatting used in fitshdr.l, e.g. # int size_t long int long long int # --- ------ -------- ------------- # gcc x86: 32 32 32 64 # gcc x86_64: 32 64 64 64 if test "x$ac_cv_sizeof_long_long_int" = x8; then AC_DEFINE([WCSLIB_INT64], [long long int], [64-bit integer data type.]) elif test "x$ac_cv_sizeof_long_int" = x8; then AC_DEFINE([WCSLIB_INT64], [long int], [64-bit integer data type.]) elif test "x$ac_cv_sizeof_int" = x8; then AC_DEFINE([WCSLIB_INT64], [int], [64-bit integer data type.]) fi # Does printf() have the z modifier for size_t type? Important for 64-bit. AC_MSG_CHECKING([for printf z format modifier for size_t type]) AC_RUN_IFELSE( [AC_LANG_PROGRAM([AC_INCLUDES_DEFAULT], [[char buf[64]; if (sprintf(buf, "%zu", (size_t)1) != 1) return 1; else if (strcmp(buf, "1")) return 2;]])], AC_DEFINE([MODZ], ["z"], [printf format modifier for size_t type.]) AC_MSG_RESULT(yes), AC_DEFINE([MODZ], [""], [printf format modifier for size_t type.]) AC_MSG_RESULT(no), AC_DEFINE([MODZ], [""], [printf format modifier for size_t type.]) AC_MSG_RESULT(assumed not) ) # Starting values, may be augmented later. SUBDIRS="C" TSTDIRS="C" INSTDIR="C" # Ways of specifying the Fortran compiler, in order of precedence: # configure --enable-fortran= # F77= configure ...bash # # Ways of disabling Fortran: # configure --disable-fortran # configure --enable-fortran=no # F77=no configure ...bash AC_ARG_ENABLE([fortran], [AS_HELP_STRING([--enable-fortran=ARG], [Fortran compiler to use])], []) AC_ARG_ENABLE([fortran], [AS_HELP_STRING([--disable-fortran], [don't build the Fortran wrappers or PGSBOX])], []) if test "x$enable_fortran" != x -a "x$enable_fortran" != xyes ; then F77="$enable_fortran" fi if test "x$F77" = xno ; then F77= AC_MSG_WARN([Compilation of Fortran wrappers and PGSBOX disabled.]) else if test "x$F77" = x ; then # Look for a Fortran compiler. AC_PROG_F77([gfortran g77 f77 ifort xlf frt pgf77 fl32 af77 fort77 f90 \ xlf90 pgf90 epcf90 f95 fort xlf95 lf95 g95]) fi if test "x$F77" = x; then AC_MSG_WARN([ ------------------------------------------------------------------ Fortran compiler not found, will skip Fortran wrappers and PGSBOX. ------------------------------------------------------------------]) # Best guess at Fortran name mangling for use if a compiler does ever # become available. AC_DEFINE([F77_FUNC(name,NAME)], [name ## _]) else if test "x$ac_cv_f77_compiler_gnu" = xyes ; then if test "x$F77" = xg77 -o "x$F77" = xf77 ; then # Not recognized by gfortran. FFLAGS="$FFLAGS -Wno-globals" fi fi AC_MSG_CHECKING(whether $F77 accepts -I) AC_LANG_PUSH(Fortran 77) FFLAGS_save=$FFLAGS FFLAGS=-I. AC_COMPILE_IFELSE(AC_LANG_PROGRAM([], []), [FFLAGS="$FFLAGS_save -I."; AC_MSG_RESULT(yes)], [FFLAGS="$FFLAGS_save"; AC_MSG_RESULT(no)]) AC_LANG_POP() # Libraries required by the Fortran compiler itself (sets FLIBS). # Required by utilities and test programs written in C that link to # 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" fi fi # System-dependent system libraries (for building the sharable library). #----------------------------------------------------------------------- # Darwin (contains stubs for long double). 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 - 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 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 fi fi AC_SUBST([SHRLIB]) AC_SUBST([SONAME]) AC_SUBST([SHRFLAGS]) AC_SUBST([SHRLD]) AC_SUBST([SHRSFX]) AC_SUBST([SHRLN]) # Installation utilities. 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. ]) # The following are required to build utilities and test programs. # ---------------------------------------------------------------- AC_MSG_NOTICE([Looking for libraries etc. for utilities and test suite...]) # 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 (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= AC_ARG_WITH([cfitsio], [AS_HELP_STRING([--without-cfitsio], [eschew CFITSIO])], []) if test "x$with_cfitsio" = xno ; then AC_MSG_WARN([CFITSIO disabled.]) else AC_ARG_WITH([cfitsiolib], [AS_HELP_STRING([--with-cfitsiolib=DIR], [directory containing cfitsio library])], []) if test "x$with_cfitsiolib" != x ; then LIBDIRS="$LIBDIRS $with_cfitsiolib" fi AC_ARG_WITH([cfitsioinc], [AS_HELP_STRING([--with-cfitsioinc=DIR], [directory containing cfitsio header files])], []) if test "x$with_cfitsioinc" != x ; then CFITSIO_INCDIRS="$with_cfitsioinc" fi CFITSIO_INCDIRS="$CFITSIO_INCDIRS \ /usr/local/cfitsio/include \ /local/cfitsio/include" LIBDIRS="$LIBDIRS \ /usr/local/cfitsio/lib \ /local/cfitsio/lib" fi AC_ARG_WITH([pgplot], [AS_HELP_STRING([--without-pgplot], [eschew PGPLOT])], []) if test "x$with_pgplot" = xno ; then AC_MSG_WARN([PGPLOT disabled.]) else AC_ARG_WITH([pgplotlib], [AS_HELP_STRING([--with-pgplotlib=DIR], [directory containing pgplot library])], []) if test "x$with_pgplotlib" != x ; then LIBDIRS="$LIBDIRS $with_pgplotlib" fi AC_ARG_WITH([pgplotinc], [AS_HELP_STRING([--with-pgplotinc=DIR], [directory containing pgplot header files])], []) if test "x$with_pgplotinc" != x ; then PGPLOT_INCDIRS="$with_pgplotinc" fi PGPLOT_INCDIRS="$PGPLOT_INCDIRS \ /usr/local/pgplot/include \ /local/pgplot/include" LIBDIRS="$LIBDIRS \ /usr/local/pgplot/lib \ /local/pgplot/lib" fi if test "x$with_cfitsio" != xno -o \ "x$with_pgplot" != xno ; then LIBDIRS="$LIBDIRS \ /usr/local/lib \ /local/lib \ /opt/local/lib \ /opt/SUNWspro/lib \ /sw/lib" for LIBDIR in $LIBDIRS ; do AC_CHECK_FILE([$LIBDIR], [LDFLAGS="$LDFLAGS -L$LIBDIR"], [continue]) done # Generic include directories. INCDIRS="/usr/local/include \ /local/include \ /opt/local/include \ /sw/include \ /local \ /usr/include" # CFITSIO. if test "x$with_cfitsio" != xno ; then # Search for CFITSIO. for INCDIR in $CFITSIO_INCDIRS $INCDIRS ; do AC_CHECK_FILE([$INCDIR/cfitsio/fitsio.h], [CFITSIOINC="-I$INCDIR/cfitsio"; break]) AC_CHECK_FILE([$INCDIR/fitsio.h], [CFITSIOINC="-I$INCDIR"; break]) done AC_CHECK_LIB([socket], [recv], [CFITSIOLIB="-lsocket"], [], [$LIBS]) AC_CHECK_LIB([cfitsio], [ffopen], [CFITSIOLIB="-lcfitsio $CFITSIOLIB"], [], [$CFITSIOLIB $LIBS]) if test "x$CFITSIOINC" = x -o "x$CFITSIOLIB" = x; then AC_MSG_WARN([CFITSIO not found, skipping CFITSIO-dependent tests.]) else AC_MSG_NOTICE([CFITSIO appears to be available.]) AC_DEFINE([HAVE_CFITSIO], [1], [Define to 1 if CFITSIO is available.]) # Check for fits_read_wcstab, present in CFITSIO 3.004beta and later. AC_CHECK_LIB([cfitsio], [fits_read_wcstab], [GETWCSTAB=], [GETWCSTAB=getwcstab.o], [$CFITSIOLIB $LIBS]) if test "x$GETWCSTAB" != x ; then AC_MSG_WARN([fits_read_wcstab not found in CFITSIO, will use getwcstab.c to compile test programs.]) fi fi AC_SUBST([CFITSIOINC]) AC_SUBST([CFITSIOLIB]) AC_SUBST([GETWCSTAB]) fi # PGPLOT. if test "x$F77" != x -a "x$with_pgplot" != xno ; then # Search for PGPLOT. for INCDIR in $PGPLOT_INCDIRS $INCDIRS ; do AC_CHECK_FILE([$INCDIR/pgplot/cpgplot.h], [PGPLOTINC="-I$INCDIR/pgplot"; break]) AC_CHECK_FILE([$INCDIR/cpgplot.h], [PGPLOTINC="-I$INCDIR"; break]) done # FLIBS (found above via AC_F77_LIBRARY_LDFLAGS) only helps if PGPLOT was # built using the same Fortran compiler that we are using here. # PGPLOT compiled by the SUN Fortran compiler but linked with something # else. AC_CHECK_LIB([M77], [iand_], [PGPLOTLIB="-lM77 $PGPLOTLIB"], [], [$PGPLOTLIB $LIBS]) AC_CHECK_LIB([F77], [f77_init], [PGPLOTLIB="-lF77 $PGPLOTLIB"], [], [$PGPLOTLIB $LIBS]) if test "x$F77" != xg77; then # For PGPLOT compiled with g77 but linked with something else. AC_CHECK_LIB([frtbegin], [main], [PGPLOTLIB="-lfrtbegin $PGPLOTLIB"], [], [$PGPLOTLIB $LIBS]) AC_CHECK_LIB([g2c], [gerror_], [PGPLOTLIB="-lg2c $PGPLOTLIB"], [], [$PGPLOTLIB $LIBS]) fi if test "x$F77" != xgfortran; then # For PGPLOT compiled with gfortran but linked with something else. # Note that if gfortran itself is driving the linker it can be harmful # to add -lgfortran to the link list without also adding -lgfortranbegin. # Doing so stops gfortran from adding -lgfortranbegin which is needed to # resolve "main". AC_CHECK_LIB([gfortran], [_gfortran_abort], [PGPLOTLIB="-lgfortran $PGPLOTLIB"], [], [$PGPLOTLIB $LIBS]) fi # Search for X11 includes and libraries. AC_PATH_X if test "x$no_x" = x; then if test "x$ac_x_libraries" != x ; then # Not needed for systems that keep the X11 libraries in /usr/lib. LDFLAGS="$LDFLAGS -L$ac_x_libraries" fi PGPLOTLIB="-lX11 $PGPLOTLIB" fi # It is possible that other libraries may be required depending on what # graphics drivers were installed with PGPLOT. AC_CHECK_LIB([z], [deflate], [PGPLOTLIB="-lz $PGPLOTLIB"], [], [$PGPLOTLIB $LIBS]) AC_CHECK_LIB([png], [png_error], [PGPLOTLIB="-lpng $PGPLOTLIB"], [], [$PGPLOTLIB $LIBS]) AC_CHECK_LIB([pgplot], [pgbeg_], [PGPLOTLIB="-lpgplot $PGPLOTLIB"], [], [$PGPLOTLIB $FLIBS $LIBS]) AC_CHECK_LIB([cpgplot], [cpgbeg], [PGPLOTLIB="-lcpgplot $PGPLOTLIB"], [PGPLOTLIB=], [$PGPLOTLIB $FLIBS $LIBS]) # Only need the PGPLOT include file to build PGSBOX. if test "x$PGPLOTINC" != x; then SUBDIRS="$SUBDIRS pgsbox" INSTDIR="pgsbox" fi # Also need the PGPLOT library to build pgtest and cpgtest. if test "x$PGPLOTLIB" = x; then AC_MSG_WARN([PGPLOT not found, skipping PGPLOT-dependent tests.]) else AC_MSG_NOTICE([PGPLOT appears to be available.]) TSTDIRS="$TSTDIRS pgsbox" fi fi AC_SUBST([PGPLOTINC]) AC_SUBST([PGPLOTLIB]) fi # Utilities are compiled last since they need the libraries. # Ways of disabling them: # configure --disable-utils # 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 AC_MSG_WARN([Compilation of WCS utilities disabled.]) else SUBDIRS="$SUBDIRS utils" INSTDIR="$INSTDIR utils" fi 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...]) AC_CONFIG_FILES([makedefs wcslib.pc]) AC_CONFIG_HEADERS([wcsconfig.h wcsconfig_f77.h wcsconfig_tests.h wcsconfig_utils.h]) AC_OUTPUT astropy-astropy-201cddb/cextern/wcslib/flavours000066400000000000000000000075231507226315300221250ustar00rootroot00000000000000#----------------------------------------------------------------------------- # Variable definitions for various combinations of architecture, operating # system, compiler, and purpose. Used for development and testing only, not # required for building WCSLIB. # # 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=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 8.4 2024/10/28 13:56:17 mcalabre Exp $ #----------------------------------------------------------------------------- F := ifeq "$(FLAVOUR)" "" F := default override FLAVOUR := default endif ifeq "$(FLAVOUR)" "dev" # Currently gcc 11.4.0. F := development CC := gcc F77 := gfortran endif ifeq "$(FLAVOUR)" "dev12" # Currently gcc 12.3.0. F := development CC := gcc-12 F77 := gfortran-12 endif # Compiler optimization level. ifndef OPT OPT := 0 endif # Quench warnings about padding in foreign structs, particularly in fitsio.h. ifneq "$(findstring $(SUBDIR),C Fortran pgsbox)" "" WPADDED := -Wpadded endif 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)" "lto" # For LTO development. F := $(FLAVOUR) 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 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 # Check FLAVOUR. ifeq "$F" "" override FLAVOUR := unrecognised endif # Check VALGRIND. ifeq "$(findstring valgrind, $(VALGRIND))" "valgrind" override MODE := interactive else # Unrecognised. override VALGRIND := endif # Check MODE. ifneq "$(MODE)" "interactive" # Unrecognised. override MODE := endif # Check EXTRA_CLEAN. ifeq "$(EXTRA_CLEAN)" "" EXTRA_CLEAN := endif # Pass to configure though the environment. export FLAVOUR export VALGRIND export MODE export EXTRA_CLEAN astropy-astropy-201cddb/cextern/wcslib/makedefs.in000066400000000000000000000231111507226315300224370ustar00rootroot00000000000000#----------------------------------------------------------------------------- # GNU makefile definitions for building WCSLIB 8.4 # # makedefs is generated from makedefs.in by configure. It contains variable # definitions and some general-purpose rules for building WCSLIB. # # Targets defined here # -------------------- # printenv: Print the environment as seen within makefile rules. # show: Print the values of all makefile variables used. # # Notes: # 1) If you need to make changes then it may be preferable to modify # makedefs.in (not makedefs). The makefile will detect this and # automatically re-run config.status to regenerate makedefs. # # 2) There are three choices for trigd functions - cosd(), sind(), tand(), # acosd(), asind(), atand(), and atan2d(), made by setting WCSTRIG: # # 1: Use the wrapper functions supplied with WCSLIB (default): # WCSTRIG := WRAPPER # # 2: Use native trigd functions supplied in a mathematics library such # as libsunmath (you will also need to add the library to the LIBS # variable below): # WCSTRIG := NATIVE # # 3: Use C preprocessor macro implementations of the trigd functions # (this method is typically 20% faster but may lead to rounding # errors near the poles): # WCSTRIG := MACRO # # 3) Variables for creating the shared (dynamic) library are currently # only set by 'configure' if the GNU C compiler is used. However, # you can set these variables by hand, preferably in makedefs.in. # # Shared libraries require position-independent code (PIC) which imposes # a performance overhead. Consequently the static libraries are # compiled separately without this option. # # The shared library will be installed with version number, e.g. as # libwcs.so.8.4 or libwcs.8.4.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.8.4. # # 4) PGPLOT is Tim Pearson's Fortran graphics library with separate C # interface available from astro.caltech.edu. It is only required by # one utility, wcsgrid, and the test programs that plot test grids # (tprj2, tcel1, tcel2, tspc, ttab2, ttab3, twcsmix, and tpih2). You can # skip these by setting PGPLOTLIB to blank. # # It is difficult for configure to deduce what auxiliary graphics # libraries may be needed for PGPLOT since it depends on which of many # possible graphics drivers were selected when PGPLOT was installed. # Therefore it is quite likely that you will need to add additional # libraries to PGPLOTLIB. # # 5) CFITSIO is Bill Pence's FITS I/O library written in C with Fortran # wrappers, available from http://heasarc.gsfc.nasa.gov/fitsio. # # CFITSIO is required by three utilities, HPXcvt, wcsgrid, and wcsware, # and also by the test programs twcstab and twcshdr. wcsware and the # test programs use fits_read_wcstab() which is implemented by # getwcstab.c. However, this implementation is included in CFITSIO post # 3.004beta, so getwcstab.c is required here only for older releases # (controlled by variable GETWCSTAB). getwcstab.o itself is not inserted # into the WCSLIB object library. # # If available, CFITSIO is also optionally used for test programs # tfitshdr, tbth1, tpih1 and tpih2 by setting preprocessor macro # -DDO_CFITSIO. # # Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. # http://www.atnf.csiro.au/people/Mark.Calabretta # $Id: makedefs.in,v 8.4 2024/10/28 13:56:17 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@ # C preprocessor and options. CPP := @CPP@ CPPFLAGS := @CPPFLAGS@ WCSTRIG := WRAPPER # C compiler and options. CC := @CC@ CFLAGS := @CFLAGS@ # Fortran compiler and options. 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). SHRLIB := @SHRLIB@ SONAME := @SONAME@ SHRFLAGS := @SHRFLAGS@ SHRLD := @SHRLD@ SHRLN := @SHRLN@ # What subdirectories to build. SUBDIRS := @SUBDIRS@ TSTDIRS := @TSTDIRS@ # Top of the 'make install' hierarchy: pgsbox -> Fortran -> C. INSTDIR := @INSTDIR@ # Installation utilities and locations. LN_S := @LN_S@ INSTALL := @INSTALL@ # Needed for the definitions provided by autoconf. prefix := @prefix@ exec_prefix := @exec_prefix@ datarootdir := @datarootdir@ PACKAGE_TARNAME := @PACKAGE_TARNAME@ docdir := @docdir@ LIBDIR := $(DESTDIR)@libdir@ BINDIR := $(DESTDIR)@bindir@ INCDIR := $(DESTDIR)@includedir@/wcslib-$(LIBVER) INCLINK := $(DESTDIR)@includedir@/wcslib DOCDIR := $(DESTDIR)@docdir@ DOCLINK := $(dir $(DESTDIR)@docdir@)wcslib HTMLDIR := $(DESTDIR)@htmldir@ PDFDIR := $(DESTDIR)@pdfdir@ MANDIR := $(DESTDIR)@mandir@ # For putting timestamps in the build log. TIMER := date +"%a %Y/%m/%d %X %z, executing on $$HOST" # The remaining options are for building utilities and test programs. # ------------------------------------------------------------------- # Linker options (use CC for linking). LD = $(CC) LDFLAGS := @LDFLAGS@ # PGPLOT (see note 4 above). PGPLOTINC := @PGPLOTINC@ PGPLOTLIB := @PGPLOTLIB@ # CFITSIO (see note 5 above). CFITSIOINC := @CFITSIOINC@ CFITSIOLIB := @CFITSIOLIB@ GETWCSTAB := @GETWCSTAB@ # Libraries required by the above Fortran compiler. FLIBS := @FLIBS@ # 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. #----------------------------------------------------------------------------- SHELL := /bin/sh VPATH := .. # Common targets. .PHONY : all build FORCE printenv show all : show -@ echo '' @ $(MAKE) build FORCE : # Print the environment as seen by makefile rules. printenv : -@ printenv | sort # Print variable definitions. show :: wcsconfig.h -@ echo '' -@ uname -a -@ echo '' -@ $(MAKE) --version | head -1 -@ echo ' SUBDIR := $(SUBDIR)' -@ echo ' MAKEFLAGS := $(MAKEFLAGS)' -@ echo '' -@ echo 'For building and installing $(WCSLIBPKG)...' -@ echo ' ARCH := $(ARCH)' -@ echo ' FLEX := $(FLEX)' -@ echo ' FLFLAGS := $(FLFLAGS)' -@ echo ' CPP := $(CPP)' -@ 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)' -@ echo ' SHRFLAGS := $(SHRFLAGS)' -@ echo ' SHRLD := $(SHRLD)' -@ echo ' SHRLN := $(SHRLN)' -@ echo ' LN_S := $(LN_S)' -@ echo ' INSTALL := $(INSTALL)' -@ echo ' LIBDIR := $(LIBDIR)' -@ echo ' BINDIR := $(BINDIR)' -@ echo ' INCDIR := $(INCDIR)' -@ echo ' INCLINK := $(INCLINK)' -@ echo ' DOCDIR := $(DOCDIR)' -@ echo ' DOCLINK := $(DOCLINK)' -@ echo ' HTMLDIR := $(HTMLDIR)' -@ echo ' PDFDIR := $(PDFDIR)' -@ echo ' MANDIR := $(MANDIR)' -@ echo ' TIMER := $(TIMER)' -@ echo '' -@ echo 'Important wcsconfig.h defines...' -@ echo " `grep HAVE_SINCOS $<`" -@ echo " `grep WCSLIB_INT64 $<`" -@ echo '' -@ echo 'To build utilities and test programs...' -@ echo ' LD := $(LD)' -@ echo ' LDFLAGS := $(LDFLAGS)' -@ echo ' PGPLOTINC := $(PGPLOTINC)' -@ echo ' PGPLOTLIB := $(PGPLOTLIB)' -@ echo ' CFITSIOINC := $(CFITSIOINC)' -@ echo ' CFITSIOLIB := $(CFITSIOLIB)' -@ echo ' GETWCSTAB := $(GETWCSTAB)' -@ 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 ifneq "$(FLAVOUR)" "default" TIMER := $(TIMER) ; echo " with $(FLAVOUR) FLAVOUR." endif astropy-astropy-201cddb/cextern/wcslib/makelog000066400000000000000000000302541507226315300217000ustar00rootroot00000000000000 Linux horus 5.4.0-77-generic #86-Ubuntu SMP Thu Jun 17 02:35:03 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux GNU Make 4.2.1 SUBDIR := . MAKEFLAGS := -Otarget For building and installing wcslib-7.6... ARCH := x86_64-linux-gnu FLEX := flex FLFLAGS := -D_POSIX_C_SOURCE=1 CPP := gcc -E CPPFLAGS := -DHAVE_CONFIG_H WCSTRIG := WRAPPER CC := gcc CFLAGS := -g -O2 FC := gfortran FFLAGS := -g -O2 -I. WCSLIB := libwcs-7.6.a ARFLAGS := U RANLIB := ranlib SHRLIB := libwcs.so.7.6 SONAME := libwcs.so.7 SHRFLAGS := -fPIC SHRLD := gcc -fPIC -shared -Wl,-hlibwcs.so.7 SHRLN := libwcs.so LN_S := ln -s INSTALL := /usr/bin/install -c LIBDIR := /usr/local/lib BINDIR := /usr/local/bin INCDIR := /usr/local/include/wcslib-7.6 INCLINK := /usr/local/include/wcslib DOCDIR := /usr/local/share/doc/wcslib-7.6 DOCLINK := /usr/local/share/doc/wcslib HTMLDIR := /usr/local/share/doc/wcslib-7.6 PDFDIR := /usr/local/share/doc/wcslib-7.6 MANDIR := /usr/local/share/man TIMER := date +"%a %Y/%m/%d %X %z, executing on $HOST" Important wcsconfig.h defines... #define HAVE_SINCOS 1 #define WCSLIB_INT64 long long int To build utilities and test programs... LD := gcc LDFLAGS := -L/usr/local/lib PGPLOTINC := -I/usr/local/include/pgplot PGPLOTLIB := -lcpgplot -lpgplot -lpng -lz -lX11 CFITSIOINC := -I/usr/local/include/cfitsio CFITSIOLIB := -lcfitsio GETWCSTAB := FLIBS := -L/lib -L/lib/x86_64-linux-gnu -L/usr/lib -L/usr/lib/gcc/x86_64-linux-gnu/9 -L/usr/lib/x86_64-linux-gnu -lgfortran -lm -lquadmath LIBS := -lm Default observer coordinates for sundazel... OBSLNG := +151.18130 OBSLAT := -33.85865 OBSTZ := +10.0 Subdirectories to be built... SUBDIRS := C Fortran pgsbox utils TSTDIRS := C Fortran pgsbox For code development... FLAVOURS := "" FLAVOUR := MODE := VALGRIND := EXTRA_CLEAN := make[1]: Entering directory '/home/mcalabre/WCS/wcslib-7.6' Mon 2021/07/12 14:15:45 +1000, executing on horus make[2]: Entering directory '/home/mcalabre/WCS/wcslib-7.6/C' flex -D_POSIX_C_SOURCE=1 -t wcsutrn.l | sed -e 's/^[ ]*#/#/' > wcsutrn.c flex -D_POSIX_C_SOURCE=1 -t wcspih.l | sed -e 's/^[ ]*#/#/' > wcspih.c flex -D_POSIX_C_SOURCE=1 -t fitshdr.l | sed -e 's/^[ ]*#/#/' > fitshdr.c flex -D_POSIX_C_SOURCE=1 -t wcsbth.l | sed -e 's/^[ ]*#/#/' > wcsbth.c flex -D_POSIX_C_SOURCE=1 -t wcsulex.l | sed -e 's/^[ ]*#/#/' > wcsulex.c Building WCSLIB C library... gcc -DHAVE_CONFIG_H -I. -I.. -g -O2 -c cel.c gcc -DHAVE_CONFIG_H -I. -I.. -g -O2 -c dis.c gcc -DHAVE_CONFIG_H -I. -I.. -g -O2 -c fitshdr.c gcc -DHAVE_CONFIG_H -I. -I.. -g -O2 -c lin.c gcc -DHAVE_CONFIG_H -I. -I.. -g -O2 -c log.c gcc -DHAVE_CONFIG_H -I. -I.. -g -O2 -c prj.c gcc -DHAVE_CONFIG_H -I. -I.. -g -O2 -c spc.c gcc -DHAVE_CONFIG_H -I. -I.. -g -O2 -c sph.c gcc -DHAVE_CONFIG_H -I. -I.. -g -O2 -c spx.c gcc -DHAVE_CONFIG_H -I. -I.. -g -O2 -c tab.c gcc -DHAVE_CONFIG_H -I. -I.. -g -O2 -c wcs.c gcc -DHAVE_CONFIG_H -I. -I.. -g -O2 -c wcsbth.c gcc -DHAVE_CONFIG_H -I. -I.. -g -O2 -c wcserr.c gcc -DHAVE_CONFIG_H -I. -I.. -g -O2 -c wcsfix.c gcc -DHAVE_CONFIG_H -I. -I.. -g -O2 -c wcshdr.c gcc -DHAVE_CONFIG_H -I. -I.. -g -O2 -c wcspih.c gcc -DHAVE_CONFIG_H -I. -I.. -g -O2 -c wcsprintf.c gcc -DHAVE_CONFIG_H -I. -I.. -g -O2 -c wcstrig.c gcc -DHAVE_CONFIG_H -I. -I.. -g -O2 -c wcsulex.c gcc -DHAVE_CONFIG_H -I. -I.. -g -O2 -c wcsunits.c gcc -DHAVE_CONFIG_H -I. -I.. -g -O2 -c wcsutil.c gcc -DHAVE_CONFIG_H -I. -I.. -g -O2 -c wcsutrn.c ar rU libwcs-7.6.a cel.o dis.o fitshdr.o lin.o log.o prj.o spc.o sph.o spx.o tab.o wcs.o wcsbth.o wcserr.o wcsfix.o wcshdr.o wcspih.o wcsprintf.o wcstrig.o wcsulex.o wcsunits.o wcsutil.o wcsutrn.o ar: creating libwcs-7.6.a ranlib libwcs-7.6.a gcc -DHAVE_CONFIG_H -I. -I.. -g -O2 -fPIC -c cel.c gcc -DHAVE_CONFIG_H -I. -I.. -g -O2 -fPIC -c dis.c gcc -DHAVE_CONFIG_H -I. -I.. -g -O2 -fPIC -c fitshdr.c gcc -DHAVE_CONFIG_H -I. -I.. -g -O2 -fPIC -c lin.c gcc -DHAVE_CONFIG_H -I. -I.. -g -O2 -fPIC -c log.c gcc -DHAVE_CONFIG_H -I. -I.. -g -O2 -fPIC -c prj.c gcc -DHAVE_CONFIG_H -I. -I.. -g -O2 -fPIC -c spc.c gcc -DHAVE_CONFIG_H -I. -I.. -g -O2 -fPIC -c sph.c gcc -DHAVE_CONFIG_H -I. -I.. -g -O2 -fPIC -c spx.c gcc -DHAVE_CONFIG_H -I. -I.. -g -O2 -fPIC -c tab.c gcc -DHAVE_CONFIG_H -I. -I.. -g -O2 -fPIC -c wcs.c gcc -DHAVE_CONFIG_H -I. -I.. -g -O2 -fPIC -c wcsbth.c gcc -DHAVE_CONFIG_H -I. -I.. -g -O2 -fPIC -c wcserr.c gcc -DHAVE_CONFIG_H -I. -I.. -g -O2 -fPIC -c wcsfix.c gcc -DHAVE_CONFIG_H -I. -I.. -g -O2 -fPIC -c wcshdr.c gcc -DHAVE_CONFIG_H -I. -I.. -g -O2 -fPIC -c wcspih.c gcc -DHAVE_CONFIG_H -I. -I.. -g -O2 -fPIC -c wcsprintf.c gcc -DHAVE_CONFIG_H -I. -I.. -g -O2 -fPIC -c wcstrig.c gcc -DHAVE_CONFIG_H -I. -I.. -g -O2 -fPIC -c wcsulex.c gcc -DHAVE_CONFIG_H -I. -I.. -g -O2 -fPIC -c wcsunits.c gcc -DHAVE_CONFIG_H -I. -I.. -g -O2 -fPIC -c wcsutil.c gcc -DHAVE_CONFIG_H -I. -I.. -g -O2 -fPIC -c wcsutrn.c ar rU libwcs-PIC.a cel.o dis.o fitshdr.o lin.o log.o prj.o spc.o sph.o spx.o tab.o wcs.o wcsbth.o wcserr.o wcsfix.o wcshdr.o wcspih.o wcsprintf.o wcstrig.o wcsulex.o wcsunits.o wcsutil.o wcsutrn.o ar: creating libwcs-PIC.a mkdir tmp && \ cd tmp && \ trap 'cd .. ; rm -f -r tmp' 0 1 2 3 15 ; \ ar x ../libwcs-PIC.a && \ gcc -fPIC -shared -Wl,-hlibwcs.so.7 -o libwcs.so.7.6 *.o -L/usr/local/lib -lm && \ mv libwcs.so.7.6 .. make[2]: Leaving directory '/home/mcalabre/WCS/wcslib-7.6/C' Mon 2021/07/12 14:16:10 +1000, executing on horus make[2]: Entering directory '/home/mcalabre/WCS/wcslib-7.6/Fortran' make[3]: Entering directory '/home/mcalabre/WCS/wcslib-7.6/C' Building WCSLIB C library... make[3]: Leaving directory '/home/mcalabre/WCS/wcslib-7.6/C' Building WCSLIB Fortran wrappers... gcc -DHAVE_CONFIG_H -I.. -I../C -g -O2 -c cel_f.c gcc -DHAVE_CONFIG_H -I.. -I../C -g -O2 -c dis_f.c gcc -DHAVE_CONFIG_H -I.. -I../C -g -O2 -c fitshdr_f.c gcc -DHAVE_CONFIG_H -I.. -I../C -g -O2 -c lin_f.c gcc -DHAVE_CONFIG_H -I.. -I../C -g -O2 -c log_f.c gcc -DHAVE_CONFIG_H -I.. -I../C -g -O2 -c prj_f.c gcc -DHAVE_CONFIG_H -I.. -I../C -g -O2 -c spc_f.c gcc -DHAVE_CONFIG_H -I.. -I../C -g -O2 -c sph_f.c gcc -DHAVE_CONFIG_H -I.. -I../C -g -O2 -c spx_f.c gcc -DHAVE_CONFIG_H -I.. -I../C -g -O2 -c tab_f.c gcc -DHAVE_CONFIG_H -I.. -I../C -g -O2 -c wcs_f.c gcc -DHAVE_CONFIG_H -I.. -I../C -g -O2 -c wcserr_f.c gcc -DHAVE_CONFIG_H -I.. -I../C -g -O2 -c wcsfix_f.c gcc -DHAVE_CONFIG_H -I.. -I../C -g -O2 -c wcshdr_f.c gcc -DHAVE_CONFIG_H -I.. -I../C -g -O2 -c wcsunits_f.c gfortran -g -O2 -I. -c cel_data.f gfortran -g -O2 -I. -c dis_data.f gfortran -g -O2 -I. -c fitshdr_data.f gfortran -g -O2 -I. -c lin_data.f gfortran -g -O2 -I. -c log_data.f gfortran -g -O2 -I. -c prj_data.f gfortran -g -O2 -I. -c spc_data.f gfortran -g -O2 -I. -c spx_data.f gfortran -g -O2 -I. -c tab_data.f gfortran -g -O2 -I. -c wcs_data.f gfortran -g -O2 -I. -c wcsfix_data.f gfortran -g -O2 -I. -c wcshdr_data.f gfortran -g -O2 -I. -c wcsunits_data.f ar rU ../C/libwcs-7.6.a cel_data.o cel_f.o dis_data.o dis_f.o fitshdr_data.o fitshdr_f.o lin_data.o lin_f.o log_data.o log_f.o prj_data.o prj_f.o spc_data.o spc_f.o sph_f.o spx_data.o spx_f.o tab_data.o tab_f.o wcs_data.o wcs_f.o wcserr_f.o wcsfix_data.o wcsfix_f.o wcshdr_data.o wcshdr_f.o wcsunits_data.o wcsunits_f.o ranlib ../C/libwcs-7.6.a gcc -DHAVE_CONFIG_H -I.. -I../C -g -O2 -fPIC -c cel_f.c gcc -DHAVE_CONFIG_H -I.. -I../C -g -O2 -fPIC -c dis_f.c gcc -DHAVE_CONFIG_H -I.. -I../C -g -O2 -fPIC -c fitshdr_f.c gcc -DHAVE_CONFIG_H -I.. -I../C -g -O2 -fPIC -c lin_f.c gcc -DHAVE_CONFIG_H -I.. -I../C -g -O2 -fPIC -c log_f.c gcc -DHAVE_CONFIG_H -I.. -I../C -g -O2 -fPIC -c prj_f.c gcc -DHAVE_CONFIG_H -I.. -I../C -g -O2 -fPIC -c spc_f.c gcc -DHAVE_CONFIG_H -I.. -I../C -g -O2 -fPIC -c sph_f.c gcc -DHAVE_CONFIG_H -I.. -I../C -g -O2 -fPIC -c spx_f.c gcc -DHAVE_CONFIG_H -I.. -I../C -g -O2 -fPIC -c tab_f.c gcc -DHAVE_CONFIG_H -I.. -I../C -g -O2 -fPIC -c wcs_f.c gcc -DHAVE_CONFIG_H -I.. -I../C -g -O2 -fPIC -c wcserr_f.c gcc -DHAVE_CONFIG_H -I.. -I../C -g -O2 -fPIC -c wcsfix_f.c gcc -DHAVE_CONFIG_H -I.. -I../C -g -O2 -fPIC -c wcshdr_f.c gcc -DHAVE_CONFIG_H -I.. -I../C -g -O2 -fPIC -c wcsunits_f.c gfortran -g -O2 -I. -fPIC -c cel_data.f gfortran -g -O2 -I. -fPIC -c dis_data.f gfortran -g -O2 -I. -fPIC -c fitshdr_data.f gfortran -g -O2 -I. -fPIC -c lin_data.f gfortran -g -O2 -I. -fPIC -c log_data.f gfortran -g -O2 -I. -fPIC -c prj_data.f gfortran -g -O2 -I. -fPIC -c spc_data.f gfortran -g -O2 -I. -fPIC -c spx_data.f gfortran -g -O2 -I. -fPIC -c tab_data.f gfortran -g -O2 -I. -fPIC -c wcs_data.f gfortran -g -O2 -I. -fPIC -c wcsfix_data.f gfortran -g -O2 -I. -fPIC -c wcshdr_data.f gfortran -g -O2 -I. -fPIC -c wcsunits_data.f ar rU ../C/libwcs-PIC.a cel_data.o cel_f.o dis_data.o dis_f.o fitshdr_data.o fitshdr_f.o lin_data.o lin_f.o log_data.o log_f.o prj_data.o prj_f.o spc_data.o spc_f.o sph_f.o spx_data.o spx_f.o tab_data.o tab_f.o wcs_data.o wcs_f.o wcserr_f.o wcsfix_data.o wcsfix_f.o wcshdr_data.o wcshdr_f.o wcsunits_data.o wcsunits_f.o mkdir tmp && \ cd tmp && \ trap 'cd .. ; rm -f -r tmp' 0 1 2 3 15 ; \ ar x ../../C/libwcs-PIC.a && \ gcc -fPIC -shared -Wl,-hlibwcs.so.7 -o libwcs.so.7.6 *.o -L/usr/local/lib -lm && \ mv libwcs.so.7.6 ../../C make[2]: Leaving directory '/home/mcalabre/WCS/wcslib-7.6/Fortran' Mon 2021/07/12 14:16:13 +1000, executing on horus make[2]: Entering directory '/home/mcalabre/WCS/wcslib-7.6/pgsbox' Building PGSBOX library... gfortran -I../Fortran -g -O2 -I. -c pgsbox.f gcc -DHAVE_CONFIG_H -I. -I.. -I../C -g -O2 -c cpgsbox.c gcc -DHAVE_CONFIG_H -I. -I.. -I../C -g -O2 -c pgwcsl.c gfortran -I../Fortran -g -O2 -I. -c pgcrfn.f ar rU libpgsbox-7.6.a cpgsbox.o pgcrfn.o pgsbox.o pgwcsl.o ar: creating libpgsbox-7.6.a ranlib libpgsbox-7.6.a gfortran -I../Fortran -g -O2 -I. -fPIC -c pgsbox.f gcc -DHAVE_CONFIG_H -I. -I.. -I../C -g -O2 -fPIC -c cpgsbox.c gcc -DHAVE_CONFIG_H -I. -I.. -I../C -g -O2 -fPIC -c pgwcsl.c gfortran -I../Fortran -g -O2 -I. -fPIC -c pgcrfn.f ar rU libpgsbox-PIC.a cpgsbox.o pgcrfn.o pgsbox.o pgwcsl.o ar: creating libpgsbox-PIC.a mkdir tmp && \ cd tmp && \ trap 'cd .. ; rm -f -r tmp' 0 1 2 3 15 ; \ ar x ../libpgsbox-PIC.a && \ gcc -fPIC -shared -Wl,-hlibpgsbox.so.7 -o libpgsbox.so.7.6 *.o ../../C/libwcs.so.7.6 -L/usr/local/lib -lcpgplot -lpgplot -lpng -lz -lX11 -L/lib -L/lib/x86_64-linux-gnu -L/usr/lib -L/usr/lib/gcc/x86_64-linux-gnu/9 -L/usr/lib/x86_64-linux-gnu -lgfortran -lm -lquadmath && \ mv libpgsbox.so.7.6 .. make[2]: Leaving directory '/home/mcalabre/WCS/wcslib-7.6/pgsbox' Mon 2021/07/12 14:16:15 +1000, executing on horus make[2]: Entering directory '/home/mcalabre/WCS/wcslib-7.6/utils' gcc -DHAVE_CONFIG_H -I.. -I../C -g -O2 -L/usr/local/lib -o tofits tofits.c gcc -DHAVE_CONFIG_H -I.. -I../C -g -O2 -c -o fitshdr.o fitshdr.c gcc -o fitshdr fitshdr.o -L/usr/local/lib -lm gcc -DHAVE_CONFIG_H -I.. -I../C -DOBSLNG=+151.18130 -DOBSLAT=-33.85865 -DOBSTZ=+10.0 -g -O2 -c -o sundazel.o sundazel.c gcc -o sundazel sundazel.o -L/usr/local/lib -lm gcc -DHAVE_CONFIG_H -I.. -I../C -I/usr/local/include/cfitsio -g -O2 -c -o HPXcvt.o HPXcvt.c gcc -o HPXcvt HPXcvt.o -L/usr/local/lib -lcfitsio -lm gcc -DDO_CFITSIO -DHAVE_CONFIG_H -I.. -I../C -I../pgsbox -I../C -I/usr/local/include/cfitsio \ -g -O2 -c -o wcsware.o wcsware.c gcc -o wcsware wcsware.o -L/usr/local/lib -lcfitsio ../C/libwcs-7.6.a \ -L/lib -L/lib/x86_64-linux-gnu -L/usr/lib -L/usr/lib/gcc/x86_64-linux-gnu/9 -L/usr/lib/x86_64-linux-gnu -lgfortran -lm -lquadmath -lm gcc -DDO_CFITSIO -DHAVE_CONFIG_H -I.. -I../C -I../pgsbox -I../C -I/usr/local/include/pgplot \ -I/usr/local/include/cfitsio -g -O2 -c -o wcsgrid.o wcsgrid.c gcc -o wcsgrid wcsgrid.o -L/usr/local/lib ../pgsbox/libpgsbox-7.6.a \ -lcpgplot -lpgplot -lpng -lz -lX11 -lcfitsio ../C/libwcs-7.6.a -L/lib -L/lib/x86_64-linux-gnu -L/usr/lib -L/usr/lib/gcc/x86_64-linux-gnu/9 -L/usr/lib/x86_64-linux-gnu -lgfortran -lm -lquadmath -lm make[2]: Leaving directory '/home/mcalabre/WCS/wcslib-7.6/utils' make[1]: Leaving directory '/home/mcalabre/WCS/wcslib-7.6' astropy-astropy-201cddb/cextern/wcslib/wcsconfig.h.in000066400000000000000000000013251507226315300230730ustar00rootroot00000000000000/*============================================================================ * wcsconfig.h is generated from wcsconfig.h.in by 'configure'. It contains * C preprocessor macro definitions for compiling WCSLIB 8.4 * * Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. * http://www.atnf.csiro.au/people/Mark.Calabretta * $Id: wcsconfig.h.in,v 8.4 2024/10/28 13:56:17 mcalabre Exp $ *===========================================================================*/ // 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. #undef HAVE_SINCOS // 64-bit integer data type. #undef WCSLIB_INT64 astropy-astropy-201cddb/cextern/wcslib/wcsconfig_f77.h.in000066400000000000000000000020621507226315300235550ustar00rootroot00000000000000/*============================================================================ * wcsconfig_f77.h is generated from wcsconfig_f77.h.in by 'configure'. It * contains C preprocessor definitions for building the WCSLIB 8.4 Fortran * wrappers. * * Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. * http://www.atnf.csiro.au/people/Mark.Calabretta * $Id: wcsconfig_f77.h.in,v 8.4 2024/10/28 13:56:17 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. 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. #undef F77_FUNC astropy-astropy-201cddb/cextern/wcslib/wcsconfig_tests.h.in000066400000000000000000000012551507226315300243170ustar00rootroot00000000000000/*============================================================================ * wcsconfig_test.h is generated from wcsconfig_test.h.in by 'configure'. It * contains C preprocessor definitions for compiling the WCSLIB 8.4 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 8.4 2024/10/28 13:56:17 mcalabre Exp $ *===========================================================================*/ #include // Define to 1 if the CFITSIO library is available. #undef HAVE_CFITSIO // Define to the printf format modifier for size_t type. #undef MODZ astropy-astropy-201cddb/cextern/wcslib/wcsconfig_utils.h.in000066400000000000000000000024011507226315300243070ustar00rootroot00000000000000/*============================================================================ * wcsconfig_utils.h is generated from wcsconfig_utils.h.in by 'configure'. * It contains C preprocessor macro definitions for compiling the WCSLIB 8.4 * utilities. * * Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. * http://www.atnf.csiro.au/people/Mark.Calabretta * $Id: wcsconfig_utils.h.in,v 8.4 2024/10/28 13:56:17 mcalabre Exp $ *===========================================================================*/ #include // 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). */ #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 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 // 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. #undef _LARGE_FILES astropy-astropy-201cddb/cextern/wcslib/wcslib.pc.in000066400000000000000000000004141507226315300225450ustar00rootroot00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@/wcslib Name: WCSLIB Description: An implementation of the FITS World Coordinate System standard Version: @PACKAGE_VERSION@ Requires: Libs: -L${libdir} -lwcs -lm Cflags: -I${includedir} astropy-astropy-201cddb/codecov.yml000066400000000000000000000003641507226315300175470ustar00rootroot00000000000000comment: 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% astropy-astropy-201cddb/conftest.py000066400000000000000000000055321507226315300176030ustar00rootroot00000000000000# 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 tempfile from pathlib import Path 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): 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)) # Make sure we use temporary directories for the config and cache # so that the tests are insensitive to local configuration. os.environ["XDG_CONFIG_HOME"] = tempfile.mkdtemp("astropy_config") os.environ["XDG_CACHE_HOME"] = tempfile.mkdtemp("astropy_cache") Path(os.environ["XDG_CONFIG_HOME"]).joinpath("astropy").mkdir() Path(os.environ["XDG_CACHE_HOME"]).joinpath("astropy").mkdir() # Note that we don't need to change the environment variables back or remove # them after testing, because they are only changed for the duration of the # Python process, and this configuration only matters if running pytest # directly, not from e.g. an IPython session. astropy-astropy-201cddb/docs/000077500000000000000000000000001507226315300163275ustar00rootroot00000000000000astropy-astropy-201cddb/docs/Makefile000066400000000000000000000107271507226315300177760ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest #This is needed with git because git doesn't create a dir if it's empty $(shell [ -d "_static" ] || mkdir -p _static) help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " text to make text files" @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" clean: -rm -rf $(BUILDDIR) -rm -rf api -rm -rf generated html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Astropy.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Astropy.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/Astropy" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Astropy" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." make -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: @echo "Run 'pytest' in the root directory to run doctests " \ @echo "in the documentation." astropy-astropy-201cddb/docs/_pkgtemplate.rst000066400000000000000000000047061507226315300215440ustar00rootroot00000000000000**************************************************** A description of the package (`astropy.packagename`) **************************************************** 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. Introduction ============ Include general content that might be useful for understanding the package here, as well as general scientific or mathematical background that might be necessary for the "big-picture" of this package. Getting Started =============== Short tutorial-like examples of how to do common-tasks - should be fairly quick, with any more detailed examples in the next section. Using `packagename` =================== .. THIS SECTION SHOULD BE EITHER This section is for the detailed documentation. For simpler packages, this should either by paragraphs or sub-divided into sub-sections like: Sub-topic 1 ----------- Content if needed A Complex example ----------------- Content if needed Sub-sub topic 1 ^^^^^^^^^^^^^^^^ Content if needed (note the use of ^^^^ at this level). Sub-sub-sub topic 1 """"""""""""""""""" Content if needed (note the use of """"" at this level). This is probably the deepest level that is practical. However, just in case, the next levels of detail should use the +, :, and ~ characters respectively. .. OR IF MORE COMPLICATED, For more complicated packages that require multiple documents, this should just be a table of contents referencing those documents: .. toctree:: subdoc1 subdoc2 subdoc3 Either a toctree or sub-sections should be used, *not* both. For example, if your toctree looks like the above example, this document should be ``docs/packagename/index.rst``, and the other documents should be ``docs/packagename/subdoc1.rst``, ``docs/packagename/subdoc2.rst``, and ``docs/packagename/subdoc3.rst``. In the "more complicated" case of using ``subdoc.rst`` files, each of those should likewise use the section character header order of ``* = - ^ " + : ~``. See Also (optional) =================== Include here any references to related packages, articles, or texts. Reference/API ============= .. automodapi:: packagename Acknowledgments and Licenses (optional) ======================================= Any acknowledgements or licenses needed for this package - remove the section if none are necessary. astropy-astropy-201cddb/docs/_static/000077500000000000000000000000001507226315300177555ustar00rootroot00000000000000astropy-astropy-201cddb/docs/_static/astropy.css000066400000000000000000000012441507226315300221710ustar00rootroot00000000000000/* Main page overview cards */ .sd-card .sd-card-img-top { height: 52px; width: 52px; margin-left: auto; margin-right: auto; margin-top: 10px; } /* Dark theme tweaking */ html[data-theme=dark] .sd-card img[src*='.svg'] { filter: invert(0.82) brightness(0.8) contrast(1.2); } /* Flip the colours on graphviz graphs on dark mode */ html[data-theme="dark"] div.graphviz > object.inheritance { filter: invert(0.82) brightness(0.8) contrast(1.2); } html[data-theme="dark"] div.graphviz > object.graphviz { filter: invert(0.82) brightness(0.8) contrast(1.2); } html[data-theme="dark"] ul.cooframelegend { filter: invert(0.82) brightness(0.8) contrast(1.2); } astropy-astropy-201cddb/docs/_static/astropy_banner.svg000066400000000000000000000527411507226315300235350ustar00rootroot00000000000000 astropy-astropy-201cddb/docs/_static/astropy_banner_96.png000066400000000000000000000643241507226315300240400ustar00rootroot00000000000000‰PNG  IHDRå`#:jŊgAMAą üasRGBŽÎébKGD˙˙˙ Ŋ§“ pHYs × ×B(›x vpAg `rügģIDATxÚíwœ$EŲĮŋÕ3ģ{ˇw{9įãā@‘ŗdQ‰ fE@A@AYD%‰¯‚=X $A2‚ä;sŪ0ĶõūņÔ3]ĶĶ3;ģˇÔ~î3×ŗ=Օģ~õÄ2ü‡’ŊšŨ$ÆO^1á×7ukRJ)Ĩ”RJЏ6[˛7ęWc‹Á`Ü_‹Ĩ<Ŋ]š/Æy4đC—z %SČ B,ÖĀ|cSˇ8Ĩ”RJ)Ĩ˙EÚ,AŲūš2ã lą„ä€!ĀB 0´’!OK7 ĐBĀZ˛äčKž5_k@@ƕ2Ë䍿ôMŨ#)Ĩ”RJ)ũ/ĐfĘöWîK´ƒČåf[€î ÖĐl ŒÆ0 zu@ ō@+°X ĖĮō0˜ŧƒaĻK')×7yfŖĖ›ē—Rhllėđ3MMM›ēÚ)Ĩ”RJíRvSWĀŪāžäPH„ã€ŌĐ@wö>J–Ũ°ėŒDuÅļC›‹Ãˆøđ,oĪO’å, €f`8r„ŦŊAž2gnę^KÉ#Uc$Q¸Š+—RJ)ĨÔÚdœ˛ũĨ˙`ČR‡%,ŊũŖ0 Œ"2؊ôÁō1T\œëÁtģTŸ×g"n\ķ] ü¸ƒ€g°´’B2îš‚Ņ˜9kSõŪ˙6•ã”Sn8Ĩ”RúO§Ęö:âĐ*`\%'ûu Į cåhC×@AŧÜ~; …‡ˆ@Û̊÷=ôĘ ŧûˇ`šË ,Æqåaa[˜ŗ7v/ūo“Ę5ĀÕĀDņ ķĀĩĀASSSĘ5§”RJ›=m4ņĩũ…û-ÂyŠYV-m|8 8ČbąˆžW˰dÁ͸gĢÜLd!Ÿƒ†ƒĄaĖŋŨeÆķȸō ÷[Wg×`ø-°Š€ĐA4`¯sÎÆęɔˆ$%=/ŊđÍŖ9sʛíDJ)Ĩ”R% Ö?‹öÉ^Cuh'ƒ!äXōŧ‚å/X%$CHÎņÅ!YBLėCU†ŠyמįÃ×ËR!Ŧ+MĢ+×Ŧģ†XrXF`ų–7Īē­EHāDÛØŸoęĄüŸ¤~G"¸œģæŨīšM]Á”RJ)ĨŽĐe{ĩ|<āÊÃČķO,÷b؁îģ”ÂŊ[°WÉ'Ĩ GNt­œīÄíMĨ(YwÍt*ķ”RJ)ĨML ”í•Dā ĒōĀVÔđWB%dOÆĄĀL ĢįŠ‹¸ŪūĀP ģ”ētŽ?āhčY ¨÷ą÷Ŧœķ XŪÄr †ŧĶ‹5=ŗWnęaũ¯'”!ĩ˛N)Ĩ”ūK¨ËAŲ^!Ÿ‚m´rĮ†ø6–a9Kہ\€%ŽqÄqP^ģV΃•ŗD|ũá]@ė">Đļ§K€íÖČëæĀēMƒˆ×-°Üå{Ž-QŨC°—oęĄũī$gaeÛšÜRJ)Ĩ”6/ęRC/{9Ī"1¸”;ŪËo€}užČxˡÅ^ĘĀf×ĀÉo@΁°v1´­\ŗüŪ0ŦjÛ ,äZ ;ė*°K*-ī~]ŗDzĖK-0œN mÔ8Oiƒ_k˙vW5ōJ)Ĩ”RúĻ.[ČėĨøđ8¯áÃW0üčŽ%į€Z}ƒ“Ëī ¤™Ŧpžĩ­pÂë0`K05Šc]i^ú#)ČKxø¤Ëß[ ƟA$å@š¤/ęI{Ī&ôT1>ėįvĮŊŖ}\-U°fbb:?\ŽMøÍT”×úÔĨŊ<ēēŨ)ĨÔU´ŪĀh‚ŋ\fœ“ĐĀ]öp!AÚįŽģ”˛`sŪ2P -­ĐŖķŒßV-„nŊ Ļ›<’o…šīÛMđÖå°b5tĪ@ˇ`›!\DŗŗHj°ÜF _ĄCāķÅoEl->Äōx+`œëķĄ@Ŋ4˜` ãûm÷iõōę`$,úÆËĢØØÆÕiĢc° ˜LŪæ{ų”p˛Un2^vĸ<(x Č455ĩË)w˛âä÷íX`?×'}]˙Īū…„u]YŽ­ cīƒZ/$ČÎG\}Ü8hŋ‡øg/ėLw–bVobÅgJ6Ղ]9āܐ œqJŠÖ íđ5à Čģ"F8qąb×r¯bÔ3F+BĐōŊ`ÁpúS°ÍæE¤­ĸmŒˆēW˃÷ū¯˙fü˛Y¨螚—Tƒ8Į|)†‹ÉЇuÚĐË^‚/t@9Yøû /ˆ.†×Įæ(‰a{@˰ī90v7šgcX Ö­}Ŋ†Â^'Â.0ũ xũ>xįZh  ÁHڈü ‡ŠI/´đ‚1í ėCčk¤š,Âŋģ˙-ā2dŗ&ô…æ3šŠ>Ö7ėPĀ÷ōęZ‘SĻúČFi2p9Đjdj™ā˜õškā?Ö¸$įcŽ/F¤7•6mČâįČÂkc.>ũ­ø0‰îvƒë§žë iY#Š‹JĸNūAÂæ6Ĩ”Ö‡:å^ ä!ėVjČ[ËŅÖō°ĩôą–ŧĩd­ÅZ‹q6ú'ô>ôĀŽ;|Gl]OCĮ%{k{ā1Ba5õ0~_8ūjė7§`ū9ä,Đ Ŧ‰û4Ģ?ŗÁ‚ąü†ō&+÷ÂvtËMMMūŠ ōXā&dáû.˛8hÔ*ŧ˛Ū5û"€č‰čúK ØĒ4pŌEh đ,p6xúÖéåęĸĪkļ@¸÷W‘\šBŸšŨ5į}*éˆĩų2ĐZŠLšX^>Đö‰•{3Xĩ.nÔ2Ū§Îåņž÷\šMO/„“äĐ̃Ÿw{}ŧĨËį ÷= zSũ¸û4čīōä`X˙|ŠbIFņˆƒX8xŲÔ÷#RA”ãáļ˙ĖÅĀ— ^oŒqã Čm]Ymˆę¤0~)ĨÔUÔaPŋ„ĸ‚5ËÉ žq‚ š×„Ô™ŧ ɘkBŒ a“}Ŧ÷É-Āԃyöģ˜eŗąAqÆāÄ×îOå AĀŲĖ q0nl+X[ĩĻôCŒ ą6$°!92†üŠž`ķXŖļ ÔØØč/}.ôM„CÖͤuAĘq->(ųāĻĪ€,*_ŽĢfxuRmbÕŧŗË˯OŅt!_bĸ€ĸā‘C¸Î'ŗ8WĨõ‹ƒņęå×Q˙Δų^žíÉ0‚X^~?ǟtđkākîģĄX UÄų3‘ŗŧĩâå…ā=‡Š’4îņ1÷ËĶ>VÉĀgÅzøŽŗ_įžŪØüx 0­Īkˆ¨ūãˆūgw=ø 2ŋĩoZÎûYü /hƒĢ[Ŋ1¨4tŧē‚´­Įx÷@ô÷*!I)Ĩ.Ŗrx!#A­˛ĩ|ÂZ~o-Įg8™hu-įÛáO 63ģpöšģ\‹ÜZ`ãëPļÆ@`?xū°/dąĘÃrQĀŦ‹ū•Ãr¤]Ä)ā$ !„ßkˇ›u19¸ˆŒu‘õAXÛ8čĄß0ĐsČA'-øí-ΚßMĀ֔Š“ĩnūæÁ˙XŠ9jƒ—ČŅ™Ū@””…Ĩ)ŽSR=“B˄^>!ŋ^Ē×=9LEU6žĨxIDvDdŋZķŠY™‡ˆNúI`{×/~čP_m‘$ČÅŌā=ŸC8܇‘ųÕY`ö׍:dsō,žuîū“ˆØ}ä´Žŋ™‰ŧc&2ÉLä-dS÷C`ā8`‘âGāëëĩ%Ėڗ/!›‡Ÿķį@GŨ˛ÆÚ"¤}ŧû÷ûe¤”RWRÕ ~ÂRf2ōaČŪ„ÜMH—hˊĢ;ĸ6Ŧņuذ›\mF8Ô΂znļ÷ė3įÌ7ÁŦĩW\‰&ŊŠ]ļf6ßZŠî>ĮŒ šK ¯öđģÉE8ŅĩŠe˙D¤¯ŒWPX-÷Y? JžŪ[¯:ö?zĶžČOīãNȡĀËÛW™Idų=“HĢēGë}¯EDę×PjđåsĩZ÷$.™Ø=˙Ėl˙ãsšQáčŗ=Žîr//0­wĪęWĘÖ6Bũ‘DzobũŦyûcWWäŊôžZ!į]ך§“j,G_F€qW÷÷,āXD§ü„ģ—{tô!šģ!pb úˆĢ[30Ņ—vūÄÂW“ˇģĢęßĮ‘Ŧé(iŋŽˆÅAæō ^y)ĨÔĨTũË  y˛­ ųŗ émCō6$č´ČÚ }0f&‹ ļÄØ!˜<˜| &׌Ɏal? Ŗ0Á8ŒŽa ÆÖv ŦļJ;˙ä։YQ;N>Xü˘Ũ΀åĶ1ÁĀbҏ˙‰ÄæÆXrÆ˛ yÎr|LĻ’ĀËãTT?ÖŠ\.Æ3{+ākßã€ķ§‰@/ ˜—ßVĀgŊrˑ.ō§iīÄŽÚĒø?Dē=˛¸N@t{?Ļ{eĩ"`ņ*ĸŸNĸ5.˙eîŗá4ũrã3DĸXûh>ÖåŨQŌ6E€ŖžÜ0•c^ĖņĘYØËŅ §ØFŠÕ¸˛ÎĸgM՗€/ ķāADoëģ$ųc¤soDôۓ΃K8Qg€XĶī„l*tuƒĄ™X$5đûj9đidŪvC8æĪ"bc­w9nų/ˆ•}āÆâ8w?HxĻ]ōŌkūŸö~~€Hz‘Z^§ÔåT(‡įŖËēÁ’§–?aâ€'c"C'Ēū˜žŒ;ģn9vų|ėüi؅S`Ũ\0­2õk‡BĻ´.ÂŽœ?ģdvÕ"lžcĀ .Í?Ip™[Šé1ûúÍØ?í ¯mŠŅ€ĩcD_Û>}Ļaļe%ØÚō‚Ņč ĀŨ;›áXō…ßČ3ōR|1˜QņŽrAG\ĸļCĀørāžĻĻχŽãjdą<˜G$õÔ_Ŧ?+ˇÜ<p4IsGķŋ‰! ¨´1Žų)Ō§#\U-X_B€Öį(Õ ęJ`8ĀžSĄÎúüЈžq×Wūg‚ËķĒXYՐÖq‘ūT7RÚ×÷ zÔmˆ|ŠwžŠˆ[ĩĪ ¯@ņĨū˜) ŋޏÃčúû˙nņr7/vBDÔqK÷,2ŸvA€\ëĐQŨ˛Ö¯ą{ø¯ũۖÁô,räüÂ2Y‹ŠŽũæŲ{á“ō.[ˇžZœRÜD†`F9éÁėwöųŸazĒPF‘˜ā XŽÃЊ…đŽ)}ȋĄŦœįŗČõĸgÖŧ´Ã„g•j˙9āŲ bc_÷Y‹ˆ–§AVæžęˇ_Gô…Åͤ¸UxYžâŨ+ä2ÁJtąŦÆĮž ‹nll,7G ÔŨ¸e¸öëŗˆ¨s‰×ĪaĖz]Õ žVᒷ#Úđøī•ē0MF¤kH(ŋN1@ÛąHŽëŽuÃØ ŗs´-ô]žČĐĒ­ųÄ*;rb 0ƒˆÕĪ"2¤;e›đœođõ4"!ÂõĶßĢm„RB]Žô~ūŗģVK=Ĩ”:CļđÛčžZFZ>Šˆ•3ĒngôS]ļj[1û]{#ˇÁdë„KUŖ+ek Áļ"+i/O#˙™>Caįa°ãĮ°‡|ķâŊØg/†|=Ļ×HČÍĮ[+"j›‹é3ûč0á`ˈmė—Ė„žÃ “õ6ōŸĩ!Æd°û2æĩŸE§OŲŠī§‚ÍvȎ"VĩĶÖĮ9¨"nÄ_@c`Ļ"Ųہķˆ|F‹†ŲåNĀ€•[”kÛŠoŋø ­K,Ž´n|WĄpŒÕÁz›ŋo*Q!mSS͆7ęXdîô8ÕēŊ@Ž-I~á:Ž_ņōÔkŧÎßEDũEų'ÄėÖM’Îö%Фˇ+ „ņ3W÷ŽrË%QÂ:ĒgõÖG?Ѝ4>ŠYTĻ~ ’ˇō‘¨Īëd›BäUĢëD__hkĒONiCPYrx6‘Ģ†:,7`ŠCÎA÷ŸHšü †a×Ö`ĪÁ|ô\Ėw?€“~Šŗ#djąĄøJŠøØƒ L€ 0&{&(Ü7Æx˛=‹ CŦ `øÖ˜c€9˙=Ėv'bįÍÂÚ!@¯„ē9īœVā‘›€ûÁ+pŅhxīŸ’wA×L¤_CĖā-āą‹W‚RIoŽį+[Į\ĐuWĐæ'ĸīRTÄ+ûGĘMg‰â_CŠŽQģrLÂīqZRæžr…!^ĘÉZŠu‹-]p@Æ&ĸ¸[ՙD!=‹ŗBlkퟝI…OIgË?}Ļ!úN7 ī":gHV'ä—ĻO–)ŋCÔY JxîwmE$4;iRåfŊgtÎ>ˆ?‚¸~)—[U›bq¸qĪ+ãō,‘ę!Õ'§´Á¨üd Ņ`™š˅„ėHHņš@.Htƒ`$vÁ\ĖđŊ0ŧŒųėOač–N",īQdĶãVãžäĨ58î!fø6đĩß`Nũ3fŨ,ls-KšmĻG=ö­kąˇ}nûÖ<ģl&˜ :fŊŽ{7böÅļÎ[Gō‰%pđŗ!tSžYõ8‰Š•<Vqp\Īëû*ĪōîõĻģņƊ“>ķalŪøâčYĀūŒ€ŗī/[¨—/’VĀō?˙a¤"áģŒjŅ^h_9rŋiß,öl<0DĒ‹LĨü6=wğMHsxėīMEZū[ˆš‚ĸnZL€+‡Ŋ”bƒ¯"ÆXaëæå(īž/ēNšä”6%‚rx&2ÍW8}™eg,8ĀÍā[Z'éų!ØEŗ0G_gũŗåîōļ…Ą šD+oųė¸fëֆŪĮ9Aûi}—<…›%SƒŲëĶđ‹ÅôŠE”`ĄŊSĶ€}áZlķ‡˜^#ąķہŋŪ‚ĩy1úŌ#ÖJūÖbú ƒūL–3°=nŲ2”=\?™ā—åĻ °R—ˆØw1‹ģœ\€„,;æH †rę ę×ÎˡæöBÜC|ގĨxsā×9ÎŲ˙'’úc_˙ĄĘÃ=€öАTËPũh‘Ž>‰ŧßüq{R˙oˆæÃ^D!@7 9 ķŦĖô귝ĻķÁÕGßāK77E|ąũvļGšn÷ą(×NM ŧRÚ ” ė|ˆhiķkÁfÚã–C×O‡+XWÃ)'ˆ¤•ëôõĘÃŖŸ 1“˜ĀO"zļËל¤1×FÕQyá Qâĩ^'ø¯ Ģ\˗KՇCū^ĩImüO!]œ_@ĩ(^8ūŅĶoéūN Ÿ íjFė^Eōę Fo&<ī—7 ēlâņĐ:ûî ÎĘŗŦ•nߞōō9Ékk5ܲöG#Qā–§ŲģëĨ”ŌĄ’E8< y4ĐE–Īa9ÄrP['OĻvM+Ļ{Ėo`v<:S­ yŊpÆéŽiY]ĩ ;:vÚØ^ÁÎxģt!vÍ2°š‚Ø[ē8S[ČˆyLŋáđõë1Ŗ?…]>‚~ĨŠÜB[_ˇR ęą[ąš–HŒmÕėĖ陇Ãė~vųR`@yßhé/íŗ}ą„ä+ŊŪ @åbJ— –¯“ņÚEˆnp,‘aOüđŠrĨé€HŊ[\õu…b‘šēéäĀ9ŲLBt›QlŊúŸ Îڏëb}P-iú>DVíIąÃAŦâõxˎŠų5ĪJKBdƏÖg6UÂ4ī{?"ßåâN,|Õųû{īįcwA7ƒ‰”`u틮īÕĸbåĨ”R—SЏRd$ĐE-†ķb)’'vĻvõ*Līmáœ{1Cˇ@ÔÃb­ÕSš‚@,Ɩ/€Šob§ūĻ=÷0ŦЁ\›üjBLŋ0æė6ûÂ6{Ā ‘˜ ã@3”ōlÄę #õč={Úo0??ģėyqŋ Ës —bzŽÆžx+æĐ/ÁvûDĸr×kCÉ{ĮƒāoW@īēö[ē`ŒÃ29€Ū„§bƒ›Š–9Â1Da: áBwĄØuFšP?ÔŖ–ÛÕģû¯"ž×E7Ōr´,u—ĶŰ?\â H`‘€ģP+zļƒ.9›šŠ62¨w‘Õz9ĐXä•ÕYWœ9îš4tŪ Özlâ1Đ~đ {!rŠĘ[Skß4!’…Ņ÷˙ XÛë?UÁėFdX6 āŠč:Ĩ@E JøuũBÆiO&d‚Į%'s‚Ôc›s˜î#á[w•ä YcÂôˇąwū.ž€ŊâãØŋ|;ųaLk€Šë‰é9Ķ{8ĻĮ@LMė˛ŲØūûĢS°…ß]ˆö–“,‹Û’”ãŒ˛Ŧ€yĀHøâĩ° Ŧm”ׇkExõü€)ĸB ÅnZ—ĩ/Â)C/BÆôʕYÁ*@"Ŋ‰Ũ؍Č0ČĨč͍œ­ķŲ¯yÕ;NŌ Á<ŽBŒTÄįviZ(=J0Dô|ˇ!ĸÆĪ-v…ēũrĖ%mk=É ã˙­!E;ÄÁÆŌŽôĘ-q.tÔc=ÛÔÕäīškŠÎ7=ãÚz¯wO žB¨(ÂÖž8Æûūwd6å’SÚĐTŒ4Ē .š–sŠ\{Ãhö†5Ģá›÷`F|DĐ?›Ā…Ģ´8Qöŧą˙w!ö°MW`[[0CFc ÃtëW@~>äæJŽp&¨Åôˆ<S7ûˇëąNĀŪw Ŧ]^ˆž…ņŦĻũ¨\[īŽ9é&˜?‚áå´m)Ļ×@xūJėœŠ‘nYEãČÁĻ˙Ėį`WŦ3 ’ÁÎ×,Ûēk~#ęū2gęŽBÎĶŊ‘č,e?<Ŗ/ÖöīŠĐĖįH§Éæ˙Úš4áƒl<`ˆĪÕo‰X߃ˆq;îŋ†ēW‘ĻEŋtdŗKW5(ų}^M]6&ų\mRô˛%|ŨAdđõq$Æ{âüЉŽkˆÜÃ@æfRŋĨ”ŌĄ(‡_A—ņŒãŊŽ#dģDãŽ"?äØšķ0_ģŗÕŽ|&đ8Uõ~ŽûĝØīŽÃ>~ĻīpĖ€>ŗZg@Û,Ņé†y91´Ū'„°Uôŋm3 ŋ͝/ĻßXė߁+NÂΙ,:ā0fĄ­nW‡vÍ, G]pL7ė2`ŌkÉŊg éø]EãW.v¤Wļîû–į\n\4xÁŗˆVUûš”KÎ!–ļ7 Q×öBb.īDä÷Ų)ņgĖ ÜFuú"Ä0GëÅĮ4ú‹šŋ™hC‚n<XnÎ˙ ny}¨šŠ4]ęzpĘņĶĻ”üS5uؘÔÍûŽG”ļG*!RCGiÄąî{9ģ ?äėŽîû "Ŗą4‚WJ…ĸ Û’wĸë/Į,ĢK93ģ`&æČaö;Ē™K@Đr`W.‚ĪÅūâdL÷‘˜~ũ!7ZįHlëĘĸßä mķ m:fÄ(ė´įą?ØûᛘÃH|n­pģaˆéŅ?ĮÅę_jôU0ūZ'¯ķŋŸ°MN”JĸQÛģg˛•ŒŊ|šA@Y9ÉÁˆņÖ(ĸÃ(üøÅ>ˇ<1úÚÍ}ÎBĸsŊJ#ēĢwúē!Đp™—"?|9ŊHmø•{ö m´‡•ŖoC€ũ”žŒôßJÚFŅÆĮĮ˙ģ§>ŗœ˛=įÅä›Ißôņî5“š/ĸv žNÄĶ)+wœ0äh‰BšĻ”ŌFĄeæyĪg1ä@A"hQƒm^Žéߎø˜lĕÚ@^0.;ûô¯1ŖĮB~´ÍM>eÉ €ĖhȌŒ}FIüljcõąĐ<Ķŗ;„Ŋą—†;ĩ˜52—rËģ€Ųá`ėę™@Īd Í/ÅÔ÷ÅžķėŌ……Ķ8ĀåŖ¯qŋū˜á`›§íV9ē—|úēk!ģ˜ÛH8ĶņD‡~@1 H8Â3NøGˆ˜Z#9ųu—Q‚ß´/6_…ø‰„{¸‰(,¤od×;kÜ䃐ČXšß˙ˇŧšČ ŠÜfd ģŽĄ‘¯˜4t4ģŨL í†{ß+×'gYpOp߃2ĪԟōîŨī——ę“SÚMÎaøG~ŲSŪŊÆÄփaū:8éNĖĀ‘N<]lÔUäEŗāŠS°Sà ÍĶ Ė•ú:C$,į˛ÅØ3°ŗfag{Ÿ™3ąķæBK+Ŗ$Äehĸû a_pĘß@ÄįßF‚A$ņĢâS†3ŠÂ…ĨJÚÔ_%ŌqYNy}ŋŽį–ˆĖĻ`Ū‰rv¨&@ôÖ_čMŧŌˇb īŪ|"ŖĮjH žūäŨûœģÆ}–5Īũų âBöĪX}RJiƒSž‚ Œ%$d0–F5DJ+Ķģ|6f¯S`īÃ"}­ãJ­ĩˆĢ—Âõgcg>‡é;Zæ”ąYČ Ã.^ƒ3ŗĮИ¯ßˆųé͘&cŽsåK˜ķîÁy ô‡5ģp>˜A õ€yfĀ8ėãˇĀķJ îXĻxŲÛnŅZåķ‚~d$dÆôwŧŊĩŪAúô^¯’EwôéS,u( ģø9’ĪƒU.x9â5ˆ‹.Xmː•e8g]<dKs-Š\ęĨKŌmæëģ{ÁzļŖPÆfĘq+Nņę›d=–(6yUØáĩH˙ĮŸ÷˛lœÂMÁ);Ôú 'ō›ĐĐ´w¤×6•ˆú’ Q Å7|~ŦkĨ&dlRŅuJ•ÄÅ@Hŋû 7žĪk1e`érøøį1ĩõŽ?&€ÍÃŊŋÆžōgˍ1Đ<ŊÔ!#čļvöĖ§ÎCO†-ļÃdk) Ŗi-|dė!Ŧũfę;đܟąŋru˜A܁Øjh™<øsėŽbúŧđÕ.ĪĄŖ0;|ûŪũ˜ž} ŋŧ´‡,bb˛b­w/Ē—Ä5؁ŖŨĢ_SÍžē„{¤aãܤŠ­ī@Â]—ˇ!Ōö@­ĖIEZwŸ‹nė&JÁGۗAÎĐŊŸĘū˛ÕH6ÛÕ$ĸãûÅēLŸôˆÔC1ĪšJ~ÄŪ¨äeg$ĸ[§ŠsęEDoĢĪl*ŌuhGœŗ!˛Š(œ!^åŲČjđõr^÷DoüŽbuJ1ĻSĢkKë:]§´Q)(üqnĘ%Ûd.švå,ĖnGĀ{Dbë~ē•^{{û0#Æ Į ¨Áļõ‡%s0ܧ_…ŋ3dj\ŦkëÅŊļ.r—ÁÔ÷Æė°/œvæšw1? ;k.ļĨ˛ƒ!ŋēÁžū–ĻŲq›QŊtĻŧ”RZo “#$Ņ÷$ä įœ$Î5}å€ēCŋ‚Šī…¸t†Tę‡lW-…?ü@„Em  ސoĀ4ΆË_Âp X§‡Æ;=Ę?ŽQcZƒ;eĘ˜`Îø9æŠg0=bį-€ėȡ ųÖķÉ-W0ģ¸3QSF¯Ü"P˛l:´U0ūėŪÛ9”´@BZ‹7'Å9!Ņ‹ ŧEî ˙Ļ‚ Ą*uŅŠ&đB"%,ô>מđ#á0  ō¤w„ī5$P ėËY-ãõĒo(´Y‰°][´ž˙^Ąü!r&ō‘D››’6%Œ{áŋāîų’ß~!đ×Xßm ō#×îŨ9åŦĒŪīžÁ×$÷ũ@`kŠí4@bĮ+ЁWz"TJ˛Á]~ްĐîŠčŽâVČÔb[į`†ÛēÃWâ|\čDģ˙xûęķ˜ņŖ ufiÉ5C°Ķgc.}ŗí‘ĄX!L&E'@áéŦ‹†rŌÆ`vÚ{åã˜?\ŊījĖV[ Ÿ4åUhYu=ˆG`Ȗ"vļåÄÎîfËZ ­éuŽŗ“ß3™ĘĻUÅ÷ZŊKJ]Ž[Ņ´>']l0ÛNI9„‰tË4š‰ˇũ+ĸ  !1!=ë9ęÛ)V7Eũã.—‰ģãœĨnD v‚S%`ŪDĮü6ÄGVÛĸĶĖoÛeˆ”d…÷\R›|)ƕČ&/‚Õ7üâ čĮ$ßh3¸Ę#ĩƉŽowŋw†‹Ī Ûå?#V$Ÿ.wŋ名đĄ.ũZÄu RJiŖR~Ÿ+Ū+fu7÷†%9Øũ,Ė î͎NO˛ÖqÉ+—Āƒ× ŸŌšĀ%g‡agĖÆ|õ˜}Į†žsė8ÆD*9ŽQ,Ēé;Nû æ´k°|€éšvæ}ØĩĢ‹Ũ™|Ęfäĩ·e¸ZÄ­ëH:‘ǤNe ÆđUKÄ×JÍD!ˉeđîųŽO÷ˇ–üD]GŠjGH÷ķˆô ˆ/´Îh𤺓¸ĩūŸđî'҇Iŋ;ĐÔČzŦāÎeüjŨ2Ņ Ëāî“—!%ßČë_Īc˙õ*ĻÛhȡÄĀŠšį `v…{ĩ€ėS œ CČÖaNüæœ[ą“?tÜk0ÍWųŦIagąm`ēõ)ŖOvuȇbĘT>xˆ˙™Q¤"pššëbĸ#ë|°ņ9ŌŖ˙dåÍÃĻĻ&-Yīí€DÉú1ÅqŠĢŪũĮ †Tz­ģ׆¸mŊœCyœŸĢ'žö{åÛĀūDâoŋíĘÅ˙Ã]“Ŧ€5í*g" üoĄŽžaSS“Ö1€Rĸ(x#“ēī¨uēī(čģŒåąîsˆ@ĮÛ˙„x?‚¸—ų€ėsÉĒ“˙ đ>Ĩ‡jÉ×Í\ĒįX‡ šĢĄE¯A6Ē5ÚSãļIDK늈ąĩîū&÷>¯=)Ĩ´ŅÉå,!ģz`a‹$­K0ƒ€ą‰r(X!;.9ßĪ=€ÜĘRË ÆÎķ™[0‡û7w}ԸցXŦYĖ‘_Ā|㠊’Šđ~åBgč¸PžÄ6YĸՐƒ-(Ã^­^ę 3_θK˙evŅÆ‡wĸ ™$š éÂzqčx`§ÆÆÆmÅæDDT÷2~y/ߐŗˆëf / å‘Ž~ޏŦ\ƒp§cë~;@‚_ė‹ˆ!¯Ļ˜ôĸWoĩ´ ËĨļįņXģ|RŽqwÄpį2ā¨ÆÆÆC€Ī gOŋF$ļ\_÷Ģ“WžēčÜL$ ™ę•ĖŊü‹ˆxútäĀ’!Āķ"ˆ{Pø€\ƒl¨~ęõag¸äĪ „6Š>) Î ÷k\>…HvrČVųIä¸OíŖéwŊ´Ú~ßāKõėŨˆæĀJä*ôGĒONicSÖšB…H€?ęOņ"Ô`׭Œœ}¸TžŋŽÕųŗāå[ĄĄrĢbËmĢEÃĩہÅ51ŪēaŊ{Ղĩđ„ !ČÂņߘ6čÖÃ5Ęs¯RjË9 ĻI†*ĶM^Õū[BļŽT„­u[šX– Š’Lē`ÍÕÆΤ$Ļ/Ŋá„}NŌÄŽ!âÂņIdÁRũ›oÁĢ€Ŧ‹žĻŗ]ruŌō.C‚+¨‹Ž‚„rk#ŗNxâK­ĮėõBļižH08úũ:/ī|;îP÷įSšĶ߸čÜ|7ÖēąØá*ƒÆÆÆMâŸŖĶí‘÷Ņīkmˇ?ž{"ĸפ NŌ1žĒGÎ"[ҝ ŗŋŗnPmˆâqd“s8k=lŽŲˇ{ØQ‡h÷刚DëŊ>ú]{?ŠpĖ[#§œõGæ­úp?…H_Öˇŧ”Rę4ž˜z,–nž(;&ēî'Ļ ;~FŦŽ­ˇ˛û@5k:v2˜ė ČįŠšÎ vÉRĖž_‚!˙}0v—dUkä3ÖB}ĻÎŲ%AŅüŠea.YÔlŗĸ‰0(jkŌ&aáL'o­ä •qÜōTåžÃã‹rŅ…ņȎ]ÅÆq@öŖ{å\ēîDĮ9ĒhR˙Î Ŗ7ĪËŖš.ִÁSŧō9n_ŦŽ"Ôdƒ7‰Ę4ĐĨUî¯Č^Ž(ļ÷}ˆî*ëĩü×`6”r–>0kŲqQ/ˆtíŗ-ŽY§nŒŽuW ŽíŅtĒ×vč˜û1Æũ͙>ĢsĨ͍į+ŪØu†´ŗ(˙8˜h.h›2$ëķ5Ũ—‘QŅ›÷EDúR̝'ךAL:īv÷uũü1¯Ô79č‚ōRJŠSxĄ‡T6ōĒŽqČhyRĪ-VŌīŧë€ŊAœ/ļßSĶ­āūTˆK ÜŖy…s—}Kéj(Î]wīũfcéŪ{Cā,ßZÖĐ  æåã9ģčeĖ›į ÆĘ€{¤X@Čûz/pÃy‹ŗrĮ =ž$ĘTŌÎoŪĶ/‹č ÷& 9XmK]Đį Á+^ĻôüäBWP|vr‘c™÷ģO (ĩ@|ÍË+‘8Ų –Ø*õ7šOāĘVC¨Œ×Ž]ēbš’bj‚ rÂŅ!WWCņ&"žĘÆ>J7=ž}Â%N$fmŨ )AxŒč$Ĩ]ņų㈅s/ŧ͚™X´! ÉË#ˆŪ_Į#D6 ĐÖā _wEøē‘HØ(<K›RJOĪ9ŌĶs–yŠąTCCôt 8…đÁ[Ęš–d€ŗĀČqĒd°ŋģ f(yú'=UKp1ÅĢŦËĮj}W,†7šD+Úļ2™ģmk!טR=zV-…9oē-å@9t×7Y…åXJjõĸ÷j!æøb‘d­Æ>ē8˙ ‰‘=(8Žbî ?g¨Ĩeŧƒč…ŋŖé P!@û\|ŌGĶh[ ˛˙ҁ.÷ŌĩʅMAņueúŠŧģ´š?ŅQ}&ĄĖĐK›Ôo]Ŋ€+`Ŋ‡Dvģ›â͎ß×ņöÅīųũœE6Uû"@¸ž€Ŧô>Âmū„("Ø!×ų"É88ÉÍqČÆëzÄ7ûI"Ũw ęķpDZRTŋ.āZunN" ”2‚He÷Â2¨d%Ĩ”6 ų]c¸:ī“—éÚHôtœš×ĘÁ Ē[/ųŧöĐą%y¨ģ’]žūz%\uvÅâØŒĐá÷E]­âœ÷;o`Ÿ{ S?ōkĨØeķ1ģž„é?Đeåe5ŸĨ‹ąīžŠé6BōI6ōŌī˙.ˆ˛Ë/į Ė‘8Ё`9QŦ.žĘ)Ģ…î7‰\A4Æ˛ęž•ŗž—ø'X`_‹œķü`6ÅŽ8$ÔËąÆÅ™3ŊæČiRÕåi?=‚ˆMÕĨ¨R?ų}Ĩ#°ŸÎ–ņĩrpqŽTû­KŦtc ¨íZ†Ä8oDDõ~_—k_R?ĪDtÕEBuú!&××?[Īzž96ô~¯_‡#"â+ Ŋ1<Ņãúˆˇ#R‹'čz@.dåŽjđåŨŊ~šTtŌĻ"˙‰N9éT¨0/KQŋa%™üW.…šA]„ëbēŲ@¸Îžˆopi&B˗cW…Ø×&•į{ĀŦ*ĒrÍ>ųe´6ÃwĀ ˇ:™Kęvv;ęęŖčeqš1E–=uËJæ”Õüų‚čúĄâlĘ,Ė˙@bøĀũĻ ā„ør :>~ŅÛ6!€˙w}ŅaįÚŠę3#VģÛ#áīA6ôę’M¨_€čļFšė€͈TŠ2ũô"bi}>ØÄRžŸ2ČFå=ā*‹-Ÿr}ú€ë/ũ<€ˆnõ$ĄõæŽڅkÛCˆA×'ņëėvæqcô0p˛§ßqáa™2;CšW­ëĪcnüwˆÚ#‰‹×ë,¤÷D¤BĘŠv) 'ˆ°G,Īĩīf‰āSŅuJ›”˛ŪŦ­˜2t–]™„h*jmÅŽĶŖ7„KJ-¯ÃņĒŋo’/oa˜=ļÃ>ú;˞Ũ‹oĀ å@Q šSĨ!ąĪ!‡ĄDßzîoØŋüŗŨ0h›đ‘ĨĄØ}ŋä|•k~ë ˜oKZš Ō‹ÚŖá‘â>ŊŸņ 'H5¸ö­E€î=d>Ŧw?WAžŪZ9Ī—Ũ§2W' bâZ$XÉLßBæ6DÜažn nU#|ũ‹č8ˇņŦĐS.9ĨMI&üdaá~ q¨7šø“‚=ķĐL˰‘ŅÉP›<ķėÉã1Ŋƒ] Ö[LÂZlÍZĖoį`“°•&–ĮÔIØCˇÁė0 ‚Vė‚ŘãáŋÂė~€XgëŲČēTr™ōŲ†!&“ÁN~ Nž€8Ãb Ą2‹ÚÛ;o)擧ÁÅ×a25ÅyĄzé%pæÁØ9obēu AŦDF6AB@”#û÷ÃŽ[ ?.˙)œp*ĻĪ€0›Å˛˛úŋ‹Įm2ė”wāâ¯aëÁ˜ĩ<$‰‚ž0m)u2ƝZe’ü˛'ŋ‹}äMĖŪ# u6퐎†ĻŊã!bœ ;¸…ļŒëNÁVžL>°žâšNÎ_đ+ÕMĶ—āúY…úTĒK‘ĨC;ũÕiŊõĨ2R đŒįÚi_—õsÉ÷E7€IđUÖúmP0Ž‘n*v"ä÷‰N]KE×)mrōÅוŅ6ˆāŠĩĩ|šÚZLo°m+0™ŒčĄ dEô=Ü)ā™žũá#{cgž€Šī -K1ŲVØoöŌīÃŖ÷ĀW/„}Áôáēē&y‡N5 äÚ°Ī= į‰í9ĶĐZWˇZ—ŪšØ31_?&ėVČOŌhūîũ~î && ŅÕåi÷wXÍë_fíđÂą!ãŽĒÛŦO§ë˛i¯>›lŖĐÅ@uŗ¨§ˇ!СŨĢų ĸbŲ$‡q¤”Rœüā!Ë*Ŋ2+Kį—dRā ë{Á€Ŋ`Ũ:°ĩ žÎÄΞûabeŦĩĐĢėđQņ4õÎÆt5Ŧ›‰Ųafųlė'b?ŋ?öwŋ€ˇ^ÅŽXâŽr¤pÄŖDæČc—/Á>ũ0öģ_Ğt$vĀpL6hY]ŧW÷=ké!šŽĪŸ†ÉÖz'XE–âƀ; ûĀ0cëĄuqšžĶ`,2 +nhÁ#¤”RJ—Tt´ûÛĶØJRJiŊÉį”—¸kōÎÖ:@]žØģ‹nÕŗ Ū Ū|zÔ&xŅb4e2ėypiú}ߏÃWÁV в0z~Ũ\ČvĮ|jŦ˜…=īXæĶ;ÁÖ`Ėxl˙Â‘Īž S߁éS°üvŦÅė< ÚEÜ~’—o1؇Ļcūp#f›ÅZ;î'mėšŸ|žë-ŗHČ ĸŨųoŨ]Ãֈ+Ĩ”RÚ äqÉĘ  9°䜿—Ũ÷TtŌfAYl4–z÷KĩŊ6pv´+Ŗ{ ‹ĢQ;f ąųR[:ÍÛV‰Mė[/BëĄļ…ĶĨ|pŸ°æķ‡b'˙ Ķŗ'äWGŋå×ÁڙPĶ ŗûp0ĩ؅īÂä7D4Ž[‹aHpĮ†zĖácäpŒæ$+k¯ÅŨ‡aߝŽ9˙TøĖÉÅŋ+—ŦiķįĀoĪ’ˆŊ- K>„ÔEč5Zų›ķÍŧ…SJ)ĨEúfžáŨģÃ]3HÄą”RÚäxâÛŪ÷?å6éšØJ¸G÷}üîrÜBPŸk9fP?ė_7["<Ä ĩlbúĀ×.įĄÚ!‘hŲ¯OŽÖ́uĶ0ĩYĖĐ~˜]‡b>1ķ‰1˜í†`ôÁÔXX;Z—–?J1Ô ÂΛ‹Ųv78÷û˜î=Šģ|}5Ā߯>Ũ†Š^ÅĢ8Xˆ^O – ™ĸ€‰)Ĩ”Ō#KV¯C‘“¨@\âîtßĶ72Ĩ͆|PžR1ĸWë ąYü÷ãØukÄHsņšÜQ#%ŽOë:áŽãa6ŗ âøĘ?+$á:Áė{ æēŸb˙:ęĮDáíKÂ"Q´Z—Âēy°fŦ™ÍķĄuyr îøķŨ†cį,Ä Ú~yfЈČåË3+y˙Mė…§b(e–uEÎNžŖjŗ‚'7õ°§”Ō/% šĄ‹ÕõˆåHgĪiN)Ĩ BŲ(“u Izå|3ĻØwŸĀŦY Ũå8ĂčYyÔ8ˁû`ß{ĶPáÚâ|ZK0ŊnÁvĻĄ€]áØ)=P"_>3åėŋĮ2VOīx +Ų~š:¨Šũ×tĖ~{Ã5ŋĮŒŪĒ2 ˇŦ…îz)'ŌÕ74@^ūe@†,ųMo‹šRJ˙ųTá8HŸ4ėlä=Ũũ!‘Ä į4§”Ō†¤Ā;(a*!K<.¯˜[Îį nŒDTžōž<=[‹éŪ8^Ļ}ļ_)gš[ƒé;{Į?áéĮĸ|Ô}ÉVŲ0Äôė?ēķ“ąN‡úqô.=ƒČįz+}4Í@Ũ0ƒ°MĮ|ũt¸ų/ŀŦŌM‚ļõÁûą×ŨŽ;Z–U:|"KȇXnuŅ–ÅūüéM=ä)Ĩô_Gz Xā}4Ühø*ĻVÃČ~ŸčđŒ”RÚŦȏ}ŊË$OšpR”;Úõß˙’k<ˆ×>Ę5Ÿ‡Đ”‚VķB‰ä{Í÷°‹æ:áōŅ{Ļī¸ėט_üûøTėęĐ}4ũ’AˇŌ'č ŨF°O˯ޜ…yđnøÁĪĄ˙`Cvm)„ÚvõyņėņŸĮ8ÖÎN>xĸøD¨kČŗK†ØâČŌ)Ĩ”R‘žžš‰E–"øÂë™Ũ?FB•… M)ĨͅĮ=fÜTūGYN9ZWĘų@˙x¸X¯ė‰yØr<æŒ/`§ĪƒLŸnyĻû ėĶĶā÷7 ?YĐZčŅ€9ķû˜>5ûÄ ėŧĩŨÆ ×›éĻ˜îō zBļŋ€pˇ1ˆŋ ûä<ėĸ˜ß\yxĻņ¨­ë‰Ŧu¯Ą{`§}§ öč ­ Ë鐍ëš,–—ąÜJrFužÛÔÝRJ˙U¤oęW‘ƒHöąGŗ rÖšĀĢĀĨH|øäĖR|BX*ēNiŗ"îYØ5~ņŧûØF֍Äūc&æÍW1;ėZXÃgh‚ûäŖØCĮ4ZŒ¯’¨~ö陘‡îĮ|ꘒŧ¤Č(R— ėĘåđŌŗ0ņØ;ī…•õĀZ Ļ/ØŦĢ}ræĖ2€nЧsܑđņcaīũ1ÃGa­‘¸×…č_ŪæÂZ‰Íd°sfĀ'`'ŋ„é×[ŒŪJãŸųNQ8Ë?0zHcđÂĻî”RęÅb_ߍœprãFu+Šé”_C,UԎÃ?uLƒų?āT  ”rJ›eÃŊ\a×ø:â*0ŽâøĩŽ,X'Â~ęqØaׄđ“îī=÷Å|ūėsa7@nUiékgaöŽũĖgā…0;ī%ĀŦų({>Â4ôÆڈŨ˙PĖw~īŊ3ĻÃäWą faÖŽ–S¨zö†á[Âøaø0ŋ †éVīáŽ6%€|ę)Øw_ ͋“NũU Î!"˛ë°üƒ€€fBę7õ0§”R—RĻĖ÷IēA؉~ ÷ôíT0ž…č˙@‹;Ū•ŌfKÂ)īØBě[1|Åi@ÕX"ĸ ›oÅYxx&fĐP0¸åįžÄîwf˙Q°ffr 2Ũ°ų˜UKā>Į}įķ"JöEÚîjŊ`#žąÍĩB.1i ÔÔb˛5ĨĪB1'ä/"ëÉpÖ׹˙~3j0Ŧ]PlR8ņĸ`mĻbؕ<+0ÔHĒāĨM=Ô)UKÔØėâb¯gÛJļ—åÚį8SM˙#āp÷÷_°OXjlläÁŒÔĶZĶÛû5 a-Ŋ!Xá{Æđ¨ĩÜcLáXÆB Ũ͐æŸŋŲčĘŗ°SÚ I@y WØy<éž?āBQŽĀ>7s˙1ĮœäôŽĻčõļŒÍaz1ö‡—aö kį%q™PĶ€]&gáÎŋÃÎ{\ –ĐåĀ .Y&Ļ“ļÆģ,ųbęxhOŨL#Ī=÷|ųcØ` ĻoÍK*‰ŦũīĀ_Á[ŋÚšÁŠJ•^Ė„į“ŽßH_n:{4e”UũˇŽU{}?1ĢŽVL0×fxÕvmÍŨ"TË`ČģąŠf{‰Q×(VĖ1öՎCJŽ"N9Gt$}ž€Ŋ(ÕŅuØæĖĐ đ§ŋaú@ƒŌp”3?„ÃĮaëúbĖN™˛@mOlŽŧ>ķĐ}˜ÃÆUŸ+.zΉžãX_x"Äąg­ĮyەËáî˙ÞúmØe&ģš——;C+.ļžø † ĩ˛4tĄķ \æŪxÔĸ"ÎĄ\^) %,”EÆBđåvĪPŽĸŽPúzÔ#Ā´ËÛËģJŋā vÚļ-Čqeđ,5ŦÆ‚yv {~øûŧÍö†íœY3īŦė-ŗÃ-í3%Šs´ëוmôh<°%2V¯ãôââ}í*PN8×;åđ̤,p„ģkŨâm¸ å¸ņ’PŽĶgö‰71Oũ Žũ\ @šĀY.ÚûĢĮā Ã`ßq°jjrMZVc˛v‰=âX¸üđÅĶ0ƒ‡b­}2Ū‰T*ĸ&ēV<{2Ļûļ~`ķ9ėk/ÂĨ—`xŗįH9ųiŨēd›Čņēah#΁Āß:?0eDX Ža•Īi—¨{Č` 0XKJ%äúOG|,đMā>āŊßÔÔÔ%GÆÆĒÆ]uķ?īš0 +.ląßŠęŲØØčŸ)ü"p!p›øč„HĪ(+ŪŪÉrlxĐg,ėd°W=ØíĄ•“ÖmĪ&ē…KŸ¤û^Ī5ž6x03Åū'€qŒtœž>h&“Ų.]e6힯U5÷ĘP Č lá›3(t– -ŒĄXŒҚy°].ũvß0CGŪPQ°ĩ˜…ß\‹=ílĖž[`W|˜Œ ­k hÆė1{áOāžá’áĀC1=œ]Ūū6Ąˆ#ö)ÎAĮŒŅL`smØ÷Ū†?܄Ŋę&7ŗįpėīÔ§2€lĸŖāĻa8Ct2ëãŧŅĨcuđ ā[ˆ%i5/Ĩj/!."[šûÍĀī€ ģoØÎčR;*‚­’ŗīT>ĄvĘÔ(P'į#P€|ccã0 đvÕ§rŽī6DA0ōČaН#'Œ=Išíaåļsų}ĐFŽ9ņYŋMfbĮu흘O:§ |ؘ^ĻŨp‰íÁŧxdsãÃCŒ%ÄÔ!‰ŨÚf82߈_•xŲ™ͧƒķCûa<Č/ n_}5Π˕בąHHĢ}= ņįŽŽA‚#­9Uö[׎IĀÚ8¨wUߡ—×úŽÕÆ\Ŗ (îˆ,C†Œĩä1œnāW@Kā†Ēõę‡Â+ķāǟaÎũŽč‘‹r7‘žļe\ņ#ė¯Āė3VL-_+ ô kōØwbŽÚžôMØįĖ€AŽÕ-ãĀV?ž´NÃŒ 45ØeKā×ā?a¯ģ zwÃlÕ_ÎnNvyĸ8cB,5,7đq,¯¸hâovj<â“ĀįjžF&ø ČŲT‰ēĻØäŠG qŽŪū„8Ší|qÛ?aNø*‹ÂiØ1 ŧ߃ØßĄ—&¨–Ĩœŧ…Ī!BdpŋīįĄácˆ=W$úÕ§\™ZÖpākĀ8Ö ÛŽ›ãÉCĢ—ž\=ĘÕEŠ;đ>Đøģ×áÔ÷zWzõm¯MķĸĢ×Č́6öFŽ.ŧ1ÜŌß*y|L 1ō*ŒĢ?~EąJtÅĘ­˙司~ šššB/ö÷áĀˆ[–J~~čÚ´¯kŸļĢ\]3Ū}m›ß~Ž”ë›rķXÕåÆ]ßŗøœÖ~8q?û2˛‰ŽK3::~Ũ ĄŒĘŒÁYĀÕȜŋ™#åÔ`IuĐ´ŸFæņŗDī$¯!ņwŧš6úõÖīí­_Iy'IŠ:ēFAņ¯´>ésEķ#˙Ųļ’–ũ¸) ø°ĖĨŽkæÁn#áŧ‹`˙1{ėS7Úį–ëēcĪŊŗv-öŠ_Â^caÅ4ĘŌęyéģĀžū2¸ ˇ;Œ-ō:yļŪC¸…ēúkh•¤ģļt "ޚ,šČHņÉú)dąxÍå ­üÅŨÃÕá툈ôQ"-ģæ5Q‘< B¸ŌĀ?‰ČŽēÂÕo‹ˆĐîB ÍcpÂúãęØ üŨĢ÷ÁȋõÄxæd_F@Īį|‡‡!Ō‰Iîī\ßoáÆ#‹H!–!ÜĀãČáĨ~e#ƒĒ§†ŧ˜æ~ߎčeWŊę9ČÆĒŅõíáŽ?ãådMØ<`6"š XāæL?÷üÛ'ų‘Nųx7ŽËû“ŧ˜€¨Qz/šqZK;ØÕåSëæĀˆčõ.Äw¸HŌS†SŪ§?qĘJûŖ‘5J9åKnygdķM7G^Gl–ÅęÚĖAĸõŠl€ŸtiA¤Gģš˛ *Í§ųŒC‚—üčœŽH?nA˜ššČ{Hėš] vĨ6Ûg_qķíeä}~‡čßĪͅā×+bíۍÅD—î4WĮĢ‘ĩÄ455ؘbŒ›'šųwŋëĪ7(Ä]]˙ rŋ߃lë\ÛŋˆˆŪ/vuĪ#ŠVäŊÍĪ ’œ“7[ŊrvEph€ûíĪZoWžž§ûģ|WŸus1"|ŋĖ\>yŋģ#ëĮšXÚņĀŽŽzšļŒrũ}/˛žÔŸsã9 9§{™{~[`wWˇ…ą1Žuķo!"E0€-Âp;16ƆXđ×YjĐTlôeƒą¯/Ā\t懗BM]ô# ˜[›ąˇŨˆ=íۘm‡C7ĢgĶ.ejĄŽdģc×Ŧ„ÉKĒ߃wŗÅčŪ g1ˇ,—cĢŖ8 Ā×,Üj2ų%ä3$EæŨjŗ,O1pŨYDOC8ܡŨDØÍÆf]Ø..G@į7vū./.: !‹ædqŽG&Îõˆ.['kY`ŸGīŸ /CƒËgd1û˛`ws埉ˆk‘—ōdŅ9¸Yč÷ûÕŽÜg1r›Œ"ųȆMDtæ×xíŦC8ĪyȂĸm|ŕ}›ëĪfD¤Vįî}ÅËãpÄŊí(oëîW¯VW§[‘MÆ\`Ž@YđËŲY\Îry\īÚĄā^‡ũĖŅ3Ž]Û"bFÃĩȂõ˛W×:āvŧךüzŸAdMžéĘ> ”ąî~=œhO<[†N€˛ŽåŸ\}FéZŒØJ܌ˆuÎöFĖ#€)^ß/qųLF¤P¸ë9nūžėÚģ ŲÔdŨ|ũ7Īrã0˛ Ú „Ŋ…žŽÍ/øoš9ŋĨô¤5ėŦAâx_îúņfš.´7˛ 9ŲxéX|ÃÕõcČģˇ ōNnįú,žŠË#›_¸q[€āO`õį›Ž1 \˙láŌëFd.dĘ-f‘wr`)˛Ų_ęÆûnW‡‰ČÆ(@Ūįoš>^‹čÔšv˙Íkc#˛ą˙‚{Wö÷æōrdķōļ7oęŨ؛˓Ũ\~ÛËû\÷Ž\‹l>rČZô;ä|ÂŊCëÜxžŒlđVš_îÆĪīŋŨ]ڋp° _rJŠ Ádå#žMČ$węQžSÛĢ`vũŲ5Ø?ß+ˆíšáū.Ä˛ŽŠ#8õ[O˙ Z–b˙5zŒšŪ•–Čĩšų°b&\‰Ųē7f Ė.Ŗ0ģo‰ŲmfW÷ŲÍũŊķHĖ1c{CÛrX>MDãmëÚ?UʏiíˇŨr –[!Cž|ĻŋÔ¯+Ų#ŨøëŽĘíŨėˇvÆ0ÆEėíŽēĢo_´… nã‘]hÎMęū‡q˛č|čå —ãX7ąwC^ž“Üo /ņ‘îÅØY<Î'rŧŲ•öBlžīž+‡˛¸ÜƒŧŒÃ€<Ė}â"θˆQEuq‰Ā„ÃēÔÕŋ/˛ˆž‰Áļ^^ÚgZßG˜‚넎Cĸ Üō Č.Ŋ'Q0Nd!D ›iš‡šë|üŪD6[5‹í´œ‡‘ ĮŽîˇ—÷§ņD:kĨī é0daú˛h}ĶũŽy˙Ā̐…Š?˛xsķϟW_ĩšž×ũÖßũū „Ģü‚ÎŖõ4ķuÅJ:fGšļ @æĪw‘wæÚX‹y{&˛1é‡ĖÅYxΆ ö;"@ķ3—NÛŠĸŲ?"˜ÁČFl*ŌC]UîāŌÜæūöՃōî(žîōŧÕũ~9L×ēûũ`âú{°7zŊ Ų° F8a傒Dų'!īātdsņ˛!Õ>‘÷āräũ‚ŧC[!īũ< ļ@æĄA6HCIËJ—× D:sÂQ÷E@đŋ…ŧ3Ã]īįęøgWV[ŦŪŋG6‰Ũ˜ŸëŽßđúWŪ1Žū]˙įę{ŋ{&>—l4{!›•—ÚˇÉŪäŊø#Âõéž{ygOFÖ>_ę|Œģę†Ä655•]–yå…DĮ˛ËŠXgđ%.w’T4¤+f`v=ņėã镋€Yũ˜­Åđ1Ė?§`ŽģûútėęZh Ô´–š6hYkŠ™°t ,› ËŨg™û{å,XģHŌæsåŋôŖ˙ –– !ë°CČއōēĪĖŧO—Ŧ:ŅŽGDkēģØ]uĐM™įē ĩÂĪŋŠŠŠđņŸG^€7U”˛‰<yIzYuC^؏#āiÜäz…h’7!‹Đ PD&žījō"Ūč&ë4DėW‹€õųČ ˛ŒPvLčē$Hp_Ń] "Nũ­ģŋm…<× ÜE‹ëįéŽžĒŗŊy9u+X‡é{zŋL€ˆ,č Čâ9Áĩų8dąø—K†ÆúņxdøĐõŲd÷Û:7VS‰|’uãņdÁœīŌŪ‚Ī>^ēmIÍSnLōŌ*HÍG8‚nîˇŧr5­ö›.Ū‡'ôΤØß+‘Åk°WīŽ&mßīžnĞp× Ūo=‘ąXëŲØØHccŖqû#īĮÅîŠ×Ÿ”“UŸķĮģą¨n”A8Ī!@_č‹m˙ ‘ė^Y:j˙s¤—^ûãv/}š ,ˆ„":7 ‘Ô.@¸ŋWÎø×6íˇ !ßŦ÷ˇ–_įęĢsVīīˆl”îGŪ ß3āi×§Ÿ"æÎ‡¨ü2V ƒ^ŪęmĸÜŠ?—u~ûƒ õWw!ÍG}f§ÄŌé}šŲ”úīë.ˆÔč˙ųDNš@!˛´æČcČs–k Šq@U ĖšÖ̓mFî0öŲ' ŗMmi‚ÂP¸æ}ĀÜ9s÷형 °ī.€äĪTĢâĒįx;úQî8Ē'$‹åm,ûay C†Ā%ĩų€.#ī…Œ‹#ŋ‚p›/";äˆčK9Fõ^č&\ßxúņĘ鍈‡æ-fžDv˜W?Kô‚ûæūĒ;Š[ëW:Mē֛u~Úneîû‹JGíŨU'^ģ¯ygÚyÖ'­—Ф–!‹ß‘ˆxĸEî/ągâųÔ"œ3Ũį d!ßŲšĢÄCUw!;í!îž.˜$ÔĪzĮÛP›đ[+Å--cRBڕČFlĨ‹d—^ûÉëoōõŖúˇŪ›įŽŊŊô†hė}•Žu}"ęŊ ŅËÖÅō÷ŠÅËĮīį[\_íūŪŲÜĪ+ļY‹DīāL/Ļ[ė>ƒŧzęģŌė×-&-ķËŌ:Ū‚0/!:w ŦqiÛņ냈aŨ,DˇķúĐ¯ģ¯nŌ{ę–ĨéŽē‘W#ƒĖˇ9Č|ĶõA)ž^čwŦt.Oõ~×r—!īŲâžIĨs9×Î}m-˛˙yGu3÷iw}0^ហœ™B´|…„d0´ņm,ˇbŠq"]°H”Ũڊi[ێ!Ü˙ė_›0#œq,ÔĨ ‚ˆkîŲ@pÂ)˜į§bžsöíšP?R ŧ6,gŦÜą¯?ļī"d/rŧƒ!CÆqČyČTpĩ^OŌIzŒ›$“Ũu1"ŪyÁMšOų ö’ŊčŽû搯Ęđ9¨V÷‰Or%]¸ã–qū‚§ŽpA듇Špŋ`ÛŪ¯–TG¨;Ŧ)ĩ O*3‹€Û¯1Üo ŸNō{z32.Ēoū "žK4ÖÕH’ęNõkõ ŋ× ŌŲÂSŌøuT˛!m$ׇÜuĩw¯Øp5rãÚˇ!Öãã.KÁĘų´&ĩ˙)„Ŗú’û[Ĩ Eĩ*õëjĨ]›đ[ãĸ1ķí@ʑļų#ˆÚâmø–"@5áŒwDė@æë\dĶ9ø%"’áæ“ÜČâdËÜ× DCBz\[)_ÕŦēqŌĩÎ/?‹ˆĶĢ™ËņMG9ŌßovųëØŸ€€y"č`”ĨĖT¯Ûō@-† §r§Į1‡EŗZZ0Íķ1ۍ&üԑ„wū“k… („ËŒ“F“Đœc0—^™x/öíiؚŠ[?š:qĩŒ#$㞟 |ÃZ˛Î<'}‘ų E:&ģ!Æ ×# î§Ü€ŧËN 7:ēhéāęė‡ÕĐyÚ+ÛIˆÛĮ÷œ/æR‘ŲäØDÛH9û^îĒ;\ŨRv%ų/ĸP×%íË×ĢŌcéÎDâš üũüô—Dč ôôĮ!ģī­‰D”žĻŋđЈēZŌ2ÕÉOįA†ˆCØĢģ‹˙Üú’/ž (æŪˑļUA)ëŨ;Ä]UĖYiĶđEDMr,"ÖÔÅŧŖmĶ6ÜčúoōŋLû>įI¤ī ? ĐÛ"bߡÚV(ŖL/ĩÅ RšO#ĸúO"Üķ.ÍQ^~ʅNAŦÕ÷C6hį'”ĢW<â¤iÔBG7Ņ|l^C,Ŧ;JšˇĪŦhŪq}ųNųT;f:/"NJciĮVÄD×$ũ‘D™i…â­$KžS°\ë8fëŒĀ”c–Ô-Ͱj6fûਓŋHxŅų°`~¤gŽƒs\×l‚ŖŽ%xæIˤ9ØLo0™ÎsÂåEÕú=-pĮ¯;qõ5Xgōf 5¯Ė46$é„UŊĐ#ŪXéZ‡ˆ)w"2LRKÖĐĨ}áļöF,f! v"/Zj‚˜÷D/ŸŠ¨ö@ŒfžCĀFķÅf>•ģoĢŧį߯†›ëŽ*Ę×Åø<×ļ5ągũŅ^™ņ{:6+h¯?uŒ@ā]ÅŠ•H¤\‰#ōë•GüĪķMMM6Á8Ī_č?Šá­D|—ņęĶLäʃĢKŧMļB]4Ÿŋ#âŌo bųV"NBƒüžĘņ+7?âi ųŧ3ŧŠ?›$ŠŅgļôŌä#›FdŋË#Š>ĒöYęŽmČø'ąëíĩD‡šqŲ‘]b•ų>€āŠŽūXœëŽŋĢĻn ÆĄĮ!Ō}Ī}iÚk÷ŦŒ@?4}‰ÁģČ|ķšx5Vė•å×).jáĘ˙‰p“{¸ž×ßž…û˙u ˙ũ÷ũ1dÍø"&o%Z3t.'å]ŽĒYŖô}Ŋ‘DœH<ōÛ­s ē]s4õ­Û,ß&äLĮaŽkÆã8!—‡eĶ1ێÄ^}=öČCą¯ŧT8+šÄĖQá÷0ÄėæÅįaĘBč>@\Ģ×ßĐËzuÍ;n?CH–‹ąėėlÄoĪFR€ĖŒĒzl}Hun_@—Ũũ6ŠŖPNøxí6ī%Ķ q˛PŸ‹ŧ0?qâÄucWĸčwˆ8í;ˆ|ņų|÷̧rŨņ]¯rxņš•dĨ\­‰]3 iũŋũß'!‹Čg=ؗ\ÛND¸Š¸Ŧ†dƒ'Ÿ›$Ą?Í#.Ÿë\9jt§cs˛˜_āŌ–ˆ¨Ę(Õc•#Ą_ƒXáū1ĐRc-õŖ~‘¸\ŽˆaÕPÉ7ŧ‰“ö‘Öw5â"ÕQœĖΧ\ŋ˙ ņį™¤2ÉĐžÃwÃē¸ģąąņîÆÆÆ{ÜßŖŧüĩīüēƒlXîsmžY[ˆ¸8ãĨOęƒ'ŨõW×ø%„UņŖ<ņyė“uųĪAtŗgģ{Eĩ27ôŌ ų7‘ áķˆņį#ļĪ! ˙#^’ŒÅ€hÛ' ~ŗČD}–u=jA֜q.íIČ{w+ÂaŽčMˆ< šr ˛ß Ú$*Wāúi˛!ü ōŽũYŸ$b"üļųãčÛø†w+Æc˛ÆŸG´f‰ĖšgbĪÄķöĮēÜ;ëß×1žËõãyޟQüž%”H™øÜĻBTË X>IČ\"?fß—Ų’ˇ°dfüėŧ…„{ėEøËk`ūÜ"‘uŠë”ķž{c~qvōhē>blßÍ)Oäæ•Áō4–=ŖÛ0X'Žļ™™™lhŌņĐ]ū-ûĩųü"*U÷Įǃŧ =‰,"ßG"CŽč-w! "/õŨŲí.ũsČîî5¯áÄ'Q 4Ķ÷ŸÖX]æģ˛|Îu.ĸ¯R=™ļmb”ŗ4–ĮÄRVãĄ*} K}ÕõŲjdyČRé„SŠŅŦBvõ‹ŧ´+\YzOûø:äûœģ~•bˇĻD–œz­Ä…Ž?ĢuŦĶ…~.ŅâŽÖ¤q]×÷ˇ nV%0k@ķŊôúĖ$dįë#›y1 q/ē ™ŸEäžĸ闸ņ[ģŸs}_ÎCĶÍquÛ ‘ėŲĶ}īîŌÍD€˛ÅËc)˛¨6"\ũ­Č"ø.â§‹øĸyßBĮø/Wģ ×ēąū(Q7ĨEˆŊ‡ē'•Û|)ûœĢ{P&­Ū[ėúBûQ}Ōsõøņˇ˙b`uâĶ‹š^} åŌ=wB¸äģÚ ß˙äÚúQdĶs1˛–<ˆl ÷@Ū…?qŲ/#›Æ­9ú3"ī‰iWėĢ=T ô*"b~q­ģ Äņ#Ü}ĩĖ dûīŽŌûĢ+ĕī ×/W"›ˆqŽīžãĨÕąCdhéoTįPüū€l„'!ë ŪWUÚB"8ĩh/ąęŅE~ů.dÉũą\čaTüĻģbëšhčŪęú`?˜ Ŗú\v qĻWī‚XŲ(`sgc?ļvö,L¯nĐÖ\}ÅŖŒ‹›?B7Kļ4čzfnÕåŦÅbWĢ&ŋ=ōęĮ)Ž€I,Ä;/–Ū">Ēåąß’ōϊûIy”ģיûY$HÃJĸ(Š.]U?ˆDr ‰€2ëúöf„ƒ,Ž…čUJí{\!­Ē2Úî¤Ņĩ=îŌO?g/‰•ŲŪŧIJ;1”™Gq\åjú-ąÕöEgEĮī ríŸīũ^í|1në‡,Î-eōĐ>.§ŖÖČl‡ œßI@…ÉU*ãÃ^î^d“2=R¨ą(Ķ—%#”!•Lø1ÃÕZy.åįC„‹žO$.Žö]ÔgPˇŊ~*W†Ÿv ˛Ūͧssšš2ũuáND˛0 /$j§AY)?”Hp”# ,h]@Ø˙-I@aûŽĀ6į`Æ<ĖĮĀüđ§˜ŊöÁꑊe€9üÕuØožŲj8,ŸĶ^5} ÖēXˇƒ3n—iš“€6w–ÖŲBf~{ElĒtĖZ5G°•95đēKOVåšËŪÛÎD­tˆzRÛēø¨Įĸ—Î[ätž%Šæt^˙wļeÚĢGâ]‹p#IãœX×õ(7>gJĘÜTķŖēV5Û™W%āķ÷‘ŽÅ‹u]mm¨ąčĖzC)8—ëŗ’{•ęTe cБšļŠæ˛w†šF+ŧQ)&Ž e€üüén°„äąÔ#úĻķÉW Š`DČWkč=잿/Â\úcĖ7ŋ…ič%~Ë ēf§w6A€}éEÂŊöÆl3MOĒV\äĸĸ_ėúp–‰֒Á`b“Ël@nĪåŖ—¤ĻB~‰;Ķ2ųVkōŋ9ŅÆŽsŧŧŨŽárĈd{<}rGĪņ-3–ãFbō6 "Öš~9x<ģ´ŸĢ™×›¸ŽĩöŸ‡ˆŅ?‹Äø2‘>4Ņđ¯ƒ}Ņáö•á”;ŽĘ:TUĪM°mč5#@lw†!ˇ}ü|ĘŦ e€üā’ĻžŪļ'đE,§!;„R7 K@}/č3ûÎÆ|įlĖO/ÃÔu+œÃ\Ę3g`GÁnĀØfČĩ%°vD@Ä-/Fô7ą”—é‹qC*Ķë‰Lü”RĒ@w\O¤ãŸü(œüëˇëöĒ^ˆNs8b…ûY$˛RŲE>ĨKąÃ5ŽGl:Z WPFœœŌyëBoÄÆfōžžŒd)ģ.Tkí™H™rÍGA•3ÍĢÉs–›ø¨ĢL#–Z †5+CÖŽ ÍÛX{õĩÆ|ņ+Ûmo âkˆŦŗā€Ŋ-Īŧ`Ō` ÛT§ߊw.b‘8q#XJ t›ļâķc3žĻ-Ĩ”Ē'¨ÍˆJwÄø¨Hŋۅ īZD‡\‡ÄZ^‡'–MøÍ†tmų9éj b”ō˙ųaĮ vo"īoÅuaŊ@Y)ãŽ#ÎËiIbče\@ʀ6ZxŠ€§ˆaoÄgp,€îX k×fΞlˇŊp¸~XN0&“ąļ[w'Š6Z–ûƒšČ"õ*âãö"Æüˆ;pŧ„„ĪTÎ8ã”Öƒŧ—*lll|ÃûМáĪú–“kll|}C•“ŌúSLí0‘œ@tĢ)ũg“7ļųÆÆÆ×ÜwUĨV|_씕āō}ĀĖ8—ĸŒ3ĸʲˆf$āAW9á<Žúž[[ĐŊûۈEĄ%Ÿ ž˛Ųŧ­ŠiŖOŋYĀ,jēMgíĘéȄw’ŅŦb6Â;ˆĶ„rĪâ•ė9e–’RJëM Æ# ŅEÆ"Ĩœ”ÖŸŒu!NĮę„*ŧ¯íJIÖK§Üåû¸/ję¸ *|pAtlĩa˛˛?°a¸ ‚ąäķkYąÜ°r…ĨļŽ™žũšm÷îë˜:uĒ7Ž 4Fœ4žMāDĶÅSAĖō =)Ĩ”RJ)ĨÔyÚ  §ŧF'VM°ē'é_YB–ēÄΐņ„tų]MĒÎÛ œÂ@feûuJ)Ĩ”RJ)Ĩ”€|ƒ|r@ū7ŋ–§Ë–ÖÚL˜Ëeėōe;{VÆ.˜ØææĀæsAūšŸKúO~ĸđ|ža=+’RJ)Ĩ”RJ›ũ?rŖō1̤܋%tEXtdate:create2012-10-18T20:57:32+02:00TŌ%tEXtdate:modify2012-10-18T20:57:32+02:00uZėntEXtSoftwarewww.inkscape.org›î<IENDŽB`‚astropy-astropy-201cddb/docs/_static/astropy_banner_96_dark.png000066400000000000000000000365171507226315300250440ustar00rootroot00000000000000‰PNG  IHDRå`#:jŊ pHYsqq{bÉ˙tEXtSoftwarewww.inkscape.org›î< IDATxœíw˜UևßĶ3$#‚H4'\æˆãbF]]ŗĢ~ę.Ž9‡5į„ĢĸĖ‚Y× *b”œĶL÷īûãV3==ŨUÕifÔzŸ§ž™ž}ëÜ[ĄëÔ=÷œs!""""""""""""""âO‚bíēîCDDDDDDXŦŽ;L9­ŲcKDg 3Đ h 4MRvY,cLC|LD|ņ…ĘœÚ>†ˆˆˆˆˆˆt~JYwЈôÄØØčŦY$ņ āKā-Œˇ€ˇíT–IvDDDDDDhę­RÖ#4fûbôÖ­ĨĻ—/÷s:ošĄZj7""""âONŊSĘē‹ŽĀ™Ā‘o4œc'V˙÷#ÆíÄyĀÎbeô%"+’zOdųz–™í\›ũ‰ˆˆˆ(”zĄ”õ,eüĘAgûŊō&°n˜ûUž~nf-îŗXQĞE€¤ŋgųzĻ™mX›ũ‰ˆˆˆ(”X]6ŽÁÄôŽåW~F"öAPô퀡nKī֎ˇąˆotû•öŦDä@¤t#""ūPԙRÖ­ėCSÆį t,@aoķž…Ļ] ŦIĄ ž# ^×m<Ģ[X¯”į'"męēŤ֕˛nfŨĘÛĀ(Äv)ɰJyΰ2Xw“b)úūĀxŨÂĨ9K!‰”rDDÄŠZSĘLšnâlāsô,Šr ĢŧįLphžU1ÍâmI0Z7sząĪUDh"Ĩņ‡ĸŧ6ŅÍl‡xŧ‘qā6xÔûPš*—šŋKœĐûÄ! ¯F9ânŨD–p‰ &QLáDsĘ(Jî}­8¸ hXęļ°œü4XĘAŖĻ)‰Ãč3aÜŊŲ÷Ī˙…`Ë)æÚAR X4ČR%ōžŽˆˆøŨQ2ĨŦÁ4Ļ w'š‚Rĩ‚ō&°ÁöpđcĐ´#Lx^?ö¸ļ_> c?ÚŌ#\ÄIQ‘Ō#i`ĻO•H)GDDüî(‰RÖul„xØĄōs&Š"7…^O@—C`áXc}7ĸޝrß˙ō|tüô(īīuö\Rx§ƒ‘´)°°.˙wK\Ūo€ŲĀ`đ?ā-3[^âūl ôđļ €õp `âĀ,`đđŽ™ô$Š0ƧJ*eI­€#€Ũļ¸ũ\ÜõxxÉĖrNH#Š)°îo‚›W/ĮyY,~>Įãī ?’ˆˆˆÚ¤čJYWą=ÆĢ@‹bË.ũž{CY‹zÅ2Xĩ>ģž¸–ĪÍŊ q†]ÆŨ…÷5ƒh—ÅęX\ęŅ\ã˛_]gf?ą?M€cpYØļÍa×qĀÃA/ ’ŽÃ-8Ō ˇāH3`}`ŸŨĀÂũØÅĖ&fiw3Ü M&æšŲÆöi\œŒ˙”M[3û%D˙’r{gBîö%đ đ™%ŸģzRNs6 Æ2ƝYëög}*č‹K´=îE­ .•íLÄDŒ÷0†Û0r~PÖ'á­'âöSs?ĸmõĄ)e^d%6‚ĸũ†""R)ĒR֕ôā˙Ã˛ˆ æąO§ũ`ĀpgŌöŖrš›Ÿžø||3üúE.­ŦDėl˙$§˛á)žŖ€3€ŋ(n pĄ™ųLŦ‡î×nĀŖ8…™/ŋ7÷dS’Ļí hÏ­Íėë,ín dü§ô™YeJũį ~YúÕĖZ‡éœ¤­˙@Aáwŗ€K‡ÍŦ Ÿõĸ#eLö>ޞá5“é¨/›#ū@8_ŖHpŠôĩ~Tߊ7§bTŨĮelnĪķmØũsA}xw<Cmøę˙#"ŠJŅBĸt%‡"^%Á:%M’OHTęÖiŋp‡]ŪĘÁ–GÂņ /Ų;\Œt#Äŗēž°õœ%•Kú0xˆÂ2ĀZĀ=’n)°ogīP˜BhÜŒ—´K˛j“nēIûīÎz16¨‚$“t6ÎD_h<|Kā`´¤P/!i–úAũYOŊš1gÍIUČˀ鸗°Uir ؏Ÿ¨7×Ē?eĄZoČܲŦŽ8s>‚¨kWđßR´ERĘú7G“āDã‚ãK­ČßģéĪŦĖ)č›Áry!čÂrîßPuŧ‡üxā.œŠļ؜+éĸ|v”t ÎŖž˜qî]€w%õ/ĸĖRĶ @Rw`áÍĘžJYRÎė\덅=1ŪčģŦVĘęÃQTđŊ—ŋ>é?8c ē˛ļ § g°&Ægc|™"/†q1<ĢūÁĮmCY<Ŋē@œ ž%ķ,įPĒĮYD3^-z?T5˜}ˆķ ĘęjôĢ\ļesҍ‹aՒđYš~x ;%Če;^—ŗ[ž§ļ'°Ežû†å*I]sŲARāJã$8xŊrKE+ÍĄĀ9ė—U)K2ÜÜ˙‰ö-âFĖ]Š Ģ™úŗ–úđđĐÜ+ąˇ g{Îm6Œ‰ŠĄ‚ö•6Œ‰6Œ;lÛGãœā’ôŖ‚{BõĀR|7Œ Xũ =¨ˆ#R> ĩGŖEi"JGAJYƒéŽÃ$ú*ÂÜYÎ[Ί˙Ķ{aátך •PÖ}|;Ä+sa€Û58¯ķ|=Î{ē””7ä¸Ī­Tyxûņ30xˇ>õ8œv6–ũĖl‘OúƸųđrÜĪoŪô*œ˙@)i ŧ"ŠĐ5ʛRÁĀņŪį%Ā,`{Æ[a…ØpūKŨqæí$'Ē/Įî;Œņ¤žĪDq_ft ë@5E™Ž#JJŪŖ]ÆÆ‚H™W̎dŌŊõĀN |žqˆyĶ[Ëæĸ{w„y“rnŪ` ]uŨßŦxsЎeųzđđ2đ đ nžŽ9°)pÎ+:h~NĀĻföCˆūlˆSļ~/ß§›Ų;öoôÎĄĻ§ö föh–v_Æ3§ĐđS*PÍ4š#Ė,ãE pôwūw#ûīhđƒ÷}sÜüûÜlN^’öFîey"ÎÉî Üĩo†;'pJ$ĖŧėSf¨øĒõąēŖW*cˆqx!Đ:”íˆņ>UĻâŲŦ¤‹ŊŠī‹šzs2ÆŪĮ Œļ6ŒYųö#Mö@ŒĮŧ3i@;ęûrQy)e]Äē*c,.6öw‹ũlzˆËü•Žân>9•éŸĄûz¸ŅsXÜ ÁĪ6—Îv?šôORCÜÃ7ՙj p;đˆ™ų†üxąŧ™Z:יY`lĩ¤3€;}Ēü†ķfž Į€~¸Qw;Üąä4‘4§āŗQpœrĨœ—k€Ėlõ+Ą¤5€Íl|†ļ_ábũX\ ܑ*;MÖVĀ`›}=ĐĖ^ QĪÉÎŦ”Ÿ )ƒŠaÖ­áQ —ÛpŽōŨg?Öd fŒú0ΡaäȸZv^ƅĄqĢ ãŧbȍˆČFÎNSŒ‡Q‘ō:m ũ.XÛ`Ŋΰ^GXģuU,qŧ–́%ŗ`Ö4},L˙~›w“q&vၞHÔTČmw€]/„wŽÍĩŠv4Ŗ/đl.;™Ų*I—áæë~ƅĩĄõ”UĀ 3{,Ķ—^ČW …ėqÁ yp€™ŊëWÉĖž–´+n$ä3pƒ¤×ŗ)ø@ÄÅ6‚ëķÚ7­x˜Y\HÕ ˙ņ‚Ģ-ŗŊ {ƒĨęÃXŊ8ˉP¸RÖÁ4ÃÅX{ EĻëˆŌ“ķHYsĄ”ķÂÎ{ū_ū `ŊN°Į?°ŗĮa§}›õĘÍé띛aų<'Ģb9Ė ˜Z-o„õē5Ÿcß]„2'VÃĖdf÷˜‘)hi,#\ tĐũōģžÎ(ķķQČûŦŋîËE¨—1Ė×ôëqj.rKNŒa@Õ‘qdĐ.6’/§Č8Ąā~X5¯ë'#…Q„VĘ:—&’ A~ĄN[ũãGėø‘°ŅNE> Ã.Øņ#ą_fÃ)ËĘUhč)n˙¯‡Ŗˇ‚‰ƒ†÷…Îûä|*Y˜KaF a¯‚R ž.Šm94ŪÃŽ=ˆ0ąŲWį™ë.LJebIĩ“…/6ŒšĀ‡)E;‡Û1åĨEå%ũČ õg}\XbR^NSOųZ)'büŅ%¯d -ˇÂN;öi7W\j6;;˙kØõ\÷2¤0'žŽîŲ 9â•čé`‰˙Ô¨|`9 ‡–âpƒ0ŗyĀŌ€jA&N 0mčúĀ{ŋŗĖ\…"āĖŧįd|ŋį<–3ŪĸĪTk„K,RīĻüŋU¨¤ x˜ī}Z‡2Ëģũ §ĘįfŦā›ŧeEDä@(ĨŦsŲÚœŸ×čx§AØŲŸ@ĮZ~N7\ë}‹{(_#¸¯ßŋ ņ„ûŅ,ôüūōÛvƒN{æ:Zî¨sØŧø[…¤F’ļ‘ÔWŌ’î•4ŠāĖPaFJCqņÄ~t>ô$ŋ9ß? /šŲWųî,Š#.ŪŲ7Í,čĨʏ‘!ęô(@~ņą”ÖFcšąYā.CYŽ‹‹ORˆe*Õt9xEԁJYƒ‰I<ˆhS*Ėka'ŽÄúß ķļ"Nרß߇ĩZå6Â˙b(|˙ϝhÛõŒ`+Aúįbž¤v’Ž’tģ¤qš€Į/ā[œŠķ mā#üc~đ{d‹›Ng`¤¤o$]"ŠT JÔ5/¸?ƒĪ l#ĖūÅJŊY,mdš œã‰qUsŋ{čPrÎ\Ļ~´v]ŨrŒgr•‘/Á#åšE‚î9ĨÂ\§ ö÷÷`ËôĪō…đË8øn´Û&ŋ}[ĩöq.´ũ vÚkШiN#[ŧßŦ_[ ëļķąÔÜ{į~ՑÔ^ŌŲ’>Ā-VņnYŋÉ?WrØũūËΖÍq1ģS$Ŋ"éIÅĖ™]ךH"Ė|N>ąŌĢ1ŗ™g‡ĢoNz3Ģ}˛ā—FÆD´ÚSÛ°ÕŲÆÂ“`ÉBã]{ĄZĻąˆˆ’â;OŖA4¸"'ŸÃĻm°ŗŪƒ9.´t.Œ}÷ĻSĀ ˛,5[ÖÚn ›ímw8´ š„o›ŽØ Ņ]û9/ë0Lũ žyļ<(ķ÷ąrč:Ūŧ9œđ k|_ĢØ‹,#5,JáFĘ4e(0ĪÛ¯ 5×|ΆzĶH††Ŧĸđ鉈ˆœČĒ”u<•ā˛Đ ,Ö4Zúc8f|…îØŨ´+|ūTVäû ßŋëäÆ-P4Ėl™õŽ„ŧ펓ô´¤zĨj‘ š~€•Eh'(f™—Ĥ>‘jŖ =-ãĨûLMb>f9ƑT%UzÕ ĪŠˆ¨5˛”s4Đ6t¸Īaˇ@ĮąĮņ 4âRtÕ_ā›Ņšz/gß>{]Ũ Ļ|ęßžvĖ` į˛NĀį>B‹ÁÆ{æö"ˇ$Ŗ¤ë› ÷āˇÄ¸E ژYs3ëaf'‹CĘČ 3{Ø 8…p @dâā%/7ôŸ0s)kĄ ‘抰é[kĘÚ¤Ū÷ŌW2îŖJŠ÷öâŽC4œbēVdēŽ¨}˛”ÅßB+ÄmûÁ!D f vWŽÉkÄĀmÁ tĶ0q”?Zt„ŽoÂū|¸¯8ë°cN/ ? $ü#¨.†õf`[3ÛÜĖÎ6ŗĄf6#ÄžEÁĖV˜Ųƒfļ-.ŪõyČmņ œYūî J@Âŧ,ēÄ"‡ŧÕ¯%3•7ŸČ­ö<ßbŧī}lHEđ2ęCgĒ2Û-e9/åŌfDD1Ȩ”u2;‘ [¨0Ÿ†kcGŪÜԌoĐĩ;ÁäΊ7:δ­\îęĶüķ\ØA—BŦA8e?ųXîķLčŧsxs¸Ûšg‘€¤Ö8EÄ@'3ģĀĖōĨ3{ĮĖĮ­ūô\Nį° ”T°wúīŒi!ęäč5YoĒĒåŊäbIhæ”Ļ<< ÕŌ’žXßĒ™Ž_°7rGDŒJ9§gU(éfëŪWBĶ6ū­üöēyo˜ûsáŖá0Šyųt˙1°ĘĮ2Øŧ=lžo8yņLų,ģŦ6ÛP#s˜ŸŠ_ž^°āÖ^; Î…f6ĐsēĒw˜Ųofv#nmᐒ Âg7āĖ’vŦū¸Ž5xė{lNđœl./OĨ'ÁvÕ>g^Ã؟†<Ģ×UŪJ‡˛ŊoũTĶĩņTÎíEDJYeM‹Ķ?”˛jŊ%ėđ ]:ŨŧĖ˙5ühˇõ–ĐûJė’˙a×OÅŽû ģ| zļÜ߅E)īá ˙ÕÛŦûQá_~ōQĘ ›äæėĪžŌŌ[*(˙sfvS@z™%Ėl(.9ÅTwŪÉāõ)s-đ9Š^ƙ)4ÛV˜tzÎĩŒ‘ę ˛ŒųĄ^ęĒ‹Ę*,Åá+–=×c3ǍĖf>!C:""ŠK͑rŒMB9wđˆų8lJčūcaö”pŖßMzbž…]ų5Öë2gnž´číģaŨ`įŊ†Ũģ;įčč?—ĢWo>NĮ]ĘÂÍ+˙6É˙L6ī^Á;ŠŲ؈`ScQp¯MĖŦŌĖ—TmD¸DAŠ,įĩÂë3[BpNņŽ’‚ÖZö#p•%ÜĸõUSƟŲ;×;›œ{ŠzvZĮŗ:üĸÎÉų–´U)#ŗ1•āƒZC}؊ÔÄ4âÕ|eŲp&o{×Ĩ"ëÜU‹WD^×uH5ĨŦA408(Ô(y˙s Ė'RgŅ,ôĖÅ!rėœ‘pЅųAûí°ËŪĮŽŋŽUs„;ÆßsšöۇŨ. HĒÔhÍ\æÅũ”r˜đ—œrJjŒIyË2ŗ ‚į—Ä€)åF@ûPĒ{žÆßzđwIA”LÜ@•ķR6ž*p•Ģâ’ā””OōÖW.„T‡¯ãŌŋÔalLÕŧũTFđQíEDäMõ‘ō2v!Aŗ@EĒėčoͰÃâyÁĘũØÛ`Û,i,Cc°į)ØÕãĄÃöÕMâ?øO•YË.áFˇ+r/”7=R–|•jPč‡áœ§B!Š0X/ė>ō:?I:ĩ€‘hPJĮ,9VĢÆ[xßuę3û‰ā•œã­„Nĸ!éTāā€j¸5—ë:u0ūēēĀxĮ†ņ}AB0—f`õŖúšßqOioˆû=DD”ŒęJ9Î.ĄŧŸ7Ũšm˜]ęĸYđöÃÁ jĮ#`ŸŋīhZvÂūų>ėĐŋĒŋsv1ŅŲXŗY¸ŅíĒ€¤Jå3Ī“gvôōv‡ņĻĸ’vÄeô*Ęē™’ārˇî^””ĶB’vüæGã@Ā>.QÉyš(ą:æß–{O„É~æĨ/õĪëxĀĖÂŧՍ¸ŠÔĩŊō…Šôžõ>–!ŽMĢŌoõ ž.´ŊˆˆB¨nžN°cđ(ŦûáérĒĄ7Ëũ•\YCėˆkŠ{4 c˙/ô8ÆS€‚U>K¯rå¨FVåKkœ§l[Ė|cS 8%晒ū–m¤*iI7 L”t¤A;KÚCM>0ŗ0Š ?'X‰m<#)Ŗˇ{˜>×föđPˆĒ€ąŪzŲ5Ėü’ēJzxŒ`gˇšĀŋrîluŠ–šĶ YJ}K˙†į?Ÿ\÷“ŧ_Ä)ėž}ęC Ɨ6‚ŧׯŽˆ(Õ´ĸ{(Ã͖{ų˙ūāĮå^§B˂r"d'V†ö0Z8 &ŧ }ęŠĨáŒUM’*-]|ĖI”ŨôjfĢ$Ŋ ôö‘ÙO‘4ø÷pl{ĀBøÔœĄ´/iâŋppǤ1¸ū_ĶqËމ3ˇ „,Ū¯U<đ=f6[ŌÛįīė!é\,nō<õZKjffyŦZÎö"Øû|SÜyž-)yŽ›á,šxiŸlfAK:úg„úp´ Īi9΍mI0”*%ŋŠ8§Ôˇėy&ĢoâĻ4:1žŊQũŅę9÷ČÁ+ĸÎY­”ՏöRˆÕ‹Öl úLiN3ƒķØ^Á v ĸŧ!vîPtË.?u6Î §L×mí˙ũ’ágĸ˜Åéü•r’ŽŪVR$­yų­*fĀŪ–/_Ãj3cž ÜBÍ kšÅŋįĐfÉ0ŗÅ’Å'…‰Õ^—Ö4Ž5ŗ/ČPlŽøH}¸„áܞĪ|Ŧúŗŧ‰[=Ėa\h/2ĄũKå>Ēü ŖĐj¯kĄČtQ÷T=dÅ&ĄĖ¸vđWrãGËØ`h_h’ĸŦą.vĘ=žU4kZ8ķõ†Ģ_Íū9ŦŖ—ˆųgq2ŗ—aõBíÅbÁąŊŲØhZÄždbpĸ™åŌĮ!P؍āpĄZÅĖÆãF÷E]Õ+ûK‹&Íh ÜJ>Sŋ@Į˛j¨7}Š`<Š#|ã>FˆÜŊ9˛€€Ë /ú¨;P5ķĄ gJŅیˆČ‘*íj´åđÔÎÕA}˙q°ŖĶļø÷ęįoō8”,´ęč˙ũ„÷B9zY[Ÿ…â0sRؐ¨īm¨ˇÖĢ?ĮāĖŋ…R \ ô%ĪTŠfö*îáUĒŦOĢ€~fæ“6­&ž?ÜŋHĨ^)e3{gÆ.ļV—Uí´"†@@•c^7ŧ¤Ū|¤>œ”me&õ¤\ŊéĨ>ŒÄxh™ōõ=lKŊ?̰w¨D<â},'ÁĢ$ÃÅ,%GÔV›¯ ڇ‰oąæíü+ü0&ĐlüGÉē} vŅķ°~‰ÃLįĪ„é߅3¸mž[öīfūáô‚,œŠÔĖĻHę '˙pĻąĀéIe'iÎų)gĖėkI;Įã”üÆyö)€ãĖ,¯ØP3ûXRÜĘTķą‹$ĢWq瀙}&i;œÉĩoDN™ŲEUEīąŠmˆq-Æ@™—"s'*ˆĢŸ“_0Úã^đZĻIZŽqĄ ã.ŠaTĪF‚û)ãbÜÜura˜J`h [ˆMÕH9A‡PáP-|ōŦZsg›p7Ú*ģŒ_'ÃcŅåûĀ€„ĸ×‚Gˇn Í}’LM:*–ā“°ũ3ŗ÷qs´š*Ŧ™Ā‰@÷´ŅgAst^ë‡qŠŊ?0Ļqs€Ģ€Žų*ä”~ŊėyyÎļ Ī•RcfŗÍŦpšßIfãLÕ[]!{؋,ŗœM‚-C¨z-/ÃŨŋGįyJûj*䔹­ +}ŧ´ŊČ4 ũ<ŧaÃB…"FD”œÕ#e-B›ųä}˜÷‹SrAŦíŗHŌĪß8Å6ãGte/ė_/ÃÚž+æĮĘeđâáœŧēûû\iü[á=¯ãš=\Íl2ĐCŌA8/į=ɜõk.āS¸+2e;y7w›‰Đ ÍĖĀsĀs’ļÄÅyî ėˆ˙hu:đđ:đt–>慙Ŋī,ÅĨLÜ įų‰Jœa”ˇe3ë˙†ŗ øQōô”fö:đ礀Ŗq/ >ķ)ĖŪōūFš™ĪriÅÃFōpŦúōoÄQũÛdŠ> x ņP­‡!÷!L)‰ŧŽ#ę U!Q‰iũRk.žN95öÉ}°dA•Œo?Aįíˆ]ņ*lX,kŠCO] ÃMŲZĪô\iŒ ­”§ķJ~yĢŊŅā+^2Œ-p <ÖÂ)㟁o‚B{ŧ5—‹ēMĀĀ¯”TŽķ mO•É}!.v†™Í,fÛúĮy‰?æĨí„ķP^7w¸7Oûc˜0(/\čúŌõ87<ĢGr*ĸn aCĒÂßf?ĶęŌīeāē¸B}iI‚ŽĀ†Ä('Îo”ķƒŊ@Iī_*ų1%ēz |äGDÔ.UJYë­:ü<¯‰pķŗ~+KaÕÜ/“Đš;agŪģ ÕÅ@>} žģ1ä\ōÎĐÁĮšmÆ0sr¨feŧË#d$O™ŒŖp¯ãĸã9^ũH=X›×…Ņc°~afķŠoË-fĀ3 ×/Ķp‡Žū_Œ´‘yÔ#"jÔ­FĐIDAT9å†ĄBz‚rۇ‘á7BmÚĒæœîĸyčš#ĐõĮ@eyÆŧ†n8֙ŲCôՎđŅ›O† …"/Š KDDD8ĒæŖĸĄ"ęŠÃ^ ŌŗÜįĨr­fÁáP `ū¯ŲetÜ&û~o=…[~*sŒ‚IÄaČŋŅeÃb/ûVĐąnŌēû„]J0ú‰°ĄPķXÉûšu:""ĸ˜¨7í¨Jp3ŸFŧV—ũ‰ˆH§J)Į™jÄ7ĪGĄ6mNŲM÷ÉŅ´%´ÛÜįĨ`)Ü0õZ=p!LāŋāĒđôuč¯ĐŖ˙‚x"ÜČÖĘąŋߝ].¸šä?…’§G §GDÔ9Ĩęš÷´ ÍmԈˆRS•fæúē†$ŋ›÷[ö:kŽ ×eūĢjÂ˙°ƒNÉúŊíy zä2_$*ᙛĐ37lÜ :n­;šší)_Á7Ü<ķ/ô=ÛÉôACŽŽ9Cœų*&îͯ#Å@ũ)Ŗ‚ÔüžÖU_""˛Qåč—=JŋMķŸUî´-|`ĨũęC˙ī:žž–yĻō@×(ÁˇcÜV ļė°‚Õ—īÁįo‡“'FÛ;|WxĮ"""ōf‡c$WÁų܆×Gšˆ?ĢÍ×11;”Y÷‡§ßÎÛËøų˜äÔŦväÅUõÃÍŲgkŪü”û8ŖKčĄËC;x™đOĀQRԟ†ĀāĒn¨ŗÎDDøę}=1”Ō PĘļíŸ^}ÔŋgG]ÛíZņe[ˇvãĐ<`E¨×…qárf#žĨ%#ũFDD””J.ÄVgnû††õIĄģæ:°ÍÁ kū4$Ā‚Ôŧ5vÛ[nĨ§bŠS§kOėÁ1Đ> ˛„n<ÍŊ˜„9OđđŧŋЈˆˆRĄŪėˆx‰ĒŦgOÚ^¯Ë>EDøąZ)$Hđu¨đž^õjNi šfũėßà Úc~Ûī›ûč7hkĐ;í:ėöŅĐ,=G~ŋŪ~!ü(9Î˙Ĩå'‹ˆˆ¨4˜˜úrƛ$SžŠoiĀéuÛŗˆĒ9RĮwâJ3jÆ"Ĩ{?¯ą6öÚlh%]öǍWXbŲāa7ŊäšüH$`ø}čŪKaņü`š~”7€CNÄN¸ Zļ ˇĪĮ¯Ąķ„Õ+õ<oÆ>fŸ‚úQĪP/:RÆäÕetļįS>×vŖqÂxņFc´zŗÔuf§PÆŪuŲ׈ˆ0TKdKđJ ŠWĀŌÅđžīRÃÆpØáFËžO„Čų‹AŋĶą~ÂNŋ6ČäŨfcėäcÃĻ`Ũ^!˙]2*ãaMâËÍ8-œđˆˆˆŧ‰ŗ'Đqđ8â~ā<Ē+么Ũ#…ņ{ ÚđTĶˤæz§5Ųq?ė?>S3‹ >aņ‚ā^”•cˇŊ ;î\7•īž€ĪŪDĮ¸0Ģ93aĨˇā:Í Ekč°9ļy7Ø~/Øh“Üä|ų?ô÷ĒbĻC`âb[VŠˆ(õn¤Ü‡!¸å,31ã&æsS”M/â÷B ›q|5q\āžą6|´îŊÎÃWŖ{˛dæJ7‰7^ģũečÖ3°éZã“QčÂÃrRČĀ8[›ĸ‡@Äõĸe\Ŋē ÎĨöb¸ÄC%ėOo`[`#`)bÆ'4ā•(fÄīJYa7Á{Ąö>ęlė‚Û˛_ą ŊLš‚^“5ą›‡ÃŽu<+Áã7Ą˙\â?‡\“å;Ûų­™ņį&ŖwUb;Æ ÜģacėÅI°ū†Ųë|ū>:Ĩ§sÔ CY9vöupėyÁÎ_Ĩ`ö tõéđnîų>Lœdãy¸ŊŠˆ(*’āæ]šŲÜēîOą´6°>0ËĖ–ÔuЁ¤-­ųĀ{fļŧŽģQBb™ MÜĘqjÅ ôh@Ŧņ_vƒS.ŸÔŖ˛Ũ|:˙0˜=Ŗā M"Ī?€ún oĖŲ‰LbH}VȒb’:Ję*iŊēîĪīI[K*Šo]÷Ĩt&˙Ŧã~䍤#%=+iĶ”âÃqĮåŗæęīI˙žū ŧtŽÛE”šŒJ™2† 愊ũ}æ^øé[ßFlĐ?aĮ}sStŖ‡ĄŪ›Ã“ˇAE §…$xc(ęˇ5ēb,ZO†°¯bå *]e’&Hš'é„÷m$é˙€ŸÉĀĀlI/KĘÃķíOŸ‡üßS %u‘´{1’ô•w}Sˇ‰’^‘t„¤ŧĖF’ļ’´c1ûZj$]#iš¤ŽUĪÁ…>V ŨĒu$ĩ.>6ļ4ŗ¯kŠíĩ$ÍôîÃŊ Ķ@Ō>’|ĖŠŠd)e™Åœ1*}[š]qĒSnY[‰a7?›ũ%7eˇxēî\´ĪFčî+`aˆ¸į°,^CīGũļAį €'æ›l†Up¨eYņ:Wƒ-€d÷4­¤&Ā+ĀÕ8Ĩ|p$p °đš¤ōėūôÜ \œ‘V~.=wōē@¸ŪÛnFÛO{ŸķájøŨ-ÚhLļAC§āŸî*yę†­p+ų 1ŗßĖ,¤sNQč4–âžų˛!î>> ú3ũ<{Ų‚Ķp7FVđŲ{0ōqŦˇĶöZë`wŋLbāî0õ‡Üz9į7¸k0zđ:ŦĮ~°īaX}aũ€…#ĒuT0å{ôáëđîËhĖģ°jeĀ>RÆblß2%|GōâÜ(÷āfI­ĖĖgaëÕ\ėÜ\`ļzÅėg$Ũldf‘—xŧšÖ›3|ÕŧDMÎ3ŗjĄt’.>Αô3›’ŖĖRõĩÎ1ŗ¯pĻŨ?*MŊŋu17~$Îá÷+ā8Ig˜Y>&Ë?ėũW*˛*eƒx%üÃÄ+aéšs°ŋėí:e¯ÔbbOŧObĐAđÍįš÷vų ôæHxs¤Ķ—­Ú`[mm:BË ’nØ–.‚UĢ`Ū,˜5ũôLü–,ĘŊMŸŪÄbô˛ ŦeY’b8ķÜķĀ‹Ām@?đ_Ō3}Œū‘ĸ0ŗŸqŖįôũZĮáKáŪr_Jßߛg]hfoIÚ ”/šŲĢ^Í€ĩ€€§Í,‘"c[`3{Đëī)¸°–qĀfļÜsÜ9g)øxÄĖæ¤Čhƒ›?üÔĖĒ] IGf64Ĩė@ ff/KÚø+îÁņĨ'{iŲ›Ų—Ūį=€-Æ’’S“€_]€ˇĖėĮ´~p"0ŨĖrĘģėƒ§€í€$5zâ~jĖIĖžÅYC:‰”ž~efĨėR)Š ÷ŪXŧbfīdmŪųØXøxĖĖf§ÕÛÄëãc@#āx\ČŌΏ{Āž+$’ē]į38ĢUz&Ķqķ°ĶGÍlRšŒCåf6JŌ@āk3ģĶûž‹Wļ9P ŒžKũ=Hjėŗh4ÎÄåz¸Ø˜mf#2ô'œ%äŠt§4ī7ÕĶû¸‡wŨĮ™Ų§)uč@מÁ]‹ßŌätöžÄ]ŗ3p÷ûÕföK“ęöYw˙÷ģ8Į;Žų•%m‡{>ĩÁã'Íėģ”ūĨCRßü×ĖKÚhgfO{Ī‚“eĀÅÉķ+ŠîŲŌʓ?ÄĖ&fčī‘{l€×îBāU3{;Ëqîäŋ‰Āãf63­Nī\ü§/OÆŨ ?÷™Ų\īÚ ÄũŪĻzįā'o˙=2Y:$ũX`f/fęcF›ō\|Sjë×MZšB,^¨ÄÉ„“YŠm“‚ˇ••]čú$€¤ŨŊŗļŸ÷ųIo˛´ũūæí:ׯ¤Ŋ$͗T)iœ¤ižŒ7$­•V÷3I¯IēER…¤)Ū~’4PŌI’Vxå+Ŋō+Ķd\蕐´@Ō I‹Ŋ˛%m.i’¤Ų’æzåߤü¸‘´¯W~q†ã™*éë´˛W$‘t¤U^d›/§ÕŨĮ+ŋĀû|x–;úiI%%ä,éũčæÕ;ßįÜO“ô]–īūîí¤¤ļŪy~*CŊMŧz˙’tv–žŪėÕŨØûü€¤Ņ’–zį"îĮi˛×’ôēˇĪ4šûŖRî~Ų;­îq^ŊŖ$ũ,wífze %ųĻŌ“tŖW×7DnîYJ™{–t‚Wö˜×ˇi’žõʖIÚ+MÆûr÷÷y^’—sŒ|$åĮz}—¤ģŌdę•"wŋ/—ģ÷[ËũF–KjJ’>ōę× MuûĨs­÷]šß‡$M—ô…ÜŊŧHNĻĘāÕ;ĀĢģĖëO3Ÿķ:ĐÛgš9áų’ÉPīšûeǤQŪ5^%÷ĸƒ¤3ƒä’îûmā¯E’ÆĨČŋ^î^œ/÷›]ė]ķĶúŅɓ{‘¤Uķ^>8­~cIÃŊ}~I9‹%õJĢÛ×ĢwĸwūfÉÍŗKŌ—rŋĮĪäî›_ŊōŲ’š{ûoāÉ~&ÃųëâÕ˙wļk‘ųu i|~ TTģ-qųŠYŽCņ¸÷^ĢøfåĢ÷ũlK+:×Ūüˆ¤;%-‘ÔØû|Ŋwŗų:NHzØ;Ķۆl§­ĒcW¯Ė$ öä<’V˙3¯_ˍ&ķė^åə)ЇWŪÆûŧPnž;)#Š”—H:Ō+[SŌĮ^ų<šZ™ˇ=ã•÷K‘‘RŽË=Ŧģ{ekIúēŗeJŨjJ9Ĩ|ŧ¤YÚ{+ũŊōkäí}ÎFĨė]ƒ¤2ÜĖ+Š zI˙įÕÛ<Ĩlޤ/2ČM*eIzHŪKWĘ1JĢ˙¨W~Ĩ<%"iš‡ÚĨ(ZU)åUr/e^ų?“2˛¯^1”ō?#§ĨÕûMî9Đ yŪŧķÕ8ĨÎ1^û'fhįšßâôä1Ϝˇ“ŧũ^VÕŊŲ\ŌŪy;(ENR)Įå^¤ÖöĘ÷ō꾝Ą]Iē5ĨŊM%M–Sč]Rę&•r…¤ķå^ÖĘ%=į•Ī—{F'ŸĪ×yå˙H‘1L™¯yuˇ$W´ Ũã]Xī‚Âl‰ģ¯Vh&|ŽøyG‡’ëģuŽ•mž:ŗKÎ'0Oŧ›|Ϥa)eģzgîĖ€}“oŌ„lëz¯ū_3|÷‘wÃwH)ûLîÍ6ũFíÉéšV~§WžYJYR)•V÷¯üš´ō¤Õā_)eų(eIjV>Č+?"Ĩ,WĨ|dē ¯|ĸ¤wĶë§Õ™&7˛hæmŊö“?ūTü!^ŲIi2ÆH›V¤”ŸÍđŨI3S>ˇ÷Ž˙'ęíÉš!Ĩė8¯ėœ´ēMŊōÎE1”ōąę?č}×3ĨėũL×˧Íä(˛JYR)*OIĻ|W.§ßK+OZ?ļöĭRVÕČkŧŌFØĒR w¤”%•ōûéõŗ´ŲLβukJŲąžŒ^)eåžī’ VʒgHûn˛Ü Fķ´ōNr ōŖ´2IĒá|)7šōš…w|2\Ģäīꞔ˛ä9Ŋ<­î^^ųŗiåmŧōĮSĘôĘĨÕũD)–$AŪØ÷|J‚ËCĮėŪ|zú0ĸa‹íˆŨô$6ønXˇEžĐäƔë63&vˇI|îŠÂ¸Pˆa)e˙~Ã9ų‘tāō}+Naoܙšáģgp÷ʞiå+Í,=šy2¸<Ũ-Yž)¯zļēéåIEbÍ@~ÍŌæúČ|˜…›_@î-x3ܜTqķÁķpsVŖps^˙I•‰ķ¨ŸšÖN\Ÿ0í¤23CŲ ĒŸã=q×ŋ†ĮyĄĮ!ãŠhÕÎąw¯,Ŗ8×/ˆŠ eīx줕/#ķ}Ŧ~™8Hnô“|l“ĄęãŠ>ž#åŖĀŽJ]ãüB&zÎjš°Î;ũŲt?`°‚Ė×âą õ3ŅhHõgÎK¸ķ9 Y`f+€'€$Pa!‚Ĩ~đÎSG`T着™M>ē˧ū›w/7OQĀģãŽohúĩ^Æy›g:ŲžQémÎÄ=G[Ĩ”ŊLĄúīĩ-°~¯Ą”2@ŲdŽ'Á­ĄXBčōĶŅАš4˰cN'öÖØņgCŦŒŧÖG.Õ&ÆÆô°Éĩîé™|ŋHn4øXč!˙ššäM´QČļ6~ķ~léLõū†qwĪæÍĖWæžË–Û4)ģŠŪré_F<īÔĮ€ũTeĄ8 ×oßŅĄĮL܃oŦÎįŧž™•šÅÉ{˜<ė&įČî!>Ę%*Š~Ž“Į2%ŊĸwĖ3 wod’]›$đéS?‰ h$uô(đ p°#î… œbL'Û}{îIōWOn `W`H.÷đģ œŖSĻk6_pō™s{Ę3g4Ž˙‡*Å4 ãŨģrąöųȧ?3’Į85ŊĸĮÜī4Œ0yŋ%ī9ŋķ'¯Í01ÕŸsŪ5PJ{ɲ‡€]T•"yžjĖ5įôŠMá|<J‘UÄŅ?NFw^U^ ÖiŠ]~ąc`ëjcôđr$¸?ļ=lJÉÞĒ!7ÕįŲû06eģv‡g{ĶGˇŲX4ÍbâJ:…,Íđ]DuĀY'’ą}€ŅfVÃܝÅf6ÔÛFšŲ—ŠŪāi<„{Đ&ãÖûšYļY!$ûP#œwŋ4ã÷qo$•Õ Š’Zá~Cûá íÍė0Č=kŸ™MÃY=ŽņŠÅyōÖx ‡ ëĩH)ĪëZxĻâŊqŅŸRũ™ķ>Î{{ĩ?™­4ŗ+pVƒŋá ĪIJëĪ•d·lÎhÉcĪ'TŦdį/€‡qŠŸĄÎl`CxÜđ?Üõ?V՝Ēāî1pJ§>ąmĒÕGÎiė\Ŧ{˜û8iž^=ãũ.üŦS~ŒÄÍCčFsûOōšˇ =āõ­ H:Lå{-āFŠŖĶŋđFûãp&ė5åŧ{§žc3[ˁjŲ “ĸ°ĶhÉųę'MqqÁŠ\€Ŧ<rŽ<]ö—8+@?yŅ!PíüĨģ—īĮĶE^;§šōJąhg:ĮÛ2q !güį$čFlø‡°™o’°ęmõ:–.EįŸ\šp–˙.›ÁVõ†WëxģžĀØdzFxu'{ ÆëqÉž—ônNl \˛ƒŸđ>f6ZŌpyžß•‹Ģk‰K6°8ŽŖ…ĸcfŋČy6īáßœ•Ą)đÎäVl^öÅeF{7'™ģúNž Œ0ŗ@sižŧŠ{)9Ÿė&ō×q/OIú.kXN/›f–tđ&đž¤;Šr6ܸĶĖFųÉȓ;$eĘøsVˆQæĀ^Ūõi‰Kˆ“N yÂ™ī’t;Îęr-­üdœÂØ—ß{îũn„—ʏd6O%ÛLUl‹ŧ˛t3ŲŨ8ÚíI[‘Čs~z÷ŌvDô•×īИYįXVŽË&•‰Ģp/ ûád=Ŋō¸cĢ‘Õ įČģūŨq/gāŌŽœdfOÛގĻdõãpŖU?Ļâŧe¸yÅô-9˜áĩ“ĒDæxe[ā^ĐÎÁe„û ØÃĖŪOkë;ĒLšŠĮû%nžž—ĻöTÜj{Onę=ąĀks6ū<ėõũ]ΜÄüîåÕŖj/ŗÖ8ËŌɸ÷-p÷uú‚?ŲŽ­_›Ÿ5rI{/ƒâ˙{-jÆēņVÜo…Boí)qåEŌĸd’œĻNVŧMyxų!ˇĘV|RąAhg¨ˆˆPHzJ.ą@ãāÚĩsŋ\ƒRX"Šˆ¤ŪĶ,Ũ$ņ'A.ņË2eČđV*[qHŧ%“â-QčmĢ ”ūt(Ŋœ8æÅ×§8[K&Tļ¤O­œ˜ˆ?réķVČKiYÂvÚÉeCûŊ­õ§D.žwŽÂ9^FüÁK,˛HRíú_T6g`ŧ“ã-PØ-qÔÁŌÜŲūJųŪ[CËËļUļāƒĘôRŨÅJFüA‘´Ÿ\6ĢIréJ[”¨Ŋå˛,}+—ą+ZĢļžâŊ õ•ËÅ-I§ÖuŸ"jšė_ĮČåíŸĢ€ÜīĨëHĮ×ãėxsž7Gņæ(ž^ĀļËVŌĒUŲĩō—_ËČŧ͉7į.5gŗāžGD䎜7ęīN/Ÿô‰Ehk†×ÎDš›ˆzŠĒR.UZēֈ?r‹˛Č{‰Ū!¨~­Œĩ.Ũ1á'ÖōĢkÃFc{ėųËE ItmŠŸŧ,Z6Ÿ×:‰5Žø!—8ŋŠC\ĘvÖÄ%9˜žOXHDí!gÛ˜•%[^ÄIkāâĮCũ^kÕ„Ģ YŖr ģĮŒ}pųEˇIīƒŨņ60‹„DĸEˆgˎĐāCÁą0Ÿ,{ĘĮˆˆˆˆˆˆzG^qĘųb3XŧæmhmZTÆØ&›"ļ0،ÆMē‘-ŊšÄĘ&+ŸnÎÕ}’âüX&že)_׃PψˆˆˆˆˆŧŠwÎNrAė­ˇvįÔŠhß>ĮÆD&숈ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ?:˙…ē´ßî¤64IENDŽB`‚astropy-astropy-201cddb/docs/_static/astropy_banner_dark.svg000066400000000000000000000543651507226315300245420ustar00rootroot00000000000000 astropy-astropy-201cddb/docs/_static/astropy_logo.ico000066400000000000000000001003341507226315300231730ustar00rootroot00000000000000@@ (@F  (n@ ( –P (žY(@€ ˙˙ ˙* ˙Vũy ũ›ŦąûÕúûüí÷ųüėũũũíũũũí§Ēû× üĢũ‹ũoüK˙ ˙ ˙˙îûHũũĖúėũķ ûøûü°ĩ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˜Ÿ˙˙ûū ûúûöũōúéûĮũ˙Iķ˙ŋ˙!ũyûĪüđüú˙˙˙˙˙˙ ˙˙>K˙˙ĪĶ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙‡˙˙ ˙˙˙˙˙˙˙˙˙˙˙˙ûú˙đûÚü—û?ã ˙˙ũ|ûÚũö˙˙˙˙˙˙ū˙ũ˙ũ˙LYū˙ĖĪ˙˙ûü˙˙˙˙˙˙˙˙˙˙˙˙˙˙ūū˙˙erū˙ũ˙ũ˙ũ˙ũ˙ũ˙ū˙˙˙˙˙˙˙üûûíúŊ˙Z˙˙üJûĮũö˙˙˙˙ū˙ũ˙ũ˙ũ˙*<ū˙™Ą˙˙ęí˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ÉÎ˙˙3Eũ˙ ũ˙ũ˙ũ˙ũ˙ũ˙ũ˙ũ˙ũ˙ū˙˙˙˙˙˙˙˙đûÄüX˙˙ü•üę˙˙˙˙ū˙ũ˙ũ˙ũ˙1ũ˙“ū˙äč˙˙ûû˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙rū˙ũ˙ũ˙ũ˙ũ˙ũ˙ũ˙ũ˙ũ˙ũ˙ũ˙ũ˙ũ˙˙˙˙˙˙˙üīų´˙=˙ø$ûŽũú˙˙˙˙ũ˙ũ˙ũ˙ũ˙.Cū˙ˇŋ˙˙ųú˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙”Ÿū˙*ũ˙ũ˙ũ˙ũ˙ũ˙ũ˙ũ˙ũ˙ũ˙ũ˙ũ˙ũ˙ũ˙ũ˙ũ˙˙˙˙˙˙˙úâû†˙˙3üÃüū˙˙ũ˙ũ˙ũ˙ũ˙ũ˙^pū˙ĶŲ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙÷ø˙˙…“ū˙+ũ˙ũ˙ũ˙ũ˙ũ˙ũ˙ũ˙ũ˙ũ˙ũ˙ũ˙ũ˙ũ˙ũ˙ũ˙ũ˙ũ˙ũ˙˙˙˙˙ũöü¸˙7 ˙(øÃ!˙˙ ˙˙ ũ˙ ũ˙ ũ˙ũ˙ũ˙sƒū˙åé˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ÅÍū˙Pfũ˙ %ũ˙ũ˙ũ˙ ũ˙ ũ˙ ũ˙ ũ˙ũ˙ũ˙ũ˙ũ˙ũ˙ũ˙ũ˙ũ˙ũ˙ũ˙ũ˙ũ˙ũ˙ū˙˙˙ ˙˙ûŲ ˙i#˙ úē#˙˙"˙˙"ũ˙"ũ˙"ũ˙ ũ˙ ũ˙y‹˙˙îđ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙âåū˙u‡ũ˙ =ũ˙ũ˙ũ˙!ũ˙"ũ˙"ũ˙"ũ˙"ũ˙"ũ˙"ũ˙"ũ˙"ũ˙"ũ˙"ũ˙"ũ˙"ũ˙"ũ˙"ũ˙"ũ˙"ũ˙"ũ˙"ũ˙"ũ˙!ũ˙!˙˙!˙˙ ûėũƒ˙˙$ũ›$ũ˙$˙˙$ũ˙$ũ˙$ũ˙"ũ˙ũ˙mū˙ëî˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ũũ˙˙Ϟū˙8Tũ˙%ũ˙ũ˙!ũ˙$ũ˙$ũ˙$ũ˙$ũ˙$ũ˙#ũ˙#ũ˙#ũ˙"ũ˙"ũ˙"ũ˙"ũ˙#ũ˙#ũ˙#ũ˙$ũ˙$ũ˙$ũ˙$ũ˙$ũ˙$ũ˙#ũ˙#ũ˙$ū˙$˙˙#üõ!ü’'˙ &˙j'ûņ(˙˙&ũ˙&ũ˙&ũ˙&ũ˙!ũ˙Niū˙Ūã˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙įëū˙mƒũ˙5ũ˙ ũ˙ ũ˙%ũ˙&ũ˙&ũ˙&ũ˙%ũ˙%ũ˙$ũ˙#ũ˙#ũ˙"ũ˙"ũ˙"ũ˙"ũ˙"ũ˙"ũ˙#ũ˙#ũ˙$ũ˙$ũ˙%ũ˙&ũ˙&ũ˙&ũ˙&ũ˙&ũ˙&ũ˙&ū˙&˙˙%ũø&üš3˙)˙%(ûØ*˙˙)ũ˙)ũ˙)ũ˙(ũ˙%ũ˙'Iū˙ĮĐ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙Ųßū˙Uqũ˙'ũ˙!ũ˙'ũ˙)ũ˙(ũ˙(ũ˙(ũ˙'ũ˙&ũ˙#ũ˙(ũ˙"Dū˙Pkū˙p†ū˙‡™ū˙’Ŗū˙‘ĸū˙„˜ū˙m„ū˙Njū˙!Cū˙&ũ˙"ũ˙%ũ˙&ũ˙'ũ˙(ũ˙(ũ˙(ũ˙(ũ˙(ũ˙(ũ˙(˙˙&üú'ü˜9˙ ˙+ũ”*ū˙+˙˙*ū˙+ū˙+ū˙)ū˙*ū˙Ž˙˙ūū˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙āå˙˙Qoū˙%ū˙%ū˙+ū˙+ū˙+ū˙+ū˙*ū˙'ū˙$ū˙"Gū˙t‹˙˙ļÂ˙˙ÔÛ˙˙äč˙˙īņ˙˙öø˙˙úû˙˙úû˙˙ö÷˙˙îņ˙˙ãč˙˙ÔÛ˙˙šÅ˙˙}’˙˙/Rū˙'ū˙&ū˙)ū˙)ū˙*ū˙*ū˙*ū˙*ū˙*ū˙*˙˙'ûų'ũ‰UĒ+˙;-üæ.˙˙-ū˙-ū˙-ū˙,ū˙)ū˙Vt˙˙ęî˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙íņ˙˙]zū˙(ū˙'ū˙-ū˙-ū˙-ū˙-ū˙,ū˙$ū˙Fū˙‰˙˙ŌÚ˙˙đķ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ķõ˙˙×Ū˙˙Ŗŗ˙˙?`˙˙)ū˙)ū˙,ū˙,ū˙,ū˙,ū˙,ū˙,ū˙.˙˙,üņ.ũt@˙/ũ™/ū˙0˙˙0ū˙0ū˙0ū˙-ū˙ :ū˙ŋĖ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙z“ū˙1ū˙(ū˙0ū˙/ū˙.ū˙/ū˙,ū˙+ū˙Zw˙˙ĮŅ˙˙øú˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙Üâ˙˙”§˙˙#Lū˙'ū˙.ū˙/ū˙/ū˙/ū˙/ū˙/ū˙0˙˙.üâ-˙I0˙%0ũá2˙˙1ū˙1ū˙1ū˙0ū˙/ū˙b˙˙đķ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙¯ŋū˙Hū˙)ū˙1ū˙2ū˙2ū˙1ū˙.ū˙8ū˙x’˙˙æë˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙üũ˙˙ÄĪ˙˙Ts˙˙+ū˙/ū˙1ū˙0ū˙0ū˙0ū˙1˙˙1˙˙/üČ2˙$@ŋ3ũn3üø5˙˙3ū˙3ū˙3ū˙1ū˙8ū˙ĀĖ˙˙ūū˙˙˙˙˙˙˙˙˙˙˙˙˙˙ëī˙˙Ekū˙,ū˙2ū˙4ū˙3ū˙4ū˙1ū˙8ū˙€š˙˙ņô˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ôö˙˙ÕŪ˙˙ËÕ˙˙ËÖ˙˙×ß˙˙õö˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙âį˙˙q˙˙1ū˙0ū˙3ū˙2ū˙2ū˙2ū˙3˙˙2ū˙0üŸ@˙ã 2üļ6üū6˙˙6ū˙6ū˙5ū˙5ū˙Dlū˙čí˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙”Ģū˙ @ū˙/ū˙6ū˙5ū˙5ū˙4ū˙0ū˙t‘˙˙ņô˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙äé˙˙—­ū˙\~ū˙?eū˙1\ū˙,Xū˙,Xū˙2]ū˙?fū˙Z}ū˙§ū˙Öß˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙īō˙˙u’˙˙3ū˙3ū˙5ū˙5ū˙5ū˙5ū˙6˙˙4üđ4˙X8˙ 5ûå:˙˙8ū˙8ū˙8ū˙7ū˙7ū˙–Ž˙˙ûü˙˙˙˙˙˙˙˙˙˙˙˙˙˙đô˙˙@jū˙0ū˙8ū˙8ū˙8ū˙8ū˙.ū˙Rx˙˙æë˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ņķ˙˙ާū˙:eū˙Eū˙1ū˙*ū˙-ū˙.ū˙.ū˙-ū˙*ū˙0ū˙Cū˙3]ū˙j‹ū˙ËÖū˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙íō˙˙lŒ˙˙1ū˙6ū˙8ū˙7ū˙7ū˙8ū˙8˙˙5ũĖ5˙:˙O9üô<˙˙:ū˙:ū˙:ū˙8ū˙ Bū˙ĪÚ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ŽÁū˙Mū˙3ū˙;ū˙:ū˙;ū˙7ū˙Oū˙ģË˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ÕŪū˙^„ū˙@ū˙/ū˙4ū˙9ū˙:ū˙:ū˙:ū˙9ū˙9ū˙9ū˙8ū˙4ū˙.ū˙6ū˙/]ū˙‹Ĩū˙ķõ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙āį˙˙Mu˙˙3ū˙9ū˙9ū˙9ū˙9ū˙;˙˙8ûũ8˙˙<˙{;ü÷=˙˙<ū˙<ū˙<ū˙;ū˙9hū˙æė˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙cˆū˙<ū˙:ū˙<ū˙<ū˙<ū˙2ū˙x™˙˙õø˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ŌŨū˙Oyū˙6ū˙5ū˙<ū˙=ū˙<ū˙<ū˙<ū˙<ū˙<ū˙<ū˙<ū˙<ū˙<ū˙<ū˙9ū˙2ū˙ Aū˙f‹ū˙āį˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ÅĶ˙˙!Sū˙9ū˙;ū˙;ū˙;ū˙<ū˙=˙˙9üŨ;˙'=ũĸ?ûũ@˙˙?ū˙?ū˙=ū˙?ū˙k˙˙đô˙˙˙˙˙˙˙˙˙˙˙˙˙˙ņô˙˙7gū˙5ū˙>ū˙>ū˙>ū˙;ū˙Mū˙žÎ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ęī˙˙c‰ū˙8ū˙9ū˙?ū˙>ū˙>ū˙>ū˙>ū˙>ū˙>ū˙>ū˙>ū˙>ū˙>ū˙>ū˙>ū˙?ū˙>ū˙7ū˙:ū˙[„ū˙āč˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙üũ˙˙‘Ŧ˙˙=ū˙<ū˙>ū˙>ū˙>ū˙@˙˙>üū>ũˆ˙AūŋB˙˙B˙˙Aū˙Aū˙?ū˙Bū˙•°˙˙úü˙˙˙˙˙˙˙˙˙˙˙˙˙˙É×˙˙#[ū˙9ū˙Aū˙Aū˙Aū˙:ū˙U˙˙äë˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙”°ū˙ Fū˙9ū˙Aū˙@ū˙@ū˙@ū˙>ū˙:ū˙6ū˙5ū˙5ū˙7ū˙;ū˙?ū˙@ū˙@ū˙@ū˙@ū˙Aū˙;ū˙=ū˙eū˙ņõ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ãë˙˙>oū˙>ū˙@ū˙@ū˙@ū˙Aū˙B˙˙?üŨ<˙BûŌD˙˙C˙˙Cū˙Cū˙Aū˙Dū˙ŗĮ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ĸēū˙Tū˙>ū˙Cū˙Cū˙Cū˙9ū˙ŒĢ˙˙ûũ˙˙˙˙˙˙˙˙˙˙˙˙˙˙âę˙˙Evū˙7ū˙Cū˙Cū˙Cū˙>ū˙4ū˙Dū˙Bsū˙s˜ū˙ƒŖ˙˙„Ŗ˙˙j˙˙0fū˙>ū˙:ū˙Bū˙Bū˙Bū˙Bū˙Cū˙;ū˙ Fū˙ˆĻū˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙üũ˙˙§ž˙˙Cū˙@ū˙Bū˙Bū˙Bū˙D˙˙BûúBũhU˙EûãG˙˙Eū˙Eū˙Eū˙Bū˙Fū˙ČÖ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙‡¨ū˙Oū˙Aū˙Eū˙Eū˙Dū˙@ū˙ŽÄ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ŠÁū˙Yū˙?ū˙Fū˙Cū˙9ū˙&aū˙Ą˙˙¨Ā˙˙Ôā˙˙öų˙˙˙˙˙˙˙˙˙˙īķ˙˙ČÖ˙˙—ŗ˙˙?r˙˙?ū˙Bū˙Eū˙Eū˙Eū˙Eū˙=ū˙$_ū˙ĮÖū˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙âę˙˙4kū˙Cū˙Dū˙Dū˙Dū˙D˙˙EũūBüÄ@˙ FúęJ˙˙Hū˙Hū˙Hū˙Eū˙Iū˙Õá˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙xžū˙Mū˙Dū˙Gū˙Gū˙Fū˙ Oū˙žĐ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙‚Ĩū˙Iū˙Eū˙?ū˙Hū˙_Š˙˙ŋŅ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙Ōß˙˙bŽ˙˙Hū˙Eū˙Gū˙Gū˙Gū˙Eū˙Eū˙\‰ū˙ūū˙˙˙˙˙˙˙˙˙˙˙˙˙˙ųû˙˙‰Ē˙˙Eū˙Eū˙Gū˙Gū˙Gū˙I˙˙DûņJ˙4IûåL˙˙Jū˙Jū˙Jū˙Gū˙Lū˙Ųä˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙tœū˙Nū˙Gū˙Jū˙Jū˙Hū˙Zū˙ÃÕ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙p™ū˙Bū˙=ū˙ ]˙˙ŠŦ˙˙ęđ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙čī˙˙]Œ˙˙Bū˙Iū˙Iū˙Iū˙Jū˙Aū˙ `ū˙Æ×ū˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ËÚ˙˙ Pū˙Gū˙Iū˙Iū˙Iū˙K˙˙HüųJ˙xLúāN˙˙L˙˙Lū˙Lū˙Iū˙Nū˙Öã˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙{ĸū˙ Rū˙Iū˙Lū˙Lū˙Jū˙\ū˙ÃÕ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙rœū˙;ū˙.h˙˙ŦÄ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ËÛ˙˙+i˙˙Gū˙Kū˙Kū˙Kū˙Hū˙Nū˙o™ū˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙įî˙˙5pū˙Jū˙Kū˙Kū˙Kū˙K˙˙KüūLüŋOûĶP˙˙N˙˙Nū˙Nū˙Lū˙Oū˙ČŲ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙°ū˙Yū˙Jū˙Nū˙Nū˙Mū˙Rū˙ŧĐ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙€Ļū˙5n˙˙ŽČ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙øû˙˙ŠŽ˙˙Fū˙Mū˙Nū˙Nū˙Nū˙Fū˙9uū˙ô÷˙˙˙˙˙˙˙˙˙˙˙˙˙˙đõ˙˙nš˙˙Mū˙Kū˙Mū˙Mū˙Mū˙O˙˙LûéPüēQū˙Q˙˙Qū˙Qū˙Oū˙Rū˙ąÉ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ŦÆ˙˙bū˙Jū˙Pū˙Pū˙Pū˙Hū˙¨Ã˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ēĪ˙˙šĐ˙˙üũ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ĀÕ˙˙\ū˙Nū˙Pū˙Pū˙Pū˙Iū˙!gū˙ČŲ˙˙˙˙˙˙˙˙˙˙˙˙˙˙ûũ˙˙™š˙˙Pū˙Nū˙Pū˙Pū˙Pū˙R˙˙PüīRũŸQüüT˙˙Sū˙Sū˙Qū˙Tū˙‘ĩ˙˙ųû˙˙˙˙˙˙˙˙˙˙˙˙˙˙Úæ˙˙'mū˙Jū˙Sū˙Sū˙Sū˙Jū˙}§˙˙õø˙˙˙˙˙˙˙˙˙˙˙˙˙˙÷ų˙˙ū˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ûü˙˙üũ˙˙˙˙˙˙˙˙˙˙˙˙˙˙Øä˙˙A˙˙Nū˙Rū˙Rū˙Rū˙Mū˙`ū˙›ŧū˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ēĐ˙˙Tū˙Pū˙Rū˙Rū˙Rū˙T˙˙PüíV˙}UüøV˙˙Uū˙Uū˙Sū˙Tū˙f™˙˙īô˙˙˙˙˙˙˙˙˙˙˙˙˙˙ûũ˙˙F„ū˙Oū˙Tū˙Uū˙Uū˙Qū˙9{˙˙Øå˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙Ņā˙˙œŊ˙˙éđ˙˙˙˙˙˙˙˙˙˙˙˙˙˙æî˙˙_“˙˙Nū˙Tū˙Tū˙Tū˙Pū˙ Zū˙~¨ū˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙Ņā˙˙Vū˙Qū˙Tū˙Tū˙Tū˙V˙˙RüíV˙PWüķY˙˙Wū˙Wū˙Vū˙Uū˙0wū˙åî˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ˆąū˙ _ū˙Sū˙Wū˙Wū˙Vū˙Xū˙ĄÁ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙įī˙˙S˙˙:}ū˙Öä˙˙˙˙˙˙˙˙˙˙˙˙˙˙ėķ˙˙j˙˙Pū˙Vū˙Vū˙Vū˙Tū˙Yū˙jœū˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙Ûį˙˙ ]ū˙Sū˙Vū˙Vū˙Vū˙X˙˙VüíZ˙"Xúė\˙˙Yū˙Yū˙Yū˙Wū˙^ū˙ÃŲ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙Üé˙˙-vū˙Qū˙Yū˙Yū˙Yū˙Rū˙;€˙˙Ųæ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ßę˙˙b™˙˙Qū˙9ū˙Ûč˙˙˙˙˙˙˙˙˙˙˙˙˙˙ëō˙˙hœ˙˙Sū˙Yū˙Yū˙Yū˙Wū˙Zū˙b™ū˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙Ūé˙˙eū˙Wū˙Xū˙Xū˙Xū˙[˙˙Xüíf˙ XüĮ]ũū\˙˙\ū˙\ū˙Zū˙Zū˙~Ŧ˙˙÷ú˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙zĒū˙^ū˙Wū˙\ū˙[ū˙[ū˙Wū˙d›˙˙ęō˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ŋ×˙˙O˙˙Xū˙Kū˙NŽū˙ķ÷˙˙˙˙˙˙˙˙˙˙˙˙˙˙áė˙˙V’˙˙Vū˙[ū˙[ū˙[ū˙Yū˙\ū˙eœū˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙Ũé˙˙ dū˙Yū˙[ū˙[ū˙[ū˙^˙˙[üíf˙_ũ„]üų_˙˙^ū˙^ū˙]ū˙\ū˙*xū˙Üč˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙āë˙˙<„ū˙Xū˙]ū˙^ū˙^ū˙\ū˙_ū˙d˙˙Ķä˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙Ņâ˙˙‡ŗ˙˙)w˙˙Tū˙[ū˙Yū˙t§ū˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙Ōâ˙˙4ū˙Yū˙]ū˙]ū˙]ū˙Zū˙aū˙rĨū˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙Ųį˙˙`ū˙Zū˙]ū˙]ū˙]ū˙`˙˙]üí˙b˙<`ûņc˙˙`ū˙`ū˙`ū˙^ū˙]ū˙˜ŋ˙˙ûũ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ŽĖū˙oū˙Yū˙`ū˙`ū˙`ū˙]ū˙[ū˙Bˆ˙˙—ž˙˙Īá˙˙øú˙˙˙˙˙˙üũ˙˙äî˙˙´Đ˙˙‹ˇ˙˙?‡˙˙Zū˙Yū˙`ū˙Wū˙"vū˙ŊÕū˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙šĶ˙˙eū˙]ū˙_ū˙_ū˙_ū˙\ū˙hū˙Šĩū˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ČÜ˙˙`ū˙\ū˙_ū˙_ū˙_ū˙b˙˙]üímíbũĖdũūbū˙bū˙bū˙bū˙aū˙/€ū˙Úč˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙‘ģū˙iū˙[ū˙aū˙bū˙bū˙aū˙Zū˙_ū˙;†ū˙r¨˙˙°˙˙yŦ˙˙W—ū˙oū˙Wū˙[ū˙aū˙bū˙_ū˙]ū˙p§ū˙øû˙˙˙˙˙˙˙˙˙˙˙˙˙˙ųû˙˙‰ˇ˙˙Yū˙aū˙bū˙bū˙bū˙]ū˙rū˙ŦĖū˙˙˙˙˙˙˙˙˙˙˙˙˙ū˙˙˙­Ė˙˙bū˙_ū˙aū˙aū˙aū˙d˙˙_üíUĒeũwdüųg˙˙eū˙eū˙eū˙dū˙`ū˙yŽ˙˙ųû˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ũū˙˙ēū˙oū˙\ū˙bū˙dū˙dū˙dū˙cū˙_ū˙Zū˙Yū˙Zū˙\ū˙aū˙dū˙dū˙dū˙aū˙]ū˙Gū˙Ôäū˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙Øį˙˙9‡˙˙_ū˙dū˙dū˙dū˙dū˙\ū˙(|ū˙Üę˙˙˙˙˙˙˙˙˙˙˙˙˙˙÷û˙˙‰ˇ˙˙eū˙bū˙dū˙dū˙dū˙g˙˙düíg˙%füâj˙˙gū˙gū˙gū˙gū˙eū˙pū˙°Ī˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ Æū˙3…ū˙cū˙_ū˙fū˙gū˙gū˙gū˙fū˙fū˙fū˙fū˙fū˙fū˙fū˙_ū˙cū˙G‘ū˙ÆŨū˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙üũ˙˙˜Á˙˙cū˙eū˙fū˙fū˙fū˙eū˙`ū˙Gū˙ūū˙˙˙˙˙˙˙˙˙˙˙˙˙˙ėô˙˙Z›ū˙eū˙eū˙fū˙fū˙fū˙i˙˙füí˙iũ—hũũk˙˙iū˙iū˙iū˙hū˙eū˙5‡ū˙Íá˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙Ëáū˙gĨū˙'}ū˙eū˙`ū˙dū˙fū˙gū˙hū˙hū˙gū˙eū˙aū˙bū˙wū˙j§ū˙Øčū˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙Đä˙˙5‡˙˙cū˙hū˙hū˙hū˙hū˙dū˙ pū˙‰šū˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙āí˙˙&ū˙fū˙gū˙hū˙hū˙hū˙k˙˙hüíj˙0kúán˙˙kū˙kū˙kū˙kū˙jū˙eū˙N˜˙˙×é˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙šÖū˙i¨ū˙=ū˙|ū˙ qū˙hū˙dū˙fū˙lū˙vū˙0…ū˙Xžū˙ĢÎū˙üũ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙īö˙˙dĻ˙˙dū˙jū˙kū˙kū˙kū˙kū˙dū˙*„ū˙Øé˙˙˙˙˙˙˙˙˙˙˙˙˙˙ū˙˙˙ŧØ˙˙kū˙hū˙jū˙jū˙jū˙jū˙m˙˙küínũmüūp˙˙nū˙nū˙nū˙nū˙mū˙gū˙Sœ˙˙Ņå˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙áî˙˙ĢĪū˙‡ēū˙mĒū˙_¤ū˙e§ū˙xąū˙˜Ãū˙Ęá˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ōø˙˙|ĩ˙˙nū˙kū˙mū˙mū˙mū˙mū˙jū˙mū˙lĢū˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ôų˙˙qŽ˙˙lū˙lū˙mū˙mū˙mū˙mū˙p˙˙küīu˙%mũÔr˙˙pū˙pū˙pū˙pū˙pū˙oū˙iū˙D•˙˙ˇ×˙˙ûũ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ėô˙˙~ˇ˙˙ tū˙kū˙oū˙oū˙nū˙jū˙kū˙iū˙.‰ū˙Ōå˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙Ųę˙˙#‚ū˙mū˙nū˙oū˙oū˙oū˙oū˙r˙˙oüér˙gpûôu˙˙qū˙rū˙rū˙rū˙rū˙qū˙lū˙ū˙Á˙˙áî˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙Õč˙˙jŦ˙˙sū˙oū˙qū˙qū˙pū˙sū˙~ū˙tū˙tū˙ŋū˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙üũ˙˙•Ä˙˙nū˙oū˙qū˙qū˙qū˙qū˙q˙˙rũūnüÂ`˙tüĢv˙˙u˙˙tū˙tū˙tū˙tū˙tū˙tū˙qū˙sū˙R ˙˙˛Õ˙˙ãđ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙âī˙˙§Ī˙˙:“˙˙oū˙rū˙tū˙tū˙tū˙mū˙%ˆū˙—Æū˙n°ū˙bŠū˙ķø˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙Üė˙˙0ū˙rū˙rū˙sū˙sū˙sū˙sū˙u˙˙süúrũyx˙1vũĶz˙˙wū˙wū˙wū˙wū˙wū˙wū˙vū˙uū˙pū˙ {ū˙W¤˙˙ŠŌ˙˙Ņæ˙˙ëô˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙đ÷˙˙Ôč˙˙ĢŅ˙˙Tĸ˙˙xū˙qū˙uū˙uū˙uū˙vū˙sū˙ vū˙p˛ū˙˙˙˙˙ī÷˙˙åņ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙üũ˙˙‰Ā˙˙sū˙uū˙vū˙vū˙vū˙vū˙vū˙y˙˙uüéw˙/x˙[wûę|˙˙yū˙xū˙xū˙xū˙xū˙yū˙yū˙xū˙wū˙tū˙vū˙+ū˙h°˙˙›Ę˙˙ŧÜ˙˙Ęã˙˙Ōį˙˙Ôč˙˙Ōč˙˙Íå˙˙Ãā˙˙§Ņ˙˙tļ˙˙2“ū˙wū˙sū˙wū˙xū˙xū˙xū˙xū˙xū˙qū˙7”ū˙Ųë˙˙˙˙˙˙˙˙˙˙ũ˙˙˙ũū˙˙˙˙˙˙˙˙˙˙˙˙˙˙Ãß˙˙…ū˙vū˙xū˙xū˙wū˙wū˙wū˙y˙˙x˙˙wüĒ’˙|û„xûų~˙˙{ū˙zū˙zū˙zū˙zū˙zū˙zū˙zū˙zū˙zū˙xū˙wū˙uū˙xū˙…ū˙)ū˙-’ū˙)ū˙Šū˙|ū˙uū˙wū˙xū˙zū˙zū˙zū˙{ū˙{ū˙{ū˙{ū˙vū˙ €ū˙‰Áū˙ūū˙˙˙˙˙˙˙˙˙˙ūū˙˙úü˙˙ū˙˙˙˙˙˙˙Ũí˙˙O¤˙˙vū˙yū˙yū˙yū˙yū˙yū˙zū˙|˙˙wûī|˙H˙Ž˙ {ü—~ũú€˙˙}ū˙}ū˙}ū˙}ū˙|ū˙|ū˙|ū˙|ū˙|ū˙|ū˙|ū˙|ū˙{ū˙{ū˙{ū˙zū˙{ū˙{ū˙{ū˙|ū˙|ū˙|ū˙|ū˙|ū˙|ū˙|ū˙|ū˙|ū˙|ū˙wū˙PĨū˙ôų˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ūū˙˙ũū˙˙õú˙˙†Á˙˙yū˙{ū˙}ū˙|ū˙|ū˙|ū˙|ū˙}˙˙}˙˙zûĢ’˙€˙€ũĸ‚ũũƒ˙˙€ū˙ū˙ū˙ū˙ū˙ū˙ū˙ū˙ū˙ū˙ū˙ū˙~ū˙~ū˙~ū˙~ū˙~ū˙~ū˙~ū˙~ū˙~ū˙~ū˙~ū˙~ū˙~ū˙~ū˙~ū˙yū˙!ū˙ĢÕū˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ëõ˙˙Āū˙„ū˙}ū˙~ū˙~ū˙~ū˙~ū˙ū˙‚˙˙~ûä}˙7„˙ú¨ƒüû…˙˙‚˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙€˙˙}˙˙bą˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙üū˙˙ŋß˙˙/˜˙˙€˙˙€˙˙€˙˙€˙˙€˙˙ƒ˙˙€ũųũ€˙€˙üž…üų‡˙˙„˙˙ƒ˙˙ƒ˙˙ƒ˙˙ƒ˙˙ƒ˙˙ƒ˙˙ƒ˙˙ƒ˙˙ƒ˙˙ƒ˙˙ƒ˙˙ƒ˙˙ƒ˙˙ƒ˙˙ƒ˙˙ƒ˙˙ƒ˙˙ƒ˙˙ƒ˙˙ƒ˙˙ƒ˙˙ƒ˙˙ƒ˙˙‚˙˙„˙˙nš˙˙úũ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ōų˙˙Įä˙˙šÎ˙˙; ˙˙ƒ˙˙ƒ˙˙ƒ˙˙ƒ˙˙‚˙˙…˙˙…˙˙€ü˛ˆ˙’˙…ũ’„üōŠ˙˙‡˙˙…˙˙…˙˙…˙˙…˙˙…˙˙…˙˙…˙˙…˙˙…˙˙…˙˙…˙˙…˙˙…˙˙…˙˙…˙˙…˙˙…˙˙…˙˙…˙˙ƒ˙˙‚˙˙˙˙€˙˙‚˙˙]ą˙˙åķ˙˙ųũ˙˙˙˙˙˙˙˙˙˙āđ˙˙ŧā˙˙ƒÄ˙˙“˙˙ƒ˙˙„˙˙…˙˙…˙˙…˙˙…˙˙†˙˙ˆ˙˙‚ũĖ‚˙+€˙‰ũu‰üáŒ˙˙Š˙˙‡˙˙‡˙˙‡˙˙‡˙˙‡˙˙‡˙˙‡˙˙‡˙˙‡˙˙‡˙˙‡˙˙‡˙˙‡˙˙‡˙˙‡˙˙‡˙˙‡˙˙‰˙˙‘˙˙•˙˙ •˙˙%–˙˙g¸˙˙Ëį˙˙ŗÛ˙˙ŦØ˙˙Õë˙˙ŠÖ˙˙LĢ˙˙‰˙˙…˙˙†˙˙†˙˙‡˙˙‡˙˙‡˙˙‡˙˙‰˙˙Œ˙˙†ũԇ˙@‹üMˆüȍüûŽ˙˙‹˙˙Š˙˙Š˙˙Š˙˙Š˙˙Š˙˙Š˙˙Š˙˙Š˙˙Š˙˙Š˙˙‰˙˙‰˙˙‰˙˙‰˙˙†˙˙•˙˙xÁ˙˙ĢØ˙˙ĻÖ˙˙Ķę˙˙ëö˙˙sž˙˙‰˙˙’˙˙&›˙˙ˆ˙˙ˆ˙˙ˆ˙˙ˆ˙˙‰˙˙‰˙˙‰˙˙‰˙˙‰˙˙‹˙˙Ž˙˙‰ũԈ˙GŠ˙#Šũ™‹üė˙˙˙˙˙˙Œ˙˙Œ˙˙Œ˙˙Œ˙˙Œ˙˙Œ˙˙Œ˙˙Œ˙˙Œ˙˙Œ˙˙Œ˙˙Œ˙˙Š˙˙•˙˙Vŗ˙˙sĀ˙˙]ļ˙˙ĩŨ˙˙´Ũ˙˙‰˙˙Š˙˙‹˙˙Š˙˙Š˙˙‹˙˙‹˙˙‹˙˙‹˙˙‹˙˙‹˙˙Œ˙˙˙˙ũū‰üƌ˙<€˙˙TŽüĮũķ‘˙˙‘˙˙˙˙Ž˙˙Ž˙˙Ž˙˙Ž˙˙Ž˙˙Ž˙˙Ž˙˙Ž˙˙Ž˙˙Ž˙˙Ž˙˙Ž˙˙˙˙˙˙”˙˙‘Î˙˙–Ņ˙˙†˙˙Ž˙˙Ž˙˙Ž˙˙Ž˙˙Ž˙˙Ž˙˙Ž˙˙Ž˙˙Ž˙˙˙˙’˙˙ŽũķŽū­Š˙%Œ˙˙qŒũԐũõ“˙˙”˙˙‘˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙‹˙˙ž˙˙’Đ˙˙sÁ˙˙‹˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙’˙˙”˙˙’ũûüܐũs€ę €˙™˙”˙w‘ũŌ“˙đ“˙˙—˙˙–˙˙”˙˙“˙˙“˙˙’˙˙’˙˙’˙˙’˙˙’˙˙’˙˙˙˙œ˙˙P´˙˙7Ē˙˙˙˙’˙˙’˙˙’˙˙’˙˙“˙˙•˙˙–˙˙”ũûüį‘ũœ•˙0€˙Ē˙”˙˜˙W“ūĩ”üč–ũô”ũū™˙˙™˙˙˜˙˙—˙˙–˙˙–˙˙–˙˙•˙˙•˙˙•˙˙–˙˙–˙˙–˙˙—˙˙˜˙˙™˙˙˜˙˙”ū˙•ũķ’úâ“ũš”˙7™˙˙˙Ž˙ š˙&—ũi˜ü¯–üā˜ũī•ũõ•ũú–ũū™˙˙š˙˙›˙˙œ˙˙›˙˙™˙˙™˙˙˜˙˙—ũû•ũ÷™˙đ—úâ—ū°–˙d‘˙Ē˙Ē˙™æ ™˙›˙8™˙i—ũŽšūĢ—üĮ—ûؗüá—üę•ũč˜ûڙũΘūš™ũ˜›˙u˜˙E›˙™˙ €˙( @ ˙üdũŸüÁūū˙˙˙˙˙˙ĸĻûíūē ũšüW ˙˙,ūĢúûũ˙ũ˙uū˙˙˙˙˙˙˙˙˙‰ū˙ũ˙ũ˙ũ˙úüüĀüJ˙˙ũ‡úûũ˙ũ˙>Pũ˙ŊÃū˙˙˙˙˙˙˙˙˙úú˙˙&:ũ˙ũ˙ũ˙ũ˙ũ˙ũ˙ũ˙ũÍüK˙ ūēũ˙ũ˙ũ˙bsū˙ũũ˙˙˙˙˙˙˙˙˙˙úû˙˙\nū˙ũ˙ũ˙ũ˙ũ˙ũ˙ũ˙ũ˙ũ˙ũ˙ũĻ ˙+˙ūģ!ũ˙!ũ˙!ũ˙Š™ū˙˙˙˙˙˙˙˙˙ôö˙˙ČĪ˙˙0Jũ˙ ũ˙ ũ˙ ũ˙ ũ˙ ũ˙ ũ˙ ũ˙ ũ˙ ũ˙ ũ˙ ũ˙üÛ˙+%ũ‹%ũ˙%ũ˙%ũ˙p…ū˙˙˙˙˙˙˙˙˙úû˙˙s‡ū˙(ũ˙%ũ˙%ũ˙%ũ˙%ũ˙%ũ˙%ũ˙%ũ˙%ũ˙%ũ˙%ũ˙%ũ˙$ũ˙$ũ˙$úí"˙5+˙0*úũ*ũ˙*ũ˙>^ũ˙üü˙˙˙˙˙˙õ÷˙˙@_ũ˙)ũ˙)ũ˙)ũ˙)ũ˙$Gũ˙yū˙Ĩŗū˙ģÆū˙¯ŧū˙ĸū˙Plū˙ 1ũ˙)ũ˙)ũ˙)ũ˙)ũ˙)úđ'˙..ūļ.ū˙.ū˙0ū˙ĖÕ˙˙˙˙˙˙ūū˙˙Xvū˙.ū˙.ū˙.ū˙Cū˙˛Ā˙˙ūū˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ęî˙˙{’ū˙3ū˙-ū˙-ū˙-ū˙*üŪ$˙3˙#3ûū3ū˙3ū˙Xyū˙˙˙˙˙˙˙˙˙¤ļ˙˙2ū˙2ū˙2ū˙8_ū˙ėđ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙Í×˙˙#Nū˙2ū˙2ū˙2ū˙2ũž7˙t7ū˙7ū˙7ū˙ˇĮ˙˙˙˙˙˙úû˙˙Oū˙7ū˙7ū˙Oū˙čí˙˙˙˙˙˙˙˙˙˙ĶÜ˙˙`‚ū˙Kū˙6ū˙ =ū˙;eū˙§˙˙ķö˙˙˙˙˙˙˙˙˙˙āį˙˙Jū˙6ū˙6ū˙6ū˙7˙O:ūŗ<ū˙;ū˙Iū˙úû˙˙˙˙˙˙¸Č˙˙;ū˙;ū˙;ū˙¯Â˙˙˙˙˙˙˙˙˙˙Ąˇ˙˙@ū˙;ū˙;ū˙;ū˙;ū˙;ū˙;ū˙Pū˙ĀĪ˙˙˙˙˙˙˙˙˙˙ÎŲ˙˙@ū˙;ū˙:ū˙:üÛU˙@üŪ@ū˙@ū˙Crū˙˙˙˙˙˙˙˙˙jū˙@ū˙@ū˙(^ū˙ũũ˙˙˙˙˙˙ĮÕ˙˙Cū˙?ū˙?ū˙?ū˙?ū˙?ū˙?ū˙?ū˙?ū˙ Fū˙ÅĶ˙˙˙˙˙˙˙˙˙˙s–ū˙?ū˙?ū˙?ū˙A˙SAûõDū˙Dū˙gū˙˙˙˙˙˙˙˙˙Atū˙Dū˙Dū˙j’ū˙˙˙˙˙˙˙˙˙M|ū˙Dū˙Dū˙7lū˙­Âū˙ŅŨ˙˙ÂŌ˙˙Hyū˙Dū˙Dū˙Dū˙Tū˙đô˙˙˙˙˙˙îō˙˙Nū˙Cū˙Cū˙DüČIûøIū˙Iū˙|Ąū˙˙˙˙˙˙˙˙˙)fū˙Iū˙Iū˙°˙˙˙˙˙˙˙˙˙˙Wū˙ Qū˙Ĩŋ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ˆĒ˙˙Hū˙Hū˙Hū˙xžū˙˙˙˙˙˙˙˙˙\Šū˙Hū˙Hū˙Hū˙F˙!MûįMū˙Mū˙ošū˙˙˙˙˙˙˙˙˙6sū˙Mū˙Mū˙‰­˙˙˙˙˙˙˙˙˙˙#eū˙ÁÔ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ũū˙˙8sū˙Lū˙Lū˙^ū˙ũū˙˙˙˙˙˙ĒÃ˙˙Lū˙Lū˙Lū˙M˙cRũĶRū˙Rū˙\ū˙˙˙˙˙˙˙˙˙YŽū˙Qū˙Qū˙h˜ū˙˙˙˙˙˙˙˙˙ËÜ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ūū˙˙ūū˙˙˙˙˙˙œģ˙˙Qū˙Qū˙Qū˙ĪŪ˙˙˙˙˙˙Ûæ˙˙Qū˙Qū˙Qū˙Oũ§VūŽVū˙Vū˙"mū˙˙˙˙˙˙˙˙˙žŋ˙˙Vū˙Vū˙cū˙öų˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ŗ˙˙ãė˙˙˙˙˙˙ÂÖ˙˙Uū˙Uū˙Uū˙­Č˙˙˙˙˙˙öų˙˙Uū˙Uū˙Uū˙SũĪ\˙l[ū˙[ū˙Zū˙Üč˙˙˙˙˙˙ķ÷˙˙jū˙Zū˙Zū˙zŠū˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙’š˙˙[ū˙įī˙˙˙˙˙˙ģĶ˙˙Zū˙Zū˙Zū˙ŸÁ˙˙˙˙˙˙ūū˙˙]ū˙Yū˙Yū˙Wúāa˙*_ū˙_ū˙_ū˙r§ū˙˙˙˙˙˙˙˙˙žÂ˙˙_ū˙_ū˙_ū˙€¯ū˙ũū˙˙˙˙˙˙˙˙˙˙ßë˙˙G‹ū˙^ū˙*yū˙˙˙˙˙˙˙˙˙—Ŋ˙˙^ū˙^ū˙^ū˙˛Î˙˙˙˙˙˙đö˙˙^ū˙^ū˙^ū˙[ûō˙aũÔcū˙cū˙hū˙áí˙˙˙˙˙˙˙˙˙˙nĻū˙cū˙cū˙cū˙eū˙({ū˙lū˙cū˙cū˙dū˙ąĪ˙˙˙˙˙˙˙˙˙˙A‹ū˙cū˙cū˙cū˙Ûé˙˙˙˙˙˙Öæ˙˙bū˙bū˙bū˙_ûķi˙_hū˙hū˙hū˙H“ū˙üũ˙˙˙˙˙˙˙˙˙˙•Ā˙˙qū˙hū˙gū˙gū˙gū˙gū˙ mū˙ŸÆ˙˙˙˙˙˙˙˙˙˙Ėá˙˙hū˙gū˙gū˙ zū˙˙˙˙˙˙˙˙˙šÃ˙˙gū˙gū˙gū˙güÜf˙kúálū˙lū˙lū˙jŠū˙ūū˙˙˙˙˙˙˙˙˙˙íõ˙˙“Á˙˙Q›ū˙8Œū˙G•ū˙€ļū˙âî˙˙˙˙˙˙˙˙˙˙îõ˙˙#€ū˙lū˙lū˙kū˙‡š˙˙˙˙˙˙˙˙˙˙Vū˙kū˙kū˙kū˙iūŗp˙]qū˙qū˙qū˙qū˙Nœū˙đ÷˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙āî˙˙7ū˙pū˙pū˙pū˙~ū˙ņ÷˙˙˙˙˙˙åđ˙˙tū˙pū˙pū˙pū˙o˙wtū­uū˙uū˙uū˙uū˙€ū˙ŽÂ˙˙ņ÷˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ņ÷˙˙“Å˙˙~ū˙uū˙tū˙ yū˙Įā˙˙Éá˙˙˙˙˙˙˙˙˙˙sŗū˙tū˙tū˙tū˙tûūv˙'y˙{ûįyū˙yū˙yū˙yū˙yū˙|ū˙@›ū˙l˛ū˙€ŧū˙q´ū˙J ū˙ ū˙yū˙yū˙yū˙yū˙ŽÃū˙˙˙˙˙ęô˙˙˙˙˙˙Ėä˙˙|ū˙yū˙yū˙yū˙wūŧ}˙5~ûô~ū˙~ū˙~ū˙~ū˙~ū˙~ū˙~ū˙~ū˙~ū˙~ū˙}ū˙}ū˙}ū˙}ū˙"Žū˙äņ˙˙˙˙˙˙˙˙˙˙čķ˙˙PĨū˙}ū˙}ū˙}ū˙}ū˙{˙<ƒ˙D‚üķ‚˙˙‚˙˙‚˙˙‚˙˙‚˙˙‚˙˙‚˙˙‚˙˙‚˙˙‚˙˙‚˙˙‚˙˙‚˙˙ŸĐ˙˙˙˙˙˙˙˙˙˙ũū˙˙Ņé˙˙sē˙˙‚˙˙‚˙˙˙˙ũ–‰˙6…üå‡˙˙‡˙˙‡˙˙‡˙˙‡˙˙†˙˙†˙˙†˙˙†˙˙†˙˙†˙˙Š˙˙œĐ˙˙Ķę˙˙ŧß˙˙i¸˙˙ˆ˙˙†˙˙†˙˙†˙˙‡üĮŽ˙ ˙‹ūģ‹˙˙‹˙˙‹˙˙‹˙˙‹˙˙‹˙˙‹˙˙‹˙˙%œ˙˙‘Í˙˙Ęæ˙˙3ĸ˙˙Ž˙˙‹˙˙‹˙˙‹˙˙Š˙˙Š˙˙ŠüĮŽ˙˙˙‘˙aŽüŪ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙‹Ė˙˙˙˙˙˙˙˙˙˙˙˙Œüūũ˜Ž˙ €˙”˙_’ũԔ˙˙”˙˙”˙˙”˙˙”˙˙”˙˙$Ŗ˙˙”˙˙“˙˙“˙˙“˙˙“ūŊ“˙;•˙$—˙l˜ū°™ũŅ•üæ˜üû˜üé™ũŅ–ū˛˜˙wš˙&(0 ˙ų\ũš&2üÅųųũîÜŪûė$1üÉũĸ˙e˙1˙ũiũĐ˙˙9E˙˙˛¸˙˙˙˙˙˙¸ŋ˙˙ ˙˙˙˙˙˙üđũ ûB˙ũ˙˙˙˙l{˙˙ėī˙˙˙˙˙˙ÚŪ˙˙;Oũ˙ũ˙ũ˙ū˙˙˙˙˙üîû˙˙ "ũŸ˙˙ ˙˙ƒ“ū˙˙˙˙˙˙˙˙˙ĄŽū˙)Cũ˙ũ˙ũ˙ũ˙ũ˙ũ˙ũ˙!˙˙"˙˙ ūžø&&ũk'üũ ˙˙g~ũ˙üü˙˙ņķ˙˙gū˙#ũ˙ũ˙#ũ˙-Mũ˙Tnũ˙[tũ˙Gcũ˙<ũ˙ũ˙!˙˙'˙˙'ũĐ&˙(1˙+ûŲ'˙˙&Nū˙čė˙˙˙˙˙˙eū˙ū˙!ū˙5Xū˙­ģū˙éí˙˙˙˙˙˙˙˙˙˙ūū˙˙Ųā˙˙ŽĄū˙Eū˙$˙˙.˙˙*üÄ˙3˙d4˙˙1˙˙ˆ ū˙˙˙˙˙Ŗĩ˙˙1ū˙&ū˙Mpū˙įė˙˙˙˙˙˙īķ˙˙ĮŌ˙˙ĀĖ˙˙ŌÜ˙˙˙˙˙˙˙˙˙˙ĶÜ˙˙@eū˙*˙˙5˙˙1ũŒ˙8ũ¨7˙˙Fū˙Ũå˙˙øú˙˙;gū˙&ū˙(Xū˙âę˙˙˙˙˙˙¤š˙˙4\ū˙2ū˙)ū˙ @ū˙Msū˙ģËū˙˙˙˙˙áč˙˙1^ū˙1˙˙8ũķ8˙D>ûÕ;˙˙8iū˙˙˙˙˙ČÖ˙˙Kū˙2ū˙Œ¨ū˙˙˙˙˙¯Ã˙˙;ū˙,ū˙Cū˙ Gū˙9ū˙,ū˙Fū˙ŦĀ˙˙˙˙˙˙ˇÉ˙˙Eū˙=˙˙>ú¨UÕBúę@˙˙[‡ū˙˙˙˙˙ĸģ˙˙>ū˙Oū˙ÅÕ˙˙ūū˙˙3jū˙@ū˙^‰ū˙ģÍū˙Ķß˙˙…Ļū˙Sū˙7ū˙Wū˙Ųã˙˙˙˙˙˙Oū˙=˙˙DüōCú5KųæF˙˙bū˙˙˙˙˙›¸˙˙@ū˙]ū˙×â˙˙õ÷˙˙+iū˙š¸˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ĸž˙˙Jū˙@ū˙o™ū˙˙˙˙˙žē˙˙K˙˙L˙˙IũpQũÔN˙˙P‡ū˙˙˙˙˙ąĘ˙˙Pū˙ Wū˙ĀĶū˙üũ˙˙ÁÕ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ūū˙˙ķ÷˙˙?|˙˙?ū˙3sū˙ųû˙˙×ä˙˙ Yū˙O˙˙Pū­VūąU˙˙#nū˙øú˙˙äí˙˙"nū˙Fū˙vĻū˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ĻÄ˙˙žÔ˙˙˙˙˙˙c—˙˙Dū˙"mū˙âė˙˙ôø˙˙bū˙T˙˙QũŅ]ũv_˙˙_˙˙ļŅū˙˙˙˙˙~­˙˙Qū˙dū˙ŠÉū˙˙˙˙˙˙˙˙˙ûü˙˙”ģū˙eū˙ĩĐ˙˙˙˙˙˙U’˙˙Lū˙"rū˙āë˙˙ôø˙˙hū˙Z˙˙Yúâaû:büö^˙˙Gū˙˙˙˙˙ôø˙˙O”ū˙Uū˙dū˙FŽū˙R•ū˙1ū˙Vū˙BŠū˙øû˙˙åī˙˙!vū˙Uū˙0€ū˙úü˙˙Ûé˙˙ jū˙a˙˙_účq˙ füŦm˙˙gū˙Ŋū˙˙˙˙˙ņ÷˙˙wŽ˙˙vū˙bū˙`ū˙hū˙Q—ū˙ßė˙˙˙˙˙˙‚ĩ˙˙aū˙aū˙bĸū˙˙˙˙˙ĻĘ˙˙jū˙h˙˙gũÔnûHoũôm˙˙tū˙’Âū˙˙˙˙˙˙˙˙˙āî˙˙ĢĪ˙˙ĄÉ˙˙Æß˙˙˙˙˙˙˙˙˙˙ĨĖū˙ rū˙dū˙rū˙Įß˙˙˙˙˙˙]Ŗū˙hū˙q˙˙mũŠĒ˙rũ“z˙˙q˙˙tū˙X¤ū˙Įā˙˙üū˙˙˙˙˙˙˙˙˙˙˙˙˙˙āî˙˙t´˙˙ xū˙jū˙Qžū˙ąĶ˙˙˙˙˙˙Īå˙˙ū˙n˙˙y˙˙tũgķzüȂ˙˙w˙˙uū˙ ~ū˙3•ū˙^Ŧū˙e¯ū˙FŸū˙…ū˙vū˙qū˙‰ū˙Ųëū˙˙˙˙˙ķų˙˙V§˙˙rū˙˙˙wüŪx˙ ‚ų+ûՈ˙˙€˙˙}ū˙|ū˙{ū˙{ū˙|ū˙|ū˙|ū˙|˙˙„Â˙˙˙˙˙˙˙˙˙˙Íæ˙˙'’ū˙}˙˙„˙˙~ũs…˙,‡üč˙˙Š˙˙†˙˙†˙˙…˙˙…˙˙…˙˙ ‰˙˙“˙˙§Ö˙˙Ņé˙˙‘Ë˙˙6Ÿ˙˙Ž˙˙Œ˙˙„ũ¨™˙ ˙Šũ™ũô•˙˙Ž˙˙Œ˙˙‹˙˙Œ˙˙>¨˙˙žĶ˙˙4Ŗ˙˙ ˙˙‹˙˙Œ˙˙Ž˙˙Šũό˙üL’ũ¨’üøš˙˙—˙˙–˙˙”˙˙;°˙˙™˙˙“˙˙—˙˙”ũŲŽũsŽ˙ ™˙”˙9™˙q–ū˛—ũĶ–üį•üä—ũΕũŠ™˙f•˙$(  ˙˙˙* ũmîīũ“cmũƒüU#˙˙˙˙1˙ üĒ üû‘šū˙ūūūū4Eüūü˙üîũ• ˙˙1˙úá6ũ˙ßãũũô÷ũüG\üûûûûũ ûūü˙ ûí'ûHU˙'ūļ+ũ˙ŨâũøÆŌũū#ũ˙!üūl„üū§ŗũūœĒũũKiüú!üū$üũ0üPU˙A˙C$ũ˙vũũöųũū 3üū Dū˙åëū˙úû˙˙ÁÎū˙ĪŲũū˙˙˙˙ÅĐũü;ū˙-üņ>˙%>ũˆ4ũ˙×āũü”Ēũū$ũ˙ĪÚū˙É×ū˙4ũ˙"ũ˙"ũ˙ Rũūáęū˙ÄŌũú6ũ˙;ũ¨Aũ¨Jū˙÷ųũüRũūTū˙˙˙˙˙0aū˙Ixū˙ŧÎū˙œ¸ū˙ Bũ˙ UũūūūūūW‚ū˙7üöU˙-Mũ Rū˙ôøũüXˆũū_ū˙öøū˙´Ëū˙˙˙˙˙ûüū˙˙˙˙˙œģū˙5ũ˙žĪũū°ÉũüAũ˙ T˙t\˙tOũ˙ČÚũü­ČũūCũ˙ģŌū˙˙˙˙˙˙˙˙˙ļĪū˙ĄÂū˙Đāū˙Bũ˙—ˇũūĪßũüNũ˙Yũ l˙-XüöNū˙ūūūūKŒũūWũ˙Wšū˙Gū˙\ũ˙Ôãū˙ĸÆū˙Iũ˙­ĘũūŋÖũüVũ˙dũ¨mũ¨dũ˙‰ēũú˙˙˙˙ĄÆũū@Žū˙N–ū˙Ôåū˙íõū˙uū˙dũ˙īöũūy°ũü_ũ˙oũˆu˙%uũņmũ˙QĄũüÕéū˙úũũūųũū˙´Øū˙ū˙{ū˙ēŲũūęķũū}üũqũ˙v˙CU˙|˙P€ũũvũūvũú ‚ũũũūtũūoũūŦÔū˙ūūūūŧũøtū˙€ūļU˙‡˙Hˆūí‰˙˙…ūū…ūũ ŒūûNŠūû–ÎũüZ˛ũũ‹˙˙…ūá„˙Ē˙Š˙ũ”ũî‘ū˙ũū:Ēũū ū˙†ūúūĒ“˙˙˙€˙˙˙—˙–˙U—˙€˙ˆ™˙l¤˙*˙˙˙astropy-astropy-201cddb/docs/_static/astropy_logo.pdf000066400000000000000000000124101507226315300231670ustar00rootroot00000000000000%PDF-1.5 %ĩíŽû 3 0 obj << /Length 4 0 R /Filter /FlateDecode >> stream xœ}ššŽ,ĮEũúŠú.枸rAɐ!ČZȀ'ü}Ũ‘Y=CAž—ŲšDÆrãFŋáĘ!ÎĪßūå?žßũ+œ?ū|į9ÆŲķÕÚųqîņˆWŅÂ8ë•G?GŋĘ8SH—œZĶS>Ÿš˜WåÔ*{œ)ÎkÔzÎqͨõš\Ŋ–3†pÕ9ØpΤĢôlã—+­ą<4ŅėdļäĢÆ¤™~؊tõŌ4œWĒÜ!aB?cÔ#Ų†pĩŽQO‰:+Ĩye‰cēf™w“?F=ZĪŅ–¤Û&+ĘUJÔXbÔhã›Æ‘Ï{B[bŋfj¯%ú3Ījã˜t„äA‘\ÚŗŊ]¨ĶÄĘUg%Ŋ9G ōĻ/v{ĐSĘ/’)ĻI÷Ž)VgJœÆ†|•‰T)_Ŗëaa˜VcĒĻe¤ĖnG=š%­ˆãMÚJãĒĄ‡ĸ‚5>–žŋ,)K:Ô.)õ’æ4f:í(ķĒĻ-ũÂ%U&é‡Ŋ$Ë||ÎnÎķÜãZlõģnėē1žhWÍõl‰›Ž5d]Ÿą‰tœÎ:QĻ›XÖ~mL’ĩ•zÖzšą¤°YęqÖ|ĨQoãÕdށ–đ5tSÅūú]–™ŨLS/×K4ÔicŊ(Ό]$yҊĄÖåĄåübĪķíĀN(ŦWķņķžbJ˛°Âå›ÕqĨh^Šķ†<Ė­š›4ÜÚUbҏZØĩęŽ ŋ=ō×cšũŗl.?ÔŪ,Ĩ rõƒĶ—ŸJ—Õ.7?–ĸÃhˆÆÛvīĮėņ˜æĒīįÁvbxЙ)š…b™dN/@Š~ÂĘC‡L>ŊŒ*OJúÃĨS<)vG–ę†EYgš"¸âxéW0(^įLgW\`ū[ĪÍvũĘf=L ÉûЎLŖĩļ&\CķŠOmíg>i›šåēQīh({f™fxpk˜$ļÜį*éjŊ| ¯‰,¤ÄAeü8¸ÍlcĘʂ æe‘!~)Û=÷PÎXûZnRĻdaX“ü–įËͲđPčŨ”ö´ëøAo ‘ß…zuiĶe#5ô0IéĘAĻÍ‚J*i8Ž’:ōVŪNPɔ[),Đ´†Ā Š’f,īŽ1pĖiŽQ¤9­#¤qœ^LņvØØH‡a›Ë“UÃ8ČUCŌ!.Šõx°R—ü7éúînŽ7XŒ`ny¯žč!w$éŧEÁ"(-b,se“ā5n;CėE+ĻĮJm”ĮƒÉx–Á…BbŠT;2Pģß,įfo(–J-[žĐŖ•{‰–_dCđž3œ ŦëzH–4Qá“į)ãÅQHuWV˛Á)H:ÃĀ0 ~ē|’Î"Ā›V]lŸ(5Žō2“OXy÷ifkžõü=ÆČ7Ŋ"tYÉGd™§f†hōėWR2P¸ŗžh‚Ylį92ޚQ'N˛ qģCá,ęRŧŋGÄĒldLV¨!įkĖe„’^+ôP ;0˛ŠÂĢĖ`IéiÚ˛*3xÁRvæ% 6ŠŌPĨA yŪŽi”Nˎwqõv”ėø8ƒå6YvO4sÁ’E“ÁĐĒjU´7 ]RŒ8–hIMEŪííÕržŌ´t2Í螊Ü~OéÚÃEĀ@,cvíöÉ҈ŠcA‘+ƒŒFe”;Js.vÛ¸;•ūäŨ’ÔņZ"@vGYG {§;¤‚ë–BãUÄ씒ФkĨí~HUūõ~ƒ?ĩ**Riˇ**1Ašŧ•UŠ%ĄÉ[ĸĨæņKÛ÷0,ŗ'Dx-ûø~B“RfšN/ļmZvÛ CÚũ‚–göë´Dē4p#‰˜ĨĸZ[Ø*…ZKÅĶļ nĐ[FēAoqow#ŋN_^°/_N˛es/:^˛/7ÛOs7Üßnz+fûņŌÜōōc+öWaæ!Ī—Āđ1Å&ŌuúI”KD1o!$I:ëíŲJžfôū0]`¸FE{ĢĘG8ë0¤^ŋoMų^ŗDÖĪ>Ė͜b_>ŊŖļ$[Ŗû%÷„hî˜÷æÎ ļmžŧ<íyß™K:¸öįXķáķõ¸Ŋ`ŊũŪŋ”ķé†ĨŊ-‚köØ~Q<ĻøĪaũʨ´ÜÎ@Į˛Ę^f7?ōãžHŲ2ĮģœŽY$üfâEjn¤ŨãËTŋLôNWĘĮ6Ī=An-Ģã•h\íl’–ŗK$kQĻíØUž,(ö\ åhđPpȤ´Ķķ=ÔrØŧûĩBĩNíļßâ îēœMVú ŲļO‡6h"Q”[Ŋ՗{VZ(mY,ë8ƒ™ RØđl*Ąm|؁k@UäF˛ĀEœŨøčķžPq€Ŧl…Ļ›Ŗá>#PŽž"ūôTQĨoГq?8¸ļqŠbßŅΚ2 iHW›õ—ĐØ †\Îũ+ŦąrÁ„* n~VU#Ā—näRQ š>/ q`Če,͉„Ö,iŠŊB—铇N(zālûŨ!ŽŽ&HG[”&LYå2„âÛÉÃÅ[xæĮ‡÷.ė[ ´]`‚‹ˇhm¯({ŲąĶĖúô†°tũHÎ[šĩ$5"Žĸgēä™čy>ē-‹qƒS„č܁r\ÃÚ0muhžæ5°øú¨Öˇø•č<éīį@‰^[@]ō)Ā:‰w~Ę>ũ >€kĨäSĪÃ?ŸßũüVĪŸßŽÄšøį¯>ŠŌƒōe;9ÂųŊūũéøĮ?ux8˙}”ķ/į7ÎīąqRäīsčAÍfŗ.˜"mæķĮƒÆgRä?Ŧ˛‡ē= éâäĘiyáƒū"§H ŗ4Ņ­Č’ˆƒÄh}ë”ÛōĻÁŅ}zš ƒĨ÷Mƒ‘m‹eaM)†Hgt/C<M3e„,šMČÕ¸åoįրč&%6•jömíƒvmÎFāųÔDäŸæ‚ūģ`‚o?Fƒ@x:4Z:,›ËįŸđ(kZM§]‚rĐŨ?ˆ 5Ŋo¯|zŋøĮö+IēGŠvō°žq÷ų:Y.#süáÉĖ´­ÃJŋäfĮÅĘŲw˙ÎĐ †Ŧ’03ļ„Cūōģ€RŌũRņ‹C@*­ûRˇų L›Š&ļø)ßeétņ!ÜJ§nœ16ūßÚlÚkÔkÞÛvŠžo™“˜H RŠv‰% k|ŽJ­*ú¸{iU(?-~[57áŗҘ԰wú*´H'éhũČŗWŌÕĨú”3Ķíĸ+o~ÄŲčŧTøĀí Dyžķ!v4TųĸIPBTõjŦ/đ3ŒwBf™Ō3ŪÅj:Œ{)&›8ĨĢ!*øáø?3ĻËS endstream endobj 4 0 obj 3867 endobj 2 0 obj << /ExtGState << /a0 << /CA 1 /ca 1 >> >> /Shading << /sh5 5 0 R >> >> endobj 6 0 obj << /Type /Page /Parent 1 0 R /MediaBox [ 0 0 960 176 ] /Contents 3 0 R /Group << /Type /Group /S /Transparency /I true /CS /DeviceRGB >> /Resources 2 0 R >> endobj 7 0 obj << /FunctionType 2 /Domain [ 0 1 ] /C0 [ 1 0.662745 0 ] /C1 [ 0.992157 0 0 ] /N 1 >> endobj 8 0 obj << /FunctionType 3 /Domain [ -3 4 ] /Functions [ 7 0 R 7 0 R 7 0 R 7 0 R 7 0 R 7 0 R 7 0 R ] /Bounds [ -2 -1 0 1 2 3 ] /Encode [ 0 1 0 1 0 1 0 1 0 1 0 1 0 1 ] >> endobj 5 0 obj << /ShadingType 2 /ColorSpace /DeviceRGB /Coords [ 458.295892 -154.186282 -184.237871 621.778606 ] /Domain [ -2.348045 3.394022 ] /Extend [ true true ] /Function 8 0 R >> 1 0 obj << /Type /Pages /Kids [ 6 0 R ] /Count 1 >> endobj 9 0 obj << /Creator (cairo 1.12.2 (http://cairographics.org)) /Producer (cairo 1.12.2 (http://cairographics.org)) >> endobj 10 0 obj << /Type /Catalog /Pages 1 0 R >> endobj xref 0 11 0000000000 65535 f 0000004834 00000 n 0000003982 00000 n 0000000015 00000 n 0000003959 00000 n 0000004598 00000 n 0000004083 00000 n 0000004297 00000 n 0000004409 00000 n 0000004899 00000 n 0000005026 00000 n trailer << /Size 11 /Root 10 0 R /Info 9 0 R >> startxref 5079 %%EOF astropy-astropy-201cddb/docs/_static/index-images/000077500000000000000000000000001507226315300223275ustar00rootroot00000000000000astropy-astropy-201cddb/docs/_static/index-images/api.svg000066400000000000000000000352351507226315300236310ustar00rootroot00000000000000 astropy-astropy-201cddb/docs/_static/index-images/contributor.svg000066400000000000000000000004521507226315300254230ustar00rootroot00000000000000astropy-astropy-201cddb/docs/_static/index-images/getting_started.svg000066400000000000000000000562551507226315300262540ustar00rootroot00000000000000 astropy-astropy-201cddb/docs/_static/index-images/learn.svg000066400000000000000000000015341507226315300241540ustar00rootroot00000000000000astropy-astropy-201cddb/docs/_static/index-images/packages.svg000066400000000000000000000006651507226315300246350ustar00rootroot00000000000000astropy-astropy-201cddb/docs/_static/index-images/user_guide.svg000066400000000000000000000021511507226315300252020ustar00rootroot00000000000000astropy-astropy-201cddb/docs/_templates/000077500000000000000000000000001507226315300204645ustar00rootroot00000000000000astropy-astropy-201cddb/docs/_templates/layout.html000066400000000000000000000021261507226315300226700ustar00rootroot00000000000000{# 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 %} astropy-astropy-201cddb/docs/changelog.rst000066400000000000000000000002401507226315300210040ustar00rootroot00000000000000.. _changelog: ************** Full Changelog ************** .. changelog:: :towncrier: ../ :towncrier-skip-if-empty: :changelog_file: ../CHANGES.rst astropy-astropy-201cddb/docs/changes/000077500000000000000000000000001507226315300177375ustar00rootroot00000000000000astropy-astropy-201cddb/docs/changes/README.rst000066400000000000000000000030321507226315300214240ustar00rootroot00000000000000: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. astropy-astropy-201cddb/docs/changes/config/000077500000000000000000000000001507226315300212045ustar00rootroot00000000000000astropy-astropy-201cddb/docs/changes/config/.gitkeep000066400000000000000000000000001507226315300226230ustar00rootroot00000000000000astropy-astropy-201cddb/docs/changes/constants/000077500000000000000000000000001507226315300217535ustar00rootroot00000000000000astropy-astropy-201cddb/docs/changes/constants/.gitkeep000066400000000000000000000000001507226315300233720ustar00rootroot00000000000000astropy-astropy-201cddb/docs/changes/convolution/000077500000000000000000000000001507226315300223165ustar00rootroot00000000000000astropy-astropy-201cddb/docs/changes/convolution/.gitkeep000066400000000000000000000000001507226315300237350ustar00rootroot00000000000000astropy-astropy-201cddb/docs/changes/coordinates/000077500000000000000000000000001507226315300222515ustar00rootroot00000000000000astropy-astropy-201cddb/docs/changes/coordinates/.gitkeep000066400000000000000000000000001507226315300236700ustar00rootroot00000000000000astropy-astropy-201cddb/docs/changes/cosmology/000077500000000000000000000000001507226315300217525ustar00rootroot00000000000000astropy-astropy-201cddb/docs/changes/cosmology/.gitkeep000066400000000000000000000000001507226315300233710ustar00rootroot00000000000000astropy-astropy-201cddb/docs/changes/extern/000077500000000000000000000000001507226315300212445ustar00rootroot00000000000000astropy-astropy-201cddb/docs/changes/extern/.gitkeep000066400000000000000000000000001507226315300226630ustar00rootroot00000000000000astropy-astropy-201cddb/docs/changes/io.ascii/000077500000000000000000000000001507226315300214355ustar00rootroot00000000000000astropy-astropy-201cddb/docs/changes/io.ascii/.gitkeep000066400000000000000000000000001507226315300230540ustar00rootroot00000000000000astropy-astropy-201cddb/docs/changes/io.fits/000077500000000000000000000000001507226315300213125ustar00rootroot00000000000000astropy-astropy-201cddb/docs/changes/io.fits/.gitkeep000066400000000000000000000000001507226315300227310ustar00rootroot00000000000000astropy-astropy-201cddb/docs/changes/io.misc/000077500000000000000000000000001507226315300213005ustar00rootroot00000000000000astropy-astropy-201cddb/docs/changes/io.misc/.gitkeep000066400000000000000000000000001507226315300227170ustar00rootroot00000000000000astropy-astropy-201cddb/docs/changes/io.registry/000077500000000000000000000000001507226315300222155ustar00rootroot00000000000000astropy-astropy-201cddb/docs/changes/io.registry/.gitkeep000066400000000000000000000000001507226315300236340ustar00rootroot00000000000000astropy-astropy-201cddb/docs/changes/io.votable/000077500000000000000000000000001507226315300220015ustar00rootroot00000000000000astropy-astropy-201cddb/docs/changes/io.votable/.gitkeep000066400000000000000000000000001507226315300234200ustar00rootroot00000000000000astropy-astropy-201cddb/docs/changes/modeling/000077500000000000000000000000001507226315300215355ustar00rootroot00000000000000astropy-astropy-201cddb/docs/changes/modeling/.gitkeep000066400000000000000000000000001507226315300231540ustar00rootroot00000000000000astropy-astropy-201cddb/docs/changes/nddata/000077500000000000000000000000001507226315300211725ustar00rootroot00000000000000astropy-astropy-201cddb/docs/changes/nddata/.gitkeep000066400000000000000000000000001507226315300226110ustar00rootroot00000000000000astropy-astropy-201cddb/docs/changes/samp/000077500000000000000000000000001507226315300206775ustar00rootroot00000000000000astropy-astropy-201cddb/docs/changes/samp/.gitkeep000066400000000000000000000000001507226315300223160ustar00rootroot00000000000000astropy-astropy-201cddb/docs/changes/stats/000077500000000000000000000000001507226315300210755ustar00rootroot00000000000000astropy-astropy-201cddb/docs/changes/stats/.gitkeep000066400000000000000000000000001507226315300225140ustar00rootroot00000000000000astropy-astropy-201cddb/docs/changes/table/000077500000000000000000000000001507226315300210265ustar00rootroot00000000000000astropy-astropy-201cddb/docs/changes/table/.gitkeep000066400000000000000000000000001507226315300224450ustar00rootroot00000000000000astropy-astropy-201cddb/docs/changes/template.rst000066400000000000000000000022751507226315300223120ustar00rootroot00000000000000{% 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 %} astropy-astropy-201cddb/docs/changes/tests/000077500000000000000000000000001507226315300211015ustar00rootroot00000000000000astropy-astropy-201cddb/docs/changes/tests/.gitkeep000066400000000000000000000000001507226315300225200ustar00rootroot00000000000000astropy-astropy-201cddb/docs/changes/time/000077500000000000000000000000001507226315300206755ustar00rootroot00000000000000astropy-astropy-201cddb/docs/changes/time/.gitkeep000066400000000000000000000000001507226315300223140ustar00rootroot00000000000000astropy-astropy-201cddb/docs/changes/timeseries/000077500000000000000000000000001507226315300221105ustar00rootroot00000000000000astropy-astropy-201cddb/docs/changes/timeseries/.gitkeep000066400000000000000000000000001507226315300235270ustar00rootroot00000000000000astropy-astropy-201cddb/docs/changes/uncertainty/000077500000000000000000000000001507226315300223045ustar00rootroot00000000000000astropy-astropy-201cddb/docs/changes/uncertainty/.gitkeep000066400000000000000000000000001507226315300237230ustar00rootroot00000000000000astropy-astropy-201cddb/docs/changes/units/000077500000000000000000000000001507226315300211015ustar00rootroot00000000000000astropy-astropy-201cddb/docs/changes/units/.gitkeep000066400000000000000000000000001507226315300225200ustar00rootroot00000000000000astropy-astropy-201cddb/docs/changes/utils/000077500000000000000000000000001507226315300210775ustar00rootroot00000000000000astropy-astropy-201cddb/docs/changes/utils/.gitkeep000066400000000000000000000000001507226315300225160ustar00rootroot00000000000000astropy-astropy-201cddb/docs/changes/visualization/000077500000000000000000000000001507226315300226405ustar00rootroot00000000000000astropy-astropy-201cddb/docs/changes/visualization/.gitkeep000066400000000000000000000000001507226315300242570ustar00rootroot00000000000000astropy-astropy-201cddb/docs/changes/wcs/000077500000000000000000000000001507226315300205335ustar00rootroot00000000000000astropy-astropy-201cddb/docs/changes/wcs/.gitkeep000066400000000000000000000000001507226315300221520ustar00rootroot00000000000000astropy-astropy-201cddb/docs/conf.py000066400000000000000000000620451507226315300176350ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst # # Astropy documentation build configuration file. # # This file is execfile()d with the current directory set to its containing dir. # # 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. # 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 # documentation root, use os.path.abspath to make it absolute, like shown here. # sys.path.insert(0, os.path.abspath('..')) # IMPORTANT: the above commented section was generated by sphinx-quickstart, but # is *NOT* appropriate for astropy or Astropy affiliated packages. It is left # commented out with this explanation to make it clear why this should not be # 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. # 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. 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 import sphinx from packaging.requirements import Requirement from packaging.specifiers import SpecifierSet from sphinx.util import logging # xref: https://github.com/sphinx-doc/sphinx/issues/13232#issuecomment-2608708175 if sys.version_info[:2] >= (3, 13) and sphinx.version_info[:2] < (8, 2): import pathlib from sphinx.util.typing import _INVALID_BUILTIN_CLASSES _INVALID_BUILTIN_CLASSES[pathlib.Path] = "pathlib.Path" # 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.v2 import * # noqa: E402, F403 from sphinx_astropy.conf.v2 import ( # noqa: E402 exclude_patterns, extensions, html_theme_options, 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 = "3.0" # The intersphinx_mapping in sphinx_astropy.sphinx refers to astropy for # the benefit of other 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_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/pandas-docs/stable/", 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. # .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 templates_path = [] 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 # 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 = "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. # The full version, including alpha/beta/rc tags. release = metadata.version(project) # The short X.Y version. version = ".".join(release.split(".")[:2]) # Only include dev docs in dev version. dev = "dev" in release if not dev: exclude_patterns += ["development/*"] # -- Options for the module index --------------------------------------------- modindex_common_prefix = ["astropy."] # -- Options for HTML output --------------------------------------------------- html_theme_options.update( { "analytics": { "google_analytics_id": "G-R0510VK4B6", }, "github_url": "https://github.com/astropy/astropy", "external_links": [ {"name": "Learn", "url": "https://learn.astropy.org/"}, {"name": "Packages", "url": "https://www.astropy.org/affiliated/"}, ], "use_edit_page_button": True, "logo": { "image_light": "_static/astropy_banner_96.png", "image_dark": "_static/astropy_banner_96_dark.png", }, # https://github.com/pydata/pydata-sphinx-theme/issues/1492 "navigation_with_keys": False, "announcement": "https://www.astropy.org/annoucement_banner.html", "header_links_before_dropdown": 6, } ) # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". html_title = f"{project} v{release}" html_favicon = "_static/astropy_logo.ico" 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" # 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 + " 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 + " 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://doi.org/", # CI blocked by service provider "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://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 def resolve_astropy_reference(app, env, node, contnode): """ Reference targets for ``astropy:`` are special cases. Documentation links in astropy can be set up as intersphinx links so that affiliate packages do not have to override the docstrings when building the docs. """ # should the node be processed? reftarget = node.get("reftarget") # str or None if str(reftarget).startswith("astropy:"): # This allows Astropy to use intersphinx links to itself and have # them resolve to local links. Downstream packages will see intersphinx. # TODO: Remove this when https://github.com/sphinx-doc/sphinx/issues/9169 is implemented upstream. process, replace = True, "astropy:" else: process, replace = False, "" # make link local if process: reftype = node.get("reftype") refdoc = node.get("refdoc", app.env.docname) # convert astropy intersphinx targets to local links. # there are a few types of intersphinx link patterns, as described in # https://docs.readthedocs.io/en/stable/guides/intersphinx.html reftarget = reftarget.replace(replace, "") if reftype == "doc": # also need to replace the doc link node.replace_attr("reftarget", reftarget) # Delegate to the ref node's original domain/target (typically :ref:) try: domain = app.env.domains[node["refdomain"]] return domain.resolve_xref( app.env, refdoc, app.builder, reftype, reftarget, node, contnode ) except Exception: pass # Otherwise return None which should delegate to intersphinx __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) # Set this to higher priority than intersphinx; this way when building # docs astropy: targets will go to the local docs instead of the # intersphinx mapping app.connect("missing-reference", resolve_astropy_reference, priority=400) astropy-astropy-201cddb/docs/config/000077500000000000000000000000001507226315300175745ustar00rootroot00000000000000astropy-astropy-201cddb/docs/config/astropy_config.rst000066400000000000000000000002031507226315300233470ustar00rootroot00000000000000.. _astropy_config_file: Astropy's Default Configuration File ************************************ .. generate_config:: astropy astropy-astropy-201cddb/docs/config/index.rst000066400000000000000000000411211507226315300214340ustar00rootroot00000000000000.. _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`:: >>> from astropy.config import get_config_dir >>> get_config_dir() # doctest: +SKIP And you should see the location of your configuration directory. The default configuration directory is ``$HOME/.astropy/config``, 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 astropy-astropy-201cddb/docs/config/ref_api.rst000066400000000000000000000000741507226315300217340ustar00rootroot00000000000000Reference/API ************* .. automodapi:: astropy.config astropy-astropy-201cddb/docs/conftest.py000066400000000000000000000033461507226315300205340ustar00rootroot00000000000000# 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 import tempfile 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. os.environ["XDG_CONFIG_HOME"] = tempfile.mkdtemp("astropy_config") os.environ["XDG_CACHE_HOME"] = tempfile.mkdtemp("astropy_cache") Path(os.environ["XDG_CONFIG_HOME"]).joinpath("astropy").mkdir() Path(os.environ["XDG_CACHE_HOME"]).joinpath("astropy").mkdir() # Note that we don't need to change the environment variables back or remove # them after testing, because they are only changed for the duration of the # Python process, and this configuration only matters if running pytest # directly, not from e.g. an IPython session. @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 astropy-astropy-201cddb/docs/constants/000077500000000000000000000000001507226315300203435ustar00rootroot00000000000000astropy-astropy-201cddb/docs/constants/index.rst000066400000000000000000000156561507226315300222210ustar00rootroot00000000000000.. _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 2018 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 2018 >>> 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 astropy-astropy-201cddb/docs/constants/performance.inc.rst000066400000000000000000000007601507226315300241510ustar00rootroot00000000000000.. 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`. astropy-astropy-201cddb/docs/convolution/000077500000000000000000000000001507226315300207065ustar00rootroot00000000000000astropy-astropy-201cddb/docs/convolution/images/000077500000000000000000000000001507226315300221535ustar00rootroot00000000000000astropy-astropy-201cddb/docs/convolution/images/astropy.png000066400000000000000000000232741507226315300243720ustar00rootroot00000000000000‰PNG  IHDRĶŌĐ[õ| ”iCCPICC Profilex­–wTSŲ‡ĪŊ鍖:„Ū‘N„ēt•@¨1„ލˆ ŽāXPÁ2ĸƒ" 6Š *bÁ `Á>Aå9XĀ‚Ęģ„3ë­7˙ŊuÎũ˛Īīė{ÎÎÎZ˛'W$J‡åČf‹C}ÜŅ1ą Üc u Âåe‰Ø!!āmęĸFėļųlŦ”ũīy~b(YNāgņ2>ƒ 3žHœ ,AüzyŲ"„Qh„ibä€+Ξ`ŽÍf9aŽYRMx¨ĸ OærÅHņˆŸ‘Ë qHÅ[ ų)B„O ėĘKæōž@Ø,#c%Âdu„ūGđ7ærbrš‚žģ ˛yągJ–([ ũō˙œ2Ōs|IŽĖdQļ{(ō¤!9SNÉæ wžãä߈y.LšgaBPđ<ķ˛<\ÎéĶVú/Äá'zzÍûŗrø0Ų#h۟Ęõ›ũͤīåŠú‹˛CÎ Lš­Š&IėŊ?1Ë+l۟-_đ'ĨxsæũĸtiÍI÷ŠsBî’(ŒXØËįzúĪë'đȇ"€5°VĀ2;1?Y+EâAr6ƒThĸƒ#äY˜1Ŧ-­ŦÁlŊĪjx_ZĮ˙—¯ú ^HŽ ŲōbHe€é Ā1¤Fqųô? àķ/Gœ;§›-WéŋHɀ ĐēĀ˜#gŗ΀…œÖƒp–H@ ō@XĘ@Ø v‚°GĀqp ´Np\7A?¸  ¯Á˜Ķá D…T -H2…Ŧ!&ä yAP(ÅCHå@EĐz¨Ē„j Pt: ]„ŽCĐhƒŪA_`L†i°l/‚™0ö‡Ãáe°Î„ áRx3\ ×ÁĮāVø"|ž Kā×đ$  H(:JeŽbĸ“Č&dr9‡ŧ™|˜ÜE~@~OĄP (,J,%›˛™Ō@šDyJų$C•ąáČđeÖĘÔĘ´Ę Ęŧ‘%Čę˲e—ËĘVɞ–Ŋ%;.G3ķãĘ­‘Ģ•;+7$7)O•ˇ’–ΐß$TūēüKœ‚‚—_ĄTá Â%…*ŠĒKõ ō¨ëЇ¨W¨Ŗ4,ÍÆĄĨŌ*hĮi}´ EE[ÅHÅ|ÅZÅsŠ:Šn@įĐĶé[č§č÷č_”4”ØJ‰J•š”•>*Ģ)ŗ”•Ë•›•ī*Qa¨xФŠlSiSyĸŠV5Q]ĸš§ēOõŠę¸MÍY§VŽvJíĄ:ŦnĸĒžJũ z¯ú¤†Ļ††HcˇÆ%qMē&K3Us‡æyÍ1-Ē–ĢVŠÖ­ Z¯Š 6#QÍ¸Ė˜ĐV×öÕÎŅ> Ũ§=­c¨ĄSĸĶŦķD—¨ËÔMŌŨĄÛ­;Ą§Ĩ¨W¤×¨÷PŸ ĪÔOÖßĨßŖ˙ŅĀĐ Ę`ƒA›ÁKCeCŽaĄaŖác#Š‘›QĻQŅcŦ1Ķ8Íx¯qŋ lbg’lRkrË6ĩ7M1Ũk:`†1s4šÕ™ ™“ÍŲæšææÃt‹‹‹6‹7‹ôÅ.Úļ¨gŅwK;ËtËC–ŦŦüŦJŦ:ŦŪY›XķŦk­īØPlŧmÖÚ´Ûŧĩ5ĩM´Ũg{ߎjhˇÁŽÛî›ŊƒŊØžÉ~ĖAĪ!ŪaÃ“Æ anb^sÄ8ē;Žuėtüėdī”ítĘéOgsį4įŖÎ/.N\|hņˆ‹Ž ×個ĕáīúŗĢÄMۍëVįöŒĨËâŗęY/ØÆėTö1öwKwą{‹ûG'Õ]ž(OĪrĪ>/¯¯¯§Ū:ŪīFī ;ŸU>]ž_ßmžC ĶĀ™đsđ[íw؟ėæ_ã˙,Ā$@Đún|¤$ j ÁœāíÁOB C2C~]‚]˛¤vÉķPĢĐĸО0j؊°ŖaSáîá[ÂEEäDtGĘFÆE6D~ŒōŒĒŒ’D/Š^}3F5&%Ļ=[;šÔkéÎĨŖqvqeq÷–.Ë_v}šęōôåįVČŽāŽ8‰Š?˙•Ė­ãN&pö$LđpÖīlw‡sG˯ŋîÔîŦ=§xnËyâųŌķ3 /Lv‰ēÆ/ .Žt¯č~t)úŌËK.÷]ņŋríĒ÷ÕK=ėž ×\Žu^wē~öķFÛMû›­ŊvŊ-ŋŲũÖŌgß×zËáV{ŋcĮĀâķƒnƒo{Ūžz‡sįæŨ ģ÷"îŨŠ’Üįßų ũÁۇš§?Æ<."÷¤ęŠúĶēߍo–ØKÎ {÷> {öh„7ōúŦ?žŽ–>§<¯zĄõĸáĨõËÎ1īąūWK_žŊž/û—üŋöŧ1zsæO֟ŊŅŖoÅogŪmz¯ōūđÛŨ“!“O§2ĻĻ?–Rųtä3ķsĪ—¨//ĻķžâžV3ūÖņŨ˙û㙌™WĖ•ö(d†“’xwJ Ô~ˆ2sũ¨TÍõĐĪöŌŗcÖū‹įzVéŠ=Mŗ°íād1ČS œ`›…xg-+ÉÆZ Š iMĒffŪ#}!΀oC33Ķm33ßꑾų!]Ss}đŦZîŠl˙¨€įWK÷˙mú7\&Ų2ANÜ pHYs  šœÎIDATxíŨYĪĮqā&y¸‹wIÔBkŗĨXvœ Åä"šM~Büįrä:ĀÄxDŲZ¨]ÜW‘áq xęi}Ķ3üd]/¯NĒŽŽ~gę°Ū¯zzöníaÛÃ??Ā×>äŖų ôˇ‘Gūáhü#Yڏæ×^Ŗņęõ§ŦŊķi¯Ŧũ~žØ @özŪGŋözbüAäŅ|˜w7ŋãī1Āø§ją™ęō9lFŋ\#_ŖņūŌų?™Ž?Į+k¯ŧ6ū‘}Œß_ļĩķŋōȟz×ŗV¯Ŋņ(¯ĩwŧøęĪ˙ÉÔį +Ą#čÅį*úO‘o"OãÅÎЈA ė†@’i7dō}X‰@’i%`1ģ!0äL֔:ÕŧrũMkέoŗ[ûŅüÆŖŊ˛ūī_‹üë•öús>eĮdĮ;ŸxęOŊū”¯Ŧũ(õúSÖŋúcüōČŸŠ_\ģVåŸ!˙´ĒÛ{Čw&˛÷îD•A ŦA É´­Ø’L3āDÖ °ųĒŗiTÜh~ũ9^Ŋ˛5ļãGzũŲWR?ZĪh~ãQvŧČųOęõ¯Ŋ˛ãgęõįüŽĮ>Ō+ øģŗõ‹˙¸Ę§ūâ…úÅ{•ũĶ?Ū-ú˙ģˆí“*ļé cÅ4bKH2-E*vA`€@’iPÔA`)g˛†ŨĢl ÖÔĘÖāŽ˙ēåG2Ū^Ŗ_/ĮÖĢ?ņtŧū•Gö#˙#ŊūG˛ëÛaĀËČßũnũâÔEF<û\58qĸČß˙ķ_ųœÉžãt—ŧąG‚@XŽ@’i9Vą ŗ$™fá‰2,G`3­ųļÃFŲĨŊ{ÕæjĘ­˙úWüÖnmŋœüŗ/aM/G™ ũJ>Žæ[;ŋxŠß(čŊÎ'‡1įwž‘ŦíÅSy4ŋzĮ;ß5Húā­úÅSû*'jû*"Ÿ}Vgôų;įŸŽž~Ž^" ° $Ķ*¸bvG É´;6ҁUlä8ŖŅÖüĘŖĶcŦ9§{›ļs[dköQüÎŋÖ~ôë#rJõƯ<Šo¤wŊʎ—ŗĪčz¨×ŋōČŪ>ß ŧƒüĢ7ëûüĨŽT¤ũIũâí*v÷įô~˜~fXÄ Ö dZƒVlƒĀ IĻpĸ kØ÷44ŚښÖėŖ$m#YŽäßņ­‘TÃģxã1~í×Î'^Î''Uīzå,Æ3’GÎĻŪžßôLƒ-6kã_e¯Ÿ˛×Ãņžƒw/"säC;‡ŪûņŋĐŗ5¯ynŪôú+Ž" °$ĶR¤b$™E–"Н›7Ę.knkøi š š[Ų@õ¯~m|reãˇ†WÖŪx•˙Ģ/r2Ī•SåŽķN4eũ‰‡ĩ—Ķ}ĖãŊë×ūsėGøLÍ]ëT—ĪA Ŧ@ É´Ŧ˜9’LsčDV Đq&k~ŗÍךv4ˇū•Gķé4ŪY˙úSīz÷*đr=Æ7ZzĮËiĮųÅcd/>rdõÆ7Ō;ŋ}JĮģ×o|ę•åXĶųÄĘĩEA`!IĻ…@Å,ŒH2Š>,D`3­ų–ŒŅ^yäCûŊfŗ5ąūԏâ[k¯ŋĩãÅÃņĘrĸŅzG5ŋzįĶŋë5~e9ŽzįĶŋ˛ãå<öĄ/įqŧūGņOũ°šÚæs3$™f‰*ŦA É´­Ø:Î4ĒG5ĨsYäĩŲ­ŋŅüęGōČŋãĩWá§ŊIYŧŧ~r"į_;ŸūåĘú¯ĩ˛ņū´7>Įk¯^˙SŲk1Õås+H2­+ĻA`$Ķ:ҁtœÉšR_֔ę×ÖČ#{įŲĢ×Ūõ(ėÕ+ëoT“Ë‘”õ¯ėú•ũ5U6^īΐpŧöŽ×xœOũHŪë|Î?ÂsjīÚ§ē|A`IĻ`Å4Ė!dšC'ē °ÍÚVßŖšRŊ˛ŲŦŪųÖĘús>9Č^ũ¯īürãÉúsũę=×Ī3#Ô{&‚'wËĄä0ĘŖûo¤¯ĩ˛×Kŧô§ũTöÚLuų‚Ā ’L+Ši˜C É4‡NtA`÷nY3ęk¤7;ĩ_+;ŋōȟÁ÷GÁĄįꉏ5´ķË ”ĩgúîũ?Î'žÆĢ9ãOĀKČ'‘/!ŋ…ėû“\ŋh$‹˙Zâ!žke–[Dą-ĘA ,G É´ĢXY’LŗđD–#ĐíÍs¨5§˛Ų¸V֟5ŦūFņi/§8ŒƒČr Ī P¯ėû{<“Āõ1}“CČÄKNhŅĨKÕÁŠÃ°WÎUû‹Ģ|šFĶåËU˙ŋĒ2b“S‰GŨēŊ‹â9˛—ŖŽÆëĪë?•ÅŪą‘ƒ@Xˆ@’i!P1 #’L#„ĸ ØČQ7Ē)§5ãvėZŲųíË(Ëa¯lŸ‚ĮsšœÄŊwöyŦé•í#ŲwQv/šŋnÆŖė^ˇÛ\Āwųâß˙­œƒđũ¨ÚŋņWŧōíjđŪģEŪųįēâĢĖī™ŪoâĢ,^ŪâëõQ–S){Oī7c)@DA`9IĻåXÅ2Ė"dš…'Ę °îy&kVåQÍjMi(ĶsĢŖorÚMYÖĖrÚ*í<>G#įÉ'ĢÁ}ŠđO>­úĪÜ|VՍáÍ3ÄOŧGÃ_GĮß!}íĩđ~PåöęĢõ‹į/VöחRôŋøEÛÛ×Ģ,Ư,^ŪĘâ/^ίžFÛJ_qdëØČA ė‚@’i`ōuX‹@’i-bąģ Đq&íŦIGĘņf̜FÎD ßäŠp×/žutkö9ė+Ößé ā0€ž ƒOrųQv/ūđ_jČ˙ų÷E>úâ…"_|ūũ"ŸāĖ āíö.Їøyŋy˙Žôr2āęú¨ĶÅč{ĒËį V dZVLƒĀIĻ9tĸ +čΡf\[sšÖėÖ ”ė]IĨŋŅ|އ´—^ŦhŊūŊ*ī<]Yץ÷ëiâžÅŊOĀĒģŽ†ē{^Įõ‰Ÿz¯[ķÚU6Ŗᐇkô}Ú-<Åžõu rĒõЁršWuĮQäHr(eņQŽŅĩŽŖđžö­ô]‰‚Āb’L‹ĄŠa˜G É4O´A`1›i͡eÍ­ŧØķ ĨÖ¨Ö¤ö‰äTÚûk GōųŸgžŽ+øÎĢUŪy–Ũ{ ÛPˆ;ô‘\/ ŖÃW,D {žÉUYŋr&ŗSũZYŽQwĘĩæûˆ|üæ…kÄ/ŋRå'<8īęĩbpųŨĘbŪ}¯¨ÛÚ†in]{€û!)rņaxüK$ĮËąždHųę:ņ]z̍ÛGœ]žT|Né#PúŦēkPÎr ×Įô]¯^Ŋž/'oûXŪßĶåĖéĻvų‚$Ķ ¨ƒĀR’LK‘Š] °ąRøƴæ´ĻUÖۚwúwûmė´‰ēŗÁΞ•NŽôúëW^Žr;Q;SŽTޞwíEüq8[ųšgFė€ĩÕ#xŒŸģƒ4bnĀŲ‡}&9МáætķŲ#čŽÃ™œ¯ĸךz9ĸ2ĶuœJŊ2—§É™¸úÍõË1õgŧĶË3ũė<‘ƒ@X@’iX1 s$™æĐ‰.Ŧ@`SCk ĐũŨŪėŖ„īÎ8ķ(ËŲ  JÔNÂ9Ns6øųķuõOqVx;É)œQ°Ŗąuū\õ‚€¯ĶW‘s H—SŪ§Čw}ž?éũË5ž;4F€Ģ;SA}õÖsf9ĨO¤9˛ķÉąÔËY€ĢãHr ņ֟œQ=đNĘ­c葃@XŠ@’i)Rą ’L€ĸKčÎÍŗÆÕ‘5¯ Ņädr¤‘,g’Cœæ(l9 ”¨ĩîІZEīÛ_åķįkŊC@w)ĸ}ŪÉį{ė3IJ=ĻîAžÉÉn3ŋÂ_K¯¯ö^oeû:r9•÷‹ķ+k¯,GÃíÎj×^|tāüę§ø|96rģ dÚ˜|Ö"dZ‹XėƒĀ.lü;ēÆ͚•6LĮ‘äLÚ+k’€Îđ>%ûJŧRĩyļuû‚WŠÔÚĄēÂÃ'ęnÁÃ6zž¨ŒsˇëAO~RY…ĪOŠî›īœuoß­ę~—ËúĮ¯kt}ßhZķoGNéŖlõu5­ŽãõÅëü{ŋųk_¯N}§ė6>9˜ņŠĮvĖôŸˇƒūäPSÆ:õ›ĪA Ŧ@ É´Ŧ˜9’LsčDV °ņ,lJøŽĩft<§{ߍ‰ļM;É;lĨskŨŲŗuĩO˛o˙QnžŗŅc#č(+”„YdĶ:vļVí¯Ŧ›éNžŦŦâÃëz>ãЄ>Žú {ũöRõ#N#GqûHÚO9Ä6õöŠj´=§cy{Ū[(gsũ^N˙ˇQžÚĢsm‘ƒ@Xˆ@’i!P1 #’L#„ĸ Ø@Iēgâa ōīūĘŖŊ{§ Ygā@ö‘.<]WvŽtāģOĀĘØ{×ā8Ũaáž=.Į˛Š?H•ˆÎ}Ŧ OTRtū|•?ø ŽWĘf8žĶwCîBzˆļÉiäĘSΰtí¯ŗËņ#˙ęå@ĘÎWkÅz+9^‹)~ÆŽmä "dZT˂$ĶĄčƒĀB†}&Hˇ÷ĘĶŽO‹ČGAÃĀį|>I$GzÎtâ,ÍwŦü^ČĄäHžpé>‹vÎsŧ}̃•ômŽ×NÜŗ‡Ū/—ōÆÍZõۇ:IcÉ­„ž.§ÚWŨw{įÜĢÆå–Av ÷Ũ™  Ûq–‡ņrxõ×xô¯ŦũTvŽrá" °$ĶrŦbfH2ÍÂeXŽ@÷<mˆîų÷îŅEi>¯cßČįÜK§Ū>Ķą3tŽž€”ų ĶĒö06bÄĘĸ] 'zˆ˙{v.¨ē¯ėÁz‡ęfÅÍÚ BŨv¸ RHˇę8É]ÎV—ƒČ9äPÚ+ƒF÷>ĻēēÖŧAģÛûįås~ã7eoõĶxō?“hE‰@’é1Ë° I&‰ÍښÔyjE˙č}Jœĩ-‡ęÆãĀžHGqîąÉN¨,gҟgF¨÷…Mr(ŸŸ˛¯Ĩ?}¸}Ĩ˛÷Ö†Äú|—ËķÜ=ũÉŠ<Px•§b ĩ Ryôk.G‘Ŗqûxuģ>™ķŋäXŽoēWQßúŠ‚ĀB’L ŠY!d!}XˆĀƚQؚ՚WŲü*ĪĶæŒĪķLãą ?žŸ \¸íÉa8¯IîVĶė+y¸ųQúbėŊkÚCr Ī­}§8âÂĮŗžāŲvķL )ā†vš|ß'UOŧhŨ;i ĮĢŅ9ÂÎËÎŨí´ī¤Üß_õ’“™SëüĪ4E#ŸƒĀH2íŧ S’LS4ō9끪čjPįōīîÖ¤ö-ް÷Kpˆž ĸŖ(÷ā4'nURvVøRņ€€q×hû´ÛkčcP¤Ķ§ëFûí+ Ŧ€##ēŊļÁŽ]¯Wčfžų8–ĶŸáÁR>9“ū¯ÁąälĀŨí ŦҎŽOäxī?ûPĘūīĄ?9˜zå)įŌˇk‰‚ĀB’L ŠY!d!}Xˆ€[¨ØûšÕš’­x]ŸāÎtķŌŖ î|T#ŗĻ–ŗ\•cÁI<‹Ü3%ŦŦÆ3|‡Ŧķkßõa@đ$}ÛPgî×sņäDžÉ>Ũ 8‘úO*el×iÃAëÅx$uGV€ˇĪOųø•÷!×ĪÍzPēŽŖÛ—’ŗÔĢۚ}!ûTPōŽ“y?>]ÆōÍUú6r*û`ö}ŒßÆĸGŖģUĐŊųųõy*Įë šÆ ÅîΈ˜öuļW’p:ÎoŸIYüõ/Gƒ˛–ŗŲĨŪi‘‚@XŒ@’i1T1 ķ$™æņ‰6,F`#įĄmŌÕ¤î…˛ÆÕŸŲĒJæ.đ øwī›}÷ö}û€wÆē×Îäg‚dZƒÛˇ˛cßJÎä^F÷Ū9ŪŖŌŨ[ˇni/ôŋŽGŽčúŽ0ß9n€Ü@7oÖ+ |€ŊäôęíkôÜ~mNö^¯+‰‚Āb’L‹ĄŠa˜G É4O´A`1÷*QŌv{íÔģWĪ™ÍVįcëWSœ>Ķ“OÕ”ísxNœ{ũŽĶ8#Ų—ņŧŽsQĶģˇĪFˆœį&Eųįôeė+U4[G1^Ÿrī¤}ąc4 åDÚģ”“šˇOüå¨÷áXö…äôr(.ww–>”­Éąė;M˙FāŊîĩ‰‚ĀB’L ŠY!d!}XˆĀJŌq$ŗMyކܯ`MĢ=Ŗ<˛‹"VŽ"Į}H_é {ŨŽâßxvā ‡‘å÷Ųûgŧr:įĶŪõŲq¯Ų~H‚IŽãüîÜŲŲ^…?ūķœÃŽãLIÄŖawˆĮ3AÔßa<˛A!ģžĶ•ŊsÛU¨—ķCÉ|üę@üáĶôoæFgœ/‚@X†@’iNą C’LCˆb–!Đõ™|†ŪšZdÍî´Ö¤P–Ž&Ö [_× Y×/ÕŪŋĘøy¸'ē?kr×û’r„ĸû>ÄGÔõYĐs阞ÃËžĘ=âyČ@]Á{$ŨäĖãņų%xöø 5l•ėÎ"w=ޟđ:ÎÄr;ŧ dG—3~÷ŽŨŠ?n=Ą‰‚ĀR’LK‘Š] duXŠĀ†Įqēį—ŦA­)§5ãvŌ‘ŠĐÕ´rkhŽŨkŋƒôČų”ƚÜų Hí(¤ÃõČųž@÷ŽyƂŒéēžŨ×믧˛ņÛGģÁFėÎĄ?÷Æqųf9ɗŨ_Pāîū3>.G×w’#Aš;Ž5Ŋ_ÄRė# °$ĶB bF$™FE"°1›Ŧ)­!­Ņ•§5ä69˛ķ)ëĪxG5­@ŽhÍ­lÍ-‡ņy,9”ū”í yŨ\¯ķŸ˛ë•CęßëãõoįsŧëÉ^eīįS/īW9ŗ}P×īõ›úËŅZŖA`’L쓝ƒĀZ’Lk‹}؁5¯5¤}ŗĪš\Î` kίlj m|ÖÔrȘÖŧÛØoüŽ×ŋm´^ã7į7>ņp>ã]ãņ~CČAŧ?œĪõi¯ėzœĪõ{˙¸ãˇĪĨŪųŒg*bŸÚæs3$™f‰*ŦA É´­Ø6rmåÖāĘf፿w>9zûö=ŒWyTŋņXSËŦŅå ÖøÚ{=œßųŒWëzåúW¯lüú7>×?ēÔ{ũg´×ON¤ėõt=Î?'{-ælŖ A`$Ķ 8Q5$™Ö Û 0ƒ@÷<“5ˇ5°5­ú™š~¯2{9†Ž9ŋ5ŽIÎ/1~įŗ$įԟņXĶ;Ÿzkt9Œøē^ãsžQ|ú?eíGņ:^åx9™œÆ3 Eúuūo +Gąæ•CdûZúŗ¯Ņˇ6/'ҟ}k|āéæĶŋãÅk4Ÿë‘čĪ_Gį—#š~9 —ŋ;kÛõã‚ČIŊžâŠ?õŽ$N'OgĀŪŋSŧ§Ÿ15$™Ö Û 0ƒ@’iœ¨‚Ā6G(ĸīņ‡û{Õ÷)Ē­é­q­­Š}ĮëmH™Ų.Į°†5kbĮ[ĶĢ×đ4āéŪoåüĘr×Ŗėŕ3ŠwŧņjüŨzô'>ĘÆįũĄ,>Ž7>9“ëQ6~ņđ~õūS?W[įŽ‚ĀB’L ŠY!d!}XˆĀæétoZ>r˛ũ!ô6ZŦqQdž>U#ۇŋO>­úƒÕOTuŗ&ÜîũPÚˑpßŊ#U˙Ž—å49‘ķÆáHœœVŽųøÜf3Ũm8ŽöwY€œĮø1÷ōw}(×ËíĐŊÃVN¤,g5¯Ÿ˛ë9ށœ~˙ô~âRâ9b‹H2-†*†A`$Ķ<>ҁÅüåËĨ÷$uŪIENDŽB`‚astropy-astropy-201cddb/docs/convolution/images/original.png000066400000000000000000000243201507226315300244660ustar00rootroot00000000000000‰PNG  IHDRĶŌĐ[õ| ”iCCPICC Profilex­–wTSŲ‡ĪŊ鍖:„Ū‘N„ēt•@¨1„ލˆ ŽāXPÁ2ĸƒ" 6Š *bÁ `Á>Aå9XĀ‚Ęģ„3ë­7˙ŊuÎũ˛Īīė{ÎÎÎZ˛'W$J‡åČf‹C}ÜŅ1ą Üc u Âåe‰Ø!!āmęĸFėļųlŦ”ũīy~b(YNāgņ2>ƒ 3žHœ ,AüzyŲ"„Qh„ibä€+Ξ`ŽÍf9aŽYRMx¨ĸ OærÅHņˆŸ‘Ë qHÅ[ ų)B„O ėĘKæōž@Ø,#c%Âdu„ūGđ7ærbrš‚žģ ˛yągJ–([ ũō˙œ2Ōs|IŽĖdQļ{(ō¤!9SNÉæ wžãä߈y.LšgaBPđ<ķ˛<\ÎéĶVú/Äá'zzÍûŗrø0Ų#h۟Ęõ›ũͤīåŠú‹˛CÎ Lš­Š&IėŊ?1Ë+l۟-_đ'ĨxsæũĸtiÍI÷ŠsBî’(ŒXØËįzúĪë'đȇ"€5°VĀ2;1?Y+EâAr6ƒThĸƒ#äY˜1Ŧ-­ŦÁlŊĪjx_ZĮ˙—¯ú ^HŽ ŲōbHe€é Ā1¤Fqųô? àķ/Gœ;§›-WéŋHɀ ĐēĀ˜#gŗ΀…œÖƒp–H@ ō@XĘ@Ø v‚°GĀqp ´Np\7A?¸  ¯Á˜Ķá D…T -H2…Ŧ!&ä yAP(ÅCHå@EĐz¨Ē„j Pt: ]„ŽCĐhƒŪA_`L†i°l/‚™0ö‡Ãáe°Î„ áRx3\ ×ÁĮāVø"|ž Kā×đ$  H(:JeŽbĸ“Č&dr9‡ŧ™|˜ÜE~@~OĄP (,J,%›˛™Ō@šDyJų$C•ąáČđeÖĘÔĘ´Ę Ęŧ‘%Čę˲e—ËĘVɞ–Ŋ%;.G3ķãĘ­‘Ģ•;+7$7)O•ˇ’–ΐß$TūēüKœ‚‚—_ĄTá Â%…*ŠĒKõ ō¨ëЇ¨W¨Ŗ4,ÍÆĄĨŌ*hĮi}´ EE[ÅHÅ|ÅZÅsŠ:Šn@įĐĶé[č§č÷č_”4”ØJ‰J•š”•>*Ģ)ŗ”•Ë•›•ī*Qa¨xФŠlSiSyĸŠV5Q]ĸš§ēOõŠę¸MÍY§VŽvJíĄ:ŦnĸĒžJũ z¯ú¤†Ļ††HcˇÆ%qMē&K3Us‡æyÍ1-Ē–ĢVŠÖ­ Z¯Š 6#QÍ¸Ė˜ĐV×öÕÎŅ> Ũ§=­c¨ĄSĸĶŦķD—¨ËÔMŌŨĄÛ­;Ą§Ĩ¨W¤×¨÷PŸ ĪÔOÖßĨßŖ˙ŅĀĐ Ę`ƒA›ÁKCeCŽaĄaŖác#Š‘›QĻQŅcŦ1Ķ8Íx¯qŋ lbg’lRkrË6ĩ7M1Ũk:`†1s4šÕ™ ™“ÍŲæšææÃt‹‹‹6‹7‹ôÅ.Úļ¨gŅwK;ËtËC–ŦŦüŦJŦ:ŦŪY›XķŦk­īØPlŧmÖÚ´Ûŧĩ5ĩM´Ũg{ߎjhˇÁŽÛî›ŊƒŊØžÉ~ĖAĪ!ŪaÃ“Æ anb^sÄ8ē;Žuėtüėdī”ítĘéOgsį4įŖÎ/.N\|hņˆ‹Ž ×個ĕáīúŗĢÄMۍëVįöŒĨËâŗęY/ØÆėTö1öwKwą{‹ûG'Õ]ž(OĪrĪ>/¯¯¯§Ū:ŪīFī ;ŸU>]ž_ßmžC ĶĀ™đsđ[íw؟ėæ_ã˙,Ā$@Đún|¤$ j ÁœāíÁOB C2C~]‚]˛¤vÉķPĢĐĸО0j؊°ŖaSáîá[ÂEEäDtGĘFÆE6D~ŒōŒĒŒ’D/Š^}3F5&%Ļ=[;šÔkéÎĨŖqvqeq÷–.Ë_v}šęōôåįVČŽāŽ8‰Š?˙•Ė­ãN&pö$LđpÖīlw‡sG˯ŋîÔîŦ=§xnËyâųŌķ3 /Lv‰ēÆ/ .Žt¯č~t)úŌËK.÷]ņŋríĒ÷ÕK=ėž ×\Žu^wē~öķFÛMû›­ŊvŊ-ŋŲũÖŌgß×zËáV{ŋcĮĀâķƒnƒo{Ūžz‡sįæŨ ģ÷"îŨŠ’Üįßų ũÁۇš§?Æ<."÷¤ęŠúĶēߍo–ØKÎ {÷> {öh„7ōúŦ?žŽ–>§<¯zĄõĸáĨõËÎ1īąūWK_žŊž/û—üŋöŧ1zsæO֟ŊŅŖoÅogŪmz¯ōūđÛŨ“!“O§2ĻĻ?–Rųtä3ķsĪ—¨//ĻķžâžV3ūÖņŨ˙û㙌™WĖ•ö(d†“’xwJ Ô~ˆ2sũ¨TÍõĐĪöŌŗcÖū‹įzVéŠ=Mŗ°íād1ČS œ`›…xg-+ÉÆZ Š iMĒffŪ#}!΀oC33Ķm33ßꑾų!]Ss}đŦZîŠl˙¨€įWK÷˙mú7\&Ų2ANÜ pHYs  šœâIDATxíŨKŦeĮUāēöqˇíN?ėn÷ˎĶNg`" $‘BĸHQˆPPf‘2a„ "ˆį ƒ0H€ "3&‘"…A"H!€ķ"/’8nÛũöŗģũh;œŨžûŪ]ß>§ëė{ģÛI÷'įŦ]UĢÖúĢęÜõŸUUgí–R~TÖ˙6Ūôx]kȝPŪÕ7ŖAKßÜöęO}Uá„Wi#>–S}%ņ֕jmVŌĮ–MÖߎÍb~ÛĻi—ß9†ö÷"õĩ_{íO}ö;ú;Q-ųt<ŦÍ}ązûįy A`"YLKõ ° ,ĻeČäy˜ˆĀl¸šä(ËbÃe}īNmo‰Õ" °*YLĢ"•zA @S UX›Į aŠ+ËøˇĨtCŅzEÛ[ŽlũĐ^íŗ\Ųūm/gŗžr×^Z˛6YĒMÖ×F}’3iōTũÖWÖ_ûSŪ‚!˙Úûy0ņ_Da\gøäüĮkĢŪņÉai)?¨ÅŌķBmĨZÄ VE ‹iU¤R/4Čbj”â °*ŗá÷üÃ÷#Í:’,å ô"ĮqĪĶŗÔ÷l ÅŜ‚ņžõĩOšÕŪO–Šíĩ§“Õ9Õ'mvŒėĶúĘæöl¯l{eëˇd1ĩ~kΉįIœ=Ã-ˆ§O׍Ü㊠}íeĪûōŧ °"YL+•jA …@S Ą”˜ ĪˇVZ˙ũzߗé`_°äõ)žËš(åpŒ¯•m¯ÜŠßõ_|ĩ—ĶČQÔŠMĘÖ×닁˛6ĢßŧSĢ?ûoÉÚŖŦ}Î Ī]¤ÃķđŅŧü؇j­īúŊūí›+-ŋņ7•XėŖ.ŨäÅbiŊČA Ŧˆ@͊@ĨZh!ÅÔB(åA`EÖöŌIud9(XWæĘō­ŽŠņ­õŋå\ļ×Ûß r<ō1jéŗžūuår&īĨPž€Rs- h>â•bĻ>}ļ1ĩŋ–,æŽqĢŊåb~7Ž!wĸ\Ū3Pģhķyäú´S)úÔįKĩ 5ƒ@X,ĻU‘JŊ Đ@ ‹ŠPŠƒĀĒTœÉx^NŖRcGãoˍ—[ú×Õ/?‘xīô8āŌō‰ĐžNŊŸN-YŸôÁ>Z˛˜ėÁį7"YŸ3û§ųˆŗyšEÛO•Ķũ hŗķNˏōĄˆ‰úú1sŦ˜’GA Ŧ‚@Ķ*(ĨNX,Ļ@J• ° kw ŌIrĪŪ+ļâ}cQ 2Un­tËĩßūäp–ëŸåĢČbĸNmÔÛÛgŸûŧ—õŅ1´÷ēɑ”õ§īwŲëÔúËô,{Žæ>ģvbÖōÉúĘËlq,—ÕËķ d15JqX,ĻU‘JŊ Đ@`6Œiå8Ã˛NąĻņŊí[ąĻņģņ¯ũë‹åōõˇ>9ô§eŋö,’§öŠOęTŸš4mVŸšĩV}1TVŋú´˙jˎ™9¤Ž?į…ÜüjŲäØ\-ŊŅn:˛˜nē!Ã× ,Ļk…lôŪtTœŠīļʍ_§]ŋ]Ųö×âdö§>ís´ĩG'g´ŋNŸ6jƒ>)Û^›Ü?Š~ų@Ëg1h‘öЉúŦoųveõ‹O§ŋ?o´ŨžZí‹Vũ” °,Ļ%Āäq˜Š@ĶTÄR?,A  ‘¯•åĘŽLãëk“ũY.PŋüÂxZ}ÖWÖû[¤_ *€į,j3´KämęĶVųTũCÛē÷Úßę_ m¯~íkÕˇ}ëŽ;ë_MYė¯Ļîč 7YL7ÕpĮŲk‰@ĶĩD7ēo*fØtøžCÁxXd\‰ļ7^ļŊ9ûS6žVļžũÉ_lo}ũSnõ§žNļO1haĻNyœ>Ę3íßöĘŪ9÷ˆ‰sĀ<”˛*k¯å˜ķēŠbņē“΃ĀO2YL?ÉŖÛŦČbúąŽķ“ŒĀlS/o7^ĩŊ@˙ē˛-WŸņšíõGŲûlo˙ōíéüķ™A ä8Ú ú ėų&9ŽÍß%žBūwäĮ‘Ũ'æÍÄGŒQ_,o‘í¯ĨėØ]Ëžĸ;ÜĐd1ŨĐÃįŽ'YL×íôuC#Pg2~uĨ)[nÂņīē!ÅtŨ NG7:YL7úĮŋë†ĀLŌ>ėŲ2Éå°n÷Ū•)šTVKļŊũÕTzŧéÔ/4”Õgūp°ä{ęT–`_TŋôiÉÚüąŸAáÖ_|`oũÎ÷å:MęQwų$˛ë¯ 8†Î ԏ6žŠ¯õÉ5"ĨˆąķŪ/=z[éģo›× d1 ĀČÛ °˛˜ļƒ^ځg2>uĨĪk¯ˇĘMڟ˛úĩwāסœķ˛7Ú˟ŽSm’ÉŦoŌqžĖĐģú“ïJKų‡˙ŠũĨÅwŋŋFE{Ž›—Ûęæå÷ÕŽcĀ#Lš˙ĢĢņ¨­ãi}“Ū&ąģî0šˆ1&Ę§ÖīeįK˙<¯A LD ‹i"`Š–!Å´ ™<X›#ĒŨÔā÷ëV45ŪvĨZn|Ģl}å–~c]Ę؟—‹¸éÔx]˙;äÔyvÎËī”õIĖĩų$AžÄ &æ@äúTg™Jąü-÷×|ė×ņđīĒ+œ?_ɟüč#•üHäãUiq(9Q나Eš?į­:§–cō†č|Ü(ț Ļ!Å4 ¯ÔKČbZ M ‚Ā4Ö 8“ą¤ycKã}WĻåîĶ’ĶsÆ˙ę×>ÛË?äHs,Ē?ˍ×q&}:^i,ÅÃ~r1¤yQŋKŒõá( ÷"=\?øČGjy˙Ÿ‹r]ŽôęÔ(}ôOëĻԟēuFŦĩ•Qލ+w^+;æĄ,ˇĪ~ļÆÎv‘ƒ@X‚@Ķ`ō8LE ‹i*bŠ– 0Æār$cEãYŖg9úúØ˛ˇ…”Ɉ´8ŌĐöN§Ÿ îkSßŊŊ!ë¯?˙Ķõƒ~°–Ũäõ—Aų\|œD‡Š™6ËŦoš˜ģßP%ŧ“A¸û™yû|Ĩ'ˇ<ô`U|_ųn%?ZIĨx~Ę9§ėĶßNŊķD Åä)l˛Ŋ폝îXöĪķ‚ĀD˛˜&–ęA`YLːÉķ 0ęâ~Wáôčûzë¯ļbMã}9úôÍØˇÅ/ôgÁīÛß^÷°ķjŦËßúy-(åk˙]ב3Éã,7‚‰ŖÜ›c >(\9W›W {ûöL{ų\Íb3ՍäKĩGād;ØĢį鏚ÂZ{)Ū)1˛gūĀQ“‡éŖ}Šyß§ĪõgA Ŧ€@Ķ ĨJX,ĻUPJ °ŗácGcKeŋã'üå}Œ˙Õ'ĮjÉö'Į’ƒí÷Ŋ¯~𖇍ØërĨ3g|2Îiȑ†xw­Å\´HŽ §åY˙‡˜Ŧ={ŸŽ+üë—kųūęHũāŊī­å¯ÖģížüGĢōgáHú+?qΜ¨´•‚šŖœRWŨyĻ\ŸĀ*ZWîĻO1íõå?@E [E ‹iĢČĨ],&‰ļŠĀlŗËúX°Wnzž¨žŲģ”Öԍ9—ũĮˇŗĪ1´ŋ‚/}Škĩųwņ…šĩŧåŸ˙qŗpūNŒū÷Uņč<’cĒũž-Oį€ūvŊ›7RöL—}X_ Žģčü­=‚ĀĘd1­ U*+#Åte|RVF`íĀ Œ6~nÅį–˙*ËiŒE-ˇŊß÷߃›ÆÛ÷AšŪöPŨā­o­åŨā_ĢS&å›ßĒëŸmtßAPŗ°ņ}l…í4—æ§ŸZnĘ1ss{ށ2—ĮjˆŠūõûØújrFõkOŊSpŒ'”īr7Ú Cy—ķÜöũŧûŪĮŧ 0,ω€ĨzX†@Ķ2dō<LD`6ŒŠ ‡e^ãWcGãqãWWŽņąąĢ{딍÷í¯Ŋ˙YōHo˙ŠēüE‰ōSlüډÁ÷øcIsõ į)gČÍi˜Ã0/­+gk—Šú(.îCseĮHJujßá`Ūč0>ŠĖ8Ĩsx/kķ™ķ’.Gû%[ŧˇŸ×ũĢú" 0,ω€ĨzX†@Ķ2dō<LD`6Œ‘9j2R%gļUž?`+Ü(žˇÜœ…ō>:š Š+€CŊ !ŲÁxž ö`Āļ5Iæŧû'kÜ wëéēü6HbņÚēŸ{gŨūīŋXËō9´o4&rëĢO{ũtvŒ‚Ņy$ íU”ī(ëÎk’>čŗÃÚĒßû¤ī‹úÎŗ V@ ‹iR%Ŧ‚@Ķ*(ĨNXęŪ<(ÄčûvË́Ȋä8慠<ŖøŊßķÔûaŽå6:|‰ūčķpĸ^o˙ú2Áķ›Žõ%¯Ŋž†īÜjĸm^M&‡Ú {ūlŨ‡ņú“lFûėëú¤ĩŠ&)Ųč÷Œ€ îl.ÉQÜ[gyĪ'zEoîßŦŋÚC8ęæ#Qt´ŽĐŦ™č#Ô6=C€ßâ`ģņéņZŨ(OŖÅbÚįLz5֗OÔŪô­6_kīĮwdČąjôÆû;ĩOY:Kœ×ō*m´žķZŸ{ōŸisÜķ.l ,ĻmÁ—ÆA`,ĻM,ō.l ĩwōLÆĢP’âŲcKĪʛ‡‚Ōõ[$íØĩ¯‡É3=üp]žRæ^=åW!;pđzŀ}ŪŊ÷DŦũqÅŋô{u„ūÄô{߯}xPûa]~ōd-g˙ lô›Y¤ąF<—ËSuwŖß­Ŋįä`ō?ņĶßöʝš@<ēīÜaė9PījĢŧŸ×ÚÖˇĪkČbšXǁed1-C&΃ĀDf}ŧ×ĩƒ"”(3žU6dž¨f ĨÜ=ė|ŪבŖu‡îŊ;Ė%kž ō,Ņāo˙ęîĘĨ߯-œíŠIÛŠÔ;ŅöHōæú^wīØ[Ŗ´ƒäÖÃ×Q˙ņãĩ‘§NÕ˛÷PėŦ](ûpķ6ö•õZåHĩ5ã߃˛ŧFŦ×ēų꜑Ī`[ŋåĪfĪ›īœįę#ého^?+ōŸiãŧ ÛB ‹i[đĨqØD ‹i‹ŧ ÛB`í}ƒ<“+KÎãÕÚGčú‰¨;jzPîBÁH™įĖ#e¯9šôīŪē—Ų”ešyŖ}Ÿ2‚¯6gԕŪB€ %*…rĪHɁnŨYŗŽú\ҟ„C=[Û(oœÕęĘwĪÕõå$Ūņāų'ųîōZr˛žoôV˜‡RfG9$Ë{ŊÃ×ÁR”ĩQōÄžžëgØgہ 0,Ļ `Ĩj¸YLWB'eA`ŗaŒË6°ĸėy|ãëįxđÁå>öĘí%Oŗ‹‹õŧī4ü`?w‰Ëä &†žw˜ÉšÎŧŽąkoÍ<Õé¨.oŸËr yŪ%xŪSl~;q˛ŽčĪĘq$yįÁƒU›ŪxRtš1sĖŨO‰ų#ūQ[_Fw‡×üēÉr&ë+C /k1ÄU"Ŗũ†=ęQ#:ĘÍõûķŸŠG,¯A`›d1mĀ4=YL=y ÛD`6Ė˜W2ū4–$4ē+ü ķ ?¨­=?ė|^t”ÄÕŨpĸ—øÂßŗCûjJSnáŖb'Ęą^•/`ßų'j.PŪy'īķnžũäÖjDJ9IšƒÍnr įy¤ŋ9ĨŧŪzÎ$š;âHÎ9üE™)3ē\žãØ_g°}@3Gû˙äaúäŠé6Â*‚@X,ρJĩ ĐB ‹Š…PƃŠˆņŸ+ËXĶXŌøJ2Šwmīų#ī;0/ô 99ŅŗĪÕ^ߊCWŪiWŠ{õNœ¨õũ@.|ĪĢ˙v ž—ß˙ĻZ§g˛<Ã%Ī3Oå=Ū ažK^čūHyä= ō‹ČįŲ\'‡ŠYæœÖîR‹Ŗ9CõŅ=€–/âxÎ;†d”OU§tfūŪ9%?ęĒ3Ŧ#mÍÛ'čķrĄķ™jƒ@X,ĻU‘JŊ Đ@ ‹ŠPŠƒĀĒĖ†ßĄËyüΞÆčˇyĖxö„đ{t§iŖŅž-c_ķŋúáÚíĪ|ϖΐãšÄÆąÛ Đ_&€ŋĐĮëjõˇ{|'yĪ3šŅ<Í9îxxĐÍíáBņž‚ÛD9œ{˙p†Ė<˜ŋaåūIyŠyŦ=p&įîŽöōA‹G9ļg^)1ƒfęÂčŒGŧŠ6ö}æ?ĶúÂČKØ.YLÛE0íƒĀ:YL™ Aā*!0ō9€gMĖķ<ļ'\/‡¨oJĘRR˙Aæ7˜úĘWęžŊyd,}Ž`˜ãK#ާ]īgHœ¸ßĐķFūž“y%m0~‚ŌĮī=ˇA,ŸcĐ^ĸÜ3[ž¯ÚF§! æĄÄx˙pÂ͍ôN‹oģˇüĩWķHâÅ,{ëæ—%ķBęĐFeį5m\Ÿ˙L ĀĪŖ °˛˜ļ‚Zځd1-%‚ĀV¨î€h)0~5ī#ōütĸÉĄŒu÷Ąā=īŠ-ūæˇjŲ;âĖc sl]KŌHåp­Ž™Į‘#éŗG ÍąØŋöĘ3ÅDÎŖ,ŋŅ?ûŋEũkŗ´xš>,“?Ëęåy ˛˜Ĩ8ŦŠ@ĶĒHĨ^h 0ƠƯÆĢÆŗî3ŗ¯VûV|o{s2ž•9ˆæXôO~@ķŅeæÁ´ŋk/š ĨCŧģ"9ÕGšžÖ§ŸSŪëë”ļŠ‚,Ļyļƒ@ĶvĐKÛ 0@ :Ī$'ōBCˍ—Å̃žFoÍKŠßXV>āũÖoÅ˙ÆįæälÆīæá:åOāĩy'mna¨MōVyĄœĖ13c˙rí՞V}í1Õ>ũ‘3НƒŅ3†åLjųĪtU`Œ’ PF—Ņ“ ļˆ@ū3m¸4 "0æbŒ­,g0~•ƒˁ<ģc<,‡3~×^ųöyžÉre9˜üĸë_ õYŲŊs‹tęוäVŜ‹c oôĶVŽ&&æ­l//c÷ēoNûƒrŦ+auĩËôõj돞 pĶ ÅtĶ uŊÖd1]k„Ŗ˙ĻA`fŒ<ô\N"‡‘˙ĘqĖÁČÁZņ¸9 ī+đ“AyČ;?ßõGYNwfÖú{m”CXŽ b`{9—&ČŲäę×'ËmīūHĮX¨–;ĮäpÎë+‹Įõ”o×ŗīôn(˛˜n¨áŒ3¯'YL¯'úéû†B`vx°œ.’äđ;}ãi9ņ­yã[ã{Ë1§Č‘ähŽŒíÕ¯ŊÆëōõ Û(Ō'1“šŋP”Í#mtŧūÆúb$†Ö—“™G˛?yå=V@9ÕYęģ¯Î1sŽŅüēŠ‹æÃu5 ,Ļe$ãĮëŽ@Ķë>1āFA`vË Č7~måŒŋåPÆĶÆīæ(äÆīr é—ĮÃrũ‘ŋ›‡rŸ—|EžĶá-m”ČYôÁ>ŪČĖ3×e}1ÕGĮø8úĩĮŧ¤{éė_<ž@ŋũ;'y/ęļ$:*‘§isĪÛōŸIä"-"Å´EāŌ,ˆ@“ˆD[D`ļ6XN—Hå<ƊrãwųÂ}ƒž:{œf#˜Ëx_Ž&ö¯>ãsųņ:æËģūåUž™:ˆ‘Ú$Ī“7ŠųQô1„Ŗģ˙¨^ÎņĀöŪûįūLí—C9Fމõ1§8™"Vߒė<cemę;ez÷ķ‚ĀT˛˜Ļ"–úA` YLK€Éã 0˙}LŸÁY­ŖIENDŽB`‚astropy-astropy-201cddb/docs/convolution/images/scipy.png000066400000000000000000000235121507226315300240130ustar00rootroot00000000000000‰PNG  IHDRĶŌĐ[õ| ”iCCPICC Profilex­–wTSŲ‡ĪŊ鍖:„Ū‘N„ēt•@¨1„ލˆ ŽāXPÁ2ĸƒ" 6Š *bÁ `Á>Aå9XĀ‚Ęģ„3ë­7˙ŊuÎũ˛Īīė{ÎÎÎZ˛'W$J‡åČf‹C}ÜŅ1ą Üc u Âåe‰Ø!!āmęĸFėļųlŦ”ũīy~b(YNāgņ2>ƒ 3žHœ ,AüzyŲ"„Qh„ibä€+Ξ`ŽÍf9aŽYRMx¨ĸ OærÅHņˆŸ‘Ë qHÅ[ ų)B„O ėĘKæōž@Ø,#c%Âdu„ūGđ7ærbrš‚žģ ˛yągJ–([ ũō˙œ2Ōs|IŽĖdQļ{(ō¤!9SNÉæ wžãä߈y.LšgaBPđ<ķ˛<\ÎéĶVú/Äá'zzÍûŗrø0Ų#h۟Ęõ›ũͤīåŠú‹˛CÎ Lš­Š&IėŊ?1Ë+l۟-_đ'ĨxsæũĸtiÍI÷ŠsBî’(ŒXØËįzúĪë'đȇ"€5°VĀ2;1?Y+EâAr6ƒThĸƒ#äY˜1Ŧ-­ŦÁlŊĪjx_ZĮ˙—¯ú ^HŽ ŲōbHe€é Ā1¤Fqųô? àķ/Gœ;§›-WéŋHɀ ĐēĀ˜#gŗ΀…œÖƒp–H@ ō@XĘ@Ø v‚°GĀqp ´Np\7A?¸  ¯Á˜Ķá D…T -H2…Ŧ!&ä yAP(ÅCHå@EĐz¨Ē„j Pt: ]„ŽCĐhƒŪA_`L†i°l/‚™0ö‡Ãáe°Î„ áRx3\ ×ÁĮāVø"|ž Kā×đ$  H(:JeŽbĸ“Č&dr9‡ŧ™|˜ÜE~@~OĄP (,J,%›˛™Ō@šDyJų$C•ąáČđeÖĘÔĘ´Ę Ęŧ‘%Čę˲e—ËĘVɞ–Ŋ%;.G3ķãĘ­‘Ģ•;+7$7)O•ˇ’–ΐß$TūēüKœ‚‚—_ĄTá Â%…*ŠĒKõ ō¨ëЇ¨W¨Ŗ4,ÍÆĄĨŌ*hĮi}´ EE[ÅHÅ|ÅZÅsŠ:Šn@įĐĶé[č§č÷č_”4”ØJ‰J•š”•>*Ģ)ŗ”•Ë•›•ī*Qa¨xФŠlSiSyĸŠV5Q]ĸš§ēOõŠę¸MÍY§VŽvJíĄ:ŦnĸĒžJũ z¯ú¤†Ļ††HcˇÆ%qMē&K3Us‡æyÍ1-Ē–ĢVŠÖ­ Z¯Š 6#QÍ¸Ė˜ĐV×öÕÎŅ> Ũ§=­c¨ĄSĸĶŦķD—¨ËÔMŌŨĄÛ­;Ą§Ĩ¨W¤×¨÷PŸ ĪÔOÖßĨßŖ˙ŅĀĐ Ę`ƒA›ÁKCeCŽaĄaŖác#Š‘›QĻQŅcŦ1Ķ8Íx¯qŋ lbg’lRkrË6ĩ7M1Ũk:`†1s4šÕ™ ™“ÍŲæšææÃt‹‹‹6‹7‹ôÅ.Úļ¨gŅwK;ËtËC–ŦŦüŦJŦ:ŦŪY›XķŦk­īØPlŧmÖÚ´Ûŧĩ5ĩM´Ũg{ߎjhˇÁŽÛî›ŊƒŊØžÉ~ĖAĪ!ŪaÃ“Æ anb^sÄ8ē;Žuėtüėdī”ítĘéOgsį4įŖÎ/.N\|hņˆ‹Ž ×個ĕáīúŗĢÄMۍëVįöŒĨËâŗęY/ØÆėTö1öwKwą{‹ûG'Õ]ž(OĪrĪ>/¯¯¯§Ū:ŪīFī ;ŸU>]ž_ßmžC ĶĀ™đsđ[íw؟ėæ_ã˙,Ā$@Đún|¤$ j ÁœāíÁOB C2C~]‚]˛¤vÉķPĢĐĸО0j؊°ŖaSáîá[ÂEEäDtGĘFÆE6D~ŒōŒĒŒ’D/Š^}3F5&%Ļ=[;šÔkéÎĨŖqvqeq÷–.Ë_v}šęōôåįVČŽāŽ8‰Š?˙•Ė­ãN&pö$LđpÖīlw‡sG˯ŋîÔîŦ=§xnËyâųŌķ3 /Lv‰ēÆ/ .Žt¯č~t)úŌËK.÷]ņŋríĒ÷ÕK=ėž ×\Žu^wē~öķFÛMû›­ŊvŊ-ŋŲũÖŌgß×zËáV{ŋcĮĀâķƒnƒo{Ūžz‡sįæŨ ģ÷"îŨŠ’Üįßų ũÁۇš§?Æ<."÷¤ęŠúĶēߍo–ØKÎ {÷> {öh„7ōúŦ?žŽ–>§<¯zĄõĸáĨõËÎ1īąūWK_žŊž/û—üŋöŧ1zsæO֟ŊŅŖoÅogŪmz¯ōūđÛŨ“!“O§2ĻĻ?–Rųtä3ķsĪ—¨//ĻķžâžV3ūÖņŨ˙û㙌™WĖ•ö(d†“’xwJ Ô~ˆ2sũ¨TÍõĐĪöŌŗcÖū‹įzVéŠ=Mŗ°íād1ČS œ`›…xg-+ÉÆZ Š iMĒffŪ#}!΀oC33Ķm33ßꑾų!]Ss}đŦZîŠl˙¨€įWK÷˙mú7\&Ų2ANÜ pHYs  šœ\IDATxíŨMgĮUđš™ĪxŪg<¯~ qbŒ-”(‰‚ąB,`‡`łĀ‚oĀW`Ág`ą "‰UEˆ` ›XļcgŦyņxėņŧ§ģŨ5™úU_×ŊŨmGr?ŪÜ9ˇN:õÜ:Ÿ§OŨēŽ”ōč@ŲŨéŽ|öŖČĮ‘ī!ßFŲ_:ŸGØ_*Ķ}ĪÅŨÎG‡´'žÎ_}ŸįŠ!?@žėø4Į;ŒÂōh<ԋķÕ?×Ŗí՞~Öûš °ÕvŅd¤ŽlngãÉ>Úķ˙4FžöFŋ\OŽĩņoImÉöWÖ?Šüe\:žúޝ,žĘÚ[Чũ•õg$īļ˙Č˙ p ŲĖHŧn ųCäęëĩˆA ĖE Á4Šč Ļ@isX™ßn×ҜrÔĮ­9åvļ7îi¤¯ũQVŠė_üëÕ :š;QG˛FãíļŨņ|žļûü”õĮųŽėĢ/Gú:}’ôÕ¯ļ 7oļōŪDnÅōōĮ[ōČoēE A` Ķ2š"`ZXԃĀÖw˜ÂvušNaĘÚÄũQ˙QD›s;Œũ•Íąí?’­čk¯ëLúëxâ)>ļ+̝,Ž/úëxÖŨOû#ųʑūá‘ĐaĄøGÚū;ũ?ؒõĩˆA ĖE Á4Šč Ļ@isØŦ3ĩaévéÚŽlDÚnkÎŦŦž“YÚŽ?ÚÉr"ĮWv<å^úcÛĩ§?âĢ=eí+kĪņF˛ö”í/G“3ŊŦ=–ŸÃžuĮęŸĪnƒ@˜‹@‚i.RŅ L€Ōæ"°ÚØgfD™C+׹â^ĩМ˛ęģ—Í÷›ä(ŽoN]íÖĢúõūÜĢũOydWŧ”íŋÔžūĘi|žŽ§ėøĘÚWV_˙ôG}ũQŪmíäē÷ŽęMÍGŋĒ~ŽA ,D Á´°¨)LSČä~XˆĀ&gåđÚT_δ~âQķŸƜĶ3 F9¸9öHßņ”gˇÔÉâãé9ļëŋ˛ķŨÆÅæ–ūŲô :ÚK§Ŋ‘Ŧ?ÎĪve×GŨ W'ũFũĮgt}ģΡ>Īޘ‰‚Ā Ļ)dr?,D Á´°¨)Vų^ÍųĒ’9Ģ9ĸ¨žu"sdÛë¸õĒ}ûëãW;õęü”Ģ^Ŋ:ž˛ãé¯öåLęËQœŸrõsęęørXĮˇŽĸ?SãLŨw|ĮOeí:9Ķktø3Ū?úūÂ÷›ū˜ūob_|ę|'Ũ" 0Ķ\¤ĸ$˜Ĩ9ĖE`ķŦq#Jyd˜Öē€ũG9˛œDyäŸúreũ7'Ö_õßČŋQũWvUŨyÖûš °ĶBĀĸĻH0M!“ûA`!۞5nlęãšCëƒölŲW_{ĘÚåđ#ûÎw$koŠūh>ÚÍW}ë|>?ĮWvŧŅüFT˙”ĩ¯=߇ÍĮųkON¤žíÕ?qq‘ƒ@˜‰@‚i&PQ #L#„Ōf"āŸÜguĢ9bU6G­÷ë՜ģŪ¯WÛwËqü…PŽãN]Õ_:?ņ™§ŪW_<”ÅGY}s|e9ũĩ/úoģ˛úŽWq™ēÚ_˙§úÕûŖųkäĩëēŠ÷s A`! Ļ…€E=L!`šB&÷ƒĀB69ĶŌŅRYˉGō¨˙¨]ûę/•ũÅqžâ'ĮĐõ­s¨¯=‰îČŋ‘ŋާ}ũ՞ūđĩŋúúãøękOl—cŲŽ<ŋúãs¨÷s A`! Ļ…€E=L!`šB&÷ƒĀBVų 9Ą9Ŗ6ÕˇŨœ×veõ•Gãioˇ˛ãkOcģũũÅr>ļkĪņä4ę;žãŲßņ•ĩ'įđ} Ûå0Ęúį|lɎ¯ūh};ߑ?ĩ]Üęũ\ƒ@Xˆ@‚i!`QS$˜ĻÉũ °ÕFūhNŠQģ9æH6‚ÕwüŨĘÚw|9‰úģÔßņÕ×ũWļŋír&åđĖ ĪD¸žßߒÃșä0Ž7ەÕWVw÷Lį=3CA`ŋ!`ÚoO<ķũĖH0}fĐÆđ~C`åģ˜Ŗ+ ŌįŨnNėøūBČI<{û(’Cˆ‘ã;žœ@™á:Qûʎ§ŋļËyÄã,ü˛í˙Kû˙ û<äLreņ˛ŋíĘÚĶņ\*3ŨĮĸ¸?nČ?‚@X†@‚i^Ņ“$˜&ĄICX†ĀæŪŧQsN#Đvsreû/¤īxr†§0pŲođē÷ĖvåąįŲØ4wĸ@YŧåLĘáËČgŸ{ŽŊq‚ÂĶÍWÛöˇZą“äœr9‹É畞ũ•;‡¸Ąž2ę“âŌu=i( A`ŋ#`Úī+ ķß3L{e íw6ŋĪ$æär#P}ÛÍáĩ7ĘQGöG펧ėüÍéåD§é ĶžœMûĘâ!‡`øĸ}9Đß/üĻëßŊŌ"úöÛíˆúsŽm.âõírPíŲ.>rHņRŋM_GuŊǝ}Į¯ĶĶNŊŸk H0-,ęA` Ķ2š"°ÚČˇÍ 0esJĮéÚĩ'õ×?ûË1´7Ōמ9ô¨ŋAüĩ/'soĄípĄ|”ÍŠīßj ¸W2TšŪĒ—w‘mˇ.'â+'É _|ŪŽ7jWŋŽo?Į‚ĀLL3ŠZ!`!”ö 0m9“9ģōČļúĘö—c,•ũEp<9” ˜ķÛní;ũąŊæØõžúÚ?UˇŽÖuė¯L÷ĄxöōKm—¯|Ĩ•ĪŸoeëR?üˇļũ? âŅj÷Ũ‹ũÅOÎĨlGt<íWũŨâ^íäö= Ļ}ŋĀ^!`Ú+$cgß#pāËëeĻŠ°ĸcN9Ę!GœĮŊzîå’ŖØn˙ŅxÖeäHr’ŅŪ;į¯lÉ犞{Ņü…{ē>ˆ­+Ĩø~ĪmôåX¯ŧĐ*\ŧÔʇyđCgØę/•ūę@ģâ~‚Īås~žfģīOdŸ—ōˆcUt|nL+bsH0ÍE*zA`€@‚iPšƒĀ\VŲĢ%GÉöWn3äRä<Ęr$ë>r 9)ąŋīû\ÄÁ^há{ær+ß# īŊļũęÕV~ÄŪ69’ķĮnī$æŠ9ŊøûüŽ]kũ; _ûzÛž×Ōo?×Z|ũíV–UNRĩœx‰įHöyŒÆsŊUŧëĩú™k;D Á´CāŌ-ˆ@‚ID""°y„9 9ŖļmWV߈u?˙TņõūށЁ °_H0í×'Ÿyī9 Ļ=‡4÷++ķķí€0Wg”ĶąŖU}s^ëĘæÜžôâ‹Î`oåƒLĐ:Î$é†×ĨēŊi>3Ÿøô?ğ7ŪhņøÁ÷Ú'üíßiÛO>ßîvŧ{Ĩ-dũøĮ­ū˙ŊÖĘr 9”ķÃŨRë<ÕĒøÕûõÚÎĻŪũՕĮ×}oĘöĘŅÅũWķ¯ !`ZW”ƒĀ4 ĻilŌ!°ÚČ÷Ė1ÍąG9ĻŠl˙‘l9“ī']¨IëÖÔ;Žô›-&¯ė˛pō柎3xp(Įƕ×ē۝ŅŽŽōēh;ÃwÔwoŪOÚvyįŨV^[k9ŌuΘxũĢ84ú歜H­;ĘąädļˁÚŲ•î\B×g՟ē_Ûs A`& Ļ™@E-ŒH0J{˜‰ĀęÛëŠ˙ōûȊF 9§íæ¸ę[WđLˆŽŽ„Á‹Z_„#}ë[mûņ—ŋÔŪøū×ZųZģyėæĢ?oÚī’ÄŸ<Ų4ßgēÅĄ‡ā!b ûĮyáį0€}įēM!æ$ąŦ$Ĩ(ÜâŖŗrĒÛusÚ ¸SäDrĘls…áģŊŒĀÕ՝¯}ZĨđ8:ŽĒ}ũ­ËŅuī8‘ƒ@˜‰@‚i&PQ #L#„Ōf"°úÎúFļ7yįŋ­ô–Ė1å8¤ôÅē2” @AJģķĢ”ŗÜ8wŽõņōåV>~™ŨzÄlcŽãđåK­ũS˜—ÃÜh)XY `kŽÜ')?ĮšäLoĄGŌQ“ú­qätNwŠœë.…I9‘Ã;]ׯã0úÅėꤸ×ímŽŽ.eėŪ÷ķũŠęOģjô:rŗH0͆*ŠAāĶH0}:>i ŗXŊˇÎ—jÎW{Yv¨÷ë՜×:œJŅqĸg:Ír¤§á§P’ō1Y}G"ÚߘC—Î׊o^/œ&kžÛĘwn_\iē—ClŽ~ō…›rŦ+Ũ၄$ÂŪ} 1÷hoŊ_ß̆}9œÅõ"‡Žâ^;ûkwtˇ0ŊŽx:Ĩ?ÂSíˇĢĻ&7‚@˜‹@‚i.RŅ L€Ōæ"°ÚؗWsžÚIcŽhKĻHY<ˇN}哄øYžtžĨ0ÅēŌžTŽBēP˜ą’p˜ĘŲ)fđ§?ŠPm{ĨwųŋiIŨŠ“máÉ÷ŸŽ^kÍŪ‚#}§ōų´Ŋ×91 ™žĪŸfŅéöÎáNˇžä80֎ŗËXE|å`Ę-Ŗí×ûˆ3Éé\˙/ũô9DA`& Ļ™@E-ŒH0J{˜‰Āj#Ÿ„Q”ŅŲĘæŒö—iĪöĶ„´ßT=Ã^ŧ3p¨ ŧĪԑ(I{īŠo°xđŨ1g0ŨĒö—- b:å›ŨfíīžS;~rŊzĩ•­[­‘Ô_ĄŽ%Įrī”ĒãHržļŠÖs9Oåur˜zŋ^õ‡éuu 9Rĩ3÷ęx,ĮĸÜ>­ō¸]ŊšãG/H0HÄ °SL;E.ũ‚Ŧ6øĮŧu{Ŗd ĘūŨÄŠNaĀŊuRœK—[¯ŸŽ•Ī\Æ ūÉĩ–J?úÃĨ=–éãūåKŋhä‹TwL’JŌīûGīs¨ĮH{ûîRxō—î‡Qŋ™Ė6‚Ëūîŗ]ãÚW–§Œú1GĒãT˙ôĢļį‚ĀBL ‹z˜B Á4…Llžé#g‚t{íÜ{w‚'ØÜ§|Šž|?ÉŊvĪ=ßÎęäŗxpņbĢā€mëré÷ūeyŸ%=.?ĶjsPŪÁ#-ĀįoŊŅč[‡úƒæÖjRŋÕËךŪ_§íÉ˙>äœŋ ­ŲŽ<â,ļä'}ßîßÖĄ”GöåPę×:šķÜÎ—Ü A` Ļ E%ĖA Á4ĨčŦ6Ū-ņīôF˜īøˇ|)gīœį(ƒí‡/šŽäaߒ„@üZU<„ÁÃŋ9(oĐ\NŗųīÕē’œ€#-Ę8Ŧș\Oʎį^=eëZSœĨ>ÃĘaĒėøú_õęU˙ęũzĩŊŽgÜTũ\ƒ@Xˆ@‚i!`QS$˜ĻÉũ °ÕÆ6,Râî|ļju{•CĒŽqf”F“Ä*w‡]ßÃãîŖąŒđ¯ßko¸y͏ĖūūZũĨŌ?ˇíáš|d§?~Đéz{f„{ëžĸPxÍ‘įš{r$Īísŧƒ.ˆvļÃõcwë>˜ë֛Mc9–˙‘S);žö*ĮĶŽ~DA`& Ļ™@E-ŒH0J{˜‰Āj#Ŋ6G´¯9Š9/ Ļ|\“Č-CîũzęŊvĪ[ÁĄž>pŖí $)°]ƒNXNķßi-ÜaBĘr8?xtŒS/üⓇËᘟGRœmå+RHËXHú=SÃī/ ŸĶŋĪpīß- EއĘēfûpJWឭÕ~ŖņõGÎTíxÍ˙™D$rØ! Ļ—nA@L"9ėÍ÷™Ė 0sHĮ’S= )ĩná7^åG¨›H!NÜk9ԚIžÅšWĪŗÆ%^¨û<¸ €CĮČō}aë"›ģC@@ŸlíŸxĐr:9ę‡.‡:ʙįųF°îə|ę&gNÜkËdÅīGA‘]^Ũ9~r (`÷M\9ËĢŗėÎNwü/õÚM 7‚@X†@‚i^Ņ“$˜&ĄICX†Āj#˙3g%cīöF™S˛¯â<֝nĶnī^°ŽcQļ9sĻ=ôāÜšVæu âû<Ęr‚Ų{(EķŦīŗg[sîūģíSą0ÃE’ÃÁwˇoĩY;Íå u<9Œßkōy{ģœI<ũd0eąnkei)oš éņ ^x-ļÛŦį–aöįDļh–Âēۍ_ûëwįXn 0Ķ<œĸ†$˜†E!ĖC`ĩQ11g6Į¤ŒŌ}ĶTeN{G!Y7ũūĐYÎ4đŗž=Ū‘<°¸Ö~.ŠX–zHoJĘ#‡đõ¤GŧásôH øČnPˇų¨Ĩ„…2X7Ëj–Ũ”ŨZ(:ÄO°īSšÕĐŊ}ĘÎWÎz|xœ]]Évë¨Ö4o<ČŲäTõOĀĸ‘ƒ@˜‹@‚i.RŅ L€Ōæ"°ÚČÍ G9&”ĸ´U•Ūžk]KŽUāæôž$ga+[ņ,îwŪiáąÎÔļ–"‡ōõ%ĮˇŽcMÎq—­~rˆëÔe>ālpû{Žžu"÷:Ęqžâ 9”[ŧĢSą÷ĪēÜiŽŽŋÅķwīŸ{=oCÚ+‡ŠĪQķŨúĩ]ŽÅ\—r×ņs A`! Ļ…€E=L!`šB&÷ƒĀBV|JiŗģœFe;)į‚×÷}RębÎ~'Ÿ}Ļâ™g[Ų˙IŽu››p‡uŗÕ–Yë"Ö]ÜkhŨÂ3Ŧc¸×OŽpē˜įŪiOŽ$‡šĪüätÖÉN°R{rD9ĨœJŽ)žę[×ĶūA8“œ^Î$âu/){ˇĪŋ1T9˙gjã0RØ1 ĻC—ŽA E ÁÔâ)ėÕƧ{Ėš09í¤ā]ŨJû5ĮŦ^Ë1Ü įŪ9ëB~ƒÕ÷ŸŪáu"Žî.īSĮ‘œ9]=ũäú$īIų5&¨ŋrēÖzéŪˇrīöē÷‰x@r$9‘{ÅÃī=yÎĄGN#gÔņ“Ã}Éáq=ŽķTyŨ^RÛåüĘP˛:Ėãk`ܞ °ĶBĀĸĻH0M!“ûA`!Ģ >ãûK֑F6­CɁĖAy=§û>ÔœãĻu–˙l=zë­V>X“Ø­Ûr*sđ[$Åú/Į8ĘfDë ]î^˛5öžÉyä Öą<3C˙îÁ1<{\NØĸˇ~›ĶôĮ÷—ė¯ÖųŽâÃuįØÉq\?˜+Ž7–SĮéåüŽgJûĩūĪäJˆvˆ@‚i‡ĀĨ[“ˆD;D`[Îd„™#B1:ÎŖž9ë¨ŋu)÷ZŊ‹Ÿũŧ=”¤;°Õ.]Bå ĮāLP´˛Æ°q ˙}ŋHÎ!įqoāŊš´oMLüå€úëķŪ_סu,Û]/u~Îģú—k H0-,ęA` Ķ2š"°ynž9ļᨙ“r99mÍ1̝Žoģ9¯9´{ŠĖiõoéxúOŲ¨ĢcšWŅīM9žū;˙€Øq–ŠkŊʙV<@ë\wØõ ‡@Ŋ;û›á8yŊtúŽí+ģ>Å×vũ—sÛ.ĮǏzužļGA`& Ļ™@E-ŒH0J{˜‰Āļg›ŗËŦۘ“›ķę‹9˙¨ŨœŨ×ŊT#˙F9°ŋ0úkNmģ9ēøŲ.g’ķißįŖŋrĎc`ą;{[ĸŦ}Ÿ§ū);_9Ēķ?ŽEėÎŊŗŨõãķ­įWeįUīį‚ĀBL ‹z˜B Á4…Ll?`Ž-Ōæ(§U_ŲæēîL 9˜9Žöo”Ķ;ũ‘ķ˜ãëĪhŧįÃČœ¯Ņį#§t|í+‹§íĘ#˙ĩ§žūə}ę:GIŽJ÷‹>—JĮ °ßH0í÷ųī Ļ=ƒ2†ö;Ģwq|Į3'öīüĘæėrÛå$ęë@Žbn9†ķįsDŨŪ1ëĘr*9€ūęŸķw>.ZĮķy8žöåŒĘŽįzPVßņÄ_˙õ×琜é×őœ§ĪŅöČA ĖD Á4¨¨ ĻBi3X]'0~ŗÔžž÷¤VÎ åāę{&Á]’r9Ž9ˇÜŨ÷‘Ŧ+é¯u7Įŗn<įb:EäâĢ}íÉI•x:žuĮמķQÖžũũ…—sЎQŧ\Ο•ŦߟÕ8ąžđ$˜žđ8üŧH0}^Hgœ/<Ģ‹KņĨžëv?äû="9Š9°œČsŲ6ī{ęîlmHŠœæ‰Ž›˙”Cø‹Ą?ÚS–c˜“;íËÔq į#gŅžŅņåHÎWÂãīžodëtrR׏ˇã˜ļ‹˙Hv}ŒdũS_|ëúĒWõ# °ĶBĀĸĻH0M!“ûA`!ĢͧÖĪ-# ŧËö•’?ĸaë9mĮIĒ“tû Øŧ0„z‘Ŗė5‡ĐžËyü…R6'—Ķ8Ύ˙CDáÁaÔË=6?ŌŊã4â!ĮĶ=92Ëą;gåŲMŽ?ĘÚŽn=Н\ņŠWĮ‹‚ĀBL ‹z˜B Á4…LŦ6øß =@!CN%ĮqLí8Ņj<ũt+{Öõ{$҇áLë4¯ųo*‡­Jr‚ÍųÖÆõëh>~ŗõ ¤@Nāx‡qđ…+9Œœôuū Š~Oéc6ŗųŊ(÷^2Ŋî,u9†ú<ž‚ģe <îŌÁ:—I™îO<Ųí˙)G…–kw&‰œļN'˙gÚīÜ ‹H0-†,‚Āö$˜ļĮ%wƒĀb~ ¨ąÕŋIENDŽB`‚astropy-astropy-201cddb/docs/convolution/index.rst000066400000000000000000000337501507226315300225570ustar00rootroot00000000000000.. _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 astropy-astropy-201cddb/docs/convolution/kernels.rst000066400000000000000000000305131507226315300231050ustar00rootroot00000000000000Convolution 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. astropy-astropy-201cddb/docs/convolution/non_normalized_kernels.rst000066400000000000000000000100051507226315300261750ustar00rootroot00000000000000************************************ 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 astropy-astropy-201cddb/docs/convolution/performance.inc.rst000066400000000000000000000012431507226315300245110ustar00rootroot00000000000000.. 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). astropy-astropy-201cddb/docs/convolution/ref_api.rst000066400000000000000000000001361507226315300230450ustar00rootroot00000000000000Reference/API ************* .. automodapi:: astropy.convolution :no-inheritance-diagram: astropy-astropy-201cddb/docs/convolution/using.rst000066400000000000000000000065571507226315300226020ustar00rootroot00000000000000Using 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`. astropy-astropy-201cddb/docs/coordinates/000077500000000000000000000000001507226315300206415ustar00rootroot00000000000000astropy-astropy-201cddb/docs/coordinates/angles.rst000066400000000000000000000312331507226315300226460ustar00rootroot00000000000000.. _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) astropy-astropy-201cddb/docs/coordinates/apply_space_motion.rst000066400000000000000000000205741507226315300252700ustar00rootroot00000000000000.. _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 astropy-astropy-201cddb/docs/coordinates/common_errors.rst000066400000000000000000000113241507226315300242600ustar00rootroot00000000000000.. _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 astropy-astropy-201cddb/docs/coordinates/definitions.rst000066400000000000000000000043661507226315300237170ustar00rootroot00000000000000.. _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. astropy-astropy-201cddb/docs/coordinates/example_gallery_index.rst000066400000000000000000000007361507226315300257420ustar00rootroot00000000000000.. _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 astropy-astropy-201cddb/docs/coordinates/example_gallery_plot_galactocentric_frame.rst000066400000000000000000000203021507226315300320140ustar00rootroot00000000000000.. _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 astropy-astropy-201cddb/docs/coordinates/example_gallery_plot_mars_coordinate_frame.rst000066400000000000000000000125241507226315300322120ustar00rootroot00000000000000.. _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 astropy-astropy-201cddb/docs/coordinates/example_gallery_plot_obs_planning.rst000066400000000000000000000151571507226315300303450ustar00rootroot00000000000000.. _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 astropy-astropy-201cddb/docs/coordinates/example_gallery_plot_sgr_coordinate_frame.rst000066400000000000000000000270561507226315300320510ustar00rootroot00000000000000.. _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 >>> from astropy.coordinates.matrix_utilities import matrix_transpose, 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 matrix_transpose(SGR_MATRIX) 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 astropy-astropy-201cddb/docs/coordinates/example_gallery_rv_to_gsr.rst000066400000000000000000000075611507226315300266420ustar00rootroot00000000000000.. _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 astropy-astropy-201cddb/docs/coordinates/formatting.rst000066400000000000000000000031551507226315300235510ustar00rootroot00000000000000Formatting 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 astropy-astropy-201cddb/docs/coordinates/frames.rst000066400000000000000000001010471507226315300226530ustar00rootroot00000000000000.. We call EarthLocation.of_site here first to force the 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.of_site('greenwich') # doctest: +IGNORE_OUTPUT +IGNORE_WARNINGS 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. astropy-astropy-201cddb/docs/coordinates/galactocentric.rst000066400000000000000000000275361507226315300243720ustar00rootroot00000000000000.. _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 astropy-astropy-201cddb/docs/coordinates/index.rst000066400000000000000000000603141507226315300225060ustar00rootroot00000000000000.. 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(force_download=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`). If there is no local cache and Internet connection is not available, a built-in list (consisting of only the Greenwich Royal Observatory as an example case) is loaded. 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 astropy-astropy-201cddb/docs/coordinates/inplace.rst000066400000000000000000000043561507226315300230160ustar00rootroot00000000000000.. _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. astropy-astropy-201cddb/docs/coordinates/matchsep.rst000066400000000000000000000341501507226315300232020ustar00rootroot00000000000000.. _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] astropy-astropy-201cddb/docs/coordinates/performance.inc.rst000066400000000000000000000240561507226315300244530ustar00rootroot00000000000000.. 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() astropy-astropy-201cddb/docs/coordinates/ref_api.rst000066400000000000000000000001011507226315300227700ustar00rootroot00000000000000Reference/API ************* .. automodapi:: astropy.coordinates astropy-astropy-201cddb/docs/coordinates/remote_methods.rst000066400000000000000000000063511507226315300244160ustar00rootroot00000000000000.. _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. astropy-astropy-201cddb/docs/coordinates/representations.rst000066400000000000000000000746271507226315300246400ustar00rootroot00000000000000.. _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.matrix_utilities 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 astropy-astropy-201cddb/docs/coordinates/satellites.rst000066400000000000000000000142341507226315300235500ustar00rootroot00000000000000.. _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 astropy-astropy-201cddb/docs/coordinates/skycoord.rst000066400000000000000000001431521507226315300232360ustar00rootroot00000000000000.. _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. .. _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 astropy-astropy-201cddb/docs/development/maintainers/testhelpers.rst000066400000000000000000000104201507226315300262540ustar00rootroot00000000000000.. _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 runner 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`` or ``dev_all``. 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 =================== 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: astropy-astropy-201cddb/docs/development/pull_button.png000066400000000000000000000362501507226315300237340ustar00rootroot00000000000000‰PNG  IHDRõXu_” AiCCPICC ProfileH –wTSŲ‡ĪŊ7ŊĐ" %ôz Ō;HQ‰I€P†„&vDF)VdTĀG‡"cE ƒ‚b× ōPÆÁQDEåŨŒk ī­5ķۚũĮYßŲįˇ×Ųgī}×ēPü‚ÂtX€4ĄXîëÁ\ËÄ÷XĀáffGøDÔüŊ=™™¨HÆŗöî.€dģÛ,ŋP&sÖ˙‘"7C$ EÕ6<~&å”SŗÅ2˙Ęô•)2†12Ą ĸŦ"ãįlö§æ+ģɘ—&äĄYÎŧ4žŒģPۚ%áŖŒĄ\˜%āgŖ|eŊTIšå÷(ĶĶøœL0™_Ėį&Ąl‰2Eî‰ō”Ä9ŧr‹ų9hžxĻg䊉IbĻטiåčČfúņŗSųb1+”ÃMáˆxLĪô´ Ž0€¯o–E%Ym™h‘í­ííYÖæhųŋŲß~Sũ=ČzûUņ&ėĪžAŒžYßlėŦ/Ŋö$Z›ŗž•U´m@åáŦOī ō´Ūœķ†l^’Äâ ' ‹ėėlsŸk.+č7ûŸ‚oĘŋ†9÷™ËîûV;Ļ?#I3eEåϧĻKDĖĖ —Īdũ÷˙ãĀ9iÍÉÃ,œŸĀņ…čUQč” „‰hģ…<X.d „Õá6'~khu_}…9P¸IČo=C#$n?z}ë[1 Čžŧh­‘¯s2zūįú \ŠnáLA"Sæö dr%ĸ,Ŗß„lÁt  4.0,` €3pŪ „€H–.Hi@˛A>Ø A1ØvƒjpԁzĐN‚6p\WĀ p €G@ †ÁK0ہi‚đĸAǐ¤™BÖZyCAP8ÅC‰’@ųĐ&¨*ƒĒĄCP=ô#tē]ƒú Đ 4ũ}„˜Ķa Øļ€Ų°;GÂËāDxœĀÛáJ¸>ˇÂáđ,…_“@ČŅFXņDBX$!k‘"¤ŠEš¤šH‘q䇥a˜Æã‡YŒábVaÖbJ0՘c˜VLæ6f3ų‚ĨbÕąĻX'Ŧ?v 6›-ÄV``[°—ąØaė;ĮĀâp~¸\2n5ގ׌ģ€ëà á&ņxŧ*Ūī‚Ásđb|!ž ߏÆŋ' Zk‚!– $l$Tįũ„Â4Q¨Ot"†yÄ\b)ąŽØAŧI&N“I†$R$)™´TIj"]&=&Ŋ!“É:dGrY@^OŽ$Ÿ _%’?P”(&OJEBŲN9Jš@y@yCĨR ¨nÔXǘēZOŊD}J}/G“3—ķ—ãÉ­“Ģ‘k•ë—{%O”×—w—_.Ÿ'_!JūĻü¸QÁ@ÁSŖ°VĄFá´Â=…IEšĸ•bˆbšb‰bƒâ5ÅQ%ŧ’’ˇOŠ@é°Ō%Ĩ!BĶĨyŌ¸´M´:ÚeÚ0G7¤ûĶ“éÅôčŊô e%e[å(åååŗĘRÂ0`ø3RĨŒ“ŒģŒķ4æšĪãĪÛ6¯i^˙ŧ)•ų*n*|•"•f••ĒLUoÕ՝ĒmĒOÔ0j&jajŲjûÕ.ĢĪ§ĪwžĪ_4˙äü‡ę°ē‰z¸újõÃę=ꓚžU—4Æ5šnšÉšåšį4Į´hZ ĩZåZįĩ^0•™îĖTf%ŗ‹9Ą­Ží§-Ņ>¤ŨĢ=­c¨ŗXgŖNŗÎ]’.[7Aˇ\ˇSwBOK/X/_¯QīĄ>QŸ­Ÿ¤ŋGŋ[ĘĀĐ Ú`‹A›Á¨ĄŠĄŋažaŖác#Ē‘ĢŅ*ŖZŖ;Æ8cļqŠņ>ã[&°‰I’IÉMSØÔŪT`ēĪ´Ī kæh&4Ģ5ģĮĸ°ÜYYŦFÖ 9Ã<Č|Ŗy›ų+ =‹X‹Ũ_,í,S-ë,Y)YXm´ę°úÃÚĚk]c}Į†jãcŗÎĻŨæĩ­Š-ßvŋí};š]°ŨģNģĪöö"û&û1=‡x‡Ŋ÷Øtv(ģ„}Õëčá¸ÎņŒã'{'ąĶI§ßYÎ)Î ÎŖ đÔ-rŅqá¸r‘.d.Œ_xpĄÔUەãZëúĖM׍įvÄmÄŨØ=Ųũ¸û+K‘G‹Į”§“įĪ ^ˆ—¯W‘W¯ˇ’÷bījī§>:>‰>>žvžĢ}/øaũũvúŨķ×đįú×ûO8Ŧ č ¤FV> 2 uÃÁÁģ‚/Ō_$\ÔBüCv…< 5 ]ús.,4Ŧ&ėy¸Ux~xw-bEDCÄģHČŌČG‹KwFÉGÅEÕGME{E—EK—X,YŗäFŒZŒ Ļ={$vrŠ÷ŌŨK‡ãėâ ãî.3\–ŗėÚrĩåŠËĪŽ_ÁYq*ß˙‰ŠåLŽô_šwåדģ‡û’įÆ+įņ]øeü‘—„˛„ŅD—Ä]‰cIŽIIãOAĩāu˛_ōäŠ””Ŗ)3ŠŅŠÍi„´ø´ĶB%aа+]3='Ŋ/Ã4Ŗ0CēĘiÕîUĸ@Ņ‘L(sYfģ˜ŽūLõHŒ$›%ƒY ŗj˛ŪgGeŸĘQĖæôäšänËÉķÉû~5f5wugžvū†üÁ5îk­…ÖŽ\ÛšNw]Áēáõžëm mHŲđËFˍeßnŠŪÔQ Q°ž`hŗīæÆBšBQáŊ-Î[lÅllíŨfŗ­jۗ"^ŅõbËâŠâO%ܒëßY}WųŨĖö„íŊĨöĨûwāvwÜŨéēķX™bY^ŲĐŽā]­åĖōĸōˇģWėžVa[q`id´2¨˛ŊJ¯jGÕ§ę¤ęšæŊę{ˇíÚĮÛ×ŋßmĶÅ>ŧČ÷Pk­AmÅaÜáŦÃĪëĸęēŋg_DíHņ‘ĪG…GĨĮuÕ;Ô×7¨7”6’ÆąãqĮoũāõC{ĢéP3Ŗšø8!9ņâĮøīž <ŲyŠ}Ēé'ũŸöļĐZŠZĄÖÜ։ļ¤6i{L{ßé€ĶÎ-?›˙|ôŒö™šŗĘgKĪ‘Îœ›9Ÿw~ōBƅņ‹‰‡:Wt>ē´äŌŽ°ŽŪˁ—¯^ņšrŠÛŊûüU—ĢgŽ9];}}Ŋí†ũÖģž–_ė~iéĩīmŊépŗũ–ã­Žž}įú]û/Ūöē}åŽ˙‹úî.ž{˙^Ü=é}ŪũŅŠ^?Ėz8ũhũcėãĸ' O*žĒ?­ũÕø×fŠŊôė ×`Īŗˆg†¸C/˙•ų¯OÃĪŠĪ+F´FęG­GΌųŒŨząôÅđˌ—Ķã…ŋ)ūļ÷•Ņ̟~wûŊgbÉÄđkŅë™?JŪ¨ž9úÖömįdčäĶwiīϧŠŪĢž?öũĄûcôĮ‘éėOøO•Ÿ?w| üōx&mfæß÷„ķû2:Y~2"IDATxí|TĮĩ˙Ģ]­$´BH!ĄBĶ›)6Í`0Ũ6‰1q‹í'vđsŪÉÛ{Î{IįŋظÄqIü—gƒ+ØŅĒŅ›čę] .­vĩ˙sîŨģ}Ĩ‰3ŸĪîŪ;wæĖĖwfwΜ{fŽÎF„€B@! „€č°‚:lÍĨâB@! „€B@(t?ũhŦ­G×Ėô&ôžíËŋqáÂÔ××ûŧ.‘B ŗ C÷îŨŅؚ&íB@! :!ÃĐŅCanlŠŖËpÁ\‚9ũėÖĖĘĘJEĄ‹‹Cpp°Û59•@SSŠŠŠĐÜ܌ČČČÎÚLi—B@!ĐIąK}°Ņ€„äė:ģÚĢYÅÅÅ…Ū ‹Dtr<åq_QQŅÉ[*ÍB@! :ƒÕjQÚĄ7čP|1ĮĢM‹ƒ˛žÖ Dtr<îÅåŦ“w˛4O! „@'!`eŊ“ô¤4C! „€Bāš%`hļ6ˇÚxQü[E$ „@ģ°Õ\Ā… l0 &.ĒŨå_iMÛđˇÕĮ–r3îŊ%õJ/å ! „€čÔÚÕRŋ˙~ŧúęĢ °%K–`äȑž4N\NûVĀ¯Īč¨ūįņqfēœĨ]~ŲįŸQ ‰ëÛḚ̈›ˇË+Î3âžÉI¸or‚g´œ ! „€D€|ę­­‚ÔRĪ ũC=¤Č{íĩ×đÎ;ī´*[KP]]Ŋ{÷bŌ¤IŠŋĨ>kjj°sįNL™2FŖņJ{u—Ķ؈FĒaHHČÕ]ĪNZ;“#ÁΆ ĸ>čč,ʼnėFjMw M õؘ ĪŽ÷Ī‘EÍøˇĨü6üxrĸßtrA! „ĀĩJ€,õí×tVĖy @UUUÁlÁg%Ÿ¯ąÔ¨QŽkÚO.8€n¸á{Qęy ÃŦŦ,LžŽwö6ā͆Lz ÚdÄMƒ#ą`Z*ÂųĢą/ũ,ū~€ĶŠ\ãƒqËđ(ü€ŌQ‚Ž*Ė<š+V޸áäP|ō8×Ä$t̘Züi}-NšēbŒAJy'ņöĒ Ŧ.Ôeđ@RLŸŸ†á‰öZX ņūû…ȧ"’ûwĮÃŧüĪäQS¨?š–@õ‰ĨWaÃG§‘i ‚­˜=0†ĮŠ[ÛÚ.žÂë+ĒŅdšÂ#đøĸ~TwßĄŠ EtÉ;qž“´×͈—îŦ(öœPûqÉE! „€¸ š›ÛĪR˙ôĶOãŅGU0žūúëŽsXĄwĩāŋûîģ>Qķn#ß×N;:NŠī÷UžO ß[dž~u•ŽĮ‹@¯ĐfØZ_záU[ ņäĪ– ĮÖīo}}ؓ¤Ķ†*|üÚQŧ}ŅŲȋôÕēX؈ŗ;Jđ׃ øâ—ŦtWãë7āOeÎtŒ$¯ļ īRēŋe™ąú^ž?XīPČųú–ŗuØ|ֆŲc›đõy3ō92ŋ˙ĮŸjÔīqŲž}XüYsyĪ-¯ĮŋŊwwÍë‡GÄ ÕHĪĶäĀõyąļ/Ž:‹õYõxiQ4Ē‹‘^ŖÖ7gwŪX¤H.Øs_Ģ#¤§ŋr)ĪķPsŊIŌ×ķRĀįšbĪkûDŠ˜š$B@k…€ÁÖŦYŋ{“]}čųXŗÚģZđkkkũTWW‡­[ˇ"##CI3gÎ 2ėŗmÛ6ôéĶëׯĮwÜVÂ7oیœœđĶ?g˘‚ËbŊ{÷VōđĶp§M›†ŅŖG+yĘĘĘđå—_ĸŧŧ\ɡpáB$$¨~ē\ūĄC‡”üŧ•Ąkžŧŧ<đžũ,Į34îĀ‹Ë>Eä¤9蓺î:ƒ˜~ķđķ‡áÄībå‘bÄķųŌģ‘Q‰õË_Äēl=æ>ö ĻĨÕŗņ^~ūUäÔôÆãĪ˙ ’]ŦâĒėuĐ%÷G˛đÁƈH…{üfTmžÕŲ[đʲ7ą;ģŽŽõíˇ>‚E7Gâŗį—c[y(î{úŒŒÉÃ˙ūæ%¨ļ—ŗJ™šƒ'žžqŽ2Ë(ßŖXYFff ÍÕe8_`FŸÆ>ĘP´‚ũĢąėÅqšú‰Ë7ũ>;Öc÷A’Ŗęüø`õyLƒTJī)gjŠįŗÍÔ[j8x˛;ŠR0}r^ˇË=™uĩHR&'ێ˛PUŲŋo’ˇŸŧ] }”âT™ų…AŊ}ģŪ8Ķ:4?{WˇVė%! „€Ū VģģŒ÷%gŒĻœ;c|šĻãũíĩājÁã7Ęžv?9/ûîŗ Ėƒ>¨(Ũ_|ņ…bšīŲŗ'<¨ŧfĪž­(ãožų&ĻNŠųķį+ĘöŠ+Ĩø>|gĪž+ėŦäŗVÜYųįģãĮĮĸE‹pôčQ|ūųįxāåķ„€' œīŗĪ>Sōqų<ÉÎÎÆˆ#”ɁkŨmuÅXËzéĸŖŅX”“ķ~ē‘=ĸcrå<Ŗ&é/NGũqJZ„ŗ_-ÄÔG‡ ęØįXšq7lŖæĸWˆ•ęâ”nĢÍ!Ų$ˆ^6ôÆø”Rdė߀ß>vië_FBŅJĖž÷%Ã(jך]ģ°|NØū-̰;#ig–`hã1ŧĩa7ĨۍŊ9#Á¸ß^ætw)S§ŗ ÷X6Ĩc-ČĸuÅ7á‘Ĩ˕s÷2>ĀŌämX´äĪJ;gΝ€˛Ģđɲ R _CĘąŊ”G•sæ@ ŦV=6#kņD,ŊĢYQ*/œYƒ}û&ĸÔ˙ĩÅØDuŋ(ØtcДŗŗxY9÷,ûŲéIJŧ¯ˇ5kÖāøƒ¯K˜5kxLļg0[ƒ0Š\h"B­č: ˙2­ĖĪāĀÉRlu™7×Ԑߌ#XoÆôąŊąĖ‹Õzt!Ũ71¸’î ×Š ėËâĖzÜąx°ŪpŌ‘›ŌRģãĨģŌˇ›üÍ{ 9BÂņÅC•øÎ)ÂK/œĮúJę@–ū•X2ÄM ŋcæ čBū:…xiYļ’–GīŪėrÜ0ē†|S‚#œĨąĮi–2&´ØaŊˇ! 3R^?ÁR|ųVRūc#A~RJ´B@!pŠ”'ʲ2ŨŌbXļŠúŌ*âš>11Q‰6™L>|¸_Yŧ ķūûīGll, ¤øˇī#w-°˛Īw"""p×]waėØą E4)Ō]ē¸+wß}7¸\–“ššŠ’’Eųį´<ˆŒŒT”{žĀūü\_Ŧā÷ęÕ  PŦũĨĨĨĘ5–Á   ¯ú…¨5´ö~ ׎ŗī.Q"Ŧ1wcÕî5øō}õÜR܀f]3ÆÜųsåzá'kQBåžYˇV9ŋįĄIČßĩ7nT^6DĩƒEˇ+ˇŽĀĢ+6ãg}šdËDV~ ļ˙ũyåzÚ#¯âÅ_ě˙ũCåü›˙=†ĄsīSŽwŸ>‡Ü|E%SΏį•"÷ŒĘõօ#ŨĘLßX‚G>^‰ą:Õu Ū^ķ2‚ÖĢĘąwGĐŪŋ~ôN,ųßãÉũ ĻP]?lÖüę§œĪŋyÉv#ĢlôFû˜ Qą#„΃ėF\.÷ų÷×bGƟ˙áīũ´īĖvŽcM;æģōÉ'•ūt$j§ƒęŦC¸}E›´n„ũęŨ‚i^Ŋã8~÷UĨÃĩFģ~ϏKŪÉį‰­›´u!ˇ…ŠF—,&ûdP“m4ņN:jeÜkIģ‘K‹s„F­ )‡–väZ`{^=Û~ŧכ؝F(,žĄ—VŒĪ*Īe—ģ( îë"ØGJ‰B@! .@ģîSīZįž{NqoaK7v}a+xK]\ؗ­īØŪu{ImĸŠūîŨģņđÃŖ[ˇnJÚˇŪzKųÔŪX‰×ëUe”;Vācbb‹ŊvÎiXN˙ūũ•ëœW+ƒĩt|H°”õRMUåsö¤ŅÎ5ÍMá˜ō˙æbųŗëņÛG§č ô]ü$ęÃqۇaŠcū`DŦAuEąV•Ÿ„Ņ$šåSŗtĪúŨJü×Ėž¨*(ĀŠBļÄvGrLWÜ9ĒË2WãM2–Nü÷š˜’ŸũoRŠæŪK1&?}<ĘLęâ\÷` Ō‘]-ŌWÜG }=fũú/xædõ_ũ0˙×~5ë‘J0ŌŽ*t7ČŦ b.Jh< ™!öįˇĩÉlQÜsZ*;Ĩ ąđTšb”#ļĖsā•|Yíđvr3SÛ7ŧ,ūsa*Lä˙ūņ˙xÛžČ”‹ŠÎ>…=ųzĖN;Ų$D"ĒĄ ëöUc5-8ÕÖŌ•’RīR+ē;¤e—8:Ô2Øct—@mŸí+ăÃĶėWĖ8–͉Õú ‹ã[ė×ČŖ§¸–öÛĄI´=&§œG­švLJW%6´oféÎb-!o(ŽÂ Åęu•˙úDOī|ģ ú°ĩģŪ ØõFķĄ×ŦõÚšSĒ ! „€žhŸzūo9°rÛÖ°eË% [ÆŲu…pv‡ņ'‹ãYŠg%›-ú䗞žžŽ¸ŧ° -˛âÍn0Ŧ´ķâVvŅáôl™gNŖŊ”JĐįIJJÂZraûääd?~{öėQ\‚x2ĄåáO-p>>g+?OP\kiœŠõt\s‘ÅqJä×?îV˛ĸ¯S’rüũˇD#M2"SŨ\ęŗUĨž•,3ÉŌģ)õFŒY4؛Žĩ˙ķúÕLEáē˙ÄĘũFØÆũÛū8cnŋČü–‹Ā¨´D$… Ŗ#õ|Ę}ãŌdE¨G™ÖēĐæļËxЇĒÜUW—b˙úđß˙ĩä“ģíõoŗ™9Aļx÷›đč-Ē_‰áüķxæÕrtŨķ:vÛČŧL"T.\KäųMme;tËíëҚN¯Hc ŊfĩW".Ã›Ģ•<>ÁD }5Nn>åĻĐsą'ˇ–áįUf!´ Í_„_ŒŽÆ€÷ŽâyjÅl.ëQÔ+6í<>c{‘\5Æ×{˙ë#`8XĨčú ųexęā_&›¨Ėlįâ\b;uPĘîTęu´`ų>ŒgfF t_1^ČSëĮe KŒ´‹ųũĪa­âãoĸ›†÷tqķqÆkGįŸQ“tŊŅōɧB@!Đ6íjŠgY ė;ŋtéRÅ7cv_áĪ–+äŦœŋōŠēđ“}ßŲž]c\C šĩ°5ßÕ­gذaøôĶOąxņbפŽcvĢawžÛnģMYĢ]`ß|.—•z_ķqh WS§Nų\(K J}´æÄāûœŨN´`…GæšđÔęZXbÃÄ$R¤Ėd­õpķaŋi,ÛĶĸGĪI˙7~M Y˙¸¯ž°“R=~)ūöâÍhļ6 iđ,š$|‹rōS§‡Á8 #Č_~ŋ­;n™DJ1šQx”É.4Ņ”6v˰´XF—B+ĸV?‡ío<‹m´(xŪÜņČû:;ßų%ķ~JũŅ >*Äēˇ^ŖÅžßā_…Ÿüqv­ü ēš„¤œČv8xļÕÚbŲÜžĢ%˜\Ŧäk7“E{ŗgÍl(*ˇāæIäÆRŖ\l,¯ÂŨËvy&ÄŧQqJœŲⴘ¯ÚQ„¯vTâĢĨa4%ôtqƒđûž;íOĄöœ,Ã=ôr ÉŠą¸Å{ Ģ(¯Åŋ}äŧCÃy‚bēafŠsҟęnČR' ĒLæßĀĄ ĮÎ°ĖŽԗÜwÚÄBßF`’\! Žiē_o˜á04_(­Ä_k–a•ËącĮ w ”XA~â‰'”¤ŧ;ÍÛoŋŨĒ"¯Éåũá5wMÁfŝ­īĪ/öo×Üj8=[öų“]tØŌĢ]c+ž––åkÖ{žXpZNĮrų˜ŨrøX+ŋĨ|œVķé×ę͟ú0„ōĶ?i3÷†:šxžéz°ķēę1Ō„/ŨŽgHПø+đ‡›ē{zS(EčŊōęÖ%äƒfKęieHX M5(¯!ûēŅ„­ hŦ‡™ "é°`öŨ°ÁB;—°åŊK¨ã°6ÕŖ&^ļZTĶPžĘCģÖø+ŖŲ†CíVTCEĮĀdT]¸ŧ&Ē\õĄÅŦŽōÔķf÷Tkm ĖÄ>‚^ÔjŨ¨ Érå¨rō_6ˇīr‡ÜÜ\åNSëå”āũ7Īâ=:$2 ŗCëi/w5wrjOüõŽ>(;tO~U‹ķ^Bõxd~*'>…ę3ĮđËĒœéôaøō—áxfY™ē šž|ųÄKšûVƯzĢū7 ‹Å3 RÕRiŸ,ĢpČîCķŠs.‹q“ēâUē‹āފĶ^ü/Ķ^üvw"Vú×˙| *Ī×{Ũqŧ÷ūV˜cĮã§ˇņ"˙]mUâےÖ‰rE! „@į"ĐŽûÔŗĢļ ”-Ü­Yæ]Q˛2Í/šu\“åz¯ŗRÎ/WenMAį4ŽĄŅeë–ÉJ?—Á2x2ĀÁŗ ŽķĖĮqž‚•hz^3xž›éēæĪŠĖįątÖũȤ=Üy›Ę{&%Bī*Ā)ĘꙗlëõuîÕÆzRŧi1nDif¤8×ōÄÂšÍ îeS9un•ÕRē|Z=ĶXᡠ+=$ŠI¯,bļ5ג;”‹>¤ēj“ĩFvõĸēęI™7÷ZĪÄ$Ëģj-”íQÔ÷{‹{~‹[ķŠpĄ‘֐Ÿ¨Ž÷ø…GÅēŠw†™QžM;2ņDŒ‚‰ž›œŌŨ-eDßAxįY3j.RķäÕ@ģŅĶ^_~6Í-û‰ŖæFúM%8]cˇęë×7ŅūÖŠR]_§ÉAŖŊîÆđ(ÄÅšĢķj]‘ÆUTo4`ūõņîE{žuš÷˙ô:ĪX9B@! .€öŠ×ßÖĘėąĮ°|šēŸ9šĪSŽĻÜ{Æ{žģĻkKYŦ˝AgDęøQļõĀ-?~ÃBĐāąčąÍõŗ49üāۜ7Đ ūʰZ”ģūĸö§ąĐ‘6eˇYĐåÍ‘r° “’HK­Zd| \Ō™bŅo°ļôĩ•2,tW…’´T÷‚}‡ņ mģ™YÉwVø>L0nĄųÛ+QßųM[û‰! „€× iŪíÖl~ĸ+?܉[šŋwåšŨZÖ΂BzcéËo*BmÖ&Ô5¸šņÛš,'| I¤sjåÃË#OaVCĄįKȕ'€]7=¤ø? ÔõÆŋš"„€BāÚ&@ģß´nĩÔ î˅åÚÆë§õä _cwađ“Bĸ…Āå%@kÆtŖg XHL¸×"lĪÂM&#íFd‰–gŒč_.HōL"įB@! „Ā÷H ]wŋųÛ!E !Іd,ųErĀ9ŸūZÚ=U‚B@!pupėSĪÖø(S‚W-y‘#[ķyĒ!p-āqĪ‹ą%! „€Bāj' ZęÉ­ŪJ!š1xąW}{õę…ââbDEE9v1ņJ$B “`…žŸhÜŗgĪNÖ2iŽB@!Đ šhەîĻdĖņnė3ĮĢŠĨž Å`’ ŽüŒVčyüKB@! „ĀÕN@Gn7íˇũÍÕŪZПB@! „€č„č1§„€B@! „€čČDŠīČŊ'uB@! „€D@”zB@! „€B ƒĨžƒw T_! „€B@ˆR/c@! „€B@tpĸÔwđ”ę ! „€B@Qęe ! „€B@N@”úہR}! „€B@! JŊŒ! „€B@!ĐÁ .\¸ĐÁ› ÕB@! „€×N‡„……) KũĩÕ˙ŌZ! „€B@N@ĀfŗĄĄĄĩĩĩJkDŠī*MB@! „€¸6 455) 7œ={öÚ$ ­B@! „€ˆ€^¯G׎]å¨5[ė9čĖfŗzä¸$B@! „€B@\m, ˛ŗŗÁĘ}÷îŨÕc%_Üo8ä@! „€B@\Ŋ RRR~ôŽ5ĨŪ•† ! „€B@Ģ˜+öVĢÕ̆ĸÔ{!‘! „€B@!ĐąˆRßąúKj+„€B@! ŧˆRī…D"„€B@! „@Į" J}Įę/Š­B@! „€đ"`đŠ‘! „€B@ 477+׃‚Ä>Ú"¨īx‘ļ^GEE…O)ŧ­c=|^ģ#e$^‹Ŋ.mB@! &Ā;ŖĻĻFÉà ũŅŖGÁ §„ËK€Y744ø|!++ëōV IK}ę,ŠĒB@!på œ8q]ētAnn.ú÷ī¯<ü‡­ĮgĪžEHHâãã•ëWžfģDæî/čt:¤ĨĨáÜšsŠb?`ĀI¯™xQ杙ޖ† ! „€—B€-ôšŌXPP€ .(bJKK•OŽ5jÂÃÃ/EŧäņC€9_wŨu>¯ÖÕÕáđáÃ:th̊}c]5Ė´­ģ1"!>ĨuŽH/Ĩžöl:>ų6Ŋ§ü7Ĩ†^ąVjåĻLüĻõķ_níŠ5ød[’&.ÂÍũûōœŲą ˙Ė*ôFôH‰Ķ)ę;žĮˇŽĮžĶe°Âˆ¸ã0ã†4xēbd|äÆĄO=î> ]Ü@ Kö!ĀØ@û!@qnÉNoü[sl˜|į=čÛÅævíģž´$û|Æ7øįąbęO &mæLę|ũŠõ[[žˇZ_ß9ˇņā÷Œû°Ĩ~øŽ}ė3ŋĨŦŨÆžOų)„€ IIIØšs'ÂÂÂ0bÄÄÆÆâȑ#>|8ĒĒĒ‹=+øžJ}Cöz|´)ߝĄ1ãĻÍ xŖ{ŧ˙ķ÷ųŒĶīTô˛”‰w~dr¤ō)›Ž^5˙síđûĖëēuëæhķ™3gPVVĻôĮ;{ļęWVV"22Ō‘žŠü$ÖŽŨ†2ŗÎ§GĘ„Ų˜6°súáûõЎAī€rEhŅCKAƒŽa‘ôō[uˇėeGŋVzjŪjFiNVí(€ŋø‚ŒUØÅ =MŒz3Оļbķ‰Z7™WÉ;‡‹8H>eûNWĩ_ÕZé‡K)¨åžŊ‰Î<ūdWX‹ÍŠBoD%*?Ŋßė-vfŧJŽÚĢ?/é{ëŖ¯Ũë8$ũ¸„ļĻŧ cŋ­UôB@tzŅŅŅ`ßn“É~š'Ÿ2ĄĄĄŽ…šė÷íŦVM™4ÂiRū‡`.GFúˇPŊķ=s¸Ÿ7C˸s*7ŲT/“I$đ˙ÜÖė&wAßËYûũ>ķšæÛØØčđ­įc^$‡’’eÂeŗ9 †:āë¯ļĢ =éŽqŅl0ļ"{įjlģ*ø´§ōuHqBЊČË\…ž­„™ŦÕ)c§cÚā84doÃįdÅīŌÄĘĸܸhJ˙šĮ‹TĨ7,:“fLGLÅ|ņm6Œ‘a¨/­ z$ Ž™cHŧg2ÖcģŨrjŒLĸ)Ķo/¸ĐQŽk­VüiE3?—ÍŽ–bl_û-N–ē”?kē[{ĘsŠ(a†/ø1F™Nāƒw úÜ EûŠ?ŽS%Ĩō?ŧũq˙8yYခC(^ žÛcskô6eF§˜ĐˇSeB_皊Jš4tÃĀŊP|ā.Xiõö€I˜wCŸ€Ķ98˜ đÍĘí äoÆ×ˇ`îh}Ģ\€2ėZŗÉĢī´ų°ī~¨Å‘éؓŖŪv4Å ÆėŲcaō#+ĄKŽo\ƒ]œŪH?8h$€ž,žëŌō8ĸ;*Č.ÍWo›{F‡íĮßVîGe˙”öÔ:“>;CĒÍņõŊ|÷›öWáģ¯ß3ĮØõų}Ŧ2Z˙ūÆe‚ąČkLOžŅč› c\¤Ī1jđüíēķGí~ˇHíyBāZ!Š=_Ęŧ'ƒäɸczE×"ũī#ĪZ‚ė“ÛąûyôļRÂpžŊ Îaęį!ÆS@ įNŲ@ÁŽ•X—U ̞€×ėķ÷ĐD˙yĮ7ŽV˙›IO‰Ž´ĄĻž &Ũ>ąE[ąrSļĪ:%‡Ôø–įCķũûÜĢ…V´|‰ī‚¸.ˆ=pā€2Ą7nŒFŖĸė{J(=´Šļ9w-œ xhd|ŽuĮ.";+SRžģÎf˙o ‰ÖôŨ0ē@:ŗr'ĀnSēÕ]—žsęw’‡ˆ—nåŲĸÖ΃6m#…ÕG°’˙Xtߞ0#{÷ė+ąŌŦ° ôĒ(*…Ձōß(JaDÂ` JG}E66m;MƒŠõæzT–Ö >-Ô8+ōoÁ™:YČ׊Žtû)9.æĘkÔōxŪ@ V‹Ž,t›QûkCņ!ģ›ĩ--­ŒÖjÚô˛ĒuN&Í4ŗ$ˇ!ëEœČ<[8šBXQšĩCáã`@:…CSÉŗßjĸ‰BĄq9G 1OÆ<ûNm°ī~8ˇņKeĐE$ôÀ䎨):ŠĪ֜„?YÅ{ŋą˙htC˙høŲ$Ā_~ ã(PŲõėDG#°#2†kRáKŗQ¯5”>;CjÍņõŊõ×oZ_ß9×īY{ôƒCF+ãÚßXđõ]߸ãĸר÷×VĪß.7'4ō)„€hwÖúB””— īÔa”)ÖûúgĒCi%ĩ¤Īp°Ö_$]ĄœôŦļßXq ‡h'žC‡v!3GŊcJŠŽŋßÃâCä‘Ā $é ŅM´]d ĖT6—Ëŋ•ūęäO^ ŋĪmk•{jv{ęŲŗ§bį+lĄīŪŊģ{"ŗō"6Ō‰ƒ†9üč{ģ÷Ū{/îœŲeíĄŗŲõâJ2`ŗÎŦ'í‚ī°Îėī˙Ėķ˙¨lģoŨĘŖ9M›ä3aü„`öĖy˜3T/VT:o+Å ƒQã'!Ž‹(*UÕ$W§˜ø ˇaÚ¤™ŸėôH/ĪįŊFõŋāVLŸŊ7ŅdąÖBüØÛ0}úLŒKāîN:k)GB@ö%`)=JŽ _#}Ûqđ?iˇ´1čåT”ÂT]ƒ\…ÛZtM62wīFfæq”Õë`#߇)M~~ĪáxÛ¯õä‘Ā˙Í?ÂõqĒ1ĶWšÎ:ų×jėÛzēéb^ŋĪßmŊûÉ8]ģvUč¤ĻĻĸoßž-’2Ød0玌Ũ§}a;ęlņc):ķ-ŠŪ°Îėī˙LĢ´ú4U…v3Ŗ‡nåÔĩ­Öo:Ž{oâĩ”ogp3šĒéĒ@“}Š[iĐXČÍ&}Hš¯8íîĢŦUsŠīfÅrLˇKøÜ€ē}e@4ĶŠŪ_ËëŪŽ’há š 1g"ˇĸŠî ”ĸĨ(ĒéâŅž*løŋ•ČĨnŒî‡™s'ĸ‡ōEō?sŽāLQÕąĮįÂfD°KŅūڐoeRkąŖ|X‰ÔžĨėfŗ˜É7N |I;ļG)Ļŗ'Vdh÷4áŌZßĩÔJŅ-ģää.4GJ@Žq ŖŠ7:ô´6AmYûûųx~DkuŅÆ‚“%@Ų]énP xׁÖIp?ō|Äu\u†ūÔØ´ôŊõėˇ`¨‹ˇZękM.^z?8Ĩ´6ŽũŋczV°ÛØ×Jō×VíˇKK'ŸB@+A@MFĮ Id3´",21zԞ=ĨD˙M.(ÆĶļīÍ’p#îšŲŸŦleØđųWČ­)Äɧfáū{ sŽú§¨Ũą Ķū¤•Z¨o-ÕÉ]^šhCĨ/]ĖĪīŗK1m>ä­CŖĸĸ”˛­eV˙-(<¤ ĩ§Ō¯)íf$ĩŖÎf´ë{&éŗdčæāī˙LšHoę˙‘ĶÄčɕõ•ļ† đ횲Éŋįč‰=Hߊú$w5ą-X ¤ŽŅA-YAՅ}ÉÚŪ“n#qĐiŖD9ķ~‹‰æ•ÛfdlĘĀŠCkņÕÚt¤o:áPlÉQÅ;“Ÿ˜ÃéĢq$§ Ũ†NÅŦ ‰j*öän]Ŗ(ôŦÆE›l8ôízlܑ…ŗ~âOŅ-ß|ģĩĻDt5“_Ií–ÜĪqë† ņ׆„„(ēj&ßß­8uŠØí.áäöÅĘĄÛ›ĸėģÅø>i5™šō,ōĢŅ:—ÖûÎģ­ŽŊ‹Nŀ„`”ĐC~ßĨņЇ 9w'2cOúœ8´Ûs,>Øz]ŧ3…(´˜‰ũnĖØŋíŽī<ĸXHē$äi¤#tüūt4…nûy~oŖüô[ ­4Qƒw_;åĩ|x?ø’ã=ŽũŋcÚmėZmĢúÛåĢ6'„€đO ž^ĩĻōâLĪ ųÔûēĻĨ5Ûobl<…^‰ˇĒ˙‰y‡3”˙Č#äqp)ÁáM`莾ĘbPÚĸŌßīĄŠqŦĪ‘æëŋŲoüé5Čō§‹šũ>{sģ”ļ˛ÅžŨpøI˛­…¤ą#/†ÆüíXš~öîØ€/ČcƒĢՋ\¤“ÛQgËŪŊEŅ™×î,TĒe2YZՑÕ˙#˙\[oĄ7 ~ŖG¸)ŦŽ$æė&%ˆŊŗ"iĮ¨x§ø ÅÎŽĄTĻãé_ŌâĩÃ+ –D788÷ēa*’Hšn$ėm™…t-“LtXÂ}åQ„xŧqē~#†R‡ņJæ5Xģ“]kôđlOQ…æ6dEQÎiä"š}:ūâ'ŒĢæy™ą=‹f[Ļ~˜1Ž]rœÁ_ĮĖÄu=‚a%˙ļmÛTvņäĒ44ÖÉN=Ō+íÕŲŖ]ųhĨšNáEžâŦ>Û*ŗąž(ˇÎåŌúnøŒŲčfC­Hßy†än7q†ų=š¯)}}é ėˤ;Jãœ,Ôļ^Z]’’MËaĮLG_oÕ9{iW#ēå–BÖ§ģץã÷§JRy÷ņŊõŨoŖw+ZúÎi×Xļvė:^í­†-k˙cĄˇŋīēĮØoŊ­žãOĢ™| ! ü8}ú´˛:ģnđ“e]ƒë5Ū…Å5čõĒäîôŠĻī3ŊŒ6EgؙY`ŋĢíūĨũîr×c>×dkēĮŲõhԐˇŋßô)S•˙ōFˇ˙fÎ ´T'ōúøûŦ–piīŧP6##Ãíĩ˙ū–…… ĀíŗF*úQuū)ÎĘĨЌ‰ŖÉ-œtÚží¨ŗĄ>ĪĄ3wŖ­ŗĮĮG #Ģ}íĢ‡wVËmĩ_ÕŅ#Žĩį•AŲŦŸlš]ü‹ļІū•äĪAŽmÕååŠĮMtLŒ—ëOÄPR ĒËÕ]zL1íõP–ÉģöD &ÆÕĻë^3mhŦ.G%MęÃ"cŅö;iî…|fĄÕßVú’‡Đv[œŠu.—ÚwÜžŗ‘Ä[-ÉbNtŸ‰XhŠŊÕR~īÔΘ@dsjNĮ;9šÖŲ)E=ęčũŠĩĮß÷ÖWŋiyžëg ũH9ūĮ‚ŋ1í9öËŲÖ@Ú i„€č\XLĄŨR •§Įj;ßp+]¯ņöŠ1¤×,¨ĢĻ'ŌĄü˙C.ÍWJŋ‡üģm ē‘‡ÂˇgČ´\'ßō˙}öUGqŦČķ7ŦÔķĶ{]C=ɗŸPTT¤ė~ÃîŧxVķģwĻUu:^ hŠŽ§JËíšTM{ž@ī)wāĻ2 ZŨuf˙˙gÎÚiGžšjWŊ?:^[ vKjQŠ×ʧB@! ŽUüôŌÜÜ\eŸú„„7 ĩĩĩČËËS<Åî5-œ\˙.ļįáÆ;D˙v~0äweÁüō÷DYM6?u–RœŽ+´5ÆOX„YŲgâĘQę¯k)I! „€W=ēō\Úd„vLHô˛bߕį§ÄØBßÚ—ČisšÆ2d5 :.ņ zf¨ĩĨžÍŊ%„€B@! „ĀÕEĀ—R¯m€quÕTj#„€B@! „@ĀDŠ•$B@! „€W'Qę¯Î~‘Z ! „€B@€ ˆR0*I(„€B@! ŽNĸÔ_ũ"ĩB@! „€Ĩ>`T’P! „€B@\DŠŋ:ûEj%„€B@! &`ā}.%! „€B@!Đq ú ę¸ĩ—š ! „€B@kŒĀÁc'‘ęŅfqŋņ"§B@! „€B ŖĨžŖõ˜ÔW! „€B@xĨŪˆœ ! „€B@ŽF@”úŽÖcR_! „€B@!āA@”z r*„€B@! :Qę;ZI}…€B@! „€Qę=€ČŠB@! „€čhDŠīh=&õB@! „€DŠ÷"§B@! „€B ŖĨžŖõ˜ÔW! „€B@xúpO…GÔw=mBÆûDûËũŽõōŋt˙ üû#?ĮŸÖœ‡š,//ûÅM:ØĘã­eī#Ž%! „€B@Ģ™@ĐŽŦöWęsvžBûËŊKņŲ›ëQÚ% ÃScĐT~ĮOĻŖĒ ¨+?„Ė“POĮ„€B@! „ĀÕLĀ  nīę#Ŧ‹ í/ˇŊëÉō*qÚĖyė LKnĻķģņ—ˇîĻOjƒÛ (A! „€B@´;C˜‘eGžÆo|| 'ˆÄ‹Ənî_ĒîÅŦ-XūĘJځĻ ē2,×\¸ŋũŸ,<üüCH &íøûsFŌOžÁ†=øãŸ÷áú QXõųVX¨ŒũIšĢņ×ĩĮ)­sūõ÷¸u`´"'°´ĄXŋüydõû1–ĖîG2(ԝĀËĪ}ˆa?ĶRCÕ8åŊ ›—˙7ĒčxŨ ˙Š‹÷<ÅũĪaųYXüÔũˆrIɇfrĮyoųëČ,˛RÕâ1ûĄGqÛȞŠäT! „€B@\y˙čqs œ‘LIENDŽB`‚astropy-astropy-201cddb/docs/development/quickstart.rst000066400000000000000000000343251507226315300236040ustar00rootroot00000000000000.. _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 `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 '.[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 `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. astropy-astropy-201cddb/docs/development/scripts.rst000066400000000000000000000037711507226315300231020ustar00rootroot00000000000000******************** Command-Line Scripts ******************** Command-line scripts in Astropy should follow a consistent scheme to promote readability and compatibility. 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`. .. _"entry points": https://setuptools.readthedocs.io/en/latest/setuptools.html#automatic-script-creation Example ======= Contents of ``/astropy/somepackage/somemod.py`` :: def do_something(args, option=False): for a in args: if option: ...do something... else: ...do something else... def main(args=None): import argparse parser = argparse.ArgumentParser(description='Process some integers.') parser.add_argument('-o', '--option', dest='op',action='store_true', help='Some option that turns something on.') parser.add_argument('stuff', metavar='S', nargs='+', help='Some input I should be able to get lots of.') res = parser.parse_args(args) do_something(res.stuff,res.op) Then add the script to the ``pyproject.toml`` under this section:: [project.scripts] somescript = "astropy.somepackage.somemod:main" astropy-astropy-201cddb/docs/development/style-guide.rst000066400000000000000000000274241507226315300236470ustar00rootroot00000000000000.. _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 `_ astropy-astropy-201cddb/docs/development/testguide.rst000066400000000000000000001074421507226315300234100ustar00rootroot00000000000000.. doctest-skip-all .. _testing-guidelines: ****************** Testing Guidelines ****************** 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-dependencies: Testing Dependencies ******************** Most commonly, you should install the full suite of testing and development dependencies:: python -m pip install --editable '.[dev_all]' 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. .. _running-tests: Running Tests ************* 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. tox === 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. You can run the test suite with all optional dependencies with:: tox -e test-alldeps Other useful invocations include:: 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:: 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 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:: tox -e test -- -v --pdb 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. 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:: tox -e test-alldeps --develop --notest source .tox/test-alldeps/bin/activate 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. .. _running-pytest: pytest ====== 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`. 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:: python -m pip install --editable '.[dev_all]' 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:: pytest -P wcs,utils 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:: 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 For more details, see the `pytest invocation guide `_ and the description of `caching `_. Test-running options ==================== .. _open-files: Testing for open files ---------------------- 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). Test coverage reports --------------------- Coverage reports can be generated using the `pytest-cov `_ plugin (which is installed automatically when installing pytest-astropy) by using e.g.:: pytest --cov astropy --cov-report html There is some configuration inside the ``pyproject.toml`` file that defines files to omit as well as lines to exclude. Running tests in parallel ------------------------- It is possible to speed up astropy's tests using the `pytest-xdist `_ plugin. Once installed, tests can be run in parallel using the ``'-n'`` commandline option. For example, to use 4 processes:: pytest -n 4 Pass ``-n auto`` to create the same number of processes as cores on your machine. .. _running-tests-installed-astropy: Running tests on an installed ``astropy`` ----------------------------------------- You can also run the tests on an installed version of ``astropy``. First you need to ensure that the testing dependencies are installed:: python -m pip install "astropy[test]" 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:: pytest --pyargs astropy You can also include other ``pytest`` options as needed. .. _writing-tests: Writing tests ************* ``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 :ref:`test discovery rules ` for detailed information on how to name files and tests so that they are 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:: pytest test.py The result is:: ============================= test session starts ============================== python: platform darwin -- Python 3.x.x -- pytest-x.x.x test object 1: /Users/username/tmp/test.py test.py F =================================== FAILURES =================================== _________________________________ test_answer __________________________________ def test_answer(): > assert func(3) == 5 E assert 4 == 5 E + where 4 = func(3) 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.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.utils.data.compute_hash` function on a local copy of the file. 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 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 @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, the Astropy test runner 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`) 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.set_temp_cache` 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 the runner would otherwise not have write access. 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. 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 We can use this for example to download a remote test data file and have all 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 setup/teardown -------------------------- Tests can be organized into classes that have their own setup/teardown functions. In the following:: def add_nums(x, y): """Add two numbers.""" return x + y 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 def teardown_class(self): pass 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 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: """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 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. These take one argument, which is the function being tested:: def setup_function(function): pass def test_1(self): """First test.""" # do test def test_2(self): """Second test.""" # do test 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, you can use ``pytest`` to avoid writing separate tests. For example, instead of writing:: def test1(): assert type('a') == str def test2(): assert type('b') == str def test3(): assert type('c') == str 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 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:: import pytest from astropy.utils.compat.optional_deps import HAS_SCIPY @pytest.mark.skipif(not HAS_SCIPY, reason='scipy is required') def test_that_uses_scipy(): ... 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``. 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). Testing warnings ================ 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. .. note:: 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 `_). Testing exceptions ================== 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 the test runner 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 =============================================== 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:: if this_rarely_happens: # pragma: no cover this_call_is_ignored() .. _image-tests: Image tests with pytest-mpl =========================== 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-mpl360-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 You can optionally pass keyword arguments to ``@figure_test`` and these will be passed on to ``mpl_image_compare``:: @figure_test(savefig_kwargs={'bbox_inches': 'tight'}) def test_figure(): ... Failing tests ------------- 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. 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``. New hash libraries ------------------ 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. To generate it, you should run the tests the first time with:: tox -e -- --mpl-generate-hash-library=astropy/tests/figures/.json for example:: tox -e py311-test-image-mpl360-cov -- --mpl-generate-hash-library=astropy/tests/figures/py311-test-image-mpl360-cov.json Then add and commit the new JSON file and try running the tests again. The tests may fail in the continuous integration if e.g. the freetype version does not match or if you generated the JSON file on a Mac or Windows machine - if that is the case, follow the instructions in `Failing tests`_ to update the hashes. As an alternative to generating the JSON file above, you can also simply copy a previous version of the JSON file and update any failing hashes as described in `Failing tests`_. 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: 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 ===================== 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. astropy-astropy-201cddb/docs/development/vision.rst000066400000000000000000000130141507226315300227110ustar00rootroot00000000000000: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 following document, this common package will be referred to as the core package. This vision is not set in stone, and we are committed to adapting it to whatever process and guidelines work in practice. The ultimate goal that we seek is a package that would contain much of the core functionality and some common tools required across Astronomy, but not *everything* Astronomers will ever need. The aim is primarily to avoid duplication for common core tasks, and to provide a robust framework upon which to build more complex tools. Such a common package should not preclude any other Astronomy package from existing, because there will always be more complex and/or specialized tools required. These tools will be able to rely on a single core library for many 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 needed (such as FITS, WCS, coordinates, tables, photometry, spectra, etc.) and will encourage teams to be formed to build standalone packages implementing this functionality. These packages will be referred to as affiliated packages (meaning that they are intended for future integration in the core package). A set of requirements will be set out concerning the interfaces and classes/methods that affiliated packages will need to make available in order to ensure consistency between the different components. As the core package grows, new potential areas/components for the core package will be identified. Competition cannot be avoided, and will not be actively discouraged, but whenever possible, developers should strive to work as a team to provide a single and robust affiliated package, for the benefit of the community. The affiliated packages will be developed outside the core package in independent repositories, which will allow the teams the choice of tool and organization. Once an affiliated package has implemented the desired functionality, and satisfies quality criteria for coding style, documentation, and testing, it will be considered for inclusion in the core package, and further development will be done directly in the core package either via direct access to the repository, or via patches/pull requests (exactly how this will be done will be decided later). To ensure uniformity across affiliated packages, and to facilitate integration with the core package, developers who wish to submit their affiliated packages 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: * The Python Standard Library NumPy, SciPy, and Matplotlib Components already * in the core Astronomy package Other packages may be used, but must be imported as needed rather than during the initial import of the package. If a dependency is needed, but is an affiliated package, the dependent package will need to wait until the dependency is integrated into the core package before being itself considered for inclusion. In the mean time, it can make use of the other affiliated package in its current form, or other packages, so as not to stall development. Thus, the first packages to be included in the core will be those only requiring the standard library, NumPy, SciPy, and Matplotlib. If the required dependency will never be part of a main package, then by default the dependency can be included but should be imported as needed (meaning that it only prevents the importing of that component, not the entire core package), unless a strong case is made and a general consensus is reached by the community that this dependency is important enough to be required at a higher level. This system means that packages will be integrated into the core package in an order depending on the dependency tree, and also ensures that the interfaces of 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 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 example with a script that will seamlessly download and install all the 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 stable, contains this functionality, and is released to the community. Thereafter, developers should encourage users to transition to using the functionality in the core package, and eventually phase out their own packages, unless they provide added value over the core package. astropy-astropy-201cddb/docs/development/worked_example_switch_branch.png000066400000000000000000001032071507226315300272660ustar00rootroot00000000000000‰PNG  IHDRs”ƒw{ AiCCPICC ProfileH –wTSŲ‡ĪŊ7ŊĐ" %ôz Ō;HQ‰I€P†„&vDF)VdTĀG‡"cE ƒ‚b× ōPÆÁQDEåŨŒk ī­5ķۚũĮYßŲįˇ×Ųgī}×ēPü‚ÂtX€4ĄXîëÁ\ËÄ÷XĀáffGøDÔüŊ=™™¨HÆŗöî.€dģÛ,ŋP&sÖ˙‘"7C$ EÕ6<~&å”SŗÅ2˙Ęô•)2†12Ą ĸŦ"ãįlö§æ+ģɘ—&äĄYÎŧ4žŒģPۚ%áŖŒĄ\˜%āgŖ|eŊTIšå÷(ĶĶøœL0™_Ėį&Ąl‰2Eî‰ō”Ä9ŧr‹ų9hžxĻg䊉IbĻטiåčČfúņŗSųb1+”ÃMáˆxLĪô´ Ž0€¯o–E%Ym™h‘í­ííYÖæhųŋŲß~Sũ=ČzûUņ&ėĪžAŒžYßlėŦ/Ŋö$Z›ŗž•U´m@åáŦOī ō´Ūœķ†l^’Äâ ' ‹ėėlsŸk.+č7ûŸ‚oĘŋ†9÷™ËîûV;Ļ?#I3eEåϧĻKDĖĖ —Īdũ÷˙ãĀ9iÍÉÃ,œŸĀņ…čUQč” „‰hģ…<X.d „Õá6'~khu_}…9P¸IČo=C#$n?z}ë[1 Čžŧh­‘¯s2zūįú \ŠnáLA"Sæö dr%ĸ,Ŗß„lÁt  4.0,` €3pŪ „€H–.Hi@˛A>Ø A1ØvƒjpԁzĐN‚6p\WĀ p €G@ †ÁK0ہi‚đĸAǐ¤™BÖZyCAP8ÅC‰’@ųĐ&¨*ƒĒĄCP=ô#tē]ƒú Đ 4ũ}„˜Ķa Øļ€Ų°;GÂËāDxœĀÛáJ¸>ˇÂáđ,…_“@ČŅFXņDBX$!k‘"¤ŠEš¤šH‘q䇥a˜Æã‡YŒábVaÖbJ0՘c˜VLæ6f3ų‚ĨbÕąĻX'Ŧ?v 6›-ÄV``[°—ąØaė;ĮĀâp~¸\2n5ގ׌ģ€ëà á&ņxŧ*Ūī‚Ásđb|!ž ߏÆŋ' Zk‚!– $l$Tįũ„Â4Q¨Ot"†yÄ\b)ąŽØAŧI&N“I†$R$)™´TIj"]&=&Ŋ!“É:dGrY@^OŽ$Ÿ _%’?P”(&OJEBŲN9Jš@y@yCĨR ¨nÔXǘēZOŊD}J}/G“3—ķ—ãÉ­“Ģ‘k•ë—{%O”×—w—_.Ÿ'_!JūĻü¸QÁ@ÁSŖ°VĄFá´Â=…IEšĸ•bˆbšb‰bƒâ5ÅQ%ŧ’’ˇOŠ@é°Ō%Ĩ!BĶĨyŌ¸´M´:ÚeÚ0G7¤ûĶ“éÅôčŊô e%e[å(åååŗĘRÂ0`ø3RĨŒ“ŒģŒķ4æšĪãĪÛ6¯i^˙ŧ)•ų*n*|•"•f••ĒLUoÕ՝ĒmĒOÔ0j&jajŲjûÕ.ĢĪ§ĪwžĪ_4˙äü‡ę°ē‰z¸újõÃę=ꓚžU—4Æ5šnšÉšåšį4Į´hZ ĩZåZįĩ^0•™îĖTf%ŗ‹9Ą­Ží§-Ņ>¤ŨĢ=­c¨ŗXgŖNŗÎ]’.[7Aˇ\ˇSwBOK/X/_¯QīĄ>QŸ­Ÿ¤ŋGŋ[ĘĀĐ Ú`‹A›Á¨ĄŠĄŋažaŖác#Ē‘ĢŅ*ŖZŖ;Æ8cļqŠņ>ã[&°‰I’IÉMSØÔŪT`ēĪ´Ī kæh&4Ģ5ģĮĸ°ÜYYŦFÖ 9Ã<Č|Ŗy›ų+ =‹X‹Ũ_,í,S-ë,Y)YXm´ę°úÃÚĚk]c}Į†jãcŗÎĻŨæĩ­Š-ßvŋí};š]°ŨģNģĪöö"û&û1=‡x‡Ŋ÷Øtv(ģ„}Õëčá¸ÎņŒã'{'ąĶI§ßYÎ)Î ÎŖ đÔ-rŅqá¸r‘.d.Œ_xpĄÔUەãZëúĖM׍įvÄmÄŨØ=Ųũ¸û+K‘G‹Į”§“įĪ ^ˆ—¯W‘W¯ˇ’÷bījī§>:>‰>>žvžĢ}/øaũũvúŨķ×đįú×ûO8Ŧ č ¤FV> 2 uÃÁÁģ‚/Ō_$\ÔBüCv…< 5 ]ús.,4Ŧ&ėy¸Ux~xw-bEDCÄģHČŌČG‹KwFÉGÅEÕGME{E—EK—X,YŗäFŒZŒ Ļ={$vrŠ÷ŌŨK‡ãėâ ãî.3\–ŗėÚrĩåŠËĪŽ_ÁYq*ß˙‰ŠåLŽô_šwåדģ‡û’įÆ+įņ]øeü‘—„˛„ŅD—Ä]‰cIŽIIãOAĩāu˛_ōäŠ””Ŗ)3ŠŅŠÍi„´ø´ĶB%aа+]3='Ŋ/Ã4Ŗ0CēĘiÕîUĸ@Ņ‘L(sYfģ˜ŽūLõHŒ$›%ƒY ŗj˛ŪgGeŸĘQĖæôäšänËÉķÉû~5f5wugžvū†üÁ5îk­…ÖŽ\ÛšNw]Áēáõžëm mHŲđËFˍeßnŠŪÔQ Q°ž`hŗīæÆBšBQáŊ-Î[lÅllíŨfŗ­jۗ"^ŅõbËâŠâO%ܒëßY}WųŨĖö„íŊĨöĨûwāvwÜŨéēķX™bY^ŲĐŽā]­åĖōĸōˇģWėžVa[q`id´2¨˛ŊJ¯jGÕ§ę¤ęšæŊę{ˇíÚĮÛ×ŋßmĶÅ>ŧČ÷Pk­AmÅaÜáŦÃĪëĸęēŋg_DíHņ‘ĪG…GĨĮuÕ;Ô×7¨7”6’ÆąãqĮoũāõC{ĢéP3Ŗšø8!9ņâĮøīž <ŲyŠ}Ēé'ũŸöļĐZŠZĄÖÜ։ļ¤6i{L{ßé€ĶÎ-?›˙|ôŒö™šŗĘgKĪ‘Îœ›9Ÿw~ōBƅņ‹‰‡:Wt>ē´äŌŽ°ŽŪˁ—¯^ņšrŠÛŊûüU—ĢgŽ9];}}Ŋí†ũÖģž–_ė~iéĩīmŊépŗũ–ã­Žž}įú]û/Ūöē}åŽ˙‹úî.ž{˙^Ü=é}ŪũŅŠ^?Ėz8ũhũcėãĸ' O*žĒ?­ũÕø×fŠŊôė ×`Īŗˆg†¸C/˙•ų¯OÃĪŠĪ+F´FęG­GΌųŒŨząôÅđˌ—Ķã…ŋ)ūļ÷•Ņ̟~wûŊgbÉÄđkŅë™?JŪ¨ž9úÖömįdčäĶwiīϧŠŪĢž?öũĄûcôĮ‘éėOøO•Ÿ?w| üōx&mfæß÷„ķû2:Y~@IDATxė]|Õöū6ģéŊŌ !AziRl ØA°ŊŋŠ TđYžúŦˆO úÄöé6z‘Ū{čéŊ×ÍÖ˙9ŗ;Éfŗ RáŪßovfn=÷›Ųīž9ˇ)Œä œ@@ \Õ8\ÕŌ á€@@B@šx@+@@y+xˆĸ €@@šx@+@@y+xˆĸ €@@šx@+@@y+xˆĸ €@@šx@+@@y+xˆĸ €@@šx@+@@y+xˆĸ €@@šx@+@@y+xˆĸ €@@šx@+@@y+xˆĸ €@@ÕŒ0bãŲÅØ~áwd%6$̧mãÛē<ŠAíoŠ‘—VĢE\\233ĄVĢk„ #āââ‚ĐĐPÄÄÄ@WІlNą&î'l:÷?„F„ÂÉŲņŠV^SĄEJb FG?„[cŽ&Ëɓ'‘’’GGG¨T*(ŠjáâF Ā{´čt:pÃĪ„ŪĩkWŠ@āĒB Ašųß§"$*ŽN*\é ‹X†đė_UƒĖ™ČYëb"W*•™ BŋĒŪĶ&–ß]>øŨāƒŋā™7)ä"ķ&@ Ad^ĸ.„R Ŋ^×ĸÕ?KĨJĖ‚¤ õz=œœœ¤?*kį‚Čk@tÍ{0™ŗVîāā€üüükĀՇ@ƒČœĢÛXy~j |C<š AÖ¸˜Đ™7ÄWuÆüs#¯ŅhŽęzá¯]LæŊÁ.ôt 2JĨ¸>AîP9U ¤ÉO+Á Ā{qFŋÖŽŧ.'“8ÎÎÎB3ŋ[yY)‘ĪWĒ篒|ä—čhh ūAžWJŒF+W›ļ?Ŧ:׈ŅxđĻČFËWdT“šŊ/ŋ6Ëãģ>(I°ųô2(C*¤k‰ČÛŪ‹ÛzOÆę~„ƒĘžÎđ õ OŪÆë¨d­K>jÂPåsęÔ)üüķĪ’Įƒ>ˆ.]ēTŠĢV‹€ünđųJēÃ+Īāå ,ƒsŸˆM÷ąÚ,Õŧxü‚TNPTģå~oo ?k‡†…áĄa!ÖŪâŪ &sļGÛãîč6ÅĘ )*_¯ÉųDē5‰F L”Žŋzt#J+J°˙üßXáø†šØ“uŖÆųå—_đÄOHy.X°ŗfͲ;˙ŌŌR?~ũúõ“:[íNØHËĘĘpäČôīß_2)5Rļ"›fDƒ”2^Ōá@_‘ÍXp“•3‰T›tīhûŋŧéõëk-9Ŗ@ƒ™?Ÿ¤ôF<<,´Öx"Ā„@ƒÉœLvšm¯Ã}%Å}åöy0f›’9{8UKīî끑]ī:+w—ũR-Ŧ9nĘËËÁ¤ĖŽÉQv˛Æn0đĐCŲíĀŖeΜ9ƒČɚõĖw _ōÚڗ¨…Æ*’/Nâۂ*ĶLũ +ŋ; ßĮĒņĮ LļÅXũõ |’S+”RĒÅwŦzP÷cË+‰˜ÃˇÆ—aKŧˇ Đbu5‚ė™š…Å|fWbúīå>Œ‰kjvÜ&į–cæĮņhüŖ—? .ÆÆ9Ÿ4|gĘEú-(­ĀG+ãąál9æÜį‡âĖ l,1ɛ´?_Ũ&ÅK;?2MũdÎmxÉ"ëKŲÄŪ-Ę:Čî{™Đ§|qXų%Pk0™ ˛&QwI3VãĻđGĨH|mév¤,âa1āŧ.ä’‚|ܤ{ËxÍqŨž}ûĘbøZž1jŠąË~•-.X›ßģw/ĶŒŨˆ#¤…ėđāAiBĘΝ;qË-ˇH6|Ž›––&ƒŋá†)}8p@ŠËiŠŠŠ0hĐ tëÖMJÃCį6mÚ$ ĄãņķcĮŽEPPDä\O’ÚˇoŸ$ģeēôôtäææJųXˆ,]j˛ãÛëāŲBS÷cel"|ڏÄä{:ãÂ_K°..møūą;ánzæ™'7cÁ‚•H¤/öč9øNÜ;Ļ3Ü(Į”Ŗ[đĶOŋâXZŧÛõ[oĨ° ėũæ["%ęŋ@>žˇ/=y3ܲ÷áĮ‹›Z&å3zôdÜ:$,ĶWķ×ÁoômˆĖیykTøø‹éw˛īŗŽccßS’,ˆ\Ûb<ЍĀĪDÎT˜ą´J€nŸ\錎o÷ƒkZūĩ¯B"oCniŖđb§ lH(ÃaSwFD¸ ˛­7ÜU؜’Į˙^õyŧbAä ˛ÍLíĒÄūXĘĮÄõX¸*#ēų#’â[į3" ‰$˜Á‰ËĀæŒ/ÍųƝ-@)¤FiįIÎÔDō ­i7gC§lœK!ĩžžĀē´ˇmbЊ[u%ÛŅ-Í/LčÂ]“šžĖõuļŌlMūĩF6ļâՈÔČ–DmymŠąĪ›7¯ÎRãããņčŖ"//ūų§¤ŠK&6ÃÜ|ķÍđõõÅ÷ߏáÇãÎ;īDFFVŦX{īŊîîîŌō<Ų‰ÃJJJ¤|Úĩk'ÆYļlˆŪŊ{ƒÍ?Lė÷Üs4Fšㆀķátŋ˙ū;8ŋ?if䲲˛¤ÁēãÚYUŽíąą ??øėii ņÚnļāúÚˆĨûXu;üür?Vbú;ŋHacn‚œŊ̰fA,R?ÅĖ躘9ûk˛7 Âķ/õĖæSØQäēĪEȅ#$…éĩK8Seî:ücÆ’lŊŠN Ôũt*ņ)žŊŽ{č|3*úÂ˕ˆÜLR’§ÅĪÖ­[1ū| ŸĒKÆų駟Žōh„+Ū}ČTâéĸ‡W§PŸœMé íŸÅߋ&CuâŒ|üKčũĀĘUĶāG÷Ķ}Ž‚ēæˆ ÚôÄKOiPė?†b}Ūo8ē׎*7”įHxúÃ'ôz<=۟4TgDtîŒŪ— iđ؏~Xļō ãzĘÍ Ÿü ÜßšûžĀ¯ŦĀ–?“đd/ŗLDâī˙ønˆņ….; šå&͐åļtüÄΚЛ‚Čšįˆ.x÷Ž8ü´šģö%aôî$öŽáŧ"=‚|“™„ |ŋ?W:8bT[ļkwA˜yÔJ•ąÄ@īE°RĨpÆl3‘súŒ n(LxÜ>ŦDäė˛R‹"‚–āH\}"˜B¤_ę›ËDΎ&ū‹Jnj{ôB00÷ˇĪĉΛÖ`:A_t~ŕ GtŒéÜĩģ$ú˛cמŗ}&Y#—s”ī-5t9Lœm#Đ`27Ø9šÅ˛x{ĶØĪ2īϏÎÉÉĄŽŖ_¤ŦeōŦ­&}î$eŗ ģ6mÚ 11Q"[&X>˜LŲžÍfÅ‹+- R|šãxÜ0Ki9_c žÉœĩnIÄ6ú‘#GJ įËéØv/§ķņņ‘äáĖš1¸”sđuGiRtJ‹(ÃÃā@÷šz3ĢĐ›?šü\paßnŦ8ŧ&]¸ęSēÃāɸŲ+Ö%ŽÂ[ĶW™ŠôģoŧßɉDf—•”‹â|ę1$wūëįp)ķ˛S$nEŽv¸tĢču†8 0=j­‰¸äxÖgkBo*"įr‹ĪÝËĒ:ÉŲ·ĒÃvķjÎŖ>ŋ÷4Ūṵ̂Ԅ"‡_Č,Į´‡đŌ#ũ06ôŌG…3õÉȉéLĢXT:2šY:'n MÂT—’"2]TËĮ"Ў÷ ę8%ŗģ]§3Ņ߃>!Ė ĮÄÁu™XŠp:™øĸk”EƜ‘pM†€ÅĢpyeČäSŸÔršrę(rõvŽ5НÖMđÉ'ŸHæ6—°suuÅäɓë,•GÂ0Ņr'$;&Xî•Og˛M"‚d{8ƒdÂeĮĢ–Žã˛ãƁąātŦísãÂ~ėxÆ"›ØÖÎäĪNîü´L'ØņŖĪŖ§(žl%ā{Öå{΂¯÷ūø2ynzi>^ŊŗŌV?…Ū;ĘÁHÉnûį{â‚ M*6.^€Ĩûöâí÷7ãך=¤8DCđpSžiĘųĻw–ā­1mQ”ž†ķéųĮŽ{¤¸†<5 r‹`¯AO&t6A5ļiÅ,ŧtŠ;Ė„ej\zÆâíģ"áAöí%Ÿ¡æÎCŽXœxR•ߓFĻ„xÃW]„õ‡‹ąŠ:ånōĮ˛‰ĖM_oRæôũŖ0ĩsĻ[ųWN`žwraÂ6aøÛátüŖgGsˆ§9˛IžAŦú3Ž&§Î,Ĩņ3d1ß'å2ēϏũ"L_Ą.QA¸Yu”Ŋ:ŗdšRŌęû‡Z}2˜ķá“1ķ¤ŲÄrŨ&Yšõŧä7ĄANO3@í=ä‚*Ę´Ršv腒ŧōjéË ¸õ79žļ7o9žœļ1ÎÜ9ÉDΚđ!C¤Īwļu×å¸ōØąc’ „IwķæÍčÕĢ—DĖéX3—GŸp§$2wlĘDĖq­3žgs 7ÉÉÉRÜķįĪãĉ’9FÎßV:ã/î­Ķ™˙¯•qŦīÍîS@qavŽX€žĮļpĸ''¨Ŋ†i¯ŧ‰7>XęFE—>&Ęp Ų]ˆ¸‰r0îÁ˙–îCÔ}ãĨtë?ƒoū†/ŪēĪ<ķ žųņŒ:sát25kRTģ~˜Đ›’ČYK­8˜–ĸđP#nˉjDÎņâvä`öî<ŧŊŋoo/„W×ÎxöņΘĘĄ&gŦ1úIÍ{H1¨2ŧČq-Ī1ũ=Í=D¸Š9xuéyéëmû˛#UŽDŌ#ē´ąLF>xvūqÄ^HĀ&˛Ķŋ›Rõ {„z›ãb|L•ŋœÁȞm-Ė9˛oÕ9áôé&ÜNKUJqÕšU3/Ķ”ĀÍÉšt7dkŗđö?fŅ…א\p¨RC0vŠŦ_ĪUŪ7ĮE,wš]Īž=1cÆ ]iƸÔÚŦŗ9ã‡~ráĪ|ļąËÚŊœ7ûņHYgķM÷îŨĨŽÎģīž[ŽVíĖÚ>Ûžī¸ã)žČļwÖū-;lå0>Ë_ |Í Šą&|֔Ų9øĘ_ļļŦûƒ/ÂwÍÛØũÍģØEZômˇDęšũØ÷ãjŧ°øsLŨ˙$žŲģīŊ°Nʗ;C?|iÉĸÅũ÷ˇCėâtlūå{Üŋ~žxIg?܀īŋØOqá7pžû`ôWKiY&ūæ°RJMų^Á_ ­xŨŌ`ˇX Cfą\Fõ$s k!WA_“?ŪkãúI~]•†ŧrwūÚ]ˆŋ^t­ĩ1SuÁŦ¨=æYŖÔų—ƒ)tXēđČ@ æėՖž@^n)f.⯋*įāīƒąU´C˛ĢÎY`¯ĀøÁÕ†ĒÔ|U„S8O/t‰Ēe_=Níw˛†^{ R ZĪ|ęâđm#ˇâĩQå?Ĩû;čx=Ęh–gj~"ĸƒēB§×būæ7ępXŠ8Ŗ˙øã¤É {g×§0įĀŖUØq•Ÿ]ˆo&îĢs͚5Ԉ&<ŲQ-‚ÅÍôéĶņŌK/I>ܑöŅGŲŊøBa‚fĸ”‰U&wög3 ›a8Œī9>ßŗŨ› —ĶąiD¯.Įeaøë€ÃØlÝĸėØ Ãubîä”˯+ pcc=s×Õ?mhdŒZd%§Cauŋ`zp¸ŽÂĶā@áîęX'xĐpA–›ŒB(Ę/ƒģt%šČ#ÍŌ‰:ũ==h˜s6˛ōéĶ>4 ­iüļ¨6pĶ— —ĩPjėũiæMqv*ÔîíĒÉdÅEųÝŌÜXķPŅ[i(åĨ]~ų:?Zp§ŗˇ+nq)§ąØĻÔá‘mņũsė8^ųĢ 52Uâéņ‘¸Ģ§i´Qņ…SxaQQU<Ĩ+ū|Á¯}œcYB&Ž?˙ŲÍJ3ÖāđĘãx)ļæ÷ËȁxíöHSŠ4*æąķ*ķî@-äEĶcÂÃCŧđųŖĻIHUbŌXúOi,ŊŲlÄdŋáŠëĒ‚­¯ĘNãĮ_v@C˙ķ'&Č&5ëHĻ{6ŠØKŪõ‰kģ´Öī[Õ_f]ígÎŲ˙yf|\üę-9ûЍŖmxÛ°đ˜i|šÚPŠŌ|ĶVn(iöąæŦų2a˛c‚Ŋ”&.E4˙0‰ōÁŽÉšŧœ*›_ø:LđLĘlg—M/ļĘ´ÔėåQ-<‚…ĶÉĻËōårŦĶÉūÖįōÜtPd•ŗžĪŖđŧĒ`px‘+ tDEaō­˜ļ 8Žn^RcĐU =)ŋRģĖĸņŲ2>RŖWžL€šÁ‹íē"$%sį15 j+™,Šo—˜ōd î N‚ü ‰hƒCMũĪZ ĐŖ;ôĐ 71 ™f͉Íā ¨Ķ3Ē ŧŽAIŊG<Må 'šųéëĢÅĢ~ã„>ãûbãČ,œO,1ãŦDPT¨ŸÚļ#Yŋ¤FĄÂ,ģ“ģ/ÍU°ĨI{Ą#‹hú°Āøū–v}YģuÆ#Otļ ŧš“y}Ƃį—gâĶ=O"Ā-ŽNžxnā<œČڃ…ąŗ 7šēˇVÆ}ƒ{n˜*Õû¯3ß >ų7XlgũüķĪĨŦĻM›vŲYJ$eGjyÔ GemŲZcŽ- [„_[Ü&ķזŖˆŽÚ\yYĘĢÚ¯jŅjāŖ.C‘UƒP-A žņ RėpNđ§]šL:x]ŅŠsø2&Ę(<ŨÕÔ?QWîR˜ŽÖo§‹ēdO;|Ÿí*ÃĄB“ŨœæãÎ^ö‰_RŠ wxÚWÄŠ“9Ųę.ÁFhNi"hJŽgîÁĪG߂>íew1ī>Ûũ¤|ÛėgŪ2LļcŗV˟Ū ZÔéPõOģôØ ôŗ%•DÎõīD&;FOÚ •Ŋ&ģ3ŧÆ#6˜ĖíÕ$máüĶĄ7H#¯zŊlÅin?&oõ!œ@ Õ!@Ļŋ~>đ3ԐPwëųH5ĒëA †Š¨ë“ëå‡ĖëŗÔˆ(˜˙¨<ķ133ŗrüwCĖC ŽČĸ"Ā9w€ĮÄÄ´@é„H­[Dn˯. Læue~ĨÃ, ģļk–Ņ2ėJË,ĘŽ ŦÉÚōžļëēi™Ĩތ¯d˜%9Ë×|ļŧ–å“ũä{qͅ€LÚ|f.’īåōežˇ“ãČįVGæ29[žųēŽ{ q͉€LĐ|–.ßōZžg“ãےąU‘š%aseeį‘,ōĩõYŽg á'š ™˜eâļ>Ëΰ?ķ–åŲ–L­ŠĖš‚\iv–._ķ(<Ģ“™Ô9ޜÆúšī……˛ėdōæ{žæÅôxm(^xOŽĮg&u>˄.§ˇ>ˇšP™e’f—IœĪŧx/DÅÛ´10 ˜ Ž8WoõKōīCĶŋ2_ņŠŧ**¯€Ę$.š|æįÂN>[zƒ7§°ĖėJ_ķKg}đrŧ(¯.Č㇙Ä‘WĩōâÚôTAŒâ}ģÔ˙ŒÉšWiåĄąŦx˛šKVH-y­6žmš9W”ŸåN>3 <žœw¨gN ´dx2kčŧ!;Ī^—5tų,kåōYŽKĢŅĖ-[.ųZ&tŪ–N ´t˜¤™ŗdū’ųĖōlĢ­RUåJË@ȟ*ō§Ž-„Ÿ@@ h)°âÉüÅÜÅŧ%—’¯AdÎōæ lßaŊN.ךĀ>ø“…wæN \-0gņ¨;6 Ë;Yš\˜āŲÉgžní‰œ;eBå ¯”c,Y3—[8–K–SœĢú.âŊ˙‡–÷- 2Yr›üŧŦšļAdŪ"ļ.ŗ i™˜äĘō™ÁāVŒ¯ÅYā Ūņ?hé< s—5¯É÷|ļt 2ŗXfÄ- /ĘcšųēšœLÔō™Ëļ<äža–‡Áá(Îņˆ˙AKæÍÂC­Í,ljáw×ōšļŅȜ‰œI´S§NÍ:P&q>sųü€˜Āų`[>ÜĄĀá €8 Ä{ ūÉœWvvļÔ(oTÎË)ģššIŗ:™€ë[^ڃ—bfB—I‰šĖšĐĖ‹‹‹Ņšsįf%rš|f°ä3_[r˜ŨgÚ¨ §$ĸ{ĀÃßÎteY†ŨųˆtõĮM]„ô< ü‚ũîâũŠ˙ûcÁMũ?eKkŅŦ4Z:ž¤Č+š<Į…gtև?ŦųKž—ëà ‰ĩkÍÜ23.Ŧ%NĘąÁRŪēŽ6ÍÅĐŅŖ1aÂ}t܊QC§āĪ#u%ŠĻN\ŽĄC‡â‘Ĩņä_„ûöáČÉK§W'Ž”ŌŊôGJĩüŦoäx/¯Iļjļ{Y†KÉz9íž; ÷Ü÷&ví´ˇË)C¤4&ōÄÄD‰Č™ŦÃÂÂ-|Í~Lō‡ãÖĮ]o5š™Ĩ>‚6G\k0øŪgL߀ßūđīÉA`ú~|˛h>ž~/<—lÂČ`§KfŖt˜ž}ØÖ•âāũÎDŧßLlû}ęJ­T™úôô9U—ŧr<ÁąÎx—´d.%kŊ‹¨8‹kŨ<én,ø—}¸Õģ ‘@ Đ@؜Ë9ģ€€đ–”–ÎÞčé`ķKNNŽ722˛Ū“­y˞ ëëFĶĖ­3nI÷uŖĩœĪH^~ĪOœ€I3fãįWGˆTč đûė˜öâį`X¸/NŸŽ/ū6iȇ—Ŋ‹éĶ߯ūLĨ´×¨ŸS>~›ũ’ 8æÎÅ–@Ĩ+J܆7§'­ĻLŸÍVZ˙Ą?aŪ[“0œÂ§ŋŋlėąå,ūīŋ5MĘį…ģPB‘*ŌˇbæôņŞŋđŲ‹ãņōŸŦåW îīŸ1™ō“ō|q6v%rlSü7_|Ÿ/\&•É2q™ĻĪ¸Ë•5íČīxöÎ;0t؝^ąšŠ<–åÄߟ`ʄ0lØ0WĨĩäÄŽÃ~ÂĢ_úŠj¸ię¨g,—wĮ„į°|íZ|úâ4ķsŠģ<ŗPâ$¨<œĩnÖž™Čy˜vrr˛ÔgĮDĪ×ėĮa˛†Îiėuõá,9Īk‚ĖåĘĘÕunÛĄŊ}˙ÜģđÄ+á÷UÛā8đß8tp&t€OáÄî_†„\ōĪŽÆūŖGņëO' !sĘŠEkq8VĮ,ŦŪŗ뒊r*Q.įâ+  ˛÷āØr´Ū<tt-Ū|î)Äs§”)Ēūđ*üv!žôđŽ›…Ö&IUr›âŠ1Y›"M.ØôÍĢxö—Ķp0âĀŅũXúųĮXąŋŲ# ‰ĮŪūIy^Ķ/Iû×âå‡ŪEĸš&Pü-û÷cŲ7ŸKeRfR™ާ3m‹MYUV 5eMFá‰E˜øÜ'ˆÍõÆāAn^OßõŠ<õšßņ4}ų”wzĪLƒD’åĮæ"“˛4ÕØŋx!ôūÁ)ÁTg.‰q+9bģIÔč”'Ŧ—Ę;–›Ĩg"æÍžßöĮâb‘u—WÕĪÂåTá+üuŋeee ‘ÔÁÉī Oŋg9“8|Í~ė|}}ĨŗœÆŪ÷LJTŸk†Ė@{F1¨ÂoƇ  <˛yfŋũ"&N‰>c˙yŽčNÚ&ģ“ņɸpáŦt­H<Œ”´ėĪUB5f<Â=Ldā늗–ƒ ēīû:ž5 —Hi†žų;–}ũž›y#Ũ+S¨&ų¤ 8Œũ;VĖĮ_?L—<4DēÕ寊ˇsũzė\ūēäqöÛmDŠĻLŒSđëēŨX<) žDœ“đ6ö+/<†ë؜C#}”æé.ĀĒL…Ū€¤ÃKĨ|­eÍ-æ‰bR YÕ8đķ\)đÁy_`ŪŧŸđúār(q$ĩ™q{Ĩ0w ôŊëaŧ÷ęĢøāŊ{āF)ōbņÍAŒ|áŧf…›ŌŊÍ:T8‘uⓜ˙^„u+6āĪī5 GXÔYžŨdõ^‰Q.Õ˙gĩã!Zqvv–x%88XŌĀ™°ųā‘,ÜņÉųšē˛š•žäÕjģņ6ŋĀõ:ĩZ›š-ėWjÔ¸bøķ_âā3%HH8‹ŗ'ļ`Åápîŧŋū>üīæ›á‡ ØŗyōöŖ˙¸;p~Õ^ŦßíŖd˜0Ļ3t†lŠx,‰˛bbvFŠJËĄpp‘n‡uöGIi ēŪ˙š¤€^ĢFq‚„ū}:ÂXZĩ‹ŖÉÃü[)ŋ™H‡\EņJa č&5ûŒģQ¤šGŠ}ũwĸƒ§åZ„!;îc|šh‘9'%ˇp4V͈ĸ2 %ĨиĘe* TTÉZZVŠ.•˛–Ŗ$Á”ËZ=Ę4ĻFâ—éãđ‹Íô2ëč8b zxG6ÎÁ?6šōt˙,ôÄīZD&ĨLš>Ĩ§MfÜü#‚ŠsjÔÁj$šŋ~†uĄt„‡WX¸)-Įę*¯O1˙@"$‡ũķ/Ė/˜tbÜØÉgéÆ|ĪÃmųŗbYŪrõ9 ÍÜJc?ģüq ĐonËGHT/Œžs&>›÷ŧ„ŠŸŌZīNŨF‹ĢŋÄÚÜļ¸ûĄ‰Ķ&‹æ-%ęhŅŊ-đ§–Ũ|g¤AvÎô5Zļš 9eŌčâî…xũõ˙`G"Ŋ9ŽĄB Ëežgæ­Ļ1˜ũĶrŠĨx=-Ģ@~:‡āĮc(ÉÔä§ãÅzŒØōé$,=TˆÉŗ~Ãîƒkņ\Ķ0*éKÅ\ĻAŌTÍ$y•ŦåRų •˛’†QG:9‡×~؀]ģļāĪ…ßã›ožÁĐO”hÛaęgs0‡îߛq?úø°gÉkX{& +?9 z\+*ķ—qûûÛu0ŨÚŠƒTäņ„lIÎō"y˜˜ĸÖō֜ÕJøWÃUhęÕß3G­xđ8rvlį˙QjjĒtÍ9Ŧ§§§Ké+h˜3;Ncīû&%¨įĪ5CæŒ yŠsD—ĄRœÕ˙ē o|ŋ ŋ/ūĪNûTōķķ§ąĸZŒ¸?Xē7(úŖsX(ēF›:÷ôw ŗ[Q‰R<5Y‰MG’Đ.ē“ä÷ķ´ˇąîī?đÁ Ÿ`Ũēe¤&ģV˜ĶČæĢJųÍūžú>\žķ^}^ú*pžõz´ąL%՗š×™›CŽ\€Ī.˛öí\‰‡\Œœŋ|_%ë[XŋųĪj˛Ęqø\=3úŨ6X ž˙õØļmū=åQL:qeH\ûž¤Ú/7į ũõÃ1°“ÉžčSv+ôxčápĐZãv†Zę@M‚:šĘûķ•'đÎīāÖĮį™Å3ÖZ^[?Si)ŋ\qžô˙äZĮM6đ8r~_xD ^áĖøākŪŽÜņ)§ą÷ũ’×ãįš"s{pņčũX:ë^2Ĩ›ū;Ėũ ąy¸yÆˇx} kŨZD_?QĘJŲ§ŧ*”ˆ~ƒt?øĄëáBJĄĻV[áDÚĩs†÷­€ƒņ ۚąŠŪĪã“zŌũĖ~‡: É43eŪ* Ļ5d,Ōq†Ö÷R!V?kž˜-ut˛|Ų+@Š¸äœŠl“sÁā‰oK— ßxĪž“ˆA}}¨ü]8—AĢ]ÖQĻĮeÉj@Ämá]ĒcŪž¯đ¯Í6Õņãß0:Ä]ī™[ÛëOÚø”ûžÆüŨ…č7i6Üö,NqîčéLÆ)úІÛQôŦŖüĖ–}üÚûgãīuĮĐ'ĸĘ|T[y‚ę$j†Nœĩ Āš<3“5pzČZ7“8ĪĐäƒ¯ŲÃ8Į•;BkɲÁŪ Úiˆ[%ŲÅĮĮŖGō­s)6-\‚dŗ=Õ2‚SČ0LčĨÛRqÃ$Üm"C9NéšĩXē3a7܇ŅŅtæVŽļ?ņÁSú ĪoĀōmÉ0Û)qöėY 2¤ZēÚoāâæ GĨÅdÆā$gOjuU:”—•KDŖrqƒĢ#ŲÉæ\VBĘ̓â“ϧ×ĸ¤L 9Üto€›‡3tĘÉŲ†â 8ēģ@OöōbúüâŧŨ•”–WXĨSCIå¸Q9rž˛ĖrūŌ=åQL+WzzŌ…ōRčT&Ų,Ķ8R>.†2Š8yz‰ú2„^CĻ…“T9žŌ™Ęt’ËÔÁõ˛dÕÁŌ)ĩTGĒ/,ęčāH͜Š/ ¤8üõ)aëHË0@ %ufj— ÉéT 7iëNĩÔ!yßzü}.>!7`ÜMŅH]˙Ü÷Ú2D=ũ=ž ŗÍōÔÔ'Ą“gĀe Ā$’ÂC!™V|||$į{îėäíß8;ŪŽÍ/öē]ģvIKŖČĶųå)ũlƒ— 6īđ!ģfíuõp…S9uö‘IŌ”ŽpUꊡ×BK"a­ŌÕ^ŽåtÔ÷C‚Ģ'k¨ÖšÖvo€š:ûÔJ8zzJ“| úb”°­Äėtj"Fsŋ&{éËJ¨ŽĘY‡—Qb~(ÕQŊií",ĨĘžNNÔ/ZŠRŠÅ:ŪĒšËxJĩú*”—RÎätÕec/-åŖ•âöÅ4͘=+Žz]*(=ˇ`fW~™˛–Q:šüäčIu$üJÍø¨Ŗˇ„•#aKŨ@õ/)5Õ_.ĶtÖPCi5ÔĩÔÁ7Ȁų/|IÉžÄė×LŠy^Āãci$ŨåU/]Ü .…“3“4oKɤ-ˇe:&aŪū­>Dn™ž>×ÍHæîrĮ$°~|ņī_ą5Š‚´đģqc¤iôDiüIîôC+ąp[!uč)Ō}Æö !ĸ@Ō¸Ĩ@—‰]ëļ!.Û4ŅÄÅ77Üt#‚åAĩO?ļŋī,@rFޤ™ŗ/uúŲs6ĐFĐ:ûãS YWūüÅ`Ž×iMœ—Hw)y zj tvԋâiė‰gCVŊ ËúؒSO“*LĒ5åãŨGāæAĄĻ(å6SĨ܊¸ ą8tĒ@ŠĮ/;q8ˆ÷@üZ:HdUŸ+Jæ´gF QI‡–üŒĩ„E÷ęN#ŖõHÜŗëö¤P\%ĸûöĸ)06œ6G…ippá%Z Í8Îŧ1Ŗátš<âÅ î~aËį˙ŠxÄ{P _×é}͐9˙AÄč1:Bŧâp5đ@Ŧ]Kā™4T‹,MęmšŽL“$2Wk†Ėy3áĀՀ@\\\ŊÅŧfĖ,õFF$W‚˝ĸ‡%Dĩ! Čŧ6d„ŋ@@ ¸Šd~=,!Ē@@ ¨ Aæĩ!#üĀU„@ŖŽf9vėXŗWĮ ķÁ˜ō™73⃧ôËGBBFŽŲ랉Āå ĀÛp2§Šhˇ.ųāuĶųāqōŧuŸų]Ŗ‘yŽV…ž]bä|›ílIæ\ų*"§|h§­V‡;7{¤ļ@ZĀtæYhĻI6„XŗÕG$´ˆHLdk„ƒ™C˜IėĄ“€Đöę#m Šĸ­įT´Í$“:“y‹Ø´%?"qˇĀ×Ņž ŧÃŊŽ<ôt°Ÿ‘6@N Ô…ÂDâ´×;T´YšŠ.”ÄälÛvhJi4Íŧ>•liqYgŌæ]4ÄäZƒZ=L椝ˇ4…<@‹G€Œ DÚDâÄÜN*:H9t"Æu$6į0{4ôúTR9ĄE<.iäLäųåFŦ=W“9zU¯ĪË$â 5đrV o° ãbx×2Ŋą;ĐV­œ 2gx‰ŗŲ´Âų"ō‚r=^äˆ6b°OÍWSøõA ģĀŽiąė¤÷usŠmēl{id'ȜåŅul^!ĶĘá ŪæŒ`oGŠŖĄ‘ņŲ ×Á><ÚSYģ5ĐčČÔBZ9›uåNŅÆ‚Cš„$í 7_į}áÅĘrrąeíZ¨kd ‡Ŗ7 ÉÂæCÅč6fÚģֈT‡Göm؈BŸū; ]ņZfžœž°\]Aķ šĖ•]܅MĮÕčwÛ(´kʂjŠAYę^lÚOĪöæ1ˆŦ×ŗ­%Cá-hšW3§ héqīÍp2îÎ &SKÁŲČDƍęLĄfKŲŧû¯y8ŦŠé¯ŧЇ†+°vūëXtĸÚĸ$œË؇ĨKw ÷¨‰čá°˙úäWäÄÜ…įž› ŸķŋcĮ‘x/ĘĪãįī~kč Š‹Q\ã(CqКd*@™:%\Áz9-J h ‚âzĨj‘ËOâ÷•+ąų4á܄NĢ-…š°Í+mÂBęČZ[Tĩļeõ~ļud*‚Œ@3kæ4[˙Č!¸Å{ Öoŋˆ;ĸ:ãČú=PÄ<ƒč ôĘę_÷˛Œ_œ‚h7ŌŪ#g ũāLlüí n™bŠvëĖĪq;-^snÅtčđāĨץÃk¯ éŲPBŅļ.Gü+ķm腃_Ü3ú:Ų\‡ģcô´Ū° :ŊČđÄMjŗ7Ģu e_ ÍKæę:*›Z]•J‹›}b5z,ķC•˧īÄ&ũitųE†×ÍNæŒĒÚą ëˆ?Ũ…ŧûœ°ãŦCgt…—îŧčNt†ŸĖĻĀē€˙L€7ē„1‰” ūT ”Ņ×ÁŖrÚOzxąYŠĮUĖ•ŽãGŠ´ ™éS<Ÿ>ÅoAPÆ.Ŧ?š'äf€–H‡ŖOŒÛžtyzvO—žR\<}hŨtæržNPĶeÆmĮîŖR\ŽåמFˆ„\žŋÔ\u‹QĢ÷7džŪJåd™ĶŌÂaŅ1˛w;›ių\ĀļŊP,iŸ7˛7Fök/ ”rd3œËŠ”Á3¨FqÃæßI~YGWbnÂĐO”ĨĒ5ëÚŲĖwXw¸XG”îĩ8žî/.3ē<ÛÄĐîQŊ`L „ĩÔ‡kĘKųČ\S÷są—IÎm]‹ÃyĻ/(ËįiS\á)hfވ>HĄM˙ą’Šeūü‘B&–:šŌŽ@Uëč´ųDoŨņΗ đõ—_á›˙~…×Ļ?Œ§eõgwCX{2r, ËA|ąBЧ×Q>‘a ģÔ$âčéc8räˆų8j]LŸâĨŌ§¸VKfm˛ŗKŅ6ō:´oCu+¸ˆũ§K KŨŽmLä.ūˆé guDˆļúrŽËDîˆ~Cú#ÂĪy ąî`&´ĨdzĐKDîé„p¯j+ģČå‘SÚ^ũ{Ą›ŲįvãDÉjÖų"Öī "‡/bzõB˜ŸŲņûąņašģ‰Čõž!’ ‘An(Î8ŊÜî+•é⌰6î÷híųT“ŽnjË7Žvs“ļL‰Ũ{IxgĮaÃÎDuš`[^ʡŽįR^˛čÅyj´‹îĪSgĀ•FĀļšŲÄRuÄænqk°Åe@ÕīI´§EÜ-MĸaŊBņ÷VŦŪ‡‡GŖđėj˚÷ÚŨų^ėT]Ā€N}ā°} –ėé Ũ\ąķģŲ8GŠnĸņā.Ŋ Xŗžz‚†ÜiswÜRͧ:d* 3Š[Ü÷V â5ŒÛ"é6d¤ .5“îŊ0ōöQ$šÎ^Xšô*øÚĘ%6RÜq#¤¸‘!mĄū}%2NŖ´‡)r`÷[1ĸŗ‡UJ 9Á”v8ĨmKĄ1á Ŧ˙ë,ʋĢēpå´gˇüF Š#ēŒ…nôEƒo”/߆*§<ĒڇŪĢÉP×ROÄg”Đö|ŽčÔ7 §ōáŪí}pvK|­ų¨{­Ö•mkÉ×ôŨUŖBäŅiĖmčÉíGį(TüĩéiįQhŠ)×ÅX”‚|›ōRžæĶÖsIÆĨņ 2ׇ°Âá‹lzžų”ĻsG“âW p…°äžfŅJq”JuÀqũđס‡qãđŽe› Ŗî&ãŸ÷ãŖ…bßBS°Oŋ⟎„@•ĖqĒ ¨mú?…į æãĶ˙ÍÆnZ0KÕŽ3pūí|áuîo‘Ã.kŗ™2TsŽlĮDÎÎEÚ]Ä /GF‘ĨŖ‹ąÝžĘ­'QĄŠÜ¯čâ)™f¤l¨Î!>ŽČĖÖKĻ6ģ…Ô$rČi\ĒPRFãĻ;ŖĨl ãødV‹S—㔊 ͝’6Æ&“~yA!-1)Q/Õ ëj;ļŪđŗ—ĸŽ|åH•g.ÅĻēV"ØĶijÚQ’ Ē.—Î×ÖsQ#•?ęÄËž~ō—#=?zf%´q¸p–‚@ķ’šcgŧûßo+ëÎ$üM˙Ę[¸GŪGæ”ûČÃd?î8úI|=¸y4ŒĀÅÍînfqÃÆQŧqR<ân”ĻGĻÛH|4˙q8ĶL¯ģˆ÷Ÿ=__SĮ\HŸÛĢ iā•-sˆí,õ02ƒYô *ˆđ\\H`RމËĖŽj›6soøPܜRÎDveDđtī(“˜ėo}ö†'ĨÍļ´Xč/`ˆ8øö‰VŅyl¯߆0jV@ŨA{42lGfą:ô‰níÛPuÎ`ųŠcßâÕ1•Ô•÷Xēķö]:ßĘŒz>RČDäãeōĖWkĄ°a$ŧtžļž‹‹xÕLW[?GĨØâB ЌØø;4cévĨpķ†@@‘ÛJSt‹šƒ9?mÁų¸ƒøáĩ˙P×i(z†Xę‚ļÖ߯!瘁4ʨ Ō¤ YØēįÔúBÄnŲ+ēąÕHĩЊË:püÁ]Ȥ~ΐÎDŊuģp*GAãvQ9å器÷÷a"÷R¨ÖŌ֙¯udéŪ€SÛw!›vâŽĶ‹Ô'âŌŠõĪ—!Uŧdņ8ģøũąjÁĘ9eŠûąö¯ĩ8*›´x.ÁJlؓĀŅ…4 ęU“äß,™ēwš‚™{`Ņo+đŲ-”ícÚĖûîĒĄN¯ÆĄļŅ,ĻR,)ŲōÚęHlÛ~¤į­ÃÅä#ø+ŲB6ŖdÚqÜSÜŸŖOFh ÅtWŗ )ũ´í}3:ŦÂyJˇŌ\ŽOûūd§‘üŲĢ*­_įąč™ŊąiĮ°RŌēÉâāŽŅ#;Ā5+§ļ^ĀékqšR9¸‘éĘąņÛp"z üO"'íļ ÄÍ=j·K´tQ]#jĪ7æ>INËø|í¨NŖ•+$o–oø `(/°a¨Ē.uå{îē€jqĨŒčGz.uāe2ĄU•a™Ž¯ĩĨų(+pČœ›Jm)ÍGPÊH„H1h.A9 lõŒp¸p†#  L6ËČ+??ŋ2ÕūŗIčŲ%Ļōžš.X|is Ú̓wôāO}NG‡:br­V‡ķįâpĮMŖjIKfŽ‚rŠiōé›[KņË]^ŌNC– xĄ­ƒZz5čē(+Ĩz%|ƒĢŲ’meZžŸFĻ&ÚfĘŅÁ55k[idŋōĸ|"-\ũáY“ähŌYW”‰ėR\Ü}āëeaĸ‰\éé…Pēø MWd#Ī'Å܇6ž.ŌĀâËŲמOĩâčĻŽ|­ãōŊųYYDšî„ƒŲŪb+ZŊķ­Ę¤>xUĨWēĐh4˜ō[ŪáO2cú¸:HũhĩĨúsũßčGę`TŅ2(**šĮB&VéPКč|ČŽUhæreŽĻŗW`0iąĪšúļCHeįŸ}iäXŽ^žpĩŗ •W[یë†āāĒgĐ$!ßĘxJxU#~˛Ļך,•|Ž+_9Žå™?ÂíŌŽžųVåXŧĒR‰+Ā•G ÅÛ˝ŖÆėü´ŗHM ؍€ sģĄ@ËE@yË}6B2€@@ `7:4ą¨´Ėî‚+ĸõ8sOį§CKcĖMcÍu(WË3ņj)•ÆĒ+hĄ/ĩYKá-Ž3 s MŽáĢZ3dÎ*)+§1æ4Î\ĨŖņæ:Ķķægė_ĢpM`Mæō¤!&ķĘŖ€§Ģ×îxāŊƒtÔG„†"ā@DÎ\c9ŲĮVžĄūۈlëK$N̞šKMf I#”„„Ŗ’–ÂĸiŒĨš*œ@@ h$˛hfĩ7ÍüT9($Ž‘l¤ėĨl™ ÜRĒ苇#Ä 0>ŨCKÕĶ码"Ā\ōíÁrôn§‚)ŒĖ5Ė9íÕfŪØÂ5g~Jj1Œã‚e'ËņúÆВ# !Ā99s‹“’ö[ Bo פdž_R†9‹ūÆÆŊ§˜W† ZĢ\[í l(BÛŧX¨ ĶŠƒ˛†Ų8T~ĄČņîŒķE*Ņ!ĐŽî‹?xcSÔ_ʓĄUŅ7 ˇš ZKũ>Ú­HŖ3Bg0Â`dá@ũ`^a9›V˜[˜ČH-W˛Íŧ~YŲģIÉüƒeģņĶúã(++†ƒJ 7"LwZ/ÛQ_Īô=HII€–VTĶ(÷ÂrhJ8;å ]p*ēF ĀC$fįbÖĸõČ/+Ã'SyCŠÆwüÅã@đ2ؒɅHœ6ž§–Dæŧ*cS@ßøõ9 -ĩÂ|ЏŠžúY#g"'n§ĐÆ—ŗIÉ|ŲÁ4͞Špō†zd•ēBx]Ø UI"2S‰Č‰@uš Œēq  ‰Čœa•&yˆÄ}&q:ÄŖešy.T8€@ õ"@Üaĩb"uŽ(sxS9įŨ¤dŽtvCh[ĶZŠŽJŒ'ĸ‡§ŗŖ”ÎÖĪÚ9Īc{˛cŸũŦ^€[}õ•ŅĘŠÛ¸¯ō^\Öˆ@“j掎îtuB["ô"iRއ—;™?4’v^V^&m$Á‹\éząĸD$ŲĘ˙ˇf4j-îģe(†\ߊwâđ ėŊg V#Ę/ÁŅ?–bM>ë˙DūTģ‹+_ÆwĒņØ´l¤­éĨĶéČT*ŌėΚQZĪNsww‡7ũN ¸ēhR2Ī//G;?w„y#=9Ö;QŅd!Z’Vĩb͜\Z„ÆÕˋlæ:LŊk8œOÁÆ=‡Ü.˙[ą;†ĀÅÛ?ūø#ūoę4h;á†)ņÅŅØđÅëXg E‡P%â“/â\Znšû߈Ųlī'9oj*- oŖĀVãÅõĖĘĘBaaĄ ôVķTEEŽUš”Ės‰(ĸÚv@x[œņr…Æ–WĐē'ž^Đh Äå4ş”j2§ŒëIūŪØuz;ŧh<:EdgĪČH…ĄH4šâ_›ķīų$Ö¯|9Ī•´Lj*wŌvÃã˙ū¸ļ¤5ü™Ė¯"įŠķ&´āYŠ ķo‚đ\]4ŠÍ<Øß‡:4i}–Ā6đpt‚ƒAƒ’Ü,¸Táēļîu3ĸ_˜& ‰ĄÅôČõÂõ#Į•FœĢĘōáįí 'Wdf—"¯lįu9'øųųI6÷ēĸ]*ŒM+Lr׊ãēļvsŌĩō,E=¯mšT3wvt@T¸?ü0vx?DEĄWĀ]¯AdD8J JĐļC–m;†Íą Đ[†ĸ‚ø89Ōöm´$-¯ũKëē””’VNkŧ'ļhR2W9*āæę˛z Sd;ôéŪÉ táœČ¤Ō-Ļ–l܎×îĻÉCÔ1ĒÎĻa.(4:ÁÕÕíÜŨhũs#u’–ŖM@€í_€@@ āŅŪMįî~āaœO˄礄 'Jdf¤AKĸūA!p FJq)ŽßK&'šH1hNî -+Ķ#'¯.Î4;ôT6ė9‹˙{ėąĻTä,ŽršT3aæ?ņķat.…“‡/Édĸ‚:Ĩ3mjjĀÖũĮO+Û:ĶČ M4*”ǧÜi{9Ús÷ŠœÎ6âîɏāåŸģĸPĢūĀ ÷ŧWM#ÚãÅy´&Ėāđjū}#—ũÚÂ=¸ŗĶ•ļį—ā¯ųß"žHŗvÎ7ãŠ;¯ŗö÷@3 ФdÎō?øÄôZĢŅī`Öˇĩˇ°€ I…{Œcn Ą<Œ§Ī@ËŅ•k:g.›gTĩįæåįRÚ%ŠÖŅ‘Ļg)ŨāĻŌ͘uį A pm"ĐädŪÚ`đÄ'x˙nĶ:1-zÎ9Žô< ĸ4›0ķÅuč8.ÛžLÆG;ŪCĘâ÷đĪ/ūĻĩ 0dŌ4ŧúėmđËũ3ßūmbÚSø¯8ę×3Ūü‚´û Ē‹[ŋÌ~…dŗŲw܋˜9s"dŊŨˇŗ°zãJĢ48¸â3üköRäR9ˇ<û/ŧōČ0x kįÎÁŋn’ō<é5*˙N7˜o=0zōTŒĻ\Ī­úĢĪĢ=öQÜVųՅ­ËIÆZ˛œ[›Ž¸iÂxD¸Ģqx坨~Ą>ÅüĐÆM‡ĸ2wŒūĮDD+SąiųZœČ°HsĨq[iK Ё€4ŠÍ܎ō¯ē({gŋ€ŠĶĻaęÔģ$"×<‡ŪíXe.Æž„ŨXøÅb¨û´GÖÚi‘ßzîmœ#åģčĐÜûâ|¤ö™BÄ; ‡V}ŒI˙2‘1ĩc&nų÷ „Jif#‘Ōįœ†'‰ČÎxīŋÚ래˙ûņRˇ~"ų-¯~ofŨGåĪŒUɍЎ<žH§7}9pæįV-“ˆÜ;ĸzGy ,ûžđ‡ę’ZVWOĪÕŅĸ^:Ú :…ļ¨k,7ā‘×0s|[)ģÂAŽ5}9~?”‰ŨM%DøûKą+ßÁësKĻ’ģĮĮâ=_U†Ō×pÅéŲÔ ŗmÜ?8ÎLĐÔĖÎŲāl6?ČY°ĮŸĶ8GâÕWg҈q#>{s•´4ÂŽE‹°‹°$aÖÎė%åÕT?'ū^Ķ‰Z8ûļ§F/9§RŠC[‹ŧ"ÉË8˜Jļ\7-røč[ą ņŲųČĪČ@>7EîxvrßĻŨqŧŠ@ų ˆs–cf>TŽ*ZūÄQšČČK 8đáā ŧļ˛k42oīëB‹j)áėė,$ĐÔgk2g"įŞĒĻķkQFvåöũø ~Õt…ĸ";˙ˇ”˛uĀđn&{7—Ą12ķpqĮŌ˛Cqīããu¤z+›ē\ģŽ=á`\OmĮsŨ1yęg0ôû[_1§’ō–)œüÎč0ČŠ‹9jß}` –|4NšK¤Č´o‡ŲUŅņ€gáų„1gú]˜CĄ˙ øų‰Ąˆ*~=‡7/ĨaķÍėg5 ‘+ĨUęšôíŪņŗûĮ/ˆĨ!¨ėÔŲ)úĮ\ļģ/$ãÔ7xŅ×Eĩ,lwī:°ūqį6/ƒÉ ĨD׎oY%Ä@ "  ÍļJEĒgķķķ+Sđę{íÛˇ¯ŧoŽ {4ķsįÎaäȑv‰”ššŠČČF˛×VŖXãOĪ*ōĩGˆââbIƒđô´'ē§‚Ō°Nėi•†ķbįDūĩI)^cüčJ‹¯Wĸ -w,ģ‹;˙žÚMĒĶ ŒėbšŸ–#žĖ7?õ4Ž“ĖF4ē…Ė,$ĨW¯Ze•ķg@kF`˖-ˆŽŽžršyk÷˛ęæėI›jÔ?%˛ũ4nĘßš˛ļ&÷úKS˙*w/´ąJ눌ƒņ´ æ2œÜƁ xE49ß̈ĭSąŋp€=“¤=(‰8 FĀ%ę<ûx_\HĖ !‰z8ĶøķNĄõm˛,†Č@ ĐjdŪjmË̘Ę=ēļ<Á„DV€@kí­lFTA ؏€ sûą1@‹Eāš1ŗđ(ájG@āS;6"D p5 pEČ|ŅöD$å˜Ļz[‚ā†IÃ",Ŋíē҆6šD-'#MËyB#Wo Ž™3‘?7>†6r6Īí&ąÕZ=>['-ũë]#‘@ \ƒ\2gœ™¸mšÚüÃÛ¸cŌPy1X[)…Ÿ@@ hEÆũzEŅ|ō*W˜r §˛ Qø ēO´÷ŽĸđĒĢĒø-ō*)ģ´EĘ%„ĀYšĩž_XˆōņøÄAĄ§ė^‚?äH‹kņ['ŽîʼnĪ`p°ÉÂ!Fŗ4öSų "pë=04‚Ė=Œ5ĮiŲ]ļĻUU•a¸į™įđØøŽT‚ĮhVĩėŽÍ\øRįŒ#gđūæ2$ęuŌ&҃ŖÜ1¸é—ņž”X"\ ؍€-o=z,âø 9I9(ņŒ/ä=ôÁģBŦí5cēâĸÅV­ŠĖ…đÔÖ øyāƒNH=‘‰OO Â ĮívÃ(" –€€+UFčŠs|>rBōöÅØ{1 •;ĸŨŒņĒú[•™EĄōĀ ŊŊ0ëŽ(´oãöáNŌæ:ũe/ Ųž¨A ¸&pCû  ōiETļ‹k¯F§~ƒŅšįwũŽ˙Ž4-Íđ´*͜뛗R‚§ÅÂ(™Yt´M„p€@ājD@ƒŦBžžDԙT'ŒúŋGĐŨSAûFĐ.\?|A¤'i‹Æhi•ÕVEæįļfā§l#^Ž^íÜĄĐ]ē_ņ^˜­ę„ę#œ@@ Đú(Fa1m ×> RëĻ͆ĖN_w˛8˜ļ+üZËi4Ŧ‡;ĀĮvû)ËÁĒĨų("]oÛ œ@@ ¸:(וãÄĻ H'q;t FdëčJƒĢw!§´é§ÖaGē\ŖēVî}ĐĒČŧËõ^§á:o.=‡~JĮ:Ŗ ē:ŅNđĮhøæ4gÂÔŠ/áXÍU ,ié‰xbęk8ŨBÚ°ĻŦkƒÁZ Ģ˙X}‰eč|3Ævô€cČ0LО”ƒX¸āk,ŲpĘ6=0y|tĨô•J{ĨO3\ŧ|Wį&)E…Ī^(BZr ™—ááĒ‚V]„Ø“-¯Ô4ĢK]‰Ļ@)Ęĩ3oIz…]S×õ WO/ht†Ũp"ģöGxÛĒ \BûNĀôž%Č,PÃÁÉíɔlQō!s‹ō›āԁÁÎĐjĩŌĨ+Ü\]š œúg™´í;Ėųu?ÔĒtĘŖ <Ą2?‚ŗ1oū2¤–+āڏO ]ŧņÍksa¸ņ%<5Ę´GgÚļyøp{ŧķÆ$lĻŠ’KÎ;i÷˙0į§]PSĘŋžyų)ʛ>Úâ—â? sFåė9I\#ņČĖ™fz-lËdJ÷ÖŌrô KÅúíxæ“҃æ7XēēęZ›<œž2]3O1ŪŲØ8˙+,?ĘÃŗ€ČabÆäëĢMs–ď@ • „`?ŧåėā`_ę­iTŠéĶJĀhiÕ(80ŗˆČ=zNÄc“¯CrŠÅ8›œÍxmîräGۊé3BHî~|ú¯¯‘‹Dz•âČĘí(—*TŠMœ€Ú;^ĩĻŠ^ķR2įĖ""o3h ^ų˙]~Ÿžü˛(šļŦ))Gą'Ŗ fžü$ē+.ā‡÷æ"ŋęȟĶå^Ü-yŋĮ!ĊČëĒk]ō”ž Y #–uÆ´{ā’rsڇ,j—Õāūi¯`ÚŊŋũ|š›žž„*h…šyeŨZÔE|ėIŊFcÖĶ#%šē¸•`ÆüŗŌõąÕ+ĄUtĮ›Ķ'€7U‹™™ŠgßûĮ’Á7õÅōų;qŦ`*c_™ŖĮ ÆąÕ/֚f€EÍOlŪ-•ûú#C%ßįßĒĀ3/ũ‚ÍgÕO*F„áĨYĄ#}!Äŧ6Īŧļ›cÕč[ģLĻüUxđ?â 4ãN䘚÷ (ä×Q×ēä‰ŲA˛ēŽĀL’Õ•¤ņđy|}° YŠÜįáN‡ē oųīGžˇEc(ÕLüŽm™7Ëķ/ʼn“¸÷ėVUšĘr-4G(ŒĮņÆÔŠUátu1W‘ŊÆ BqģcķĐAˇU"đ‘‘J¤î¨=ÍķSUiĄę/pīgQŽ›—D”…lP§xFĮp„Ȇ7oo)Œtv*ũRų;ÂËl?ûû‡øæ¨ItE×G0øĸíēÖ%OiûG$Y{™eÚ ~ o Ļ|ËüŅõČXōųŋą„nŲüōČS–M–Šlņ+¸–dŪ,OŸfĸ:Q^Ä%MΑîe§Ķ–Á¨€Ų_ū{WßT•Ŋŋ´Mēo´”˛´”–Ō˛ ed@…qDqQ˙".¸o3.ãↂŖ0(‹č¨8˛). ˛uT(ĘÖZZ tOŌäĪKnķ’˜BÚ$íšŋßëŊī.įžûŨô{įwß}ˇ#´Â­!?n?†n=ÉgÖÃ/ŌâŸkW`…ņ(bCœČÍsÕfE˛ q$%š°MÕ/ •ŠË&:T\LgkŒR %&'"wŠ“UžRUüɚü^ŗ4„6L‹ū§ÃąēŌ'*4-H×âb)g˛?ƇŲI˜0<W?ōχ;ÅmÂ'‹×cÁŠū˜sw÷Úēœ`š;ė3o”_€—Œh ķŪÅølŸXåoČÒÅ?‰ž-ĻmR—TAĒ;ąaĪ)„ ˙ķÖ¯āŖŸá´•īû"vOۅŸK´5ĸŖĸņĩĄJFazgh¯ôģú+\ÖĪû@pxgЅB'îvaî§9–˛ ĸĮāŽ!pKžŧiÃ„Ū–C+.NĮĒqĄOf$ē‘Žû— ĐĩâW,˜ˇģ‹5Ø÷ŲLŧøĖ"†ĩDˇ—ĄSģXĄq`ę Ā–y8î$yø4 Ũō4VĪžÕĸm\ŒđW W†˜–§alö#XųÖcøÚĒÂĐģUüØtĒIũē‡}Ÿ…?šOŧĨ‚Ģ6–ßu ã›úũĶļGą\ôK. ądWNģ mEJîŋoí,L]ĢĸßøG‘N :Šå[ZÕũëjŦŽôĐõŠė§ju5ĨážÉũŅŊ˛?ŗ\¸Ąč(\CâÉÂ-wvŠÛ)Ÿ1͍Y„ķÅāôiąœÍ ‘’’"O-&õé0™LĘQSSŖŅXģ4‘–(戍ŪGŽŲh:šę¨¤¨mâŖ… nô%E8%ÜŅņņĩ~cģ*įœēÛĻ\ô{J<ŖŒOJŦ•]ž=÷- Į›s&Ї˜EĐÅÅ#JíĘŊš+˙ÅD†Ģą:ŌGʨ8Tˆ‡u12 ā(aŠÄÄxqäĀ4]ÖŦYƒôôthĩâ^×z‰wų•e‰´4‘>PA‡ l™K$)ŽŽOtړ.:ÎK7sˇM¸č—ÖƒÔ záŪŅ(;ãA: îĘwÔÖÕXëc‘&p8÷R§EboLīgÎc&ķfü;k7#¯Ŧĩԛ1o­ęŅĐŲPõCĐĒ*ڕ„#Ā0ūqVeeĨ˛˜CrYŖ=õÅÕ,4x:BBhM5F€`üâŦPņ"\ÉBņ‘9ģYücnYKF€`\"Ādî.dFĀ?`2÷yb-F€p‰“šKx¸`˙@€ÉÜ?æ‰ĩdFĀ%[šč˛(Tī#ãę° Œ#ĀxfCæ×ŋmųĒGŅcaŒ#Ā43úÔ_(ģYꏷ`FĀįđŠežtsŽûļhr|Æ iīs ąBŒ#Āø:^!s"ōéŖ:!DX‹O•ĄoŦÚŠÕųĩ8Á0Œ#❐9iCÄí(8ËOnŽqƒ“5á‰@yš ¨ÃÎHmķé͍tĸqęNęøIv)ö4ļ¯ĐļÁí3ŅŖU°ĪŒĄo§h¤øŒ6Ŧ#Āø"áႚŸÛB5~?m‚&*:[fTō™Gĸm Ā|\5žš,û¸­ ĒĖ ON’¤YŧY\=Ü &´Â*>ztēĈØh#^|!ßČģ(7嘅íĩû:#pß~L_]ĸ´šrS\€r1ûZq9×ÍЈ[˛{ã‰oĪBÛŠæ\´ˆ@čĢ*đŨæŧø“íúîHĻ›ęp5F€hĄA)Î ŲQŠüŗ8]ǁ&Ĩ%œ™ĻMČ223‚UxĶqčĢOá‡/Obuq tQžûlųÄ!m­‡…ÔŨ™ËĀVQ ‘įÆã›Nc[n rëIä#ĻāåÉč!H9"ÄvÖ@€pņ‰gžž­V[{‰÷HÅîb%š8čwŖūí°ĪÜęMÄåđė/āũtÆD.1iĖXmĄķCŅÆDžųôĨž×n>Ŗæ‘2^@@MčėC÷Â4ņ.™Ė›øķđ| {BīŌ:TYũ"Ö4ú–ĸŦß!ĀdîwSÆ ûōQųĘ{õęŠG­<׊ĒǧŸ~*öÎŅáÉ{ĀdÔ+ëĶeëč{°ĪÜ÷æ„5j"~&O{/ĢV~¨Xį+ŋXƒë/ë˙ė:‰@]¨˛/Ql”Ųņ›Nˆŗ*++•e֒Ëmiĸ/Ž3§ÁĶâšũĖũæ×Šz ąö7 ųEgqÕËÛú -V~—#Ŧķ›ņŌßÚO˙…Šŋϐš&@ėĮkÍŊ4WžÛ-qVhhhísZoūGdÎnߝOÖĖßP^âĀÉR#Æ<÷BcZ+î”jM8Öí<ŠŠ“nB׎]ņ˞Ũč—ŲZyĩŸöq‘w—žûÛ4g}=æfiÎ ō؉€Fė].>ÛG~pņé/qN–zĨÁ„ĩģN`ĮŽ8[QšŸíßxÛĶ’D_]–(.Nö+lØˇ/gÚ÷b&sߛÖȟ.“"rAāâNtq^mĐá/˛ņÆōÍJž˛Š%˛ĨØWąmŽ7ßĖuJĐĘEFõ`VEîNÛøķÜųšî͆Ė?ūŋ ?Ÿ*VßWP[¯2MûáŅ9íT]]ōōråĄå“O4<<\YNûløB-cŌÉQÚQž/čߔtØēukŊ‡ĶlČ<6ÖŊ¯õÔAnĐŦä­Ž?ސ8åÉC’ģúa<­X CMŽŪSö¯Ž)íčŧmÛļuōI_YĪēsŸ6š ™Û†Ė)FĀ3¨ œ$lj;%åä9Čú˛ĀßHđđaá˛ú÷IwJĢc9.ŽŊƒ“šwpį^ũIję˜Ōtî(øyۏÆ%ĮKej"§|ŸũxũíœÉÜßfŒõõ$ąQŦ>ČGŪKŽYŽOMč2cī ā2_ē9GŠÎ}[4Y|zܐöŪA‚{eÜD@Mhj—–Ģ3ËÜMņ>[ÆE¸úĄ­´Č ™öŲA4aÅŧBæDäĶGuBˆVŧũf U†ŧąj?(VįËrŽ_B@’8é$ĶŲŲ[æÆ˛ŖX6÷ |/>Õ7áąÉÜ^뙡ĢQ…`„Ôëŋ؈3E%âƒ:ÄÄGâœĻ.dŌ¸h|Dä4V Ō2—i%“˙xsæ˛ą´ âvœå'ˇ Į¸ÁɎš¸ČĢĀĢrđCuFŽtQ‹7$&ĢK"—V+Å´—†:|ķĖu¸åëIøä™L˜ŠˇcHętĖ:ŗ ũ"-„¨ŽëNÚP° L˜…9áÍ˙~‹iũíÍ*đJÖP<˛ĢŽ”ų;žÅYĸNŲĖ<¯f[Ë{Ė@Yö Kɝ˴ÉĸqŅød¤ŽļÆ õšŦËqũ Q´YŊ¯ ŽC‡´V¨*8ŒâjË*#Â^Ŗ BLR*âƒ-ŋ#¯‘yũ†;ĪÕ?˜pø„ Ų?\i܂ $SLä&Ī)MÖk]ËŧŋmŪz Æd‘E^wˇ/Cëķ#ręß ÁČG€iË4hęų:ÜŧōcüÅ ŪS‚Ļ›ŅeØl´ŽÖ‰VxkôxŦøķl”íŧDx5 *l}'ČšLęҤeNgŌ"§1;ršX›ptžlŲ˛ššš ļÉ¸mÚ|÷Ų*Šą}û“æ íđ)¸ĄGŽ~˙ŅšwYįŲˇ6 @¸Í“ãŖ:˛ZūŽšúP[æģÍÄ};āIˇâ] x â;ôÁ;KĶPõĶR$ųßûĩÖāėŪ5čßķWŦ:û:†:'û°äîøKr5tļG.mRÛŖ’mÄ'¯ĪÆUo,Á¨4-ôG6áŪ Ŋąķ߃Ą+9‹’ā0$&Ú>rî\Ļ­—ŧPIë›>0ĖÁķ\tQ:´4āŋ{NB× ĄâbŦ7j63…aąĖã:FÆ\|ŗŊ°é‘yŅ/ûņėę2äÕ‘€ŗ€úų=?1,ąi @¸:H‹œō$™Û,s ÉõēņV<ū_ dæËÂʟ14ũ_¨4NFæ kąęąŲ¸$ŠåĀß{? kWģ$r[˙â­RĄN˜-ŖNę÷ū‰ą Æá˜Ą“’_]Z"n)v +Ē/ēŠN÷ oÉŊķâ•)=j­sĀĩL96"r:¤›…päNŲŸ×QŒOÜB ==æŧEŨ(\qMVīGĄ8ëÖŗ/2„ˇŲâfĄy0Ŗlßnœe~ãfqĶ‘}˜˛ļšČ`<Ú' [ž/Æ/ĸĄô(ē#ƒë0î ‰œb"ryNi‹õjĩXC␠v’lŲ11b+æX´¨}{?W=ˇ3˙;>ĀĀgįâũaqJ÷9˙YŒ‡—G+rh‹pļ ã^zŖ2ŨØÎš*ˇ]ˆ™ŋF[ÕÖ*$gūw_ãŽKĸ”ģ€čî/aÂõKŅ/ļîEŠę9 4.9N9n"q™§&tGí9Ī}Ē˙€­y5H6 Š:ąôĩ¤B\jŨŸŋ…ŸÅoL#vâėØ÷r\=8 GsŽ‹’āĻEæģˇWˆ‹´Xp['„ čÖ1ŋĪ)Ā>÷1tĢæüųķ•zS§NuĢ~ÃW:‹ßväŖ]ī D4|gÍēI\y.ÉMZ螜b‹ílíšU§,ĩSZEÕ6Kė=ΧØ>ĒbŌkĐ–|ßļ ũŨļKęÛWīÃęÁaų ›<Ĩ$āR é-’fDĨļAWÍd*dn3wœÉ¤öōBEVĄÚ*Wd“Të “ēDäüãīˇäC›z).īd™›ōĸļ.C°°Ä^5wL_ëę7Ĩ*Yđ<ƒ€š¸eZƞéÁw¤Čqɘ4S§}GĶæŠIzęš ”6Ĩeî)"hacŅē+‚ŅšÔ×č:tR_{WYŨ$ŖÆ@āŌ™˙Qo|>qú-ŊS¸Pf*ƒ5i.ÃÂGÅë`Á…o+XÆâ_ߎ7īÉÁôąCņ˜’į>\ QKŦD>NMKá_ˇpä1ˆÔԇĮû õø˜Ä}hbŦĒh„ĪÆ0õÔīôiZ i ………HII‘§“útČ%S´–öP\,VW ųĖGžĮļ‰öBmPb3ĻŗbÅApd¤b]‹¯ŖZ˜Ų–Å'Âŋ~ĸz]$âÄĻ–<‹fäU!kœBui)hEb$ÉPrøĪ…" k2Ļßũîč÷&÷-ŅëõØŋŋōgúˆ3Ģ‹ęēÔVĘ ™ˆi­8í¯B1íĐGie§>­Zë,~T´ƒÅJžŦGuĨ &{ĪÍāš5kžž^;4r~o:$Ū˛WļĖ%biĄ;(jāŦ`DÅĢ(¸–ČŠÛ`ġąlĨd¯„$rĨ“¸=<?—ÖŠ$E"gú'ëØąc-‰™KãBžŒĨ"/žRН ƒ$Šģ$dI’Đ)&"'2ĄXļ“dB:Sī#Ādîũ9` üI\’0%šQLgĘē4LYOZäDčž&sWpJ]é--tЉŧĨõ­“ŦŖn/û“yōœãÆE€ÉŧqņæŪš’¸(&˛#R–i:wDâTN$N‘ĸšČ%É64<˛ĩūjŊI/yĐԍKŨÎūŧĄugųŽ`2w—2ĩ‘I2¤LIl”&bŖ2"BŠåAe¨œÜ+AĄSIčT‡Î+î(–¤LēĶ9ŤŖÔ•Ō’ā)Ļ|:d{)KɰʔiŽ&ķÆÃš{jĸHb“N„'‰™ˆŽ"mĒGąúmŧÔO´Œ%iÛšĖ—õŧĨ7÷{.LæįbÂ9Œ€KԖ¨$hĘŖ´:¨‰’\úɉĀ%ĄKŌWˇkė´Ôô—i5iSZ’ÄÕc“iŠ9x&sīaĪ=û!DXj–DfOä44Y&‰›ęHK\ē”%ãÆ„D’/ŒČĨΒ´)–Ä.cY&ëĒu–2Õyœn˜ĖgîĨ‰! I‹HXĻ)&ĸ“ÎéP7;ĢYŸō+HŠ?ЧŒi ”VĮ’ĀežŦĢnßXēs?Žđ™'$$ ĸĸÂq/ ˜+˙)¤õCˇ˛ô ‰ų˛FU•í‹- ¨ ‹n&‘IâĨ´ Dtę|IˆôÛ¤2ŠÉē•ŋYY—ÚĢĶR^CĮjŨ)-ęW’ļŗXÖĩ—ŅĐ:7ųÄY•••u¸L>ŋ 9‘ķĸÆßcdî‹o€Ōāé Qī Ü\~<ΆF€ū‘ˆ„Õ˙PÔ'ũŖŠÉYֺΧ:ęzętcč.û ũÔcį“ÎÔäĄ.§2u[:įpág…††ō %K`\#@FäĢŽe ĘS[ᲕKoLâ–zũQLzʃęĘ4Å$ąË|ʓe2Ļ<ŪAĀc–šwÔwŋWõ>2îˇâšŒ€k$)ģŠíËHĸŖ<×=5\Š$bSO2Mą:m_Ļ>§4ī!ĐlČ<6ļî—;ŧ9÷ÜԐÄLã’iĢķÔiu9åûB°'mŌIæŠĶŽō|A˙æŽCŗ!ķæ>Ņ<ū†C@’´L;ëÍžÜHŨ^'ĩîę2giu}N{&sīaĪ=71$ŲŲ“:å;#mŲÆĄp¤›Ŗ<_ÔŊ9ęÄdŪgĮÜ Øž=š7hįn?‹gqD€É܃`˛(FĀLˆŽPáą+6áOã¯E‡0įĘúĪüČÉrįŖ°–”žŠÂ–R=*j}Yö”UŖÄô‡Í¸#Ā0>‹€ąô8JJ ÔčZE¯YæŽÕ:ĪŌÛv‘™ą$Ĩ“9į)Ž›1Œ#āmÂ3ŽÆ™ąa ë-’›™ĢP¯>v/|Uņ×§Šrũ1yģ~<Œ´=åDũō=īāūšÁxãíÛꤎŖėõoŊˆō+îĮč ŪUŌ>œĮøÕ'~ħŠ0lâMh[¸Ž;†6IČ;x&ņBZXģž¸ytkø›Ĩž ę…‹e_Ĩ•ÂÍR¤žÍ}¨~.æ-š‹<Ŋk•ĖÂĩä¤ —ŋŽ•{ãTĨÁĩ`.e¯"`,-BYåI” 7‹Q_‚ŠęĶČÍ=‰ĖAÃŅ;-Įļaũū(ˇ˙˙Ŋ:˜ķę\år9‘ëũSčŽĀË#):[öžBS1éÁ1 ÉBÁŋ}3sWėi•ŋ>øú$Āë÷Ī9ŧu˙˸oæ#ˆØö^ũč'Ĩ]‘īēw2ÚR s1V,x›ˇ†ņ¸éG1,#ŌtåøböKøž¤É^ĮŒ™ŗ?F@|Є‚ÚXë6j2.ī¨{%"˙p4ˇ¨éZæļŠ+ĮĄ“ļ3oĨ gqėX6ļtÁƒÜ‰îš\ŧ˙üln0ķÁņš ō6ÃūŠ'EYí¯x÷ÕĨ0ĸ5†_•%&1ũĮ G‹ƒ‹đ‚ ō–nÆĶÆ"D\‘g/Ú*†$&šØŧ?“Ūq…Xöæ T:l8nyeæĪšė\qg2>Ž@0ÚĩĶYu Cp 5ÂjoæY8â#]?8hœ™Ķ ‹9 Ŋ0R{áž'n‚Vđ†˙UA—…n˜‚{oŧIIĐ))â|¨@8ē ¨\‹~ƒûāÄw?Â:NŒŒn—ã[{ ĢŧGInûÛ4 čŲwÜÚã>äˆ÷˛Ę…KeĪž=Ęq¨Č1Ŋ7ÎøšF€đ äoŠ+‰lwĩå^ˇ´ %'i}b4fm2ÚĘ7ŅŅև•‚ŠĩZŲļ+V,°éĒ.‚Á2kCví5#ŧOĪڇœ-Ū…§ ÂŪķ+˚îčni^a N,Nö}˙~ķŗ-ųšŽS1īŪ,Ë ˙e&ƒ€xdÖ´Č\§•L)FĻąŨtÄd$ųĤiŒuˇ0°XÔZėūlļJĀmOŪ…‹“âpâ›įđˇO-ĨRq­ ü¤DļË,œÉūf'aBáĒÔī蒕5ų5ŧfĨ sņÆA­TN0Œ€ī! ļģÕi‹ĻÁAŪ!ķGÆtnŦBŌ:ãß÷×(_´6¤w’ģ 0 bš#šk\~mķ6Ėũ4Œiƒõ >>íx î‚Ōíd}‡ Kô[ąāãc@Há@ĪI"ŽÄoûK1h@{|˛b)6ėģҎaÁŧõ8~;&9éÕHŪ%mÂ|aøNtälF€p@xĮŋāîÎZņqz3;]ƒé™â{ŦôĪMÆkŽšį~EĀšīZ.—^ ûÖÎÂÔĩ!ũÆ?ŠtA´úaW#tÛ'xvęTĨ ĩS[hö˙„gôÜ)Ņh§5ã뷞BÚėāŠė'ą|ö ,5ÍAi¸grāčh,ŧUÚéČ{ã"ítđp#āghÄÆųįũtPũ‘äÂÂB¤¤¤4úđI}:čkčtÔÔX-sa‘“UŽXæ999rdŖëĻî°<{.î[Ž7įL%‹ ‹‹GT˛-GQA%ÂD>YŌfņÔ(Ü"˛ŠJ-9W” Ģ= ņŅLĮjŒ9Í4ÖŦYƒôôtņ?/ܧÖ#((b™b@YæĘ' Õ{å˙?ZiõuPgIENDŽB`‚astropy-astropy-201cddb/docs/environment_variables.rst000066400000000000000000000025361507226315300234630ustar00rootroot00000000000000.. currentmodule:: astropy .. _environment_variables: ********************* Environment variables ********************* .. glossary:: ``XDG_CONFIG_HOME`` This environment variables control where configuration files are read and written. Astropy will look for configuration files in ``$XDG_CONFIG_HOME/astropy``. If not set, or if ``$XDG_CONFIG_HOME/astropy`` does not exist, astropy will default to ``$HOME/.astropy/config``. See :ref:`astropy_config` for how to programmatically set or get the location of the corresponding directory at runtime. ``XDG_CACHE_HOME`` These environment variables control where data files are cached. Astropy will cache files in ``$XDG_CACHE_HOME/astropy``. If not set, or if ``$XDG_CACHE_HOME/astropy`` does not exist, astropy will default to ``$HOME/.astropy/cache``. See :ref:`utils-data` for how to programmatically set or get the location of the corresponding directory at runtime. .. note:: ``XDG_CONFIG_HOME`` and ``XDG_CACHE_HOME`` come from a Linux-centric specification (see `here `_ for more details), but ``astropy`` will use them on any OS as a more general means to know where user-specific configurations should be written. astropy-astropy-201cddb/docs/glossary.rst000066400000000000000000000102541507226315300207260ustar00rootroot00000000000000.. 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. astropy-astropy-201cddb/docs/impact_health.rst000066400000000000000000000064361507226315300216740ustar00rootroot00000000000000################# 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 astropy-astropy-201cddb/docs/importing_astropy.rst000066400000000000000000000047141507226315300226600ustar00rootroot00000000000000************************************** 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. astropy-astropy-201cddb/docs/index.rst000066400000000000000000000102161507226315300201700ustar00rootroot00000000000000.. Astropy documentation index 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. :tocdepth: 3 ################################################# astropy: A Community Python Library for Astronomy ################################################# **Version**: |release| - :ref:`whatsnew-7.1` **Useful links**: :ref:`Installation ` | `Issues & Ideas `__ | `Get Help `__ | `Contribute `__ | `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 `Astropy Project `_, which aims to enable the community to develop a robust ecosystem of `affiliated packages`_ 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 `citation or acknowledgement `_. This also applies to use of software or `affiliated packages`_ 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 :img-top: _static/index-images/getting_started.svg :link: index_getting_started :link-type: doc :text-align: center 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 :img-top: _static/index-images/user_guide.svg :link: index_user_docs :link-type: doc :text-align: center The user guide provides in-depth information on the key concepts of astropy with useful background information and explanation. .. grid-item-card:: Learn Astropy :img-top: _static/index-images/learn.svg :link: https://learn.astropy.org :link-type: url :text-align: center 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 :img-top: _static/index-images/packages.svg :link: https://www.astropy.org/affiliated/ :link-type: url :text-align: center The Astropy Project ecosystem includes numerous `Coordinated `_ and `Affiliated `_ packages. Coordinated packages are maintained by the Project. .. grid-item-card:: Contributor's Guide :img-top: _static/index-images/contributor.svg :link: index_dev :link-type: doc :text-align: center 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 :img-top: _static/index-images/api.svg :link: index_project_details :link-type: doc :text-align: center 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 .. _feedback@astropy.org: mailto:feedback@astropy.org .. _affiliated packages: https://www.astropy.org/affiliated/ astropy-astropy-201cddb/docs/index_dev.rst000066400000000000000000000054741507226315300210400ustar00rootroot00000000000000.. _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 `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 `Contribute to Astropy `_ 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. .. 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 `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%} astropy-astropy-201cddb/docs/index_getting_started.rst000066400000000000000000000006021507226315300234350ustar00rootroot00000000000000.. _getting-started: *************** Getting Started *************** :ref:`whatsnew-7.1` .. toctree:: :maxdepth: 1 install importing_astropy Tutorials Get Help Contribute and Report Problems About the Astropy Project astropy-astropy-201cddb/docs/index_project_details.rst000066400000000000000000000003111507226315300234160ustar00rootroot00000000000000.. _project-details: *************** Project Details *************** .. toctree:: :maxdepth: 1 whatsnew/index changelog lts_policy known_issues credits impact_health license astropy-astropy-201cddb/docs/index_user_docs.rst000066400000000000000000000015031507226315300222350ustar00rootroot00000000000000.. _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 samp/index .. toctree:: :caption: Nuts and bolts :maxdepth: 1 config/index io/registry io/typing logging warnings utils/index environment_variables glossary astropy-astropy-201cddb/docs/install.rst000066400000000000000000000300341507226315300205270ustar00rootroot00000000000000.. _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 python -m pip install "astropy[dev_all]" # All optional and test 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: - |Python| |minimum_python_version| or later - |NumPy| |minimum_numpy_version| or later - |PyERFA| |minimum_pyerfa_version| or later - `PyYAML `_ |minimum_pyyaml_version| or later - |packaging| |minimum_packaging_version| or later ``astropy`` also depends on a number of other packages for optional features. The following are particularly recommended: - |SciPy| |minimum_scipy_version| or later: To power a variety of features in several modules. - |Matplotlib| |minimum_matplotlib_version| or later: To provide plotting functionality that `astropy.visualization` enhances. The further dependencies provide more specific features: - `h5py `_: To read/write :class:`~astropy.table.Table` objects from/to HDF5 files. - `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. - `bleach `_: Used to sanitize text when disabling HTML escaping in the :class:`~astropy.table.Table` HTML writer. - `ipydatagrid `_: Used in :meth:`astropy.table.Table.show_in_notebook` to display the Astropy table in Jupyter notebook for ``backend="ipydatagrid"``. - `xmllint `_: To validate VOTABLE XML files. This is a command line tool installed outside of Python. - `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. - `pytz `_: To specify and convert between timezones. - `jplephem `_: To retrieve JPL ephemeris of Solar System objects. - `setuptools `_: Used for discovery of entry points which are used to insert fitters into `astropy.modeling.fitting`. - `mpmath `_: Used for the 'kraft-burrows-nousek' interval in `~astropy.stats.poisson_conf_interval`. - `asdf-astropy `_ |minimum_asdf_astropy_version| or later: Enables the serialization of various Astropy classes into a portable, hierarchical, human-readable representation. - `bottleneck `_: Improves the performance of sigma-clipping and other functionality that may require computing statistics on arrays with NaN values. - `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). - `pyarrow `_ |minimum_pyarrow_version| or later: To read/write :class:`~astropy.table.Table` objects from/to Parquet files. - |fsspec| |minimum_fsspec_version| or later: Enables access to :ref:`subsets of remote FITS files ` without having to download the entire file. - |s3fs| |minimum_s3fs_version| or later: Enables access to files hosted in AWS S3 cloud storage. 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` - `pytest-xdist `_: Used for distributed testing. - `pytest-mpl `_: Used for testing with Matplotlib figures. - `objgraph `_: Used only in tests to test for reference leaks. - |IPython| |minimum_ipython_version| or later: Used for testing the notebook interface of `~astropy.table.Table`. - `coverage `_: Used for code coverage measurements. - `skyfield `_: Used for testing Solar System coordinates. - `sgp4 `_: Used for testing satellite positions. - `tox `_: Used to automate testing and documentation builds. .. _sourcebuildinstructions: Build from Source ================= {% if is_development %} 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:: git clone https://github.com/astropy/astropy.git cd astropy 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`. {%else%} See the `latest documentation on how to build astropy from source `_. {%endif%} .. _sourcebuildtest: Test Source Code Build ---------------------- {% if is_development %} The easiest way to run the tests in a source checkout of ``astropy`` is to use `tox `_:: tox -e test-alldeps There are also alternative methods of :ref:`running-tests` if you would like more control over the testing process. {%else%} See the `latest documentation on how to run the tests in a source checkout of astropy `_. {%endif%} .. _install_astropy_nightly: Install Pre-built Development Version ===================================== 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. Installing these "nightlies" of ``astropy`` can be achieved by using ``pip``:: python -m pip install --upgrade --extra-index-url https://pypi.anaconda.org/astropy/simple astropy --pre 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). You can test this installation by running the tests as described in the section `Running tests on an installed astropy `_. astropy-astropy-201cddb/docs/io/000077500000000000000000000000001507226315300167365ustar00rootroot00000000000000astropy-astropy-201cddb/docs/io/ascii/000077500000000000000000000000001507226315300200265ustar00rootroot00000000000000astropy-astropy-201cddb/docs/io/ascii/base_classes.rst000066400000000000000000000021731507226315300232120ustar00rootroot00000000000000.. include:: references.txt .. _base_class_elements: Base Class Elements ******************* The key elements in :mod:`astropy.io.ascii` are: * :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 tunable parameters are modularized into manageable groups. In certain places these attributes are actually functions for handling special cases. astropy-astropy-201cddb/docs/io/ascii/ecsv.rst000066400000000000000000000355641507226315300215350ustar00rootroot00000000000000.. _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 ----- 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 read/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 astropy-astropy-201cddb/docs/io/ascii/extension_classes.rst000066400000000000000000000047261507226315300243220ustar00rootroot00000000000000.. include:: references.txt .. _extension_reader_classes: Extension Reader Classes ************************ 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.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.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 astropy-astropy-201cddb/docs/io/ascii/fast_ascii_io.rst000066400000000000000000000175571507226315300233730ustar00rootroot00000000000000.. 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. astropy-astropy-201cddb/docs/io/ascii/fixed_width_gallery.rst000066400000000000000000000371531507226315300246060ustar00rootroot00000000000000.. include:: references.txt .. _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, for example:: Col1 Col2 Col3 Col4 ---- --------- ---- ---- 1.2 "hello" 1 a 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 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, 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 of the options and their interactions is by example. Reading ======= .. EXAMPLE START Reading Fixed-Width Tables Fixed Width ----------- **Nice, typical, fixed-format table:** :: >>> from astropy.io import ascii >>> table = """ ... # comment (with blank line above) ... | Col1 | Col2 | ... | 1.2 | "hello" | ... | 2.4 |'s worlds| ... """ >>> 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 = """ ... # comment (with blank line above) ... | Col1 | Col2 | ... | 1.2 | "hello" | ... | 2.4 |'s worlds| ... """ >>> 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" ... 2.4 sdf's worlds ... """ >>> ascii.read(table, format='fixed_width')
Col1 Col2 float64 str7 ------- ------- 1.2 "hel 2.4 df's wo **Table with double delimiters:** :: >>> table = """ ... || Name || Phone || TCP|| ... | John | 555-1234 |192.168.1.10X| ... | Mary | 555-2134 |192.168.1.12X| ... | Bob | 555-4527 | 192.168.1.9X| ... """ >>> 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 = """ ... Name --Phone- ----TCP----- ... John 555-1234 192.168.1.10 ... Mary 555-2134 192.168.1.12 ... Bob 555-4527 192.168.1.9 ... """ >>> 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 = """ ... | John | 555-1234 |192.168.1.10| ... | Mary | 555-2134 |192.168.1.12| ... | Bob | 555-4527 | 192.168.1.9| ... """ >>> 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| ", ... "| Bob | 555-4527 | 192.168.1.9| "] >>> 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 = """ ... | John | 555-1234 |192.168.1.10| ... | Mary | 555-2134 |192.168.1.12| ... | Bob | 555-4527 | 192.168.1.9| ... """ >>> 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 = """ ... # 5 9 17 18 28 <== Column start / end indexes ... # | | || | <== Column separation positions ... John 555- 1234 192.168.1.10 ... Mary 555- 2134 192.168.1.12 ... Bob 555- 4527 192.168.1.9 ... """ >>> ascii.read(table, format='fixed_width_no_header', ... names=('Name', 'Phone', 'TCP'), ... col_starts=(0, 9, 18), ... col_ends=(5, 17, 28), ... )
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. 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. The two examples below read the same table and produce the same result. :: >>> 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 ... """ >>> 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 -------------------- **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 ... ======= =========== ... 1.2 "hello" ... 2.4 's worlds ... ======= =========== ... """ >>> ascii.read(table, format='fixed_width_two_line', ... header_start=1, position_line=2, data_end=-1)
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:** :: >>> table = """ ... +------+----------+ ... | Col1 | Col2 | ... +------|----------+ ... | 1.2 | "hello" | ... | 2.4 | 's worlds| ... +------+----------+ ... """ >>> ascii.read(table, format='fixed_width_two_line', delimiter='+', ... header_start=1, position_line=0, data_start=3, data_end=-1)
Col1 Col2 float64 str9 ------- --------- 1.2 "hello" 2.4 's worlds Writing ======= .. EXAMPLE START Writing Fixed-Width Tables Fixed Width ----------- **Define input values ``dat`` for all write examples:** :: >>> table = """ ... | Col1 | Col2 | Col3 | Col4 | ... | 1.2 | "hello" | 1 | a | ... | 2.4 | 's worlds | 2 | 2 | ... """ >>> dat = ascii.read(table, format='fixed_width') **Write a table as a normal fixed-width table:** :: >>> 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:** :: >>> 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:** :: >>> 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:** :: >>> 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:** :: >>> 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 | Fixed Width No Header --------------------- **Write a table as a normal fixed-width table:** :: >>> 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:** :: >>> 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:** :: >>> 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:** :: >>> ascii.write(dat, format='fixed_width_no_header', bookend=False, ... delimiter=None) 1.2 "hello" 1 a 2.4 's worlds 2 2 Fixed Width Two Line -------------------- **Write a table as a normal fixed-width table:** :: >>> 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:** :: >>> 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:** :: >>> 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``. astropy-astropy-201cddb/docs/io/ascii/index.rst000066400000000000000000000260071507226315300216740ustar00rootroot00000000000000.. include:: references.txt .. _io-ascii: ********************************* Text Tables (`astropy.io.ascii`) ********************************* `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. Additional information is available in the :ref:`Unified I/O ` and :ref:`Unified I/O Table Data ` pages. 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.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.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 =============== Reading Tables -------------- 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:: >>> from astropy.io import ascii >>> data = ascii.read("sources.dat") # doctest: +SKIP >>> print(data) # doctest: +SKIP obsid redshift X Y object ----- -------- ---- ---- ----------- 3102 0.32 4167 4085 Q1250+568-A 877 0.22 4378 3892 Source 82 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. The return value (``data`` in this case) is a :ref:`Table ` object. By default, |read| will try to :ref:`guess the table format ` by trying most 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``. 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 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:: >>> 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) The ``values.dat`` file will then contain:: x y 1 1 2 4 3 9 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`). 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 ``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 ============ 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 ============= .. toctree:: :maxdepth: 2 ref_api astropy-astropy-201cddb/docs/io/ascii/performance.inc.rst000066400000000000000000000020511507226315300236270ustar00rootroot00000000000000.. 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 astropy-astropy-201cddb/docs/io/ascii/read.rst000066400000000000000000001263201507226315300214770ustar00rootroot00000000000000.. include:: references.txt .. _astropy.io.ascii_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) # 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:: >>> 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 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 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 There are four ways to specify the table to be read: - 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, 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. **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 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 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 ``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 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 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, then include all names. **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_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_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. **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.TableOutputter`, which returns a :class:`~astropy.table.Table` object (see :ref:`Data Tables `). **inputter_cls** : Inputter class This is generally not specified. **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: 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 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 (|). 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 look like a number. Guess Order ----------- The order of guessing is shown by this Python code:: 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(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 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 column or column names that look like a number can still be successfully read. 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 = ascii.read(table, format="no_header", quotechar="'") would only try the four delimiter possibilities, skipping all the conflicting 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:: import astropy.io.ascii data = astropy.io.ascii.read(table) # guessing enabled by default 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 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 or numpy dtype types such as ``np.float64``. The default converters are:: default_converters = [int, float, str] The default converters for each column can be overridden with the ``converters`` keyword:: >>> import numpy as np >>> converters = {'col1': np.uint, ... 'col2': np.float32} >>> ascii.read('file.dat', converters=converters) # doctest: +SKIP 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 examples, the best reference is to read the code for the existing :ref:`extension_reader_classes`. Examples -------- .. EXAMPLE START Advanced Customization to Extend Base Functionality of astropy.io.ascii For special cases, these examples demonstrate how to extend the base functionality of `astropy.io.ascii`. **Define custom readers by class inheritance** 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 = 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. Obtain the Data Table in a Different Format ------------------------------------------- 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 astropy-astropy-201cddb/docs/io/ascii/ref_api.rst000066400000000000000000000000761507226315300221700ustar00rootroot00000000000000Reference/API ************* .. automodapi:: astropy.io.ascii astropy-astropy-201cddb/docs/io/ascii/references.txt000066400000000000000000000002601507226315300227060ustar00rootroot00000000000000.. |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 astropy-astropy-201cddb/docs/io/ascii/toc.txt000066400000000000000000000001641507226315300213550ustar00rootroot00000000000000.. toctree:: :maxdepth: 2 read write base_classes fixed_width_gallery fast_ascii_io ascii_api astropy-astropy-201cddb/docs/io/ascii/write.rst000066400000000000000000000506171507226315300217230ustar00rootroot00000000000000.. include:: references.txt .. _astropy.io.ascii_write: Writing Tables ============== :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. Help on the ``write()`` function arguments is available interactively as shown in this example: .. doctest-skip:: >>> from astropy.io import ascii >>> 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:: x y 1 1 2 4 3 9 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, format='latex') # doctest: +SKIP \begin{table} \begin{tabular}{cc} x & y \\ 1 & 1 \\ 2 & 4 \\ 3 & 9 \\ \end{tabular} \end{table} 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 .. EXAMPLE END .. Note:: 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. .. _io_ascii_write_parameters: Parameters for ``write()`` -------------------------- 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. **output**: output specifier There are two ways to specify the output for the write operation: - 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``. **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)}, **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. **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_values**: list of fill value specifiers This can be used to fill missing values in the table or replace values with special meaning. 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. **fill_exclude_names**: list of column names, which are not affected by ``fill_values`` If not supplied, then ``fill_values`` can affect all columns. **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. .. _cds_mrt_format: Machine-Readable Table Format ----------------------------- 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. The :class:`~astropy.io.ascii.Mrt` writer supports writing tables to MRT format. .. note:: 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. Examples """""""" .. EXAMPLE START Writing MRT Format Tables Using astropy.io.ascii 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. 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. The example below initializes a table with columns that have a ``unit`` attribute and has masked values. >>> 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. astropy-astropy-201cddb/docs/io/fits/000077500000000000000000000000001507226315300177035ustar00rootroot00000000000000astropy-astropy-201cddb/docs/io/fits/api/000077500000000000000000000000001507226315300204545ustar00rootroot00000000000000astropy-astropy-201cddb/docs/io/fits/api/cards.rst000066400000000000000000000002571507226315300223060ustar00rootroot00000000000000.. currentmodule:: astropy.io.fits Cards ***** :class:`Card` ============= .. autoclass:: Card :members: :inherited-members: :undoc-members: :show-inheritance: astropy-astropy-201cddb/docs/io/fits/api/diff.rst000066400000000000000000000015151507226315300221200ustar00rootroot00000000000000Differs ******* .. 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: :show-inheritance: astropy-astropy-201cddb/docs/io/fits/api/files.rst000066400000000000000000000013501507226315300223070ustar00rootroot00000000000000.. 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 astropy-astropy-201cddb/docs/io/fits/api/hdulists.rst000066400000000000000000000003131507226315300230420ustar00rootroot00000000000000.. currentmodule:: astropy.io.fits HDU Lists ********* .. inheritance-diagram:: HDUList :class:`HDUList` ================ .. autoclass:: HDUList :members: :undoc-members: :show-inheritance: astropy-astropy-201cddb/docs/io/fits/api/hdus.rst000066400000000000000000000020211507226315300221440ustar00rootroot00000000000000.. currentmodule:: astropy.io.fits 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`. 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:: Group :members: :show-inheritance: :class:`StreamingHDU` ===================== .. autoclass:: StreamingHDU :members: :inherited-members: :show-inheritance: astropy-astropy-201cddb/docs/io/fits/api/headers.rst000066400000000000000000000002711507226315300226210ustar00rootroot00000000000000.. currentmodule:: astropy.io.fits Headers ******* :class:`Header` =============== .. autoclass:: Header :members: :inherited-members: :undoc-members: :show-inheritance: astropy-astropy-201cddb/docs/io/fits/api/images.rst000066400000000000000000000010101507226315300224430ustar00rootroot00000000000000.. currentmodule:: astropy.io.fits .. _images: Images ****** `ImageHDU` ========== .. autoclass:: ImageHDU :members: :inherited-members: :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: astropy-astropy-201cddb/docs/io/fits/api/index.rst000066400000000000000000000003411507226315300223130ustar00rootroot00000000000000Reference/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 astropy-astropy-201cddb/docs/io/fits/api/tables.rst000066400000000000000000000017511507226315300224640ustar00rootroot00000000000000.. currentmodule:: astropy.io.fits .. _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: :show-inheritance: Table Functions =============== :func:`tabledump` ----------------- .. autofunction:: tabledump :func:`tableload` ----------------- .. autofunction:: tableload :func:`table_to_hdu` -------------------- .. autofunction:: table_to_hdu astropy-astropy-201cddb/docs/io/fits/api/tiled_compression.rst000066400000000000000000000026321507226315300247330ustar00rootroot00000000000000***************** 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 astropy-astropy-201cddb/docs/io/fits/api/verification.rst000066400000000000000000000055441507226315300237000ustar00rootroot00000000000000.. currentmodule:: astropy.io.fits .. _verify: Verification Options ******************** 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'`` =============== 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 overwrite this default on output, the other options listed below can be used. ``'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 to FITS standard. The ``ignore`` option is useful in these situations, for example: 1. An input FITS file with non-standard is read and the user wants to copy or write out after some modification to an output file. The non-standard will be preserved in such output file. 2. A user wants to create a non-standard FITS file on purpose, possibly for testing purpose. No warning message will be printed out. This is like a silent warn (see below) option. ``'fix'`` ========= 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 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. 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. 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'`` =============== 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'`` ========== 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. astropy-astropy-201cddb/docs/io/fits/appendix/000077500000000000000000000000001507226315300215135ustar00rootroot00000000000000astropy-astropy-201cddb/docs/io/fits/appendix/faq.rst000066400000000000000000001204451507226315300230220ustar00rootroot00000000000000.. _io-fits-faq: astropy.io.fits FAQ ******************* .. contents:: General Questions ================= 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 low-level manipulation of headers, and it supports reading image and table data as |NumPy| arrays. It also supports more obscure and nonstandard formats found in some 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 is required to read/write compressed image data. However, the rest of PyFITS functions without this extension module. .. _PyFITS: https://github.com/spacetelescope/pyfits .. _FITS: https://fits.gsfc.nasa.gov/ What is the development status of PyFITS? ----------------------------------------- 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`_. 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. 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. The current (and last) stable release is 3.4.0. .. _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 did not work as I expected. Did I do something wrong? --------------------------------------------------------------- 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. `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, `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). ``astropy`` crashed and output a long string of code. What do I do? ------------------------------------------------------------------- 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 ``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:: >>> from astropy.io import fits >>> h = fits.Header() >>> h['NAXIS'] Traceback (most recent call last): ... 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 ``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 more vague and mysterious the error message in an exception appears, the more 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: https://en.wikipedia.org/wiki/Stack_trace 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 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 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. 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. 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``. .. _fitsverify: https://heasarc.gsfc.nasa.gov/docs/software/ftools/fitsverify/ .. _online FITS verifier: https://fits.gsfc.nasa.gov/fits_verify.html How do I turn off the warning messages ``astropy`` outputs to my console? ------------------------------------------------------------------------- ``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:: Card is too long, comment is truncated. 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. .. _warnings: https://docs.python.org/3/library/warnings.html What convention does ``astropy`` use for indexing, such as of image coordinates? -------------------------------------------------------------------------------- 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 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. 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. 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:: [[[1, 2], [3, 4]], [[5, 6], [7, 8]]] is represented linearly in row-major order as:: [1, 2, 3, 4, 5, 6, 7, 8] 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 ``astropy`` is likely to cause more difficulties than it is worth. How do I open a very large image that will not fit in memory? ------------------------------------------------------------- `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. 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 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. 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 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 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 .. _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) Then use the `astropy.io.fits.writeto()` method to write out the new file to disk: .. code:: python 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 order to create such a large file efficiently requires a little extra work, and a few assumptions. 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 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 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: .. code:: python 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: .. code:: python 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 filesystems that support sparse file creation (most Linux filesystems, but not 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 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 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: https://www.hdfgroup.org/HDF5/ .. _PyTables: http://www.pytables.org/ .. _fits-scaled-data-faq: Why is an image containing integer data being converted unexpectedly to floats? ------------------------------------------------------------------------------- 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:: 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). 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:: >>> fits_scaledimage_filename = fits.util.get_testdata_filepath('scale.fits') >>> hdul = fits.open(fits_scaledimage_filename) >>> image = hdul[0] >>> image.header['BITPIX'] 16 >>> image.header['BSCALE'] 0.045777764213996 >>> data = image.data # Read the data into memory >>> data.dtype.name # Got float32 despite BITPIX = 16 (16-bit int) 'float32' >>> image.header['BITPIX'] # The BITPIX will automatically update too -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 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 `~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:: >>> hdul = fits.open(fits_scaledimage_filename, do_not_scale_image_data=True) >>> image = hdul[0] >>> image.data.dtype.name 'int16' >>> hdul.close() 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 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 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 "free" format in which the ASCII representation can be stored anywhere, using the full 70 bytes of the card (after the keyword). 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. 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 = fits.Card.fromstring('FOO = 1234567890.123456789') >>> h = fits.Header() >>> h.append(c) >>> h FOO = 1234567890.123456789 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. astropy-astropy-201cddb/docs/io/fits/appendix/header_transition.rst000066400000000000000000000500531507226315300257520ustar00rootroot00000000000000.. 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. astropy-astropy-201cddb/docs/io/fits/appendix/history.rst000066400000000000000000004114441507226315300237560ustar00rootroot00000000000000.. 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`_. 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. .. contents:: PyFITS Changelog :depth: 2 :local: 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`` class). See API Changes below for more details. - Memory maps are now used by default to access HDU data. See API Changes below for more details. - Now includes a new version of the ``fitsdiff`` program for comparing two FITS files, and a new FITS comparison API used by ``fitsdiff``. See New 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 unchanged, and will not be noticed by most users. But there are differences 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 further details and background. Some highlights are listed below: * The Header class now fully implements the Python dict interface, and can 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 keywords). For example:: >>> header['NAXIS'] = 2 will update the existing 'FOO' keyword if it already exists, or add a new one if it doesn't exist, just like a dict. * It is possible to assign both a value and a comment at the same time using a tuple:: >>> header['NAXIS'] = (2, 'Number of axes') * To add/update a new card and ensure it's added in a specific location, use ``Header.set()``:: >>> header.set('NAXIS', 2, 'Number of axes', after='BITPIX') This works the same as the old ``Header.update()``. ``Header.update()`` still works in the old way too, but is deprecated. * Although ``Card`` objects still exist, it generally is not necessary to work with them directly. ``Header.ascardlist()``/``Header.ascard`` are deprecated and should not be used. To directly access the ``Card`` objects in a header, use ``Header.cards``. * To access card comments, it is still possible to either go through the card itself, or through ``Header.comments``. For example:: >>> header.cards['NAXIS'].comment Number of axes >>> header.comments['NAXIS'] Number of axes * ``Card`` objects can now be used interchangeably with ``(keyword, value, comment)`` 3-tuples. They still have ``.value`` and ``.comment`` attributes as well. The ``.key`` attribute has been renamed to ``.keyword`` for consistency, though ``.key`` is still supported (but deprecated). - Memory mapping is now used by default to access HDU data. That is, ``pyfits.open()`` uses ``memmap=True`` as the default. This provides better performance in the majority of use cases--there are only some I/O intensive applications where it might not be desirable. Enabling mmap by default also enabled finding and fixing a large number of bugs in PyFITS' handling of memory-mapped data (most of these bug fixes were backported to PyFITS 3.0.5). (#85) * A new ``pyfits.USE_MEMMAP`` global variable was added. Set ``pyfits.USE_MEMMAP = False`` to change the default memmap setting for opening files. This is especially useful for controlling the behavior in applications where pyfits is deeply embedded. * Likewise, a new ``PYFITS_USE_MEMMAP`` environment variable is supported. Set ``PYFITS_USE_MEMMAP = 0`` in your environment to change the default behavior. - The ``size()`` method on HDU objects is now a ``.size`` property--this returns the size in bytes of the data portion of the HDU, and in most cases is equivalent to ``hdu.data.nbytes`` (#83) - ``BinTableHDU.tdump`` and ``BinTableHDU.tcreate`` are deprecated--use ``BinTableHDU.dump`` and ``BinTableHDU.load`` instead. The new methods output the table data in a slightly different format from previous versions, which places quotes around each value. This format is compatible with data dumps from previous versions of PyFITS, but not vice-versa due to a parsing bug in older versions. - Likewise the ``pyfits.tdump`` and ``pyfits.tcreate`` convenience function versions of these methods have been renamed ``pyfits.tabledump`` and ``pyfits.tableload``. The old deprecated, but currently retained for backwards compatibility. (r1125) - 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 distinct from 'SCI') set ``pyfits.EXTENSION_NAME_CASE_SENSITIVE = True``. The default is ``False``. (r1139) - A new global configuration variable ``pyfits.STRIP_HEADER_WHITESPACE`` was added. By default, if a string value in a header contains trailing whitespace, that whitespace is automatically removed when the value is read. Now if you set ``pyfits.STRIP_HEADER_WHITESPACE = False`` all whitespace is preserved. (#146) - The old ``classExtensions`` extension mechanism (which was deprecated in PyFITS 3.0) is removed outright. To our knowledge it was no longer used anywhere. (r1309) - Warning messages from PyFITS issued through the Python warnings API are now output to stderr instead of stdout, as is the default. PyFITS no longer modifies the default behavior of the warnings module with respect to which stream it outputs to. (r1319) - The ``checksum`` argument to ``pyfits.open()`` now accepts a value of 'remove', which causes any existing CHECKSUM/DATASUM keywords to be ignored, and removed when the file is saved. New Features ------------ - 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 that embeds the supplied HDUList. (#80) - Added a new ``.is_image`` attribute on HDU objects, which is True if the HDU data is an 'image' as opposed to a table or something else. Here the meaning of 'image' is fairly loose, and mostly just means a Primary or Image extension HDU, or possibly a compressed image HDU (#71) - Added an ``HDUList.fromstring`` classmethod which can parse a FITS file already in memory and instantiate and ``HDUList`` object from it. This could be useful for integrating PyFITS with other libraries that work on FITS file, such as CFITSIO. It may also be useful in streaming applications. The name is a slight misnomer, in that it actually accepts any Python object that implements the buffer interface, which includes ``bytes``, ``bytearray``, ``memoryview``, ``numpy.ndarray``, etc. (#90) - Added a new ``pyfits.diff`` module which contains facilities for comparing FITS files. One can use the ``pyfits.diff.FITSDiff`` class to compare two FITS files in their entirety. There is also a ``pyfits.diff.HeaderDiff`` class for just comparing two FITS headers, and other similar interfaces. See the PyFITS Documentation for more details on this interface. The ``pyfits.diff`` module powers the new ``fitsdiff`` program installed with PyFITS. After installing PyFITS, run ``fitsdiff --help`` for usage details. - ``pyfits.open()`` now accepts a ``scale_back`` argument. If set to ``True``, this automatically scales the data using the original BZERO and BSCALE parameters the file had when it was first opened, if any, as well as the original BITPIX. For example, if the original BITPIX were 16, this would be equivalent to calling ``hdu.scale('int16', 'old')`` just before calling ``flush()`` or ``close()`` on the file. This option applies to all HDUs in the file. (#120) - ``pyfits.open()`` now accepts a ``save_backup`` argument. If set to ``True``, this automatically saves a backup of the original file before flushing any changes to it (this of course only applies to update and append 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) - Fixed a fairly rare crash that could occur in the handling of CONTINUE cards when using Numpy 1.4 or lower (though 1.4 is the oldest version supported by PyFITS). (r1330) - Fixed ``_BaseHDU.fromstring`` to actually correctly instantiate an HDU object from a string/buffer containing the header and data of that HDU. This allowed for the implementation of ``HDUList.fromstring`` described above. (#90) - Fixed a rare corner case where, in some use cases, (mildly, recoverable) malformatted float values in headers were not properly returned as floats. (#137) - Fixed a corollary to the previous bug where float values with a leading zero before the decimal point had the leading zero unnecessarily removed when saving changes to the file (eg. "0.001" would be written back as ".001" even if no changes were otherwise made to the file). (#137) - When opening a file containing CHECKSUM and/or DATASUM keywords in update mode, the CHECKSUM/DATASUM are updated and preserved even if the file was opened with checksum=False. This change in behavior prevents checksums from being unintentionally removed. (#148) - 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. Previously, in order to read such images in sections, it was necessary to manually apply the BSCALE+BZERO to each section. It's worth noting that sections *did* support pseudo-unsigned ints (flakily). This change just 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 prevent other table updates from being saved as well. (#139) - Fixed a corner case in which a keyword comment ending with the string "END" 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 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) - Fixed the documentation to point out that image data sections *do* support non-contiguous slices (and have for a long time). The documentation was never updated to reflect this, and misinformed users that only contiguous slices were supported, leading to some confusion. (#144) - Fixed a bug where creating an ``HDUList`` object containing multiple PRIMARY HDUs caused an infinite recursion when validating the object prior to writing to a file. (#145) - Fixed a rare but serious case where saving an update to a file that previously had a CHECKSUM and/or DATASUM keyword, but removed the checksum in saving, could cause the file to be slightly corrupted and unreadable. (#147) - Fixed problems with reading "non-standard" FITS files with primary headers containing SIMPLE = F. PyFITS has never made many guarantees as to how such files are handled. But it should at least be possible to read their headers, and the data if possible. Saving changes to such a file should not try to prepend an unwanted valid PRIMARY HDU. (#157) - Fixed a bug where opening an image with ``disable_image_compression = True`` caused compression to be disabled for all subsequent ``pyfits.open()`` calls. (r1651) 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 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 records in a GroupData object are renamed Group and are now a public interface. However, there's almost no good reason to create Group objects directly, so it shouldn't be considered a "new feature". - An annoyance from PyFITS 3.0.6 was fixed, where the value of the EXTEND keyword was always being set to F if there are not actually any extension 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. - 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) - Fixed overly strict handling of the EXTEND keyword; see "Changes in behavior" above. (#124) - Fixed many cases where an HDU's header would be marked as "modified" by PyFITS and rewritten, even when no changes to the header are necessary. (#125) - 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 causing some slowdown when opening files containing a large number of 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 padding (i.e. null bytes instead of spaces) couldn't be parsed by PyFITS. Such headers can be parsed again, but a warning is raised, as such headers are not valid FITS. (#136) - Fixed a memory leak where table data in random groups HDUs weren't being garbage collected. (#138) 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 non-trivial BSCALE and BZERO keywords) in 'update' mode would cause the data to be automatically rescaled--possibly converting the data from ints to floats--as soon as the file is closed, even if the application did not touch the data. Now PyFITS will only rescale the data in an extension when the data is actually accessed by the application. So opening a file in 'update' mode in order to modify the header or append new extensions will not cause any change to the data in existing extensions. 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) - Fixed a possible reference cycle/memory leak that was caught through more extensive testing on Windows. (#112) - Fixed 'ostream' mode to open the underlying file in 'wb' mode instead of 'w' mode. (#112) - Fixed a Windows-only issue where trying to save updates to a resized FITS file could result in a crash due to there being open mmaps on that file. (#112) - Fixed a crash when trying to create a FITS table (i.e. with new_table()) from a Numpy array containing bool fields. (#113) - Fixed a bug where manually initializing an ``HDUList`` with a list of of HDUs wouldn't set the correct EXTEND keyword value on the primary HDU. (#114) - Fixed a crash that could occur when trying to deepcopy a Header in Python < 2.7. (#115) - Fixed an issue where merely opening a scaled image in 'update' mode would cause the data to be converted to floats when the file is closed. (#119) 3.0.5 (2012-01-30) ================== - Fixed a crash that could occur when accessing image sections of files opened with memmap=True. (r1211) - Fixed the inconsistency in the behavior of files opened in 'readonly' mode when memmap=True vs. when memmap=False. In the latter case, although changes to array data were not saved to disk, it was possible to update the array data in memory. On the other hand with memmap=True, 'readonly' mode prevented even in-memory modification to the data. This is what 'copyonwrite' mode was for, but difference in behavior was confusing. Now 'readonly' is equivalent to 'copyonwrite' when using memmap. If the old behavior of denying changes to the array data is necessary, a new 'denywrite' mode may be used, though it is only applicable to files opened with memmap. (r1275) - Fixed an issue where files opened with memmap=True would return image data as a raw numpy.memmap object, which can cause some unexpected behaviors--instead memmap object is viewed as a numpy.ndarray. (r1285) - Fixed an issue in Python 3 where a workaround for a bug in Numpy on Python 3 interacted badly with some other software, namely to vo.table package (and possibly others). (r1320, r1337, and #110) - Fixed buggy behavior in the handling of SIGINTs (i.e. Ctrl-C keyboard interrupts) while flushing changes to a FITS file. PyFITS already prevented SIGINTs from causing an incomplete flush, but did not clean up the signal handlers properly afterwards, or reraise the keyboard interrupt once the flush was complete. (r1321) - 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 when using Numpy below version 1.5. - 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 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 values. (#111) 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) - Fixed a crash when slicing an table in a file opened in 'readonly' mode with memmap=True. (r1230) - Writing changes to a file or writing to a new file verifies the output in 'fix' mode by default instead of 'exception'--that is, PyFITS will automatically fix common FITS format errors rather than raising an exception. (r1243) - Fixed a bug where convenience functions such as getval() and getheader() crashed when specifying just 'PRIMARY' as the extension to use (r1263). - Fixed a bug that prevented passing keyword arguments (beyond the standard data and header arguments) as positional arguments to the constructors of extension HDU classes. - Fixed some tests that were failing on Windows--in this case the tests themselves failed to close some temp files and Windows refused to delete them while there were still open handles on them. (r1295) - Fixed an issue with floating point formatting in header values on Python 2.5 for Windows (and possibly other platforms). The exponent was zero-padded to 3 digits; although the FITS standard makes no specification on this, the formatting is now normalized to always pad the exponent to two digits. (r1295) - Fixed a bug where long commentary cards (such as HISTORY and COMMENT) were broken into multiple CONTINUE cards. However, commentary cards are not expected to be found in CONTINUE cards. Instead these long cards are broken into multiple commentary cards. (#97) - GZIP/ZIP-compressed FITS files can be detected and opened regardless of their filename extension. (#99) - Fixed a serious bug where opening scaled images in 'update' mode and then closing the file without touching the data would cause the file to be corrupted. (#101) 3.0.3 (2011-10-05) ================== - Fixed several small bugs involving corner cases in record-valued keyword cards (#70) - In some cases HDU creation failed if the first keyword value in the header was not a string value (#89) - Fixed a crash when trying to compute the HDU checksum when the data array contains an odd number of bytes (#91) - Disabled an unnecessary warning that was displayed on opening compressed HDUs with disable_image_compression = True (#92) - Fixed a typo in code for handling HCOMPRESS compressed images. 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) - The presence, value, and position of the EXTEND keyword in Primary HDUs is verified when reading/writing a FITS file (#32) - Improved documentation (in warning messages as well as in the handbook) that PyFITS uses zero-based indexing (as one would expect for C/Python code, but contrary to the PyFITS standard which was written with FORTRAN in mind) (#68) - 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. - Fixed a related bug where changes made directly to Card object in a header (i.e. assigning directly to card.value or card.comment) would not propagate when flushing changes to the file (#69) [Note: This and the bug above it were originally reported as being fixed in version 3.0.1, but the fix was never included in the release.] - Improved file handling, particularly in Python 3 which had a few small file I/O-related bugs (#76) - Fixed a bug where updating a FITS file would sometimes cause it to lose its original file permissions (#79) - Fixed the handling of TDIMn keywords; 3.0 added support for them, but got 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 immediately written to a new file without explicitly viewing the data first (#84) - Fixed a bug where creating a table with columns named either 'names' or 'formats' resulted in an infinite recursion (#86) 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. - Changed ``_TableBaseHDU.data`` so that if the data contain an empty table a ``FITS_rec`` object with zero rows is returned rather than ``None``. - The ``.key`` attribute of ``RecordValuedKeywordCards`` now returns the full keyword+field-specifier value, instead of just the plain keyword (#46) - Fixed a related bug where changes made directly to Card object in a header (i.e. assigning directly to card.value or card.comment) would not propagate when flushing changes to the file (#69) - Fixed a bug where writing a table with zero rows could fail in some cases (#72) - 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 order would not preserve the byte order, thus interpreting the column array using the wrong byte order (#77) 3.0.0 (2011-08-23) ==================== - Contains major changes, bumping the version to 3.0 - Large amounts of refactoring and reorganization of the code; tried to preserve public API backwards-compatibility with older versions (private API has many changes and is not guaranteed to be backwards-compatible). There are a few small public API changes to be aware of: * The pyfits.rec module has been removed completely. If your version of numpy does not have the numpy.core.records module it is too old to be used with PyFITS. * The ``Header.ascardlist()`` method is deprecated--use the ``.ascard`` attribute instead. * ``Card`` instances have a new ``.cardimage`` attribute that should be used rather than ``.ascardimage()``, which may become deprecated. * The ``Card.fromstring()`` method is now a classmethod. It returns a new ``Card`` instance rather than modifying an existing instance. * The ``req_cards()`` method on HDU instances has changed: The ``pos`` argument is not longer a string. It is either an integer value (meaning the card's position must match that value) or it can be a function that takes the card's position as it's argument, and returns True if the position is valid. Likewise, the ``test`` argument no longer takes a string, but instead a function that validates the card's value and returns True or False. * The ``get_coldefs()`` method of table HDUs is deprecated. Use the ``.columns`` attribute instead. * The ``ColDefs.data`` attribute is deprecated--use ``ColDefs.columns`` instead (though in general you shouldn't mess with it directly--it might become internal at some point). * ``FITS_record`` objects take ``start`` and ``end`` as arguments instead of ``startColumn`` and ``endColumn`` (these are rarely created manually, so it's unlikely that this change will affect anyone). * ``BinTableHDU.tcreate()`` is now a classmethod, and returns a new ``BinTableHDU`` instance. * Use ``ExtensionHDU`` and ``NonstandardExtHDU`` for making new extension HDU classes. They are now public interfaces, wheres previously they were private and prefixed with underscores. * Possibly others--please report if you find any changes that cause difficulties. - 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 functions. If we get close to actually removing any functions, we might make the Deprecation warnings display by default. - Added basic Python 3 support - Added support for multi-dimensional columns in tables as specified by the TDIMn keywords (#47) - Fixed a major memory leak that occurred when creating new tables with the ``new_table()`` function (#49) be padded with zero-bytes) vs ASCII tables (where strings are padded with spaces) (#15) - Fixed a bug in which the case of Random Access Group parameters names was not preserved when writing (#41) - 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) - Fixed a bug that caused the index_of() method of HDULists to crash when the HDUList object is created from scratch (#48) - Fixed the behavior of string padding in binary tables (where strings should be padded with nulls instead of spaces) - Fixed a rare issue that caused excessive memory usage when computing checksums using a non-standard block size (see r818) - Add support for forced uint data in image sections (#53) - Fixed an issue where variable-length array columns were not extended when creating a new table with more rows than the original (#54) - Fixed tuple and list-based indexing of FITS_rec objects (#55) - Fixed an issue where BZERO and BSCALE keywords were appended to headers in the wrong location (#56) - ``FITS_record`` objects (table rows) have full slicing support, including stepping, etc. (#59) - Fixed a bug where updating multiple files simultaneously (such as when running parallel processes) could lead to a race condition with mktemp() (#61) - Fixed a bug where compressed image headers were not in the order expected by the funpack utility (#62) 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 provided to verify and update checksums. - Added a new optional keyword argument ``do_not_scale_image_data`` to the ``pyfits.open`` convenience function. When this argument is provided as True, and an ImageHDU is read that contains scaled data, the data is not automatically scaled when it is read. This option may be used when opening a fits file for update, when you only want to update some header data. Without the use of this argument, if the header updates required the size of the fits file to change, then when writing the updated information, the data would be read, scaled, and written back out in its scaled format (usually with a different data type) instead of in its non-scaled format. - Added a new optional keyword argument ``disable_image_compression`` to the ``pyfits.open`` function. When ``True``, any compressed image HDU's will be read in like they are binary table HDU's. - Added a ``verify`` keyword argument to the ``pyfits.append`` function. When ``False``, ``append`` will assume the existing FITS file is already valid and simply append new content to the end of the file, resulting in a large speed up appending to large files. - Added HDU methods ``update_ext_name`` and ``update_ext_version`` for updating the name and version of an HDU. - Added HDU method ``filebytes`` to calculate the number of bytes that will be written to the file associated with the HDU. - Enhanced the section class to allow reading non-contiguous image data. Previously, the section class could only be used to read contiguous data. (CNSHD781626) - Added method ``HDUList.fileinfo()`` that returns a dictionary with information about the location of header and data in the file associated with the HDU. The following bugs were fixed: - Reading in some malformed FITS headers would cause a ``NameError`` exception, rather than information about the cause of the error. - pyfits can now handle non-compliant ``CONTINUE`` cards produced by Java FITS. - ``BinTable`` columns with ``TSCALn`` are now byte-swapped correctly. - Ensure that floating-point card values are no longer than 20 characters. - Updated ``flush`` so that when the data has changed in an HDU for a file opened in update mode, the header will be updated to match the changed data before writing out the HDU. - Allow ``HIERARCH`` cards to contain a keyword and value whose total character length is 69 characters. Previous length was limited at 68 characters. - Calls to ``FITS_rec['columnName']`` now return an ``ndarray``. exactly the same as a call to ``FITS_rec.field('columnName')`` or ``FITS_rec.columnName``. Previously, ``FITS_rec['columnName']`` returned a much less useful ``fits_record`` object. (CNSHD789053) - Corrected the ``append`` convenience function to eliminate the reading of the HDU data from the file that is being appended to. (CNSHD794738) - Eliminated common symbols between the pyfitsComp module and the cfitsio and zlib libraries. These can cause problems on systems that use both PyFITS and cfitsio or zlib. (CNSHD795046) 2.3.1 (2010-06-03) ==================== The following bugs were fixed: - Replaced code in the Compressed Image HDU extension which was covered under a GNU General Public License with code that is covered under a BSD License. This change allows the distribution of pyfits under a BSD License. 2.3 (2010-05-11) ================== The following enhancements were made: - Completely eliminate support for numarray. - Rework pyfits documentation to use Sphinx. - Support python 2.6 and future division. - Added a new method to get the file name associated with an HDUList object. The method HDUList.filename() returns the name of an associated file. It returns None if no file is associated with the HDUList. - Support the python 2.5 'with' statement when opening fits files. (CNSHD766308) It is now possible to use the following construct: >>> from __future__ import with_statement import pyfits >>> with pyfits.open("input.fits") as hdul: ... #process hdul >>> - Extended the support for reading unsigned integer 16 values from an ImageHDU to include unsigned integer 32 and unsigned integer 64 values. ImageHDU data is considered to be unsigned integer 16 when the data type is signed integer 16 and BZERO is equal to 2**15 (32784) and BSCALE is equal to 1. ImageHDU data is considered to be unsigned integer 32 when the data type is signed integer 32 and BZERO is equal to 2**31 and BSCALE is equal to 1. ImageHDU data is considered to be unsigned integer 64 when the data type is signed integer 64 and BZERO is equal to 2**63 and BSCALE is equal to 1. An optional keyword argument (uint) was added to the open convenience function for this purpose. Supplying a value of True for this argument will cause data of any of these types to be read in and scaled into the appropriate unsigned integer array (uint16, uint32, or uint64) instead of into the normal float 32 or float 64 array. If an HDU associated with a file that was opened with the 'int' option and containing unsigned integer 16, 32, or 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 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 indexing the table using the column name. This is consistent with Record Arrays in numpy (array with fields). (CNSHD763378) The following example will illustrate this: >>> import pyfits >>> hdul = pyfits.open('input.fits') >>> table = hdul[1].data >>> table.names ['c1','c2','c3','c4'] >>> 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') >>> print table[1] # this is the data for row 1 (2, 'xy', 6.6999997138977054, True) - Provided capabilities to create a BinaryTableHDU directly from a numpy Record Array (array with fields). The new capabilities include table creation, writing a numpy Record Array directly to a fits file using the pyfits.writeto and pyfits.append convenience functions. Reading the data for a BinaryTableHDU from a fits file directly into a numpy Record Array using the pyfits.getdata convenience function. (CNSHD749034) Thanks to Erin Sheldon at Brookhaven National Laboratory for help with this. The following should illustrate these new capabilities: >>> import pyfits >>> import numpy >>> t=numpy.zeros(5,dtype=[('x','f4'),('y','2i4')]) \ ... # Create a numpy Record Array with fields >>> hdu = pyfits.BinTableHDU(t) \ ... # Create a Binary Table HDU directly from the Record Array >>> print hdu.data [(0.0, array([0, 0], dtype=int32)) (0.0, array([0, 0], dtype=int32)) (0.0, array([0, 0], dtype=int32)) (0.0, array([0, 0], dtype=int32)) (0.0, array([0, 0], dtype=int32))] >>> hdu.writeto('test1.fits',clobber=True) \ ... # Write the HDU to a file >>> pyfits.info('test1.fits') Filename: test1.fits No. Name Type Cards Dimensions Format 0 PRIMARY PrimaryHDU 4 () uint8 1 BinTableHDU 12 5R x 2C [E, 2J] >>> pyfits.writeto('test.fits', t, clobber=True) \ ... # Write the Record Array directly to a file >>> pyfits.append('test.fits', t) \ ... # Append another Record Array to the file >>> pyfits.info('test.fits') Filename: test.fits No. Name Type Cards Dimensions Format 0 PRIMARY PrimaryHDU 4 () uint8 1 BinTableHDU 12 5R x 2C [E, 2J] 2 BinTableHDU 12 5R x 2C [E, 2J] >>> d=pyfits.getdata('test.fits',ext=1) \ ... # Get the first extension from the file as a FITS_rec >>> print type(d) >>> print d [(0.0, array([0, 0], dtype=int32)) (0.0, array([0, 0], dtype=int32)) (0.0, array([0, 0], dtype=int32)) (0.0, array([0, 0], dtype=int32)) (0.0, array([0, 0], dtype=int32))] >>> d=pyfits.getdata('test.fits',ext=1,view=numpy.ndarray) \ ... # Get the first extension from the file as a numpy Record Array >>> print type(d) >>> print d [(0.0, [0, 0]) (0.0, [0, 0]) (0.0, [0, 0]) (0.0, [0, 0]) (0.0, [0, 0])] >>> print d.dtype [('x', '>f4'), ('y', '>i4', 2)] >>> d=pyfits.getdata('test.fits',ext=1,upper=True, ... view=pyfits.FITS_rec) \ ... # Force the Record Array field names to be in upper case regardless of how they are stored in the file >>> print d.dtype [('X', '>f4'), ('Y', '>i4', 2)] - Provided support for writing fits data to file-like objects that do not support the random access methods seek() and tell(). Most pyfits functions or methods will treat these file-like objects as an empty file that cannot be read, only written. It is also expected that the file-like object is in a writable condition (ie. opened) when passed into a pyfits function or method. The following methods and functions will allow writing to a non-random access file-like object: HDUList.writeto(), HDUList.flush(), pyfits.writeto(), and pyfits.append(). The pyfits.open() convenience function may be used to create an HDUList object that is associated with the provided file-like object. (CNSHD770036) An illustration of the new capabilities follows. In this example fits data is written to standard output which is associated with a file opened in write-only mode: >>> import pyfits >>> import numpy as np >>> import sys >>> >>> hdu = pyfits.PrimaryHDU(np.arange(100,dtype=np.int32)) >>> hdul = pyfits.HDUList() >>> hdul.append(hdu) >>> tmpfile = open('tmpfile.py','w') >>> sys.stdout = tmpfile >>> hdul.writeto(sys.stdout, clobber=True) >>> sys.stdout = sys.__stdout__ >>> tmpfile.close() >>> pyfits.info('tmpfile.py') Filename: tmpfile.py No. Name Type Cards Dimensions Format 0 PRIMARY PrimaryHDU 5 (100,) int32 >>> - Provided support for slicing a FITS_record object. The FITS_record object represents the data from a row of a table. Pyfits now supports the slice syntax to retrieve values from the row. The following illustrates this new syntax: >>> hdul = pyfits.open('table.fits') >>> row = hdul[1].data[0] >>> row ('clear', 'nicmos', 1, 30, 'clear', 'idno= 100') >>> a, b, c, d, e = row[0:5] >>> a 'clear' >>> b 'nicmos' >>> c 1 >>> d 30 >>> e 'clear' >>> - Allow the assignment of a row value for a pyfits table using a tuple or a list as input. The following example illustrates this new feature: >>> c1=pyfits.Column(name='target',format='10A') >>> c2=pyfits.Column(name='counts',format='J',unit='DN') >>> c3=pyfits.Column(name='notes',format='A10') >>> c4=pyfits.Column(name='spectrum',format='5E') >>> c5=pyfits.Column(name='flag',format='L') >>> coldefs=pyfits.ColDefs([c1,c2,c3,c4,c5]) >>> >>> tbhdu=pyfits.new_table(coldefs, nrows = 5) >>> >>> # Assigning data to a table's row using a tuple >>> tbhdu.data[2] = ('NGC1',312,'A Note', ... num.array([1.1,2.2,3.3,4.4,5.5],dtype=num.float32), ... True) >>> >>> # Assigning data to a tables row using a list >>> tbhdu.data[3] = ['JIM1','33','A Note', ... num.array([1.,2.,3.,4.,5.],dtype=num.float32),True] - Allow the creation of a Variable Length Format (P format) column from a list of data. The following example illustrates this new feature: >>> a = [num.array([7.2e-20,7.3e-20]),num.array([0.0]), ... num.array([0.0])] >>> acol = pyfits.Column(name='testa',format='PD()',array=a) >>> acol.array _VLF([[ 7.20000000e-20 7.30000000e-20], [ 0.], [ 0.]], dtype=object) >>> - Allow the assignment of multiple rows in a table using the slice syntax. The following example illustrates this new feature: >>> counts = num.array([312,334,308,317]) >>> names = num.array(['NGC1','NGC2','NGC3','NCG4']) >>> c1=pyfits.Column(name='target',format='10A',array=names) >>> c2=pyfits.Column(name='counts',format='J',unit='DN', ... array=counts) >>> c3=pyfits.Column(name='notes',format='A10') >>> c4=pyfits.Column(name='spectrum',format='5E') >>> c5=pyfits.Column(name='flag',format='L',array=[1,0,1,1]) >>> coldefs=pyfits.ColDefs([c1,c2,c3,c4,c5]) >>> >>> tbhdu1=pyfits.new_table(coldefs) >>> >>> counts = num.array([112,134,108,117]) >>> names = num.array(['NGC5','NGC6','NGC7','NCG8']) >>> c1=pyfits.Column(name='target',format='10A',array=names) >>> c2=pyfits.Column(name='counts',format='J',unit='DN', ... array=counts) >>> c3=pyfits.Column(name='notes',format='A10') >>> c4=pyfits.Column(name='spectrum',format='5E') >>> c5=pyfits.Column(name='flag',format='L',array=[0,1,0,0]) >>> coldefs=pyfits.ColDefs([c1,c2,c3,c4,c5]) >>> >>> tbhdu=pyfits.new_table(coldefs) >>> tbhdu.data[0][3] = num.array([1.,2.,3.,4.,5.], ... dtype=num.float32) >>> >>> tbhdu2=pyfits.new_table(tbhdu1.data, nrows=9) >>> >>> # Assign the 4 rows from the second table to rows 5 thru ... 8 of the new table. Note that the last row of the new ... table will still be initialized to the default values. >>> tbhdu2.data[4:] = tbhdu.data >>> >>> print tbhdu2.data [ ('NGC1', 312, '0.0', array([ 0., 0., 0., 0., 0.], dtype=float32), True) ('NGC2', 334, '0.0', array([ 0., 0., 0., 0., 0.], dtype=float32), False) ('NGC3', 308, '0.0', array([ 0., 0., 0., 0., 0.], dtype=float32), True) ('NCG4', 317, '0.0', array([ 0., 0., 0., 0., 0.], dtype=float32), True) ('NGC5', 112, '0.0', array([ 1., 2., 3., 4., 5.], dtype=float32), False) ('NGC6', 134, '0.0', array([ 0., 0., 0., 0., 0.], dtype=float32), True) ('NGC7', 108, '0.0', array([ 0., 0., 0., 0., 0.], dtype=float32), False) ('NCG8', 117, '0.0', array([ 0., 0., 0., 0., 0.], dtype=float32), False) ('0.0', 0, '0.0', array([ 0., 0., 0., 0., 0.], dtype=float32), False)] >>> The following bugs were fixed: - Corrected bugs in HDUList.append and HDUList.insert to correctly handle the situation where you want to insert or append a Primary HDU as something other than the first HDU in an HDUList and the situation where you want to insert or append an Extension HDU as the first HDU in an HDUList. - Corrected a bug involving scaled images (both compressed and not compressed) that include a BLANK, or ZBLANK card in the header. When the image values match the BLANK or ZBLANK value, the value should be replaced with NaN after scaling. Instead, pyfits was scaling the BLANK or ZBLANK value and returning it. (CNSHD766129) - Corrected a byteswapping bug that occurs when writing certain column data. (CNSHD763307) - Corrected a bug that occurs when creating a column from a chararray when one or more elements are shorter than the specified format length. The bug wrote nulls instead of spaces to the file. (CNSHD695419) - Corrected a bug in the HDU verification software to ensure that the header contains no NAXISn cards where n > NAXIS. - Corrected a bug involving reading and writing compressed image data. When written, the header keyword card ZTENSION will always have the value 'IMAGE' and when read, if the ZTENSION value is not 'IMAGE' the user will receive a warning, but the data will still be treated as image data. - Corrected a bug that restricted the ability to create a custom HDU class and use it with pyfits. The bug fix will allow something like this: >>> import pyfits >>> class MyPrimaryHDU(pyfits.PrimaryHDU): ... def __init__(self, data=None, header=None): ... pyfits.PrimaryHDU.__init__(self, data, header) ... def _summary(self): ... """ ... Reimplement a method of the class. ... """ ... s = pyfits.PrimaryHDU._summary(self) ... # change the behavior to suit me. ... s1 = 'MyPRIMARY ' + s[11:] ... return s1 ... >>> hdul=pyfits.open("pix.fits", ... classExtensions={pyfits.PrimaryHDU: MyPrimaryHDU}) >>> hdul.info() Filename: pix.fits No. Name Type Cards Dimensions Format 0 MyPRIMARY MyPrimaryHDU 59 (512, 512) int16 >>> - Modified ColDefs.add_col so that instead of returning a new ColDefs object with the column added to the end, it simply appends the new column to the current ColDefs object in place. (CNSHD768778) - Corrected a bug in ColDefs.del_col which raised a KeyError exception when deleting a column from a ColDefs object. - Modified the open convenience function so that when a file is opened in readonly mode and the file contains no HDU's an IOError is raised. - Modified _TableBaseHDU to ensure that all locations where data is referenced in the object actually reference the same ndarray, instead of copies of the array. - Corrected a bug in the Column class that failed to initialize data when the data is a boolean array. (CNSHD779136) - Corrected a bug that caused an exception to be raised when creating a variable length format column from character data (PA format). - Modified installation code so that when installing on Windows, when a C++ 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. The following bugs were fixed: - Corrected a bug that caused an exception to be raised when creating a CompImageHDU using an initial header that does not match the image data in terms of the number of axis. 2.2.1 (2009-10-06) ==================== Updates described in this release are only supported in the NUMPY version of pyfits. The following bugs were fixed: - Corrected a bug that prevented the opening of a fits file where a header contained a CHECKSUM card but no DATASUM card. - Corrected a bug that caused NULLs to be written instead of blanks when an ASCII table was created using a numpy chararray in which the original data contained trailing blanks. (CNSHD695419) 2.2 (2009-09-23) ================== Updates described in this release are only supported in the NUMPY version of pyfits. The following enhancements were made: - Provide support for the FITS Checksum Keyword Convention. (CNSHD754301) - Adding the checksum=True keyword argument to the open convenience function will cause checksums to be verified on file open: >>> hdul=pyfits.open('in.fits', checksum=True) - On output, CHECKSUM and DATASUM cards may be output to all HDU's in a fits file by using the keyword argument checksum=True in calls to the writeto convenience function, the HDUList.writeto method, the writeto methods of all of the HDU classes, and the append convenience function: >>> hdul.writeto('out.fits', checksum=True) - Implemented a new insert method to the HDUList class that allows for the insertion of a HDU into a HDUList at a given index: >>> hdul.insert(2,hdu) - Provided the capability to handle Unicode input for file names. - Provided support for integer division required by Python 3.0. The following bugs were fixed: - Corrected a bug that caused an index out of bounds exception to be raised when iterating over the rows of a binary table HDU using the syntax "for row in tbhdu.data: ". (CNSHD748609) - Corrected a bug that prevented the use of the writeto convenience function for writing table data to a file. (CNSHD749024) - Modified the code to raise an IOError exception with the comment "Header missing END card." when pyfits can't find a valid END card for a header when opening a file. - This change addressed a problem with a non-standard fits file that contained several new-line characters at the end of each header and at the end of the file. However, since some people want to be able to open these non-standard files anyway, an option was added to the open convenience function to allow these files to be opened without exception: >>> pyfits.open('infile.fits',ignore_missing_end=True) - Corrected a bug that prevented the use of StringIO objects as fits files when reading and writing table data. Previously, only image data was supported. (CNSHD753698) - Corrected a bug that caused a bus error to be generated when compressing image data using GZIP_1 under the Solaris operating system. - Corrected bugs that prevented pyfits from properly reading Random Groups HDU's using numpy. (CNSHD756570) - Corrected a bug that can occur when writing a fits file. (CNSHD757508) - If no default SIGINT signal handler has not been assigned, before the write, a TypeError exception is raised in the _File.flush() method when attempting to return the signal handler to its previous state. Notably this occurred when using mod_python. The code was changed to use SIG_DFL when no old handler was defined. - Corrected a bug in CompImageHDU that prevented rescaling the image data using hdu.scale(option='old'). 2.1.1 (2009-04-22) =================== Updates described in this release are only supported in the NUMPY version of pyfits. The following bugs were fixed: - Corrected a bug that caused an exception to be raised when closing a file opened for append, where an HDU was appended to the file, after data was accessed from the file. This exception was only raised when running on a Windows platform. - Updated the installation scripts, compression source code, and benchmark test scripts to properly install, build, and execute on a Windows platform. 2.1 (2009-04-14) ================== Updates described in this release are only supported in the NUMPY version of pyfits. The following enhancements were made: - Added new tdump and tcreate capabilities to pyfits. - The new tdump convenience function allows the contents of a binary table HDU to be dumped to a set of three files in ASCII format. One file will contain column definitions, the second will contain header parameters, and the third will contain header data. - The new tcreate convenience function allows the creation of a binary table HDU from the three files dumped by the tdump convenience function. - The primary use for the tdump/tcreate methods are to allow editing in a standard text editor of the binary table data and parameters. - Added support for case sensitive values of the EXTNAME card in an extension header. (CNSHD745784) - By default, pyfits converts the value of EXTNAME cards to upper case when reading from a file. A new convenience function (setExtensionNameCaseSensitive) was implemented to allow a user to circumvent this behavior so that the EXTNAME value remains in the same case as it is in the file. - With the following function call, pyfits will maintain the case of all characters in the EXTNAME card values of all extension HDU's during the entire python session, or until another call to the function is made: >>> import pyfits >>> pyfits.setExtensionNameCaseSensitive() - The following function call will return pyfits to its default (all upper case) behavior: >>> pyfits.setExtensionNameCaseSensitive(False) - Added support for reading and writing FITS files in which the value of the first card in the header is 'SIMPLE=F'. In this case, the pyfits open function returns an HDUList object that contains a single HDU of the new type _NonstandardHDU. The header for this HDU is like a normal header (with the exception that the first card contains SIMPLE=F instead of SIMPLE=T). Like normal HDU's the reading of the data is delayed until actually requested. The data is read from the file into a string starting from the first byte after the header END card and continuing till the end of the file. When written, the header is written, followed by the data string. No attempt is made to pad the data string so that it fills into a standard 2880 byte FITS block. (CNSHD744730) - Added support for FITS files containing extensions with unknown XTENSION card values. (CNSHD744730) Standard FITS files support extension HDU's of types TABLE, IMAGE, BINTABLE, and A3DTABLE. Accessing a nonstandard extension from a FITS file will now create a _NonstandardExtHDU object. Accessing the data of this object will cause the data to be read from the file into a string. If the HDU is written back to a file the string data is written after the Header and padded to fill a standard 2880 byte FITS block. The following bugs were fixed: - Extensive changes were made to the tiled image compression code to support the latest enhancements made in CFITSIO version 3.13 to support this convention. - Eliminated a memory leak in the tiled image compression code. - Corrected a bug in the FITS_record.__setitem__ method which raised a NameError exception when attempting to set a value in a FITS_record object. (CNSHD745844) - Corrected a bug that caused a TypeError exception to be raised when reading fits files containing large table HDU's (>2Gig). (CNSHD745522) - Corrected a bug that caused a TypeError exception to be raised for all calls to the warnings module when running under Python 2.6. The formatwarning method in the warnings module was changed in Python 2.6 to include a new argument. (CNSHD746592) - Corrected the behavior of the membership (in) operator in the Header class to check against header card keywords instead of card values. (CNSHD744730) - Corrected the behavior of iteration on a Header object. The new behavior iterates over the unique card keywords instead of the card values. 2.0.1 (2009-02-03) ==================== Updates described in this release are only supported in the NUMPY version of pyfits. The following bugs were fixed: - Eliminated a memory leak when reading Table HDU's from a fits file. (CNSHD741877) 2.0 (2009-01-30) ================== Updates described in this release are only supported in the NUMPY version of pyfits. The following enhancements were made: - Provide initial support for an image compression convention known as the "Tiled Image Compression Convention" `[1]`_. - The principle used in this convention is to first divide the n-dimensional image into a rectangular grid of subimages or "tiles". Each tile is then compressed as a continuous block of data, and the resulting compressed byte stream is stored in a row of a variable length column in a FITS binary table. Several commonly used algorithms for compressing image tiles are supported. These include, GZIP, RICE, H-Compress and IRAF pixel list (PLIO). - Support for compressed image data is provided using the optional "pyfitsComp" module contained in a C shared library (pyfitsCompmodule.so). - The header of a compressed image HDU appears to the user like any image header. The actual header stored in the FITS file is that of a binary table HDU with a set of special keywords, defined by the convention, to describe the structure of the compressed image. The conversion between binary table HDU header and image HDU header is all performed behind the scenes. Since the HDU is actually a binary table, it may not appear as a primary HDU in a FITS file. - 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. - To create a compressed image HDU from scratch, simply construct a CompImageHDU object from an uncompressed image data array and its associated image header. From there, the HDU can be treated just like any image HDU: >>> hdu=pyfits.CompImageHDU(imageData,imageHeader) >>> hdu.writeto('compressed_image.fits') - The signature for the CompImageHDU initializer method describes the possible options for constructing a CompImageHDU object:: def __init__(self, data=None, header=None, name=None, compressionType='RICE_1', tileSize=None, hcompScale=0., hcompSmooth=0, quantizeLevel=16.): """ data: data of the image header: header to be associated with the image name: the EXTNAME value; if this value is None, then the name from the input image header will be used; if there is no name in the input image header then the default name 'COMPRESSED_IMAGE' is used compressionType: compression algorithm 'RICE_1', 'PLIO_1', 'GZIP_1', 'HCOMPRESS_1' tileSize: compression tile sizes default treats each row of image as a tile hcompScale: HCOMPRESS scale parameter hcompSmooth: HCOMPRESS smooth parameter quantizeLevel: floating point quantization level; """ - Added two new convenience functions. The setval function allows the setting of the value of a single header card in a fits file. The delval function allows the deletion of a single header card in a fits file. - A modification was made to allow the reading of data from a fits file containing a Table HDU that has duplicate field names. It is normally a requirement that the field names in a Table HDU be unique. Prior to this change a ValueError was raised, when the data was accessed, to indicate that the HDU contained duplicate field names. Now, a warning is issued and the field names are made unique in the internal record array. This will not change the TTYPEn header card values. You will be able to get the data from all fields using the field name, including the first field containing the name that is duplicated. To access the data of the other fields with the duplicated names you will need to use the field number instead of the field name. (CNSHD737193) - An enhancement was made to allow the reading of unsigned integer 16 values from an ImageHDU when the data is signed integer 16 and BZERO is equal to 32784 and BSCALE is equal to 1 (the standard way for scaling unsigned integer 16 data). A new optional keyword argument (uint16) was added to the open convenience function. Supplying a value of True for this argument will cause data of this type to be read in and scaled into an unsigned integer 16 array, instead of a float 32 array. If a HDU associated with a file that was opened with the uint16 option and containing unsigned integer 16 data is written to a file, the data will be reverse scaled into an integer 16 array and written out to the file and the BSCALE/BZERO header cards will be written with the values 1 and 32768 respectively. (CHSHD736064) Reference the following example: >>> import pyfits >>> hdul=pyfits.open('o4sp040b0_raw.fits',uint16=1) >>> hdul[1].data array([[1507, 1509, 1505, ..., 1498, 1500, 1487], [1508, 1507, 1509, ..., 1498, 1505, 1490], [1505, 1507, 1505, ..., 1499, 1504, 1491], ..., [1505, 1506, 1507, ..., 1497, 1502, 1487], [1507, 1507, 1504, ..., 1495, 1499, 1486], [1515, 1507, 1504, ..., 1492, 1498, 1487]], dtype=uint16) >>> hdul.writeto('tmp.fits') >>> hdul1=pyfits.open('tmp.fits',uint16=1) >>> hdul1[1].data array([[1507, 1509, 1505, ..., 1498, 1500, 1487], [1508, 1507, 1509, ..., 1498, 1505, 1490], [1505, 1507, 1505, ..., 1499, 1504, 1491], ..., [1505, 1506, 1507, ..., 1497, 1502, 1487], [1507, 1507, 1504, ..., 1495, 1499, 1486], [1515, 1507, 1504, ..., 1492, 1498, 1487]], dtype=uint16) >>> hdul1=pyfits.open('tmp.fits') >>> hdul1[1].data array([[ 1507., 1509., 1505., ..., 1498., 1500., 1487.], [ 1508., 1507., 1509., ..., 1498., 1505., 1490.], [ 1505., 1507., 1505., ..., 1499., 1504., 1491.], ..., [ 1505., 1506., 1507., ..., 1497., 1502., 1487.], [ 1507., 1507., 1504., ..., 1495., 1499., 1486.], [ 1515., 1507., 1504., ..., 1492., 1498., 1487.]], dtype=float32) - Enhanced the message generated when a ValueError exception is raised when attempting to access a header card with an unparsable value. The message now includes the Card name. The following bugs were fixed: - Corrected a bug that occurs when appending a binary table HDU to a fits file. Data was not being byteswapped on little endian machines. (CNSHD737243) - Corrected a bug that occurs when trying to write an ImageHDU that is missing the required PCOUNT card in the header. An UnboundLocalError exception complaining that the local variable 'insert_pos' was referenced before assignment was being raised in the method _ValidHDU.req_cards. The code was modified so that it would properly issue a more meaningful ValueError exception with a description of what required card is missing in the header. - Eliminated a redundant warning message about the PCOUNT card when validating an ImageHDU header with a PCOUNT card that is missing or has a value other than 0. .. _[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. The following enhancements were made: - Enhanced the way import errors are reported to provide more information. The following bugs were fixed: - Corrected a bug that occurs when a card value is a string and contains a colon but is not a record-valued keyword card. - Corrected a bug where pyfits fails to properly handle a record-valued keyword card with values using exponential notation and trailing blanks. 1.4 (2008-07-07) ================== Updates described in this release are only supported in the NUMPY version of pyfits. The following enhancements were made: - Added support for file objects and file like objects. - All convenience functions and class methods that take a file name will now also accept a file object or file like object. File like objects supported are StringIO and GzipFile objects. Other file like objects will work only if they implement all of the standard file object methods. - For the most part, file or file like objects may be either opened or closed at function call. An opened object must be opened with the proper mode depending on the function or method called. Whenever possible, if the object is opened before the method is called, it will remain open after the call. This will not be possible when writing a HDUList that has been resized or when writing to a GzipFile object regardless of whether it is resized. If the object is closed at the time of the function call, only the name from the object is used, not the object itself. The pyfits code will extract the file name used by the object and use that to create an underlying file object on which the function will be performed. - Added support for record-valued keyword cards as introduced in the "FITS WCS 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 point value. In a FITS header they have the following syntax:: keyword= 'field-specifier: float' where keyword is a standard eight-character FITS keyword name, float is the standard FITS ASCII representation of a floating point number, and these are separated by a colon followed by a single blank. The grammar for field-specifier is:: field-specifier: field field-specifier.field field: identifier identifier.index where identifier is a sequence of letters (upper or lower case), underscores, and digits of which the first character must not be a digit, and index is a sequence of digits. No blank characters may occur in the field-specifier. The index is provided primarily for defining array elements though it need not be used for that purpose. Multiple record-valued keywords of the same name but differing values may be present in a FITS header. The field-specifier may be viewed as part of the keyword name. Some examples follow:: DP1 = 'NAXIS: 2' DP1 = 'AXIS.1: 1' DP1 = 'AXIS.2: 2' DP1 = 'NAUX: 2' DP1 = 'AUX.1.COEFF.0: 0' DP1 = 'AUX.1.POWER.0: 1' DP1 = 'AUX.1.COEFF.1: 0.00048828125' DP1 = 'AUX.1.POWER.1: 1' - As with standard header cards, the value of a record-valued keyword card can be accessed using either the index of the card in a HDU's header or via the keyword name. When accessing using the keyword name, the user may specify just the card keyword or the card keyword followed by a period followed by the field-specifier. Note that while the card keyword is case insensitive, the field-specifier is not. Thus, hdu['abc.def'], hdu['ABC.def'], or hdu['aBc.def'] are all equivalent but hdu['ABC.DEF'] is not. - When accessed using the card index of the HDU's header the value returned will be the entire string value of the card. For example: >>> print hdr[10] NAXIS: 2 >>> print hdr[11] AXIS.1: 1 - When accessed using the keyword name exclusive of the field-specifier, the entire string value of the header card with the lowest index having that keyword name will be returned. For example: >>> print hdr['DP1'] NAXIS: 2 - When accessing using the keyword name and the field-specifier, the value returned will be the floating point value associated with the record-valued keyword card. For example: >>> print hdr['DP1.NAXIS'] 2.0 - Any attempt to access a non-existent record-valued keyword card value will cause an exception to be raised (IndexError exception for index access or KeyError for keyword name access). - Updating the value of a record-valued keyword card can also be accomplished using either index or keyword name. For example: >>> print hdr['DP1.NAXIS'] 2.0 >>> hdr['DP1.NAXIS'] = 3.0 >>> print hdr['DP1.NAXIS'] 3.0 - Adding a new record-valued keyword card to an existing header is accomplished using the Header.update() method just like any other card. For example: >>> hdr.update('DP1', 'AXIS.3: 1', 'a comment', after='DP1.AXIS.2') - Deleting a record-valued keyword card from an existing header is accomplished using the standard list deletion syntax just like any other card. For example: >>> del hdr['DP1.AXIS.1'] - In addition to accessing record-valued keyword cards individually using a card index or keyword name, cards can be accessed in groups using a set of special pattern matching keys. This access is made available via the standard list indexing operator providing a keyword name string that contains one or more of the special pattern matching keys. Instead of returning a value, a CardList object will be returned containing shared instances of the Cards in the header that match the given keyword specification. - There are three special pattern matching keys. The first key '*' will match any string of zero or more characters within the current level of the field-specifier. The second key '?' will match a single character. The third key '...' must appear at the end of the keyword name string and will match all keywords that match the preceding pattern down all levels of the field-specifier. All combinations of ?, \*, and ... are permitted (though ... is only permitted at the end). Some examples follow: >>> cl=hdr['DP1.AXIS.*'] >>> print cl DP1 = 'AXIS.1: 1' DP1 = 'AXIS.2: 2' >>> cl=hdr['DP1.*'] >>> print cl DP1 = 'NAXIS: 2' DP1 = 'NAUX: 2' >>> cl=hdr['DP1.AUX...'] >>> print cl DP1 = 'AUX.1.COEFF.0: 0' DP1 = 'AUX.1.POWER.0: 1' DP1 = 'AUX.1.COEFF.1: 0.00048828125' DP1 = 'AUX.1.POWER.1: 1' >>> cl=hdr['DP?.NAXIS'] >>> print cl DP1 = 'NAXIS: 2' DP2 = 'NAXIS: 2' DP3 = 'NAXIS: 2' >>> cl=hdr['DP1.A*S.*'] >>> print cl DP1 = 'AXIS.1: 1' DP1 = 'AXIS.2: 2' - The use of the special pattern matching keys for adding or updating header cards in an existing header is not allowed. However, the deletion of cards from the header using the special keys is allowed. For example: >>> del hdr['DP3.A*...'] - As noted above, accessing pyfits Header object using the special pattern matching keys will return a CardList object. This CardList object can itself be searched in order to further refine the list of Cards. For example: >>> cl=hdr['DP1...'] >>> print cl DP1 = 'NAXIS: 2' DP1 = 'AXIS.1: 1' DP1 = 'AXIS.2: 2' DP1 = 'NAUX: 2' DP1 = 'AUX.1.COEFF.1: 0.000488' DP1 = 'AUX.2.COEFF.2: 0.00097656' >>> cl1=cl['*.*AUX...'] >>> print cl1 DP1 = 'NAUX: 2' DP1 = 'AUX.1.COEFF.1: 0.000488' DP1 = 'AUX.2.COEFF.2: 0.00097656' - The CardList keys() method will allow the retrieval of all of the key values in the CardList. For example: >>> cl=hdr['DP1.AXIS.*'] >>> print cl DP1 = 'AXIS.1: 1' DP1 = 'AXIS.2: 2' >>> cl.keys() ['DP1.AXIS.1', 'DP1.AXIS.2'] - The CardList values() method will allow the retrieval of all of the values in the CardList. For example: >>> cl=hdr['DP1.AXIS.*'] >>> print cl DP1 = 'AXIS.1: 1' DP1 = 'AXIS.2: 2' >>> cl.values() [1.0, 2.0] - Individual cards can be retrieved from the list using standard list indexing. For example: >>> cl=hdr['DP1.AXIS.*'] >>> c=cl[0] >>> print c DP1 = 'AXIS.1: 1' >>> c=cl['DP1.AXIS.2'] >>> print c DP1 = 'AXIS.2: 2' - Individual card values can be retrieved from the list using the value attribute of the card. For example: >>> cl=hdr['DP1.AXIS.*'] >>> cl[0].value 1.0 - The cards in the CardList are shared instances of the cards in the source header. Therefore, modifying a card in the CardList also modifies it in the source header. However, making an addition or a deletion to the CardList will not affect the source header. For example: >>> hdr['DP1.AXIS.1'] 1.0 >>> cl=hdr['DP1.AXIS.*'] >>> cl[0].value = 4.0 >>> hdr['DP1.AXIS.1'] 4.0 >>> del cl[0] >>> print cl['DP1.AXIS.1'] Traceback (most recent call last): ... KeyError: "Keyword 'DP1.AXIS.1' not found." >>> hdr['DP1.AXIS.1'] 4.0 - A FITS header consists of card images. In pyfits each card image is manifested by a Card object. A pyfits Header object contains a list of Card objects in the form of a CardList object. A record-valued keyword card image is represented in pyfits by a RecordValuedKeywordCard object. This object inherits from a Card object and has all of the methods and attributes of a Card object. - A new RecordValuedKeywordCard object is created with the RecordValuedKeywordCard constructor: RecordValuedKeywordCard(key, value, comment). The key and value arguments may be specified in two ways. The key value may be given as the 8 character keyword only, in which case the value must be a character string containing the field-specifier, a colon followed by a space, followed by the actual value. The second option is to provide the key as a string containing the keyword and field-specifier, in which case the value must be the actual floating point value. For example: >>> c1 = pyfits.RecordValuedKeywordCard('DP1', 'NAXIS: 2', 'Number of variables') >>> c2 = pyfits.RecordValuedKeywordCard('DP1.AXIS.1', 1.0, 'Axis number') - RecordValuedKeywordCards have attributes .key, .field_specifier, .value, and .comment. Both .value and .comment can be changed but not .key or .field_specifier. The constructor will extract the field-specifier from the input key or value, whichever is appropriate. The .key attribute is the 8 character keyword. - Just like standard Cards, a RecordValuedKeywordCard may be constructed from a string using the fromstring() method or verified using the verify() method. For example: >>> c1 = pyfits.RecordValuedKeywordCard().fromstring( "DP1 = 'NAXIS: 2' / Number of independent variables") >>> c2 = pyfits.RecordValuedKeywordCard().fromstring( "DP1 = 'AXIS.1: X' / Axis number") >>> print c1; print c2 DP1 = 'NAXIS: 2' / Number of independent variables DP1 = 'AXIS.1: X' / Axis number >>> c2.verify() Output verification result: Card image is not FITS standard (unparsable value string). - A standard card that meets the criteria of a RecordValuedKeywordCard may be turned into a RecordValuedKeywordCard using the class method coerce. If the card object does not meet the required criteria then the original card object is just returned. >>> c1 = pyfits.Card('DP1','AUX: 1','comment') >>> c2 = pyfits.RecordValuedKeywordCard.coerce(c1) >>> print type(c2) <'pyfits.NP_pyfits.RecordValuedKeywordCard'> - Two other card creation methods are also available as RecordVauedKeywordCard class methods. These are createCard() which will create the appropriate card object (Card or RecordValuedKeywordCard) given input key, value, and comment, and createCardFromString which will create 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') or >>> c1 = pyfits.createCard('DP1','AUX: 1','comment') >>> print type(c1) <'pyfits.NP_pyfits.RecordValuedKeywordCard'> >>> c1 = pyfits.RecordValuedKeywordCard.createCard('DP1','AUX 1','comment') or >>> c1 = pyfits.createCard('DP1','AUX 1','comment') >>> print type(c1) <'pyfits.NP_pyfits.Card'> >>> c1 = pyfits.RecordValuedKeywordCard.createCardFromString \ ("DP1 = 'AUX: 1.0' / comment") or >>> c1 = pyfits.createCardFromString("DP1 = 'AUX: 1.0' / comment") >>> print type(c1) <'pyfits.NP_pyfits.RecordValuedKeywordCard'> The following bugs were fixed: - Corrected a bug that occurs when writing a HDU out to a file. During the write, any Keyboard Interrupts are trapped so that the write completes before the interrupt is handled. Unfortunately, the Keyboard Interrupt was not properly reinstated after the write completed. This was fixed. (CNSHD711138) - Corrected a bug when using ipython, where temporary files created with the tempFile.NamedTemporaryFile method are not automatically removed. This can happen for instance when opening a Gzipped fits file or when open a fits file over the internet. The files will now be removed. (CNSHD718307) - Corrected a bug in the append convenience function's call to the writeto convenience function. The classExtensions argument must be passed as a keyword argument. - Corrected a bug that occurs when retrieving variable length character arrays from binary table HDUs (PA() format) and using slicing to obtain rows of data containing variable length arrays. The code issued a TypeError exception. The data can now be accessed with no exceptions. (CNSHD718749) - Corrected a bug that occurs when retrieving data from a fits file opened in memory map mode when the file contains multiple image extensions or ASCII table or binary table HDUs. The code issued a TypeError exception. The data can now be accessed with no exceptions. (CNSHD707426) - Corrected a bug that occurs when attempting to get a subset of data from a Binary Table HDU and then use the data to create a new Binary Table HDU object. A TypeError exception was raised. The data can now be subsetted and used to create a new HDU. (CNSHD723761) - Corrected a bug that occurs when attempting to scale an Image HDU back to its original data type using the _ImageBaseHDU.scale method. The code was not resetting the BITPIX header card back to the original data type. This has been corrected. - Changed the code to issue a KeyError exception instead of a NameError exception when accessing a non-existent field in a table. 1.3 (2008-02-22) ================== Updates described in this release are only supported in the NUMPY version of pyfits. The following enhancements were made: - Provided support for a new extension to pyfits called *stpyfits*. - The *stpyfits* module is a wrapper around pyfits. It provides all of the features and functions of pyfits along with some STScI specific features. Currently, the only new feature supported by stpyfits is the ability to read and write fits files that contain image data quality extensions with constant data value arrays. See stpyfits `[2]`_ for more details on stpyfits. - Added a new feature to allow trailing HDUs to be deleted from a fits 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. - Updated pyfits to use the warnings module to issue warnings. All warnings will still be issued to stdout, exactly as they were before, however, you may now suppress warnings with the -Wignore command line option. For example, to run a script that will ignore warnings use the following command line syntax: python -Wignore yourscript.py - Updated the open convenience function to allow the input of an already opened file object in place of a file name when opening a fits file. - Updated the writeto convenience function to allow it to accept the output_verify option. - In this way, the user can use the argument output_verify='fix' to allow pyfits to correct any errors it encounters in the provided header before writing the data to the file. - Updated the verification code to provide additional detail with a VerifyError exception. - Added the capability to create a binary table HDU directly from a numpy.ndarray. This may be done using either the new_table convenience function or the BinTableHDU constructor. The following performance improvements were made: - Modified the import logic to dramatically decrease the time it takes to import pyfits. - Modified the code to provide performance improvements when copying and examining header cards. The following bugs were fixed: - Corrected a bug that occurs when reading the data from a fits file that includes BZERO/BSCALE scaling. When the data is read in from the file, pyfits automatically scales the data using the BZERO/BSCALE values in the header. In the previous release, pyfits created a 32 bit floating point array to hold the scaled data. This could cause a problem when the value of BZERO is so large that the scaled value will not fit into the float 32. For this release, when the input data is 32 bit integer, a 64 bit floating point array is used for the scaled data. - Corrected a bug that caused an exception to be raised when attempting to scale image data using the ImageHDU.scale method. - Corrected a bug in the new_table convenience function that occurred when a binary table was created using a ColDefs object as input and supplying an nrows argument for a number of rows that is greater than the number of rows present in the input ColDefs object. The previous version of pyfits failed to allocate the necessary memory for the additional rows. - Corrected a bug in the new_table convenience function that caused an exception to be thrown when creating an ASCII table. - Corrected a bug in the new_table convenience function that will allow the input of a ColDefs object that was read from a file as a binary table with a data value equal to None. - Corrected a bug in the construction of ASCII tables from Column objects that are created with noncontinuous start columns. - Corrected bugs in a number of areas that would sometimes cause a failure to improperly raise an exception when an error occurred. - Corrected a bug where attempting to open a non-existent fits file on a windows platform using a drive letter in the file specification caused a misleading IOError exception to be raised. .. _[2]: https://stscitools.readthedocs.io/en/latest/stpyfits.html 1.1 (2007-06-15) ================== - Modified to use either NUMPY or NUMARRAY. - New file writing modes have been provided to allow streaming data to extensions without requiring the whole output extension image in memory. See documentation on StreamingHDU. - Improvements to minimize byteswapping and memory usage by byteswapping in place. - Now supports ':' characters in filenames. - Handles keyboard interrupts during long operations. - Preserves the byte order of the input image arrays. 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 fitsdiff which were distributed with PyFITS in previous releases were moved to pytools. This release of PyFITS is v1.0.1. The next release of PyFITS will support both numarray and numpy (and will be available separately from stsci_python, as are all the python packages contained within stsci_python). An alpha release for PyFITS numpy support will be made around the time of this stsci_python release. - Updated docstrings for public functions. - Made some previously public functions private. 1.0 (2005-11-01) ================== Major Changes since v0.9.6: - Added support for the HIERARCH convention - Added support for iteration and slicing for HDU lists - PyFITS now uses the standard setup.py installation script - Add utility functions at the module level, they include: - getheader - getdata - getval - writeto - append - update - info Minor changes since v0.9.6: - Fix a bug to make single-column ASCII table work. - Fix a bug so a new table constructed from an existing table with X-formatted columns will work. - Fix a problem in verifying HDUList right after the open statement. - Verify that elements in an HDUList, besides the first one, are ExtensionHDU. - Add output verification in methods flush() and close(). - Modify the design of the open() function to remove the output_verify argument. - 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, e.g. combining two column definitions. - Replace the Boolean class with the Python Boolean type. The old TRUE/FALSE will still work. - Convert classes to the new style. - Better format when printing card or card list. - Add the optional argument clobber to all writeto() functions and methods. - If adding a blank card, will not use existing blank card's space. PyFITS Version 1.0 REQUIRES Python 2.3 or later. 0.9.6 (2004-11-11) ==================== Major changes since v0.9.3: - Support for variable length array tables. - Support for writing ASCII table extensions. - Support for random groups, both reading and writing. Some minor changes: - Support for numbers with leading zeros in an ASCII table extension. - Changed scaled columns' data type from Float32 to Float64 to preserve precision. - Made Column constructor more flexible in accepting format specification. 0.9.3 (2004-07-02) ==================== Changes since v0.9.0: - 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. - Support the X format (bit flags) columns, both reading and writing, in a 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. - 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: - Rewriting of the Card class to separate the parsing and verification of header cards - Restructure the keyword indexing scheme which speed up certain applications (update large number of new keywords and reading a header with larger numbers of cards) by a factor of 30 or more - Change the default to be lenient FITS standard checking on input and strict FITS standard checking on output - Support CONTINUE cards, both reading and writing - Verification can now be performed at any of the HDUList, HDU, and Card levels - Support (contiguous) subsection (attribute .section) of images to reduce memory usage for large images 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 must be updated simultaneously. Changes since 0.7.6: - Compatible with numarray 0.6/records 2.0 - For binary tables, now it is possible to update the original array if a scaled field is updated. - Support of complex columns - Modify the __getitem__ method in FITS_rec. In order to make sure the scaled 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 close HDUList object. Earlier version does not close memmap object and can cause memory lockup. - Enable 'update' as a legitimate memmap mode. - Do not print message when closing an HDUList object which is not created from reading a FITS file. Such message is confusing. - remove the internal attribute "closed" and related method (__getattr__ in HDUList). It is redundant. 0.7.6 (2002-11-22) **NOTE:** This version will only work with numarray Version 0.4. Changes since 0.7.5: - Change x*=n to numarray.multiply(x, n, x) where n is a floating number, in 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 misalignment as shown below: After running drizzle on ACS images it creates a CD matrix whose elements have very many digits, *e.g.*: CD1_1 = 1.1187596304411E-05 / partial of first axis coordinate w.r.t. x CD1_2 = -8.502767249350019E-06 / partial of first axis coordinate w.r.t. y with pyfits, an "update" on these header items and write in new values which has fewer digits, *e.g.*: CD1_1 = 1.0963011E-05 / partial of first axis coordinate w.r.t. x CD1_2 = -8.527229E-06 / partial of first axis coordinate w.r.t. y - 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 __number_RE _number_RE. tmpName() _tmpName() dimShape _dimShape ErrList _ErrList - 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 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 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: - Memory mapping now works for readonly mode, both for images and binary tables. Usage: pyfits.open('filename', memmap=1) - Edit the field method in FITS_rec class to make the column scaling for numbers use less temporary memory. (does not work under 2.2, due to Python "bug" of array \*=) - Delete bscale/bzero in the ImageBaseHDU constructor. - Update bitpix in BaseImageHDU.__getattr__ after deleting bscale/bzero. (bug fix) - In BaseImageHDU.__getattr__ point self.data to raw_data if float and if not memmap. (bug fix). - Change the function get_tbdata() to private: _get_tbdata(). 0.7.3 (2002-07-12) ==================== Changes since v0.7.2: - It will scale all integer image data to Float32, if BSCALE/BZERO != 1/0. It will also expunge the BSCALE/BZERO keywords. - Add the scale() method for ImageBaseHDU, so data can be scaled just before being written to the file. It has the following arguments: type: destination data type (string), e.g. Int32, Float32, UInt8, etc. option: scaling scheme. if 'old', use the old BSCALE/BZERO values. if 'minmax', use the data range to fit into the full range of specified integer type. Float destination data type will not be scaled for this option. bscale/bzero: user specifiable BSCALE/BZERO values. They overwrite the "option". - Deal with data area resizing in 'update' mode. - Make the data scaling (both input and output) faster and use less memory. - Bug fix to make column name change takes effect for field. - Bug fix to avoid exception if the key is not present in the header already. This affects (fixes) add_history(), add_comment(), and add_blank(). - Bug fix 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.1 (2002-06-25) ====================== 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(). - 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: - 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. 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. - 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. - 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. - Add the argument output_verify to the open method and writeto method. Not 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. - The tostring() call in writeHDUdata takes up extra space to store the string object. Use tofile() instead, to save space. - Make changes from _byteswap to _byteorder, following corresponding changes in numarray and recarray. - Insert(update) EXTEND in PrimaryHDU only when header is None. - 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. - Add checking of data against header, update header keywords (NAXIS's, BITPIX) when they don't agree with the data. - change version to __version__. There are also many other minor internal bug fixes and technical changes. 0.6.2 (2002-02-12) ==================== This version requires numarray version 0.2. 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 about a month. Currently, one may construct FITS headers that are inconsistent with the data and write such FITS objects to disk. Future versions will provide options to either a) correct discrepancies and warn, b) correct discrepancies silently, c) throw a Python exception, or d) write illegal FITS (for test purposes!). - Support for ascii tables or random groups format. Support for ASCII tables will be done soon (~1 month). When random group support is added is uncertain. - Support for memory mapping FITS data (to reduce memory demands). We expect to provide this capability in about 3 months. - Support for columns in binary tables having scaled values (e.g. BSCALE or BZERO) or boolean values. Currently booleans are stored as Int8 arrays and users must explicitly convert them into a boolean array. Likewise, scaled columns must be copied with scaling and offset by testing for those attributes explicitly. Future versions will produce such copies automatically. - 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: https://github.com/spacetelescope/pyfits astropy-astropy-201cddb/docs/io/fits/index.rst000066400000000000000000001231121507226315300215440ustar00rootroot00000000000000.. currentmodule:: astropy.io.fits .. _astropy-io-fits: ************************************** FITS File Handling (`astropy.io.fits`) ************************************** Introduction ============ 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. See also the :ref:`FAQ ` for answers to common questions and issues. Reading and Updating Existing FITS Files ---------------------------------------- Opening a 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 >>> 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: >>> 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: >>> 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 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. 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 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 FITS Headers ^^^^^^^^^^^^^^^^^^^^^^^^^ As mentioned earlier, each element of an :class:`HDUList` is an HDU object with ``.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 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 :class:`Header` instance, another ``astropy`` object. To get the value associated with a header keyword, do (à la Python dicts):: >>> hdul = fits.open(fits_image_filename) >>> hdul[0].header['DATE'] '01/04/99' 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 convenience. If the specified keyword name does not exist, it will raise a `KeyError` exception. We can also get the keyword value by indexing (à la Python lists):: >>> hdul[0].header[7] 32768.0 This example returns the eighth (like Python lists, it is 0-indexed) keyword's value — a float — 32768.0. Similarly, it is possible 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 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:: >>> hdr = hdul[0].header >>> hdr['targname'] = ('NGC121-a', 'the observation target') >>> hdr['targname'] 'NGC121-a' >>> hdr.comments['targname'] 'the observation target' 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:: >>> 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:: >>> 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 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 To see the entire header as it appears in the FITS file (with the END card and padding stripped), enter the header object by itself, or ``print(repr(hdr))``:: >>> 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 ... >>> 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 ... 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. 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:: >>> list(hdr.keys()) # doctest: +ELLIPSIS ['SIMPLE', 'BITPIX', 'NAXIS', ...] .. note:: See also :ref:`io-fits-intro-convenience-functions`. .. _structural_keywords: 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 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`` `~numpy.ndarray` object. Refer to the ``numpy`` documentation for details on manipulating these numerical arrays:: >>> data = hdul[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):: >>> data = hdul['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:: >>> data = hdul['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, for example:: >>> 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:: >>> 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 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:: >>> 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 subsection:: >>> data[30:40, 10:20] = data[1, 4] = 999 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:: >>> photflam = hdul[1].header['photflam'] >>> exptime = hdr['exptime'] >>> data = data * photflam / exptime >>> hdul.close() 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 of the changes you made and write it to a new file, you can use the :meth:`HDUList.writeto` method (see below). .. _Numpy documentation: https://numpy.org/doc/stable/reference/routines.indexing.html .. note:: See more information in :doc:`/io/fits/usage/image`. Working with Table Data ^^^^^^^^^^^^^^^^^^^^^^^ .. 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:: >>> 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. To see the first row of the table:: >>> print(data[0]) (np.int32(1), 'abc', np.float64(3.7000000715255736), np.False_) 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:: >>> data.field(0) array([1, 2]...) 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:: >>> data.field('c1') array([1, 2]...) When accessing a column by name, dict-like access is also possible (and even preferable):: >>> data['c1'] array([1, 2]...) 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 from the interactive prompt:: >>> cols.info() 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 ['c1', 'c2', 'c3', 'c4'] returns a (Python) list of field names. 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:: >>> data['c3'].mean() # doctest: +FLOAT_CMP np.float64(5.19999989271164) and so on. Save File Changes ^^^^^^^^^^^^^^^^^ As mentioned earlier, after a user opened a file, made a few changes to either 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: .. 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 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: .. code:: python with fits.open('original.fits', mode='update') as hdul: # Change something in hdul. hdul.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 ------------------------ 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 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:: >>> import numpy as np >>> 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:: >>> hdu = fits.PrimaryHDU(data=data) We then create an :class:`HDUList` to contain the newly created primary HDU, and write to a new file:: >>> hdul = fits.HDUList([hdu]) >>> hdul.writeto('new1.fits') That is it! In fact, ``astropy`` even provides a shortcut for the last two lines to accomplish the same behavior:: >>> 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 ^^^^^^^^^^^^^^^^^^^^^^^^^ .. 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 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:: >>> 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) .. 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:`BinTableHDU.from_columns` function:: >>> hdu = fits.BinTableHDU.from_columns(cols) This function returns (in this case) a :class:`BinTableHDU`. 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. 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:: >>> 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) 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. .. note:: 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. 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`:: >>> hdr = fits.Header() >>> hdr['OBSERVER'] = 'Edwin Hubble' >>> hdr['COMMENT'] = "Here's some commentary about this FITS file." >>> empty_primary = fits.PrimaryHDU(header=hdr) 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. We then create an HDUList containing both the primary HDU and any other HDUs want:: >>> 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 task. By using these "convenience" functions, a user does not have to worry about 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 access:: >>> from astropy.io.fits import getheader >>> 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:: >>> 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 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:: >>> from astropy.io.fits import getval >>> # 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:: >>> from astropy.io.fits import getdata >>> # 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:: >>> 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, 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. .. 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 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(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 usage/headers usage/image usage/table usage/verification usage/unfamiliar usage/scripts usage/misc 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 ================= .. toctree:: :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 ============= .. automodule:: astropy.io.fits .. 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". astropy-astropy-201cddb/docs/io/fits/performance.inc.rst000066400000000000000000000027061507226315300235130ustar00rootroot00000000000000.. 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 ================ 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') .. TODO: determine whether the following is quantitatively true, and either .. uncomment or remove. .. Performance Tips .. ================ .. .. 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) astropy-astropy-201cddb/docs/io/fits/usage/000077500000000000000000000000001507226315300210075ustar00rootroot00000000000000astropy-astropy-201cddb/docs/io/fits/usage/cloud.rst000066400000000000000000000266371507226315300226650ustar00rootroot00000000000000.. 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.open`. You can pass these parameters via the ``fsspec_kwargs`` argument of `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 >>> fsspec_kwargs = {"block_size": 1_000_000, "cache_type": "bytes"} >>> with fits.open(url, use_fsspec=True, fsspec_kwargs=fsspec_kwargs) 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. astropy-astropy-201cddb/docs/io/fits/usage/headers.rst000066400000000000000000000425411507226315300231620ustar00rootroot00000000000000.. currentmodule:: astropy.io.fits FITS Headers ************ 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 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:: >>> 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 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 :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' >>> 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-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. As with Python's :class:`dict` type, new keywords can also be added to the header using assignment syntax:: >>> hdr = hdul[1].header >>> 'DARKCORR' in hdr # Check for existence False >>> hdr['DARKCORR'] = 'OMIT' # Add a new DARKCORR keyword You can also add a new value *and* comment by assigning them as a tuple:: >>> 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 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 be added at the end of the existing keywords, but before any HISTORY/COMMENT keywords at the end of the header. There are a couple of ways to override this functionality: * Use the :meth:`Header.append` method with the ``end=True`` argument: >>> 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: >>> 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. A keyword (and its corresponding card) can be deleted using the same index/name syntax:: >>> 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 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:: >>> del hdr[3:5] 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:: >>> 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 >>> 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:: >>> hdr['NAXIS'] 2 >>> 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 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:: >>> 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 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, 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. 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' >>> hdu.header['COMMENT'] = 'comment 1' >>> hdu.header['HISTORY'] = 'history 2' >>> hdu.header[''] = 'blank 2' >>> hdu.header['COMMENT'] = 'comment 2' and the part in the modified header becomes: .. parsed-literal:: HISTORY history 1 HISTORY history 2 blank 1 blank 2 COMMENT comment 1 COMMENT comment 2 Users can also directly control exactly where in the header to add a new commentary card by using the :meth:`Header.insert` method. .. note:: 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 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. 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 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)``. 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 >>> c3 = fits.Card('MIR_REVR', True, ... '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 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` class method can be used. 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. :: >>> c1 = fits.Card.fromstring('ABC = 3.456D023') >>> c2 = fits.Card.fromstring("P.I. ='Hubble'") >>> print(c1) ABC = 3.456D023 >>> print(c2) # doctest: +SKIP P.I. ='Hubble' >>> 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 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 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 >>> 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 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 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) HIERARCH abcdefghi = 10 >>> hdu = fits.PrimaryHDU() >>> hdu.header['hierarch abcdefghi'] = 99 >>> hdu.header['abcdefghi'] 99 >>> hdu.header['abcdefghi'] = 10 >>> hdu.header['abcdefghi'] 10 >>> 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 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 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. astropy-astropy-201cddb/docs/io/fits/usage/image.rst000066400000000000000000000262021507226315300226250ustar00rootroot00000000000000.. currentmodule:: astropy.io.fits Image Data ********** 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. 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:: **BITPIX** **Numpy Data Type** 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, 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). 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 ``data`` to zero. The second one is to take logarithm of the pixel values which are positive:: >>> 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. .. EXAMPLE END Scaled Data =========== 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 BSCALE and BZERO are stored as keywords of the same names in the header of the 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 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``). 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' >>> hdu.header['bitpix'] # BITPIX is also updated -32 >>> # 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 ``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 especially useful for updating some header values, while ensuring that the data is not modified. 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 ^^^^^^^^ .. 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, 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. 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 an example of what happens to the :attr:`~ImageHDU.data` attribute after the :meth:`~ImageHDU.scale` call:: >>> 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 [-32768 -32767 -32766 -32765] >>> hdu.writeto('new.fits') .. EXAMPLE END .. _data-sections: Data Sections ============= 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 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 ``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. ``astropy`` will do its best to join together discontiguous sections of the array while reading as little as possible into main memory. Sections cannot currently be assigned. Any modifications made to a data section are not saved back to the original file. .. EXAMPLE END astropy-astropy-201cddb/docs/io/fits/usage/misc.rst000066400000000000000000000032671507226315300225040ustar00rootroot00000000000000.. currentmodule:: astropy.io.fits Miscellaneous Features ********************** This section describes some of the miscellaneous features of :mod:`astropy.io.fits`. .. _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 components of a FITS file. The :class:`FITSDiff` class can be used to generate and represent the 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 :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. 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 astropy-astropy-201cddb/docs/io/fits/usage/scripts.rst000066400000000000000000000016251507226315300232340ustar00rootroot00000000000000Executable Scripts ****************** ``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. .. _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, 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 ``fitsdiff --help`` to see the full program usage documentation. astropy-astropy-201cddb/docs/io/fits/usage/table.rst000066400000000000000000000622241507226315300226360ustar00rootroot00000000000000 .. currentmodule:: astropy.io.fits Table Data ********** 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 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 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 sample record array:: >>> 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 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 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 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. 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 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. Examples ^^^^^^^^ .. 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 To merge the column definitions of the input tables:: >>> 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 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. 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 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 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 name and format. Here is a summary of all allowed formats for a binary table: .. parsed-literal:: **FITS format code Description 8-bit bytes** L logical (Boolean) 1 X bit \* B Unsigned byte 1 I 16-bit integer 2 J 32-bit integer 4 K 64-bit integer 8 A character 1 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 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 :ref:`variable_length_array_tables`) will also be discussed in a later chapter. Besides the required name and format arguments in constructing a :class:`Column`, there are many optional arguments which can be used in creating a column. Here is a list of these arguments and their corresponding header keywords and descriptions: .. parsed-literal:: **Argument Corresponding Description** **in Column() header keyword** name TTYPE column name format TFORM column format unit TUNIT unit null TNULL null value (only for B, I, and J) bscale TSCAL scaling factor for data bzero TZERO zero point for data scaling 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 ^^^^^^^^ .. EXAMPLE START Creating a FITS Table Here are a few Columns using various combinations of the optional arguments:: >>> counts = np.array([312, 334, 308, 317]) >>> names = np.array(['NGC1', 'NGC2', 'NGC3', 'NGC4']) >>> 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 "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:: >>> col1 = fits.Column(name='target', format='10A') Then call :meth:`BinTableHDU.from_columns`:: >>> 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 you can assign your array directly to the HDU's data attribute: .. 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). 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. astropy-astropy-201cddb/docs/io/fits/usage/unfamiliar.rst000066400000000000000000000600121507226315300236670ustar00rootroot00000000000000.. currentmodule:: astropy.io.fits Less Familiar Objects ********************* 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 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. :: >>> from astropy.io import fits >>> 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 , 0. , 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'). .. _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 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: .. parsed-literal:: Aw Character string Iw (Decimal) Integer 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 syntax difference between ASCII and binary tables can be confusing. For example, 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 (column) that have the same data type but have different lengths/dimensions. Compared with the standard table data structure, the variable length table can save storage space if there is a large dynamic range of data lengths in 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. 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`` 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). 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 ' .. 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 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, 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: .. parsed-literal:: \|BITPIX\| \* GCOUNT \* (PCOUNT + NAXIS2 \* NAXIS3 \* ... \* NAXISn) Header and Summary ------------------ Accessing the header of a Random Access Group HDU is no different from any other HDU; you can use the .header attribute. The content of the HDU can similarly be summarized by using the :meth:`HDUList.info` method:: >>> filename = fits.util.get_testdata_filepath('group.fits') >>> hdul = fits.open(filename) >>> hdul[0].header['groups'] True >>> 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 ``.data`` attribute. It includes both parameter(s) and image array(s). Examples ^^^^^^^^ .. EXAMPLE START Group Parameters in Random Access Group HDUs To show the contents of the third group, including parameters and data:: >>> 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 convention. To access the parameters, first find out what the parameter names are, with the ``.parnames`` attribute:: >>> 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:: >>> 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):: >>> 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:`~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:: >>> 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 :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, 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') >>> 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', '>> 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(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 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, 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 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 astropy-astropy-201cddb/docs/io/fits/usage/verification.rst000066400000000000000000000370731507226315300242350ustar00rootroot00000000000000.. currentmodule:: astropy.io.fits .. _fits_io_verification: Verification ************ ``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 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. On the other hand, when writing to an output FITS file, the content to be written must be strictly compliant to the FITS standard by default. This default behavior can be overwritten by several other options, so the user will 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 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 three ``astropy`` verification levels: 1. The HDU List 2. Each HDU 3. Each Card in the HDU Header 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. 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 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 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 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 the HDU List content to the output FITS file, whether or not it is conforming to the FITS standard. The ignore option is useful in the following situations: 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 nonstandard FITS file on purpose, possibly for testing or consistency. No warning message will be printed out. This is like a silent warning option (see below). **fix** 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) 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. 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. 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** 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. In addition the following combined options are available: * **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 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. 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 following keywords: .. parsed-literal:: SIMPLE = T / BITPIX = 8 / NAXIS = 0 If any of the mandatory keywords are missing or in the wrong order, the fix 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. 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':: >>> 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:: >>> c = fits.Card.fromstring('FIX2= 2') >>> c.verify('silentfix') >>> print(c) FIX2 = 2 3. String value without enclosing quotes:: >>> c = fits.Card.fromstring('FIX3 = string value without quotes') >>> c.verify('silentfix') >>> print(c) FIX3 = 'string value without quotes' 4. Missing equal sign before column nine in the card image. 5. Space between numbers and E or D in floating point values:: >>> c = fits.Card.fromstring('FIX5 = 2.4 e 03') >>> c.verify('silentfix') >>> print(c) FIX5 = 2.4E03 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. 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.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'") >>> 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 >>> 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 >>> 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 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 character string containing the unsigned integer value of the 32-bit 1's 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 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 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 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 header by supplying the checksum keyword argument with a value of 'datasum'. 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 astropy-astropy-201cddb/docs/io/misc.rst000066400000000000000000000014521507226315300204250ustar00rootroot00000000000000************************************************************** 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 astropy-astropy-201cddb/docs/io/misc_ref_api.rst000066400000000000000000000003371507226315300221130ustar00rootroot00000000000000Reference/API ************* .. automodapi:: astropy.io.misc .. automodapi:: astropy.io.misc.hdf5 .. automodapi:: astropy.io.misc.yaml .. automodapi:: astropy.io.misc.parquet .. automodapi:: astropy.io.misc.pyarrow.csv astropy-astropy-201cddb/docs/io/overview.rst000066400000000000000000000030221507226315300213330ustar00rootroot00000000000000Overview 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. astropy-astropy-201cddb/docs/io/registry.rst000066400000000000000000000156601507226315300213500ustar00rootroot00000000000000.. _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') 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') 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 ... # 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-custom-format', Table, my_table_writer) We can write the table out to a file:: 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') .. 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 astropy-astropy-201cddb/docs/io/registry_ref_api.rst000066400000000000000000000001011507226315300230150ustar00rootroot00000000000000Reference/API ************* .. automodapi:: astropy.io.registry astropy-astropy-201cddb/docs/io/typing.rst000066400000000000000000000032701507226315300210040ustar00rootroot00000000000000******************************** 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 astropy-astropy-201cddb/docs/io/unified.rst000066400000000000000000000043711507226315300211200ustar00rootroot00000000000000.. _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. .. doctest-skip:: >>> from astropy.table import Table >>> t = Table.read('photometry.dat', format='ascii.daophot') >>> t.write('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: .. doctest-skip:: >>> from astropy.table import Table >>> from astropy.nddata import CCDData >>> CCDData.read.help('fits') >>> Table.read.help('ascii') >>> Table.read.help('ascii.latex') >>> Table.write.help('hdf5') >>> Table.write.help('csv') astropy-astropy-201cddb/docs/io/unified_image.rst000066400000000000000000000022451507226315300222600ustar00rootroot00000000000000.. _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: .. doctest-skip:: >>> # Read CCD image >>> from astropy.nddata import CCDData >>> ccd = CCDData.read('image.fits') .. doctest-skip:: >>> # Write back CCD image >>> ccd.write('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 ========================================== ... astropy-astropy-201cddb/docs/io/unified_table.rst000066400000000000000000000320151507226315300222630ustar00rootroot00000000000000.. _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'``: .. doctest-skip:: >>> from astropy.table import Table >>> t = Table.read('photometry.dat', format='ascii.daophot') .. 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') # doctest: +SKIP .. EXAMPLE END .. EXAMPLE START Writing a LaTeX Table For writing a table, the format can be explicitly specified:: >>> t.write(filename, format='latex') # doctest: +SKIP 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.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``. astropy-astropy-201cddb/docs/io/unified_table_fits.rst000066400000000000000000000615151507226315300233170ustar00rootroot00000000000000.. _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") astropy-astropy-201cddb/docs/io/votable/dataorigin.rst000066400000000000000000000073431507226315300232540ustar00rootroot00000000000000Introduction ------------ 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 --------------- To extract DataOrigin from VOTable Example: VizieR catalogue J/AJ/167/18 .. doctest-remote-data-all:: >>> from astropy.io.votable import parse >>> from astropy.io.votable.dataorigin import extract_data_origin >>> votable = parse("https://vizier.cds.unistra.fr/viz-bin/conesearch/J/AJ/167/18/table4?RA=265.51&DEC=-22.71&SR=0.1") >>> data_origin = extract_data_origin(votable) >>> print(data_origin) # doctest: +IGNORE_OUTPUT 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 publisher: CDS 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. ... 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})") 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) editor: 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: .. doctest-skip:: >>> votable = parse("votable.xml") >>> dataorigin.add_data_origin_info(votable, "query", "Data center name") >>> dataorigin.add_data_origin_info(votable.resources[0], "creator", "Author name") astropy-astropy-201cddb/docs/io/votable/index.rst000066400000000000000000000240211507226315300222320ustar00rootroot00000000000000.. 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. It accepts either a string or Unicode path, or a Python file-like object:: votable.to_xml('output.xml') 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 astropy-astropy-201cddb/docs/io/votable/mivot_blocks.rst000066400000000000000000000107251507226315300236240ustar00rootroot00000000000000.. 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 astropy-astropy-201cddb/docs/io/votable/performance.inc.rst000066400000000000000000000007611507226315300242010ustar00rootroot00000000000000.. 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`. astropy-astropy-201cddb/docs/io/votable/ref_api.rst000066400000000000000000000014421507226315300225320ustar00rootroot00000000000000.. 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: astropy-astropy-201cddb/docs/io/votable/references.txt000066400000000000000000000037371507226315300232660ustar00rootroot00000000000000.. _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 astropy-astropy-201cddb/docs/io/votable/table_element.rst000066400000000000000000000204361507226315300237310ustar00rootroot00000000000000.. 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. astropy-astropy-201cddb/docs/known_issues.rst000066400000000000000000000260271507226315300216170ustar00rootroot00000000000000************ 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). astropy-astropy-201cddb/docs/license.rst000066400000000000000000000005311507226315300205020ustar00rootroot00000000000000******** 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. astropy-astropy-201cddb/docs/logging.rst000066400000000000000000000121361507226315300205120ustar00rootroot00000000000000************** 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 ======== The Astropy logging system is designed to give users flexibility in deciding which log messages to show, to capture them, and to send them to a file. All messages printed by Astropy routines should use the built-in logging facility (normal ``print()`` calls should only be done by routines that are explicitly requested to print output). Messages can have one of several levels: * DEBUG: Detailed information, typically of interest only when diagnosing problems. * INFO: An message conveying information about the current task, and confirming that things are working as expected * WARNING: An indication that something unexpected happened, and that user action may be required. * ERROR: indicates a more serious issue, including exceptions 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 ============================== First, import the logger:: from astropy import log The threshold level (defined above) for messages can be set with e.g.:: log.setLevel('DEBUG') Color (enabled by default) can be disabled with:: log.disable_color() and enabled with:: log.enable_color() Warnings from ``warnings.warn`` can be logged with:: log.enable_warnings_logging() which can be disabled with:: log.disable_warnings_logging() and exceptions can be included in the log with:: log.enable_exception_logging() which can be disabled with:: log.disable_exception_logging() It is also possible to set these settings from the Astropy configuration file, which also allows an overall log file to be specified. See `Using the configuration file`_ for more information. Context managers ================ In some cases, you may want to capture the log messages, for example to check whether a specific message was output, or to log the messages from a specific section of code to a file. Both of these are possible using context managers. To add the log messages to a list, first import the logger if you have not already done so:: from astropy import log then enclose the code in which you want to log the messages to a list in a ``with`` statement:: with log.log_to_list() as log_list: # your code here In the above example, once the block of code has executed, ``log_list`` will be a Python list containing all the Astropy logging messages that were raised. Note that messages continue to be output as normal. Similarly, you can output the log messages of a specific section of code to a file using:: with log.log_to_file('myfile.log'): # your code here which will add all the messages to ``myfile.log`` (this is in addition to the overall log file mentioned in `Using the configuration file`_). While these context managers will include all the messages emitted by the logger (using the global level set by ``log.setLevel``), it is possible to filter a subset of these using ``filter_level=``, and specifying one of ``'DEBUG'``, ``'INFO'``, ``'WARN'``, ``'ERROR'``. Note that if ``filter_level`` is a lower level than that set via ``setLevel``, only messages with the level set by ``setLevel`` or higher will be included (i.e. ``filter_level`` is only filtering a subset of the messages normally emitted by the logger). Similarly, it is possible to filter a subset of the messages by origin by specifying ``filter_origin=`` followed by a string. If the origin of a message starts with that string, the message will be included in the context manager. For example, ``filter_origin='astropy.wcs'`` will include only messages emitted in the ``astropy.wcs`` sub-package. Using the configuration file ============================ Options for the logger can be set in the ``[logger]`` section of the Astropy configuration file:: [logger] # Threshold for the logging messages. Logging messages that are less severe # than this level will be ignored. The levels are 'DEBUG', 'INFO', 'WARNING', # 'ERROR' log_level = 'INFO' # Whether to use color for the level names use_color = True # Whether to log warnings.warn calls log_warnings = False # Whether to log exceptions before raising them log_exceptions = False # Whether to always log messages to a log file log_to_file = True # 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 log_file_level = 'INFO' # 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: astropy-astropy-201cddb/docs/lts_policy.rst000066400000000000000000000004071507226315300212430ustar00rootroot00000000000000******************* 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. astropy-astropy-201cddb/docs/make.bat000066400000000000000000000107051507226315300177370ustar00rootroot00000000000000@ECHO OFF REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set BUILDDIR=_build set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% ) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^` where ^ is one of echo. html to make standalone HTML files echo. dirhtml to make HTML files named index.html in directories echo. singlehtml to make a single large HTML file echo. pickle to make pickle files echo. json to make JSON files echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. devhelp to make HTML files and a Devhelp project echo. epub to make an epub echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. text to make text files echo. man to make manual pages echo. changes to make an overview over 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 goto end ) 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 ) if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "singlehtml" ( %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "qthelp" ( %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Astropy.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Astropy.ghc goto end ) if "%1" == "devhelp" ( %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp if errorlevel 1 exit /b 1 echo. echo.Build finished. goto end ) if "%1" == "epub" ( %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub file is in %BUILDDIR%/epub. goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex if errorlevel 1 exit /b 1 echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "text" ( %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text if errorlevel 1 exit /b 1 echo. echo.Build finished. The text files are in %BUILDDIR%/text. goto end ) if "%1" == "man" ( %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man if errorlevel 1 exit /b 1 echo. echo.Build finished. The manual pages are in %BUILDDIR%/man. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes if errorlevel 1 exit /b 1 echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck if errorlevel 1 exit /b 1 echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest if errorlevel 1 exit /b 1 echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) :end astropy-astropy-201cddb/docs/modeling/000077500000000000000000000000001507226315300201255ustar00rootroot00000000000000astropy-astropy-201cddb/docs/modeling/add-units.rst000066400000000000000000000113131507226315300225460ustar00rootroot00000000000000.. _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. astropy-astropy-201cddb/docs/modeling/compound-models.rst000066400000000000000000001361771507226315300240030ustar00rootroot00000000000000.. 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) astropy-astropy-201cddb/docs/modeling/example-fitting-constraints.rst000066400000000000000000000141411507226315300263220ustar00rootroot00000000000000Fitting 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() astropy-astropy-201cddb/docs/modeling/example-fitting-line.rst000066400000000000000000000105521507226315300247040ustar00rootroot00000000000000.. _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() astropy-astropy-201cddb/docs/modeling/example-fitting-model-sets.rst000066400000000000000000000163721507226315300260370ustar00rootroot00000000000000.. _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() astropy-astropy-201cddb/docs/modeling/fitting.rst000066400000000000000000000204201507226315300223210ustar00rootroot00000000000000********************** 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") astropy-astropy-201cddb/docs/modeling/index.rst000066400000000000000000000063271507226315300217760ustar00rootroot00000000000000.. 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 astropy-astropy-201cddb/docs/modeling/jointfitter.rst000066400000000000000000000174001507226315300232220ustar00rootroot00000000000000.. _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) astropy-astropy-201cddb/docs/modeling/links.inc000066400000000000000000000004401507226315300217360ustar00rootroot00000000000000.. _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 astropy-astropy-201cddb/docs/modeling/models.rst000066400000000000000000001067361507226315300221570ustar00rootroot00000000000000.. 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`. astropy-astropy-201cddb/docs/modeling/new-fitter.rst000066400000000000000000000160741507226315300227530ustar00rootroot00000000000000.. _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 .. doctest-skip:: setup( # ... entry_points = {'astropy.modeling': 'PluginFitterName = fitter_module:PlugFitterClass'} ) This would allow users to import the ``PlugFitterName`` through `astropy.modeling.fitting` by .. doctest-skip:: 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 astropy-astropy-201cddb/docs/modeling/new-model.rst000066400000000000000000000302161507226315300225500ustar00rootroot00000000000000.. _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. astropy-astropy-201cddb/docs/modeling/parallel-fitting.rst000066400000000000000000000447651507226315300241350ustar00rootroot00000000000000.. _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. .. _parallel-fitinfo: 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. astropy-astropy-201cddb/docs/modeling/parameters.rst000066400000000000000000000217571507226315300230360ustar00rootroot00000000000000.. 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. astropy-astropy-201cddb/docs/modeling/performance.rst000066400000000000000000000015131507226315300231600ustar00rootroot00000000000000 .. _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. astropy-astropy-201cddb/docs/modeling/physical_models.rst000066400000000000000000000241521507226315300240420ustar00rootroot00000000000000.. _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 astropy-astropy-201cddb/docs/modeling/polynomial_models.rst000066400000000000000000000057441507226315300244170ustar00rootroot00000000000000.. 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 astropy-astropy-201cddb/docs/modeling/powerlaw_models.rst000066400000000000000000000006411507226315300240630ustar00rootroot00000000000000.. _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` astropy-astropy-201cddb/docs/modeling/predef_models1D.rst000066400000000000000000000121501507226315300236530ustar00rootroot00000000000000.. _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() astropy-astropy-201cddb/docs/modeling/predef_models2D.rst000066400000000000000000000117601507226315300236620ustar00rootroot00000000000000.. _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() astropy-astropy-201cddb/docs/modeling/reference_api.rst000066400000000000000000000016111507226315300234450ustar00rootroot00000000000000Reference/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 astropy-astropy-201cddb/docs/modeling/spline_models.rst000066400000000000000000000047141507226315300235220ustar00rootroot00000000000000.. 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``. astropy-astropy-201cddb/docs/modeling/units.rst000066400000000000000000000175101507226315300220250ustar00rootroot00000000000000.. _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) astropy-astropy-201cddb/docs/nddata/000077500000000000000000000000001507226315300175625ustar00rootroot00000000000000astropy-astropy-201cddb/docs/nddata/bitmask.rst000066400000000000000000000257761507226315300217670ustar00rootroot00000000000000.. _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 astropy-astropy-201cddb/docs/nddata/ccddata.rst000066400000000000000000000210221507226315300216740ustar00rootroot00000000000000.. _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`. For more details on setting flags, see `~astropy.nddata.NDData`. 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. astropy-astropy-201cddb/docs/nddata/covariance.rst000066400000000000000000000642101507226315300224310ustar00rootroot00000000000000 .. _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])) astropy-astropy-201cddb/docs/nddata/decorator.rst000066400000000000000000000050341507226315300223000ustar00rootroot00000000000000********************************************* 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): ... astropy-astropy-201cddb/docs/nddata/examples/000077500000000000000000000000001507226315300214005ustar00rootroot00000000000000astropy-astropy-201cddb/docs/nddata/examples/cutout2d_tofits.py000066400000000000000000000021041507226315300251100ustar00rootroot00000000000000# 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) astropy-astropy-201cddb/docs/nddata/index.rst000066400000000000000000000423541507226315300214330ustar00rootroot00000000000000.. _astropy_nddata: ***************************************** N-Dimensional Datasets (`astropy.nddata`) ***************************************** Introduction ============ 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`_. .. _astropy_nddata_getting_started: Getting Started =============== 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 >>> 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 --------- Building upon this pure container, `~astropy.nddata.NDDataRef` implements: + A ``read`` and ``write`` method to access ``astropy``'s unified file I/O interface. + Simple arithmetic like addition, subtraction, division, and multiplication. + Slicing. Instances are created in the same way:: >>> 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 >>> 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) .. EXAMPLE END .. _nddata_reading_writing: Reading and Writing ------------------- A `~astropy.nddata.CCDData` can be saved to a FITS file:: >>> 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 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 ============= .. toctree:: :maxdepth: 2 ref_api .. _APE 7: https://github.com/astropy/astropy-APEs/blob/main/APE7.rst astropy-astropy-201cddb/docs/nddata/mixins/000077500000000000000000000000001507226315300210715ustar00rootroot00000000000000astropy-astropy-201cddb/docs/nddata/mixins/index.rst000066400000000000000000000002221507226315300227260ustar00rootroot00000000000000Mixins for Added Functionality ****************************** .. toctree:: :maxdepth: 2 ndslicing.rst ndarithmetic.rst ndio.rst astropy-astropy-201cddb/docs/nddata/mixins/ndarithmetic.rst000066400000000000000000000352461507226315300243100ustar00rootroot00000000000000.. _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. astropy-astropy-201cddb/docs/nddata/mixins/ndio.rst000066400000000000000000000007411507226315300225560ustar00rootroot00000000000000.. _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. astropy-astropy-201cddb/docs/nddata/mixins/ndslicing.rst000066400000000000000000000117021507226315300235760ustar00rootroot00000000000000.. _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 astropy-astropy-201cddb/docs/nddata/nddata.rst000066400000000000000000000373351507226315300215620ustar00rootroot00000000000000.. _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 astropy-astropy-201cddb/docs/nddata/performance.inc.rst000066400000000000000000000020351507226315300233650ustar00rootroot00000000000000.. 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`. astropy-astropy-201cddb/docs/nddata/ref_api.rst000066400000000000000000000003411507226315300217170ustar00rootroot00000000000000Reference/API ************* .. automodapi:: astropy.nddata :no-inheritance-diagram: .. automodapi:: astropy.nddata.bitmask :no-inheritance-diagram: .. automodapi:: astropy.nddata.utils :no-inheritance-diagram: astropy-astropy-201cddb/docs/nddata/subclassing.rst000066400000000000000000000445451507226315300226450ustar00rootroot00000000000000.. _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]) astropy-astropy-201cddb/docs/nddata/utils.rst000066400000000000000000000307641507226315300214660ustar00rootroot00000000000000.. _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 astropy-astropy-201cddb/docs/nitpick-exceptions000066400000000000000000000043541507226315300221000ustar00rootroot00000000000000# 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 ndarray py:obj args # 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 astropy-astropy-201cddb/docs/robots.txt000066400000000000000000000003201507226315300203730ustar00rootroot00000000000000User-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: / astropy-astropy-201cddb/docs/rtd_environment.yaml000066400000000000000000000001471507226315300224320ustar00rootroot00000000000000name: rtd313 channels: - conda-forge - defaults dependencies: - python=3.13 - pip - graphviz astropy-astropy-201cddb/docs/samp/000077500000000000000000000000001507226315300172675ustar00rootroot00000000000000astropy-astropy-201cddb/docs/samp/advanced_embed_samp_hub.rst000066400000000000000000000105341507226315300246030ustar00rootroot00000000000000.. 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 astropy-astropy-201cddb/docs/samp/example_clients.rst000066400000000000000000000131031507226315300231730ustar00rootroot00000000000000.. 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() astropy-astropy-201cddb/docs/samp/example_hub.rst000066400000000000000000000025401507226315300223130ustar00rootroot00000000000000.. 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`. astropy-astropy-201cddb/docs/samp/example_table_image.rst000066400000000000000000000242051507226315300237700ustar00rootroot00000000000000.. 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']) astropy-astropy-201cddb/docs/samp/index.rst000066400000000000000000000065441507226315300211410ustar00rootroot00000000000000.. 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. astropy-astropy-201cddb/docs/samp/performance.inc.rst000066400000000000000000000007451507226315300231000ustar00rootroot00000000000000.. 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`. astropy-astropy-201cddb/docs/samp/ref_api.rst000066400000000000000000000000721507226315300214250ustar00rootroot00000000000000Reference/API ************* .. automodapi:: astropy.samp astropy-astropy-201cddb/docs/stats/000077500000000000000000000000001507226315300174655ustar00rootroot00000000000000astropy-astropy-201cddb/docs/stats/circ.rst000066400000000000000000000006021507226315300211350ustar00rootroot00000000000000.. _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. astropy-astropy-201cddb/docs/stats/index.rst000066400000000000000000000151271507226315300213340ustar00rootroot00000000000000.. _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 astropy-astropy-201cddb/docs/stats/performance.inc.rst000066400000000000000000000020251507226315300232670ustar00rootroot00000000000000.. 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. astropy-astropy-201cddb/docs/stats/ref_api.rst000066400000000000000000000000731507226315300216240ustar00rootroot00000000000000Reference/API ************* .. automodapi:: astropy.stats astropy-astropy-201cddb/docs/stats/ripley.rst000066400000000000000000000064451507226315300215340ustar00rootroot00000000000000.. _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*. astropy-astropy-201cddb/docs/stats/robust.rst000066400000000000000000000161561507226315300215460ustar00rootroot00000000000000.. _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) astropy-astropy-201cddb/docs/table/000077500000000000000000000000001507226315300174165ustar00rootroot00000000000000astropy-astropy-201cddb/docs/table/access_table.rst000066400000000000000000001024041507226315300225610ustar00rootroot00000000000000.. _access_table: Accessing a Table ***************** Accessing table properties and data is generally consistent with the basic interface for ``numpy`` `structured arrays `_. Basics ====== 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 a table** :: 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'}}) **Table properties** :: 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 **Access table data** :: t['a'] # Column 'a' t['a'][1] # Row 1 of column 'a' 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 t.pprint() # Same as above t.pprint(show_unit=True) # Show column unit t.pprint(show_name=False) # Do not show column names 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 t['a'].pprint() # Same as above, with same options as Table.pprint() 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 values as a list Details ======= 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, dtype=np.int32).reshape(5, 3) >>> t = Table(arr, names=('a', 'b', 'c'), meta={'keywords': {'key1': 'val1'}}) >>> 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 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 metadata, and number of table rows. The table metadata is a :class:`dict` by default. :: >>> t.columns >>> t.colnames ['a', 'b', 'c'] >>> t.meta # Dict of meta-data {'keywords': {'key1': 'val1'}} >>> len(t) 5 Accessing Data -------------- 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' 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 np.int32(3) A |Row| object has the same columns and metadata as its parent table:: >>> t[1].columns >>> t[1].meta {'keywords': {'key1': 'val1'}} 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)
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 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()` 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.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. 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 ... 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.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 | **r** : refresh same page | **n** : next row | **p** : previous row | **<** : go to beginning | **>** : go to end | **q** : quit browsing | **h** : print this help pprint() method ^^^^^^^^^^^^^^^ 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['col0'].unit = 'km**2' >>> t['col29'].unit = 'kg sec m**-2' >>> t.pprint(max_lines=8, max_width=40) col0 ... col29 km2 ... kg sec m**-2 ------------ ... ------------ 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_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) 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 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 ... 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.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) 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 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:: >>> t = Table() >>> 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 ----------- 1.0 .. 20.0 3.0 .. 40.0 5.0 .. 60.0 In order to see all of the data values for a multidimensional column use the column representation. This uses the standard ``numpy`` mechanism for printing any 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 astropy-astropy-201cddb/docs/table/construct_table.rst000066400000000000000000001255611507226315300233550ustar00rootroot00000000000000.. _construct_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| and |QTable| constructors are in the `Initialization Details`_ section. However, the best way to understand how to make a table is by example. Examples ======== Setup ----- 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 QTable, Table, Column >>> from astropy import units as u >>> import numpy as np 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 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['a'] = [1, 4] >>> t['b'] = [2.0, 5.0] >>> t['c'] = ['x', 'y'] >>> 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 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 by the input data, so they must either be set using the ``names`` keyword or they will be automatically generated as ``col``. :: >>> 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
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 a new table by selecting columns and putting them into a Python list (e.g., ``[ t['c'], t['a'] ]``):: >>> Table([t['c'], t['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 |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])
a b int32 float64 ----- ------- 1 12.0 16 15.0 **Different types of column data** 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]], 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. Dict of Columns --------------- .. EXAMPLE START: Creating an Astropy Table from a Dictionary of Columns 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)
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', '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:: >>> 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'))
a b c float64 int64[2] str1 ------- -------- ---- 1.0 2 .. 3 x 4.0 5 .. 6 y 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): ... 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:: >>> 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', 'i4'), ('b', 'f8'), ('c', 'U2')]) From ``arr`` it is possible to create the corresponding |Table| object:: >>> Table(arr)
a b c int32 float64 str2 ----- ------- ---- 1 2.0 x 4 5.0 y 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) 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** The column names can be changed from the original values by providing the ``names`` argument:: >>> Table(arr, names=('a_new', 'b_new', 'c_new'))
a_new b_new c_new int32 float64 str2 ----- ------- ----- 1 2.0 x 4 5.0 y **New data types** The data type for each column can likewise be changed with ``dtype``:: >>> 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. **Basic example with automatic column names** :: >>> arr = np.array([[1, 2, 3], ... [4, 5, 6]], dtype=np.int32) >>> Table(arr)
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'), 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 as long as the data types are not changed:: >>> t = Table(arr, copy=False) 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 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) >>> print(Table(arr)) # Two columns, three rows col0 col1 ---- ---- 1 4 2 5 3 6 >>> 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 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')) >>> 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])
a b float64 float64 ------- ------- >>> Table([t.columns[0], t.columns['c']])
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`` : |ndarray|, :class:`dict`, :class:`list`, |Table|, or table-like object, optional Data to initialize table. ``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)** 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 ``dtype`` list (optional) must match the length of ``names`` and is used to override the existing ``data`` types. **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 ``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 ``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 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 ``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 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 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 ------------ 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*. 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')]) >>> print(arr['a']) # column "a" of the input array [1 4] >>> t = Table(arr, copy=False) >>> t['a'][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 occurs:: >>> t = Table(arr, copy=False, dtype=('f4', 'i4', 'S4')) Traceback (most recent call last): ... 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 =============================== 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`` should be provided as a keyword argument and you can optionally provide these values: ``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 data but not length or shape** Examples:: 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 a :class:`numpy.dtype`. See the reference for `data type objects `_. Examples include: - 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 provided, then the type is inferred using :func:`numpy.array`. When ``data`` is provided then the ``shape`` and ``length`` arguments are ignored. **Provide length and optionally shape, but not data** Examples:: 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. .. 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 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. ``".4f"`` will give four digits after the decimal in float format, or ``"6d"`` will give integers in six-character fields. **Old style format string** This corresponds to syntax like ``"%.4f" % value`` as documented in `printf-style String Formatting `_. ``"%.4f"`` to print four digits after the decimal in float format, or ``"%6d"`` to print an integer in a six-character wide field. **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 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, which is an enhanced ordered dictionary that provides easier ways to select multiple columns. There are a few key points to remember: - A |Table| can be initialized from a |TableColumns| object (``copy`` is always `True`). - Selecting multiple columns from a |TableColumns| object returns another |TableColumns| object. - Selecting one column from a |TableColumns| object returns a |Column|. There are a few different ways to select columns from a |TableColumns| object: **Select columns by name** :: >>> t = Table(names=('a', 'b', 'c', 'd')) >>> t.columns['d', 'c', 'b'] **Select columns by index slicing** :: >>> t.columns[0:2] # Select first two columns >>> t.columns[::-1] # Reverse column order **Select single columns by index or name** :: >>> 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:: >>> 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) astropy-astropy-201cddb/docs/table/implementation_details.rst000066400000000000000000000030371507226315300247050ustar00rootroot00000000000000 .. _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. astropy-astropy-201cddb/docs/table/index.rst000066400000000000000000000252321507226315300212630ustar00rootroot00000000000000.. _astropy-table: ***************************** Data Tables (`astropy.table`) ***************************** Introduction ============ `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: * 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`. The :ref:`astropy-table-and-dataframes` page provides the rationale for maintaining and using the dedicated `astropy.table` package instead of relying on `pandas`. Getting Started =============== The basic workflow for creating a table, accessing table elements, 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 columns of data named ``a``, ``b``, ``c``, and ``d``. These columns have integer, float, string, and |Quantity| values respectively:: >>> from astropy.table import QTable >>> import astropy.units as u >>> import numpy as np >>> 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| or a :class:`pandas.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 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:: >>> 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 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() # 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', 'd'] >>> len(t) 3 >>> t.meta {'name': 'first table'} Access the data by column or row using familiar ``numpy`` structured array syntax:: >>> t['a'] # Column 'a' 1 4 5 >>> t['a'][1] # Row 1 of column 'a' 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]['a'] # Column 'a' of row 1 np.int32(4) 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 d m / s --- ------- --- ----- 1 2.000 x 10.0 4 5.000 y 20.0 >>> print(t['a', 'c']) # Table with cols 'a' and 'c' a c --- --- 1 x 4 y 5 z :ref:`modify_table` in place is flexible and works as you would expect:: >>> 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", 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 'b' of rows 0 and 1 >>> print(t) 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'] 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_row([-8, 'string', 10 * u.cm / u.s, 10]) >>> t['d'] Tables can be used for data with missing values:: >>> 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`` =============== 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 Mixin Columns ------------- .. toctree:: :maxdepth: 2 mixin_columns.rst Astropy Table and DataFrames ---------------------------- .. toctree:: :maxdepth: 2 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 ============= .. toctree:: :maxdepth: 2 ref_api astropy-astropy-201cddb/docs/table/indexing.rst000066400000000000000000000214001507226315300217520ustar00rootroot00000000000000.. |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 and improved performance for certain table operations. Creating an Index ================= .. EXAMPLE START: Creating Indexes 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 Note that by default, `~astropy.table.Table.loc` uses the primary index, which here is column ``'a'``. To use a different index, pass the indexed column name before the retrieval data:: >>> t.add_index('b') >>> t.loc['b', 8:10]
a b int64 int64 ----- ----- 3 9 4 9 1 10 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['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:: >>> 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 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. astropy-astropy-201cddb/docs/table/masking.rst000066400000000000000000000153511507226315300216060ustar00rootroot00000000000000.. _masking_and_missing_values: 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 astropy-astropy-201cddb/docs/table/mixin_columns.rst000066400000000000000000000337531507226315300230470ustar00rootroot00000000000000.. |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. astropy-astropy-201cddb/docs/table/modify_table.rst000066400000000000000000000301531507226315300226100ustar00rootroot00000000000000.. _modify_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 modify the structure of the table: you can add or remove columns, and add new rows of data. 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** :: >>> 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** :: >>> 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 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[[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 Instead use ``table[column][row]`` order:: >>> 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 **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 .. EXAMPLE END **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']) .. EXAMPLE END **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 **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. **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 **Rename columns** .. EXAMPLE START: Renaming Columns in Tables To rename a column:: >>> t.rename_column('a', 'a_new') >>> t['b'].name = 'b_new' To rename multiple columns at once:: >>> t.rename_columns(['a_new', 'b_new'], ['a', 'b']) .. EXAMPLE END **Add a row of data** .. EXAMPLE START: Adding a Row of Data to a Table To add a row:: >>> t.add_row([-8, -9]) .. EXAMPLE END **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 **Sort by one or more columns** .. EXAMPLE START: Sorting Columns in Tables To sort columns:: >>> t.sort('b') >>> t.sort(['a', 'b']) .. EXAMPLE END **Reverse table rows** .. EXAMPLE START: Reversing Table Rows To reverse the order of table rows:: >>> t.reverse() .. EXAMPLE END **Modify metadata** .. EXAMPLE START: Modifying Metadata in Tables To modify metadata:: >>> t.meta['key'] = 'value' .. EXAMPLE END **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'] 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 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. **Forcing in-place update** It is possible to force an in-place update of a column as follows:: t[colname][:] = value **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). astropy-astropy-201cddb/docs/table/operations.rst000066400000000000000000001354701507226315300223450ustar00rootroot00000000000000.. |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 astropy-astropy-201cddb/docs/table/pandas.rst000066400000000000000000000144221507226315300214210ustar00rootroot00000000000000.. doctest-skip-all .. _pandas: Interfacing with the Pandas Package *********************************** The `pandas `__ package is a package for high performance data analysis of table-like structures that is complementary to the :class:`~astropy.table.Table` class in ``astropy``. In order to exchange data between the :class:`~astropy.table.Table` class and the :class:`pandas.DataFrame` class (the main data structure in ``pandas``), the |Table| class includes two methods, :meth:`~astropy.table.Table.to_pandas` and :meth:`~astropy.table.Table.from_pandas`. Basic Example ------------- .. EXAMPLE START: Interfacing Tables with the Pandas Package 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'] Which we can then convert to a :class:`~pandas.DataFrame`:: >>> df = t.to_pandas() >>> df a b 0 1 a 1 2 b 2 3 c 3 4 d >>> type(df) It is also possible to create a table from a :class:`~pandas.DataFrame`:: >>> t2 = Table.from_pandas(df) >>> t2
a b int64 string8 ----- ------- 1 a 2 b 3 c 4 d .. EXAMPLE END Details ------- The conversions to and from ``pandas`` are subject to the following caveats: * The :class:`~pandas.DataFrame` structure does not support multidimensional columns, so |Table| objects with multidimensional columns cannot be converted to :class:`~pandas.DataFrame`. * Masked tables can be converted, but pandas uses a sentinel such as `numpy.nan` to represent missing values. Astropy uses a separate mask array to represent masked values, where the value "under the mask" is preserved. When converting any astropy masked column to a pandas DataFrame, the original values are lost. * Tables with :ref:`mixin_columns` such as `~astropy.time.Time`, `~astropy.coordinates.SkyCoord`, and |Quantity| can be converted, but *with loss of information or fidelity*. For instance, `~astropy.time.Time` columns will be converted to a `pandas TimeSeries `_, but this object has only 64-bit precision and does not support leap seconds or time scales. These issues are highlighted below in a more complex example with a table that includes masked and mixin columns. .. EXAMPLE START: Interfacing Tables with the Pandas Package (Complex Example) First we create a table with a masked columns and a mixin column:: >>> 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 Now we convert this table to a :class:`~pandas.DataFrame`:: >>> df = t.to_pandas() >>> df 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 >>> df.info() RangeIndex: 3 entries, 0 to 2 Data columns (total 7 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 a 2 non-null Int64 1 b 2 non-null float64 2 c 2 non-null object 3 tm 3 non-null datetime64[ns] 4 sc.ra 3 non-null float64 5 sc.dec 3 non-null float64 6 q 3 non-null float64 dtypes: Int64(1), datetime64[ns](1), float64(4), object(1) memory usage: 303.0+ bytes Notice a few things: - The masked values in the original table are replaced with sentinel values in pandas. The integer column ``a`` is converted to a nullable integer column, and the string column ``c`` is converted to an ``object`` column. - The `~astropy.time.Time` object is converted to a pandas TimeSeries using ``datetime64[ns]``. - The `~astropy.coordinates.SkyCoord` object is converted to two float columns ``sc.ra`` and ``sc.dec``, and the unit is lost. - The `~astropy.units.Quantity` object is converted to a float column and the unit is lost. Now convert back to a table:: >>> t_df = QTable.from_pandas(df) >>> t_df a b c tm sc.ra sc.dec q int64 float64 str1 Time float64 float64 float64 ----- ------- ---- ----------------------- ------- ------- ------- 1 1.0 -- 2021-01-01T00:00:00.000 1.0 4.0 1.0 -- 2.0 b 2021-01-02T00:00:00.000 2.0 5.0 2.0 3 -- c 2021-01-03T00:00:00.000 3.0 6.0 3.0 The `~astropy.time.Time` column is restored (subject to the limitations discussed previously), but the `~astropy.coordinates.SkyCoord` and `~astropy.units.Quantity` columns are not restored as they were in the original table. Finally see that the masked values in the original table are replaced with zero or "" in the round-trip conversion:: # Original data values >>> for nm in 'a', 'b', 'c': ... print(t[nm].data.data) [1 2 3] [1. 2. 3.] ['a' 'b' 'c'] # Data values after round-trip conversion >>> for nm in 'a', 'b', 'c': ... print(t_df[nm].data.data) [1 0 3] [ 1. 2. nan] ['' 'b' 'c'] .. EXAMPLE END astropy-astropy-201cddb/docs/table/performance.inc.rst000066400000000000000000000054111507226315300232220ustar00rootroot00000000000000.. 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. astropy-astropy-201cddb/docs/table/ref_api.rst000066400000000000000000000002541507226315300215560ustar00rootroot00000000000000Reference/API ************* Capabilities ============ .. automodapi:: astropy.table Notebook Backends ================= .. automodapi:: astropy.table.notebook_backends astropy-astropy-201cddb/docs/table/table_and_dataframes.rst000066400000000000000000000103031507226315300242450ustar00rootroot00000000000000.. _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. 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|. *Pandas does not 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.* 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.* 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.* 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. 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 `~pandas.DataFrame`, as documented in :ref:`pandas`. This allows users to take advantage of the features of both packages as needed, within the limitations stated above. We are also committed to supporting interoperability with a more generalized concept of the DataFrame, with packages like `polars `_ gaining popularity. astropy-astropy-201cddb/docs/table/table_architecture.png000066400000000000000000000701431507226315300237620ustar00rootroot00000000000000‰PNG  IHDRRܟ‰sgAMAą üa AiCCPICC ProfileH –wTSŲ‡ĪŊ7ŊĐ" %ôz Ō;HQ‰I€P†„&vDF)VdTĀG‡"cE ƒ‚b× ōPÆÁQDEåŨŒk ī­5ķۚũĮYßŲįˇ×Ųgī}×ēPü‚ÂtX€4ĄXîëÁ\ËÄ÷XĀáffGøDÔüŊ=™™¨HÆŗöî.€dģÛ,ŋP&sÖ˙‘"7C$ EÕ6<~&å”SŗÅ2˙Ęô•)2†12Ą ĸŦ"ãįlö§æ+ģɘ—&äĄYÎŧ4žŒģPۚ%áŖŒĄ\˜%āgŖ|eŊTIšå÷(ĶĶøœL0™_Ėį&Ąl‰2Eî‰ō”Ä9ŧr‹ų9hžxĻg䊉IbĻטiåčČfúņŗSųb1+”ÃMáˆxLĪô´ Ž0€¯o–E%Ym™h‘í­ííYÖæhųŋŲß~Sũ=ČzûUņ&ėĪžAŒžYßlėŦ/Ŋö$Z›ŗž•U´m@åáŦOī ō´Ūœķ†l^’Äâ ' ‹ėėlsŸk.+č7ûŸ‚oĘŋ†9÷™ËîûV;Ļ?#I3eEåϧĻKDĖĖ —Īdũ÷˙ãĀ9iÍÉÃ,œŸĀņ…čUQč” „‰hģ…<X.d „Õá6'~khu_}…9P¸IČo=C#$n?z}ë[1 Čžŧh­‘¯s2zūįú \ŠnáLA"Sæö dr%ĸ,Ŗß„lÁt  4.0,` €3pŪ „€H–.Hi@˛A>Ø A1ØvƒjpԁzĐN‚6p\WĀ p €G@ †ÁK0ہi‚đĸAǐ¤™BÖZyCAP8ÅC‰’@ųĐ&¨*ƒĒĄCP=ô#tē]ƒú Đ 4ũ}„˜Ķa Øļ€Ų°;GÂËāDxœĀÛáJ¸>ˇÂáđ,…_“@ČŅFXņDBX$!k‘"¤ŠEš¤šH‘q䇥a˜Æã‡YŒábVaÖbJ0՘c˜VLæ6f3ų‚ĨbÕąĻX'Ŧ?v 6›-ÄV``[°—ąØaė;ĮĀâp~¸\2n5ގ׌ģ€ëà á&ņxŧ*Ūī‚Ásđb|!ž ߏÆŋ' Zk‚!– $l$Tįũ„Â4Q¨Ot"†yÄ\b)ąŽØAŧI&N“I†$R$)™´TIj"]&=&Ŋ!“É:dGrY@^OŽ$Ÿ _%’?P”(&OJEBŲN9Jš@y@yCĨR ¨nÔXǘēZOŊD}J}/G“3—ķ—ãÉ­“Ģ‘k•ë—{%O”×—w—_.Ÿ'_!JūĻü¸QÁ@ÁSŖ°VĄFá´Â=…IEšĸ•bˆbšb‰bƒâ5ÅQ%ŧ’’ˇOŠ@é°Ō%Ĩ!BĶĨyŌ¸´M´:ÚeÚ0G7¤ûĶ“éÅôčŊô e%e[å(åååŗĘRÂ0`ø3RĨŒ“ŒģŒķ4æšĪãĪÛ6¯i^˙ŧ)•ų*n*|•"•f••ĒLUoÕ՝ĒmĒOÔ0j&jajŲjûÕ.ĢĪ§ĪwžĪ_4˙äü‡ę°ē‰z¸újõÃę=ꓚžU—4Æ5šnšÉšåšį4Į´hZ ĩZåZįĩ^0•™îĖTf%ŗ‹9Ą­Ží§-Ņ>¤ŨĢ=­c¨ŗXgŖNŗÎ]’.[7Aˇ\ˇSwBOK/X/_¯QīĄ>QŸ­Ÿ¤ŋGŋ[ĘĀĐ Ú`‹A›Á¨ĄŠĄŋažaŖác#Ē‘ĢŅ*ŖZŖ;Æ8cļqŠņ>ã[&°‰I’IÉMSØÔŪT`ēĪ´Ī kæh&4Ģ5ģĮĸ°ÜYYŦFÖ 9Ã<Č|Ŗy›ų+ =‹X‹Ũ_,í,S-ë,Y)YXm´ę°úÃÚĚk]c}Į†jãcŗÎĻŨæĩ­Š-ßvŋí};š]°ŨģNģĪöö"û&û1=‡x‡Ŋ÷Øtv(ģ„}Õëčá¸ÎņŒã'{'ąĶI§ßYÎ)Î ÎŖ đÔ-rŅqá¸r‘.d.Œ_xpĄÔUەãZëúĖM׍įvÄmÄŨØ=Ųũ¸û+K‘G‹Į”§“įĪ ^ˆ—¯W‘W¯ˇ’÷bījī§>:>‰>>žvžĢ}/øaũũvúŨķ×đįú×ûO8Ŧ č ¤FV> 2 uÃÁÁģ‚/Ō_$\ÔBüCv…< 5 ]ús.,4Ŧ&ėy¸Ux~xw-bEDCÄģHČŌČG‹KwFÉGÅEÕGME{E—EK—X,YŗäFŒZŒ Ļ={$vrŠ÷ŌŨK‡ãėâ ãî.3\–ŗėÚrĩåŠËĪŽ_ÁYq*ß˙‰ŠåLŽô_šwåדģ‡û’įÆ+įņ]øeü‘—„˛„ŅD—Ä]‰cIŽIIãOAĩāu˛_ōäŠ””Ŗ)3ŠŅŠÍi„´ø´ĶB%aа+]3='Ŋ/Ã4Ŗ0CēĘiÕîUĸ@Ņ‘L(sYfģ˜ŽūLõHŒ$›%ƒY ŗj˛ŪgGeŸĘQĖæôäšänËÉķÉû~5f5wugžvū†üÁ5îk­…ÖŽ\ÛšNw]Áēáõžëm mHŲđËFˍeßnŠŪÔQ Q°ž`hŗīæÆBšBQáŊ-Î[lÅllíŨfŗ­jۗ"^ŅõbËâŠâO%ܒëßY}WųŨĖö„íŊĨöĨûwāvwÜŨéēķX™bY^ŲĐŽā]­åĖōĸōˇģWėžVa[q`id´2¨˛ŊJ¯jGÕ§ę¤ęšæŊę{ˇíÚĮÛ×ŋßmĶÅ>ŧČ÷Pk­AmÅaÜáŦÃĪëĸęēŋg_DíHņ‘ĪG…GĨĮuÕ;Ô×7¨7”6’ÆąãqĮoũāõC{ĢéP3Ŗšø8!9ņâĮøīž <ŲyŠ}Ēé'ũŸöļĐZŠZĄÖÜ։ļ¤6i{L{ßé€ĶÎ-?›˙|ôŒö™šŗĘgKĪ‘Îœ›9Ÿw~ōBƅņ‹‰‡:Wt>ē´äŌŽ°ŽŪˁ—¯^ņšrŠÛŊûüU—ĢgŽ9];}}Ŋí†ũÖģž–_ė~iéĩīmŊépŗũ–ã­Žž}įú]û/Ūöē}åŽ˙‹úî.ž{˙^Ü=é}ŪũŅŠ^?Ėz8ũhũcėãĸ' O*žĒ?­ũÕø×fŠŊôė ×`Īŗˆg†¸C/˙•ų¯OÃĪŠĪ+F´FęG­GΌųŒŨząôÅđˌ—Ķã…ŋ)ūļ÷•Ņ̟~wûŊgbÉÄđkŅë™?JŪ¨ž9úÖömįdčäĶwiīϧŠŪĢž?öũĄûcôĮ‘éėOøO•Ÿ?w| üōx&mfæß÷„ķû2:Y~ pHYs  šœ@IDATxí œUUš˙׆s22ŦņJ9$]Qąđ†]LĖ!1041001|Á+Ļ–&ž¨Ĩ\)4ņŠĄ‰‚‰‰×7Lm05šI‰WTü‹1%^§$ÅÄ@Îū›ŗûœ9īsf朙g}>ëŦĩžõŦĩžõ[{¯gŊíuœ3c†€!`†€!`†€!`†€!`†€!`†€!`†€!`†€!`†€!`†€!`Œ€§ōzôč1Ö÷ũū\ļg†€!PxžˇvĮŽwVKy ˙X Ų0 C 3@w¸jÍ<âĘcū!ˆ•i†€!P /´AēŖ:YĘ#‹ÍÃæ†€!`ŠTUUš¸qUŠ‘6 CĀ0ōAĀH>(!`†@+L´‚Ć€!`ų ` $”ŒĮ0 C Ļ@ZAbCĀ0 |0’JÆc†€!Đ S ­ 1‚!`†@>˜É%ã1 CĀh…€)VÁ0 C Läƒ’ņ†€!`´BĀH+HŒ`†€!Ļ@ōAÉx CĀ0Z!` ¤$F0 CĀČS ų d<†€!`­0Ō #†€!`䃀)|P2CĀ0 V˜i‰ CĀ0ōAĀH>(!`†@+L´‚¤[ôׯŌÔŧZmē‘ CĀčiĢÎ/ņßî•ÜĻ=zôXŒ­ÕÕÕŗái m6Ū|ãČkv~*?´ĢąA7%’ Ž… C ˛HĪž=ŌÁŊI;öéĖļD?‹ÍĒĸrûž?[ĨĨņ/‡ļģۀm“ŠĒĒ:— FíØąã¤ÔŒ yí{PÆķđßĘNx=ö5Â}"´gáIíü5K™ ˛BžĐ ëD¸_„vTœ7ĄØ?œJ ų͏5âEļŗŌÄ3&âUÍŦˆ@F€~ Ą3Ēģp=sVåŖÕt¸‰ũ‹h–cšĸáųkéh$¯ Ŗeä.ü5ˆÍ6„F˙+á†sš,U ‡§™zI …Š(hgĘūõŌ ČÁˇŸ\ĖēNÖß­ČĐG]:.ōÔŌVÚå­tüF3 Ž@ˇV Ų„ņĶi?DGŦŊ•ņ˛s€_ËXŠJ&Ŗčä­NžéĻĨ0­ nc„ļ}K„f^CĀ0ōF ėDōNPόEՃNŊ?ŖįųéęÄ4í::üëĶÅC#/mô¯Â=:LΌ 1ôgréôĩė´6S|*Åą–4›ŠWڙUČΟáU°6gūđ×ÁU@aVæ†@7E "7ŅÃļb *čøčėujŠ`ŖFtč}3ؒ) FįĢŲÆæPH”ĮüƒÂpÄM(Cíg@‰Īę%_ķí &Ĩ0&ōėVɁ˙đžVA­äŦ§Į[EB }ˆø.ōIo4CĀčšT´ĄIšąs_G‡Š čWčČôåt9š9Ĩ“JĪJVĘ„WGĨą—˙ŧøāy{M|!`UZYŗDÃÔ;8…R\­ŧÉį*ÅAoÄÕFüģAģ~6ãŊ;q)ŊÅ1<%ëštÄ ‚^ ßAéâf]¤Qi%V‘Îõl侑ōs¸[ ŋЙõ ãΌĸrĐšŪLø)äü<<Íđ, W'ÖÂļÖ1čBŌv6/ĪæšČū4rä}ŋZgËlå—/Ļ@JĶ6úĒ{,Į^ë8Ûŋ4Ų–o.áYp:#?ÍWr†ˇ›­C+žtøÃ‘]OžÕ—úīĄđ—"īŦūĶĨÕé5čeiTwžÍYØpkY iBUv Ģ4ÍĨË *+:¤SåvuÃWį éW=ņĢcšŠÚNõ. ž´ÍH:üeČšĨq&מl„Ļ›j ‘›ļū!’´ ¤Ģ/$m'ōęĒ™[(_WãŦčD9Ŧč.„€)ŠËõwŗƒ ļĐÉčoq/Ž´s-ņú‹Ø҈_Aüx52ÎËĐŲč/x{“.ņwģų’§Žš•ÎXĶģ?™)ΉÄ]ī'đOÁ΁'¸j„|æãŋ˜øŅ¤EÜ&ü3é WáM ´s‰×å†ęuĮÖ-Õ ūO=dÂíG~7ā.!Ī…z>Ū&ōk#yčę]иBnÔ°D5˜zÅjVVƒĢ‰uÕJs”ŋū†wZ\æuČ3# OJ’VÁzdųÔXũ;å­ČOÖla´GÉû$lˇ)đėú‘zF@’ŧēpSßÃlÜŒŨ‡ Į“né. 9Shũ)ãdėÃđMƒg.ū×áŸFxŽŽŊŅ=g×bī…ļÁSą-ČuuŌ58 CŪčYĐlNĪÚ3éoŠÉGuǁūCâÍ%AĀ–°Aōr=š§ōĸę"ÂÕŧhŅe uÆZ3žHÜrėJâ/āĨÕĨ…(ā!¤S§”0ņppƒ-ųî‡=Č °éDôøUø›p¯K$BNäy„´ãĄßŊžNá>ܚŲnÃ/yÅÕ˛ÚŦI’!ŸąFwYRD Č6šüõ˙$ÂnY‘ü¸IØ!ķ|ę$eķ$v<¡#e¨vė…]€Õß7ĸ˜ÂītŖ0ų×cŖX**ɐĮbxĻ“VäĩœČqČĸöWžyŌ „qL”9JC&í÷¨}´˛÷šx{ëŲ›-lĢ1Đo"ū*ŦÚ˛W2ÕcƒlWãšúJ\ĩĩfâY˜vūŒĮY‚ŨĄ™×h<€Ũú.,:Œ{ĀāP _Ú$@‰û<īBÔKuFĐ´ö|H ]hØųa8tES\–åÅ?ëĮ7•?MƒŦča:ųą‰8ίtƒ#<[$wÎâjsú&lQ×á‡ų’>7 gsÕĄÃ¯ú _X?h7„éTņ7$¤….ôFl:|ī‚ž¤tß#~ĨÅ eŦķJuቖƒ"qÂ(í> ôųØ ˙ŋfͰë3Ņ"užŸ˙šø5ƒōåb•n^~ oøėé-}ėɊ—!^û90d3ŊâéZ=¯ŲYœ!žšizžäÚ dį˙mÜP›ĶÅˆ°ēFÍaŧ–‹émĀ&:í0Ž-.ų5é:våĄüĶ- )™´ Ņ"?ü›äÂuŪ¯Q4 |mēNXüqĶĖ áll{ßĸĢNr,v#jÎĨ8z‡BČE-k&\ŽŖNû…´<ÜđŦSįZÂÂ&PTy¤ŽÃ(sMX~<Ú}q­”Y>yfãĄÎZf Ú\2Ũ†ŧÉ >ŖŅŲÛ2čãĀv>õÖl+§4š}õ“Ëķđē\3†@Šhõ°•*ã ʧ†Î!|A͉­™ISšˆM¤ë•†^*RĐą“ĶIt“I{*âø×ŌÁœFĮ¸˛˜üڒ†r5ŗ˜ˆ}ĢŊ˜´3Ŋ”2ŠŠ{-uF>Š cE<ī ķĨ3í)Åí ’žōmL)EÔ' Ŋ­¤bę™T&Šg7AœˆüŌÖúËäI<Q…žįm./Šp t{l˛s­ŧ_–'AG}J|5/i^Ø5)ôBƒųtĻ…æ)ū:–빇Б€Ŧ›p“Q͍3Ÿ‚GųĮaOÃ&6–ŗätÖȝMą§&WĮ¯ÍãĄ)öD1BĶĢČ-7Ą=7@—ĖIú@mjkę.C%å]ŠuZˆ=ûIĘYEžK°ĩaŪ(Ė&ųyö iæĨ@ĀˆsKyéϤŦ{XāĨ¸ZÎ(ī"üú‹×ā8gHĪæRÆ&:")ĒāÅ&?m ʖĻČ86ūĩØÕØl’Ņ’Ŗ×-ØyI% /eEŨ߉gŲ›˛TītFrËTÃs%Xé”WŌiŖŅ—3+ŒŖ&ĖwÄ%Čĸcņ‡ôÚ°íĄĢ=u"/ąídŌ €ĻåĻ|fz†‚įHy⟘oâųĸm­eÕûI¯ē…õSvÍ™0c” čCVĒ<+*Fmō‚÷CáßâĒŗTYGøņ&ŒFûŖ‰×ōNS‚Z¸'¨(cJōĘĐė­6^N°Ņ‘WėĩĒ#Īāž<¯‚”M <‰I‹/ĪíÖ°cßÉlæ9Ū_aōz™ø5x7‡ņqW›úŸĮ¯Î_<*/jtJm-ĨķŽĄ Ú# D]괒pØ9ëä“ę­˙[Ņ!…^‘öļiØ~JĢÃjË )+dk†Ü¤8™xûëy]FĪĨęÛ ĢgWm•—``ôėi`qøĖ ‰æ…"ÕzÉG˛ ÍČø C rā]? %˛EŒĘ‘Ú$-7ĸ:CŖ_3 Ā‹~-mx]5Ė@ž ˜C%ÃŲRWŽoÉëÆĖw-3Ŋ;Kžqfh „F“ōĀŅk6Ą‰lŽĪúÔÂSYŠú -ov{%b „'‚uáūRå“}=Oû f CĀh…ĀŸ9Ōũ6WŠĪhŲ Ļ@".åąĘķFHæ5 C Ā |o;g{HqDė;ÄŖaCĀ0 B°H!hoÅ ĀRÃno8ˇÛ`Ī{+*´č/;÷‘Æų×w˜yn Ãåæ>îû{ŊĪ6](×W û:?LfnXÄ÷DãųÎĻ YtŲ¤Ļ@JÜ´| ÷‘ŲÎMxĶ÷?‚s3ĪŦĒząĐ"¸§ũ‚Mü7Čw<īōöî0ŪŖszŠNǘŽôūHŠĘëĮ?J]™Úa‡qéJI|Į÷grąÕēÁÎũ4ZöÍž˙Ĩ§#ȇq•úžĒǍQË;ŒxŪķŧ~˙MgA‡ą¸„"&˛’ĸûOį&ũÕš/Įœû€"vÅÖ]ÅUŠ/Q"QŠį@Ī{•Ë˙æyŸzÕšoū#žO [‡ŋĪĒė`öq…si¯hų ī7s­ĀI`iNŪŨĀÁ8wæģžwāÅΝ”ŠsoKÅ~Í_#ÜäÎ Q>×úū;C}˙ÚKĒĒôáb„rJYG_‚Ą{^÷ŧžœCŪįß?ŠV <ˇûoķŧŊn÷ŧ/ąšq_Ōc=īõ'bąŸ<Å c~,ö÷ōDŖģŊŋ+īôŅSúr—#wp‡ˇwk˙—īŨBgúeß_tמ|ÜšmŔy$#ĒbŌå›FŠîRūĖčīÎ}ūFāę$ŋåû“úxŪ“ŧ‰å’\ųiZ˙#ūUīsž˙L.ŪŽŠ˙Q,vø›ÎyŗĄB—ϤTOāJš{øßp) ö”y™sÔ˙(*.ŪßËšįõŧR,íYnWĘû Ī[ĖøētšĢxÃ,ôĻlŠgVU=ØÛš'—:weuå>3 iã*yĸ+;qû(Žā vu­Ŗ ŽX§Ŋƒ{¨•&Nß{čVØv;ģũŖø;Īûę9;īcJ”ĪßÎÕōåÖqĶh‡ŽŠYĘ[‡2zįŽõVī_YéĨāΓ¤Ņh"3<}˙L–ŸöēžÎŽPEu-*Ew×ØđX[?Íš—Ŗehí÷AįF˙“NMōōĸ=p^UÕŖ<Ųüá,įDfQ'xŪ_ěJ;Ņkä/Gų(¯k˜š]ÆĖč}ö).Á¯Ų”Â˙!ī×e.`yPō|ÜķžŸÍ‰šŨS:;ã“ÎũwX^6ųRã>ÃŊ\ģųū›ßrît倧›šĘSĒđĩt~QŲˇûū<Öچ<áûÚcƓMî_Ö"{;ønaIķ_<īQÕn?ãųeÆÔOĪīn˞ŋčûj ¤ŧ•7f{K¯xŪ‘ĮųūĪ˙äÜ'×đ/œGúūŅį†;“†>īyGkæģ7ÄEÎŨ]¨˛W™,Ŋ֑Įnõž÷…ŖFōÎå9gÔlk%ī—|˙žŗwŪeu<[}Ņ [¸píŠL3Œo1›ũąį-bŗŦ))q7Tš6ÕIë:ũĶܛ|QËŦŪģŠ6‡mĀß7P{öŪËõŲ÷Ķî_ülāĒÛûė6ˇž¨rūąķ?yĻŅ÷ûžÎˆOįÖīīÜ#ę,đŧ˙œ‹ ™Ôa‹-"“S†ņ_Ų™:AŊ¯˛dÔėÜŅwĮī} ķČĮmōũ/Ԛߏõŧõ™øgÅb‡ūœĩ_Éų™ĒĒßđ ė¸×ķ~Ėĩ˔&•ޞÔFöˆ žOĩwšTÚ˙ųū‘ŒÆ'×0’¤súÂyÎ])N:Ŗ)¸ü(Ô~¯úūČŗŸŅ“]õ{ŽīŸŽøĐŋŌ4øūÃ!­Wúž73î…ä•‹7Ē<ĸŧŦ%5kæQ¨?Ä÷6ĶŽˇ¤œ×ō_2úūIa^Oûū§xŪæåZ§į!k5S›‹(åņ‚s'nSØm÷2@ŗŒĄ>ī<Æ>E˜ĪD>ŋãök”üúyŪãë}Äd–} ‘,Cg˙#dķūÅšå3=ī—Š)YÖڇgíĀO8÷ĸž_É{—įÍåŋžņáž˙Ë&įF~Å÷¯Čļ§ĨŦ^Čü~ar–*V— —û $¸ėÔGĐųKAÔG[āã{īã(wáģHŌğ_& ž_:ü‡Yîûå<īĐ3é¤E–ōū.3˜ß1‹Ō~–EšŊąž)멊˙5mÆ[8öĢžq8ķæ¯3˙ČĀ`Ņ:ķ‹ĒĒVNōũ 4^4Qķuß??œņĐ!,:˜g3S§¯Ã+ČĩS^2aOņÁQžwTŪ!ėá}„KdĩW™í} e؛ŲŌsžwJ6wįM°åˆCf6t"(Wûá=üĪ ėˆÂ8čsŸGaėôÄåTí d’G{K|˙stŒ†Ķč~Gdã™Īá7cƒ—1S!ũ§iF]a\.7F™šQdâ{œ% uØ}˙ú¸˛ XŲëx|9KŠ?’%Lé Ĩ÷GQ…iPžktÎČfËöČ>„6žÃÎMŧĩÎŊɌåķa:šoŗĄ ߛQ™Ŗņåę×iĸ&ĪÃËšŽ˜1DĮ›€[ߏ2Š`Ƒ˜9JĸVΝįĄķv™3?–ŋ͕Q{I‘ŗų_rhyÎ' mģCB:wŅŋŗŪ혨¸•_G1ŊėûĪ…qQ÷čČģ•ĪéÆTySŸ )ŸL (ZnčīFČΤĘLˆ@šÍ@zŖ8&Ŗ,XÉާv{÷Šo:|dõg˙í î_ú|ĒėFd.WSį;Yáé{ĢW\I´ũ`Ž”Ĩgč­ÍėŸdĘų˙Ą ô˛}ŋ\ō|(~ųŪû.7DķHį§ʨĖŌņgĸĄ{đ€”$¯Le”š.åŅčy?øŦs7ĩå(jŠåJÍOË= ~PÃ,yxGށH”/öoĮ÷W°DMˇģī¯GS5§vđQžBũZf_íŌlōš§ņˇF \H=§¤XŌö'!"mî‚Ŋ‹¯x˛Cq”‹Œ­Ņ+€r?kÂf“úžĒĒ Âd,ŲĖ.åKæ›Ëe-÷yևÃ~āãiÖÜeŊ÷2ŲäÜŅŧŪpîĖ\ļą„öV”žÉ :s~HļĶP&“‡ö1Lëšßđŧ/ˇOî­sÕ āhæØ:67%Ē<Žķŧģs§ČĖq'{lĖļ´×ü ö1Čõ]UU—‡RŒĐ,bįrlHĘęöfV"ūéü˙–aĶ1?čÜákŲ;IWÃ÷ųÎĐ‹ŧcŸīÎĒĒ+Ãŧ •7Lē,ŅÕĸ$ĩ´œ¤8Ãøîčvvį\ËføĩĪۚČrČĄ_tĮųĻë×˙ .Õšm0‚=Ŧ”ÎõķÁūКCZžŽ6Įō/zÛ=ī#3PL…LÃUÆPւÉI6//ŋ”ŊÄđež);x(ë 8{ņ÷lÔszėę ĩqŋš0 įņtJ'ėG˛ĖUåûÛ^äō9â˙¨Ĩ¯;(ŗN']ž™hûûūK/đ’k3=ÛRbĻô…Đ5ÂeD~õ=ž˙—=oB!iÅ[Jåą’C 7ŗĄŦÎíãl|‡íY¨LŲø므 čôõüĄEŽĶˇŲŌ¤Æbŋã ddĪãôšˆĐReČŖ<žNŠ´ĘTyJ^fÚÁr`ąō†˛…./īĀ;—H÷ iŨŨí4?‚ģ˜ā0†įąáhĮÉZĻę’mr`UÕ=Ģ|˙ģĮÆbķ¤LXļŲRëûëŠéL_c/EáZB`ÃeĪÁ,Iš6D›bąkŲP<û|į–}0k–2Ōúî|Ō_Ņ,ˇŨĖ×ܓûe-næ” /øzv%o ËbŖõr>z<0_ĻcøéObą(œĨAGã'9Rû KÔû°­lÔđŧĪ97%ĖŖ”îéÔë>ßëQNoį´P1yŖ\ƒtņ‘õÕAžš#āû;ˇ`näĸÍC<¯épR;H1JYRŪ8ą¤=0aA™ <´w~<˜o~ŸFÁ×Äb¯Köúĸ@ĐõĨ5ûzŪ#ÚÜ>.ë‡âØŗ'>2•|šįšŪiˆÅ~¸‚Ĩ%6ā‡|åĢŽž‘ũ§xŽN/åėŠŪķ–Gåeį-N˜ũĨyŖJIķ’}ãėOC7§3ˆūFô{ÁŊ ÅQŊĪ'ëcįLQÕUGø^ËŽ|€˛†ã_Yī}‹NüœJ)ęAԌā4ߟŧŅ÷÷_ä×î3ų8ęĪt–wąąÉ‹ąįžŧ`U|IyKfe=œ‘õ/bąĪ)ž¯īŋ–ēqy+×ĩœĢ¸tļ‰YÕĪģ‘ Ķ§Ū`ųëPį^Rž„_ 7DG3į4MĀ?^f7Aų'FžĀ×ÆęŸ{&ZH: n ęļį|˙l°Ŋģ˜I'}ĸå„ūũSî RŪˇúūī˙N§2(Ī%Ŋ0/šĒëÛ|ŸĨÉ_ŗs‰$•œ5Ŧ!Jdų ,Ûô-P‘)ãtXŠÆ O…Kyō%öJõ{ûū_ĨŦõü‚š–t‚nTļLü˛t5x#G°éün+´ å›/v™déú@õ^:cĻ[_Ģc–Ā ąLōär3⛒0~<šũNĶĒâôÁ‘ĶMQ˜N3lB9…ôRģŠōFßŊBĘŌ‚×=o$ƒšķouîß IÛÕy;L $ŽĖ[°Ŋ÷čĩ§öw/ņt׌!PJԙ_Ė&ítßŋq’īį6>KÍ_Ëu(VMœã+øĩ¤—Ę“-ü3įžÁĖãĀ/úūOÚû‚=}Šŋ•cßīeX6a/a8ĘcÄŋzŪ=W—`ī [Ŋģcœ.S|ˆ+fPxsÎæKfĻ@"B‡(–ŦĻQæt•ĢMō‰ßųž—úąŸâJeūųŪf÷ŪæÍî­ŋ˙ÍŊŋuĢûķŸō[gIgā R aųt*-ŦUkz2ˆĶĸ‚ <öäkâ—Ļ׉ÎĻû°":…-Ė~ū@û<”’´ŦRø˙é\ߘīëÔĪ– Wķ|&Õ'(ƒ;ˍënGúūøR”iy$#ĀŨãŋ剰WAÛ'3tãPģ+”ĮšėuLĮúã'žã}ė %›;­ÜúWÖē˙÷Ō÷ō ˙ë^ûķŸZļüķŊīm~÷ƒY hĩäį čœSȚžN ™éĘ$wåßŪ•&o÷xv2õ%]šö­ęÖŽ åĄ¯“ō˜|á4ī ‡ŲJ€| šUŦ{ųE÷ŌķĪēWÖžčÖ­}}ø‰ī ČGu ëŗ‰cÁ›(w4öę\3áĩ$ā´ck#:˛*"•ļæ0Š!` š3õ% Žnâ ;ܒW—=ظë•ņI§ģ(åĄYÆV>é{čŪ@q¤É'ūj”ÄJčOÂËÉČ@YHilė(2K`ŨvQ qåąl{|ķŒÉnÄׯķ[ßt<đ+÷ø#ú›ßũG¸āČ^ĸãŒŋĨ!…ĄS› ĘØ˜ģ:"+›ēÔ Zīņk–j&7šĄ×DØ4Xk‰„ÍÛūôŖˆü6uÛ_–]WL”Ē,Ž$B^‹°Õ#ŋ>.oåĄŲÆŗĪ<íf]1՝?qŦ˙–8)Æė$â÷ÂeĻp1îä_ŽĘŖ7 ô–Ä^âcÉÁ!Ž„/›V'‚Âø¨Kúyđ? ­>Jo'ŋ*GkA9'`CKârYÔ a/M­´ŅØõĢ™r:ĶbTҤã)š:õzlÁmXHĀlqˇõ¤Ffv"0€wöė´T@ĀLīÉâTzē°ŌcÕw J¯očÁ^›&ŽSHŅ=„RЇÎū>2Ē9úدëŧō|áš?¸Š“'¸ŲW]âū÷˙Ãa˙}.¨ÃPĮ——*a”X‹ĖO ģūˇäėíÛˇ¯`ÖԟzåģaËŠAo yŒ ͎ƒ+E7û.Vęģ”'Ĩ•w§ ?ßEēÔk%n6::%Ø9†ēĖCĻZ§“€¸&âŧ¸]å!íhę%<^މm$žhÅHzužōŒ–S*?yĢ ˇ`ƒ6Ä}¤XyI;Réąoc%sRg^Cã˜õ-•ü]%đâŗ*Oˇj\–Z'žˇzlb0™ ëŊĮö§Ī;#Jû[h“Sˆ?ōNNßᤒ**%ÍØKGuO9휜•Ųôö[îĻ_åfūā÷×7^—âX‡Ŋ€‡toėitĀę˜*ÆPžUōø˜ÛûaKKË ßZŒđ¤oõ“O–4Rt`= žSÁē/áCņ/ÃjF’—‘‚$íBŌ>œW‚`ĸ ާ˜1ŧ€'á:čœŠ#Lh‡Ąøį<ÜbŒ”ņ,=×Å$Î' 2.DÖ"ōDŪŧfēŅüiC)‹ĨČēŠ<ĮŠî`˜¤\ŖüæOFüĩ*˛"™Zxˆv˜‘-ũʓ´ŅUđĖÁæ=ØË–g[âJĻ@â˙9>ŽgΏ “ÎË*`ģGîŋĮ]8iŧ{úˇ‰w3 Lå=ûc›˛fPĻ‘Ô!˜ŌãĻ=Hę­LMõ‚kÔ¨ŠčätUƒĐ[pīM/i¯&Ûđ<ęWštåC[pÊkBŦ"Ŧ‡ˇ˜ˆŦ”ĩ˜6>*ŒËĶ f9đöđ'ҐåZM×㘨>Z~ē {ūāÁĪūTéÃŋú8_#á+ ˇRvņ—k.qką…š­āĄY¯žÁ& Čo~)ڂ ˛Ū@ĸ5ØE'Î3A| ĸēōâō훯į§à 0ŪeXū3v1y>ΎАLJÁĢ%p=äÕK.ęŲŽŅÎR˙4ē–Ô¯(ÎÖĩ %ãŖ kF5Rųâ>=iY$NōQy”S-Æđw!Áû¯AX+ļ!öC†yX=ŋZ^mõü‚˙ūJ ŋŪË´ž™”ĩ•<ž—–Ą‰ĨR 5Tčŋ$÷č“O×=eŦĮoŨô ÎvˇņĐīoŨ"žeŧ´ZϚ‰ŋ%c ˆ AWJL°˜N\čuÔķ1^ƒŊŽ4į†ŧę° ŋ]31ØL`oxĻ?ž`P˜žwŧËąęÜŌōŽŦĪYÜKp[påá/dęŦ5ųSÉĢ.RH*Mŗ…Ųä­lųß#^ƒÁbBÜ/<'~WƒGŠ?ˇŦėēĒ[|ÂēÖĸÛ2rN}ŪĒü 1Ôc$üĮōlŸVHē6ōĒ­„ÁŠBō/užĢâJŖ¤%įEūũ_mũ4Ž–ÄĨĮ€gtČ­-Ū8čM°Ģgg i&ãēøķ6‰įjA­j\‡˙&ÜāƒV#>ōä>Pŋģ–āōÔû÷4ūčķ*ÖŦ†|4āyôëhīŗĶ1SF/x”ˇžmšJYWGxëŋDxņ“r´…”‡ŪÉņ‘ôâmĨ‹‘‚ĘH›~zonŌžåÄÕō˙^ę˙üŋ l NpMũē„ŅȍNėFķz [ęĢå­Ŗá{R‘eæāéEü,ęņCņ!‡œéú‰íap"ģ”Ęąøo=Î(Üwp'aĩTû8|zo¤ 6Pįëp/ãyĶāUæNĘZ= Ú x5€Y ˆ4Fųøo'åŅ”†/ ÅåGšāų%ÍVh’EmĨAĘ&hgŌ7čŨߌÍj]ƒ¨ķ`ęƒŨ•š#ĢKˇ:Å •Ī™į|_/vĢ,ÍŨâxäū_qTū5ldĢ|*īü3‰ē2Tq­7ëá:ŋŧ^XđĶ2KąĻ†„zh3™>ČĨ Á)) KĄ ­Ôí‚H9ęŦ%›^ĸڐÎ3à ôąā印^šaaŧ\ɧGÉÅúĢy–į“_5ĪoA2é4˛Ü@ē``PŦ¤Sg¨Iī:ĸKÁĒ™õ<Č^†ĖOáj´<˙]øûbŗ=+D—Ū  <”3õڈ“xâĨÕ0Ķ8œzëY”QŨ“äDū{áiP$ūÛqBŪ`ļJR$CņAkĻŧō“æaâ†ĘŸjx×†´ī|†œČ;ņüŌFOB›N¤djÂnMé e6Čö˛úZäëGēĘU Tâ&ĒųÁ/ ;&íŸ@me™ęæŲWķA VÜûؓŖ@ŠØMŌßQ8đāę%IŧLŠå2 ęC‰ŦöJŌŧGZ…K1đH䋧T8HŽRä%åą˜ēu ęĀō2`7ˆ4SH;‰—{ˆá¯ĮĒÃkāeo†”č|ōĘ4å%r¤îGŽģéØVåHšˆ&ž­ē7NØHXŧFōXH>‰ Û×Ķ¸‘zˇHnŠŌĖX3liŌs¨&âO dÉCˇXhĐŪĻ [ĸĶ“TŸ4ņBjSGY>´{­ûæéßn%°NYÍūáĨÁUDūÆUˆ–m•a#đ öĄJęP:ÔčĨŖėc)TŖÎÄË A‡ąNŖx>ŌäŪGüųĘ>ä‰i)ĪL/^|eWrƒ|MÔm\3NUkŗä7€8uZ›BęvüČ1yB˛ÂÂOž č¨OKD0r“ū`ž2B+ÚŽL‘c2IU ­ä¤öT\ԄmĨå믇QĪV6ėōÍĢNËÍÔņ`"9ĄÍĮ_ߊ9~hŖžqÚE‡V¤c#ūpʘ•!N§E3Št<ŲhČ_čģ””ƒ‘ũČÃ!{SRDÚ´‰NN–ŧ‡ũŠ“‰š˙ÛđwÅ÷'‡ĘãUĀBe5ZčÎ&ǰĨ<Ļ`o)}Pô,ö5ŌÖšž—æzl/ČbŌöĶĮGĖZ ؈U‡6̰ŒÖØ^ŠŨ*BFëV^†8ož•Ų.<ū‡ŒÕ!,˛€ŧ•ØéƒÄį)ķyĘRgę§cĩá7& nĶąMqzTyhɗ˙iŠ=M>Áp˜O‘n ų]DÚM”o ķI‘7úŠev„!?ōžŨ€b[Ōōu)ëlŌ‡´ųĻ͓OԞÁPyŒDÖyĻ Ų֐FėÂKųEM€ŪMÛôMgM‡?ŠšŪĨ Hŗ›īģ”T2 &Ŋ_MIh“A}Ÿ$yįīhŖF3Ģ/;ßmü[ŗČ+{îÚ(OwķĶaoĸÎ:ĸ¸ž—ëiŦN[-įAĐRAĄĻ–´Ú{ęÃK_Whâ¸"ŸHēäx3.Ķģ<Đ‚‘mvaš} ûvŊęũü° 9Zm\§ŌëäægĀíH\ jÆ­C~JÔ펚g,hĮ¨ž°}ƒ‘müyî>ãÄëxŦžķ5øKfĀQĪĸ6ôõîKÆ]õÖŠŽ|~\2!‹ÍĀĻ!/ˇŖÂ!éx:€Ļ¤ŽÉVãŠÛÔeļŠŪ鋄öÚtI!2ÚÖ{Oâf„fJ‡€žĀŋė–īĨË︜Š!đ gųj÷>û~Ú}boöpümė+nÁMŗCI&i*Ė5:f^°]#›Háŧ€ĪŅak‰+0„ŗ.?…|íāęģ€Ī´Cží–%ŠWûK5ņ´,7†Ɨ„Ãæ–­´NiÉŽ,LQ ɏ•ôúöCF˙¨ãēÛˇoSđ*¨õu3†@g"vũNN{/˛ĄŊææ@€÷za‹nghí˙”)j ‹ V öíģ_P‘ģī˜īô'P¸:; ڏ!`†@—F (â<÷iĄŌûã{;Ųũ 9+Ãr€”‡­{h؏!`]ĸČömÛö,{}ĸÎŨˇôv UpY|y@~3†@g#P‹éNŠ^ą ›ÉáYąÅ.įWšqĨC _:b -ž”$Å‹Q }TÔG>ēgđ'P>ÜžŲqaq"tšT:áĶę/m Š%éį1›{–4õ…¤+’W€Ę)¸#EÎ ØÆĐ’Gēr‡Ũ,ûöŌԒĄåû—ļšQŸŋޤĻ=u”šwj˜^ÃIģž<ÎHk0eé?2Š–—´úߕWpĮĻ“úbŦęXx‚ž o7¤éÃF}ĐYĐįŠ8)=ļ-iĢcžĪcG§æŨ.a LayÂQŊÁâī×˙ ŸMt_~ėüvŽō2­UãƒĮ#t ˆžX)¨&¤×Uj]ŗĐ^&<ĘēEeÅ˓Ōęoņī& įÕJOēú|Ķļ'˛,F&ÕĨ•R$NōŽĪU~ŧNo¯δ(ÅHį ܗr•×ÖxžˇŲ”ÕVyuLWōęŽl&Ķw ŲŌté8p)ė°ÛÛRQŌëĪą”ĪMéōŅąs•=9]ŧh´Ÿ>[ô~fâi ü§‘ŋž“i*lW \I¨œŋī§?ãS™°›2å!b§ą€ËÂ&Ž<Š–ƒôÕmĶ^ DŠN÷8ŊF9ę ëyØéYĀ_pgŠúJ^åƒíTƒĮcõ÷O'q¨g.ĸŒ^Ō´/qē|3ĐôQŖŽŠy)C|ŠČē~};í0ģ†ōš)HÚ QíPDŌD’ø LũjÆgOī)ņo“(ã`x)ĸg—Đ/?P /aé-ɲáĪŌíĸōŪ‚]'Ow7!6¸—,hT)āÛ°zØn⁜ŠGK@ŗŋ´ŋ āÔQi6…^ĖjÚqnŌKĘËŖŅŨ<čŊBf\ČõQâđMŪzŦfĄK°íi$K5ĪÍ8ܨ\šnĀļzãôÔQŦ‚’w)ļS ęž8-ö’‹œzOn m}P7úâxŧ:K „B͟ø˛úK[ Ļį9ƒŋ´•Ģ熇.m¸ŋü|īqgHKuáÉų—ļņ-…A”s|júR† V žô€ŌP7—R JÎ ,VJ~:žéęAcŪ}v3<+ÄCC^ތFäai ā3,&—8žú›Ķ' ģöüK[Šæ6đë~ĨzBÃËĢsøŖÔ„4üķ%ŖîŠĐ™‡.)Lü9R_b7Ŗŧ”#Ex•ܰ˸ e õ{=¤!ĢäÕ˛ßĸ7,ļ•‹Ŧ]ō/myv¯Ļn÷SáÜ`¯ë芠ûæôŧ, žiĢAũ“Øņ‘2JīEhM‡vŽgå‘}ČOķFIē‹”î“Ziđ ×HGĻÆEÂēŠ\3ŗÚá{Ø+aJt YD"‘eKĶ î|ĸB E—GÃæw|Ÿņ˙F0˛Đ ˇŲž$ĩŋ´Ũš|ŌÂŗ´›x™ĀŽŖ˙Ōļ™ļlN÷L3Ķx˜—įž]#.e?ģ.ĘKÚ`6Ĩĩŗ?­ŧČąŠNdeŸŠÕ[š‘ŒĮƒM˜N7Qv:OtÆáI< q~u¸ņ—ļŦ2 §šqdģĄŖäiK™M`Õ;ŽWģ8mR Hd $˙fŅx¤íōĪĸ4œt ēÖ:ãȅSw˙Kۜ@ĶÜNĮ<,u§ÕÎ/isĻędŧ…ö–¨ŖĶ=ˆpīYįšŠũPāÔĄRūŌVËVR‚•Ę”2¯ĸej‹Ņ­§esŠWŅtPB^āM<đũ)N˜wjã#˓<ĐĮ"‡FĪ[ąI†ŅhwūKÛ$,2tŠm1q÷cĩŋR‚NkŧąŖ°ˇä™Ž(6ęô ÖūŌ6Ží,‚ÉlHRŦ2z÷ ~wƒ”ügÎŋ´å=o€õÉ0M{¸E+u†í!PW͓Fũ€ÛT¤đodßĻãHlÆPwûKÛ4`Å1։¤b0M“ckRŧŖG;Ē3ŪH™+(oáëâŅ­eĄ6xÁIߐ…­ÍQČ}+ehŦ•ƒö^r ęD9GĩYđôhīiuĒ”ŋ´]G5ôŧŒŧûę˛mǧ¯ųNj5ΊØ%;ƒiÕ?ËÁ“6aAD*Ąú`G=Ÿ„!ŋŌķÂäSFįŅā:Aq2ø ĸŽz 6¤Č4ļ­/h-yčlúšŅ+EmTņéÚ:.CÚ_ —]֐_’2ŠwX´gÃvķŧķųKÛu´Ķq´÷ŠŽĀĒ­ Ä1‰°Ų>éˆzX†@*kS ĶɭƑ #æĐÛ]ŸQNJņž/,U^–OqÄYë`GV†FŊ„Õûã‰ũ›%”Į˛2 CŠQ Á‡;˙>¤!ŦĸöAÚ$$šk†€!Đĩ(Xh=5„äđ†Ŗˇã×ɋč…da´š†€!`]ĸČ_ßø?÷í .ī)lØX›„cŗ.ü XÕ CĀHE ōwe˛éíŋģũúŦ;zä ÛÚ,$Y †€!ĐÅ(XpŠ%8 ¸ņo  aō–ĩb6 éâOŠUĪ0  V ¤Čώt—ĸsŸŦ˙´;æøoøxk8Ō{i@´CĀ0 .@1 DwũIČ<˙Įg€Î<÷ĸš…˜ĸĪčĸũ†€!`tiŠR |¨ō PųŨãāhōí äÅRÖ"ˆ}‚û1 CĀč˛Ĩ@˜mÜ/D~ûØō0§}žûÂá ; čļYũŲN›ŋrOdnCĀ0 ˛C ( äq”Č–—_xÎũũ͝›éĒŲ~zk^ũ˜ž ÂÅkW–]mM CĀ0 ’!P”Ąt]ŌöIņÄcģūÕļמs×Ūŧ¨§öC°Sm?¤díd†€!PvĢ@T‘{ôķԊGä$Ė!‡ŨYŠ’ˆ4!`†@—A h­ÁÔCû Ņe,!Ŗũa_ĨŖŊĩĖT˛™H—y^Ŧ"†€!`$(ZƒîÄZļŖeģ›˙_ú¯øds͍ ŧq§ę†“@‰ÜĪÆz1o™œŠ… CĀ0ʁļ(ũ˙tđ_w-ēĩÕ,D5ŧ`Ú5.ŽDt"k‰)‘˛iwÄ0 6# õW— 䴂åĻĮ ɑ?ĢiF‰ôķcąĪļloq_lÖ*šhU=z¸UO?!eõuĘÚF9Oĩb4‚!`†@Ų#Õmš¨Ļė…h˛#Ķ,D<ĪšĐ}÷ōÉ[Má×0iÄo 3†€!`T(mV Ô{ö6í…ÜrÃĩaøæéßvŗoŊĶÕ~x]yŌ€y;!c‹0 CĀ(kņšy%ŦIDATJĄ@4 ų!ĩlšëļ[כ¤Ģõ_xļjčđãŨ ģ%ĸãĀŊE(Đp¸Ģú(ŌÔ˜ÎØ CĀ0J€@IrŦc_ã2ÉséšŨ_ßx=ŖhÁĮ†sosWüäfWŗÛ‡4…y‰Ŋ”ÉøķVđŸE™’ÖŽMɈļE†€!Đ~”J86Ôg"æōwßŲä.žršÛŅŌ’Uę¯ž0ÖŨũØ3U˙~ėÅכŊ‘9(…õØs kv’Ë„ŗ–Q¤ąkSrĄeņ†€!`”’)ÉÅRÖ)Ė 6üīīnœ•ģO˙ÄŪû¸ņ+7ëæÛŨ~ûčŖDę°×1̐"™F–Ą’ČZmŌL%ÍY™,Ō0 C ¤”úÆÜtü'ĄDqķõՃ;Â}ņČÖG{SkĐđ•‘ëũūŠnŪO˙Ķ=ûûßõB)LG)L…÷üËt#ūV͚OÖīį^kzUYÎeOäUøV(ÉH1)īLņFo…Ā C[Q;‰`í×&ā­-Û_y%ϟÎʏNÁvš)é Dĩ ’Šû!œu˛˙ė3Oį]9ރwķî|ĐÍŋû÷ĨaĮ(öDϐŸö:ŪÆ.ƞ -13ņĩ1î[giÕËUç ųū ˜) %ËÉ2ęlŦ-;ģēXųĨžđh?„Ž~āļ÷ß7ų”Q>KT^>3‘ۃ˙íP÷“y‹ŨË/ü¯{ėĄ{Ũo–ßį7Ŋú˙j‰'Kۚ‰l ų'_pš#Ūũö҇z1BŊ‡ō nįî†LiÜCËgŗ%MŌ.O¸īvųZZ C -xá’@{L‡XRšMžįądä_3gwÔW/ZÖ˙ÛđŽŽ_îž_K\ūŽ-ž2ĶŠúÆäŸīŊįNķˇní "kĒ~4nĢ%/Eĸ€tŅŖû$vŊį•ÍōŒd*Ķ—eČ×â€e€ušČļŸä)7ŲĘŖ¨åŒW9ËŰ\üå€WTg”| + 4ËYįŖ<ŽC‰xS§œę?öāŊŅč‚ü˙ŌįSnė„˙psīøo׸ܟŊ™7.tį^|…;nŒV´œûĐîģōŊö Vˇz^Aŗ!`†@A´Ģ‘$R"(ËB%2˙ĻŲ ˜ŽYĘBŗ™Sūã;îÃ{|$ÁōąŊ>á~:˙—îƒ5Áį$§ĸ)/JDšĮ0 C ¤´ģ‘´ėIüPKYR"7ūį 7ņĮøŲ>6lK üėŋš^kŗŨģUüēY[ą´†€!`tq:DC”Čõ8Įb7Ž~æioôQƒvܡôvE•ÜčXđäīë3įP"wôėŲsPÉ ą CĀčæt˜Îlx>€=īŌ­[ļô˜qádwūÄqūώū^ōfĐŋ"Žüú8ĮŦįC(¯( Žä…X††€!`tc:TÄqۈ9˙xė&NVyĮ9pĮ æļŊŋĩ¤Mņƒ™sÜ!_øĸōü8'Âôŧ: lÆ0 C t† ÄF‰ÜŽ=˜Āō÷6ŋÛãÚéßwÃ˙ŊË?˙/÷î?Ū)AÕ8Ž[]íÎŋôĒ /f"ŸÅcŗ’ k™†€!ā\§)8øP"úä|üj.bŦūɕ—¸c÷ßqķėĢ][—ļôqáwNûFPTuĪŽÁĶ/×CĀ0 6"ĐŲ $%r/û‡°á­‹ŗVhdŪOgēá_ø×ØĖ\ātGVŽÛ}SqĐ*'û%”ĐF>ėųģ÷ˇūķ0x6§ōYØ0 C 8ĘB„ĸķÍČc(“Ą|ŋĄÎ~ū*ũIÕˇĮr‡ø/;.úö„ŪsgΙ‰>Xœō­Üû[ˇ:¯GeÛļm=’üLy„@›k†@ h—ģ°Ú*×öíÛW’Į ŋˆTËömt/–,3•؁Ÿ=dĮq'Žī9æäĶ“Š[ļäžįüXĖqOËO[ļmû i¯4IJhŽŠ€öŊÔū9īFëĒXŊ öB ,HXYÉjü˛—c뙙ŒDyhŋdČ ĪũąëzķõšžûŅWîúPQžËvlßūà `?Ũ>$Õ?V619­Û‚`7Ú ˛ZÂĘQĮ&öIn¤#8ģŧ į{ef͘(ÍNP“ā5å c?†€!`´e=ÉRåÍ(‰&”'ļ¸ 'Â-Ŋ÷iik;ô“PwgI[TÔߜûĀjßßS‰÷ŧ7wįģČĸ2˛D‰@Ÿxá:S+ģÍč_d7Åm›3´ ŠC RHĸļˇßzãÎÍrĪÛņ+ĖNžLD–ĀķgßßíbįžķWįžC‰(KūšęÁž?įĘĒĒGKP„eŅūÔą”Ĩ6/jĪÉ)ø×ļŅVB ¨Ĩį“ß(Ŧú.ímŨB[ž÷ã˜éH*i +-.ÁIĢĒĒœāúļ¤ĘCrŖÖéo:wØ8ß?ŋ‘˙šÚšŅ{8÷âī<īģīņ­bZĄŒXnŒ@ )Ũ°Ÿa–ZÍ~Ú=Đ*~Un@ˇ§Īķf͉Å_īyûŗ`žų įæ}Ĩqy,öô;ŋ=ɑƒEw&t4×`ßãÄN3îU{’įN\SüŲ0E”ŋ#ī3(’Ņ(>´ërÚR‡htēÎL' ` $čúâüĸĒĒ•°É&Œ}D˜€ĸŦ=:ę Ččufč7ˇbØ„Ō¸ĩbĨīb‚ÛHkPĢŽ!`…€)ŽBÚĘ1 C ‹!` ¤‹5¨UĮ0 ŽBĀHG!m冀!ĐÅ0ŌÅÔĒc†@G!` ¤Ŗļr CĀčb˜éb jÕ1 C Ŗ0ŌQH[9†€!`t1Ltąĩꆀ!ĐQ˜é(¤­CĀ0ēĻ@ēXƒZu CĀč(ēŨ]X\¨§Ģŧf¨ī7&æ){Rۏ‹õėYĘžÕŌ hm™—|ŠÂûŨfäËß|6iT-OCĀ0ēĻ@ēA#[ CĀhēŨ–Ļ|,{LÁlôŧĄĄßÜ]ô-ĶĨŊÔöãjoo—ÔæK‡@š.ķY[Ļk­ė´h[vöō•$ĩHöö˛XCĀ0  ˜Ɍ‘ CĀ0˛#` $;>k†€!S €1˛!`†@vLdĮĮb CĀ02 ` $0F6 CĀȎ€)ėøXŦ!`†@LdÆČ†€!`Ų0’‹5 CĀȀ€) ĀšË!Їɚ1 !` $ßôũ‹FÅbŗs°%E˙Ú÷뮋Åū-J\äûũžîû3ōũ‡ą*ß?ûūnQžĐ˙žs=TfĄå†éÍMB †ëą¯ÉrƒéY¸ēÁˇ>‰ĢíZō›’Mohķ°ocˇc!~@ O"ˆlWÂcˇ ')ØŖļ#UŊ0ŽŽŽnČÁgŅy ` $HŸôũįûxŪīs°%EĪöũYk=īā(…2ßšŸriĶŽņž˙¯øūûÂųÎMyĸîÅž?úĪĨ™ŋ8č,F’˛;‘{ŗ “~š¸œ˛§ĸ 2&E¸Ē)ëß÷‡xž7˙ÄՈ†Û+Âx{öė9žŠŠt įmpÜSr¤ØLü‚–––æ|Ũî2Å<0Ib™YUõ`!CČQļ_87žÆ÷›QUuųCq›cą›~īy˙ąÚ÷?2ĐķŪ ų5SyÁš3>āܛ!ÍÜ6!PĢÔ([ãšÔĩ)ˇĖ‰“Ū%:3ÍFúKiqé]S<ŲIПÅũHVĩ„Ū€ŠĐÍ[Im!éF°>-Cœ‘ DĀf 9›âû'ęû“ÅļŌ÷÷Ô˛ŌbąÃGûū´ÅlâîĶ}’–ÄŖøížˇg“īëû—‹ö7įÛĪķ–īN?ϰ˕UU>ėy߈*ø>pirîÖŨ|˙õœö[,Œö/bŒęé´N—q°Ī`ˇÃŖeŽká OœŋÚÕØ—āņąobī".PF,‡0éôGŊ1N?÷Ql64čŧöJQŽ47 8ÖĀ´ d4ˇ5šĨÕØ-X- >WqŌ6jëDĀĢļPģ.VŽŌm#WņJ‡‚ÚO.|¯ājŠņi•ĨøĐwV$ū Ō ‡/l됭ۚĻ@r4ų[Î}jŗąũƒ^KKŋaŠácέåû—|Ęšå띋Ļ8N_<Đbijŋ w$v5üęxžĮއ_J§›0Đ´f,JåėŅ<é¨Ĩũ¤0ցåPpƒ_Šũ>1ĢmŖm°}ûvÆp~=QŖ°ņĪÂJĄ×`đˈâÁN„6…ôlŒQíĄöf4”qqs߂˙â—a—nĀ*ŋnkō™ōu[p2U|įų™įũœžC,ĢGÆb‡5;ˇŋÂįTU­~ Ûļ‡į5ŸYUõĸö?Äô¸įËvŨ!ÎŨöf(/9wĘdßßį.Ī f)3cąÁŦ_Œ¸ČķNß=2SQZ3E#ĀV”ˇVŠYķ^Ī%h¸ŋ†x)Œ9t@—cE^É貙b9îQ¤{ Z |įŅŠÜĪ*:’A¸ęĝÕjA3<5‘rhnw*v3ö:Ŧ#Ī H÷åhlļv.vvÖLfę‰ęÆˇ‚÷Ę8Ûëā9ŋ°V;§ļØ´ītíō¤˜úā7ųGKˆōŗ’œûąjŸ&Ŧž‘[h7^ĶĪyÖ!ËĐģĩ1RDķīãû/Į•GZŗ†\ŲhäîĒĒKÂtôVo>éy—jĪcûyŪ…ƒ|ÎWĒĒ‚‡8W~ßvXĻčO‡ NIEÂĐŲ<ŒBØJÜ`ˆŅšœ‚[˙ hûĄh2wl"AųĒcbĖā6ÆY”ī+t>gĪņkŗ›qfĀ0B^îR€ÛŖøīCÆjąë#)'šNöŧāĄ~Ņ÷÷ãøÎÆ5ZÛk’īOŨĘúēŦüá,ĻÄëÖYŌË€đ~*ÄŠĶXt:úûÕĐ/S‡‚mJM“Ö(v56TŠÖ˛ĘŌ ŋã Ãŋ‘ü§Éâo“üt^_a3RčZ>ŌRT=XÍÃ×°ZŦɁ‘ÚĸXSĢ„”—:H ĻĸÅfÚUŌ™iį–<ŌķŪԞČ6į’žųøk< qˇ™ÔúūēfßZÖLjef¯$xˆÛYÔn—=ŖË`؈‰-­$SK‡ĄQįfl?üsā™ËLáŖtbãžFxMRŠ”iŸŽcS[n&žņ[A>œŖđdņ×CSg%˙žō›IB@û ZjTė FS‰ƒ›ÄUÚ@đŒPæAŅl);õ™‰Fwŋ-au@S׹ōšsGķáāĸ}=o‹ŠdØ4 íŊí8Ī{Žäˇô‹Ŗĸ„.ĢĒ:?J7IвČ::ėIäz/6Š2˜ŦRč4f&Đ}Ķą‚°:})QØlīĪRâ鴏öHđ;ė!~x—k™ W6a4ķ 0Îqh‚hž”ÄH<÷ƒö¤œ›Áōfč×Đ>QEõiÛøŗ‰ô÷cõŒ,ÄnÆĒŒķ°ŨŪd{ē=8Å =‘W;/Īü•į]4ÅšŸ_áûķÎû¤sŋá ÜĢ…‚â˜30ũ“bå°tš ĶžD´ŒŽ¨÷Q”‰:øņø¯Â]CG¯“>Lũ9¸s Šķ˜ˆÕ,BŧÕØâ7Öé­F:šŗąwâgkD -ĀmÁ*ŨRâĸ߀@2“ 0{üuĒM'ąaĨĖubj¸ŪŠôø›Â6€ī|ԈÜfC>į“ߨWČ_ŗÆÁdēąÍw ôđ›É‚g1Ÿ¤Ã׈Ķ}ŠNcŽ|čyč‡]æ@F”,Amf9ķw%3ŒŅėkôĐQ]>1~ë'ÎMžáŲyįĢžņž÷ĖŽ\’}Ę3™bĄbāĨŽ`z˜…ĐMa,u:ë1œC é¨æ ü†ķ†’f~žųŒFüdčÃĢ“:“t:Nzá:ėxfŪJœ–ždZČCN Ã9ē:¸ŠđŨē3ēõ/é'~zëŖ€€đņv`ÜåjĀs ´ņm‰˙ĮÄW‡mOąÖ,¯‰tÚoæAz…eÂg$ä$%Ĩ[ ø´Ë×é+)0õ K°RdŨÖx€ĸÍģéft$xŠ}Õ퓨õtŨĄÎ…Öą¯ī7˛ė^¤šąĐLډ?l?e_n˛ĩS•Û”m9ãUβ… #ãmø—ņŦ%NËŅo^Iŋ9ÚGCžŽp˯¨Î°HG´ē•a•Œ€–(ĩ„Y‡]Ë@{Ęc*´Ģ*šRĨŨH)P´< C Ë"Ā,ƒīƒƒ &ĸ<ôŨĐ*; ÆåČ. FJÅL¤bACĀ0RĐ>˜–÷ģÅJŨŗí;ŦđX¤!`†@&LdBÆč†€!`Y0’‹4 CĀȄ€)LČŨ0 C +Ļ@˛Âc‘†€!`™0’ Ŗ†€!`dEĀHVx,Ō0 C Ļ@2!ctCĀ0 Ŧtģ uK‘ĄÜų ›ŋŧHmŋčŨ@å-šI—Š€ĩe*"………_üĮ–Ûf %͞2 C ;!` ¤;ĩļÕÕ0 "Đ햰4åcŲczˆaŖ]įB‘äę:÷$B™RÛĪŽsĪŨ0åēĖgm™ģíR9ĸmŲŲËW’Íf Š-daCĀ0 ŧ0’LÆd†€!Š€)TD,l†€!Ļ@ō‚ɘ CĀ0R0’Šˆ… CĀ0ōBĀH^0“!`†@*Ļ@Rą°!`†@^˜É &c2 CĀHEĀH*"6 CĀČ S yÁdL†€!`Š˜IE$Kø=įzœäû—,ōũ~YØ,ĒĖ¨ŽŽÂ‹ąØ‡¸Åô,DĖûŸž={TZŌԕYÕē•8´Á T¸^íI~/ŸĘ“æ x¯Åž‹]ŸOãÉS yb%åq:ĘŖŲšŖßöũÚ<“['#@2Ōßy¯—ÆüĢ=Ī›}^žĸÕpßÖmđ6`kōLclĨG`@˙&ÚPטߚg›hīŪXšŊōLcly"` $ îņũO‹ÍÚčÜay°Ky!0q–ŖNÄ.¤ãš˜đyØSą9g’(škéxlæXi˜uôĻü1ĖŖ@ę™M\š§<4ŦĨío‡]žiŒ-OLäÔÍž?“NdĮ(f y°Ky!°qæFEĸķyNa:Ĩ>QzǟøáĐÆcĨpĖt"šAPüjėVüĢpĪš^šIŌž“ņ' á+ąWĮ #4Ü ßXÂ×$˜ĖSō^.IišÉxĪģģî×ļ^q-ČČķœTĄĄ¤jÁŦI‹„{Ķņ,€o öuFą‘(ķv4Ûˇo—ōJÛŠč“Âōi—&Úį:f&€g%Šã,—aĨ8ügŠW33%FĀf y*å‘›ąTڀUCĮsâj›Öhd ߓÖņ¤…§lˆ(‰™ŗŠvš;€6›EÛ^ÃāāᲲ b3.ܸVĩdPGŅš,ƒēŒŽG{!iFąđ ‚ᐴ F,'ZP'ĄđŸĮ>`khˇËËIĀŽ,‹Í@ērëZŨĐšœRXa‰:ܖDd˛§Fਤ‰@‡¤Ŋ‡bßINaĄÎF  40¨ÅŪ‚ÍÔļD™)%6)%š–WY"ßPŠ™Š2ВGFƒ‚čŸ:ĸņqëP&|yd[ )-KŌfãæIŦ6ĘÕ>°fÚ›´3Ā–}į"€ō˜LĮ?)ÆįR’”ĩķĖPŧ¨%ũPÅAë‹ ü ›) jiW}§ŗŒļ9E˛™Ycžßø”E*YS •Üz&{.zŅųkDĒÍōat,ķŖZem4ŠfŊž1PØLå @ÛŨD÷ByœÔ›iĮĶpGāž[9ĩ¨\Im Ģ€ļÛÚmáKæzŪæ’k'!€B¨gtē*^|}ĒÄ×pôSKTjĪ&ėÖT…‰ßÄČvŪ´ņâ1ĶņĐ~iß>”< ,Yi‰ō˜N›€ļģ kĻ0R°ƒ=ī­ež§‘Ž™ @ üv “¨ŒZƒ¨ø‘·Ãp*Ž|Rų-Ü1dj”ĘŒŽ‘ĀJą%,{ CĀ0ŠBĀHQ°Y"CĀ0 S ö †€!`…€)ĸ`ŗD†€!`Ļ@ė0 CĀ( S EÁf‰ CĀ0LØ3`†€!PĻ@Š‚Í†€!`˜ągĀ0 C (L›%2 CĀ0bĪ€!`†@Qtģģ°¸hmZŠĄžß ›ŋŧHm?ncĩ?+/ī&Ë(ĩeFhōŠ~}ī—Í@ōj*c2 CĀHEĀH*"6 CĀČ nˇ„Ĩ)ËĶCtã˙6†ÍŨ‰@ß2]ÚKm?Ž`÷ŦͲ#PŽË|֖ŲÛ-]l´-;{ųJōŲ $]+Í0 C 'Ļ@rBd †€!`é0’Ŗ†€!`äDĀHNˆŒÁ0 C Ļ@ŌĄb4CĀ0 œ˜É ‘1†€!`¤CĀH:TŒf†€!S 9!2CĀ0 t˜I‡ŠŅ CĀ0r"` $'DÆ`†€!nw•I:rŅÖú~íĪ› ų3ä1ŲŲ3 ĢZBļLũÎOú?|ĻŽødŗĶGX‘‹\”Q{ö˙YŽ˙-ŲYŌá9L`ŖōÅĄ1˛œeu˜&c*ä ÂÔČ(Ȑ¯ ¸r{?áKCã‡ė?p$°fđ-”Ęe†Aփl,ˊ Ō{§ ‚YaíŅ8A.+N1åŠgÄ ųGgņ$AąÃĖËį’ŲI3ãũ†|nåķXÃ>ōųq‰Š8Ņļ×7{ÂB;áᥓp{ē @üCäL:aŒÁCŲĨ~ŸnŊ:ãū¸ô}ã \ØâNp&?ÜÎí ĩßĮ*Éø[-‡|‘íČ(yŲ—lųc*Ö*Î#^d•úžЏRGĒ0ŌķcßՏ īa?Zb˰ÃX3vģˆ5bu€‰Äęą돌GÖÆcųÚž-FO&ô#øi>öМ˛ĒIėĒėzė>õ\ŪŦ\ŲĮ0C4[,Hįį2ũānÍc˛„œąc˜vöîČö~ÅÖō†!ßĶÆĨoēœS¸Aeú7îAĮž@÷Mgō.ûÕoãHÅy .ģ¨Â/EĀŊËfä\€'đA`ˆq LƒuæÃu*3Á\°‚b°Ŧe`Ø ö‚ā¨ā48.ƒ6p܅kĨŧ}ā@„„Đ:ĸƒ"fˆ ‸!ŪHŽÄ IH ’Ž)2YŒ#%H˛ŠD~GŽ!§‘‹H;ryˆô ¯‘O(†RQMT5GĮĄn¨†ÆĄSŅt4ÍG— +Ņhē­EOŖ—Ņh'úíĮό10#Ėsð(,KÃÄØ|Ŧ+Å*°jŦžékX'֋}ĉ8gâļpŊ†âņ8ĪÁįã+đ2|/^‹ŸÅ¯áņ>ü+FĐ#Ø<,Â$B:a&ĄPJØM8J8ŋnÂ;"‘Č Z]ᷗDĖ Î!Ž n!ÖOۉ]Ä~‰¤C˛!y‘ĸHlR.А´‰´Ÿt’t•ÔMú ¤Ŧd¨ä Ŧ”Ŧ$T*P*UÚ§tBéĒŌSĨ˛،ėAŽ"sÉŗÉĢČģČ ä+änōEbAņĸÄQ2(‹()Քs”{”7ĘĘĘÆĘîĘ•Ę •7*Tž üPų#UƒjM  NĄJŠ+Š{¨§¨ˇŠoh4š9͗–LËĨ­¤UŌÎĐĐ>¨ĐUÆĒ°T¸* TĘUjUŽĒŧT%̚ŠúŠNSÍW-U=ŦzEĩWŦfŽ ÆV›¯VŽvL­C­_Žn¯Ĩž­žB}ŸúEõg$ s ŽÆg4ēčŨ„@įĐĶwŅĪŅģ5‰šš,Í ÍbÍš­š}ZZNZ Zŗ´ĘĩŽku20†9ƒÅČbŦbbÜd|Ĩ?ĘooÔōQÕŖŽŽz¯=ZÛW›§]¤]Ŗ}Cû“S'H'SgNÎ}]\×ZwĸîLŨ­ēįt{GkŽöÍ]4úĐč;z¨žĩ^ŒŪŊz-zũúú!ú"ũMúgô{ žë NôŌ Ŋ †ë O>gj1ũ˜YĖĖŗĖ>#=ŖP#ŠŅŖVŖc ãxãããû&7“4“u&M&}φĻĻsMĢLī˜‘ÍÜĖøf˚ÍŪ›[˜'š/5¯3fĄmÁ˛Čˇ¨˛¸gIŗôąĖąŦ°ŧnE´rŗĘ´ÚbÕfZ;[ķ­Ë­¯Ø 6.6›-6íccÜĮĮTŒé°ĨÚúŲæŲVŲ>Ë>ļ`lŨؗãLĮ%[3ŽyÜW;gģ,ģ]vwí5ė'ØØ7Øŋv°vā8”;\w¤9;.pŦw|ådãÄsÚęt˙îáŧÔšÉų‹‹Ģ‹ØĨÚĨĮÕÔ5Åuŗk‡›Ļ[´Û ˇ îw÷îî=\õŗōËđÛī÷ŌßÎ_ėÔ˙}€GĀŧ€SX`H`Q`kFP|PYЃ`ãāôāĒāžį9!§B ĄaĄkB;Xú,Ģ’Õ7ÁuÂŧ gèaąaeaÂ­ÃÅá hĄˆĩ÷"Í"…‘uQ Šĩ6ę~´EtNô‰Ŗ'–O|c37Ļ9–;=v_ėģ8˙¸Uqwã-ãĨņM Ē S*Ū'&–$vN7iŪ¤ËIēI‚¤údRrBōîäūÉA“×Oîžâ<ĨpĘÍŠSgMŊ8MwZÖ´ãĶU§ŗ§N!¤$ĻėKųĖŽbW°ûSYŠ›Sû8œ œ\_î:nĪ‹WÂ{šæ•V’ö,Ũ+}mz߇_ĘīƝ2B3ļeŧΌĘܓ9˜•˜U“­”’}L¨!Ėža0c֌v‘¨PÔ™ã‘ŗ>§O&Ū-A$S%õššđ'ģEj)ũEú0Ī;¯<īÃĖ„™‡gŠĪÎj™m={ųė§ųÁųŋÍÁįpæ4Í5šģhîÃy~ķvĖGæ§ÎoZ`˛`ɂî…! ÷.ĸ,Ę\ôg]AIÁÛʼn‹–č/Y¸¤ë—_Ē U ŅK=—n[†/,k]î¸|Ķō¯EÜĸKÅvÅĨşWpV\úÕū׍ŋŽL[ŲēĘeÕÖÕÄÕÂÕ7×øŦŲ[ĸ^’_Ōĩ6bmí:æēĸuo×O_ąÔŠtÛĘé†Îáë7™nZŊésŋėFšyÍfŊÍË7ŋßÂŨruĢīÖęmúۊˇ}Ú.Ø~kGČŽÚ ķŠŌÄy;ŸėJØÕü›Ûo•ģuwīū˛G¸§soĖŪŗ•Ž••ûôö­ĒB̤U=û§ėo;x žÚļzG ŖĻø 8(=øü÷”ßo ;ÔtØípõŗ#›ŌÕ"ĩŗkûęøuõIõíĮ&kjđl8úĮØ?ö45–×:žęåĒƒ'ķOöŸę=~ēĢizĶŨ3“Î\?;ņlëš°sΟ?Ķė×|ō‚×…Æ‹]rģTwŲårm‹sËŅ?˙<ÚęŌZ{ÅõJ}›{[CûøöW}ŽžžxíüuÖõË7"o´ßŒŋyĢcJGį-î­gˇŗnŋē“wgāîÂ{„{E÷Õî—>Đ{Pņ/ĢÕtētø°åQėŖģ]œŽ%?w/yB{RúÔđiå3‡g=Á=mĪ'?ī~!z1Đ[ø—ú_›_Zž<ōˇīß-}“úē_‰_ ž^ņFį͞ˇNo›úŖûŧË~7đžčƒÎ‡ŊŨ>6Jüôt`ægŌį_Ŧž4| ûzo0{pPÄŗåŋlhZ¯÷@K‚˙mP&+ÎfrAįI9˙Ċķ›\\Øã @üBÂá?ĘVØĖ Sá]ö į PGĮ‘6$’4G…/*<ą> žŅ€ÔĀņāāĀ–ÁÁ/ģ`°ˇ8•Ŗ8ĘDvŨŽ#Ŗ–5đŖü$Wp2t× pHYs%%IR$điTXtXML:com.adobe.xmp 976 472 Y7faiDOTė(ėėMȆ2`I@IDATxėŨ¸ÕŲĀņ—{éR"  ‚0XČ‡A?b5XŖą`, ĸĸ‰XŖÆF4¨XkÄ.jDcÒEEE@@é.ËŨīŧŖ3œŲģuîîŨŲš˙yØigæĖī]xæŨsæL­˜™„ @@@R Ô"NéÃF@@@hž € € €d @ģ € € € @Íw@@@ H 3@b@@@H ų € € €  tHė‚ € € € 4߁Ŧ6nÜ(K—.•˛˛2Ѝ¨Čē<@@@0”””HãÆĨUĢVŌ°aôU"NKÄļ€&ĪŗgĪ&qļQ˜G@@ĸĐDēC‡i“hčĸsõW~îÜš˛víZiÚ´Š´lŲRJKKĢŋœ@@ȁĀöíÛeŲ˛e^ŽĶŽ]ģ”G%NÉÃÆxŠS§:­Ī;v$yŽĮa@@ŠN@“čYŗf‰ļBwëÖ-eũI Sō°1^`ōäÉÎĒΝ;Įob@@(J3f8õūÅ/~‘˛ū$Đ)yØ/@/Â2 € €ģ tąG0¤õ'i`¨ € € LGÁT$ĐŠt؆ € €Å(@]ŒQ+‚:“@A¨" € €d%@;g*@Šû!€ € €@ą@K¤ŠŦž$ĐE0Ē‹ € €iH ĶąCč j”A@@0 @‡9:E\7č"UG@@„$Đ YXYUčĒ R@@Â&@ļˆD¤>$Đ $— € €ž ´GÁL.H sŠÉą@@@ $ĐaˆBë@Á rI € €ÔpčūČ×å“@įK–ã"€ € €@ĄH  %ņķ’@G<Ā\ € €5P€ēŊ:.™ē:”9 € €T§ tuj× s‘@× `sŠ € €Ôčzķ–r)[ŋUĘË+¤nŨRŲšYƒÕ$?§%Î+GE@@Â @WŗũôoVČ#OO•w?œį;ķ>?k!Wé%;ėė[_Ŧ šJ 7lØ ;í´Są2Po@@ˆ@^čˇßŸ+?_$õë×NIļuëvi`öxÎūNklʝ ŧqîü5ōôK_ûęšvŨšāŦîŌĻuã¤ĩ‹ÅÄ$ÎSäžŅ“’îŖū:Ŧqhû”ûčÆ×ߙ#ŸM^, ÖqöUÃ&ëÉĪø…¯ni”§r‘@6L†.ŖG–ĶN;-O5å° € € €@fyK 5aŧęĻ÷D“čLĻæ?Š//>vĸ45I`˜§7Ū#WßōAĨ*>:â7˛ī>-+­wWĖ_¸Núų‚ģ(įŸŲ]Žęģ—ėdāqoĪ–ģüÔÛöōŋ“=Ú4ņ–ãgÔvđ°ˇåßīÛ¤†/CM¤ =å"ē>hîéĶ§Ëš5kœÖæ!C†Č¤I“d—]v‘§žzJj×ö?K_§NŲwß}E?™@@@| Tk­]1đŸ%Đߛg†ŗžNągÛĻōÂ#Į›ÄĢ$ŨŽUÚî&Ģz\&Đ¯ž9KŽŋũßNŨuņNUiˇNŲ&Đúc„vũļ§Ë.öĒ@ķAčŲŗgK׎]ŗ:ß| =zôČĒ ;#€ € €AB›@ëÅŦ5ŨžŋžąB^õĻwmŋūU;šxžkĢäĶ•{čīy-ŌšLhŊÆÍ¸ÉĒŽÎÕų´ûöéŧ"ĢÖlv{ᑤÕ.™ŋēÉ­S6 ´ļ>Ÿ7ä ™üÕRßōË=œÖü’’Ēu—’@/]ēT<đ@Yž|š¯NŠ´…ēK—.Šva € € P'Đz…vKt?ĶĩyX\׿ŗWĘiįŋâ`dō\uUÕÜdU“‹ZŸķ>į˙Æ9­ōzĖ!öß˙îį:›ņäÖ)›Z{œ;xœL›šÂwž_÷iįŧJĢĒĪ”I ŨŠlŲ˛ÅŧŠĢޜūųōä“OJ¯^Ŋä_˙ú—ģŲûÔgĸXĖã`@@ō,úÚ~ũ“ļĖļßķ'>’lē…û \p“U-^ÕZ[‡\ûŽ÷:*'yŊēdÛúëÖ)›Zë˙âk3äæģūĢŗŪtįõ‡™į ę-ŠJížĶ…ûÄO”'žx¸äˇ{ž{^>@@@DE“@k—fM õõWöTŦ ´&Ī×ßņįĐz=mvm,:ĒwüõŲךl>h­ĮûjúrųƌŠŽ­šúëø(’3Ũú\&ĐúĢįž{Ž::Û@@@ ¯E“@'k]-ÆzëļírÕ_Ū“÷'Ėw‚Ģ×öė¨ãeįæ ģ* t fPˆ:$vA@@ĸ Î2\n˛ĒłtáÖ÷Z_4ôMođ.mY×ãMžĩn’ũČ û$›ôŊÚĢÍāe:í˛sÌß=ėxîzhW‚O@@ˆŠ t–‘t“U-–m­‰ęyC^—oŋ[ãœUģmë1š6Š—e-üģģuĘ6ÖØ]ųĻ3úˇQ“ų‡ī>ZÚ´nė?A€Ĩ\&Đ< A@@œ Tk]nZ;OŋđUgÄéLGĖ^´dŊüæôįœänėã'Jũzĩ}AŽé;@– n˛ĒŞI ĩžŧlĮkŖôu\7^yˆÔĢWęÕĀQ\_ĶuöŠŨŧõéfÜ:e“@Įb"\ņ†|úÅbßá8´ŊÜ|õ¯¤Ŗpk…bĻ‚]t‘<öØcŌĻMųüķĪĨI“&žēę{Ŗß{ī=Y°` iĀXY¸¸ĖŠBĮöÍŊ÷>'ĒSļIŦ['Û&ŅqíuĻ…÷‚ËūU)>åø.2tPÕŅ\$Đ+VŦC9D4aN5õčŅCƎ+͛7OĩÛ@@@* ä5=fŠÜ7zRFÔéûo?B6øá5UņĪ ģŅw%ßōį_Iii‰$:ž&â÷ß~¤ü|ī]Ü"9ũt“U=hĻ ´v—~č‰/äAķ'“éÚË˙GúÕ)“]}Ü:e“@kÁīŽ“‹Ė3ĐnbŋĪĪZČđûJË 3>w˛s‘@ëąŋûî;šéĻ›ä˙ø‡īT7–K.šD~ûÛßJˇn™ww÷„@@@ ŧ&ĐYÔŖhvu“U­pĻ tž/έSļ ´ÖĢĸ"&:2¸&ųAŪAėÚr•@ģĮ߸qŖŦ_ŋŪYŦW¯ž4mÚÔŨÄ' € € P-$ĐY2ģÉĒķĀqŌšãÎY!÷ģģu ’@įž6?1× tžęÉq@@@LH 3•úq?7YÕÅģoę+vßÍ-ËCUiw}ķÆMÛ¤Ž í-ķœųwūGH ĢDJa@@@ Ĩ tJžĘíÚŪZŨŨš5a~ųoė*@û4X@@@r+@ĨįÄĪ™×?ũĢRŠgęįŧbĢŌ†<­1ę3yė™ŠžŖˇŲĩąh=rų,ŗīY,Ѕ; ,vE@@ĸ &í>ĶQˇŦŠnRkŠzfˇnÛî;Q!ęāĢ€ĩ@ma0‹ € €‘ ŽDÃw$Đዠ5B@@Ē @W͏ŌIH “°@@ŠV€ēhC@‡;>Ô@@˛ Îی@g€Ä. € € PT$ĐEŽâŠ, tņĊš"€ € €@f$Й9ąW–$ĐY‚ą; € €„^€:ô!*Î ’@gܨ5 € €$ NnÖ*@Wĸ € € JčP†Ĩø+E]ü1ä @@@Ā/@í÷`)G$Đ9‚ä0 € € čЄ"Z!ŽV<š@@!æ[čŧ°rP@@(  tņŖ|ęŠS§JEE…tėØQJKKŖ|Š\ € €ԁíÛˇËŦYŗ¤¤¤Dēuë–ōŠkÅĖ”r6"` Ė;WÖŽ]+M›6•–-[’D[6Ė"€ € €@q hōŧlŲ2/Įi׎]Ę NÉÃÆx7ĘėŲŗVčøm,#€ € €Å( ­Ī:t† ĻŦ> tJ6&Đ$zéŌĨRVVF"ˆu € € Pš87nÜXZĩj•6yÖ ".аRI@@@B @:œ@@@ (H ‹"LT@@@ Đ$ЅŽįG@@( čĸ•D@@(´ tĄ#Āų@@@ŠB€ē(ÂD%@@@ -@]čp~@@@ĸ .Š0QI@@@B @:œ@@@ (H ‹"LT@@@ Đ$ЅŽįG@@( čĸ•D@@(´ tĄ#Āų@@@ŠB€ē(ÂD%@@@ -@]čp~@@@ĸ .Š0QI@@@B @į8ʤ|õ ‰mÛ*% JVmEjÕĘņY8 € € €@u ä$._ĩL4qÔŠVíēR§u3SRm×Û˛Ybą )Šß°ÚÎĸõŸ| K¸Iļ~˙­oSiŖĻŌfØße§ũ{ûÖŗ€ € € P\UJ 7|öĄ,ēc¨”¯Zîŋj“<īrÆÅŌâô"%Ĩūm9^Úōí ųöücLâ^G:>ûą”6ųI 3lšöš,õW)ŠSĪ+ŋŊl­ė:ôvŠß~oo]Ĩ™XLŪ:D֍ĩŌ&{Å.g‘§4͊'GČúOŪ÷~ ¨ØļEJ7“6Wßí­KsˆĸØlÚų &Č÷ßīœ÷Øc•† ŗûÁfõęÕŪĩvíÚUöŨwß´įŦę…8gU뜋ōŗgĪ–>úȉÕa‡&ģíļ[.čƒīžûNô{ĢßŊæÍ›Kģví¤~ũúŽG!@@Â K MŌ¸äžeõ+O¤Ŧš&ŗí~Sj7k‘rŋĒlÜ4sĒĖtŧsˆ=īzVū˛m鯌ÎÔ*-•cū#ĩ›g—đų’…%K–ČŸūô'yėąĮŌÖ荪Žrö‹Otg͚%:uJ[>Õ¯žúĒhœé3ąéßŋŋŧøâ‹Ō¸qc™3gŽė˛Ë.™wöûôĶOĨGÎüwŪ)—]vYVåƒė\ˆsŠgļeVŦX!ú§víÚŌĄC‡JÅõûuöŲg;ë?øā9äC*í“īú#Ņ•W^)Ī?ŋã˙ûœŖG–?üáRjū}3!€ €‡@ö ´I$Ũ~…Ŧ}gŦw…?9ędiÖī )mĐH6|>A–Üs­Äļowļ×Ē[O:>ķ_͊ŦeØ;I’;ŨũæŅŌ¨GŸ${Ļ^ŊōšQ˛lÔ­ÎNM=Vš˜D¸bëiüË˙•’†’֖ëyƒû;Ûĩ2/žûjY3îg{ŗãΔփŽKz,Ũ°ųÛé˛eîLķüt#YķÆŗ˛ūãņ?&ĐL]–ōDØøųįŸËūûī_éĖÚB¨-Ī˖-“1cÆøļkōzˇRųō5ĢbŗNīííˇā–ÁŪ>ÛļÅļ.ų>ļmų’ØÖÅķcÛ7oôļéŒ.ëzgûûÅ**|ûØ ›įL÷Îŗūķ öĻŦæW<ûP ãl[š4öíŋ‰-ž{˜Š|yĨsn™?Û;îŧKO1jo¯´O˛+_zĖ);ũˆŽąm+—%Û­(ÖōÉ'1ķUôũš˙ūûc˗/÷Õßts™ŲˇŸimŽmŨēÕÛoíÚĩą ÄLkļīéļ3IąWÖ$šąĨK—úöŅ2 .4_Šäß)īD֌iõöŽkZ­-™Ī^tŅEÎ1LR›?~æ̰g!ÎY…ęfTTcwÜqĮy–ĢV­ō•Û´iSĖü ãlīÕĢWl›ų?§:'­ÆØũžxā1Ķũ?ļxņâØôéĶcũë_Ŋmēi!¯Îęq.@@*d×mZvžģėTŲøå§NZŪjāuŌŧߙÎ|ü_ۖ-’9göqZĸĩ ō^ŋ'uZî& ū2HĘūũ/o÷†]{ČžÃŸr‹•o“Y§,Û׎ōļëL|˲>sŊiÚ$‘ŌÚ˛yÆYņôHg˙&‡ũVšô9ÆÛ>@ŊļíĨ^ûÎöĒJķv tüų*íœÅ ÷m-RŋSWiwĪ‹?îÖé‡.ܙˇ@ĢãŠ'Ū‘’zõMˇōŖdįߝ“ņyŗ¸ŧŒvÕnÛúŧ¯ļ0뤭‚úvˇ}Ķ†ØŒßvuöI”ĐęŗÔúlô†/>ōÎąvü?âēÍûcöL&7Y­”°gR8Á>ú¸[=f&?8ÄÆ­S6 ´ēÍė×Ũ3q`˜sîá;üãO”ĮeĶōė% ĻU8ĢįQíg]5aŌd-Ų¤ûžuÖYŪš%ęwŨu—ˇ=QæŽĶgTíIŸšÖd(Uâí–ÕĪD ˛ÜÛץõ2dHÚziânFļĢ•v>įLô,ģ}íņķæõMžzęftõ”×Ģ?D˜îōŪ>ĶĻM‹Ų?ĻğC—ŨXÔĶ Ôæ•M´¯Ŋδ`ûęWÕŊ>Ķ…ÜųŖĪé'š´ŽîŗéZ—gžy&ŅnŦC@Ą@V ôō'īņŗ%÷^Ÿörļ,üÎk˙įŗ„×)dn Ķ&ĐS'ĐîÉíVņD‰ļģ_ēO7YÍE­­Ān ģO“× ­ŋn˛I õ:—Ü“'7^ų|åVĪt&UŨރ9ŲI§éN›õ!/žøb/˛Īø­_ŋŪ;—&`ڂiOvkŦ&-z\ķžāØ7ß|ã đôë_˙Ú;ZfēúzÅÍŗÔŪ67ųŌ„íšįžsÎcēû’w=ŋ=H˜Üģ‰ž{pm]vŠŸ×\sķC&a¯ŧōŠ×ÚĢÛĖķŧnടų:į“O>é̝ÖëøãiËžš›QÔ}Ûí2ė–|÷šõG ļ:Û?ļ¸Ûõ‡ķĖŗ3—öøÛßūæ_-GŽĶ–p­—éZŗŋî1ôS÷Ķķ|ņÅžXU÷ cúŊ0#„{×?@^Úž € €@A˛J Ë>ī%f™$ĐvrëëBáZ“įoĪ˙į4ũčŊcåkW rĐZOļnÂÛą%ÜĶžĻ~čüU-¤ŖģŖbĮ'Ž™;Ķzn\×Y;ļ“IMĻ4؊Ÿ´[ąÛęŠuĩGv6īņõ-¯­‡f€.ß!âĪņøã{Ûí¤ÎNØĖkŊŧãjŌ­­ģņ“^—ÖGĪë&“ņû$ZÎ×9í–Ķd–ãƍķŽK^wzꊧŧõzŊæõdî&īķ­ˇŪröqxŅ^ šģ“všvŋS§œrJĨŅÔ5IÖzšĖ@tÎHėnyũÔnÕnwp;Öj­]žõĮ–l˙ččņ‰&ũ^Ü|ķÍąũöÛĪ‹Ŗ[7g˛VęDĮb € €@á˛J í„xņˆëŌÖŪŪߗp››Ę(ļ@ë+¸fŸyčŽäŲ<í­đA§Ē$ĐAĪ™ËrúĘ7YĐä4Čë„ė„-U ´ũLŠyˇŽ/ąr“meք,ŅsŠēΉ'žčÔW4mõt'}mU&×a_¯&wîŗÔš˜šåŨW`Å'Ü˙üį¸į´?íV×DI§Ŋ¯;ŸsÚ¯‡ŌëIôC„ž˙ũ÷ß÷ŽW_IĻS|Ųd×ßJm'āzûG3zĩŽōMÚjīZkW¯Žüã•~ûôéãė§û”••9Į0īĖöĘēĮČäS 0ƒÉųęá.č1ɎĄŨŌ™@@ŠK ĢZÅrģĪ8fŸ˜–jZúđíŪūÉčdĪæn×.ÜĮūÜ)ŸĒkļ¤§Ú/U=u››ŦíÂŊ}ũ:ßā]ĶüY•’gģNŲváÖ˛}õ–ųsĖ‚P,Ũ^“ŨxŌI'y eĻįŽOēĖ̟’ĩß­Ģ]z3™´YŸ3fÄn¸á/ŅąëĒɖ=H–^S˛Énõĩ[NíäŪ}ŪÕNpĩT[ĶdKëc˙Ņäî–[nņę–ęGģ^ų8§= \ĒŽĮv7oˇžīŧķŽw ļ]gwŪîu`w×íöācÚm<~ēūúëŊķ${d@Ÿiˇ[¸Ũ:â{$K|ã×ë÷#Ņ2Z7}÷ŗĻƒÚšŅiyšļŽëw† @@ x˛J uĐ.ģ{˛;hWĸËĩ`zĶėi;v3ĮI×mā*1ļčõ“ūŗãYÎU%ŪētĄ—ėkŽŨļˇ.[œe *īîÖ)ÛzũgzĪž;õ1ÉüĻ9_W>Až×Ø ´&Mnĸ’éiíŨTŨ—ĩ5×NNôŲæøIģtđÁ1M´ĩ.nˇčødH—íVO{Ôi;ąŽ?ž.k2ä×Ū×~ļ×)Ún)MT‡dë܄4Ņųíuų8§Ũ˛œjähˇ×€Z¸#§Û?p$KlŨúģ ´ļėÆ?Ëî[ˇŲĪ™kYũ~ŲŨđÍĢĘÜCú>íī•kí*Ž?˜dûĮwđ4 æ=āŪwDcŦ#Œ3!€ €@v ´šŽ˛ŪņZ•5ąK˜˜™„æģ+Īôö›wé)ūVPŗ]_-å´f›äZ“`{ŌŽĐö \ë?Ÿ`oöÍۉvĸ‘Áĩex͛/Äæ˙éŦØĘ—õ•ĩÜd5Û芭[|-ĪŗĪūuL<°'Mhõ¸ oŊĖ8T؛RÎģuĘ*6­ÍߜüKĪŪ1ցĖėQĐSž5wíÚN(3=ƒŨuųÜsĪõuËļáŽ’­ ‰&mÚęgOæŊĶ^Ģd˛ÄÔ^oˇlÚ]†_|ņEû°•æídÛmeĩģ†kŨÜg̃ļv&zN:ž"ų:g˛QŊíķÛŊėįŊŨÄ×6°ËšķvŨĩ{ĩũ,ģ}ėømZ^muŊÆ2Õ÷ÍŧÜû>hËxuOö'Š~ˆ¨îzq>@@ôĩtsÙųdv˙î˛Se㗟ūPĻV‰´t4éķ‘’Ųēā[YxĶ%˛méBg{­ŌRi˙ČÛRwˇ=}įXūČpYņôHoŸ–į˙YvúÅ/eĶ×_Ȓ‘7Jlëo˙Ļ}—FŋLVŋö´čą+6m”:ģė* ~~€3¯jÕ­+§L”íkWI]w—Ŋ}7áųÜ­“ÖˇÃ˜ Rģų.‰vķ­‹•o“Ų§÷6.Ë}ëŗ=ˇ¯pĀĶ5ZöŪ{o§´éō+ĻõUęÔŠ“đh&9rÖ7hĐĀų4ĪŽJįΝÅ$ĨβyO˛yä‘ ËÚį1I›ŧ÷Ū{ŪyĖhÖŌŽ];_9ķ˛˜A¤¤M›6ŌĸE įķōË/3Rŗ˜OĻL™"­[ˇvʘįbÅŧə7]Čeß}÷õË^Đ:}ôŅÎ*ķ|Ž 4HĖŗÔŌŊ{w1-Šb’j1ɛ”šx>øāƒrÁ8ûš÷ËÁ,f *ûpÎ|]ķũq'Ķ2*t¸FîúøĪ|œŗ~ũúbž-—Į{Ė12¯–rėâĪm{:TLK¸ŗË%—\"jĸĶ… eˇŨvsæã˙2ƒjÉîģīîŦ6Ī‘‹xĖüˇRâ,ÛÛĖ*bēęK­ZĩŧCč÷Ģk׎βųņE.ŊôRo›=sÕUW9õ2­ØNŦãŋöž™Î›Ök1?0ˆé~/͛7—zõŽ?†mdhgßø}XF@§@ö ´šMŌžģü4Ų4íķ”WĨ‰ßžÃŸ‘ûėWiŋøÄˇŌ V´ŊūiÜëוļ,ųY5öąJëãW´<įrŲųÔ ãW;Ën˛Ē ™&ĐĻu[fÜĶKöKî$7$<žŽĖ6‰uë”M-ÛeÖŠŊ*%Đ ÷=Höŧã)“ŅīH8’V4G4 >ā€œäQiZvĨwīŪ•ŽŽ _ßž}Äōíˇß–F‰&_šXę”.ų6Ũ`Ĩ˙ūΞĻû¯Œ1™×ŋėX“.MŦš4iâm×ķî^éŅŖ‡˜×U‰&āĻĢ˛ÔŽ]ÛŲG!MŦuJ•@ëīPæuNbZ}ĩNfP2ą“{ĶY4yĶÉŊ>Mâ4u“FgcÜ_šœé¤Éĸ&ßéĻ|œĶ6ŌŽ?Ū3˛ëcZtÅ ÖæŦ2ˆ‰&Á:š ´ū@ĄõkÖŦ™ŗ>ū¯ŋ˙ũī2xđ`gu|riēā‹üËŲĻÉķ€|Åíīî{Č!‡øļë‚Zę1&L˜ ĻĩÚųQĮũAB×é9ŨO*N˛Â´’‹yؘ‘ļ|ô‡’—_~9im_Gü5&9Ģ@@Â"ž‘:ÉĻ+ōĒ—¯ÔUØí2Ŧī}ŪļzE’Â?ŦÖAÉ´Ëŗ[Æũœ?l@lķܙĻwoo›Ķ]|–õĩ}dS—e÷=÷ëKĄ]Ąˇ­\f—¨4īv—ÖrŠžšö4įũū†‹ŧ:ēįLöštÔmžâéÜ:eՅÛtĶĖŠÎsØn=föëî (–î|šŪŽŨqMë­×]6Ų`KüãŊ}ôõBÆ ķ–Íŋ“Xü@Rņõ´GÉ~â‰'|›ŨŽÃz}×pĸIßįŦÛõũŽeģ˰nKõėŽI˜ŧcØĪkÛŨuM ĩwzģ wĒëŗs ķ’d&įœk$nÅWÁîvoZōŊÍÚß5NvŊņīێ–Ũ$×Ū1= î~=íVÄîf?Z{ĐQ¸ĩ[ē>6āL–Ē›züŋ‰Tß)ˇÎ|"€ €„G ëg ãĢŽŖ=oøōSķlôģæ}ÃcëūûvÖhm]˛ ļuŅüØÖ%ßĮĘËÖÆŸ"ķe“°m[ąÄIõXÛ7ųŸENu 7YÍ*NuĀlsë”m­§Ž0"•—­‰•¯1¯×1.…šėgˆ5ŌgSãGÖ×ųtëÖÍKŽÜDK?M7č¤Ī>ë5ŏ’?Rˇ›TéąŊęÍ7ßô×NîėQ§ĩŧ&FĻ+v%J}g°]gĶĒčísŨu×yÛėŧí2šp/Y˛Ä+ãÎØû蹗.]ęnJų™sÚŖz'{\“Cw0ˇø$Ō~´^¯Úړi…÷ ŽĨåãŸew“pŨæNæCGÁÖh4‰žv÷›8qĸøŅÚÕ[Gc7=˛úķ—ŋü%Ļī§vΝuĐ>M÷ŨwŸwūTõLT–u € €…¨r]øKČM ÜdÕI ?ų 7­âQÜ:I Ģxęœˇ“XM.´…đŽģîŠi ŖŽˆŦI|¸É¨ž*IäT“ŨĸŸ´i9ģĨWĪĢ-Ԛųå—1ĶÚKfÜsj‹ ļ<ëdērûļëņu?­ģ&|úã@üĩ™îÍ^uThĶ]Ø)ŖÉ’ũniMø´ĩŨ=¯ÖmôčŅ1ĨY ģâŠ+ŧmēž)“)_į´GŅÖkO4Ųƒššƒ¨šûé_ޟ{͚čë{ĸÍŗāžkÕí‰^“åŽÎ­Ûu^ËēÉŧ&Ûîņ5ŅÖd>ҤÆîųã[¸íŸÍ:ķøwl=‡ūX¤#ŋëwüĩ×^ķž îųí ؜‡}@@ '@ũŖŊ›Ŧj­]Ķĩ%|Û*ķœ$7âų YÅæMąm˗ÄĘ×­Ži—o­Oą'КĖÜyįžäÂM"R}jŌ™¨eÖļ×dĶ=†ŧēûhëļģ=ĶĪkŽšÆ)nž}vĘjbfwNv}…’žˇŲė¤1ҍĐö딒S×k+ŧžb)“)į´[–ĩõXåD“ū(á^‡&Üņ“Ũĸîî—čSŊõ„øÉmļ˸‰zĒ–e÷8zgu–Ķønwŋ Ÿzüø>ėēÚķ/ŊôRĐĶP@@ €$Đ?âÛ ´&­îŸŒŸ‡ÎEÍ ¸÷z/Ģ?$ЙußÍE5ōu mš4myI–P¸ķÚ*Ŧ‰ŗģŦÉTĸnĶnĮįí›(iĶũôŲZ÷ųT÷¸úŠ­žúÜĢ&éö9Ũg5‘ÖũĖ@XN‹Ļž+QKšÖQ[ēãßs=×znØîÖíÖ]?5‰ŗ[Víúi ĩļjf3åãœÚ"īļ–ëɒyģÖî o×ÖŦY•ZūĩûžÆáŅGõb™čŲ`íöm(sö9ė°ÃœOˇļũ=Hô|´ÖÁîæčĮģžU™7Ŧ%üžilõĮ9sæTåđ”E@( @ Q¸Í`ä&ķŽhY|į••ŽkÛžöëUi}^V˜‘œŋ6@ÖōžīđúꮎO˙WJ›üġžX–/_.ú*ķ,´čkštÄëwŪYLëĻčë’ÜQšM"ä\ĸIhåä“OŽōåę+ąô•HúĘ,=¯I|Ŋ× ™–cg„f}U”ŽO5™V^ŅÆuŌÃ[ĩj•j÷ŒļéHÎ+VŦpF×ëoÚ´Šķ'ŖÂwĒŽsĒģų?Îyĩ˜žâI'ķcƒã­ķõęÕsļëHęĻKļ˜6œŅą÷ØcŨ\iŌ×}éhäz -ÖI¯[cŠ“Žô­ßqũ~3!€ €¯ ´쨖Íf(fmØÚ1•ÔûáŊÄ;ÖäŽbËīDvĪTR×ÜtWãë§ÜķōS“ģÃ?\L—ÛJ¯+*dŊ8wvúú+ĶēīŧŪI_Gfz!$|ũ•iA–ßüÆŧKŪLöû˛ŗ;{#€ € _čüúrtj´€ļ<ÛīĮÖwa2Äé 0ú^fĶ]Ûygļ •ėÎîv>@@(” tĄä9/5DĀŧ—ZúõëįģÚ#Ž8ÂéĸnŪÁí[ož–#Fx]ë}Y@@@ Ā$ЧG &<üđÃrŪyįĨŧT3˜š˜w0‡úšæ”ĀF@@Č @G>Ä\ á0¯“÷Ū{OĖûŗeÁ‚NĨZˇn-=zô3ĒļėēëŽá¨(ĩ@@@ ‰ tV#€ € € ` @ÛĖ#€ € € D€: Ģ@@@°H m æ@@@H"@†Õ € € €Ø$Đļķ € € €$ NÃj@@@lh[ƒy@@@’@'a5 € € €ļ ´­Á< € € €IH “°@@@[€ÚÖ`@@@$$ĐI`X € € €€-@mk0 € € €@č$0ŦF@@@Ā ļ5˜G@@@ ‰ tV#€ € € ` @ÛĖ#€ € € D€: Ģ@@@°H m æ@@@H"@†Õ € € €Ø$Đļķ € € €$ NÃj@@@lh[ƒy@@@’@'a5 € € €ļ ´­Á< € € €IH “°@@@[€ÚÖ`@@@$$ĐI`X]5oŋũļj 4 €@ öíÛ*G!@H/@Ūˆ=@@Ŗ č r@’@'auÕÜššėqËÎĢēö&.~i<üaY".a‰õ@ˆ˛ t”Ŗ[ĀkãF.>nÁÜō]Џø…ņđ{„e‰¸„%Ô@ Ę$ĐQŽn¯š`ø¸sËw)ââÆÃī–%â–HP@( @G9ēŧ6nä‚áãĖ-ßĨˆ‹_ŋGX–ˆKX"A=@ĸ,@åčđÚ¸‘ †[0ˇ|—".~a<üaY".a‰õ@ˆ˛ t”Ŗ[ĀkãF.>nÁÜō]Џø…ņđ{„e‰¸„%Ô@ Ę$ĐQŽn¯š`ø¸sËw)ââÆÃī–%â–HP@( @G9ēŧ6nä‚áãĖ-ßĨˆ‹_ŋGX–ˆKX"A=@ĸ,@åčđÚ¸‘ †[0ˇ|—".~a<üaY".a‰õ@ˆ˛ t”Ŗ[ĀkãF.>nÁÜō]Џø…ņđ{„e‰¸„%Ô@ Ę$ĐQŽn¯š`ø¸sËw)ââÆÃī–%â–HP@( @G9ēŧ6nä‚áãĖ-ßĨˆ‹_ŋGX–ˆKX"A=@ĸ,@åčđÚ¸‘ †[0ˇ|—".~a<üaY".a‰õ@ˆ˛ t”Ŗ[ĀkãF.>nÁÜō]Џø…ņđ{„e‰¸„%Ô@ Ę$ĐQŽn¯š`ø¸sËw)ââÆÃī–%â–HP@( @G9ēŧ6nä‚áãĖ-ßĨˆ‹_ŋGX–ˆKX"A=@ĸ,@åčđÚ¸‘ †[07-õÄOČŧyķ¤V­ZŌēuk0`€3üˆ;J—:‡‡ß#—K ,#FHãÆeëÖ­rđÁËQG•Ņ)ˆKFLė„ €@•H ĢÄGádÜČ%“IŊˇÔ>ŠļjÂ\nģíļ„ėßŋŋÜpà žįîh­Ŧ nÖåætÖN O:é$9˙üķåŧķÎķŧrOöėŗĪŠnĪf*ö¸|ūųįrã7Šū¨?]yå•2tčPiŪŧyüϤËÅî‘ôÂĒiÖ-[äŽ;îkŽšÆwÆ#Ž8BN9å9ûėŗŊõ$Đ3 €„B€:aˆ^%ĸ~ƒ­#åîŋ˙ū˛lŲ˛´Á›6mštéŌ%í~ēCÔŨ2B¸“@§;„ūđ1dȐtģyۋ9.Ī?˙|Ú Zļl)“&MrZčŊ‹N1SĖ).ĢZ6•——Kŋ~ũdܸq:#&vB@ ÚH Ģēf(ę7Ø:*îȑ#Ŋ xāréĨ—:]…¯žúj_bÍ °Į”×™d ´ļ6?÷Üs•Î=sæLß`M•v°Vë÷9~DgŊ$M–ûôéSɤwīŪ2~üøŒē¸̇Ō‚Í>ōČ#rîšįúÎß­[7ųŲĪ~&úc‡žžĒŦŦĖÛÎ˙3 €„CĀ<{ń@ÎæĖ™Ķ?Qœ6mÚkßž}Ėü vū˜„$fž+õ.õ̝žōļé>æØÛ–n&ĘnéŽŊĒÛ/žøbŸ{Īž=c+VŦpģvíÚØ1ĮãÛnēwĮ***2:m1ÆE¯Í$jžkžâŠ+bĻÔšæ… ÆLĪoģIÜbķįĪŦGF–įĖX >sũ˙ÁŧzÍûšÄ|˙ˇđ˙GžÂá@ @ ´šCaĘŊ@Ô[¨žūúkŅįëׯ/uë֕ŊöÚËAÔÁĒūųĪĘŠ§žęĄŌ‚äQäu&ž:žë|üāLŲŧęĒŋĪú]ėŅŖ‡čwU'ķŖLž<ŲiátĄĪDkwâũöÛOô9éO?ũT8āwsŌĪbôHz1Õ¸!žG@ĸ÷•ŋūúëb~ėņjÅ˙3 €„B€:aˆ^%j ļi‰–/žøÂIJ&L˜ īŧķŽ¯ëļUn€]‰ü~Ú ´&‹Ļ'€3Ō´{Ö͛7Ë!‡â$‰ēNGäž:uĒ4kÖĖŨ%ég1~Ÿã_‡tíĩ×:ƒÚ%ŊČ,6ŖG——ˇ]õ;ŲĩkWīøŖF’xË:cz;H‡ŧuü˙áQ0ƒ €@(H C†čU"ę7Øīŋ˙žz襎⌘Ēŧ“@ëHÆcƌq^cåØôБŖŽ:JŪ|ķMg•> Ŧ-‚5%N”Ŧš6Ų~Fũßwļ™î? [ĸ˙ôUcöˆč‰öIv>â’L†õ €äN€:w–Ɉōœyv´ŌhÅÚV_=ŖŖm¯\šŌ7ę17ĀÖ#ŗv¨zÛļmŌĢW¯Ķß]8Q ´ž‡Ø<÷ėEĨM›6ž.ŪŪ†¸™(˙ûŽģԜ.Æ÷ HôÃėŲŗĨcĮŽŪyíãmŒ›!.q ,"€ č< rČhŋŽ)žĻŧJîēë.)--uBĪ paūØ ´Ö Ļ?­-™ú<ŗTMŸ>ŨyfßĐŖ>*įœsŽģ(ú(ÂÁė-'›ą™lÖWˆ˙Q#ŅČį<]Ų5 €„J ĀĀcA ­@1ŽZœöĸ~ÜaėØąŪČÅæsėoû›¯čõ×_īÛnēÎúļ§Zˆ˛[ĒëÎÅļTŖp¯[ˇŽŌ(܃ ōF?NwūbŒËöíÛcĮwœīģxË-ˇÄtŊNzMņŖÉ›÷š§ŖđĘjyĻėtķĘ;_LĖ;ɓÆD˙1-ĐŸ¤ŋ§_;"€ Z CõsFt*åĒøV$Úu×]'ú.×W_}Uüq_ õyܧžzJJJJ|ë-DŲ-ŅõærŨmŋKˇ˙ūÎûuãĪUŪ­ŖnwīŪŨwéúėwßž}gÄí æWrûíˇÛĢ’Îķ=MJ“vƒ>›úé§ûö͘üęWŋJø=Ĩ ˇŠ@ .@]đDŗQžÁÖįFûôéã=K›.‚Q]Rē믎픑#Gftēl’=`1ŸõĮ›ß˙ū÷)]Ŋâ*UböHu]ÕąÍŧŸ[N8áŅWˆĨš4Š6=$›ī*qI%Ę6@r#@GŽ'õš%K–ˆļxę¨ē:š7ģúiēdĘwÜáŧ"Iˇir2eĘiÔ¨‘.Ϝĸî–ōâ̏qčĐĄŽģFGÛÖ$åŧķÎķU[ĻG-Ú*ÍTėqŅw<ßx㍠“ļûīŋßyZßgžéTė™^gžö+//—ģīž[´Õߞô˙GyDîģī>yã7œMÚŖåĖ3Ī´wK:O\’Ō°@œ @įŒ’Ų5åFNjŌé† ĘÖ­[ĨUĢV6CÖķ5Å-k˜€Ėķž˛téR'F 4pâSģvíŦ•¸Ŧ]ģVôš›ËĖ ˆIDATOũúõEßcŽŖn×dŦŋ9.°eËįûŠ˙wč÷sˇŨvķŊz-ÛĶEå{šíuŗ? €Õ)@]Ú5č\ÜČ 6nÁÜō]Џø…ņđ{„e‰¸„%Ô@ Ę$ĐQŽn¯š`ø¸sËw)ââÆÃī–%â–HP@( @G9ēŧ6nä‚áãĖ-ßĨˆ‹_ŋGX–ˆKX"A=@ĸ,@åčđÚ¸‘ †[0ˇ|—".~a<üaY".a‰õ@ˆ˛ t”Ŗ[ĀkãF.>nÁÜō]Џø…ņđ{„e‰¸„%Ô@ Ę$ĐQŽn¯š`ø¸sËw)ââÆÃī–%â–HP@( @G9ēŧ6nä‚áãĖ-ßĨˆ‹_ŋGX–ˆKX"A=@ĸ,@åčđÚ¸‘ †[0ˇ|—".~a<üaY".a‰õ@ˆ˛ t”Ŗ[ĀkãF.>nÁÜō]Џø…ņđ{„e‰¸„%Ô@ Ę$ĐQŽn¯š`ø¸sËw)ââÆÃī–%â–HP@( @G9ēŧ6nä‚áãĖ-ßĨˆ‹_ŋGX–ˆKX"A=@ĸ,@åčđÚ¸‘ †[0ˇ|—".~a<üaY".a‰õ@ˆ˛ t”Ŗ[ĀkãF.>nÁÜō]Џø…ņđ{„e‰¸„%Ô@ Ę$ĐQŽn¯š`ø¸sËw)ââÆÃī–%â–HP@( @G9ēŧ6nä‚áãĖ-ßĨˆ‹_ŋGX–ˆKX"A=@ĸ,@åčđÚÜšVS#€5R }ûö5ōēšh@ĒC€ē:”kā9H k`Đšd… t(Â@%@"*@ŅĀú˛H  Ώ5U€ēĻFžëF¨čęPŽį ŽAį’@ $ĐĄ•@ˆ¨ tD[čËrhn䲋nŲyU×ŪÄÅ/‡ß#,KÄ%,‘  €Q Žrt xmÜČÃĮ-˜[žKŋ0~°,—°D‚z €DY€:ĘŅ-āĩq# ˇ`nų.E\üÂxø=²D\Â ę eč(Gˇ€×ƍ\0|܂šåģqņ ãá÷Ëq K$¨ €@”H ŖŨ^7rÁđq æ–īRÄÅ/Œ‡ß#,KÄ%,‘  €Q Žrt xmÜČÃĮ-˜[žKŋ0~°,—°D‚z €DY€:ĘŅ-āĩq# ˇ`nų.E\üÂxø=²D\Â ę eč(Gˇ€×ƍ\0|܂šåģqņ ãá÷Ëq K$¨ €@”H ŖŨ^7rÁđq æ–īRÄÅ/Œ‡ß#,KÄ%,‘  €Q Žrt xmÜČÃĮ-˜[žKŋ0~°,—°D‚z €DY€:ĘŅ-āĩq# ˇ`nų.E\üÂxø=²D\Â ę eč(Gˇ€×ƍ\0|܂šåģqņ ãá÷Ëq K$¨ €@”H ŖŨ^7rÁđq æ–īRÄÅ/Œ‡ß#,KÄ%,‘  €Q Žrt xmÜČÃĮ-˜[žKŋ0~°,—°D‚z €DY€:ĘŅ-āĩq# ˇ`nų.E\üÂxø=²D\Â ę eč(Gˇ€×ƍ\0|܂šåģqņ ãá÷p—ļoß.›7ovëÕĢ'ĩk×v7UË'qŠfN‚ PÃH kø _—Ī\0Y܂šåģqņ ãá÷p—Žēę*šíļۜÅ{īŊWčnĒ–OâR-˜@ † @×đ/@ž.Ÿš`˛¸sËw)ââÆÃīĄKĢW¯–Ν;˲e˜_}õ•ėŗĪ>•wĖãâ’G\ €Ā$Đ|ō"Ā\0V܂šåģqņ ãá÷ĐĨW_}UŽ;î8gCīŪŊeüøņtáŽĖÄ@Š^€ēčCÎ ā;X\p æ–īRÄÅ/Œ‡ß#‹ÉņĮ/¯ŧōŠŗá‰'ž3Î8ÃŋS5,—j@æ €5^€ēÆōĀ\0W܂šåģqņ ‹ĮƍeņâÅĸ nŖF¤uëÖž Ņm6lpZŠ[´háėãÛ!ÅyķæIģvíœŊ[ļl)SĻLŠtŽd‡ŌĮÖŽ]+eeeR^^îÔĄiĶĻRŋ~ũdE’Ž/–¸$Ŋ6 € P$ĐE¤bŦ"7rÁĸ†[0ˇ|—".~á°ylÛļM=ôP™0a‚SҧŸ~ÚIŠû÷īīĢxÛļmåŖ>rÖi ņûīŋīÛ>tčPšņÆEGĐÎfzđÁå‚ .pŠœ{îš2jÔ(ŠUĢVĘClŲ˛Ezč!šä’KîwņÅ˰aÃDōL§°Å%Ķzŗ €Å$@]LŅ*ĸēr#,X¸sËw)â⛇ļ"÷čŅCžūúkE,iâ:bĈŒKjōŪĢW/ųôĶO2īžûŽvØa)ËkKķ9įœ#O>ųdĘũ4yž:uĒ´jÕ*å~îÆ°ÅÅ­Ÿ €DI€:JŅ Ņĩp#,¸sËw)â⛇ž{ų ƒr’MMŗ_jܸąL›6Mvß}÷Œ ņŞß~û9ûļoß^&Ož,zŒTĶŦYŗ¤S§NŪ.š(_ũõŌŦY3ŅÖlģeüæ›o–?˙ųĪŪžŠf—Tue €Å*@]Ŧ‘ yŊš‘  ܂šåģqņ ‡Í#Q­‰įå—_.%%%ĸ­Ę<đ€ī"t”líö­ĪA÷íÛ××zũņĮ; š¯@’ûŨĪ×^{­Üpà IöÜąÚą[×ÚīŒÖkŅ×_跰rŠ<õÔSÎuė8Bâ9ˇŒ&ōL € čü¸ÖøŖr#ė+€[0ˇ|—".~á°yÄ'ĐÚēĢīaŽS§ŽSqí^­]ŧŨIģO?üđÃŪsĘcƌ‘ĶO?ŨŨėtĮ>ā€ŧåd3ëÖ­“îŨģ{ÉŽÖ­[ˇdģ{ë_ũu9æ˜cŧeŅį¯u]—.]œdšnŨēĸŨÃ4hņ€ba‹‹īY@@ "$Đ dØ.ƒš`Á-˜[žKŋpØ<âč“N:It 1m}ÖiöėŲŌącGī"t¯xË/ŧđ‚ØŽiIũÆoČŅGíGŸƒÖŽ×ĩk×öŽ›lfɒ%˛ëŽģ&Ûė$᧝všS§lZ“פČ@ŠX€爃æĒs#,:¸sËw)ââ›G|}ėąĮĘË/ŋė%ĐņĪÛ]Ļõʂ$ĐújŦ?üáŪ@`?ū¸œyæ™~¨K_~ųĨôë×ĪkŊNļë‹/ž('œpB˛Ížõa‹‹¯r, € čˆ2l—Á\°ˆāĖ-ßĨˆ‹_8l…H ,Xā 4σ†Íœ93e̞_pĮ’>ƒ=qâDyë­ˇäūûīßąáĮ9`lƌÎc•6Æ­[\âĒĮ" €‘ ŽDÃwÜČ‹ nÁÜō]Џø…ÃæQˆúąĮ“ŗĪ>ہÉf /-đÚk¯Éʕ+g°[ˇn-‡~¸sœ­[ˇĘ¤I“ä÷ŋ˙Ŋ×2MíĐ𠀥 M(ĸU‘°Ũ`‹.náŒqņĮ%l՝@ĮŋûYŸ…>ōČ#ũH)–ô•UöhŨ}ô‘ôėŲĶ)ß5œ:$›@(€ tĐkÂ)Ãvƒ],清3RÄÅ—°yTw­Ī/ģŖmˇmÛÖyVēw?ۂöācîúëŽģN:wî,Ī=÷œŒ;Ö]-:ˆ˜Ž(ŽŖq§›Â—tõe; €Å(@]ŒQ+‚:s#,H¸sËw)ââ›GĸZ“ĐŌŌR§âú ņŪ{īí]Dü bĪ?˙ŧčČŨî”nnģ9Ķw?ģĮÖOmeÖWii7đt͇~(Ŋ{÷Nˇ›ŗ=lqɨŌė„ €@‘ @YŠĨēÜČ‹nÁÜō]Џø…ÃæŸ@ë;•oŊõVī=Ī:ā×ūûī/˖-s.DGŨ>ņÄŊ‹úāƒ¤OŸ>ŪōgŸ}æėī­°f6lØā´>ģ“'O–}÷Ũ×Ú#ŗYMĸŸyæÜü¸• ]\Âđpy„’B—P” € 3 tĖęļ16:r~đáæĮ­ė\čↇË#”ē„ĸõ€ ˜ ` cVˇąŅ‘ķƒ7?neįB—0<\Ą¤Đ%%¨ ÄLŗēmŒŽœ|¸ųq+;珄ááō%….Ą(A= @ fč˜ÕmcltäüāÃ͏[ŲšĐÅ% —G()t E ę@1Ā@ĮŦncŖ#įn~ÜĘ΅..ax¸Üü¸• ]\Âđpy„’B—P” € 3 tĖęļ16:r~đáæĮ­ė\čↇË#”ē„ĸõ€ ˜ ` cVˇąŅ‘ķƒ7?neįB—0<\Ą¤Đ%%¨ ÄLŗēmŒŽœ|¸ųq+;珄ááō%….Ą(A= @ fč˜Õmclڑkcøi@]I`čĐĄ]7AC€V ôę Ü…ŋîBŅ ‚:¨ DJаí Ũnø}@ [ ` ģUy↠ÕAŊ:(wáo` ģPtB†‚ €B*@‘Ā@G*lģÃRMGŽgJĀ­gŧV×Ũč⒆‡Ë#”ē„ĸõ€ ˜ ` cVˇąŅ‘ķƒ7?neįB—0<\Ą¤Đ%%¨ ÄLŗēmŒŽœ|¸ųq+;珄ááō%….Ą(A= @ fč˜ÕmcltäüāÃ͏[ŲšĐÅ% —G()t E ę@1Ā@ĮŦncŖ#įn~ÜĘ΅..ax¸Üü¸• ]\Âđpy„’B—P” € 3 tĖęļ16:r~đáæĮ­ė\čↇË#”ē„ĸõ€ ˜ ` cVˇąŅ‘ķƒ7?neįB—0<\Ą¤Đ%%¨ ÄLŗēmŒŽœ|¸ųq+;珄ááō%….Ą(A= @ fč˜ÕmcltäüāÃ͏[ŲšĐÅ% —G()t E ę@1Ā@ĮŦncŖ#įn~ÜĘ΅..ax¸Üü¸• ]\Âđpy„’B—P” € 3 tĖęļ16ßŽÜ /ŧ`.ēč"ķ‘|ÄŧņÆæĐC5›nēéj‰äŨwß5=ôš˙ūûÍŌĨKÍZk­eūņ˙Ņlŋũöfà 7Ŧ[‡?˙ųĪfΜ9抧ž2īŋ˙žōČ#Ķëí6ĐÍt‘˜ÆŽ›Öˇߑ#GVÄ|ÔÛäáG6oļ ÔËĢį{Ģ‹–Ķ“}+|zRžÜ+L†žōØgŸ}’vŅĶrz˃vZKŧSÛim$œ ÄMˇžm‹Îˇƒ]>Œ8žwŪy• &T/^Üë1‡~xj(Äh‰ą<ķĖ3+{íĩ—s^ŽÍŸ??ŠŒ,gMŲV[mUŲwß}k΋ymdđė}šiS§Nu~_Lū=÷ÜSš÷Ū{“[:É@Μ9͉ExW§ WĻM›–hdķŋéĻ›ŗĪ+Cōu“^ž|yōwôÛßūļf–Åž{îŲM;ušiĨ“ÛŠ )@€@ü0Đņkܖ{k[­ô‹/žXų͟ūTyúé§+¯ŋūznļK.šÄ1bļyúë_˙ę˜1™ÆmO žā‚ œŧW_}uōŲŅįqãÆĨŖĶÕ÷Ą<=éí 7y0zôhᎺŖß­čęģ˜ Saģ°:B˙Î;īä˛Íž\ļlYå•W^IōJ>9Ž—ˇ‘.KöÆÜšs͟{ä‘Gœ8ejē乡—^zÉŅÖ6Üv°ķÔ;î.v™ō€<Ēž‡ž0zųå—ŊøØefĨM‹nō÷ ¯+ØqgÛa i§áˇĶl›" @€Āß` ˙ƂŖ øŽģîēËéđĢŅąG‘÷ØcJuą¯Ę.ģėâÜ+æ@ŒÔ[oŊ•DRoš¨š™ž*S͞ŋæšk YŖvūųį'åÛĶ`ÅtWfrōüņNš:ríܔ“đå&#ævŲcЎlöh{žą_°`AqÕ˛ÆŒ“âœjWĒ‹AUöŪ{īēu:› ]žūy§,y`‘ŨDģ;î¸ŖrË-ˇT~˙ûß;ZĻĢK;Ņēg÷ÚŽ˛eÖKûęĸåÉ(đšįž[ˇ>ō`C6ČÖ Ÿėt|yī]Ėø¨QŖŌß?~|å‰'žHĶY’^Ũšvv;ÕöĘ€ ú0ĐõŲpĨ| ‡m”Ĩƒ¯FGĪ8°Ą!<2Z Fö}ęŦķP]Aģrúé§WdʸėeÔÔŪdē°OŪɃn/8–gBN8á„4ŸÔYx´˛ųp“={q0ģžz|öŲg'?ßČ@_{íĩi5_v/ąˆÉļˇ3f4Í'åŗęęæ-钭Ëī~÷ģŠü;ė°Ã*ōācŋũöĢ\uÕU•ęJívUŌc‰Wë. ž÷ŊīĨi9¯í*ÍĐäĀG-RĻīpĀÎīkŨėŊÔSLp+íVF˛íŲv9z,Md4ÚnĢzM÷ymWëŨhīÃvZK4¤vZ[;Î@€ GG…sŊ&āĶÁ–UŖŦ|5:Ųķz]Ū;ÖcŨ‹Éûŋ˙ûŋĘ­ˇŪZŲu×]+Ûmˇ]Í=2:9yōäÜ8eĒđ¤I“*_˙ú×Ķ|ō;C† ŠŧúęĢIyįVFå=m5ˇ71áb†´>y#Ŋöũöą/7™j.ąÚŋ+ŋ/įälą—Í6]vŊ˛#í’W8ŠYÕ8t/Ū|ķͤ<™žmÆË=’įúë¯¯Č´vÍŖ{™Ūۊ.õôÖrėŊŒ>Û[v÷˙÷“ß´ķhģ˛ķ5:öÕEĘĖNŖî'NŦČëŲÕÁå!N+|¤\Õ2Ë_ãÔYbļåĄŒ‚g_YXZęL; lĄĩS­{@€Ā@7æÃUOž†#kœÔčdĪ‹‰“÷ûŦ=÷¸“ tVsÚi­ÄŊi§ĩĨq€ <č<*œë5ߎ\=Ķ`Ÿc(SĨíÍž.ĨUŖ&Ŗ¯2Ę#Ŗ§ŌA—ŅO{Ë~ )kJŗŸŦRs$Ģ"Ë;§=Ũ|šéīØĻ*[Wš'īz6ƓO>Y‹K÷ŲŅ])[G›å=pģŲžU]˛zę”q­PvÚšÔįŌK/mšZOYŠē•­ˇēHûʎškėŊLÛÖ-ËĀf'÷äiŠyëí;Ų@ĶNëŠúˇķŊm§+‰#@€ęĀ@×#Ãų^đíČÕ3 öųŽ=5Y¯ÕÛËęßŲīúÖģ×>o›ĻF}ši™ļŠĘãTīēŧŖk×WŽ[ųôUW]åä“<úc{Ņ1-Û~—š‘.˛Â÷Ė™3˛ĩŒŧŊhŌhËū–Ũ>åĶkŊŅ%oĒŋ˜j­ˇŋ5.qÉhôÛoŋülļΡ´[áckŲ Z ĐNĩEæī{ĶNķKä, @Yč,Ō…đíČeMƒû|ž1´¯‹ŅĐ|ĖÂęJØj>ėŊN{͛Žmß§ĮãÆKkõ~Í'{ģ>ûrĶ2mS•Į)ûŪ°ŊJˇŦ:n×9īX ō‚ ôį’oqįŨWīœũÎq3]äGÎ9įœĻu˛ŪJ+–9hÔ>2ˇæ&{Ŗ‹ŒœĘ'¸ę1ąĪÛ¯4ãckŲǁ–FöƒVķeĄô†‡”E;Íũ ŨÎvš_#ÎB€ %€Î!]ßļŨŗĻ#‹ö7–åķPļņ“ ÛųÄĖ;׉cƌ•íļÛÎ11ļyĨ?ūxįē›]vŲ%™Ö­Ξ=;÷>Ŋ?o˙Øciö†{_nZ¨mLō8É ÛZ?áĄŖš_ĻXg§sëũcƌIĻOëŊē—<ö{ŧz˙ŽģîZÉž#-S­ÅPęÖLšOĻÁÛå˃ų#FTæÍ›§E5Ü7k 3W/öV‰YFëĩîĘH÷2%=ûŽŗÔŠ{6}Ô,™Ö.Üôw[͗-ˇˇÜü¸• ]\Âđpy„’B—P” € 3 tĖęļ16:r~đáæĮ­ė\čↇË#”ē„ĸõ€ ˜ ` cVˇąŅ‘ķƒ7?neįB—0<\Ą¤Đ%%¨ ÄLŗēmŒŽœ|¸ųq+;珄ááō%….Ą(A= @ fč˜ÕmcltäüāÃ͏[ŲšĐÅ% —G()t E ę@1Ā@ĮŦncŖ#įn~ÜĘ΅..ax¸Üü¸• ]\Âđpy„’B—P” € 3 tĖęļ16:r~đáæĮ­ė\čↇË#”ē„ĸõ€ ˜ ` cVˇąŅ‘ķƒ7?neįB—0<\Ą¤Đ%%¨ ÄLŗēmŒM;rmŦ? @ + :´+ã&h@€Āę €^”ģđ70Đ](:!CAĀ@!•€ H ` #ļŨaa Û­ŋt+ tˇ*OÜ€ °:` Wå.ü tŠNČ€@0ĐAČ@% @ RčH…mwXj 7ÜpÃvWĨŖ~˙å—_Nę ˇ°dCWx¸}ú¤å­ģîē=.¯›uɃisĶĻM3#GŽ4Æ Ë 9='ąÕޤĐ"ËS]0ĐŠ\@€ '€.) n4ĐO=õ”ŲrË-Í'>ņ 3ū|ŗÎ:ëô¸1h¸› ôŠ+Ė~ô#3qâÄ\^“'O6ûîģoîĩŧ“•JÅüüį?7'tRŪesöŲg›C9ĈŠnuëF]ą‰ĮÔŠSÍA”čÛnģÍôëׯ&üĸÛUŅåI…U t|œ€ F]J ˛ t›^´h‘Ųc=ĖŖ>ЁļB“ã÷ßß|ûÛß6ˇÜrKzįhžxâ ķ¤į&MšdöÛoŋ4ŨčāŧķÎ3'œpBzËŪ{īmĤÛŋŅ“ō¤ 5&Ũô`#˜syØ%#ΞI[šôŌKMßž}kĸ.ē]]žTXuÁ@×ČĮ @€@a0ЅĄ¤ ›@7h1Ë2íøá‡6§žzj>#Đ)ŠĻ3fĖ0_ũęW“ûvÛmˇÄŧ 80IĪž=Û|ųË_NŽ…é#øāäÜŊ÷ŪkļÚj+įz6aō[oŊÕė¸ãŽÎ-ōžëūû›9sĻ1b„sŊ^B úBĖcŲ˛efÛmˇ5 .Ŧ‘ģž.ē]]žĸē` •{@€@ņ0ĐÅ3ĨÄ*Ø ´ˆ|îšįš?ūņéh•ßwß}LáîÁ_ĀŅGŒŽ5Ę\rÉ%5ī%?˙üķføđáI‰­č“O>9yĮšŪˆĩ=ō'īBzčĄ-ÕVI§hŠ÷vÛm—ŒČo˛É&fΜ9ΨûŌĨK“īzúęA§ōąe:˙ąĮëč.ĶûeÖB=]tģ*ē< FuÁ@+ö€ â ` ‹gJ‰UŨ` ŗBOŸ>ŨėŗĪ>č,˜é &˜SN9Å\pÁfôčŅ5w>ôĐCf‡vHΡb ŋ˙ũī› /ŧ01B2z] jåʕf÷ŨwOtˆy˙ÉO~Rķ›y'Ԙtš–X~üã›3Ī<3 ëÄOLlĶeĄĩŸũėgIRŪũ•­lĖ#/>ģŨäMáļ¯ŅŽŠ.OcR]0ĐJ„= @ xčâ™Rb•@7hĖîžũ ŧ÷Ū{5FWJÆÄĐŨ~ûíÉC youƒ 6¨[¸LĢ?ė°ÃĖ”)S’|ŋúÕ¯ŌŲvĻfæÅžWÕ˜tĸ^ž|yō.š<ŒíÎ;īL͒÷Ãŋņo$į„Ûøņãkf$sūĶÉīŧķĖ 'œ°˜4i’Ųoŋũ’UÍĮ—œëÉhŧdčtIĐÖšhßvÕŽvЁļÄå€ P0 tÁ@)îh tŗŋ{1%Yi[V‰~įwŒŧOjoũúõKž =lØ0Ķŋ˙ä’ũ.¯mūė2åFe–÷¨ĨĖ+¯ŧŌ{ėąIūM7ŨÔĖ;ˇåQ×N6Œļų“ŠíW_}uˇ°úˇûˇôŊķ™3gš#F$|šũ§“yäÅÖĖ@ûļĢvĩS tžĘœƒ C] GJÉčF=uęTsĐA™><>:Ķ$j’O?ũ´ųÜį>Ws>ī„ŒP?ųä“fuÖI.ßpà æ€HŽŗ+nß{īŊÉtåŧrôÜŦYŗZí–<j—,Y’L‰—ocįŊīlŋžŅF™x eŦŦōöĘ#/9gŋ“œ÷´ÜãĶŽÚÕN1Đĸ @ čr¸v}ŠŨh įĖ™“˜•ž‘lC‰Í˜dãŗĶöûČöųŧã,ĶûîģĪėŧķÎÉ­:%ŲÎ'0dTQîŗ7É#Sģe4ģ'[§ę2yōätĄ´K/ŊԌ5Ē&ėsĪ=לxâ‰ÉyųdØčŅŖkîɞčTŲ84-mh˘1ÉláÔˇo_Ŋäė{ÚŽÚÕN1Ўl$ @…Ā@Š“Â”@7hŊ7û،IoX‘÷Í7ß4oŊõVR”ŒĀļō=éŧßE—Jˇķ(Ē])ÕĸĘS]0ĐJ–= @ xčâ™Rb•Ú¯h¸SĢō‹:ü\čâj—G()ÕŠ"Ô€b$€ŽQÕbÂ@û‰ ` ´ŋ˛rĄ‹K.PRĒ :E¨ ÄHŖĒĄöA;Āh?~eåB—,<\Ą¤T t(ŠP@ˆ‘:FUˆ í'‚v€1Đ~üĘʅ..Yx¸Üü¸• ]\Âđpy„’B—P” € 3 tĖęļ16:r~đáæĮ­ė\čↇË#”ē„ĸõ€ ˜ ` cVˇąŅ‘ķƒ7?neįB—0<\Ą¤Đ%%¨ ÄLŗēmŒŽœ|¸ųq+;珄ááō%….Ą(A= @ fč˜ÕmcltäüāÃ͏[ŲšĐÅ% —G()t E ę@1Ā@ĮŦncŖ#įn~ÜĘ΅..ax¸Üü¸• ]\Âđpy„’B—P” € 3 tĖęļ16:r~đáæĮ­ė\čↇË#”ē„ĸõ€ ˜ ` cVˇąŅ‘ķƒ7?neįB—0<\Ą¤Đ%%¨ ÄLŗēmŒŽœ|¸ųq+;珄ááō%….Ą(A= @ fč˜ÕmclŨܑ›1c†yæ™gĖŋ˙ûŋ›õ×_ŋG*t+7‰ûũ÷߯Ëjƒ 60Ŧ{=īÂK/Ŋdūđ‡?˜E‹™>}ú˜6ÚČląÅfŊõÖËģŊášnÕĨ”Xx<ūøã抧ž2ožųĻYk­ĩ˧>õ)ķŲĪ~Öôīßŋ^čĻČv%?RdyąčR> @Ā@ BŒUč֎ÜOēGōv#ˇįž{Îl˛É& 9íģīžæĘ+¯4}ûömxŸ\ŦT*füøņæ¸ãŽËŊ÷üķĪ7‡~xbĒsoČ9Ųēä`HOu:^xÁėąĮæá‡NcŌyčuķÍ7›Īūķz*ŲŨފ.O*Ųéē8ĀI@€%€T˜N¯V7vä–/_nvØaķāƒ&ō‰Qû¯˙ú¯IŲÜxāŗíļÛ6ä´įž{šiĶĻ™~ũú5ŧO.žsÎ9æ˜cŽIīÛgŸ}ˊ+Ė7ۘžģė˛ËĖwžķ4Ũė uiĤ“yŧúęĢfķÍ77ũë_“Å0īĩ×^IûĐsraÁ‚ɌåPtģ*ē<Šg'ëĸœŲC€‚'P} ΁ T§0Wä_7m§Ÿ~zĨúŸūĢč‡ßÜĒ#Ë ŗĒIŽTBTĒf7ŲËąũ¯˜UsTŠĸ¤<ŲW§čĻŲæÍ›—j3dȐJuÚnz­ŲA7ęԈI'ķ;vlÚÎ8ãŒĘ{īŊ—„ēråĘʤI“ŌkÕ/éĩĸÛUŅåŠVŦ‹ÆĀ€ :™îȁ t[GnöėŲiĮ[M4ēĩfuä‘G&ė&L˜ĐZ†wŨu×]ŠŋûŨījîŧîēëŌësæĖŠš^īDˇĩįzô|§ōxë­ˇ*ÕW,’6 lÄ4Û[õ=üJuzr]ĀŧöÚkÉåĸÛUŅåi Ē‹ÖŸ= @ ` ;AĨŦc7u䤓=tčФĶ]&\9å”S’c tķ†+fäȑ ¯Ûnģ­y†&wüņŠųyã7jîļGūzĸO§ļgi›je´5o{÷ŨwS ĒŖŽy÷ęšNåačę4~ ĮŲËß°<ŗ tŅíĒčō4€NÕEëĪ€ Đ 0Н RÖą[:röˆ•šQí‰AS‰ģ…›Æ+&W>\}õՕK.š¤2f˘ĘQGU9ëŦŗ*sįÎÕ[[Ú+{]\ĩjUMÛ,VĢš^īD§ę"Ķ“ŋøÅ/&†P8įM[—iî:kâĻ›nLJĀ9ߊ<䁍ōx衇œ˜4ņ‹_üĸÆ@Ũފ.OëŪŠēhũŲC€:ēTęĀ:vKGî7ŋųMÚŲŽ~Ž&QJ§€b ›7Üę'„RķĻ&.ģ5jTEF›mō0côčŅIyú0#/O3ķ’—§“ÛŗŽ¨ ×ģīžģ&ŧęjņiÖ)Ë57eNt2 %īኜˇg(|á _HĻxŨފ.Oę­[§ëĸq°‡ „L˛:\ˇnčČU?…“šŋęgoRĩÔ a S$uls'īāƒŽČÔÚę*Ú)[=/ÆŖŅfOΕŠęmĒŒRëRõîÕķÜžíQū#Ž8BCJöīŧķNĨúmė„ĩŧzĐęÖÉ<ęÅøúë¯WFŒ‘ļ;/ē]]žOŒēØņq @:"ŦCė91^ÕOß$m1}ļšSƒ†nŪ°§L™’08p`EVÉļˇę'yŌéŨbĸ›Mį T“qãÆŲE9ĮĒOˇh ūÔSOM8ÛīõĘųęwSÃ8ū|9ÕŌÛߡ,('l¤É?yĐ WŠnWE—g ›.vlC€B!€E‰Čę{GnōäÉIG[Ū+Í.VĨ­Ņ(h=šcį–[>YĩdɒĘŌĨKŗ—’tõŅŠŠiö@ÂŲkt¯ęcĻ(÷Į­“ŽËO<‘rԑU OÃ( šeW¤ļ¯9ėtŧ~ôŅG§l„…ũi+š¯čvUty‹ėcŅŎ‰c@€@h0ĐĄ)I}bîČũųĪN;Ü2Rõ§?ũ)ɓҨ¸øâ‹s3īd íŲfųë_˙ē"„E+&˛L:™ĮėŲŗĶ60dȐŠLooļŨފ.OëßÉēh ė!@ĄĀ@‡ŽP‡Ö/öŽœ.0dË#įė)­>;ŋĮÎ͎× –Qæŧ÷ Ī9įœÔė\ũõiv1ƒ˛ ēü“Õ“uŗß™–Ī3eˇ“O>9-īąĮË^Ž›ŽE—ącĮĻņ냄ŧO[Õņá…Nå!3ä}o‰]ڜ|ļĒ•Íˇ]ŅN[ĄË=€ Î"€î,Ŋ:ĻļÚÁ.°žßˆnNķg?ûYjčļÛnģdZąk™b|ÖYgĨ×ÄėŧũöÛiēzˇĄņãĮ§įíšäšŒ2‹1#sŪyįĨå}ú͟Ž;m<-Ė:ˆĨ=ۋ‰Š‰´šZ!7<ėTvüōŠ4Yŋ@VĪû'ëčæÛŽh§J= @ čx´ *’Ní`Ũ:E1Ë:"(†.īŸŧŗk›)}ęÔŠéŊŲ3gÎL¯å•'įš}+A,íYfIėąĮ)ŸŧQúlėyéNåqŨu×Ĩą×kz>ģvOģĸæĩÎA€:›ēŗõ ļöÚÁ.¨h>cÕÍå˗W.ēčĸ\c3f˘d4:[ŌŦYŗŌûķV–Õ”ķŒųŽģîZyôŅGŗÅ5MĮԞmÖō>ŋĪÖŠÜü¸•+]Njޙŋ˙ûŋOpŨtĶM‰‰öa ŸØC΃.!ĢCŨ @ čX” ,:r~‚Ā͏[Ųš:]—›ožŲ|ôŖ5˙ķ?˙cîēë.Sũ|“ŠžWnĒī—{Ąët^Aw@&t鑨" t< tĮKftäüt›ˇ˛su˛.˖-3[lą…Ņ„Uõ;ĐfīŊ÷öÆĻeņІ7ÂR2ĸK)X)€ āĀ@;8HE€ŽœI¸ųq+;W'ë˛bŊÄ,OŸ>ŨŒ1Âüā?0ŖFę˛NæŅĢĀΌ. Dõ @ č(d /:r~šĀ͏[ŲšĐÅ% —G()t E ę@1Ā@ĮŦncŖ#įn~ÜĘ΅..ax¸Üü¸• ]\Âđpy„’B—P” € 3 tĖęļ16:r~đáæĮ­ė\čↇË#”ē„ĸõ€ ˜ ` cVˇąŅ‘ķƒ7?neįB—0<\Ą¤Đ%%¨ ÄLŗēmŒŽœ|¸ųq+;珄ááō%….Ą(A= @ fč˜ÕmcltäüāÃ͏[ŲšĐÅ% —G()t E ę@1Ā@ĮŦncŖ#įn~ÜĘ΅..ax¸úč–ĸKŠŽ´ƒNo§O?ũ´ųÚמ–˜į,¤O|âfúô鿟ū韜K•JÅüüį?7't’s^gŸ}ļ9äCS­įí‹.O~KuÁ@7"Ī5@€@ī` {ĮÜutƒž;wŽŲqĮëøāôWžōsÍ5×$#Ē oüđĸv€ģÉ@Ÿ|ōÉFˇn;īŧŗš>cÆ ķÕ¯~ĩn;6mšŲ˙ũ“ë3gÎ4#FŒhščōôĮT ´a@(žēxĻ”X%쁖ŅĒŨvÛÍÜwß}æ7ŋųŲe—] Ņ];ĀŨh e*ė—žô%‡ŖhYˆMF ‡ â\Ī&Đ%KÄ1ōĒÁŖ>Z{Ņ:#Ŗ§<đ€YguŦŗų‡ÚN—-[fļŨvÛäŨį_ūō—æ?ūã?œe‰ŒHK[”Ų—^ziōîŊžf ry䑚™öˆ˛=rín%Š.O‹V]0ĐJ„= @ xčâ™Rb•@ėzéŌĨæ_ūå_’ޏt˛e_éXˊÜbōvØaŗå–[ö¸-h¸[ ô‹/žh6ŨtĶ„Ķ÷ž÷=ķ͟ū4],L˰13­NE—Ú&×ǁs8ūü¨ ´´)™É {™ų°Ūzë9ĀėéØļÖY õÖ4‘ëŨwß=y & ŪÉĸxļĸËĶßŌ˙` •{@€@ņ0ĐÅ3ĨÄ*Ø ´=õ¸žāßøÆ7Ė/~ņ‹äķ8õîɞ×pˇh‰ܸqæôĶOOPČb˙ųŸ˙iÖXc 3vėØtÔtōäÉÉČ`–W6.Y"¤_yå•äũXų<“~vM>ÛtėąĮšßūöˇÉMō‚čĐčNZzLítɒ%æĨ—^2˛øœLÛūĖg>cdDYĻYË{ų2*-įe6DŖUõ›™beWtyZŽėU ´M…c@€@ą0ĐÅō¤´ Än å37ûėŗOǎŧOų¯˙ú¯É(ôšįžëœĪžg™^Ė9Đp7h1t“j™ž-߆nļĄK3Bģūä“Oš­ļÚ*91räH#ī Ëƒ‹VļXÚŠĖ‘Ų":Ŋ]Ėŗ,Z'+įīšįž ™ö-÷Čyų[=zt."Û@_{íĩuD]ž]ÕmSဠP, tą<)íCąhé ÷ģßMVە•Ŗu!, _V–‡.\˜Đ˜5kVËĶšĩÜ-ZĻŌĘčŪÕW_ũaËÉß}į;ß1įŸ~S.ųü˛geDZŪ–O9Éôxy—Ũu×ÍŪV7K;U3+ŠAļˇÛnģ-æÜ+¤ öIDAT1Îö{ҧžzĒŖœˇĩj ‹.ĪŽ‹ę‚ļŠp @(–ēXž”ö!Ø ôģīž›,Î$Ķ]eĢė&#Ļ2j%›ũ.eöžlZ;ĀŨb õ“SÂA>$Sĩõč?üáæ€HD\y啿k_ûZ™“FGnÂ^‰Znļ*#¯=Ųbl§2}ûíˇ§ßqÖwÂû÷īŸŽ@7ú[Vm/>–ĮTMģö"ĘŗCuÁ@ÛT8† K],OJû@ėē™ĐbRdZlŗNrļíwƒ–ŠÛ˛(ĶŊ÷Ū›|ĻJŪAÍ.ęôüķĪ›áÇ'˜äŪFScŗ,ķŌŨŽ‹Œ~Ž3ÆČ÷eķ]A>æv*͎>øā„´M™æŽæ¸Ū"bÂõ˜cŽ1˛˛wŊ{’?üOŅåiŲĒ Z‰°‡ O]9yæ™gš#<ŌŊĄÅT§ļSyŨâĸ‹.JūfeļCŪėûĄŽËĘÚĖąŊŌy+߅/ē<•MuÁ@+ö€ â ` ‹gJ‰U1hÛËHŪˇŋũíÍíĪ)i'ŧæĻœÚîm›ŽFúđÃ7—]vYĶ‘=tÉiPÖ)1_˙úד3ōNš,ˆÕƊÛVéa§ļSYY[Ö'hô]qÛ@ëũ„ Ė)§œ’Ė”¸˙ūûÍl˛;Īĉ<4k´]žū–ꂁV"ė!@ÅĀ@Ī”Ģb6Đļņ“ĪÜĖ™3§f$ËéģęĒĢĖ^{íÕRģĐp7hĸæXŪ79sĻŲxãN?ū¸1bDrN?~ŧéͧ‘ōO6ųĶ Aƒ’wŌwÜqĮdEetIФ˙ą9îļÛnÉ*Ķōn¯īÖŠíÔūĖYŊī5Ë7œeY6Âm¯i7r˙ã˙ØČyŲėwĘķÚŠŪŖk$ô¤ŧäüGuÁ@7€Ä%@€@/ ` { ėųb6ĐąŒŪwÜqIđŸ˙üį|ēJŋ’ŗæūđ‡É51r>ø ųģŋûģ$Ũė?Úî}ķÍ7§ßw}É%—$†YŪ–‘>{Ņ01ØjĻuĩmáiO™E—Ú&+nī´ĶNéblÂxË-ˇ4oŋũvÍͲ÷!CjÎgOtj;•Ußå‚Ŧ:.›˜h™ũ ŸQ{ņōŒË?ŲäīYŽ­ĩÖZÆ~Ũ@ŽÉ=˛X˜ŧû,‹ÛÉ÷´e“<ō.ŋ~Ŧ^;õ-/ų‘˙Q]0Đ q € ĐKč^${>Ø ´ŒBËTPíˆįQiĸ÷ÜsŲlŗÍō.įžĶpˇh{ņĨ\ ž<í´ĶR“"§n¸á†d…n9ļ§ČŖ‹q7{Ĩs÷JmĘ~Q{õog:š>õÔSM?+—÷ˇ+ŖŅbžmŲOÖÕk§R†Oy~[ŽŠ.čf¤¸@đ'€ögGÎb7Đē|öFFŸōb:âˆ#’•{eTĩ'›v€ģÅ@+›oŧҜtŌIé(Šž˙įūg#SjŋôĨ/éŠd/.vŪyįäxŌ¤I駇äē$XŌ˙L›6Íėŋ˙ūiēҁŒā=ēŅ-ÉĩNo§Ryā /Ŧ‰UVĶūī˙ūīšáåÆųķį'×ŲgŌe*ö°aÜōĩSŸōœÂsĒ :§ @Ā@’b\Ũ` 5â÷Ū{ĪŧöÚkfåʕÉÔMywÍ5×ÔË=Úk¸Û ´B˛ß•QĀ}ėczŠĮ{té1˛–3ÄŌNåaËoŧadjˇŧ>xđātúu#˛Úû[oŊ•ÜŌÛv*…Užę‚n¤× @Ŋ#€î?r×!ĐMē¯ĶÚîVím5dB2<\Ą¤T t(ŠP@ˆ‘:FUˆ í'‚v€1Đ~üĘʅ..Yx¸Ø A1ØvƒjpԁzĐN‚6p\WĀ p €G@ †ÁK0ہi‚đĸAǐ¤™BÖZyCAP8ÅC‰’@ųĐ&¨*ƒĒĄCP=ô#tē]ƒú Đ 4ũ}„˜Ķa Øļ€Ų°;GÂËāDxœĀÛáJ¸>ˇÂáđ,…_“@ČŅFXņDBX$!k‘"¤ŠEš¤šH‘q䇥a˜Æã‡YŒábVaÖbJ0՘c˜VLæ6f3ų‚ĨbÕąĻX'Ŧ?v 6›-ÄV``[°—ąØaė;ĮĀâp~¸\2n5ގ׌ģ€ëà á&ņxŧ*Ūī‚Ásđb|!ž ߏÆŋ' Zk‚!– $l$Tįũ„Â4Q¨Ot"†yÄ\b)ąŽØAŧI&N“I†$R$)™´TIj"]&=&Ŋ!“É:dGrY@^OŽ$Ÿ _%’?P”(&OJEBŲN9Jš@y@yCĨR ¨nÔXǘēZOŊD}J}/G“3—ķ—ãÉ­“Ģ‘k•ë—{%O”×—w—_.Ÿ'_!JūĻü¸QÁ@ÁSŖ°VĄFá´Â=…IEšĸ•bˆbšb‰bƒâ5ÅQ%ŧ’’ˇOŠ@é°Ō%Ĩ!BĶĨyŌ¸´M´:ÚeÚ0G7¤ûĶ“éÅôčŊô e%e[å(åååŗĘRÂ0`ø3RĨŒ“ŒģŒķ4æšĪãĪÛ6¯i^˙ŧ)•ų*n*|•"•f••ĒLUoÕ՝ĒmĒOÔ0j&jajŲjûÕ.ĢĪ§ĪwžĪ_4˙äü‡ę°ē‰z¸újõÃę=ꓚžU—4Æ5šnšÉšåšį4Į´hZ ĩZåZįĩ^0•™îĖTf%ŗ‹9Ą­Ží§-Ņ>¤ŨĢ=­c¨ŗXgŖNŗÎ]’.[7Aˇ\ˇSwBOK/X/_¯QīĄ>QŸ­Ÿ¤ŋGŋ[ĘĀĐ Ú`‹A›Á¨ĄŠĄŋažaŖác#Ē‘ĢŅ*ŖZŖ;Æ8cļqŠņ>ã[&°‰I’IÉMSØÔŪT`ēĪ´Ī kæh&4Ģ5ģĮĸ°ÜYYŦFÖ 9Ã<Č|Ŗy›ų+ =‹X‹Ũ_,í,S-ë,Y)YXm´ę°úÃÚĚk]c}Į†jãcŗÎĻŨæĩ­Š-ßvŋí};š]°ŨģNģĪöö"û&û1=‡x‡Ŋ÷Øtv(ģ„}Õëčá¸ÎņŒã'{'ąĶI§ßYÎ)Î ÎŖ đÔ-rŅqá¸r‘.d.Œ_xpĄÔUەãZëúĖM׍įvÄmÄŨØ=Ųũ¸û+K‘G‹Į”§“įĪ ^ˆ—¯W‘W¯ˇ’÷bījī§>:>‰>>žvžĢ}/øaũũvúŨķ×đįú×ûO8Ŧ č ¤FV> 2 uÃÁÁģ‚/Ō_$\ÔBüCv…< 5 ]ús.,4Ŧ&ėy¸Ux~xw-bEDCÄģHČŌČG‹KwFÉGÅEÕGME{E—EK—X,YŗäFŒZŒ Ļ={$vrŠ÷ŌŨK‡ãėâ ãî.3\–ŗėÚrĩåŠËĪŽ_ÁYq*ß˙‰ŠåLŽô_šwåדģ‡û’įÆ+įņ]øeü‘—„˛„ŅD—Ä]‰cIŽIIãOAĩāu˛_ōäŠ””Ŗ)3ŠŅŠÍi„´ø´ĶB%aа+]3='Ŋ/Ã4Ŗ0CēĘiÕîUĸ@Ņ‘L(sYfģ˜ŽūLõHŒ$›%ƒY ŗj˛ŪgGeŸĘQĖæôäšänËÉķÉû~5f5wugžvū†üÁ5îk­…ÖŽ\ÛšNw]Áēáõžëm mHŲđËFˍeßnŠŪÔQ Q°ž`hŗīæÆBšBQáŊ-Î[lÅllíŨfŗ­jۗ"^ŅõbËâŠâO%ܒëßY}WųŨĖö„íŊĨöĨûwāvwÜŨéēķX™bY^ŲĐŽā]­åĖōĸōˇģWėžVa[q`id´2¨˛ŊJ¯jGÕ§ę¤ęšæŊę{ˇíÚĮÛ×ŋßmĶÅ>ŧČ÷Pk­AmÅaÜáŦÃĪëĸęēŋg_DíHņ‘ĪG…GĨĮuÕ;Ô×7¨7”6’ÆąãqĮoũāõC{ĢéP3Ŗšø8!9ņâĮøīž <ŲyŠ}Ēé'ũŸöļĐZŠZĄÖÜ։ļ¤6i{L{ßé€ĶÎ-?›˙|ôŒö™šŗĘgKĪ‘Îœ›9Ÿw~ōBƅņ‹‰‡:Wt>ē´äŌŽ°ŽŪˁ—¯^ņšrŠÛŊûüU—ĢgŽ9];}}Ŋí†ũÖģž–_ė~iéĩīmŊépŗũ–ã­Žž}įú]û/Ūöē}åŽ˙‹úî.ž{˙^Ü=é}ŪũŅŠ^?Ėz8ũhũcėãĸ' O*žĒ?­ũÕø×fŠŊôė ×`Īŗˆg†¸C/˙•ų¯OÃĪŠĪ+F´FęG­GΌųŒŨząôÅđˌ—Ķã…ŋ)ūļ÷•Ņ̟~wûŊgbÉÄđkŅë™?JŪ¨ž9úÖömįdčäĶwiīϧŠŪĢž?öũĄûcôĮ‘éėOøO•Ÿ?w| üōx&mfæß÷„ķû2:Y~ pHYs  šœ@IDATxėŊp]ŕ˙ųd Đ1Ņ$ĘDÉ(“Į b8ÁLĖ`~Čeŗ˜ĻpøÖĻėT 0…ŠĀōƒ”ÍB(ĖSÅ,fm˙bÖf)ĖØ,öĪōØ Îā &ƒ+ƒfPĪDDX–öķŊē}šīęž§wß{’ž¤ĶUįu÷éͧOûū9ˇ_ßžŠ”CĀ0 CĀ0 CĀ0 CĀ0 CĀ0 C`""P1;e}2 C`4˜:uęåŖŅ–ĩa†€!0ū¨¨¨h;zôčS•ãŋ+ÖCĀ0F9ߴ癋éč7n-†€!`Œ[¸¤ĖˇÃg††ĀX" ™oßųn!Ũ2–ļXۆ€!`å÷ŒfŦlÖũÃđō/ŗĐ0Ę9ßũũũw–ą‰fš!`†@ 0eʔ”ī„§Ļ”=f‚!`†€!`†€!0i0|Ō ĩuÔ0 CĀ0 C 0ŧFÁl0 CĀ0 CĀ˜4˜>i†Ú:j†€!`†€!P˜^Ŗ`6†€!`†€!`LĖŸ4Cm5 CĀ0 CĀ(Ė/‡Q0 CĀ0 CĀ0& æ€OšĄļކ€!`†€!`”怗Ã(˜ †€!`†€!`“sĀ'ÍP[G CĀ0 CĀ0ĘsĀËaĖCĀ0 CĀ0 Iƒ€9ā“f¨­Ŗ†€!`†€!`倀9āå0 fƒ!`†€!`†Ā¤AĀđI3ÔÖQCĀ0 CĀ0 r@ĀđrŗÁ0 CĀ0 C`Ō `ø¤jë¨!`†€!`†@9 `x9Œ‚Ų`†€!`†€!0i0|Ō ĩuÔ0 CĀ0 C 0ŧFÁl0 C¤1ŧ2jü1Į3=Ęŗŧ!`s†€!0ų¨š]–ŗ;uęÔ]ÃôĨN2!Z4Œ|>ōSĻLy # ũũũ¯Â/EՖ5 ‰€€9āa­†€!`$DĮņÄm Ģ•TV`Ã@6Ęˇąd›‡‘īĄ|ŊOMÔI“.&Tc˙Ī+**ļ=ztCDQ;ųĨĐZfDĘ,k†ĀĐŋÍ CĀ0 ‰ŽcŨX÷’Y⠕••ģeąf‹c×ė˛ĢĮ9Ę8Î+‹mCč¨Dį qēÔ¯Ŗü[P_œœņ C`r"0dŨÚä„Ázm†Ā¸@ ‡uF__ß~ŦՌnâ€ãx?Nn-ī,*Wã$ĘAôÎâ}$ÚüėhDôĨC a×yؕ"ßĸ|8ĐįYØ{9ŧFņIīĮy€äaåCĄ=?DúÖFî$î •į“l“T[č{ėȑ#û"ëhcŧkĄŪHYĨũĄëmč2ŌOŸ%j){”ū ??ųŒm)CĀ˜čØ”‰>ÂÖ?CĀ˜0āŦ]ŽŗĻu˝ą´Ą€"žWiGĘCepN/JėÜí ŊĮxWÔPx{pŒ§#ĶͧįŖ2šōūZōW}™õÄ}´Ŋ‡€fŸįE´Ŗ‡ž,NuX´L ´°oĖØŧrHĄ1 CĀ0 CĀ02Ā ķÖ/+Î,šÛ%[3ŨGÛˇŌZA˙dĸg4ĉÍ×zÕ Ųâlōâ|u„åža^ļ4íz8Pž–ŒdŲBü ō ¤įˆ÷ B[‡âÆ ųmP†Ķîķ2ÖĘÃûyTn°ÕĄŋ´ķCd?ZâqôÆƒ”/ĘRnlCĀ˜@¸ëœbīÂÍɯYũŨfÁ0 I‰ŗZ˛ˇL lđĀžƒ\¯û0H×îŠØ|/ų á_Ežs4 år÷ Ŋü8ZĄžžIcUē_Ņ÷!í˛|å&Į$ũ2ō)f°O…§%;ų„fôŽ‘Cī„iĢ ō>ŋž¸ÕÉäŠŅ§e2Ր°ęŽČö„mŽ”YÖ0&0•\ ôWÚæ¸‹ŲîˇuÍ0 !Ča+'<-§zŽæÔЧsĖągöųô¤ūū*gô˙ōŋ^•úģ–ŠÃ˙Úuã[Č^ƒíĪēō‰ƒÅãôG; Ãܯęōč_!ËiĒpØįĄzX?ųŒ5ā`]•āžYˆáæ-m­Šs3 Z3×2ûh]2 C '8SÍ4ëz˜S°ô…u8Īgāôé:ÜTYyėwŽö÷ėmĪÉëīOųdЇĢųBmjÚŠ§§Ļ}ķ´ÔŌūKęĶ[{S˙û­ËRģcÛ ˜ö ŽęZœđëH÷•ŪÔLØ|+xÍĖ䚃T‘ -!Ą­Ĩ`1ŲâĨN3Ô´=oÕŪXRīŨaäÂÅZŋ õ2jրNũې×ė?vÖ"/ŊŅŲīŦú­Ā0&>Ūu“‹D 9Ŋ1nÁ0 I…Ë)R8IÍ#Øé°Ŗ­]6ÎâšÛČ5÷xbĩí5Ũ×÷ŠŸđųšTôoztņ´SNKĨž‘:á>ŸaâįŽ?>õßߔúŋ˙¯˙#õßîú¯*[ŠîíųĖ„ĶŽ–Fȉ­ƒē D6ŽLTĄ@aėÔō …ŖTŧúéh¤{š>ôīî ä´Ŧh_T(G~ e×BZŠÔšāôzyôîcĖ–“Éā;ápŒÜ,ōŲlhÂÎ=”īĮÎķÃõ,mĀŸØŨ´Ū†€!0*d8ÚSĻNŗÖȌv5qāhãlyƄíôIßHôS<§ģæ _Ldė[ŋz͓§œúŨųTFî1<ŽÂ|‡zÔŅ~W2ËܒOũŅ’Áž}ØÕF{zAōņtč™pû8šÚÍĨō⎒n€zčãÅäŊ(ģ Ū*ÕCžžh9z“îd<Οøxjį˛j̇:ÄkĐLP‘~ ™•Đ|ękŦz)sŦ.Ú: š™č9 ϜúwŅ}áR›‘š›øÉACzV ¯Š}š—v_@ŋ>Öķ›!•asĀ'ôđZį C Hjqĸšp NÅQkÄ[;øt9Ú!ŊAš—ũÜņÕŪR‘iß<=u"KF4ĢũÍĶž•J:Ŗ-eŨ˙ūoŠ_ŋũFĒũā[ŠCíŋN|ëWŠâ?|¤åăá?ÍŊPëŋ•‘xuU–+čër Ë!å‰1FNrÆLsĖL}ˇã!Ŗ"ÕEšį°Į†xˆõí%/’ã*ú, _ŗå¯Bw1ū?úŦd0Åq3ũZĘ/¤<ÖA—$zĘ˙h,o#ˆ€9ā#ŽŠ6 qƒ@•,Åšū f$Jō4ĪŅøcœ¨`F›—eôžL*ÎŅ֋‘_üŌ—wøÃ˙øŊįXÄŲ–ƒ-G[N÷‡ŋīŽÕ…C÷{:­‡ūĒī|ËĻ;ā™#‹Øˆ1;‹īŊˇ4G:<Ë]_ ē ~ˈY`Š C`Ü"`ø¸:3Ü0 @ †å"8Egā<5â ąûČÖi÷×úēžëtR–:ŽĒ*åŊšŅ.ÔŅū”L4“=8Ģũ6ÎöAœí7´ k2#Æž°íāŅŖ}-­äßdfļÛ“^ÁÂJŋÂzœīœģvd(ļLÉĐL9ĮĶl–¨hIJ8t2>›ķ†ÄŌ†€!FĀđ0–6 ‰‚ĀG›iė3™*ūÎĒ7Ŗ-[įÛs´Ķ'MãČÁ— Ķ Ķŧ"ŋZ˙g‰ņ8Úח:ôî¯Sŋy÷`6[Ž÷{īÆęÂQûtjåąŋî;ōÉ~9ÚäĩsĮëPg¸‚ėˇåeäžâ[r”đךiįŧe͆€!`ø˜n‡‚!`ŒgĒ5ŖsĒÖ4áŧ6Ņ™oCu´ņ´Kęh ´÷:ūŅ{!R÷Á7YŖMŦ$Y#Žâh8Ęu9ōéëØúļj6û Bm8kQ؜yúöKeēŠ¯uŅÉ*įÔl…†€!`Ŗ€9⪁˛ĩaÅ"u´Qx:ôgr´phŊX?S+ ^‚<õôoņbdá3ÚŌ÷oüÖ[."[ŽvûA–ŧ{0õ ;‘Ä…ĘĘcŪ;:Đ ŋ¯īMė:€Ŗü&rĖl\*gY/O>×ļņ CĀ0ĘsĀËŒĖBC`2!P°Ŗ­/DžČŽ#é“Növ)4Ŋ)[ŗØŸ9Ûoeė<ÖË>ßZÃō8Ã˙¯f´ĩN[ëÉģ%"aqK†ĀøF ķunÛų=žĮą,Ŧ7ŧ,†ÁŒ0&UūŌ‘i,Į˜ŽķĒíĶ “†›Ņ.…Ŗũ‡>ōf°ũVëā6ūĖv÷ŋĮoĄcũûÔÔŠ­ũGŽŧ†Ŋmä_ĮÎųėķ}ų˙NúN ގ" `)Í"Äģ*`¨/lÆŋ™ę„,ö`ĢÃ9ö´wy֗p9Voåüšį +žiôi›žø­z\c‘{/ā|šÍąąí&X¸|™ĮĩØģ 7ģķŊĖí5ķĘsĀË|€Ė ŸūķÁl÷)ö¯3Ŗ­t!Áí<Ōņŧé¯ŅVüÛ÷˙9Vļôq“}'åUėԌļÖgë…Č.žöԑí”Í –(pÔÃĖz˙¸sVU°˛qR‘>âXKĮ™ +ķuđÕÃkGœĮŖ­—‘ë*žÕčŌî;×ĸģZúŅŠ9ékŖmĘ8—ŪåKc­?­"ŽŽNš”c÷&léâZđ“rąÉėߘ>žĮĪŦ7Ę]KšIéeHoF›8ÖŅæÆ›Ęæhk ÉÔĘÂ.KZ2ŌĄGŧ5ÚoyKHäxĮŊ‰ ŸN™2ĩ-ū~…Čk=ųƙ¸'Á›Í°äYAüĐĖŽ63Áėč69æĒ9Oޤ¯ŪƒĮØ&ŽŋíІƒ×QJ9ž÷ĸoo‘øVbķķč™-ĮÆŨüƒĨ¯’ę!4É Ë9w´:iô­"`č l×ŋtĨzc\ôŨŒ9’œ8#g…i6 ņ‚€Žr´ĩtÄÛu„Øs´á{× ¯/.>‘ õEČđŒv1ŽļûģÛGÛ})ōhßg3Ôa0y!ōP_ߑ_b§ļø vá>ÜHũ™Ŋp5KቀīØ-DŧęãÍ8}/ēę8j3q2å¤ũō'|§Đ‰ ×rŧŨķŖenĻ5ƒĮøŨĖS´s=2}ČŪDūAŌr¯#î&‰ŽUdž%Ŋž>–ŗƒŧôĮé&ĘĨ§RĶ8dĢGÚŧ‘ž6ûeņk—ëųåĄcŸcĸ§;:ĐĶâx~\EŲژEžŌrÍČ=å—c0 ˛š–øúôXP˜g‚>k)Ш -čxˆ´ˇÖ™˛ËH7cË\Æn'iūōΡčō]dG${7bīŗÄy杪Œc$Ė“[Ą_ Pã™ūä5M¤oß.<°˙-u5Ö˛GįūˆÃëˇĶÔŊžē3ā+t"sąlwA×ĩ•´õ0q§cZl‹€wÃ,V‰Õ7  ‡€Ž ‰í¯ĨO vŅ:m-%ŅįØ=ΛčK P>Ÿb+ÅŅū—žŖ}˜ņ~‹ęnŦî…ČŪ°œĨK‡ߊcr/7Ëi#_ƒĶ3“ŧį€ãüĖ'ŋEåĐFh.r{ā_Ŗ3đ¤<.TÃ\ŒSĩįĪ9āQŪôÍEN3É×ĸŋž¸ļįaĶBâGhw:åW‘_NZļčaa%yũĶá–Čų~…2ŲŦĨÕČ4’āß ŗų­´=;ŌÔŅÆذ—x;mÉßėc8ؔ‹)2˛ˇ›x 6õ`Ķ“đķ čŧ‡ē‹ÖXę|šĄã\Ō}ėë9ßä‡´Ļƒã ZŨŒÂ[öjIÃwHŊÆbęˇĀß* xčGųE”ĩëĨĨ!Ũ¤5Ģ~#6Ū@ú$äĶļä´ŋūVōˇQ^Kų•žŪĘ_ßJ~#2•Ä â pܝGYõ˃BKE q ĄĮdž¸EĀ9ÚšÉæĢšg´Kíh'ũ;ËWē™c×ːZ;ÜÆMõufãä„gļôWūø‘ņayšãånÆáfÆá™Lœa9ekYÃXhVQá ÆĢūŨ¤ķuĀŊŠÃũЎöE˙úgĸ_Ķ…¤§“Žuu‘I“–ķīÍĘRŪo.<ĪA$&i͚_C,Į3W¸–ē3x((i?B jV\° °ONųĨ0gŧúz<īáD2¤—Aų:āĮÛ `v›ē;Ņķtē_C]öÃĪûû)܇üÅNˇ‹zwfĶ]Y1ௗöfcßã¤į‘>…ļnGgøaIã~/öxĮ&åz˜.^ĀąÖą!ėfÃđ°ķ‹2"tëøh‡:3 ,c‰@e‘õ­ē!`ŒŽļn>ēŠę†UĨ.özââ/åOŊe#ÚCû›ÚKģČm÷BdžŸbĮŅîaĩ8ŗ˜GŊ/Drõ>Ŏg}Ø3Ô˙áÎZz”`<.QSā˙H–&ĩDI3ŅQu#ŧԑƒ”1–YôäËŪÔ öĀ1߉Ŋžķ­BōíØ"‡Õ 8cŋÄŠëĄo¯Â˜ŧ>tk#erpķurHū[…ŖxvÖûUkˆŖĪĀ Hd÷aģœŪŧ}ŊĀÅ~Ō7NaÍö´ÔįŽ?ž ūō)vœíˇØŌī7đāSė8Ú7=nŲc•F ÆĢ íŊq-āti=2+íû$šß‹Oŗt8((>1œƒ×B´ŽūI9§ņV„W¯!^ īâ¨lœžRōpwqÜ÷Ų^ĩnšĪ8/b”3*‡9ß S#Z+z9Ųk‹XãT.Œ¤ĨŖščą §XĄr0*Ųo)ÆĸlĪŌXĶw-OYuĪ0Ö×—ō¸,Y§MŅÄB Ô'ÅÄBĮzc”7Zķ¨]G4“Ũ„ŠŽŧ›/<Īz‡g´KáhKyŌOąŗúāŨĐÎ#rvĸĻÍ3Ô~Ƈ9ļä”ÅÎdãxˇs|˛}úÔSįv×9œžSŠ'Įl8GŌUŗŪXK†đKÄĐqy5ēĒp€aëâ´,û(Q;9Õ€Û ÎíÚ= AĪᄷŽt:WEėmĸ^k.™pōŪųGŸ/†ī氈œķä–Á”Ŗņ@å Š×ƒœŽ… 𖆧‡°Ž€9|B} û&5ÃW)XÂŊtyØÎGËFÆúV0ט^ ßrÔuœ[0JŠ@ø /ŠâI LØyÅP_žQ~Hdž@bâíF´TK7ˆ …5_¨MĢá%Čož6Ŋ¨m)Oū)öĘâĻûÜ|Û°/ô)öŖvndŒÖøÍā”<‡Ķ˛ zÔwXŨėgš^u@ŒŋfQī ­—÷äÜɁ[=å{,ČšëŖŊīˇ@iÚĶN$KtN9'´—6Ÿĸ÷Â̊ļ˙įđšqØfû[)FEŠÍËņĻN´'ņ"¨ w߯ ē æy°Üî^čŌ7Q)<.Ū}Ŗ|ô´?ÖRŽ/éûa’ĐVh¤%GĘW3ūšUo: ŧu´,¨á' ÚÔ‘‘Õ(ucÍnGw2–Zŗ.~°GKztĖÖCę—C $č䲐.—qRŪÁEI/ž+ø?%_É|eB•&n:ŽĄ39ļÂ3Úy;ÚĶN9-•Æá>á>_0šI?ÅÎŌ‘Ãė:ĸ-ūŪā&eŸb/ųqYQÎČUĐZŽÛ84#.§q ×ĀkÔ#Ž‘WãÔlŖü=˛r§Cû)ג/Pļ‰ē3‘­ĸ~ yí¤ĄëęjxzyONčZĘîĨė âäoƒ÷0TŌ€ūĸ_ÎV;$š VėØ@œd3˛5”DÁŒÂŌdÖ  6Ŋ-u´§—÷‘ ;ˆ‡ÉkgŲŌMZøŽĮĻ`ļ|÷Pˇ^6|썺XõĐŗ=m¤ÕF#ã¤Ų÷Hx,@Æúq'rÕčŦÁ ?'\3É7Q˙%ĘßAv?¤™ø.d4ŗîĘõĸæČķ=(ÛOãĢ­ŨŦķōĢáO§ mš–|=iĸŌėŧ mŖ_ŋ‡éÃôãp+ä_@ļ{´›Î}á2KÅ 0ŽpūŪzFíĨēŸDŨF$ĐĻö[ŨĖ { čNԇ8Q_áD]w–02¨ã8:ƒcDŽļlÊk$ŊҜđų^‚üvJ{gËÁ.…ŖôSėÚy„ãūWxúp÷)vf¸[™6Ō+œAÚÆûYzų"Įķ9Äu\ûFfƒåŧžLųy7Ž\Nd¨seŪą/Ļ;ūÃËhį:t}ŪÜ;.Ãi먀ŗø"ąÄŧāķžsĮ-ų‡Đģ{ę%@}me)grH@ö\ÚÕōŊ¨Ypđm—œĐĢ—OĻüh’Į“%ū ŊķĄ‡€Jōŋ$θ˙҇[čO•WŸ,øjÖZ;Ÿh5Ũč:HÜyŧė9|f’Mŋ„œírļŋEšî‘_§ßPļšā>Ių”KBļ8zh{ˆ>žL|Ēßüŗqã‹ūĀ!F^ãë%|“ŗ];ŠŖ<ōĪ"ķ.EÂN¸ŧ oÉĀ^ņ ZZŗšōÛH?Ķ‚!P,ÜWŊOËŽä[É<ޜF.ģ°[‹-&Ž‚ĒčĮ)ÄŪ˜¸”Ak5~ZĪЏMzĒیœfb/Ü,3‘ČéhG;.GÛ}ŦFŽŅːĘ×|á‹QŅŧķnį‘Ÿb˙˜;Q›?Ģ|ŠƒdŪ#ÁBރ…ÔG˜Š†€!đē÷kgœœô`F˙ŗbKų!žoČiMôtËAØ'įRÎ&p# w2Ą'ŨQØr€Â{pĐS2Î÷kØ5žff\ĐÛäZ?Wƒŧūū|‘´žrõ…ËČk#ę°kŨíy”m Ė čøõ5ûāíëøá˜2mģ´;î~¸ĖĨąs:rsŅl}°͕[<î¨åŧhb<õrYÖO‡!íĖ8š^„Ŗ-ŨI?ÅÎĶ÷¯Ųy¤ĩNW/žŊ)5Ōå}rI‹ CĀ˜Ŧč}€ŋÂØé%Í`æ}˛bũ.Ä87ë%×:âZnÜ]Ä+É_Ä <økŠxĶ’kāĄ@/’¨bđ’ī4k­ ädkģ!},āBŌ ëqžÂ đ˜ČĖ!q/8⤗ŅW­O‹:ë°2Âjr[ ¨=Ŗ„ mo7=¸TډE§ŦķaG[ļœmQ-ĮÅGûsĮW{KFNeųHŠf´…NŌOąO™2ĩŊ´ŨáG‚ķDz-†€!`dE€ëh˙lJ‡ģ˙gU`†@Ä¸_Y/jŦƑÔĶZˇ‹h)”—Ž'HęĪSŨh@į•đēĸüųjĘŌ8ŅĶô ĩq’h†[!M;zÉâfxz™GĄ {_ņëá=@™ÚĒW:š™É?LRŗ‚zé#ė<ë‹iĸԓxÖ@´–ŽŌ zX‡ĢŖĪ6_AųÎátš *‰í˙ésĮœxō´Šiß<Ũûôēœíiß<-õÅ/}š(Ŗ“~ŠŊĸĸōŸúô…Č6ŽS}dÂ>Å^ÔXeCĀ02hËČYÆ(Bđũ8ĒžķíˇŊ‘›~mžvā´!_•Ež7 ?{ŽėôŠ\ÎąÖgy:p@ôĨ6ũuôˆ ũĐJŧæ?@ÜNŨ´Ę[‡Žvœæķá‹8Ī8į'Š ú­—6† =Čv"Ô'H;wÂŋ“8ŽØxŖ‡€ŪāכūgpŦ42f4=ĒÕXs dX’ÃŅÎˍ5|&ņ§Ø+*~‡}˙€}op ĩqĖzŸbįŋ•žpkô+œĩ´!`†€!`” …:āŅŊŪ Î;āhÍĩ¨a=úŽÁē‡d ë Íb+h ŽŌ}^îŗņŌ~Vŗ…i]ļ šĄZ(MŨÄ^ -Ŋ }dÅ˙ĸˇ&žÄ¸ŖŒĀG›ņÔWuz"fsīÎ3ëØãŽ8ąĄąBĢŅKŪ§ØŲK›íĸm÷Bd۟b¯¨øƒŪÆ6í§ímņĮ`+uÜņ¯ßoézņwŠ'1-=‘>­w?^ā˜{…CęĐ!ŌžäCJŅąæôÜ@w‘RĮČŅn[}†Ŋ œĩ|DqZx3ÚJsėqũ~rã”đŒv)mY”ôSėÚy„-ūŪÄ^ˇķČAÔ w€Í¤7ËéÖAv$Įø1b}há]ŌZnŌÉp/ŦvdWWR?|ņQŨč čI(k@įåvŖowœmŨÆEL[#^NÛYˇ4ŒĢ;Éx9ma!ÛÅS§Vz/Cz/AžĘ ‘úpÍI'k’)žP?I?Å΁uˆ— ^gŦÛtsåØ|“æÛا¯3ŦĒ!0aāēĢ×ëúČuą %Š8đ_Ļ~# Ũȉž‘ [/Ą{9ˇÕ–‚Ūû‘ƒ÷Ā`6¯ßõžÔRęNĪĢÆČ Š?ĄePøūny1×ŗ aF(]Í}Í}MT×ŊŲĄ˛’%ũcEø×ûJˇĶÖ5¤ģ\#žžČ;ŽÜâÁ÷Āæ°zĒ<8ŪOb ¨{74ÄGī-{hciˇķ\!MMČ:•ôj uj ¨7UÖŖTŽą } ö2Š™0Ē ^ũJ_q3ŠėžŲ?Ø3Úę&<¯ˇŠĪ~9Ų'2Ģ=-ĶŅ*—¤ŸbĮ°˜n×§Øĩt$ø;ŽvôØ)Â*ĢjL|8‡îāڗЉ͆Ií@ĩf3ä]ã3Jņī'úōFîWs/8 īLōÕIÔsrÎa3õŌIꎔ,ũX‡n9ŗ‰īMÂÅŋīéa¤ØĐ"äŊc>Np2ō;āXĐĖÄH€7˜\€_¤-QĶĶ]ļ'< wÂ$Č{ŗãažŌčyƒh+ņãdΆ†8Ø\ td ‘ęŠ&[;ÚZ›-GÛûģ0SpmÅĐŸØ0 å#ß<Ã˙{‰f´Š}pį‘×1Qkĩ?û{æ Ž¯öcÉĐõ€rš› 7!â)áēĒĪ›k"E2ZbĻ1u=Ŧ|(čÅ{-˙ÛoČõؗͧÛ$Ŋ†ķ5éũŦũĢŅ˙ u¯vį;q‡¯ÛE•8&úpÛ<rõ`Ą¨ŊN~HŒšeŸE=-Ųô<}‡ŖŪ}0ô-‰K ũđn#/ģ~OúvŌ˛Īsĸ)ŋų'¨{&üĢ >äVëa…t(×ĮęfRī䀙žĪ“žŪe‹I럁-ÄŪņKf0;—ŧ‚ļSžAųęßAē‘ô~ŌגہœÆRÎ÷đö’ÖR-9Đ[)_ė—“>pÜ\Ā1ŗ–úōY.ĻÆã‘˛ÅđŖöęáė|×íęArš”ŧÉņãbÚy‚6—éüá~*û-€€9āɃÃHs@鏌‹'†žH/Ėą<%ykå]CĮf“´tD/Aę†ãŊ Fžåđ‚˜ô@G{ją]Mú)vÚĶ~ôoAî…Č7áu„ípũķ,mĨG€›ø÷¸AO'ŪÕÎyøã0kNy93i¨’3ņ×ŪHļ+Ÿ- KŗÁuČ?•ôüF~šŽi8áļĐ­{Ã\dõpā䞄˙m.‚ŸĶyë.- Č\Jü{âk!9cģiëfŌuP¤ ¯.ŸEÜɖ }(.pššÎĨėahŧ9ž—BkĮĐđ*đÕÕđ} l߇ÎßįdņՊ] Ŧ“ôF¨ōœtântü„cd)ãŠņŌ,øLÆEcŦYéÛáuĄ+˜øŖŧŠō•ËA×ōĖÅ1Ą…zŗÅׄK>õŽÜôeø0Ž^{…uˇäüķMŗâĘį j ÛÔŋsÛ›Kv2•UNĻΖ°¯zŅS^Fā }6ƒ1q2:NĸŽļˇtžw éϤāâ¯~íëG˜Í>ÆŊŠ]Güíĸí¤Ÿbgį‘wØyD/Dzë´§ƒ˜ĒxėÅGũ°`ŖŽ@Ī0ŗc ÜÄĩŒBN‡Ž?ę&ŸÁȞéäp]öâœ%r@ähe šm–-­!NԚҜI\2\úéËs8gÍ~ZŽXŊŌá@ģĢČ;›Ã§ņ #‡]vާPÉ1ņ4xkbė'chøÃ´íđu˙*¸cřĨ¯pŸAF˙ä¸ãˇšŧ-Nxc(í%éß\ƨUŗČŽ žÖÚ7¸mÛ}8‚’9ā@&QÖÍhËÁžî?‘ëĻR% ü;ˆŋō§_ûDŽö‰ Ķ*´ķHČŅ>ļXĖ øûŋc¯>ÅŪʅ÷ŗOąGv =ņkĸÕ7 1B@ģNqžëoųyːŋ(34ãË5`Ūh›Ä5å8Súû}>6Å:āØŠŋÚĶQÛ°W3•[Ŗü„y͘ŽT误ú‘RŅ[˸60‹ē/ÂĪ7ëœī&°–ķŨ•oÅš*ާ[˛%ÎņŠ’?‹ãå¤ĩN˙\÷OĮ¯wŊߐõß°Đ.6C‚ęÁÔŌ•aƒŗ—cü,Ægŋ*hoF[%:ū3tŽ÷Œ9āã}‡ˇ?‘Ŗũ…Ú?ųdZĶĮüyÃ4īÃ5Z:’>iZęsĮÜđMå–p/D&ų;'­Öe Ö ‘orĶMĪũeį5ČE'wÃVjãÎ}įt~äwĸŪ íÎÄÔß­æÚņŖ„zڑ߂CŖåÚq†Ü˛ÃmĨę;”ĪRžœō%”?!ũ¤iג+\¯ÜLŠf<ģ¨{#ņ¨‰°ģ›Q<âK9Āc8\DŸŽ%~,agÂΡ–\ã|Ëšŧ‡ca9ąv†š)Ą-ųˆ{Į/ØöøÂõ´w{>#2ΐŋãLī:•ɏķĀōÄŋФg/m¸qd¯vg)&¤é´?Áņ_L{ãĸî¸vĀuA‚^į¤iɆ6'ēļršîĘü;]z‚Åiúęž 9ėŒļíoœŌ4å$Öië ‘Ĩt´“~Šqø„SÛûŊiv7Ž×áw†ĮČNÜ0–6&\ß÷qoŖˇÛ¸ÆÉņĐõ\ÎHAëˆ^×2‘Y…(āÚt5vlĸî3Ě5–ŖŦekāíÃŪŊØ{7ŧ5Ä7Ós¤]2–9Į 'J;JH‡B-¤m )ƒÍčv?ī6ÚĐŽLēę~ŊLJF"H÷zHúÕ^ĸ€‹¨°>\ Ū€ōôĩ"˧?Āį"h&üD8ޞļ¤\@]=ØŧŅģÜŨãŅĩËŠ77ŦĢTiúū"ãØū]ØĢ㸠Ö‰ÚŖūCÔo&~ }ÂOĮ^=ņ^Ú¸ĻÄövŌÖĢč”ŊĶiCÛ6ÚöjûLŊ{ąģPą^åXvŠ^Gû[īMálŅIŗžz-9ęÖrđ¤)o€tÁMtr"_Ō  /6ëƒŗ‹QĖ­=fuņŌÉÜyO­đ<ĩ.Ž>á>mĀ#įYī›čáĀ ´ņcÚmåbíåŧ›8°Ų6ėŋÆöÜ5›ē‡}VéĶĐ1‡2éxž–nԒÖnĐ1[˛á4Ų.äÅ÷ÚŖū•´×A>*§NÆurøW"¯ûĶՁpž ęčŖ8ž=ÃUĄ_Âæ9eÃÉFË#}Í(ƆŽ Fęč ÚÂ,<Ēz"qXFxZ’¤qÔņ[E{ģ‰ƒÕSÄCÆ(<^Øuc×í5ÄwęžĪøOGŸ^æÔR¨wÁŖÕ//U${O‰ąW~Zp 'hL3ßËëÛ¨Ķ— Ū„StC-ЈØ'PŽ“]OįrĀĮ4p`OÀæbŒ /ÚĮvMXĮņÕ'|ŌxÚôÔÉĶN=.ÍKĶN9-Ĩø„?ú|Ņk´ÕNâOą§R˙Hĩ_qŌĩqō`´”D8;Á‚!`$B —kHÆD ŽGK" Ÿ ˙•ķSlĐ”v)!ŽĶÕŋ#Ž^wžöëŖ,a[;qȜĶÖN÷†u#ŋ/jC¸ŋAëá_įķÍƒŽ€'Į]kņŪFæVōר0 ˇsW#įtČŽËám†§ŋL: /p0íDŋœīg|VŪuīCø>tkæ{=öyä­ $Č,E+zRíŋqã“nį‘$ŸbįûÚkFÛ>Å­q CĀ˜„—G„;Č}ėĄpžœĶūŋĘÁÄļ—ŗšÕļŧ'B'*šú5&x.ƒBeúģBTՉSŦ§(僀“¸šË͚k ÛVōsaČ_‚HˇīĀ{Ō¤Ÿ“ƒ éS˛!ZĮ”hæ;Tˇ”ÉÃS§V~ø‡zNĐÚė/~é˞î$Ÿb§‚v‰žŠYO™˙c­0–6 CĀ0 C xĘŲ¯Ã~œ.jŲ†o9ãrÄŖOTkqģ‘Š†ō ú;MKWb*”->•ĮTž}ôhß_Üļėš”Ömb6ühߑ˜.x.ÁĐmņ×ĘC…ÛyD˜Z0 CĀ0 CĀEĘÖÁÄ!Öæø8Ô_9āš‘Ū56ĖęĘI?<œ\¨ŧO>í|%Ä+û䧟|ōwų¯ũŊĸ üŠ`įf÷?ÅūŲ2ž@І€!`†@āˇĮ”Õà û 12ÆŠ€0v.ô’đüĮ°xÄpcĐ1â-åŲĀ”<åFJLÉ7fQۈ3ŊŸ2wĻ‘ÕE!W¨CæRhK.ĄpNę>9íŧtĸP† iž‡ŧ7̇ÎRŽO!|2rü˛Fī>úŠ}T¯Ĩg3Ģ}ôučb&n!~Ō_įÚK ŨD CĀ}tæúĒmđ6…Z×ŋĄģ´˙qˆ—-ŠŨŦŪ†îĘ&Pbžvč*čZŽ>9ĸß–ØŽ‚ÕaĶ%Đ(rŋ×=”r”Ĩ‘*øiHĪxš´K°Ąõ3?ß#ÜtųĢ×ņĪąõ–9æĀkWžū™vÃ{ y­œā/¤ü ˛ųœCęŒ8ƒV`؀âo,Ō€@§í#ĐĶĐOĄGöÜī—‰˙8ô;xī¯ É‚÷ŧŸJŌ…aåēxA|ÕđĶëh{–“ˇ ’›_áÛõ’+w1e?DFXŊãxÄ5Ô’­ę›l[T€Ģb%B€ķq…nį},¤N‰ĖjĀ!t\õ™ųđX\—›‡ë82s$+=Č&YŠ8œęŒrô_‚zá_m‰>ôíΐ˕ĄÎ"ŸöīĘ%;ŠeØō!;ÉDQ–í~UCŲ:čcČarkļkS9T#âŸčŨŋķm•ŊĐŪ ´W÷uų o GžÄ@6=” ŖrīlfŽ*<ö@:Į‡<ˆŠe;æÂvęØuاÃ.Mų͌‘üˇģ6¸ļ˛Å´ŋB}R<ĻO¨ĖâŪ‚!û0TÎpŗššņöŗ¸?ÂĀvf§gÂבŗ=Օ+†w>2ķ‘ŅN&•ČŨÆĖ¯^Čė Éi C­ī€v8>õ‚ĩãÔšŨš5זˆÚūđ0ņf'ëbø8@ęÉz\Y‚X_™:›öRGŸ'æ…ĘŠą! 5 C`b!ĀĩđŽģû¸>>PhΏ§ėäzĒës3ÔW¨ž\õĐ¯ĄmÆÖ\Į¯æŸF]ÃõEĖD7uŽųÔu›‰ŌИlY‡ÛąM;€% ÔŊ€ Māp>ãĐ 6ÚŪ÷^î™/“ß›HÂÔŋ‘H˙0ŒHиaÛl{W Оž.ŊĮü—”žHë#>šøëŖū*č7yÔ1‘āw įũ+P1įl*7B‹CĒ3’×ß§­7 ´uKFáXdd'Έ“Ō žŽÁ\ũûLÚO!˙8¤Ĩ áž‘s dcĮەƒĶ­čŋÄ?&tėiŧõĀM”kõĘv)MaÚ Hŧf_f|ov؟‡åHkkčÛáëØS[‰psí)Ļîh îœZä“ÚŅņÛŽī§Ģ(ÛŊS°(_} CKF=čXĻ}Īįë5āŖŪykĐ0 C ,؎Ur\Ī‹ŗŽ̇á/eĻ\˙šÖ“‰XģXy›ÚŖĐ{Ė@v1ĶuƒãGcôË!ŅîZ+ŖeÃåšņë_R}L-į~ØØą ]zGg/¤ųĢhwq`īpmQOkŪDäÔū<ņ°EÄRpXE܎ü*Ōˇ’VUÂÚŪ_öcũÛŧ•8ę€\OļļC…†¸ĖŪ¤ĘĀīqęlf_HZˇPyÚ<‡ē}´š;9ŨÂMc×÷NJÕąÁØŪÍq°‘x/ģĄÍ:nv_”Õ@ÛĄ-Č^OՒ‘xRV éC‡›āéũē4r×ûzôpĩ ūĨä7SĻļĻ<ãŅ—ÍiŨö;ŋĨū{kCäŅŋŌņģŸBŋ:ŋœ :ô đ>ųjÆījĮ‹)J|ęčëéc0"đÆĮÖkŨ0 ąA ë`!uÆĻwãĻÕjßQSinŽēáÃteÚ:VŗĢ?p‚ūėYpSvü˜X/Ä=J[Í1e9YjOļ ”Õ‘Ļü2_Ļ)¤Lm~L}-ĢČđ×Aģ2˜dt|A‡Âü°Ŧė'/\4ãéŌ‹÷đRŦēŌéĨJĪÁ"öļŨĨžD‚€ŒŪMē=`Ä$¤‹zjc؀Üč‚ųŒI Ī¯÷ŒZ1ɏä?ôúg@ø? i=xR§Ŧ‰:ƒŸCøé‰ÅtwŦĒA Lí ī@ ˆ„ôjQŅčÔĀĶÚöŦãĒķ™ā _‡œb9Ųë|}š]–Ũ뤗xô!Éā< ŋŌx{<Ō적(é’?Tųįŧ;6?ˆũúšÕv2˛E}Z ö7|ž…І&Š/۞Z2ōŲ­>)Ž=@FŪkÁ0 CĀČ@ '×ZafÛô5a7Ëx˜Y°vxÁM7ÛėYF ƒ}eųē~>,įpôäž‹m(o Ét’ŪŊ3‰sΞ‡ę䕤/Īš‡ Ōúėw}´"펂įl>ė—ģr/‹L ŲYtĀŠ›Á,ęjp¸eí ĘvŲērv&¨žX´;p†j°W}WÛú ][>šĀŊ–úÕŋ.ŗ‰;pŽ. یÎvtü>ƒ”ĮíÔ ėĮ>{eD—ŋO3Æĩԉ§6ÃđÎ5__oD@3ííôē㓗<ŦĘ&Įô˙&xî|qbz ė—īÍuÎ;!â‡!wüʖVėj •ũ ņr%;(Lį˛č ŒF›Ö†!`†€!P܀ģQáDĨ0Ę´)'MA3Œ­^jčO•o[´DöqHĸBEæ=§ŠHÅVoÄŠ{Gk-ņI”áŪĶÖIŨÑkV]ōZ‘Vgí,áXĒЁ3x§ījÆW3Ŗc÷šI@ūäŊãôŗč؊Íķāē>œŨ`ģÛnCn/ÔIē:ĻÎpĮ‘–aikčđ,´Ô´ĀwŽrŒÚ’ąä„ęš_2ËCŠĖaICĀ0 q€î]8›GĶZœĢ_ā\io>N[ŦŽĸŽŌQģ°ĩŪÖ(?a>ÎYJ¨"Ģx76Ög-ͯ@/†jÄfœå›rTŠb–ŧ)ēÛíĪôģ]‘ē‹)[Œ>Ŋ-ŽLË'Đq˜|§ãk“ũŒįh¨Ļ­Æ¨ŊØŌĻãšÆą>†s`Ŗúƒ<}œ…^é*åƒ†Ķ¯Ųî•`y-ũ|ĖgĻąČ3֌ŗfĪgGåáy,ôßĒņŒ–ĶžÆ*ŅNTzuœnōäk‘Õq3ĻÁ^ÂSø­qCĀ0 <˜ęäpđn%]ÉMŪ{ĄĘņķ‰qpôą­Įž'ųˆL;ų-8w #ŧ^¸Í9ĪR.§&؉„ôBxZoŦ%"ų†nęÔ!,’͝õãI%UÍ7čŖtÍų ĮČåë|ËaŨÅØŊJŸ~ÖīD¸Š0ЇkĨxaįv#ō¯Ąë ŌE?˜Č™§ĢĐĩĘ!{ŖkøģÜ] éĄPĄŌ­^.Áí\FŊ=ÄÛTK"ęáÄ8÷ø•´ķKÖõá9ËųÕ š—Gd)ÆëJg”āâ|ziŗs)lĻtÍįŊaæX¤ĮÚ×K7Úg~Š:¯‹:›KĨĪô†€!`Œ)Ŋ´Ū#‡†{…œļC87Óŗ$(QĀ!hĸ‚–‰ĖJTŅƁ¸šd ô öčq‡Ŗw‘DpĩķÉŨ$×PϝrĘA\ o™[§.g]õD”ÉŠįëÉë;OQ§ŨN‡œŧ*Ą Ũ͐×~Ō6°ķV0ÕnËI„ ]é°>djüŧ‹ÃÅųĻ;Ņ́p ÷üųVrr`Ģ?Úú=đ֋~Ī˙ØÉ¸˜vjũ´‹]‘Œô>ÁtéĸĪ{ ×ČK–Ĩ$ [Š ã}fĸŠų w Ú­õm}t”Ī1ÖĢŠ¨ĩîoŖK˙zĮˇ+žÂaDt.Ŋæë_CūZw. _=S[åoVëüĘ,ũ\åč7ųY‹ôŊÚn@IDATqör8ĶĄRm7´`×Ŗ¯*Į uf 1l1đŊ\Ŧ_,G#Í&CĀ0ĘŊāö%ÍPrm˙:7áßrŨ܇mrTt<Ä5X/bîN\y°Bu/&Ų€}€G['žvhCNÜ”ŸCėļļ mÃiŧfPŨā/zŧuåp(t‘> s(“Ũ#kÁĄ–´ũč˜-ÁpZõßkúWŌ^ų ¨œ:mƒ:Ÿĸ+‘ŋ‡´2ę܇=˛T úŽrúuž°!~6‹|Ā–­čîŸ%zHęĄáŽh_>ɞĸí§°Ą ųAŨ´ņKbå‡dg#û8{Áv?NĻ|ą–e|Ÿ8Û! ŗ3Z)ڇ_Ԑ]${ ãwödœØĨņđĮžķ§9´QGúhĶą¸ Õļ(¤Ŗ[iĘīSŦN+ŧ–=‚ū3‰Ģ¨×īu••2ĐîWÜšĪą ĩ_FŋÆžPIŋīFĪz*˜ĸ¨$um(.‰ÂdJtáēpg$Ģ–]}šU‹žd7Ę/į˰íbmS¤oÆd؊&`#Š€Ž:'“\; Š3ĸ0å†@‘āˆ5s.‹SÅ1¯YŪr¸oié„î§ú(Ōãqļ–+{×AģbėK͟ë)ūå€qŒ‰ã›ž÷@šõ¯ĢžĐ~p¯Ņ“ߘ„ĐĻ?ĒĸFp!đūäiįkÆX{—îäÉl_DV{BęiNŸĀÕ_RCt!_‡­UŌ—›öĄs§ĶĄ užÎS–ūŽpOŽŌy9úŪEv¯“õë/ĄėeėĐSoŌ°Œ [°áIž¤õ7•CĀ0 C ,ā~×Âũiš?C¨ŲĖöÖrlŸûæ51å#ÎÂF9¨WAwcƒ{™pÄÛ-ĸų/÷Ģ>ū†>ĻÔÕEšŪI¨ƒ.ĨOĨZmfŌæÁW>ā2ŽķņŋŧūącĖp:žØīŧ^|ØB:ėčę ]OŽsšƒU`酛•8ĖķB´ž_Bļ9Ä ˆĩä`#O~9é×Ūą73;8$¯ˇžõ° =MčŋEĩHßEt-í|Kyā/A~5uõ÷Ō)ŽŸoL=ũ-¨ŋßô‚NžÕLÎ0 C?=Â}ķM‹sžWsŋŗŲC6>DZ4^B¸uøÆ*viŸå=ĖČ'ą0B衆Õŗ œ<ĢÆĖwÎ(ŽhÜ_1ŽŗķH,GÖ;Ņp€ĩĀ˙ țÁĻŽūzę üBâ>HÎķ!Å~Ķž‰ôÃÄ?ōyúr•ŪœžŒzO1ša×˙Ã\t^ Ö&ņˑÕĶuÆūNdå|?ãë˛Č0 CĀ˜Ph&<ŽCÜĮ“ĶׅąâikĘ;ĮĒqk×C §œœoY4fxžÄžđ sŦå' ~ŨZâšĐEį|ûü ŌÚrę×ãho˜l–Ī“¨žĸ4 ž–hFú1ōfâud{)[+į\e‘ĐŠLâ™īˆË†€!`†€!`LbĘŨīÍ668× 8Úņū-ÎrŦåĶT€s­™ķh8f ãäŪ‡§7Üo —YÚ0 CĀ0 CĀ(cŊxÁũ❭ ×Zņꐭō¤ģT†C}2”ąš?ųŒ—GpžŊOāJ§ūVÅ CĀ0 Iˆ€û§9ÚõzéEË-€&;Ķ!Ē‹3î"⯠W—Q›që€RÔÃ,ˇöYõNôŖ$´4Å Ŧcû‰^jíĸ’5Po…W!§ŊO¯Åq×^¨qŌ˓ĪCzAԂ!`†@ ā=}Ĩō%HīŽëÃ5z‘~4‚î7rJú¸Ëũ~0ą‚Ē€M—@úxĐ'\÷PĻ­=Ęa‚#˙2…âķstėĄūhŧôY {ådŗˇŪaæĮ…ß9 ŋE:ū9V´3ܐc.A¯´i‡ŪīĶģCü…”ŋBAãBŸŒļ)Ô{‰ÁDn6ŲRđĮÄėYttˆėF(Č'čT/õöåŸĸËĢuâ.h‡“;|ít˛ē؟#ā=尔e:é5Č­ÆaßËˏÖ~o†6Buĩ7ęEČŪFœ8č Ŗm]tŊ“Oz”/§‹oâNYCĀ0J€×…\ˇBÛš˙¸•\KŋĖõžŌ5zÄnĸØz ÷}đč=čCō?LhķzäEú8ˆîCåäœčŪˇ jĪbĐb÷r–r9Bē×ëŸæŽ,r9Ų`ĸīƒČ7¸$§`ņ…Ŋ´ĨI¸‚ėĨŪLHöÆMŪiŗī_xdÖCBhŦ9g§sū^b'MÖ Ŗ‰JKĄt´2øo |/Įäķ”Å^š˛^ÆĐķŅĸ:J¯,ĩÂ|ô‚fŽ;|Ų5Š?D[HEŸ|3xčø1`v o&õ7’×—Į.‡÷Ōž"âT̈́ËyNCú¤q q7¤0Ī 7ņ}Ę+0ץãFtI:¸!ŗ]zšßá &üĄ×.UˇĢ:yEúą`†ĀdE€kĄ&Kôâũ…bĀ$ĘNŽÛē>7C}…ęÉUO÷Ę7cëFîWŗō°îäcoęŲtÉ!Pu›‰ŌИlY‡zzĸPcĀä`Ą¯,ę]ĒŽBõ ãęŽ/´~žõÂöbsGžõbän†ˇ‘ã.ĻŊ Ãb náŧ*ģ6ŅÃãâlĀ0žß§­7 ´åm;‘•oz%įÁĐB䟌”—6+Ch(ŅāJki3 C`l(ä:XHąíeyˇŽût+5‹4Sy2Û Ÿ ˙Ąøēy>SæXžŽBfVõõf}}1—~ĩ#9}ÕPË^"­%1ÁōHgˆbĘõođŽ0Īįk–ũū0š%đÜ;JJ3)4ž–íĖ'­XáŗÄÕķu¨Ī ũ˛įUĮ•ģ˜˛ųf„/Ŗë2‹ĸüpžrõG>Å=a>i}Ŋōq(°MåČ §Ÿ’ŦRŪՇ˙!ų:Į‰˜6œŊŖ øXރQ0I„ø„Ø{ŋooė8;~{CÆÛ•cíčŋDãKŦcOĮ‚ÃĄ‰rũ¯2÷|Ú HåÍžŒf‚uNŠoZ9ȑև o‡¯cOmå<.Ī¨ģđW$ÄÉÉõMíëqEų:ÚĐōŠ’ôŽ@ŋįsÉ”’÷Ȇ€!`Œwô¯ ×ķb:"gķ5ø—2Sž:Ā éŦ°7ĩGĄ÷āw1sĨŋ’czäĖCĮĘXLœ}ŰĘš6vlCÍ´ąÚAZīí!ÎpÚr4ĨG§Sž "Ŗöቇ-r –2cˇŠ¸ųU¤%n%˙0äÚ´Ŋ žėÆUÄ[‰ŖČđdk;TLŽv6G”hIhŧ58Z3UĻšÕ$÷CŊâҝ °÷=’‹!ŲTč’ĒæöJ ;<›\ ūMi#}ļ,t<ÅôáF"=$v+˛÷*t\ ë°øch_ĮĩūŅßHŦ~ÉÆÍ:n=ôį<Ęj íĐd/‚÷ åՒ‘xRV ÍāXŪ¯Žtšë}=•:Æá_J~3ejëaĘ3}Ų\‘Ēå讂–ęģ,1Â’Ņš-íhéũ´+ükœ,åzxŸ|5įž–e ”k™ąü´*"6Pöm4„q‹,–IC7^Ŧ.Ģo†ĀxD ë`!uÆ#6ŖhsĩÃ̌ļÉÍUŗČoĀ÷œ„hšōūėYC\Y„WžGiĢ9Â6‹ ? î‚ÁÍ?Z‰ōË|9 .¨ÍŠ/į-#Ā× ėQ_ĐĄ°pXVö“€4ãéŌ‹O‹¯XuĨ c§ĪĸĢ^ÆĖ(2īCˇĢNļ ]ÔS9ƒī`×ÅÉqĶXžM™f5ß&ŋ."Wį×rDäJ–õÛĢ*ÄļMž­Ž¨žÆŅ9ĸâËŪÄyŲĢūBģœÂRÆŌ‹mrD^xĀË:ŽØ>™w>ø:äkŦÖųú4ģ,ģŊą"^é߉ā< ŋ:âx¤wAQBß!gqíęá ëyMũĮŠŖ˛ ­P}—TÃį›ãĮÆžmOĮúLd y¯#—J¯ŒžŦ&Šķ:`†Õh†€!`†@qôčEøē9Ī…–A=1å+ËėYœx'ŗ`×ÅäÁsN@V;Đ1—YēÄ­!}¤5s§Y֜ŗįĄ:y%éËsÎy"­5ĢõҊ´ģ žŗų°_îfČŊ,2r˜egҁਗEI6^ķņš}w#úoEõģ˛Ôv6{Áä1Ô2Y:6qœŧ‡fˆÃkƒGŨŪ\ `ķvĘۜ ĮĸÆŊŌåũX„ß!­ãZęDŠŊ(úÄôõõF4ĶŪ.ĶŸŧäaU6 +Æ÷&xî|qbz Ôׯ]ž7Ë9īĘ/€†ē•É‚ūf)˛;`¤ŖĖpÛ;č{C˜WęttPJ­ßô†€!`Å PĢĘܸÛC7îbô\—˛sP5ã֚EQrq΂xC’,: e{NSĄ•GŠ^—īÔ-ÆÉš6{FŠŨÄÍāļpÜÉš[텮€ļ@qã ģüũYūÂ]ũé$­Ühî8Ō2Ŧ4ãˇ"Rą~ŠĮSģ›tDÚņ,möŽt#怏4ÂĻß0 C 4ڇķŨDüb1ŠŠ­Ëlá/˜9ÔÃĀ|ė‰uĀšqwS–Žļ…ŗŌok”Ÿ0į,%T‘U\k´ëŗ––¨ĀŸEž ukĀj%K v2ŊŋõZ>AŨÃÔí,¤~žuÖ"ˇŌNiÍØ|ņˆļ]ĮÖH8ųší^É8_Ë1ĒŲ}-YZ0˜ĖûW%š=Ÿ­ĪcĄ˙Vڙ-§}ÕšQ~ŽŧÆwzŽōBŠjŠ$Ŋš‚0žÉ%Pl™Ŋ„Y,‚Vŧ#P™ĨŲøYÄķb„Îŧž B†įØHˇ4%Įõf¨)TVŗˆƒs‡Öņj'…¤Ą [p"î@Gø%ŽœAįlöŋČ!StQY9ā¯ú—ÚīŪķÛŪ×YЅĩh4Ę@´ļžZáh“´ ļpZįSpÁĻŪx.ÖMėQ:  ų°A8j–$‹ ÷vu´\3ÔĶ‹"Ū/KŨDltjÛ§Ÿ&Ē4‚ÂØr ´h›(Šę8{…§p-iCĻŦ,ŗ†ĶĐɘkßCĐ{¤ß+ÄX9ņZ&2̐úØĸ]Z g°áw˛G1zŊķIkZššßMųĘŪ†ä Ęé\ÆlíŌŪîĒ'"+§zf(īŨ˙Đ÷uēŅít\‹ÜFh¤‚t7C#u˙Õũęiú¤ĨD?V'ĀōÆA/øm"[)^‚ЉŽäk¸ĖHP/Šča*lfBk“VN"‘ī…ÔÖH„”ļ@kÁ|TĐ9„Ī1nĢŅŗ™1Ջ´ģˆunž ¯¤ļ~„BSjã=Hį›^4­+¤!ęΧ^ĩίlõic‘Ž--AĘ&S ~ŌžmfÕq´oĘ9)ļ†úøãc}ˆaŪ=ī§§ T<85Õ˙Ŗį˙ëWÛ˛60> ôQS Ũ î„bŌ6ũMĸ gTĐĢŧL™œzȸ–¸‹(+6Î|°\‰\yīÆįøÄUčĐ6JíîĻčĘ8éömĢĘŠ{6ü^WVHŒũw’¤Oū…4—oKLCō­0ÆrCė× Œ.ĘēĄ?2ÆöYķ#‹€ļ<‹‡åŒõ4šę&˙ËBš¤ūCœßzsw!õŠŖĩŨ\zômøfø:Br0õa¸sˆõâĄÚę‚ŧ€lNã5.¯=^–ērøt; s(“ŽāՂA-é׊č˜-ÁpZõßkúWŌ^ų ¨œ:÷Jt>E?V"i=d”:ČŋÁoW÷+…ôu6ũ¨'­ŲŲn1ķ šÅÕŊīŽh_ōŦŸˇ×īv0Ķ˃å]Š0ÁVĒíŖ=Ũ˙đŊ‰ąÎ¸_ųã{ā}>xĪĄ },I3ŧ:7€ĄÚÖ?-Ōáå÷9#Âiņ×‡–A˙™ÄēŸę߁×UVâ ķíBt6ŅÖŠÄŊäÕN”4TŌīģËõT 0‰(Ч|ŧ›#üŌg-ØĨôړiüî_wŨiļ Q˜˙×]K¨÷qķʁĘDËX˜m4ÍDŨˆTN<3›ĖåkÖúVúV—O˙„‘°ŒĘĸã~HŗOēčĮm‘Ĩmšî‰+LĀĶEã¤§î˛ ØŗÚU6 cH6{ũô1Õ ēašfŅ“ø:XHמņ@9"€#ÖĖų¤ëÕeqöqĖĸlČõ4Nv„yZ:qöčŖHp[ē†kKēbīž™čĘvíM͆ŽgÂŋ0aXG_ŊÆŌŦM–ÖĩÔč%Q–ōĸŲ´ÜkÆÔam^y¨ęsĮ}n~EĒŋ!5Pq˜Ų)Â=Ô˛”ž#SæLI ¤ûSSúĩĨNÅo^¸ŊŽõģ÷v]RŅΆōЁ<ˇ>ūØŽ…ķ˙ú}¯ú@åÔ˙æŋüIđ„sáŨ]lø? 'ĩķ üÔžđ_ŋ˛!Ü kvU3-ši($蝴 xšŽĘl>ę^NæÉx¯¯Ī‹¤ūīáˇø|øÂnyŊÄ$[:ü2Eu*÷ķ˛1›ã믠ũž|4Ōîr˜W@OąAë/oŖd-¤ŲÕlēb+;&6ßEzoĖô@q 2zĢô5!3t/éy”{į$éûá7#ĢãĨŠôJxĪP~%y¯đ6‹GĖCq…Žĩ;āĪäøŊI2Ųû3ÚžˆōōqöJÎ ČË{˜ņŽ‘Ã%E€{Đ øŗsMZ2Ūō7ƒēJÚx6e\Átx6™RķŊ—-ŌõKG~ętëÅK–‘ Č1O3ÖäČ w2šņ–ŒœjĮSŒžĩ %Ėsi^ę\GŲ;á;ŋû×īoûî=]?s2~Ŧ™Ū=ÁÄ&Ôs_?Ģ‹č ˛Ō m 8´ā¯eš%ÍČ-‚xVČ đö ˆø•+"Ŋ%Ÿo†U!aÍfžĮ…7xŠŧūŽy?$“8‰>9ŒÁWļ¤?„÷!IoÉk9ˆÚ °Ŗ\Ÿ•dTĪéƒ4;‘āŊeŒ_œŦ/—×ߖČ> éĄ)q ŪčÃ8;=šŠ×>ęĚ ×_ĒŽ—&­1~Ge’Oíā ãBŽp^ú? Îīę_ũŌ¸#÷2îÜŌėĩvؤFH¯ í-Ĩ!^ä§ëeN“ 7gʖ@˛×Ųīđ4Nnüī"/ûŧ@[úVųŸ•"Ÿa¯ãûąfÕ K"üŧŗ~˙„õŠ|+R'_Ũ&g†€!0ņß7Æd”ŖŸ˛/4|ôé—ŨŒWĒb ōLAŨÛ;ø˛\Åoƒ÷íÛ~ôåĪļmę÷ū jmYų'=nXæ˙¤Ģ‰ģ{õĀĀ”_:ž‹õr&3ė‹ĄåÛn­īt|–ģĖ`Ļ}LčĨžsĄ‹ÉöEʆÍRī ęáÜCÜSAŽÉEКP™–9ė€f†x9“˛‘§´ēY‚J‡(Ɍ€0ė…äĨ!ŊüŠ™đ"ßi+9%õPâ ŋ4õäR(Y m†Ü8^Jz#Ôy~>@B3ãįųŦáĸ4šQ]M<Üø5`Oûp UŽ\+”ÎG6*C=ĩŖãN8gúu ŊLžYW˙7RgnX˜>-'īaž:Îäˆ×…erĨŠå[ĄļlrC’ҏ1†[ąeĻĢCē{Ŋ?HëåŠ'Ņũ{Ęu šĐí'ęˆĶĶWåķŊˆēšíņĘĐĄą¨qåžŊΐĪkŒ6ÚøŅé°Ø0 CĀ(gÆÄyЁū­-++t“ Gæ‘đœëÁ—)+š+ˇūqZŧ=:đtJÅä{˙päK­ažŌSĻČÉéũčHŨ‹Žėģw˙Ë tÔöOę°;™cĩ…œ+o;ׄt9c 嚭ķˆō̠ƐÜh%5k¸ ŌË%ģD4<=Ü8NŌôįĸeČŊíÁņ›–É3Ŋ99xUü¤e&M´÷„ĢK^KEĸã᜝Z'—+ÆŽ´ĘqPßÍ%į—éaãprE‰Đ†œĐž,JÜņ =z¨Wf˙(œOšf ͐Ã3ļēl…îeŒƒc“üaaōŨĄ|/éžPŪKRcĢcåS´ģ:*ãįŗáĸ‡žz(ŠK5ƒläe[†“Ÿŗ‚†€!`cˆĀX9āõSRŽßŪōŠŠÅЊ6ņĒŽë’ÂZÔĀ™š÷“Ž9Šf8S™ŽÚāK{8î!gŪ)ŽđūöîĘ(›2åv¯Ūc2õ¸:EÄ8ŦĪ2“x ŽĮˇpV4ŗŊGd‰¯R‹ÂEČUDhö`Ņčũb׃rt°ã+nÖ÷E- ė>ÉЗš”õRg;qST.W(ג Ŋ|ējeV¸­Ãđ4æáāåiˇ+ĖĖ–FÎÃß9ļ ĪĖUiK3ũ逑;!§Î]nÉH)mtĀĒ°]Öé ÛĢ2ūÍņę9šRÄŊ“Cډ(îĄŨå‘ã˛BĮFD.WļÂĩĐôœ ē´Ģ™î¤Ą Ņą‹æ3t2ūu0ō:V2*ZÆ0 C¯ȝ⅄ Ī!Đzî)}ƒëLûSũ bĘQŨ€™$O¯XúԊÁĨŧhŲ!ž Üxk*œ3ãØáX”žû×ŋՌô<¨{ËĘ?îäŋrũ4ā˜ĀšÔN!7āĐho†¯Ļ‹|éÅ~~Ä#ú2ú0ŽOØ2ė`„sZ´\BT\`bšo'}’3Ĩäô¯C’ 6ļB pƝ‰Tn!ŋ œ,ˇëÉwĶînâa˜ˇ"ԋnɊ÷âa\Eõrã'đ$įãđōMøíČ1ô&Âõ°ķeō}ôĶ= ŠXũŋV% ØąܛBÆī2Ōj'ö"wŒĘ03Iš!ÔĪJÚzÖÕ#=ĮĨķ}Üf9ų,öēbÅMÎß×ÃLK†€!`åŠ@Á7Ûĸ:T‘ÚQ1ZöŨŸŧ_SQŅ?§z…Ö  nÍŖõÚŧlŲĘ:ņÕŧDš Õ7p2÷2#žjjjāˇm÷÷ī`öųŸŖƒ™ÍTÕßÜ^÷}ÉôWTėœÂ. ”ņ—ö˜ŽĖÚvDÉĐAįa¨m>ßéĨōüÁ‘ēĮĄíTé%=“X/Y>ŗåiw3 -AŅn);H÷Ák ۈŒˇļ–˛už°˙šË#÷Îč^ŋ,ßč"5Ŗē€ø†p%ôm'ŋ”~köYŽßâÃalŋ 9íZĄ>)Ė#Ũ‰SåŲ:ČĘûw-’[Ņ''YÛցüé§^.}Ļd„‰Ä+ ^H/áÉQĨ´–ÂŪmØŖņęÖS÷nøĒ?Ōē`íāB2#l!ˇǃÜH†€ŸQy3t…ŸOҝØŌCŋ´UŸÖ´‡ƒfá9ĻŊeÚŲFkœú ¯ ×)(MQqļčāNHØ ƒZČ ØøcÚÕR¤7`l…z¨'[čG°\ČÎōÃņŲę÷w"ŌĄã¯*‹xV6í>Î^ŸÚUå*h<†á‹Lgä_•!rÆ0 CĀ0ʁʹ0äŸTÜđšc5;\Q‡“ŧR/Z~÷']7˛j{ˇŗ§ŋbābœôëŲį›Ũ9RßĢü丟÷iĩb'Ŗøon˙Ę#ŪĶuxĘ@j7áJĻĪ÷ģōíˇũÉ^vNš”—.q8*:Ļsôę#M,e dœ,ÎÃ>näw“-YĀņ؍ŖÂEEZ5ąĨ‡æ:xÎy՞Šr\OŖ…ČՓ֒ÍNžŠ: ä;ŧÄā6E;ü´vĄčqiÅä_GßĘ0/šĻü>ė’ėÚČ(Ļė&øm0õāĄ-ųÎBöÔ°å;áWbĢmŖ´–ü“¤…Qĸ@{/ĸOëɅíáHeí-\Qî9üØr–ūIÉÉëđķ+ųFĮ čh…§>mÄÖĮČ_ +WEãđ$<9ęˇĢNPI¨\mĸįšHQžŲę¯AøfHNpÆĸ÷Į<ĩ s)6ׯ‚ˇš^HA+ąąÃË ūÄņBÅC“zpŖŲčZõĐÆYäͤŋî¤ÁZįÄÉā˛[H×Rۊė/$7ų?-?uėqUŠæ˙yžGŸ~Ō›šķ퓎|ü‡ŽÁp;gĘrôĖ(CĀ0 C`ü `3āãgŦĖŌF€ŲooųÉ_ž77ŖĨéü§”ī|ÖW-3 -c†€!`†@BĖO˜‰OXĒčYŗzuĀ÷ėÜ.ļÂÖÁČ~ CĀ0 CĀ(sĀ ĮÎjN *++ĪŖ;Uß<ãÛŠš/|1ŖgģcÛ§>#đÄ3,c†€!`†@ĖO–‰N\Üî'3gÍÎčä>ú(õú/˙~*ĖžŖGž˜QhCĀ0 CĀ0 @Āđ@ŗ*đyęÕŲ‘õ߯ėŪ‘ęī?*\kŋģ%cÁ0 CĀ0 b0ŧôŦîDA@Û6|îøęÔéß:+ŖO/ˇüũbā Ûō“ d,c†€!`…"`xĄČYŊ ƒÛÎQgū’í§Vfî2Ø˛cÛ•ņ1[~" ,†€!`†@ŅŪ3€Í8"E+4†Ā8Dā?Ëæsš3ˇl?øVę?~÷īĮQÔÃúīųœķĮaßĖä<Đõ/ą˛áqƍë>Œ&¸üŖĩ˛ŋŋ˙ÎŅl3W[6~šĐÉYÖÂĩ9ķŝœâ#[Č=bג•#ÛʄÖ^Vã9ŌHËīōiī7Ą‘ËôOl†l?ø?‚Iījģ¨NėąõÎ]CŦq‘lVš‘Ų°ņˆLnžá–ŸņV:ŠÆŗ’Y€Įü™īēņ6Rf¯!P,8ÖŽŽ˙mÚŠg¤žøĨ/g¨ÛŗcÛQzķ˙aÆė­ŒBËLDēt=œˆŗ>†€!`”Ūģé”× ˜5Ŗ‡Ÿ÷ㄧž3Ģ9ŖQm?øÆk¯jM–ļ\Bܓ!`C Lāx­(SĶÆÔ,–y 8Ęių‰ŗÉÅ6~‰ėqx,ŗK~‰Ž+l[éZļątHäŽËuęŠdöģƒ‚ļŒBˆ€!PsĖ1Ķų†ÁŠ20ÅL0 "0ŧđŦę¸D@/XΕåyžĐúoÛ~0€Ä†€!PfđīÜ\ŖV–™YfŽ!`$DĀđ„€™øøF€ígԃš¯ĨOJ}ĩūĪ2:ŗįŧč-?áæfÛf cCĀ0 CĀ(%š ´`L´ũ vę?E–Ÿt˙ûŋĨÚZč|číëëŗí'Í1y;ĘÃh3įÃw@ Šsĸm7_ Ũ=yw=¯â}–…,™ĢgIĘ^Ž[ƒ_wŨ0ƒ9įp.jr¨—ø9âvCeâ#`3āŒ­‡!p4ŧõßį4Į/?A´ę Uą¤!0ҍÄq{ Įíy·™Đt:¸'îmâډÖŲ‰ÚÆëUúv-ã7Cģ61ĻOOÔžNā~U3nģŋ­GhųˇĄË&pŸ­k>6n‡ÂdB ŽÎΨŦ<&5}†&> nũˇ.„Ÿq-eL<ü›û\fŧOŖw­~›āŋ]˙ɉ×ë‰×#œĩŊŒÕuę™ūÍāÚĩ‹ņ[hã7~ƚ‡¨bíLÆė[Äm˛œ1\G´ŌL¸ˇ,’ØÂ˙ßŪų€IUŪ÷ūœaąxŗ­ÄŽÍzƒÉš’Š)&܊ D ` $QŸÂZÉÃÍUŊFÅ̉ ’ÕTMM#UËōh Fŧ’ĒFRÉ+‰Ü+ÉŽ…ÔĩŽ-i7…¸ģsî÷7œŗžfv†™93gÎ~Ūįy÷ũ˙īķ;ŗįwŪųÍ{H€đ •%å'`ʅ•œ99ûøÁū~gë3OX™žū{ÚB’J@7û§äO×úåۖÚk¤ÄŲC*ŽH†ˇĶ”ųÉVÅwĘĪ ō‚€Ék|FųļKŽ7J1ŋĀâ¸d`<ŲōeuŲ2æ'Īyũü?žø‚søĐĄR>ĖîÛģlf¤’G W;Ļī×õ~‹–6N~Ŧ|p/hNŪr“ˇ"Éî€VՓŗ2SâÆį䑌1}‹1N˛\›3Şžžž\ŲæT!™ė€'AŠŦĄT<÷øÁį9~°T~ÔK}tĄnúüĨÜĢŨļ™Úu;)K6Kâ֜gąŖ”—ų&#OYņ$`ō ~ã9CfđČĐŌqœčå“4ŸŅ˙õ”÷;§´} kj;~đLÚ2tSãøÁ,2$J ] øn)ŨŸ—_ĨŨļ]Zg[BךÔeÖÂėۋĀŲ ÄĻčØî ƒ°!ė‘ĖėŪ4čĖž_ɇ”aßLáL<ÁÂeiīĸ‘ą˙>{ÚĖw2{ëÍ7œ˙÷ĘËö9č•åsY…$ L=ēéŅŌ‚üXŨđīOæR“ģ*_f­ZĄ¤q—dÚĸ˙a+’ģâDŽė^­j†ž…ú’BÛ ŖŠĨ íA sHAH˛ãĢ$K—ĩ ĐÍ)c~rNŽũwpú‰*n•?<؀J@Ŗ‹uß(ĨíU‰=ēé/ŌgÄž īĒ&”@ã/KōęŌ*vH†ûÚîwˇd:[ņŊō¸! Īâ#’[Ģ>wK$KSÆÍmUūÅGĸüM2đ$K—ĩZ™4BĮNœ4%Č˄ĪmŨ” u{2Ģ€’K`ŸNûąSPlÜîäûåŋ-‹9)gĢ4Eķæž)oöā$S“!ŽÁHn÷hĘöͅ}›a›@Ũō¸a@|y¸/Q; 퓉yŽÜļņīÍū;ÅņƒÃũ*–ë7Åרė´ ķ¸Æ&`ŠwWc/Ų+lĀ•õ‘@ÆüdŌ”iYsņ…įžˇŌîˇ}mەUH€ DD<"°t+Åėŋ1?‰•¸˜  @ ŲPĀ“-ßaŋ:˙øÁ–÷œü^§íw/‹‡o~âčG0˛ H@€   €G—ŽëO@?Xú„Íâc9Įžņú/œ×~ūĒ]˙?X11@€Ā°"€>ŦÄ=ü?øņŸĘZüķÛ6iÛũļĀā @€@M €×3ƒÔ‰€Ŋ-.süā™’5…įļn ԘŸ$!@¨ đš`fzđlúogMvūËģŪ58…ˇ}ØŲąåČëįeĸōô`@€ P(ā5€Ėu#9ũäŖįLΚĀî];ƒã÷¨€ŗŗč€ @ j(āQĻ˙ēĐųŪGŽ<7ķžÁyoŋTĮR!@€@­ €×Š4ãԔ€Žœ¨`ļļüNĢ3ö´eŊmãSööK;~ķ“,2$ @¨^E_ ƌQsvü p?8#kė>đO΁×:íÁĶŽܑUH  ß>x XF¤KĐÃ÷Ât:Ŋ8ŌAĘėų• .Íėē OY†iĪ%Āx.Ō‰ ?xöԙYëųáļMAÚĖOúƒ! @¨đZ‘fœZhÖ`SÜTʙ<5{|ûĻõÁ<8~0 A@€@M `‚RSÜ V Áņƒ&N:ęøÁøáŗf˙âøÁZH‚1ęA@×ļ[qã>fØ Žæ'Æųŋ’²,^ģv5ėēŌÜ#"ˀÄĐa\å9ôŦ+/eŧr†ô?™ĶOr_?ŋkįŽĖņƒšî.ųîøM›A€ 0 €)ŗ5Į~ėãŸČZųöÍGŦNTŽųI€ Ԓ x-i3V-Œ×0ĮŒ>ąÅ9í÷?œ5Ūs[žÉœÁņƒYXH@€ Pc(ā5ÎpҐr}žpvÎņƒûģ~îčBŗ=¨ãwF; z‡ @…  €fCIŽĖĩ˙æøÁ&S† $” xB;Œ–ÕŦ_PÛŽ÷Xy;~p’?øąœãŸõíŋUŽũˇ ā @¨Ž!Ŧ{FŽ)ßsÔM‡BGģßôËQgL˜čüæo0ØûÛŋ>ėėúáŗf˙írüā "€ ԉ;āuΰÕ! ›ī—ÔS捖RžĮX¯šæ'vüāĀ@ŋŲÛņƒ=V@€ęEŧ^äˇ*úúúvĢŖëėˇOzwåõˇ8ō…?ĩä {ëÍ7‚øD)ė/Ę_dB€ Z@¯5qÆĢ:™•ÜĻNJŅv?ügeŊũŌûôė žsnf\™ŠLŋģŠŠ)ûđL) @€@ôPĀŖgĖŅč–ųÉ2æĪo[Đ?ПąHɌjģßw}ũÖĀÜōŦ°CGnËTↁŅzĻeĒÂŌ›FŽ9IŋÁøŒúʘ€ Ņgŗ?îU(*‘€ũøü2}›w‹øOđšļ•ØļÕÆû2_Ŧ3ÍmŠęŒ.Vōō Ø5 ÖãĘ€Ę'0J7ŖWuSđnYzˇéĮûŧK.û’÷ŋ1Ęŗ<ų>ų•ęŪNKÁA bēŪÚĩeaŝU؁gŽõb]™˛fõU¯­XŨ \7ø‰jķhļĐo­}ŋō˙ÍûŪŽŽĢŪjyˇ.ÎÆ|]&0Ä ÁŧJåŖzëåw§ÂËäŖ¸[ÕījMģ-4uËÛ.ČÛū¯ŽWyŪ‡9ÕšBå^ĩŧŦ·æ‹h0/ Ģ0ĄŅęį!õĶ6T_ĒŗE~åPuâ\fŦįyVcnúL,´ĩZXūčą  ‹zŽ]Øījnî?îÅģ_yö Å;RJÎ$âôÔŽûĀ—@ؔ¨ųĒgĮv–äė3¤õv†*7+Ŋ_ų+/Ŗ|)([ļã™åTīųĖŗ j˜Æˇ°†Ã–4ÔąÎÍę‹÷ Aį~zaŽF¨>į[ŋęĢ-čOéG5îĢJßvØN¸)ãˇuBáX•ũ‡õĸr ŅR–Uo–üQŸģcĒŽUí |]'RƒÁõYYhkĩ0ī1„*ÜĸyLĢÁ\U'pøĐĄĄN-ŪŽkē=”GtyŌĸt:Ŋx-šØRęwÛTé°_ąÕ÷ÚNˇå)­=Ę N jQܔuģ_´ÉwëķtĄÂŅęįR…™~Äx…ōíĢđÜûJ›ōî•X~ž<Ž|ö°cō \›"]A"'4S#“‡#yîVp0§Ü’cTgŦET§KysÍēZô{GåcTfmí:˜+Åa†d}Ā*ÉíQŊ›fށLΑ?Mēl÷|<2É%Æ•ß$ÎÆuŸ_Įdđ‹‡dĐĢäųúŦšųO“"ØĨvƒ§)/¸FēĪČWrėĩzJįēÜrëÛÆČw­äļ%]!B6āĶ*ė—æ€ Cē™›Ív§Ļf7[Į%ųĨRŦ^”bĩ^7}3oØ/ovŨV~‡‚šē‰QŧĶÚ+=M~ĢŧŨ¨Į*ošBSȝ’2°UņĀ™"ļR Sž7™„åÉÎŪ{p§É#_O’åM*{]˛|ÔŧŕwE¨î(åYū~É|Ĩ_§Séû­ŽÂ9ÖŋÅUļ]m¯UŪT%{%ßÍ [|™ˇŠũ=ō+ŦnāTĄÚ5ézāÁ7€’'43.“X=arPÜĖ(ŸPUûÜØ;.ÖXŗ@Šf>ƒâ{ģęÛįõ“ƒęÚgØ>§ĻŦg>ßžĖ:ÕöQ̧ōí*Î|[eõLFĘëTŲjųŠoTŪ ÖÎĘqŅ(¤€G?2#@€@\\i —Ļw˟ŦIŲÎæŨ69Ĩm—ģCé.Å]_Á6åũ°nÚOČŋǞ- s•<ģÉß ˛VĩģŅúÂUFĀØ› ü^ÚCņÁŽ%‡ķ$Ëeâ~ĢĘO2¯ø"åŨ'ål–Uô•ŦķUv†”¯Sũ:KTtšü8ĨW)l—7ųŸĒ:‹ÕĮųĩ5Å͔{“y§ŧ)íMVלƘĸąžŦ6+؟ÉäO^bdJŗÉô$“ƒ¸Ŗôųb:Įd ôtkČĀâžŗo"&Ģ|FÎoPnhķŦõ­ĖŲōSÔī%VÁŽ•/’ MN'ĢŽ}æ*/üíŠUÅEH`đCShŒ~˙(TN~í4é^0r HÔ7D&õå_hô°\tƒY\¨ųƒvé&lJ—šƒē1wčf|ī‘äŅUŪĻrûÚ|ƒ7e|ŠōLÉû‰”‚ÚŨ› ö‹¤(œŖ0×DáčNÉŠĢÕŅN1˙fĐĄâË%‡‹”%Ģ͒ánÅ÷„ę<ĸü[%ŋVÉoo„’m›â-j7[2?Cņ}žB÷€úîŌ_W^ŗÂ‡Ū*o}´Éã Ëfqĩ‡Yķ]öšQxĒ|ˇüPî™”ėǂƖɰ:’×÷%#ëĶÆąõvģ$Ģo[Zî°ę\%yÎ=’äo-°^ ƌ@ ŪēÂĶ“RĐNį‰[yŋnڗ*ė‘īUüŨØ÷ÉĪWēYéՊ/+`{Ē*¸(ˆų8õģ+ˇoÉtˇĘÆ[žÉDé7¤”™ŠĘJųįÍįļÉI›Ė›Uī*…Ļ\›üW)ėo—7Ķ•ī¨_SÆīą4nhbĩH5&ˆ[§dņŠüzp­ŧÃCĩT쪐rëĢÎ[9yƒ}ę:˜ ˛=9å=ĘīĘÉ#!đáŌ5 !PLáÎZ†nîļŗf;¨ũá?ŦŠĪ(nŠāåR*Lš0[å;­ŽŸÎØĢZW{’ĪeR’_”ŒfK鲨{ŋ`¨™¨^f75wįÕōÕvŦÚÚîę<ųņĖšŨąŖąĖF9ˇ4î=Ā|_ūĨ.ÃŨō—›\ÄĘb :ÕûeÁÂŌ ˛>ˇĨ5ĄVĩ 4UģCúƒ dŌe;¨Wk•ŖäÃ;kvrÆN)?’ŅŽxÄ%LŅX$˙3)á2âU" ļûÔUf§;§Ëņ*ËėzJ~K_#9™vÆI^įņ|Ą”é]j—9˙=Ŧ„Ģ3Ką1ʡ[[åY`Ļ- ėÁĢC~—<.D@L¯Đįā9ÉáAe›ˇĮšŠiģŧ}쉓|ėېI9ˇ*ŋÍdŒĢ vĀkÙQ 4,Ũ”{tsnÖLán’Ō`ʁ) ļ“ŲČQü2ÕąŨOS$öIŠXöĘÛ!oö¨Ģ|[WKâĒO C]ÚÉ™ÜY÷&åļ´īĖÔ!pŋ5Høa…2‰0%ڎČÛĻë`¯d—Ō™ļfį¯xģ|‡ŧ™!eÉ\uSž}ëņ´Ę2qKã ,†”ĢŸ¯xÆļ{°†eę(H Ây•Ä%{ôYĢđvõcŸéVÍce%}ŌöØ  €;3Z@VtŗŪЎč&}HŪŽ'´Īyōí牛‰Éˊß'åáËáŨQåájL@Šîw%‡eļCr1ģâW7Ų,QŲ#6ÉĶ”í*ŗcīļËÛI6O*ī°”æĶŦŽęūDA¯Ōv„)ŨũŠVõLYÛ/˙ŧ•)˙I…Ë⎑€ø_Ē&“ÄōMųíōû•6Ûú¯XWzčŲŖ WĖ­,sD¤åWęė7ęÃN>ēZũÚgÚÆĩĪ´šū#Ŗ&Pĩ'Ǎ'J˙€ P9ŨÔM™>Uū€õĻ›ũQĮÅ)Īv+ˇYš9?}zø„Œ ŽnŪSUe”ŌĪ)ė’Īë‚úy É<&biōËėP[C?}0čD ą)pߖlÎöËM6yûiû–bŗ_n'`˜Ŧ­ŋUōIQ—ō?(™Ķ5Ķ­|sļn×ÁTSÄĨ@ūÄW掔ũ÷€?ˇ ũŅ5†qޏîĐōOņ?CfžõšxY^ ÷„d`ōÉËSu?ÃĄĪwsÉíĩī•7ggöÛ5đ]ųVyË?¨y\­9 ^'ĘÃEH )žé€âGĀŦŽĐ´˛nÔ~žŨƒ›uPu¯nî{ƒ„š‚PĒiAž>sē#Y"Žœzši+6e-ŗãS7Hš‚Ũ$ü0Wųę)ßAÕÃĘ{Újä M‘ė*TH~†€™î<5‹\tåŠū į~žƒęƒ˛ÕƒĶ Ę´|~Pa—Uō=ßBÍåĮâĸ'€=cF€ @ą ÅûA)ávBŅËÚņŪĒIĩČRßjÜŠp<ŽPĀk™! @€@LØYí§KŋĐL‰4'{ŠŌ×ųũFmĨƒ^[ی@€ęMā ”đÁ$ĸ„5"PĩSPūA¯TũŸéô„éô?õŧãk4†)Bā~Ī{¯ÉåĄ#OšEjŋSŧPŋ„ˇv&×wr‰UJZÆÕdSŦ?û,]NΞēöŲē(ūxą6”C€ oUŲŸ¨Wŋä8"[ĸCZîŊƒö¸ézŗÖ3ŽķDŧ—ŸÜŲ™âö ĮšíMĮ™|œÎũ+ũŌųVĪûÁ×]~Šãŧ]håéyīģŲķžņļëžä:ÎĄī8ÎņŌøžŗŲqÖjC~ižáycpŨy×;ÎĸĪ:ÎM×9ÎßkŠ_ܰŪuoÖ-–ũÄu'ty…đŗÅÚQ@€@| Tŧ>/>{ˇÎ‚áy÷Hi›ũĻüGįoāy×%ūP|—žė™Íqœ/žå8Zæ8_øĪTęb _wœ?Ô!Žv\Q^§ƒ@û_zkŲh×íԙUsLžā8éí ךō˜ˇ™%Đąyãtž šë`×VÅíuH7Íu˙õtĮɜd …}ĩĻ~9¸­(ĪIDATd !@ˆ=ŠđíŽ{nŗįũßõŠÔßÛjMšØėēĢãŪŽ{Vė $t‚Ržß7QŠÛ)ĶļD O–åÃkGvPķŽú¤Ķ3tfTķãŽŗü]׎ sžqŨÕmŽķT§įŊ;o#2K&đ+ąũGĮųŧ5ø•ŪBvãĖ*Ĩņŋ:Î{?įē/=ãy§ŸĸW—Ō†:€ ė@Å&(Žķ­˙tŨá%ūģĖP<íĻjˇŽ ŠC¸>ņęø×]îUÛĻ#ūÍķN}ˇ¯‡Ë‚øO]÷Ėßqœ›ōŊÅķNØĒ]ZSÔšĪuīÔĢĶ‚j„eøcĮyîdĪë6–Ÿ’IÉd×ũ™}ƒ´Æu—ü™žąøĒëî ē>ÃķŽüšįMųįTjūu2Uųŧëvŋ!s ‡]wSP‡€ Æ$Pą>ŨuĨÛeģĪxŪ<)ā#dë`oßÂՑĀSžwâŸ;Îô—ëDŲĢĖüĩL%V:ÎÅkįscgĮZ×ũ6ā…¨U–o ļŽEŲđsĮ™jЎõĻßN|¤ĪqNēŅq2Jze#Đ€ ¸¨x<ŧ jį[§6\vĻã<(Ûđī…ˈׇĀŌTę§Kį§ļ›ũ{žwŨ#RĻgKNs]W¤ídhōöN×Ŋ=8)eĻ~”Ųęyg>ä8ŸÖíˇŽnANĨæKŅžŲq.4Åû¯RŠ˙ŖSO>yĸL ɨŌņhŸlzĄ†—ėVž:Ŋ€D'‚ĻWŪSõ{@~ÕgZĢíē …,Ã4ˆį¨Ę¸íܝęy_•ōŨnļ­?Ō0s"]×8Îcē3ˇÆķÆį›MŗëūâĪë ”oĢc;´ú‘í/zd?ž¯ y•0û!ķםiĮGpœ)ķŧ •÷L€ đ@Å ¸)ßã=ī6SūÔķļ­q\ėpšĶņž÷ŊOĨ͟¯YJtæIŖ ü8VĮâíûw×}_` ´ÕYy'ļ ņãÍ aųÎLĨ6ė—Ę ž7ŨzųKlîˇIK@€@Ė Tl‚ržlžužôTÛų>K'5„߸(ûáCų~¤s&‰˜žvTą]f zãâŗvljíŦŽpŨ/Œtœ7e–ō‚-ŌėēŸ—Mør×}öCz‰ŌŎŗékŽ3÷tVxđ-ÆÜtzúa)å3=ī.NB‰îŌĐK’ļü‘ã\ĩMŋŸxŋãü ü-DtŖŌs ¸I\WĨk ›ÄÕüÄֈüŠK:,ËâĩkWÃŽ+ÍmQ0"˛ H ÆUžCĪēōԊđé ˜6 {[ßúœųŧīȏ˖įd“Ŧ¯šî]7yŪō)zyË éô>ļqĒ~á÷ö—<īv%9ō;ÎŲģ]ˇũsŽķ’Ū˜tČNéØžN˙…ÎoŋūˇŌésõõČÛŊŽ{ÚĮéøV*ÅųĶĘÍTek˙üëŽ;õĶžˇ‰‡aĶ5 @ Î*VôŧE…ÖđAŊůPųҰhfģî|ũ°īŦIĨN<1ūåŠÔÎ@ųļŅ¯Õ˙^đŧ—ĻĨRƒr˛*­õŧÖ[õÍãĩžÜl”Ŗ-Ŋ™užåy§ņ°Ãõ@H6Šp”…ø^ fÆđp*uä,öÔŅæūörķš+˜ĢRä7eōyO.žHŌ˛˙>îÕ>AgG2B€ +āąY @ƒx¯Ė‚ô–ŌąŽ{ÜŨŽķDƒ.ƒiC€ P"đAQ Qø€ãŧÜŖoŽ×1‘öƒŲ¨ÆĄ_@€âA<r`Ø/­ÆÂgé€ 0, m<,1°h@€ Ԇ xm83  @€2PĀš @€ PC(ā5„ÍP€ @@ဠ0ŧ ŒŌō[†7‚†]}Ģf>ĻĖŲ›ĖG—Ų–fÕ#`24„GR…˙Ž9rä$jjjšĻ°Ôv…{¤¤.PĀ낝A!ăˆ#•ŋ#ŗaĨŧÎKĨRûž.ŋߏŸWJ[ŋN›ÚŊĒvz!2Ž^¤@ß%9œ­ņĮ+ŧŋ”y¨ŪMĢUˇÕķŧ-JĪ*ĨuâG<~2aF€jA`”nŪi ķk1cT‡€v?'¨§uŽë>)Eėdķˇ<ŋŦØ@M’ûJUb÷ģЈËĨ@O’ü6ëAČžÖ–8Ü:Õ;č{{cõáÛQ-fPĀc&Ļ@ jöÕĩnú/J˜!ßõxô_=’×|ųn)nWŠ×nķŠ_Ŗđ •IrŋAõZŦbu)œĀD˙[3ë(e4ÕM\]Ēk/më—ėTJ;ęď@Ņņ4é+ŽøM›!—ø]Č$~2ąé†ĩ0N/Žįėę3+)_—č&žU7īÛ´úh}fÁ¨åčīī_Ąîīęš7ä:ä=ŨvČÕn‘>“%ûĮï=Ék‰dąÍF6šX¨ôí .Rú…{ũ<3û¤ōfĒÍhÉî‹J7ë3|§Â.y\ōÃڀëaĘ€ P„€nāļ{šQڊTĨ8~ööõõeÍJQ—(ŖE ™™ĸrŖ$wŗ^¤öģĨčĒG~HĄ^,ŒļÕ"J/—læIĻ÷K^į(4ķ”ōķäģõ6øÍ…⛕‡kP…>[t=L€Š@ų.Ψ!jȜhŠ&zŸüÃRȞ.4i)rwHA?(ī›…ę Ŋ’ÕŚÉ$)âˇ(ė0/eüģ q "w\‚žž 5˛@€@âØî¨LÖhatßž´Đũ]ÔvÕ9Cuxø**&ųú†b§”īez`Z"ųîÕC“ŲøãF ¯ž°5˛@€@ĸHAŗ#Íø>)Ö×),¨XK‰[Ļōn)â Î~ˆ9[ymC)īAeš45ŠųČ X…LPj28ƒ@€ pl¤|/ĩāI^ åŲvG *ß~ĪTĮąBí:'y-ŗ'…-zH˛aâF€đ„ ”å@€@r H›ŖÕŨ,šL,°R{Kâ$Ų„wŠŧKõnĖ­'%~šōÖIį„ \8õM7I6öcŲ=’›R´Kņĩ íÜ÷§ę;5F¯&vĀĢI“ž @Ņ0åÛÜRĘŧo/Ø1—yKĸšĸį‚ŠÎ߸Ėn׎÷8ß,ČÎų~LsŗĶm:ä[åq !ĀxBÉ2 ”CĀžęVģ_•Ķ–6u!poĄQ%˟ųe= ÛĨœŋ¤]ÔŧÕUw”ŊŸ*ĪÛˆĖ¨ 4I.{äĪ×@{ƒÁ¤„Q˛œĨsÜ[õMlÃ0 ĸ€7¸™> J ut]%ũŌ6RÆV•Đs¯Õ“/XUe U^°!Q°ī|Į ÚÛN‡”g”“ĸīh`‚ Wz… @€@^(āyą @€ h €GÕ^!@€ — x^,dB€ @ (āŅpĨW@€ ä%€ž ™€ @ˆ† x4\é€ @y  €įÅB& @€ĸ!€ Wz… @€@^(āyą @€ h €GÕ^!@€ —@SŪ\2!@ ! Œ1Âkȉ×pŌŠTja:^\Ã!K ų•Œ*víē O Y†iĪ%Āx.Ō€ @ˆ x„pé€ @š0AÉ%B€@pxú‘M=lWķ[<ō+~ „eYŧvíjØuĨš- FD–‰ĄÃ¸ĘsčYW^Ęxå é€ @%@/!@€ P9đĘŌ @€J&€^2**B€ @ r(ā•3¤@€ ”LŧdTT„ @€@åPĀ+gH€ @(™ xɨ¨@€ Ę  €Wΐ @€ P2đ’QQ€ T•o$¯*ÎÆé ŧqdÅL!T‹@›^˙üP*•ę4¯øú‘#GNŦVįô9ą’Ųãžü^Q¸T#6UmΓßîˇ{YņˊĩĄ<:’Ãō&ƒV“§Âĸʸę­4ųŠŽ}†=ųųŅ͐žŖ$€%]ú† ?Ŗuߎis]÷jůTŧ?NoQ8>~ĶeF9Z}ų5›ė$ÃE*o—"ļ:§^VRås”ņ¤įy;ÕîR‹Ë?€—…ŠĻ Éî"}îVH—hā‡åûK˜ĀÕéõũÎęS%ĻŠ>mÅtŪL €Ę `7{Ũø[ÎRķnŋ‹Íʡđk•˙Å2ēĨIHN—I~͒ĶאmXÉÍîåĻĀĩĘ2Uô'ÅûnĩģW ߍōV°UíėërųU–Ģ9‰’įBÔĩ Ũb3Ü“ÜæŠ^üĩIyŚQC(ā1 S‚ ģaK[ ūÊZŋ”ŗĘ3c’Ũˇ5ŊīËg”oLJ‡š˛™ŠŨ)jÖvĐ)}Á`‚H=ŦÕįnˇ?p‡…RŽWëaiäõu??(˙ EÖXBrģJA‹ęíëëë Ú[€ lļsĨ›ĸÚÁĒۂm/sãÔžOkšŠĖöąođą0n“­`ncšššfIn¯Ē‚&(*›/ß§uQxŋ_‹âįōE)ķŅŧ+ŊŪKĻŦ:•ÎM˛YĒ>ŪÔāŖ‚ HƟ°~•ä%)Ŧ”Y#ą|Úz-Äŧ‘$Į\!TŸ€Ų?nģiūîjõG ĮHHn/Hn´‹Ú*ŋĸĐ ĒĶϞƒĒoļ˙ömĮ2…fGü¤” âbB@ŸA3j‘\fS’üĖäd‡üž °ņ  €7ž Y r Œ‘Rf?žttã˙”SĘp B@2û Ė~SĶ퐒ļŅvJ‡˜z‹Ęî3ķųåÍüÄ~”šdˆ6՞Ā^ šSŪ”nsļ>WžC— (ā &K P*ŲO˛TÕ?,EnēÂŽRÛR/6ėŠWĘô5R¤ģä¯.03ûÁž=d™íxØ­Ķn¸™5˜rŽ‹ ÉąCS9_žY;ávzM“düˆB\‚ €'H˜,€@)´S:EĘØF)_ģžŖ6ŨĨ´ŖNũ H!›¯§rg"Yš mˇô(§úļĢęčĄ+Ģ\Š^“_Ųy\Lč3™Qļ}ķ vMk­<2Љ|Ē5 đj‘¤@A MŠ×zMuƒvÕ>̐{cČ-3KÉÎÎo7ŗ‘1ĄiS|ĸĘļ†ōŖũũũĪ)Ņ#ÅîŠÁLEÔĪ\;ä‡ ųm’? ųlŽĶܘKu €W‡#Ŋ@hēĄwÚQ“˛ff ¸x°ėŗ¤8ß 9š}X2ŊĶWžƒo3,ėPųî`)Râž/ĶŖéĒû%åŲÎĒ)v“å÷uãC@›õ°tP3ęˆĪŦ˜I5  €W“&}Aˆ9)j7Æ|ŠL¯8SÂŋ2Dĩ)֗æ–KŠÛĄ<폘đ_ž4Zrļc q $€ž@Ą˛$@€€ī zxš*ĨžÁX§ėkŧU0ãRđ#ĖR(Q€ DL@&BfĶŋHž[J¸Ŋr—Pė€'T°, € Æ" 3Ą­šņģkÖĖļ뀗C6€ @(“ x™āh@€ r €—C6€ @(“ x™āh@€ r €—C6€ @(“ x™āh@€ r €—C6€ @(“ x™āh@€ rđ"žr¨Ņ€@L Œ1‹éÔb3­T*ĩ0N/ŽÍ„BA~! ĩë*>> 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