From da6a6ffb5b261aa71b9bae5f77070e88fc17e316 Mon Sep 17 00:00:00 2001 From: Paul Landes Date: Sun, 14 Apr 2024 15:09:05 -0500 Subject: [PATCH] new doc push --- .buildinfo | 4 + .nojekyll | 0 CHANGELOG.html | 579 ++++++ CONTRIBUTING.html | 314 +++ LICENSE.html | 257 +++ ...973ec6ef1ddf476f6d66c87fa9e5b841890f24.png | Bin 0 -> 16096 bytes ...c6ef1ddf476f6d66c87fa9e5b841890f24.png.map | 9 + ...1de5f6e4d2753bb404dc09c89f7bf34a48e5c2.png | Bin 0 -> 2375 bytes ...f6e4d2753bb404dc09c89f7bf34a48e5c2.png.map | 3 + ...dc5b16a527babdcdd2309da615fa4d25560728.png | Bin 0 -> 3377 bytes ...16a527babdcdd2309da615fa4d25560728.png.map | 4 + ...853c4c0e15588a9ea928c1eb553ce4dec273d2.png | Bin 0 -> 17714 bytes ...4c0e15588a9ea928c1eb553ce4dec273d2.png.map | 10 + ...92336cf0a65c395e54a43c00d596b8083b5fa3.png | Bin 0 -> 22528 bytes ...6cf0a65c395e54a43c00d596b8083b5fa3.png.map | 11 + ...6dc213e1e1979224b41188516a94c68c9f1e67.png | Bin 0 -> 23583 bytes ...13e1e1979224b41188516a94c68c9f1e67.png.map | 9 + ...8dd1009c7207a9531135c46e5758a8c51c42f9.png | Bin 0 -> 18226 bytes ...009c7207a9531135c46e5758a8c51c42f9.png.map | 9 + ...4d65e1f65eb1564a9d2eb12099f3e704cb95a2.png | Bin 0 -> 10398 bytes ...e1f65eb1564a9d2eb12099f3e704cb95a2.png.map | 6 + ...b73e22d9747748d09a3d35b0dc235420febf11.png | Bin 0 -> 10003 bytes ...22d9747748d09a3d35b0dc235420febf11.png.map | 8 + ...02f5c01c540af5802615d91cc88c2e9c5f3dea.png | Bin 0 -> 2913 bytes ...c01c540af5802615d91cc88c2e9c5f3dea.png.map | 3 + ...733c816e29e4abb64bedaf024f51ca55559cf3.png | Bin 0 -> 24805 bytes ...816e29e4abb64bedaf024f51ca55559cf3.png.map | 10 + _modules/index.html | 251 +++ _modules/zensols/mednlp.html | 265 +++ _modules/zensols/mednlp/app.html | 475 +++++ _modules/zensols/mednlp/cli.html | 276 +++ _modules/zensols/mednlp/ctakes.html | 395 ++++ _modules/zensols/mednlp/cui2vec.html | 298 +++ _modules/zensols/mednlp/domain.html | 285 +++ _modules/zensols/mednlp/entlink.html | 381 ++++ _modules/zensols/mednlp/lib.html | 368 ++++ _modules/zensols/mednlp/parser.html | 314 +++ _modules/zensols/mednlp/resource.html | 504 +++++ _modules/zensols/mednlp/tok.html | 387 ++++ _modules/zensols/mednlp/uts.html | 558 ++++++ _sources/CHANGELOG.md.txt | 176 ++ _sources/CONTRIBUTING.md.txt | 47 + _sources/LICENSE.md.txt | 21 + _sources/api.rst.txt | 8 + _sources/api/zensols.mednlp.rst.txt | 123 ++ _sources/doc/api-usage.md.txt | 336 ++++ _sources/index.md.txt | 166 ++ _sources/top.rst.txt | 20 + _static/basic.css | 925 +++++++++ _static/check-solid.svg | 4 + _static/clipboard.min.js | 7 + _static/copy-button.svg | 5 + _static/copybutton.css | 94 + _static/copybutton.js | 248 +++ _static/copybutton_funcs.js | 73 + _static/debug.css | 69 + _static/doctools.js | 156 ++ _static/documentation_options.js | 13 + _static/file.png | Bin 0 -> 286 bytes _static/graphviz.css | 19 + _static/language_data.js | 199 ++ _static/minus.png | Bin 0 -> 90 bytes _static/plus.png | Bin 0 -> 90 bytes _static/pygments.css | 249 +++ _static/scripts/furo-extensions.js | 0 _static/scripts/furo.js | 3 + _static/scripts/furo.js.LICENSE.txt | 7 + _static/scripts/furo.js.map | 1 + _static/searchtools.js | 574 ++++++ _static/skeleton.css | 296 +++ _static/sphinx_highlight.js | 154 ++ _static/styles/furo-extensions.css | 2 + _static/styles/furo-extensions.css.map | 1 + _static/styles/furo.css | 2 + _static/styles/furo.css.map | 1 + api.html | 281 +++ api/zensols.mednlp.html | 1718 +++++++++++++++++ doc/api-usage.html | 566 ++++++ genindex.html | 891 +++++++++ index.html | 410 ++++ objects.inv | Bin 0 -> 2696 bytes py-modindex.html | 329 ++++ search.html | 250 +++ searchindex.js | 1 + top.html | 310 +++ 85 files changed, 14748 insertions(+) create mode 100644 .buildinfo create mode 100644 .nojekyll create mode 100644 CHANGELOG.html create mode 100644 CONTRIBUTING.html create mode 100644 LICENSE.html create mode 100644 _images/inheritance-08973ec6ef1ddf476f6d66c87fa9e5b841890f24.png create mode 100644 _images/inheritance-08973ec6ef1ddf476f6d66c87fa9e5b841890f24.png.map create mode 100644 _images/inheritance-0e1de5f6e4d2753bb404dc09c89f7bf34a48e5c2.png create mode 100644 _images/inheritance-0e1de5f6e4d2753bb404dc09c89f7bf34a48e5c2.png.map create mode 100644 _images/inheritance-28dc5b16a527babdcdd2309da615fa4d25560728.png create mode 100644 _images/inheritance-28dc5b16a527babdcdd2309da615fa4d25560728.png.map create mode 100644 _images/inheritance-36853c4c0e15588a9ea928c1eb553ce4dec273d2.png create mode 100644 _images/inheritance-36853c4c0e15588a9ea928c1eb553ce4dec273d2.png.map create mode 100644 _images/inheritance-3b92336cf0a65c395e54a43c00d596b8083b5fa3.png create mode 100644 _images/inheritance-3b92336cf0a65c395e54a43c00d596b8083b5fa3.png.map create mode 100644 _images/inheritance-4b6dc213e1e1979224b41188516a94c68c9f1e67.png create mode 100644 _images/inheritance-4b6dc213e1e1979224b41188516a94c68c9f1e67.png.map create mode 100644 _images/inheritance-4f8dd1009c7207a9531135c46e5758a8c51c42f9.png create mode 100644 _images/inheritance-4f8dd1009c7207a9531135c46e5758a8c51c42f9.png.map create mode 100644 _images/inheritance-904d65e1f65eb1564a9d2eb12099f3e704cb95a2.png create mode 100644 _images/inheritance-904d65e1f65eb1564a9d2eb12099f3e704cb95a2.png.map create mode 100644 _images/inheritance-b9b73e22d9747748d09a3d35b0dc235420febf11.png create mode 100644 _images/inheritance-b9b73e22d9747748d09a3d35b0dc235420febf11.png.map create mode 100644 _images/inheritance-d302f5c01c540af5802615d91cc88c2e9c5f3dea.png create mode 100644 _images/inheritance-d302f5c01c540af5802615d91cc88c2e9c5f3dea.png.map create mode 100644 _images/inheritance-d4733c816e29e4abb64bedaf024f51ca55559cf3.png create mode 100644 _images/inheritance-d4733c816e29e4abb64bedaf024f51ca55559cf3.png.map create mode 100644 _modules/index.html create mode 100644 _modules/zensols/mednlp.html create mode 100644 _modules/zensols/mednlp/app.html create mode 100644 _modules/zensols/mednlp/cli.html create mode 100644 _modules/zensols/mednlp/ctakes.html create mode 100644 _modules/zensols/mednlp/cui2vec.html create mode 100644 _modules/zensols/mednlp/domain.html create mode 100644 _modules/zensols/mednlp/entlink.html create mode 100644 _modules/zensols/mednlp/lib.html create mode 100644 _modules/zensols/mednlp/parser.html create mode 100644 _modules/zensols/mednlp/resource.html create mode 100644 _modules/zensols/mednlp/tok.html create mode 100644 _modules/zensols/mednlp/uts.html create mode 100644 _sources/CHANGELOG.md.txt create mode 100644 _sources/CONTRIBUTING.md.txt create mode 100644 _sources/LICENSE.md.txt create mode 100644 _sources/api.rst.txt create mode 100644 _sources/api/zensols.mednlp.rst.txt create mode 100644 _sources/doc/api-usage.md.txt create mode 100644 _sources/index.md.txt create mode 100644 _sources/top.rst.txt create mode 100644 _static/basic.css create mode 100644 _static/check-solid.svg create mode 100644 _static/clipboard.min.js create mode 100644 _static/copy-button.svg create mode 100644 _static/copybutton.css create mode 100644 _static/copybutton.js create mode 100644 _static/copybutton_funcs.js create mode 100644 _static/debug.css create mode 100644 _static/doctools.js create mode 100644 _static/documentation_options.js create mode 100644 _static/file.png create mode 100644 _static/graphviz.css create mode 100644 _static/language_data.js create mode 100644 _static/minus.png create mode 100644 _static/plus.png create mode 100644 _static/pygments.css create mode 100644 _static/scripts/furo-extensions.js create mode 100644 _static/scripts/furo.js create mode 100644 _static/scripts/furo.js.LICENSE.txt create mode 100644 _static/scripts/furo.js.map create mode 100644 _static/searchtools.js create mode 100644 _static/skeleton.css create mode 100644 _static/sphinx_highlight.js create mode 100644 _static/styles/furo-extensions.css create mode 100644 _static/styles/furo-extensions.css.map create mode 100644 _static/styles/furo.css create mode 100644 _static/styles/furo.css.map create mode 100644 api.html create mode 100644 api/zensols.mednlp.html create mode 100644 doc/api-usage.html create mode 100644 genindex.html create mode 100644 index.html create mode 100644 objects.inv create mode 100644 py-modindex.html create mode 100644 search.html create mode 100644 searchindex.js create mode 100644 top.html diff --git a/.buildinfo b/.buildinfo new file mode 100644 index 0000000..42ae373 --- /dev/null +++ b/.buildinfo @@ -0,0 +1,4 @@ +# Sphinx build info version 1 +# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. +config: 2ede4f641b32fd3f5de96bd9dfeb95d2 +tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/CHANGELOG.html b/CHANGELOG.html new file mode 100644 index 0000000..99758c3 --- /dev/null +++ b/CHANGELOG.html @@ -0,0 +1,579 @@ + + + + + + + + + Change Log - Medical natural language parsing and utility library 1.7.0 documentation + + + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark mode + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+ +
+ +
+ +
+
+
+

Change Log#

+

All notable changes to this project will be documented in this file.

+

The format is based on Keep a Changelog +and this project adheres to Semantic Versioning.

+
+

[Unreleased]#

+
+
+

[1.7.0] - 2024-04-14#

+
+

Removed#

+
    +
  • Deprecated feature document parser mednlp_combine_medfirst_doc_parser from +resource library.

  • +
+
+
+

Changed#

+
    +
  • Renamed MeddicalFeatureDocumentParser to MedCatFeatureDocumentParser +since the parser only adds MedCAT CUIs.

  • +
  • Fixed the configured parser that adds both ScispaCy biomed entities and +MedCAT CUIs in mednlp_combine_biomed_doc_parser.

  • +
  • Separated combiner with non-combiner models in configuration space. The +non-combiners (biomed ScispaCy and MedCAT) are parses that are used in their +respective combiners. These are then used by a new composite parser that +uses both (mednlp_combine_biomed_doc_parser). Unit test case added for +all configured parsers.

  • +
+
+
+
+

[1.6.0] - 2024-02-27#

+
+

Added#

+
    +
  • Added a ScispaCy biomedical document parser, which is enabled by setting +mednlp_default:doc_parser = mednlp_combine_biomed_doc_parser. This uses +better linguistic features and detects more entity span(s).

  • +
+
+
+

Changed#

+
    +
  • Fixed normalize token and sentence/token indexes from being clobbered by +combined medical parsers.

  • +
  • Fixed numeric entity feature from the combined medical parser is non-zero +for medical entities.

  • +
  • Combined medical parser defines linguistic and medical features in the +document parser object instance.

  • +
+
+
+
+

[1.5.0] - 2023-12-05#

+
+

Changed#

+
    +
  • Upgrade libraries: numpy, lxml, scikitlearn, scipy, medcat.

  • +
  • Added scispacy dependency.

  • +
+
+
+

Added#

+
    +
  • Support for Python 3.11.

  • +
+
+
+

Removed#

+
    +
  • Support for Python 3.9.

  • +
+
+
+
+

[1.4.1] - 2023-09-08#

+
+

Changed#

+
    +
  • Fix unit tests using the model provided for the MedCAT tutorials.

  • +
  • Re-enable GitHub workflow unit tests CI.

  • +
+
+
+
+

[1.4.0] - 2023-08-16#

+

Downstream moderate risk update release.

+
+

Changed#

+
    +
  • Switch order of medical specific and general spaCy language parsing and +chunking. Now the medical parser is the source parser and the default spaCy +parser is the target in MappingCombinerFeatureDocumentParser. This was +done to get better sentence chunking as MedCAT (used in the medical parser) +does not sentence chunk well as it was not designed for it.

  • +
  • Upgrade to zensols.util 1.13.0

  • +
  • Fix cui2vec weight archive re-download on each access.

  • +
  • Mapping combiner default is to use token’s character absolute index.

  • +
+
+
+
+

[1.3.2] - 2023-06-29#

+
+

Added#

+
    +
  • Feature document parser shortcut from application factory.

  • +
+
+
+

Changed#

+
    +
  • Resource library configuration to clean up model resources after download.

  • +
+
+
+
+

[1.3.1] - 2023-06-27#

+
+

Changed#

+
    +
  • Updated to zensols.nlp to 1.7.2 and use its new feature to auto load any +missing spaCy base model(s).

  • +
  • Remove configuration file requirement from the CLI.

  • +
  • Fix spaCy dependency requirement to align with scipy and zensols.nlp.

  • +
+
+
+
+

[1.3.0] - 2023-06-20#

+
+

Changed#

+
    +
  • Fix contraction tokenization. This was done by swapping the target and +source parser in the lang.conf:mednlp_combine_doc_parser resource library +configuration.

  • +
+
+
+
+

[1.2.0] - 2023-06-09#

+
+

Changed#

+
    +
  • Upgrade to medcat 1.7.0.

  • +
  • Better UTS error handling with raised exceptions and messages.

  • +
+
+
+
+

[1.1.0] - 2023-04-05#

+
+

Changed#

+ +
+
+
+

[1.0.0] - 2023-02-02#

+
+

Changed#

+ +
+
+
+

[0.1.1] - 2022-10-02#

+
+

Removed#

+
    +
  • Make scispacy optional and remove the dependency. See the test cases in +test.

  • +
+
+
+
+

[0.1.0] - 2022-10-01#

+
+

Added#

+
    +
  • Unit test in GitHub CI.

  • +
+
+
+

Changed#

+
    +
  • Upgrade to spaCy 2.2, MedCAT 3.0, zensols.nlp 1.4.0.

  • +
  • Make cui2vec a standard word embedding with vectorizer and layer.

  • +
  • Replace entity splitter by not embedding named entities as default +configuration.

  • +
+
+
+

Removed#

+
    +
  • Support for Python 3.7, 3.8 from dropped support in zensols.util.

  • +
+
+
+
+

[0.0.2] - 2022-05-04#

+
+

Added#

+
    +
  • A CSV features dump example.

  • +
+
+
+

Changed#

+
    +
  • Use token instead of sentence level mapping in cases where MedCAT creates +unaligned sentence boundaries.

  • +
  • Make medical parser stand-alone and use delegate mapping combiner instead of +using a class hierarchy.

  • +
  • Entity linker is now a token decorator.

  • +
  • Protect against unk (unknown) keys in TUIs.

  • +
+
+
+
+

[0.0.1] - 2022-01-30#

+
+

Added#

+
    +
  • Initial version.

  • +
+

[Unreleased]: https://github.com/Paul Landes/mednlp/compare/v1.7.0…HEAD +[1.7.0]: https://github.com/Paul Landes/mednlp/compare/v1.6.0…v1.7.0 +[1.6.0]: https://github.com/Paul Landes/mednlp/compare/v1.5.0…v1.6.0 +[1.5.0]: https://github.com/Paul Landes/mednlp/compare/v1.4.1…v1.5.0 +[1.4.1]: https://github.com/Paul Landes/mednlp/compare/v1.4.0…v1.4.1 +[1.4.0]: https://github.com/Paul Landes/mednlp/compare/v1.3.2…v1.4.0 +[1.3.2]: https://github.com/Paul Landes/mednlp/compare/v1.3.1…v1.3.2 +[1.3.1]: https://github.com/Paul Landes/mednlp/compare/v1.3.0…v1.3.1 +[1.3.0]: https://github.com/Paul Landes/mednlp/compare/v1.2.0…v1.3.0 +[1.2.0]: https://github.com/Paul Landes/mednlp/compare/v1.1.0…v1.2.0 +[1.1.0]: https://github.com/Paul Landes/mednlp/compare/v1.0.0…v1.1.0 +[1.0.0]: https://github.com/Paul Landes/mednlp/compare/v0.1.1…v1.0.0 +[0.1.1]: https://github.com/Paul Landes/mednlp/compare/v0.1.0…v0.1.1 +[0.1.0]: https://github.com/Paul Landes/mednlp/compare/v0.0.2…v0.1.0 +[0.0.2]: https://github.com/Paul Landes/mednlp/compare/v0.0.1…v0.0.2 +[0.0.1]: https://github.com/Paul Landes/mednlp/compare/v0.0.0…v0.0.1

+
+
+
+ +
+
+
+ + +
+
+ + Made with Sphinx and @pradyunsg's + + Furo + +
+
+ +
+
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/CONTRIBUTING.html b/CONTRIBUTING.html new file mode 100644 index 0000000..039c5f8 --- /dev/null +++ b/CONTRIBUTING.html @@ -0,0 +1,314 @@ + + + + + + + + + Contributing - Medical natural language parsing and utility library 1.7.0 documentation + + + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark mode + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+ +
+ +
+ +
+
+
+

Contributing#

+

If you discover issues, have ideas for improvements or new features, please +report them to the issue tracker of the repository or submit a pull +request. Please, try to follow these guidelines when you do so.

+
+

Issue reporting#

+
    +
  • Check that the issue has not already been reported.

  • +
  • Check that the issue has not already been fixed in the latest code +(a.k.a. master).

  • +
  • Be clear, concise and precise in your description of the problem.

  • +
  • Open an issue with a descriptive title and a summary in grammatically correct, +complete sentences.

  • +
  • Mention your mednlp Python dependency version and operating +system.

  • +
  • Include any relevant code to the issue summary.

  • +
+
+

Reporting bugs#

+

Adding information like the backtrace and the REPL messages to the bug report +makes it easier to track down bugs. Some steps to reproduce a bug reliably +would also make a huge difference.

+
+
+
+

Pull requests#

+ +
+
+ +
+
+ +
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/LICENSE.html b/LICENSE.html new file mode 100644 index 0000000..27a7d0e --- /dev/null +++ b/LICENSE.html @@ -0,0 +1,257 @@ + + + + + + + + + <no title> - Medical natural language parsing and utility library 1.7.0 documentation + + + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark mode + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+ +
+ +
+ +
+
+

MIT License

+

Copyright (c) 2021 - 2023 Paul Landes

+

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.

+ +
+
+
+ + +
+
+ + Made with Sphinx and @pradyunsg's + + Furo + +
+
+ +
+
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/_images/inheritance-08973ec6ef1ddf476f6d66c87fa9e5b841890f24.png b/_images/inheritance-08973ec6ef1ddf476f6d66c87fa9e5b841890f24.png new file mode 100644 index 0000000000000000000000000000000000000000..9cf71fb377368806c9fb329c3b5f1bdf1a668fc0 GIT binary patch literal 16096 zcmdsecU05cwr&swr79vdf*>LyQX>QiARVL%NRi%q?;S-zL_kD(6Ht&YAiaw;0|cZ? zuc5a{Z*PU|KIhzf-+67kKi+uRdymZ^VP&nkX8Gnfzh9`bqBJo94FLoKA(oYqP=P>j z@gWeL8<+9GZ^}jzcESJfjO3&xAZOTrKGbDLLm;;xvJ#?d9?7fY-pNqcl#Mfur>?F_ zd-5pxch5V5m|ieFqN2!r876wM*W|_=Gm>oV##UQXCiJ~4rH14a_s|N-A^Z?i(QYrn zyeCz!$*KrgFEfbVk|%3tz;kNol+W_c;usjv-8s7b&=~d6Jj?uNbZTmCS|mQHf`Y<8 zws&q`!$$7Lp0<>nTv+5Q>}k)bll;tZ_?xd|6)38~Z?8eU1AgUcP4Wi{0H_t2lt>SK(vJ6c8 zOu@Z|ec`yE4fUI%L_LFpSq`^O%I(H;%lj_EbiC!pUrS(L7nR{x zvELIF@a?aIK>nPAtS4Yje2%QG>4G0MNi@$JTt7c2VbrmhVIw!`A{UWc0<*O^?wOjj z7u|-N?w~fs%8RtJc}y~=1?{JNq7d1b&!Q4@$}~&09-Q4B^RG(Id^Bv)mF!%T=yY*4 zWcoy!%%|k{C}*GaMtj>N#nRt9E6&dRiN0yxx35jaqIinlxREy2MP1h0Jj?c?3)}Wo z>_itoca3d9mUj&@>|VLL;~Tr+)X72q>OfeVEo|}^uf67Az|KSx(hw)|Vp_wqV+1P{ zdLc|FPSD$H_v|(udR=BN!1c7%ao>ZPXO5Rwo8oM^r;*->kC4qceUPa)N5Ht_5UraWty;>CGP65z*~WfIrK&aHKN_AEONY z(gi#45$Jm1le!}go6si=@^I`yrGB*~Xe3^O$vN@Y>ud0>3hS4$oxYR(S!%EC`b<(xx2jwcZDU&=^s>-9 zCBH)s1&Mw2q8rNYtG%6ecj3t9a?rjW?8kBj>9q2wE}v|}zNCrE4Xr3H z3=X7AwYt0KE1Bug{wAfz3cQ4kLtfrm^aPHoy@#bjk*F+{+6QUo6_(p3@XobfmX@I{ z8s9Y3$3C_nXv=NHV2Ie*wQv#(ow0buXfywYd6Wrv?YOIKJac4l^RYDSHNH+#$NT(w z;7NUmNPjjj--6V(8zXGy)$BXhJNXf!q8h;mfP~m{&qx1 zv`b^U-PYterr{H(nU!jC%L>RDdKwyw-@fv@kw-d-$ZI>kr{)OZTU5W7xQR~F)1ONg z3eFLUd=ieF_3h@@T-N9A7G8y~@(|Hg*aGj#6K7y|h0;iQhAsJ4K_S}r;pzs(E87fh z(}k&Y&x9iHM2HV^2J86Ar#F@!?K)P?e&Hpb>2-)N_es@vcgGY@O+oEy7#WpMJ-2iV zky=evMZ>ZO2=pZlh&NH7i-X@1ufpz)6zeQcI;GXoHf^Cuv~AKX;idX_kGJO4a{W^> z5s{N?qiuJ2s7qEGXy6UXQ|aq=?9R!jI}_B&L+Z?A$xGGw<+fQ~bbOyw*f!RJ46XQ^ zT|Je0S1fjY*JYXZU6`BG9)wZaB;}QA(QT}odG&aB>a(}m*VOR$xsDiUAuLC0%ao^D z)n0Gl89)3nu3v>-chbw0dEm_iZN;x2A4N`@mqM3_-_+h;Ual-x(L}j|y_-n)GYvIZCLj%35N3}v}A_kB2>NcD; z*jl$gaIA~qu2ff7_B-xBc5q$OSGgCljbOL1ni=_kvuWR07kZIzZ+BZ0K6V!5t&`q3 zWK?`w-}z(`T`@lHGwfJdoNhS$?DhDszGiL0OWn41X5tQ?HX|(Fqd|ZIgRRUs+R0>h zK;#k=7YPbA{YdszH7F?sNrqZyO*b~mQ&US&Xiq`+MYY$5&?&+a!pxy`Z2b;{BQV(ifHu zekvM6XY&`1j+T9eC^EbEstkH=)TfuLuT-Z!`1 zDs?L8p)X7wiD_C`I5p_0I0Jrs^(at@U&?#E@Vhm6Li>8S1l)C5?X;{uH9JtyNyD7` zm@#2aBUIvST#(jc7eRKzrde$^nTMj8RLC?_FV8ax2aJi9^!o zVce+8-hmvr9GdQ7mfJ@eO#JWiX18$g#~lO$N5rI_P3#c`?~+Y_v}bE7mUJd2#X5*j z_)aZc-CHg>q#PeTBsICQp~BgR(M*sb(kx=%pApJhtSxVYIpWzC@$9>Q-%EevJTWHr zymBLM^~GjL#?VQ%DOVq+k~L|*(kIn8kPz`ev}@kaGjexKH*|=FTRmuMpJtu=v+mak zOG;J7_4c;wrNyLDpBmHRa*5f417(XxxBWKbertZccm%bC4n7@WLwb7` z2WW^h3Fnbenqc8u9b=^UbYq_;$z3iplZ>k;O!N`M^)|hz7Uz+N!g9D0?a>uL~Y@q@%o(i)-^S%CC?# zwD>L$p3z(kbUM1YGWMYC<3_CmfM=O^{DfQfEVpX86o%Gm@Q|wab`zP+AM(ZNpd-t> z9wrd+5MKIXt49>1*?KBVUeEj8`69^i!ciSxnqksJ_ycfoFRjowffOiXS@t$(am4FU z^Rrj`uQvAf8d4VZD%iks8gP<8(v$mvLmxP(u2KKc><$|bZgN2xTlGHuej5>4E~+Z9 z_t3jJb2YH0t7OQhYfEgmVz7)}Y+)%mcd zENG&dFQ%5P;WQakql-B`*3>s^Zm%h!?Q-s*hIu0l9c%@JK6opnt^Bl}igC5Z?nc5W08WI0xeTBK%DIl+)PyP5 zTANIpWcMkXp|v7;d9%STiT6uKZ~_%rr)Lw0D9IH<9uEJIF?lpO=mZaL(pXukD##A; zCc9F9JuHfoh}{t?9NV>>SuG&sp&$_qi^a^$PICgN?0zSDj5?fq0JAT_8)i+V{9AI^ zeQ)PbKDh0hp`V^va}#d8ramh2ovdG?{C2qJ8^t~oOZGN!A$wAc2%#U&ru=0-RrRT7 zip&RE+#T*lTsBjg2}Gd!o72(^8)(fD1)4@QBSqsff<$jKQPi$WO`6+NAN5sfTghfy zIw>H!_o8L#9y56$pbG%;Wf?5Ax>vDB1~ngVaFr+7g|=ueR{PwEnel0x<{w=kMj9!m zH@?$}z?NIu@LiFVtZ67N5nU5x~Ro8mIzvsw=4)39N4KocFfI^NDWDJC3$&VeC zINzFCbAC4W^c6~M6O#z$1~wyIS%#M@Pi+7&<)m8@+_bl{JZzTeQ?RIYsUcLtgCS{N ztj!SEXFPM?Nr#%mSv@*!Sm|$u!as-G`MkZWMBs6| zRCxNiMQHq{fwsI^N8!{C2Mr}silZYVe?AIhfc?S%)YgephargDtb`l75WNN0 zFe~-xajZlnwcujm*^TfL1ch<_O~Q&q<_&whg_Rs~kne}gxT#hf4(oeMD%t7K=7GZE z-ty56wpKHk9CA6Qs#9}{{nduPt)iWsOoHK%nPlfLf!X1)u@otkQeSLFxPSfn1*zO> zrOngn`rfnb`O>!`gVYpPv)+dvvbMGcddiH9QHrP^p!YNeO(Wz_lj%6F|9vlE#nYyo zEx6|W&&4D1fPSi@RmB|yKwq}8u05p%G1^EMRA$qjXLHG8;WF1;^plMvo6>wk+O7F@ z``YgD7LuJUhU=<#>QM^2sH`$1tPOVAlZm(<1)2svw!D1Hp%tIBgj8v!{Z6{o{Q#?E z=R+dQUX`{xiHW>h+#(MfBR3tinPp{K7=P4bk-H1W_n1_ws%(|U&|f!8R^!rMwsS>4 z87b?oL6M3Vao|p7<#O6XqEnqFFA!|^Lb}QRW0(DC`q6nG3kmwGU%v9E{l5Ic;nw*l zaTH7i9{ZvML6;lPW3E^x_x6fZ+e>n58ymIJoK_a-8=a&#WeRP>5#G$mi|71RtAd?qUUC9_6|uU&4FURPK5 zW8hW0Nt6sz|J3y;!<_LaHT?xL|C1yAIV%o1$@vGF{yyv9J^aN#Z~Xh_{!iC`?Qip4 z_|NRc-*5bTyZ*nhiRa2?gl~Ub0XO@_VkFb8sA=X4D zMyl|5lfvp{){Nf808_a*_Di>-J2u1ZeKKiGLjhkSL#$kpAYJDlo+=S?GHD`TV}D0Q_K@JrXXe-Tr}t z)c2gf;WJqzCW&<*mHXr*%;_rQA$ooX^Q>y;K2>J&vrD~ya8P}XjQM41D&5Q$90I&S zK7l*vo@$m_RqisaU1K@eTKjajg{5VJny*4EhrZq#naPD3Y~1UB;K$*vu(L!f}Dqx?$i@8>omgHT>oZ)0c zt^J#(=~Llkm!Gl&?4sbkkIeGF9Bz3nCSX^669Enti*;e_*S+e$N-RTxHN&+>?j#WS zoiy~K7*2qGP_qef=A0LgP-IIXVdD>n`2MRa;e&$Po)P!RD>=y0A#9y!{&Ho5r!3zo zkgNxS<~8i9h{&Vc*B}&C?Dku=*H#4h)6D%gpB;HTiZuL!glW;zc*p?q1kgz*Dy#>7 z*zcBcWr0Hc)4H4_c?koqM`8?mrs!6e<%3tasUZ{!31U{Sf{!e8qq?@EY+~`74GDsq zOh?!>%u8#@3ARob%(n0Nq5GC#_DJ+ma}#Lz{a4`0++w=^vv41T2k|G65l@TLpNkCh zLMRuUK&_A5r}>vJChx$ZoOim@{2T``7yC_TBIJOja@G~-YG^aMF}ZtA!mN|UKN{QP zA9p=a6X+s+C)=IM9b~(p@0Bv%)8o*n8)PIXzoU$Gq8wKOe92UT5Knt{>CQb_(q%^H z*DKtk7){gBWD1uDH1;W39U`Cl5Kxj*!}#C>%~}eAt(gzD!j_}t0kl6YW9OWF8Vc7r zRfT;pBS|TEH zC;h6sL?%A$vl0t=C0o+G&5&uN<+gvw; zsjjK&bUYkNTJuEojCW1KNg66_%M+L@_I685dQJ~Pe}2kCGhejrDXc5iaQ4_kUl}>b z6+y4LS`D^SnU@`+jxr9Nef#hYLma&-%jRmmug*Fgn85=E&3cfA9s=Dc8x=nHnA`cz zoLolAWKvH`Z|JVWajPjFxamgxMy!WP;K4MHj^$Weo}{*2Qcyt9>Uf^gf zWP_Te89Hcihh9n^80%dB5P8bMc+Kx&C+2cJU05+JZWh?O;Yx^>e)|1+W`EgWyq+0P zz$~yK%^ZmW@tdb{R{XhAvaDaV?)Q~N9Wk2I|geyrPxHF_r| zc?w--nta_-$&oOZmqQ*o;^wbRZ3PGrkWr{XW7iJaH`jWRDXY_#i01q0%Ko!un$a5= z(KbT`mVSC7gR`lHwEh9EJHzUk8`#Ua;p%$Q1le_;^~_z|!*eDI1UyhJW{$_ME^O-5?|kq}^;mntNi2 zSoP1SYL;OR%Ue8%{)Q@LoEeqMVG0=B)WFC|bkwaj)mo*Bdx;%cY?E?SVJf`rSs zhpUMPV&+A6AAcO@Gv?&fmExlBK40u*s-j@AWS-7j=!fZ7E2J}zi{yiqvirtVo{xml zy^?BVlhpIR~7@~5g1ZD8D8bL4l%(sV0Z`2EuBJx;|ISU0*sXqgqkptB`VS3Tru-QyOWIM^}l($KLL>UpBUp}IvtJlUPouXGzqr8Ih(Q?p`2EY#5$1h1$Xi;cC2rG5se}?~X z+y3#V;h!TN357_#K9fAl?TQGVTTuf?!t_)|>H}H@C(gbgQO{B%ekrkq9WJ| z%m_&gA8g~HW@kx^b^Q_(b=jZASze63jdS11SEyp=w}TR66KG1vf}8F{G`E##`Kf7S z;}>B|8C1;5lX&|1Ehy-Zm#8x2o}|T0b85hNx4YC{r!hx2o(2HCzCgIMbFv}?+A`Wm z3@L~=MW)T?AIb3o-uhqc#IF5eCoQ)e$!B@_g(ph|_y_70ivm zS}NC>ZQ&MHRv9@tIloM?W%E@(k?VajY#>~NyaD|~!!Jph`LeRI9rwIv#YUGvv+%U= z9KSGE*0`eofayPf@n45R|DWJSod0{^6(Tmk{)jcZurR{C;b{%j<={dOeu%kj(Wic{b}15~OIV!QqJ(_F=K zd?w&}Khb2cP*-pcg~g2Gn&%j=pC%|!)~wQD^T+)n=(oq%PktO^fy#5Iq@>u4mahfO zkCxkJmX?+hoMS?n0%dso%g3(tDshb1`=<^CVD2~g+bK`Ivj*SEY=Adk|MkfSJvNee zYB4DrChKEW1MIUOKK5DC(9j&7oq3{DH0EEEbSK%@chH~hw}Ck&@H6h5d(iis?&~_B z56#t;YUv*uQhGg!eY=V9Ve2e0K)^@@K|}64M4`&bk%9?qCflL);r+f;|d$F8aYp@dui} zUU>>o3)xx%&M8SqKw5)&+a{P7cm|1Sjn?y#rEf%;sR~6(9w6>6V1^q7kTa&EW-A|j z%K^AlZr#-gZvIG=nJ<9dhl!Ia{PWu|Xo!+2ck*OE%mluVc1Z+lfte(h(n{@kU$&+n z+zgnLQr)Ah;&Y#aX`ZWjs-*42t>((lhz?B<&r|mJIw2sZUKsTm#CC|0gXKj(Y!qqd80< z%K-9a*cGgWrBMJAMb^e(O(o7QlBGN~;(Js%=BPAj_N%>_U(Z!I0_FpUiznH;t6hMh__UCJuEf@5jJ(1+nN`F$(IABJD4;#dmSAn~Enxa_ipCcr z$%Xdwb?7!N;GI_v5JRyZxlY4-TCrU-xDCW1()!0Nb#_{-4;2a>pm^?@)!gwpGUp`E z$KB7DG#CITdAJ3iRATmIiJAiA**Gx<(A4ng#{JDsijv;DKLjRTgt~D>A61e!;0W{4 zT0^bQd*qt$kdA=ScF%mWOH|FXwIv@+2sM9I@a>1pOOf~d%;}NFR6by!GV0}3c{gfG zu$byf^AuUzwDAa=NN!$V;L!Pz-ad1<@%7*HeE0dsE%?j-Gtu`52TaCwC?heYWbSvm zk@Lah>l8>DS2$$flSz!Cl0Rl9RU)IIRXg)Z?9dR;?{-3Lic9zmoMg3L=H(INHThlC za48aoql56sHssA4K==O4geuqi;4)a}w7_|#jKQ3W^E zdSWhzG@l@SCbMg5lIpzo-XZ5(zm7jUO2THle#XpCpIVCCu5r64I1^%A;9-iABcSp)?wQt-f2bPQ?TQ4^%9@fa!4SF+v zFFX00W7|#qMpY%DmhKe6Je_MEscnV2H6Eu2=%v-wkDopT9UgiSHGcpSQ2g^kTc zgXR95WTzkZI+OS!)mSB^rKMx?u=tXH^&YgpBu6#7#AQi^sM(-7nD}@}$v@XkR(yGJ z(V#2wq0O(ap$iLUS&AtHk5pB!-%a!3CZu3%bkzl@FK{C-8*3Js8!TWIo^E)caRfhJ zz}-}j7ed~CgcgU{j`D{t@+WwO8^%d&NQfTHR0{jwKVe0#JC8jv!JtnMKp| zWved;*+10R)3rHPo}C_TA&P$8iFptu^TloL8L!Q7fbA%t){Lm|h>3~WG`|D{5mH|G z;*9Q2^wPyT$wT?+59(dX{L#Sl_yHPvo4XHaTL)S?{j>N@aG^VG= z2W-#2Uhad96dQ+#owHZ_D)7%w!zek-0O){V;g5@}>(i=?!05-HG#mVcIO_Kqvn;ud zTZ!cRn4l!itHo_j%Q@L(Bq1YZ)*4UKX0eEuI~3TGv6-`SF(d4iROUyOdwx54`U;7s z2O~DI;k0}uW?eKsljf~&s5N7QY)7HKzk8G2ep!B>b#z-F<6pqfJqP2mz!Hm#nUQ8& zGcAzX&2NOYyT7cT7JlA#s)8o*+8XUF56odavy{>zY)4;;F_@uJyQs~RMZwhwy03qC zOty-{wu9W%;6^gAIIuJEAz?tweGSEB{LrdG47nX_O6JdRd zf>=r)+^M)gN}*d7)}&>?q=TBVEqCH{Uf7PVj#fm5(EC;Xx^t5(NbEV_lebq(QGip9 z=Q74~1^xW&bw!aA7qhNJ;9dAaLPCG}=tl}*l*HKx!3b4F{!0N*^VEwi`?6RyTr3Xi zQ4u!8W`ugqMSVBo`lqV{$XZ=fb9d+Fc^BmP!9m%&TWC+`nYK2L$7U10>?ajq z69ELuc&Jb}|5@4Vwb6>cpFhQnjg9?f;PdZQDqfplX(UdQQ%jfUGQLMd5?F)3-e%BU?c zzZl>UQnFt(Xp6W>a0PoK{-DRdZ&XZ0spEi?Wm)37qMqt^xCEA%-ueBzXJVqe+I0ma zufO7?*W&#F2E7;Z5>Up|o_c@9dyW7&MzO=R7zLXqq;bRl%)8unRDJ%nl{9ON(aGUX z?eCv*APIt*0+&K`b+y0(Dh-t(83q!7s$}&+tP536V1pM*ezTQ7DQ0$CW)EO%H%f&(xzllBo`Dv~e z(+x6%p8tt!s{iSMRh(Miq1dxP97*m2p?KN==_fz zch|?Y=U+36pfJPW>?ol~K#P00O14bQ}c0*o9D+woDBBYqA zW(PDtuWB4{m5=K8pzP(Cu@+^u_{&FCU7qw)j?yb)<);emYwD&0I?p-H=Jz^2VBGBrL{FvtkWNF zBZteoz@>s+{Bio6?9|6_s2K=7a)O{gITZ@5xeSt?85mzc5WjoG69t@KAc4y5a5=9Q zZ0`gm&8ZOW0PpOTrtkU_H4}BTL#}zch1dje4_s@Msg{mY$DQGF1q}_#NN~Mna__iJ z{|cJF{GFP|>?%Rf7u%%FNa4gjC6N>lL^r?$4$$mh^H?&I91M_N0YmjnnxBlAr_H1M zPn~1#zw33SmeH`VG)6-vU^WsuQ8k;!?Bty9;*cu3;+#9n>e z?MMV!06x9XTQ%0F^RG?vLCk?@k@=(o0ypKKwE^q__NyvAtzpUu5KC_yEYS8`uR;T$f-N!tvYc*R$xd;j0h`MxEd^rc)W0km zau-;1VJ=!J3rkj+r+NlI&@NZ2V$5Nh+yk{4AM5AX;s8PlwDrbRcwqPmDmOM0{2UmN zWQ_WJo<^KJz%0xnmRz{7Nou^i&*~UFzx3ym_;8fWB4*nd*%ZnY)sVVZ$ zlY?3a(q2YpX6BCpVA*yq-~DyvttYwQ6S%hN1({*R^$M?0?G4;2om)o zy)H0kjR%1P)YuT%!-tLl;guRRUHG{K^WIx8IFB6ejsiYF-2EONXXPde1dIjsu!zS% zxvjz3qdAZTo|eLL-eL3Epq9Dg@m3qDX1({G-RWkC$mTYZVv&%Kj_ z@ARvJv`KIoyGIHF`~hAyIK*SEY{+BkIX;P_BzG@BK7&R2C^?S0mxffp191CbAVQ(u ztHs#MyBz_4aa{>&5iDEMFBy9&^NeSB#T~x z{;+cdV^$)-o`%@?b9;V1$^z`3+Oj$f2RM!p0Ur2kMinsTd%AEWyfRn-^#9o3F#egc zm!$nIwr?hq5vGcRlTd6T9paM>)m%o=b50V^W2PM>D+IHi>BPlvV0svGO>6gtz`Rk6 zy7W!~5Z6E5-w+si@UJt~s_v0OBspsKRK`SYRQO&RdY{an53AD$0;^NRcVW^`dkapQ zjkobVkC)+~NKuR2AjSVzb^H@vm)8n=2*8N%tFyJXesIJnB@p!qZM+Q$R4MaePFeU2 zMsrz;1qUZMI`K$HUjd;RuQ04i9)~8WY_7y67ARu%2!cHPop}z(R(u3f7HkiWKQ}i? zMl@%0Cm0SLm@<7UBPNDFwPEvTNMT+*I+^~^&!tCl6Km+%?3wAQ=7?suzK?C3KpQCZ z*}>tzs?>e_<0o@dPHxlyopD)uI%)XLEU(P)fzfTxLb>dNJx6#}k(JF{OGbls|DM)r zwGI?2N8LPC9UT06!#$s57gK=sm5?;SfKX`Q&v`9^avGnryb#dsN?? zXFh3AGL;wGxTz^vET3mTA zCSw$)Wv=DJo?@0{>L>^M5dzJg+jdP?*4iN{8+VQVz_9)Lj%eX`-q8#%DwvJ$2&#N|9;%;osHF@8=5jUfsvZks97%B5$FrIC-kVY zVtI7p_Q>lZ%qG#P5-*g1)OMuAqp7?~AZZ@R+p@fmlBTdTcUyNpAnT%K3%hHZR6#*7 z7XNr_+#p}7>PYb4(VIP+ta}e5T4oGt+;@Hh5f_^yv?Q2oZrf?)=JMX%zyVS5J?xlU zNUY(&X|gB^=poo#blnon{5e_nFpk>x9DE;DT&P-4i?7zB;y~XUFTY!)+Ku-7k{+?; zw9EHQKoQiQ9~#ASI`$99;a#o=MF11zy~Ju1N9*cl*p#Xh@fCV0L{@18~B? zGXc7QLkg__aYmHCvOJ^GHhsTo0O>FL3H}39`5`CN+?F=LA9r1Wsj{jfuI=0ODPD(T z&;|Xlmcygx(!ObvZtfVvw&~X3U_U_{7R#|?oyd2XL|p_?@mrm-7oV2bH5R+Uv^Bb6jRAL^9x5SVv-y@ z+U?vk4~{N?gD1zHA$N8v(OW=0e^Ld-+dRk+RGjw_CTP6WWWJXc=C>D-N(Y|S0fc7+ z*X|03>l?nq)6cuve=>+0+Ca-eat$rXFm+1l~O&l*;Es3(@H$LL8)zg8va?80paa zIjU_?qG0Y{C+88FQ~P?1|4RTea6}nA)dkO9L^}UbllP>2BHP6)Ww8%9(G&#Veba^>>&%~t_pbPjm&7l5>cn(8>2U6y zgfCwG(ihp8Z?wKtSUQ`iPyijC2QZ#QsL z3#TYLBR^1kOC9;)=u8Z^19kn}5y)=)X7(Il)NnhhJwVI%FybUin4W)1-}GQBx>8#XXdC9AGC`BUL}}R*u-Do6T@@31Wk5{s_T53* zjw|6>{Ttdqylyw7k!>i)0a`pACw%{L=g9=ws`E$THVxBK@bpN_1wG;*>_kJE>6=y+ z6dku%A@u(6mi&7!jzh0El=^}Hzx4U^LSMrzfpFRndt6*#p$11&*SQ2lM+YN9}dvQ_!ch$&*{ zD>NyP1_&-u#EJJhfNDX+sFA9G?}cwSYj27nP;Ns}vxcJ!v~_j0WCV^{#PCsEw3+>PfXy9tFUhQxh61Uqdrb%GZO@s zja^nBOnT?w)uAx$gRr9hAxzAnmzf8hzWk@kPRGjcj+OW0B|odAp+Oc)bxD2=G;r%5 z1c@s==zwZHwR8>l1l7;~wVgQ`e%d=Uh_-CuHTgI3D)vMWMSd>y96gAslPK9*3ce5%FDl zW2iRpL>r%gU{$(>`X?~2*bBt-a(1wZJ@TZp+2qn&KW&YUPz5aT*v;P+W2Q}UiyXn8 zWZNk4L=Z#D!VjBRQ1twrJLUmak4sa@)bHuk>4&gdAIT1Xt+_D zhZ<;oQM|A(O(pHrct~hSTp+t7A;uFteG_`AsHS&sM7#AA+B}uFvoki>*DMrzaWb?; zh6)d$RC=V@!4uu_c>1e5*x||RtWv0rDHFE=RFc27bmG|%RtC~Lu>81QpRdiRR%9{N z*`4Xz^=XHZw8#*~YlR(V9aMM?gdqvqEDj3HfrKZx$;cA~sroV%hq=Em_MTsr0jROT zJZQV45KO+kCe4#fiART7-2Fjm8s5Y2^cIN<*QlHIJuWXRD+W?>2zV-4)Z5#;W$(!k zk5gadfsg!-Zx$uN>KC@^5Y6l{6vp5a7Erje^evAVZci;%FT+^JJH4Ug`FEp_W1^-+&{eNbCLvMIb)-S-|uK|VnK-KQj% zum^Rj?BzQbN4)ySlgIy4k%>Wj)dir#wzReud|&pe|2@C+f?CZ*EG_w(h8qexcIj~X zC_^^A+IY}RYSa*&zw@NxTm*3qrxD0$x56kt-;a{V>o9qTB|m&>0IL94o0^%?nC3o@ z^9wL`*~6KhI + + + + + + + + diff --git a/_images/inheritance-0e1de5f6e4d2753bb404dc09c89f7bf34a48e5c2.png b/_images/inheritance-0e1de5f6e4d2753bb404dc09c89f7bf34a48e5c2.png new file mode 100644 index 0000000000000000000000000000000000000000..d4498330f50a7f7d7fb3a84ddd6db8108e97626a GIT binary patch literal 2375 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 literal 0 HcmV?d00001 diff --git a/_images/inheritance-0e1de5f6e4d2753bb404dc09c89f7bf34a48e5c2.png.map b/_images/inheritance-0e1de5f6e4d2753bb404dc09c89f7bf34a48e5c2.png.map new file mode 100644 index 0000000..db805a0 --- /dev/null +++ b/_images/inheritance-0e1de5f6e4d2753bb404dc09c89f7bf34a48e5c2.png.map @@ -0,0 +1,3 @@ + + + diff --git a/_images/inheritance-28dc5b16a527babdcdd2309da615fa4d25560728.png b/_images/inheritance-28dc5b16a527babdcdd2309da615fa4d25560728.png new file mode 100644 index 0000000000000000000000000000000000000000..b1e99eccd0e0b46ade9e4ab3b804954f26a40839 GIT binary patch literal 3377 zcmcJScTm$!x5s}VB~(L`AWftrNLP9f7pvKhhQt_J4uLb-0_i6@k>OG7OapfB!O%0;l%+dJL6 zmpCtlJa5FjZHfYA`s%{?plY>T?-r9bSLQyQJlzm4ZKKT+M1RNZN2MOh-OZ5qWZ=*Wme=yOGB2bO;??`A5(hq#5~^It)BfN1Ft^IA>E z?&Mt5^rRu1Q9jkcw9N6>-L4$I&^i=h@sKZ`erMU5y@cTfe#`9`6jhmH ziT{T!-q2X?Q%L069RdyDL!~lSeK8K(+2Dx(gtragR^MAKU$T(a!>e6Sgi_z)b z`U1by?L=~m-x719S*#k^wrZ{w!Ys1r&8(`(4U zSYsdLx9ExzOs8+>05#>=+Mx|@UUVbTk9Xb8B}DwlNKMFim1*eqaD6O+QYc4O`Baw= z#<`nZLQRQ~9Yj|$&*_!Rk<GqQf+j@; zcu_SmS@uCA$LEieo|n}X^?n^vl#+VT@74KvOS~@gH!j$X>Ng0rK=(bkBPKob3vj0=W4`geQSt67|-gO9FjsstKvO4C2xrs1vdI?<}Qc8KZ;FW zB(H!O0Y;TLdu0<=X;kn8+Fh7aEF$V>C_^EplC?>?{c!!0fR?_MXTjuRl)kl;KlsOV zBHRkJ-2AArz@_z6EO8y(?Jls?<6yT2xASgOiZubwWkqwc0DxM3QgSh8e8orv>wI3J z{qvi*dl_Paw-$VReicwLvW09#Nh=KV12eiZdyQOxXNF&s%JA#na-4>6f2UcEa-{4! zP=6RxOU0?L*VG1QuMA1lv-?H5cvh^dpqTPj&$%EK8Q(gscJy-?P_El}RIFD#YiS3K zy>fV{vOlGJ?eKGTn8(?VA=7_O3buaH0(VuNABE01;Kx^w>I^7SlWBJ0;Md`^^UcQz zOzWnrp78vD+{<~_hfi*k-}r8?E*(Aru>i^gwcJ0EYAw2FZ7lYhU)KLR&Y6?41@S3O z%-9D$jm$wHO1uZt{C{MwdXao*UpRZq?1@Kq=#auR?$ug`QqhF_4`3li!;|9hLQ&u3P-gqr=nF z(Tne&B%gkTQ@q*?2DnAE>}f{*W{!@ISS*%M6NjvE#qX0iOIDPoJVFLF$1#tx!jy+? z5^iPBI+Y-Y1M6ob@Pa|Z#zKAg>UE`cGCi_?OMmc)I$}4;wdMnKq~zV4G|Af8nmvsN z?`ye!a*Wy;pY^NxmGAcrOLEqay$5i+ahG#WM0U@csISXwO!|g2O z+$@iy5u4##^4fi%eLqj{SL;CB{cbG5J^H3&`Cd6cppj_u;iYA6)yUrS)guE~tu{KL zvg+&JU!Zr9H*ajCTW)`;Ust@KG;zbT^AvSG=~wR(q$KISR24TejT;ppp2T(Z|J( zeq|ze+`%)>h5tc?XY3oLQL*X#vZ5&q_iyGbUGI5%8xrk#Kd9U%f9cTH_&wj7fe_ko zy*ZgEvsipiuTYK_`^=uIWn* zL!opfu@Z#j>94}viP4eR$?3cF} zff8P|agCnVRBiNgDgU%dB5F=&BMBOUSblxJPHEl8dA5)^>9XRcbDoD`ek?b)PVY33 zR`#9X8swpNSRz+a#aWd!-1rhR%2k!_whqVPS!e&f~-^22umG$5DR6_eTy2S+g zhIrhAARD!cj4|R=l0HX^5M6`_@W{8bmkmg;PR;3BVBWg2|1SZY2YZFvoFL8xdg!mv z03ZDqKDKY-A4F7X8Xsf1Z;@0ngryUaCXcqd(==W%oxiIX5pg|ZWlu$+Ukbu7Aqz8n z=NgfJ``PtJ)cR9m>~o?Y`naRnLo;mgRnYUge8~$A1I=_51Hu02uUOIS#?)++}Ut5xDgfHR$G_)E)gHh2cTg_|HT+uI#U_ zBNbjSE%MF^*gc^}_jTo1gj@mAZ*Kwkk+Ia~C@+lkF4TYmjYr2r{k}sa-)ZMPMiY9N z1(bgw*T%T=GayY>emQ6y#VGEVy#xUVD0k#4Pqs_}B;XGE_}F}Odt^<>7$Z|BFUGd+ zP!4@|oS4V$W}xw7CYvQ}=2GX~0KE>~vsi2AB9SJ3s%GoSn;``87{m6qp1eI<{>2!s zXV`n3#|?9LcHRqCJB(3e0l1Blq>jfn?K-1f`S0>@o>-20Ju(-KJC**0D3`}RdqnmP zo;gBROY(lGtNOXSXDE$wil4pIJ5T#3?wR8ZaAJteGo77Il!gmGrSoYtT4Q4)@8OUS zyF>$ScPFOXeI}594YRCKnQXt$r|vRi$z34c=<7O6JP&bNQPqOs@0iSgtB(>Q + + + diff --git a/_images/inheritance-36853c4c0e15588a9ea928c1eb553ce4dec273d2.png b/_images/inheritance-36853c4c0e15588a9ea928c1eb553ce4dec273d2.png new file mode 100644 index 0000000000000000000000000000000000000000..b6b414da7a1207bae569c1c8f0469e321cf49ee9 GIT binary patch literal 17714 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 literal 0 HcmV?d00001 diff --git a/_images/inheritance-36853c4c0e15588a9ea928c1eb553ce4dec273d2.png.map b/_images/inheritance-36853c4c0e15588a9ea928c1eb553ce4dec273d2.png.map new file mode 100644 index 0000000..786e9dc --- /dev/null +++ b/_images/inheritance-36853c4c0e15588a9ea928c1eb553ce4dec273d2.png.map @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/_images/inheritance-3b92336cf0a65c395e54a43c00d596b8083b5fa3.png b/_images/inheritance-3b92336cf0a65c395e54a43c00d596b8083b5fa3.png new file mode 100644 index 0000000000000000000000000000000000000000..6d631113be92623988f49ce8c89e7d90c78860f7 GIT binary patch literal 22528 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 literal 0 HcmV?d00001 diff --git a/_images/inheritance-3b92336cf0a65c395e54a43c00d596b8083b5fa3.png.map b/_images/inheritance-3b92336cf0a65c395e54a43c00d596b8083b5fa3.png.map new file mode 100644 index 0000000..cb32a6a --- /dev/null +++ b/_images/inheritance-3b92336cf0a65c395e54a43c00d596b8083b5fa3.png.map @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/_images/inheritance-4b6dc213e1e1979224b41188516a94c68c9f1e67.png b/_images/inheritance-4b6dc213e1e1979224b41188516a94c68c9f1e67.png new file mode 100644 index 0000000000000000000000000000000000000000..31509b41afe02131484d473a35525c968e4c4236 GIT binary patch literal 23583 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 literal 0 HcmV?d00001 diff --git a/_images/inheritance-4b6dc213e1e1979224b41188516a94c68c9f1e67.png.map b/_images/inheritance-4b6dc213e1e1979224b41188516a94c68c9f1e67.png.map new file mode 100644 index 0000000..b1ef16c --- /dev/null +++ b/_images/inheritance-4b6dc213e1e1979224b41188516a94c68c9f1e67.png.map @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/_images/inheritance-4f8dd1009c7207a9531135c46e5758a8c51c42f9.png b/_images/inheritance-4f8dd1009c7207a9531135c46e5758a8c51c42f9.png new file mode 100644 index 0000000000000000000000000000000000000000..1f2f0d9209d9cc62b6d10b0c2a200138f5a85f62 GIT binary patch literal 18226 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 Np{k=&s%#bUe*iS=>q-Cs literal 0 HcmV?d00001 diff --git a/_images/inheritance-4f8dd1009c7207a9531135c46e5758a8c51c42f9.png.map b/_images/inheritance-4f8dd1009c7207a9531135c46e5758a8c51c42f9.png.map new file mode 100644 index 0000000..5a58953 --- /dev/null +++ b/_images/inheritance-4f8dd1009c7207a9531135c46e5758a8c51c42f9.png.map @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/_images/inheritance-904d65e1f65eb1564a9d2eb12099f3e704cb95a2.png b/_images/inheritance-904d65e1f65eb1564a9d2eb12099f3e704cb95a2.png new file mode 100644 index 0000000000000000000000000000000000000000..0384412c9da3c30670698d1dfd071184925d3aaa GIT binary patch literal 10398 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 bR{t6q_SXKnnhelzNOkY7z6SEPZRGy}L>F(* literal 0 HcmV?d00001 diff --git a/_images/inheritance-904d65e1f65eb1564a9d2eb12099f3e704cb95a2.png.map b/_images/inheritance-904d65e1f65eb1564a9d2eb12099f3e704cb95a2.png.map new file mode 100644 index 0000000..7d7ec1d --- /dev/null +++ b/_images/inheritance-904d65e1f65eb1564a9d2eb12099f3e704cb95a2.png.map @@ -0,0 +1,6 @@ + + + + + + diff --git a/_images/inheritance-b9b73e22d9747748d09a3d35b0dc235420febf11.png b/_images/inheritance-b9b73e22d9747748d09a3d35b0dc235420febf11.png new file mode 100644 index 0000000000000000000000000000000000000000..62458c5060fdd1aec8bda932ea668f11916b3a42 GIT binary patch literal 10003 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 LUmIGbVjccpx@~zy literal 0 HcmV?d00001 diff --git a/_images/inheritance-b9b73e22d9747748d09a3d35b0dc235420febf11.png.map b/_images/inheritance-b9b73e22d9747748d09a3d35b0dc235420febf11.png.map new file mode 100644 index 0000000..3a6dbd0 --- /dev/null +++ b/_images/inheritance-b9b73e22d9747748d09a3d35b0dc235420febf11.png.map @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/_images/inheritance-d302f5c01c540af5802615d91cc88c2e9c5f3dea.png b/_images/inheritance-d302f5c01c540af5802615d91cc88c2e9c5f3dea.png new file mode 100644 index 0000000000000000000000000000000000000000..a362cf4da0f3be78df2ef1029bfc5b454d7ae9f2 GIT binary patch literal 2913 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 LNkvXXu0mjf3 + + diff --git a/_images/inheritance-d4733c816e29e4abb64bedaf024f51ca55559cf3.png b/_images/inheritance-d4733c816e29e4abb64bedaf024f51ca55559cf3.png new file mode 100644 index 0000000000000000000000000000000000000000..80b3d7767d2b1804de900cab3afc49f8a7c100aa GIT binary patch literal 24805 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 + + + + + + + + + diff --git a/_modules/index.html b/_modules/index.html new file mode 100644 index 0000000..4454752 --- /dev/null +++ b/_modules/index.html @@ -0,0 +1,251 @@ + + + + + + + + Overview: module code - Medical natural language parsing and utility library 1.7.0 documentation + + + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark mode + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/_modules/zensols/mednlp.html b/_modules/zensols/mednlp.html new file mode 100644 index 0000000..2df22b0 --- /dev/null +++ b/_modules/zensols/mednlp.html @@ -0,0 +1,265 @@ + + + + + + + + zensols.mednlp - Medical natural language parsing and utility library 1.7.0 documentation + + + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark mode + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for zensols.mednlp

+
+[docs] +def surpress_warnings(): + """Supress future warnings generated by spaCy and ScispaCy models.""" + import warnings + # spaCy 3.6 and medcat 1.7 - 1.9 warns with what appears to be an + # unserialized regular expression from the language model; ScispaCy also + # complains + warnings.filterwarnings( + 'ignore', + message=r'^Possible set union at position', + category=FutureWarning)
+ + + +from .domain import * +from .uts import UTSError, NoResultsError, AuthenticationError, UTSClient +from .resource import * +from .tok import * +from .lib import * +from .parser import * +from .app import * +from .cli import * + + +surpress_warnings() +
+
+
+
+ + +
+
+ + Made with Sphinx and @pradyunsg's + + Furo + +
+
+ +
+
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/_modules/zensols/mednlp/app.html b/_modules/zensols/mednlp/app.html new file mode 100644 index 0000000..b20f9c1 --- /dev/null +++ b/_modules/zensols/mednlp/app.html @@ -0,0 +1,475 @@ + + + + + + + + zensols.mednlp.app - Medical natural language parsing and utility library 1.7.0 documentation + + + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark mode + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for zensols.mednlp.app

+"""A natural language medical domain parsing library.
+
+"""
+__author__ = 'Paul Landes'
+
+from typing import Optional
+from dataclasses import dataclass, field
+from enum import Enum, auto
+import sys
+import logging
+from io import TextIOBase
+import re
+from pprint import pprint
+from pathlib import Path
+import pandas as pd
+from zensols.config import Dictable, ConfigFactory
+from zensols.cli import ApplicationError
+from zensols.nlp import FeatureDocumentParser, FeatureDocument
+from zensols.nlp.dataframe import FeatureDataFrameFactory
+from . import MedCatResource, MedicalLibrary
+
+logger = logging.getLogger(__name__)
+
+
+
+[docs] +class GroupInfo(Enum): + """Used to group TUI information in :meth:`.Application.group` + + """ + csv = auto() + byname = auto()
+ + + +
+[docs] +@dataclass +class Application(Dictable): + """A natural language medical domain parsing library. + + """ + config_factory: ConfigFactory = field() + """Used to create a cTAKES stash.""" + + doc_parser: FeatureDocumentParser = field() + """Parses and NER tags medical terms.""" + + library: MedicalLibrary = field() + """Medical resource library that contains UMLS access, cui2vec etc..""" + + def _get_text(self, text_or_file: str) -> str: + """Return the text from a file or the text passed based on if + ``text_or_file`` is a file on the file system. + + """ + path = Path(text_or_file) + if path.is_file(): + with open(path) as f: + text_or_file = f.read() + return text_or_file + + def _write_doc(self, doc: FeatureDocument, only_medical: bool, + depth: int = 0, writer: TextIOBase = sys.stdout): + for sent in doc.sents: + if len(sent.text.strip()) == 0: + continue + self._write_line(sent.text, depth, writer) + for tok in sent: + if not only_medical or tok.is_ent: + self._write_line(f'{tok.norm}:', depth + 1, writer) + tok.write_attributes( + depth + 2, writer, + feature_ids=self.doc_parser.token_feature_ids + ) + self._write_line('entities:', depth, writer) + for ents in sent.entities: + self._write_line( + ' '.join(map(lambda e: e.norm, ents)), depth + 1, writer) + +
+[docs] + def show(self, text_or_file: str, only_medical: bool = False): + """Parse and output medical entities. + + :param text_or_file: natural language to be processed + + :param only_medical: only provide medical linked tokens + + """ + if logger.isEnabledFor(logging.INFO): + logger.info(f'parsing: <{text_or_file}>...') + text: str = self._get_text(text_or_file) + doc: FeatureDocument = self.doc_parser.parse(text) + self._write_doc(doc, only_medical)
+ + + def _output_dataframe(self, df: pd.DataFrame, out: Optional[Path] = None): + """Output the dataframe generated by other actions of the app. + + :param df: the dataframe to output: + + :param out: the output path, or ``None`` standard out + """ + if out is None: + out = sys.stdout + df.to_csv(out, index=False) + row_s = 's' if len(df) != 1 else '' + if out != sys.stdout: + logger.info(f'wrote {len(df)} row{row_s} to {out}') + +
+[docs] + def features(self, text_or_file: str, out: Path = None, ids: str = None, + only_medical: bool = False): + """Dump features as CSV output. + + :param text_or_file: natural language to be processed + + :param out: the path to output the CSV file or stdout if missing + + :param ids: the comma separate feature IDs to output + + :param only_medical: only provide medical linked tokens + + """ + if logger.isEnabledFor(logging.INFO): + logger.info(f'parsing: <{text_or_file}>...') + params = {} + if ids is None: + ids = self.doc_parser.token_feature_ids + else: + ids = set(re.split(r'\W+', ids)) + needs = 'norm cui_ is_concept'.split() + missing = set(filter(lambda i: i not in ids, needs)) + ids |= missing + params['token_feature_ids'] = ids + params['priority_feature_ids'] = needs + df_fac = FeatureDataFrameFactory(**params) + self.doc_parser.token_feature_ids = ids + text: str = self._get_text(text_or_file) + doc: FeatureDocument = self.doc_parser.parse(text) + df: pd.DataFrame = df_fac(doc) + if only_medical: + df = df[df['is_concept']] + self._output_dataframe(df, out)
+ + +
+[docs] + def search(self, term: str): + """Search the UMLS database using UTS and show results. + + :param term: the term to search for (eg 'lung cancer') + + """ + pprint(self.library.uts_client.search_term(term))
+ + +
+[docs] + def atom(self, cui: str): + """Search the UMLS database using UTS and show results. + + :param cui: the concept ID to search for (eg 'C0242379') + + """ + pprint(self.library.uts_client.get_atoms(cui))
+ + +
+[docs] + def define(self, cui: str): + """Look up an entity by CUI. This takes a long time. + + :param cui: the concept ID to search for (eg 'C0242379') + + """ + entity = self.library.get_linked_entity(cui) + print(entity)
+ + +
+[docs] + def group(self, info: GroupInfo, query: str = None): + """Get TUI group information. + + :param info: the type of information to return + + :param query: comma delimited name list used to subset the output data + + """ + res: MedCatResource = self.library.medcat_resource + df: pd.DataFrame = res.groups + if info == GroupInfo.csv: + path = Path('tui-groups.csv') + df.to_csv(path) + logger.info(f'wrote TUI groups to {path}') + elif info == GroupInfo.byname: + if query is None: + raise ApplicationError('Missing query string for grouping') + reg = '.*(' + '|'.join(query.split(',')) + ')' + df = df[df['name'].str.match(reg)] + print(','.join(df['tui'].tolist())) + else: + raise ApplicationError(f'Unknown query info type: {info}')
+ + +
+[docs] + def ctakes(self, text_or_file: str, only_medical: bool = False): + """Invoke cTAKES on a directory with text files. + + :param text_or_file: natural language to be processed + + :param only_medical: only provide medical linked tokens + + """ + from .ctakes import CTakesParserStash + text: str = self._get_text(text_or_file) + stash: CTakesParserStash = self.library.get_new_ctakes_parser_stash() + stash.set_documents([text]) + print(stash['0'].to_string())
+ + +
+[docs] + def similarity(self, term: str): + """Get the cosine similarity between two CUIs. + + """ + for sim in self.library.similarity_by_term(term): + print(sim.cui) + sim.write(1)
+
+ +
+
+
+
+ + +
+
+ + Made with Sphinx and @pradyunsg's + + Furo + +
+
+ +
+
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/_modules/zensols/mednlp/cli.html b/_modules/zensols/mednlp/cli.html new file mode 100644 index 0000000..6b7b735 --- /dev/null +++ b/_modules/zensols/mednlp/cli.html @@ -0,0 +1,276 @@ + + + + + + + + zensols.mednlp.cli - Medical natural language parsing and utility library 1.7.0 documentation + + + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark mode + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for zensols.mednlp.cli

+"""Command line entry point to the application.
+
+"""
+__author__ = 'Paul Landes'
+
+from typing import List, Any, Dict, Type
+import sys
+from zensols.cli import ApplicationFactory, ActionResult, CliHarness
+from zensols.nlp import FeatureDocumentParser
+
+
+
+[docs] +class ApplicationFactory(ApplicationFactory): +
+[docs] + def __init__(self, *args, **kwargs): + kwargs['package_resource'] = 'zensols.mednlp' + super().__init__(*args, **kwargs)
+ + +
+[docs] + @classmethod + def get_doc_parser(cls: Type) -> FeatureDocumentParser: + """Get the default application's document parser.""" + return cls.create_harness().get_application().doc_parser
+
+ + + +
+[docs] +def main(args: List[str] = sys.argv, **kwargs: Dict[str, Any]) -> ActionResult: + harness: CliHarness = ApplicationFactory.create_harness(relocate=False) + harness.invoke(args, **kwargs)
+ +
+
+
+
+ + +
+
+ + Made with Sphinx and @pradyunsg's + + Furo + +
+
+ +
+
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/_modules/zensols/mednlp/ctakes.html b/_modules/zensols/mednlp/ctakes.html new file mode 100644 index 0000000..9b9b2cb --- /dev/null +++ b/_modules/zensols/mednlp/ctakes.html @@ -0,0 +1,395 @@ + + + + + + + + zensols.mednlp.ctakes - Medical natural language parsing and utility library 1.7.0 documentation + + + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark mode + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for zensols.mednlp.ctakes

+"""Parse and normalize discharge notes.
+
+"""
+__author__ = 'Paul Landes'
+
+from typing import Iterable
+from dataclasses import dataclass, field
+import logging
+import os
+from pathlib import Path
+import pandas as pd
+from zensols.config import Dictable
+from zensols.persist import Stash, DirectoryStash, ReadOnlyStash, Primeable
+from zensols.util.executor import Executor
+import ctakes_parser.ctakes_parser as ctparser
+from . import MedNLPError
+
+logger = logging.getLogger(__name__)
+ctakes_logger = logging.getLogger(__name__ + '.ctakes')
+
+
+@dataclass
+class _TextDirectoryStash(DirectoryStash):
+    pattern: str = field(default='{name}.txt')
+
+    def _load_file(self, path: Path) -> str:
+        with open(path, 'r') as f:
+            return f.read()
+
+    def _dump_file(self, inst: str, path: Path):
+        with open(path, 'w') as f:
+            f.write(inst)
+
+
+
+[docs] +@dataclass +class CTakesParserStash(ReadOnlyStash, Primeable, Dictable): + """Runs the cTAKES CUI entity linker on a directory of medical notes. For + each medical text file, it generates an ``xmi`` file, which is then parsed + by the the :mod:`ctakes_parser` library. + + This straightforward wrapper around the ``ctparser`` library automates the + file system orchestration that needs to happen. Configure an instance of + this class as an application configuration and use a + :class:`~zensols.config.ImportConfigFactory` to create the objects. See + the ``examples/ctakes`` directory for a quick start guide on how to use + this class. + + """ + entry_point_bin: Path = field() + """Entry point script in to the cTAKES parser.""" + + entry_point_cmd: str = field() + """Command line arguments passed to cTAKES.""" + + home: Path = field() + """The directory where cTAKES is installed.""" + + source_dir: Path = field() + """Contains a path to the source directory where the text documents live. + + """ + output_dir: Path = field(default=None) + """The directory where to output the xmi files.""" + + def __post_init__(self): + super().__post_init__() + self.strict = True + self._pattern: str = field(default='{name}.txt.xmi') + if self.output_dir is None: + self.output_dir = self.source_dir.parent / 'output' + for attr in 'entry_point_bin source_dir output_dir'.split(): + setattr(self, attr, getattr(self, attr).absolute()) + self._source_stash = _TextDirectoryStash(self.source_dir) + self._out_stash = _TextDirectoryStash( + path=self.output_dir, + pattern=self._source_stash.pattern + '.xmi') + + @property + def source_stash(self) -> Stash: + """The stash that tracks the text documents that are to be parsed by + cTAKES. + + """ + return self._source_stash + +
+[docs] + def set_documents(self, docs: Iterable[str]): + """Set the document to be parsed by cTAKES. + + :param docs: an iterable of string text documents to persist to the + file system, and then be parsed by cTAKES. + + """ + self.clear() + for i, doc in enumerate(docs): + self._source_stash.dump(str(i), doc)
+ + + def _run(self): + """Run cTAKES (see class docs).""" + if logger.isEnabledFor(logging.INFO): + logger.info(f'running ctakes parser on {self.source_dir}') + os.environ['CTAKES_HOME'] = str(self.home.absolute()) + cmd = self.entry_point_cmd.format(**self.asdict()) + if logger.isEnabledFor(logging.INFO): + logger.info(f'executing {cmd}') + exc = Executor(ctakes_logger) + exc.run(cmd) + +
+[docs] + def prime(self): + super().prime() + if not self.source_dir.is_dir(): + raise MedNLPError('cTAKES temporary path is not an existing ' + + f'directory: {self.source_dir}') + if len(self._source_stash) == 0: + raise MedNLPError( + f'Source directory contains no data: {self.source_dir}') + if len(self._out_stash) == 0: + self.output_dir.mkdir(parents=True, exist_ok=True) + self._run()
+ + +
+[docs] + def load(self, name: str) -> pd.DataFrame: + self.prime() + path: Path = self._out_stash.key_to_path(name) + return ctparser.parse_file(file_path=str(path))
+ + +
+[docs] + def keys(self) -> Iterable[str]: + self.prime() + return self._out_stash.keys()
+ + +
+[docs] + def exists(self, name: str) -> bool: + self.prime() + return self._out_stash.exists(name)
+ + +
+[docs] + def clear(self): + self._out_stash.clear() + self._source_stash.clear()
+
+ +
+
+
+
+ + +
+
+ + Made with Sphinx and @pradyunsg's + + Furo + +
+
+ +
+
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/_modules/zensols/mednlp/cui2vec.html b/_modules/zensols/mednlp/cui2vec.html new file mode 100644 index 0000000..a1bfb8a --- /dev/null +++ b/_modules/zensols/mednlp/cui2vec.html @@ -0,0 +1,298 @@ + + + + + + + + zensols.mednlp.cui2vec - Medical natural language parsing and utility library 1.7.0 documentation + + + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark mode + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for zensols.mednlp.cui2vec

+"""This module contains the embedding subclass for cui2vec embeddings.
+
+"""
+__author__ = 'Paul Landes'
+
+from typing import Dict, List
+from dataclasses import dataclass, field
+import logging
+import csv
+from h5py import Dataset
+from zensols.deepnlp.embed import (
+    WordEmbedError, TextWordEmbedModel, TextWordModelMetadata
+)
+
+logger = logging.getLogger(__name__)
+
+
+
+[docs] +@dataclass +class Cui2VecEmbedModel(TextWordEmbedModel): + """This class uses the pretrained cui2vec embeddings. + + """ + dimension: str = field(default=500) + """The word vector dimension.""" + + vocab_size: int = field(default=109053) + """Vocabulary size.""" + + def _populate_vec_lines(self, words: List[str], word2idx: Dict[str, int], + ds: Dataset): + idx = 0 + lc = 0 + meta = self.metadata + with open(meta.source_path) as csvfile: + csv_reader = csv.reader(csvfile) + next(csv_reader) + for rix, line in enumerate(csv_reader): + lc += 1 + word = line[0] + words.append(word) + word2idx[word] = idx + idx += 1 + try: + ds[rix, :] = tuple(map(float, line[1:])) + except Exception as e: + raise WordEmbedError( + f'Could not parse line {lc} (word: {word}): ' + + f'{e}; line: {line}') from e + + def _get_metadata(self) -> TextWordModelMetadata: + name = 'cui2vec' + dim = self.dimension + path = self.path.parent / self.resource.check_path + return TextWordModelMetadata( + name, 'default', dim, self.vocab_size, path, + sub_directory='cui2vec-bin')
+ +
+
+
+
+ + +
+
+ + Made with Sphinx and @pradyunsg's + + Furo + +
+
+ +
+
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/_modules/zensols/mednlp/domain.html b/_modules/zensols/mednlp/domain.html new file mode 100644 index 0000000..b7b27eb --- /dev/null +++ b/_modules/zensols/mednlp/domain.html @@ -0,0 +1,285 @@ + + + + + + + + zensols.mednlp.domain - Medical natural language parsing and utility library 1.7.0 documentation + + + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark mode + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for zensols.mednlp.domain

+"""Contains the classes for the medical token type and others.
+
+"""
+__author__ = 'Paul Landes'
+from typing import Optional
+from dataclasses import dataclass, field
+import logging
+from spacy.tokens.span import Span
+from zensols.util import APIError
+
+logger = logging.getLogger(__name__)
+
+
+
+[docs] +class MedNLPError(APIError): + """Raised by any medical NLP speicic reason in this library.""" + pass
+ + + +@dataclass +class _MedicalEntity(object): + """Container class for general and medical specific named entities. + Instances of this class have the NER from a vanilla spaCy model output and + the NER+L linked UMLS concepts. + + """ + concept_span: Optional[Span] = field(default=None) + """The UMLS concept medical domain entity.""" + + @property + def is_concept(self) -> bool: + return self.concept_span is not None + + @property + def is_ent(self) -> bool: + return self.concept_span is not None + + @property + def cui(self) -> str: + return int(self.cui_[1:]) + + @property + def cui_(self) -> str: + return self.concept_span._.cui +
+
+
+
+ + +
+
+ + Made with Sphinx and @pradyunsg's + + Furo + +
+
+ +
+
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/_modules/zensols/mednlp/entlink.html b/_modules/zensols/mednlp/entlink.html new file mode 100644 index 0000000..ae795b9 --- /dev/null +++ b/_modules/zensols/mednlp/entlink.html @@ -0,0 +1,381 @@ + + + + + + + + zensols.mednlp.entlink - Medical natural language parsing and utility library 1.7.0 documentation + + + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark mode + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for zensols.mednlp.entlink

+"""Contains the classes for the medical token type and others.
+
+"""
+__author__ = 'Paul Landes'
+
+from typing import List, Dict, Any, ClassVar
+from dataclasses import dataclass, field, InitVar
+import logging
+from scispacy.linking import EntityLinker
+from scispacy.linking_utils import Entity as SciSpacyEntity
+from zensols.persist import persisted, PersistedWork
+from zensols.config import Dictable
+from zensols.nlp import FeatureToken, FeatureTokenDecorator
+from . import MedicalLibrary
+
+logger = logging.getLogger(__name__)
+
+
+
+[docs] +@dataclass +class Entity(Dictable): + """A convenience container class that Wraps a SciSpacy entity. + + """ + _DICTABLE_ATTRIBUTES: ClassVar[List[str]] = 'cui name definition'.split() + + sci_spacy_entity: SciSpacyEntity = field(repr=False) + """The entity identified by :mod:`scispacy.linking_utils`.""" + + @property + def name(self) -> str: + """The canonical name of the entity.""" + return self.sci_spacy_entity.canonical_name + + @property + def definition(self) -> str: + """The human readable description of the entity.""" + return self.sci_spacy_entity.definition + + @property + def cui(self) -> str: + """The unique concept identifier.""" + return self.sci_spacy_entity.concept_id + + def __str__(self) -> str: + return f'{self.name} ({self.cui})' + + def __repr__(self): + return self.cui
+ + + +
+[docs] +@dataclass +class EntitySimilarity(Entity): + """A similarity measure of a medical concept in cui2vec. + + :see: :meth:`.MedCatFeatureDocumentParser.similarity_by_term` + """ + similiarty: float = field()
+ + + +
+[docs] +@dataclass +class EntityLinkerResource(object): + """Provides a way resolve :class:`scispacy.linking_utils.Entity` instances + from CUIs. + + :see: :meth:`.get_linked_entity` + + """ + params: Dict[str, Any] = field( + default_factory=lambda: {'resolve_abbreviations': True, + 'linker_name': 'umls'}) + """Parameters given to the scispaCy entity linker.""" + + cache_global: InitVar[bool] = field(default=True) + """Whether or not to globally cache resources, which saves load time. + + """ + def __post_init__(self, cache_global: bool): + self._linker = PersistedWork( + '_linker', self, cache_global=cache_global) + + @property + @persisted('_linker') + def linker(self) -> EntityLinker: + """The ScispaCy entity linker.""" + self._silence_scispacy_warn() + return EntityLinker(**self.params) + + @staticmethod + def _silence_scispacy_warn(): + """This warning has should have no bearing on this application as we're + simply doing a CUI looking. + + """ + import warnings + s = '.*Trying to unpickle estimator Tfidf(?:Transformer|Vectorizer) from version.*' + warnings.filterwarnings('ignore', message=s) + +
+[docs] + def get_linked_entity(self, cui: str) -> Entity: + """Get a scispaCy linked entity. + + :param cui: the unique concept ID + + """ + linker: EntityLinker = self.linker + se: SciSpacyEntity = linker.kb.cui_to_entity.get(cui) + if se is not None: + return Entity(se)
+
+ + + +
+[docs] +@dataclass +class LinkFeatureTokenDecorator(FeatureTokenDecorator): + """Adds linked SciSpacy definitions to tokens using the + :class:`.MedicalLibrary`. + + """ + lib: MedicalLibrary = field(default=None) + """The medical library used for linking entities.""" + +
+[docs] + def decorate(self, token: FeatureToken): + e: SciSpacyEntity = self.lib.get_linked_entity(token.cui_) + if logger.isEnabledFor(logging.DEBUG): + logger.debug(f'entity: {token.cui_} -> {e} ({id(token)})') + if e is not None: + token._definition = e.definition
+
+ +
+
+
+
+ + +
+
+ + Made with Sphinx and @pradyunsg's + + Furo + +
+
+ +
+
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/_modules/zensols/mednlp/lib.html b/_modules/zensols/mednlp/lib.html new file mode 100644 index 0000000..f9537ac --- /dev/null +++ b/_modules/zensols/mednlp/lib.html @@ -0,0 +1,368 @@ + + + + + + + + zensols.mednlp.lib - Medical natural language parsing and utility library 1.7.0 documentation + + + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark mode + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for zensols.mednlp.lib

+"""Medical resource library that contains UMLS access, cui2vec etc..
+
+"""
+from __future__ import annotations
+__author__ = 'Paul Landes'
+from typing import Any, List, Dict, Tuple
+from dataclasses import dataclass, field
+from zensols.config import ConfigFactory
+from . import MedCatResource, UTSClient
+
+
+
+[docs] +@dataclass +class MedicalLibrary(object): + """A utility class that provides access to medical APIs. + + """ + config_factory: ConfigFactory = field(default=None) + """The configuration factory used to create cTAKES and cui2vec instances. + + """ + medcat_resource: MedCatResource = field(default=None) + """The MedCAT factory resource.""" + + entity_linker_resource: 'EntityLinkerResource' = field(default=None) + """The entity linker resource.""" + + uts_client: UTSClient = field(default=None) + """Queries UMLS data.""" + +
+[docs] + def get_entities(self, text: str) -> Dict[str, Any]: + """Return the all concept entity data. + + :return: concepts as a multi-tiered dict + + """ + return self.medcat_resource.cat.get_entities(text)
+ + +
+[docs] + def get_linked_entity(self, cui: str) -> 'Entity': + """Get a scispaCy linked entity. + + :param cui: the unique concept ID + + """ + from .entlink import Entity + ent: Entity = self.entity_linker_resource.get_linked_entity(cui) + return ent
+ + +
+[docs] + def get_atom(self, cui: str) -> Dict[str, str]: + """Get the UMLS atoms of a CUI from UTS. + + :param cui: the concept ID used to query + + :param preferred: if ``True`` only return preferred atoms + + :return: a list of atom entries in dictionary form + + """ + return self.uts_client.get_atoms(cui, preferred=True)
+ + +
+[docs] + def get_relations(self, cui: str) -> List[Dict[str, Any]]: + """Get the UMLS related concepts connected to a concept by ID. + + :param cui: the concept ID used to get related concepts + + :return: a list of relation entries in dictionary form in the order + returned by UTS + + """ + return self.uts_client.get_relations(cui)
+ + +
+[docs] + def get_new_ctakes_parser_stash(self) -> 'CTakesParserStash': + """Return a new instance of a ctakes parser stash. + + """ + return self.config_factory.new_instance('ctakes_parser_stash')
+ + + @property + def cui2vec_embedding(self) -> 'Cui2VecEmbedModel': + """The cui2vec embedding model. + + """ + return self.config_factory('cui2vec_500_embedding') + +
+[docs] + def similarity_by_term(self, term: str, topn: int = 5) -> \ + List['EntitySimilarity']: + """Return similaries of a medical term. + + :param term: the medical term (i.e. ``heart disease``) + + :param topn: the top N count similarities to return + + """ + from .entlink import Entity, EntitySimilarity + from .cui2vec import Cui2VecEmbedModel + from gensim.models.keyedvectors import KeyedVectors + embedding: Cui2VecEmbedModel = self.cui2vec_embedding + kv: KeyedVectors = embedding.keyed_vectors + res: List[Dict[str, str]] = self.uts_client.search_term(term) + cui: str = res[0]['ui'] + sims_by_word: List[Tuple[str, float]] = kv.similar_by_word(cui, topn) + sims: List[EntitySimilarity] = [] + for rel_cui, proba in sims_by_word: + entity: Entity = self.get_linked_entity(rel_cui) + # name: str = entity.canonical_name.lower() + # defn: str = entity.definition + sim: float = embedding.keyed_vectors.similarity(cui, rel_cui) + sims.append(EntitySimilarity(entity.sci_spacy_entity, sim)) + return sims
+
+ +
+
+
+
+ + +
+
+ + Made with Sphinx and @pradyunsg's + + Furo + +
+
+ +
+
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/_modules/zensols/mednlp/parser.html b/_modules/zensols/mednlp/parser.html new file mode 100644 index 0000000..98eff39 --- /dev/null +++ b/_modules/zensols/mednlp/parser.html @@ -0,0 +1,314 @@ + + + + + + + + zensols.mednlp.parser - Medical natural language parsing and utility library 1.7.0 documentation + + + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark mode + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for zensols.mednlp.parser

+"""Medical langauge parser.
+
+"""
+__author__ = 'Paul Landes'
+
+from typing import Type, Iterable, Dict, Set
+from dataclasses import dataclass, field
+import logging
+import collections
+import textwrap as tw
+from spacy.tokens.doc import Doc
+from spacy.language import Language
+from zensols.nlp import FeatureToken, FeatureDocumentParser
+from zensols.nlp.sparser import SpacyFeatureDocumentParser
+from . import MedNLPError, MedCatResource, MedicalFeatureToken
+from .domain import _MedicalEntity
+
+logger = logging.getLogger(__name__)
+
+
+
+[docs] +@dataclass +class MedCatFeatureDocumentParser(SpacyFeatureDocumentParser): + """A medical based language resources that parses concepts. + + """ + TOKEN_FEATURE_IDS = frozenset(FeatureDocumentParser.TOKEN_FEATURE_IDS | + MedicalFeatureToken.FEATURE_IDS) + """Default token feature ID set for the medical parser. + + """ + token_feature_ids: Set[str] = field(default=TOKEN_FEATURE_IDS) + """The features to keep from spaCy tokens. + + :see: :obj:`TOKEN_FEATURE_IDS` + + """ + token_class: Type[FeatureToken] = field(default=MedicalFeatureToken) + """The class to use for instances created by :meth:`features`.""" + + medcat_resource: MedCatResource = field(default=None) + """The MedCAT factory resource.""" + + def __post_init__(self): + if self.medcat_resource is None: + raise MedNLPError('No medcat resource set') + super().__post_init__() + + def _create_model_key(self) -> str: + return f'name-{self.name}' + + def _create_model(self) -> Language: + return self.medcat_resource.cat.pipe.spacy_nlp + + def _normalize_tokens(self, doc: Doc) -> Iterable[FeatureToken]: + if logger.isEnabledFor(logging.INFO): + logger.info(f'parsing: {tw.shorten(str(doc), 60)}') + + # load/create model resources + res: MedCatResource = self.medcat_resource + ix2ent: Dict[int, _MedicalEntity] = \ + collections.defaultdict(_MedicalEntity) + + # add entities + for ent in doc.ents: + for i in range(ent.start, ent.end): + tok = doc[i] + ix2ent[tok.idx].concept_span = ent + + if logger.isEnabledFor(logging.DEBUG): + logger.debug(f'normalizing with: {self.token_normalizer}') + + return super()._normalize_tokens(doc, res=res, ix2ent=ix2ent)
+ +
+
+
+
+ + +
+
+ + Made with Sphinx and @pradyunsg's + + Furo + +
+
+ +
+
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/_modules/zensols/mednlp/resource.html b/_modules/zensols/mednlp/resource.html new file mode 100644 index 0000000..a30468e --- /dev/null +++ b/_modules/zensols/mednlp/resource.html @@ -0,0 +1,504 @@ + + + + + + + + zensols.mednlp.resource - Medical natural language parsing and utility library 1.7.0 documentation + + + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark mode + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for zensols.mednlp.resource

+"""MedCAT wrapper.
+
+"""
+__author__ = 'Paul Landes'
+
+from typing import Tuple, Iterable, Dict, Any, Set
+from dataclasses import dataclass, field, InitVar
+import logging
+import warnings
+from pathlib import Path
+import re
+from frozendict import frozendict
+import pandas as pd
+import spacy.util
+from medcat.config import Config, MixingConfig
+from medcat.vocab import Vocab
+from medcat.cdb import CDB
+from medcat.cat import CAT
+from medcat.meta_cat import MetaCAT
+from zensols.util import APIError
+from zensols.install import Resource, Installer
+from zensols.persist import persisted, PersistedWork
+
+logger = logging.getLogger(__name__)
+
+
+
+[docs] +@dataclass +class MedCatResource(object): + """A factory class that creates MedCAT resources. + + """ + _MODEL_REGEX = re.compile(r'^([^@]+) @ .+$') + """A regular expression for a spaCy model dependency (http syntax).""" + + installer: Installer = field() + """Installs and provides paths to the model files.""" + + vocab_resource: Resource = field() + """The path to the ``vocab.dat`` file.""" + + cdb_resource: Resource = field() + """The ``cdb-medmen-v1.dat`` file. + + """ + mc_status_resource: Resource = field() + """The the ``mc_status`` directory. + + """ + umls_tuis: Resource = field() + """The UMLS TUIs (types) mapping resource that maps from TUIs to + descriptions. + + :see: `Semantic Types <https://lhncbc.nlm.nih.gov/ii/tools/MetaMap/documentation/SemanticTypesAndGroups.html>`_ + + """ + umls_groups: Resource = field() + """Like :obj:`umls_tuis` but groups TUIs in gropus.""" + + filter_tuis: Set[str] = field(default=None) + """Types used to filter linked CUIs (i.e. ``{'T047', 'T048'}``). + + """ + filter_groups: Set[str] = field(default=None) + """Just like :obj:`filter_tuis` but each element is treated as a group used + to generate a list of CUIs from those mapped from ``name`` to ``tui` in + :obj:`groups`. + + """ + spacy_enable_components: Set[str] = field( + default_factory=lambda: set('sentencizer parser'.split())) + """By default, MedCAT disables several pipeline components. Some of these + are needed for sentence chunking and other downstream tasks. Otherwise + sentence indexing won't work because sentence boundaries are missing. + + :see: `MedCAT Config <https://github.com/CogStack/MedCAT/blob/master/medcat/config.py>`_ + + """ + cat_config: Dict[str, Dict[str, Any]] = field(default=None) + """If provieded, set the CDB configuration. Keys are ``general``, + ``preprocessing`` and all other attributes documented in the `MedCAT Config + <https://github.com/CogStack/MedCAT/blob/master/medcat/config.py>`_ + + """ + cache_global: InitVar[bool] = field(default=True) + """Whether or not to globally cache resources, which saves load time. + + """ + requirements_dir: Path = field(default=None) + """The directory with the pip requirements files.""" + + auto_install_models: Tuple[str, ...] = field(default=()) + """A list of spaCy models that will be installed if not already.""" + + def __post_init__(self, cache_global: bool): + self._tuis = PersistedWork('_tuis', self, cache_global=cache_global) + self._cat = PersistedWork('_cat', self, cache_global=cache_global) + self._installed = False + + @staticmethod + def _filter_medcat_logger(): + class NoCdbExportFilter(logging.Filter): + def filter(self, record): + s = 'The CDB was exported by an unknown version of MedCAT.' + return not record.getMessage() == s + + logging.getLogger('medcat.cdb').addFilter(NoCdbExportFilter()) + + def _assert_installed(self): + if not self._installed: + self.installer() + self._installed = True + + def _override_config(self, targ: Config, src: Dict[str, Dict[str, Any]]): + src_top: str + src_conf = Dict[str, Any] + for src_top, src_conf in src.items(): + targ_any: Any = getattr(targ, src_top) + if logger.isEnabledFor(logging.DEBUG): + logger.debug(f"updating dict '{src_top}' ({type(targ_any)}): " + + f"<{targ_any}> with <{src_conf}>") + if isinstance(targ_any, dict): + targ_any.update(src_conf) + elif isinstance(targ_any, MixingConfig): + targ_any.merge_config(src_conf) + else: + setattr(targ, src_top, src_conf) + + def _add_filters(self, config: Config, cdb: CDB): + filter_tuis = set() + if self.filter_tuis is not None: + filter_tuis.update(self.filter_tuis) + if self.filter_groups is not None: + df: pd.DataFrame = self.groups + reg = '.*(' + '|'.join(self.filter_groups) + ')' + df = df[df['name'].str.match(reg)] + filter_tuis.update(df['tui'].tolist()) + if logger.isEnabledFor(logging.INFO): + logger.info(f'filtering on tuis: {", ".join(filter_tuis)}') + if len(filter_tuis) > 0: + cui_filters = set() + for tui in filter_tuis: + cui_filters.update(cdb.addl_info['type_id2cuis'][tui]) + config.linking['filters']['cuis'] = cui_filters + + @property + @persisted('_tuis') + def tuis(self) -> Dict[str, str]: + """A mapping of type identifiers (TUIs) to their descriptions.""" + self._assert_installed() + path: Path = self.installer[self.umls_tuis] + df = pd.read_csv(path, delimiter='|', header=None) + df.columns = 'abbrev tui desc'.split() + df_tups = df[['tui', 'desc']].itertuples(name=None, index=False) + return frozendict(df_tups) + + @property + @persisted('_groups') + def groups(self) -> pd.DataFrame: + """A dataframe of TUIs, their abbreviations, descriptions and a group + name associated with each. + + """ + self._assert_installed() + path: Path = self.installer[self.umls_groups] + df = pd.read_csv(path, delimiter='|', header=None) + df.columns = 'abbrev name tui desc'.split() + return df + + @property + @persisted('_cat') + def cat(self) -> CAT: + """The MedCAT NER tagger instance. + + When this property is accessed, all models are downloaded first, then + loaded, if not already. + + """ + self._assert_installed() + # Load the vocab model you downloaded + vocab = Vocab.load(self.installer[self.vocab_resource]) + # Load the cdb model you downloaded + cdb = CDB.load(self.installer[self.cdb_resource]) + # mc status model + mc_status = MetaCAT.load(self.installer[self.mc_status_resource]) + # enable sentence boundary annotation + for name in self.spacy_enable_components: + cdb.config.general['spacy_disabled_components'].remove(name) + # override configuration + if self.cat_config is not None: + self._override_config(cdb.config, self.cat_config) + # add TUI filters (i.e. filter out non-medical terms) + self._add_filters(cdb.config, cdb) + # ensure models are installed + self._assert_spacy_models() + # create cat - each cdb comes with a config that was used to train it; + # you can change that config in any way you want, before or after + # creating cat + try: + cat = CAT(cdb=cdb, config=cdb.config, vocab=vocab, + meta_cats=[mc_status]) + except OSError as e: + msg: str = str(e) + if msg.find("Can't find model") == -1: + raise e + else: + logger.info('no scispacy model found--attempting to install') + self._install_model() + cat = CAT(cdb=cdb, config=cdb.config, vocab=vocab, + meta_cats=[mc_status]) + return cat + + def _install_model(self): + if self.requirements_dir is None: + raise APIError('model not installed and no requirements found') + else: + from pip._internal import main as pipmain + req_file: Path + for req_file in self.requirements_dir.iterdir(): + pipmain(['install', '--use-deprecated=legacy-resolver', + '-r', str(req_file), '--no-deps']) + + def _get_model_requirements(self) -> Iterable[Tuple[str, str]]: + path: Path + for path in self.requirements_dir.iterdir(): + with open(path) as f: + line: str + for line in map(str.strip, f.readlines()): + m: re.Match = self._MODEL_REGEX.match(line) + if m is not None: + yield (m.group(1), line) + + def _install_dependency(self, dep: str): + from pip._internal import main as pipmain + pipmain(['install', '--use-deprecated=legacy-resolver', + dep, '--no-deps']) + + def _assert_spacy_models(self): + missing: Set[str] = set() + model_name: str + for model_name in self.auto_install_models: + if not spacy.util.is_package(model_name): + missing.add(model_name) + if len(missing) > 0: + if logger.isEnabledFor(logging.INFO): + logger.info(f'installing missing models: {missing}') + reqs: Dict[str, str] = dict(self._get_model_requirements()) + for model_name in missing: + dep: str = reqs.get(model_name) + if dep is None: + raise APIError( + f'Resource needs unmapped model: {model_name}') + self._install_dependency(dep) + +
+[docs] + def clear(self): + self._tuis.clear() + self._cat.clear()
+
+ + + +MedCatResource._filter_medcat_logger() +
+
+
+
+ + +
+
+ + Made with Sphinx and @pradyunsg's + + Furo + +
+
+ +
+
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/_modules/zensols/mednlp/tok.html b/_modules/zensols/mednlp/tok.html new file mode 100644 index 0000000..2c53556 --- /dev/null +++ b/_modules/zensols/mednlp/tok.html @@ -0,0 +1,387 @@ + + + + + + + + zensols.mednlp.tok - Medical natural language parsing and utility library 1.7.0 documentation + + + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark mode + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for zensols.mednlp.tok

+"""Contains the classes for the medical token type.
+
+"""
+__author__ = 'Paul Landes'
+
+from typing import Dict, Tuple, Optional, Union
+import logging
+from functools import reduce
+from frozendict import frozendict
+from spacy.tokens.token import Token
+from spacy.tokens.span import Span
+from medcat.cdb import CDB
+from zensols.nlp import FeatureToken, SpacyFeatureToken
+from . import MedCatResource
+from .domain import _MedicalEntity
+
+logger = logging.getLogger(__name__)
+
+
+
+[docs] +class MedicalFeatureToken(SpacyFeatureToken): + """A set of token features that optionally contains a medical concept. + + """ + FEATURE_IDS_BY_TYPE = frozendict({ + 'str': frozenset(('cui_ pref_name_ detected_name_ tuis_ ' + + 'definition_ tui_descs_').split()), + 'bool': frozenset('is_concept'.split()), + 'float': frozenset('context_similarity'.split()), + 'int': frozenset('cui'.split()), + 'list': frozenset('tuis sub_names'.split())}) + FEATURE_IDS = frozenset( + reduce(lambda res, x: res | x, FEATURE_IDS_BY_TYPE.values())) + WRITABLE_FEATURE_IDS = tuple(list(FeatureToken.WRITABLE_FEATURE_IDS) + + 'cui_'.split()) + _NONE_SET = frozenset() + +
+[docs] + def __init__(self, spacy_token: Union[Token, Span], norm: str, + res: MedCatResource, ix2ent: Dict[int, _MedicalEntity]): + super().__init__(spacy_token, norm) + self._definition: str = self.NONE + self._cdb: CDB = res.cat.cdb + self._res = res + med_ent: Optional[_MedicalEntity] = ix2ent.get(self.idx) + if med_ent is None: + med_ent = _MedicalEntity() + self.med_ent = med_ent + self.is_ent = med_ent.is_ent
+ + + @property + def ent_(self) -> str: + # self.med_ent.concept_span.label_ just gives 'concept', which then + # clobbers other useful entities in the combiner parsers + return self.NONE + + @property + def ent(self) -> int: + # self.med_ent.concept_span.label just gives 'concept', which then + # clobbers other useful entities in the combiner parsers + return 0 + + @property + def is_concept(self) -> bool: + """``True`` if this has a CUI and identifies a medical concept.""" + return self.is_ent + + @property + def cui_(self) -> str: + """The unique UMLS concept ID.""" + return self.med_ent.cui_ if self.is_concept else self.NONE + + @property + def cui(self) -> int: + """Returns the numeric part of the concept ID.""" + return -1 if not self.is_concept else int(self.cui_[1:]) + + @property + def pref_name_(self) -> str: + """The preferred name of the concept.""" + if self.is_concept: + return self._cdb.cui2preferred_name.get(self.cui_) + else: + return self.NONE + + @property + def detected_name_(self) -> str: + """The detected name of the concept.""" + if self.is_concept: + return self.med_ent.concept_span._.detected_name + else: + return self.NONE + + @property + def sub_names(self) -> Tuple[str, ...]: + """Return other names for the concept.""" + if self.is_concept: + return tuple(sorted(self._cdb.cui2names[self.cui_])) + else: + return [] + + @property + def context_similarity(self) -> float: + """The similiarity of the concept.""" + if self.is_concept: + return self.med_ent.concept_span._.context_similarity + else: + return -1 + + @property + def definition_(self) -> str: + """The definition if the concept.""" + return self._definition or FeatureToken.NONE + + @property + def tuis(self) -> Tuple[str, ...]: + """The the CUI type of the concept.""" + if self.is_concept: + cui: str = self.cui_ + return tuple(sorted(self._cdb.cui2type_ids.get(cui))) + else: + return self._NONE_SET + + @property + def tuis_(self) -> str: + """All CUI TUIs (types) of the concept sorted as a comma delimited list. + + """ + return ','.join(sorted(self.tuis)) + + @property + def tui_descs_(self) -> str: + """Descriptions of :obj:`tuis_`.""" + def map_tui(k: str) -> str: + v = self._res.tuis.get(k) + if v is None: + v = f'? ({k})' + return v + + return ', '.join(map(map_tui, sorted(self.tuis))) + + def __str__(self): + cui_str = f' ({self.cui_})' if self.is_concept else '' + return self.norm + cui_str
+ +
+
+
+
+ + +
+
+ + Made with Sphinx and @pradyunsg's + + Furo + +
+
+ +
+
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/_modules/zensols/mednlp/uts.html b/_modules/zensols/mednlp/uts.html new file mode 100644 index 0000000..e9bf3d7 --- /dev/null +++ b/_modules/zensols/mednlp/uts.html @@ -0,0 +1,558 @@ + + + + + + + + zensols.mednlp.uts - Medical natural language parsing and utility library 1.7.0 documentation + + + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark mode + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for zensols.mednlp.uts

+"""Interface to the UTS (UMLS Terminology Services (UTS)) RESTful service,
+which was taken from the UTS example repo.
+
+:see `UTS GitHug repo <https://github.com/HHS/uts-rest-api/>`_
+
+"""
+__author__ = 'Paul Landes'
+
+from typing import List, Dict, Any, Tuple, Union
+from dataclasses import dataclass, field
+import logging
+import re
+import json
+from json.decoder import JSONDecodeError
+import requests
+from lxml.html import fromstring
+from lxml.etree import _Element as Element
+from zensols.persist import Stash
+from . import MedNLPError
+
+logger = logging.getLogger(__name__)
+
+
+
+[docs] +class UTSError(MedNLPError): + """An error thrown by wrapper of the UTS system. + + """ + pass
+ + + +
+[docs] +class NoResultsError(UTSError): + """Thrown when no results, usually for a CUI not found. + + """ + pass
+ + + +
+[docs] +class AuthenticationError(UTSError): + """Thrown when authentication fails.""" +
+[docs] + def __init__(self, api_key: str): + super().__init__(f'Authentication error using key: {api_key}') + self.api_key = api_key
+
+ + + +
+[docs] +@dataclass +class Authentication(object): + """A utility class to manage the authentication with the UTS system. + + """ + SERVICE = 'http://umlsks.nlm.nih.gov' + """The service endpoint URL.""" + + AUTH_URI = 'https://utslogin.nlm.nih.gov' + """The authetication service endpoint URL.""" + + api_key: str = field() + """The API key used for the RESTful NIH service.""" + + auth_endpoint: str = field(default='/cas/v1/api-key') + """The path of the authentication service endpoint.""" + +
+[docs] + def gettgt(self): + params = {'apikey': self.api_key} + h = {'Content-type': 'application/x-www-form-urlencoded', + 'Accept': 'text/plain', + 'User-Agent': 'python'} + r = requests.post( + self.AUTH_URI + self.auth_endpoint, data=params, headers=h) + if r.text[0] == '{': + try: + obj = json.loads(r.text) + if 'authentication_exceptions' in obj: + raise AuthenticationError(self.api_key) + except JSONDecodeError as e: + logger.warning('looks like JSON, but not decodable: ' + + f'<{r.text}>: {e}') + response: Element = fromstring(r.text) + if isinstance(response, Element): + err: str = None + if response.tag == 'p': + try: + content: str = json.loads(response.text) + if 'name' in content and 'message' in content: + name: str = content['name'] + err = f"{name}: {content['message']}" + if name == 'UnauthorizedError': + raise AuthenticationError(err) + else: + raise UTSError(f'Unknown response: {content}') + except json.decoder.JSONDecodeError as e: + err = f'Appears to be an error, but can not parse: {e}' + raise UTSError(err) from e + if err is not None: + raise UTSError(err) + # extract the entire URL needed from the HTML form (action attribute) + # returned - looks similar to + # https://utslogin.nlm.nih.gov/cas/v1/tickets/TGT-36471-aYqNLN2rFIJPXKzxwdTNC5ZT7z3B3cTAKfSc5ndHQcUxeaDOLN-cas + # we make a POST call to this URL in the getst method + tgt = response.xpath('//form/@action')[0] + return tgt
+ + +
+[docs] + def getst(self, tgt): + params = {'service': self.SERVICE} + h = {'Content-type': 'application/x-www-form-urlencoded', + 'Accept': 'text/plain', + 'User-Agent': 'python'} + r = requests.post(tgt, data=params, headers=h) + st = r.text + return st
+
+ + + +
+[docs] +@dataclass +class UTSClient(object): + URI = 'https://uts-ws.nlm.nih.gov' + """The service URL endpoint.""" + + REL_ID_REGEX = re.compile(r'.*CUI\/(.+)$') + """Used to parse related CUIs in :meth:`get_related_cuis`.""" + + NO_RESULTS_ERR = 'No results containing all your search terms were found.' + """Error message from UTS indicating a missing CUI.""" + + MISSING_VALUE = '<missing>' + """Value to store in the stash when there is a missing CUI.""" + + api_key: str = field() + """The API key used for the RESTful NIH service.""" + + version: str = field(default='2020AA') + """The version of the UML we want.""" + + request_stash: Stash = field(default=None) + + def _get_ticket(self) -> str: + """Generate a new service ticket for each page if needed.""" + if logger.isEnabledFor(logging.INFO): + logger.info(f'logging in to UTS with {self.api_key}') + auth_client = Authentication(self.api_key) + tgt = auth_client.gettgt() + return auth_client.getst(tgt) + + def _parse_json(self, s: str) -> Union[Exception, Dict[str, Any]]: + try: + return json.loads(s) + except JSONDecodeError as e: + logger.debug(f'can not parse: <{s}>: {e}') + return e + + def _request_remote(self, url: str, query: Dict[str, str], + expect: bool) -> Any: + query['ticket'] = self._get_ticket() + r = requests.get(url, params=query) + r.encoding = 'utf-8' + items = self._parse_json(r.text) + if isinstance(items, Exception): + raise UTSError(f'Could not parse: <{r.text}>, ' + + f'code: {r.status_code}') from items + err = items.get('error') + if err is not None and err == self.NO_RESULTS_ERR: + if not expect: + return None + raise NoResultsError( + f'Could not request {url}, query={query}: {err}') + if r.status_code != 200: + if err is None: + msg = f'response: <{r.text}>' + else: + msg = err + raise UTSError(f'Could not request {url}, query={query}: {msg}') + if err is not None: + raise UTSError(f'Could not request {url}, query={query}: {err}') + if 'result' not in items: + raise UTSError(f'Unknown resposne: <{r.text}>') + return items['result'] + + def _request_cache(self, url: str, query: Dict[str, str], + expect: bool) -> Any: + q = '&'.join(map(lambda k: f'{k}={query[k]}', sorted(query.keys()))) + key = url + '?' + q + if logger.isEnabledFor(logging.DEBUG): + logger.debug(f'key: {key}') + val = self.request_stash.load(key) + if val is None: + val = self._request_remote(url, query, expect) + if val is None: + val = self.MISSING_VALUE + self.request_stash.dump(key, val) + if val == self.MISSING_VALUE: + val = None + return val + + def _request(self, url: str, query: Dict[str, str], expect: bool) -> Any: + if self.request_stash is None: + return self._request_remote(url, query, expect) + else: + return self._request_cache(url, query, expect) + +
+[docs] + def search_term(self, term: str, pages: int = 1) -> List[Dict[str, str]]: + """Search for a string term in UMLS. + + :param term: the string term to match against + + :return: a list (one for each page), each with a dictionary of matching + terms that have the ``name`` of the term, the ``ui`` (CUI), + the ``uri`` of the term and the ``rootSource`` of the + orginitating system + + """ + url = '{uri}/rest/search/{version}'.format( + **{'uri': self.URI, 'version': self.version}) + res = [] + for page_n in range(pages): + if logger.isEnabledFor(logging.DEBUG): + logger.debug(f'fetching page {page_n}') + query = {'string': term, + 'page_n': page_n} + res.extend(self._request(url, query, False)['results']) + return res
+ + +
+[docs] + def get_atoms(self, cui: str, preferred: bool = True, + expect: bool = True) -> \ + Union[Dict[str, str], List[Dict[str, str]]]: + """Get the UMLS atoms of a CUI from UTS. + + :param cui: the concept ID used to query + + :param preferred: if ``True`` only return preferred atoms + + :return: a list of atom entries in dictionary form or a single dict if + ` ``preferred`` is ``True`` + + """ + pat = '{uri}/rest/content/{version}/CUI/{cui}/atoms/' + if preferred: + pat += 'preferred/' + url = pat.format( + **{'uri': self.URI, 'version': self.version, 'cui': cui}) + if logger.isEnabledFor(logging.DEBUG): + logger.debug(f'fetching atom {cui}') + return self._request(url, {}, expect)
+ + +
+[docs] + def get_relations(self, cui: str, expect: bool = True) -> \ + List[Dict[str, Any]]: + """Get the UMLS related concepts connected to a concept by ID. + + :param cui: the concept ID used to get related concepts + + :return: a list of relation entries in dictionary form in the order + returned by UTS + + """ + url = '{uri}/rest/content/{version}/CUI/{cui}/relations/'.format( + **{'uri': self.URI, 'version': self.version, 'cui': cui}) + try: + return self._request(url, {}, expect) + except NoResultsError as e: + if expect: + raise e
+ + + +
+ +
+
+
+
+ + +
+
+ + Made with Sphinx and @pradyunsg's + + Furo + +
+
+ +
+
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/_sources/CHANGELOG.md.txt b/_sources/CHANGELOG.md.txt new file mode 100644 index 0000000..8f4915b --- /dev/null +++ b/_sources/CHANGELOG.md.txt @@ -0,0 +1,176 @@ +# Change Log +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](http://keepachangelog.com/) +and this project adheres to [Semantic Versioning](http://semver.org/). + + +## [Unreleased] + + +## [1.7.0] - 2024-04-14 +### Removed +- Deprecated feature document parser `mednlp_combine_medfirst_doc_parser` from + resource library. + +### Changed +- Renamed `MeddicalFeatureDocumentParser` to `MedCatFeatureDocumentParser` + since the parser only adds MedCAT CUIs. +- Fixed the configured parser that adds both ScispaCy biomed entities and + MedCAT CUIs in `mednlp_combine_biomed_doc_parser`. +- Separated combiner with non-combiner models in configuration space. The + non-combiners (biomed ScispaCy and MedCAT) are parses that are used in their + respective combiners. These are then used by a new composite parser that + uses both (`mednlp_combine_biomed_doc_parser`). Unit test case added for + all configured parsers. + + +## [1.6.0] - 2024-02-27 +### Added +- Added a ScispaCy biomedical document parser, which is enabled by setting + `mednlp_default:doc_parser = mednlp_combine_biomed_doc_parser`. This uses + better linguistic features and detects more entity span(s). + +### Changed +- Fixed normalize token and sentence/token indexes from being clobbered by + combined medical parsers. +- Fixed numeric entity feature from the combined medical parser is non-zero + for medical entities. +- Combined medical parser defines linguistic and medical features in the + document parser object instance. + + +## [1.5.0] - 2023-12-05 +### Changed +- Upgrade libraries: `numpy`, `lxml`, `scikitlearn`, `scipy`, `medcat`. +- Added `scispacy` dependency. + +### Added +- Support for Python 3.11. + +### Removed +- Support for Python 3.9. + + +## [1.4.1] - 2023-09-08 +### Changed +- Fix unit tests using the model provided for the MedCAT tutorials. +- Re-enable GitHub workflow unit tests CI. + + +## [1.4.0] - 2023-08-16 +Downstream moderate risk update release. + +### Changed +- Switch order of medical specific and general spaCy language parsing and + chunking. Now the medical parser is the source parser and the default spaCy + parser is the target in `MappingCombinerFeatureDocumentParser`. This was + done to get better sentence chunking as MedCAT (used in the medical parser) + does not sentence chunk well as it was not designed for it. +- Upgrade to [zensols.util] 1.13.0 +- Fix `cui2vec` weight archive re-download on each access. +- Mapping combiner default is to use token's character absolute index. + + +## [1.3.2] - 2023-06-29 +### Added +- Feature document parser shortcut from application factory. + +### Changed +- Resource library configuration to clean up model resources after download. + + +## [1.3.1] - 2023-06-27 +### Changed +- Updated to [zensols.nlp] to 1.7.2 and use its new feature to auto load any + missing spaCy base model(s). +- Remove configuration file requirement from the CLI. +- Fix spaCy dependency requirement to align with `scipy` and [zensols.nlp]. + + +## [1.3.0] - 2023-06-20 +### Changed +- Fix contraction tokenization. This was done by swapping the target and + source parser in the `lang.conf:mednlp_combine_doc_parser` resource library + configuration. + + +## [1.2.0] - 2023-06-09 +### Changed +- Upgrade to [medcat] 1.7.0. +- Better UTS error handling with raised exceptions and messages. + + +## [1.1.0] - 2023-04-05 +### Changed +- Install missing models and packages on start up. +- Updated [zensols.install] to 0.2.1 +- Updated [zensols.nlp] to 1.6.0. + + +## [1.0.0] - 2023-02-02 +### Changed +- Updated [zensols.util] to 1.12.1. +- Updated [zensols.nlp] to 1.5.0. + + +## [0.1.1] - 2022-10-02 +### Removed +- Make scispacy optional and remove the dependency. See the test cases in + [test](test/entlink). + + +## [0.1.0] - 2022-10-01 +### Added +- Unit test in GitHub CI. + +### Changed +- Upgrade to spaCy 2.2, MedCAT 3.0, `zensols.nlp` 1.4.0. +- Make `cui2vec` a standard word embedding with vectorizer and layer. +- Replace entity splitter by not embedding named entities as default + configuration. + +### Removed +- Support for Python 3.7, 3.8 from dropped support in `zensols.util`. + + +## [0.0.2] - 2022-05-04 +### Added +- A CSV features dump example. + +### Changed +- Use token instead of sentence level mapping in cases where MedCAT creates + unaligned sentence boundaries. +- Make medical parser stand-alone and use delegate mapping combiner instead of + using a class hierarchy. +- Entity linker is now a token decorator. +- Protect against `unk` (unknown) keys in TUIs. + + +## [0.0.1] - 2022-01-30 +### Added +- Initial version. + + + +[Unreleased]: https://github.com/Paul Landes/mednlp/compare/v1.7.0...HEAD +[1.7.0]: https://github.com/Paul Landes/mednlp/compare/v1.6.0...v1.7.0 +[1.6.0]: https://github.com/Paul Landes/mednlp/compare/v1.5.0...v1.6.0 +[1.5.0]: https://github.com/Paul Landes/mednlp/compare/v1.4.1...v1.5.0 +[1.4.1]: https://github.com/Paul Landes/mednlp/compare/v1.4.0...v1.4.1 +[1.4.0]: https://github.com/Paul Landes/mednlp/compare/v1.3.2...v1.4.0 +[1.3.2]: https://github.com/Paul Landes/mednlp/compare/v1.3.1...v1.3.2 +[1.3.1]: https://github.com/Paul Landes/mednlp/compare/v1.3.0...v1.3.1 +[1.3.0]: https://github.com/Paul Landes/mednlp/compare/v1.2.0...v1.3.0 +[1.2.0]: https://github.com/Paul Landes/mednlp/compare/v1.1.0...v1.2.0 +[1.1.0]: https://github.com/Paul Landes/mednlp/compare/v1.0.0...v1.1.0 +[1.0.0]: https://github.com/Paul Landes/mednlp/compare/v0.1.1...v1.0.0 +[0.1.1]: https://github.com/Paul Landes/mednlp/compare/v0.1.0...v0.1.1 +[0.1.0]: https://github.com/Paul Landes/mednlp/compare/v0.0.2...v0.1.0 +[0.0.2]: https://github.com/Paul Landes/mednlp/compare/v0.0.1...v0.0.2 +[0.0.1]: https://github.com/Paul Landes/mednlp/compare/v0.0.0...v0.0.1 + +[zensols.util]: https://github.com/plandes/util +[zensols.nlp]: https://github.com/plandes/nlparse +[zensols.install]: https://github.com/plandes/install +[medcat]: https://github.com/CogStack/MedCAT diff --git a/_sources/CONTRIBUTING.md.txt b/_sources/CONTRIBUTING.md.txt new file mode 100644 index 0000000..89c8df6 --- /dev/null +++ b/_sources/CONTRIBUTING.md.txt @@ -0,0 +1,47 @@ +# Contributing + +If you discover issues, have ideas for improvements or new features, please +report them to the [issue tracker][1] of the repository or submit a pull +request. Please, try to follow these guidelines when you do so. + + +## Issue reporting + +* Check that the issue has not already been reported. +* Check that the issue has not already been fixed in the latest code + (a.k.a. `master`). +* Be clear, concise and precise in your description of the problem. +* Open an issue with a descriptive title and a summary in grammatically correct, + complete sentences. +* Mention your `mednlp` Python dependency version and operating + system. +* Include any relevant code to the issue summary. + + +### Reporting bugs + +Adding information like the backtrace and the REPL messages to the bug report +makes it easier to track down bugs. Some steps to reproduce a bug reliably +would also make a huge difference. + + +## Pull requests + +* Read [how to properly contribute to open source projects on Github][2]. +* Use a topic branch to easily amend a pull request later, if necessary. +* Use the same coding conventions as the rest of the project. +* Make sure that the unit tests are passing (`make test`). +* Write [good commit messages][3]. +* Mention related tickets in the commit messages (e.g. `[Fix #N] Add command ...`). +* Update the [changelog][6]. +* [Squash related commits together][5]. +* Open a [pull request][4] that relates to *only* one subject with a clear title + and description in grammatically correct, complete sentences. + + +[1]: https://github.com/plandes/mednlp/issues +[2]: http://gun.io/blog/how-to-github-fork-branch-and-pull-request +[3]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html +[4]: https://help.github.com/articles/using-pull-requests +[5]: http://gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html +[6]: https://github.com/plandes/mednlp/blob/master/CHANGELOG.md diff --git a/_sources/LICENSE.md.txt b/_sources/LICENSE.md.txt new file mode 100644 index 0000000..ca5a3bf --- /dev/null +++ b/_sources/LICENSE.md.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 - 2023 Paul Landes + +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. diff --git a/_sources/api.rst.txt b/_sources/api.rst.txt new file mode 100644 index 0000000..26d1e58 --- /dev/null +++ b/_sources/api.rst.txt @@ -0,0 +1,8 @@ +API Reference +============= + +.. toctree:: + :maxdepth: 2 + :glob: + + api/* diff --git a/_sources/api/zensols.mednlp.rst.txt b/_sources/api/zensols.mednlp.rst.txt new file mode 100644 index 0000000..504a105 --- /dev/null +++ b/_sources/api/zensols.mednlp.rst.txt @@ -0,0 +1,123 @@ +zensols.mednlp package +====================== + +Submodules +---------- + +zensols.mednlp.app +------------------ +.. inheritance-diagram:: zensols.mednlp.app + :parts: 1 + +.. automodule:: zensols.mednlp.app + :members: + :undoc-members: + :show-inheritance: + +zensols.mednlp.cli +------------------ +.. inheritance-diagram:: zensols.mednlp.cli + :parts: 1 + +.. automodule:: zensols.mednlp.cli + :members: + :undoc-members: + :show-inheritance: + +zensols.mednlp.ctakes +--------------------- +.. inheritance-diagram:: zensols.mednlp.ctakes + :parts: 1 + +.. automodule:: zensols.mednlp.ctakes + :members: + :undoc-members: + :show-inheritance: + +zensols.mednlp.cui2vec +---------------------- +.. inheritance-diagram:: zensols.mednlp.cui2vec + :parts: 1 + +.. automodule:: zensols.mednlp.cui2vec + :members: + :undoc-members: + :show-inheritance: + +zensols.mednlp.domain +--------------------- +.. inheritance-diagram:: zensols.mednlp.domain + :parts: 1 + +.. automodule:: zensols.mednlp.domain + :members: + :undoc-members: + :show-inheritance: + +zensols.mednlp.entlink +---------------------- +.. inheritance-diagram:: zensols.mednlp.entlink + :parts: 1 + +.. automodule:: zensols.mednlp.entlink + :members: + :undoc-members: + :show-inheritance: + +zensols.mednlp.lib +------------------ +.. inheritance-diagram:: zensols.mednlp.lib + :parts: 1 + +.. automodule:: zensols.mednlp.lib + :members: + :undoc-members: + :show-inheritance: + +zensols.mednlp.parser +--------------------- +.. inheritance-diagram:: zensols.mednlp.parser + :parts: 1 + +.. automodule:: zensols.mednlp.parser + :members: + :undoc-members: + :show-inheritance: + +zensols.mednlp.resource +----------------------- +.. inheritance-diagram:: zensols.mednlp.resource + :parts: 1 + +.. automodule:: zensols.mednlp.resource + :members: + :undoc-members: + :show-inheritance: + +zensols.mednlp.tok +------------------ +.. inheritance-diagram:: zensols.mednlp.tok + :parts: 1 + +.. automodule:: zensols.mednlp.tok + :members: + :undoc-members: + :show-inheritance: + +zensols.mednlp.uts +------------------ +.. inheritance-diagram:: zensols.mednlp.uts + :parts: 1 + +.. automodule:: zensols.mednlp.uts + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: zensols.mednlp + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/doc/api-usage.md.txt b/_sources/doc/api-usage.md.txt new file mode 100644 index 0000000..198de61 --- /dev/null +++ b/_sources/doc/api-usage.md.txt @@ -0,0 +1,336 @@ +# Medical NLP and Utility API + +This API primarily wraps others with the [Zensols Framework] to provide easy +way and reproducible method of utilization and experimentation with medical and +clinical natural language text. It provides the following functionality: + +* [UMLS Access via UTS] +* [Medical Concept and Entity Linking] +* [Using CUI as Word Embeddings](#using-cui-as-word-embeddings) +* [Entity Linking with cTAKES](#entity-linking-with-ctakes) + +The rest of this document is structured as a cookbook style tutorial. Each +sub-section describes the examples in the [examples] directory. + +**Important**: many of the examples use [UMLS] UTS service, which requires a +key that is provided by NIH. If you do not have a key, request one and add it +to the [UTS key file]. + + +## Medical Concept and Entity Linking + +Concept linking with [CUIs] is provided using the same interface as the +[Zensols NLP parsing API]. The resource library provided with this package +creates a `mednlp_doc_parser` as shown in the [entity-example]. First we start +with the configuration with file name `features.conf`, which starts with +telling the [CLI] to import the [Zensols NLP package] and this +(`zensols.mednlp`) package: +```ini +[import] +sections = list: imp_conf + +[imp_conf] +type = importini +config_files = list: + resource(zensols.nlp): resources/obj.conf, + resource(zensols.nlp): resources/mapper.conf, + resource(zensols.mednlp): resources/lang.conf +``` + +Next configure the parser with specific features, since otherwise, the parser +will retain all medical and non-medical features: +```ini +[mednlp_doc_parser] +token_feature_ids = set: norm, is_ent, cui, cui_, pref_name_, detected_name_, is_concept, ent_, ent +``` + +Finally, declare the application, which is needed by the [CLI] glue code to +invoke the class we will write afterward: +```ini +[app] +class_name = ${program:name}.Application +doc_parser = instance: mednlp_doc_parser +``` + +Next comes the application class: +```python +@dataclass +class Application(object): + doc_parser: FeatureDocumentParser = field() + + def show(self, sent: str = None): + if sent is None: + sent = 'He was diagnosed with kidney failure in the United States.' + doc: FeatureDocument = self.doc_parser(sent) + print('first three tokens:') + for tok in it.islice(doc.token_iter(), 3): + print(tok.norm) + tok.write_attributes(1, include_type=False) +``` +This uses the document parser to create the feature document, which has both +the medical and linguistic features in tokens (provided by `token_iter()`) of the document. + +Use the [CLI] API in the entry point to use the configuration and application +class: +```python +if (__name__ == '__main__'): + CliHarness( + app_config_resource='uts.conf', + app_config_context=ProgramNameConfigurator( + None, default='uts').create_section(), + proto_args='', + ).run() +``` + +Running the program produces one such token data: +``` +... +diagnosed + cui=11900 + cui_=C0011900 + detected_name_=diagnosed + ent=13188083023294932426 + ent_=concept + i=2 + i_sent=2 + idx=7 + is_concept=True + is_ent=True + norm=diagnosed + pref_name_=Diagnosis +... +``` +See the full [entity example] for the full example code, which will also output +both linguistic and medical features as a [Pandas] data frame. + + +## UMLS Access via UTS + +NIH provides a very rough REST client using the `requests` library given as an +example. This API takes that example, adds some "rigor" and structure in a +an easy to use class called `UTSClient`. This is configured by first defining +paths for where fetched entities are cached: +```ini +[default] +# root directory given by the application, which is the parent directory +root_dir = ${appenv:root_dir}/.. +# the directory to hold the cached UMLS data +cache_dir = ${root_dir}/cache +``` + +Next, import the this package's resource library (`zensols.mednlp`). Note we +have to refer to sections that substitute the `default` section's data: +```ini +[import] +references = list: uts, default +sections = list: imp_uts_key, imp_conf + +[imp_conf] +type = importini +config_file = resource(zensols.mednlp): resources/uts.conf + +[imp_uts_key] +type = json +default_section = uts +config_file = ${default:root_dir}/uts-key.json +``` +The `imp_uts_key` points to a file where you put add your UTS key, which is +given by NIH. + +Now indicate where to cache the [UMLS] data and define our application we'll +write afterward: +```ini +# UTS (UMLS access) +[uts] +cache_file = ${default:cache_dir}/uts-request.dat +``` + +For brevity the [CLI] application code and configuration is omitted (see [UMLS +Access via UTS] for more detail). + +To use the API to first search a term, then print entity information, we can +use the `search_term` method with `get_atoms`: +```python +@dataclass +class Application(object): + ... + def lookup(self, term: str = 'heart'): + # terms are returned as a list of pages with dictionaries of data + pages: List[Dict[str, str]] = self.uts_client.search_term(term) + # get all term dictionaries from the first page + terms: Dict[str, str] = pages[0] + # get the concept unique identifier + cui: str = terms['ui'] + + # print atoms of this concept + print('atoms:') + pprint(self.uts_client.get_atoms(cui)) +``` +This yields the following output: +``` +atoms: +{'ancestors': None, + 'classType': 'Atom', + 'code': 'https://uts-ws.nlm.nih.gov/rest/content/2020AA/source/MTH/NOCODE', + 'concept': 'https://uts-ws.nlm.nih.gov/rest/content/2020AA/CUI/C0018787', + 'contentViewMemberships': [{'memberUri': 'https://uts-ws.nlm.nih.gov/rest/content-views/2020AA/CUI/C1700357/member/A0066369', + 'name': 'MetaMap NLP View', + 'uri': 'https://uts-ws.nlm.nih.gov/rest/content-views/2020AA/CUI/C1700357'}], + 'name': 'Heart', + 'obsolete': 'false', + 'rootSource': 'MTH', +... +} +``` + +See the full [UTS example] for the full example code. + + +## Using CUI as Word Embeddings + +[cui2vec] was trained and can be in the same way as [word2vec]. Such examples +is computing a similarity between [UMLS] [CUIs]. This API provides access to +the vectors directly along with all the functionality using [cui2vec] with the +[gensim] package. This example computes the similarity between two medical +concepts. For brevity the [CLI] application code and configuration is omitted +(see [UMLS Access via UTS] for more detail). + +Let's jump right to how we import everything we need for the [cui2vec] example, +which the `uts` and `cui2vec` resource libraries: +```ini +[imp_conf] +type = importini +config_files = list: + resource(zensols.mednlp): resources/uts.conf, + resource(zensols.mednlp): resources/cui2vec.conf +``` +The UTS configuration is given as in the [UMLS Access via UTS] section and the +parser is configured as in the [Medical Concept and Entity Linking] section. + +With the high level classes given the configuration is class looks similar to +what we've seen before, this time we define a `similarity` method/[CLI] action: +```python +@dataclass +class Application(object): + def similarity(self, term: str = 'heart disease', topn: int = 5): +``` + +Next, get the [gensim] `KeyedVectors` instance, which provides (among *many* +other useful methods) one to compute the similarity between two words, or in +our case, two medical [CUIs]: +```python + embedding: Cui2VecEmbedModel = self.cui2vec_embedding + kv: KeyedVectors = embedding.keyed_vectors +``` + +Next we use UTS to get the term we're searching on, use [gensim] to find +similarities, and output them: +```python + res: List[Dict[str, str]] = self.uts_client.search_term(term) + cui: str = res[0]['ui'] + sims_by_word: List[Tuple[str, float]] = kv.similar_by_word(cui, topn) + for rel_cui, proba in sims_by_word: + rel_atom: Dict[str, str] = self.uts_client.get_atoms(rel_cui) + rel_name = rel_atom.get('name', 'Unknown') + print(f'{rel_name} ({rel_cui}): {proba * 100:.2f}%') +``` + +The output contains the top (`topn`) 5 matches and their similarity to the +search term in the example `heart`: +``` +Heart failure (C0018801): 72.03% +Atrial Premature Complexes (C0033036): 71.53% +Chronic myocardial ischemia (C0264694): 69.68% +Right bundle branch block (C0085615): 69.34% +First degree atrioventricular block (C0085614): 69.09% +``` + +See the full [cui2vec example] for the full example code. + + +## Entity Linking with cTAKES + +This package provides an interface to [cTAKES], which primarily manages the +file system and invokes the Java program to produce results. It then uses the +[ctakes-parser] to create a data frame of features and linked entities from +tokens of the source text. + +The configuration is a bit more involved since you have to indicate where the +[cTAKES] program is installed, and provide your NIH key as detailed in the +[UMLS Access via UTS] section: +```ini +[import] +# refer to sections for which we need substitution in this file +references = list: default, ctakes, uts +sections = list: imp_env, imp_uts_key, imp_conf + +# expose the user HOME environment variable +[imp_env] +type = environment +section_name = env +includes = set: HOME + +# import the Zensols NLP UTS resource library +[imp_conf] +type = importini +config_files = list: + resource(zensols.mednlp): resources/uts.conf, + resource(zensols.mednlp): resources/ctakes.conf + +# indicate where Apache cTAKES is installed +[ctakes] +home = ${env:home}/opt/app/ctakes-4.0.0.1 +source_dir = ${default:cache_dir}/ctakes/source +``` +For brevity the [CLI] application code and configuration is omitted, and other +configuration given in previous sections (see [UMLS Access via UTS] for more +detail). See the full [cTAKES example] for the full example code. + +The pertinent snippet to get the [Pandas] data frame from the medical text is +very simple: +```python +@dataclass +class Application(object): + def entities(self, sent: str = None, output: Path = None): + if sent is None: + sent = 'He was diagnosed with kidney failure in the United States.' + self.ctakes_stash.set_documents([sent]) + df: pd.DataFrame = self.ctakes_stash['0'] + print(df) + if output is not None: + df.to_csv(output) + print(f'wrote: {output}') +``` +The `set_documents` expects a list of text, which is saved to disk. When +[cTAKES] is run, the directory where this list of text is saved (one file per +element in the list). The access to the [Stash] accesses the first document by +element ID. **Note**: the element ID has to be a string to follow the [Stash] +API. + + + +[UMLS Access via UTS]: #umls-access-via-uts +[Medical Concept and Entity Linking]: #medical-concept-and-entity-linking + +[UMLS]: https://www.nlm.nih.gov/research/umls/ +[CUIs]: https://www.nlm.nih.gov/research/umls/new_users/online_learning/Meta_005.html +[cui2vec]: https://arxiv.org/abs/1804.01486 +[word2vec]: https://arxiv.org/abs/1301.3781 + +[Pandas]: https://pandas.pydata.org +[gensim]: https://radimrehurek.com/gensim/ +[cTAKES]: https://ctakes.apache.org +[ctakes-parser]: https://pypi.org/project/ctakes-parser + +[Zensols Framework]: https://arxiv.org/abs/2109.03383 +[CLI]: https://plandes.github.io/util/doc/command-line.html +[Stash]: https://plandes.github.io/util/api/zensols.persist.html#zensols.persist.domain.Stash +[Zensols NLP package]: https://github.com/plandes/nlparse +[Zensols NLP parsing API]: https://plandes.github.io/nlparse/doc/feature-doc.html + +[examples]: https://github.com/plandes/mednlp/tree/master/example +[entity example]: https://github.com/plandes/mednlp/tree/master/example/features +[cTAKES example]: https://github.com/plandes/mednlp/tree/master/example/ctakes +[cui2vec example]: https://github.com/plandes/mednlp/tree/master/example/cui2vec +[UTS example]: https://github.com/plandes/mednlp/tree/master/example/uts +[UTS key file]: https://github.com/plandes/mednlp/tree/master/example/uts-key.json diff --git a/_sources/index.md.txt b/_sources/index.md.txt new file mode 100644 index 0000000..14d255c --- /dev/null +++ b/_sources/index.md.txt @@ -0,0 +1,166 @@ +# Medical natural language parsing and utility library + +[![PyPI][pypi-badge]][pypi-link] +[![Python 3.10][python310-badge]][python310-link] +[![Python 3.11][python311-badge]][python311-link] +[![Build Status][build-badge]][build-link] + +A natural language medical domain parsing library. This library: + +- Provides an interface to the [UTS] ([UMLS] Terminology Services) RESTful + service with data caching (NIH login needed). +- Wraps the [MedCAT] library by parsing medical and clinical text into first + class Python objects reflecting the structure of the natural language + complete with [UMLS] entity linking with [CUIs] and other domain specific + features. +- Combines non-medical (such as POS and NER tags) and medical features (such as + [CUIs]) in one API and resulting data structure and/or as a [Pandas] data + frame. +- Provides [cui2vec] as a [word embedding model] for either fast indexing and + access or to use directly as features in a [Zensols Deep NLP embedding layer] + model. +- Provides access to [cTAKES] using as a dictionary like [Stash] abstraction. +- Includes a command line program to access all of these features without + having to write any code. + + +## Documentation + +See the [full documentation](https://plandes.github.io/mednlp/index.html). +The [API reference](https://plandes.github.io/mednlp/api.html) is also +available. + + +## Obtaining + +The easiest way to install the command line program is via the `pip` installer. +```bash +pip3 install zensols.mednlp +``` + +Binaries are also available on [pypi]. + + +## Usage + +To parse text, create features, and extract clinical concept identifiers: +```python +>>> from zensols.mednlp import ApplicationFactory +>>> doc_parser = ApplicationFactory.get_doc_parser() +>>> doc = doc_parser('John was diagnosed with kidney failure') +>>> for tok in doc.tokens: print(tok.norm, tok.pos_, tok.tag_, tok.cui_, tok.detected_name_) +John PROPN NNP -- -- +was AUX VBD -- -- +diagnosed VERB VBN -- -- +with ADP IN -- -- +kidney NOUN NN C0035078 kidney~failure +failure NOUN NN C0035078 kidney~failure +>>> print(doc.entities) +(, ) +``` +See the [full example](example/features/simple.py), and for other +functionality, see the [examples](example). + + +## MedCAT Models + +By default, this library uses the small MedCAT model used for +[tutorials](https://github.com/CogStack/MedCATtutorials/pull/12), and is not +sufficient for any serious project. To get the UMLS trained model,the [MedCAT +UMLS request form] from be filled out (see the [MedCAT] repository). + +After you obtain access and download the new model, add the following to +`~/.mednlprc` with the following: + +```ini +[medcat_status_resource] +url = file:///location/to/the/downloaded/file/umls_sm_wstatus_2021_oct.zip' +``` + + +## Attribution + +This API utilizes the following frameworks: + +* [MedCAT]: used to extract information from Electronic Health Records (EHRs) + and link it to biomedical ontologies like SNOMED-CT and UMLS. +* [cTAKES]: a natural language processing system for extraction of information + from electronic medical record clinical free-text. +* [cui2vec]: a new set of (like word) embeddings for medical concepts learned + using an extremely large collection of multimodal medical data. +* [Zensols Deep NLP library]: a deep learning utility library for natural + language processing that aids in feature engineering and embedding layers. +* [ctakes-parser]: parses [cTAKES] output in to a [Pandas] data frame. + + +## Citation + +If you use this project in your research please use the following BibTeX entry: + +```bibtex +@inproceedings{landes-etal-2023-deepzensols, + title = "{D}eep{Z}ensols: A Deep Learning Natural Language Processing Framework for Experimentation and Reproducibility", + author = "Landes, Paul and + Di Eugenio, Barbara and + Caragea, Cornelia", + editor = "Tan, Liling and + Milajevs, Dmitrijs and + Chauhan, Geeticka and + Gwinnup, Jeremy and + Rippeth, Elijah", + booktitle = "Proceedings of the 3rd Workshop for Natural Language Processing Open Source Software (NLP-OSS 2023)", + month = dec, + year = "2023", + address = "Singapore, Singapore", + publisher = "Empirical Methods in Natural Language Processing", + url = "https://aclanthology.org/2023.nlposs-1.16", + pages = "141--146" +} +``` + + +## Community + +Please star the project and let me know how and where you use this API. +Contributions as pull requests, feedback and any input is welcome. + + +## Changelog + +An extensive changelog is available [here](CHANGELOG.md). + + +## License + +[MIT License](LICENSE.md) + +Copyright (c) 2021 - 2023 Paul Landes + + + +[pypi]: https://pypi.org/project/zensols.mednlp/ +[pypi-link]: https://pypi.python.org/pypi/zensols.mednlp +[pypi-badge]: https://img.shields.io/pypi/v/zensols.mednlp.svg +[python310-badge]: https://img.shields.io/badge/python-3.10-blue.svg +[python310-link]: https://www.python.org/downloads/release/python-3100 +[python311-badge]: https://img.shields.io/badge/python-3.11-blue.svg +[python311-link]: https://www.python.org/downloads/release/python-3110 +[build-badge]: https://github.com/plandes/mednlp/workflows/CI/badge.svg +[build-link]: https://github.com/plandes/mednlp/actions + +[MedCAT]: https://github.com/CogStack/MedCAT +[MedCAT UMLS request form]: https://uts.nlm.nih.gov/uts/login?service=https:%2F%2Fmedcat.rosalind.kcl.ac.uk%2Fauth-callback + +[Pandas]: https://pandas.pydata.org +[ctakes-parser]: https://pypi.org/project/ctakes-parser + +[UTS]: https://uts.nlm.nih.gov/uts/ +[UMLS]: https://www.nlm.nih.gov/research/umls/ +[CUIs]: https://www.nlm.nih.gov/research/umls/new_users/online_learning/Meta_005.html +[cui2vec]: https://arxiv.org/abs/1804.01486 +[cTAKES]: https://ctakes.apache.org +[word embedding model]: https://plandes.github.io/deepnlp/api/zensols.deepnlp.embed.html#zensols.deepnlp.embed.domain.WordEmbedModel +[Zensols NLP parsing API]: https://plandes.github.io/nlparse/doc/feature-doc.html +[Zensols Deep NLP library]: https://github.com/plandes/deepnlp +[Zensols Deep NLP embedding layer]: https://plandes.github.io/deepnlp/api/zensols.deepnlp.layer.html#zensols.deepnlp.layer.embed.EmbeddingNetworkModule +[Stash]: https://plandes.github.io/util/api/zensols.persist.html#zensols.persist.domain.Stash diff --git a/_sources/top.rst.txt b/_sources/top.rst.txt new file mode 100644 index 0000000..25ddaf3 --- /dev/null +++ b/_sources/top.rst.txt @@ -0,0 +1,20 @@ +Medical natural language parsing and utility library +==================================================== +A natural language medical domain parsing library. + +.. toctree:: + :maxdepth: 3 + :glob: + + Overview + doc/* + API Reference + Contributing + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/_static/basic.css b/_static/basic.css new file mode 100644 index 0000000..30fee9d --- /dev/null +++ b/_static/basic.css @@ -0,0 +1,925 @@ +/* + * basic.css + * ~~~~~~~~~ + * + * Sphinx stylesheet -- basic theme. + * + * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +/* -- main layout ----------------------------------------------------------- */ + +div.clearer { + clear: both; +} + +div.section::after { + display: block; + content: ''; + clear: left; +} + +/* -- relbar ---------------------------------------------------------------- */ + +div.related { + width: 100%; + font-size: 90%; +} + +div.related h3 { + display: none; +} + +div.related ul { + margin: 0; + padding: 0 0 0 10px; + list-style: none; +} + +div.related li { + display: inline; +} + +div.related li.right { + float: right; + margin-right: 5px; +} + +/* -- sidebar --------------------------------------------------------------- */ + +div.sphinxsidebarwrapper { + padding: 10px 5px 0 10px; +} + +div.sphinxsidebar { + float: left; + width: 230px; + margin-left: -100%; + font-size: 90%; + word-wrap: break-word; + overflow-wrap : break-word; +} + +div.sphinxsidebar ul { + list-style: none; +} + +div.sphinxsidebar ul ul, +div.sphinxsidebar ul.want-points { + margin-left: 20px; + list-style: square; +} + +div.sphinxsidebar ul ul { + margin-top: 0; + margin-bottom: 0; +} + +div.sphinxsidebar form { + margin-top: 10px; +} + +div.sphinxsidebar input { + border: 1px solid #98dbcc; + font-family: sans-serif; + font-size: 1em; +} + +div.sphinxsidebar #searchbox form.search { + overflow: hidden; +} + +div.sphinxsidebar #searchbox input[type="text"] { + float: left; + width: 80%; + padding: 0.25em; + box-sizing: border-box; +} + +div.sphinxsidebar #searchbox input[type="submit"] { + float: left; + width: 20%; + border-left: none; + padding: 0.25em; + box-sizing: border-box; +} + + +img { + border: 0; + max-width: 100%; +} + +/* -- search page ----------------------------------------------------------- */ + +ul.search { + margin: 10px 0 0 20px; + padding: 0; +} + +ul.search li { + padding: 5px 0 5px 20px; + background-image: url(file.png); + background-repeat: no-repeat; + background-position: 0 7px; +} + +ul.search li a { + font-weight: bold; +} + +ul.search li p.context { + color: #888; + margin: 2px 0 0 30px; + text-align: left; +} + +ul.keywordmatches li.goodmatch a { + font-weight: bold; +} + +/* -- index page ------------------------------------------------------------ */ + +table.contentstable { + width: 90%; + margin-left: auto; + margin-right: auto; +} + +table.contentstable p.biglink { + line-height: 150%; +} + +a.biglink { + font-size: 1.3em; +} + +span.linkdescr { + font-style: italic; + padding-top: 5px; + font-size: 90%; +} + +/* -- general index --------------------------------------------------------- */ + +table.indextable { + width: 100%; +} + +table.indextable td { + text-align: left; + vertical-align: top; +} + +table.indextable ul { + margin-top: 0; + margin-bottom: 0; + list-style-type: none; +} + +table.indextable > tbody > tr > td > ul { + padding-left: 0em; +} + +table.indextable tr.pcap { + height: 10px; +} + +table.indextable tr.cap { + margin-top: 10px; + background-color: #f2f2f2; +} + +img.toggler { + margin-right: 3px; + margin-top: 3px; + cursor: pointer; +} + +div.modindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +div.genindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +/* -- domain module index --------------------------------------------------- */ + +table.modindextable td { + padding: 2px; + border-collapse: collapse; +} + +/* -- general body styles --------------------------------------------------- */ + +div.body { + min-width: 360px; + max-width: 800px; +} + +div.body p, div.body dd, div.body li, div.body blockquote { + -moz-hyphens: auto; + -ms-hyphens: auto; + -webkit-hyphens: auto; + hyphens: auto; +} + +a.headerlink { + visibility: hidden; +} + +a:visited { + color: #551A8B; +} + +h1:hover > a.headerlink, +h2:hover > a.headerlink, +h3:hover > a.headerlink, +h4:hover > a.headerlink, +h5:hover > a.headerlink, +h6:hover > a.headerlink, +dt:hover > a.headerlink, +caption:hover > a.headerlink, +p.caption:hover > a.headerlink, +div.code-block-caption:hover > a.headerlink { + visibility: visible; +} + +div.body p.caption { + text-align: inherit; +} + +div.body td { + text-align: left; +} + +.first { + margin-top: 0 !important; +} + +p.rubric { + margin-top: 30px; + font-weight: bold; +} + +img.align-left, figure.align-left, .figure.align-left, object.align-left { + clear: left; + float: left; + margin-right: 1em; +} + +img.align-right, figure.align-right, .figure.align-right, object.align-right { + clear: right; + float: right; + margin-left: 1em; +} + +img.align-center, figure.align-center, .figure.align-center, object.align-center { + display: block; + margin-left: auto; + margin-right: auto; +} + +img.align-default, figure.align-default, .figure.align-default { + display: block; + margin-left: auto; + margin-right: auto; +} + +.align-left { + text-align: left; +} + +.align-center { + text-align: center; +} + +.align-default { + text-align: center; +} + +.align-right { + text-align: right; +} + +/* -- sidebars -------------------------------------------------------------- */ + +div.sidebar, +aside.sidebar { + margin: 0 0 0.5em 1em; + border: 1px solid #ddb; + padding: 7px; + background-color: #ffe; + width: 40%; + float: right; + clear: right; + overflow-x: auto; +} + +p.sidebar-title { + font-weight: bold; +} + +nav.contents, +aside.topic, +div.admonition, div.topic, blockquote { + clear: left; +} + +/* -- topics ---------------------------------------------------------------- */ + +nav.contents, +aside.topic, +div.topic { + border: 1px solid #ccc; + padding: 7px; + margin: 10px 0 10px 0; +} + +p.topic-title { + font-size: 1.1em; + font-weight: bold; + margin-top: 10px; +} + +/* -- admonitions ----------------------------------------------------------- */ + +div.admonition { + margin-top: 10px; + margin-bottom: 10px; + padding: 7px; +} + +div.admonition dt { + font-weight: bold; +} + +p.admonition-title { + margin: 0px 10px 5px 0px; + font-weight: bold; +} + +div.body p.centered { + text-align: center; + margin-top: 25px; +} + +/* -- content of sidebars/topics/admonitions -------------------------------- */ + +div.sidebar > :last-child, +aside.sidebar > :last-child, +nav.contents > :last-child, +aside.topic > :last-child, +div.topic > :last-child, +div.admonition > :last-child { + margin-bottom: 0; +} + +div.sidebar::after, +aside.sidebar::after, +nav.contents::after, +aside.topic::after, +div.topic::after, +div.admonition::after, +blockquote::after { + display: block; + content: ''; + clear: both; +} + +/* -- tables ---------------------------------------------------------------- */ + +table.docutils { + margin-top: 10px; + margin-bottom: 10px; + border: 0; + border-collapse: collapse; +} + +table.align-center { + margin-left: auto; + margin-right: auto; +} + +table.align-default { + margin-left: auto; + margin-right: auto; +} + +table caption span.caption-number { + font-style: italic; +} + +table caption span.caption-text { +} + +table.docutils td, table.docutils th { + padding: 1px 8px 1px 5px; + border-top: 0; + border-left: 0; + border-right: 0; + border-bottom: 1px solid #aaa; +} + +th { + text-align: left; + padding-right: 5px; +} + +table.citation { + border-left: solid 1px gray; + margin-left: 1px; +} + +table.citation td { + border-bottom: none; +} + +th > :first-child, +td > :first-child { + margin-top: 0px; +} + +th > :last-child, +td > :last-child { + margin-bottom: 0px; +} + +/* -- figures --------------------------------------------------------------- */ + +div.figure, figure { + margin: 0.5em; + padding: 0.5em; +} + +div.figure p.caption, figcaption { + padding: 0.3em; +} + +div.figure p.caption span.caption-number, +figcaption span.caption-number { + font-style: italic; +} + +div.figure p.caption span.caption-text, +figcaption span.caption-text { +} + +/* -- field list styles ----------------------------------------------------- */ + +table.field-list td, table.field-list th { + border: 0 !important; +} + +.field-list ul { + margin: 0; + padding-left: 1em; +} + +.field-list p { + margin: 0; +} + +.field-name { + -moz-hyphens: manual; + -ms-hyphens: manual; + -webkit-hyphens: manual; + hyphens: manual; +} + +/* -- hlist styles ---------------------------------------------------------- */ + +table.hlist { + margin: 1em 0; +} + +table.hlist td { + vertical-align: top; +} + +/* -- object description styles --------------------------------------------- */ + +.sig { + font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; +} + +.sig-name, code.descname { + background-color: transparent; + font-weight: bold; +} + +.sig-name { + font-size: 1.1em; +} + +code.descname { + font-size: 1.2em; +} + +.sig-prename, code.descclassname { + background-color: transparent; +} + +.optional { + font-size: 1.3em; +} + +.sig-paren { + font-size: larger; +} + +.sig-param.n { + font-style: italic; +} + +/* C++ specific styling */ + +.sig-inline.c-texpr, +.sig-inline.cpp-texpr { + font-family: unset; +} + +.sig.c .k, .sig.c .kt, +.sig.cpp .k, .sig.cpp .kt { + color: #0033B3; +} + +.sig.c .m, +.sig.cpp .m { + color: #1750EB; +} + +.sig.c .s, .sig.c .sc, +.sig.cpp .s, .sig.cpp .sc { + color: #067D17; +} + + +/* -- other body styles ----------------------------------------------------- */ + +ol.arabic { + list-style: decimal; +} + +ol.loweralpha { + list-style: lower-alpha; +} + +ol.upperalpha { + list-style: upper-alpha; +} + +ol.lowerroman { + list-style: lower-roman; +} + +ol.upperroman { + list-style: upper-roman; +} + +:not(li) > ol > li:first-child > :first-child, +:not(li) > ul > li:first-child > :first-child { + margin-top: 0px; +} + +:not(li) > ol > li:last-child > :last-child, +:not(li) > ul > li:last-child > :last-child { + margin-bottom: 0px; +} + +ol.simple ol p, +ol.simple ul p, +ul.simple ol p, +ul.simple ul p { + margin-top: 0; +} + +ol.simple > li:not(:first-child) > p, +ul.simple > li:not(:first-child) > p { + margin-top: 0; +} + +ol.simple p, +ul.simple p { + margin-bottom: 0; +} + +aside.footnote > span, +div.citation > span { + float: left; +} +aside.footnote > span:last-of-type, +div.citation > span:last-of-type { + padding-right: 0.5em; +} +aside.footnote > p { + margin-left: 2em; +} +div.citation > p { + margin-left: 4em; +} +aside.footnote > p:last-of-type, +div.citation > p:last-of-type { + margin-bottom: 0em; +} +aside.footnote > p:last-of-type:after, +div.citation > p:last-of-type:after { + content: ""; + clear: both; +} + +dl.field-list { + display: grid; + grid-template-columns: fit-content(30%) auto; +} + +dl.field-list > dt { + font-weight: bold; + word-break: break-word; + padding-left: 0.5em; + padding-right: 5px; +} + +dl.field-list > dd { + padding-left: 0.5em; + margin-top: 0em; + margin-left: 0em; + margin-bottom: 0em; +} + +dl { + margin-bottom: 15px; +} + +dd > :first-child { + margin-top: 0px; +} + +dd ul, dd table { + margin-bottom: 10px; +} + +dd { + margin-top: 3px; + margin-bottom: 10px; + margin-left: 30px; +} + +.sig dd { + margin-top: 0px; + margin-bottom: 0px; +} + +.sig dl { + margin-top: 0px; + margin-bottom: 0px; +} + +dl > dd:last-child, +dl > dd:last-child > :last-child { + margin-bottom: 0; +} + +dt:target, span.highlighted { + background-color: #fbe54e; +} + +rect.highlighted { + fill: #fbe54e; +} + +dl.glossary dt { + font-weight: bold; + font-size: 1.1em; +} + +.versionmodified { + font-style: italic; +} + +.system-message { + background-color: #fda; + padding: 5px; + border: 3px solid red; +} + +.footnote:target { + background-color: #ffa; +} + +.line-block { + display: block; + margin-top: 1em; + margin-bottom: 1em; +} + +.line-block .line-block { + margin-top: 0; + margin-bottom: 0; + margin-left: 1.5em; +} + +.guilabel, .menuselection { + font-family: sans-serif; +} + +.accelerator { + text-decoration: underline; +} + +.classifier { + font-style: oblique; +} + +.classifier:before { + font-style: normal; + margin: 0 0.5em; + content: ":"; + display: inline-block; +} + +abbr, acronym { + border-bottom: dotted 1px; + cursor: help; +} + +.translated { + background-color: rgba(207, 255, 207, 0.2) +} + +.untranslated { + background-color: rgba(255, 207, 207, 0.2) +} + +/* -- code displays --------------------------------------------------------- */ + +pre { + overflow: auto; + overflow-y: hidden; /* fixes display issues on Chrome browsers */ +} + +pre, div[class*="highlight-"] { + clear: both; +} + +span.pre { + -moz-hyphens: none; + -ms-hyphens: none; + -webkit-hyphens: none; + hyphens: none; + white-space: nowrap; +} + +div[class*="highlight-"] { + margin: 1em 0; +} + +td.linenos pre { + border: 0; + background-color: transparent; + color: #aaa; +} + +table.highlighttable { + display: block; +} + +table.highlighttable tbody { + display: block; +} + +table.highlighttable tr { + display: flex; +} + +table.highlighttable td { + margin: 0; + padding: 0; +} + +table.highlighttable td.linenos { + padding-right: 0.5em; +} + +table.highlighttable td.code { + flex: 1; + overflow: hidden; +} + +.highlight .hll { + display: block; +} + +div.highlight pre, +table.highlighttable pre { + margin: 0; +} + +div.code-block-caption + div { + margin-top: 0; +} + +div.code-block-caption { + margin-top: 1em; + padding: 2px 5px; + font-size: small; +} + +div.code-block-caption code { + background-color: transparent; +} + +table.highlighttable td.linenos, +span.linenos, +div.highlight span.gp { /* gp: Generic.Prompt */ + user-select: none; + -webkit-user-select: text; /* Safari fallback only */ + -webkit-user-select: none; /* Chrome/Safari */ + -moz-user-select: none; /* Firefox */ + -ms-user-select: none; /* IE10+ */ +} + +div.code-block-caption span.caption-number { + padding: 0.1em 0.3em; + font-style: italic; +} + +div.code-block-caption span.caption-text { +} + +div.literal-block-wrapper { + margin: 1em 0; +} + +code.xref, a code { + background-color: transparent; + font-weight: bold; +} + +h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { + background-color: transparent; +} + +.viewcode-link { + float: right; +} + +.viewcode-back { + float: right; + font-family: sans-serif; +} + +div.viewcode-block:target { + margin: -1px -10px; + padding: 0 10px; +} + +/* -- math display ---------------------------------------------------------- */ + +img.math { + vertical-align: middle; +} + +div.body div.math p { + text-align: center; +} + +span.eqno { + float: right; +} + +span.eqno a.headerlink { + position: absolute; + z-index: 1; +} + +div.math:hover a.headerlink { + visibility: visible; +} + +/* -- printout stylesheet --------------------------------------------------- */ + +@media print { + div.document, + div.documentwrapper, + div.bodywrapper { + margin: 0 !important; + width: 100%; + } + + div.sphinxsidebar, + div.related, + div.footer, + #top-link { + display: none; + } +} \ No newline at end of file diff --git a/_static/check-solid.svg b/_static/check-solid.svg new file mode 100644 index 0000000..92fad4b --- /dev/null +++ b/_static/check-solid.svg @@ -0,0 +1,4 @@ + + + + diff --git a/_static/clipboard.min.js b/_static/clipboard.min.js new file mode 100644 index 0000000..54b3c46 --- /dev/null +++ b/_static/clipboard.min.js @@ -0,0 +1,7 @@ +/*! + * clipboard.js v2.0.8 + * https://clipboardjs.com/ + * + * Licensed MIT © Zeno Rocha + */ +!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.ClipboardJS=e():t.ClipboardJS=e()}(this,function(){return n={686:function(t,e,n){"use strict";n.d(e,{default:function(){return o}});var e=n(279),i=n.n(e),e=n(370),u=n.n(e),e=n(817),c=n.n(e);function a(t){try{return document.execCommand(t)}catch(t){return}}var f=function(t){t=c()(t);return a("cut"),t};var l=function(t){var e,n,o,r=1 + + + + diff --git a/_static/copybutton.css b/_static/copybutton.css new file mode 100644 index 0000000..f1916ec --- /dev/null +++ b/_static/copybutton.css @@ -0,0 +1,94 @@ +/* Copy buttons */ +button.copybtn { + position: absolute; + display: flex; + top: .3em; + right: .3em; + width: 1.7em; + height: 1.7em; + opacity: 0; + transition: opacity 0.3s, border .3s, background-color .3s; + user-select: none; + padding: 0; + border: none; + outline: none; + border-radius: 0.4em; + /* The colors that GitHub uses */ + border: #1b1f2426 1px solid; + background-color: #f6f8fa; + color: #57606a; +} + +button.copybtn.success { + border-color: #22863a; + color: #22863a; +} + +button.copybtn svg { + stroke: currentColor; + width: 1.5em; + height: 1.5em; + padding: 0.1em; +} + +div.highlight { + position: relative; +} + +/* Show the copybutton */ +.highlight:hover button.copybtn, button.copybtn.success { + opacity: 1; +} + +.highlight button.copybtn:hover { + background-color: rgb(235, 235, 235); +} + +.highlight button.copybtn:active { + background-color: rgb(187, 187, 187); +} + +/** + * A minimal CSS-only tooltip copied from: + * https://codepen.io/mildrenben/pen/rVBrpK + * + * To use, write HTML like the following: + * + *

Short

+ */ + .o-tooltip--left { + position: relative; + } + + .o-tooltip--left:after { + opacity: 0; + visibility: hidden; + position: absolute; + content: attr(data-tooltip); + padding: .2em; + font-size: .8em; + left: -.2em; + background: grey; + color: white; + white-space: nowrap; + z-index: 2; + border-radius: 2px; + transform: translateX(-102%) translateY(0); + transition: opacity 0.2s cubic-bezier(0.64, 0.09, 0.08, 1), transform 0.2s cubic-bezier(0.64, 0.09, 0.08, 1); +} + +.o-tooltip--left:hover:after { + display: block; + opacity: 1; + visibility: visible; + transform: translateX(-100%) translateY(0); + transition: opacity 0.2s cubic-bezier(0.64, 0.09, 0.08, 1), transform 0.2s cubic-bezier(0.64, 0.09, 0.08, 1); + transition-delay: .5s; +} + +/* By default the copy button shouldn't show up when printing a page */ +@media print { + button.copybtn { + display: none; + } +} diff --git a/_static/copybutton.js b/_static/copybutton.js new file mode 100644 index 0000000..2ea7ff3 --- /dev/null +++ b/_static/copybutton.js @@ -0,0 +1,248 @@ +// Localization support +const messages = { + 'en': { + 'copy': 'Copy', + 'copy_to_clipboard': 'Copy to clipboard', + 'copy_success': 'Copied!', + 'copy_failure': 'Failed to copy', + }, + 'es' : { + 'copy': 'Copiar', + 'copy_to_clipboard': 'Copiar al portapapeles', + 'copy_success': '¡Copiado!', + 'copy_failure': 'Error al copiar', + }, + 'de' : { + 'copy': 'Kopieren', + 'copy_to_clipboard': 'In die Zwischenablage kopieren', + 'copy_success': 'Kopiert!', + 'copy_failure': 'Fehler beim Kopieren', + }, + 'fr' : { + 'copy': 'Copier', + 'copy_to_clipboard': 'Copier dans le presse-papier', + 'copy_success': 'Copié !', + 'copy_failure': 'Échec de la copie', + }, + 'ru': { + 'copy': 'Скопировать', + 'copy_to_clipboard': 'Скопировать в буфер', + 'copy_success': 'Скопировано!', + 'copy_failure': 'Не удалось скопировать', + }, + 'zh-CN': { + 'copy': '复制', + 'copy_to_clipboard': '复制到剪贴板', + 'copy_success': '复制成功!', + 'copy_failure': '复制失败', + }, + 'it' : { + 'copy': 'Copiare', + 'copy_to_clipboard': 'Copiato negli appunti', + 'copy_success': 'Copiato!', + 'copy_failure': 'Errore durante la copia', + } +} + +let locale = 'en' +if( document.documentElement.lang !== undefined + && messages[document.documentElement.lang] !== undefined ) { + locale = document.documentElement.lang +} + +let doc_url_root = DOCUMENTATION_OPTIONS.URL_ROOT; +if (doc_url_root == '#') { + doc_url_root = ''; +} + +/** + * SVG files for our copy buttons + */ +let iconCheck = ` + ${messages[locale]['copy_success']} + + +` + +// If the user specified their own SVG use that, otherwise use the default +let iconCopy = ``; +if (!iconCopy) { + iconCopy = ` + ${messages[locale]['copy_to_clipboard']} + + + +` +} + +/** + * Set up copy/paste for code blocks + */ + +const runWhenDOMLoaded = cb => { + if (document.readyState != 'loading') { + cb() + } else if (document.addEventListener) { + document.addEventListener('DOMContentLoaded', cb) + } else { + document.attachEvent('onreadystatechange', function() { + if (document.readyState == 'complete') cb() + }) + } +} + +const codeCellId = index => `codecell${index}` + +// Clears selected text since ClipboardJS will select the text when copying +const clearSelection = () => { + if (window.getSelection) { + window.getSelection().removeAllRanges() + } else if (document.selection) { + document.selection.empty() + } +} + +// Changes tooltip text for a moment, then changes it back +// We want the timeout of our `success` class to be a bit shorter than the +// tooltip and icon change, so that we can hide the icon before changing back. +var timeoutIcon = 2000; +var timeoutSuccessClass = 1500; + +const temporarilyChangeTooltip = (el, oldText, newText) => { + el.setAttribute('data-tooltip', newText) + el.classList.add('success') + // Remove success a little bit sooner than we change the tooltip + // So that we can use CSS to hide the copybutton first + setTimeout(() => el.classList.remove('success'), timeoutSuccessClass) + setTimeout(() => el.setAttribute('data-tooltip', oldText), timeoutIcon) +} + +// Changes the copy button icon for two seconds, then changes it back +const temporarilyChangeIcon = (el) => { + el.innerHTML = iconCheck; + setTimeout(() => {el.innerHTML = iconCopy}, timeoutIcon) +} + +const addCopyButtonToCodeCells = () => { + // If ClipboardJS hasn't loaded, wait a bit and try again. This + // happens because we load ClipboardJS asynchronously. + if (window.ClipboardJS === undefined) { + setTimeout(addCopyButtonToCodeCells, 250) + return + } + + // Add copybuttons to all of our code cells + const COPYBUTTON_SELECTOR = 'div.highlight pre'; + const codeCells = document.querySelectorAll(COPYBUTTON_SELECTOR) + codeCells.forEach((codeCell, index) => { + const id = codeCellId(index) + codeCell.setAttribute('id', id) + + const clipboardButton = id => + `` + codeCell.insertAdjacentHTML('afterend', clipboardButton(id)) + }) + +function escapeRegExp(string) { + return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string +} + +/** + * Removes excluded text from a Node. + * + * @param {Node} target Node to filter. + * @param {string} exclude CSS selector of nodes to exclude. + * @returns {DOMString} Text from `target` with text removed. + */ +function filterText(target, exclude) { + const clone = target.cloneNode(true); // clone as to not modify the live DOM + if (exclude) { + // remove excluded nodes + clone.querySelectorAll(exclude).forEach(node => node.remove()); + } + return clone.innerText; +} + +// Callback when a copy button is clicked. Will be passed the node that was clicked +// should then grab the text and replace pieces of text that shouldn't be used in output +function formatCopyText(textContent, copybuttonPromptText, isRegexp = false, onlyCopyPromptLines = true, removePrompts = true, copyEmptyLines = true, lineContinuationChar = "", hereDocDelim = "") { + var regexp; + var match; + + // Do we check for line continuation characters and "HERE-documents"? + var useLineCont = !!lineContinuationChar + var useHereDoc = !!hereDocDelim + + // create regexp to capture prompt and remaining line + if (isRegexp) { + regexp = new RegExp('^(' + copybuttonPromptText + ')(.*)') + } else { + regexp = new RegExp('^(' + escapeRegExp(copybuttonPromptText) + ')(.*)') + } + + const outputLines = []; + var promptFound = false; + var gotLineCont = false; + var gotHereDoc = false; + const lineGotPrompt = []; + for (const line of textContent.split('\n')) { + match = line.match(regexp) + if (match || gotLineCont || gotHereDoc) { + promptFound = regexp.test(line) + lineGotPrompt.push(promptFound) + if (removePrompts && promptFound) { + outputLines.push(match[2]) + } else { + outputLines.push(line) + } + gotLineCont = line.endsWith(lineContinuationChar) & useLineCont + if (line.includes(hereDocDelim) & useHereDoc) + gotHereDoc = !gotHereDoc + } else if (!onlyCopyPromptLines) { + outputLines.push(line) + } else if (copyEmptyLines && line.trim() === '') { + outputLines.push(line) + } + } + + // If no lines with the prompt were found then just use original lines + if (lineGotPrompt.some(v => v === true)) { + textContent = outputLines.join('\n'); + } + + // Remove a trailing newline to avoid auto-running when pasting + if (textContent.endsWith("\n")) { + textContent = textContent.slice(0, -1) + } + return textContent +} + + +var copyTargetText = (trigger) => { + var target = document.querySelector(trigger.attributes['data-clipboard-target'].value); + + // get filtered text + let exclude = '.linenos'; + + let text = filterText(target, exclude); + return formatCopyText(text, '', false, true, true, true, '', '') +} + + // Initialize with a callback so we can modify the text before copy + const clipboard = new ClipboardJS('.copybtn', {text: copyTargetText}) + + // Update UI with error/success messages + clipboard.on('success', event => { + clearSelection() + temporarilyChangeTooltip(event.trigger, messages[locale]['copy'], messages[locale]['copy_success']) + temporarilyChangeIcon(event.trigger) + }) + + clipboard.on('error', event => { + temporarilyChangeTooltip(event.trigger, messages[locale]['copy'], messages[locale]['copy_failure']) + }) +} + +runWhenDOMLoaded(addCopyButtonToCodeCells) \ No newline at end of file diff --git a/_static/copybutton_funcs.js b/_static/copybutton_funcs.js new file mode 100644 index 0000000..dbe1aaa --- /dev/null +++ b/_static/copybutton_funcs.js @@ -0,0 +1,73 @@ +function escapeRegExp(string) { + return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string +} + +/** + * Removes excluded text from a Node. + * + * @param {Node} target Node to filter. + * @param {string} exclude CSS selector of nodes to exclude. + * @returns {DOMString} Text from `target` with text removed. + */ +export function filterText(target, exclude) { + const clone = target.cloneNode(true); // clone as to not modify the live DOM + if (exclude) { + // remove excluded nodes + clone.querySelectorAll(exclude).forEach(node => node.remove()); + } + return clone.innerText; +} + +// Callback when a copy button is clicked. Will be passed the node that was clicked +// should then grab the text and replace pieces of text that shouldn't be used in output +export function formatCopyText(textContent, copybuttonPromptText, isRegexp = false, onlyCopyPromptLines = true, removePrompts = true, copyEmptyLines = true, lineContinuationChar = "", hereDocDelim = "") { + var regexp; + var match; + + // Do we check for line continuation characters and "HERE-documents"? + var useLineCont = !!lineContinuationChar + var useHereDoc = !!hereDocDelim + + // create regexp to capture prompt and remaining line + if (isRegexp) { + regexp = new RegExp('^(' + copybuttonPromptText + ')(.*)') + } else { + regexp = new RegExp('^(' + escapeRegExp(copybuttonPromptText) + ')(.*)') + } + + const outputLines = []; + var promptFound = false; + var gotLineCont = false; + var gotHereDoc = false; + const lineGotPrompt = []; + for (const line of textContent.split('\n')) { + match = line.match(regexp) + if (match || gotLineCont || gotHereDoc) { + promptFound = regexp.test(line) + lineGotPrompt.push(promptFound) + if (removePrompts && promptFound) { + outputLines.push(match[2]) + } else { + outputLines.push(line) + } + gotLineCont = line.endsWith(lineContinuationChar) & useLineCont + if (line.includes(hereDocDelim) & useHereDoc) + gotHereDoc = !gotHereDoc + } else if (!onlyCopyPromptLines) { + outputLines.push(line) + } else if (copyEmptyLines && line.trim() === '') { + outputLines.push(line) + } + } + + // If no lines with the prompt were found then just use original lines + if (lineGotPrompt.some(v => v === true)) { + textContent = outputLines.join('\n'); + } + + // Remove a trailing newline to avoid auto-running when pasting + if (textContent.endsWith("\n")) { + textContent = textContent.slice(0, -1) + } + return textContent +} diff --git a/_static/debug.css b/_static/debug.css new file mode 100644 index 0000000..74d4aec --- /dev/null +++ b/_static/debug.css @@ -0,0 +1,69 @@ +/* + This CSS file should be overridden by the theme authors. It's + meant for debugging and developing the skeleton that this theme provides. +*/ +body { + font-family: -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, + "Apple Color Emoji", "Segoe UI Emoji"; + background: lavender; +} +.sb-announcement { + background: rgb(131, 131, 131); +} +.sb-announcement__inner { + background: black; + color: white; +} +.sb-header { + background: lightskyblue; +} +.sb-header__inner { + background: royalblue; + color: white; +} +.sb-header-secondary { + background: lightcyan; +} +.sb-header-secondary__inner { + background: cornflowerblue; + color: white; +} +.sb-sidebar-primary { + background: lightgreen; +} +.sb-main { + background: blanchedalmond; +} +.sb-main__inner { + background: antiquewhite; +} +.sb-header-article { + background: lightsteelblue; +} +.sb-article-container { + background: snow; +} +.sb-article-main { + background: white; +} +.sb-footer-article { + background: lightpink; +} +.sb-sidebar-secondary { + background: lightgoldenrodyellow; +} +.sb-footer-content { + background: plum; +} +.sb-footer-content__inner { + background: palevioletred; +} +.sb-footer { + background: pink; +} +.sb-footer__inner { + background: salmon; +} +.sb-article { + background: white; +} diff --git a/_static/doctools.js b/_static/doctools.js new file mode 100644 index 0000000..d06a71d --- /dev/null +++ b/_static/doctools.js @@ -0,0 +1,156 @@ +/* + * doctools.js + * ~~~~~~~~~~~ + * + * Base JavaScript utilities for all Sphinx HTML documentation. + * + * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ +"use strict"; + +const BLACKLISTED_KEY_CONTROL_ELEMENTS = new Set([ + "TEXTAREA", + "INPUT", + "SELECT", + "BUTTON", +]); + +const _ready = (callback) => { + if (document.readyState !== "loading") { + callback(); + } else { + document.addEventListener("DOMContentLoaded", callback); + } +}; + +/** + * Small JavaScript module for the documentation. + */ +const Documentation = { + init: () => { + Documentation.initDomainIndexTable(); + Documentation.initOnKeyListeners(); + }, + + /** + * i18n support + */ + TRANSLATIONS: {}, + PLURAL_EXPR: (n) => (n === 1 ? 0 : 1), + LOCALE: "unknown", + + // gettext and ngettext don't access this so that the functions + // can safely bound to a different name (_ = Documentation.gettext) + gettext: (string) => { + const translated = Documentation.TRANSLATIONS[string]; + switch (typeof translated) { + case "undefined": + return string; // no translation + case "string": + return translated; // translation exists + default: + return translated[0]; // (singular, plural) translation tuple exists + } + }, + + ngettext: (singular, plural, n) => { + const translated = Documentation.TRANSLATIONS[singular]; + if (typeof translated !== "undefined") + return translated[Documentation.PLURAL_EXPR(n)]; + return n === 1 ? singular : plural; + }, + + addTranslations: (catalog) => { + Object.assign(Documentation.TRANSLATIONS, catalog.messages); + Documentation.PLURAL_EXPR = new Function( + "n", + `return (${catalog.plural_expr})` + ); + Documentation.LOCALE = catalog.locale; + }, + + /** + * helper function to focus on search bar + */ + focusSearchBar: () => { + document.querySelectorAll("input[name=q]")[0]?.focus(); + }, + + /** + * Initialise the domain index toggle buttons + */ + initDomainIndexTable: () => { + const toggler = (el) => { + const idNumber = el.id.substr(7); + const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`); + if (el.src.substr(-9) === "minus.png") { + el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`; + toggledRows.forEach((el) => (el.style.display = "none")); + } else { + el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`; + toggledRows.forEach((el) => (el.style.display = "")); + } + }; + + const togglerElements = document.querySelectorAll("img.toggler"); + togglerElements.forEach((el) => + el.addEventListener("click", (event) => toggler(event.currentTarget)) + ); + togglerElements.forEach((el) => (el.style.display = "")); + if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler); + }, + + initOnKeyListeners: () => { + // only install a listener if it is really needed + if ( + !DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS && + !DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS + ) + return; + + document.addEventListener("keydown", (event) => { + // bail for input elements + if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; + // bail with special keys + if (event.altKey || event.ctrlKey || event.metaKey) return; + + if (!event.shiftKey) { + switch (event.key) { + case "ArrowLeft": + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; + + const prevLink = document.querySelector('link[rel="prev"]'); + if (prevLink && prevLink.href) { + window.location.href = prevLink.href; + event.preventDefault(); + } + break; + case "ArrowRight": + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; + + const nextLink = document.querySelector('link[rel="next"]'); + if (nextLink && nextLink.href) { + window.location.href = nextLink.href; + event.preventDefault(); + } + break; + } + } + + // some keyboard layouts may need Shift to get / + switch (event.key) { + case "/": + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break; + Documentation.focusSearchBar(); + event.preventDefault(); + } + }); + }, +}; + +// quick alias for translations +const _ = Documentation.gettext; + +_ready(Documentation.init); diff --git a/_static/documentation_options.js b/_static/documentation_options.js new file mode 100644 index 0000000..fea8c69 --- /dev/null +++ b/_static/documentation_options.js @@ -0,0 +1,13 @@ +const DOCUMENTATION_OPTIONS = { + VERSION: '1.7.0', + LANGUAGE: 'en', + COLLAPSE_INDEX: false, + BUILDER: 'html', + FILE_SUFFIX: '.html', + LINK_SUFFIX: '.html', + HAS_SOURCE: true, + SOURCELINK_SUFFIX: '.txt', + NAVIGATION_WITH_KEYS: false, + SHOW_SEARCH_SUMMARY: true, + ENABLE_SEARCH_SHORTCUTS: true, +}; \ No newline at end of file diff --git a/_static/file.png b/_static/file.png new file mode 100644 index 0000000000000000000000000000000000000000..a858a410e4faa62ce324d814e4b816fff83a6fb3 GIT binary patch literal 286 zcmV+(0pb3MP)s`hMrGg#P~ix$^RISR_I47Y|r1 z_CyJOe}D1){SET-^Amu_i71Lt6eYfZjRyw@I6OQAIXXHDfiX^GbOlHe=Ae4>0m)d(f|Me07*qoM6N<$f}vM^LjV8( literal 0 HcmV?d00001 diff --git a/_static/graphviz.css b/_static/graphviz.css new file mode 100644 index 0000000..8d81c02 --- /dev/null +++ b/_static/graphviz.css @@ -0,0 +1,19 @@ +/* + * graphviz.css + * ~~~~~~~~~~~~ + * + * Sphinx stylesheet -- graphviz extension. + * + * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +img.graphviz { + border: 0; + max-width: 100%; +} + +object.graphviz { + max-width: 100%; +} diff --git a/_static/language_data.js b/_static/language_data.js new file mode 100644 index 0000000..250f566 --- /dev/null +++ b/_static/language_data.js @@ -0,0 +1,199 @@ +/* + * language_data.js + * ~~~~~~~~~~~~~~~~ + * + * This script contains the language-specific data used by searchtools.js, + * namely the list of stopwords, stemmer, scorer and splitter. + * + * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +var stopwords = ["a", "and", "are", "as", "at", "be", "but", "by", "for", "if", "in", "into", "is", "it", "near", "no", "not", "of", "on", "or", "such", "that", "the", "their", "then", "there", "these", "they", "this", "to", "was", "will", "with"]; + + +/* Non-minified version is copied as a separate JS file, is available */ + +/** + * Porter Stemmer + */ +var Stemmer = function() { + + var step2list = { + ational: 'ate', + tional: 'tion', + enci: 'ence', + anci: 'ance', + izer: 'ize', + bli: 'ble', + alli: 'al', + entli: 'ent', + eli: 'e', + ousli: 'ous', + ization: 'ize', + ation: 'ate', + ator: 'ate', + alism: 'al', + iveness: 'ive', + fulness: 'ful', + ousness: 'ous', + aliti: 'al', + iviti: 'ive', + biliti: 'ble', + logi: 'log' + }; + + var step3list = { + icate: 'ic', + ative: '', + alize: 'al', + iciti: 'ic', + ical: 'ic', + ful: '', + ness: '' + }; + + var c = "[^aeiou]"; // consonant + var v = "[aeiouy]"; // vowel + var C = c + "[^aeiouy]*"; // consonant sequence + var V = v + "[aeiou]*"; // vowel sequence + + var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0 + var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 + var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 + var s_v = "^(" + C + ")?" + v; // vowel in stem + + this.stemWord = function (w) { + var stem; + var suffix; + var firstch; + var origword = w; + + if (w.length < 3) + return w; + + var re; + var re2; + var re3; + var re4; + + firstch = w.substr(0,1); + if (firstch == "y") + w = firstch.toUpperCase() + w.substr(1); + + // Step 1a + re = /^(.+?)(ss|i)es$/; + re2 = /^(.+?)([^s])s$/; + + if (re.test(w)) + w = w.replace(re,"$1$2"); + else if (re2.test(w)) + w = w.replace(re2,"$1$2"); + + // Step 1b + re = /^(.+?)eed$/; + re2 = /^(.+?)(ed|ing)$/; + if (re.test(w)) { + var fp = re.exec(w); + re = new RegExp(mgr0); + if (re.test(fp[1])) { + re = /.$/; + w = w.replace(re,""); + } + } + else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1]; + re2 = new RegExp(s_v); + if (re2.test(stem)) { + w = stem; + re2 = /(at|bl|iz)$/; + re3 = new RegExp("([^aeiouylsz])\\1$"); + re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + if (re2.test(w)) + w = w + "e"; + else if (re3.test(w)) { + re = /.$/; + w = w.replace(re,""); + } + else if (re4.test(w)) + w = w + "e"; + } + } + + // Step 1c + re = /^(.+?)y$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(s_v); + if (re.test(stem)) + w = stem + "i"; + } + + // Step 2 + re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = new RegExp(mgr0); + if (re.test(stem)) + w = stem + step2list[suffix]; + } + + // Step 3 + re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = new RegExp(mgr0); + if (re.test(stem)) + w = stem + step3list[suffix]; + } + + // Step 4 + re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; + re2 = /^(.+?)(s|t)(ion)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(mgr1); + if (re.test(stem)) + w = stem; + } + else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1] + fp[2]; + re2 = new RegExp(mgr1); + if (re2.test(stem)) + w = stem; + } + + // Step 5 + re = /^(.+?)e$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(mgr1); + re2 = new RegExp(meq1); + re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) + w = stem; + } + re = /ll$/; + re2 = new RegExp(mgr1); + if (re.test(w) && re2.test(w)) { + re = /.$/; + w = w.replace(re,""); + } + + // and turn initial Y back to y + if (firstch == "y") + w = firstch.toLowerCase() + w.substr(1); + return w; + } +} + diff --git a/_static/minus.png b/_static/minus.png new file mode 100644 index 0000000000000000000000000000000000000000..d96755fdaf8bb2214971e0db9c1fd3077d7c419d GIT binary patch literal 90 zcmeAS@N?(olHy`uVBq!ia0vp^+#t*WBp7;*Yy1LIik>cxAr*|t7R?Mi>2?kWtu=nj kDsEF_5m^0CR;1wuP-*O&G^0G}KYk!hp00i_>zopr08q^qX#fBK literal 0 HcmV?d00001 diff --git a/_static/plus.png b/_static/plus.png new file mode 100644 index 0000000000000000000000000000000000000000..7107cec93a979b9a5f64843235a16651d563ce2d GIT binary patch literal 90 zcmeAS@N?(olHy`uVBq!ia0vp^+#t*WBp7;*Yy1LIik>cxAr*|t7R?Mi>2?kWtu>-2 m3q%Vub%g%s<8sJhVPMczOq}xhg9DJoz~JfX=d#Wzp$Pyb1r*Kz literal 0 HcmV?d00001 diff --git a/_static/pygments.css b/_static/pygments.css new file mode 100644 index 0000000..ed1f4cf --- /dev/null +++ b/_static/pygments.css @@ -0,0 +1,249 @@ +.highlight pre { line-height: 125%; } +.highlight td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +.highlight span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +.highlight td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +.highlight span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +.highlight .hll { background-color: #ffffcc } +.highlight { background: #eeffcc; } +.highlight .c { color: #408090; font-style: italic } /* Comment */ +.highlight .err { border: 1px solid #FF0000 } /* Error */ +.highlight .k { color: #007020; font-weight: bold } /* Keyword */ +.highlight .o { color: #666666 } /* Operator */ +.highlight .ch { color: #408090; font-style: italic } /* Comment.Hashbang */ +.highlight .cm { color: #408090; font-style: italic } /* Comment.Multiline */ +.highlight .cp { color: #007020 } /* Comment.Preproc */ +.highlight .cpf { color: #408090; font-style: italic } /* Comment.PreprocFile */ +.highlight .c1 { color: #408090; font-style: italic } /* Comment.Single */ +.highlight .cs { color: #408090; background-color: #fff0f0 } /* Comment.Special */ +.highlight .gd { color: #A00000 } /* Generic.Deleted */ +.highlight .ge { font-style: italic } /* Generic.Emph */ +.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */ +.highlight .gr { color: #FF0000 } /* Generic.Error */ +.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ +.highlight .gi { color: #00A000 } /* Generic.Inserted */ +.highlight .go { color: #333333 } /* Generic.Output */ +.highlight .gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */ +.highlight .gs { font-weight: bold } /* Generic.Strong */ +.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ +.highlight .gt { color: #0044DD } /* Generic.Traceback */ +.highlight .kc { color: #007020; font-weight: bold } /* Keyword.Constant */ +.highlight .kd { color: #007020; font-weight: bold } /* Keyword.Declaration */ +.highlight .kn { color: #007020; font-weight: bold } /* Keyword.Namespace */ +.highlight .kp { color: #007020 } /* Keyword.Pseudo */ +.highlight .kr { color: #007020; font-weight: bold } /* Keyword.Reserved */ +.highlight .kt { color: #902000 } /* Keyword.Type */ +.highlight .m { color: #208050 } /* Literal.Number */ +.highlight .s { color: #4070a0 } /* Literal.String */ +.highlight .na { color: #4070a0 } /* Name.Attribute */ +.highlight .nb { color: #007020 } /* Name.Builtin */ +.highlight .nc { color: #0e84b5; font-weight: bold } /* Name.Class */ +.highlight .no { color: #60add5 } /* Name.Constant */ +.highlight .nd { color: #555555; font-weight: bold } /* Name.Decorator */ +.highlight .ni { color: #d55537; font-weight: bold } /* Name.Entity */ +.highlight .ne { color: #007020 } /* Name.Exception */ +.highlight .nf { color: #06287e } /* Name.Function */ +.highlight .nl { color: #002070; font-weight: bold } /* Name.Label */ +.highlight .nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */ +.highlight .nt { color: #062873; font-weight: bold } /* Name.Tag */ +.highlight .nv { color: #bb60d5 } /* Name.Variable */ +.highlight .ow { color: #007020; font-weight: bold } /* Operator.Word */ +.highlight .w { color: #bbbbbb } /* Text.Whitespace */ +.highlight .mb { color: #208050 } /* Literal.Number.Bin */ +.highlight .mf { color: #208050 } /* Literal.Number.Float */ +.highlight .mh { color: #208050 } /* Literal.Number.Hex */ +.highlight .mi { color: #208050 } /* Literal.Number.Integer */ +.highlight .mo { color: #208050 } /* Literal.Number.Oct */ +.highlight .sa { color: #4070a0 } /* Literal.String.Affix */ +.highlight .sb { color: #4070a0 } /* Literal.String.Backtick */ +.highlight .sc { color: #4070a0 } /* Literal.String.Char */ +.highlight .dl { color: #4070a0 } /* Literal.String.Delimiter */ +.highlight .sd { color: #4070a0; font-style: italic } /* Literal.String.Doc */ +.highlight .s2 { color: #4070a0 } /* Literal.String.Double */ +.highlight .se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */ +.highlight .sh { color: #4070a0 } /* Literal.String.Heredoc */ +.highlight .si { color: #70a0d0; font-style: italic } /* Literal.String.Interpol */ +.highlight .sx { color: #c65d09 } /* Literal.String.Other */ +.highlight .sr { color: #235388 } /* Literal.String.Regex */ +.highlight .s1 { color: #4070a0 } /* Literal.String.Single */ +.highlight .ss { color: #517918 } /* Literal.String.Symbol */ +.highlight .bp { color: #007020 } /* Name.Builtin.Pseudo */ +.highlight .fm { color: #06287e } /* Name.Function.Magic */ +.highlight .vc { color: #bb60d5 } /* Name.Variable.Class */ +.highlight .vg { color: #bb60d5 } /* Name.Variable.Global */ +.highlight .vi { color: #bb60d5 } /* Name.Variable.Instance */ +.highlight .vm { color: #bb60d5 } /* Name.Variable.Magic */ +.highlight .il { color: #208050 } /* Literal.Number.Integer.Long */ +@media not print { +body[data-theme="dark"] .highlight pre { line-height: 125%; } +body[data-theme="dark"] .highlight td.linenos .normal { color: #aaaaaa; background-color: transparent; padding-left: 5px; padding-right: 5px; } +body[data-theme="dark"] .highlight span.linenos { color: #aaaaaa; background-color: transparent; padding-left: 5px; padding-right: 5px; } +body[data-theme="dark"] .highlight td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +body[data-theme="dark"] .highlight span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +body[data-theme="dark"] .highlight .hll { background-color: #404040 } +body[data-theme="dark"] .highlight { background: #202020; color: #d0d0d0 } +body[data-theme="dark"] .highlight .c { color: #ababab; font-style: italic } /* Comment */ +body[data-theme="dark"] .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ +body[data-theme="dark"] .highlight .esc { color: #d0d0d0 } /* Escape */ +body[data-theme="dark"] .highlight .g { color: #d0d0d0 } /* Generic */ +body[data-theme="dark"] .highlight .k { color: #6ebf26; font-weight: bold } /* Keyword */ +body[data-theme="dark"] .highlight .l { color: #d0d0d0 } /* Literal */ +body[data-theme="dark"] .highlight .n { color: #d0d0d0 } /* Name */ +body[data-theme="dark"] .highlight .o { color: #d0d0d0 } /* Operator */ +body[data-theme="dark"] .highlight .x { color: #d0d0d0 } /* Other */ +body[data-theme="dark"] .highlight .p { color: #d0d0d0 } /* Punctuation */ +body[data-theme="dark"] .highlight .ch { color: #ababab; font-style: italic } /* Comment.Hashbang */ +body[data-theme="dark"] .highlight .cm { color: #ababab; font-style: italic } /* Comment.Multiline */ +body[data-theme="dark"] .highlight .cp { color: #ff3a3a; font-weight: bold } /* Comment.Preproc */ +body[data-theme="dark"] .highlight .cpf { color: #ababab; font-style: italic } /* Comment.PreprocFile */ +body[data-theme="dark"] .highlight .c1 { color: #ababab; font-style: italic } /* Comment.Single */ +body[data-theme="dark"] .highlight .cs { color: #e50808; font-weight: bold; background-color: #520000 } /* Comment.Special */ +body[data-theme="dark"] .highlight .gd { color: #d22323 } /* Generic.Deleted */ +body[data-theme="dark"] .highlight .ge { color: #d0d0d0; font-style: italic } /* Generic.Emph */ +body[data-theme="dark"] .highlight .ges { color: #d0d0d0; font-weight: bold; font-style: italic } /* Generic.EmphStrong */ +body[data-theme="dark"] .highlight .gr { color: #d22323 } /* Generic.Error */ +body[data-theme="dark"] .highlight .gh { color: #ffffff; font-weight: bold } /* Generic.Heading */ +body[data-theme="dark"] .highlight .gi { color: #589819 } /* Generic.Inserted */ +body[data-theme="dark"] .highlight .go { color: #cccccc } /* Generic.Output */ +body[data-theme="dark"] .highlight .gp { color: #aaaaaa } /* Generic.Prompt */ +body[data-theme="dark"] .highlight .gs { color: #d0d0d0; font-weight: bold } /* Generic.Strong */ +body[data-theme="dark"] .highlight .gu { color: #ffffff; text-decoration: underline } /* Generic.Subheading */ +body[data-theme="dark"] .highlight .gt { color: #d22323 } /* Generic.Traceback */ +body[data-theme="dark"] .highlight .kc { color: #6ebf26; font-weight: bold } /* Keyword.Constant */ +body[data-theme="dark"] .highlight .kd { color: #6ebf26; font-weight: bold } /* Keyword.Declaration */ +body[data-theme="dark"] .highlight .kn { color: #6ebf26; font-weight: bold } /* Keyword.Namespace */ +body[data-theme="dark"] .highlight .kp { color: #6ebf26 } /* Keyword.Pseudo */ +body[data-theme="dark"] .highlight .kr { color: #6ebf26; font-weight: bold } /* Keyword.Reserved */ +body[data-theme="dark"] .highlight .kt { color: #6ebf26; font-weight: bold } /* Keyword.Type */ +body[data-theme="dark"] .highlight .ld { color: #d0d0d0 } /* Literal.Date */ +body[data-theme="dark"] .highlight .m { color: #51b2fd } /* Literal.Number */ +body[data-theme="dark"] .highlight .s { color: #ed9d13 } /* Literal.String */ +body[data-theme="dark"] .highlight .na { color: #bbbbbb } /* Name.Attribute */ +body[data-theme="dark"] .highlight .nb { color: #2fbccd } /* Name.Builtin */ +body[data-theme="dark"] .highlight .nc { color: #71adff; text-decoration: underline } /* Name.Class */ +body[data-theme="dark"] .highlight .no { color: #40ffff } /* Name.Constant */ +body[data-theme="dark"] .highlight .nd { color: #ffa500 } /* Name.Decorator */ +body[data-theme="dark"] .highlight .ni { color: #d0d0d0 } /* Name.Entity */ +body[data-theme="dark"] .highlight .ne { color: #bbbbbb } /* Name.Exception */ +body[data-theme="dark"] .highlight .nf { color: #71adff } /* Name.Function */ +body[data-theme="dark"] .highlight .nl { color: #d0d0d0 } /* Name.Label */ +body[data-theme="dark"] .highlight .nn { color: #71adff; text-decoration: underline } /* Name.Namespace */ +body[data-theme="dark"] .highlight .nx { color: #d0d0d0 } /* Name.Other */ +body[data-theme="dark"] .highlight .py { color: #d0d0d0 } /* Name.Property */ +body[data-theme="dark"] .highlight .nt { color: #6ebf26; font-weight: bold } /* Name.Tag */ +body[data-theme="dark"] .highlight .nv { color: #40ffff } /* Name.Variable */ +body[data-theme="dark"] .highlight .ow { color: #6ebf26; font-weight: bold } /* Operator.Word */ +body[data-theme="dark"] .highlight .pm { color: #d0d0d0 } /* Punctuation.Marker */ +body[data-theme="dark"] .highlight .w { color: #666666 } /* Text.Whitespace */ +body[data-theme="dark"] .highlight .mb { color: #51b2fd } /* Literal.Number.Bin */ +body[data-theme="dark"] .highlight .mf { color: #51b2fd } /* Literal.Number.Float */ +body[data-theme="dark"] .highlight .mh { color: #51b2fd } /* Literal.Number.Hex */ +body[data-theme="dark"] .highlight .mi { color: #51b2fd } /* Literal.Number.Integer */ +body[data-theme="dark"] .highlight .mo { color: #51b2fd } /* Literal.Number.Oct */ +body[data-theme="dark"] .highlight .sa { color: #ed9d13 } /* Literal.String.Affix */ +body[data-theme="dark"] .highlight .sb { color: #ed9d13 } /* Literal.String.Backtick */ +body[data-theme="dark"] .highlight .sc { color: #ed9d13 } /* Literal.String.Char */ +body[data-theme="dark"] .highlight .dl { color: #ed9d13 } /* Literal.String.Delimiter */ +body[data-theme="dark"] .highlight .sd { color: #ed9d13 } /* Literal.String.Doc */ +body[data-theme="dark"] .highlight .s2 { color: #ed9d13 } /* Literal.String.Double */ +body[data-theme="dark"] .highlight .se { color: #ed9d13 } /* Literal.String.Escape */ +body[data-theme="dark"] .highlight .sh { color: #ed9d13 } /* Literal.String.Heredoc */ +body[data-theme="dark"] .highlight .si { color: #ed9d13 } /* Literal.String.Interpol */ +body[data-theme="dark"] .highlight .sx { color: #ffa500 } /* Literal.String.Other */ +body[data-theme="dark"] .highlight .sr { color: #ed9d13 } /* Literal.String.Regex */ +body[data-theme="dark"] .highlight .s1 { color: #ed9d13 } /* Literal.String.Single */ +body[data-theme="dark"] .highlight .ss { color: #ed9d13 } /* Literal.String.Symbol */ +body[data-theme="dark"] .highlight .bp { color: #2fbccd } /* Name.Builtin.Pseudo */ +body[data-theme="dark"] .highlight .fm { color: #71adff } /* Name.Function.Magic */ +body[data-theme="dark"] .highlight .vc { color: #40ffff } /* Name.Variable.Class */ +body[data-theme="dark"] .highlight .vg { color: #40ffff } /* Name.Variable.Global */ +body[data-theme="dark"] .highlight .vi { color: #40ffff } /* Name.Variable.Instance */ +body[data-theme="dark"] .highlight .vm { color: #40ffff } /* Name.Variable.Magic */ +body[data-theme="dark"] .highlight .il { color: #51b2fd } /* Literal.Number.Integer.Long */ +@media (prefers-color-scheme: dark) { +body:not([data-theme="light"]) .highlight pre { line-height: 125%; } +body:not([data-theme="light"]) .highlight td.linenos .normal { color: #aaaaaa; background-color: transparent; padding-left: 5px; padding-right: 5px; } +body:not([data-theme="light"]) .highlight span.linenos { color: #aaaaaa; background-color: transparent; padding-left: 5px; padding-right: 5px; } +body:not([data-theme="light"]) .highlight td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +body:not([data-theme="light"]) .highlight span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +body:not([data-theme="light"]) .highlight .hll { background-color: #404040 } +body:not([data-theme="light"]) .highlight { background: #202020; color: #d0d0d0 } +body:not([data-theme="light"]) .highlight .c { color: #ababab; font-style: italic } /* Comment */ +body:not([data-theme="light"]) .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ +body:not([data-theme="light"]) .highlight .esc { color: #d0d0d0 } /* Escape */ +body:not([data-theme="light"]) .highlight .g { color: #d0d0d0 } /* Generic */ +body:not([data-theme="light"]) .highlight .k { color: #6ebf26; font-weight: bold } /* Keyword */ +body:not([data-theme="light"]) .highlight .l { color: #d0d0d0 } /* Literal */ +body:not([data-theme="light"]) .highlight .n { color: #d0d0d0 } /* Name */ +body:not([data-theme="light"]) .highlight .o { color: #d0d0d0 } /* Operator */ +body:not([data-theme="light"]) .highlight .x { color: #d0d0d0 } /* Other */ +body:not([data-theme="light"]) .highlight .p { color: #d0d0d0 } /* Punctuation */ +body:not([data-theme="light"]) .highlight .ch { color: #ababab; font-style: italic } /* Comment.Hashbang */ +body:not([data-theme="light"]) .highlight .cm { color: #ababab; font-style: italic } /* Comment.Multiline */ +body:not([data-theme="light"]) .highlight .cp { color: #ff3a3a; font-weight: bold } /* Comment.Preproc */ +body:not([data-theme="light"]) .highlight .cpf { color: #ababab; font-style: italic } /* Comment.PreprocFile */ +body:not([data-theme="light"]) .highlight .c1 { color: #ababab; font-style: italic } /* Comment.Single */ +body:not([data-theme="light"]) .highlight .cs { color: #e50808; font-weight: bold; background-color: #520000 } /* Comment.Special */ +body:not([data-theme="light"]) .highlight .gd { color: #d22323 } /* Generic.Deleted */ +body:not([data-theme="light"]) .highlight .ge { color: #d0d0d0; font-style: italic } /* Generic.Emph */ +body:not([data-theme="light"]) .highlight .ges { color: #d0d0d0; font-weight: bold; font-style: italic } /* Generic.EmphStrong */ +body:not([data-theme="light"]) .highlight .gr { color: #d22323 } /* Generic.Error */ +body:not([data-theme="light"]) .highlight .gh { color: #ffffff; font-weight: bold } /* Generic.Heading */ +body:not([data-theme="light"]) .highlight .gi { color: #589819 } /* Generic.Inserted */ +body:not([data-theme="light"]) .highlight .go { color: #cccccc } /* Generic.Output */ +body:not([data-theme="light"]) .highlight .gp { color: #aaaaaa } /* Generic.Prompt */ +body:not([data-theme="light"]) .highlight .gs { color: #d0d0d0; font-weight: bold } /* Generic.Strong */ +body:not([data-theme="light"]) .highlight .gu { color: #ffffff; text-decoration: underline } /* Generic.Subheading */ +body:not([data-theme="light"]) .highlight .gt { color: #d22323 } /* Generic.Traceback */ +body:not([data-theme="light"]) .highlight .kc { color: #6ebf26; font-weight: bold } /* Keyword.Constant */ +body:not([data-theme="light"]) .highlight .kd { color: #6ebf26; font-weight: bold } /* Keyword.Declaration */ +body:not([data-theme="light"]) .highlight .kn { color: #6ebf26; font-weight: bold } /* Keyword.Namespace */ +body:not([data-theme="light"]) .highlight .kp { color: #6ebf26 } /* Keyword.Pseudo */ +body:not([data-theme="light"]) .highlight .kr { color: #6ebf26; font-weight: bold } /* Keyword.Reserved */ +body:not([data-theme="light"]) .highlight .kt { color: #6ebf26; font-weight: bold } /* Keyword.Type */ +body:not([data-theme="light"]) .highlight .ld { color: #d0d0d0 } /* Literal.Date */ +body:not([data-theme="light"]) .highlight .m { color: #51b2fd } /* Literal.Number */ +body:not([data-theme="light"]) .highlight .s { color: #ed9d13 } /* Literal.String */ +body:not([data-theme="light"]) .highlight .na { color: #bbbbbb } /* Name.Attribute */ +body:not([data-theme="light"]) .highlight .nb { color: #2fbccd } /* Name.Builtin */ +body:not([data-theme="light"]) .highlight .nc { color: #71adff; text-decoration: underline } /* Name.Class */ +body:not([data-theme="light"]) .highlight .no { color: #40ffff } /* Name.Constant */ +body:not([data-theme="light"]) .highlight .nd { color: #ffa500 } /* Name.Decorator */ +body:not([data-theme="light"]) .highlight .ni { color: #d0d0d0 } /* Name.Entity */ +body:not([data-theme="light"]) .highlight .ne { color: #bbbbbb } /* Name.Exception */ +body:not([data-theme="light"]) .highlight .nf { color: #71adff } /* Name.Function */ +body:not([data-theme="light"]) .highlight .nl { color: #d0d0d0 } /* Name.Label */ +body:not([data-theme="light"]) .highlight .nn { color: #71adff; text-decoration: underline } /* Name.Namespace */ +body:not([data-theme="light"]) .highlight .nx { color: #d0d0d0 } /* Name.Other */ +body:not([data-theme="light"]) .highlight .py { color: #d0d0d0 } /* Name.Property */ +body:not([data-theme="light"]) .highlight .nt { color: #6ebf26; font-weight: bold } /* Name.Tag */ +body:not([data-theme="light"]) .highlight .nv { color: #40ffff } /* Name.Variable */ +body:not([data-theme="light"]) .highlight .ow { color: #6ebf26; font-weight: bold } /* Operator.Word */ +body:not([data-theme="light"]) .highlight .pm { color: #d0d0d0 } /* Punctuation.Marker */ +body:not([data-theme="light"]) .highlight .w { color: #666666 } /* Text.Whitespace */ +body:not([data-theme="light"]) .highlight .mb { color: #51b2fd } /* Literal.Number.Bin */ +body:not([data-theme="light"]) .highlight .mf { color: #51b2fd } /* Literal.Number.Float */ +body:not([data-theme="light"]) .highlight .mh { color: #51b2fd } /* Literal.Number.Hex */ +body:not([data-theme="light"]) .highlight .mi { color: #51b2fd } /* Literal.Number.Integer */ +body:not([data-theme="light"]) .highlight .mo { color: #51b2fd } /* Literal.Number.Oct */ +body:not([data-theme="light"]) .highlight .sa { color: #ed9d13 } /* Literal.String.Affix */ +body:not([data-theme="light"]) .highlight .sb { color: #ed9d13 } /* Literal.String.Backtick */ +body:not([data-theme="light"]) .highlight .sc { color: #ed9d13 } /* Literal.String.Char */ +body:not([data-theme="light"]) .highlight .dl { color: #ed9d13 } /* Literal.String.Delimiter */ +body:not([data-theme="light"]) .highlight .sd { color: #ed9d13 } /* Literal.String.Doc */ +body:not([data-theme="light"]) .highlight .s2 { color: #ed9d13 } /* Literal.String.Double */ +body:not([data-theme="light"]) .highlight .se { color: #ed9d13 } /* Literal.String.Escape */ +body:not([data-theme="light"]) .highlight .sh { color: #ed9d13 } /* Literal.String.Heredoc */ +body:not([data-theme="light"]) .highlight .si { color: #ed9d13 } /* Literal.String.Interpol */ +body:not([data-theme="light"]) .highlight .sx { color: #ffa500 } /* Literal.String.Other */ +body:not([data-theme="light"]) .highlight .sr { color: #ed9d13 } /* Literal.String.Regex */ +body:not([data-theme="light"]) .highlight .s1 { color: #ed9d13 } /* Literal.String.Single */ +body:not([data-theme="light"]) .highlight .ss { color: #ed9d13 } /* Literal.String.Symbol */ +body:not([data-theme="light"]) .highlight .bp { color: #2fbccd } /* Name.Builtin.Pseudo */ +body:not([data-theme="light"]) .highlight .fm { color: #71adff } /* Name.Function.Magic */ +body:not([data-theme="light"]) .highlight .vc { color: #40ffff } /* Name.Variable.Class */ +body:not([data-theme="light"]) .highlight .vg { color: #40ffff } /* Name.Variable.Global */ +body:not([data-theme="light"]) .highlight .vi { color: #40ffff } /* Name.Variable.Instance */ +body:not([data-theme="light"]) .highlight .vm { color: #40ffff } /* Name.Variable.Magic */ +body:not([data-theme="light"]) .highlight .il { color: #51b2fd } /* Literal.Number.Integer.Long */ +} +} \ No newline at end of file diff --git a/_static/scripts/furo-extensions.js b/_static/scripts/furo-extensions.js new file mode 100644 index 0000000..e69de29 diff --git a/_static/scripts/furo.js b/_static/scripts/furo.js new file mode 100644 index 0000000..32e7c05 --- /dev/null +++ b/_static/scripts/furo.js @@ -0,0 +1,3 @@ +/*! For license information please see furo.js.LICENSE.txt */ +(()=>{var t={212:function(t,e,n){var o,r;r=void 0!==n.g?n.g:"undefined"!=typeof window?window:this,o=function(){return function(t){"use strict";var e={navClass:"active",contentClass:"active",nested:!1,nestedClass:"active",offset:0,reflow:!1,events:!0},n=function(t,e,n){if(n.settings.events){var o=new CustomEvent(t,{bubbles:!0,cancelable:!0,detail:n});e.dispatchEvent(o)}},o=function(t){var e=0;if(t.offsetParent)for(;t;)e+=t.offsetTop,t=t.offsetParent;return e>=0?e:0},r=function(t){t&&t.sort((function(t,e){return o(t.content)=Math.max(document.body.scrollHeight,document.documentElement.scrollHeight,document.body.offsetHeight,document.documentElement.offsetHeight,document.body.clientHeight,document.documentElement.clientHeight)},l=function(t,e){var n=t[t.length-1];if(function(t,e){return!(!s()||!c(t.content,e,!0))}(n,e))return n;for(var o=t.length-1;o>=0;o--)if(c(t[o].content,e))return t[o]},a=function(t,e){if(e.nested&&t.parentNode){var n=t.parentNode.closest("li");n&&(n.classList.remove(e.nestedClass),a(n,e))}},i=function(t,e){if(t){var o=t.nav.closest("li");o&&(o.classList.remove(e.navClass),t.content.classList.remove(e.contentClass),a(o,e),n("gumshoeDeactivate",o,{link:t.nav,content:t.content,settings:e}))}},u=function(t,e){if(e.nested){var n=t.parentNode.closest("li");n&&(n.classList.add(e.nestedClass),u(n,e))}};return function(o,c){var s,a,d,f,m,v={setup:function(){s=document.querySelectorAll(o),a=[],Array.prototype.forEach.call(s,(function(t){var e=document.getElementById(decodeURIComponent(t.hash.substr(1)));e&&a.push({nav:t,content:e})})),r(a)},detect:function(){var t=l(a,m);t?d&&t.content===d.content||(i(d,m),function(t,e){if(t){var o=t.nav.closest("li");o&&(o.classList.add(e.navClass),t.content.classList.add(e.contentClass),u(o,e),n("gumshoeActivate",o,{link:t.nav,content:t.content,settings:e}))}}(t,m),d=t):d&&(i(d,m),d=null)}},h=function(e){f&&t.cancelAnimationFrame(f),f=t.requestAnimationFrame(v.detect)},g=function(e){f&&t.cancelAnimationFrame(f),f=t.requestAnimationFrame((function(){r(a),v.detect()}))};return v.destroy=function(){d&&i(d,m),t.removeEventListener("scroll",h,!1),m.reflow&&t.removeEventListener("resize",g,!1),a=null,s=null,d=null,f=null,m=null},m=function(){var t={};return Array.prototype.forEach.call(arguments,(function(e){for(var n in e){if(!e.hasOwnProperty(n))return;t[n]=e[n]}})),t}(e,c||{}),v.setup(),v.detect(),t.addEventListener("scroll",h,!1),m.reflow&&t.addEventListener("resize",g,!1),v}}(r)}.apply(e,[]),void 0===o||(t.exports=o)}},e={};function n(o){var r=e[o];if(void 0!==r)return r.exports;var c=e[o]={exports:{}};return t[o].call(c.exports,c,c.exports,n),c.exports}n.n=t=>{var e=t&&t.__esModule?()=>t.default:()=>t;return n.d(e,{a:e}),e},n.d=(t,e)=>{for(var o in e)n.o(e,o)&&!n.o(t,o)&&Object.defineProperty(t,o,{enumerable:!0,get:e[o]})},n.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(t){if("object"==typeof window)return window}}(),n.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),(()=>{"use strict";var t=n(212),e=n.n(t),o=null,r=null,c=window.pageYOffset||document.documentElement.scrollTop;const s=64;function l(){const t=localStorage.getItem("theme")||"auto";var e;"light"!==(e=window.matchMedia("(prefers-color-scheme: dark)").matches?"auto"===t?"light":"light"==t?"dark":"auto":"auto"===t?"dark":"dark"==t?"light":"auto")&&"dark"!==e&&"auto"!==e&&(console.error(`Got invalid theme mode: ${e}. Resetting to auto.`),e="auto"),document.body.dataset.theme=e,localStorage.setItem("theme",e),console.log(`Changed to ${e} mode.`)}function a(){!function(){const t=document.getElementsByClassName("theme-toggle");Array.from(t).forEach((t=>{t.addEventListener("click",l)}))}(),function(){let t=0,e=!1;window.addEventListener("scroll",(function(n){t=window.scrollY,e||(window.requestAnimationFrame((function(){var n;n=t,0==Math.floor(r.getBoundingClientRect().top)?r.classList.add("scrolled"):r.classList.remove("scrolled"),function(t){tc&&document.documentElement.classList.remove("show-back-to-top"),c=t}(n),function(t){null!==o&&(0==t?o.scrollTo(0,0):Math.ceil(t)>=Math.floor(document.documentElement.scrollHeight-window.innerHeight)?o.scrollTo(0,o.scrollHeight):document.querySelector(".scroll-current"))}(n),e=!1})),e=!0)})),window.scroll()}(),null!==o&&new(e())(".toc-tree a",{reflow:!0,recursive:!0,navClass:"scroll-current",offset:()=>{let t=parseFloat(getComputedStyle(document.documentElement).fontSize);return r.getBoundingClientRect().height+.5*t+1}})}document.addEventListener("DOMContentLoaded",(function(){document.body.parentNode.classList.remove("no-js"),r=document.querySelector("header"),o=document.querySelector(".toc-scroll"),a()}))})()})(); +//# sourceMappingURL=furo.js.map \ No newline at end of file diff --git a/_static/scripts/furo.js.LICENSE.txt b/_static/scripts/furo.js.LICENSE.txt new file mode 100644 index 0000000..1632189 --- /dev/null +++ b/_static/scripts/furo.js.LICENSE.txt @@ -0,0 +1,7 @@ +/*! + * gumshoejs v5.1.2 (patched by @pradyunsg) + * A simple, framework-agnostic scrollspy script. + * (c) 2019 Chris Ferdinandi + * MIT License + * http://github.com/cferdinandi/gumshoe + */ diff --git a/_static/scripts/furo.js.map b/_static/scripts/furo.js.map new file mode 100644 index 0000000..7b7ddb1 --- /dev/null +++ b/_static/scripts/furo.js.map @@ -0,0 +1 @@ +{"version":3,"file":"scripts/furo.js","mappings":"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","sources":["webpack:///./src/furo/assets/scripts/gumshoe-patched.js","webpack:///webpack/bootstrap","webpack:///webpack/runtime/compat get default export","webpack:///webpack/runtime/define property getters","webpack:///webpack/runtime/global","webpack:///webpack/runtime/hasOwnProperty shorthand","webpack:///./src/furo/assets/scripts/furo.js"],"sourcesContent":["/*!\n * gumshoejs v5.1.2 (patched by @pradyunsg)\n * A simple, framework-agnostic scrollspy script.\n * (c) 2019 Chris Ferdinandi\n * MIT License\n * http://github.com/cferdinandi/gumshoe\n */\n\n(function (root, factory) {\n if (typeof define === \"function\" && define.amd) {\n define([], function () {\n return factory(root);\n });\n } else if (typeof exports === \"object\") {\n module.exports = factory(root);\n } else {\n root.Gumshoe = factory(root);\n }\n})(\n typeof global !== \"undefined\"\n ? global\n : typeof window !== \"undefined\"\n ? window\n : this,\n function (window) {\n \"use strict\";\n\n //\n // Defaults\n //\n\n var defaults = {\n // Active classes\n navClass: \"active\",\n contentClass: \"active\",\n\n // Nested navigation\n nested: false,\n nestedClass: \"active\",\n\n // Offset & reflow\n offset: 0,\n reflow: false,\n\n // Event support\n events: true,\n };\n\n //\n // Methods\n //\n\n /**\n * Merge two or more objects together.\n * @param {Object} objects The objects to merge together\n * @returns {Object} Merged values of defaults and options\n */\n var extend = function () {\n var merged = {};\n Array.prototype.forEach.call(arguments, function (obj) {\n for (var key in obj) {\n if (!obj.hasOwnProperty(key)) return;\n merged[key] = obj[key];\n }\n });\n return merged;\n };\n\n /**\n * Emit a custom event\n * @param {String} type The event type\n * @param {Node} elem The element to attach the event to\n * @param {Object} detail Any details to pass along with the event\n */\n var emitEvent = function (type, elem, detail) {\n // Make sure events are enabled\n if (!detail.settings.events) return;\n\n // Create a new event\n var event = new CustomEvent(type, {\n bubbles: true,\n cancelable: true,\n detail: detail,\n });\n\n // Dispatch the event\n elem.dispatchEvent(event);\n };\n\n /**\n * Get an element's distance from the top of the Document.\n * @param {Node} elem The element\n * @return {Number} Distance from the top in pixels\n */\n var getOffsetTop = function (elem) {\n var location = 0;\n if (elem.offsetParent) {\n while (elem) {\n location += elem.offsetTop;\n elem = elem.offsetParent;\n }\n }\n return location >= 0 ? location : 0;\n };\n\n /**\n * Sort content from first to last in the DOM\n * @param {Array} contents The content areas\n */\n var sortContents = function (contents) {\n if (contents) {\n contents.sort(function (item1, item2) {\n var offset1 = getOffsetTop(item1.content);\n var offset2 = getOffsetTop(item2.content);\n if (offset1 < offset2) return -1;\n return 1;\n });\n }\n };\n\n /**\n * Get the offset to use for calculating position\n * @param {Object} settings The settings for this instantiation\n * @return {Float} The number of pixels to offset the calculations\n */\n var getOffset = function (settings) {\n // if the offset is a function run it\n if (typeof settings.offset === \"function\") {\n return parseFloat(settings.offset());\n }\n\n // Otherwise, return it as-is\n return parseFloat(settings.offset);\n };\n\n /**\n * Get the document element's height\n * @private\n * @returns {Number}\n */\n var getDocumentHeight = function () {\n return Math.max(\n document.body.scrollHeight,\n document.documentElement.scrollHeight,\n document.body.offsetHeight,\n document.documentElement.offsetHeight,\n document.body.clientHeight,\n document.documentElement.clientHeight,\n );\n };\n\n /**\n * Determine if an element is in view\n * @param {Node} elem The element\n * @param {Object} settings The settings for this instantiation\n * @param {Boolean} bottom If true, check if element is above bottom of viewport instead\n * @return {Boolean} Returns true if element is in the viewport\n */\n var isInView = function (elem, settings, bottom) {\n var bounds = elem.getBoundingClientRect();\n var offset = getOffset(settings);\n if (bottom) {\n return (\n parseInt(bounds.bottom, 10) <\n (window.innerHeight || document.documentElement.clientHeight)\n );\n }\n return parseInt(bounds.top, 10) <= offset;\n };\n\n /**\n * Check if at the bottom of the viewport\n * @return {Boolean} If true, page is at the bottom of the viewport\n */\n var isAtBottom = function () {\n if (\n Math.ceil(window.innerHeight + window.pageYOffset) >=\n getDocumentHeight()\n )\n return true;\n return false;\n };\n\n /**\n * Check if the last item should be used (even if not at the top of the page)\n * @param {Object} item The last item\n * @param {Object} settings The settings for this instantiation\n * @return {Boolean} If true, use the last item\n */\n var useLastItem = function (item, settings) {\n if (isAtBottom() && isInView(item.content, settings, true)) return true;\n return false;\n };\n\n /**\n * Get the active content\n * @param {Array} contents The content areas\n * @param {Object} settings The settings for this instantiation\n * @return {Object} The content area and matching navigation link\n */\n var getActive = function (contents, settings) {\n var last = contents[contents.length - 1];\n if (useLastItem(last, settings)) return last;\n for (var i = contents.length - 1; i >= 0; i--) {\n if (isInView(contents[i].content, settings)) return contents[i];\n }\n };\n\n /**\n * Deactivate parent navs in a nested navigation\n * @param {Node} nav The starting navigation element\n * @param {Object} settings The settings for this instantiation\n */\n var deactivateNested = function (nav, settings) {\n // If nesting isn't activated, bail\n if (!settings.nested || !nav.parentNode) return;\n\n // Get the parent navigation\n var li = nav.parentNode.closest(\"li\");\n if (!li) return;\n\n // Remove the active class\n li.classList.remove(settings.nestedClass);\n\n // Apply recursively to any parent navigation elements\n deactivateNested(li, settings);\n };\n\n /**\n * Deactivate a nav and content area\n * @param {Object} items The nav item and content to deactivate\n * @param {Object} settings The settings for this instantiation\n */\n var deactivate = function (items, settings) {\n // Make sure there are items to deactivate\n if (!items) return;\n\n // Get the parent list item\n var li = items.nav.closest(\"li\");\n if (!li) return;\n\n // Remove the active class from the nav and content\n li.classList.remove(settings.navClass);\n items.content.classList.remove(settings.contentClass);\n\n // Deactivate any parent navs in a nested navigation\n deactivateNested(li, settings);\n\n // Emit a custom event\n emitEvent(\"gumshoeDeactivate\", li, {\n link: items.nav,\n content: items.content,\n settings: settings,\n });\n };\n\n /**\n * Activate parent navs in a nested navigation\n * @param {Node} nav The starting navigation element\n * @param {Object} settings The settings for this instantiation\n */\n var activateNested = function (nav, settings) {\n // If nesting isn't activated, bail\n if (!settings.nested) return;\n\n // Get the parent navigation\n var li = nav.parentNode.closest(\"li\");\n if (!li) return;\n\n // Add the active class\n li.classList.add(settings.nestedClass);\n\n // Apply recursively to any parent navigation elements\n activateNested(li, settings);\n };\n\n /**\n * Activate a nav and content area\n * @param {Object} items The nav item and content to activate\n * @param {Object} settings The settings for this instantiation\n */\n var activate = function (items, settings) {\n // Make sure there are items to activate\n if (!items) return;\n\n // Get the parent list item\n var li = items.nav.closest(\"li\");\n if (!li) return;\n\n // Add the active class to the nav and content\n li.classList.add(settings.navClass);\n items.content.classList.add(settings.contentClass);\n\n // Activate any parent navs in a nested navigation\n activateNested(li, settings);\n\n // Emit a custom event\n emitEvent(\"gumshoeActivate\", li, {\n link: items.nav,\n content: items.content,\n settings: settings,\n });\n };\n\n /**\n * Create the Constructor object\n * @param {String} selector The selector to use for navigation items\n * @param {Object} options User options and settings\n */\n var Constructor = function (selector, options) {\n //\n // Variables\n //\n\n var publicAPIs = {};\n var navItems, contents, current, timeout, settings;\n\n //\n // Methods\n //\n\n /**\n * Set variables from DOM elements\n */\n publicAPIs.setup = function () {\n // Get all nav items\n navItems = document.querySelectorAll(selector);\n\n // Create contents array\n contents = [];\n\n // Loop through each item, get it's matching content, and push to the array\n Array.prototype.forEach.call(navItems, function (item) {\n // Get the content for the nav item\n var content = document.getElementById(\n decodeURIComponent(item.hash.substr(1)),\n );\n if (!content) return;\n\n // Push to the contents array\n contents.push({\n nav: item,\n content: content,\n });\n });\n\n // Sort contents by the order they appear in the DOM\n sortContents(contents);\n };\n\n /**\n * Detect which content is currently active\n */\n publicAPIs.detect = function () {\n // Get the active content\n var active = getActive(contents, settings);\n\n // if there's no active content, deactivate and bail\n if (!active) {\n if (current) {\n deactivate(current, settings);\n current = null;\n }\n return;\n }\n\n // If the active content is the one currently active, do nothing\n if (current && active.content === current.content) return;\n\n // Deactivate the current content and activate the new content\n deactivate(current, settings);\n activate(active, settings);\n\n // Update the currently active content\n current = active;\n };\n\n /**\n * Detect the active content on scroll\n * Debounced for performance\n */\n var scrollHandler = function (event) {\n // If there's a timer, cancel it\n if (timeout) {\n window.cancelAnimationFrame(timeout);\n }\n\n // Setup debounce callback\n timeout = window.requestAnimationFrame(publicAPIs.detect);\n };\n\n /**\n * Update content sorting on resize\n * Debounced for performance\n */\n var resizeHandler = function (event) {\n // If there's a timer, cancel it\n if (timeout) {\n window.cancelAnimationFrame(timeout);\n }\n\n // Setup debounce callback\n timeout = window.requestAnimationFrame(function () {\n sortContents(contents);\n publicAPIs.detect();\n });\n };\n\n /**\n * Destroy the current instantiation\n */\n publicAPIs.destroy = function () {\n // Undo DOM changes\n if (current) {\n deactivate(current, settings);\n }\n\n // Remove event listeners\n window.removeEventListener(\"scroll\", scrollHandler, false);\n if (settings.reflow) {\n window.removeEventListener(\"resize\", resizeHandler, false);\n }\n\n // Reset variables\n contents = null;\n navItems = null;\n current = null;\n timeout = null;\n settings = null;\n };\n\n /**\n * Initialize the current instantiation\n */\n var init = function () {\n // Merge user options into defaults\n settings = extend(defaults, options || {});\n\n // Setup variables based on the current DOM\n publicAPIs.setup();\n\n // Find the currently active content\n publicAPIs.detect();\n\n // Setup event listeners\n window.addEventListener(\"scroll\", scrollHandler, false);\n if (settings.reflow) {\n window.addEventListener(\"resize\", resizeHandler, false);\n }\n };\n\n //\n // Initialize and return the public APIs\n //\n\n init();\n return publicAPIs;\n };\n\n //\n // Return the Constructor\n //\n\n return Constructor;\n },\n);\n","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// getDefaultExport function for compatibility with non-harmony modules\n__webpack_require__.n = (module) => {\n\tvar getter = module && module.__esModule ?\n\t\t() => (module['default']) :\n\t\t() => (module);\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.g = (function() {\n\tif (typeof globalThis === 'object') return globalThis;\n\ttry {\n\t\treturn this || new Function('return this')();\n\t} catch (e) {\n\t\tif (typeof window === 'object') return window;\n\t}\n})();","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","import Gumshoe from \"./gumshoe-patched.js\";\n\n////////////////////////////////////////////////////////////////////////////////\n// Scroll Handling\n////////////////////////////////////////////////////////////////////////////////\nvar tocScroll = null;\nvar header = null;\nvar lastScrollTop = window.pageYOffset || document.documentElement.scrollTop;\nconst GO_TO_TOP_OFFSET = 64;\n\nfunction scrollHandlerForHeader() {\n if (Math.floor(header.getBoundingClientRect().top) == 0) {\n header.classList.add(\"scrolled\");\n } else {\n header.classList.remove(\"scrolled\");\n }\n}\n\nfunction scrollHandlerForBackToTop(positionY) {\n if (positionY < GO_TO_TOP_OFFSET) {\n document.documentElement.classList.remove(\"show-back-to-top\");\n } else {\n if (positionY < lastScrollTop) {\n document.documentElement.classList.add(\"show-back-to-top\");\n } else if (positionY > lastScrollTop) {\n document.documentElement.classList.remove(\"show-back-to-top\");\n }\n }\n lastScrollTop = positionY;\n}\n\nfunction scrollHandlerForTOC(positionY) {\n if (tocScroll === null) {\n return;\n }\n\n // top of page.\n if (positionY == 0) {\n tocScroll.scrollTo(0, 0);\n } else if (\n // bottom of page.\n Math.ceil(positionY) >=\n Math.floor(document.documentElement.scrollHeight - window.innerHeight)\n ) {\n tocScroll.scrollTo(0, tocScroll.scrollHeight);\n } else {\n // somewhere in the middle.\n const current = document.querySelector(\".scroll-current\");\n if (current == null) {\n return;\n }\n\n // https://github.com/pypa/pip/issues/9159 This breaks scroll behaviours.\n // // scroll the currently \"active\" heading in toc, into view.\n // const rect = current.getBoundingClientRect();\n // if (0 > rect.top) {\n // current.scrollIntoView(true); // the argument is \"alignTop\"\n // } else if (rect.bottom > window.innerHeight) {\n // current.scrollIntoView(false);\n // }\n }\n}\n\nfunction scrollHandler(positionY) {\n scrollHandlerForHeader();\n scrollHandlerForBackToTop(positionY);\n scrollHandlerForTOC(positionY);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Theme Toggle\n////////////////////////////////////////////////////////////////////////////////\nfunction setTheme(mode) {\n if (mode !== \"light\" && mode !== \"dark\" && mode !== \"auto\") {\n console.error(`Got invalid theme mode: ${mode}. Resetting to auto.`);\n mode = \"auto\";\n }\n\n document.body.dataset.theme = mode;\n localStorage.setItem(\"theme\", mode);\n console.log(`Changed to ${mode} mode.`);\n}\n\nfunction cycleThemeOnce() {\n const currentTheme = localStorage.getItem(\"theme\") || \"auto\";\n const prefersDark = window.matchMedia(\"(prefers-color-scheme: dark)\").matches;\n\n if (prefersDark) {\n // Auto (dark) -> Light -> Dark\n if (currentTheme === \"auto\") {\n setTheme(\"light\");\n } else if (currentTheme == \"light\") {\n setTheme(\"dark\");\n } else {\n setTheme(\"auto\");\n }\n } else {\n // Auto (light) -> Dark -> Light\n if (currentTheme === \"auto\") {\n setTheme(\"dark\");\n } else if (currentTheme == \"dark\") {\n setTheme(\"light\");\n } else {\n setTheme(\"auto\");\n }\n }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Setup\n////////////////////////////////////////////////////////////////////////////////\nfunction setupScrollHandler() {\n // Taken from https://developer.mozilla.org/en-US/docs/Web/API/Document/scroll_event\n let last_known_scroll_position = 0;\n let ticking = false;\n\n window.addEventListener(\"scroll\", function (e) {\n last_known_scroll_position = window.scrollY;\n\n if (!ticking) {\n window.requestAnimationFrame(function () {\n scrollHandler(last_known_scroll_position);\n ticking = false;\n });\n\n ticking = true;\n }\n });\n window.scroll();\n}\n\nfunction setupScrollSpy() {\n if (tocScroll === null) {\n return;\n }\n\n // Scrollspy -- highlight table on contents, based on scroll\n new Gumshoe(\".toc-tree a\", {\n reflow: true,\n recursive: true,\n navClass: \"scroll-current\",\n offset: () => {\n let rem = parseFloat(getComputedStyle(document.documentElement).fontSize);\n return header.getBoundingClientRect().height + 0.5 * rem + 1;\n },\n });\n}\n\nfunction setupTheme() {\n // Attach event handlers for toggling themes\n const buttons = document.getElementsByClassName(\"theme-toggle\");\n Array.from(buttons).forEach((btn) => {\n btn.addEventListener(\"click\", cycleThemeOnce);\n });\n}\n\nfunction setup() {\n setupTheme();\n setupScrollHandler();\n setupScrollSpy();\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Main entrypoint\n////////////////////////////////////////////////////////////////////////////////\nfunction main() {\n document.body.parentNode.classList.remove(\"no-js\");\n\n header = document.querySelector(\"header\");\n tocScroll = document.querySelector(\".toc-scroll\");\n\n setup();\n}\n\ndocument.addEventListener(\"DOMContentLoaded\", main);\n"],"names":["root","g","window","this","defaults","navClass","contentClass","nested","nestedClass","offset","reflow","events","emitEvent","type","elem","detail","settings","event","CustomEvent","bubbles","cancelable","dispatchEvent","getOffsetTop","location","offsetParent","offsetTop","sortContents","contents","sort","item1","item2","content","isInView","bottom","bounds","getBoundingClientRect","parseFloat","getOffset","parseInt","innerHeight","document","documentElement","clientHeight","top","isAtBottom","Math","ceil","pageYOffset","max","body","scrollHeight","offsetHeight","getActive","last","length","item","useLastItem","i","deactivateNested","nav","parentNode","li","closest","classList","remove","deactivate","items","link","activateNested","add","selector","options","navItems","current","timeout","publicAPIs","querySelectorAll","Array","prototype","forEach","call","getElementById","decodeURIComponent","hash","substr","push","active","activate","scrollHandler","cancelAnimationFrame","requestAnimationFrame","detect","resizeHandler","destroy","removeEventListener","merged","arguments","obj","key","hasOwnProperty","extend","setup","addEventListener","factory","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","undefined","exports","module","__webpack_modules__","n","getter","__esModule","d","a","definition","o","Object","defineProperty","enumerable","get","globalThis","Function","e","prop","tocScroll","header","lastScrollTop","scrollTop","GO_TO_TOP_OFFSET","cycleThemeOnce","currentTheme","localStorage","getItem","mode","matchMedia","matches","console","error","dataset","theme","setItem","log","buttons","getElementsByClassName","from","btn","setupTheme","last_known_scroll_position","ticking","scrollY","positionY","floor","scrollHandlerForBackToTop","scrollTo","querySelector","scrollHandlerForTOC","scroll","setupScrollHandler","recursive","rem","getComputedStyle","fontSize","height"],"sourceRoot":""} \ No newline at end of file diff --git a/_static/searchtools.js b/_static/searchtools.js new file mode 100644 index 0000000..7918c3f --- /dev/null +++ b/_static/searchtools.js @@ -0,0 +1,574 @@ +/* + * searchtools.js + * ~~~~~~~~~~~~~~~~ + * + * Sphinx JavaScript utilities for the full-text search. + * + * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ +"use strict"; + +/** + * Simple result scoring code. + */ +if (typeof Scorer === "undefined") { + var Scorer = { + // Implement the following function to further tweak the score for each result + // The function takes a result array [docname, title, anchor, descr, score, filename] + // and returns the new score. + /* + score: result => { + const [docname, title, anchor, descr, score, filename] = result + return score + }, + */ + + // query matches the full name of an object + objNameMatch: 11, + // or matches in the last dotted part of the object name + objPartialMatch: 6, + // Additive scores depending on the priority of the object + objPrio: { + 0: 15, // used to be importantResults + 1: 5, // used to be objectResults + 2: -5, // used to be unimportantResults + }, + // Used when the priority is not in the mapping. + objPrioDefault: 0, + + // query found in title + title: 15, + partialTitle: 7, + // query found in terms + term: 5, + partialTerm: 2, + }; +} + +const _removeChildren = (element) => { + while (element && element.lastChild) element.removeChild(element.lastChild); +}; + +/** + * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping + */ +const _escapeRegExp = (string) => + string.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string + +const _displayItem = (item, searchTerms, highlightTerms) => { + const docBuilder = DOCUMENTATION_OPTIONS.BUILDER; + const docFileSuffix = DOCUMENTATION_OPTIONS.FILE_SUFFIX; + const docLinkSuffix = DOCUMENTATION_OPTIONS.LINK_SUFFIX; + const showSearchSummary = DOCUMENTATION_OPTIONS.SHOW_SEARCH_SUMMARY; + const contentRoot = document.documentElement.dataset.content_root; + + const [docName, title, anchor, descr, score, _filename] = item; + + let listItem = document.createElement("li"); + let requestUrl; + let linkUrl; + if (docBuilder === "dirhtml") { + // dirhtml builder + let dirname = docName + "/"; + if (dirname.match(/\/index\/$/)) + dirname = dirname.substring(0, dirname.length - 6); + else if (dirname === "index/") dirname = ""; + requestUrl = contentRoot + dirname; + linkUrl = requestUrl; + } else { + // normal html builders + requestUrl = contentRoot + docName + docFileSuffix; + linkUrl = docName + docLinkSuffix; + } + let linkEl = listItem.appendChild(document.createElement("a")); + linkEl.href = linkUrl + anchor; + linkEl.dataset.score = score; + linkEl.innerHTML = title; + if (descr) { + listItem.appendChild(document.createElement("span")).innerHTML = + " (" + descr + ")"; + // highlight search terms in the description + if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js + highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted")); + } + else if (showSearchSummary) + fetch(requestUrl) + .then((responseData) => responseData.text()) + .then((data) => { + if (data) + listItem.appendChild( + Search.makeSearchSummary(data, searchTerms) + ); + // highlight search terms in the summary + if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js + highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted")); + }); + Search.output.appendChild(listItem); +}; +const _finishSearch = (resultCount) => { + Search.stopPulse(); + Search.title.innerText = _("Search Results"); + if (!resultCount) + Search.status.innerText = Documentation.gettext( + "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories." + ); + else + Search.status.innerText = _( + `Search finished, found ${resultCount} page(s) matching the search query.` + ); +}; +const _displayNextItem = ( + results, + resultCount, + searchTerms, + highlightTerms, +) => { + // results left, load the summary and display it + // this is intended to be dynamic (don't sub resultsCount) + if (results.length) { + _displayItem(results.pop(), searchTerms, highlightTerms); + setTimeout( + () => _displayNextItem(results, resultCount, searchTerms, highlightTerms), + 5 + ); + } + // search finished, update title and status message + else _finishSearch(resultCount); +}; + +/** + * Default splitQuery function. Can be overridden in ``sphinx.search`` with a + * custom function per language. + * + * The regular expression works by splitting the string on consecutive characters + * that are not Unicode letters, numbers, underscores, or emoji characters. + * This is the same as ``\W+`` in Python, preserving the surrogate pair area. + */ +if (typeof splitQuery === "undefined") { + var splitQuery = (query) => query + .split(/[^\p{Letter}\p{Number}_\p{Emoji_Presentation}]+/gu) + .filter(term => term) // remove remaining empty strings +} + +/** + * Search Module + */ +const Search = { + _index: null, + _queued_query: null, + _pulse_status: -1, + + htmlToText: (htmlString) => { + const htmlElement = new DOMParser().parseFromString(htmlString, 'text/html'); + htmlElement.querySelectorAll(".headerlink").forEach((el) => { el.remove() }); + const docContent = htmlElement.querySelector('[role="main"]'); + if (docContent !== undefined) return docContent.textContent; + console.warn( + "Content block not found. Sphinx search tries to obtain it via '[role=main]'. Could you check your theme or template." + ); + return ""; + }, + + init: () => { + const query = new URLSearchParams(window.location.search).get("q"); + document + .querySelectorAll('input[name="q"]') + .forEach((el) => (el.value = query)); + if (query) Search.performSearch(query); + }, + + loadIndex: (url) => + (document.body.appendChild(document.createElement("script")).src = url), + + setIndex: (index) => { + Search._index = index; + if (Search._queued_query !== null) { + const query = Search._queued_query; + Search._queued_query = null; + Search.query(query); + } + }, + + hasIndex: () => Search._index !== null, + + deferQuery: (query) => (Search._queued_query = query), + + stopPulse: () => (Search._pulse_status = -1), + + startPulse: () => { + if (Search._pulse_status >= 0) return; + + const pulse = () => { + Search._pulse_status = (Search._pulse_status + 1) % 4; + Search.dots.innerText = ".".repeat(Search._pulse_status); + if (Search._pulse_status >= 0) window.setTimeout(pulse, 500); + }; + pulse(); + }, + + /** + * perform a search for something (or wait until index is loaded) + */ + performSearch: (query) => { + // create the required interface elements + const searchText = document.createElement("h2"); + searchText.textContent = _("Searching"); + const searchSummary = document.createElement("p"); + searchSummary.classList.add("search-summary"); + searchSummary.innerText = ""; + const searchList = document.createElement("ul"); + searchList.classList.add("search"); + + const out = document.getElementById("search-results"); + Search.title = out.appendChild(searchText); + Search.dots = Search.title.appendChild(document.createElement("span")); + Search.status = out.appendChild(searchSummary); + Search.output = out.appendChild(searchList); + + const searchProgress = document.getElementById("search-progress"); + // Some themes don't use the search progress node + if (searchProgress) { + searchProgress.innerText = _("Preparing search..."); + } + Search.startPulse(); + + // index already loaded, the browser was quick! + if (Search.hasIndex()) Search.query(query); + else Search.deferQuery(query); + }, + + /** + * execute search (requires search index to be loaded) + */ + query: (query) => { + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const titles = Search._index.titles; + const allTitles = Search._index.alltitles; + const indexEntries = Search._index.indexentries; + + // stem the search terms and add them to the correct list + const stemmer = new Stemmer(); + const searchTerms = new Set(); + const excludedTerms = new Set(); + const highlightTerms = new Set(); + const objectTerms = new Set(splitQuery(query.toLowerCase().trim())); + splitQuery(query.trim()).forEach((queryTerm) => { + const queryTermLower = queryTerm.toLowerCase(); + + // maybe skip this "word" + // stopwords array is from language_data.js + if ( + stopwords.indexOf(queryTermLower) !== -1 || + queryTerm.match(/^\d+$/) + ) + return; + + // stem the word + let word = stemmer.stemWord(queryTermLower); + // select the correct list + if (word[0] === "-") excludedTerms.add(word.substr(1)); + else { + searchTerms.add(word); + highlightTerms.add(queryTermLower); + } + }); + + if (SPHINX_HIGHLIGHT_ENABLED) { // set in sphinx_highlight.js + localStorage.setItem("sphinx_highlight_terms", [...highlightTerms].join(" ")) + } + + // console.debug("SEARCH: searching for:"); + // console.info("required: ", [...searchTerms]); + // console.info("excluded: ", [...excludedTerms]); + + // array of [docname, title, anchor, descr, score, filename] + let results = []; + _removeChildren(document.getElementById("search-progress")); + + const queryLower = query.toLowerCase(); + for (const [title, foundTitles] of Object.entries(allTitles)) { + if (title.toLowerCase().includes(queryLower) && (queryLower.length >= title.length/2)) { + for (const [file, id] of foundTitles) { + let score = Math.round(100 * queryLower.length / title.length) + results.push([ + docNames[file], + titles[file] !== title ? `${titles[file]} > ${title}` : title, + id !== null ? "#" + id : "", + null, + score, + filenames[file], + ]); + } + } + } + + // search for explicit entries in index directives + for (const [entry, foundEntries] of Object.entries(indexEntries)) { + if (entry.includes(queryLower) && (queryLower.length >= entry.length/2)) { + for (const [file, id] of foundEntries) { + let score = Math.round(100 * queryLower.length / entry.length) + results.push([ + docNames[file], + titles[file], + id ? "#" + id : "", + null, + score, + filenames[file], + ]); + } + } + } + + // lookup as object + objectTerms.forEach((term) => + results.push(...Search.performObjectSearch(term, objectTerms)) + ); + + // lookup as search terms in fulltext + results.push(...Search.performTermsSearch(searchTerms, excludedTerms)); + + // let the scorer override scores with a custom scoring function + if (Scorer.score) results.forEach((item) => (item[4] = Scorer.score(item))); + + // now sort the results by score (in opposite order of appearance, since the + // display function below uses pop() to retrieve items) and then + // alphabetically + results.sort((a, b) => { + const leftScore = a[4]; + const rightScore = b[4]; + if (leftScore === rightScore) { + // same score: sort alphabetically + const leftTitle = a[1].toLowerCase(); + const rightTitle = b[1].toLowerCase(); + if (leftTitle === rightTitle) return 0; + return leftTitle > rightTitle ? -1 : 1; // inverted is intentional + } + return leftScore > rightScore ? 1 : -1; + }); + + // remove duplicate search results + // note the reversing of results, so that in the case of duplicates, the highest-scoring entry is kept + let seen = new Set(); + results = results.reverse().reduce((acc, result) => { + let resultStr = result.slice(0, 4).concat([result[5]]).map(v => String(v)).join(','); + if (!seen.has(resultStr)) { + acc.push(result); + seen.add(resultStr); + } + return acc; + }, []); + + results = results.reverse(); + + // for debugging + //Search.lastresults = results.slice(); // a copy + // console.info("search results:", Search.lastresults); + + // print the results + _displayNextItem(results, results.length, searchTerms, highlightTerms); + }, + + /** + * search for object names + */ + performObjectSearch: (object, objectTerms) => { + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const objects = Search._index.objects; + const objNames = Search._index.objnames; + const titles = Search._index.titles; + + const results = []; + + const objectSearchCallback = (prefix, match) => { + const name = match[4] + const fullname = (prefix ? prefix + "." : "") + name; + const fullnameLower = fullname.toLowerCase(); + if (fullnameLower.indexOf(object) < 0) return; + + let score = 0; + const parts = fullnameLower.split("."); + + // check for different match types: exact matches of full name or + // "last name" (i.e. last dotted part) + if (fullnameLower === object || parts.slice(-1)[0] === object) + score += Scorer.objNameMatch; + else if (parts.slice(-1)[0].indexOf(object) > -1) + score += Scorer.objPartialMatch; // matches in last name + + const objName = objNames[match[1]][2]; + const title = titles[match[0]]; + + // If more than one term searched for, we require other words to be + // found in the name/title/description + const otherTerms = new Set(objectTerms); + otherTerms.delete(object); + if (otherTerms.size > 0) { + const haystack = `${prefix} ${name} ${objName} ${title}`.toLowerCase(); + if ( + [...otherTerms].some((otherTerm) => haystack.indexOf(otherTerm) < 0) + ) + return; + } + + let anchor = match[3]; + if (anchor === "") anchor = fullname; + else if (anchor === "-") anchor = objNames[match[1]][1] + "-" + fullname; + + const descr = objName + _(", in ") + title; + + // add custom score for some objects according to scorer + if (Scorer.objPrio.hasOwnProperty(match[2])) + score += Scorer.objPrio[match[2]]; + else score += Scorer.objPrioDefault; + + results.push([ + docNames[match[0]], + fullname, + "#" + anchor, + descr, + score, + filenames[match[0]], + ]); + }; + Object.keys(objects).forEach((prefix) => + objects[prefix].forEach((array) => + objectSearchCallback(prefix, array) + ) + ); + return results; + }, + + /** + * search for full-text terms in the index + */ + performTermsSearch: (searchTerms, excludedTerms) => { + // prepare search + const terms = Search._index.terms; + const titleTerms = Search._index.titleterms; + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const titles = Search._index.titles; + + const scoreMap = new Map(); + const fileMap = new Map(); + + // perform the search on the required terms + searchTerms.forEach((word) => { + const files = []; + const arr = [ + { files: terms[word], score: Scorer.term }, + { files: titleTerms[word], score: Scorer.title }, + ]; + // add support for partial matches + if (word.length > 2) { + const escapedWord = _escapeRegExp(word); + Object.keys(terms).forEach((term) => { + if (term.match(escapedWord) && !terms[word]) + arr.push({ files: terms[term], score: Scorer.partialTerm }); + }); + Object.keys(titleTerms).forEach((term) => { + if (term.match(escapedWord) && !titleTerms[word]) + arr.push({ files: titleTerms[word], score: Scorer.partialTitle }); + }); + } + + // no match but word was a required one + if (arr.every((record) => record.files === undefined)) return; + + // found search word in contents + arr.forEach((record) => { + if (record.files === undefined) return; + + let recordFiles = record.files; + if (recordFiles.length === undefined) recordFiles = [recordFiles]; + files.push(...recordFiles); + + // set score for the word in each file + recordFiles.forEach((file) => { + if (!scoreMap.has(file)) scoreMap.set(file, {}); + scoreMap.get(file)[word] = record.score; + }); + }); + + // create the mapping + files.forEach((file) => { + if (fileMap.has(file) && fileMap.get(file).indexOf(word) === -1) + fileMap.get(file).push(word); + else fileMap.set(file, [word]); + }); + }); + + // now check if the files don't contain excluded terms + const results = []; + for (const [file, wordList] of fileMap) { + // check if all requirements are matched + + // as search terms with length < 3 are discarded + const filteredTermCount = [...searchTerms].filter( + (term) => term.length > 2 + ).length; + if ( + wordList.length !== searchTerms.size && + wordList.length !== filteredTermCount + ) + continue; + + // ensure that none of the excluded terms is in the search result + if ( + [...excludedTerms].some( + (term) => + terms[term] === file || + titleTerms[term] === file || + (terms[term] || []).includes(file) || + (titleTerms[term] || []).includes(file) + ) + ) + break; + + // select one (max) score for the file. + const score = Math.max(...wordList.map((w) => scoreMap.get(file)[w])); + // add result to the result list + results.push([ + docNames[file], + titles[file], + "", + null, + score, + filenames[file], + ]); + } + return results; + }, + + /** + * helper function to return a node containing the + * search summary for a given text. keywords is a list + * of stemmed words. + */ + makeSearchSummary: (htmlText, keywords) => { + const text = Search.htmlToText(htmlText); + if (text === "") return null; + + const textLower = text.toLowerCase(); + const actualStartPosition = [...keywords] + .map((k) => textLower.indexOf(k.toLowerCase())) + .filter((i) => i > -1) + .slice(-1)[0]; + const startWithContext = Math.max(actualStartPosition - 120, 0); + + const top = startWithContext === 0 ? "" : "..."; + const tail = startWithContext + 240 < text.length ? "..." : ""; + + let summary = document.createElement("p"); + summary.classList.add("context"); + summary.textContent = top + text.substr(startWithContext, 240).trim() + tail; + + return summary; + }, +}; + +_ready(Search.init); diff --git a/_static/skeleton.css b/_static/skeleton.css new file mode 100644 index 0000000..467c878 --- /dev/null +++ b/_static/skeleton.css @@ -0,0 +1,296 @@ +/* Some sane resets. */ +html { + height: 100%; +} + +body { + margin: 0; + min-height: 100%; +} + +/* All the flexbox magic! */ +body, +.sb-announcement, +.sb-content, +.sb-main, +.sb-container, +.sb-container__inner, +.sb-article-container, +.sb-footer-content, +.sb-header, +.sb-header-secondary, +.sb-footer { + display: flex; +} + +/* These order things vertically */ +body, +.sb-main, +.sb-article-container { + flex-direction: column; +} + +/* Put elements in the center */ +.sb-header, +.sb-header-secondary, +.sb-container, +.sb-content, +.sb-footer, +.sb-footer-content { + justify-content: center; +} +/* Put elements at the ends */ +.sb-article-container { + justify-content: space-between; +} + +/* These elements grow. */ +.sb-main, +.sb-content, +.sb-container, +article { + flex-grow: 1; +} + +/* Because padding making this wider is not fun */ +article { + box-sizing: border-box; +} + +/* The announcements element should never be wider than the page. */ +.sb-announcement { + max-width: 100%; +} + +.sb-sidebar-primary, +.sb-sidebar-secondary { + flex-shrink: 0; + width: 17rem; +} + +.sb-announcement__inner { + justify-content: center; + + box-sizing: border-box; + height: 3rem; + + overflow-x: auto; + white-space: nowrap; +} + +/* Sidebars, with checkbox-based toggle */ +.sb-sidebar-primary, +.sb-sidebar-secondary { + position: fixed; + height: 100%; + top: 0; +} + +.sb-sidebar-primary { + left: -17rem; + transition: left 250ms ease-in-out; +} +.sb-sidebar-secondary { + right: -17rem; + transition: right 250ms ease-in-out; +} + +.sb-sidebar-toggle { + display: none; +} +.sb-sidebar-overlay { + position: fixed; + top: 0; + width: 0; + height: 0; + + transition: width 0ms ease 250ms, height 0ms ease 250ms, opacity 250ms ease; + + opacity: 0; + background-color: rgba(0, 0, 0, 0.54); +} + +#sb-sidebar-toggle--primary:checked + ~ .sb-sidebar-overlay[for="sb-sidebar-toggle--primary"], +#sb-sidebar-toggle--secondary:checked + ~ .sb-sidebar-overlay[for="sb-sidebar-toggle--secondary"] { + width: 100%; + height: 100%; + opacity: 1; + transition: width 0ms ease, height 0ms ease, opacity 250ms ease; +} + +#sb-sidebar-toggle--primary:checked ~ .sb-container .sb-sidebar-primary { + left: 0; +} +#sb-sidebar-toggle--secondary:checked ~ .sb-container .sb-sidebar-secondary { + right: 0; +} + +/* Full-width mode */ +.drop-secondary-sidebar-for-full-width-content + .hide-when-secondary-sidebar-shown { + display: none !important; +} +.drop-secondary-sidebar-for-full-width-content .sb-sidebar-secondary { + display: none !important; +} + +/* Mobile views */ +.sb-page-width { + width: 100%; +} + +.sb-article-container, +.sb-footer-content__inner, +.drop-secondary-sidebar-for-full-width-content .sb-article, +.drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 100vw; +} + +.sb-article, +.match-content-width { + padding: 0 1rem; + box-sizing: border-box; +} + +@media (min-width: 32rem) { + .sb-article, + .match-content-width { + padding: 0 2rem; + } +} + +/* Tablet views */ +@media (min-width: 42rem) { + .sb-article-container { + width: auto; + } + .sb-footer-content__inner, + .drop-secondary-sidebar-for-full-width-content .sb-article, + .drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 42rem; + } + .sb-article, + .match-content-width { + width: 42rem; + } +} +@media (min-width: 46rem) { + .sb-footer-content__inner, + .drop-secondary-sidebar-for-full-width-content .sb-article, + .drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 46rem; + } + .sb-article, + .match-content-width { + width: 46rem; + } +} +@media (min-width: 50rem) { + .sb-footer-content__inner, + .drop-secondary-sidebar-for-full-width-content .sb-article, + .drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 50rem; + } + .sb-article, + .match-content-width { + width: 50rem; + } +} + +/* Tablet views */ +@media (min-width: 59rem) { + .sb-sidebar-secondary { + position: static; + } + .hide-when-secondary-sidebar-shown { + display: none !important; + } + .sb-footer-content__inner, + .drop-secondary-sidebar-for-full-width-content .sb-article, + .drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 59rem; + } + .sb-article, + .match-content-width { + width: 42rem; + } +} +@media (min-width: 63rem) { + .sb-footer-content__inner, + .drop-secondary-sidebar-for-full-width-content .sb-article, + .drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 63rem; + } + .sb-article, + .match-content-width { + width: 46rem; + } +} +@media (min-width: 67rem) { + .sb-footer-content__inner, + .drop-secondary-sidebar-for-full-width-content .sb-article, + .drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 67rem; + } + .sb-article, + .match-content-width { + width: 50rem; + } +} + +/* Desktop views */ +@media (min-width: 76rem) { + .sb-sidebar-primary { + position: static; + } + .hide-when-primary-sidebar-shown { + display: none !important; + } + .sb-footer-content__inner, + .drop-secondary-sidebar-for-full-width-content .sb-article, + .drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 59rem; + } + .sb-article, + .match-content-width { + width: 42rem; + } +} + +/* Full desktop views */ +@media (min-width: 80rem) { + .sb-article, + .match-content-width { + width: 46rem; + } + .sb-footer-content__inner, + .drop-secondary-sidebar-for-full-width-content .sb-article, + .drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 63rem; + } +} + +@media (min-width: 84rem) { + .sb-article, + .match-content-width { + width: 50rem; + } + .sb-footer-content__inner, + .drop-secondary-sidebar-for-full-width-content .sb-article, + .drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 67rem; + } +} + +@media (min-width: 88rem) { + .sb-footer-content__inner, + .drop-secondary-sidebar-for-full-width-content .sb-article, + .drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 67rem; + } + .sb-page-width { + width: 88rem; + } +} diff --git a/_static/sphinx_highlight.js b/_static/sphinx_highlight.js new file mode 100644 index 0000000..8a96c69 --- /dev/null +++ b/_static/sphinx_highlight.js @@ -0,0 +1,154 @@ +/* Highlighting utilities for Sphinx HTML documentation. */ +"use strict"; + +const SPHINX_HIGHLIGHT_ENABLED = true + +/** + * highlight a given string on a node by wrapping it in + * span elements with the given class name. + */ +const _highlight = (node, addItems, text, className) => { + if (node.nodeType === Node.TEXT_NODE) { + const val = node.nodeValue; + const parent = node.parentNode; + const pos = val.toLowerCase().indexOf(text); + if ( + pos >= 0 && + !parent.classList.contains(className) && + !parent.classList.contains("nohighlight") + ) { + let span; + + const closestNode = parent.closest("body, svg, foreignObject"); + const isInSVG = closestNode && closestNode.matches("svg"); + if (isInSVG) { + span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); + } else { + span = document.createElement("span"); + span.classList.add(className); + } + + span.appendChild(document.createTextNode(val.substr(pos, text.length))); + const rest = document.createTextNode(val.substr(pos + text.length)); + parent.insertBefore( + span, + parent.insertBefore( + rest, + node.nextSibling + ) + ); + node.nodeValue = val.substr(0, pos); + /* There may be more occurrences of search term in this node. So call this + * function recursively on the remaining fragment. + */ + _highlight(rest, addItems, text, className); + + if (isInSVG) { + const rect = document.createElementNS( + "http://www.w3.org/2000/svg", + "rect" + ); + const bbox = parent.getBBox(); + rect.x.baseVal.value = bbox.x; + rect.y.baseVal.value = bbox.y; + rect.width.baseVal.value = bbox.width; + rect.height.baseVal.value = bbox.height; + rect.setAttribute("class", className); + addItems.push({ parent: parent, target: rect }); + } + } + } else if (node.matches && !node.matches("button, select, textarea")) { + node.childNodes.forEach((el) => _highlight(el, addItems, text, className)); + } +}; +const _highlightText = (thisNode, text, className) => { + let addItems = []; + _highlight(thisNode, addItems, text, className); + addItems.forEach((obj) => + obj.parent.insertAdjacentElement("beforebegin", obj.target) + ); +}; + +/** + * Small JavaScript module for the documentation. + */ +const SphinxHighlight = { + + /** + * highlight the search words provided in localstorage in the text + */ + highlightSearchWords: () => { + if (!SPHINX_HIGHLIGHT_ENABLED) return; // bail if no highlight + + // get and clear terms from localstorage + const url = new URL(window.location); + const highlight = + localStorage.getItem("sphinx_highlight_terms") + || url.searchParams.get("highlight") + || ""; + localStorage.removeItem("sphinx_highlight_terms") + url.searchParams.delete("highlight"); + window.history.replaceState({}, "", url); + + // get individual terms from highlight string + const terms = highlight.toLowerCase().split(/\s+/).filter(x => x); + if (terms.length === 0) return; // nothing to do + + // There should never be more than one element matching "div.body" + const divBody = document.querySelectorAll("div.body"); + const body = divBody.length ? divBody[0] : document.querySelector("body"); + window.setTimeout(() => { + terms.forEach((term) => _highlightText(body, term, "highlighted")); + }, 10); + + const searchBox = document.getElementById("searchbox"); + if (searchBox === null) return; + searchBox.appendChild( + document + .createRange() + .createContextualFragment( + '" + ) + ); + }, + + /** + * helper function to hide the search marks again + */ + hideSearchWords: () => { + document + .querySelectorAll("#searchbox .highlight-link") + .forEach((el) => el.remove()); + document + .querySelectorAll("span.highlighted") + .forEach((el) => el.classList.remove("highlighted")); + localStorage.removeItem("sphinx_highlight_terms") + }, + + initEscapeListener: () => { + // only install a listener if it is really needed + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) return; + + document.addEventListener("keydown", (event) => { + // bail for input elements + if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; + // bail with special keys + if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) return; + if (DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS && (event.key === "Escape")) { + SphinxHighlight.hideSearchWords(); + event.preventDefault(); + } + }); + }, +}; + +_ready(() => { + /* Do not call highlightSearchWords() when we are on the search page. + * It will highlight words from the *previous* search query. + */ + if (typeof Search === "undefined") SphinxHighlight.highlightSearchWords(); + SphinxHighlight.initEscapeListener(); +}); diff --git a/_static/styles/furo-extensions.css b/_static/styles/furo-extensions.css new file mode 100644 index 0000000..bc447f2 --- /dev/null +++ b/_static/styles/furo-extensions.css @@ -0,0 +1,2 @@ +#furo-sidebar-ad-placement{padding:var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal)}#furo-sidebar-ad-placement .ethical-sidebar{background:var(--color-background-secondary);border:none;box-shadow:none}#furo-sidebar-ad-placement .ethical-sidebar:hover{background:var(--color-background-hover)}#furo-sidebar-ad-placement .ethical-sidebar a{color:var(--color-foreground-primary)}#furo-sidebar-ad-placement .ethical-callout a{color:var(--color-foreground-secondary)!important}#furo-readthedocs-versions{background:transparent;display:block;position:static;width:100%}#furo-readthedocs-versions .rst-versions{background:#1a1c1e}#furo-readthedocs-versions .rst-current-version{background:var(--color-sidebar-item-background);cursor:unset}#furo-readthedocs-versions .rst-current-version:hover{background:var(--color-sidebar-item-background)}#furo-readthedocs-versions .rst-current-version .fa-book{color:var(--color-foreground-primary)}#furo-readthedocs-versions>.rst-other-versions{padding:0}#furo-readthedocs-versions>.rst-other-versions small{opacity:1}#furo-readthedocs-versions .injected .rst-versions{position:unset}#furo-readthedocs-versions:focus-within,#furo-readthedocs-versions:hover{box-shadow:0 0 0 1px var(--color-sidebar-background-border)}#furo-readthedocs-versions:focus-within .rst-current-version,#furo-readthedocs-versions:hover .rst-current-version{background:#1a1c1e;font-size:inherit;height:auto;line-height:inherit;padding:12px;text-align:right}#furo-readthedocs-versions:focus-within .rst-current-version .fa-book,#furo-readthedocs-versions:hover .rst-current-version .fa-book{color:#fff;float:left}#furo-readthedocs-versions:focus-within .fa-caret-down,#furo-readthedocs-versions:hover .fa-caret-down{display:none}#furo-readthedocs-versions:focus-within .injected,#furo-readthedocs-versions:focus-within .rst-current-version,#furo-readthedocs-versions:focus-within .rst-other-versions,#furo-readthedocs-versions:hover .injected,#furo-readthedocs-versions:hover .rst-current-version,#furo-readthedocs-versions:hover .rst-other-versions{display:block}#furo-readthedocs-versions:focus-within>.rst-current-version,#furo-readthedocs-versions:hover>.rst-current-version{display:none}.highlight:hover button.copybtn{color:var(--color-code-foreground)}.highlight button.copybtn{align-items:center;background-color:var(--color-code-background);border:none;color:var(--color-background-item);cursor:pointer;height:1.25em;opacity:1;right:.5rem;top:.625rem;transition:color .3s,opacity .3s;width:1.25em}.highlight button.copybtn:hover{background-color:var(--color-code-background);color:var(--color-brand-content)}.highlight button.copybtn:after{background-color:transparent;color:var(--color-code-foreground);display:none}.highlight button.copybtn.success{color:#22863a;transition:color 0ms}.highlight button.copybtn.success:after{display:block}.highlight button.copybtn svg{padding:0}body{--sd-color-primary:var(--color-brand-primary);--sd-color-primary-highlight:var(--color-brand-content);--sd-color-primary-text:var(--color-background-primary);--sd-color-shadow:rgba(0,0,0,.05);--sd-color-card-border:var(--color-card-border);--sd-color-card-border-hover:var(--color-brand-content);--sd-color-card-background:var(--color-card-background);--sd-color-card-text:var(--color-foreground-primary);--sd-color-card-header:var(--color-card-marginals-background);--sd-color-card-footer:var(--color-card-marginals-background);--sd-color-tabs-label-active:var(--color-brand-content);--sd-color-tabs-label-hover:var(--color-foreground-muted);--sd-color-tabs-label-inactive:var(--color-foreground-muted);--sd-color-tabs-underline-active:var(--color-brand-content);--sd-color-tabs-underline-hover:var(--color-foreground-border);--sd-color-tabs-underline-inactive:var(--color-background-border);--sd-color-tabs-overline:var(--color-background-border);--sd-color-tabs-underline:var(--color-background-border)}.sd-tab-content{box-shadow:0 -2px var(--sd-color-tabs-overline),0 1px var(--sd-color-tabs-underline)}.sd-card{box-shadow:0 .1rem .25rem var(--sd-color-shadow),0 0 .0625rem rgba(0,0,0,.1)}.sd-shadow-sm{box-shadow:0 .1rem .25rem var(--sd-color-shadow),0 0 .0625rem rgba(0,0,0,.1)!important}.sd-shadow-md{box-shadow:0 .3rem .75rem var(--sd-color-shadow),0 0 .0625rem rgba(0,0,0,.1)!important}.sd-shadow-lg{box-shadow:0 .6rem 1.5rem var(--sd-color-shadow),0 0 .0625rem rgba(0,0,0,.1)!important}.sd-card-hover:hover{transform:none}.sd-cards-carousel{gap:.25rem;padding:.25rem}body{--tabs--label-text:var(--color-foreground-muted);--tabs--label-text--hover:var(--color-foreground-muted);--tabs--label-text--active:var(--color-brand-content);--tabs--label-text--active--hover:var(--color-brand-content);--tabs--label-background:transparent;--tabs--label-background--hover:transparent;--tabs--label-background--active:transparent;--tabs--label-background--active--hover:transparent;--tabs--padding-x:0.25em;--tabs--margin-x:1em;--tabs--border:var(--color-background-border);--tabs--label-border:transparent;--tabs--label-border--hover:var(--color-foreground-muted);--tabs--label-border--active:var(--color-brand-content);--tabs--label-border--active--hover:var(--color-brand-content)}[role=main] .container{max-width:none;padding-left:0;padding-right:0}.shadow.docutils{border:none;box-shadow:0 .2rem .5rem rgba(0,0,0,.05),0 0 .0625rem rgba(0,0,0,.1)!important}.sphinx-bs .card{background-color:var(--color-background-secondary);color:var(--color-foreground)} +/*# sourceMappingURL=furo-extensions.css.map*/ \ No newline at end of file diff --git a/_static/styles/furo-extensions.css.map b/_static/styles/furo-extensions.css.map new file mode 100644 index 0000000..9ba5637 --- /dev/null +++ b/_static/styles/furo-extensions.css.map @@ -0,0 +1 @@ +{"version":3,"file":"styles/furo-extensions.css","mappings":"AAGA,2BACE,oFACA,4CAKE,6CAHA,YACA,eAEA,CACA,kDACE,yCAEF,8CACE,sCAEJ,8CACE,kDAEJ,2BAGE,uBACA,cAHA,gBACA,UAEA,CAGA,yCACE,mBAEF,gDAEE,gDADA,YACA,CACA,sDACE,gDACF,yDACE,sCAEJ,+CACE,UACA,qDACE,UAGF,mDACE,eAEJ,yEAEE,4DAEA,mHASE,mBAPA,kBAEA,YADA,oBAGA,aADA,gBAIA,CAEA,qIAEE,WADA,UACA,CAEJ,uGACE,aAEF,iUAGE,cAEF,mHACE,aC1EJ,gCACE,mCAEF,0BAKE,mBAUA,8CACA,YAFA,mCAKA,eAZA,cALA,UASA,YADA,YAYA,iCAdA,YAcA,CAEA,gCAEE,8CADA,gCACA,CAEF,gCAGE,6BADA,mCADA,YAEA,CAEF,kCAEE,cADA,oBACA,CACA,wCACE,cAEJ,8BACE,UC5CN,KAEE,6CAA8C,CAC9C,uDAAwD,CACxD,uDAAwD,CAGxD,iCAAsC,CAGtC,+CAAgD,CAChD,uDAAwD,CACxD,uDAAwD,CACxD,oDAAqD,CACrD,6DAA8D,CAC9D,6DAA8D,CAG9D,uDAAwD,CACxD,yDAA0D,CAC1D,4DAA6D,CAC7D,2DAA4D,CAC5D,8DAA+D,CAC/D,iEAAkE,CAClE,uDAAwD,CACxD,wDAAyD,CAG3D,gBACE,qFAGF,SACE,6EAEF,cACE,uFAEF,cACE,uFAEF,cACE,uFAGF,qBACE,eAEF,mBACE,WACA,eChDF,KACE,gDAAiD,CACjD,uDAAwD,CACxD,qDAAsD,CACtD,4DAA6D,CAC7D,oCAAqC,CACrC,2CAA4C,CAC5C,4CAA6C,CAC7C,mDAAoD,CACpD,wBAAyB,CACzB,oBAAqB,CACrB,6CAA8C,CAC9C,gCAAiC,CACjC,yDAA0D,CAC1D,uDAAwD,CACxD,8DAA+D,CCbjE,uBACE,eACA,eACA,gBAGF,iBACE,YACA,+EAGF,iBACE,mDACA","sources":["webpack:///./src/furo/assets/styles/extensions/_readthedocs.sass","webpack:///./src/furo/assets/styles/extensions/_copybutton.sass","webpack:///./src/furo/assets/styles/extensions/_sphinx-design.sass","webpack:///./src/furo/assets/styles/extensions/_sphinx-inline-tabs.sass","webpack:///./src/furo/assets/styles/extensions/_sphinx-panels.sass"],"sourcesContent":["// This file contains the styles used for tweaking how ReadTheDoc's embedded\n// contents would show up inside the theme.\n\n#furo-sidebar-ad-placement\n padding: var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal)\n .ethical-sidebar\n // Remove the border and box-shadow.\n border: none\n box-shadow: none\n // Manage the background colors.\n background: var(--color-background-secondary)\n &:hover\n background: var(--color-background-hover)\n // Ensure the text is legible.\n a\n color: var(--color-foreground-primary)\n\n .ethical-callout a\n color: var(--color-foreground-secondary) !important\n\n#furo-readthedocs-versions\n position: static\n width: 100%\n background: transparent\n display: block\n\n // Make the background color fit with the theme's aesthetic.\n .rst-versions\n background: rgb(26, 28, 30)\n\n .rst-current-version\n cursor: unset\n background: var(--color-sidebar-item-background)\n &:hover\n background: var(--color-sidebar-item-background)\n .fa-book\n color: var(--color-foreground-primary)\n\n > .rst-other-versions\n padding: 0\n small\n opacity: 1\n\n .injected\n .rst-versions\n position: unset\n\n &:hover,\n &:focus-within\n box-shadow: 0 0 0 1px var(--color-sidebar-background-border)\n\n .rst-current-version\n // Undo the tweaks done in RTD's CSS\n font-size: inherit\n line-height: inherit\n height: auto\n text-align: right\n padding: 12px\n\n // Match the rest of the body\n background: #1a1c1e\n\n .fa-book\n float: left\n color: white\n\n .fa-caret-down\n display: none\n\n .rst-current-version,\n .rst-other-versions,\n .injected\n display: block\n\n > .rst-current-version\n display: none\n",".highlight\n &:hover button.copybtn\n color: var(--color-code-foreground)\n\n button.copybtn\n // Make it visible\n opacity: 1\n\n // Align things correctly\n align-items: center\n\n height: 1.25em\n width: 1.25em\n\n top: 0.625rem // $code-spacing-vertical\n right: 0.5rem\n\n // Make it look better\n color: var(--color-background-item)\n background-color: var(--color-code-background)\n border: none\n\n // Change to cursor to make it obvious that you can click on it\n cursor: pointer\n\n // Transition smoothly, for aesthetics\n transition: color 300ms, opacity 300ms\n\n &:hover\n color: var(--color-brand-content)\n background-color: var(--color-code-background)\n\n &::after\n display: none\n color: var(--color-code-foreground)\n background-color: transparent\n\n &.success\n transition: color 0ms\n color: #22863a\n &::after\n display: block\n\n svg\n padding: 0\n","body\n // Colors\n --sd-color-primary: var(--color-brand-primary)\n --sd-color-primary-highlight: var(--color-brand-content)\n --sd-color-primary-text: var(--color-background-primary)\n\n // Shadows\n --sd-color-shadow: rgba(0, 0, 0, 0.05)\n\n // Cards\n --sd-color-card-border: var(--color-card-border)\n --sd-color-card-border-hover: var(--color-brand-content)\n --sd-color-card-background: var(--color-card-background)\n --sd-color-card-text: var(--color-foreground-primary)\n --sd-color-card-header: var(--color-card-marginals-background)\n --sd-color-card-footer: var(--color-card-marginals-background)\n\n // Tabs\n --sd-color-tabs-label-active: var(--color-brand-content)\n --sd-color-tabs-label-hover: var(--color-foreground-muted)\n --sd-color-tabs-label-inactive: var(--color-foreground-muted)\n --sd-color-tabs-underline-active: var(--color-brand-content)\n --sd-color-tabs-underline-hover: var(--color-foreground-border)\n --sd-color-tabs-underline-inactive: var(--color-background-border)\n --sd-color-tabs-overline: var(--color-background-border)\n --sd-color-tabs-underline: var(--color-background-border)\n\n// Tabs\n.sd-tab-content\n box-shadow: 0 -2px var(--sd-color-tabs-overline), 0 1px var(--sd-color-tabs-underline)\n\n// Shadows\n.sd-card // Have a shadow by default\n box-shadow: 0 0.1rem 0.25rem var(--sd-color-shadow), 0 0 0.0625rem rgba(0, 0, 0, 0.1)\n\n.sd-shadow-sm\n box-shadow: 0 0.1rem 0.25rem var(--sd-color-shadow), 0 0 0.0625rem rgba(0, 0, 0, 0.1) !important\n\n.sd-shadow-md\n box-shadow: 0 0.3rem 0.75rem var(--sd-color-shadow), 0 0 0.0625rem rgba(0, 0, 0, 0.1) !important\n\n.sd-shadow-lg\n box-shadow: 0 0.6rem 1.5rem var(--sd-color-shadow), 0 0 0.0625rem rgba(0, 0, 0, 0.1) !important\n\n// Cards\n.sd-card-hover:hover // Don't change scale on hover\n transform: none\n\n.sd-cards-carousel // Have a bit of gap in the carousel by default\n gap: 0.25rem\n padding: 0.25rem\n","// This file contains styles to tweak sphinx-inline-tabs to work well with Furo.\n\nbody\n --tabs--label-text: var(--color-foreground-muted)\n --tabs--label-text--hover: var(--color-foreground-muted)\n --tabs--label-text--active: var(--color-brand-content)\n --tabs--label-text--active--hover: var(--color-brand-content)\n --tabs--label-background: transparent\n --tabs--label-background--hover: transparent\n --tabs--label-background--active: transparent\n --tabs--label-background--active--hover: transparent\n --tabs--padding-x: 0.25em\n --tabs--margin-x: 1em\n --tabs--border: var(--color-background-border)\n --tabs--label-border: transparent\n --tabs--label-border--hover: var(--color-foreground-muted)\n --tabs--label-border--active: var(--color-brand-content)\n --tabs--label-border--active--hover: var(--color-brand-content)\n","// This file contains styles to tweak sphinx-panels to work well with Furo.\n\n// sphinx-panels includes Bootstrap 4, which uses .container which can conflict\n// with docutils' `.. container::` directive.\n[role=\"main\"] .container\n max-width: initial\n padding-left: initial\n padding-right: initial\n\n// Make the panels look nicer!\n.shadow.docutils\n border: none\n box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.05), 0 0 0.0625rem rgba(0, 0, 0, 0.1) !important\n\n// Make panel colors respond to dark mode\n.sphinx-bs .card\n background-color: var(--color-background-secondary)\n color: var(--color-foreground)\n"],"names":[],"sourceRoot":""} \ No newline at end of file diff --git a/_static/styles/furo.css b/_static/styles/furo.css new file mode 100644 index 0000000..3d29a21 --- /dev/null +++ b/_static/styles/furo.css @@ -0,0 +1,2 @@ +/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{-webkit-text-size-adjust:100%;line-height:1.15}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}[hidden],template{display:none}@media print{.content-icon-container,.headerlink,.mobile-header,.related-pages{display:none!important}.highlight{border:.1pt solid var(--color-foreground-border)}a,blockquote,dl,ol,pre,table,ul{page-break-inside:avoid}caption,figure,h1,h2,h3,h4,h5,h6,img{page-break-after:avoid;page-break-inside:avoid}dl,ol,ul{page-break-before:avoid}}.visually-hidden{clip:rect(0,0,0,0)!important;border:0!important;height:1px!important;margin:-1px!important;overflow:hidden!important;padding:0!important;position:absolute!important;white-space:nowrap!important;width:1px!important}:-moz-focusring{outline:auto}body{--font-stack:-apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji;--font-stack--monospace:"SFMono-Regular",Menlo,Consolas,Monaco,Liberation Mono,Lucida Console,monospace;--font-size--normal:100%;--font-size--small:87.5%;--font-size--small--2:81.25%;--font-size--small--3:75%;--font-size--small--4:62.5%;--sidebar-caption-font-size:var(--font-size--small--2);--sidebar-item-font-size:var(--font-size--small);--sidebar-search-input-font-size:var(--font-size--small);--toc-font-size:var(--font-size--small--3);--toc-font-size--mobile:var(--font-size--normal);--toc-title-font-size:var(--font-size--small--4);--admonition-font-size:0.8125rem;--admonition-title-font-size:0.8125rem;--code-font-size:var(--font-size--small--2);--api-font-size:var(--font-size--small);--header-height:calc(var(--sidebar-item-line-height) + var(--sidebar-item-spacing-vertical)*4);--header-padding:0.5rem;--sidebar-tree-space-above:1.5rem;--sidebar-caption-space-above:1rem;--sidebar-item-line-height:1rem;--sidebar-item-spacing-vertical:0.5rem;--sidebar-item-spacing-horizontal:1rem;--sidebar-item-height:calc(var(--sidebar-item-line-height) + var(--sidebar-item-spacing-vertical)*2);--sidebar-expander-width:var(--sidebar-item-height);--sidebar-search-space-above:0.5rem;--sidebar-search-input-spacing-vertical:0.5rem;--sidebar-search-input-spacing-horizontal:0.5rem;--sidebar-search-input-height:1rem;--sidebar-search-icon-size:var(--sidebar-search-input-height);--toc-title-padding:0.25rem 0;--toc-spacing-vertical:1.5rem;--toc-spacing-horizontal:1.5rem;--toc-item-spacing-vertical:0.4rem;--toc-item-spacing-horizontal:1rem;--icon-search:url('data:image/svg+xml;charset=utf-8,');--icon-pencil:url('data:image/svg+xml;charset=utf-8,');--icon-abstract:url('data:image/svg+xml;charset=utf-8,');--icon-info:url('data:image/svg+xml;charset=utf-8,');--icon-flame:url('data:image/svg+xml;charset=utf-8,');--icon-question:url('data:image/svg+xml;charset=utf-8,');--icon-warning:url('data:image/svg+xml;charset=utf-8,');--icon-failure:url('data:image/svg+xml;charset=utf-8,');--icon-spark:url('data:image/svg+xml;charset=utf-8,');--color-admonition-title--caution:#ff9100;--color-admonition-title-background--caution:rgba(255,145,0,.2);--color-admonition-title--warning:#ff9100;--color-admonition-title-background--warning:rgba(255,145,0,.2);--color-admonition-title--danger:#ff5252;--color-admonition-title-background--danger:rgba(255,82,82,.2);--color-admonition-title--attention:#ff5252;--color-admonition-title-background--attention:rgba(255,82,82,.2);--color-admonition-title--error:#ff5252;--color-admonition-title-background--error:rgba(255,82,82,.2);--color-admonition-title--hint:#00c852;--color-admonition-title-background--hint:rgba(0,200,82,.2);--color-admonition-title--tip:#00c852;--color-admonition-title-background--tip:rgba(0,200,82,.2);--color-admonition-title--important:#00bfa5;--color-admonition-title-background--important:rgba(0,191,165,.2);--color-admonition-title--note:#00b0ff;--color-admonition-title-background--note:rgba(0,176,255,.2);--color-admonition-title--seealso:#448aff;--color-admonition-title-background--seealso:rgba(68,138,255,.2);--color-admonition-title--admonition-todo:grey;--color-admonition-title-background--admonition-todo:hsla(0,0%,50%,.2);--color-admonition-title:#651fff;--color-admonition-title-background:rgba(101,31,255,.2);--icon-admonition-default:var(--icon-abstract);--color-topic-title:#14b8a6;--color-topic-title-background:rgba(20,184,166,.2);--icon-topic-default:var(--icon-pencil);--color-problematic:#b30000;--color-foreground-primary:#000;--color-foreground-secondary:#5a5c63;--color-foreground-muted:#646776;--color-foreground-border:#878787;--color-background-primary:#fff;--color-background-secondary:#f8f9fb;--color-background-hover:#efeff4;--color-background-hover--transparent:#efeff400;--color-background-border:#eeebee;--color-background-item:#ccc;--color-announcement-background:#000000dd;--color-announcement-text:#eeebee;--color-brand-primary:#2962ff;--color-brand-content:#2a5adf;--color-api-background:var(--color-background-hover--transparent);--color-api-background-hover:var(--color-background-hover);--color-api-overall:var(--color-foreground-secondary);--color-api-name:var(--color-problematic);--color-api-pre-name:var(--color-problematic);--color-api-paren:var(--color-foreground-secondary);--color-api-keyword:var(--color-foreground-primary);--color-highlight-on-target:#ffc;--color-inline-code-background:var(--color-background-secondary);--color-highlighted-background:#def;--color-highlighted-text:var(--color-foreground-primary);--color-guilabel-background:#ddeeff80;--color-guilabel-border:#bedaf580;--color-guilabel-text:var(--color-foreground-primary);--color-admonition-background:transparent;--color-table-header-background:var(--color-background-secondary);--color-table-border:var(--color-background-border);--color-card-border:var(--color-background-secondary);--color-card-background:transparent;--color-card-marginals-background:var(--color-background-secondary);--color-header-background:var(--color-background-primary);--color-header-border:var(--color-background-border);--color-header-text:var(--color-foreground-primary);--color-sidebar-background:var(--color-background-secondary);--color-sidebar-background-border:var(--color-background-border);--color-sidebar-brand-text:var(--color-foreground-primary);--color-sidebar-caption-text:var(--color-foreground-muted);--color-sidebar-link-text:var(--color-foreground-secondary);--color-sidebar-link-text--top-level:var(--color-brand-primary);--color-sidebar-item-background:var(--color-sidebar-background);--color-sidebar-item-background--current:var( --color-sidebar-item-background );--color-sidebar-item-background--hover:linear-gradient(90deg,var(--color-background-hover--transparent) 0%,var(--color-background-hover) var(--sidebar-item-spacing-horizontal),var(--color-background-hover) 100%);--color-sidebar-item-expander-background:transparent;--color-sidebar-item-expander-background--hover:var( --color-background-hover );--color-sidebar-search-text:var(--color-foreground-primary);--color-sidebar-search-background:var(--color-background-secondary);--color-sidebar-search-background--focus:var(--color-background-primary);--color-sidebar-search-border:var(--color-background-border);--color-sidebar-search-icon:var(--color-foreground-muted);--color-toc-background:var(--color-background-primary);--color-toc-title-text:var(--color-foreground-muted);--color-toc-item-text:var(--color-foreground-secondary);--color-toc-item-text--hover:var(--color-foreground-primary);--color-toc-item-text--active:var(--color-brand-primary);--color-content-foreground:var(--color-foreground-primary);--color-content-background:transparent;--color-link:var(--color-brand-content);--color-link--hover:var(--color-brand-content);--color-link-underline:var(--color-background-border);--color-link-underline--hover:var(--color-foreground-border)}.only-light{display:block!important}html body .only-dark{display:none!important}@media not print{body[data-theme=dark]{--color-problematic:#ee5151;--color-foreground-primary:#ffffffcc;--color-foreground-secondary:#9ca0a5;--color-foreground-muted:#81868d;--color-foreground-border:#666;--color-background-primary:#131416;--color-background-secondary:#1a1c1e;--color-background-hover:#1e2124;--color-background-hover--transparent:#1e212400;--color-background-border:#303335;--color-background-item:#444;--color-announcement-background:#000000dd;--color-announcement-text:#eeebee;--color-brand-primary:#2b8cee;--color-brand-content:#368ce2;--color-highlighted-background:#083563;--color-guilabel-background:#08356380;--color-guilabel-border:#13395f80;--color-api-keyword:var(--color-foreground-secondary);--color-highlight-on-target:#330;--color-admonition-background:#18181a;--color-card-border:var(--color-background-secondary);--color-card-background:#18181a;--color-card-marginals-background:var(--color-background-hover)}html body[data-theme=dark] .only-light{display:none!important}body[data-theme=dark] .only-dark{display:block!important}@media(prefers-color-scheme:dark){body:not([data-theme=light]){--color-problematic:#ee5151;--color-foreground-primary:#ffffffcc;--color-foreground-secondary:#9ca0a5;--color-foreground-muted:#81868d;--color-foreground-border:#666;--color-background-primary:#131416;--color-background-secondary:#1a1c1e;--color-background-hover:#1e2124;--color-background-hover--transparent:#1e212400;--color-background-border:#303335;--color-background-item:#444;--color-announcement-background:#000000dd;--color-announcement-text:#eeebee;--color-brand-primary:#2b8cee;--color-brand-content:#368ce2;--color-highlighted-background:#083563;--color-guilabel-background:#08356380;--color-guilabel-border:#13395f80;--color-api-keyword:var(--color-foreground-secondary);--color-highlight-on-target:#330;--color-admonition-background:#18181a;--color-card-border:var(--color-background-secondary);--color-card-background:#18181a;--color-card-marginals-background:var(--color-background-hover)}html body:not([data-theme=light]) .only-light{display:none!important}body:not([data-theme=light]) .only-dark{display:block!important}}}body[data-theme=auto] .theme-toggle svg.theme-icon-when-auto,body[data-theme=dark] .theme-toggle svg.theme-icon-when-dark,body[data-theme=light] .theme-toggle svg.theme-icon-when-light{display:block}body{font-family:var(--font-stack)}code,kbd,pre,samp{font-family:var(--font-stack--monospace)}body{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}article{line-height:1.5}h1,h2,h3,h4,h5,h6{border-radius:.5rem;font-weight:700;line-height:1.25;margin:.5rem -.5rem;padding-left:.5rem;padding-right:.5rem}h1+p,h2+p,h3+p,h4+p,h5+p,h6+p{margin-top:0}h1{font-size:2.5em;margin-bottom:1rem}h1,h2{margin-top:1.75rem}h2{font-size:2em}h3{font-size:1.5em}h4{font-size:1.25em}h5{font-size:1.125em}h6{font-size:1em}small{font-size:80%;opacity:75%}p{margin-bottom:.75rem;margin-top:.5rem}hr.docutils{background-color:var(--color-background-border);border:0;height:1px;margin:2rem 0;padding:0}.centered{text-align:center}a{color:var(--color-link);text-decoration:underline;text-decoration-color:var(--color-link-underline)}a:hover{color:var(--color-link--hover);text-decoration-color:var(--color-link-underline--hover)}a.muted-link{color:inherit}a.muted-link:hover{color:var(--color-link);text-decoration-color:var(--color-link-underline--hover)}html{overflow-x:hidden;overflow-y:scroll;scroll-behavior:smooth}.sidebar-scroll,.toc-scroll,article[role=main] *{scrollbar-color:var(--color-foreground-border) transparent;scrollbar-width:thin}.sidebar-scroll::-webkit-scrollbar,.toc-scroll::-webkit-scrollbar,article[role=main] ::-webkit-scrollbar{height:.25rem;width:.25rem}.sidebar-scroll::-webkit-scrollbar-thumb,.toc-scroll::-webkit-scrollbar-thumb,article[role=main] ::-webkit-scrollbar-thumb{background-color:var(--color-foreground-border);border-radius:.125rem}body,html{background:var(--color-background-primary);color:var(--color-foreground-primary);height:100%}article{background:var(--color-content-background);color:var(--color-content-foreground);overflow-wrap:break-word}.page{display:flex;min-height:100%}.mobile-header{background-color:var(--color-header-background);border-bottom:1px solid var(--color-header-border);color:var(--color-header-text);display:none;height:var(--header-height);width:100%;z-index:10}.mobile-header.scrolled{border-bottom:none;box-shadow:0 0 .2rem rgba(0,0,0,.1),0 .2rem .4rem rgba(0,0,0,.2)}.mobile-header .header-center a{color:var(--color-header-text);text-decoration:none}.main{display:flex;flex:1}.sidebar-drawer{background:var(--color-sidebar-background);border-right:1px solid var(--color-sidebar-background-border);box-sizing:border-box;display:flex;justify-content:flex-end;min-width:15em;width:calc(50% - 26em)}.sidebar-container,.toc-drawer{box-sizing:border-box;width:15em}.toc-drawer{background:var(--color-toc-background);padding-right:1rem}.sidebar-sticky,.toc-sticky{display:flex;flex-direction:column;height:min(100%,100vh);height:100vh;position:sticky;top:0}.sidebar-scroll,.toc-scroll{flex-grow:1;flex-shrink:1;overflow:auto;scroll-behavior:smooth}.content{display:flex;flex-direction:column;justify-content:space-between;padding:0 3em;width:46em}.icon{display:inline-block;height:1rem;width:1rem}.icon svg{height:100%;width:100%}.announcement{align-items:center;background-color:var(--color-announcement-background);color:var(--color-announcement-text);display:flex;height:var(--header-height);overflow-x:auto}.announcement+.page{min-height:calc(100% - var(--header-height))}.announcement-content{box-sizing:border-box;min-width:100%;padding:.5rem;text-align:center;white-space:nowrap}.announcement-content a{color:var(--color-announcement-text);text-decoration-color:var(--color-announcement-text)}.announcement-content a:hover{color:var(--color-announcement-text);text-decoration-color:var(--color-link--hover)}.no-js .theme-toggle-container{display:none}.theme-toggle-container{vertical-align:middle}.theme-toggle{background:transparent;border:none;cursor:pointer;padding:0}.theme-toggle svg{color:var(--color-foreground-primary);display:none;height:1rem;vertical-align:middle;width:1rem}.theme-toggle-header{float:left;padding:1rem .5rem}.nav-overlay-icon,.toc-overlay-icon{cursor:pointer;display:none}.nav-overlay-icon .icon,.toc-overlay-icon .icon{color:var(--color-foreground-secondary);height:1rem;width:1rem}.nav-overlay-icon,.toc-header-icon{align-items:center;justify-content:center}.toc-content-icon{height:1.5rem;width:1.5rem}.content-icon-container{display:flex;float:right;gap:.5rem;margin-bottom:1rem;margin-left:1rem;margin-top:1.5rem}.content-icon-container .edit-this-page svg{color:inherit;height:1rem;width:1rem}.sidebar-toggle{display:none;position:absolute}.sidebar-toggle[name=__toc]{left:20px}.sidebar-toggle:checked{left:40px}.overlay{background-color:rgba(0,0,0,.54);height:0;opacity:0;position:fixed;top:0;transition:width 0ms,height 0ms,opacity .25s ease-out;width:0}.sidebar-overlay{z-index:20}.toc-overlay{z-index:40}.sidebar-drawer{transition:left .25s ease-in-out;z-index:30}.toc-drawer{transition:right .25s ease-in-out;z-index:50}#__navigation:checked~.sidebar-overlay{height:100%;opacity:1;width:100%}#__navigation:checked~.page .sidebar-drawer{left:0;top:0}#__toc:checked~.toc-overlay{height:100%;opacity:1;width:100%}#__toc:checked~.page .toc-drawer{right:0;top:0}.back-to-top{background:var(--color-background-primary);border-radius:1rem;box-shadow:0 .2rem .5rem rgba(0,0,0,.05),0 0 1px 0 hsla(220,9%,46%,.502);display:none;font-size:.8125rem;left:0;margin-left:50%;padding:.5rem .75rem .5rem .5rem;position:fixed;text-decoration:none;top:1rem;transform:translateX(-50%);z-index:10}.back-to-top svg{fill:currentColor;display:inline-block;height:1rem;width:1rem}.back-to-top span{margin-left:.25rem}.show-back-to-top .back-to-top{align-items:center;display:flex}@media(min-width:97em){html{font-size:110%}}@media(max-width:82em){.toc-content-icon{display:flex}.toc-drawer{border-left:1px solid var(--color-background-muted);height:100vh;position:fixed;right:-15em;top:0}.toc-tree{border-left:none;font-size:var(--toc-font-size--mobile)}.sidebar-drawer{width:calc(50% - 18.5em)}}@media(max-width:67em){.nav-overlay-icon{display:flex}.sidebar-drawer{height:100vh;left:-15em;position:fixed;top:0;width:15em}.toc-header-icon{display:flex}.theme-toggle-content,.toc-content-icon{display:none}.theme-toggle-header{display:block}.mobile-header{align-items:center;display:flex;justify-content:space-between;position:sticky;top:0}.mobile-header .header-left,.mobile-header .header-right{display:flex;height:var(--header-height);padding:0 var(--header-padding)}.mobile-header .header-left label,.mobile-header .header-right label{height:100%;-webkit-user-select:none;-moz-user-select:none;user-select:none;width:100%}.nav-overlay-icon .icon,.theme-toggle svg{height:1.25rem;width:1.25rem}:target{scroll-margin-top:var(--header-height)}.back-to-top{top:calc(var(--header-height) + .5rem)}.page{flex-direction:column;justify-content:center}.content{margin-left:auto;margin-right:auto}}@media(max-width:52em){.content{overflow-x:auto;width:100%}}@media(max-width:46em){.content{padding:0 1em}article aside.sidebar{float:none;margin:1rem 0;width:100%}}.admonition,.topic{background:var(--color-admonition-background);border-radius:.2rem;box-shadow:0 .2rem .5rem rgba(0,0,0,.05),0 0 .0625rem rgba(0,0,0,.1);font-size:var(--admonition-font-size);margin:1rem auto;overflow:hidden;padding:0 .5rem .5rem;page-break-inside:avoid}.admonition>:nth-child(2),.topic>:nth-child(2){margin-top:0}.admonition>:last-child,.topic>:last-child{margin-bottom:0}.admonition p.admonition-title,p.topic-title{font-size:var(--admonition-title-font-size);font-weight:500;line-height:1.3;margin:0 -.5rem .5rem;padding:.4rem .5rem .4rem 2rem;position:relative}.admonition p.admonition-title:before,p.topic-title:before{content:"";height:1rem;left:.5rem;position:absolute;width:1rem}p.admonition-title{background-color:var(--color-admonition-title-background)}p.admonition-title:before{background-color:var(--color-admonition-title);-webkit-mask-image:var(--icon-admonition-default);mask-image:var(--icon-admonition-default);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}p.topic-title{background-color:var(--color-topic-title-background)}p.topic-title:before{background-color:var(--color-topic-title);-webkit-mask-image:var(--icon-topic-default);mask-image:var(--icon-topic-default);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}.admonition{border-left:.2rem solid var(--color-admonition-title)}.admonition.caution{border-left-color:var(--color-admonition-title--caution)}.admonition.caution>.admonition-title{background-color:var(--color-admonition-title-background--caution)}.admonition.caution>.admonition-title:before{background-color:var(--color-admonition-title--caution);-webkit-mask-image:var(--icon-spark);mask-image:var(--icon-spark)}.admonition.warning{border-left-color:var(--color-admonition-title--warning)}.admonition.warning>.admonition-title{background-color:var(--color-admonition-title-background--warning)}.admonition.warning>.admonition-title:before{background-color:var(--color-admonition-title--warning);-webkit-mask-image:var(--icon-warning);mask-image:var(--icon-warning)}.admonition.danger{border-left-color:var(--color-admonition-title--danger)}.admonition.danger>.admonition-title{background-color:var(--color-admonition-title-background--danger)}.admonition.danger>.admonition-title:before{background-color:var(--color-admonition-title--danger);-webkit-mask-image:var(--icon-spark);mask-image:var(--icon-spark)}.admonition.attention{border-left-color:var(--color-admonition-title--attention)}.admonition.attention>.admonition-title{background-color:var(--color-admonition-title-background--attention)}.admonition.attention>.admonition-title:before{background-color:var(--color-admonition-title--attention);-webkit-mask-image:var(--icon-warning);mask-image:var(--icon-warning)}.admonition.error{border-left-color:var(--color-admonition-title--error)}.admonition.error>.admonition-title{background-color:var(--color-admonition-title-background--error)}.admonition.error>.admonition-title:before{background-color:var(--color-admonition-title--error);-webkit-mask-image:var(--icon-failure);mask-image:var(--icon-failure)}.admonition.hint{border-left-color:var(--color-admonition-title--hint)}.admonition.hint>.admonition-title{background-color:var(--color-admonition-title-background--hint)}.admonition.hint>.admonition-title:before{background-color:var(--color-admonition-title--hint);-webkit-mask-image:var(--icon-question);mask-image:var(--icon-question)}.admonition.tip{border-left-color:var(--color-admonition-title--tip)}.admonition.tip>.admonition-title{background-color:var(--color-admonition-title-background--tip)}.admonition.tip>.admonition-title:before{background-color:var(--color-admonition-title--tip);-webkit-mask-image:var(--icon-info);mask-image:var(--icon-info)}.admonition.important{border-left-color:var(--color-admonition-title--important)}.admonition.important>.admonition-title{background-color:var(--color-admonition-title-background--important)}.admonition.important>.admonition-title:before{background-color:var(--color-admonition-title--important);-webkit-mask-image:var(--icon-flame);mask-image:var(--icon-flame)}.admonition.note{border-left-color:var(--color-admonition-title--note)}.admonition.note>.admonition-title{background-color:var(--color-admonition-title-background--note)}.admonition.note>.admonition-title:before{background-color:var(--color-admonition-title--note);-webkit-mask-image:var(--icon-pencil);mask-image:var(--icon-pencil)}.admonition.seealso{border-left-color:var(--color-admonition-title--seealso)}.admonition.seealso>.admonition-title{background-color:var(--color-admonition-title-background--seealso)}.admonition.seealso>.admonition-title:before{background-color:var(--color-admonition-title--seealso);-webkit-mask-image:var(--icon-info);mask-image:var(--icon-info)}.admonition.admonition-todo{border-left-color:var(--color-admonition-title--admonition-todo)}.admonition.admonition-todo>.admonition-title{background-color:var(--color-admonition-title-background--admonition-todo)}.admonition.admonition-todo>.admonition-title:before{background-color:var(--color-admonition-title--admonition-todo);-webkit-mask-image:var(--icon-pencil);mask-image:var(--icon-pencil)}.admonition-todo>.admonition-title{text-transform:uppercase}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) dd{margin-left:2rem}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) dd>:first-child{margin-top:.125rem}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) .field-list,dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) dd>:last-child{margin-bottom:.75rem}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) .field-list>dt{font-size:var(--font-size--small);text-transform:uppercase}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) .field-list dd:empty{margin-bottom:.5rem}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) .field-list dd>ul{margin-left:-1.2rem}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) .field-list dd>ul>li>p:nth-child(2){margin-top:0}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) .field-list dd>ul>li>p+p:last-child:empty{margin-bottom:0;margin-top:0}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple)>dt{color:var(--color-api-overall)}.sig:not(.sig-inline){background:var(--color-api-background);border-radius:.25rem;font-family:var(--font-stack--monospace);font-size:var(--api-font-size);font-weight:700;margin-left:-.25rem;margin-right:-.25rem;padding:.25rem .5rem .25rem 3em;text-indent:-2.5em;transition:background .1s ease-out}.sig:not(.sig-inline):hover{background:var(--color-api-background-hover)}.sig:not(.sig-inline) a.reference .viewcode-link{font-weight:400;width:3.5rem}em.property{font-style:normal}em.property:first-child{color:var(--color-api-keyword)}.sig-name{color:var(--color-api-name)}.sig-prename{color:var(--color-api-pre-name);font-weight:400}.sig-paren{color:var(--color-api-paren)}.sig-param{font-style:normal}.versionmodified{font-style:italic}div.deprecated p,div.versionadded p,div.versionchanged p{margin-bottom:.125rem;margin-top:.125rem}.viewcode-back,.viewcode-link{float:right;text-align:right}.line-block{margin-bottom:.75rem;margin-top:.5rem}.line-block .line-block{margin-bottom:0;margin-top:0;padding-left:1rem}.code-block-caption,article p.caption,table>caption{font-size:var(--font-size--small);text-align:center}.toctree-wrapper.compound .caption,.toctree-wrapper.compound :not(.caption)>.caption-text{font-size:var(--font-size--small);margin-bottom:0;text-align:initial;text-transform:uppercase}.toctree-wrapper.compound>ul{margin-bottom:0;margin-top:0}.sig-inline,code.literal{background:var(--color-inline-code-background);border-radius:.2em;font-size:var(--font-size--small--2);padding:.1em .2em}pre.literal-block .sig-inline,pre.literal-block code.literal{font-size:inherit;padding:0}p .sig-inline,p code.literal{border:1px solid var(--color-background-border)}.sig-inline{font-family:var(--font-stack--monospace)}div[class*=" highlight-"],div[class^=highlight-]{display:flex;margin:1em 0}div[class*=" highlight-"] .table-wrapper,div[class^=highlight-] .table-wrapper,pre{margin:0;padding:0}pre{overflow:auto}article[role=main] .highlight pre{line-height:1.5}.highlight pre,pre.literal-block{font-size:var(--code-font-size);padding:.625rem .875rem}pre.literal-block{background-color:var(--color-code-background);border-radius:.2rem;color:var(--color-code-foreground);margin-bottom:1rem;margin-top:1rem}.highlight{border-radius:.2rem;width:100%}.highlight .gp,.highlight span.linenos{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.highlight .hll{display:block;margin-left:-.875rem;margin-right:-.875rem;padding-left:.875rem;padding-right:.875rem}.code-block-caption{background-color:var(--color-code-background);border-bottom:1px solid;border-radius:.25rem;border-bottom-left-radius:0;border-bottom-right-radius:0;border-color:var(--color-background-border);color:var(--color-code-foreground);display:flex;font-weight:300;padding:.625rem .875rem}.code-block-caption+div[class]{margin-top:0}.code-block-caption+div[class] pre{border-top-left-radius:0;border-top-right-radius:0}.highlighttable{display:block;width:100%}.highlighttable tbody{display:block}.highlighttable tr{display:flex}.highlighttable td.linenos{background-color:var(--color-code-background);border-bottom-left-radius:.2rem;border-top-left-radius:.2rem;color:var(--color-code-foreground);padding:.625rem 0 .625rem .875rem}.highlighttable .linenodiv{box-shadow:-.0625rem 0 var(--color-foreground-border) inset;font-size:var(--code-font-size);padding-right:.875rem}.highlighttable td.code{display:block;flex:1;overflow:hidden;padding:0}.highlighttable td.code .highlight{border-bottom-left-radius:0;border-top-left-radius:0}.highlight span.linenos{box-shadow:-.0625rem 0 var(--color-foreground-border) inset;display:inline-block;margin-right:.875rem;padding-left:0;padding-right:.875rem}.footnote-reference{font-size:var(--font-size--small--4);vertical-align:super}dl.footnote.brackets{color:var(--color-foreground-secondary);display:grid;font-size:var(--font-size--small);grid-template-columns:max-content auto}dl.footnote.brackets dt{margin:0}dl.footnote.brackets dt>.fn-backref{margin-left:.25rem}dl.footnote.brackets dt:after{content:":"}dl.footnote.brackets dt .brackets:before{content:"["}dl.footnote.brackets dt .brackets:after{content:"]"}dl.footnote.brackets dd{margin:0;padding:0 1rem}aside.footnote{color:var(--color-foreground-secondary);font-size:var(--font-size--small)}aside.footnote>span,div.citation>span{float:left;font-weight:500;padding-right:.25rem}aside.footnote>p,div.citation>p{margin-left:2rem}img{box-sizing:border-box;height:auto;max-width:100%}article .figure,article figure{border-radius:.2rem;margin:0}article .figure :last-child,article figure :last-child{margin-bottom:0}article .align-left{clear:left;float:left;margin:0 1rem 1rem}article .align-right{clear:right;float:right;margin:0 1rem 1rem}article .align-center,article .align-default{display:block;margin-left:auto;margin-right:auto;text-align:center}article table.align-default{display:table;text-align:initial}.domainindex-jumpbox,.genindex-jumpbox{border-bottom:1px solid var(--color-background-border);border-top:1px solid var(--color-background-border);padding:.25rem}.domainindex-section h2,.genindex-section h2{margin-bottom:.5rem;margin-top:.75rem}.domainindex-section ul,.genindex-section ul{margin-bottom:0;margin-top:0}ol,ul{margin-bottom:1rem;margin-top:1rem;padding-left:1.2rem}ol li>p:first-child,ul li>p:first-child{margin-bottom:.25rem;margin-top:.25rem}ol li>p:last-child,ul li>p:last-child{margin-top:.25rem}ol li>ol,ol li>ul,ul li>ol,ul li>ul{margin-bottom:.5rem;margin-top:.5rem}ol.arabic{list-style:decimal}ol.loweralpha{list-style:lower-alpha}ol.upperalpha{list-style:upper-alpha}ol.lowerroman{list-style:lower-roman}ol.upperroman{list-style:upper-roman}.simple li>ol,.simple li>ul,.toctree-wrapper li>ol,.toctree-wrapper li>ul{margin-bottom:0;margin-top:0}.field-list dt,.option-list dt,dl.footnote dt,dl.glossary dt,dl.simple dt,dl:not([class]) dt{font-weight:500;margin-top:.25rem}.field-list dt+dt,.option-list dt+dt,dl.footnote dt+dt,dl.glossary dt+dt,dl.simple dt+dt,dl:not([class]) dt+dt{margin-top:0}.field-list dt .classifier:before,.option-list dt .classifier:before,dl.footnote dt .classifier:before,dl.glossary dt .classifier:before,dl.simple dt .classifier:before,dl:not([class]) dt .classifier:before{content:":";margin-left:.2rem;margin-right:.2rem}.field-list dd ul,.field-list dd>p:first-child,.option-list dd ul,.option-list dd>p:first-child,dl.footnote dd ul,dl.footnote dd>p:first-child,dl.glossary dd ul,dl.glossary dd>p:first-child,dl.simple dd ul,dl.simple dd>p:first-child,dl:not([class]) dd ul,dl:not([class]) dd>p:first-child{margin-top:.125rem}.field-list dd ul,.option-list dd ul,dl.footnote dd ul,dl.glossary dd ul,dl.simple dd ul,dl:not([class]) dd ul{margin-bottom:.125rem}.math-wrapper{overflow-x:auto;width:100%}div.math{position:relative;text-align:center}div.math .headerlink,div.math:focus .headerlink{display:none}div.math:hover .headerlink{display:inline-block}div.math span.eqno{position:absolute;right:.5rem;top:50%;transform:translateY(-50%);z-index:1}abbr[title]{cursor:help}.problematic{color:var(--color-problematic)}kbd:not(.compound){background-color:var(--color-background-secondary);border:1px solid var(--color-foreground-border);border-radius:.2rem;box-shadow:0 .0625rem 0 rgba(0,0,0,.2),inset 0 0 0 .125rem var(--color-background-primary);color:var(--color-foreground-primary);display:inline-block;font-size:var(--font-size--small--3);margin:0 .2rem;padding:0 .2rem;vertical-align:text-bottom}blockquote{background:var(--color-background-secondary);border-left:4px solid var(--color-background-border);margin-left:0;margin-right:0;padding:.5rem 1rem}blockquote .attribution{font-weight:600;text-align:right}blockquote.highlights,blockquote.pull-quote{font-size:1.25em}blockquote.epigraph,blockquote.pull-quote{border-left-width:0;border-radius:.5rem}blockquote.highlights{background:transparent;border-left-width:0}p .reference img{vertical-align:middle}p.rubric{font-size:1.125em;font-weight:700;line-height:1.25}dd p.rubric{font-size:var(--font-size--small);font-weight:inherit;line-height:inherit;text-transform:uppercase}article .sidebar{background-color:var(--color-background-secondary);border:1px solid var(--color-background-border);border-radius:.2rem;clear:right;float:right;margin-left:1rem;margin-right:0;width:30%}article .sidebar>*{padding-left:1rem;padding-right:1rem}article .sidebar>ol,article .sidebar>ul{padding-left:2.2rem}article .sidebar .sidebar-title{border-bottom:1px solid var(--color-background-border);font-weight:500;margin:0;padding:.5rem 1rem}.table-wrapper{margin-bottom:.5rem;margin-top:1rem;overflow-x:auto;padding:.2rem .2rem .75rem;width:100%}table.docutils{border-collapse:collapse;border-radius:.2rem;border-spacing:0;box-shadow:0 .2rem .5rem rgba(0,0,0,.05),0 0 .0625rem rgba(0,0,0,.1)}table.docutils th{background:var(--color-table-header-background)}table.docutils td,table.docutils th{border-bottom:1px solid var(--color-table-border);border-left:1px solid var(--color-table-border);border-right:1px solid var(--color-table-border);padding:0 .25rem}table.docutils td p,table.docutils th p{margin:.25rem}table.docutils td:first-child,table.docutils th:first-child{border-left:none}table.docutils td:last-child,table.docutils th:last-child{border-right:none}table.docutils td.text-left,table.docutils th.text-left{text-align:left}table.docutils td.text-right,table.docutils th.text-right{text-align:right}table.docutils td.text-center,table.docutils th.text-center{text-align:center}:target{scroll-margin-top:.5rem}@media(max-width:67em){:target{scroll-margin-top:calc(.5rem + var(--header-height))}section>span:target{scroll-margin-top:calc(.8rem + var(--header-height))}}.headerlink{font-weight:100;-webkit-user-select:none;-moz-user-select:none;user-select:none}.code-block-caption>.headerlink,dl dt>.headerlink,figcaption p>.headerlink,h1>.headerlink,h2>.headerlink,h3>.headerlink,h4>.headerlink,h5>.headerlink,h6>.headerlink,p.caption>.headerlink,table>caption>.headerlink{margin-left:.5rem;visibility:hidden}.code-block-caption:hover>.headerlink,dl dt:hover>.headerlink,figcaption p:hover>.headerlink,h1:hover>.headerlink,h2:hover>.headerlink,h3:hover>.headerlink,h4:hover>.headerlink,h5:hover>.headerlink,h6:hover>.headerlink,p.caption:hover>.headerlink,table>caption:hover>.headerlink{visibility:visible}.code-block-caption>.toc-backref,dl dt>.toc-backref,figcaption p>.toc-backref,h1>.toc-backref,h2>.toc-backref,h3>.toc-backref,h4>.toc-backref,h5>.toc-backref,h6>.toc-backref,p.caption>.toc-backref,table>caption>.toc-backref{color:inherit;text-decoration-line:none}figure:hover>figcaption>p>.headerlink,table:hover>caption>.headerlink{visibility:visible}:target>h1:first-of-type,:target>h2:first-of-type,:target>h3:first-of-type,:target>h4:first-of-type,:target>h5:first-of-type,:target>h6:first-of-type,span:target~h1:first-of-type,span:target~h2:first-of-type,span:target~h3:first-of-type,span:target~h4:first-of-type,span:target~h5:first-of-type,span:target~h6:first-of-type{background-color:var(--color-highlight-on-target)}:target>h1:first-of-type code.literal,:target>h2:first-of-type code.literal,:target>h3:first-of-type code.literal,:target>h4:first-of-type code.literal,:target>h5:first-of-type code.literal,:target>h6:first-of-type code.literal,span:target~h1:first-of-type code.literal,span:target~h2:first-of-type code.literal,span:target~h3:first-of-type code.literal,span:target~h4:first-of-type code.literal,span:target~h5:first-of-type code.literal,span:target~h6:first-of-type code.literal{background-color:transparent}.literal-block-wrapper:target .code-block-caption,.this-will-duplicate-information-and-it-is-still-useful-here li :target,figure:target,table:target>caption{background-color:var(--color-highlight-on-target)}dt:target{background-color:var(--color-highlight-on-target)!important}.footnote-reference:target,.footnote>dt:target+dd{background-color:var(--color-highlight-on-target)}.guilabel{background-color:var(--color-guilabel-background);border:1px solid var(--color-guilabel-border);border-radius:.5em;color:var(--color-guilabel-text);font-size:.9em;padding:0 .3em}footer{display:flex;flex-direction:column;font-size:var(--font-size--small);margin-top:2rem}.bottom-of-page{align-items:center;border-top:1px solid var(--color-background-border);color:var(--color-foreground-secondary);display:flex;justify-content:space-between;line-height:1.5;margin-top:1rem;padding-bottom:1rem;padding-top:1rem}@media(max-width:46em){.bottom-of-page{flex-direction:column-reverse;gap:.25rem;text-align:center}}.bottom-of-page .left-details{font-size:var(--font-size--small)}.bottom-of-page .right-details{display:flex;flex-direction:column;gap:.25rem;text-align:right}.bottom-of-page .icons{display:flex;font-size:1rem;gap:.25rem;justify-content:flex-end}.bottom-of-page .icons a{text-decoration:none}.bottom-of-page .icons img,.bottom-of-page .icons svg{font-size:1.125rem;height:1em;width:1em}.related-pages a{align-items:center;display:flex;text-decoration:none}.related-pages a:hover .page-info .title{color:var(--color-link);text-decoration:underline;text-decoration-color:var(--color-link-underline)}.related-pages a svg.furo-related-icon,.related-pages a svg.furo-related-icon>use{color:var(--color-foreground-border);flex-shrink:0;height:.75rem;margin:0 .5rem;width:.75rem}.related-pages a.next-page{clear:right;float:right;max-width:50%;text-align:right}.related-pages a.prev-page{clear:left;float:left;max-width:50%}.related-pages a.prev-page svg{transform:rotate(180deg)}.page-info{display:flex;flex-direction:column;overflow-wrap:anywhere}.next-page .page-info{align-items:flex-end}.page-info .context{align-items:center;color:var(--color-foreground-muted);display:flex;font-size:var(--font-size--small);padding-bottom:.1rem;text-decoration:none}ul.search{list-style:none;padding-left:0}ul.search li{border-bottom:1px solid var(--color-background-border);padding:1rem 0}[role=main] .highlighted{background-color:var(--color-highlighted-background);color:var(--color-highlighted-text)}.sidebar-brand{display:flex;flex-direction:column;flex-shrink:0;padding:var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal);text-decoration:none}.sidebar-brand-text{color:var(--color-sidebar-brand-text);font-size:1.5rem;overflow-wrap:break-word}.sidebar-brand-text,.sidebar-logo-container{margin:var(--sidebar-item-spacing-vertical) 0}.sidebar-logo{display:block;margin:0 auto;max-width:100%}.sidebar-search-container{align-items:center;background:var(--color-sidebar-search-background);display:flex;margin-top:var(--sidebar-search-space-above);position:relative}.sidebar-search-container:focus-within,.sidebar-search-container:hover{background:var(--color-sidebar-search-background--focus)}.sidebar-search-container:before{background-color:var(--color-sidebar-search-icon);content:"";height:var(--sidebar-search-icon-size);left:var(--sidebar-item-spacing-horizontal);-webkit-mask-image:var(--icon-search);mask-image:var(--icon-search);position:absolute;width:var(--sidebar-search-icon-size)}.sidebar-search{background:transparent;border:none;border-bottom:1px solid var(--color-sidebar-search-border);border-top:1px solid var(--color-sidebar-search-border);box-sizing:border-box;color:var(--color-sidebar-search-foreground);padding:var(--sidebar-search-input-spacing-vertical) var(--sidebar-search-input-spacing-horizontal) var(--sidebar-search-input-spacing-vertical) calc(var(--sidebar-item-spacing-horizontal) + var(--sidebar-search-input-spacing-horizontal) + var(--sidebar-search-icon-size));width:100%;z-index:10}.sidebar-search:focus{outline:none}.sidebar-search::-moz-placeholder{font-size:var(--sidebar-search-input-font-size)}.sidebar-search::placeholder{font-size:var(--sidebar-search-input-font-size)}#searchbox .highlight-link{margin:0;padding:var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal) 0;text-align:center}#searchbox .highlight-link a{color:var(--color-sidebar-search-icon);font-size:var(--font-size--small--2)}.sidebar-tree{font-size:var(--sidebar-item-font-size);margin-bottom:var(--sidebar-item-spacing-vertical);margin-top:var(--sidebar-tree-space-above)}.sidebar-tree ul{display:flex;flex-direction:column;list-style:none;margin-bottom:0;margin-top:0;padding:0}.sidebar-tree li{margin:0;position:relative}.sidebar-tree li>ul{margin-left:var(--sidebar-item-spacing-horizontal)}.sidebar-tree .icon,.sidebar-tree .reference{color:var(--color-sidebar-link-text)}.sidebar-tree .reference{box-sizing:border-box;display:inline-block;height:100%;line-height:var(--sidebar-item-line-height);overflow-wrap:anywhere;padding:var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal);text-decoration:none;width:100%}.sidebar-tree .reference:hover{background:var(--color-sidebar-item-background--hover)}.sidebar-tree .reference.external:after{color:var(--color-sidebar-link-text);content:url("data:image/svg+xml;charset=utf-8,%3Csvg width='12' height='12' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' stroke-width='1.5' stroke='%23607D8B' fill='none' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M0 0h24v24H0z' stroke='none'/%3E%3Cpath d='M11 7H6a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2-2v-5M10 14 20 4M15 4h5v5'/%3E%3C/svg%3E");margin:0 .25rem;vertical-align:middle}.sidebar-tree .current-page>.reference{font-weight:700}.sidebar-tree label{align-items:center;cursor:pointer;display:flex;height:var(--sidebar-item-height);justify-content:center;position:absolute;right:0;top:0;-webkit-user-select:none;-moz-user-select:none;user-select:none;width:var(--sidebar-expander-width)}.sidebar-tree .caption,.sidebar-tree :not(.caption)>.caption-text{color:var(--color-sidebar-caption-text);font-size:var(--sidebar-caption-font-size);font-weight:700;margin:var(--sidebar-caption-space-above) 0 0 0;padding:var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal);text-transform:uppercase}.sidebar-tree li.has-children>.reference{padding-right:var(--sidebar-expander-width)}.sidebar-tree .toctree-l1>.reference,.sidebar-tree .toctree-l1>label .icon{color:var(--color-sidebar-link-text--top-level)}.sidebar-tree label{background:var(--color-sidebar-item-expander-background)}.sidebar-tree label:hover{background:var(--color-sidebar-item-expander-background--hover)}.sidebar-tree .current>.reference{background:var(--color-sidebar-item-background--current)}.sidebar-tree .current>.reference:hover{background:var(--color-sidebar-item-background--hover)}.toctree-checkbox{display:none;position:absolute}.toctree-checkbox~ul{display:none}.toctree-checkbox~label .icon svg{transform:rotate(90deg)}.toctree-checkbox:checked~ul{display:block}.toctree-checkbox:checked~label .icon svg{transform:rotate(-90deg)}.toc-title-container{padding:var(--toc-title-padding);padding-top:var(--toc-spacing-vertical)}.toc-title{color:var(--color-toc-title-text);font-size:var(--toc-title-font-size);padding-left:var(--toc-spacing-horizontal);text-transform:uppercase}.no-toc{display:none}.toc-tree-container{padding-bottom:var(--toc-spacing-vertical)}.toc-tree{border-left:1px solid var(--color-background-border);font-size:var(--toc-font-size);line-height:1.3;padding-left:calc(var(--toc-spacing-horizontal) - var(--toc-item-spacing-horizontal))}.toc-tree>ul>li:first-child{padding-top:0}.toc-tree>ul>li:first-child>ul{padding-left:0}.toc-tree>ul>li:first-child>a{display:none}.toc-tree ul{list-style-type:none;margin-bottom:0;margin-top:0;padding-left:var(--toc-item-spacing-horizontal)}.toc-tree li{padding-top:var(--toc-item-spacing-vertical)}.toc-tree li.scroll-current>.reference{color:var(--color-toc-item-text--active);font-weight:700}.toc-tree .reference{color:var(--color-toc-item-text);overflow-wrap:anywhere;text-decoration:none}.toc-scroll{max-height:100vh;overflow-y:scroll}.contents:not(.this-will-duplicate-information-and-it-is-still-useful-here){background:rgba(255,0,0,.25);color:var(--color-problematic)}.contents:not(.this-will-duplicate-information-and-it-is-still-useful-here):before{content:"ERROR: Adding a table of contents in Furo-based documentation is unnecessary, and does not work well with existing styling.Add a 'this-will-duplicate-information-and-it-is-still-useful-here' class, if you want an escape hatch."}.text-align\:left>p{text-align:left}.text-align\:center>p{text-align:center}.text-align\:right>p{text-align:right} +/*# sourceMappingURL=furo.css.map*/ \ No newline at end of file diff --git a/_static/styles/furo.css.map b/_static/styles/furo.css.map new file mode 100644 index 0000000..d1dfb10 --- /dev/null +++ b/_static/styles/furo.css.map @@ -0,0 +1 @@ +{"version":3,"file":"styles/furo.css","mappings":"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0CACE,CAFF,kBACA,CACE,wEACA,CARA,YACA,CAKF,mBAFF,OAII,eACA,CAJF,iCAJE,cAGJ,CANI,oBAEA,CAKF,SAIE,2BADA,UACA,kBAGF,sCACA,CAFF,WACE,WACA,qCACE,gCACA,2EACA,sDAKJ,aACE,mDAII,CAJJ,6CAII,kEACA,iBACE,iDACA,+CACE,aACA,WADA,+BACA,uEANN,YACE,mDAEE,mBADF,0CACE,CADF,qBACE,0DACA,YACE,4DACA,sEANN,YACE,8CACA,kBADA,UACA,2CACE,2EACA,cACE,kEACA,mEANN,yBACE,4DACA,sBACE,+EAEE,iEACA,qEANN,sCACE,CAGE,iBAHF,gBAGE,qBACE,CAJJ,uBACA,gDACE,wDACA,6DAHF,2CACA,CADA,gBACA,eACE,CAGE,sBANN,8BACE,CAII,iBAFF,4DACA,WACE,YADF,uCACE,6EACA,2BANN,8CACE,kDACA,0CACE,8BACA,yFACE,sBACA,sFALJ,mEACA,sBACE,kEACA,6EACE,uCACA,kEALJ,qGAEE,kEACA,6EACE,uCACA,kEALJ,8CACA,uDACE,sEACA,2EACE,sCACA,iEALJ,mGACA,qCACE,oDACA,0DACE,6GACA,gDAGR,yDCrEA,sEACE,CACA,6GACE,gEACF,iGAIF,wFACE,qDAGA,mGAEE,2CAEF,4FACE,gCACF,wGACE,8DAEE,6FAIA,iJAKN,6GACE,gDAKF,yDACA,qCAGA,6BACA,kBACA,qDAKA,oCAEA,+DAGA,2CAGE,oDAIA,oEAEE,qBAGJ,wDAEE,uCAEF,kEAGA,8CAEA,uDAKA,oCAEA,yDAEE,gEAKF,+CC5FA,0EAGE,CACA,qDCLJ,+DAIE,sCAIA,kEACE,yBACA,2FAMA,gBACA,yGCbF,mBAOA,2MAIA,4HAYA,0DACE,8GAYF,8HAQE,mBAEA,6HAOF,YAGA,mIAME,eACA,CAFF,YAEE,4FAMJ,8BAEE,uBAYA,sCAEE,CAJF,oBAEA,CARA,wCAEA,CAHA,8BACA,CAFA,eACA,CAGA,wCAEA,CAEA,mDAIE,kCACE,6BACA,4CAKJ,kDAIA,eACE,aAGF,8BACE,uDACA,sCACA,cAEA,+BACA,CAFA,eAEA,wCAEF,YACE,iBACA,mCACA,0DAGF,qBAEE,CAFF,kBAEE,+BAIA,yCAEE,qBADA,gBACA,yBAKF,eACA,CAFF,YACE,CACA,iBACA,qDAEA,mDCvIJ,2FAOE,iCACA,CAEA,eACA,CAHA,kBAEA,CAFA,wBAGA,8BACA,eACE,CAFF,YAEE,0BACA,8CAGA,oBACE,oCAGA,kBACE,8DAEA,iBAEN,UACE,8BAIJ,+CAEE,qDAEF,kDAIE,YAEF,CAFE,YAEF,CCjCE,mFAJA,QACA,UAIE,CADF,iBACE,mCAGA,iDACE,+BAGF,wBAEA,mBAKA,6CAEF,CAHE,mBACA,CAEF,kCAIE,CARA,kBACA,CAFF,eASE,YACA,mBAGF,CAJE,UAIF,wCCjCA,oBDmCE,wBCpCJ,uCACE,8BACA,4CACA,oBAGA,2CCAA,6CAGE,CAPF,uBAIA,CDGA,gDACE,6BCVJ,CAWM,2CAEF,CAJA,kCAEE,CDJF,aCLF,gBDKE,uBCMA,gCAGA,gDAGE,wBAGJ,0BAEA,iBACE,aACF,CADE,UACF,uBACE,aACF,oBACE,YACF,4BACE,6CAMA,CAYF,6DAZE,mCAGE,iCASJ,4BAGE,4DADA,+BACA,CAFA,qBAEA,yBACE,aAEF,wBAHA,SAGA,iHACE,2DAKF,CANA,yCACE,CADF,oCAMA,uSAIA,sGACE,oDChEJ,WAEF,yBACE,QACA,eAEA,gBAEE,uCAGA,CALF,iCAKE,uCAGA,0BACA,CACA,oBACA,iCClBJ,gBACE,KAGF,qBACE,YAGF,CAHE,cAGF,gCAEE,mBACA,iEAEA,oCACA,wCAEA,sBACA,WAEA,CAFA,YAEA,8EAEA,mCAFA,iBAEA,6BAIA,wEAKA,sDAIE,CARF,mDAIA,CAIE,cAEF,8CAIA,oBAFE,iBAEF,8CAGE,eAEF,CAFE,YAEF,OAEE,kBAGJ,CAJI,eACA,CAFF,mBAKF,yCCjDE,oBACA,CAFA,iBAEA,uCAKE,iBACA,qCAGA,mBCZJ,CDWI,gBCXJ,6BAEE,eACA,sBAGA,eAEA,sBACA,oDACA,iGAMA,gBAFE,YAEF,8FAME,iJClBF,YACA,gNAUE,6BAEF,oTAcI,kBACF,gHAIA,qBACE,eACF,qDACE,kBACF,6DACE,4BCxCJ,oBAEF,qCAEI,+CAGF,uBACE,uDAGJ,oBAkBE,mDAhBA,+CAaA,CAbA,oBAaA,0FAEE,CAFF,gGAbA,+BAaA,0BAGA,mQAIA,oNAEE,iBAGJ,CAHI,gBADA,gBAIJ,8CAYI,CAZJ,wCAYI,sVACE,iCAGA,uEAHA,QAGA,qXAKJ,iDAGF,CARM,+CACE,iDAIN,CALI,gBAQN,mHACE,gBAGF,2DACE,0EAOA,0EAKA,6EC/EA,iDACA,gCACA,oDAGA,qBACA,oDCFA,cACA,eAEA,yBAGF,sBAEE,iBACA,sNAWA,iBACE,kBACA,wRAgBA,kBAEA,iOAgBA,uCACE,uEAEA,kBAEF,qUAuBE,iDAIJ,CACA,geCxFF,4BAEE,CAQA,6JACA,iDAIA,sEAGA,mDAOF,iDAGE,4DAIA,8CACA,qDAEE,eAFF,cAEE,oBAEF,uBAFE,kCAGA,eACA,iBACA,mBAIA,mDACA,CAHA,uCAEA,CAJA,0CACA,CAIA,gBAJA,gBACA,oBADA,gBAIA,wBAEJ,gBAGE,6BACA,YAHA,iBAGA,gCACA,iEAEA,6CACA,sDACA,0BADA,wBACA,0BACA,oIAIA,mBAFA,YAEA,qBACA,0CAIE,uBAEF,CAHA,yBACE,CAEF,iDACE,mFAKJ,oCACE,CANE,aAKJ,CACE,qEAIA,YAFA,WAEA,CAHA,aACA,CAEA,gBACE,4BACA,sBADA,aACA,gCAMF,oCACA,yDACA,2CAEA,qBAGE,kBAEA,CACA,mCAIF,CARE,YACA,CAOF,iCAEE,CAPA,oBACA,CAQA,oBACE,uDAEJ,sDAGA,CAHA,cAGA,0BACE,oDAIA,oCACA,4BACA,sBAGA,cAEA,oFAGA,sBAEA,yDACE,CAIA,iBAJA,wBAIA,6CAJA,6CAOA,4BAGJ,CAHI,cAGJ,yCAGA,kBACE,CAIA,iDAEA,CATA,YAEF,CACE,4CAGA,kBAIA,wEAEA,wDAIF,kCAOE,iDACA,CARF,WAIE,sCAGA,CANA,2CACA,CAMA,oEARF,iBACE,CACA,qCAMA,iBAuBE,uBAlBF,YAKA,2DALA,uDAKA,CALA,sBAiBA,4CACE,CALA,gRAIF,YACE,UAEN,uBACE,YACA,mCAOE,+CAGA,8BAGF,+CAGA,4BCjNA,SDiNA,qFCjNA,gDAGA,sCACA,qCACA,sDAIF,CAIE,kDAGA,CAPF,0CAOE,kBAEA,kDAEA,CAHA,eACA,CAFA,YACA,CADA,SAIA,mHAIE,CAGA,6CAFA,oCAeE,CAbF,yBACE,qBAEJ,CAGE,oBACA,CAEA,YAFA,2CACF,CACE,uBAEA,mFAEE,CALJ,oBACE,CAEA,UAEE,gCAGF,sDAEA,yCC7CJ,oCAGA,CD6CE,yXAQE,sCCrDJ,wCAGA,oCACE","sources":["webpack:///./node_modules/normalize.css/normalize.css","webpack:///./src/furo/assets/styles/base/_print.sass","webpack:///./src/furo/assets/styles/base/_screen-readers.sass","webpack:///./src/furo/assets/styles/base/_theme.sass","webpack:///./src/furo/assets/styles/variables/_fonts.scss","webpack:///./src/furo/assets/styles/variables/_spacing.scss","webpack:///./src/furo/assets/styles/variables/_icons.scss","webpack:///./src/furo/assets/styles/variables/_admonitions.scss","webpack:///./src/furo/assets/styles/variables/_colors.scss","webpack:///./src/furo/assets/styles/base/_typography.sass","webpack:///./src/furo/assets/styles/_scaffold.sass","webpack:///./src/furo/assets/styles/content/_admonitions.sass","webpack:///./src/furo/assets/styles/content/_api.sass","webpack:///./src/furo/assets/styles/content/_blocks.sass","webpack:///./src/furo/assets/styles/content/_captions.sass","webpack:///./src/furo/assets/styles/content/_code.sass","webpack:///./src/furo/assets/styles/content/_footnotes.sass","webpack:///./src/furo/assets/styles/content/_images.sass","webpack:///./src/furo/assets/styles/content/_indexes.sass","webpack:///./src/furo/assets/styles/content/_lists.sass","webpack:///./src/furo/assets/styles/content/_math.sass","webpack:///./src/furo/assets/styles/content/_misc.sass","webpack:///./src/furo/assets/styles/content/_rubrics.sass","webpack:///./src/furo/assets/styles/content/_sidebar.sass","webpack:///./src/furo/assets/styles/content/_tables.sass","webpack:///./src/furo/assets/styles/content/_target.sass","webpack:///./src/furo/assets/styles/content/_gui-labels.sass","webpack:///./src/furo/assets/styles/components/_footer.sass","webpack:///./src/furo/assets/styles/components/_sidebar.sass","webpack:///./src/furo/assets/styles/components/_table_of_contents.sass","webpack:///./src/furo/assets/styles/_shame.sass"],"sourcesContent":["/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */\n\n/* Document\n ========================================================================== */\n\n/**\n * 1. Correct the line height in all browsers.\n * 2. Prevent adjustments of font size after orientation changes in iOS.\n */\n\nhtml {\n line-height: 1.15; /* 1 */\n -webkit-text-size-adjust: 100%; /* 2 */\n}\n\n/* Sections\n ========================================================================== */\n\n/**\n * Remove the margin in all browsers.\n */\n\nbody {\n margin: 0;\n}\n\n/**\n * Render the `main` element consistently in IE.\n */\n\nmain {\n display: block;\n}\n\n/**\n * Correct the font size and margin on `h1` elements within `section` and\n * `article` contexts in Chrome, Firefox, and Safari.\n */\n\nh1 {\n font-size: 2em;\n margin: 0.67em 0;\n}\n\n/* Grouping content\n ========================================================================== */\n\n/**\n * 1. Add the correct box sizing in Firefox.\n * 2. Show the overflow in Edge and IE.\n */\n\nhr {\n box-sizing: content-box; /* 1 */\n height: 0; /* 1 */\n overflow: visible; /* 2 */\n}\n\n/**\n * 1. Correct the inheritance and scaling of font size in all browsers.\n * 2. Correct the odd `em` font sizing in all browsers.\n */\n\npre {\n font-family: monospace, monospace; /* 1 */\n font-size: 1em; /* 2 */\n}\n\n/* Text-level semantics\n ========================================================================== */\n\n/**\n * Remove the gray background on active links in IE 10.\n */\n\na {\n background-color: transparent;\n}\n\n/**\n * 1. Remove the bottom border in Chrome 57-\n * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.\n */\n\nabbr[title] {\n border-bottom: none; /* 1 */\n text-decoration: underline; /* 2 */\n text-decoration: underline dotted; /* 2 */\n}\n\n/**\n * Add the correct font weight in Chrome, Edge, and Safari.\n */\n\nb,\nstrong {\n font-weight: bolder;\n}\n\n/**\n * 1. Correct the inheritance and scaling of font size in all browsers.\n * 2. Correct the odd `em` font sizing in all browsers.\n */\n\ncode,\nkbd,\nsamp {\n font-family: monospace, monospace; /* 1 */\n font-size: 1em; /* 2 */\n}\n\n/**\n * Add the correct font size in all browsers.\n */\n\nsmall {\n font-size: 80%;\n}\n\n/**\n * Prevent `sub` and `sup` elements from affecting the line height in\n * all browsers.\n */\n\nsub,\nsup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n}\n\nsub {\n bottom: -0.25em;\n}\n\nsup {\n top: -0.5em;\n}\n\n/* Embedded content\n ========================================================================== */\n\n/**\n * Remove the border on images inside links in IE 10.\n */\n\nimg {\n border-style: none;\n}\n\n/* Forms\n ========================================================================== */\n\n/**\n * 1. Change the font styles in all browsers.\n * 2. Remove the margin in Firefox and Safari.\n */\n\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n font-family: inherit; /* 1 */\n font-size: 100%; /* 1 */\n line-height: 1.15; /* 1 */\n margin: 0; /* 2 */\n}\n\n/**\n * Show the overflow in IE.\n * 1. Show the overflow in Edge.\n */\n\nbutton,\ninput { /* 1 */\n overflow: visible;\n}\n\n/**\n * Remove the inheritance of text transform in Edge, Firefox, and IE.\n * 1. Remove the inheritance of text transform in Firefox.\n */\n\nbutton,\nselect { /* 1 */\n text-transform: none;\n}\n\n/**\n * Correct the inability to style clickable types in iOS and Safari.\n */\n\nbutton,\n[type=\"button\"],\n[type=\"reset\"],\n[type=\"submit\"] {\n -webkit-appearance: button;\n}\n\n/**\n * Remove the inner border and padding in Firefox.\n */\n\nbutton::-moz-focus-inner,\n[type=\"button\"]::-moz-focus-inner,\n[type=\"reset\"]::-moz-focus-inner,\n[type=\"submit\"]::-moz-focus-inner {\n border-style: none;\n padding: 0;\n}\n\n/**\n * Restore the focus styles unset by the previous rule.\n */\n\nbutton:-moz-focusring,\n[type=\"button\"]:-moz-focusring,\n[type=\"reset\"]:-moz-focusring,\n[type=\"submit\"]:-moz-focusring {\n outline: 1px dotted ButtonText;\n}\n\n/**\n * Correct the padding in Firefox.\n */\n\nfieldset {\n padding: 0.35em 0.75em 0.625em;\n}\n\n/**\n * 1. Correct the text wrapping in Edge and IE.\n * 2. Correct the color inheritance from `fieldset` elements in IE.\n * 3. Remove the padding so developers are not caught out when they zero out\n * `fieldset` elements in all browsers.\n */\n\nlegend {\n box-sizing: border-box; /* 1 */\n color: inherit; /* 2 */\n display: table; /* 1 */\n max-width: 100%; /* 1 */\n padding: 0; /* 3 */\n white-space: normal; /* 1 */\n}\n\n/**\n * Add the correct vertical alignment in Chrome, Firefox, and Opera.\n */\n\nprogress {\n vertical-align: baseline;\n}\n\n/**\n * Remove the default vertical scrollbar in IE 10+.\n */\n\ntextarea {\n overflow: auto;\n}\n\n/**\n * 1. Add the correct box sizing in IE 10.\n * 2. Remove the padding in IE 10.\n */\n\n[type=\"checkbox\"],\n[type=\"radio\"] {\n box-sizing: border-box; /* 1 */\n padding: 0; /* 2 */\n}\n\n/**\n * Correct the cursor style of increment and decrement buttons in Chrome.\n */\n\n[type=\"number\"]::-webkit-inner-spin-button,\n[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n/**\n * 1. Correct the odd appearance in Chrome and Safari.\n * 2. Correct the outline style in Safari.\n */\n\n[type=\"search\"] {\n -webkit-appearance: textfield; /* 1 */\n outline-offset: -2px; /* 2 */\n}\n\n/**\n * Remove the inner padding in Chrome and Safari on macOS.\n */\n\n[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n/**\n * 1. Correct the inability to style clickable types in iOS and Safari.\n * 2. Change font properties to `inherit` in Safari.\n */\n\n::-webkit-file-upload-button {\n -webkit-appearance: button; /* 1 */\n font: inherit; /* 2 */\n}\n\n/* Interactive\n ========================================================================== */\n\n/*\n * Add the correct display in Edge, IE 10+, and Firefox.\n */\n\ndetails {\n display: block;\n}\n\n/*\n * Add the correct display in all browsers.\n */\n\nsummary {\n display: list-item;\n}\n\n/* Misc\n ========================================================================== */\n\n/**\n * Add the correct display in IE 10+.\n */\n\ntemplate {\n display: none;\n}\n\n/**\n * Add the correct display in IE 10.\n */\n\n[hidden] {\n display: none;\n}\n","// This file contains styles for managing print media.\n\n////////////////////////////////////////////////////////////////////////////////\n// Hide elements not relevant to print media.\n////////////////////////////////////////////////////////////////////////////////\n@media print\n // Hide icon container.\n .content-icon-container\n display: none !important\n\n // Hide showing header links if hovering over when printing.\n .headerlink\n display: none !important\n\n // Hide mobile header.\n .mobile-header\n display: none !important\n\n // Hide navigation links.\n .related-pages\n display: none !important\n\n////////////////////////////////////////////////////////////////////////////////\n// Tweaks related to decolorization.\n////////////////////////////////////////////////////////////////////////////////\n@media print\n // Apply a border around code which no longer have a color background.\n .highlight\n border: 0.1pt solid var(--color-foreground-border)\n\n////////////////////////////////////////////////////////////////////////////////\n// Avoid page break in some relevant cases.\n////////////////////////////////////////////////////////////////////////////////\n@media print\n ul, ol, dl, a, table, pre, blockquote\n page-break-inside: avoid\n\n h1, h2, h3, h4, h5, h6, img, figure, caption\n page-break-inside: avoid\n page-break-after: avoid\n\n ul, ol, dl\n page-break-before: avoid\n",".visually-hidden\n position: absolute !important\n width: 1px !important\n height: 1px !important\n padding: 0 !important\n margin: -1px !important\n overflow: hidden !important\n clip: rect(0,0,0,0) !important\n white-space: nowrap !important\n border: 0 !important\n\n:-moz-focusring\n outline: auto\n","// This file serves as the \"skeleton\" of the theming logic.\n//\n// This contains the bulk of the logic for handling dark mode, color scheme\n// toggling and the handling of color-scheme-specific hiding of elements.\n\nbody\n @include fonts\n @include spacing\n @include icons\n @include admonitions\n @include default-admonition(#651fff, \"abstract\")\n @include default-topic(#14B8A6, \"pencil\")\n\n @include colors\n\n.only-light\n display: block !important\nhtml body .only-dark\n display: none !important\n\n// Ignore dark-mode hints if print media.\n@media not print\n // Enable dark-mode, if requested.\n body[data-theme=\"dark\"]\n @include colors-dark\n\n html & .only-light\n display: none !important\n .only-dark\n display: block !important\n\n // Enable dark mode, unless explicitly told to avoid.\n @media (prefers-color-scheme: dark)\n body:not([data-theme=\"light\"])\n @include colors-dark\n\n html & .only-light\n display: none !important\n .only-dark\n display: block !important\n\n//\n// Theme toggle presentation\n//\nbody[data-theme=\"auto\"]\n .theme-toggle svg.theme-icon-when-auto\n display: block\n\nbody[data-theme=\"dark\"]\n .theme-toggle svg.theme-icon-when-dark\n display: block\n\nbody[data-theme=\"light\"]\n .theme-toggle svg.theme-icon-when-light\n display: block\n","// Fonts used by this theme.\n//\n// There are basically two things here -- using the system font stack and\n// defining sizes for various elements in %ages. We could have also used `em`\n// but %age is easier to reason about for me.\n\n@mixin fonts {\n // These are adapted from https://systemfontstack.com/\n --font-stack: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial,\n sans-serif, Apple Color Emoji, Segoe UI Emoji;\n --font-stack--monospace: \"SFMono-Regular\", Menlo, Consolas, Monaco,\n Liberation Mono, Lucida Console, monospace;\n\n --font-size--normal: 100%;\n --font-size--small: 87.5%;\n --font-size--small--2: 81.25%;\n --font-size--small--3: 75%;\n --font-size--small--4: 62.5%;\n\n // Sidebar\n --sidebar-caption-font-size: var(--font-size--small--2);\n --sidebar-item-font-size: var(--font-size--small);\n --sidebar-search-input-font-size: var(--font-size--small);\n\n // Table of Contents\n --toc-font-size: var(--font-size--small--3);\n --toc-font-size--mobile: var(--font-size--normal);\n --toc-title-font-size: var(--font-size--small--4);\n\n // Admonitions\n //\n // These aren't defined in terms of %ages, since nesting these is permitted.\n --admonition-font-size: 0.8125rem;\n --admonition-title-font-size: 0.8125rem;\n\n // Code\n --code-font-size: var(--font-size--small--2);\n\n // API\n --api-font-size: var(--font-size--small);\n}\n","// Spacing for various elements on the page\n//\n// If the user wants to tweak things in a certain way, they are permitted to.\n// They also have to deal with the consequences though!\n\n@mixin spacing {\n // Header!\n --header-height: calc(\n var(--sidebar-item-line-height) + 4 * #{var(--sidebar-item-spacing-vertical)}\n );\n --header-padding: 0.5rem;\n\n // Sidebar\n --sidebar-tree-space-above: 1.5rem;\n --sidebar-caption-space-above: 1rem;\n\n --sidebar-item-line-height: 1rem;\n --sidebar-item-spacing-vertical: 0.5rem;\n --sidebar-item-spacing-horizontal: 1rem;\n --sidebar-item-height: calc(\n var(--sidebar-item-line-height) + 2 *#{var(--sidebar-item-spacing-vertical)}\n );\n\n --sidebar-expander-width: var(--sidebar-item-height); // be square\n\n --sidebar-search-space-above: 0.5rem;\n --sidebar-search-input-spacing-vertical: 0.5rem;\n --sidebar-search-input-spacing-horizontal: 0.5rem;\n --sidebar-search-input-height: 1rem;\n --sidebar-search-icon-size: var(--sidebar-search-input-height);\n\n // Table of Contents\n --toc-title-padding: 0.25rem 0;\n --toc-spacing-vertical: 1.5rem;\n --toc-spacing-horizontal: 1.5rem;\n --toc-item-spacing-vertical: 0.4rem;\n --toc-item-spacing-horizontal: 1rem;\n}\n","// Expose theme icons as CSS variables.\n\n$icons: (\n // Adapted from tabler-icons\n // url: https://tablericons.com/\n \"search\":\n url('data:image/svg+xml;charset=utf-8,'),\n // Factored out from mkdocs-material on 24-Aug-2020.\n // url: https://squidfunk.github.io/mkdocs-material/reference/admonitions/\n \"pencil\":\n url('data:image/svg+xml;charset=utf-8,'),\n \"abstract\":\n url('data:image/svg+xml;charset=utf-8,'),\n \"info\":\n url('data:image/svg+xml;charset=utf-8,'),\n \"flame\":\n url('data:image/svg+xml;charset=utf-8,'),\n \"question\":\n url('data:image/svg+xml;charset=utf-8,'),\n \"warning\":\n url('data:image/svg+xml;charset=utf-8,'),\n \"failure\":\n url('data:image/svg+xml;charset=utf-8,'),\n \"spark\":\n url('data:image/svg+xml;charset=utf-8,')\n);\n\n@mixin icons {\n @each $name, $glyph in $icons {\n --icon-#{$name}: #{$glyph};\n }\n}\n","// Admonitions\n\n// Structure of these is:\n// admonition-class: color \"icon-name\";\n//\n// The colors are translated into CSS variables below. The icons are\n// used directly in the main declarations to set the `mask-image` in\n// the title.\n\n// prettier-ignore\n$admonitions: (\n // Each of these has an reST directives for it.\n \"caution\": #ff9100 \"spark\",\n \"warning\": #ff9100 \"warning\",\n \"danger\": #ff5252 \"spark\",\n \"attention\": #ff5252 \"warning\",\n \"error\": #ff5252 \"failure\",\n \"hint\": #00c852 \"question\",\n \"tip\": #00c852 \"info\",\n \"important\": #00bfa5 \"flame\",\n \"note\": #00b0ff \"pencil\",\n \"seealso\": #448aff \"info\",\n \"admonition-todo\": #808080 \"pencil\"\n);\n\n@mixin default-admonition($color, $icon-name) {\n --color-admonition-title: #{$color};\n --color-admonition-title-background: #{rgba($color, 0.2)};\n\n --icon-admonition-default: var(--icon-#{$icon-name});\n}\n\n@mixin default-topic($color, $icon-name) {\n --color-topic-title: #{$color};\n --color-topic-title-background: #{rgba($color, 0.2)};\n\n --icon-topic-default: var(--icon-#{$icon-name});\n}\n\n@mixin admonitions {\n @each $name, $values in $admonitions {\n --color-admonition-title--#{$name}: #{nth($values, 1)};\n --color-admonition-title-background--#{$name}: #{rgba(\n nth($values, 1),\n 0.2\n )};\n }\n}\n","// Colors used throughout this theme.\n//\n// The aim is to give the user more control. Thus, instead of hard-coding colors\n// in various parts of the stylesheet, the approach taken is to define all\n// colors as CSS variables and reusing them in all the places.\n//\n// `colors-dark` depends on `colors` being included at a lower specificity.\n\n@mixin colors {\n --color-problematic: #b30000;\n\n // Base Colors\n --color-foreground-primary: black; // for main text and headings\n --color-foreground-secondary: #5a5c63; // for secondary text\n --color-foreground-muted: #646776; // for muted text\n --color-foreground-border: #878787; // for content borders\n\n --color-background-primary: white; // for content\n --color-background-secondary: #f8f9fb; // for navigation + ToC\n --color-background-hover: #efeff4ff; // for navigation-item hover\n --color-background-hover--transparent: #efeff400;\n --color-background-border: #eeebee; // for UI borders\n --color-background-item: #ccc; // for \"background\" items (eg: copybutton)\n\n // Announcements\n --color-announcement-background: #000000dd;\n --color-announcement-text: #eeebee;\n\n // Brand colors\n --color-brand-primary: #2962ff;\n --color-brand-content: #2a5adf;\n\n // API documentation\n --color-api-background: var(--color-background-hover--transparent);\n --color-api-background-hover: var(--color-background-hover);\n --color-api-overall: var(--color-foreground-secondary);\n --color-api-name: var(--color-problematic);\n --color-api-pre-name: var(--color-problematic);\n --color-api-paren: var(--color-foreground-secondary);\n --color-api-keyword: var(--color-foreground-primary);\n --color-highlight-on-target: #ffffcc;\n\n // Inline code background\n --color-inline-code-background: var(--color-background-secondary);\n\n // Highlighted text (search)\n --color-highlighted-background: #ddeeff;\n --color-highlighted-text: var(--color-foreground-primary);\n\n // GUI Labels\n --color-guilabel-background: #ddeeff80;\n --color-guilabel-border: #bedaf580;\n --color-guilabel-text: var(--color-foreground-primary);\n\n // Admonitions!\n --color-admonition-background: transparent;\n\n //////////////////////////////////////////////////////////////////////////////\n // Everything below this should be one of:\n // - var(...)\n // - *-gradient(...)\n // - special literal values (eg: transparent, none)\n //////////////////////////////////////////////////////////////////////////////\n\n // Tables\n --color-table-header-background: var(--color-background-secondary);\n --color-table-border: var(--color-background-border);\n\n // Cards\n --color-card-border: var(--color-background-secondary);\n --color-card-background: transparent;\n --color-card-marginals-background: var(--color-background-secondary);\n\n // Header\n --color-header-background: var(--color-background-primary);\n --color-header-border: var(--color-background-border);\n --color-header-text: var(--color-foreground-primary);\n\n // Sidebar (left)\n --color-sidebar-background: var(--color-background-secondary);\n --color-sidebar-background-border: var(--color-background-border);\n\n --color-sidebar-brand-text: var(--color-foreground-primary);\n --color-sidebar-caption-text: var(--color-foreground-muted);\n --color-sidebar-link-text: var(--color-foreground-secondary);\n --color-sidebar-link-text--top-level: var(--color-brand-primary);\n\n --color-sidebar-item-background: var(--color-sidebar-background);\n --color-sidebar-item-background--current: var(\n --color-sidebar-item-background\n );\n --color-sidebar-item-background--hover: linear-gradient(\n 90deg,\n var(--color-background-hover--transparent) 0%,\n var(--color-background-hover) var(--sidebar-item-spacing-horizontal),\n var(--color-background-hover) 100%\n );\n\n --color-sidebar-item-expander-background: transparent;\n --color-sidebar-item-expander-background--hover: var(\n --color-background-hover\n );\n\n --color-sidebar-search-text: var(--color-foreground-primary);\n --color-sidebar-search-background: var(--color-background-secondary);\n --color-sidebar-search-background--focus: var(--color-background-primary);\n --color-sidebar-search-border: var(--color-background-border);\n --color-sidebar-search-icon: var(--color-foreground-muted);\n\n // Table of Contents (right)\n --color-toc-background: var(--color-background-primary);\n --color-toc-title-text: var(--color-foreground-muted);\n --color-toc-item-text: var(--color-foreground-secondary);\n --color-toc-item-text--hover: var(--color-foreground-primary);\n --color-toc-item-text--active: var(--color-brand-primary);\n\n // Actual page contents\n --color-content-foreground: var(--color-foreground-primary);\n --color-content-background: transparent;\n\n // Links\n --color-link: var(--color-brand-content);\n --color-link--hover: var(--color-brand-content);\n --color-link-underline: var(--color-background-border);\n --color-link-underline--hover: var(--color-foreground-border);\n}\n\n@mixin colors-dark {\n --color-problematic: #ee5151;\n\n // Base Colors\n --color-foreground-primary: #ffffffcc; // for main text and headings\n --color-foreground-secondary: #9ca0a5; // for secondary text\n --color-foreground-muted: #81868d; // for muted text\n --color-foreground-border: #666666; // for content borders\n\n --color-background-primary: #131416; // for content\n --color-background-secondary: #1a1c1e; // for navigation + ToC\n --color-background-hover: #1e2124ff; // for navigation-item hover\n --color-background-hover--transparent: #1e212400;\n --color-background-border: #303335; // for UI borders\n --color-background-item: #444; // for \"background\" items (eg: copybutton)\n\n // Announcements\n --color-announcement-background: #000000dd;\n --color-announcement-text: #eeebee;\n\n // Brand colors\n --color-brand-primary: #2b8cee;\n --color-brand-content: #368ce2;\n\n // Highlighted text (search)\n --color-highlighted-background: #083563;\n\n // GUI Labels\n --color-guilabel-background: #08356380;\n --color-guilabel-border: #13395f80;\n\n // API documentation\n --color-api-keyword: var(--color-foreground-secondary);\n --color-highlight-on-target: #333300;\n\n // Admonitions\n --color-admonition-background: #18181a;\n\n // Cards\n --color-card-border: var(--color-background-secondary);\n --color-card-background: #18181a;\n --color-card-marginals-background: var(--color-background-hover);\n}\n","// This file contains the styling for making the content throughout the page,\n// including fonts, paragraphs, headings and spacing among these elements.\n\nbody\n font-family: var(--font-stack)\npre,\ncode,\nkbd,\nsamp\n font-family: var(--font-stack--monospace)\n\n// Make fonts look slightly nicer.\nbody\n -webkit-font-smoothing: antialiased\n -moz-osx-font-smoothing: grayscale\n\n// Line height from Bootstrap 4.1\narticle\n line-height: 1.5\n\n//\n// Headings\n//\nh1,\nh2,\nh3,\nh4,\nh5,\nh6\n line-height: 1.25\n font-weight: bold\n\n border-radius: 0.5rem\n margin-top: 0.5rem\n margin-bottom: 0.5rem\n margin-left: -0.5rem\n margin-right: -0.5rem\n padding-left: 0.5rem\n padding-right: 0.5rem\n\n + p\n margin-top: 0\n\nh1\n font-size: 2.5em\n margin-top: 1.75rem\n margin-bottom: 1rem\nh2\n font-size: 2em\n margin-top: 1.75rem\nh3\n font-size: 1.5em\nh4\n font-size: 1.25em\nh5\n font-size: 1.125em\nh6\n font-size: 1em\n\nsmall\n opacity: 75%\n font-size: 80%\n\n// Paragraph\np\n margin-top: 0.5rem\n margin-bottom: 0.75rem\n\n// Horizontal rules\nhr.docutils\n height: 1px\n padding: 0\n margin: 2rem 0\n background-color: var(--color-background-border)\n border: 0\n\n.centered\n text-align: center\n\n// Links\na\n text-decoration: underline\n\n color: var(--color-link)\n text-decoration-color: var(--color-link-underline)\n\n &:hover\n color: var(--color-link--hover)\n text-decoration-color: var(--color-link-underline--hover)\n &.muted-link\n color: inherit\n &:hover\n color: var(--color-link)\n text-decoration-color: var(--color-link-underline--hover)\n","// This file contains the styles for the overall layouting of the documentation\n// skeleton, including the responsive changes as well as sidebar toggles.\n//\n// This is implemented as a mobile-last design, which isn't ideal, but it is\n// reasonably good-enough and I got pretty tired by the time I'd finished this\n// to move the rules around to fix this. Shouldn't take more than 3-4 hours,\n// if you know what you're doing tho.\n\n// HACK: Not all browsers account for the scrollbar width in media queries.\n// This results in horizontal scrollbars in the breakpoint where we go\n// from displaying everything to hiding the ToC. We accomodate for this by\n// adding a bit of padding to the TOC drawer, disabling the horizontal\n// scrollbar and allowing the scrollbars to cover the padding.\n// https://www.456bereastreet.com/archive/201301/media_query_width_and_vertical_scrollbars/\n\n// HACK: Always having the scrollbar visible, prevents certain browsers from\n// causing the content to stutter horizontally between taller-than-viewport and\n// not-taller-than-viewport pages.\n\nhtml\n overflow-x: hidden\n overflow-y: scroll\n scroll-behavior: smooth\n\n.sidebar-scroll, .toc-scroll, article[role=main] *\n // Override Firefox scrollbar style\n scrollbar-width: thin\n scrollbar-color: var(--color-foreground-border) transparent\n\n // Override Chrome scrollbar styles\n &::-webkit-scrollbar\n width: 0.25rem\n height: 0.25rem\n &::-webkit-scrollbar-thumb\n background-color: var(--color-foreground-border)\n border-radius: 0.125rem\n\n//\n// Overalls\n//\nhtml,\nbody\n height: 100%\n color: var(--color-foreground-primary)\n background: var(--color-background-primary)\n\narticle\n color: var(--color-content-foreground)\n background: var(--color-content-background)\n overflow-wrap: break-word\n\n.page\n display: flex\n // fill the viewport for pages with little content.\n min-height: 100%\n\n.mobile-header\n width: 100%\n height: var(--header-height)\n background-color: var(--color-header-background)\n color: var(--color-header-text)\n border-bottom: 1px solid var(--color-header-border)\n\n // Looks like sub-script/super-script have this, and we need this to\n // be \"on top\" of those.\n z-index: 10\n\n // We don't show the header on large screens.\n display: none\n\n // Add shadow when scrolled\n &.scrolled\n border-bottom: none\n box-shadow: 0 0 0.2rem rgba(0, 0, 0, 0.1), 0 0.2rem 0.4rem rgba(0, 0, 0, 0.2)\n\n .header-center\n a\n color: var(--color-header-text)\n text-decoration: none\n\n.main\n display: flex\n flex: 1\n\n// Sidebar (left) also covers the entire left portion of screen.\n.sidebar-drawer\n box-sizing: border-box\n\n border-right: 1px solid var(--color-sidebar-background-border)\n background: var(--color-sidebar-background)\n\n display: flex\n justify-content: flex-end\n // These next two lines took me two days to figure out.\n width: calc((100% - #{$full-width}) / 2 + #{$sidebar-width})\n min-width: $sidebar-width\n\n// Scroll-along sidebars\n.sidebar-container,\n.toc-drawer\n box-sizing: border-box\n width: $sidebar-width\n\n.toc-drawer\n background: var(--color-toc-background)\n // See HACK described on top of this document\n padding-right: 1rem\n\n.sidebar-sticky,\n.toc-sticky\n position: sticky\n top: 0\n height: min(100%, 100vh)\n height: 100vh\n\n display: flex\n flex-direction: column\n\n.sidebar-scroll,\n.toc-scroll\n flex-grow: 1\n flex-shrink: 1\n\n overflow: auto\n scroll-behavior: smooth\n\n// Central items.\n.content\n padding: 0 $content-padding\n width: $content-width\n\n display: flex\n flex-direction: column\n justify-content: space-between\n\n.icon\n display: inline-block\n height: 1rem\n width: 1rem\n svg\n width: 100%\n height: 100%\n\n//\n// Accommodate announcement banner\n//\n.announcement\n background-color: var(--color-announcement-background)\n color: var(--color-announcement-text)\n\n height: var(--header-height)\n display: flex\n align-items: center\n overflow-x: auto\n & + .page\n min-height: calc(100% - var(--header-height))\n\n.announcement-content\n box-sizing: border-box\n padding: 0.5rem\n min-width: 100%\n white-space: nowrap\n text-align: center\n\n a\n color: var(--color-announcement-text)\n text-decoration-color: var(--color-announcement-text)\n\n &:hover\n color: var(--color-announcement-text)\n text-decoration-color: var(--color-link--hover)\n\n////////////////////////////////////////////////////////////////////////////////\n// Toggles for theme\n////////////////////////////////////////////////////////////////////////////////\n.no-js .theme-toggle-container // don't show theme toggle if there's no JS\n display: none\n\n.theme-toggle-container\n vertical-align: middle\n\n.theme-toggle\n cursor: pointer\n border: none\n padding: 0\n background: transparent\n\n.theme-toggle svg\n vertical-align: middle\n height: 1rem\n width: 1rem\n color: var(--color-foreground-primary)\n display: none\n\n.theme-toggle-header\n float: left\n padding: 1rem 0.5rem\n\n////////////////////////////////////////////////////////////////////////////////\n// Toggles for elements\n////////////////////////////////////////////////////////////////////////////////\n.toc-overlay-icon, .nav-overlay-icon\n display: none\n cursor: pointer\n\n .icon\n color: var(--color-foreground-secondary)\n height: 1rem\n width: 1rem\n\n.toc-header-icon, .nav-overlay-icon\n // for when we set display: flex\n justify-content: center\n align-items: center\n\n.toc-content-icon\n height: 1.5rem\n width: 1.5rem\n\n.content-icon-container\n float: right\n display: flex\n margin-top: 1.5rem\n margin-left: 1rem\n margin-bottom: 1rem\n gap: 0.5rem\n\n .edit-this-page svg\n color: inherit\n height: 1rem\n width: 1rem\n\n.sidebar-toggle\n position: absolute\n display: none\n// \n.sidebar-toggle[name=\"__toc\"]\n left: 20px\n.sidebar-toggle:checked\n left: 40px\n// \n\n.overlay\n position: fixed\n top: 0\n width: 0\n height: 0\n\n transition: width 0ms, height 0ms, opacity 250ms ease-out\n\n opacity: 0\n background-color: rgba(0, 0, 0, 0.54)\n.sidebar-overlay\n z-index: 20\n.toc-overlay\n z-index: 40\n\n// Keep things on top and smooth.\n.sidebar-drawer\n z-index: 30\n transition: left 250ms ease-in-out\n.toc-drawer\n z-index: 50\n transition: right 250ms ease-in-out\n\n// Show the Sidebar\n#__navigation:checked\n & ~ .sidebar-overlay\n width: 100%\n height: 100%\n opacity: 1\n & ~ .page\n .sidebar-drawer\n top: 0\n left: 0\n // Show the toc sidebar\n#__toc:checked\n & ~ .toc-overlay\n width: 100%\n height: 100%\n opacity: 1\n & ~ .page\n .toc-drawer\n top: 0\n right: 0\n\n////////////////////////////////////////////////////////////////////////////////\n// Back to top\n////////////////////////////////////////////////////////////////////////////////\n.back-to-top\n text-decoration: none\n\n display: none\n position: fixed\n left: 0\n top: 1rem\n padding: 0.5rem\n padding-right: 0.75rem\n border-radius: 1rem\n font-size: 0.8125rem\n\n background: var(--color-background-primary)\n box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.05), #6b728080 0px 0px 1px 0px\n\n z-index: 10\n\n margin-left: 50%\n transform: translateX(-50%)\n svg\n height: 1rem\n width: 1rem\n fill: currentColor\n display: inline-block\n\n span\n margin-left: 0.25rem\n\n .show-back-to-top &\n display: flex\n align-items: center\n\n////////////////////////////////////////////////////////////////////////////////\n// Responsive layouting\n////////////////////////////////////////////////////////////////////////////////\n// Make things a bit bigger on bigger screens.\n@media (min-width: $full-width + $sidebar-width)\n html\n font-size: 110%\n\n@media (max-width: $full-width)\n // Collapse \"toc\" into the icon.\n .toc-content-icon\n display: flex\n .toc-drawer\n position: fixed\n height: 100vh\n top: 0\n right: -$sidebar-width\n border-left: 1px solid var(--color-background-muted)\n .toc-tree\n border-left: none\n font-size: var(--toc-font-size--mobile)\n\n // Accomodate for a changed content width.\n .sidebar-drawer\n width: calc((100% - #{$full-width - $sidebar-width}) / 2 + #{$sidebar-width})\n\n@media (max-width: $full-width - $sidebar-width)\n // Collapse \"navigation\".\n .nav-overlay-icon\n display: flex\n .sidebar-drawer\n position: fixed\n height: 100vh\n width: $sidebar-width\n\n top: 0\n left: -$sidebar-width\n\n // Swap which icon is visible.\n .toc-header-icon\n display: flex\n .toc-content-icon, .theme-toggle-content\n display: none\n .theme-toggle-header\n display: block\n\n // Show the header.\n .mobile-header\n position: sticky\n top: 0\n display: flex\n justify-content: space-between\n align-items: center\n\n .header-left,\n .header-right\n display: flex\n height: var(--header-height)\n padding: 0 var(--header-padding)\n label\n height: 100%\n width: 100%\n user-select: none\n\n .nav-overlay-icon .icon,\n .theme-toggle svg\n height: 1.25rem\n width: 1.25rem\n\n // Add a scroll margin for the content\n :target\n scroll-margin-top: var(--header-height)\n\n // Show back-to-top below the header\n .back-to-top\n top: calc(var(--header-height) + 0.5rem)\n\n // Center the page, and accommodate for the header.\n .page\n flex-direction: column\n justify-content: center\n .content\n margin-left: auto\n margin-right: auto\n\n@media (max-width: $content-width + 2* $content-padding)\n // Content should respect window limits.\n .content\n width: 100%\n overflow-x: auto\n\n@media (max-width: $content-width)\n .content\n padding: 0 $content-padding--small\n // Don't float sidebars to the right.\n article aside.sidebar\n float: none\n width: 100%\n margin: 1rem 0\n","//\n// The design here is strongly inspired by mkdocs-material.\n.admonition, .topic\n margin: 1rem auto\n padding: 0 0.5rem 0.5rem 0.5rem\n\n background: var(--color-admonition-background)\n\n border-radius: 0.2rem\n box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.05), 0 0 0.0625rem rgba(0, 0, 0, 0.1)\n\n font-size: var(--admonition-font-size)\n\n overflow: hidden\n page-break-inside: avoid\n\n // First element should have no margin, since the title has it.\n > :nth-child(2)\n margin-top: 0\n\n // Last item should have no margin, since we'll control that w/ padding\n > :last-child\n margin-bottom: 0\n\n.admonition p.admonition-title,\np.topic-title\n position: relative\n margin: 0 -0.5rem 0.5rem\n padding-left: 2rem\n padding-right: .5rem\n padding-top: .4rem\n padding-bottom: .4rem\n\n font-weight: 500\n font-size: var(--admonition-title-font-size)\n line-height: 1.3\n\n // Our fancy icon\n &::before\n content: \"\"\n position: absolute\n left: 0.5rem\n width: 1rem\n height: 1rem\n\n// Default styles\np.admonition-title\n background-color: var(--color-admonition-title-background)\n &::before\n background-color: var(--color-admonition-title)\n mask-image: var(--icon-admonition-default)\n mask-repeat: no-repeat\n\np.topic-title\n background-color: var(--color-topic-title-background)\n &::before\n background-color: var(--color-topic-title)\n mask-image: var(--icon-topic-default)\n mask-repeat: no-repeat\n\n//\n// Variants\n//\n.admonition\n border-left: 0.2rem solid var(--color-admonition-title)\n\n @each $type, $value in $admonitions\n &.#{$type}\n border-left-color: var(--color-admonition-title--#{$type})\n > .admonition-title\n background-color: var(--color-admonition-title-background--#{$type})\n &::before\n background-color: var(--color-admonition-title--#{$type})\n mask-image: var(--icon-#{nth($value, 2)})\n\n.admonition-todo > .admonition-title\n text-transform: uppercase\n","// This file stylizes the API documentation (stuff generated by autodoc). It's\n// deeply nested due to how autodoc structures the HTML without enough classes\n// to select the relevant items.\n\n// API docs!\ndl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple)\n // Tweak the spacing of all the things!\n dd\n margin-left: 2rem\n > :first-child\n margin-top: 0.125rem\n > :last-child\n margin-bottom: 0.75rem\n\n // This is used for the arguments\n .field-list\n margin-bottom: 0.75rem\n\n // \"Headings\" (like \"Parameters\" and \"Return\")\n > dt\n text-transform: uppercase\n font-size: var(--font-size--small)\n\n dd:empty\n margin-bottom: 0.5rem\n dd > ul\n margin-left: -1.2rem\n > li\n > p:nth-child(2)\n margin-top: 0\n // When the last-empty-paragraph follows a paragraph, it doesn't need\n // to augument the existing spacing.\n > p + p:last-child:empty\n margin-top: 0\n margin-bottom: 0\n\n // Colorize the elements\n > dt\n color: var(--color-api-overall)\n\n.sig:not(.sig-inline)\n font-weight: bold\n\n font-size: var(--api-font-size)\n font-family: var(--font-stack--monospace)\n\n margin-left: -0.25rem\n margin-right: -0.25rem\n padding-top: 0.25rem\n padding-bottom: 0.25rem\n padding-right: 0.5rem\n\n // These are intentionally em, to properly match the font size.\n padding-left: 3em\n text-indent: -2.5em\n\n border-radius: 0.25rem\n\n background: var(--color-api-background)\n transition: background 100ms ease-out\n\n &:hover\n background: var(--color-api-background-hover)\n\n // adjust the size of the [source] link on the right.\n a.reference\n .viewcode-link\n font-weight: normal\n width: 3.5rem\n\nem.property\n font-style: normal\n &:first-child\n color: var(--color-api-keyword)\n.sig-name\n color: var(--color-api-name)\n.sig-prename\n font-weight: normal\n color: var(--color-api-pre-name)\n.sig-paren\n color: var(--color-api-paren)\n.sig-param\n font-style: normal\n\n.versionmodified\n font-style: italic\ndiv.versionadded, div.versionchanged, div.deprecated\n p\n margin-top: 0.125rem\n margin-bottom: 0.125rem\n\n// Align the [docs] and [source] to the right.\n.viewcode-link, .viewcode-back\n float: right\n text-align: right\n",".line-block\n margin-top: 0.5rem\n margin-bottom: 0.75rem\n .line-block\n margin-top: 0rem\n margin-bottom: 0rem\n padding-left: 1rem\n","// Captions\narticle p.caption,\ntable > caption,\n.code-block-caption\n font-size: var(--font-size--small)\n text-align: center\n\n// Caption above a TOCTree\n.toctree-wrapper.compound\n .caption, :not(.caption) > .caption-text\n font-size: var(--font-size--small)\n text-transform: uppercase\n\n text-align: initial\n margin-bottom: 0\n\n > ul\n margin-top: 0\n margin-bottom: 0\n","// Inline code\ncode.literal, .sig-inline\n background: var(--color-inline-code-background)\n border-radius: 0.2em\n // Make the font smaller, and use padding to recover.\n font-size: var(--font-size--small--2)\n padding: 0.1em 0.2em\n\n pre.literal-block &\n font-size: inherit\n padding: 0\n\n p &\n border: 1px solid var(--color-background-border)\n\n.sig-inline\n font-family: var(--font-stack--monospace)\n\n// Code and Literal Blocks\n$code-spacing-vertical: 0.625rem\n$code-spacing-horizontal: 0.875rem\n\n// Wraps every literal block + line numbers.\ndiv[class*=\" highlight-\"],\ndiv[class^=\"highlight-\"]\n margin: 1em 0\n display: flex\n\n .table-wrapper\n margin: 0\n padding: 0\n\npre\n margin: 0\n padding: 0\n overflow: auto\n\n // Needed to have more specificity than pygments' \"pre\" selector. :(\n article[role=\"main\"] .highlight &\n line-height: 1.5\n\n &.literal-block,\n .highlight &\n font-size: var(--code-font-size)\n padding: $code-spacing-vertical $code-spacing-horizontal\n\n // Make it look like all the other blocks.\n &.literal-block\n margin-top: 1rem\n margin-bottom: 1rem\n\n border-radius: 0.2rem\n background-color: var(--color-code-background)\n color: var(--color-code-foreground)\n\n// All code is always contained in this.\n.highlight\n width: 100%\n border-radius: 0.2rem\n\n // Make line numbers and prompts un-selectable.\n .gp, span.linenos\n user-select: none\n pointer-events: none\n\n // Expand the line-highlighting.\n .hll\n display: block\n margin-left: -$code-spacing-horizontal\n margin-right: -$code-spacing-horizontal\n padding-left: $code-spacing-horizontal\n padding-right: $code-spacing-horizontal\n\n/* Make code block captions be nicely integrated */\n.code-block-caption\n display: flex\n padding: $code-spacing-vertical $code-spacing-horizontal\n\n border-radius: 0.25rem\n border-bottom-left-radius: 0\n border-bottom-right-radius: 0\n font-weight: 300\n border-bottom: 1px solid\n\n background-color: var(--color-code-background)\n color: var(--color-code-foreground)\n border-color: var(--color-background-border)\n\n + div[class]\n margin-top: 0\n pre\n border-top-left-radius: 0\n border-top-right-radius: 0\n\n// When `html_codeblock_linenos_style` is table.\n.highlighttable\n width: 100%\n display: block\n tbody\n display: block\n\n tr\n display: flex\n\n // Line numbers\n td.linenos\n background-color: var(--color-code-background)\n color: var(--color-code-foreground)\n padding: $code-spacing-vertical $code-spacing-horizontal\n padding-right: 0\n border-top-left-radius: 0.2rem\n border-bottom-left-radius: 0.2rem\n\n .linenodiv\n padding-right: $code-spacing-horizontal\n font-size: var(--code-font-size)\n box-shadow: -0.0625rem 0 var(--color-foreground-border) inset\n\n // Actual code\n td.code\n padding: 0\n display: block\n flex: 1\n overflow: hidden\n\n .highlight\n border-top-left-radius: 0\n border-bottom-left-radius: 0\n\n// When `html_codeblock_linenos_style` is inline.\n.highlight\n span.linenos\n display: inline-block\n padding-left: 0\n padding-right: $code-spacing-horizontal\n margin-right: $code-spacing-horizontal\n box-shadow: -0.0625rem 0 var(--color-foreground-border) inset\n","// Inline Footnote Reference\n.footnote-reference\n font-size: var(--font-size--small--4)\n vertical-align: super\n\n// Definition list, listing the content of each note.\n// docutils <= 0.17\ndl.footnote.brackets\n font-size: var(--font-size--small)\n color: var(--color-foreground-secondary)\n\n display: grid\n grid-template-columns: max-content auto\n dt\n margin: 0\n > .fn-backref\n margin-left: 0.25rem\n\n &:after\n content: \":\"\n\n .brackets\n &:before\n content: \"[\"\n &:after\n content: \"]\"\n\n dd\n margin: 0\n padding: 0 1rem\n\n// docutils >= 0.18\naside.footnote\n font-size: var(--font-size--small)\n color: var(--color-foreground-secondary)\n\naside.footnote > span,\ndiv.citation > span\n float: left\n font-weight: 500\n padding-right: 0.25rem\n\naside.footnote > p,\ndiv.citation > p\n margin-left: 2rem\n","//\n// Figures\n//\nimg\n box-sizing: border-box\n max-width: 100%\n height: auto\n\narticle\n figure, .figure\n border-radius: 0.2rem\n\n margin: 0\n :last-child\n margin-bottom: 0\n\n .align-left\n float: left\n clear: left\n margin: 0 1rem 1rem\n\n .align-right\n float: right\n clear: right\n margin: 0 1rem 1rem\n\n .align-default,\n .align-center\n display: block\n text-align: center\n margin-left: auto\n margin-right: auto\n\n // WELL, table needs to be stylised like a table.\n table.align-default\n display: table\n text-align: initial\n",".genindex-jumpbox, .domainindex-jumpbox\n border-top: 1px solid var(--color-background-border)\n border-bottom: 1px solid var(--color-background-border)\n padding: 0.25rem\n\n.genindex-section, .domainindex-section\n h2\n margin-top: 0.75rem\n margin-bottom: 0.5rem\n ul\n margin-top: 0\n margin-bottom: 0\n","ul,\nol\n padding-left: 1.2rem\n\n // Space lists out like paragraphs\n margin-top: 1rem\n margin-bottom: 1rem\n // reduce margins within li.\n li\n > p:first-child\n margin-top: 0.25rem\n margin-bottom: 0.25rem\n\n > p:last-child\n margin-top: 0.25rem\n\n > ul,\n > ol\n margin-top: 0.5rem\n margin-bottom: 0.5rem\n\nol\n &.arabic\n list-style: decimal\n &.loweralpha\n list-style: lower-alpha\n &.upperalpha\n list-style: upper-alpha\n &.lowerroman\n list-style: lower-roman\n &.upperroman\n list-style: upper-roman\n\n// Don't space lists out when they're \"simple\" or in a `.. toctree::`\n.simple,\n.toctree-wrapper\n li\n > ul,\n > ol\n margin-top: 0\n margin-bottom: 0\n\n// Definition Lists\n.field-list,\n.option-list,\ndl:not([class]),\ndl.simple,\ndl.footnote,\ndl.glossary\n dt\n font-weight: 500\n margin-top: 0.25rem\n + dt\n margin-top: 0\n\n .classifier::before\n content: \":\"\n margin-left: 0.2rem\n margin-right: 0.2rem\n\n dd\n > p:first-child,\n ul\n margin-top: 0.125rem\n\n ul\n margin-bottom: 0.125rem\n",".math-wrapper\n width: 100%\n overflow-x: auto\n\ndiv.math\n position: relative\n text-align: center\n\n .headerlink,\n &:focus .headerlink\n display: none\n\n &:hover .headerlink\n display: inline-block\n\n span.eqno\n position: absolute\n right: 0.5rem\n top: 50%\n transform: translate(0, -50%)\n z-index: 1\n","// Abbreviations\nabbr[title]\n cursor: help\n\n// \"Problematic\" content, as identified by Sphinx\n.problematic\n color: var(--color-problematic)\n\n// Keyboard / Mouse \"instructions\"\nkbd:not(.compound)\n margin: 0 0.2rem\n padding: 0 0.2rem\n border-radius: 0.2rem\n border: 1px solid var(--color-foreground-border)\n color: var(--color-foreground-primary)\n vertical-align: text-bottom\n\n font-size: var(--font-size--small--3)\n display: inline-block\n\n box-shadow: 0 0.0625rem 0 rgba(0, 0, 0, 0.2), inset 0 0 0 0.125rem var(--color-background-primary)\n\n background-color: var(--color-background-secondary)\n\n// Blockquote\nblockquote\n border-left: 4px solid var(--color-background-border)\n background: var(--color-background-secondary)\n\n margin-left: 0\n margin-right: 0\n padding: 0.5rem 1rem\n\n .attribution\n font-weight: 600\n text-align: right\n\n &.pull-quote,\n &.highlights\n font-size: 1.25em\n\n &.epigraph,\n &.pull-quote\n border-left-width: 0\n border-radius: 0.5rem\n\n &.highlights\n border-left-width: 0\n background: transparent\n\n// Center align embedded-in-text images\np .reference img\n vertical-align: middle\n","p.rubric\n line-height: 1.25\n font-weight: bold\n font-size: 1.125em\n\n // For Numpy-style documentation that's got rubrics within it.\n // https://github.com/pradyunsg/furo/discussions/505\n dd &\n line-height: inherit\n font-weight: inherit\n\n font-size: var(--font-size--small)\n text-transform: uppercase\n","article .sidebar\n float: right\n clear: right\n width: 30%\n\n margin-left: 1rem\n margin-right: 0\n\n border-radius: 0.2rem\n background-color: var(--color-background-secondary)\n border: var(--color-background-border) 1px solid\n\n > *\n padding-left: 1rem\n padding-right: 1rem\n\n > ul, > ol // lists need additional padding, because bullets.\n padding-left: 2.2rem\n\n .sidebar-title\n margin: 0\n padding: 0.5rem 1rem\n border-bottom: var(--color-background-border) 1px solid\n\n font-weight: 500\n\n// TODO: subtitle\n// TODO: dedicated variables?\n",".table-wrapper\n width: 100%\n overflow-x: auto\n margin-top: 1rem\n margin-bottom: 0.5rem\n padding: 0.2rem 0.2rem 0.75rem\n\ntable.docutils\n border-radius: 0.2rem\n border-spacing: 0\n border-collapse: collapse\n\n box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.05), 0 0 0.0625rem rgba(0, 0, 0, 0.1)\n\n th\n background: var(--color-table-header-background)\n\n td,\n th\n // Space things out properly\n padding: 0 0.25rem\n\n // Get the borders looking just-right.\n border-left: 1px solid var(--color-table-border)\n border-right: 1px solid var(--color-table-border)\n border-bottom: 1px solid var(--color-table-border)\n\n p\n margin: 0.25rem\n\n &:first-child\n border-left: none\n &:last-child\n border-right: none\n\n // MyST-parser tables set these classes for control of column alignment\n &.text-left\n text-align: left\n &.text-right\n text-align: right\n &.text-center\n text-align: center\n",":target\n scroll-margin-top: 0.5rem\n\n@media (max-width: $full-width - $sidebar-width)\n :target\n scroll-margin-top: calc(0.5rem + var(--header-height))\n\n // When a heading is selected\n section > span:target\n scroll-margin-top: calc(0.8rem + var(--header-height))\n\n// Permalinks\n.headerlink\n font-weight: 100\n user-select: none\n\nh1,\nh2,\nh3,\nh4,\nh5,\nh6,\ndl dt,\np.caption,\nfigcaption p,\ntable > caption,\n.code-block-caption\n > .headerlink\n margin-left: 0.5rem\n visibility: hidden\n &:hover > .headerlink\n visibility: visible\n\n // Don't change to link-like, if someone adds the contents directive.\n > .toc-backref\n color: inherit\n text-decoration-line: none\n\n// Figure and table captions are special.\nfigure:hover > figcaption > p > .headerlink,\ntable:hover > caption > .headerlink\n visibility: visible\n\n:target >, // Regular section[id] style anchors\nspan:target ~ // Non-regular span[id] style \"extra\" anchors\n h1,\n h2,\n h3,\n h4,\n h5,\n h6\n &:nth-of-type(1)\n background-color: var(--color-highlight-on-target)\n // .headerlink\n // visibility: visible\n code.literal\n background-color: transparent\n\ntable:target > caption,\nfigure:target\n background-color: var(--color-highlight-on-target)\n\n// Inline page contents\n.this-will-duplicate-information-and-it-is-still-useful-here li :target\n background-color: var(--color-highlight-on-target)\n\n// Code block permalinks\n.literal-block-wrapper:target .code-block-caption\n background-color: var(--color-highlight-on-target)\n\n// When a definition list item is selected\n//\n// There isn't really an alternative to !important here, due to the\n// high-specificity of API documentation's selector.\ndt:target\n background-color: var(--color-highlight-on-target) !important\n\n// When a footnote reference is selected\n.footnote > dt:target + dd,\n.footnote-reference:target\n background-color: var(--color-highlight-on-target)\n",".guilabel\n background-color: var(--color-guilabel-background)\n border: 1px solid var(--color-guilabel-border)\n color: var(--color-guilabel-text)\n\n padding: 0 0.3em\n border-radius: 0.5em\n font-size: 0.9em\n","// This file contains the styles used for stylizing the footer that's shown\n// below the content.\n\nfooter\n font-size: var(--font-size--small)\n display: flex\n flex-direction: column\n\n margin-top: 2rem\n\n// Bottom of page information\n.bottom-of-page\n display: flex\n align-items: center\n justify-content: space-between\n\n margin-top: 1rem\n padding-top: 1rem\n padding-bottom: 1rem\n\n color: var(--color-foreground-secondary)\n border-top: 1px solid var(--color-background-border)\n\n line-height: 1.5\n\n @media (max-width: $content-width)\n text-align: center\n flex-direction: column-reverse\n gap: 0.25rem\n\n .left-details\n font-size: var(--font-size--small)\n\n .right-details\n display: flex\n flex-direction: column\n gap: 0.25rem\n text-align: right\n\n .icons\n display: flex\n justify-content: flex-end\n gap: 0.25rem\n font-size: 1rem\n\n a\n text-decoration: none\n\n svg,\n img\n font-size: 1.125rem\n height: 1em\n width: 1em\n\n// Next/Prev page information\n.related-pages\n a\n display: flex\n align-items: center\n\n text-decoration: none\n &:hover .page-info .title\n text-decoration: underline\n color: var(--color-link)\n text-decoration-color: var(--color-link-underline)\n\n svg.furo-related-icon,\n svg.furo-related-icon > use\n flex-shrink: 0\n\n color: var(--color-foreground-border)\n\n width: 0.75rem\n height: 0.75rem\n margin: 0 0.5rem\n\n &.next-page\n max-width: 50%\n\n float: right\n clear: right\n text-align: right\n\n &.prev-page\n max-width: 50%\n\n float: left\n clear: left\n\n svg\n transform: rotate(180deg)\n\n.page-info\n display: flex\n flex-direction: column\n overflow-wrap: anywhere\n\n .next-page &\n align-items: flex-end\n\n .context\n display: flex\n align-items: center\n\n padding-bottom: 0.1rem\n\n color: var(--color-foreground-muted)\n font-size: var(--font-size--small)\n text-decoration: none\n","// This file contains the styles for the contents of the left sidebar, which\n// contains the navigation tree, logo, search etc.\n\n////////////////////////////////////////////////////////////////////////////////\n// Brand on top of the scrollable tree.\n////////////////////////////////////////////////////////////////////////////////\n.sidebar-brand\n display: flex\n flex-direction: column\n flex-shrink: 0\n\n padding: var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal)\n text-decoration: none\n\n.sidebar-brand-text\n color: var(--color-sidebar-brand-text)\n overflow-wrap: break-word\n margin: var(--sidebar-item-spacing-vertical) 0\n font-size: 1.5rem\n\n.sidebar-logo-container\n margin: var(--sidebar-item-spacing-vertical) 0\n\n.sidebar-logo\n margin: 0 auto\n display: block\n max-width: 100%\n\n////////////////////////////////////////////////////////////////////////////////\n// Search\n////////////////////////////////////////////////////////////////////////////////\n.sidebar-search-container\n display: flex\n align-items: center\n margin-top: var(--sidebar-search-space-above)\n\n position: relative\n\n background: var(--color-sidebar-search-background)\n &:hover,\n &:focus-within\n background: var(--color-sidebar-search-background--focus)\n\n &::before\n content: \"\"\n position: absolute\n left: var(--sidebar-item-spacing-horizontal)\n width: var(--sidebar-search-icon-size)\n height: var(--sidebar-search-icon-size)\n\n background-color: var(--color-sidebar-search-icon)\n mask-image: var(--icon-search)\n\n.sidebar-search\n box-sizing: border-box\n\n border: none\n border-top: 1px solid var(--color-sidebar-search-border)\n border-bottom: 1px solid var(--color-sidebar-search-border)\n\n padding-top: var(--sidebar-search-input-spacing-vertical)\n padding-bottom: var(--sidebar-search-input-spacing-vertical)\n padding-right: var(--sidebar-search-input-spacing-horizontal)\n padding-left: calc(var(--sidebar-item-spacing-horizontal) + var(--sidebar-search-input-spacing-horizontal) + var(--sidebar-search-icon-size))\n\n width: 100%\n\n color: var(--color-sidebar-search-foreground)\n background: transparent\n z-index: 10\n\n &:focus\n outline: none\n\n &::placeholder\n font-size: var(--sidebar-search-input-font-size)\n\n//\n// Hide Search Matches link\n//\n#searchbox .highlight-link\n padding: var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal) 0\n margin: 0\n text-align: center\n\n a\n color: var(--color-sidebar-search-icon)\n font-size: var(--font-size--small--2)\n\n////////////////////////////////////////////////////////////////////////////////\n// Structure/Skeleton of the navigation tree (left)\n////////////////////////////////////////////////////////////////////////////////\n.sidebar-tree\n font-size: var(--sidebar-item-font-size)\n margin-top: var(--sidebar-tree-space-above)\n margin-bottom: var(--sidebar-item-spacing-vertical)\n\n ul\n padding: 0\n margin-top: 0\n margin-bottom: 0\n\n display: flex\n flex-direction: column\n\n list-style: none\n\n li\n position: relative\n margin: 0\n\n > ul\n margin-left: var(--sidebar-item-spacing-horizontal)\n\n .icon\n color: var(--color-sidebar-link-text)\n\n .reference\n box-sizing: border-box\n color: var(--color-sidebar-link-text)\n\n // Fill the parent.\n display: inline-block\n line-height: var(--sidebar-item-line-height)\n text-decoration: none\n\n // Don't allow long words to cause wrapping.\n overflow-wrap: anywhere\n\n height: 100%\n width: 100%\n\n padding: var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal)\n\n &:hover\n background: var(--color-sidebar-item-background--hover)\n\n // Add a nice little \"external-link\" arrow here.\n &.external::after\n content: url('data:image/svg+xml,')\n margin: 0 0.25rem\n vertical-align: middle\n color: var(--color-sidebar-link-text)\n\n // Make the current page reference bold.\n .current-page > .reference\n font-weight: bold\n\n label\n position: absolute\n top: 0\n right: 0\n height: var(--sidebar-item-height)\n width: var(--sidebar-expander-width)\n\n cursor: pointer\n user-select: none\n\n display: flex\n justify-content: center\n align-items: center\n\n .caption, :not(.caption) > .caption-text\n font-size: var(--sidebar-caption-font-size)\n color: var(--color-sidebar-caption-text)\n\n font-weight: bold\n text-transform: uppercase\n\n margin: var(--sidebar-caption-space-above) 0 0 0\n padding: var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal)\n\n // If it has children, add a bit more padding to wrap the content to avoid\n // overlapping with the
+
+
+ +
+
+
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+ +
+
+ + +
+
+ + Made with Sphinx and @pradyunsg's + + Furo + +
+
+ +
+
+ +
+
+ +
+