Browse Source

merge multiple repositories into an existing monorepo

- merged using: 'monorepo_add.sh services-clsi:services/clsi'
- see https://github.com/shopsys/monorepo-tools
Jakob Ackermann 5 năm trước cách đây
mục cha
commit
de77f1ce67
100 tập tin đã thay đổi với 27724 bổ sung và 0 xóa
  1. 11 0
      services/clsi/.dockerignore
  2. 86 0
      services/clsi/.eslintrc
  3. 38 0
      services/clsi/.github/ISSUE_TEMPLATE.md
  4. 48 0
      services/clsi/.github/PULL_REQUEST_TEMPLATE.md
  5. 23 0
      services/clsi/.github/dependabot.yml
  6. 17 0
      services/clsi/.gitignore
  7. 3 0
      services/clsi/.mocharc.json
  8. 1 0
      services/clsi/.nvmrc
  9. 11 0
      services/clsi/.prettierrc
  10. 35 0
      services/clsi/.viminfo
  11. 28 0
      services/clsi/Dockerfile
  12. 661 0
      services/clsi/LICENSE
  13. 90 0
      services/clsi/Makefile
  14. 184 0
      services/clsi/README.md
  15. 425 0
      services/clsi/app.js
  16. 20 0
      services/clsi/app/js/CommandRunner.js
  17. 269 0
      services/clsi/app/js/CompileController.js
  18. 761 0
      services/clsi/app/js/CompileManager.js
  19. 273 0
      services/clsi/app/js/ContentCacheManager.js
  20. 115 0
      services/clsi/app/js/ContentCacheMetrics.js
  21. 24 0
      services/clsi/app/js/ContentController.js
  22. 38 0
      services/clsi/app/js/ContentTypeMapper.js
  23. 18 0
      services/clsi/app/js/DbQueue.js
  24. 113 0
      services/clsi/app/js/DockerLockManager.js
  25. 625 0
      services/clsi/app/js/DockerRunner.js
  26. 57 0
      services/clsi/app/js/DraftModeManager.js
  27. 41 0
      services/clsi/app/js/Errors.js
  28. 237 0
      services/clsi/app/js/LatexRunner.js
  29. 103 0
      services/clsi/app/js/LocalCommandRunner.js
  30. 72 0
      services/clsi/app/js/LockManager.js
  31. 3 0
      services/clsi/app/js/Metrics.js
  32. 563 0
      services/clsi/app/js/OutputCacheManager.js
  33. 78 0
      services/clsi/app/js/OutputFileFinder.js
  34. 103 0
      services/clsi/app/js/OutputFileOptimiser.js
  35. 207 0
      services/clsi/app/js/ProjectPersistenceManager.js
  36. 239 0
      services/clsi/app/js/RequestParser.js
  37. 116 0
      services/clsi/app/js/ResourceStateManager.js
  38. 354 0
      services/clsi/app/js/ResourceWriter.js
  39. 63 0
      services/clsi/app/js/SafeReader.js
  40. 94 0
      services/clsi/app/js/StaticServerForbidSymlinks.js
  41. 101 0
      services/clsi/app/js/TikzManager.js
  42. 281 0
      services/clsi/app/js/UrlCache.js
  43. 131 0
      services/clsi/app/js/UrlFetcher.js
  44. 67 0
      services/clsi/app/js/db.js
  45. 43 0
      services/clsi/app/lib/pdfjs/FSPdfManager.js
  46. 148 0
      services/clsi/app/lib/pdfjs/FSStream.js
  47. 27 0
      services/clsi/app/lib/pdfjs/parseXrefTable.js
  48. 0 0
      services/clsi/bin/.gitignore
  49. 4 0
      services/clsi/bin/acceptance_test
  50. BIN
      services/clsi/bin/synctex
  51. 9 0
      services/clsi/buildscript.txt
  52. 170 0
      services/clsi/config/settings.defaults.js
  53. 2 0
      services/clsi/db/.gitignore
  54. 5 0
      services/clsi/debug
  55. 34 0
      services/clsi/docker-compose-config.yml
  56. 42 0
      services/clsi/docker-compose.ci.yml
  57. 43 0
      services/clsi/docker-compose.yml
  58. 19 0
      services/clsi/entrypoint.sh
  59. 24 0
      services/clsi/install_deps.sh
  60. 41 0
      services/clsi/kube.yaml
  61. 17 0
      services/clsi/nodemon.json
  62. 6775 0
      services/clsi/package-lock.json
  63. 67 0
      services/clsi/package.json
  64. 3 0
      services/clsi/patch-texlive-dockerfile
  65. 12 0
      services/clsi/scripts/demo-pdfjs-Xref.js
  66. 836 0
      services/clsi/seccomp/clsi-profile.json
  67. 66 0
      services/clsi/src/synctex.c
  68. 4249 0
      services/clsi/src/synctex/synctex_parser.c
  69. 346 0
      services/clsi/src/synctex/synctex_parser.h
  70. 45 0
      services/clsi/src/synctex/synctex_parser_local.h
  71. 141 0
      services/clsi/src/synctex/synctex_parser_readme.txt
  72. 479 0
      services/clsi/src/synctex/synctex_parser_utils.c
  73. 141 0
      services/clsi/src/synctex/synctex_parser_utils.h
  74. 1 0
      services/clsi/src/synctex/synctex_parser_version.txt
  75. 34 0
      services/clsi/synctex.profile
  76. 117 0
      services/clsi/test/acceptance/fixtures/examples/asymptote/main.tex
  77. BIN
      services/clsi/test/acceptance/fixtures/examples/asymptote/output.pdf
  78. 9 0
      services/clsi/test/acceptance/fixtures/examples/biber_bibliography/bibliography.bib
  79. 12 0
      services/clsi/test/acceptance/fixtures/examples/biber_bibliography/main.tex
  80. 48 0
      services/clsi/test/acceptance/fixtures/examples/biber_bibliography/output.bbl
  81. BIN
      services/clsi/test/acceptance/fixtures/examples/biber_bibliography/output.pdf
  82. 84 0
      services/clsi/test/acceptance/fixtures/examples/biber_bibliography/output.run.xml
  83. BIN
      services/clsi/test/acceptance/fixtures/examples/epstopdf/image-eps-converted-to.pdf
  84. 6673 0
      services/clsi/test/acceptance/fixtures/examples/epstopdf/image.eps
  85. 10 0
      services/clsi/test/acceptance/fixtures/examples/epstopdf/main.tex
  86. BIN
      services/clsi/test/acceptance/fixtures/examples/epstopdf/output.pdf
  87. 28 0
      services/clsi/test/acceptance/fixtures/examples/feynmf/main.tex
  88. BIN
      services/clsi/test/acceptance/fixtures/examples/feynmf/output.pdf
  89. 28 0
      services/clsi/test/acceptance/fixtures/examples/feynmp/main.tex
  90. 3 0
      services/clsi/test/acceptance/fixtures/examples/feynmp/options.json
  91. BIN
      services/clsi/test/acceptance/fixtures/examples/feynmp/output.pdf
  92. 12 0
      services/clsi/test/acceptance/fixtures/examples/fontawesome/main.tex
  93. BIN
      services/clsi/test/acceptance/fixtures/examples/fontawesome/output.pdf
  94. 16 0
      services/clsi/test/acceptance/fixtures/examples/fontawesome_xelatex/main.tex
  95. 3 0
      services/clsi/test/acceptance/fixtures/examples/fontawesome_xelatex/options.json
  96. BIN
      services/clsi/test/acceptance/fixtures/examples/fontawesome_xelatex/output.pdf
  97. 17 0
      services/clsi/test/acceptance/fixtures/examples/glossaries/main.tex
  98. 7 0
      services/clsi/test/acceptance/fixtures/examples/glossaries/output.glg
  99. 1 0
      services/clsi/test/acceptance/fixtures/examples/glossaries/output.glo
  100. 6 0
      services/clsi/test/acceptance/fixtures/examples/glossaries/output.gls

+ 11 - 0
services/clsi/.dockerignore

@@ -0,0 +1,11 @@
+node_modules/*
+gitrev
+.git
+.gitignore
+.npm
+.nvmrc
+nodemon.json
+cache/
+compiles/
+db/
+output/

+ 86 - 0
services/clsi/.eslintrc

@@ -0,0 +1,86 @@
+// this file was auto-generated, do not edit it directly.
+// instead run bin/update_build_scripts from
+// https://github.com/sharelatex/sharelatex-dev-environment
+{
+  "extends": [
+    "eslint:recommended",
+    "standard",
+    "prettier"
+  ],
+  "parserOptions": {
+    "ecmaVersion": 2018
+  },
+  "plugins": [
+    "mocha",
+    "chai-expect",
+    "chai-friendly"
+  ],
+  "env": {
+    "node": true,
+    "mocha": true
+  },
+  "rules": {
+    // TODO(das7pad): remove overrides after fixing all the violations manually (https://github.com/overleaf/issues/issues/3882#issuecomment-878999671)
+    // START of temporary overrides
+    "array-callback-return": "off",
+    "no-dupe-else-if": "off",
+    "no-var": "off",
+    "no-empty": "off",
+    "node/handle-callback-err": "off",
+    "no-loss-of-precision": "off",
+    "node/no-callback-literal": "off",
+    "node/no-path-concat": "off",
+    "prefer-regex-literals": "off",
+    // END of temporary overrides
+
+    // Swap the no-unused-expressions rule with a more chai-friendly one
+    "no-unused-expressions": 0,
+    "chai-friendly/no-unused-expressions": "error",
+
+    // Do not allow importing of implicit dependencies.
+    "import/no-extraneous-dependencies": "error"
+  },
+  "overrides": [
+    {
+      // Test specific rules
+      "files": ["test/**/*.js"],
+      "globals": {
+        "expect": true
+      },
+      "rules": {
+        // mocha-specific rules
+        "mocha/handle-done-callback": "error",
+        "mocha/no-exclusive-tests": "error",
+        "mocha/no-global-tests": "error",
+        "mocha/no-identical-title": "error",
+        "mocha/no-nested-tests": "error",
+        "mocha/no-pending-tests": "error",
+        "mocha/no-skipped-tests": "error",
+        "mocha/no-mocha-arrows": "error",
+
+        // chai-specific rules
+        "chai-expect/missing-assertion": "error",
+        "chai-expect/terminating-properties": "error",
+
+        // prefer-arrow-callback applies to all callbacks, not just ones in mocha tests.
+        // we don't enforce this at the top-level - just in tests to manage `this` scope
+        // based on mocha's context mechanism
+        "mocha/prefer-arrow-callback": "error"
+      }
+    },
+    {
+      // Backend specific rules
+      "files": ["app/**/*.js", "app.js", "index.js"],
+      "rules": {
+        // don't allow console.log in backend code
+        "no-console": "error",
+
+        // Do not allow importing of implicit dependencies.
+        "import/no-extraneous-dependencies": ["error", {
+          // Do not allow importing of devDependencies.
+          "devDependencies": false
+        }]
+      }
+    }
+  ]
+}

+ 38 - 0
services/clsi/.github/ISSUE_TEMPLATE.md

@@ -0,0 +1,38 @@
+<!-- BUG REPORT TEMPLATE -->
+
+## Steps to Reproduce
+<!-- Describe the steps leading up to when / where you found the bug. -->
+<!-- Screenshots may be helpful here. -->
+
+1.
+2.
+3.
+
+## Expected Behaviour
+<!-- What should have happened when you completed the steps above? -->
+
+## Observed Behaviour
+<!-- What actually happened when you completed the steps above? -->
+<!-- Screenshots may be helpful here. -->
+
+## Context
+<!-- How has this issue affected you? What were you trying to accomplish? -->
+
+## Technical Info
+<!-- Provide any technical details that may be applicable (or N/A if not applicable). -->
+
+* URL:
+* Browser Name and version:
+* Operating System and version (desktop or mobile):
+* Signed in as:
+* Project and/or file:
+
+## Analysis
+<!--- Optionally, document investigation of / suggest a fix for the bug, e.g. 'comes from this line / commit' -->
+
+## Who Needs to Know?
+<!-- If you want to bring this to the attention of particular people, @-mention them below. -->
+<!-- If a user reported this bug and should be notified when it is fixed, provide the Front conversation link. -->
+
+-
+-

+ 48 - 0
services/clsi/.github/PULL_REQUEST_TEMPLATE.md

@@ -0,0 +1,48 @@
+
+<!-- ** This is an Overleaf public repository ** -->
+
+<!-- Please review https://github.com/overleaf/overleaf/blob/master/CONTRIBUTING.md for guidance on what is expected of a contribution. -->
+
+### Description
+
+
+
+#### Screenshots
+
+
+
+#### Related Issues / PRs
+
+
+
+### Review
+
+
+
+#### Potential Impact
+
+
+
+#### Manual Testing Performed
+
+- [ ]
+- [ ]
+
+#### Accessibility
+
+
+
+### Deployment
+
+
+
+#### Deployment Checklist
+
+- [ ] Update documentation not included in the PR (if any)
+- [ ]
+
+#### Metrics and Monitoring
+
+
+
+#### Who Needs to Know?

+ 23 - 0
services/clsi/.github/dependabot.yml

@@ -0,0 +1,23 @@
+version: 2
+updates:
+  - package-ecosystem: "npm"
+    directory: "/"
+    schedule:
+      interval: "daily"
+
+    pull-request-branch-name:
+      # Separate sections of the branch name with a hyphen
+      # Docker images use the branch name and do not support slashes in tags
+      # https://github.com/overleaf/google-ops/issues/822
+      # https://docs.github.com/en/github/administering-a-repository/configuration-options-for-dependency-updates#pull-request-branch-nameseparator
+      separator: "-"
+
+    # Block informal upgrades -- security upgrades use a separate queue.
+    # https://docs.github.com/en/github/administering-a-repository/configuration-options-for-dependency-updates#open-pull-requests-limit
+    open-pull-requests-limit: 0
+
+    # currently assign team-magma to all dependabot PRs - this may change in
+    # future if we reorganise teams
+    labels:
+      - "dependencies"
+      - "type:maintenance"

+ 17 - 0
services/clsi/.gitignore

@@ -0,0 +1,17 @@
+**.swp
+node_modules
+test/acceptance/fixtures/tmp
+compiles
+output
+.DS_Store
+*~
+cache
+.vagrant
+db.sqlite
+db.sqlite-wal
+db.sqlite-shm
+config/*
+npm-debug.log
+
+# managed by dev-environment$ bin/update_build_scripts
+.npmrc

+ 3 - 0
services/clsi/.mocharc.json

@@ -0,0 +1,3 @@
+{
+  "require": "test/setup.js"
+}

+ 1 - 0
services/clsi/.nvmrc

@@ -0,0 +1 @@
+12.22.3

+ 11 - 0
services/clsi/.prettierrc

@@ -0,0 +1,11 @@
+# This file was auto-generated, do not edit it directly.
+# Instead run bin/update_build_scripts from
+# https://github.com/sharelatex/sharelatex-dev-environment
+{
+  "arrowParens": "avoid",
+  "semi": false,
+  "singleQuote": true,
+  "trailingComma": "es5",
+  "tabWidth": 2,
+  "useTabs": false
+}

+ 35 - 0
services/clsi/.viminfo

@@ -0,0 +1,35 @@
+# This viminfo file was generated by Vim 7.4.
+# You may edit it if you're careful!
+
+# Value of 'encoding' when this file was written
+*encoding=latin1
+
+
+# hlsearch on (H) or off (h):
+~h
+# Command Line History (newest to oldest):
+:x
+
+# Search String History (newest to oldest):
+
+# Expression History (newest to oldest):
+
+# Input Line History (newest to oldest):
+
+# Input Line History (newest to oldest):
+
+# Registers:
+
+# File marks:
+'0  1  0  ~/hello
+
+# Jumplist (newest first):
+-'  1  0  ~/hello
+
+# History of marks within files (newest to oldest):
+
+> ~/hello
+	"	1	0
+	^	1	1
+	.	1	0
+	+	1	0

+ 28 - 0
services/clsi/Dockerfile

@@ -0,0 +1,28 @@
+# This file was auto-generated, do not edit it directly.
+# Instead run bin/update_build_scripts from
+# https://github.com/sharelatex/sharelatex-dev-environment
+
+FROM node:12.22.3 as base
+
+WORKDIR /app
+COPY install_deps.sh /app
+RUN chmod 0755 ./install_deps.sh && ./install_deps.sh
+ENTRYPOINT ["/bin/sh", "entrypoint.sh"]
+COPY entrypoint.sh /app
+
+FROM base as app
+
+#wildcard as some files may not be in all repos
+COPY package*.json npm-shrink*.json /app/
+
+RUN npm ci --quiet
+
+COPY . /app
+
+FROM base
+
+COPY --from=app /app /app
+RUN mkdir -p cache compiles db output \
+&&  chown node:node cache compiles db output
+
+CMD ["node", "--expose-gc", "app.js"]

+ 661 - 0
services/clsi/LICENSE

@@ -0,0 +1,661 @@
+                    GNU AFFERO GENERAL PUBLIC LICENSE
+                       Version 3, 19 November 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU Affero General Public License is a free, copyleft license for
+software and other kinds of works, specifically designed to ensure
+cooperation with the community in the case of network server software.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+our General Public Licenses are intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  Developers that use our General Public Licenses protect your rights
+with two steps: (1) assert copyright on the software, and (2) offer
+you this License which gives you legal permission to copy, distribute
+and/or modify the software.
+
+  A secondary benefit of defending all users' freedom is that
+improvements made in alternate versions of the program, if they
+receive widespread use, become available for other developers to
+incorporate.  Many developers of free software are heartened and
+encouraged by the resulting cooperation.  However, in the case of
+software used on network servers, this result may fail to come about.
+The GNU General Public License permits making a modified version and
+letting the public access it on a server without ever releasing its
+source code to the public.
+
+  The GNU Affero General Public License is designed specifically to
+ensure that, in such cases, the modified source code becomes available
+to the community.  It requires the operator of a network server to
+provide the source code of the modified version running there to the
+users of that server.  Therefore, public use of a modified version, on
+a publicly accessible server, gives the public access to the source
+code of the modified version.
+
+  An older license, called the Affero General Public License and
+published by Affero, was designed to accomplish similar goals.  This is
+a different license, not a version of the Affero GPL, but Affero has
+released a new version of the Affero GPL which permits relicensing under
+this license.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU Affero General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Remote Network Interaction; Use with the GNU General Public License.
+
+  Notwithstanding any other provision of this License, if you modify the
+Program, your modified version must prominently offer all users
+interacting with it remotely through a computer network (if your version
+supports such interaction) an opportunity to receive the Corresponding
+Source of your version by providing access to the Corresponding Source
+from a network server at no charge, through some standard or customary
+means of facilitating copying of software.  This Corresponding Source
+shall include the Corresponding Source for any work covered by version 3
+of the GNU General Public License that is incorporated pursuant to the
+following paragraph.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the work with which it is combined will remain governed by version
+3 of the GNU General Public License.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU Affero General Public License from time to time.  Such new versions
+will be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU Affero General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU Affero General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU Affero General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If your software can interact with users remotely through a computer
+network, you should also make sure that it provides a way for users to
+get its source.  For example, if your program is a web application, its
+interface could display a "Source" link that leads users to an archive
+of the code.  There are many ways you could offer source, and different
+solutions will be better for different programs; see section 13 for the
+specific requirements.
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU AGPL, see
+<http://www.gnu.org/licenses/>.

+ 90 - 0
services/clsi/Makefile

@@ -0,0 +1,90 @@
+# This file was auto-generated, do not edit it directly.
+# Instead run bin/update_build_scripts from
+# https://github.com/sharelatex/sharelatex-dev-environment
+
+BUILD_NUMBER ?= local
+BRANCH_NAME ?= $(shell git rev-parse --abbrev-ref HEAD)
+PROJECT_NAME = clsi
+BUILD_DIR_NAME = $(shell pwd | xargs basename | tr -cd '[a-zA-Z0-9_.\-]')
+
+DOCKER_COMPOSE_FLAGS ?= -f docker-compose.yml
+DOCKER_COMPOSE := BUILD_NUMBER=$(BUILD_NUMBER) \
+	BRANCH_NAME=$(BRANCH_NAME) \
+	PROJECT_NAME=$(PROJECT_NAME) \
+	MOCHA_GREP=${MOCHA_GREP} \
+	docker-compose ${DOCKER_COMPOSE_FLAGS}
+
+DOCKER_COMPOSE_TEST_ACCEPTANCE = \
+	COMPOSE_PROJECT_NAME=test_acceptance_$(BUILD_DIR_NAME) $(DOCKER_COMPOSE)
+
+DOCKER_COMPOSE_TEST_UNIT = \
+	COMPOSE_PROJECT_NAME=test_unit_$(BUILD_DIR_NAME) $(DOCKER_COMPOSE)
+
+clean:
+	-docker rmi ci/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER)
+	-docker rmi gcr.io/overleaf-ops/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER)
+	-$(DOCKER_COMPOSE_TEST_UNIT) down --rmi local
+	-$(DOCKER_COMPOSE_TEST_ACCEPTANCE) down --rmi local
+
+format:
+	$(DOCKER_COMPOSE) run --rm test_unit npm run --silent format
+
+format_fix:
+	$(DOCKER_COMPOSE) run --rm test_unit npm run --silent format:fix
+
+lint:
+	$(DOCKER_COMPOSE) run --rm test_unit npm run --silent lint
+
+test: format lint test_unit test_acceptance
+
+test_unit:
+ifneq (,$(wildcard test/unit))
+	$(DOCKER_COMPOSE_TEST_UNIT) run --rm test_unit
+	$(MAKE) test_unit_clean
+endif
+
+test_clean: test_unit_clean
+test_unit_clean:
+ifneq (,$(wildcard test/unit))
+	$(DOCKER_COMPOSE_TEST_UNIT) down -v -t 0
+endif
+
+test_acceptance: test_acceptance_clean test_acceptance_pre_run test_acceptance_run
+	$(MAKE) test_acceptance_clean
+
+test_acceptance_debug: test_acceptance_clean test_acceptance_pre_run test_acceptance_run_debug
+	$(MAKE) test_acceptance_clean
+
+test_acceptance_run:
+ifneq (,$(wildcard test/acceptance))
+	$(DOCKER_COMPOSE_TEST_ACCEPTANCE) run --rm test_acceptance
+endif
+
+test_acceptance_run_debug:
+ifneq (,$(wildcard test/acceptance))
+	$(DOCKER_COMPOSE_TEST_ACCEPTANCE) run -p 127.0.0.9:19999:19999 --rm test_acceptance npm run test:acceptance -- --inspect=0.0.0.0:19999 --inspect-brk
+endif
+
+test_clean: test_acceptance_clean
+test_acceptance_clean:
+	$(DOCKER_COMPOSE_TEST_ACCEPTANCE) down -v -t 0
+
+test_acceptance_pre_run:
+ifneq (,$(wildcard test/acceptance/js/scripts/pre-run))
+	$(DOCKER_COMPOSE_TEST_ACCEPTANCE) run --rm test_acceptance test/acceptance/js/scripts/pre-run
+endif
+
+build:
+	docker build --pull --tag ci/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) \
+		--tag gcr.io/overleaf-ops/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) \
+		.
+
+tar:
+	$(DOCKER_COMPOSE) up tar
+
+publish:
+
+	docker push $(DOCKER_REPO)/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER)
+
+
+.PHONY: clean test test_unit test_acceptance test_clean build publish

+ 184 - 0
services/clsi/README.md

@@ -0,0 +1,184 @@
+overleaf/clsi
+===============
+
+A web api for compiling LaTeX documents in the cloud
+
+The Common LaTeX Service Interface (CLSI) provides a RESTful interface to traditional LaTeX tools (or, more generally, any command line tool for composing marked-up documents into a display format such as PDF or HTML). The CLSI listens on the following ports by default:
+
+* TCP/3013 - the RESTful interface
+* TCP/3048 - reports load information
+* TCP/3049 - HTTP interface to control the CLSI service
+
+These defaults can be modified in `config/settings.defaults.js`.
+
+The provided `Dockerfile` builds a Docker image which has the Docker command line tools installed. The configuration in `docker-compose-config.yml` mounts the Docker socket, in order that the CLSI container can talk to the Docker host it is running in. This allows it to spin up `sibling containers` running an image with a TeX distribution installed to perform the actual compiles.
+
+The CLSI can be configured through the following environment variables:
+
+* `ALLOWED_COMPILE_GROUPS` - Space separated list of allowed compile groups
+* `ALLOWED_IMAGES` - Space separated list of allowed Docker TeX Live images
+* `CATCH_ERRORS` - Set to `true` to log uncaught exceptions
+* `COMPILE_GROUP_DOCKER_CONFIGS` - JSON string of Docker configs for compile groups
+* `COMPILES_HOST_DIR` - Working directory for LaTeX compiles
+* `COMPILE_SIZE_LIMIT` - Sets the body-parser [limit](https://github.com/expressjs/body-parser#limit)
+* `DOCKER_RUNNER` - Set to true to use sibling containers
+* `DOCKER_RUNTIME` -
+* `FILESTORE_DOMAIN_OVERRIDE` - The url for the filestore service e.g.`http://$FILESTORE_HOST:3009`
+* `FILESTORE_PARALLEL_FILE_DOWNLOADS` - Number of parallel file downloads
+* `FILESTORE_PARALLEL_SQL_QUERY_LIMIT` - Number of parallel SQL queries
+* `LISTEN_ADDRESS` - The address for the RESTful service to listen on. Set to `0.0.0.0` to listen on all network interfaces
+* `PROCESS_LIFE_SPAN_LIMIT_MS` - Process life span limit in milliseconds
+* `SENTRY_DSN` - Sentry [Data Source Name](https://docs.sentry.io/product/sentry-basics/dsn-explainer/)
+* `SMOKE_TEST` - Whether to run smoke tests
+* `SQLITE_PATH` - Path to SQLite database
+* `SYNCTEX_BIN_HOST_PATH` - Path to SyncTeX binary
+* `TEXLIVE_IMAGE` - The TeX Live Docker image to use for sibling containers, e.g. `gcr.io/overleaf-ops/texlive-full:2017.1`
+* `TEX_LIVE_IMAGE_NAME_OVERRIDE` - The name of the registry for the Docker image e.g. `gcr.io/overleaf-ops`
+* `TEXLIVE_IMAGE_USER` - When using sibling containers, the user to run as in the TeX Live image. Defaults to `tex`
+* `TEXLIVE_OPENOUT_ANY` - Sets the `openout_any` environment variable for TeX Live (see the `\openout` primitive [documentation](http://tug.org/texinfohtml/web2c.html#tex-invocation))
+
+Further environment variables configure the [metrics module](https://github.com/overleaf/metrics-module)
+
+Installation
+------------
+
+The CLSI can be installed and set up as part of the entire [Overleaf stack](https://github.com/overleaf/overleaf) (complete with front end editor and document storage), or it can be run as a standalone service. To run is as a standalone service, first checkout this repository:
+
+    $ git clone git@github.com:overleaf/clsi.git
+
+Then build the Docker image:
+
+    $ docker build . -t overleaf/clsi
+
+Then pull the TeX Live image:
+
+    $ docker pull texlive/texlive
+
+Then start the Docker container:
+
+    $ docker run --rm \
+        -p 127.0.0.1:3013:3013 \
+        -e LISTEN_ADDRESS=0.0.0.0 \
+        -e DOCKER_RUNNER=true \
+        -e TEXLIVE_IMAGE=texlive/texlive \
+        -e TEXLIVE_IMAGE_USER=root \
+        -e COMPILES_HOST_DIR="$PWD/compiles" \
+        -v "$PWD/compiles:/app/compiles" \
+        -v "$PWD/cache:/app/cache" \
+        -v /var/run/docker.sock:/var/run/docker.sock \
+        --name clsi \
+        overleaf/clsi
+
+Note: if you're running the CLSI in macOS you may need to use `-v /var/run/docker.sock.raw:/var/run/docker.sock` instead.
+
+The CLSI should then be running at <http://localhost:3013>
+
+Important note for Linux users
+==============================
+
+The Node application runs as user `node` in the CLSI, which has uid `1000`. As a consequence of this, the `compiles` folder gets created on your host with `uid` and `gid` set to `1000`.
+```
+ls -lnd compiles
+drwxr-xr-x 2 1000 1000 4096 Mar 19 12:41 compiles
+```
+
+If there is a user/group on your host which also happens to have `uid` / `gid` `1000` then that user/group will have ownership of the compiles folder on your host.
+
+LaTeX runs in the sibling containers as the user specified in the `TEXLIVE_IMAGE_USER` environment variable. In the example above this is set to `root`, which has uid `0`. This creates a problem with the above permissions, as the root user does not have permission to write to subfolders of `compiles`.
+
+A quick fix is to give the `root` group ownership and read write permissions to `compiles`, with `setgid` set so that new subfolders also inherit this ownership:
+```
+sudo chown -R 1000:root compiles
+sudo chmod -R g+w compiles
+sudo chmod g+s compiles
+```
+Another solution is to create a `sharelatex` group and add both `root` and the user with `uid` `1000` to it. If the host does not have a user with that `uid`, you will need to create one first.
+```
+sudo useradd --uid 1000 host-node-user # If required
+sudo groupadd sharelatex
+sudo usermod -a -G sharelatex root
+sudo usermod -a -G sharelatex $(id -nu 1000)
+sudo chown -R 1000:sharelatex compiles
+sudo chmod -R g+w compiles
+sudo chmod g+s compiles
+```
+
+This is a facet of the way docker works on Linux. See this [upstream issue](https://github.com/moby/moby/issues/7198)
+
+
+Config
+------
+
+The CLSI will use a SQLite database by default, but you can optionally set up a MySQL database and then fill in the database name, username and password in the config file at `config/settings.development.js`.
+
+API
+---
+
+The CLSI is based on a JSON API.
+
+#### Example Request
+
+(Note that valid JSON should not contain any comments like the example below).
+
+    POST /project/<project-id>/compile
+
+```json5
+{
+    "compile": {
+        "options": {
+            // Which compiler to use. Can be latex, pdflatex, xelatex or lualatex
+            "compiler": "lualatex",
+            // How many seconds to wait before killing the process. Default is 60.
+            "timeout": 40
+        },
+        // The main file to run LaTeX on
+        "rootResourcePath": "main.tex",
+        // An array of files to include in the compilation. May have either the content
+        // passed directly, or a URL where it can be downloaded.
+        "resources": [
+          {
+            "path": "main.tex",
+            "content": "\\documentclass{article}\n\\begin{document}\nHello World\n\\end{document}"
+          }
+          // ,{
+          //     "path": "image.png",
+          //     "url": "www.example.com/image.png",
+          //     "modified": 123456789 // Unix time since epoch
+          // }
+        ]
+    }
+}
+```
+
+With `curl`, if you place the above JSON in a file called `data.json`, the request would look like this:
+
+``` shell
+$ curl -X POST -H 'Content-Type: application/json' -d @data.json http://localhost:3013/project/<id>/compile
+```
+
+You can specify any project-id in the URL, and the files and LaTeX environment will be persisted between requests.
+URLs will be downloaded and cached until provided with a more recent modified date.
+
+#### Example Response
+
+```json
+{
+    "compile": {
+        "status": "success",
+        "outputFiles": [{
+            "type": "pdf",
+            "url": "http://localhost:3013/project/<project-id>/output/output.pdf"
+        }, {
+            "type": "log",
+            "url": "http://localhost:3013/project/<project-id>/output/output.log"
+        }]
+    }
+}
+```
+
+License
+-------
+
+The code in this repository is released under the GNU AFFERO GENERAL PUBLIC LICENSE, version 3. A copy can be found in the `LICENSE` file.
+
+Copyright (c) Overleaf, 2014-2021.

+ 425 - 0
services/clsi/app.js

@@ -0,0 +1,425 @@
+/*
+ * decaffeinate suggestions:
+ * DS102: Remove unnecessary code created because of implicit returns
+ * DS103: Rewrite code to no longer use __guard__
+ * DS207: Consider shorter variations of null checks
+ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
+ */
+const tenMinutes = 10 * 60 * 1000
+const Metrics = require('@overleaf/metrics')
+Metrics.initialize('clsi')
+
+const CompileController = require('./app/js/CompileController')
+const ContentController = require('./app/js/ContentController')
+const Settings = require('@overleaf/settings')
+const logger = require('logger-sharelatex')
+logger.initialize('clsi')
+if ((Settings.sentry != null ? Settings.sentry.dsn : undefined) != null) {
+  logger.initializeErrorReporting(Settings.sentry.dsn)
+}
+
+const smokeTest = require('./test/smoke/js/SmokeTests')
+const ContentTypeMapper = require('./app/js/ContentTypeMapper')
+const Errors = require('./app/js/Errors')
+
+const Path = require('path')
+
+Metrics.open_sockets.monitor(logger)
+Metrics.memory.monitor(logger)
+
+const ProjectPersistenceManager = require('./app/js/ProjectPersistenceManager')
+const OutputCacheManager = require('./app/js/OutputCacheManager')
+const ContentCacheManager = require('./app/js/ContentCacheManager')
+
+require('./app/js/db').sync()
+
+const express = require('express')
+const bodyParser = require('body-parser')
+const app = express()
+
+Metrics.injectMetricsRoute(app)
+app.use(Metrics.http.monitor(logger))
+
+// Compile requests can take longer than the default two
+// minutes (including file download time), so bump up the
+// timeout a bit.
+const TIMEOUT = 10 * 60 * 1000
+app.use(function (req, res, next) {
+  req.setTimeout(TIMEOUT)
+  res.setTimeout(TIMEOUT)
+  res.removeHeader('X-Powered-By')
+  return next()
+})
+
+app.param('project_id', function (req, res, next, projectId) {
+  if (projectId != null ? projectId.match(/^[a-zA-Z0-9_-]+$/) : undefined) {
+    return next()
+  } else {
+    return next(new Error('invalid project id'))
+  }
+})
+
+app.param('user_id', function (req, res, next, userId) {
+  if (userId != null ? userId.match(/^[0-9a-f]{24}$/) : undefined) {
+    return next()
+  } else {
+    return next(new Error('invalid user id'))
+  }
+})
+
+app.param('build_id', function (req, res, next, buildId) {
+  if (
+    buildId != null ? buildId.match(OutputCacheManager.BUILD_REGEX) : undefined
+  ) {
+    return next()
+  } else {
+    return next(new Error(`invalid build id ${buildId}`))
+  }
+})
+
+app.param('contentId', function (req, res, next, contentId) {
+  if (
+    contentId != null
+      ? contentId.match(OutputCacheManager.CONTENT_REGEX)
+      : undefined
+  ) {
+    return next()
+  } else {
+    return next(new Error(`invalid content id ${contentId}`))
+  }
+})
+
+app.param('hash', function (req, res, next, hash) {
+  if (hash != null ? hash.match(ContentCacheManager.HASH_REGEX) : undefined) {
+    return next()
+  } else {
+    return next(new Error(`invalid hash ${hash}`))
+  }
+})
+
+app.post(
+  '/project/:project_id/compile',
+  bodyParser.json({ limit: Settings.compileSizeLimit }),
+  CompileController.compile
+)
+app.post('/project/:project_id/compile/stop', CompileController.stopCompile)
+app.delete('/project/:project_id', CompileController.clearCache)
+
+app.get('/project/:project_id/sync/code', CompileController.syncFromCode)
+app.get('/project/:project_id/sync/pdf', CompileController.syncFromPdf)
+app.get('/project/:project_id/wordcount', CompileController.wordcount)
+app.get('/project/:project_id/status', CompileController.status)
+app.post('/project/:project_id/status', CompileController.status)
+
+// Per-user containers
+app.post(
+  '/project/:project_id/user/:user_id/compile',
+  bodyParser.json({ limit: Settings.compileSizeLimit }),
+  CompileController.compile
+)
+app.post(
+  '/project/:project_id/user/:user_id/compile/stop',
+  CompileController.stopCompile
+)
+app.delete('/project/:project_id/user/:user_id', CompileController.clearCache)
+
+app.get(
+  '/project/:project_id/user/:user_id/sync/code',
+  CompileController.syncFromCode
+)
+app.get(
+  '/project/:project_id/user/:user_id/sync/pdf',
+  CompileController.syncFromPdf
+)
+app.get(
+  '/project/:project_id/user/:user_id/wordcount',
+  CompileController.wordcount
+)
+
+const ForbidSymlinks = require('./app/js/StaticServerForbidSymlinks')
+
+// create a static server which does not allow access to any symlinks
+// avoids possible mismatch of root directory between middleware check
+// and serving the files
+const staticCompileServer = ForbidSymlinks(
+  express.static,
+  Settings.path.compilesDir,
+  {
+    setHeaders(res, path, stat) {
+      if (Path.basename(path) === 'output.pdf') {
+        // Calculate an etag in the same way as nginx
+        // https://github.com/tj/send/issues/65
+        const etag = (path, stat) =>
+          `"${Math.ceil(+stat.mtime / 1000).toString(16)}` +
+          '-' +
+          Number(stat.size).toString(16) +
+          '"'
+        res.set('Etag', etag(path, stat))
+      }
+      return res.set('Content-Type', ContentTypeMapper.map(path))
+    },
+  }
+)
+
+const staticOutputServer = ForbidSymlinks(
+  express.static,
+  Settings.path.outputDir,
+  {
+    setHeaders(res, path, stat) {
+      if (Path.basename(path) === 'output.pdf') {
+        // Calculate an etag in the same way as nginx
+        // https://github.com/tj/send/issues/65
+        const etag = (path, stat) =>
+          `"${Math.ceil(+stat.mtime / 1000).toString(16)}` +
+          '-' +
+          Number(stat.size).toString(16) +
+          '"'
+        res.set('Etag', etag(path, stat))
+      }
+      return res.set('Content-Type', ContentTypeMapper.map(path))
+    },
+  }
+)
+
+app.get(
+  '/project/:project_id/user/:user_id/build/:build_id/output/*',
+  function (req, res, next) {
+    // for specific build get the path from the OutputCacheManager (e.g. .clsi/buildId)
+    req.url =
+      `/${req.params.project_id}-${req.params.user_id}/` +
+      OutputCacheManager.path(req.params.build_id, `/${req.params[0]}`)
+    return staticOutputServer(req, res, next)
+  }
+)
+
+app.get(
+  '/project/:projectId/content/:contentId/:hash',
+  ContentController.getPdfRange
+)
+app.get(
+  '/project/:projectId/user/:userId/content/:contentId/:hash',
+  ContentController.getPdfRange
+)
+
+app.get(
+  '/project/:project_id/build/:build_id/output/*',
+  function (req, res, next) {
+    // for specific build get the path from the OutputCacheManager (e.g. .clsi/buildId)
+    req.url =
+      `/${req.params.project_id}/` +
+      OutputCacheManager.path(req.params.build_id, `/${req.params[0]}`)
+    return staticOutputServer(req, res, next)
+  }
+)
+
+app.get(
+  '/project/:project_id/user/:user_id/output/*',
+  function (req, res, next) {
+    // for specific user get the path to the top level file
+    logger.warn(
+      { url: req.url },
+      'direct request for file in compile directory'
+    )
+    req.url = `/${req.params.project_id}-${req.params.user_id}/${req.params[0]}`
+    return staticCompileServer(req, res, next)
+  }
+)
+
+app.get('/project/:project_id/output/*', function (req, res, next) {
+  logger.warn({ url: req.url }, 'direct request for file in compile directory')
+  if (
+    (req.query != null ? req.query.build : undefined) != null &&
+    req.query.build.match(OutputCacheManager.BUILD_REGEX)
+  ) {
+    // for specific build get the path from the OutputCacheManager (e.g. .clsi/buildId)
+    req.url =
+      `/${req.params.project_id}/` +
+      OutputCacheManager.path(req.query.build, `/${req.params[0]}`)
+  } else {
+    req.url = `/${req.params.project_id}/${req.params[0]}`
+  }
+  return staticCompileServer(req, res, next)
+})
+
+app.get('/oops', function (req, res, next) {
+  logger.error({ err: 'hello' }, 'test error')
+  return res.send('error\n')
+})
+
+app.get('/oops-internal', function (req, res, next) {
+  setTimeout(function () {
+    throw new Error('Test error')
+  }, 1)
+})
+
+app.get('/status', (req, res, next) => res.send('CLSI is alive\n'))
+
+Settings.processTooOld = false
+if (Settings.processLifespanLimitMs) {
+  Settings.processLifespanLimitMs +=
+    Settings.processLifespanLimitMs * (Math.random() / 10)
+  logger.info(
+    'Lifespan limited to ',
+    Date.now() + Settings.processLifespanLimitMs
+  )
+
+  setTimeout(() => {
+    logger.log('shutting down, process is too old')
+    Settings.processTooOld = true
+  }, Settings.processLifespanLimitMs)
+}
+
+function runSmokeTest() {
+  if (Settings.processTooOld) return
+  logger.log('running smoke tests')
+  smokeTest.triggerRun(err => {
+    if (err) logger.error({ err }, 'smoke tests failed')
+    setTimeout(runSmokeTest, 30 * 1000)
+  })
+}
+if (Settings.smokeTest) {
+  runSmokeTest()
+}
+
+app.get('/health_check', function (req, res) {
+  if (Settings.processTooOld) {
+    return res.status(500).json({ processTooOld: true })
+  }
+  smokeTest.sendLastResult(res)
+})
+
+app.get('/smoke_test_force', (req, res) => smokeTest.sendNewResult(res))
+
+app.use(function (error, req, res, next) {
+  if (error instanceof Errors.NotFoundError) {
+    logger.log({ err: error, url: req.url }, 'not found error')
+    return res.sendStatus(404)
+  } else if (error.code === 'EPIPE') {
+    // inspect container returns EPIPE when shutting down
+    return res.sendStatus(503) // send 503 Unavailable response
+  } else {
+    logger.error({ err: error, url: req.url }, 'server error')
+    return res.sendStatus((error != null ? error.statusCode : undefined) || 500)
+  }
+})
+
+const net = require('net')
+const os = require('os')
+
+let STATE = 'up'
+
+const loadTcpServer = net.createServer(function (socket) {
+  socket.on('error', function (err) {
+    if (err.code === 'ECONNRESET') {
+      // this always comes up, we don't know why
+      return
+    }
+    logger.err({ err }, 'error with socket on load check')
+    return socket.destroy()
+  })
+
+  if (STATE === 'up' && Settings.internal.load_balancer_agent.report_load) {
+    let availableWorkingCpus
+    const currentLoad = os.loadavg()[0]
+
+    // staging clis's have 1 cpu core only
+    if (os.cpus().length === 1) {
+      availableWorkingCpus = 1
+    } else {
+      availableWorkingCpus = os.cpus().length - 1
+    }
+
+    const freeLoad = availableWorkingCpus - currentLoad
+    let freeLoadPercentage = Math.round((freeLoad / availableWorkingCpus) * 100)
+    if (freeLoadPercentage <= 0) {
+      freeLoadPercentage = 1 // when its 0 the server is set to drain and will move projects to different servers
+    }
+    socket.write(`up, ${freeLoadPercentage}%\n`, 'ASCII')
+    return socket.end()
+  } else {
+    socket.write(`${STATE}\n`, 'ASCII')
+    return socket.end()
+  }
+})
+
+const loadHttpServer = express()
+
+loadHttpServer.post('/state/up', function (req, res, next) {
+  STATE = 'up'
+  logger.info('getting message to set server to down')
+  return res.sendStatus(204)
+})
+
+loadHttpServer.post('/state/down', function (req, res, next) {
+  STATE = 'down'
+  logger.info('getting message to set server to down')
+  return res.sendStatus(204)
+})
+
+loadHttpServer.post('/state/maint', function (req, res, next) {
+  STATE = 'maint'
+  logger.info('getting message to set server to maint')
+  return res.sendStatus(204)
+})
+
+const port =
+  __guard__(
+    Settings.internal != null ? Settings.internal.clsi : undefined,
+    x => x.port
+  ) || 3013
+const host =
+  __guard__(
+    Settings.internal != null ? Settings.internal.clsi : undefined,
+    x1 => x1.host
+  ) || 'localhost'
+
+const loadTcpPort = Settings.internal.load_balancer_agent.load_port
+const loadHttpPort = Settings.internal.load_balancer_agent.local_port
+
+if (!module.parent) {
+  // Called directly
+
+  // handle uncaught exceptions when running in production
+  if (Settings.catchErrors) {
+    process.removeAllListeners('uncaughtException')
+    process.on('uncaughtException', error =>
+      logger.error({ err: error }, 'uncaughtException')
+    )
+  }
+
+  app.listen(port, host, error => {
+    if (error) {
+      logger.fatal({ error }, `Error starting CLSI on ${host}:${port}`)
+    } else {
+      logger.info(`CLSI starting up, listening on ${host}:${port}`)
+    }
+  })
+
+  loadTcpServer.listen(loadTcpPort, host, function (error) {
+    if (error != null) {
+      throw error
+    }
+    return logger.info(`Load tcp agent listening on load port ${loadTcpPort}`)
+  })
+
+  loadHttpServer.listen(loadHttpPort, host, function (error) {
+    if (error != null) {
+      throw error
+    }
+    return logger.info(`Load http agent listening on load port ${loadHttpPort}`)
+  })
+}
+
+module.exports = app
+
+setInterval(() => {
+  ProjectPersistenceManager.refreshExpiryTimeout(() => {
+    ProjectPersistenceManager.clearExpiredProjects()
+  })
+}, tenMinutes)
+
+function __guard__(value, transform) {
+  return typeof value !== 'undefined' && value !== null
+    ? transform(value)
+    : undefined
+}

+ 20 - 0
services/clsi/app/js/CommandRunner.js

@@ -0,0 +1,20 @@
+// TODO: This file was created by bulk-decaffeinate.
+// Sanity-check the conversion and remove this comment.
+/*
+ * decaffeinate suggestions:
+ * DS207: Consider shorter variations of null checks
+ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
+ */
+let commandRunnerPath
+const Settings = require('@overleaf/settings')
+const logger = require('logger-sharelatex')
+
+if ((Settings.clsi != null ? Settings.clsi.dockerRunner : undefined) === true) {
+  commandRunnerPath = './DockerRunner'
+} else {
+  commandRunnerPath = './LocalCommandRunner'
+}
+logger.info({ commandRunnerPath }, 'selecting command runner for clsi')
+const CommandRunner = require(commandRunnerPath)
+
+module.exports = CommandRunner

+ 269 - 0
services/clsi/app/js/CompileController.js

@@ -0,0 +1,269 @@
+/* eslint-disable
+    camelcase,
+    handle-callback-err,
+    no-unused-vars,
+*/
+// TODO: This file was created by bulk-decaffeinate.
+// Fix any style issues and re-enable lint.
+/*
+ * decaffeinate suggestions:
+ * DS101: Remove unnecessary use of Array.from
+ * DS102: Remove unnecessary code created because of implicit returns
+ * DS207: Consider shorter variations of null checks
+ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
+ */
+let CompileController
+const RequestParser = require('./RequestParser')
+const CompileManager = require('./CompileManager')
+const Settings = require('@overleaf/settings')
+const Metrics = require('./Metrics')
+const ProjectPersistenceManager = require('./ProjectPersistenceManager')
+const logger = require('logger-sharelatex')
+const Errors = require('./Errors')
+
+function isImageNameAllowed(imageName) {
+  const ALLOWED_IMAGES =
+    Settings.clsi && Settings.clsi.docker && Settings.clsi.docker.allowedImages
+  return !ALLOWED_IMAGES || ALLOWED_IMAGES.includes(imageName)
+}
+
+module.exports = CompileController = {
+  compile(req, res, next) {
+    if (next == null) {
+      next = function (error) {}
+    }
+    const timer = new Metrics.Timer('compile-request')
+    return RequestParser.parse(req.body, function (error, request) {
+      if (error != null) {
+        return next(error)
+      }
+      request.project_id = req.params.project_id
+      if (req.params.user_id != null) {
+        request.user_id = req.params.user_id
+      }
+      return ProjectPersistenceManager.markProjectAsJustAccessed(
+        request.project_id,
+        function (error) {
+          if (error != null) {
+            return next(error)
+          }
+          return CompileManager.doCompileWithLock(
+            request,
+            function (error, outputFiles, stats, timings) {
+              let code, status
+              if (outputFiles == null) {
+                outputFiles = []
+              }
+              if (error instanceof Errors.AlreadyCompilingError) {
+                code = 423 // Http 423 Locked
+                status = 'compile-in-progress'
+              } else if (error instanceof Errors.FilesOutOfSyncError) {
+                code = 409 // Http 409 Conflict
+                status = 'retry'
+              } else if (error && error.code === 'EPIPE') {
+                // docker returns EPIPE when shutting down
+                code = 503 // send 503 Unavailable response
+                status = 'unavailable'
+              } else if (error != null ? error.terminated : undefined) {
+                status = 'terminated'
+              } else if (error != null ? error.validate : undefined) {
+                status = `validation-${error.validate}`
+              } else if (error != null ? error.timedout : undefined) {
+                status = 'timedout'
+                logger.log(
+                  { err: error, project_id: request.project_id },
+                  'timeout running compile'
+                )
+              } else if (error != null) {
+                status = 'error'
+                code = 500
+                logger.warn(
+                  { err: error, project_id: request.project_id },
+                  'error running compile'
+                )
+              } else {
+                let file
+                status = 'failure'
+                for (file of Array.from(outputFiles)) {
+                  if (file.path === 'output.pdf' && file.size > 0) {
+                    status = 'success'
+                  }
+                }
+
+                if (status === 'failure') {
+                  logger.warn(
+                    { project_id: request.project_id, outputFiles },
+                    'project failed to compile successfully, no output.pdf generated'
+                  )
+                }
+
+                // log an error if any core files are found
+                for (file of Array.from(outputFiles)) {
+                  if (file.path === 'core') {
+                    logger.error(
+                      { project_id: request.project_id, req, outputFiles },
+                      'core file found in output'
+                    )
+                  }
+                }
+              }
+
+              if (error != null) {
+                outputFiles = error.outputFiles || []
+              }
+
+              timer.done()
+              return res.status(code || 200).send({
+                compile: {
+                  status,
+                  error: (error != null ? error.message : undefined) || error,
+                  stats,
+                  timings,
+                  outputFiles: outputFiles.map(file => {
+                    return {
+                      url:
+                        `${Settings.apis.clsi.url}/project/${request.project_id}` +
+                        (request.user_id != null
+                          ? `/user/${request.user_id}`
+                          : '') +
+                        (file.build != null ? `/build/${file.build}` : '') +
+                        `/output/${file.path}`,
+                      ...file,
+                    }
+                  }),
+                },
+              })
+            }
+          )
+        }
+      )
+    })
+  },
+
+  stopCompile(req, res, next) {
+    const { project_id, user_id } = req.params
+    return CompileManager.stopCompile(project_id, user_id, function (error) {
+      if (error != null) {
+        return next(error)
+      }
+      return res.sendStatus(204)
+    })
+  },
+
+  clearCache(req, res, next) {
+    if (next == null) {
+      next = function (error) {}
+    }
+    return ProjectPersistenceManager.clearProject(
+      req.params.project_id,
+      req.params.user_id,
+      function (error) {
+        if (error != null) {
+          return next(error)
+        }
+        return res.sendStatus(204)
+      }
+    )
+  }, // No content
+
+  syncFromCode(req, res, next) {
+    if (next == null) {
+      next = function (error) {}
+    }
+    const { file } = req.query
+    const line = parseInt(req.query.line, 10)
+    const column = parseInt(req.query.column, 10)
+    const { imageName } = req.query
+    const { project_id } = req.params
+    const { user_id } = req.params
+
+    if (imageName && !isImageNameAllowed(imageName)) {
+      return res.status(400).send('invalid image')
+    }
+
+    return CompileManager.syncFromCode(
+      project_id,
+      user_id,
+      file,
+      line,
+      column,
+      imageName,
+      function (error, pdfPositions) {
+        if (error != null) {
+          return next(error)
+        }
+        return res.json({
+          pdf: pdfPositions,
+        })
+      }
+    )
+  },
+
+  syncFromPdf(req, res, next) {
+    if (next == null) {
+      next = function (error) {}
+    }
+    const page = parseInt(req.query.page, 10)
+    const h = parseFloat(req.query.h)
+    const v = parseFloat(req.query.v)
+    const { imageName } = req.query
+    const { project_id } = req.params
+    const { user_id } = req.params
+
+    if (imageName && !isImageNameAllowed(imageName)) {
+      return res.status(400).send('invalid image')
+    }
+    return CompileManager.syncFromPdf(
+      project_id,
+      user_id,
+      page,
+      h,
+      v,
+      imageName,
+      function (error, codePositions) {
+        if (error != null) {
+          return next(error)
+        }
+        return res.json({
+          code: codePositions,
+        })
+      }
+    )
+  },
+
+  wordcount(req, res, next) {
+    if (next == null) {
+      next = function (error) {}
+    }
+    const file = req.query.file || 'main.tex'
+    const { project_id } = req.params
+    const { user_id } = req.params
+    const { image } = req.query
+    if (image && !isImageNameAllowed(image)) {
+      return res.status(400).send('invalid image')
+    }
+    logger.log({ image, file, project_id }, 'word count request')
+
+    return CompileManager.wordcount(
+      project_id,
+      user_id,
+      file,
+      image,
+      function (error, result) {
+        if (error != null) {
+          return next(error)
+        }
+        return res.json({
+          texcount: result,
+        })
+      }
+    )
+  },
+
+  status(req, res, next) {
+    if (next == null) {
+      next = function (error) {}
+    }
+    return res.send('OK')
+  },
+}

+ 761 - 0
services/clsi/app/js/CompileManager.js

@@ -0,0 +1,761 @@
+/* eslint-disable
+    camelcase,
+    handle-callback-err,
+    no-return-assign,
+    no-undef,
+    no-unused-vars,
+*/
+// TODO: This file was created by bulk-decaffeinate.
+// Fix any style issues and re-enable lint.
+/*
+ * decaffeinate suggestions:
+ * DS101: Remove unnecessary use of Array.from
+ * DS102: Remove unnecessary code created because of implicit returns
+ * DS103: Rewrite code to no longer use __guard__
+ * DS207: Consider shorter variations of null checks
+ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
+ */
+let CompileManager
+const ResourceWriter = require('./ResourceWriter')
+const LatexRunner = require('./LatexRunner')
+const OutputFileFinder = require('./OutputFileFinder')
+const OutputCacheManager = require('./OutputCacheManager')
+const Settings = require('@overleaf/settings')
+const Path = require('path')
+const logger = require('logger-sharelatex')
+const Metrics = require('./Metrics')
+const child_process = require('child_process')
+const DraftModeManager = require('./DraftModeManager')
+const TikzManager = require('./TikzManager')
+const LockManager = require('./LockManager')
+const fs = require('fs')
+const fse = require('fs-extra')
+const os = require('os')
+const async = require('async')
+const Errors = require('./Errors')
+const CommandRunner = require('./CommandRunner')
+const { emitPdfStats } = require('./ContentCacheMetrics')
+
+const getCompileName = function (project_id, user_id) {
+  if (user_id != null) {
+    return `${project_id}-${user_id}`
+  } else {
+    return project_id
+  }
+}
+
+const getCompileDir = (project_id, user_id) =>
+  Path.join(Settings.path.compilesDir, getCompileName(project_id, user_id))
+
+const getOutputDir = (project_id, user_id) =>
+  Path.join(Settings.path.outputDir, getCompileName(project_id, user_id))
+
+module.exports = CompileManager = {
+  doCompileWithLock(request, callback) {
+    if (callback == null) {
+      callback = function (error, outputFiles) {}
+    }
+    const compileDir = getCompileDir(request.project_id, request.user_id)
+    const lockFile = Path.join(compileDir, '.project-lock')
+    // use a .project-lock file in the compile directory to prevent
+    // simultaneous compiles
+    return fse.ensureDir(compileDir, function (error) {
+      if (error != null) {
+        return callback(error)
+      }
+      return LockManager.runWithLock(
+        lockFile,
+        releaseLock => CompileManager.doCompile(request, releaseLock),
+        callback
+      )
+    })
+  },
+
+  doCompile(request, callback) {
+    if (callback == null) {
+      callback = function (error, outputFiles) {}
+    }
+    const compileDir = getCompileDir(request.project_id, request.user_id)
+    const outputDir = getOutputDir(request.project_id, request.user_id)
+
+    const timerE2E = new Metrics.Timer('compile-e2e')
+    let timer = new Metrics.Timer('write-to-disk')
+    logger.log(
+      { project_id: request.project_id, user_id: request.user_id },
+      'syncing resources to disk'
+    )
+    return ResourceWriter.syncResourcesToDisk(
+      request,
+      compileDir,
+      function (error, resourceList) {
+        // NOTE: resourceList is insecure, it should only be used to exclude files from the output list
+        if (error != null && error instanceof Errors.FilesOutOfSyncError) {
+          logger.warn(
+            { project_id: request.project_id, user_id: request.user_id },
+            'files out of sync, please retry'
+          )
+          return callback(error)
+        } else if (error != null) {
+          logger.err(
+            {
+              err: error,
+              project_id: request.project_id,
+              user_id: request.user_id,
+            },
+            'error writing resources to disk'
+          )
+          return callback(error)
+        }
+        logger.log(
+          {
+            project_id: request.project_id,
+            user_id: request.user_id,
+            time_taken: Date.now() - timer.start,
+          },
+          'written files to disk'
+        )
+        const syncStage = timer.done()
+
+        const injectDraftModeIfRequired = function (callback) {
+          if (request.draft) {
+            return DraftModeManager.injectDraftMode(
+              Path.join(compileDir, request.rootResourcePath),
+              callback
+            )
+          } else {
+            return callback()
+          }
+        }
+
+        const createTikzFileIfRequired = callback =>
+          TikzManager.checkMainFile(
+            compileDir,
+            request.rootResourcePath,
+            resourceList,
+            function (error, needsMainFile) {
+              if (error != null) {
+                return callback(error)
+              }
+              if (needsMainFile) {
+                return TikzManager.injectOutputFile(
+                  compileDir,
+                  request.rootResourcePath,
+                  callback
+                )
+              } else {
+                return callback()
+              }
+            }
+          )
+        // set up environment variables for chktex
+        const env = {}
+        if (Settings.texliveOpenoutAny && Settings.texliveOpenoutAny !== '') {
+          // override default texlive openout_any environment variable
+          env.openout_any = Settings.texliveOpenoutAny
+        }
+        // only run chktex on LaTeX files (not knitr .Rtex files or any others)
+        const isLaTeXFile =
+          request.rootResourcePath != null
+            ? request.rootResourcePath.match(/\.tex$/i)
+            : undefined
+        if (request.check != null && isLaTeXFile) {
+          env.CHKTEX_OPTIONS = '-nall -e9 -e10 -w15 -w16'
+          env.CHKTEX_ULIMIT_OPTIONS = '-t 5 -v 64000'
+          if (request.check === 'error') {
+            env.CHKTEX_EXIT_ON_ERROR = 1
+          }
+          if (request.check === 'validate') {
+            env.CHKTEX_VALIDATE = 1
+          }
+        }
+
+        // apply a series of file modifications/creations for draft mode and tikz
+        return async.series(
+          [injectDraftModeIfRequired, createTikzFileIfRequired],
+          function (error) {
+            if (error != null) {
+              return callback(error)
+            }
+            timer = new Metrics.Timer('run-compile')
+            // find the image tag to log it as a metric, e.g. 2015.1 (convert . to - for graphite)
+            let tag =
+              __guard__(
+                __guard__(
+                  request.imageName != null
+                    ? request.imageName.match(/:(.*)/)
+                    : undefined,
+                  x1 => x1[1]
+                ),
+                x => x.replace(/\./g, '-')
+              ) || 'default'
+            if (!request.project_id.match(/^[0-9a-f]{24}$/)) {
+              tag = 'other'
+            } // exclude smoke test
+            Metrics.inc('compiles')
+            Metrics.inc(`compiles-with-image.${tag}`)
+            const compileName = getCompileName(
+              request.project_id,
+              request.user_id
+            )
+            return LatexRunner.runLatex(
+              compileName,
+              {
+                directory: compileDir,
+                mainFile: request.rootResourcePath,
+                compiler: request.compiler,
+                timeout: request.timeout,
+                image: request.imageName,
+                flags: request.flags,
+                environment: env,
+                compileGroup: request.compileGroup,
+              },
+              function (error, output, stats, timings) {
+                // request was for validation only
+                let metric_key, metric_value
+                if (request.check === 'validate') {
+                  const result = (error != null ? error.code : undefined)
+                    ? 'fail'
+                    : 'pass'
+                  error = new Error('validation')
+                  error.validate = result
+                }
+                // request was for compile, and failed on validation
+                if (
+                  request.check === 'error' &&
+                  (error != null ? error.message : undefined) === 'exited'
+                ) {
+                  error = new Error('compilation')
+                  error.validate = 'fail'
+                }
+                // compile was killed by user, was a validation, or a compile which failed validation
+                if (
+                  (error != null ? error.terminated : undefined) ||
+                  (error != null ? error.validate : undefined) ||
+                  (error != null ? error.timedout : undefined)
+                ) {
+                  OutputFileFinder.findOutputFiles(
+                    resourceList,
+                    compileDir,
+                    function (err, outputFiles) {
+                      if (err != null) {
+                        return callback(err)
+                      }
+                      error.outputFiles = outputFiles // return output files so user can check logs
+                      return callback(error)
+                    }
+                  )
+                  return
+                }
+                // compile completed normally
+                if (error != null) {
+                  return callback(error)
+                }
+                Metrics.inc('compiles-succeeded')
+                stats = stats || {}
+                const object = stats || {}
+                for (metric_key in object) {
+                  metric_value = object[metric_key]
+                  Metrics.count(metric_key, metric_value)
+                }
+                timings = timings || {}
+                const object1 = timings || {}
+                for (metric_key in object1) {
+                  metric_value = object1[metric_key]
+                  Metrics.timing(metric_key, metric_value)
+                }
+                const loadavg =
+                  typeof os.loadavg === 'function' ? os.loadavg() : undefined
+                if (loadavg != null) {
+                  Metrics.gauge('load-avg', loadavg[0])
+                }
+                const ts = timer.done()
+                logger.log(
+                  {
+                    project_id: request.project_id,
+                    user_id: request.user_id,
+                    time_taken: ts,
+                    stats,
+                    timings,
+                    loadavg,
+                  },
+                  'done compile'
+                )
+                if ((stats != null ? stats['latex-runs'] : undefined) > 0) {
+                  Metrics.timing(
+                    'run-compile-per-pass',
+                    ts / stats['latex-runs']
+                  )
+                }
+                if (
+                  (stats != null ? stats['latex-runs'] : undefined) > 0 &&
+                  (timings != null ? timings['cpu-time'] : undefined) > 0
+                ) {
+                  Metrics.timing(
+                    'run-compile-cpu-time-per-pass',
+                    timings['cpu-time'] / stats['latex-runs']
+                  )
+                }
+                // Emit compile time.
+                timings.compile = ts
+
+                timer = new Metrics.Timer('process-output-files')
+
+                return OutputFileFinder.findOutputFiles(
+                  resourceList,
+                  compileDir,
+                  function (error, outputFiles) {
+                    if (error != null) {
+                      return callback(error)
+                    }
+                    return OutputCacheManager.saveOutputFiles(
+                      { request, stats, timings },
+                      outputFiles,
+                      compileDir,
+                      outputDir,
+                      (err, newOutputFiles) => {
+                        if (err) {
+                          const { project_id: projectId, user_id: userId } =
+                            request
+                          logger.err(
+                            { projectId, userId, err },
+                            'failed to save output files'
+                          )
+                        }
+
+                        const outputStage = timer.done()
+                        timings.sync = syncStage
+                        timings.output = outputStage
+
+                        // Emit e2e compile time.
+                        timings.compileE2E = timerE2E.done()
+
+                        if (stats['pdf-size']) {
+                          emitPdfStats(stats, timings)
+                        }
+
+                        callback(null, newOutputFiles, stats, timings)
+                      }
+                    )
+                  }
+                )
+              }
+            )
+          }
+        )
+      }
+    )
+  },
+
+  stopCompile(project_id, user_id, callback) {
+    if (callback == null) {
+      callback = function (error) {}
+    }
+    const compileName = getCompileName(project_id, user_id)
+    return LatexRunner.killLatex(compileName, callback)
+  },
+
+  clearProject(project_id, user_id, _callback) {
+    if (_callback == null) {
+      _callback = function (error) {}
+    }
+    const callback = function (error) {
+      _callback(error)
+      return (_callback = function () {})
+    }
+
+    const compileDir = getCompileDir(project_id, user_id)
+    const outputDir = getOutputDir(project_id, user_id)
+
+    return CompileManager._checkDirectory(compileDir, function (err, exists) {
+      if (err != null) {
+        return callback(err)
+      }
+      if (!exists) {
+        return callback()
+      } // skip removal if no directory present
+
+      const proc = child_process.spawn('rm', [
+        '-r',
+        '-f',
+        '--',
+        compileDir,
+        outputDir,
+      ])
+
+      proc.on('error', callback)
+
+      let stderr = ''
+      proc.stderr.setEncoding('utf8').on('data', chunk => (stderr += chunk))
+
+      return proc.on('close', function (code) {
+        if (code === 0) {
+          return callback(null)
+        } else {
+          return callback(
+            new Error(`rm -r ${compileDir} ${outputDir} failed: ${stderr}`)
+          )
+        }
+      })
+    })
+  },
+
+  _findAllDirs(callback) {
+    if (callback == null) {
+      callback = function (error, allDirs) {}
+    }
+    const root = Settings.path.compilesDir
+    return fs.readdir(root, function (err, files) {
+      if (err != null) {
+        return callback(err)
+      }
+      const allDirs = Array.from(files).map(file => Path.join(root, file))
+      return callback(null, allDirs)
+    })
+  },
+
+  clearExpiredProjects(max_cache_age_ms, callback) {
+    if (callback == null) {
+      callback = function (error) {}
+    }
+    const now = Date.now()
+    // action for each directory
+    const expireIfNeeded = (checkDir, cb) =>
+      fs.stat(checkDir, function (err, stats) {
+        if (err != null) {
+          return cb()
+        } // ignore errors checking directory
+        const age = now - stats.mtime
+        const hasExpired = age > max_cache_age_ms
+        if (hasExpired) {
+          return fse.remove(checkDir, cb)
+        } else {
+          return cb()
+        }
+      })
+    // iterate over all project directories
+    return CompileManager._findAllDirs(function (error, allDirs) {
+      if (error != null) {
+        return callback()
+      }
+      return async.eachSeries(allDirs, expireIfNeeded, callback)
+    })
+  },
+
+  _checkDirectory(compileDir, callback) {
+    if (callback == null) {
+      callback = function (error, exists) {}
+    }
+    return fs.lstat(compileDir, function (err, stats) {
+      if ((err != null ? err.code : undefined) === 'ENOENT') {
+        return callback(null, false) //  directory does not exist
+      } else if (err != null) {
+        logger.err(
+          { dir: compileDir, err },
+          'error on stat of project directory for removal'
+        )
+        return callback(err)
+      } else if (!(stats != null ? stats.isDirectory() : undefined)) {
+        logger.err(
+          { dir: compileDir, stats },
+          'bad project directory for removal'
+        )
+        return callback(new Error('project directory is not directory'))
+      } else {
+        return callback(null, true)
+      }
+    })
+  }, // directory exists
+
+  syncFromCode(
+    project_id,
+    user_id,
+    file_name,
+    line,
+    column,
+    imageName,
+    callback
+  ) {
+    // If LaTeX was run in a virtual environment, the file path that synctex expects
+    // might not match the file path on the host. The .synctex.gz file however, will be accessed
+    // wherever it is on the host.
+    if (callback == null) {
+      callback = function (error, pdfPositions) {}
+    }
+    const compileName = getCompileName(project_id, user_id)
+    const base_dir = Settings.path.synctexBaseDir(compileName)
+    const file_path = base_dir + '/' + file_name
+    const compileDir = getCompileDir(project_id, user_id)
+    const synctex_path = `${base_dir}/output.pdf`
+    const command = ['code', synctex_path, file_path, line, column]
+    CompileManager._runSynctex(
+      project_id,
+      user_id,
+      command,
+      imageName,
+      function (error, stdout) {
+        if (error != null) {
+          return callback(error)
+        }
+        logger.log(
+          { project_id, user_id, file_name, line, column, command, stdout },
+          'synctex code output'
+        )
+        return callback(
+          null,
+          CompileManager._parseSynctexFromCodeOutput(stdout)
+        )
+      }
+    )
+  },
+
+  syncFromPdf(project_id, user_id, page, h, v, imageName, callback) {
+    if (callback == null) {
+      callback = function (error, filePositions) {}
+    }
+    const compileName = getCompileName(project_id, user_id)
+    const compileDir = getCompileDir(project_id, user_id)
+    const base_dir = Settings.path.synctexBaseDir(compileName)
+    const synctex_path = `${base_dir}/output.pdf`
+    const command = ['pdf', synctex_path, page, h, v]
+    CompileManager._runSynctex(
+      project_id,
+      user_id,
+      command,
+      imageName,
+      function (error, stdout) {
+        if (error != null) {
+          return callback(error)
+        }
+        logger.log(
+          { project_id, user_id, page, h, v, stdout },
+          'synctex pdf output'
+        )
+        return callback(
+          null,
+          CompileManager._parseSynctexFromPdfOutput(stdout, base_dir)
+        )
+      }
+    )
+  },
+
+  _checkFileExists(dir, filename, callback) {
+    if (callback == null) {
+      callback = function (error) {}
+    }
+    const file = Path.join(dir, filename)
+    return fs.stat(dir, function (error, stats) {
+      if ((error != null ? error.code : undefined) === 'ENOENT') {
+        return callback(new Errors.NotFoundError('no output directory'))
+      }
+      if (error != null) {
+        return callback(error)
+      }
+      return fs.stat(file, function (error, stats) {
+        if ((error != null ? error.code : undefined) === 'ENOENT') {
+          return callback(new Errors.NotFoundError('no output file'))
+        }
+        if (error != null) {
+          return callback(error)
+        }
+        if (!(stats != null ? stats.isFile() : undefined)) {
+          return callback(new Error('not a file'))
+        }
+        return callback()
+      })
+    })
+  },
+
+  _runSynctex(project_id, user_id, command, imageName, callback) {
+    if (callback == null) {
+      callback = function (error, stdout) {}
+    }
+    const seconds = 1000
+
+    command.unshift('/opt/synctex')
+
+    const directory = getCompileDir(project_id, user_id)
+    const timeout = 60 * 1000 // increased to allow for large projects
+    const compileName = getCompileName(project_id, user_id)
+    const compileGroup = 'synctex'
+    CompileManager._checkFileExists(directory, 'output.synctex.gz', error => {
+      if (error) {
+        return callback(error)
+      }
+      return CommandRunner.run(
+        compileName,
+        command,
+        directory,
+        imageName ||
+          (Settings.clsi && Settings.clsi.docker
+            ? Settings.clsi.docker.image
+            : undefined),
+        timeout,
+        {},
+        compileGroup,
+        function (error, output) {
+          if (error != null) {
+            logger.err(
+              { err: error, command, project_id, user_id },
+              'error running synctex'
+            )
+            return callback(error)
+          }
+          return callback(null, output.stdout)
+        }
+      )
+    })
+  },
+
+  _parseSynctexFromCodeOutput(output) {
+    const results = []
+    for (const line of Array.from(output.split('\n'))) {
+      const [node, page, h, v, width, height] = Array.from(line.split('\t'))
+      if (node === 'NODE') {
+        results.push({
+          page: parseInt(page, 10),
+          h: parseFloat(h),
+          v: parseFloat(v),
+          height: parseFloat(height),
+          width: parseFloat(width),
+        })
+      }
+    }
+    return results
+  },
+
+  _parseSynctexFromPdfOutput(output, base_dir) {
+    const results = []
+    for (let line of Array.from(output.split('\n'))) {
+      let column, file_path, node
+      ;[node, file_path, line, column] = Array.from(line.split('\t'))
+      if (node === 'NODE') {
+        const file = file_path.slice(base_dir.length + 1)
+        results.push({
+          file,
+          line: parseInt(line, 10),
+          column: parseInt(column, 10),
+        })
+      }
+    }
+    return results
+  },
+
+  wordcount(project_id, user_id, file_name, image, callback) {
+    if (callback == null) {
+      callback = function (error, pdfPositions) {}
+    }
+    logger.log({ project_id, user_id, file_name, image }, 'running wordcount')
+    const file_path = `$COMPILE_DIR/${file_name}`
+    const command = [
+      'texcount',
+      '-nocol',
+      '-inc',
+      file_path,
+      `-out=${file_path}.wc`,
+    ]
+    const compileDir = getCompileDir(project_id, user_id)
+    const timeout = 60 * 1000
+    const compileName = getCompileName(project_id, user_id)
+    const compileGroup = 'wordcount'
+    return fse.ensureDir(compileDir, function (error) {
+      if (error != null) {
+        logger.err(
+          { error, project_id, user_id, file_name },
+          'error ensuring dir for sync from code'
+        )
+        return callback(error)
+      }
+      return CommandRunner.run(
+        compileName,
+        command,
+        compileDir,
+        image,
+        timeout,
+        {},
+        compileGroup,
+        function (error) {
+          if (error != null) {
+            return callback(error)
+          }
+          return fs.readFile(
+            compileDir + '/' + file_name + '.wc',
+            'utf-8',
+            function (err, stdout) {
+              if (err != null) {
+                // call it node_err so sentry doesn't use random path error as unique id so it can't be ignored
+                logger.err(
+                  { node_err: err, command, compileDir, project_id, user_id },
+                  'error reading word count output'
+                )
+                return callback(err)
+              }
+              const results = CompileManager._parseWordcountFromOutput(stdout)
+              logger.log(
+                { project_id, user_id, wordcount: results },
+                'word count results'
+              )
+              return callback(null, results)
+            }
+          )
+        }
+      )
+    })
+  },
+
+  _parseWordcountFromOutput(output) {
+    const results = {
+      encode: '',
+      textWords: 0,
+      headWords: 0,
+      outside: 0,
+      headers: 0,
+      elements: 0,
+      mathInline: 0,
+      mathDisplay: 0,
+      errors: 0,
+      messages: '',
+    }
+    for (const line of Array.from(output.split('\n'))) {
+      const [data, info] = Array.from(line.split(':'))
+      if (data.indexOf('Encoding') > -1) {
+        results.encode = info.trim()
+      }
+      if (data.indexOf('in text') > -1) {
+        results.textWords = parseInt(info, 10)
+      }
+      if (data.indexOf('in head') > -1) {
+        results.headWords = parseInt(info, 10)
+      }
+      if (data.indexOf('outside') > -1) {
+        results.outside = parseInt(info, 10)
+      }
+      if (data.indexOf('of head') > -1) {
+        results.headers = parseInt(info, 10)
+      }
+      if (data.indexOf('Number of floats/tables/figures') > -1) {
+        results.elements = parseInt(info, 10)
+      }
+      if (data.indexOf('Number of math inlines') > -1) {
+        results.mathInline = parseInt(info, 10)
+      }
+      if (data.indexOf('Number of math displayed') > -1) {
+        results.mathDisplay = parseInt(info, 10)
+      }
+      if (data === '(errors') {
+        // errors reported as (errors:123)
+        results.errors = parseInt(info, 10)
+      }
+      if (line.indexOf('!!! ') > -1) {
+        // errors logged as !!! message !!!
+        results.messages += line + '\n'
+      }
+    }
+    return results
+  },
+}
+
+function __guard__(value, transform) {
+  return typeof value !== 'undefined' && value !== null
+    ? transform(value)
+    : undefined
+}

+ 273 - 0
services/clsi/app/js/ContentCacheManager.js

@@ -0,0 +1,273 @@
+/**
+ * ContentCacheManager - maintains a cache of stream hashes from a PDF file
+ */
+
+const { callbackify } = require('util')
+const fs = require('fs')
+const crypto = require('crypto')
+const Path = require('path')
+const Settings = require('@overleaf/settings')
+const OError = require('@overleaf/o-error')
+const pLimit = require('p-limit')
+const { parseXrefTable } = require('../lib/pdfjs/parseXrefTable')
+const { TimedOutError } = require('./Errors')
+
+/**
+ *
+ * @param {String} contentDir path to directory where content hash files are cached
+ * @param {String} filePath the pdf file to scan for streams
+ * @param {number} size the pdf size
+ * @param {number} compileTime
+ */
+async function update(contentDir, filePath, size, compileTime) {
+  const checkDeadline = getDeadlineChecker(compileTime)
+  const ranges = []
+  const newRanges = []
+  // keep track of hashes expire old ones when they reach a generation > N.
+  const tracker = await HashFileTracker.from(contentDir)
+  tracker.updateAge()
+
+  checkDeadline('after init HashFileTracker')
+
+  const rawTable = await parseXrefTable(filePath, size, checkDeadline)
+  rawTable.sort((a, b) => {
+    return a.offset - b.offset
+  })
+  rawTable.forEach((obj, idx) => {
+    obj.idx = idx
+  })
+
+  checkDeadline('after parsing')
+
+  const uncompressedObjects = []
+  for (const object of rawTable) {
+    if (!object.uncompressed) {
+      continue
+    }
+    const nextObject = rawTable[object.idx + 1]
+    if (!nextObject) {
+      // Ignore this possible edge case.
+      // The last object should be part of the xRef table.
+      continue
+    } else {
+      object.endOffset = nextObject.offset
+    }
+    const size = object.endOffset - object.offset
+    object.size = size
+    if (size < Settings.pdfCachingMinChunkSize) {
+      continue
+    }
+    uncompressedObjects.push({ object, idx: uncompressedObjects.length })
+  }
+
+  checkDeadline('after finding uncompressed')
+
+  const handle = await fs.promises.open(filePath)
+  try {
+    for (const { object, idx } of uncompressedObjects) {
+      let buffer = Buffer.alloc(object.size, 0)
+      const { bytesRead } = await handle.read(
+        buffer,
+        0,
+        object.size,
+        object.offset
+      )
+      checkDeadline('after read ' + idx)
+      if (bytesRead !== object.size) {
+        throw new OError('could not read full chunk', {
+          object,
+          bytesRead,
+        })
+      }
+      const idxObj = buffer.indexOf('obj')
+      if (idxObj > 100) {
+        throw new OError('objectId is too large', {
+          object,
+          idxObj,
+        })
+      }
+      const objectIdRaw = buffer.subarray(0, idxObj)
+      buffer = buffer.subarray(objectIdRaw.byteLength)
+
+      const hash = pdfStreamHash(buffer)
+      checkDeadline('after hash ' + idx)
+      const range = {
+        objectId: objectIdRaw.toString(),
+        start: object.offset + objectIdRaw.byteLength,
+        end: object.endOffset,
+        hash,
+      }
+      ranges.push(range)
+
+      // Optimization: Skip writing of duplicate streams.
+      if (tracker.track(range)) continue
+
+      await writePdfStream(contentDir, hash, buffer)
+      checkDeadline('after write ' + idx)
+      newRanges.push(range)
+    }
+  } finally {
+    await handle.close()
+  }
+
+  // NOTE: Bailing out below does not make sense.
+  //       Let the next compile use the already written ranges.
+  const reclaimedSpace = await tracker.deleteStaleHashes(5)
+  await tracker.flush()
+  return [ranges, newRanges, reclaimedSpace]
+}
+
+function getStatePath(contentDir) {
+  return Path.join(contentDir, '.state.v0.json')
+}
+
+class HashFileTracker {
+  constructor(contentDir, { hashAge = [], hashSize = [] }) {
+    this.contentDir = contentDir
+    this.hashAge = new Map(hashAge)
+    this.hashSize = new Map(hashSize)
+  }
+
+  static async from(contentDir) {
+    const statePath = getStatePath(contentDir)
+    let state = {}
+    try {
+      const blob = await fs.promises.readFile(statePath)
+      state = JSON.parse(blob)
+    } catch (e) {}
+    return new HashFileTracker(contentDir, state)
+  }
+
+  track(range) {
+    const exists = this.hashAge.has(range.hash)
+    if (!exists) {
+      this.hashSize.set(range.hash, range.end - range.start)
+    }
+    this.hashAge.set(range.hash, 0)
+    return exists
+  }
+
+  updateAge() {
+    for (const [hash, age] of this.hashAge) {
+      this.hashAge.set(hash, age + 1)
+    }
+    return this
+  }
+
+  findStale(maxAge) {
+    const stale = []
+    for (const [hash, age] of this.hashAge) {
+      if (age > maxAge) {
+        stale.push(hash)
+      }
+    }
+    return stale
+  }
+
+  async flush() {
+    const statePath = getStatePath(this.contentDir)
+    const blob = JSON.stringify({
+      hashAge: Array.from(this.hashAge.entries()),
+      hashSize: Array.from(this.hashSize.entries()),
+    })
+    const atomicWrite = statePath + '~'
+    try {
+      await fs.promises.writeFile(atomicWrite, blob)
+    } catch (err) {
+      try {
+        await fs.promises.unlink(atomicWrite)
+      } catch (e) {}
+      throw err
+    }
+    try {
+      await fs.promises.rename(atomicWrite, statePath)
+    } catch (err) {
+      try {
+        await fs.promises.unlink(atomicWrite)
+      } catch (e) {}
+      throw err
+    }
+  }
+
+  async deleteStaleHashes(n) {
+    // delete any hash file older than N generations
+    const hashes = this.findStale(n)
+
+    let reclaimedSpace = 0
+    if (hashes.length === 0) {
+      return reclaimedSpace
+    }
+
+    await promiseMapWithLimit(10, hashes, async hash => {
+      await fs.promises.unlink(Path.join(this.contentDir, hash))
+      this.hashAge.delete(hash)
+      reclaimedSpace += this.hashSize.get(hash)
+      this.hashSize.delete(hash)
+    })
+    return reclaimedSpace
+  }
+}
+
+function pdfStreamHash(buffer) {
+  const hash = crypto.createHash('sha256')
+  hash.update(buffer)
+  return hash.digest('hex')
+}
+
+async function writePdfStream(dir, hash, buffer) {
+  const filename = Path.join(dir, hash)
+  const atomicWriteFilename = filename + '~'
+  if (Settings.enablePdfCachingDark) {
+    // Write an empty file in dark mode.
+    buffer = Buffer.alloc(0)
+  }
+  try {
+    await fs.promises.writeFile(atomicWriteFilename, buffer)
+    await fs.promises.rename(atomicWriteFilename, filename)
+  } catch (err) {
+    try {
+      await fs.promises.unlink(atomicWriteFilename)
+    } catch (_) {
+      throw err
+    }
+  }
+}
+
+function getDeadlineChecker(compileTime) {
+  const maxOverhead = Math.min(
+    // Adding 10s to a  40s compile time is OK.
+    // Adding  1s to a   3s compile time is OK.
+    Math.max(compileTime / 4, 1000),
+    // Adding 30s to a 120s compile time is not OK, limit to 10s.
+    Settings.pdfCachingMaxProcessingTime
+  )
+
+  const deadline = Date.now() + maxOverhead
+  let lastStage = { stage: 'start', now: Date.now() }
+  let completedStages = 0
+  return function (stage) {
+    const now = Date.now()
+    if (now > deadline) {
+      throw new TimedOutError(stage, {
+        completedStages,
+        lastStage: lastStage.stage,
+        diffToLastStage: now - lastStage.now,
+      })
+    }
+    completedStages++
+    lastStage = { stage, now }
+  }
+}
+
+function promiseMapWithLimit(concurrency, array, fn) {
+  const limit = pLimit(concurrency)
+  return Promise.all(array.map(x => limit(() => fn(x))))
+}
+
+module.exports = {
+  HASH_REGEX: /^[0-9a-f]{64}$/,
+  update: callbackify(update),
+  promises: {
+    update,
+  },
+}

+ 115 - 0
services/clsi/app/js/ContentCacheMetrics.js

@@ -0,0 +1,115 @@
+const logger = require('logger-sharelatex')
+const Metrics = require('./Metrics')
+const os = require('os')
+
+let CACHED_LOAD = {
+  expires: -1,
+  load: [0, 0, 0],
+}
+function getSystemLoad() {
+  if (CACHED_LOAD.expires < Date.now()) {
+    CACHED_LOAD = {
+      expires: Date.now() + 10 * 1000,
+      load: os.loadavg(),
+    }
+  }
+  return CACHED_LOAD.load
+}
+
+const ONE_MB = 1024 * 1024
+
+function emitPdfStats(stats, timings) {
+  if (stats['pdf-caching-timed-out']) {
+    Metrics.inc('pdf-caching-timed-out')
+  }
+  if (timings['compute-pdf-caching']) {
+    emitPdfCachingStats(stats, timings)
+  } else {
+    // How much bandwidth will the pdf incur when downloaded in full?
+    Metrics.summary('pdf-bandwidth', stats['pdf-size'])
+  }
+}
+
+function emitPdfCachingStats(stats, timings) {
+  if (!stats['pdf-size']) return // double check
+
+  // How much extra time did we spent in PDF.js?
+  Metrics.timing('compute-pdf-caching', timings['compute-pdf-caching'])
+
+  // How large is the overhead of hashing up-front?
+  const fraction =
+    timings.compileE2E - timings['compute-pdf-caching'] !== 0
+      ? timings.compileE2E /
+        (timings.compileE2E - timings['compute-pdf-caching'])
+      : 1
+  if (fraction > 1.5 && timings.compileE2E > 10 * 1000) {
+    logger.warn(
+      {
+        stats,
+        timings,
+        load: getSystemLoad(),
+      },
+      'slow pdf caching'
+    )
+  }
+  Metrics.summary('overhead-compute-pdf-ranges', fraction * 100 - 100)
+
+  // How does the hashing scale to pdf size in MB?
+  Metrics.timing(
+    'compute-pdf-caching-relative-to-pdf-size',
+    timings['compute-pdf-caching'] / (stats['pdf-size'] / ONE_MB)
+  )
+  if (stats['pdf-caching-total-ranges-size']) {
+    // How does the hashing scale to total ranges size in MB?
+    Metrics.timing(
+      'compute-pdf-caching-relative-to-total-ranges-size',
+      timings['compute-pdf-caching'] /
+        (stats['pdf-caching-total-ranges-size'] / ONE_MB)
+    )
+    // How fast is the hashing per range on average?
+    Metrics.timing(
+      'compute-pdf-caching-relative-to-ranges-count',
+      timings['compute-pdf-caching'] / stats['pdf-caching-n-ranges']
+    )
+
+    // How many ranges are new?
+    Metrics.summary(
+      'new-pdf-ranges-relative-to-total-ranges',
+      (stats['pdf-caching-n-new-ranges'] / stats['pdf-caching-n-ranges']) * 100
+    )
+  }
+
+  // How much content is cacheable?
+  Metrics.summary(
+    'cacheable-ranges-to-pdf-size',
+    (stats['pdf-caching-total-ranges-size'] / stats['pdf-size']) * 100
+  )
+
+  const sizeWhenDownloadedInFull =
+    // All of the pdf
+    stats['pdf-size'] -
+    // These ranges are potentially cached.
+    stats['pdf-caching-total-ranges-size'] +
+    // These ranges are not cached.
+    stats['pdf-caching-new-ranges-size']
+
+  // How much bandwidth can we save when downloading the pdf in full?
+  Metrics.summary(
+    'pdf-bandwidth-savings',
+    100 - (sizeWhenDownloadedInFull / stats['pdf-size']) * 100
+  )
+
+  // How much bandwidth will the pdf incur when downloaded in full?
+  Metrics.summary('pdf-bandwidth', sizeWhenDownloadedInFull)
+
+  // How much space do the ranges use?
+  // This will accumulate the ranges size over time, skipping already written ranges.
+  Metrics.summary(
+    'pdf-ranges-disk-size',
+    stats['pdf-caching-new-ranges-size'] - stats['pdf-caching-reclaimed-space']
+  )
+}
+
+module.exports = {
+  emitPdfStats,
+}

+ 24 - 0
services/clsi/app/js/ContentController.js

@@ -0,0 +1,24 @@
+const Path = require('path')
+const send = require('send')
+const Settings = require('@overleaf/settings')
+const OutputCacheManager = require('./OutputCacheManager')
+
+const ONE_DAY_S = 24 * 60 * 60
+const ONE_DAY_MS = ONE_DAY_S * 1000
+
+function getPdfRange(req, res, next) {
+  const { projectId, userId, contentId, hash } = req.params
+  const perUserDir = userId ? `${projectId}-${userId}` : projectId
+  const path = Path.join(
+    Settings.path.outputDir,
+    perUserDir,
+    OutputCacheManager.CONTENT_SUBDIR,
+    contentId,
+    hash
+  )
+  res.setHeader('cache-control', `public, max-age=${ONE_DAY_S}`)
+  res.setHeader('expires', new Date(Date.now() + ONE_DAY_MS).toUTCString())
+  send(req, path).pipe(res)
+}
+
+module.exports = { getPdfRange }

+ 38 - 0
services/clsi/app/js/ContentTypeMapper.js

@@ -0,0 +1,38 @@
+/* eslint-disable
+    no-unused-vars,
+*/
+// TODO: This file was created by bulk-decaffeinate.
+// Fix any style issues and re-enable lint.
+let ContentTypeMapper
+const Path = require('path')
+
+// here we coerce html, css and js to text/plain,
+// otherwise choose correct mime type based on file extension,
+// falling back to octet-stream
+module.exports = ContentTypeMapper = {
+  map(path) {
+    switch (Path.extname(path)) {
+      case '.txt':
+      case '.html':
+      case '.js':
+      case '.css':
+      case '.svg':
+        return 'text/plain'
+      case '.csv':
+        return 'text/csv'
+      case '.pdf':
+        return 'application/pdf'
+      case '.png':
+        return 'image/png'
+      case '.jpg':
+      case '.jpeg':
+        return 'image/jpeg'
+      case '.tiff':
+        return 'image/tiff'
+      case '.gif':
+        return 'image/gif'
+      default:
+        return 'application/octet-stream'
+    }
+  },
+}

+ 18 - 0
services/clsi/app/js/DbQueue.js

@@ -0,0 +1,18 @@
+// TODO: This file was created by bulk-decaffeinate.
+// Sanity-check the conversion and remove this comment.
+/*
+ * decaffeinate suggestions:
+ * DS102: Remove unnecessary code created because of implicit returns
+ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
+ */
+const async = require('async')
+const Settings = require('@overleaf/settings')
+const logger = require('logger-sharelatex')
+const queue = async.queue(
+  (task, cb) => task(cb),
+  Settings.parallelSqlQueryLimit
+)
+
+queue.drain = () => logger.debug('all items have been processed')
+
+module.exports = { queue }

+ 113 - 0
services/clsi/app/js/DockerLockManager.js

@@ -0,0 +1,113 @@
+/* eslint-disable
+    handle-callback-err,
+*/
+// TODO: This file was created by bulk-decaffeinate.
+// Fix any style issues and re-enable lint.
+/*
+ * decaffeinate suggestions:
+ * DS101: Remove unnecessary use of Array.from
+ * DS102: Remove unnecessary code created because of implicit returns
+ * DS207: Consider shorter variations of null checks
+ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
+ */
+let LockManager
+const logger = require('logger-sharelatex')
+
+const LockState = {} // locks for docker container operations, by container name
+
+module.exports = LockManager = {
+  MAX_LOCK_HOLD_TIME: 15000, // how long we can keep a lock
+  MAX_LOCK_WAIT_TIME: 10000, // how long we wait for a lock
+  LOCK_TEST_INTERVAL: 1000, // retry time
+
+  tryLock(key, callback) {
+    let lockValue
+    if (callback == null) {
+      callback = function (err, gotLock) {}
+    }
+    const existingLock = LockState[key]
+    if (existingLock != null) {
+      // the lock is already taken, check how old it is
+      const lockAge = Date.now() - existingLock.created
+      if (lockAge < LockManager.MAX_LOCK_HOLD_TIME) {
+        return callback(null, false) // we didn't get the lock, bail out
+      } else {
+        logger.error(
+          { key, lock: existingLock, age: lockAge },
+          'taking old lock by force'
+        )
+      }
+    }
+    // take the lock
+    LockState[key] = lockValue = { created: Date.now() }
+    return callback(null, true, lockValue)
+  },
+
+  getLock(key, callback) {
+    let attempt
+    if (callback == null) {
+      callback = function (error, lockValue) {}
+    }
+    const startTime = Date.now()
+    return (attempt = () =>
+      LockManager.tryLock(key, function (error, gotLock, lockValue) {
+        if (error != null) {
+          return callback(error)
+        }
+        if (gotLock) {
+          return callback(null, lockValue)
+        } else if (Date.now() - startTime > LockManager.MAX_LOCK_WAIT_TIME) {
+          const e = new Error('Lock timeout')
+          e.key = key
+          return callback(e)
+        } else {
+          return setTimeout(attempt, LockManager.LOCK_TEST_INTERVAL)
+        }
+      }))()
+  },
+
+  releaseLock(key, lockValue, callback) {
+    if (callback == null) {
+      callback = function (error) {}
+    }
+    const existingLock = LockState[key]
+    if (existingLock === lockValue) {
+      // lockValue is an object, so we can test by reference
+      delete LockState[key] // our lock, so we can free it
+      return callback()
+    } else if (existingLock != null) {
+      // lock exists but doesn't match ours
+      logger.error(
+        { key, lock: existingLock },
+        'tried to release lock taken by force'
+      )
+      return callback()
+    } else {
+      logger.error(
+        { key, lock: existingLock },
+        'tried to release lock that has gone'
+      )
+      return callback()
+    }
+  },
+
+  runWithLock(key, runner, callback) {
+    if (callback == null) {
+      callback = function (error) {}
+    }
+    return LockManager.getLock(key, function (error, lockValue) {
+      if (error != null) {
+        return callback(error)
+      }
+      return runner((error1, ...args) =>
+        LockManager.releaseLock(key, lockValue, function (error2) {
+          error = error1 || error2
+          if (error != null) {
+            return callback(error)
+          }
+          return callback(null, ...Array.from(args))
+        })
+      )
+    })
+  },
+}

+ 625 - 0
services/clsi/app/js/DockerRunner.js

@@ -0,0 +1,625 @@
+const Settings = require('@overleaf/settings')
+const logger = require('logger-sharelatex')
+const Docker = require('dockerode')
+const dockerode = new Docker()
+const crypto = require('crypto')
+const async = require('async')
+const LockManager = require('./DockerLockManager')
+const fs = require('fs')
+const Path = require('path')
+const _ = require('lodash')
+
+const ONE_HOUR_IN_MS = 60 * 60 * 1000
+logger.info('using docker runner')
+
+function usingSiblingContainers() {
+  return (
+    Settings != null &&
+    Settings.path != null &&
+    Settings.path.sandboxedCompilesHostDir != null
+  )
+}
+
+let containerMonitorTimeout
+let containerMonitorInterval
+
+const DockerRunner = {
+  run(
+    projectId,
+    command,
+    directory,
+    image,
+    timeout,
+    environment,
+    compileGroup,
+    callback
+  ) {
+    if (usingSiblingContainers()) {
+      const _newPath = Settings.path.sandboxedCompilesHostDir
+      logger.log(
+        { path: _newPath },
+        'altering bind path for sibling containers'
+      )
+      // Server Pro, example:
+      //   '/var/lib/sharelatex/data/compiles/<project-id>'
+      //   ... becomes ...
+      //   '/opt/sharelatex_data/data/compiles/<project-id>'
+      directory = Path.join(
+        Settings.path.sandboxedCompilesHostDir,
+        Path.basename(directory)
+      )
+    }
+
+    const volumes = { [directory]: '/compile' }
+
+    command = command.map(arg =>
+      arg.toString().replace('$COMPILE_DIR', '/compile')
+    )
+    if (image == null) {
+      image = Settings.clsi.docker.image
+    }
+
+    if (
+      Settings.clsi.docker.allowedImages &&
+      !Settings.clsi.docker.allowedImages.includes(image)
+    ) {
+      return callback(new Error('image not allowed'))
+    }
+
+    if (Settings.texliveImageNameOveride != null) {
+      const img = image.split('/')
+      image = `${Settings.texliveImageNameOveride}/${img[2]}`
+    }
+
+    const options = DockerRunner._getContainerOptions(
+      command,
+      image,
+      volumes,
+      timeout,
+      environment,
+      compileGroup
+    )
+    const fingerprint = DockerRunner._fingerprintContainer(options)
+    const name = `project-${projectId}-${fingerprint}`
+    options.name = name
+
+    // logOptions = _.clone(options)
+    // logOptions?.HostConfig?.SecurityOpt = "secomp used, removed in logging"
+    logger.log({ projectId }, 'running docker container')
+    DockerRunner._runAndWaitForContainer(
+      options,
+      volumes,
+      timeout,
+      (error, output) => {
+        if (error && error.statusCode === 500) {
+          logger.log(
+            { err: error, projectId },
+            'error running container so destroying and retrying'
+          )
+          DockerRunner.destroyContainer(name, null, true, error => {
+            if (error != null) {
+              return callback(error)
+            }
+            DockerRunner._runAndWaitForContainer(
+              options,
+              volumes,
+              timeout,
+              callback
+            )
+          })
+        } else {
+          callback(error, output)
+        }
+      }
+    )
+
+    // pass back the container name to allow it to be killed
+    return name
+  },
+
+  kill(containerId, callback) {
+    logger.log({ containerId }, 'sending kill signal to container')
+    const container = dockerode.getContainer(containerId)
+    container.kill(error => {
+      if (
+        error != null &&
+        error.message != null &&
+        error.message.match(/Cannot kill container .* is not running/)
+      ) {
+        logger.warn(
+          { err: error, containerId },
+          'container not running, continuing'
+        )
+        error = null
+      }
+      if (error != null) {
+        logger.error({ err: error, containerId }, 'error killing container')
+        callback(error)
+      } else {
+        callback()
+      }
+    })
+  },
+
+  _runAndWaitForContainer(options, volumes, timeout, _callback) {
+    const callback = _.once(_callback)
+    const { name } = options
+
+    let streamEnded = false
+    let containerReturned = false
+    let output = {}
+
+    function callbackIfFinished() {
+      if (streamEnded && containerReturned) {
+        callback(null, output)
+      }
+    }
+
+    function attachStreamHandler(error, _output) {
+      if (error != null) {
+        return callback(error)
+      }
+      output = _output
+      streamEnded = true
+      callbackIfFinished()
+    }
+
+    DockerRunner.startContainer(
+      options,
+      volumes,
+      attachStreamHandler,
+      (error, containerId) => {
+        if (error != null) {
+          return callback(error)
+        }
+
+        DockerRunner.waitForContainer(name, timeout, (error, exitCode) => {
+          if (error != null) {
+            return callback(error)
+          }
+          if (exitCode === 137) {
+            // exit status from kill -9
+            const err = new Error('terminated')
+            err.terminated = true
+            return callback(err)
+          }
+          if (exitCode === 1) {
+            // exit status from chktex
+            const err = new Error('exited')
+            err.code = exitCode
+            return callback(err)
+          }
+          containerReturned = true
+          if (options != null && options.HostConfig != null) {
+            options.HostConfig.SecurityOpt = null
+          }
+          logger.log({ exitCode, options }, 'docker container has exited')
+          callbackIfFinished()
+        })
+      }
+    )
+  },
+
+  _getContainerOptions(
+    command,
+    image,
+    volumes,
+    timeout,
+    environment,
+    compileGroup
+  ) {
+    const timeoutInSeconds = timeout / 1000
+
+    const dockerVolumes = {}
+    for (const hostVol in volumes) {
+      const dockerVol = volumes[hostVol]
+      dockerVolumes[dockerVol] = {}
+
+      if (volumes[hostVol].slice(-3).indexOf(':r') === -1) {
+        volumes[hostVol] = `${dockerVol}:rw`
+      }
+    }
+
+    // merge settings and environment parameter
+    const env = {}
+    for (const src of [Settings.clsi.docker.env, environment || {}]) {
+      for (const key in src) {
+        const value = src[key]
+        env[key] = value
+      }
+    }
+    // set the path based on the image year
+    const match = image.match(/:([0-9]+)\.[0-9]+/)
+    const year = match ? match[1] : '2014'
+    env.PATH = `/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/texlive/${year}/bin/x86_64-linux/`
+    const options = {
+      Cmd: command,
+      Image: image,
+      Volumes: dockerVolumes,
+      WorkingDir: '/compile',
+      NetworkDisabled: true,
+      Memory: 1024 * 1024 * 1024 * 1024, // 1 Gb
+      User: Settings.clsi.docker.user,
+      Env: Object.entries(env).map(([key, value]) => `${key}=${value}`),
+      HostConfig: {
+        Binds: Object.entries(volumes).map(
+          ([hostVol, dockerVol]) => `${hostVol}:${dockerVol}`
+        ),
+        LogConfig: { Type: 'none', Config: {} },
+        Ulimits: [
+          {
+            Name: 'cpu',
+            Soft: timeoutInSeconds + 5,
+            Hard: timeoutInSeconds + 10,
+          },
+        ],
+        CapDrop: 'ALL',
+        SecurityOpt: ['no-new-privileges'],
+      },
+    }
+
+    if (Settings.path != null && Settings.path.synctexBinHostPath != null) {
+      options.HostConfig.Binds.push(
+        `${Settings.path.synctexBinHostPath}:/opt/synctex:ro`
+      )
+    }
+
+    if (Settings.clsi.docker.seccomp_profile != null) {
+      options.HostConfig.SecurityOpt.push(
+        `seccomp=${Settings.clsi.docker.seccomp_profile}`
+      )
+    }
+
+    if (Settings.clsi.docker.apparmor_profile != null) {
+      options.HostConfig.SecurityOpt.push(
+        `apparmor=${Settings.clsi.docker.apparmor_profile}`
+      )
+    }
+
+    if (Settings.clsi.docker.runtime) {
+      options.HostConfig.Runtime = Settings.clsi.docker.runtime
+    }
+
+    if (Settings.clsi.docker.Readonly) {
+      options.HostConfig.ReadonlyRootfs = true
+      options.HostConfig.Tmpfs = { '/tmp': 'rw,noexec,nosuid,size=65536k' }
+      options.Volumes['/home/tex'] = {}
+    }
+
+    // Allow per-compile group overriding of individual settings
+    if (
+      Settings.clsi.docker.compileGroupConfig &&
+      Settings.clsi.docker.compileGroupConfig[compileGroup]
+    ) {
+      const override = Settings.clsi.docker.compileGroupConfig[compileGroup]
+      for (const key in override) {
+        _.set(options, key, override[key])
+      }
+    }
+
+    return options
+  },
+
+  _fingerprintContainer(containerOptions) {
+    // Yay, Hashing!
+    const json = JSON.stringify(containerOptions)
+    return crypto.createHash('md5').update(json).digest('hex')
+  },
+
+  startContainer(options, volumes, attachStreamHandler, callback) {
+    LockManager.runWithLock(
+      options.name,
+      releaseLock =>
+        // Check that volumes exist before starting the container.
+        // When a container is started with volume pointing to a
+        // non-existent directory then docker creates the directory but
+        // with root ownership.
+        DockerRunner._checkVolumes(options, volumes, err => {
+          if (err != null) {
+            return releaseLock(err)
+          }
+          DockerRunner._startContainer(
+            options,
+            volumes,
+            attachStreamHandler,
+            releaseLock
+          )
+        }),
+
+      callback
+    )
+  },
+
+  // Check that volumes exist and are directories
+  _checkVolumes(options, volumes, callback) {
+    if (usingSiblingContainers()) {
+      // Server Pro, with sibling-containers active, skip checks
+      return callback(null)
+    }
+
+    const checkVolume = (path, cb) =>
+      fs.stat(path, (err, stats) => {
+        if (err != null) {
+          return cb(err)
+        }
+        if (!stats.isDirectory()) {
+          return cb(new Error('not a directory'))
+        }
+        cb()
+      })
+    const jobs = []
+    for (const vol in volumes) {
+      jobs.push(cb => checkVolume(vol, cb))
+    }
+    async.series(jobs, callback)
+  },
+
+  _startContainer(options, volumes, attachStreamHandler, callback) {
+    callback = _.once(callback)
+    const { name } = options
+
+    logger.log({ container_name: name }, 'starting container')
+    const container = dockerode.getContainer(name)
+
+    function createAndStartContainer() {
+      dockerode.createContainer(options, (error, container) => {
+        if (error != null) {
+          return callback(error)
+        }
+        startExistingContainer()
+      })
+    }
+
+    function startExistingContainer() {
+      DockerRunner.attachToContainer(
+        options.name,
+        attachStreamHandler,
+        error => {
+          if (error != null) {
+            return callback(error)
+          }
+          container.start(error => {
+            if (error != null && error.statusCode !== 304) {
+              callback(error)
+            } else {
+              // already running
+              callback()
+            }
+          })
+        }
+      )
+    }
+
+    container.inspect((error, stats) => {
+      if (error != null && error.statusCode === 404) {
+        createAndStartContainer()
+      } else if (error != null) {
+        logger.err(
+          { container_name: name, error },
+          'unable to inspect container to start'
+        )
+        callback(error)
+      } else {
+        startExistingContainer()
+      }
+    })
+  },
+
+  attachToContainer(containerId, attachStreamHandler, attachStartCallback) {
+    const container = dockerode.getContainer(containerId)
+    container.attach({ stdout: 1, stderr: 1, stream: 1 }, (error, stream) => {
+      if (error != null) {
+        logger.error(
+          { err: error, containerId },
+          'error attaching to container'
+        )
+        return attachStartCallback(error)
+      } else {
+        attachStartCallback()
+      }
+
+      logger.log({ containerId }, 'attached to container')
+
+      const MAX_OUTPUT = 1024 * 1024 // limit output to 1MB
+      function createStringOutputStream(name) {
+        return {
+          data: '',
+          overflowed: false,
+          write(data) {
+            if (this.overflowed) {
+              return
+            }
+            if (this.data.length < MAX_OUTPUT) {
+              this.data += data
+            } else {
+              logger.error(
+                {
+                  containerId,
+                  length: this.data.length,
+                  maxLen: MAX_OUTPUT,
+                },
+                `${name} exceeds max size`
+              )
+              this.data += `(...truncated at ${MAX_OUTPUT} chars...)`
+              this.overflowed = true
+            }
+          },
+          // kill container if too much output
+          // docker.containers.kill(containerId, () ->)
+        }
+      }
+
+      const stdout = createStringOutputStream('stdout')
+      const stderr = createStringOutputStream('stderr')
+
+      container.modem.demuxStream(stream, stdout, stderr)
+
+      stream.on('error', err =>
+        logger.error(
+          { err, containerId },
+          'error reading from container stream'
+        )
+      )
+
+      stream.on('end', () =>
+        attachStreamHandler(null, { stdout: stdout.data, stderr: stderr.data })
+      )
+    })
+  },
+
+  waitForContainer(containerId, timeout, _callback) {
+    const callback = _.once(_callback)
+
+    const container = dockerode.getContainer(containerId)
+
+    let timedOut = false
+    const timeoutId = setTimeout(() => {
+      timedOut = true
+      logger.log({ containerId }, 'timeout reached, killing container')
+      container.kill(err => {
+        logger.warn({ err, containerId }, 'failed to kill container')
+      })
+    }, timeout)
+
+    logger.log({ containerId }, 'waiting for docker container')
+    container.wait((error, res) => {
+      if (error != null) {
+        clearTimeout(timeoutId)
+        logger.error({ err: error, containerId }, 'error waiting for container')
+        return callback(error)
+      }
+      if (timedOut) {
+        logger.log({ containerId }, 'docker container timed out')
+        error = new Error('container timed out')
+        error.timedout = true
+        callback(error)
+      } else {
+        clearTimeout(timeoutId)
+        logger.log(
+          { containerId, exitCode: res.StatusCode },
+          'docker container returned'
+        )
+        callback(null, res.StatusCode)
+      }
+    })
+  },
+
+  destroyContainer(containerName, containerId, shouldForce, callback) {
+    // We want the containerName for the lock and, ideally, the
+    // containerId to delete.  There is a bug in the docker.io module
+    // where if you delete by name and there is an error, it throws an
+    // async exception, but if you delete by id it just does a normal
+    // error callback. We fall back to deleting by name if no id is
+    // supplied.
+    LockManager.runWithLock(
+      containerName,
+      releaseLock =>
+        DockerRunner._destroyContainer(
+          containerId || containerName,
+          shouldForce,
+          releaseLock
+        ),
+      callback
+    )
+  },
+
+  _destroyContainer(containerId, shouldForce, callback) {
+    logger.log({ containerId }, 'destroying docker container')
+    const container = dockerode.getContainer(containerId)
+    container.remove({ force: shouldForce === true, v: true }, error => {
+      if (error != null && error.statusCode === 404) {
+        logger.warn(
+          { err: error, containerId },
+          'container not found, continuing'
+        )
+        error = null
+      }
+      if (error != null) {
+        logger.error({ err: error, containerId }, 'error destroying container')
+      } else {
+        logger.log({ containerId }, 'destroyed container')
+      }
+      callback(error)
+    })
+  },
+
+  // handle expiry of docker containers
+
+  MAX_CONTAINER_AGE: Settings.clsi.docker.maxContainerAge || ONE_HOUR_IN_MS,
+
+  examineOldContainer(container, callback) {
+    const name = container.Name || (container.Names && container.Names[0])
+    const created = container.Created * 1000 // creation time is returned in seconds
+    const now = Date.now()
+    const age = now - created
+    const maxAge = DockerRunner.MAX_CONTAINER_AGE
+    const ttl = maxAge - age
+    logger.log(
+      { containerName: name, created, now, age, maxAge, ttl },
+      'checking whether to destroy container'
+    )
+    return { name, id: container.Id, ttl }
+  },
+
+  destroyOldContainers(callback) {
+    dockerode.listContainers({ all: true }, (error, containers) => {
+      if (error != null) {
+        return callback(error)
+      }
+      const jobs = []
+      for (const container of containers) {
+        const { name, id, ttl } = DockerRunner.examineOldContainer(container)
+        if (name.slice(0, 9) === '/project-' && ttl <= 0) {
+          // strip the / prefix
+          // the LockManager uses the plain container name
+          const plainName = name.slice(1)
+          jobs.push(cb =>
+            DockerRunner.destroyContainer(plainName, id, false, () => cb())
+          )
+        }
+      }
+      // Ignore errors because some containers get stuck but
+      // will be destroyed next time
+      async.series(jobs, callback)
+    })
+  },
+
+  startContainerMonitor() {
+    logger.log(
+      { maxAge: DockerRunner.MAX_CONTAINER_AGE },
+      'starting container expiry'
+    )
+
+    // guarantee only one monitor is running
+    DockerRunner.stopContainerMonitor()
+
+    // randomise the start time
+    const randomDelay = Math.floor(Math.random() * 5 * 60 * 1000)
+    containerMonitorTimeout = setTimeout(() => {
+      containerMonitorInterval = setInterval(
+        () =>
+          DockerRunner.destroyOldContainers(err => {
+            if (err) {
+              logger.error({ err }, 'failed to destroy old containers')
+            }
+          }),
+        ONE_HOUR_IN_MS
+      )
+    }, randomDelay)
+  },
+
+  stopContainerMonitor() {
+    if (containerMonitorTimeout) {
+      clearTimeout(containerMonitorTimeout)
+      containerMonitorTimeout = undefined
+    }
+    if (containerMonitorInterval) {
+      clearInterval(containerMonitorInterval)
+      containerMonitorInterval = undefined
+    }
+  },
+}
+
+DockerRunner.startContainerMonitor()
+
+module.exports = DockerRunner

+ 57 - 0
services/clsi/app/js/DraftModeManager.js

@@ -0,0 +1,57 @@
+/* eslint-disable
+    camelcase,
+    handle-callback-err,
+    no-useless-escape,
+*/
+// TODO: This file was created by bulk-decaffeinate.
+// Fix any style issues and re-enable lint.
+/*
+ * decaffeinate suggestions:
+ * DS102: Remove unnecessary code created because of implicit returns
+ * DS207: Consider shorter variations of null checks
+ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
+ */
+let DraftModeManager
+const fs = require('fs')
+const logger = require('logger-sharelatex')
+
+module.exports = DraftModeManager = {
+  injectDraftMode(filename, callback) {
+    if (callback == null) {
+      callback = function (error) {}
+    }
+    return fs.readFile(filename, 'utf8', function (error, content) {
+      if (error != null) {
+        return callback(error)
+      }
+      // avoid adding draft mode more than once
+      if (
+        (content != null
+          ? content.indexOf('\\documentclass[draft')
+          : undefined) >= 0
+      ) {
+        return callback()
+      }
+      const modified_content = DraftModeManager._injectDraftOption(content)
+      logger.log(
+        {
+          content: content.slice(0, 1024), // \documentclass is normally v near the top
+          modified_content: modified_content.slice(0, 1024),
+          filename,
+        },
+        'injected draft class'
+      )
+      return fs.writeFile(filename, modified_content, callback)
+    })
+  },
+
+  _injectDraftOption(content) {
+    return (
+      content
+        // With existing options (must be first, otherwise both are applied)
+        .replace(/\\documentclass\[/g, '\\documentclass[draft,')
+        // Without existing options
+        .replace(/\\documentclass\{/g, '\\documentclass[draft]{')
+    )
+  },
+}

+ 41 - 0
services/clsi/app/js/Errors.js

@@ -0,0 +1,41 @@
+/* eslint-disable
+    no-proto,
+    no-unused-vars,
+*/
+// TODO: This file was created by bulk-decaffeinate.
+// Fix any style issues and re-enable lint.
+const OError = require('@overleaf/o-error')
+
+let Errors
+var NotFoundError = function (message) {
+  const error = new Error(message)
+  error.name = 'NotFoundError'
+  error.__proto__ = NotFoundError.prototype
+  return error
+}
+NotFoundError.prototype.__proto__ = Error.prototype
+
+var FilesOutOfSyncError = function (message) {
+  const error = new Error(message)
+  error.name = 'FilesOutOfSyncError'
+  error.__proto__ = FilesOutOfSyncError.prototype
+  return error
+}
+FilesOutOfSyncError.prototype.__proto__ = Error.prototype
+
+var AlreadyCompilingError = function (message) {
+  const error = new Error(message)
+  error.name = 'AlreadyCompilingError'
+  error.__proto__ = AlreadyCompilingError.prototype
+  return error
+}
+AlreadyCompilingError.prototype.__proto__ = Error.prototype
+
+class TimedOutError extends OError {}
+
+module.exports = Errors = {
+  TimedOutError,
+  NotFoundError,
+  FilesOutOfSyncError,
+  AlreadyCompilingError,
+}

+ 237 - 0
services/clsi/app/js/LatexRunner.js

@@ -0,0 +1,237 @@
+/* eslint-disable
+    camelcase,
+    handle-callback-err,
+    no-return-assign,
+    no-unused-vars,
+*/
+// TODO: This file was created by bulk-decaffeinate.
+// Fix any style issues and re-enable lint.
+/*
+ * decaffeinate suggestions:
+ * DS102: Remove unnecessary code created because of implicit returns
+ * DS103: Rewrite code to no longer use __guard__
+ * DS207: Consider shorter variations of null checks
+ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
+ */
+let LatexRunner
+const Path = require('path')
+const Settings = require('@overleaf/settings')
+const logger = require('logger-sharelatex')
+const Metrics = require('./Metrics')
+const CommandRunner = require('./CommandRunner')
+const fs = require('fs')
+
+const ProcessTable = {} // table of currently running jobs (pids or docker container names)
+
+const TIME_V_METRICS = Object.entries({
+  'cpu-percent': /Percent of CPU this job got: (\d+)/m,
+  'cpu-time': /User time.*: (\d+.\d+)/m,
+  'sys-time': /System time.*: (\d+.\d+)/m,
+})
+
+module.exports = LatexRunner = {
+  runLatex(project_id, options, callback) {
+    let command
+    if (callback == null) {
+      callback = function (error) {}
+    }
+    let {
+      directory,
+      mainFile,
+      compiler,
+      timeout,
+      image,
+      environment,
+      flags,
+      compileGroup,
+    } = options
+    if (!compiler) {
+      compiler = 'pdflatex'
+    }
+    if (!timeout) {
+      timeout = 60000
+    } // milliseconds
+
+    logger.log(
+      {
+        directory,
+        compiler,
+        timeout,
+        mainFile,
+        environment,
+        flags,
+        compileGroup,
+      },
+      'starting compile'
+    )
+
+    // We want to run latexmk on the tex file which we will automatically
+    // generate from the Rtex/Rmd/md file.
+    mainFile = mainFile.replace(/\.(Rtex|md|Rmd)$/, '.tex')
+
+    if (compiler === 'pdflatex') {
+      command = LatexRunner._pdflatexCommand(mainFile, flags)
+    } else if (compiler === 'latex') {
+      command = LatexRunner._latexCommand(mainFile, flags)
+    } else if (compiler === 'xelatex') {
+      command = LatexRunner._xelatexCommand(mainFile, flags)
+    } else if (compiler === 'lualatex') {
+      command = LatexRunner._lualatexCommand(mainFile, flags)
+    } else {
+      return callback(new Error(`unknown compiler: ${compiler}`))
+    }
+
+    if (Settings.clsi != null ? Settings.clsi.strace : undefined) {
+      command = ['strace', '-o', 'strace', '-ff'].concat(command)
+    }
+
+    const id = `${project_id}` // record running project under this id
+
+    return (ProcessTable[id] = CommandRunner.run(
+      project_id,
+      command,
+      directory,
+      image,
+      timeout,
+      environment,
+      compileGroup,
+      function (error, output) {
+        delete ProcessTable[id]
+        if (error != null) {
+          return callback(error)
+        }
+        const runs =
+          __guard__(
+            __guard__(output != null ? output.stderr : undefined, x1 =>
+              x1.match(/^Run number \d+ of .*latex/gm)
+            ),
+            x => x.length
+          ) || 0
+        const failed =
+          __guard__(output != null ? output.stdout : undefined, x2 =>
+            x2.match(/^Latexmk: Errors/m)
+          ) != null
+            ? 1
+            : 0
+        // counters from latexmk output
+        const stats = {}
+        stats['latexmk-errors'] = failed
+        stats['latex-runs'] = runs
+        stats['latex-runs-with-errors'] = failed ? runs : 0
+        stats[`latex-runs-${runs}`] = 1
+        stats[`latex-runs-with-errors-${runs}`] = failed ? 1 : 0
+        // timing information from /usr/bin/time
+        const timings = {}
+        const stderr = (output && output.stderr) || ''
+        if (stderr.includes('Command being timed:')) {
+          // Add metrics for runs with `$ time -v ...`
+          for (const [timing, matcher] of TIME_V_METRICS) {
+            const match = stderr.match(matcher)
+            if (match) {
+              timings[timing] = parseFloat(match[1])
+            }
+          }
+        }
+        // record output files
+        LatexRunner.writeLogOutput(project_id, directory, output, () => {
+          return callback(error, output, stats, timings)
+        })
+      }
+    ))
+  },
+
+  writeLogOutput(project_id, directory, output, callback) {
+    if (!output) {
+      return callback()
+    }
+    // internal method for writing non-empty log files
+    function _writeFile(file, content, cb) {
+      if (content && content.length > 0) {
+        fs.writeFile(file, content, err => {
+          if (err) {
+            logger.error({ project_id, file }, 'error writing log file') // don't fail on error
+          }
+          cb()
+        })
+      } else {
+        cb()
+      }
+    }
+    // write stdout and stderr, ignoring errors
+    _writeFile(Path.join(directory, 'output.stdout'), output.stdout, () => {
+      _writeFile(Path.join(directory, 'output.stderr'), output.stderr, () => {
+        callback()
+      })
+    })
+  },
+
+  killLatex(project_id, callback) {
+    if (callback == null) {
+      callback = function (error) {}
+    }
+    const id = `${project_id}`
+    logger.log({ id }, 'killing running compile')
+    if (ProcessTable[id] == null) {
+      logger.warn({ id }, 'no such project to kill')
+      return callback(null)
+    } else {
+      return CommandRunner.kill(ProcessTable[id], callback)
+    }
+  },
+
+  _latexmkBaseCommand(flags) {
+    let args = [
+      'latexmk',
+      '-cd',
+      '-f',
+      '-jobname=output',
+      '-auxdir=$COMPILE_DIR',
+      '-outdir=$COMPILE_DIR',
+      '-synctex=1',
+      '-interaction=batchmode',
+    ]
+    if (flags) {
+      args = args.concat(flags)
+    }
+    return (
+      __guard__(
+        Settings != null ? Settings.clsi : undefined,
+        x => x.latexmkCommandPrefix
+      ) || []
+    ).concat(args)
+  },
+
+  _pdflatexCommand(mainFile, flags) {
+    return LatexRunner._latexmkBaseCommand(flags).concat([
+      '-pdf',
+      Path.join('$COMPILE_DIR', mainFile),
+    ])
+  },
+
+  _latexCommand(mainFile, flags) {
+    return LatexRunner._latexmkBaseCommand(flags).concat([
+      '-pdfdvi',
+      Path.join('$COMPILE_DIR', mainFile),
+    ])
+  },
+
+  _xelatexCommand(mainFile, flags) {
+    return LatexRunner._latexmkBaseCommand(flags).concat([
+      '-xelatex',
+      Path.join('$COMPILE_DIR', mainFile),
+    ])
+  },
+
+  _lualatexCommand(mainFile, flags) {
+    return LatexRunner._latexmkBaseCommand(flags).concat([
+      '-lualatex',
+      Path.join('$COMPILE_DIR', mainFile),
+    ])
+  },
+}
+
+function __guard__(value, transform) {
+  return typeof value !== 'undefined' && value !== null
+    ? transform(value)
+    : undefined
+}

+ 103 - 0
services/clsi/app/js/LocalCommandRunner.js

@@ -0,0 +1,103 @@
+/* eslint-disable
+    camelcase,
+    handle-callback-err,
+    no-return-assign,
+    no-unused-vars,
+*/
+// TODO: This file was created by bulk-decaffeinate.
+// Fix any style issues and re-enable lint.
+/*
+ * decaffeinate suggestions:
+ * DS101: Remove unnecessary use of Array.from
+ * DS102: Remove unnecessary code created because of implicit returns
+ * DS207: Consider shorter variations of null checks
+ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
+ */
+let CommandRunner
+const { spawn } = require('child_process')
+const _ = require('lodash')
+const logger = require('logger-sharelatex')
+
+logger.info('using standard command runner')
+
+module.exports = CommandRunner = {
+  run(
+    project_id,
+    command,
+    directory,
+    image,
+    timeout,
+    environment,
+    compileGroup,
+    callback
+  ) {
+    let key, value
+    if (callback == null) {
+      callback = function (error) {}
+    } else {
+      callback = _.once(callback)
+    }
+    command = Array.from(command).map(arg =>
+      arg.toString().replace('$COMPILE_DIR', directory)
+    )
+    logger.log({ project_id, command, directory }, 'running command')
+    logger.warn('timeouts and sandboxing are not enabled with CommandRunner')
+
+    // merge environment settings
+    const env = {}
+    for (key in process.env) {
+      value = process.env[key]
+      env[key] = value
+    }
+    for (key in environment) {
+      value = environment[key]
+      env[key] = value
+    }
+
+    // run command as detached process so it has its own process group (which can be killed if needed)
+    const proc = spawn(command[0], command.slice(1), { cwd: directory, env })
+
+    let stdout = ''
+    proc.stdout.setEncoding('utf8').on('data', data => (stdout += data))
+
+    proc.on('error', function (err) {
+      logger.err(
+        { err, project_id, command, directory },
+        'error running command'
+      )
+      return callback(err)
+    })
+
+    proc.on('close', function (code, signal) {
+      let err
+      logger.info({ code, signal, project_id }, 'command exited')
+      if (signal === 'SIGTERM') {
+        // signal from kill method below
+        err = new Error('terminated')
+        err.terminated = true
+        return callback(err)
+      } else if (code === 1) {
+        // exit status from chktex
+        err = new Error('exited')
+        err.code = code
+        return callback(err)
+      } else {
+        return callback(null, { stdout: stdout })
+      }
+    })
+
+    return proc.pid
+  }, // return process id to allow job to be killed if necessary
+
+  kill(pid, callback) {
+    if (callback == null) {
+      callback = function (error) {}
+    }
+    try {
+      process.kill(-pid) // kill all processes in group
+    } catch (err) {
+      return callback(err)
+    }
+    return callback()
+  },
+}

+ 72 - 0
services/clsi/app/js/LockManager.js

@@ -0,0 +1,72 @@
+/* eslint-disable
+    handle-callback-err,
+    no-unused-vars,
+*/
+// TODO: This file was created by bulk-decaffeinate.
+// Fix any style issues and re-enable lint.
+/*
+ * decaffeinate suggestions:
+ * DS101: Remove unnecessary use of Array.from
+ * DS102: Remove unnecessary code created because of implicit returns
+ * DS207: Consider shorter variations of null checks
+ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
+ */
+let LockManager
+const Settings = require('@overleaf/settings')
+const logger = require('logger-sharelatex')
+const Lockfile = require('lockfile') // from https://github.com/npm/lockfile
+const Errors = require('./Errors')
+const fs = require('fs')
+const Path = require('path')
+module.exports = LockManager = {
+  LOCK_TEST_INTERVAL: 1000, // 50ms between each test of the lock
+  MAX_LOCK_WAIT_TIME: 15000, // 10s maximum time to spend trying to get the lock
+  LOCK_STALE: 5 * 60 * 1000, // 5 mins time until lock auto expires
+
+  runWithLock(path, runner, callback) {
+    if (callback == null) {
+      callback = function (error) {}
+    }
+    const lockOpts = {
+      wait: this.MAX_LOCK_WAIT_TIME,
+      pollPeriod: this.LOCK_TEST_INTERVAL,
+      stale: this.LOCK_STALE,
+    }
+    return Lockfile.lock(path, lockOpts, function (error) {
+      if ((error != null ? error.code : undefined) === 'EEXIST') {
+        return callback(new Errors.AlreadyCompilingError('compile in progress'))
+      } else if (error != null) {
+        return fs.lstat(path, (statLockErr, statLock) =>
+          fs.lstat(Path.dirname(path), (statDirErr, statDir) =>
+            fs.readdir(Path.dirname(path), function (readdirErr, readdirDir) {
+              logger.err(
+                {
+                  error,
+                  path,
+                  statLock,
+                  statLockErr,
+                  statDir,
+                  statDirErr,
+                  readdirErr,
+                  readdirDir,
+                },
+                'unable to get lock'
+              )
+              return callback(error)
+            })
+          )
+        )
+      } else {
+        return runner((error1, ...args) =>
+          Lockfile.unlock(path, function (error2) {
+            error = error1 || error2
+            if (error != null) {
+              return callback(error)
+            }
+            return callback(null, ...Array.from(args))
+          })
+        )
+      }
+    })
+  },
+}

+ 3 - 0
services/clsi/app/js/Metrics.js

@@ -0,0 +1,3 @@
+// TODO: This file was created by bulk-decaffeinate.
+// Sanity-check the conversion and remove this comment.
+module.exports = require('@overleaf/metrics')

+ 563 - 0
services/clsi/app/js/OutputCacheManager.js

@@ -0,0 +1,563 @@
+/* eslint-disable
+    handle-callback-err,
+*/
+// TODO: This file was created by bulk-decaffeinate.
+// Fix any style issues and re-enable lint.
+/*
+ * decaffeinate suggestions:
+ * DS101: Remove unnecessary use of Array.from
+ * DS102: Remove unnecessary code created because of implicit returns
+ * DS103: Rewrite code to no longer use __guard__
+ * DS104: Avoid inline assignments
+ * DS204: Change includes calls to have a more natural evaluation order
+ * DS207: Consider shorter variations of null checks
+ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
+ */
+let OutputCacheManager
+const async = require('async')
+const fs = require('fs')
+const fse = require('fs-extra')
+const Path = require('path')
+const logger = require('logger-sharelatex')
+const _ = require('lodash')
+const Settings = require('@overleaf/settings')
+const crypto = require('crypto')
+const Metrics = require('./Metrics')
+
+const OutputFileOptimiser = require('./OutputFileOptimiser')
+const ContentCacheManager = require('./ContentCacheManager')
+const { TimedOutError } = require('./Errors')
+
+module.exports = OutputCacheManager = {
+  CONTENT_SUBDIR: 'content',
+  CACHE_SUBDIR: 'generated-files',
+  ARCHIVE_SUBDIR: 'archived-logs',
+  // build id is HEXDATE-HEXRANDOM from Date.now()and RandomBytes
+  // for backwards compatibility, make the randombytes part optional
+  BUILD_REGEX: /^[0-9a-f]+(-[0-9a-f]+)?$/,
+  CONTENT_REGEX: /^[0-9a-f]+(-[0-9a-f]+)?$/,
+  CACHE_LIMIT: 2, // maximum number of cache directories
+  CACHE_AGE: 60 * 60 * 1000, // up to one hour old
+
+  path(buildId, file) {
+    // used by static server, given build id return '.cache/clsi/buildId'
+    if (buildId.match(OutputCacheManager.BUILD_REGEX)) {
+      return Path.join(OutputCacheManager.CACHE_SUBDIR, buildId, file)
+    } else {
+      // for invalid build id, return top level
+      return file
+    }
+  },
+
+  generateBuildId(callback) {
+    // generate a secure build id from Date.now() and 8 random bytes in hex
+    if (callback == null) {
+      callback = function (error, buildId) {}
+    }
+    return crypto.randomBytes(8, function (err, buf) {
+      if (err != null) {
+        return callback(err)
+      }
+      const random = buf.toString('hex')
+      const date = Date.now().toString(16)
+      return callback(err, `${date}-${random}`)
+    })
+  },
+
+  saveOutputFiles(
+    { request, stats, timings },
+    outputFiles,
+    compileDir,
+    outputDir,
+    callback
+  ) {
+    if (callback == null) {
+      callback = function (error) {}
+    }
+    return OutputCacheManager.generateBuildId(function (err, buildId) {
+      if (err != null) {
+        return callback(err)
+      }
+      return OutputCacheManager.saveOutputFilesInBuildDir(
+        outputFiles,
+        compileDir,
+        outputDir,
+        buildId,
+        function (err, result) {
+          if (err != null) {
+            return callback(err)
+          }
+          OutputCacheManager.collectOutputPdfSize(
+            result,
+            outputDir,
+            stats,
+            (err, result) => {
+              if (err) return callback(err, result)
+
+              if (!Settings.enablePdfCaching || !request.enablePdfCaching) {
+                return callback(null, result)
+              }
+
+              OutputCacheManager.saveStreamsInContentDir(
+                { stats, timings },
+                result,
+                compileDir,
+                outputDir,
+                callback
+              )
+            }
+          )
+        }
+      )
+    })
+  },
+
+  saveOutputFilesInBuildDir(
+    outputFiles,
+    compileDir,
+    outputDir,
+    buildId,
+    callback
+  ) {
+    // make a compileDir/CACHE_SUBDIR/build_id directory and
+    // copy all the output files into it
+    if (callback == null) {
+      callback = function (error) {}
+    }
+    const cacheRoot = Path.join(outputDir, OutputCacheManager.CACHE_SUBDIR)
+    // Put the files into a new cache subdirectory
+    const cacheDir = Path.join(
+      outputDir,
+      OutputCacheManager.CACHE_SUBDIR,
+      buildId
+    )
+    // Is it a per-user compile? check if compile directory is PROJECTID-USERID
+    const perUser = Path.basename(compileDir).match(
+      /^[0-9a-f]{24}-[0-9a-f]{24}$/
+    )
+
+    // Archive logs in background
+    if (
+      (Settings.clsi != null ? Settings.clsi.archive_logs : undefined) ||
+      (Settings.clsi != null ? Settings.clsi.strace : undefined)
+    ) {
+      OutputCacheManager.archiveLogs(
+        outputFiles,
+        compileDir,
+        outputDir,
+        buildId,
+        function (err) {
+          if (err != null) {
+            return logger.warn({ err }, 'erroring archiving log files')
+          }
+        }
+      )
+    }
+
+    // make the new cache directory
+    return fse.ensureDir(cacheDir, function (err) {
+      if (err != null) {
+        logger.error(
+          { err, directory: cacheDir },
+          'error creating cache directory'
+        )
+        return callback(err, outputFiles)
+      } else {
+        // copy all the output files into the new cache directory
+        const results = []
+        return async.mapSeries(
+          outputFiles,
+          function (file, cb) {
+            // don't send dot files as output, express doesn't serve them
+            if (OutputCacheManager._fileIsHidden(file.path)) {
+              logger.debug(
+                { compileDir, path: file.path },
+                'ignoring dotfile in output'
+              )
+              return cb()
+            }
+            // copy other files into cache directory if valid
+            const newFile = _.clone(file)
+            const [src, dst] = Array.from([
+              Path.join(compileDir, file.path),
+              Path.join(cacheDir, file.path),
+            ])
+            return OutputCacheManager._checkFileIsSafe(
+              src,
+              function (err, isSafe) {
+                if (err != null) {
+                  return cb(err)
+                }
+                if (!isSafe) {
+                  return cb()
+                }
+                return OutputCacheManager._checkIfShouldCopy(
+                  src,
+                  function (err, shouldCopy) {
+                    if (err != null) {
+                      return cb(err)
+                    }
+                    if (!shouldCopy) {
+                      return cb()
+                    }
+                    return OutputCacheManager._copyFile(
+                      src,
+                      dst,
+                      function (err) {
+                        if (err != null) {
+                          return cb(err)
+                        }
+                        newFile.build = buildId // attach a build id if we cached the file
+                        results.push(newFile)
+                        return cb()
+                      }
+                    )
+                  }
+                )
+              }
+            )
+          },
+          function (err) {
+            if (err != null) {
+              // pass back the original files if we encountered *any* error
+              callback(err, outputFiles)
+              // clean up the directory we just created
+              return fse.remove(cacheDir, function (err) {
+                if (err != null) {
+                  return logger.error(
+                    { err, dir: cacheDir },
+                    'error removing cache dir after failure'
+                  )
+                }
+              })
+            } else {
+              // pass back the list of new files in the cache
+              callback(err, results)
+              // let file expiry run in the background, expire all previous files if per-user
+              return OutputCacheManager.expireOutputFiles(cacheRoot, {
+                keep: buildId,
+                limit: perUser ? 1 : null,
+              })
+            }
+          }
+        )
+      }
+    })
+  },
+
+  collectOutputPdfSize(outputFiles, outputDir, stats, callback) {
+    const outputFile = outputFiles.find(x => x.path === 'output.pdf')
+    if (!outputFile) return callback(null, outputFiles)
+    const outputFilePath = Path.join(
+      outputDir,
+      OutputCacheManager.path(outputFile.build, outputFile.path)
+    )
+    fs.stat(outputFilePath, (err, stat) => {
+      if (err) return callback(err, outputFiles)
+
+      outputFile.size = stat.size
+      stats['pdf-size'] = outputFile.size
+      callback(null, outputFiles)
+    })
+  },
+
+  saveStreamsInContentDir(
+    { stats, timings },
+    outputFiles,
+    compileDir,
+    outputDir,
+    callback
+  ) {
+    const cacheRoot = Path.join(outputDir, OutputCacheManager.CONTENT_SUBDIR)
+    // check if content dir exists
+    OutputCacheManager.ensureContentDir(cacheRoot, function (err, contentDir) {
+      if (err) return callback(err, outputFiles)
+
+      const outputFile = outputFiles.find(x => x.path === 'output.pdf')
+      if (outputFile) {
+        // possibly we should copy the file from the build dir here
+        const outputFilePath = Path.join(
+          outputDir,
+          OutputCacheManager.path(outputFile.build, outputFile.path)
+        )
+        const pdfSize = outputFile.size
+        const timer = new Metrics.Timer('compute-pdf-ranges')
+        ContentCacheManager.update(
+          contentDir,
+          outputFilePath,
+          pdfSize,
+          timings.compile,
+          function (err, result) {
+            if (err && err instanceof TimedOutError) {
+              logger.warn(
+                { err, outputDir, stats, timings },
+                'pdf caching timed out'
+              )
+              stats['pdf-caching-timed-out'] = 1
+              return callback(null, outputFiles)
+            }
+            if (err) return callback(err, outputFiles)
+            const [contentRanges, newContentRanges, reclaimedSpace] = result
+
+            if (Settings.enablePdfCachingDark) {
+              // In dark mode we are doing the computation only and do not emit
+              //  any ranges to the frontend.
+            } else {
+              outputFile.contentId = Path.basename(contentDir)
+              outputFile.ranges = contentRanges
+            }
+
+            timings['compute-pdf-caching'] = timer.done()
+            stats['pdf-caching-n-ranges'] = contentRanges.length
+            stats['pdf-caching-total-ranges-size'] = contentRanges.reduce(
+              (sum, next) => sum + (next.end - next.start),
+              0
+            )
+            stats['pdf-caching-n-new-ranges'] = newContentRanges.length
+            stats['pdf-caching-new-ranges-size'] = newContentRanges.reduce(
+              (sum, next) => sum + (next.end - next.start),
+              0
+            )
+            stats['pdf-caching-reclaimed-space'] = reclaimedSpace
+            callback(null, outputFiles)
+          }
+        )
+      } else {
+        callback(null, outputFiles)
+      }
+    })
+  },
+
+  ensureContentDir(contentRoot, callback) {
+    fse.ensureDir(contentRoot, function (err) {
+      if (err != null) {
+        return callback(err)
+      }
+      fs.readdir(contentRoot, function (err, results) {
+        const dirs = results.sort()
+        const contentId = dirs.find(dir =>
+          OutputCacheManager.BUILD_REGEX.test(dir)
+        )
+        if (contentId) {
+          callback(null, Path.join(contentRoot, contentId))
+        } else {
+          // make a content directory
+          OutputCacheManager.generateBuildId(function (err, contentId) {
+            if (err) {
+              return callback(err)
+            }
+            const contentDir = Path.join(contentRoot, contentId)
+            fse.ensureDir(contentDir, function (err) {
+              if (err) {
+                return callback(err)
+              }
+              return callback(null, contentDir)
+            })
+          })
+        }
+      })
+    })
+  },
+
+  archiveLogs(outputFiles, compileDir, outputDir, buildId, callback) {
+    if (callback == null) {
+      callback = function (error) {}
+    }
+    const archiveDir = Path.join(
+      outputDir,
+      OutputCacheManager.ARCHIVE_SUBDIR,
+      buildId
+    )
+    logger.log({ dir: archiveDir }, 'archiving log files for project')
+    return fse.ensureDir(archiveDir, function (err) {
+      if (err != null) {
+        return callback(err)
+      }
+      return async.mapSeries(
+        outputFiles,
+        function (file, cb) {
+          const [src, dst] = Array.from([
+            Path.join(compileDir, file.path),
+            Path.join(archiveDir, file.path),
+          ])
+          return OutputCacheManager._checkFileIsSafe(
+            src,
+            function (err, isSafe) {
+              if (err != null) {
+                return cb(err)
+              }
+              if (!isSafe) {
+                return cb()
+              }
+              return OutputCacheManager._checkIfShouldArchive(
+                src,
+                function (err, shouldArchive) {
+                  if (err != null) {
+                    return cb(err)
+                  }
+                  if (!shouldArchive) {
+                    return cb()
+                  }
+                  return OutputCacheManager._copyFile(src, dst, cb)
+                }
+              )
+            }
+          )
+        },
+        callback
+      )
+    })
+  },
+
+  expireOutputFiles(cacheRoot, options, callback) {
+    // look in compileDir for build dirs and delete if > N or age of mod time > T
+    if (callback == null) {
+      callback = function (error) {}
+    }
+    return fs.readdir(cacheRoot, function (err, results) {
+      if (err != null) {
+        if (err.code === 'ENOENT') {
+          return callback(null)
+        } // cache directory is empty
+        logger.error({ err, project_id: cacheRoot }, 'error clearing cache')
+        return callback(err)
+      }
+
+      const dirs = results.sort().reverse()
+      const currentTime = Date.now()
+
+      const isExpired = function (dir, index) {
+        if ((options != null ? options.keep : undefined) === dir) {
+          return false
+        }
+        // remove any directories over the requested (non-null) limit
+        if (
+          (options != null ? options.limit : undefined) != null &&
+          index > options.limit
+        ) {
+          return true
+        }
+        // remove any directories over the hard limit
+        if (index > OutputCacheManager.CACHE_LIMIT) {
+          return true
+        }
+        // we can get the build time from the first part of the directory name DDDD-RRRR
+        // DDDD is date and RRRR is random bytes
+        const dirTime = parseInt(
+          __guard__(dir.split('-'), x => x[0]),
+          16
+        )
+        const age = currentTime - dirTime
+        return age > OutputCacheManager.CACHE_AGE
+      }
+
+      const toRemove = _.filter(dirs, isExpired)
+
+      const removeDir = (dir, cb) =>
+        fse.remove(Path.join(cacheRoot, dir), function (err, result) {
+          logger.log({ cache: cacheRoot, dir }, 'removed expired cache dir')
+          if (err != null) {
+            logger.error({ err, dir }, 'cache remove error')
+          }
+          return cb(err, result)
+        })
+      return async.eachSeries(
+        toRemove,
+        (dir, cb) => removeDir(dir, cb),
+        callback
+      )
+    })
+  },
+
+  _fileIsHidden(path) {
+    return (path != null ? path.match(/^\.|\/\./) : undefined) != null
+  },
+
+  _checkFileIsSafe(src, callback) {
+    // check if we have a valid file to copy into the cache
+    if (callback == null) {
+      callback = function (error, isSafe) {}
+    }
+    return fs.stat(src, function (err, stats) {
+      if ((err != null ? err.code : undefined) === 'ENOENT') {
+        logger.warn(
+          { err, file: src },
+          'file has disappeared before copying to build cache'
+        )
+        return callback(err, false)
+      } else if (err != null) {
+        // some other problem reading the file
+        logger.error({ err, file: src }, 'stat error for file in cache')
+        return callback(err, false)
+      } else if (!stats.isFile()) {
+        // other filetype - reject it
+        logger.warn(
+          { src, stat: stats },
+          'nonfile output - refusing to copy to cache'
+        )
+        return callback(null, false)
+      } else {
+        // it's a plain file, ok to copy
+        return callback(null, true)
+      }
+    })
+  },
+
+  _copyFile(src, dst, callback) {
+    // copy output file into the cache
+    return fse.copy(src, dst, function (err) {
+      if ((err != null ? err.code : undefined) === 'ENOENT') {
+        logger.warn(
+          { err, file: src },
+          'file has disappeared when copying to build cache'
+        )
+        return callback(err, false)
+      } else if (err != null) {
+        logger.error({ err, src, dst }, 'copy error for file in cache')
+        return callback(err)
+      } else {
+        if (
+          Settings.clsi != null ? Settings.clsi.optimiseInDocker : undefined
+        ) {
+          // don't run any optimisations on the pdf when they are done
+          // in the docker container
+          return callback()
+        } else {
+          // call the optimiser for the file too
+          return OutputFileOptimiser.optimiseFile(src, dst, callback)
+        }
+      }
+    })
+  },
+
+  _checkIfShouldCopy(src, callback) {
+    if (callback == null) {
+      callback = function (err, shouldCopy) {}
+    }
+    return callback(null, !Path.basename(src).match(/^strace/))
+  },
+
+  _checkIfShouldArchive(src, callback) {
+    let needle
+    if (callback == null) {
+      callback = function (err, shouldCopy) {}
+    }
+    if (Path.basename(src).match(/^strace/)) {
+      return callback(null, true)
+    }
+    if (
+      (Settings.clsi != null ? Settings.clsi.archive_logs : undefined) &&
+      ((needle = Path.basename(src)),
+      ['output.log', 'output.blg'].includes(needle))
+    ) {
+      return callback(null, true)
+    }
+    return callback(null, false)
+  },
+}
+
+function __guard__(value, transform) {
+  return typeof value !== 'undefined' && value !== null
+    ? transform(value)
+    : undefined
+}

+ 78 - 0
services/clsi/app/js/OutputFileFinder.js

@@ -0,0 +1,78 @@
+let OutputFileFinder
+const Path = require('path')
+const _ = require('lodash')
+const { spawn } = require('child_process')
+const logger = require('logger-sharelatex')
+
+module.exports = OutputFileFinder = {
+  findOutputFiles(resources, directory, callback) {
+    const incomingResources = new Set(resources.map(resource => resource.path))
+
+    OutputFileFinder._getAllFiles(directory, function (error, allFiles) {
+      if (allFiles == null) {
+        allFiles = []
+      }
+      if (error) {
+        logger.err({ err: error }, 'error finding all output files')
+        return callback(error)
+      }
+      const outputFiles = []
+      for (const file of allFiles) {
+        if (!incomingResources.has(file)) {
+          outputFiles.push({
+            path: file,
+            type: Path.extname(file).replace(/^\./, '') || undefined,
+          })
+        }
+      }
+      callback(null, outputFiles, allFiles)
+    })
+  },
+
+  _getAllFiles(directory, callback) {
+    callback = _.once(callback)
+    // don't include clsi-specific files/directories in the output list
+    const EXCLUDE_DIRS = [
+      '-name',
+      '.cache',
+      '-o',
+      '-name',
+      '.archive',
+      '-o',
+      '-name',
+      '.project-*',
+    ]
+    const args = [
+      directory,
+      '(',
+      ...EXCLUDE_DIRS,
+      ')',
+      '-prune',
+      '-o',
+      '-type',
+      'f',
+      '-print',
+    ]
+    logger.log({ args }, 'running find command')
+
+    const proc = spawn('find', args)
+    let stdout = ''
+    proc.stdout.setEncoding('utf8').on('data', chunk => (stdout += chunk))
+    proc.on('error', callback)
+    proc.on('close', function (code) {
+      if (code !== 0) {
+        logger.warn(
+          { directory, code },
+          "find returned error, directory likely doesn't exist"
+        )
+        return callback(null, [])
+      }
+      let fileList = stdout.trim().split('\n')
+      fileList = fileList.map(function (file) {
+        // Strip leading directory
+        return Path.relative(directory, file)
+      })
+      callback(null, fileList)
+    })
+  },
+}

+ 103 - 0
services/clsi/app/js/OutputFileOptimiser.js

@@ -0,0 +1,103 @@
+/* eslint-disable
+    handle-callback-err,
+    no-return-assign,
+    no-undef,
+    no-unused-vars,
+    node/no-deprecated-api,
+*/
+// TODO: This file was created by bulk-decaffeinate.
+// Fix any style issues and re-enable lint.
+/*
+ * decaffeinate suggestions:
+ * DS102: Remove unnecessary code created because of implicit returns
+ * DS207: Consider shorter variations of null checks
+ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
+ */
+let OutputFileOptimiser
+const fs = require('fs')
+const Path = require('path')
+const { spawn } = require('child_process')
+const logger = require('logger-sharelatex')
+const Metrics = require('./Metrics')
+const _ = require('lodash')
+
+module.exports = OutputFileOptimiser = {
+  optimiseFile(src, dst, callback) {
+    // check output file (src) and see if we can optimise it, storing
+    // the result in the build directory (dst)
+    if (callback == null) {
+      callback = function (error) {}
+    }
+    if (src.match(/\/output\.pdf$/)) {
+      return OutputFileOptimiser.checkIfPDFIsOptimised(
+        src,
+        function (err, isOptimised) {
+          if (err != null || isOptimised) {
+            return callback(null)
+          }
+          return OutputFileOptimiser.optimisePDF(src, dst, callback)
+        }
+      )
+    } else {
+      return callback(null)
+    }
+  },
+
+  checkIfPDFIsOptimised(file, callback) {
+    const SIZE = 16 * 1024 // check the header of the pdf
+    const result = Buffer.alloc(SIZE) // fills with zeroes by default
+    return fs.open(file, 'r', function (err, fd) {
+      if (err != null) {
+        return callback(err)
+      }
+      return fs.read(fd, result, 0, SIZE, 0, (errRead, bytesRead, buffer) =>
+        fs.close(fd, function (errClose) {
+          if (errRead != null) {
+            return callback(errRead)
+          }
+          if (typeof errReadClose !== 'undefined' && errReadClose !== null) {
+            return callback(errClose)
+          }
+          const isOptimised =
+            buffer.toString('ascii').indexOf('/Linearized 1') >= 0
+          return callback(null, isOptimised)
+        })
+      )
+    })
+  },
+
+  optimisePDF(src, dst, callback) {
+    if (callback == null) {
+      callback = function (error) {}
+    }
+    const tmpOutput = dst + '.opt'
+    const args = ['--linearize', '--newline-before-endstream', src, tmpOutput]
+    logger.log({ args }, 'running qpdf command')
+
+    const timer = new Metrics.Timer('qpdf')
+    const proc = spawn('qpdf', args)
+    let stdout = ''
+    proc.stdout.setEncoding('utf8').on('data', chunk => (stdout += chunk))
+    callback = _.once(callback) // avoid double call back for error and close event
+    proc.on('error', function (err) {
+      logger.warn({ err, args }, 'qpdf failed')
+      return callback(null)
+    }) // ignore the error
+    return proc.on('close', function (code) {
+      timer.done()
+      if (code !== 0) {
+        logger.warn({ code, args }, 'qpdf returned error')
+        return callback(null) // ignore the error
+      }
+      return fs.rename(tmpOutput, dst, function (err) {
+        if (err != null) {
+          logger.warn(
+            { tmpOutput, dst },
+            'failed to rename output of qpdf command'
+          )
+        }
+        return callback(null)
+      })
+    })
+  }, // ignore the error
+}

+ 207 - 0
services/clsi/app/js/ProjectPersistenceManager.js

@@ -0,0 +1,207 @@
+/* eslint-disable
+    camelcase,
+    handle-callback-err,
+*/
+// TODO: This file was created by bulk-decaffeinate.
+// Fix any style issues and re-enable lint.
+/*
+ * decaffeinate suggestions:
+ * DS101: Remove unnecessary use of Array.from
+ * DS102: Remove unnecessary code created because of implicit returns
+ * DS207: Consider shorter variations of null checks
+ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
+ */
+let ProjectPersistenceManager
+const Metrics = require('./Metrics')
+const UrlCache = require('./UrlCache')
+const CompileManager = require('./CompileManager')
+const db = require('./db')
+const dbQueue = require('./DbQueue')
+const async = require('async')
+const logger = require('logger-sharelatex')
+const oneDay = 24 * 60 * 60 * 1000
+const Settings = require('@overleaf/settings')
+const diskusage = require('diskusage')
+const { callbackify } = require('util')
+
+async function refreshExpiryTimeout() {
+  const paths = [
+    Settings.path.compilesDir,
+    Settings.path.outputDir,
+    Settings.path.clsiCacheDir,
+  ]
+  for (const path of paths) {
+    try {
+      const stats = await diskusage.check(path)
+      const lowDisk = stats.available / stats.total < 0.1
+
+      const lowerExpiry = ProjectPersistenceManager.EXPIRY_TIMEOUT * 0.9
+      if (lowDisk && Settings.project_cache_length_ms / 2 < lowerExpiry) {
+        logger.warn(
+          {
+            stats,
+            newExpiryTimeoutInDays: (lowerExpiry / oneDay).toFixed(2),
+          },
+          'disk running low on space, modifying EXPIRY_TIMEOUT'
+        )
+        ProjectPersistenceManager.EXPIRY_TIMEOUT = lowerExpiry
+        break
+      }
+    } catch (err) {
+      logger.err({ err, path }, 'error getting disk usage')
+    }
+  }
+}
+
+module.exports = ProjectPersistenceManager = {
+  EXPIRY_TIMEOUT: Settings.project_cache_length_ms || oneDay * 2.5,
+
+  promises: {
+    refreshExpiryTimeout,
+  },
+
+  refreshExpiryTimeout: callbackify(refreshExpiryTimeout),
+  markProjectAsJustAccessed(project_id, callback) {
+    if (callback == null) {
+      callback = function (error) {}
+    }
+    const timer = new Metrics.Timer('db-bump-last-accessed')
+    const job = cb =>
+      db.Project.findOrCreate({ where: { project_id } })
+        .spread((project, created) =>
+          project
+            .update({ lastAccessed: new Date() })
+            .then(() => cb())
+            .error(cb)
+        )
+        .error(cb)
+    dbQueue.queue.push(job, error => {
+      timer.done()
+      callback(error)
+    })
+  },
+
+  clearExpiredProjects(callback) {
+    if (callback == null) {
+      callback = function (error) {}
+    }
+    return ProjectPersistenceManager._findExpiredProjectIds(function (
+      error,
+      project_ids
+    ) {
+      if (error != null) {
+        return callback(error)
+      }
+      logger.log({ project_ids }, 'clearing expired projects')
+      const jobs = Array.from(project_ids || []).map(project_id =>
+        (
+          project_id => callback =>
+            ProjectPersistenceManager.clearProjectFromCache(
+              project_id,
+              function (err) {
+                if (err != null) {
+                  logger.error({ err, project_id }, 'error clearing project')
+                }
+                return callback()
+              }
+            )
+        )(project_id)
+      )
+      return async.series(jobs, function (error) {
+        if (error != null) {
+          return callback(error)
+        }
+        return CompileManager.clearExpiredProjects(
+          ProjectPersistenceManager.EXPIRY_TIMEOUT,
+          error => callback()
+        )
+      })
+    })
+  }, // ignore any errors from deleting directories
+
+  clearProject(project_id, user_id, callback) {
+    if (callback == null) {
+      callback = function (error) {}
+    }
+    logger.log({ project_id, user_id }, 'clearing project for user')
+    return CompileManager.clearProject(project_id, user_id, function (error) {
+      if (error != null) {
+        return callback(error)
+      }
+      return ProjectPersistenceManager.clearProjectFromCache(
+        project_id,
+        function (error) {
+          if (error != null) {
+            return callback(error)
+          }
+          return callback()
+        }
+      )
+    })
+  },
+
+  clearProjectFromCache(project_id, callback) {
+    if (callback == null) {
+      callback = function (error) {}
+    }
+    logger.log({ project_id }, 'clearing project from cache')
+    return UrlCache.clearProject(project_id, function (error) {
+      if (error != null) {
+        logger.err({ error, project_id }, 'error clearing project from cache')
+        return callback(error)
+      }
+      return ProjectPersistenceManager._clearProjectFromDatabase(
+        project_id,
+        function (error) {
+          if (error != null) {
+            logger.err(
+              { error, project_id },
+              'error clearing project from database'
+            )
+          }
+          return callback(error)
+        }
+      )
+    })
+  },
+
+  _clearProjectFromDatabase(project_id, callback) {
+    if (callback == null) {
+      callback = function (error) {}
+    }
+    logger.log({ project_id }, 'clearing project from database')
+    const job = cb =>
+      db.Project.destroy({ where: { project_id } })
+        .then(() => cb())
+        .error(cb)
+    return dbQueue.queue.push(job, callback)
+  },
+
+  _findExpiredProjectIds(callback) {
+    if (callback == null) {
+      callback = function (error, project_ids) {}
+    }
+    const job = function (cb) {
+      const keepProjectsFrom = new Date(
+        Date.now() - ProjectPersistenceManager.EXPIRY_TIMEOUT
+      )
+      const q = {}
+      q[db.op.lt] = keepProjectsFrom
+      return db.Project.findAll({ where: { lastAccessed: q } })
+        .then(projects =>
+          cb(
+            null,
+            projects.map(project => project.project_id)
+          )
+        )
+        .error(cb)
+    }
+
+    return dbQueue.queue.push(job, callback)
+  },
+}
+
+logger.log(
+  { EXPIRY_TIMEOUT: ProjectPersistenceManager.EXPIRY_TIMEOUT },
+  'project assets kept timeout'
+)

+ 239 - 0
services/clsi/app/js/RequestParser.js

@@ -0,0 +1,239 @@
+/* eslint-disable
+    handle-callback-err,
+    no-control-regex,
+    no-throw-literal,
+    no-unused-vars,
+    no-useless-escape,
+    valid-typeof,
+*/
+// TODO: This file was created by bulk-decaffeinate.
+// Fix any style issues and re-enable lint.
+/*
+ * decaffeinate suggestions:
+ * DS101: Remove unnecessary use of Array.from
+ * DS102: Remove unnecessary code created because of implicit returns
+ * DS205: Consider reworking code to avoid use of IIFEs
+ * DS207: Consider shorter variations of null checks
+ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
+ */
+let RequestParser
+const settings = require('@overleaf/settings')
+
+module.exports = RequestParser = {
+  VALID_COMPILERS: ['pdflatex', 'latex', 'xelatex', 'lualatex'],
+  MAX_TIMEOUT: 600,
+
+  parse(body, callback) {
+    let resource
+    if (callback == null) {
+      callback = function (error, data) {}
+    }
+    const response = {}
+
+    if (body.compile == null) {
+      return callback('top level object should have a compile attribute')
+    }
+
+    const { compile } = body
+    if (!compile.options) {
+      compile.options = {}
+    }
+
+    try {
+      response.compiler = this._parseAttribute(
+        'compiler',
+        compile.options.compiler,
+        {
+          validValues: this.VALID_COMPILERS,
+          default: 'pdflatex',
+          type: 'string',
+        }
+      )
+      response.enablePdfCaching = this._parseAttribute(
+        'enablePdfCaching',
+        compile.options.enablePdfCaching,
+        {
+          default: false,
+          type: 'boolean',
+        }
+      )
+      response.timeout = this._parseAttribute(
+        'timeout',
+        compile.options.timeout,
+        {
+          default: RequestParser.MAX_TIMEOUT,
+          type: 'number',
+        }
+      )
+      response.imageName = this._parseAttribute(
+        'imageName',
+        compile.options.imageName,
+        {
+          type: 'string',
+          validValues:
+            settings.clsi &&
+            settings.clsi.docker &&
+            settings.clsi.docker.allowedImages,
+        }
+      )
+      response.draft = this._parseAttribute('draft', compile.options.draft, {
+        default: false,
+        type: 'boolean',
+      })
+      response.check = this._parseAttribute('check', compile.options.check, {
+        type: 'string',
+      })
+      response.flags = this._parseAttribute('flags', compile.options.flags, {
+        default: [],
+        type: 'object',
+      })
+      if (settings.allowedCompileGroups) {
+        response.compileGroup = this._parseAttribute(
+          'compileGroup',
+          compile.options.compileGroup,
+          {
+            validValues: settings.allowedCompileGroups,
+            default: '',
+            type: 'string',
+          }
+        )
+      }
+      // The syncType specifies whether the request contains all
+      // resources (full) or only those resources to be updated
+      // in-place (incremental).
+      response.syncType = this._parseAttribute(
+        'syncType',
+        compile.options.syncType,
+        {
+          validValues: ['full', 'incremental'],
+          type: 'string',
+        }
+      )
+
+      // The syncState is an identifier passed in with the request
+      // which has the property that it changes when any resource is
+      // added, deleted, moved or renamed.
+      //
+      // on syncType full the syncState identifier is passed in and
+      // stored
+      //
+      // on syncType incremental the syncState identifier must match
+      // the stored value
+      response.syncState = this._parseAttribute(
+        'syncState',
+        compile.options.syncState,
+        { type: 'string' }
+      )
+
+      if (response.timeout > RequestParser.MAX_TIMEOUT) {
+        response.timeout = RequestParser.MAX_TIMEOUT
+      }
+      response.timeout = response.timeout * 1000 // milliseconds
+
+      response.resources = (() => {
+        const result = []
+        for (resource of Array.from(compile.resources || [])) {
+          result.push(this._parseResource(resource))
+        }
+        return result
+      })()
+
+      const rootResourcePath = this._parseAttribute(
+        'rootResourcePath',
+        compile.rootResourcePath,
+        {
+          default: 'main.tex',
+          type: 'string',
+        }
+      )
+      const originalRootResourcePath = rootResourcePath
+      const sanitizedRootResourcePath =
+        RequestParser._sanitizePath(rootResourcePath)
+      response.rootResourcePath = RequestParser._checkPath(
+        sanitizedRootResourcePath
+      )
+
+      for (resource of Array.from(response.resources)) {
+        if (resource.path === originalRootResourcePath) {
+          resource.path = sanitizedRootResourcePath
+        }
+      }
+    } catch (error1) {
+      const error = error1
+      return callback(error)
+    }
+
+    return callback(null, response)
+  },
+
+  _parseResource(resource) {
+    let modified
+    if (resource.path == null || typeof resource.path !== 'string') {
+      throw 'all resources should have a path attribute'
+    }
+
+    if (resource.modified != null) {
+      modified = new Date(resource.modified)
+      if (isNaN(modified.getTime())) {
+        throw `resource modified date could not be understood: ${resource.modified}`
+      }
+    }
+
+    if (resource.url == null && resource.content == null) {
+      throw 'all resources should have either a url or content attribute'
+    }
+    if (resource.content != null && typeof resource.content !== 'string') {
+      throw 'content attribute should be a string'
+    }
+    if (resource.url != null && typeof resource.url !== 'string') {
+      throw 'url attribute should be a string'
+    }
+
+    return {
+      path: resource.path,
+      modified,
+      url: resource.url,
+      content: resource.content,
+    }
+  },
+
+  _parseAttribute(name, attribute, options) {
+    if (attribute != null) {
+      if (options.validValues != null) {
+        if (options.validValues.indexOf(attribute) === -1) {
+          throw `${name} attribute should be one of: ${options.validValues.join(
+            ', '
+          )}`
+        }
+      }
+      if (options.type != null) {
+        if (typeof attribute !== options.type) {
+          throw `${name} attribute should be a ${options.type}`
+        }
+      }
+    } else {
+      if (options.default != null) {
+        return options.default
+      }
+    }
+    return attribute
+  },
+
+  _sanitizePath(path) {
+    // See http://php.net/manual/en/function.escapeshellcmd.php
+    return path.replace(
+      /[\#\&\;\`\|\*\?\~\<\>\^\(\)\[\]\{\}\$\\\x0A\xFF\x00]/g,
+      ''
+    )
+  },
+
+  _checkPath(path) {
+    // check that the request does not use a relative path
+    for (const dir of Array.from(path.split('/'))) {
+      if (dir === '..') {
+        throw 'relative path in root resource'
+      }
+    }
+    return path
+  },
+}

+ 116 - 0
services/clsi/app/js/ResourceStateManager.js

@@ -0,0 +1,116 @@
+const Path = require('path')
+const fs = require('fs')
+const logger = require('logger-sharelatex')
+const Errors = require('./Errors')
+const SafeReader = require('./SafeReader')
+
+module.exports = {
+  // The sync state is an identifier which must match for an
+  // incremental update to be allowed.
+  //
+  // The initial value is passed in and stored on a full
+  // compile, along with the list of resources..
+  //
+  // Subsequent incremental compiles must come with the same value - if
+  // not they will be rejected with a 409 Conflict response. The
+  // previous list of resources is returned.
+  //
+  // An incremental compile can only update existing files with new
+  // content.  The sync state identifier must change if any docs or
+  // files are moved, added, deleted or renamed.
+
+  SYNC_STATE_FILE: '.project-sync-state',
+  SYNC_STATE_MAX_SIZE: 128 * 1024,
+
+  saveProjectState(state, resources, basePath, callback) {
+    const stateFile = Path.join(basePath, this.SYNC_STATE_FILE)
+    if (state == null) {
+      // remove the file if no state passed in
+      logger.log({ state, basePath }, 'clearing sync state')
+      fs.unlink(stateFile, function (err) {
+        if (err && err.code !== 'ENOENT') {
+          return callback(err)
+        } else {
+          return callback()
+        }
+      })
+    } else {
+      logger.log({ state, basePath }, 'writing sync state')
+      const resourceList = resources.map(resource => resource.path)
+      fs.writeFile(
+        stateFile,
+        [...resourceList, `stateHash:${state}`].join('\n'),
+        callback
+      )
+    }
+  },
+
+  checkProjectStateMatches(state, basePath, callback) {
+    const stateFile = Path.join(basePath, this.SYNC_STATE_FILE)
+    const size = this.SYNC_STATE_MAX_SIZE
+    SafeReader.readFile(
+      stateFile,
+      size,
+      'utf8',
+      function (err, result, bytesRead) {
+        if (err) {
+          return callback(err)
+        }
+        if (bytesRead === size) {
+          logger.error(
+            { file: stateFile, size, bytesRead },
+            'project state file truncated'
+          )
+        }
+        const array = result ? result.toString().split('\n') : []
+        const adjustedLength = Math.max(array.length, 1)
+        const resourceList = array.slice(0, adjustedLength - 1)
+        const oldState = array[adjustedLength - 1]
+        const newState = `stateHash:${state}`
+        logger.log(
+          { state, oldState, basePath, stateMatches: newState === oldState },
+          'checking sync state'
+        )
+        if (newState !== oldState) {
+          return callback(
+            new Errors.FilesOutOfSyncError(
+              'invalid state for incremental update'
+            )
+          )
+        } else {
+          const resources = resourceList.map(path => ({ path }))
+          callback(null, resources)
+        }
+      }
+    )
+  },
+
+  checkResourceFiles(resources, allFiles, basePath, callback) {
+    // check the paths are all relative to current directory
+    const containsRelativePath = resource => {
+      const dirs = resource.path.split('/')
+      return dirs.indexOf('..') !== -1
+    }
+    if (resources.some(containsRelativePath)) {
+      return callback(new Error('relative path in resource file list'))
+    }
+    // check if any of the input files are not present in list of files
+    const seenFiles = new Set(allFiles)
+    const missingFiles = resources
+      .map(resource => resource.path)
+      .filter(path => !seenFiles.has(path))
+    if (missingFiles.length > 0) {
+      logger.err(
+        { missingFiles, basePath, allFiles, resources },
+        'missing input files for project'
+      )
+      return callback(
+        new Errors.FilesOutOfSyncError(
+          'resource files missing in incremental update'
+        )
+      )
+    } else {
+      callback()
+    }
+  },
+}

+ 354 - 0
services/clsi/app/js/ResourceWriter.js

@@ -0,0 +1,354 @@
+/* eslint-disable
+    camelcase,
+    handle-callback-err,
+    no-return-assign,
+    no-unused-vars,
+    no-useless-escape,
+*/
+// TODO: This file was created by bulk-decaffeinate.
+// Fix any style issues and re-enable lint.
+/*
+ * decaffeinate suggestions:
+ * DS101: Remove unnecessary use of Array.from
+ * DS102: Remove unnecessary code created because of implicit returns
+ * DS207: Consider shorter variations of null checks
+ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
+ */
+let ResourceWriter
+const UrlCache = require('./UrlCache')
+const Path = require('path')
+const fs = require('fs')
+const async = require('async')
+const OutputFileFinder = require('./OutputFileFinder')
+const ResourceStateManager = require('./ResourceStateManager')
+const Metrics = require('./Metrics')
+const logger = require('logger-sharelatex')
+const settings = require('@overleaf/settings')
+
+const parallelFileDownloads = settings.parallelFileDownloads || 1
+
+module.exports = ResourceWriter = {
+  syncResourcesToDisk(request, basePath, callback) {
+    if (callback == null) {
+      callback = function (error, resourceList) {}
+    }
+    if (request.syncType === 'incremental') {
+      logger.log(
+        { project_id: request.project_id, user_id: request.user_id },
+        'incremental sync'
+      )
+      return ResourceStateManager.checkProjectStateMatches(
+        request.syncState,
+        basePath,
+        function (error, resourceList) {
+          if (error != null) {
+            return callback(error)
+          }
+          return ResourceWriter._removeExtraneousFiles(
+            resourceList,
+            basePath,
+            function (error, outputFiles, allFiles) {
+              if (error != null) {
+                return callback(error)
+              }
+              return ResourceStateManager.checkResourceFiles(
+                resourceList,
+                allFiles,
+                basePath,
+                function (error) {
+                  if (error != null) {
+                    return callback(error)
+                  }
+                  return ResourceWriter.saveIncrementalResourcesToDisk(
+                    request.project_id,
+                    request.resources,
+                    basePath,
+                    function (error) {
+                      if (error != null) {
+                        return callback(error)
+                      }
+                      return callback(null, resourceList)
+                    }
+                  )
+                }
+              )
+            }
+          )
+        }
+      )
+    } else {
+      logger.log(
+        { project_id: request.project_id, user_id: request.user_id },
+        'full sync'
+      )
+      return this.saveAllResourcesToDisk(
+        request.project_id,
+        request.resources,
+        basePath,
+        function (error) {
+          if (error != null) {
+            return callback(error)
+          }
+          return ResourceStateManager.saveProjectState(
+            request.syncState,
+            request.resources,
+            basePath,
+            function (error) {
+              if (error != null) {
+                return callback(error)
+              }
+              return callback(null, request.resources)
+            }
+          )
+        }
+      )
+    }
+  },
+
+  saveIncrementalResourcesToDisk(project_id, resources, basePath, callback) {
+    if (callback == null) {
+      callback = function (error) {}
+    }
+    return this._createDirectory(basePath, error => {
+      if (error != null) {
+        return callback(error)
+      }
+      const jobs = Array.from(resources).map(resource =>
+        (resource => {
+          return callback =>
+            this._writeResourceToDisk(project_id, resource, basePath, callback)
+        })(resource)
+      )
+      return async.parallelLimit(jobs, parallelFileDownloads, callback)
+    })
+  },
+
+  saveAllResourcesToDisk(project_id, resources, basePath, callback) {
+    if (callback == null) {
+      callback = function (error) {}
+    }
+    return this._createDirectory(basePath, error => {
+      if (error != null) {
+        return callback(error)
+      }
+      return this._removeExtraneousFiles(resources, basePath, error => {
+        if (error != null) {
+          return callback(error)
+        }
+        const jobs = Array.from(resources).map(resource =>
+          (resource => {
+            return callback =>
+              this._writeResourceToDisk(
+                project_id,
+                resource,
+                basePath,
+                callback
+              )
+          })(resource)
+        )
+        return async.parallelLimit(jobs, parallelFileDownloads, callback)
+      })
+    })
+  },
+
+  _createDirectory(basePath, callback) {
+    if (callback == null) {
+      callback = function (error) {}
+    }
+    return fs.mkdir(basePath, function (err) {
+      if (err != null) {
+        if (err.code === 'EEXIST') {
+          return callback()
+        } else {
+          logger.log({ err, dir: basePath }, 'error creating directory')
+          return callback(err)
+        }
+      } else {
+        return callback()
+      }
+    })
+  },
+
+  _removeExtraneousFiles(resources, basePath, _callback) {
+    if (_callback == null) {
+      _callback = function (error, outputFiles, allFiles) {}
+    }
+    const timer = new Metrics.Timer('unlink-output-files')
+    const callback = function (error, ...result) {
+      timer.done()
+      return _callback(error, ...Array.from(result))
+    }
+
+    return OutputFileFinder.findOutputFiles(
+      resources,
+      basePath,
+      function (error, outputFiles, allFiles) {
+        if (error != null) {
+          return callback(error)
+        }
+
+        const jobs = []
+        for (const file of Array.from(outputFiles || [])) {
+          ;(function (file) {
+            const { path } = file
+            let should_delete = true
+            if (
+              path.match(/^output\./) ||
+              path.match(/\.aux$/) ||
+              path.match(/^cache\//)
+            ) {
+              // knitr cache
+              should_delete = false
+            }
+            if (path.match(/^output-.*/)) {
+              // Tikz cached figures (default case)
+              should_delete = false
+            }
+            if (path.match(/\.(pdf|dpth|md5)$/)) {
+              // Tikz cached figures (by extension)
+              should_delete = false
+            }
+            if (
+              path.match(/\.(pygtex|pygstyle)$/) ||
+              path.match(/(^|\/)_minted-[^\/]+\//)
+            ) {
+              // minted files/directory
+              should_delete = false
+            }
+            if (
+              path.match(/\.md\.tex$/) ||
+              path.match(/(^|\/)_markdown_[^\/]+\//)
+            ) {
+              // markdown files/directory
+              should_delete = false
+            }
+            if (path.match(/-eps-converted-to\.pdf$/)) {
+              // Epstopdf generated files
+              should_delete = false
+            }
+            if (
+              path === 'output.pdf' ||
+              path === 'output.dvi' ||
+              path === 'output.log' ||
+              path === 'output.xdv' ||
+              path === 'output.stdout' ||
+              path === 'output.stderr'
+            ) {
+              should_delete = true
+            }
+            if (path === 'output.tex') {
+              // created by TikzManager if present in output files
+              should_delete = true
+            }
+            if (should_delete) {
+              return jobs.push(callback =>
+                ResourceWriter._deleteFileIfNotDirectory(
+                  Path.join(basePath, path),
+                  callback
+                )
+              )
+            }
+          })(file)
+        }
+
+        return async.series(jobs, function (error) {
+          if (error != null) {
+            return callback(error)
+          }
+          return callback(null, outputFiles, allFiles)
+        })
+      }
+    )
+  },
+
+  _deleteFileIfNotDirectory(path, callback) {
+    if (callback == null) {
+      callback = function (error) {}
+    }
+    return fs.stat(path, function (error, stat) {
+      if (error != null && error.code === 'ENOENT') {
+        return callback()
+      } else if (error != null) {
+        logger.err(
+          { err: error, path },
+          'error stating file in deleteFileIfNotDirectory'
+        )
+        return callback(error)
+      } else if (stat.isFile()) {
+        return fs.unlink(path, function (error) {
+          if (error != null) {
+            logger.err(
+              { err: error, path },
+              'error removing file in deleteFileIfNotDirectory'
+            )
+            return callback(error)
+          } else {
+            return callback()
+          }
+        })
+      } else {
+        return callback()
+      }
+    })
+  },
+
+  _writeResourceToDisk(project_id, resource, basePath, callback) {
+    if (callback == null) {
+      callback = function (error) {}
+    }
+    return ResourceWriter.checkPath(
+      basePath,
+      resource.path,
+      function (error, path) {
+        if (error != null) {
+          return callback(error)
+        }
+        return fs.mkdir(
+          Path.dirname(path),
+          { recursive: true },
+          function (error) {
+            if (error != null) {
+              return callback(error)
+            }
+            // TODO: Don't overwrite file if it hasn't been modified
+            if (resource.url != null) {
+              return UrlCache.downloadUrlToFile(
+                project_id,
+                resource.url,
+                path,
+                resource.modified,
+                function (err) {
+                  if (err != null) {
+                    logger.err(
+                      {
+                        err,
+                        project_id,
+                        path,
+                        resource_url: resource.url,
+                        modified: resource.modified,
+                      },
+                      'error downloading file for resources'
+                    )
+                    Metrics.inc('download-failed')
+                  }
+                  return callback()
+                }
+              ) // try and continue compiling even if http resource can not be downloaded at this time
+            } else {
+              fs.writeFile(path, resource.content, callback)
+            }
+          }
+        )
+      }
+    )
+  },
+
+  checkPath(basePath, resourcePath, callback) {
+    const path = Path.normalize(Path.join(basePath, resourcePath))
+    if (path.slice(0, basePath.length + 1) !== basePath + '/') {
+      return callback(new Error('resource path is outside root directory'))
+    } else {
+      return callback(null, path)
+    }
+  },
+}

+ 63 - 0
services/clsi/app/js/SafeReader.js

@@ -0,0 +1,63 @@
+/* eslint-disable
+    handle-callback-err,
+    no-unused-vars,
+    node/no-deprecated-api,
+*/
+// TODO: This file was created by bulk-decaffeinate.
+// Fix any style issues and re-enable lint.
+/*
+ * decaffeinate suggestions:
+ * DS101: Remove unnecessary use of Array.from
+ * DS102: Remove unnecessary code created because of implicit returns
+ * DS207: Consider shorter variations of null checks
+ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
+ */
+let SafeReader
+const fs = require('fs')
+const logger = require('logger-sharelatex')
+
+module.exports = SafeReader = {
+  // safely read up to size bytes from a file and return result as a
+  // string
+
+  readFile(file, size, encoding, callback) {
+    if (callback == null) {
+      callback = function (error, result) {}
+    }
+    return fs.open(file, 'r', function (err, fd) {
+      if (err != null && err.code === 'ENOENT') {
+        return callback()
+      }
+      if (err != null) {
+        return callback(err)
+      }
+
+      // safely return always closing the file
+      const callbackWithClose = (err, ...result) =>
+        fs.close(fd, function (err1) {
+          if (err != null) {
+            return callback(err)
+          }
+          if (err1 != null) {
+            return callback(err1)
+          }
+          return callback(null, ...Array.from(result))
+        })
+      const buff = Buffer.alloc(size) // fills with zeroes by default
+      return fs.read(
+        fd,
+        buff,
+        0,
+        buff.length,
+        0,
+        function (err, bytesRead, buffer) {
+          if (err != null) {
+            return callbackWithClose(err)
+          }
+          const result = buffer.toString(encoding, 0, bytesRead)
+          return callbackWithClose(null, result, bytesRead)
+        }
+      )
+    })
+  },
+}

+ 94 - 0
services/clsi/app/js/StaticServerForbidSymlinks.js

@@ -0,0 +1,94 @@
+/* eslint-disable
+    camelcase,
+    no-cond-assign,
+    no-unused-vars,
+    node/no-deprecated-api,
+*/
+// TODO: This file was created by bulk-decaffeinate.
+// Fix any style issues and re-enable lint.
+/*
+ * decaffeinate suggestions:
+ * DS101: Remove unnecessary use of Array.from
+ * DS102: Remove unnecessary code created because of implicit returns
+ * DS103: Rewrite code to no longer use __guard__
+ * DS207: Consider shorter variations of null checks
+ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
+ */
+let ForbidSymlinks
+const Path = require('path')
+const fs = require('fs')
+const Settings = require('@overleaf/settings')
+const logger = require('logger-sharelatex')
+const url = require('url')
+
+module.exports = ForbidSymlinks = function (staticFn, root, options) {
+  const expressStatic = staticFn(root, options)
+  const basePath = Path.resolve(root)
+  return function (req, res, next) {
+    let file, project_id, result
+    const path = __guard__(url.parse(req.url), x => x.pathname)
+    // check that the path is of the form /project_id_or_name/path/to/file.log
+    if ((result = path.match(/^\/?([a-zA-Z0-9_-]+)\/(.*)/))) {
+      project_id = result[1]
+      file = result[2]
+    } else {
+      logger.warn({ path }, 'unrecognized file request')
+      return res.sendStatus(404)
+    }
+    // check that the file does not use a relative path
+    for (const dir of Array.from(file.split('/'))) {
+      if (dir === '..') {
+        logger.warn({ path }, 'attempt to use a relative path')
+        return res.sendStatus(404)
+      }
+    }
+    // check that the requested path is normalized
+    const requestedFsPath = `${basePath}/${project_id}/${file}`
+    if (requestedFsPath !== Path.normalize(requestedFsPath)) {
+      logger.error(
+        { path: requestedFsPath },
+        'requestedFsPath is not normalized'
+      )
+      return res.sendStatus(404)
+    }
+    // check that the requested path is not a symlink
+    return fs.realpath(requestedFsPath, function (err, realFsPath) {
+      if (err != null) {
+        if (err.code === 'ENOENT') {
+          return res.sendStatus(404)
+        } else {
+          logger.error(
+            {
+              err,
+              requestedFsPath,
+              realFsPath,
+              path: req.params[0],
+              project_id: req.params.project_id,
+            },
+            'error checking file access'
+          )
+          return res.sendStatus(500)
+        }
+      } else if (requestedFsPath !== realFsPath) {
+        logger.warn(
+          {
+            requestedFsPath,
+            realFsPath,
+            path: req.params[0],
+            project_id: req.params.project_id,
+          },
+          'trying to access a different file (symlink), aborting'
+        )
+        return res.sendStatus(404)
+      } else {
+        return expressStatic(req, res, next)
+      }
+    })
+  }
+}
+
+function __guard__(value, transform) {
+  return typeof value !== 'undefined' && value !== null
+    ? transform(value)
+    : undefined
+}

+ 101 - 0
services/clsi/app/js/TikzManager.js

@@ -0,0 +1,101 @@
+/* eslint-disable
+    handle-callback-err,
+    no-unused-vars,
+*/
+// TODO: This file was created by bulk-decaffeinate.
+// Fix any style issues and re-enable lint.
+/*
+ * decaffeinate suggestions:
+ * DS101: Remove unnecessary use of Array.from
+ * DS102: Remove unnecessary code created because of implicit returns
+ * DS207: Consider shorter variations of null checks
+ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
+ */
+let TikzManager
+const fs = require('fs')
+const Path = require('path')
+const ResourceWriter = require('./ResourceWriter')
+const SafeReader = require('./SafeReader')
+const logger = require('logger-sharelatex')
+
+// for \tikzexternalize or pstool to work the main file needs to match the
+// jobname.  Since we set the -jobname to output, we have to create a
+// copy of the main file as 'output.tex'.
+
+module.exports = TikzManager = {
+  checkMainFile(compileDir, mainFile, resources, callback) {
+    // if there's already an output.tex file, we don't want to touch it
+    if (callback == null) {
+      callback = function (error, needsMainFile) {}
+    }
+    for (const resource of Array.from(resources)) {
+      if (resource.path === 'output.tex') {
+        logger.log({ compileDir, mainFile }, 'output.tex already in resources')
+        return callback(null, false)
+      }
+    }
+    // if there's no output.tex, see if we are using tikz/pgf or pstool in the main file
+    return ResourceWriter.checkPath(
+      compileDir,
+      mainFile,
+      function (error, path) {
+        if (error != null) {
+          return callback(error)
+        }
+        return SafeReader.readFile(
+          path,
+          65536,
+          'utf8',
+          function (error, content) {
+            if (error != null) {
+              return callback(error)
+            }
+            const usesTikzExternalize =
+              (content != null
+                ? content.indexOf('\\tikzexternalize')
+                : undefined) >= 0
+            const usesPsTool =
+              (content != null ? content.indexOf('{pstool}') : undefined) >= 0
+            logger.log(
+              { compileDir, mainFile, usesTikzExternalize, usesPsTool },
+              'checked for packages needing main file as output.tex'
+            )
+            const needsMainFile = usesTikzExternalize || usesPsTool
+            return callback(null, needsMainFile)
+          }
+        )
+      }
+    )
+  },
+
+  injectOutputFile(compileDir, mainFile, callback) {
+    if (callback == null) {
+      callback = function (error) {}
+    }
+    return ResourceWriter.checkPath(
+      compileDir,
+      mainFile,
+      function (error, path) {
+        if (error != null) {
+          return callback(error)
+        }
+        return fs.readFile(path, 'utf8', function (error, content) {
+          if (error != null) {
+            return callback(error)
+          }
+          logger.log(
+            { compileDir, mainFile },
+            'copied file to output.tex as project uses packages which require it'
+          )
+          // use wx flag to ensure that output file does not already exist
+          return fs.writeFile(
+            Path.join(compileDir, 'output.tex'),
+            content,
+            { flag: 'wx' },
+            callback
+          )
+        })
+      }
+    )
+  },
+}

+ 281 - 0
services/clsi/app/js/UrlCache.js

@@ -0,0 +1,281 @@
+/* eslint-disable
+    camelcase,
+    handle-callback-err,
+    no-return-assign,
+*/
+// TODO: This file was created by bulk-decaffeinate.
+// Fix any style issues and re-enable lint.
+/*
+ * decaffeinate suggestions:
+ * DS101: Remove unnecessary use of Array.from
+ * DS102: Remove unnecessary code created because of implicit returns
+ * DS207: Consider shorter variations of null checks
+ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
+ */
+let UrlCache
+const db = require('./db')
+const dbQueue = require('./DbQueue')
+const UrlFetcher = require('./UrlFetcher')
+const Settings = require('@overleaf/settings')
+const crypto = require('crypto')
+const fs = require('fs')
+const logger = require('logger-sharelatex')
+const async = require('async')
+const Metrics = require('./Metrics')
+
+module.exports = UrlCache = {
+  downloadUrlToFile(project_id, url, destPath, lastModified, callback) {
+    if (callback == null) {
+      callback = function (error) {}
+    }
+    return UrlCache._ensureUrlIsInCache(
+      project_id,
+      url,
+      lastModified,
+      (error, pathToCachedUrl) => {
+        if (error != null) {
+          return callback(error)
+        }
+        return fs.copyFile(pathToCachedUrl, destPath, function (error) {
+          if (error != null) {
+            logger.error(
+              { err: error, from: pathToCachedUrl, to: destPath },
+              'error copying file from cache'
+            )
+            return UrlCache._clearUrlDetails(project_id, url, () =>
+              callback(error)
+            )
+          } else {
+            return callback(error)
+          }
+        })
+      }
+    )
+  },
+
+  clearProject(project_id, callback) {
+    if (callback == null) {
+      callback = function (error) {}
+    }
+    return UrlCache._findAllUrlsInProject(project_id, function (error, urls) {
+      logger.log(
+        { project_id, url_count: urls.length },
+        'clearing project URLs'
+      )
+      if (error != null) {
+        return callback(error)
+      }
+      const jobs = Array.from(urls || []).map(url =>
+        (
+          url => callback =>
+            UrlCache._clearUrlFromCache(project_id, url, function (error) {
+              if (error != null) {
+                logger.error(
+                  { err: error, project_id, url },
+                  'error clearing project URL'
+                )
+              }
+              return callback()
+            })
+        )(url)
+      )
+      return async.series(jobs, callback)
+    })
+  },
+
+  _ensureUrlIsInCache(project_id, url, lastModified, callback) {
+    if (callback == null) {
+      callback = function (error, pathOnDisk) {}
+    }
+    if (lastModified != null) {
+      // MYSQL only stores dates to an accuracy of a second but the incoming lastModified might have milliseconds.
+      // So round down to seconds
+      lastModified = new Date(Math.floor(lastModified.getTime() / 1000) * 1000)
+    }
+    return UrlCache._doesUrlNeedDownloading(
+      project_id,
+      url,
+      lastModified,
+      (error, needsDownloading) => {
+        if (error != null) {
+          return callback(error)
+        }
+        if (needsDownloading) {
+          logger.log({ url, lastModified }, 'downloading URL')
+          return UrlFetcher.pipeUrlToFileWithRetry(
+            url,
+            UrlCache._cacheFilePathForUrl(project_id, url),
+            error => {
+              if (error != null) {
+                return callback(error)
+              }
+              return UrlCache._updateOrCreateUrlDetails(
+                project_id,
+                url,
+                lastModified,
+                error => {
+                  if (error != null) {
+                    return callback(error)
+                  }
+                  return callback(
+                    null,
+                    UrlCache._cacheFilePathForUrl(project_id, url)
+                  )
+                }
+              )
+            }
+          )
+        } else {
+          logger.log({ url, lastModified }, 'URL is up to date in cache')
+          return callback(null, UrlCache._cacheFilePathForUrl(project_id, url))
+        }
+      }
+    )
+  },
+
+  _doesUrlNeedDownloading(project_id, url, lastModified, callback) {
+    if (callback == null) {
+      callback = function (error, needsDownloading) {}
+    }
+    if (lastModified == null) {
+      return callback(null, true)
+    }
+    return UrlCache._findUrlDetails(
+      project_id,
+      url,
+      function (error, urlDetails) {
+        if (error != null) {
+          return callback(error)
+        }
+        if (
+          urlDetails == null ||
+          urlDetails.lastModified == null ||
+          urlDetails.lastModified.getTime() < lastModified.getTime()
+        ) {
+          return callback(null, true)
+        } else {
+          return callback(null, false)
+        }
+      }
+    )
+  },
+
+  _cacheFileNameForUrl(project_id, url) {
+    return project_id + ':' + crypto.createHash('md5').update(url).digest('hex')
+  },
+
+  _cacheFilePathForUrl(project_id, url) {
+    return `${Settings.path.clsiCacheDir}/${UrlCache._cacheFileNameForUrl(
+      project_id,
+      url
+    )}`
+  },
+
+  _clearUrlFromCache(project_id, url, callback) {
+    if (callback == null) {
+      callback = function (error) {}
+    }
+    return UrlCache._clearUrlDetails(project_id, url, function (error) {
+      if (error != null) {
+        return callback(error)
+      }
+      return UrlCache._deleteUrlCacheFromDisk(
+        project_id,
+        url,
+        function (error) {
+          if (error != null) {
+            return callback(error)
+          }
+          return callback(null)
+        }
+      )
+    })
+  },
+
+  _deleteUrlCacheFromDisk(project_id, url, callback) {
+    if (callback == null) {
+      callback = function (error) {}
+    }
+    return fs.unlink(
+      UrlCache._cacheFilePathForUrl(project_id, url),
+      function (error) {
+        if (error != null && error.code !== 'ENOENT') {
+          // no error if the file isn't present
+          return callback(error)
+        } else {
+          return callback()
+        }
+      }
+    )
+  },
+
+  _findUrlDetails(project_id, url, callback) {
+    if (callback == null) {
+      callback = function (error, urlDetails) {}
+    }
+    const timer = new Metrics.Timer('db-find-url-details')
+    const job = cb =>
+      db.UrlCache.findOne({ where: { url, project_id } })
+        .then(urlDetails => cb(null, urlDetails))
+        .error(cb)
+    dbQueue.queue.push(job, (error, urlDetails) => {
+      timer.done()
+      callback(error, urlDetails)
+    })
+  },
+
+  _updateOrCreateUrlDetails(project_id, url, lastModified, callback) {
+    if (callback == null) {
+      callback = function (error) {}
+    }
+    const timer = new Metrics.Timer('db-update-or-create-url-details')
+    const job = cb =>
+      db.UrlCache.findOrCreate({ where: { url, project_id } })
+        .spread((urlDetails, created) =>
+          urlDetails
+            .update({ lastModified })
+            .then(() => cb())
+            .error(cb)
+        )
+        .error(cb)
+    dbQueue.queue.push(job, error => {
+      timer.done()
+      callback(error)
+    })
+  },
+
+  _clearUrlDetails(project_id, url, callback) {
+    if (callback == null) {
+      callback = function (error) {}
+    }
+    const timer = new Metrics.Timer('db-clear-url-details')
+    const job = cb =>
+      db.UrlCache.destroy({ where: { url, project_id } })
+        .then(() => cb(null))
+        .error(cb)
+    dbQueue.queue.push(job, error => {
+      timer.done()
+      callback(error)
+    })
+  },
+
+  _findAllUrlsInProject(project_id, callback) {
+    if (callback == null) {
+      callback = function (error, urls) {}
+    }
+    const timer = new Metrics.Timer('db-find-urls-in-project')
+    const job = cb =>
+      db.UrlCache.findAll({ where: { project_id } })
+        .then(urlEntries =>
+          cb(
+            null,
+            urlEntries.map(entry => entry.url)
+          )
+        )
+        .error(cb)
+    dbQueue.queue.push(job, (err, urls) => {
+      timer.done()
+      callback(err, urls)
+    })
+  },
+}

+ 131 - 0
services/clsi/app/js/UrlFetcher.js

@@ -0,0 +1,131 @@
+/* eslint-disable
+    handle-callback-err,
+    no-return-assign,
+    no-unused-vars,
+    node/no-deprecated-api,
+*/
+// TODO: This file was created by bulk-decaffeinate.
+// Fix any style issues and re-enable lint.
+/*
+ * decaffeinate suggestions:
+ * DS102: Remove unnecessary code created because of implicit returns
+ * DS207: Consider shorter variations of null checks
+ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
+ */
+let UrlFetcher
+const request = require('request').defaults({ jar: false })
+const fs = require('fs')
+const logger = require('logger-sharelatex')
+const settings = require('@overleaf/settings')
+const URL = require('url')
+const async = require('async')
+
+const oneMinute = 60 * 1000
+
+module.exports = UrlFetcher = {
+  pipeUrlToFileWithRetry(url, filePath, callback) {
+    const doDownload = function (cb) {
+      UrlFetcher.pipeUrlToFile(url, filePath, cb)
+    }
+    async.retry(3, doDownload, callback)
+  },
+
+  pipeUrlToFile(url, filePath, _callback) {
+    if (_callback == null) {
+      _callback = function (error) {}
+    }
+    const callbackOnce = function (error) {
+      if (timeoutHandler != null) {
+        clearTimeout(timeoutHandler)
+      }
+      _callback(error)
+      return (_callback = function () {})
+    }
+
+    const u = URL.parse(url)
+    if (
+      settings.filestoreDomainOveride &&
+      u.host !== settings.apis.clsiPerf.host
+    ) {
+      url = `${settings.filestoreDomainOveride}${u.path}`
+    }
+    var timeoutHandler = setTimeout(
+      function () {
+        timeoutHandler = null
+        logger.error({ url, filePath }, 'Timed out downloading file to cache')
+        return callbackOnce(
+          new Error(`Timed out downloading file to cache ${url}`)
+        )
+      },
+      // FIXME: maybe need to close fileStream here
+      3 * oneMinute
+    )
+
+    logger.log({ url, filePath }, 'started downloading url to cache')
+    const urlStream = request.get({ url, timeout: oneMinute })
+    urlStream.pause() // stop data flowing until we are ready
+
+    // attach handlers before setting up pipes
+    urlStream.on('error', function (error) {
+      logger.error({ err: error, url, filePath }, 'error downloading url')
+      return callbackOnce(
+        error || new Error(`Something went wrong downloading the URL ${url}`)
+      )
+    })
+
+    urlStream.on('end', () =>
+      logger.log({ url, filePath }, 'finished downloading file into cache')
+    )
+
+    return urlStream.on('response', function (res) {
+      if (res.statusCode >= 200 && res.statusCode < 300) {
+        const fileStream = fs.createWriteStream(filePath)
+
+        // attach handlers before setting up pipes
+        fileStream.on('error', function (error) {
+          logger.error(
+            { err: error, url, filePath },
+            'error writing file into cache'
+          )
+          return fs.unlink(filePath, function (err) {
+            if (err != null) {
+              logger.err({ err, filePath }, 'error deleting file from cache')
+            }
+            return callbackOnce(error)
+          })
+        })
+
+        fileStream.on('finish', function () {
+          logger.log({ url, filePath }, 'finished writing file into cache')
+          return callbackOnce()
+        })
+
+        fileStream.on('pipe', () =>
+          logger.log({ url, filePath }, 'piping into filestream')
+        )
+
+        urlStream.pipe(fileStream)
+        return urlStream.resume() // now we are ready to handle the data
+      } else {
+        logger.error(
+          { statusCode: res.statusCode, url, filePath },
+          'unexpected status code downloading url to cache'
+        )
+        // https://nodejs.org/api/http.html#http_class_http_clientrequest
+        // If you add a 'response' event handler, then you must consume
+        // the data from the response object, either by calling
+        // response.read() whenever there is a 'readable' event, or by
+        // adding a 'data' handler, or by calling the .resume()
+        // method. Until the data is consumed, the 'end' event will not
+        // fire. Also, until the data is read it will consume memory
+        // that can eventually lead to a 'process out of memory' error.
+        urlStream.resume() // discard the data
+        return callbackOnce(
+          new Error(
+            `URL returned non-success status code: ${res.statusCode} ${url}`
+          )
+        )
+      }
+    })
+  },
+}

+ 67 - 0
services/clsi/app/js/db.js

@@ -0,0 +1,67 @@
+/* eslint-disable
+    no-console,
+*/
+// TODO: This file was created by bulk-decaffeinate.
+// Fix any style issues and re-enable lint.
+/*
+ * decaffeinate suggestions:
+ * DS102: Remove unnecessary code created because of implicit returns
+ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
+ */
+const Sequelize = require('sequelize')
+const Settings = require('@overleaf/settings')
+const _ = require('lodash')
+const logger = require('logger-sharelatex')
+
+const options = _.extend({ logging: false }, Settings.mysql.clsi)
+
+logger.log({ dbPath: Settings.mysql.clsi.storage }, 'connecting to db')
+
+const sequelize = new Sequelize(
+  Settings.mysql.clsi.database,
+  Settings.mysql.clsi.username,
+  Settings.mysql.clsi.password,
+  options
+)
+
+if (Settings.mysql.clsi.dialect === 'sqlite') {
+  logger.log('running PRAGMA journal_mode=WAL;')
+  sequelize.query('PRAGMA journal_mode=WAL;')
+  sequelize.query('PRAGMA synchronous=OFF;')
+  sequelize.query('PRAGMA read_uncommitted = true;')
+}
+
+module.exports = {
+  UrlCache: sequelize.define(
+    'UrlCache',
+    {
+      url: Sequelize.STRING,
+      project_id: Sequelize.STRING,
+      lastModified: Sequelize.DATE,
+    },
+    {
+      indexes: [{ fields: ['url', 'project_id'] }, { fields: ['project_id'] }],
+    }
+  ),
+
+  Project: sequelize.define(
+    'Project',
+    {
+      project_id: { type: Sequelize.STRING, primaryKey: true },
+      lastAccessed: Sequelize.DATE,
+    },
+    {
+      indexes: [{ fields: ['lastAccessed'] }],
+    }
+  ),
+
+  op: Sequelize.Op,
+
+  sync() {
+    logger.log({ dbPath: Settings.mysql.clsi.storage }, 'syncing db schema')
+    return sequelize
+      .sync()
+      .then(() => logger.log('db sync complete'))
+      .catch(err => console.log(err, 'error syncing'))
+  },
+}

+ 43 - 0
services/clsi/app/lib/pdfjs/FSPdfManager.js

@@ -0,0 +1,43 @@
+const { PDFDocument } = require('pdfjs-dist/lib/core/document')
+const { LocalPdfManager } = require('pdfjs-dist/lib/core/pdf_manager')
+const { MissingDataException } = require('pdfjs-dist/lib/core/core_utils')
+const { FSStream } = require('./FSStream')
+
+class FSPdfManager extends LocalPdfManager {
+  constructor(docId, { fh, size, checkDeadline }) {
+    const nonEmptyDummyBuffer = Buffer.alloc(1, 0)
+    super(docId, nonEmptyDummyBuffer)
+    this.stream = new FSStream(fh, 0, size, null, null, checkDeadline)
+    this.pdfDocument = new PDFDocument(this, this.stream)
+  }
+
+  async ensure(obj, prop, args) {
+    try {
+      const value = obj[prop]
+      if (typeof value === 'function') {
+        return value.apply(obj, args)
+      }
+      return value
+    } catch (ex) {
+      if (!(ex instanceof MissingDataException)) {
+        throw ex
+      }
+      await this.requestRange(ex.begin, ex.end)
+      return this.ensure(obj, prop, args)
+    }
+  }
+
+  requestRange(begin, end) {
+    return this.stream.requestRange(begin, end)
+  }
+
+  requestLoadedStream() {}
+
+  onLoadedStream() {}
+
+  terminate(reason) {}
+}
+
+module.exports = {
+  FSPdfManager,
+}

+ 148 - 0
services/clsi/app/lib/pdfjs/FSStream.js

@@ -0,0 +1,148 @@
+const { Stream } = require('pdfjs-dist/lib/core/stream')
+const { MissingDataException } = require('pdfjs-dist/lib/core/core_utils')
+
+const BUF_SIZE = 1024 // read from the file in 1024 byte pages
+
+class FSStream extends Stream {
+  constructor(fh, start, length, dict, cachedBytes, checkDeadline) {
+    const nonEmptyDummyBuffer = Buffer.alloc(1, 0)
+    super(nonEmptyDummyBuffer, start, length, dict)
+    delete this.bytes
+    this.fh = fh
+    this.checkDeadline = checkDeadline
+    this.cachedBytes = cachedBytes || []
+  }
+
+  get length() {
+    return this.end - this.start
+  }
+
+  get isEmpty() {
+    return this.length === 0
+  }
+
+  // Manage cached reads from the file
+
+  requestRange(begin, end) {
+    this.checkDeadline(`request range ${begin} - ${end}`)
+    // expand small ranges to read a larger amount
+    if (end - begin < BUF_SIZE) {
+      end = begin + BUF_SIZE
+    }
+    end = Math.min(end, this.length)
+    // keep a cache of previous reads with {begin,end,buffer} values
+    const result = {
+      begin: begin,
+      end: end,
+      buffer: Buffer.alloc(end - begin, 0),
+    }
+    this.cachedBytes.push(result)
+    return this.fh.read(result.buffer, 0, end - begin, begin)
+  }
+
+  _ensureGetPos(pos) {
+    const found = this.cachedBytes.find(x => {
+      return x.begin <= pos && pos < x.end
+    })
+    if (!found) {
+      throw new MissingDataException(pos, pos + 1)
+    }
+    return found
+  }
+
+  _ensureGetRange(begin, end) {
+    end = Math.min(end, this.length) // BG: handle overflow case
+    const found = this.cachedBytes.find(x => {
+      return x.begin <= begin && end <= x.end
+    })
+    if (!found) {
+      throw new MissingDataException(begin, end)
+    }
+    return found
+  }
+
+  _readByte(found, pos) {
+    return found.buffer[pos - found.begin]
+  }
+
+  _readBytes(found, pos, end) {
+    return found.buffer.subarray(pos - found.begin, end - found.begin)
+  }
+
+  // handle accesses to the bytes
+
+  ensureByte(pos) {
+    this._ensureGetPos(pos) // may throw a MissingDataException
+  }
+
+  getByte() {
+    const pos = this.pos
+    if (this.pos >= this.end) {
+      return -1
+    }
+    const found = this._ensureGetPos(pos)
+    return this._readByte(found, this.pos++)
+  }
+
+  // BG: for a range, end is not included (see Buffer.subarray for example)
+
+  ensureBytes(length, forceClamped = false) {
+    const pos = this.pos
+    this._ensureGetRange(pos, pos + length)
+  }
+
+  getBytes(length, forceClamped = false) {
+    const pos = this.pos
+    const strEnd = this.end
+
+    const found = this._ensureGetRange(pos, pos + length)
+    if (!length) {
+      const subarray = this._readBytes(found, pos, strEnd)
+      // `this.bytes` is always a `Uint8Array` here.
+      return forceClamped ? new Uint8ClampedArray(subarray) : subarray
+    }
+    let end = pos + length
+    if (end > strEnd) {
+      end = strEnd
+    }
+    this.pos = end
+    const subarray = this._readBytes(found, pos, end)
+    // `this.bytes` is always a `Uint8Array` here.
+    return forceClamped ? new Uint8ClampedArray(subarray) : subarray
+  }
+
+  getByteRange() {
+    // BG: this isn't needed as far as I can tell
+    throw new Error('not implemented')
+  }
+
+  reset() {
+    this.pos = this.start
+  }
+
+  moveStart() {
+    this.start = this.pos
+  }
+
+  makeSubStream(start, length, dict = null) {
+    this.checkDeadline(`make sub stream start=${start}/length=${length}`)
+    // BG: had to add this check for null length, it is being called with only
+    // the start value at one point in the xref decoding. The intent is clear
+    // enough
+    // - a null length means "to the end of the file" -- not sure how it is
+    //   working in the existing pdfjs code without this.
+    if (!length) {
+      length = this.end - start
+    }
+    return new FSStream(
+      this.fh,
+      start,
+      length,
+      dict,
+      this.cachedBytes,
+      this.checkDeadline
+    )
+  }
+}
+
+module.exports = { FSStream }

+ 27 - 0
services/clsi/app/lib/pdfjs/parseXrefTable.js

@@ -0,0 +1,27 @@
+const fs = require('fs')
+const { FSPdfManager } = require('./FSPdfManager')
+
+async function parseXrefTable(path, size, checkDeadline) {
+  if (size === 0) {
+    return []
+  }
+
+  const file = await fs.promises.open(path)
+  try {
+    const manager = new FSPdfManager(0, { fh: file, size, checkDeadline })
+
+    await manager.ensureDoc('checkHeader')
+    checkDeadline('pdfjs: after checkHeader')
+    await manager.ensureDoc('parseStartXRef')
+    checkDeadline('pdfjs: after parseStartXRef')
+    await manager.ensureDoc('parse')
+    checkDeadline('pdfjs: after parse')
+    return manager.pdfDocument.catalog.xref.entries
+  } finally {
+    file.close()
+  }
+}
+
+module.exports = {
+  parseXrefTable,
+}

+ 0 - 0
services/clsi/bin/.gitignore


+ 4 - 0
services/clsi/bin/acceptance_test

@@ -0,0 +1,4 @@
+#!/bin/bash
+set -e;
+MOCHA="node_modules/.bin/mocha --recursive --reporter spec --timeout 15000"
+$MOCHA "$@"

BIN
services/clsi/bin/synctex


+ 9 - 0
services/clsi/buildscript.txt

@@ -0,0 +1,9 @@
+clsi
+--data-dirs=cache,compiles,db,output
+--dependencies=
+--docker-repos=gcr.io/overleaf-ops
+--env-add=ENABLE_PDF_CACHING="true"
+--env-pass-through=TEXLIVE_IMAGE
+--node-version=12.22.3
+--public-repo=True
+--script-version=3.11.0

+ 170 - 0
services/clsi/config/settings.defaults.js

@@ -0,0 +1,170 @@
+const Path = require('path')
+
+module.exports = {
+  // Options are passed to Sequelize.
+  // See http://sequelizejs.com/documentation#usage-options for details
+  mysql: {
+    clsi: {
+      database: 'clsi',
+      username: 'clsi',
+      dialect: 'sqlite',
+      storage:
+        process.env.SQLITE_PATH || Path.resolve(__dirname, '../db/db.sqlite'),
+      pool: {
+        max: 1,
+        min: 1,
+      },
+      retry: {
+        max: 10,
+      },
+    },
+  },
+
+  compileSizeLimit: process.env.COMPILE_SIZE_LIMIT || '7mb',
+
+  processLifespanLimitMs:
+    parseInt(process.env.PROCESS_LIFE_SPAN_LIMIT_MS) || 60 * 60 * 24 * 1000 * 2,
+
+  catchErrors: process.env.CATCH_ERRORS === 'true',
+
+  path: {
+    compilesDir: Path.resolve(__dirname, '../compiles'),
+    outputDir: Path.resolve(__dirname, '../output'),
+    clsiCacheDir: Path.resolve(__dirname, '../cache'),
+    synctexBaseDir(projectId) {
+      return Path.join(this.compilesDir, projectId)
+    },
+  },
+
+  internal: {
+    clsi: {
+      port: 3013,
+      host: process.env.LISTEN_ADDRESS || 'localhost',
+    },
+
+    load_balancer_agent: {
+      report_load: true,
+      load_port: 3048,
+      local_port: 3049,
+    },
+  },
+  apis: {
+    clsi: {
+      url: `http://${process.env.CLSI_HOST || 'localhost'}:3013`,
+    },
+    clsiPerf: {
+      host: `${process.env.CLSI_PERF_HOST || 'localhost'}:${
+        process.env.CLSI_PERF_PORT || '3043'
+      }`,
+    },
+  },
+
+  smokeTest: process.env.SMOKE_TEST || false,
+  project_cache_length_ms: 1000 * 60 * 60 * 24,
+  parallelFileDownloads: process.env.FILESTORE_PARALLEL_FILE_DOWNLOADS || 1,
+  parallelSqlQueryLimit: process.env.FILESTORE_PARALLEL_SQL_QUERY_LIMIT || 1,
+  filestoreDomainOveride: process.env.FILESTORE_DOMAIN_OVERRIDE,
+  texliveImageNameOveride: process.env.TEX_LIVE_IMAGE_NAME_OVERRIDE,
+  texliveOpenoutAny: process.env.TEXLIVE_OPENOUT_ANY,
+  sentry: {
+    dsn: process.env.SENTRY_DSN,
+  },
+
+  enablePdfCaching: process.env.ENABLE_PDF_CACHING === 'true',
+  enablePdfCachingDark: process.env.ENABLE_PDF_CACHING_DARK === 'true',
+  pdfCachingMinChunkSize:
+    parseInt(process.env.PDF_CACHING_MIN_CHUNK_SIZE, 10) || 1024,
+  pdfCachingMaxProcessingTime:
+    parseInt(process.env.PDF_CACHING_MAX_PROCESSING_TIME, 10) || 10 * 1000,
+}
+
+if (process.env.ALLOWED_COMPILE_GROUPS) {
+  try {
+    module.exports.allowedCompileGroups =
+      process.env.ALLOWED_COMPILE_GROUPS.split(' ')
+  } catch (error) {
+    console.error(error, 'could not apply allowed compile group setting')
+    process.exit(1)
+  }
+}
+
+if (process.env.DOCKER_RUNNER) {
+  let seccompProfilePath
+  module.exports.clsi = {
+    dockerRunner: process.env.DOCKER_RUNNER === 'true',
+    docker: {
+      runtime: process.env.DOCKER_RUNTIME,
+      image:
+        process.env.TEXLIVE_IMAGE || 'quay.io/sharelatex/texlive-full:2017.1',
+      env: {
+        HOME: '/tmp',
+      },
+      socketPath: '/var/run/docker.sock',
+      user: process.env.TEXLIVE_IMAGE_USER || 'tex',
+    },
+    optimiseInDocker: true,
+    expireProjectAfterIdleMs: 24 * 60 * 60 * 1000,
+    checkProjectsIntervalMs: 10 * 60 * 1000,
+  }
+
+  try {
+    // Override individual docker settings using path-based keys, e.g.:
+    // compileGroupDockerConfigs = {
+    //    priority: { 'HostConfig.CpuShares': 100 }
+    //    beta: { 'dotted.path.here', 'value'}
+    // }
+    const compileGroupConfig = JSON.parse(
+      process.env.COMPILE_GROUP_DOCKER_CONFIGS || '{}'
+    )
+    // Automatically clean up wordcount and synctex containers
+    const defaultCompileGroupConfig = {
+      wordcount: { 'HostConfig.AutoRemove': true },
+      synctex: { 'HostConfig.AutoRemove': true },
+    }
+    module.exports.clsi.docker.compileGroupConfig = Object.assign(
+      defaultCompileGroupConfig,
+      compileGroupConfig
+    )
+  } catch (error) {
+    console.error(error, 'could not apply compile group docker configs')
+    process.exit(1)
+  }
+
+  try {
+    seccompProfilePath = Path.resolve(__dirname, '../seccomp/clsi-profile.json')
+    module.exports.clsi.docker.seccomp_profile = JSON.stringify(
+      JSON.parse(require('fs').readFileSync(seccompProfilePath))
+    )
+  } catch (error) {
+    console.error(
+      error,
+      `could not load seccomp profile from ${seccompProfilePath}`
+    )
+    process.exit(1)
+  }
+
+  if (process.env.APPARMOR_PROFILE) {
+    try {
+      module.exports.clsi.docker.apparmor_profile = process.env.APPARMOR_PROFILE
+    } catch (error) {
+      console.error(error, 'could not apply apparmor profile setting')
+      process.exit(1)
+    }
+  }
+
+  if (process.env.ALLOWED_IMAGES) {
+    try {
+      module.exports.clsi.docker.allowedImages =
+        process.env.ALLOWED_IMAGES.split(' ')
+    } catch (error) {
+      console.error(error, 'could not apply allowed images setting')
+      process.exit(1)
+    }
+  }
+
+  module.exports.path.synctexBaseDir = () => '/compile'
+
+  module.exports.path.sandboxedCompilesHostDir = process.env.COMPILES_HOST_DIR
+
+  module.exports.path.synctexBinHostPath = process.env.SYNCTEX_BIN_HOST_PATH
+}

+ 2 - 0
services/clsi/db/.gitignore

@@ -0,0 +1,2 @@
+*
+!.gitignore

+ 5 - 0
services/clsi/debug

@@ -0,0 +1,5 @@
+#!/bin/bash
+echo "hello world"
+sleep 3
+echo "awake"
+/opt/synctex pdf /compile/output.pdf 1 100 200

+ 34 - 0
services/clsi/docker-compose-config.yml

@@ -0,0 +1,34 @@
+version: "2.3"
+
+services:
+  dev:
+    environment:
+      ALLOWED_IMAGES: "quay.io/sharelatex/texlive-full:2017.1"
+      TEXLIVE_IMAGE: quay.io/sharelatex/texlive-full:2017.1
+      TEXLIVE_IMAGE_USER: "tex"
+      SHARELATEX_CONFIG: /app/config/settings.defaults.js
+      DOCKER_RUNNER: "true"
+      COMPILES_HOST_DIR: $PWD/compiles
+      SYNCTEX_BIN_HOST_PATH: $PWD/bin/synctex
+    volumes:
+      - /var/run/docker.sock:/var/run/docker.sock
+      - ./compiles:/app/compiles
+      - ./cache:/app/cache
+      - ./bin/synctex:/app/bin/synctex
+
+
+  ci:
+    environment:
+      ALLOWED_IMAGES: ${TEXLIVE_IMAGE}
+      TEXLIVE_IMAGE: quay.io/sharelatex/texlive-full:2017.1
+      TEXLIVE_IMAGE_USER: "tex"
+      SHARELATEX_CONFIG: /app/config/settings.defaults.js
+      DOCKER_RUNNER: "true"
+      COMPILES_HOST_DIR: $PWD/compiles
+      SYNCTEX_BIN_HOST_PATH: $PWD/bin/synctex
+      SQLITE_PATH: /app/compiles/db.sqlite
+    volumes:
+      - /var/run/docker.sock:/var/run/docker.sock:rw
+      - ./compiles:/app/compiles
+      - ./cache:/app/cache
+      - ./bin/synctex:/app/bin/synctex

+ 42 - 0
services/clsi/docker-compose.ci.yml

@@ -0,0 +1,42 @@
+# This file was auto-generated, do not edit it directly.
+# Instead run bin/update_build_scripts from
+# https://github.com/sharelatex/sharelatex-dev-environment
+
+version: "2.3"
+
+services:
+  test_unit:
+    image: ci/$PROJECT_NAME:$BRANCH_NAME-$BUILD_NUMBER
+    command: npm run test:unit:_run
+    environment:
+      NODE_ENV: test
+      NODE_OPTIONS: "--unhandled-rejections=strict"
+
+
+  test_acceptance:
+    build: .
+    image: ci/$PROJECT_NAME:$BRANCH_NAME-$BUILD_NUMBER
+    extends:
+      file: docker-compose-config.yml
+      service: ci
+    environment:
+      ELASTIC_SEARCH_DSN: es:9200
+      REDIS_HOST: redis
+      QUEUES_REDIS_HOST: redis
+      MONGO_HOST: mongo
+      POSTGRES_HOST: postgres
+      MOCHA_GREP: ${MOCHA_GREP}
+      NODE_ENV: test
+      NODE_OPTIONS: "--unhandled-rejections=strict"
+      TEXLIVE_IMAGE:
+      ENABLE_PDF_CACHING: "true"
+    command: npm run test:acceptance:_run
+
+
+  tar:
+    build: .
+    image: ci/$PROJECT_NAME:$BRANCH_NAME-$BUILD_NUMBER
+    volumes:
+      - ./:/tmp/build/
+    command: tar -czf /tmp/build/build.tar.gz --exclude=build.tar.gz --exclude-vcs .
+    user: root

+ 43 - 0
services/clsi/docker-compose.yml

@@ -0,0 +1,43 @@
+# This file was auto-generated, do not edit it directly.
+# Instead run bin/update_build_scripts from
+# https://github.com/sharelatex/sharelatex-dev-environment
+
+version: "2.3"
+
+services:
+  test_unit:
+    build:
+      context: .
+      target: base
+    volumes:
+      - .:/app
+    working_dir: /app
+    environment:
+      MOCHA_GREP: ${MOCHA_GREP}
+      NODE_ENV: test
+      NODE_OPTIONS: "--unhandled-rejections=strict"
+    command: npm run --silent test:unit
+
+  test_acceptance:
+    build:
+      context: .
+      target: base
+    volumes:
+      - .:/app
+    working_dir: /app
+    extends:
+      file: docker-compose-config.yml
+      service: dev
+    environment:
+      ELASTIC_SEARCH_DSN: es:9200
+      REDIS_HOST: redis
+      QUEUES_REDIS_HOST: redis
+      MONGO_HOST: mongo
+      POSTGRES_HOST: postgres
+      MOCHA_GREP: ${MOCHA_GREP}
+      LOG_LEVEL: ERROR
+      NODE_ENV: test
+      NODE_OPTIONS: "--unhandled-rejections=strict"
+      ENABLE_PDF_CACHING: "true"
+    command: npm run --silent test:acceptance
+

+ 19 - 0
services/clsi/entrypoint.sh

@@ -0,0 +1,19 @@
+#!/bin/sh
+
+docker --version >&2
+
+# add the node user to the docker group on the host
+DOCKER_GROUP=$(stat -c '%g' /var/run/docker.sock)
+groupadd --non-unique --gid ${DOCKER_GROUP} dockeronhost
+usermod -aG dockeronhost node
+
+# compatibility: initial volume setup
+mkdir -p /app/cache && chown node:node /app/cache
+mkdir -p /app/compiles && chown node:node /app/compiles
+mkdir -p /app/db && chown node:node /app/db
+mkdir -p /app/output && chown node:node /app/output
+
+# make synctex available for remount in compiles
+cp /app/bin/synctex /app/bin/synctex-mount/synctex
+
+exec runuser -u node -- "$@"

+ 24 - 0
services/clsi/install_deps.sh

@@ -0,0 +1,24 @@
+#!/bin/bash
+set -ex
+
+apt-get update
+apt-get install -y \
+    apt-transport-https \
+    ca-certificates \
+    curl \
+    gnupg \
+    lsb-release
+
+curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
+echo \
+  "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian \
+  $(lsb_release -cs) stable" \
+  > /etc/apt/sources.list.d/docker.list
+apt-get update
+
+apt-get install -y \
+  docker-ce-cli \
+  poppler-utils \
+  ghostscript \
+
+rm -rf /var/lib/apt/lists/*

+ 41 - 0
services/clsi/kube.yaml

@@ -0,0 +1,41 @@
+apiVersion: v1
+kind: Service
+metadata:
+  name: clsi
+  namespace: default
+spec:
+  type: LoadBalancer
+  ports:
+  - port: 80
+    protocol: TCP
+    targetPort: 80
+  selector:
+    run: clsi
+---
+apiVersion: extensions/v1beta1
+kind: Deployment
+metadata:
+  name: clsi
+  namespace: default
+spec:
+  replicas: 2
+  template:
+    metadata:
+      labels:
+        run: clsi
+    spec:
+      containers:
+      - name: clsi
+        image: gcr.io/henry-terraform-admin/clsi
+        imagePullPolicy: Always
+        readinessProbe:
+          httpGet:
+            path: status
+            port: 80
+          periodSeconds: 5
+          initialDelaySeconds: 0
+          failureThreshold: 3
+          successThreshold: 1
+
+
+

+ 17 - 0
services/clsi/nodemon.json

@@ -0,0 +1,17 @@
+{
+  "ignore": [
+    ".git",
+    "node_modules/"
+  ],
+  "verbose": true,
+  "legacyWatch": true,
+  "execMap": {
+    "js": "npm run start"
+  },
+  "watch": [
+    "app/js/",
+    "app.js",
+    "config/"
+  ],
+  "ext": "js"
+}

+ 6775 - 0
services/clsi/package-lock.json

@@ -0,0 +1,6775 @@
+{
+  "name": "node-clsi",
+  "version": "0.1.4",
+  "lockfileVersion": 1,
+  "requires": true,
+  "dependencies": {
+    "@babel/code-frame": {
+      "version": "7.12.11",
+      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz",
+      "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==",
+      "dev": true,
+      "requires": {
+        "@babel/highlight": "^7.10.4"
+      }
+    },
+    "@babel/helper-validator-identifier": {
+      "version": "7.14.5",
+      "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz",
+      "integrity": "sha512-5lsetuxCLilmVGyiLEfoHBRX8UCFD+1m2x3Rj97WrW3V7H3u4RWRXA4evMjImCsin2J2YT0QaVDGf+z8ondbAg==",
+      "dev": true
+    },
+    "@babel/highlight": {
+      "version": "7.14.5",
+      "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz",
+      "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-validator-identifier": "^7.14.5",
+        "chalk": "^2.0.0",
+        "js-tokens": "^4.0.0"
+      },
+      "dependencies": {
+        "chalk": {
+          "version": "2.4.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+          "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^3.2.1",
+            "escape-string-regexp": "^1.0.5",
+            "supports-color": "^5.3.0"
+          }
+        }
+      }
+    },
+    "@eslint/eslintrc": {
+      "version": "0.4.2",
+      "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.2.tgz",
+      "integrity": "sha512-8nmGq/4ycLpIwzvhI4tNDmQztZ8sp+hI7cyG8i1nQDhkAbRzHpXPidRAHlNvCZQpJTKw5ItIpMw9RSToGF00mg==",
+      "dev": true,
+      "requires": {
+        "ajv": "^6.12.4",
+        "debug": "^4.1.1",
+        "espree": "^7.3.0",
+        "globals": "^13.9.0",
+        "ignore": "^4.0.6",
+        "import-fresh": "^3.2.1",
+        "js-yaml": "^3.13.1",
+        "minimatch": "^3.0.4",
+        "strip-json-comments": "^3.1.1"
+      },
+      "dependencies": {
+        "ajv": {
+          "version": "6.12.6",
+          "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+          "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+          "dev": true,
+          "requires": {
+            "fast-deep-equal": "^3.1.1",
+            "fast-json-stable-stringify": "^2.0.0",
+            "json-schema-traverse": "^0.4.1",
+            "uri-js": "^4.2.2"
+          }
+        },
+        "debug": {
+          "version": "4.3.2",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz",
+          "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==",
+          "dev": true,
+          "requires": {
+            "ms": "2.1.2"
+          }
+        },
+        "ms": {
+          "version": "2.1.2",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+          "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+          "dev": true
+        },
+        "strip-json-comments": {
+          "version": "3.1.1",
+          "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+          "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+          "dev": true
+        },
+        "type-fest": {
+          "version": "0.20.2",
+          "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+          "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ=="
+        }
+      }
+    },
+    "@google-cloud/common": {
+      "version": "2.4.0",
+      "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-2.4.0.tgz",
+      "integrity": "sha512-zWFjBS35eI9leAHhjfeOYlK5Plcuj/77EzstnrJIZbKgF/nkqjcQuGiMCpzCwOfPyUbz8ZaEOYgbHa759AKbjg==",
+      "requires": {
+        "@google-cloud/projectify": "^1.0.0",
+        "@google-cloud/promisify": "^1.0.0",
+        "arrify": "^2.0.0",
+        "duplexify": "^3.6.0",
+        "ent": "^2.2.0",
+        "extend": "^3.0.2",
+        "google-auth-library": "^5.5.0",
+        "retry-request": "^4.0.0",
+        "teeny-request": "^6.0.0"
+      },
+      "dependencies": {
+        "google-auth-library": {
+          "version": "5.10.1",
+          "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-5.10.1.tgz",
+          "integrity": "sha512-rOlaok5vlpV9rSiUu5EpR0vVpc+PhN62oF4RyX/6++DG1VsaulAFEMlDYBLjJDDPI6OcNOCGAKy9UVB/3NIDXg==",
+          "requires": {
+            "arrify": "^2.0.0",
+            "base64-js": "^1.3.0",
+            "ecdsa-sig-formatter": "^1.0.11",
+            "fast-text-encoding": "^1.0.0",
+            "gaxios": "^2.1.0",
+            "gcp-metadata": "^3.4.0",
+            "gtoken": "^4.1.0",
+            "jws": "^4.0.0",
+            "lru-cache": "^5.0.0"
+          }
+        }
+      }
+    },
+    "@google-cloud/debug-agent": {
+      "version": "5.1.3",
+      "resolved": "https://registry.npmjs.org/@google-cloud/debug-agent/-/debug-agent-5.1.3.tgz",
+      "integrity": "sha512-WbzeEz4MvPlM7DX2QBsPcWgF62u7LSQv/oMYPl0L+TddTebqjDKiVXwxpzWk61NIfcKiet3dyCbPIt3N5o8XPQ==",
+      "requires": {
+        "@google-cloud/common": "^3.0.0",
+        "acorn": "^8.0.0",
+        "coffeescript": "^2.0.0",
+        "console-log-level": "^1.4.0",
+        "extend": "^3.0.2",
+        "findit2": "^2.2.3",
+        "gcp-metadata": "^4.0.0",
+        "p-limit": "^3.0.1",
+        "semver": "^7.0.0",
+        "source-map": "^0.6.1",
+        "split": "^1.0.0"
+      },
+      "dependencies": {
+        "@google-cloud/common": {
+          "version": "3.5.0",
+          "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-3.5.0.tgz",
+          "integrity": "sha512-10d7ZAvKhq47L271AqvHEd8KzJqGU45TY+rwM2Z3JHuB070FeTi7oJJd7elfrnKaEvaktw3hH2wKnRWxk/3oWQ==",
+          "requires": {
+            "@google-cloud/projectify": "^2.0.0",
+            "@google-cloud/promisify": "^2.0.0",
+            "arrify": "^2.0.1",
+            "duplexify": "^4.1.1",
+            "ent": "^2.2.0",
+            "extend": "^3.0.2",
+            "google-auth-library": "^6.1.1",
+            "retry-request": "^4.1.1",
+            "teeny-request": "^7.0.0"
+          }
+        },
+        "@google-cloud/projectify": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-2.0.1.tgz",
+          "integrity": "sha512-ZDG38U/Yy6Zr21LaR3BTiiLtpJl6RkPS/JwoRT453G+6Q1DhlV0waNf8Lfu+YVYGIIxgKnLayJRfYlFJfiI8iQ=="
+        },
+        "@google-cloud/promisify": {
+          "version": "2.0.3",
+          "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-2.0.3.tgz",
+          "integrity": "sha512-d4VSA86eL/AFTe5xtyZX+ePUjE8dIFu2T8zmdeNBSa5/kNgXPCx/o/wbFNHAGLJdGnk1vddRuMESD9HbOC8irw=="
+        },
+        "bignumber.js": {
+          "version": "9.0.1",
+          "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.1.tgz",
+          "integrity": "sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA=="
+        },
+        "duplexify": {
+          "version": "4.1.1",
+          "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.1.tgz",
+          "integrity": "sha512-DY3xVEmVHTv1wSzKNbwoU6nVjzI369Y6sPoqfYr0/xlx3IdX2n94xIszTcjPO8W8ZIv0Wb0PXNcjuZyT4wiICA==",
+          "requires": {
+            "end-of-stream": "^1.4.1",
+            "inherits": "^2.0.3",
+            "readable-stream": "^3.1.1",
+            "stream-shift": "^1.0.0"
+          }
+        },
+        "gaxios": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-4.1.0.tgz",
+          "integrity": "sha512-vb0to8xzGnA2qcgywAjtshOKKVDf2eQhJoiL6fHhgW5tVN7wNk7egnYIO9zotfn3lQ3De1VPdf7V5/BWfCtCmg==",
+          "requires": {
+            "abort-controller": "^3.0.0",
+            "extend": "^3.0.2",
+            "https-proxy-agent": "^5.0.0",
+            "is-stream": "^2.0.0",
+            "node-fetch": "^2.3.0"
+          }
+        },
+        "gcp-metadata": {
+          "version": "4.2.1",
+          "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-4.2.1.tgz",
+          "integrity": "sha512-tSk+REe5iq/N+K+SK1XjZJUrFPuDqGZVzCy2vocIHIGmPlTGsa8owXMJwGkrXr73NO0AzhPW4MF2DEHz7P2AVw==",
+          "requires": {
+            "gaxios": "^4.0.0",
+            "json-bigint": "^1.0.0"
+          }
+        },
+        "google-auth-library": {
+          "version": "6.1.6",
+          "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-6.1.6.tgz",
+          "integrity": "sha512-Q+ZjUEvLQj/lrVHF/IQwRo6p3s8Nc44Zk/DALsN+ac3T4HY/g/3rrufkgtl+nZ1TW7DNAw5cTChdVp4apUXVgQ==",
+          "requires": {
+            "arrify": "^2.0.0",
+            "base64-js": "^1.3.0",
+            "ecdsa-sig-formatter": "^1.0.11",
+            "fast-text-encoding": "^1.0.0",
+            "gaxios": "^4.0.0",
+            "gcp-metadata": "^4.2.0",
+            "gtoken": "^5.0.4",
+            "jws": "^4.0.0",
+            "lru-cache": "^6.0.0"
+          }
+        },
+        "google-p12-pem": {
+          "version": "3.0.3",
+          "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-3.0.3.tgz",
+          "integrity": "sha512-wS0ek4ZtFx/ACKYF3JhyGe5kzH7pgiQ7J5otlumqR9psmWMYc+U9cErKlCYVYHoUaidXHdZ2xbo34kB+S+24hA==",
+          "requires": {
+            "node-forge": "^0.10.0"
+          }
+        },
+        "gtoken": {
+          "version": "5.2.1",
+          "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-5.2.1.tgz",
+          "integrity": "sha512-OY0BfPKe3QnMsY9MzTHTSKn+Vl2l1CcLe6BwDEQj00mbbkl5nyQ/7EUREstg4fQNZ8iYE7br4JJ7TdKeDOPWmw==",
+          "requires": {
+            "gaxios": "^4.0.0",
+            "google-p12-pem": "^3.0.3",
+            "jws": "^4.0.0"
+          }
+        },
+        "json-bigint": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz",
+          "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==",
+          "requires": {
+            "bignumber.js": "^9.0.0"
+          }
+        },
+        "lru-cache": {
+          "version": "6.0.0",
+          "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+          "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+          "requires": {
+            "yallist": "^4.0.0"
+          }
+        },
+        "node-forge": {
+          "version": "0.10.0",
+          "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz",
+          "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA=="
+        },
+        "readable-stream": {
+          "version": "3.6.0",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+          "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+          "requires": {
+            "inherits": "^2.0.3",
+            "string_decoder": "^1.1.1",
+            "util-deprecate": "^1.0.1"
+          }
+        },
+        "semver": {
+          "version": "7.3.4",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz",
+          "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==",
+          "requires": {
+            "lru-cache": "^6.0.0"
+          }
+        },
+        "teeny-request": {
+          "version": "7.0.1",
+          "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-7.0.1.tgz",
+          "integrity": "sha512-sasJmQ37klOlplL4Ia/786M5YlOcoLGQyq2TE4WHSRupbAuDaQW0PfVxV4MtdBtRJ4ngzS+1qim8zP6Zp35qCw==",
+          "requires": {
+            "http-proxy-agent": "^4.0.0",
+            "https-proxy-agent": "^5.0.0",
+            "node-fetch": "^2.6.1",
+            "stream-events": "^1.0.5",
+            "uuid": "^8.0.0"
+          }
+        },
+        "uuid": {
+          "version": "8.3.2",
+          "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
+          "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
+        },
+        "yallist": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+          "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
+        }
+      }
+    },
+    "@google-cloud/logging": {
+      "version": "7.3.0",
+      "resolved": "https://registry.npmjs.org/@google-cloud/logging/-/logging-7.3.0.tgz",
+      "integrity": "sha512-xTW1V4MKpYC0mjSugyuiyUoZ9g6A42IhrrO3z7Tt3SmAb2IRj2Gf4RLoguKKncs340ooZFXrrVN/++t2Aj5zgg==",
+      "requires": {
+        "@google-cloud/common": "^2.2.2",
+        "@google-cloud/paginator": "^2.0.0",
+        "@google-cloud/projectify": "^1.0.0",
+        "@google-cloud/promisify": "^1.0.0",
+        "@opencensus/propagation-stackdriver": "0.0.20",
+        "arrify": "^2.0.0",
+        "dot-prop": "^5.1.0",
+        "eventid": "^1.0.0",
+        "extend": "^3.0.2",
+        "gcp-metadata": "^3.1.0",
+        "google-auth-library": "^5.2.2",
+        "google-gax": "^1.11.0",
+        "is": "^3.3.0",
+        "on-finished": "^2.3.0",
+        "pumpify": "^2.0.0",
+        "snakecase-keys": "^3.0.0",
+        "stream-events": "^1.0.4",
+        "through2": "^3.0.0",
+        "type-fest": "^0.12.0"
+      },
+      "dependencies": {
+        "google-auth-library": {
+          "version": "5.10.1",
+          "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-5.10.1.tgz",
+          "integrity": "sha512-rOlaok5vlpV9rSiUu5EpR0vVpc+PhN62oF4RyX/6++DG1VsaulAFEMlDYBLjJDDPI6OcNOCGAKy9UVB/3NIDXg==",
+          "requires": {
+            "arrify": "^2.0.0",
+            "base64-js": "^1.3.0",
+            "ecdsa-sig-formatter": "^1.0.11",
+            "fast-text-encoding": "^1.0.0",
+            "gaxios": "^2.1.0",
+            "gcp-metadata": "^3.4.0",
+            "gtoken": "^4.1.0",
+            "jws": "^4.0.0",
+            "lru-cache": "^5.0.0"
+          }
+        },
+        "type-fest": {
+          "version": "0.12.0",
+          "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.12.0.tgz",
+          "integrity": "sha512-53RyidyjvkGpnWPMF9bQgFtWp+Sl8O2Rp13VavmJgfAP9WWG6q6TkrKU8iyJdnwnfgHI6k2hTlgqH4aSdjoTbg=="
+        }
+      }
+    },
+    "@google-cloud/logging-bunyan": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/@google-cloud/logging-bunyan/-/logging-bunyan-3.0.0.tgz",
+      "integrity": "sha512-ZLVXEejNQ27ktGcA3S/sd7GPefp7kywbn+/KoBajdb1Syqcmtc98jhXpYQBXVtNP2065iyu77s4SBaiYFbTC5A==",
+      "requires": {
+        "@google-cloud/logging": "^7.0.0",
+        "google-auth-library": "^6.0.0"
+      }
+    },
+    "@google-cloud/paginator": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-2.0.3.tgz",
+      "integrity": "sha512-kp/pkb2p/p0d8/SKUu4mOq8+HGwF8NPzHWkj+VKrIPQPyMRw8deZtrO/OcSiy9C/7bpfU5Txah5ltUNfPkgEXg==",
+      "requires": {
+        "arrify": "^2.0.0",
+        "extend": "^3.0.2"
+      }
+    },
+    "@google-cloud/profiler": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/@google-cloud/profiler/-/profiler-4.1.0.tgz",
+      "integrity": "sha512-9e1zXRctLSUHAoAsFGwE4rS28fr0siiG+jXl5OpwTK8ZAUlxb70aosHaZGdsv8YXrYKjuiufjRZ/OXCs0XLI9g==",
+      "requires": {
+        "@google-cloud/common": "^3.0.0",
+        "@types/console-log-level": "^1.4.0",
+        "@types/semver": "^7.0.0",
+        "console-log-level": "^1.4.0",
+        "delay": "^4.0.1",
+        "extend": "^3.0.2",
+        "gcp-metadata": "^4.0.0",
+        "parse-duration": "^0.4.4",
+        "pprof": "3.0.0",
+        "pretty-ms": "^7.0.0",
+        "protobufjs": "~6.10.0",
+        "semver": "^7.0.0",
+        "teeny-request": "^7.0.0"
+      },
+      "dependencies": {
+        "@google-cloud/common": {
+          "version": "3.5.0",
+          "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-3.5.0.tgz",
+          "integrity": "sha512-10d7ZAvKhq47L271AqvHEd8KzJqGU45TY+rwM2Z3JHuB070FeTi7oJJd7elfrnKaEvaktw3hH2wKnRWxk/3oWQ==",
+          "requires": {
+            "@google-cloud/projectify": "^2.0.0",
+            "@google-cloud/promisify": "^2.0.0",
+            "arrify": "^2.0.1",
+            "duplexify": "^4.1.1",
+            "ent": "^2.2.0",
+            "extend": "^3.0.2",
+            "google-auth-library": "^6.1.1",
+            "retry-request": "^4.1.1",
+            "teeny-request": "^7.0.0"
+          }
+        },
+        "@google-cloud/projectify": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-2.0.1.tgz",
+          "integrity": "sha512-ZDG38U/Yy6Zr21LaR3BTiiLtpJl6RkPS/JwoRT453G+6Q1DhlV0waNf8Lfu+YVYGIIxgKnLayJRfYlFJfiI8iQ=="
+        },
+        "@google-cloud/promisify": {
+          "version": "2.0.3",
+          "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-2.0.3.tgz",
+          "integrity": "sha512-d4VSA86eL/AFTe5xtyZX+ePUjE8dIFu2T8zmdeNBSa5/kNgXPCx/o/wbFNHAGLJdGnk1vddRuMESD9HbOC8irw=="
+        },
+        "@types/long": {
+          "version": "4.0.1",
+          "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz",
+          "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w=="
+        },
+        "@types/node": {
+          "version": "13.13.42",
+          "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.42.tgz",
+          "integrity": "sha512-g+w2QgbW7k2CWLOXzQXbO37a7v5P9ObPvYahKphdBLV5aqpbVZRhTpWCT0SMRqX1i30Aig791ZmIM2fJGL2S8A=="
+        },
+        "bignumber.js": {
+          "version": "9.0.1",
+          "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.1.tgz",
+          "integrity": "sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA=="
+        },
+        "duplexify": {
+          "version": "4.1.1",
+          "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.1.tgz",
+          "integrity": "sha512-DY3xVEmVHTv1wSzKNbwoU6nVjzI369Y6sPoqfYr0/xlx3IdX2n94xIszTcjPO8W8ZIv0Wb0PXNcjuZyT4wiICA==",
+          "requires": {
+            "end-of-stream": "^1.4.1",
+            "inherits": "^2.0.3",
+            "readable-stream": "^3.1.1",
+            "stream-shift": "^1.0.0"
+          }
+        },
+        "gaxios": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-4.1.0.tgz",
+          "integrity": "sha512-vb0to8xzGnA2qcgywAjtshOKKVDf2eQhJoiL6fHhgW5tVN7wNk7egnYIO9zotfn3lQ3De1VPdf7V5/BWfCtCmg==",
+          "requires": {
+            "abort-controller": "^3.0.0",
+            "extend": "^3.0.2",
+            "https-proxy-agent": "^5.0.0",
+            "is-stream": "^2.0.0",
+            "node-fetch": "^2.3.0"
+          }
+        },
+        "gcp-metadata": {
+          "version": "4.2.1",
+          "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-4.2.1.tgz",
+          "integrity": "sha512-tSk+REe5iq/N+K+SK1XjZJUrFPuDqGZVzCy2vocIHIGmPlTGsa8owXMJwGkrXr73NO0AzhPW4MF2DEHz7P2AVw==",
+          "requires": {
+            "gaxios": "^4.0.0",
+            "json-bigint": "^1.0.0"
+          }
+        },
+        "google-auth-library": {
+          "version": "6.1.6",
+          "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-6.1.6.tgz",
+          "integrity": "sha512-Q+ZjUEvLQj/lrVHF/IQwRo6p3s8Nc44Zk/DALsN+ac3T4HY/g/3rrufkgtl+nZ1TW7DNAw5cTChdVp4apUXVgQ==",
+          "requires": {
+            "arrify": "^2.0.0",
+            "base64-js": "^1.3.0",
+            "ecdsa-sig-formatter": "^1.0.11",
+            "fast-text-encoding": "^1.0.0",
+            "gaxios": "^4.0.0",
+            "gcp-metadata": "^4.2.0",
+            "gtoken": "^5.0.4",
+            "jws": "^4.0.0",
+            "lru-cache": "^6.0.0"
+          }
+        },
+        "google-p12-pem": {
+          "version": "3.0.3",
+          "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-3.0.3.tgz",
+          "integrity": "sha512-wS0ek4ZtFx/ACKYF3JhyGe5kzH7pgiQ7J5otlumqR9psmWMYc+U9cErKlCYVYHoUaidXHdZ2xbo34kB+S+24hA==",
+          "requires": {
+            "node-forge": "^0.10.0"
+          }
+        },
+        "gtoken": {
+          "version": "5.2.1",
+          "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-5.2.1.tgz",
+          "integrity": "sha512-OY0BfPKe3QnMsY9MzTHTSKn+Vl2l1CcLe6BwDEQj00mbbkl5nyQ/7EUREstg4fQNZ8iYE7br4JJ7TdKeDOPWmw==",
+          "requires": {
+            "gaxios": "^4.0.0",
+            "google-p12-pem": "^3.0.3",
+            "jws": "^4.0.0"
+          }
+        },
+        "json-bigint": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz",
+          "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==",
+          "requires": {
+            "bignumber.js": "^9.0.0"
+          }
+        },
+        "lru-cache": {
+          "version": "6.0.0",
+          "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+          "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+          "requires": {
+            "yallist": "^4.0.0"
+          }
+        },
+        "node-forge": {
+          "version": "0.10.0",
+          "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz",
+          "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA=="
+        },
+        "protobufjs": {
+          "version": "6.10.2",
+          "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.10.2.tgz",
+          "integrity": "sha512-27yj+04uF6ya9l+qfpH187aqEzfCF4+Uit0I9ZBQVqK09hk/SQzKa2MUqUpXaVa7LOFRg1TSSr3lVxGOk6c0SQ==",
+          "requires": {
+            "@protobufjs/aspromise": "^1.1.2",
+            "@protobufjs/base64": "^1.1.2",
+            "@protobufjs/codegen": "^2.0.4",
+            "@protobufjs/eventemitter": "^1.1.0",
+            "@protobufjs/fetch": "^1.1.0",
+            "@protobufjs/float": "^1.0.2",
+            "@protobufjs/inquire": "^1.1.0",
+            "@protobufjs/path": "^1.1.2",
+            "@protobufjs/pool": "^1.1.0",
+            "@protobufjs/utf8": "^1.1.0",
+            "@types/long": "^4.0.1",
+            "@types/node": "^13.7.0",
+            "long": "^4.0.0"
+          }
+        },
+        "readable-stream": {
+          "version": "3.6.0",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+          "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+          "requires": {
+            "inherits": "^2.0.3",
+            "string_decoder": "^1.1.1",
+            "util-deprecate": "^1.0.1"
+          }
+        },
+        "semver": {
+          "version": "7.3.4",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz",
+          "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==",
+          "requires": {
+            "lru-cache": "^6.0.0"
+          }
+        },
+        "teeny-request": {
+          "version": "7.0.1",
+          "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-7.0.1.tgz",
+          "integrity": "sha512-sasJmQ37klOlplL4Ia/786M5YlOcoLGQyq2TE4WHSRupbAuDaQW0PfVxV4MtdBtRJ4ngzS+1qim8zP6Zp35qCw==",
+          "requires": {
+            "http-proxy-agent": "^4.0.0",
+            "https-proxy-agent": "^5.0.0",
+            "node-fetch": "^2.6.1",
+            "stream-events": "^1.0.5",
+            "uuid": "^8.0.0"
+          }
+        },
+        "uuid": {
+          "version": "8.3.2",
+          "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
+          "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
+        },
+        "yallist": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+          "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
+        }
+      }
+    },
+    "@google-cloud/projectify": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-1.0.4.tgz",
+      "integrity": "sha512-ZdzQUN02eRsmTKfBj9FDL0KNDIFNjBn/d6tHQmA/+FImH5DO6ZV8E7FzxMgAUiVAUq41RFAkb25p1oHOZ8psfg=="
+    },
+    "@google-cloud/promisify": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-1.0.4.tgz",
+      "integrity": "sha512-VccZDcOql77obTnFh0TbNED/6ZbbmHDf8UMNnzO1d5g9V0Htfm4k5cllY8P1tJsRKC3zWYGRLaViiupcgVjBoQ=="
+    },
+    "@google-cloud/trace-agent": {
+      "version": "5.1.3",
+      "resolved": "https://registry.npmjs.org/@google-cloud/trace-agent/-/trace-agent-5.1.3.tgz",
+      "integrity": "sha512-f+5DX7n6QpDlHA+4kr81z69SLAdrlvd9T8skqCMgnYvtXx14AwzXZyzEDf3jppOYzYoqPPJv8XYiyYHHmYD0BA==",
+      "requires": {
+        "@google-cloud/common": "^3.0.0",
+        "@opencensus/propagation-stackdriver": "0.0.22",
+        "builtin-modules": "^3.0.0",
+        "console-log-level": "^1.4.0",
+        "continuation-local-storage": "^3.2.1",
+        "extend": "^3.0.2",
+        "gcp-metadata": "^4.0.0",
+        "google-auth-library": "^7.0.0",
+        "hex2dec": "^1.0.1",
+        "is": "^3.2.0",
+        "methods": "^1.1.1",
+        "require-in-the-middle": "^5.0.0",
+        "semver": "^7.0.0",
+        "shimmer": "^1.2.0",
+        "source-map-support": "^0.5.16",
+        "uuid": "^8.0.0"
+      },
+      "dependencies": {
+        "@google-cloud/common": {
+          "version": "3.5.0",
+          "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-3.5.0.tgz",
+          "integrity": "sha512-10d7ZAvKhq47L271AqvHEd8KzJqGU45TY+rwM2Z3JHuB070FeTi7oJJd7elfrnKaEvaktw3hH2wKnRWxk/3oWQ==",
+          "requires": {
+            "@google-cloud/projectify": "^2.0.0",
+            "@google-cloud/promisify": "^2.0.0",
+            "arrify": "^2.0.1",
+            "duplexify": "^4.1.1",
+            "ent": "^2.2.0",
+            "extend": "^3.0.2",
+            "google-auth-library": "^6.1.1",
+            "retry-request": "^4.1.1",
+            "teeny-request": "^7.0.0"
+          },
+          "dependencies": {
+            "google-auth-library": {
+              "version": "6.1.6",
+              "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-6.1.6.tgz",
+              "integrity": "sha512-Q+ZjUEvLQj/lrVHF/IQwRo6p3s8Nc44Zk/DALsN+ac3T4HY/g/3rrufkgtl+nZ1TW7DNAw5cTChdVp4apUXVgQ==",
+              "requires": {
+                "arrify": "^2.0.0",
+                "base64-js": "^1.3.0",
+                "ecdsa-sig-formatter": "^1.0.11",
+                "fast-text-encoding": "^1.0.0",
+                "gaxios": "^4.0.0",
+                "gcp-metadata": "^4.2.0",
+                "gtoken": "^5.0.4",
+                "jws": "^4.0.0",
+                "lru-cache": "^6.0.0"
+              }
+            }
+          }
+        },
+        "@google-cloud/projectify": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-2.0.1.tgz",
+          "integrity": "sha512-ZDG38U/Yy6Zr21LaR3BTiiLtpJl6RkPS/JwoRT453G+6Q1DhlV0waNf8Lfu+YVYGIIxgKnLayJRfYlFJfiI8iQ=="
+        },
+        "@google-cloud/promisify": {
+          "version": "2.0.3",
+          "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-2.0.3.tgz",
+          "integrity": "sha512-d4VSA86eL/AFTe5xtyZX+ePUjE8dIFu2T8zmdeNBSa5/kNgXPCx/o/wbFNHAGLJdGnk1vddRuMESD9HbOC8irw=="
+        },
+        "@opencensus/core": {
+          "version": "0.0.22",
+          "resolved": "https://registry.npmjs.org/@opencensus/core/-/core-0.0.22.tgz",
+          "integrity": "sha512-ErazJtivjceNoOZI1bG9giQ6cWS45J4i6iPUtlp7dLNu58OLs/v+CD0FsaPCh47XgPxAI12vbBE8Ec09ViwHNA==",
+          "requires": {
+            "continuation-local-storage": "^3.2.1",
+            "log-driver": "^1.2.7",
+            "semver": "^7.0.0",
+            "shimmer": "^1.2.0",
+            "uuid": "^8.0.0"
+          }
+        },
+        "@opencensus/propagation-stackdriver": {
+          "version": "0.0.22",
+          "resolved": "https://registry.npmjs.org/@opencensus/propagation-stackdriver/-/propagation-stackdriver-0.0.22.tgz",
+          "integrity": "sha512-eBvf/ihb1mN8Yz/ASkz8nHzuMKqygu77+VNnUeR0yEh3Nj+ykB8VVR6lK+NAFXo1Rd1cOsTmgvuXAZgDAGleQQ==",
+          "requires": {
+            "@opencensus/core": "^0.0.22",
+            "hex2dec": "^1.0.1",
+            "uuid": "^8.0.0"
+          }
+        },
+        "bignumber.js": {
+          "version": "9.0.1",
+          "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.1.tgz",
+          "integrity": "sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA=="
+        },
+        "duplexify": {
+          "version": "4.1.1",
+          "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.1.tgz",
+          "integrity": "sha512-DY3xVEmVHTv1wSzKNbwoU6nVjzI369Y6sPoqfYr0/xlx3IdX2n94xIszTcjPO8W8ZIv0Wb0PXNcjuZyT4wiICA==",
+          "requires": {
+            "end-of-stream": "^1.4.1",
+            "inherits": "^2.0.3",
+            "readable-stream": "^3.1.1",
+            "stream-shift": "^1.0.0"
+          }
+        },
+        "gaxios": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-4.1.0.tgz",
+          "integrity": "sha512-vb0to8xzGnA2qcgywAjtshOKKVDf2eQhJoiL6fHhgW5tVN7wNk7egnYIO9zotfn3lQ3De1VPdf7V5/BWfCtCmg==",
+          "requires": {
+            "abort-controller": "^3.0.0",
+            "extend": "^3.0.2",
+            "https-proxy-agent": "^5.0.0",
+            "is-stream": "^2.0.0",
+            "node-fetch": "^2.3.0"
+          }
+        },
+        "gcp-metadata": {
+          "version": "4.2.1",
+          "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-4.2.1.tgz",
+          "integrity": "sha512-tSk+REe5iq/N+K+SK1XjZJUrFPuDqGZVzCy2vocIHIGmPlTGsa8owXMJwGkrXr73NO0AzhPW4MF2DEHz7P2AVw==",
+          "requires": {
+            "gaxios": "^4.0.0",
+            "json-bigint": "^1.0.0"
+          }
+        },
+        "google-auth-library": {
+          "version": "7.0.2",
+          "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-7.0.2.tgz",
+          "integrity": "sha512-vjyNZR3pDLC0u7GHLfj+Hw9tGprrJwoMwkYGqURCXYITjCrP9HprOyxVV+KekdLgATtWGuDkQG2MTh0qpUPUgg==",
+          "requires": {
+            "arrify": "^2.0.0",
+            "base64-js": "^1.3.0",
+            "ecdsa-sig-formatter": "^1.0.11",
+            "fast-text-encoding": "^1.0.0",
+            "gaxios": "^4.0.0",
+            "gcp-metadata": "^4.2.0",
+            "gtoken": "^5.0.4",
+            "jws": "^4.0.0",
+            "lru-cache": "^6.0.0"
+          }
+        },
+        "google-p12-pem": {
+          "version": "3.0.3",
+          "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-3.0.3.tgz",
+          "integrity": "sha512-wS0ek4ZtFx/ACKYF3JhyGe5kzH7pgiQ7J5otlumqR9psmWMYc+U9cErKlCYVYHoUaidXHdZ2xbo34kB+S+24hA==",
+          "requires": {
+            "node-forge": "^0.10.0"
+          }
+        },
+        "gtoken": {
+          "version": "5.2.1",
+          "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-5.2.1.tgz",
+          "integrity": "sha512-OY0BfPKe3QnMsY9MzTHTSKn+Vl2l1CcLe6BwDEQj00mbbkl5nyQ/7EUREstg4fQNZ8iYE7br4JJ7TdKeDOPWmw==",
+          "requires": {
+            "gaxios": "^4.0.0",
+            "google-p12-pem": "^3.0.3",
+            "jws": "^4.0.0"
+          }
+        },
+        "json-bigint": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz",
+          "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==",
+          "requires": {
+            "bignumber.js": "^9.0.0"
+          }
+        },
+        "lru-cache": {
+          "version": "6.0.0",
+          "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+          "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+          "requires": {
+            "yallist": "^4.0.0"
+          }
+        },
+        "node-forge": {
+          "version": "0.10.0",
+          "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz",
+          "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA=="
+        },
+        "readable-stream": {
+          "version": "3.6.0",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+          "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+          "requires": {
+            "inherits": "^2.0.3",
+            "string_decoder": "^1.1.1",
+            "util-deprecate": "^1.0.1"
+          }
+        },
+        "semver": {
+          "version": "7.3.4",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz",
+          "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==",
+          "requires": {
+            "lru-cache": "^6.0.0"
+          }
+        },
+        "teeny-request": {
+          "version": "7.0.1",
+          "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-7.0.1.tgz",
+          "integrity": "sha512-sasJmQ37klOlplL4Ia/786M5YlOcoLGQyq2TE4WHSRupbAuDaQW0PfVxV4MtdBtRJ4ngzS+1qim8zP6Zp35qCw==",
+          "requires": {
+            "http-proxy-agent": "^4.0.0",
+            "https-proxy-agent": "^5.0.0",
+            "node-fetch": "^2.6.1",
+            "stream-events": "^1.0.5",
+            "uuid": "^8.0.0"
+          }
+        },
+        "uuid": {
+          "version": "8.3.2",
+          "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
+          "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
+        },
+        "yallist": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+          "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
+        }
+      }
+    },
+    "@grpc/grpc-js": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.0.5.tgz",
+      "integrity": "sha512-Hm+xOiqAhcpT9RYM8lc15dbQD7aQurM7ZU8ulmulepiPlN7iwBXXwP3vSBUimoFoApRqz7pSIisXU8pZaCB4og==",
+      "requires": {
+        "semver": "^6.2.0"
+      },
+      "dependencies": {
+        "semver": {
+          "version": "6.3.0",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+          "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
+        }
+      }
+    },
+    "@grpc/proto-loader": {
+      "version": "0.5.5",
+      "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.5.5.tgz",
+      "integrity": "sha512-WwN9jVNdHRQoOBo9FDH7qU+mgfjPc8GygPYms3M+y3fbQLfnCe/Kv/E01t7JRgnrsOHH8euvSbed3mIalXhwqQ==",
+      "requires": {
+        "lodash.camelcase": "^4.3.0",
+        "protobufjs": "^6.8.6"
+      }
+    },
+    "@humanwhocodes/config-array": {
+      "version": "0.5.0",
+      "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz",
+      "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==",
+      "dev": true,
+      "requires": {
+        "@humanwhocodes/object-schema": "^1.2.0",
+        "debug": "^4.1.1",
+        "minimatch": "^3.0.4"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "4.3.2",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz",
+          "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==",
+          "dev": true,
+          "requires": {
+            "ms": "2.1.2"
+          }
+        },
+        "ms": {
+          "version": "2.1.2",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+          "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+          "dev": true
+        }
+      }
+    },
+    "@humanwhocodes/object-schema": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz",
+      "integrity": "sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==",
+      "dev": true
+    },
+    "@opencensus/core": {
+      "version": "0.0.20",
+      "resolved": "https://registry.npmjs.org/@opencensus/core/-/core-0.0.20.tgz",
+      "integrity": "sha512-vqOuTd2yuMpKohp8TNNGUAPjWEGjlnGfB9Rh5e3DKqeyR94YgierNs4LbMqxKtsnwB8Dm2yoEtRuUgoe5vD9DA==",
+      "requires": {
+        "continuation-local-storage": "^3.2.1",
+        "log-driver": "^1.2.7",
+        "semver": "^6.0.0",
+        "shimmer": "^1.2.0",
+        "uuid": "^3.2.1"
+      },
+      "dependencies": {
+        "semver": {
+          "version": "6.3.0",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+          "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
+        }
+      }
+    },
+    "@opencensus/propagation-stackdriver": {
+      "version": "0.0.20",
+      "resolved": "https://registry.npmjs.org/@opencensus/propagation-stackdriver/-/propagation-stackdriver-0.0.20.tgz",
+      "integrity": "sha512-P8yuHSLtce+yb+2EZjtTVqG7DQ48laC+IuOWi3X9q78s1Gni5F9+hmbmyP6Nb61jb5BEvXQX1s2rtRI6bayUWA==",
+      "requires": {
+        "@opencensus/core": "^0.0.20",
+        "hex2dec": "^1.0.1",
+        "uuid": "^3.2.1"
+      }
+    },
+    "@overleaf/metrics": {
+      "version": "3.5.1",
+      "resolved": "https://registry.npmjs.org/@overleaf/metrics/-/metrics-3.5.1.tgz",
+      "integrity": "sha512-RLHxkMF7Y3725L3QwXo9cIn2gGobsMYUGuxKxg7PVMrPTMsomHEMeG7StOxCO7ML1Z/BwB/9nsVYNrsRdAJtKg==",
+      "requires": {
+        "@google-cloud/debug-agent": "^5.1.2",
+        "@google-cloud/profiler": "^4.0.3",
+        "@google-cloud/trace-agent": "^5.1.1",
+        "compression": "^1.7.4",
+        "prom-client": "^11.1.3",
+        "underscore": "~1.6.0",
+        "yn": "^3.1.1"
+      }
+    },
+    "@overleaf/o-error": {
+      "version": "3.3.1",
+      "resolved": "https://registry.npmjs.org/@overleaf/o-error/-/o-error-3.3.1.tgz",
+      "integrity": "sha512-1FRBYZO0lbJ0U+FRGZVS8ou6RhEw3e2B86WW/NbtBw554g0h5iC8ESf+juIfPMU/WDf/JDIFbg3eB/LnP2RSow==",
+      "requires": {
+        "core-js": "^3.8.3"
+      }
+    },
+    "@overleaf/settings": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/@overleaf/settings/-/settings-2.1.1.tgz",
+      "integrity": "sha512-vcJwqCGFKmQxTP/syUqCeMaSRjHmBcQgKOACR9He2uJcErg2GZPa1go+nGvszMbkElM4HfRKm/MfxvqHhoN4TQ=="
+    },
+    "@protobufjs/aspromise": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
+      "integrity": "sha1-m4sMxmPWaafY9vXQiToU00jzD78="
+    },
+    "@protobufjs/base64": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
+      "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="
+    },
+    "@protobufjs/codegen": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz",
+      "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg=="
+    },
+    "@protobufjs/eventemitter": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz",
+      "integrity": "sha1-NVy8mLr61ZePntCV85diHx0Ga3A="
+    },
+    "@protobufjs/fetch": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz",
+      "integrity": "sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU=",
+      "requires": {
+        "@protobufjs/aspromise": "^1.1.1",
+        "@protobufjs/inquire": "^1.1.0"
+      }
+    },
+    "@protobufjs/float": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
+      "integrity": "sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E="
+    },
+    "@protobufjs/inquire": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz",
+      "integrity": "sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik="
+    },
+    "@protobufjs/path": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
+      "integrity": "sha1-bMKyDFya1q0NzP0hynZz2Nf79o0="
+    },
+    "@protobufjs/pool": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
+      "integrity": "sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q="
+    },
+    "@protobufjs/utf8": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
+      "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA="
+    },
+    "@sindresorhus/is": {
+      "version": "0.14.0",
+      "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz",
+      "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==",
+      "dev": true
+    },
+    "@sinonjs/commons": {
+      "version": "1.7.1",
+      "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.7.1.tgz",
+      "integrity": "sha512-Debi3Baff1Qu1Unc3mjJ96MgpbwTn43S1+9yJ0llWygPwDNu2aaWBD6yc9y/Z8XDRNhx7U+u2UDg2OGQXkclUQ==",
+      "dev": true,
+      "requires": {
+        "type-detect": "4.0.8"
+      }
+    },
+    "@sinonjs/fake-timers": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-6.0.0.tgz",
+      "integrity": "sha512-atR1J/jRXvQAb47gfzSK8zavXy7BcpnYq21ALon0U99etu99vsir0trzIO3wpeLtW+LLVY6X7EkfVTbjGSH8Ww==",
+      "dev": true,
+      "requires": {
+        "@sinonjs/commons": "^1.7.0"
+      }
+    },
+    "@sinonjs/formatio": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-5.0.1.tgz",
+      "integrity": "sha512-KaiQ5pBf1MpS09MuA0kp6KBQt2JUOQycqVG1NZXvzeaXe5LGFqAKueIS0bw4w0P9r7KuBSVdUk5QjXsUdu2CxQ==",
+      "dev": true,
+      "requires": {
+        "@sinonjs/commons": "^1",
+        "@sinonjs/samsam": "^5.0.2"
+      }
+    },
+    "@sinonjs/samsam": {
+      "version": "5.0.3",
+      "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-5.0.3.tgz",
+      "integrity": "sha512-QucHkc2uMJ0pFGjJUDP3F9dq5dx8QIaqISl9QgwLOh6P9yv877uONPGXh/OH/0zmM3tW1JjuJltAZV2l7zU+uQ==",
+      "dev": true,
+      "requires": {
+        "@sinonjs/commons": "^1.6.0",
+        "lodash.get": "^4.4.2",
+        "type-detect": "^4.0.8"
+      }
+    },
+    "@sinonjs/text-encoding": {
+      "version": "0.7.1",
+      "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz",
+      "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==",
+      "dev": true
+    },
+    "@szmarczak/http-timer": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz",
+      "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==",
+      "dev": true,
+      "requires": {
+        "defer-to-connect": "^1.0.1"
+      }
+    },
+    "@tootallnate/once": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
+      "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw=="
+    },
+    "@types/console-log-level": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/@types/console-log-level/-/console-log-level-1.4.0.tgz",
+      "integrity": "sha512-x+OscEQwcx5Biair4enH7ov9W+clcqUWaZRaxn5IkT4yNWWjRr2oiYDkY/x1uXSTVZOQ2xlbFQySaQGB+VdXGQ=="
+    },
+    "@types/fs-extra": {
+      "version": "8.1.1",
+      "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.1.1.tgz",
+      "integrity": "sha512-TcUlBem321DFQzBNuz8p0CLLKp0VvF/XH9E4KHNmgwyp4E3AfgI5cjiIVZWlbfThBop2qxFIh4+LeY6hVWWZ2w==",
+      "requires": {
+        "@types/node": "*"
+      }
+    },
+    "@types/long": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.0.tgz",
+      "integrity": "sha512-1w52Nyx4Gq47uuu0EVcsHBxZFJgurQ+rTKS3qMHxR1GY2T8c2AJYd6vZoZ9q1rupaDjU0yT+Jc2XTyXkjeMA+Q=="
+    },
+    "@types/node": {
+      "version": "10.12.15",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.15.tgz",
+      "integrity": "sha1-IOhWUbYv2GZW5Xycm8dxqxVwvFk="
+    },
+    "@types/semver": {
+      "version": "7.3.4",
+      "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.4.tgz",
+      "integrity": "sha512-+nVsLKlcUCeMzD2ufHEYuJ9a2ovstb6Dp52A5VsoKxDXgvE051XgHI/33I1EymwkRGQkwnA0LkhnUzituGs4EQ=="
+    },
+    "@ungap/promise-all-settled": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz",
+      "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==",
+      "dev": true
+    },
+    "abbrev": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
+      "integrity": "sha1-+PLIh60Qv2f2NPAFtph/7TF5qsg="
+    },
+    "abort-controller": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
+      "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
+      "requires": {
+        "event-target-shim": "^5.0.0"
+      }
+    },
+    "accepts": {
+      "version": "1.3.7",
+      "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
+      "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
+      "requires": {
+        "mime-types": "~2.1.24",
+        "negotiator": "0.6.2"
+      }
+    },
+    "acorn": {
+      "version": "8.0.5",
+      "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.0.5.tgz",
+      "integrity": "sha512-v+DieK/HJkJOpFBETDJioequtc3PfxsWMaxIdIwujtF7FEV/MAyDQLlm6/zPvr7Mix07mLh6ccVwIsloceodlg=="
+    },
+    "acorn-jsx": {
+      "version": "5.3.2",
+      "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+      "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+      "dev": true
+    },
+    "agent-base": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.1.tgz",
+      "integrity": "sha512-01q25QQDwLSsyfhrKbn8yuur+JNw0H+0Y4JiGIKd3z9aYk/w/2kxD/Upc+t2ZBBSUNff50VjPsSW2YxM8QYKVg==",
+      "requires": {
+        "debug": "4"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "4.3.2",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz",
+          "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==",
+          "requires": {
+            "ms": "2.1.2"
+          }
+        },
+        "ms": {
+          "version": "2.1.2",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+          "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+        }
+      }
+    },
+    "ajv": {
+      "version": "6.12.0",
+      "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz",
+      "integrity": "sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==",
+      "requires": {
+        "fast-deep-equal": "^3.1.1",
+        "fast-json-stable-stringify": "^2.0.0",
+        "json-schema-traverse": "^0.4.1",
+        "uri-js": "^4.2.2"
+      }
+    },
+    "ansi-align": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz",
+      "integrity": "sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw==",
+      "dev": true,
+      "requires": {
+        "string-width": "^3.0.0"
+      },
+      "dependencies": {
+        "ansi-regex": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+          "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+          "dev": true
+        },
+        "emoji-regex": {
+          "version": "7.0.3",
+          "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
+          "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
+          "dev": true
+        },
+        "is-fullwidth-code-point": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+          "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+          "dev": true
+        },
+        "string-width": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+          "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+          "dev": true,
+          "requires": {
+            "emoji-regex": "^7.0.1",
+            "is-fullwidth-code-point": "^2.0.0",
+            "strip-ansi": "^5.1.0"
+          }
+        },
+        "strip-ansi": {
+          "version": "5.2.0",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+          "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+          "dev": true,
+          "requires": {
+            "ansi-regex": "^4.1.0"
+          }
+        }
+      }
+    },
+    "ansi-colors": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
+      "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==",
+      "dev": true
+    },
+    "ansi-regex": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+      "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
+    },
+    "ansi-styles": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+      "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+      "dev": true,
+      "requires": {
+        "color-convert": "^1.9.0"
+      }
+    },
+    "any-promise": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
+      "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8="
+    },
+    "anymatch": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz",
+      "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==",
+      "dev": true,
+      "requires": {
+        "normalize-path": "^3.0.0",
+        "picomatch": "^2.0.4"
+      }
+    },
+    "aproba": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
+      "integrity": "sha1-aALmJk79GMeQobDVF/DyYnvyyUo="
+    },
+    "are-we-there-yet": {
+      "version": "1.1.5",
+      "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz",
+      "integrity": "sha1-SzXClE8GKov82mZBB2A1D+nd/CE=",
+      "requires": {
+        "delegates": "^1.0.0",
+        "readable-stream": "^2.0.6"
+      }
+    },
+    "argparse": {
+      "version": "1.0.10",
+      "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+      "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+      "dev": true,
+      "requires": {
+        "sprintf-js": "~1.0.2"
+      }
+    },
+    "array-flatten": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
+      "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
+    },
+    "array-includes": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.3.tgz",
+      "integrity": "sha512-gcem1KlBU7c9rB+Rq8/3PPKsK2kjqeEBa3bD5kkQo4nYlOHQCJqIJFqBXDEfwaRuYTT4E+FxA9xez7Gf/e3Q7A==",
+      "dev": true,
+      "requires": {
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.3",
+        "es-abstract": "^1.18.0-next.2",
+        "get-intrinsic": "^1.1.1",
+        "is-string": "^1.0.5"
+      }
+    },
+    "array.prototype.flat": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.4.tgz",
+      "integrity": "sha512-4470Xi3GAPAjZqFcljX2xzckv1qeKPizoNkiS0+O4IoPR2ZNpcjE0pkhdihlDouK+x6QOast26B4Q/O9DJnwSg==",
+      "dev": true,
+      "requires": {
+        "call-bind": "^1.0.0",
+        "define-properties": "^1.1.3",
+        "es-abstract": "^1.18.0-next.1"
+      },
+      "dependencies": {
+        "has-symbols": {
+          "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz",
+          "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw=="
+        },
+        "is-callable": {
+          "version": "1.2.3",
+          "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz",
+          "integrity": "sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ=="
+        },
+        "is-string": {
+          "version": "1.0.6",
+          "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.6.tgz",
+          "integrity": "sha512-2gdzbKUuqtQ3lYNrUTQYoClPhm7oQu4UdpSZMp1/DGgkHBT8E2Z1l0yMdb6D4zNAxwDiMv8MdulKROJGNl0Q0w=="
+        }
+      }
+    },
+    "arrify": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz",
+      "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug=="
+    },
+    "asn1": {
+      "version": "0.2.4",
+      "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
+      "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
+      "requires": {
+        "safer-buffer": "~2.1.0"
+      }
+    },
+    "assert-plus": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
+      "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
+    },
+    "assertion-error": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
+      "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==",
+      "dev": true
+    },
+    "astral-regex": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz",
+      "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==",
+      "dev": true
+    },
+    "async": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz",
+      "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw=="
+    },
+    "async-listener": {
+      "version": "0.6.10",
+      "resolved": "https://registry.npmjs.org/async-listener/-/async-listener-0.6.10.tgz",
+      "integrity": "sha512-gpuo6xOyF4D5DE5WvyqZdPA3NGhiT6Qf07l7DCB0wwDEsLvDIbCr6j9S5aj5Ch96dLace5tXVzWBZkxU/c5ohw==",
+      "requires": {
+        "semver": "^5.3.0",
+        "shimmer": "^1.1.0"
+      }
+    },
+    "asynckit": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+      "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
+    },
+    "aws-sign2": {
+      "version": "0.7.0",
+      "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
+      "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
+    },
+    "aws4": {
+      "version": "1.9.1",
+      "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz",
+      "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug=="
+    },
+    "balanced-match": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+      "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
+    },
+    "base64-js": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
+      "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g=="
+    },
+    "bcrypt-pbkdf": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
+      "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
+      "requires": {
+        "tweetnacl": "^0.14.3"
+      }
+    },
+    "bignumber.js": {
+      "version": "7.2.1",
+      "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-7.2.1.tgz",
+      "integrity": "sha512-S4XzBk5sMB+Rcb/LNcpzXr57VRTxgAvaAEDAl1AwRx27j00hT84O6OkteE7u8UB3NuaaygCRrEpqox4uDOrbdQ=="
+    },
+    "binary-extensions": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz",
+      "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==",
+      "dev": true
+    },
+    "bindings": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
+      "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
+      "requires": {
+        "file-uri-to-path": "1.0.0"
+      }
+    },
+    "bintrees": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.1.tgz",
+      "integrity": "sha1-DmVcm5wkNeqraL9AJyJtK1WjRSQ="
+    },
+    "bl": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/bl/-/bl-4.0.3.tgz",
+      "integrity": "sha512-fs4G6/Hu4/EE+F75J8DuN/0IpQqNjAdC7aEQv7Qt8MHGUH7Ckv2MwTEEeN9QehD0pfIDkMI1bkHYkKy7xHyKIg==",
+      "requires": {
+        "buffer": "^5.5.0",
+        "inherits": "^2.0.4",
+        "readable-stream": "^3.4.0"
+      },
+      "dependencies": {
+        "inherits": {
+          "version": "2.0.4",
+          "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+          "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+        },
+        "readable-stream": {
+          "version": "3.6.0",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+          "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+          "requires": {
+            "inherits": "^2.0.3",
+            "string_decoder": "^1.1.1",
+            "util-deprecate": "^1.0.1"
+          }
+        }
+      }
+    },
+    "bluebird": {
+      "version": "3.7.2",
+      "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
+      "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg=="
+    },
+    "body-parser": {
+      "version": "1.19.0",
+      "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
+      "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
+      "requires": {
+        "bytes": "3.1.0",
+        "content-type": "~1.0.4",
+        "debug": "2.6.9",
+        "depd": "~1.1.2",
+        "http-errors": "1.7.2",
+        "iconv-lite": "0.4.24",
+        "on-finished": "~2.3.0",
+        "qs": "6.7.0",
+        "raw-body": "2.4.0",
+        "type-is": "~1.6.17"
+      },
+      "dependencies": {
+        "iconv-lite": {
+          "version": "0.4.24",
+          "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+          "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+          "requires": {
+            "safer-buffer": ">= 2.1.2 < 3"
+          }
+        }
+      }
+    },
+    "boxen": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/boxen/-/boxen-4.2.0.tgz",
+      "integrity": "sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ==",
+      "dev": true,
+      "requires": {
+        "ansi-align": "^3.0.0",
+        "camelcase": "^5.3.1",
+        "chalk": "^3.0.0",
+        "cli-boxes": "^2.2.0",
+        "string-width": "^4.1.0",
+        "term-size": "^2.1.0",
+        "type-fest": "^0.8.1",
+        "widest-line": "^3.1.0"
+      },
+      "dependencies": {
+        "ansi-regex": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
+          "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
+          "dev": true
+        },
+        "ansi-styles": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+          "requires": {
+            "color-convert": "^2.0.1"
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
+        },
+        "is-fullwidth-code-point": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+          "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+          "dev": true
+        },
+        "string-width": {
+          "version": "4.2.2",
+          "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz",
+          "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==",
+          "dev": true,
+          "requires": {
+            "emoji-regex": "^8.0.0",
+            "is-fullwidth-code-point": "^3.0.0",
+            "strip-ansi": "^6.0.0"
+          }
+        },
+        "strip-ansi": {
+          "version": "6.0.0",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
+          "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
+          "dev": true,
+          "requires": {
+            "ansi-regex": "^5.0.0"
+          }
+        },
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
+        }
+      }
+    },
+    "brace-expansion": {
+      "version": "1.1.11",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+      "integrity": "sha1-PH/L9SnYcibz0vUrlm/1Jx60Qd0=",
+      "requires": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "braces": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+      "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+      "dev": true,
+      "requires": {
+        "fill-range": "^7.0.1"
+      }
+    },
+    "browser-stdout": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
+      "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==",
+      "dev": true
+    },
+    "buffer": {
+      "version": "5.6.0",
+      "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz",
+      "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==",
+      "requires": {
+        "base64-js": "^1.0.2",
+        "ieee754": "^1.1.4"
+      }
+    },
+    "buffer-equal-constant-time": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
+      "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk="
+    },
+    "buffer-from": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
+      "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A=="
+    },
+    "builtin-modules": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.2.0.tgz",
+      "integrity": "sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA=="
+    },
+    "bunyan": {
+      "version": "1.8.15",
+      "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.15.tgz",
+      "integrity": "sha512-0tECWShh6wUysgucJcBAoYegf3JJoZWibxdqhTm7OHPeT42qdjkZ29QCMcKwbgU1kiH+auSIasNRXMLWXafXig==",
+      "requires": {
+        "dtrace-provider": "~0.8",
+        "moment": "^2.19.3",
+        "mv": "~2",
+        "safe-json-stringify": "~1"
+      }
+    },
+    "bytes": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
+      "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg=="
+    },
+    "cacheable-request": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz",
+      "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==",
+      "dev": true,
+      "requires": {
+        "clone-response": "^1.0.2",
+        "get-stream": "^5.1.0",
+        "http-cache-semantics": "^4.0.0",
+        "keyv": "^3.0.0",
+        "lowercase-keys": "^2.0.0",
+        "normalize-url": "^4.1.0",
+        "responselike": "^1.0.2"
+      },
+      "dependencies": {
+        "get-stream": {
+          "version": "5.2.0",
+          "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
+          "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
+          "dev": true,
+          "requires": {
+            "pump": "^3.0.0"
+          }
+        },
+        "lowercase-keys": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz",
+          "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==",
+          "dev": true
+        }
+      }
+    },
+    "call-bind": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
+      "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
+      "dev": true,
+      "requires": {
+        "function-bind": "^1.1.1",
+        "get-intrinsic": "^1.0.2"
+      }
+    },
+    "callsites": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+      "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+      "dev": true
+    },
+    "camelcase": {
+      "version": "5.3.1",
+      "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+      "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+      "dev": true
+    },
+    "caseless": {
+      "version": "0.12.0",
+      "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
+      "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
+    },
+    "chai": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz",
+      "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==",
+      "dev": true,
+      "requires": {
+        "assertion-error": "^1.1.0",
+        "check-error": "^1.0.2",
+        "deep-eql": "^3.0.1",
+        "get-func-name": "^2.0.0",
+        "pathval": "^1.1.0",
+        "type-detect": "^4.0.5"
+      }
+    },
+    "chai-as-promised": {
+      "version": "7.1.1",
+      "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.1.tgz",
+      "integrity": "sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA==",
+      "dev": true,
+      "requires": {
+        "check-error": "^1.0.2"
+      }
+    },
+    "chalk": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
+      "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
+      "dev": true
+    },
+    "charenc": {
+      "version": "0.0.2",
+      "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz",
+      "integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc="
+    },
+    "check-error": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
+      "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=",
+      "dev": true
+    },
+    "chokidar": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.0.tgz",
+      "integrity": "sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A==",
+      "dev": true,
+      "requires": {
+        "anymatch": "~3.1.1",
+        "braces": "~3.0.2",
+        "fsevents": "~2.1.1",
+        "glob-parent": "~5.1.0",
+        "is-binary-path": "~2.1.0",
+        "is-glob": "~4.0.1",
+        "normalize-path": "~3.0.0",
+        "readdirp": "~3.2.0"
+      }
+    },
+    "chownr": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz",
+      "integrity": "sha1-VHJri4//TfBTxCGH6AH7RBLfFJQ="
+    },
+    "ci-info": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz",
+      "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==",
+      "dev": true
+    },
+    "cli-boxes": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz",
+      "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==",
+      "dev": true
+    },
+    "cliui": {
+      "version": "7.0.4",
+      "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
+      "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
+      "dev": true,
+      "requires": {
+        "wrap-ansi": "^7.0.0"
+      }
+    },
+    "clone-response": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz",
+      "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=",
+      "dev": true,
+      "requires": {
+        "mimic-response": "^1.0.0"
+      }
+    },
+    "cls-bluebird": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/cls-bluebird/-/cls-bluebird-2.1.0.tgz",
+      "integrity": "sha1-N+8eCAqP+1XC9BZPU28ZGeeWiu4=",
+      "requires": {
+        "is-bluebird": "^1.0.2",
+        "shimmer": "^1.1.0"
+      }
+    },
+    "code-point-at": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
+      "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
+    },
+    "coffeescript": {
+      "version": "2.5.1",
+      "resolved": "https://registry.npmjs.org/coffeescript/-/coffeescript-2.5.1.tgz",
+      "integrity": "sha512-J2jRPX0eeFh5VKyVnoLrfVFgLZtnnmp96WQSLAS8OrLm2wtQLcnikYKe1gViJKDH7vucjuhHvBKKBP3rKcD1tQ=="
+    },
+    "color-convert": {
+      "version": "1.9.3",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+      "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+      "dev": true,
+      "requires": {
+        "color-name": "1.1.3"
+      }
+    },
+    "color-name": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+      "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+      "dev": true
+    },
+    "combined-stream": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+      "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+      "requires": {
+        "delayed-stream": "~1.0.0"
+      }
+    },
+    "compressible": {
+      "version": "2.0.18",
+      "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz",
+      "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==",
+      "requires": {
+        "mime-db": ">= 1.43.0 < 2"
+      }
+    },
+    "compression": {
+      "version": "1.7.4",
+      "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz",
+      "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==",
+      "requires": {
+        "accepts": "~1.3.5",
+        "bytes": "3.0.0",
+        "compressible": "~2.0.16",
+        "debug": "2.6.9",
+        "on-headers": "~1.0.2",
+        "safe-buffer": "5.1.2",
+        "vary": "~1.1.2"
+      },
+      "dependencies": {
+        "bytes": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
+          "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg="
+        }
+      }
+    },
+    "concat-map": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+      "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
+    },
+    "concat-stream": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz",
+      "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==",
+      "requires": {
+        "buffer-from": "^1.0.0",
+        "inherits": "^2.0.3",
+        "readable-stream": "^3.0.2",
+        "typedarray": "^0.0.6"
+      },
+      "dependencies": {
+        "readable-stream": {
+          "version": "3.6.0",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+          "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+          "requires": {
+            "inherits": "^2.0.3",
+            "string_decoder": "^1.1.1",
+            "util-deprecate": "^1.0.1"
+          }
+        }
+      }
+    },
+    "configstore": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz",
+      "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==",
+      "dev": true,
+      "requires": {
+        "dot-prop": "^5.2.0",
+        "graceful-fs": "^4.1.2",
+        "make-dir": "^3.0.0",
+        "unique-string": "^2.0.0",
+        "write-file-atomic": "^3.0.0",
+        "xdg-basedir": "^4.0.0"
+      }
+    },
+    "console-control-strings": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
+      "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4="
+    },
+    "console-log-level": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/console-log-level/-/console-log-level-1.4.1.tgz",
+      "integrity": "sha512-VZzbIORbP+PPcN/gg3DXClTLPLg5Slwd5fL2MIc+o1qZ4BXBvWyc6QxPk6T/Mkr6IVjRpoAGf32XxP3ZWMVRcQ=="
+    },
+    "content-disposition": {
+      "version": "0.5.3",
+      "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
+      "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
+      "requires": {
+        "safe-buffer": "5.1.2"
+      }
+    },
+    "content-type": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
+      "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
+    },
+    "continuation-local-storage": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/continuation-local-storage/-/continuation-local-storage-3.2.1.tgz",
+      "integrity": "sha512-jx44cconVqkCEEyLSKWwkvUXwO561jXMa3LPjTPsm5QR22PA0/mhe33FT4Xb5y74JDvt/Cq+5lm8S8rskLv9ZA==",
+      "requires": {
+        "async-listener": "^0.6.0",
+        "emitter-listener": "^1.1.1"
+      }
+    },
+    "cookie": {
+      "version": "0.3.1",
+      "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
+      "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
+    },
+    "cookie-signature": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
+      "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
+    },
+    "core-js": {
+      "version": "3.13.0",
+      "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.13.0.tgz",
+      "integrity": "sha512-iWDbiyha1M5vFwPFmQnvRv+tJzGbFAm6XimJUT0NgHYW3xZEs1SkCAcasWSVFxpI2Xb/V1DDJckq3v90+bQnog=="
+    },
+    "core-util-is": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+      "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
+    },
+    "cross-spawn": {
+      "version": "7.0.3",
+      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
+      "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+      "dev": true,
+      "requires": {
+        "path-key": "^3.1.0",
+        "shebang-command": "^2.0.0",
+        "which": "^2.0.1"
+      }
+    },
+    "crypt": {
+      "version": "0.0.2",
+      "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz",
+      "integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs="
+    },
+    "crypto-random-string": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz",
+      "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==",
+      "dev": true
+    },
+    "d64": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/d64/-/d64-1.0.0.tgz",
+      "integrity": "sha1-QAKofoUMv8n52XBrYPymE6MzbpA="
+    },
+    "dashdash": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
+      "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
+      "requires": {
+        "assert-plus": "^1.0.0"
+      }
+    },
+    "debug": {
+      "version": "2.6.9",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+      "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=",
+      "requires": {
+        "ms": "2.0.0"
+      }
+    },
+    "decamelize": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz",
+      "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==",
+      "dev": true
+    },
+    "decompress-response": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz",
+      "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=",
+      "dev": true,
+      "requires": {
+        "mimic-response": "^1.0.0"
+      }
+    },
+    "deep-eql": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz",
+      "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==",
+      "dev": true,
+      "requires": {
+        "type-detect": "^4.0.0"
+      }
+    },
+    "deep-extend": {
+      "version": "0.6.0",
+      "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
+      "integrity": "sha1-xPp8lUBKF6nD6Mp+FTcxK3NjMKw="
+    },
+    "deep-is": {
+      "version": "0.1.3",
+      "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
+      "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
+      "dev": true
+    },
+    "defer-to-connect": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz",
+      "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==",
+      "dev": true
+    },
+    "define-properties": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
+      "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
+      "dev": true,
+      "requires": {
+        "object-keys": "^1.0.12"
+      }
+    },
+    "delay": {
+      "version": "4.4.1",
+      "resolved": "https://registry.npmjs.org/delay/-/delay-4.4.1.tgz",
+      "integrity": "sha512-aL3AhqtfhOlT/3ai6sWXeqwnw63ATNpnUiN4HL7x9q+My5QtHlO3OIkasmug9LKzpheLdmUKGRKnYXYAS7FQkQ=="
+    },
+    "delayed-stream": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+      "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
+    },
+    "delegates": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
+      "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o="
+    },
+    "depd": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
+      "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
+    },
+    "destroy": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
+      "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
+    },
+    "detect-libc": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
+      "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups="
+    },
+    "diff": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz",
+      "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==",
+      "dev": true
+    },
+    "diskusage": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/diskusage/-/diskusage-1.1.3.tgz",
+      "integrity": "sha512-EAyaxl8hy4Ph07kzlzGTfpbZMNAAAHXSZtNEMwdlnSd1noHzvA6HsgKt4fEMSvaEXQYLSphe5rPMxN4WOj0hcQ==",
+      "requires": {
+        "es6-promise": "^4.2.5",
+        "nan": "^2.14.0"
+      }
+    },
+    "docker-modem": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-2.1.1.tgz",
+      "integrity": "sha512-zSFwYN4AP38LJhTIOpZMjiDbAqSJbv8+u9i/Xq5XABIeTzgp83VF63epu6sVHWxe+6tfhMXqgV+sYjZWh/UzSQ==",
+      "requires": {
+        "debug": "^4.1.1",
+        "readable-stream": "^3.5.0",
+        "split-ca": "^1.0.1",
+        "ssh2": "^0.8.7"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "4.1.1",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+          "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+          "requires": {
+            "ms": "^2.1.1"
+          }
+        },
+        "ms": {
+          "version": "2.1.2",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+          "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+        },
+        "readable-stream": {
+          "version": "3.6.0",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+          "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+          "requires": {
+            "inherits": "^2.0.3",
+            "string_decoder": "^1.1.1",
+            "util-deprecate": "^1.0.1"
+          }
+        }
+      }
+    },
+    "dockerode": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/dockerode/-/dockerode-3.1.0.tgz",
+      "integrity": "sha512-E0KknBBTlIVEvtt2XJRZ3he59u2UN8Yr1A08Sey/BKIox+WlwnJp5fL5SKyhPgNmSXgamPEuKYCJxMi31uj0Nw==",
+      "requires": {
+        "concat-stream": "~2.0.0",
+        "docker-modem": "^2.1.0",
+        "tar-fs": "~2.0.0"
+      }
+    },
+    "doctrine": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
+      "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+      "dev": true,
+      "requires": {
+        "esutils": "^2.0.2"
+      }
+    },
+    "dot-prop": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz",
+      "integrity": "sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==",
+      "requires": {
+        "is-obj": "^2.0.0"
+      }
+    },
+    "dottie": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.2.tgz",
+      "integrity": "sha512-fmrwR04lsniq/uSr8yikThDTrM7epXHBAAjH9TbeH3rEA8tdCO7mRzB9hdmdGyJCxF8KERo9CITcm3kGuoyMhg=="
+    },
+    "dtrace-provider": {
+      "version": "0.8.8",
+      "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.8.tgz",
+      "integrity": "sha512-b7Z7cNtHPhH9EJhNNbbeqTcXB8LGFFZhq1PGgEvpeHlzd36bhbdTWoE/Ba/YguqpBSlAPKnARWhVlhunCMwfxg==",
+      "optional": true,
+      "requires": {
+        "nan": "^2.14.0"
+      }
+    },
+    "duplexer3": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz",
+      "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=",
+      "dev": true
+    },
+    "duplexify": {
+      "version": "3.7.1",
+      "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz",
+      "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==",
+      "requires": {
+        "end-of-stream": "^1.0.0",
+        "inherits": "^2.0.1",
+        "readable-stream": "^2.0.0",
+        "stream-shift": "^1.0.0"
+      }
+    },
+    "ecc-jsbn": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
+      "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
+      "requires": {
+        "jsbn": "~0.1.0",
+        "safer-buffer": "^2.1.0"
+      }
+    },
+    "ecdsa-sig-formatter": {
+      "version": "1.0.11",
+      "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
+      "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
+      "requires": {
+        "safe-buffer": "^5.0.1"
+      }
+    },
+    "ee-first": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+      "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
+    },
+    "emitter-listener": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/emitter-listener/-/emitter-listener-1.1.2.tgz",
+      "integrity": "sha512-Bt1sBAGFHY9DKY+4/2cV6izcKJUf5T7/gkdmkxzX/qv9CcGH8xSwVRW5mtX03SWJtRTWSOpzCuWN9rBFYZepZQ==",
+      "requires": {
+        "shimmer": "^1.2.0"
+      }
+    },
+    "emoji-regex": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+      "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
+    },
+    "encodeurl": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
+      "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
+    },
+    "end-of-stream": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz",
+      "integrity": "sha1-7SljTRm6ukY7bOa4CjchPqtx7EM=",
+      "requires": {
+        "once": "^1.4.0"
+      }
+    },
+    "enquirer": {
+      "version": "2.3.6",
+      "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz",
+      "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==",
+      "dev": true,
+      "requires": {
+        "ansi-colors": "^4.1.1"
+      }
+    },
+    "ent": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz",
+      "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0="
+    },
+    "error-ex": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+      "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+      "dev": true,
+      "requires": {
+        "is-arrayish": "^0.2.1"
+      }
+    },
+    "es-abstract": {
+      "version": "1.18.3",
+      "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.3.tgz",
+      "integrity": "sha512-nQIr12dxV7SSxE6r6f1l3DtAeEYdsGpps13dR0TwJg1S8gyp4ZPgy3FZcHBgbiQqnoqSTb+oC+kO4UQ0C/J8vw==",
+      "dev": true,
+      "requires": {
+        "call-bind": "^1.0.2",
+        "es-to-primitive": "^1.2.1",
+        "function-bind": "^1.1.1",
+        "get-intrinsic": "^1.1.1",
+        "has": "^1.0.3",
+        "is-negative-zero": "^2.0.1",
+        "is-regex": "^1.1.3",
+        "object-inspect": "^1.10.3",
+        "object-keys": "^1.1.1",
+        "object.assign": "^4.1.2",
+        "string.prototype.trimend": "^1.0.4",
+        "string.prototype.trimstart": "^1.0.4",
+        "unbox-primitive": "^1.0.1"
+      }
+    },
+    "es-to-primitive": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
+      "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
+      "dev": true,
+      "requires": {
+        "is-callable": "^1.1.4",
+        "is-date-object": "^1.0.1",
+        "is-symbol": "^1.0.2"
+      }
+    },
+    "es6-promise": {
+      "version": "4.2.8",
+      "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz",
+      "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w=="
+    },
+    "escalade": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
+      "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
+      "dev": true
+    },
+    "escape-goat": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz",
+      "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==",
+      "dev": true
+    },
+    "escape-html": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+      "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
+    },
+    "escape-string-regexp": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+      "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
+      "dev": true
+    },
+    "eslint": {
+      "version": "7.30.0",
+      "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.30.0.tgz",
+      "integrity": "sha512-VLqz80i3as3NdloY44BQSJpFw534L9Oh+6zJOUaViV4JPd+DaHwutqP7tcpkW3YiXbK6s05RZl7yl7cQn+lijg==",
+      "dev": true,
+      "requires": {
+        "@babel/code-frame": "7.12.11",
+        "@eslint/eslintrc": "^0.4.2",
+        "@humanwhocodes/config-array": "^0.5.0",
+        "ajv": "^6.10.0",
+        "chalk": "^4.0.0",
+        "cross-spawn": "^7.0.2",
+        "debug": "^4.0.1",
+        "doctrine": "^3.0.0",
+        "enquirer": "^2.3.5",
+        "escape-string-regexp": "^4.0.0",
+        "eslint-scope": "^5.1.1",
+        "eslint-utils": "^2.1.0",
+        "eslint-visitor-keys": "^2.0.0",
+        "espree": "^7.3.1",
+        "esquery": "^1.4.0",
+        "esutils": "^2.0.2",
+        "fast-deep-equal": "^3.1.3",
+        "file-entry-cache": "^6.0.1",
+        "functional-red-black-tree": "^1.0.1",
+        "glob-parent": "^5.1.2",
+        "globals": "^13.6.0",
+        "ignore": "^4.0.6",
+        "import-fresh": "^3.0.0",
+        "imurmurhash": "^0.1.4",
+        "is-glob": "^4.0.0",
+        "js-yaml": "^3.13.1",
+        "json-stable-stringify-without-jsonify": "^1.0.1",
+        "levn": "^0.4.1",
+        "lodash.merge": "^4.6.2",
+        "minimatch": "^3.0.4",
+        "natural-compare": "^1.4.0",
+        "optionator": "^0.9.1",
+        "progress": "^2.0.0",
+        "regexpp": "^3.1.0",
+        "semver": "^7.2.1",
+        "strip-ansi": "^6.0.0",
+        "strip-json-comments": "^3.1.0",
+        "table": "^6.0.9",
+        "text-table": "^0.2.0",
+        "v8-compile-cache": "^2.0.3"
+      },
+      "dependencies": {
+        "ansi-regex": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
+          "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg=="
+        },
+        "chalk": {
+          "version": "4.1.1",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz",
+          "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
+          },
+          "dependencies": {
+            "ansi-styles": {
+              "version": "4.3.0",
+              "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+              "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+              "dev": true,
+              "requires": {
+                "color-convert": "^2.0.1"
+              }
+            },
+            "supports-color": {
+              "version": "7.2.0",
+              "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+              "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+              "dev": true,
+              "requires": {
+                "has-flag": "^4.0.0"
+              }
+            }
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "dev": true,
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
+        },
+        "debug": {
+          "version": "4.3.2",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz",
+          "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==",
+          "dev": true,
+          "requires": {
+            "ms": "2.1.2"
+          }
+        },
+        "escape-string-regexp": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+          "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+          "dev": true
+        },
+        "eslint-visitor-keys": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
+          "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
+          "dev": true
+        },
+        "estraverse": {
+          "version": "5.2.0",
+          "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz",
+          "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ=="
+        },
+        "fast-deep-equal": {
+          "version": "3.1.3",
+          "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+          "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+          "dev": true
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
+        "is-fullwidth-code-point": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+          "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
+        },
+        "json-schema-traverse": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+          "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="
+        },
+        "lru-cache": {
+          "version": "6.0.0",
+          "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+          "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+          "dev": true,
+          "requires": {
+            "yallist": "^4.0.0"
+          }
+        },
+        "ms": {
+          "version": "2.1.2",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+          "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+          "dev": true
+        },
+        "rimraf": {
+          "version": "3.0.2",
+          "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+          "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+          "requires": {
+            "glob": "^7.1.3"
+          }
+        },
+        "semver": {
+          "version": "7.3.5",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
+          "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
+          "dev": true,
+          "requires": {
+            "lru-cache": "^6.0.0"
+          }
+        },
+        "string-width": {
+          "version": "4.2.2",
+          "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz",
+          "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==",
+          "requires": {
+            "emoji-regex": "^8.0.0",
+            "is-fullwidth-code-point": "^3.0.0",
+            "strip-ansi": "^6.0.0"
+          }
+        },
+        "strip-ansi": {
+          "version": "6.0.0",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
+          "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
+          "requires": {
+            "ansi-regex": "^5.0.0"
+          }
+        },
+        "strip-json-comments": {
+          "version": "3.1.1",
+          "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+          "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+          "dev": true
+        },
+        "type-fest": {
+          "version": "0.20.2",
+          "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+          "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ=="
+        },
+        "yallist": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+          "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+          "dev": true
+        }
+      }
+    },
+    "eslint-config-prettier": {
+      "version": "8.3.0",
+      "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.3.0.tgz",
+      "integrity": "sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew==",
+      "dev": true
+    },
+    "eslint-config-standard": {
+      "version": "16.0.3",
+      "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-16.0.3.tgz",
+      "integrity": "sha512-x4fmJL5hGqNJKGHSjnLdgA6U6h1YW/G2dW9fA+cyVur4SK6lyue8+UgNKWlZtUDTXvgKDD/Oa3GQjmB5kjtVvg==",
+      "dev": true
+    },
+    "eslint-import-resolver-node": {
+      "version": "0.3.4",
+      "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz",
+      "integrity": "sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==",
+      "dev": true,
+      "requires": {
+        "debug": "^2.6.9",
+        "resolve": "^1.13.1"
+      },
+      "dependencies": {
+        "resolve": {
+          "version": "1.20.0",
+          "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz",
+          "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==",
+          "dev": true,
+          "requires": {
+            "is-core-module": "^2.2.0",
+            "path-parse": "^1.0.6"
+          }
+        }
+      }
+    },
+    "eslint-module-utils": {
+      "version": "2.6.1",
+      "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.1.tgz",
+      "integrity": "sha512-ZXI9B8cxAJIH4nfkhTwcRTEAnrVfobYqwjWy/QMCZ8rHkZHFjf9yO4BzpiF9kCSfNlMG54eKigISHpX0+AaT4A==",
+      "dev": true,
+      "requires": {
+        "debug": "^3.2.7",
+        "pkg-dir": "^2.0.0"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "3.2.7",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+          "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+          "dev": true,
+          "requires": {
+            "ms": "^2.1.1"
+          }
+        },
+        "ms": {
+          "version": "2.1.3",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+          "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+          "dev": true
+        }
+      }
+    },
+    "eslint-plugin-chai-expect": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-chai-expect/-/eslint-plugin-chai-expect-2.2.0.tgz",
+      "integrity": "sha512-ExTJKhgeYMfY8wDj3UiZmgpMKJOUHGNHmWMlxT49JUDB1vTnw0sSNfXJSxnX+LcebyBD/gudXzjzD136WqPJrQ==",
+      "dev": true
+    },
+    "eslint-plugin-chai-friendly": {
+      "version": "0.6.0",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-chai-friendly/-/eslint-plugin-chai-friendly-0.6.0.tgz",
+      "integrity": "sha512-Uvvv1gkbRGp/qfN15B0kQyQWg+oFA8buDSqrwmW3egNSk/FpqH2MjQqKOuKwmEL6w4QIQrIjDp+gg6kGGmD3oQ==",
+      "dev": true
+    },
+    "eslint-plugin-es": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz",
+      "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==",
+      "dev": true,
+      "requires": {
+        "eslint-utils": "^2.0.0",
+        "regexpp": "^3.0.0"
+      }
+    },
+    "eslint-plugin-import": {
+      "version": "2.23.4",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.23.4.tgz",
+      "integrity": "sha512-6/wP8zZRsnQFiR3iaPFgh5ImVRM1WN5NUWfTIRqwOdeiGJlBcSk82o1FEVq8yXmy4lkIzTo7YhHCIxlU/2HyEQ==",
+      "dev": true,
+      "requires": {
+        "array-includes": "^3.1.3",
+        "array.prototype.flat": "^1.2.4",
+        "debug": "^2.6.9",
+        "doctrine": "^2.1.0",
+        "eslint-import-resolver-node": "^0.3.4",
+        "eslint-module-utils": "^2.6.1",
+        "find-up": "^2.0.0",
+        "has": "^1.0.3",
+        "is-core-module": "^2.4.0",
+        "minimatch": "^3.0.4",
+        "object.values": "^1.1.3",
+        "pkg-up": "^2.0.0",
+        "read-pkg-up": "^3.0.0",
+        "resolve": "^1.20.0",
+        "tsconfig-paths": "^3.9.0"
+      },
+      "dependencies": {
+        "doctrine": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
+          "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
+          "dev": true,
+          "requires": {
+            "esutils": "^2.0.2"
+          }
+        },
+        "has-symbols": {
+          "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz",
+          "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw=="
+        },
+        "is-callable": {
+          "version": "1.2.3",
+          "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz",
+          "integrity": "sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ=="
+        },
+        "resolve": {
+          "version": "1.20.0",
+          "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz",
+          "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==",
+          "dev": true,
+          "requires": {
+            "is-core-module": "^2.2.0",
+            "path-parse": "^1.0.6"
+          }
+        }
+      }
+    },
+    "eslint-plugin-mocha": {
+      "version": "8.2.0",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-mocha/-/eslint-plugin-mocha-8.2.0.tgz",
+      "integrity": "sha512-8oOR47Ejt+YJPNQzedbiklDqS1zurEaNrxXpRs+Uk4DMDPVmKNagShFeUaYsfvWP55AhI+P1non5QZAHV6K78A==",
+      "dev": true,
+      "requires": {
+        "eslint-utils": "^2.1.0",
+        "ramda": "^0.27.1"
+      }
+    },
+    "eslint-plugin-node": {
+      "version": "11.1.0",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz",
+      "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==",
+      "dev": true,
+      "requires": {
+        "eslint-plugin-es": "^3.0.0",
+        "eslint-utils": "^2.0.0",
+        "ignore": "^5.1.1",
+        "minimatch": "^3.0.4",
+        "resolve": "^1.10.1",
+        "semver": "^6.1.0"
+      },
+      "dependencies": {
+        "ignore": {
+          "version": "5.1.8",
+          "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz",
+          "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==",
+          "dev": true
+        },
+        "semver": {
+          "version": "6.3.0",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+          "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+          "dev": true
+        }
+      }
+    },
+    "eslint-plugin-prettier": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.2.tgz",
+      "integrity": "sha512-GlolCC9y3XZfv3RQfwGew7NnuFDKsfI4lbvRK+PIIo23SFH+LemGs4cKwzAaRa+Mdb+lQO/STaIayno8T5sJJA==",
+      "dev": true,
+      "requires": {
+        "prettier-linter-helpers": "^1.0.0"
+      }
+    },
+    "eslint-plugin-promise": {
+      "version": "4.2.1",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-4.2.1.tgz",
+      "integrity": "sha512-VoM09vT7bfA7D+upt+FjeBO5eHIJQBUWki1aPvB+vbNiHS3+oGIJGIeyBtKQTME6UPXXy3vV07OL1tHd3ANuDw==",
+      "dev": true
+    },
+    "eslint-scope": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
+      "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
+      "dev": true,
+      "requires": {
+        "esrecurse": "^4.3.0",
+        "estraverse": "^4.1.1"
+      }
+    },
+    "eslint-utils": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz",
+      "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==",
+      "dev": true,
+      "requires": {
+        "eslint-visitor-keys": "^1.1.0"
+      }
+    },
+    "eslint-visitor-keys": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz",
+      "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==",
+      "dev": true
+    },
+    "espree": {
+      "version": "7.3.1",
+      "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz",
+      "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==",
+      "dev": true,
+      "requires": {
+        "acorn": "^7.4.0",
+        "acorn-jsx": "^5.3.1",
+        "eslint-visitor-keys": "^1.3.0"
+      },
+      "dependencies": {
+        "acorn": {
+          "version": "7.4.1",
+          "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
+          "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
+          "dev": true
+        },
+        "eslint-visitor-keys": {
+          "version": "1.3.0",
+          "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
+          "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
+          "dev": true
+        }
+      }
+    },
+    "esprima": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+      "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+      "dev": true
+    },
+    "esquery": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz",
+      "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==",
+      "dev": true
+    },
+    "esrecurse": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+      "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+      "dev": true,
+      "requires": {
+        "estraverse": "^5.2.0"
+      },
+      "dependencies": {
+        "estraverse": {
+          "version": "5.2.0",
+          "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz",
+          "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==",
+          "dev": true
+        }
+      }
+    },
+    "estraverse": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
+      "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
+      "dev": true
+    },
+    "esutils": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+      "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+      "dev": true
+    },
+    "etag": {
+      "version": "1.8.1",
+      "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+      "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
+    },
+    "event-target-shim": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
+      "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="
+    },
+    "eventid": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/eventid/-/eventid-1.0.0.tgz",
+      "integrity": "sha512-4upSDsvpxhWPsmw4fsJCp0zj8S7I0qh1lCDTmZXP8V3TtryQKDI8CgQPN+e5JakbWwzaAX3lrdp2b3KSoMSUpw==",
+      "requires": {
+        "d64": "^1.0.0",
+        "uuid": "^3.0.1"
+      }
+    },
+    "express": {
+      "version": "4.17.1",
+      "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
+      "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
+      "requires": {
+        "accepts": "~1.3.7",
+        "array-flatten": "1.1.1",
+        "body-parser": "1.19.0",
+        "content-disposition": "0.5.3",
+        "content-type": "~1.0.4",
+        "cookie": "0.4.0",
+        "cookie-signature": "1.0.6",
+        "debug": "2.6.9",
+        "depd": "~1.1.2",
+        "encodeurl": "~1.0.2",
+        "escape-html": "~1.0.3",
+        "etag": "~1.8.1",
+        "finalhandler": "~1.1.2",
+        "fresh": "0.5.2",
+        "merge-descriptors": "1.0.1",
+        "methods": "~1.1.2",
+        "on-finished": "~2.3.0",
+        "parseurl": "~1.3.3",
+        "path-to-regexp": "0.1.7",
+        "proxy-addr": "~2.0.5",
+        "qs": "6.7.0",
+        "range-parser": "~1.2.1",
+        "safe-buffer": "5.1.2",
+        "send": "0.17.1",
+        "serve-static": "1.14.1",
+        "setprototypeof": "1.1.1",
+        "statuses": "~1.5.0",
+        "type-is": "~1.6.18",
+        "utils-merge": "1.0.1",
+        "vary": "~1.1.2"
+      },
+      "dependencies": {
+        "cookie": {
+          "version": "0.4.0",
+          "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
+          "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
+        }
+      }
+    },
+    "extend": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+      "integrity": "sha1-+LETa0Bx+9jrFAr/hYsQGewpFfo="
+    },
+    "extsprintf": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
+      "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU="
+    },
+    "fast-deep-equal": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz",
+      "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA=="
+    },
+    "fast-diff": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz",
+      "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==",
+      "dev": true
+    },
+    "fast-json-stable-stringify": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
+      "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I="
+    },
+    "fast-levenshtein": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+      "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
+      "dev": true
+    },
+    "fast-text-encoding": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.0.tgz",
+      "integrity": "sha512-R9bHCvweUxxwkDwhjav5vxpFvdPGlVngtqmx4pIZfSUhM/Q4NiIUHB456BAf+Q1Nwu3HEZYONtu+Rya+af4jiQ=="
+    },
+    "file-entry-cache": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
+      "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
+      "dev": true,
+      "requires": {
+        "flat-cache": "^3.0.4"
+      }
+    },
+    "file-uri-to-path": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
+      "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="
+    },
+    "fill-range": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+      "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+      "dev": true,
+      "requires": {
+        "to-regex-range": "^5.0.1"
+      }
+    },
+    "finalhandler": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
+      "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
+      "requires": {
+        "debug": "2.6.9",
+        "encodeurl": "~1.0.2",
+        "escape-html": "~1.0.3",
+        "on-finished": "~2.3.0",
+        "parseurl": "~1.3.3",
+        "statuses": "~1.5.0",
+        "unpipe": "~1.0.0"
+      }
+    },
+    "find-up": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
+      "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
+      "dev": true,
+      "requires": {
+        "locate-path": "^2.0.0"
+      }
+    },
+    "findit2": {
+      "version": "2.2.3",
+      "resolved": "https://registry.npmjs.org/findit2/-/findit2-2.2.3.tgz",
+      "integrity": "sha1-WKRmaX34piBc39vzlVNri9d3pfY="
+    },
+    "flat": {
+      "version": "5.0.2",
+      "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz",
+      "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==",
+      "dev": true
+    },
+    "flat-cache": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz",
+      "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==",
+      "dev": true,
+      "requires": {
+        "flatted": "^3.1.0"
+      }
+    },
+    "flatted": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.1.tgz",
+      "integrity": "sha512-OMQjaErSFHmHqZe+PSidH5n8j3O0F2DdnVh8JB4j4eUQ2k6KvB0qGfrKIhapvez5JerBbmWkaLYUYWISaESoXg==",
+      "dev": true
+    },
+    "forever-agent": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
+      "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE="
+    },
+    "form-data": {
+      "version": "2.3.3",
+      "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
+      "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
+      "requires": {
+        "asynckit": "^0.4.0",
+        "combined-stream": "^1.0.6",
+        "mime-types": "^2.1.12"
+      }
+    },
+    "forwarded": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
+      "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
+    },
+    "fresh": {
+      "version": "0.5.2",
+      "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+      "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
+    },
+    "fs-constants": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
+      "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
+    },
+    "fs-extra": {
+      "version": "10.0.0",
+      "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz",
+      "integrity": "sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==",
+      "requires": {
+        "graceful-fs": "^4.2.0",
+        "jsonfile": "^6.0.1",
+        "universalify": "^2.0.0"
+      }
+    },
+    "fs-minipass": {
+      "version": "1.2.5",
+      "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.5.tgz",
+      "integrity": "sha1-BsJ3IYRU7CiN93raVKA7hwKqy50=",
+      "requires": {
+        "minipass": "^2.2.1"
+      }
+    },
+    "fs.realpath": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+      "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
+    },
+    "fsevents": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz",
+      "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==",
+      "dev": true,
+      "optional": true
+    },
+    "function-bind": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+      "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
+      "dev": true
+    },
+    "functional-red-black-tree": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
+      "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
+      "dev": true
+    },
+    "gauge": {
+      "version": "2.7.4",
+      "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
+      "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
+      "requires": {
+        "aproba": "^1.0.3",
+        "console-control-strings": "^1.0.0",
+        "has-unicode": "^2.0.0",
+        "object-assign": "^4.1.0",
+        "signal-exit": "^3.0.0",
+        "string-width": "^1.0.1",
+        "strip-ansi": "^3.0.1",
+        "wide-align": "^1.1.0"
+      }
+    },
+    "gaxios": {
+      "version": "2.3.4",
+      "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-2.3.4.tgz",
+      "integrity": "sha512-US8UMj8C5pRnao3Zykc4AAVr+cffoNKRTg9Rsf2GiuZCW69vgJj38VK2PzlPuQU73FZ/nTk9/Av6/JGcE1N9vA==",
+      "requires": {
+        "abort-controller": "^3.0.0",
+        "extend": "^3.0.2",
+        "https-proxy-agent": "^5.0.0",
+        "is-stream": "^2.0.0",
+        "node-fetch": "^2.3.0"
+      }
+    },
+    "gcp-metadata": {
+      "version": "3.5.0",
+      "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-3.5.0.tgz",
+      "integrity": "sha512-ZQf+DLZ5aKcRpLzYUyBS3yo3N0JSa82lNDO8rj3nMSlovLcz2riKFBsYgDzeXcv75oo5eqB2lx+B14UvPoCRnA==",
+      "requires": {
+        "gaxios": "^2.1.0",
+        "json-bigint": "^0.3.0"
+      }
+    },
+    "get-caller-file": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+      "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+      "dev": true
+    },
+    "get-func-name": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz",
+      "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=",
+      "dev": true
+    },
+    "get-intrinsic": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz",
+      "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==",
+      "dev": true,
+      "requires": {
+        "function-bind": "^1.1.1",
+        "has": "^1.0.3",
+        "has-symbols": "^1.0.1"
+      }
+    },
+    "get-stream": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
+      "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
+      "dev": true,
+      "requires": {
+        "pump": "^3.0.0"
+      }
+    },
+    "getpass": {
+      "version": "0.1.7",
+      "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
+      "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
+      "requires": {
+        "assert-plus": "^1.0.0"
+      }
+    },
+    "glob": {
+      "version": "7.1.3",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
+      "integrity": "sha1-OWCDLT8VdBCDQtr9OmezMsCWnfE=",
+      "requires": {
+        "fs.realpath": "^1.0.0",
+        "inflight": "^1.0.4",
+        "inherits": "2",
+        "minimatch": "^3.0.4",
+        "once": "^1.3.0",
+        "path-is-absolute": "^1.0.0"
+      }
+    },
+    "glob-parent": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+      "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+      "dev": true,
+      "requires": {
+        "is-glob": "^4.0.1"
+      }
+    },
+    "global-dirs": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-2.1.0.tgz",
+      "integrity": "sha512-MG6kdOUh/xBnyo9cJFeIKkLEc1AyFq42QTU4XiX51i2NEdxLxLWXIjEjmqKeSuKR7pAZjTqUVoT2b2huxVLgYQ==",
+      "dev": true,
+      "requires": {
+        "ini": "1.3.7"
+      },
+      "dependencies": {
+        "ini": {
+          "version": "1.3.7",
+          "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.7.tgz",
+          "integrity": "sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ==",
+          "dev": true
+        }
+      }
+    },
+    "globals": {
+      "version": "13.10.0",
+      "resolved": "https://registry.npmjs.org/globals/-/globals-13.10.0.tgz",
+      "integrity": "sha512-piHC3blgLGFjvOuMmWZX60f+na1lXFDhQXBf1UYp2fXPXqvEUbOhNwi6BsQ0bQishwedgnjkwv1d9zKf+MWw3g==",
+      "dev": true
+    },
+    "google-auth-library": {
+      "version": "6.0.6",
+      "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-6.0.6.tgz",
+      "integrity": "sha512-fWYdRdg55HSJoRq9k568jJA1lrhg9i2xgfhVIMJbskUmbDpJGHsbv9l41DGhCDXM21F9Kn4kUwdysgxSYBYJUw==",
+      "requires": {
+        "arrify": "^2.0.0",
+        "base64-js": "^1.3.0",
+        "ecdsa-sig-formatter": "^1.0.11",
+        "fast-text-encoding": "^1.0.0",
+        "gaxios": "^3.0.0",
+        "gcp-metadata": "^4.1.0",
+        "gtoken": "^5.0.0",
+        "jws": "^4.0.0",
+        "lru-cache": "^6.0.0"
+      },
+      "dependencies": {
+        "bignumber.js": {
+          "version": "9.0.0",
+          "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz",
+          "integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A=="
+        },
+        "gaxios": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-3.1.0.tgz",
+          "integrity": "sha512-DDTn3KXVJJigtz+g0J3vhcfbDbKtAroSTxauWsdnP57sM5KZ3d2c/3D9RKFJ86s43hfw6WULg6TXYw/AYiBlpA==",
+          "requires": {
+            "abort-controller": "^3.0.0",
+            "extend": "^3.0.2",
+            "https-proxy-agent": "^5.0.0",
+            "is-stream": "^2.0.0",
+            "node-fetch": "^2.3.0"
+          }
+        },
+        "gcp-metadata": {
+          "version": "4.1.4",
+          "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-4.1.4.tgz",
+          "integrity": "sha512-5J/GIH0yWt/56R3dNaNWPGQ/zXsZOddYECfJaqxFWgrZ9HC2Kvc5vl9upOgUUHKzURjAVf2N+f6tEJiojqXUuA==",
+          "requires": {
+            "gaxios": "^3.0.0",
+            "json-bigint": "^1.0.0"
+          }
+        },
+        "google-p12-pem": {
+          "version": "3.0.2",
+          "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-3.0.2.tgz",
+          "integrity": "sha512-tbjzndQvSIHGBLzHnhDs3cL4RBjLbLXc2pYvGH+imGVu5b4RMAttUTdnmW2UH0t11QeBTXZ7wlXPS7hrypO/tg==",
+          "requires": {
+            "node-forge": "^0.9.0"
+          }
+        },
+        "gtoken": {
+          "version": "5.0.3",
+          "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-5.0.3.tgz",
+          "integrity": "sha512-Nyd1wZCMRc2dj/mAD0LlfQLcAO06uKdpKJXvK85SGrF5+5+Bpfil9u/2aw35ltvEHjvl0h5FMKN5knEU+9JrOg==",
+          "requires": {
+            "gaxios": "^3.0.0",
+            "google-p12-pem": "^3.0.0",
+            "jws": "^4.0.0",
+            "mime": "^2.2.0"
+          }
+        },
+        "json-bigint": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz",
+          "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==",
+          "requires": {
+            "bignumber.js": "^9.0.0"
+          }
+        },
+        "lru-cache": {
+          "version": "6.0.0",
+          "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+          "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+          "requires": {
+            "yallist": "^4.0.0"
+          }
+        },
+        "mime": {
+          "version": "2.4.6",
+          "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz",
+          "integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA=="
+        },
+        "yallist": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+          "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
+        }
+      }
+    },
+    "google-gax": {
+      "version": "1.15.3",
+      "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-1.15.3.tgz",
+      "integrity": "sha512-3JKJCRumNm3x2EksUTw4P1Rad43FTpqrtW9jzpf3xSMYXx+ogaqTM1vGo7VixHB4xkAyATXVIa3OcNSh8H9zsQ==",
+      "requires": {
+        "@grpc/grpc-js": "~1.0.3",
+        "@grpc/proto-loader": "^0.5.1",
+        "@types/fs-extra": "^8.0.1",
+        "@types/long": "^4.0.0",
+        "abort-controller": "^3.0.0",
+        "duplexify": "^3.6.0",
+        "google-auth-library": "^5.0.0",
+        "is-stream-ended": "^0.1.4",
+        "lodash.at": "^4.6.0",
+        "lodash.has": "^4.5.2",
+        "node-fetch": "^2.6.0",
+        "protobufjs": "^6.8.9",
+        "retry-request": "^4.0.0",
+        "semver": "^6.0.0",
+        "walkdir": "^0.4.0"
+      },
+      "dependencies": {
+        "@types/node": {
+          "version": "13.13.15",
+          "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.15.tgz",
+          "integrity": "sha512-kwbcs0jySLxzLsa2nWUAGOd/s21WU1jebrEdtzhsj1D4Yps1EOuyI1Qcu+FD56dL7NRNIJtDDjcqIG22NwkgLw=="
+        },
+        "google-auth-library": {
+          "version": "5.10.1",
+          "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-5.10.1.tgz",
+          "integrity": "sha512-rOlaok5vlpV9rSiUu5EpR0vVpc+PhN62oF4RyX/6++DG1VsaulAFEMlDYBLjJDDPI6OcNOCGAKy9UVB/3NIDXg==",
+          "requires": {
+            "arrify": "^2.0.0",
+            "base64-js": "^1.3.0",
+            "ecdsa-sig-formatter": "^1.0.11",
+            "fast-text-encoding": "^1.0.0",
+            "gaxios": "^2.1.0",
+            "gcp-metadata": "^3.4.0",
+            "gtoken": "^4.1.0",
+            "jws": "^4.0.0",
+            "lru-cache": "^5.0.0"
+          }
+        },
+        "protobufjs": {
+          "version": "6.10.1",
+          "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.10.1.tgz",
+          "integrity": "sha512-pb8kTchL+1Ceg4lFd5XUpK8PdWacbvV5SK2ULH2ebrYtl4GjJmS24m6CKME67jzV53tbJxHlnNOSqQHbTsR9JQ==",
+          "requires": {
+            "@protobufjs/aspromise": "^1.1.2",
+            "@protobufjs/base64": "^1.1.2",
+            "@protobufjs/codegen": "^2.0.4",
+            "@protobufjs/eventemitter": "^1.1.0",
+            "@protobufjs/fetch": "^1.1.0",
+            "@protobufjs/float": "^1.0.2",
+            "@protobufjs/inquire": "^1.1.0",
+            "@protobufjs/path": "^1.1.2",
+            "@protobufjs/pool": "^1.1.0",
+            "@protobufjs/utf8": "^1.1.0",
+            "@types/long": "^4.0.1",
+            "@types/node": "^13.7.0",
+            "long": "^4.0.0"
+          },
+          "dependencies": {
+            "@types/long": {
+              "version": "4.0.1",
+              "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz",
+              "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w=="
+            }
+          }
+        },
+        "semver": {
+          "version": "6.3.0",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+          "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
+        }
+      }
+    },
+    "google-p12-pem": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-2.0.4.tgz",
+      "integrity": "sha512-S4blHBQWZRnEW44OcR7TL9WR+QCqByRvhNDZ/uuQfpxywfupikf/miba8js1jZi6ZOGv5slgSuoshCWh6EMDzg==",
+      "requires": {
+        "node-forge": "^0.9.0"
+      }
+    },
+    "got": {
+      "version": "9.6.0",
+      "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz",
+      "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==",
+      "dev": true,
+      "requires": {
+        "@sindresorhus/is": "^0.14.0",
+        "@szmarczak/http-timer": "^1.1.2",
+        "cacheable-request": "^6.0.0",
+        "decompress-response": "^3.3.0",
+        "duplexer3": "^0.1.4",
+        "get-stream": "^4.1.0",
+        "lowercase-keys": "^1.0.1",
+        "mimic-response": "^1.0.1",
+        "p-cancelable": "^1.0.0",
+        "to-readable-stream": "^1.0.0",
+        "url-parse-lax": "^3.0.0"
+      }
+    },
+    "graceful-fs": {
+      "version": "4.2.6",
+      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz",
+      "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ=="
+    },
+    "growl": {
+      "version": "1.10.5",
+      "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz",
+      "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==",
+      "dev": true
+    },
+    "gtoken": {
+      "version": "4.1.4",
+      "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-4.1.4.tgz",
+      "integrity": "sha512-VxirzD0SWoFUo5p8RDP8Jt2AGyOmyYcT/pOUgDKJCK+iSw0TMqwrVfY37RXTNmoKwrzmDHSk0GMT9FsgVmnVSA==",
+      "requires": {
+        "gaxios": "^2.1.0",
+        "google-p12-pem": "^2.0.0",
+        "jws": "^4.0.0",
+        "mime": "^2.2.0"
+      },
+      "dependencies": {
+        "mime": {
+          "version": "2.4.6",
+          "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz",
+          "integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA=="
+        }
+      }
+    },
+    "har-schema": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
+      "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI="
+    },
+    "har-validator": {
+      "version": "5.1.3",
+      "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz",
+      "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==",
+      "requires": {
+        "ajv": "^6.5.5",
+        "har-schema": "^2.0.0"
+      }
+    },
+    "has": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+      "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+      "dev": true,
+      "requires": {
+        "function-bind": "^1.1.1"
+      }
+    },
+    "has-bigints": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz",
+      "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==",
+      "dev": true
+    },
+    "has-flag": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+      "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+      "dev": true
+    },
+    "has-symbols": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
+      "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==",
+      "dev": true
+    },
+    "has-unicode": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
+      "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk="
+    },
+    "has-yarn": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz",
+      "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==",
+      "dev": true
+    },
+    "he": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
+      "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
+      "dev": true
+    },
+    "heapdump": {
+      "version": "0.3.15",
+      "resolved": "https://registry.npmjs.org/heapdump/-/heapdump-0.3.15.tgz",
+      "integrity": "sha512-n8aSFscI9r3gfhOcAECAtXFaQ1uy4QSke6bnaL+iymYZ/dWs9cqDqHM+rALfsHUwukUbxsdlECZ0pKmJdQ/4OA==",
+      "requires": {
+        "nan": "^2.13.2"
+      }
+    },
+    "hex2dec": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/hex2dec/-/hex2dec-1.1.2.tgz",
+      "integrity": "sha512-Yu+q/XWr2fFQ11tHxPq4p4EiNkb2y+lAacJNhAdRXVfRIcDH6gi7htWFnnlIzvqHMHoWeIsfXlNAjZInpAOJDA=="
+    },
+    "hosted-git-info": {
+      "version": "2.8.9",
+      "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
+      "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
+      "dev": true
+    },
+    "http-cache-semantics": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz",
+      "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==",
+      "dev": true
+    },
+    "http-errors": {
+      "version": "1.7.2",
+      "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
+      "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
+      "requires": {
+        "depd": "~1.1.2",
+        "inherits": "2.0.3",
+        "setprototypeof": "1.1.1",
+        "statuses": ">= 1.5.0 < 2",
+        "toidentifier": "1.0.0"
+      }
+    },
+    "http-proxy-agent": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz",
+      "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==",
+      "requires": {
+        "@tootallnate/once": "1",
+        "agent-base": "6",
+        "debug": "4"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "4.1.1",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+          "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+          "requires": {
+            "ms": "^2.1.1"
+          }
+        },
+        "ms": {
+          "version": "2.1.2",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+          "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+        }
+      }
+    },
+    "http-signature": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
+      "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
+      "requires": {
+        "assert-plus": "^1.0.0",
+        "jsprim": "^1.2.2",
+        "sshpk": "^1.7.0"
+      }
+    },
+    "https-proxy-agent": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz",
+      "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==",
+      "requires": {
+        "agent-base": "6",
+        "debug": "4"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "4.1.1",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+          "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+          "requires": {
+            "ms": "^2.1.1"
+          }
+        },
+        "ms": {
+          "version": "2.1.2",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+          "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+        }
+      }
+    },
+    "iconv-lite": {
+      "version": "0.4.23",
+      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
+      "integrity": "sha1-KXhx9jvlB63Pv8pxXQzQ7thOmmM=",
+      "requires": {
+        "safer-buffer": ">= 2.1.2 < 3"
+      }
+    },
+    "ieee754": {
+      "version": "1.1.13",
+      "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
+      "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg=="
+    },
+    "ignore": {
+      "version": "4.0.6",
+      "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
+      "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
+      "dev": true
+    },
+    "ignore-by-default": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
+      "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=",
+      "dev": true
+    },
+    "ignore-walk": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz",
+      "integrity": "sha1-qD5i59JyrA47VRqqgoMaGbafgvg=",
+      "requires": {
+        "minimatch": "^3.0.4"
+      }
+    },
+    "import-fresh": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz",
+      "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==",
+      "dev": true,
+      "requires": {
+        "parent-module": "^1.0.0",
+        "resolve-from": "^4.0.0"
+      }
+    },
+    "import-lazy": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz",
+      "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=",
+      "dev": true
+    },
+    "imurmurhash": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+      "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
+      "dev": true
+    },
+    "inflection": {
+      "version": "1.12.0",
+      "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.12.0.tgz",
+      "integrity": "sha1-ogCTVlbW9fa8TcdQLhrstwMihBY="
+    },
+    "inflight": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+      "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+      "requires": {
+        "once": "^1.3.0",
+        "wrappy": "1"
+      }
+    },
+    "inherits": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+      "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
+    },
+    "ini": {
+      "version": "1.3.8",
+      "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
+      "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="
+    },
+    "ipaddr.js": {
+      "version": "1.9.1",
+      "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+      "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
+    },
+    "is": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/is/-/is-3.3.0.tgz",
+      "integrity": "sha512-nW24QBoPcFGGHJGUwnfpI7Yc5CdqWNdsyHQszVE/z2pKHXzh7FZ5GWhJqSyaQ9wMkQnsTx+kAI8bHlCX4tKdbg=="
+    },
+    "is-arrayish": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+      "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
+      "dev": true
+    },
+    "is-bigint": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.2.tgz",
+      "integrity": "sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA==",
+      "dev": true
+    },
+    "is-binary-path": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+      "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+      "dev": true,
+      "requires": {
+        "binary-extensions": "^2.0.0"
+      }
+    },
+    "is-bluebird": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/is-bluebird/-/is-bluebird-1.0.2.tgz",
+      "integrity": "sha1-CWQ5Bg9KpBGr7hkUOoTWpVNG1uI="
+    },
+    "is-boolean-object": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.1.tgz",
+      "integrity": "sha512-bXdQWkECBUIAcCkeH1unwJLIpZYaa5VvuygSyS/c2lf719mTKZDU5UdDRlpd01UjADgmW8RfqaP+mRaVPdr/Ng==",
+      "dev": true,
+      "requires": {
+        "call-bind": "^1.0.2"
+      }
+    },
+    "is-buffer": {
+      "version": "1.1.6",
+      "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
+      "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
+    },
+    "is-callable": {
+      "version": "1.1.5",
+      "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz",
+      "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==",
+      "dev": true
+    },
+    "is-ci": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz",
+      "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==",
+      "dev": true,
+      "requires": {
+        "ci-info": "^2.0.0"
+      }
+    },
+    "is-core-module": {
+      "version": "2.5.0",
+      "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.5.0.tgz",
+      "integrity": "sha512-TXCMSDsEHMEEZ6eCA8rwRDbLu55MRGmrctljsBX/2v1d9/GzqHOxW5c5oPSgrUt2vBFXebu9rGqckXGPWOlYpg==",
+      "dev": true,
+      "requires": {
+        "has": "^1.0.3"
+      }
+    },
+    "is-date-object": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz",
+      "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==",
+      "dev": true
+    },
+    "is-extglob": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+      "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+      "dev": true
+    },
+    "is-fullwidth-code-point": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
+      "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
+      "requires": {
+        "number-is-nan": "^1.0.0"
+      }
+    },
+    "is-glob": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
+      "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
+      "dev": true,
+      "requires": {
+        "is-extglob": "^2.1.1"
+      }
+    },
+    "is-installed-globally": {
+      "version": "0.3.2",
+      "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.3.2.tgz",
+      "integrity": "sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g==",
+      "dev": true,
+      "requires": {
+        "global-dirs": "^2.0.1",
+        "is-path-inside": "^3.0.1"
+      }
+    },
+    "is-negative-zero": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz",
+      "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==",
+      "dev": true
+    },
+    "is-npm": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-4.0.0.tgz",
+      "integrity": "sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig==",
+      "dev": true
+    },
+    "is-number": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+      "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+      "dev": true
+    },
+    "is-number-object": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.5.tgz",
+      "integrity": "sha512-RU0lI/n95pMoUKu9v1BZP5MBcZuNSVJkMkAG2dJqC4z2GlkGUNeH68SuHuBKBD/XFe+LHZ+f9BKkLET60Niedw==",
+      "dev": true
+    },
+    "is-obj": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz",
+      "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w=="
+    },
+    "is-path-inside": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
+      "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
+      "dev": true
+    },
+    "is-plain-obj": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz",
+      "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==",
+      "dev": true
+    },
+    "is-regex": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.3.tgz",
+      "integrity": "sha512-qSVXFz28HM7y+IWX6vLCsexdlvzT1PJNFSBuaQLQ5o0IEw8UDYW6/2+eCMVyIsbM8CNLX2a/QWmSpyxYEHY7CQ==",
+      "dev": true,
+      "requires": {
+        "call-bind": "^1.0.2"
+      }
+    },
+    "is-stream": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz",
+      "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw=="
+    },
+    "is-stream-ended": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/is-stream-ended/-/is-stream-ended-0.1.4.tgz",
+      "integrity": "sha512-xj0XPvmr7bQFTvirqnFr50o0hQIh6ZItDqloxt5aJrR4NQsYeSsyFQERYGCAzfindAcnKjINnwEEgLx4IqVzQw=="
+    },
+    "is-string": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz",
+      "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==",
+      "dev": true
+    },
+    "is-symbol": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz",
+      "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==",
+      "dev": true,
+      "requires": {
+        "has-symbols": "^1.0.1"
+      }
+    },
+    "is-typedarray": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
+      "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
+    },
+    "is-yarn-global": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz",
+      "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==",
+      "dev": true
+    },
+    "isarray": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+      "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
+    },
+    "isexe": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+      "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+      "dev": true
+    },
+    "isstream": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
+      "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
+    },
+    "js-tokens": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+      "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+      "dev": true
+    },
+    "js-yaml": {
+      "version": "3.13.1",
+      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
+      "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
+      "dev": true,
+      "requires": {
+        "argparse": "^1.0.7",
+        "esprima": "^4.0.0"
+      }
+    },
+    "jsbn": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
+      "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM="
+    },
+    "json-bigint": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-0.3.0.tgz",
+      "integrity": "sha1-DM2RLEuCcNBfBW+9E4FLU9OCWx4=",
+      "requires": {
+        "bignumber.js": "^7.0.0"
+      }
+    },
+    "json-buffer": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz",
+      "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=",
+      "dev": true
+    },
+    "json-parse-better-errors": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
+      "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
+      "dev": true
+    },
+    "json-schema": {
+      "version": "0.2.3",
+      "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
+      "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM="
+    },
+    "json-schema-traverse": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+      "integrity": "sha1-afaofZUTq4u4/mO9sJecRI5oRmA="
+    },
+    "json-stable-stringify-without-jsonify": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+      "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
+      "dev": true
+    },
+    "json-stringify-safe": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
+      "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
+    },
+    "json5": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz",
+      "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==",
+      "dev": true,
+      "requires": {
+        "minimist": "^1.2.5"
+      },
+      "dependencies": {
+        "minimist": {
+          "version": "1.2.5",
+          "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
+          "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
+          "dev": true
+        }
+      }
+    },
+    "jsonfile": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
+      "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
+      "requires": {
+        "graceful-fs": "^4.1.6",
+        "universalify": "^2.0.0"
+      }
+    },
+    "jsprim": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
+      "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
+      "requires": {
+        "assert-plus": "1.0.0",
+        "extsprintf": "1.3.0",
+        "json-schema": "0.2.3",
+        "verror": "1.10.0"
+      }
+    },
+    "just-extend": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.1.0.tgz",
+      "integrity": "sha512-ApcjaOdVTJ7y4r08xI5wIqpvwS48Q0PBG4DJROcEkH1f8MdAiNFyFxz3xoL0LWAVwjrwPYZdVHHxhRHcx/uGLA==",
+      "dev": true
+    },
+    "jwa": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz",
+      "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==",
+      "requires": {
+        "buffer-equal-constant-time": "1.0.1",
+        "ecdsa-sig-formatter": "1.0.11",
+        "safe-buffer": "^5.0.1"
+      }
+    },
+    "jws": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz",
+      "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==",
+      "requires": {
+        "jwa": "^2.0.0",
+        "safe-buffer": "^5.0.1"
+      }
+    },
+    "keyv": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz",
+      "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==",
+      "dev": true,
+      "requires": {
+        "json-buffer": "3.0.0"
+      }
+    },
+    "latest-version": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz",
+      "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==",
+      "dev": true,
+      "requires": {
+        "package-json": "^6.3.0"
+      }
+    },
+    "levn": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+      "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+      "dev": true,
+      "requires": {
+        "prelude-ls": "^1.2.1",
+        "type-check": "~0.4.0"
+      }
+    },
+    "load-json-file": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz",
+      "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=",
+      "dev": true,
+      "requires": {
+        "graceful-fs": "^4.1.2",
+        "parse-json": "^4.0.0",
+        "pify": "^3.0.0",
+        "strip-bom": "^3.0.0"
+      },
+      "dependencies": {
+        "pify": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+          "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
+          "dev": true
+        }
+      }
+    },
+    "locate-path": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
+      "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=",
+      "dev": true,
+      "requires": {
+        "p-locate": "^2.0.0",
+        "path-exists": "^3.0.0"
+      }
+    },
+    "lockfile": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/lockfile/-/lockfile-1.0.4.tgz",
+      "integrity": "sha1-B/gZ0lrkj4flOOZXi2lkpJgaVgk=",
+      "requires": {
+        "signal-exit": "^3.0.2"
+      }
+    },
+    "lodash": {
+      "version": "4.17.21",
+      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+      "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
+    },
+    "lodash.at": {
+      "version": "4.6.0",
+      "resolved": "https://registry.npmjs.org/lodash.at/-/lodash.at-4.6.0.tgz",
+      "integrity": "sha1-k83OZk8KGZTqM9181A4jr9EbD/g="
+    },
+    "lodash.camelcase": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
+      "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY="
+    },
+    "lodash.clonedeep": {
+      "version": "4.5.0",
+      "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
+      "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=",
+      "dev": true
+    },
+    "lodash.get": {
+      "version": "4.4.2",
+      "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
+      "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=",
+      "dev": true
+    },
+    "lodash.has": {
+      "version": "4.5.2",
+      "resolved": "https://registry.npmjs.org/lodash.has/-/lodash.has-4.5.2.tgz",
+      "integrity": "sha1-0Z9NwQlQWMzL4rDN9O4P5Ko3yGI="
+    },
+    "lodash.merge": {
+      "version": "4.6.2",
+      "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+      "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+      "dev": true
+    },
+    "lodash.truncate": {
+      "version": "4.4.2",
+      "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz",
+      "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=",
+      "dev": true
+    },
+    "log-driver": {
+      "version": "1.2.7",
+      "resolved": "https://registry.npmjs.org/log-driver/-/log-driver-1.2.7.tgz",
+      "integrity": "sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg=="
+    },
+    "log-symbols": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz",
+      "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==",
+      "dev": true,
+      "requires": {
+        "chalk": "^4.0.0"
+      },
+      "dependencies": {
+        "ansi-styles": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+          "dev": true,
+          "requires": {
+            "color-convert": "^2.0.1"
+          }
+        },
+        "chalk": {
+          "version": "4.1.1",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz",
+          "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "dev": true,
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
+        }
+      }
+    },
+    "logger-sharelatex": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/logger-sharelatex/-/logger-sharelatex-2.2.0.tgz",
+      "integrity": "sha512-ko+OmE25XHJJCiz1R9EgwlfM7J/5olpunUfR3WcfuqOQrcUqsdBrDA2sOytngT0ViwjCR0Fh4qZVPwEWfmrvwA==",
+      "requires": {
+        "@google-cloud/logging-bunyan": "^3.0.0",
+        "@overleaf/o-error": "^3.0.0",
+        "bunyan": "^1.8.14",
+        "node-fetch": "^2.6.0",
+        "raven": "^2.6.4",
+        "yn": "^4.0.0"
+      },
+      "dependencies": {
+        "yn": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/yn/-/yn-4.0.0.tgz",
+          "integrity": "sha512-huWiiCS4TxKc4SfgmTwW1K7JmXPPAmuXWYy4j9qjQo4+27Kni8mGhAAi1cloRWmBe2EqcLgt3IGqQoRL/MtPgg=="
+        }
+      }
+    },
+    "long": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
+      "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="
+    },
+    "lowercase-keys": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz",
+      "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==",
+      "dev": true
+    },
+    "lru-cache": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+      "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+      "requires": {
+        "yallist": "^3.0.2"
+      }
+    },
+    "make-dir": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
+      "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
+      "dev": true,
+      "requires": {
+        "semver": "^6.0.0"
+      },
+      "dependencies": {
+        "semver": {
+          "version": "6.3.0",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+          "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+          "dev": true
+        }
+      }
+    },
+    "map-obj": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.1.0.tgz",
+      "integrity": "sha512-glc9y00wgtwcDmp7GaE/0b0OnxpNJsVf3ael/An6Fe2Q51LLwN1er6sdomLRzz5h0+yMpiYLhWYF5R7HeqVd4g=="
+    },
+    "md5": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz",
+      "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==",
+      "requires": {
+        "charenc": "0.0.2",
+        "crypt": "0.0.2",
+        "is-buffer": "~1.1.6"
+      }
+    },
+    "media-typer": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+      "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
+    },
+    "merge-descriptors": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
+      "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
+    },
+    "methods": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
+      "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
+    },
+    "mime": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+      "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
+    },
+    "mime-db": {
+      "version": "1.43.0",
+      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz",
+      "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ=="
+    },
+    "mime-types": {
+      "version": "2.1.26",
+      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz",
+      "integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==",
+      "requires": {
+        "mime-db": "1.43.0"
+      }
+    },
+    "mimic-response": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz",
+      "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==",
+      "dev": true
+    },
+    "minimatch": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+      "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=",
+      "requires": {
+        "brace-expansion": "^1.1.7"
+      }
+    },
+    "minimist": {
+      "version": "0.0.8",
+      "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
+      "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
+    },
+    "minipass": {
+      "version": "2.3.5",
+      "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz",
+      "integrity": "sha1-ys6+SSAiSX9law8PUeJoKp7S2Eg=",
+      "requires": {
+        "safe-buffer": "^5.1.2",
+        "yallist": "^3.0.0"
+      }
+    },
+    "minizlib": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.2.1.tgz",
+      "integrity": "sha1-3SfqYTYkPHyIBoToZyuzpF/ZthQ=",
+      "requires": {
+        "minipass": "^2.2.1"
+      }
+    },
+    "mkdirp": {
+      "version": "0.5.1",
+      "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
+      "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
+      "requires": {
+        "minimist": "0.0.8"
+      }
+    },
+    "mocha": {
+      "version": "8.4.0",
+      "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.4.0.tgz",
+      "integrity": "sha512-hJaO0mwDXmZS4ghXsvPVriOhsxQ7ofcpQdm8dE+jISUOKopitvnXFQmpRR7jd2K6VBG6E26gU3IAbXXGIbu4sQ==",
+      "dev": true,
+      "requires": {
+        "@ungap/promise-all-settled": "1.1.2",
+        "ansi-colors": "4.1.1",
+        "browser-stdout": "1.3.1",
+        "chokidar": "3.5.1",
+        "debug": "4.3.1",
+        "diff": "5.0.0",
+        "escape-string-regexp": "4.0.0",
+        "find-up": "5.0.0",
+        "glob": "7.1.6",
+        "growl": "1.10.5",
+        "he": "1.2.0",
+        "js-yaml": "4.0.0",
+        "log-symbols": "4.0.0",
+        "minimatch": "3.0.4",
+        "ms": "2.1.3",
+        "nanoid": "3.1.20",
+        "serialize-javascript": "5.0.1",
+        "strip-json-comments": "3.1.1",
+        "supports-color": "8.1.1",
+        "which": "2.0.2",
+        "wide-align": "1.1.3",
+        "workerpool": "6.1.0",
+        "yargs": "16.2.0",
+        "yargs-parser": "20.2.4",
+        "yargs-unparser": "2.0.0"
+      },
+      "dependencies": {
+        "ansi-regex": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
+          "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg=="
+        },
+        "ansi-styles": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+          "requires": {
+            "color-convert": "^2.0.1"
+          }
+        },
+        "argparse": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+          "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+          "dev": true
+        },
+        "chokidar": {
+          "version": "3.5.1",
+          "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz",
+          "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==",
+          "dev": true,
+          "requires": {
+            "anymatch": "~3.1.1",
+            "braces": "~3.0.2",
+            "glob-parent": "~5.1.0",
+            "is-binary-path": "~2.1.0",
+            "is-glob": "~4.0.1",
+            "normalize-path": "~3.0.0",
+            "readdirp": "~3.5.0"
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+        },
+        "debug": {
+          "version": "4.3.1",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
+          "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
+          "dev": true,
+          "requires": {
+            "ms": "2.1.2"
+          },
+          "dependencies": {
+            "ms": {
+              "version": "2.1.2",
+              "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+              "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+              "dev": true
+            }
+          }
+        },
+        "escape-string-regexp": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+          "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+          "dev": true
+        },
+        "find-up": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+          "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+          "dev": true,
+          "requires": {
+            "locate-path": "^6.0.0",
+            "path-exists": "^4.0.0"
+          }
+        },
+        "glob": {
+          "version": "7.1.6",
+          "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
+          "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
+          "dev": true,
+          "requires": {
+            "fs.realpath": "^1.0.0",
+            "inflight": "^1.0.4",
+            "inherits": "2",
+            "minimatch": "^3.0.4",
+            "once": "^1.3.0",
+            "path-is-absolute": "^1.0.0"
+          }
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
+        "is-fullwidth-code-point": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+          "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
+        },
+        "js-yaml": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.0.0.tgz",
+          "integrity": "sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==",
+          "dev": true,
+          "requires": {
+            "argparse": "^2.0.1"
+          }
+        },
+        "locate-path": {
+          "version": "6.0.0",
+          "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+          "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+          "dev": true,
+          "requires": {
+            "p-locate": "^5.0.0"
+          }
+        },
+        "ms": {
+          "version": "2.1.3",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+          "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+          "dev": true
+        },
+        "p-locate": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+          "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+          "dev": true,
+          "requires": {
+            "p-limit": "^3.0.2"
+          }
+        },
+        "path-exists": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+          "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+          "dev": true
+        },
+        "readdirp": {
+          "version": "3.5.0",
+          "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz",
+          "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==",
+          "dev": true,
+          "requires": {
+            "picomatch": "^2.2.1"
+          }
+        },
+        "string-width": {
+          "version": "4.2.2",
+          "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz",
+          "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==",
+          "requires": {
+            "emoji-regex": "^8.0.0",
+            "is-fullwidth-code-point": "^3.0.0",
+            "strip-ansi": "^6.0.0"
+          }
+        },
+        "strip-ansi": {
+          "version": "6.0.0",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
+          "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
+          "requires": {
+            "ansi-regex": "^5.0.0"
+          }
+        },
+        "strip-json-comments": {
+          "version": "3.1.1",
+          "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+          "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+          "dev": true
+        },
+        "supports-color": {
+          "version": "8.1.1",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+          "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
+        }
+      }
+    },
+    "module-details-from-path": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.3.tgz",
+      "integrity": "sha1-EUyUlnPiqKNenTV4hSeqN7Z52is="
+    },
+    "moment": {
+      "version": "2.23.0",
+      "resolved": "https://registry.npmjs.org/moment/-/moment-2.23.0.tgz",
+      "integrity": "sha1-dZ6kkayX1UusWtd2mW4qWMwbwiU="
+    },
+    "moment-timezone": {
+      "version": "0.5.28",
+      "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.28.tgz",
+      "integrity": "sha512-TDJkZvAyKIVWg5EtVqRzU97w0Rb0YVbfpqyjgu6GwXCAohVRqwZjf4fOzDE6p1Ch98Sro/8hQQi65WDXW5STPw==",
+      "requires": {
+        "moment": ">= 2.9.0"
+      }
+    },
+    "ms": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+      "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+    },
+    "mv": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz",
+      "integrity": "sha1-rmzg1vbV4KT32JN5jQPB6pVZtqI=",
+      "optional": true,
+      "requires": {
+        "mkdirp": "~0.5.1",
+        "ncp": "~2.0.0",
+        "rimraf": "~2.4.0"
+      },
+      "dependencies": {
+        "glob": {
+          "version": "6.0.4",
+          "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz",
+          "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=",
+          "optional": true,
+          "requires": {
+            "inflight": "^1.0.4",
+            "inherits": "2",
+            "minimatch": "2 || 3",
+            "once": "^1.3.0",
+            "path-is-absolute": "^1.0.0"
+          }
+        },
+        "rimraf": {
+          "version": "2.4.5",
+          "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz",
+          "integrity": "sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto=",
+          "optional": true,
+          "requires": {
+            "glob": "^6.0.1"
+          }
+        }
+      }
+    },
+    "mysql": {
+      "version": "2.18.1",
+      "resolved": "https://registry.npmjs.org/mysql/-/mysql-2.18.1.tgz",
+      "integrity": "sha512-Bca+gk2YWmqp2Uf6k5NFEurwY/0td0cpebAucFpY/3jhrwrVGuxU2uQFCHjU19SJfje0yQvi+rVWdq78hR5lig==",
+      "requires": {
+        "bignumber.js": "9.0.0",
+        "readable-stream": "2.3.7",
+        "safe-buffer": "5.1.2",
+        "sqlstring": "2.3.1"
+      },
+      "dependencies": {
+        "bignumber.js": {
+          "version": "9.0.0",
+          "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz",
+          "integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A=="
+        },
+        "readable-stream": {
+          "version": "2.3.7",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+          "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+          "requires": {
+            "core-util-is": "~1.0.0",
+            "inherits": "~2.0.3",
+            "isarray": "~1.0.0",
+            "process-nextick-args": "~2.0.0",
+            "safe-buffer": "~5.1.1",
+            "string_decoder": "~1.1.1",
+            "util-deprecate": "~1.0.1"
+          }
+        }
+      }
+    },
+    "nan": {
+      "version": "2.14.0",
+      "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz",
+      "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg=="
+    },
+    "nanoid": {
+      "version": "3.1.20",
+      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz",
+      "integrity": "sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==",
+      "dev": true
+    },
+    "natural-compare": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+      "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
+      "dev": true
+    },
+    "ncp": {
+      "version": "2.0.0",
+      "resolved": "http://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz",
+      "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=",
+      "optional": true
+    },
+    "needle": {
+      "version": "2.2.4",
+      "resolved": "https://registry.npmjs.org/needle/-/needle-2.2.4.tgz",
+      "integrity": "sha1-UZMb/4JTOxkot9HWngHxsA/9Kk4=",
+      "requires": {
+        "debug": "^2.1.2",
+        "iconv-lite": "^0.4.4",
+        "sax": "^1.2.4"
+      }
+    },
+    "negotiator": {
+      "version": "0.6.2",
+      "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
+      "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
+    },
+    "nise": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/nise/-/nise-4.0.3.tgz",
+      "integrity": "sha512-EGlhjm7/4KvmmE6B/UFsKh7eHykRl9VH+au8dduHLCyWUO/hr7+N+WtTvDUwc9zHuM1IaIJs/0lQ6Ag1jDkQSg==",
+      "dev": true,
+      "requires": {
+        "@sinonjs/commons": "^1.7.0",
+        "@sinonjs/fake-timers": "^6.0.0",
+        "@sinonjs/text-encoding": "^0.7.1",
+        "just-extend": "^4.0.2",
+        "path-to-regexp": "^1.7.0"
+      },
+      "dependencies": {
+        "isarray": {
+          "version": "0.0.1",
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+          "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
+          "dev": true
+        },
+        "path-to-regexp": {
+          "version": "1.8.0",
+          "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz",
+          "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==",
+          "dev": true,
+          "requires": {
+            "isarray": "0.0.1"
+          }
+        }
+      }
+    },
+    "node-fetch": {
+      "version": "2.6.1",
+      "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
+      "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw=="
+    },
+    "node-forge": {
+      "version": "0.9.1",
+      "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.9.1.tgz",
+      "integrity": "sha512-G6RlQt5Sb4GMBzXvhfkeFmbqR6MzhtnT7VTHuLadjkii3rdYHNdw0m8zA4BTxVIh68FicCQ2NSUANpsqkr9jvQ=="
+    },
+    "node-pre-gyp": {
+      "version": "0.11.0",
+      "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.11.0.tgz",
+      "integrity": "sha512-TwWAOZb0j7e9eGaf9esRx3ZcLaE5tQ2lvYy1pb5IAaG1a2e2Kv5Lms1Y4hpj+ciXJRofIxxlt5haeQ/2ANeE0Q==",
+      "requires": {
+        "detect-libc": "^1.0.2",
+        "mkdirp": "^0.5.1",
+        "needle": "^2.2.1",
+        "nopt": "^4.0.1",
+        "npm-packlist": "^1.1.6",
+        "npmlog": "^4.0.2",
+        "rc": "^1.2.7",
+        "rimraf": "^2.6.1",
+        "semver": "^5.3.0",
+        "tar": "^4"
+      }
+    },
+    "nodemon": {
+      "version": "2.0.7",
+      "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.7.tgz",
+      "integrity": "sha512-XHzK69Awgnec9UzHr1kc8EomQh4sjTQ8oRf8TsGrSmHDx9/UmiGG9E/mM3BuTfNeFwdNBvrqQq/RHL0xIeyFOA==",
+      "dev": true,
+      "requires": {
+        "chokidar": "^3.2.2",
+        "debug": "^3.2.6",
+        "ignore-by-default": "^1.0.1",
+        "minimatch": "^3.0.4",
+        "pstree.remy": "^1.1.7",
+        "semver": "^5.7.1",
+        "supports-color": "^5.5.0",
+        "touch": "^3.1.0",
+        "undefsafe": "^2.0.3",
+        "update-notifier": "^4.1.0"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "3.2.7",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+          "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+          "dev": true,
+          "requires": {
+            "ms": "^2.1.1"
+          }
+        },
+        "ms": {
+          "version": "2.1.3",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+          "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+          "dev": true
+        },
+        "semver": {
+          "version": "5.7.1",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+          "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+          "dev": true
+        }
+      }
+    },
+    "nopt": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz",
+      "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=",
+      "requires": {
+        "abbrev": "1",
+        "osenv": "^0.1.4"
+      }
+    },
+    "normalize-package-data": {
+      "version": "2.5.0",
+      "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
+      "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
+      "dev": true,
+      "requires": {
+        "hosted-git-info": "^2.1.4",
+        "resolve": "^1.10.0",
+        "semver": "2 || 3 || 4 || 5",
+        "validate-npm-package-license": "^3.0.1"
+      }
+    },
+    "normalize-path": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+      "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+      "dev": true
+    },
+    "normalize-url": {
+      "version": "4.5.1",
+      "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz",
+      "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==",
+      "dev": true
+    },
+    "npm-bundled": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.5.tgz",
+      "integrity": "sha1-PBcyt7qTazoQMlrvYWRnwMy8yXk="
+    },
+    "npm-packlist": {
+      "version": "1.1.12",
+      "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.1.12.tgz",
+      "integrity": "sha1-Ir3i68EucspIKr1nr8UetJN3JDo=",
+      "requires": {
+        "ignore-walk": "^3.0.1",
+        "npm-bundled": "^1.0.1"
+      }
+    },
+    "npmlog": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
+      "integrity": "sha1-CKfyqL9zRgR3mp76StXMcXq7lUs=",
+      "requires": {
+        "are-we-there-yet": "~1.1.2",
+        "console-control-strings": "~1.1.0",
+        "gauge": "~2.7.3",
+        "set-blocking": "~2.0.0"
+      }
+    },
+    "number-is-nan": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
+      "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
+    },
+    "oauth-sign": {
+      "version": "0.9.0",
+      "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
+      "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ=="
+    },
+    "object-assign": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+      "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
+    },
+    "object-inspect": {
+      "version": "1.11.0",
+      "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz",
+      "integrity": "sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==",
+      "dev": true
+    },
+    "object-keys": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+      "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+      "dev": true
+    },
+    "object.assign": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz",
+      "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==",
+      "dev": true,
+      "requires": {
+        "call-bind": "^1.0.0",
+        "define-properties": "^1.1.3",
+        "has-symbols": "^1.0.1",
+        "object-keys": "^1.1.1"
+      }
+    },
+    "object.values": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.4.tgz",
+      "integrity": "sha512-TnGo7j4XSnKQoK3MfvkzqKCi0nVe/D9I9IjwTNYdb/fxYHpjrluHVOgw0AF6jrRFGMPHdfuidR09tIDiIvnaSg==",
+      "dev": true,
+      "requires": {
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.3",
+        "es-abstract": "^1.18.2"
+      }
+    },
+    "on-finished": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
+      "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
+      "requires": {
+        "ee-first": "1.1.1"
+      }
+    },
+    "on-headers": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
+      "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA=="
+    },
+    "once": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+      "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+      "requires": {
+        "wrappy": "1"
+      }
+    },
+    "optionator": {
+      "version": "0.9.1",
+      "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
+      "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==",
+      "dev": true,
+      "requires": {
+        "deep-is": "^0.1.3",
+        "fast-levenshtein": "^2.0.6",
+        "levn": "^0.4.1",
+        "prelude-ls": "^1.2.1",
+        "type-check": "^0.4.0",
+        "word-wrap": "^1.2.3"
+      }
+    },
+    "os-homedir": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
+      "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M="
+    },
+    "os-tmpdir": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
+      "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ="
+    },
+    "osenv": {
+      "version": "0.1.5",
+      "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz",
+      "integrity": "sha1-hc36+uso6Gd/QW4odZK18/SepBA=",
+      "requires": {
+        "os-homedir": "^1.0.0",
+        "os-tmpdir": "^1.0.0"
+      }
+    },
+    "p-cancelable": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz",
+      "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==",
+      "dev": true
+    },
+    "p-limit": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+      "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+      "requires": {
+        "yocto-queue": "^0.1.0"
+      }
+    },
+    "p-locate": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
+      "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=",
+      "dev": true,
+      "requires": {
+        "p-limit": "^1.1.0"
+      },
+      "dependencies": {
+        "p-limit": {
+          "version": "1.3.0",
+          "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
+          "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==",
+          "dev": true,
+          "requires": {
+            "p-try": "^1.0.0"
+          }
+        }
+      }
+    },
+    "p-try": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
+      "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=",
+      "dev": true
+    },
+    "package-json": {
+      "version": "6.5.0",
+      "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz",
+      "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==",
+      "dev": true,
+      "requires": {
+        "got": "^9.6.0",
+        "registry-auth-token": "^4.0.0",
+        "registry-url": "^5.0.0",
+        "semver": "^6.2.0"
+      },
+      "dependencies": {
+        "semver": {
+          "version": "6.3.0",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+          "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+          "dev": true
+        }
+      }
+    },
+    "parent-module": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+      "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+      "dev": true,
+      "requires": {
+        "callsites": "^3.0.0"
+      }
+    },
+    "parse-duration": {
+      "version": "0.4.4",
+      "resolved": "https://registry.npmjs.org/parse-duration/-/parse-duration-0.4.4.tgz",
+      "integrity": "sha512-KbAJuYGUhZkB9gotDiKLnZ7Z3VTacK3fgwmDdB6ZVDtJbMBT6MfLga0WJaYpPDu0mzqT0NgHtHDt5PY4l0nidg=="
+    },
+    "parse-json": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
+      "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
+      "dev": true,
+      "requires": {
+        "error-ex": "^1.3.1",
+        "json-parse-better-errors": "^1.0.1"
+      }
+    },
+    "parse-ms": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-2.1.0.tgz",
+      "integrity": "sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA=="
+    },
+    "parseurl": {
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+      "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
+    },
+    "path-exists": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+      "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
+      "dev": true
+    },
+    "path-is-absolute": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+      "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
+    },
+    "path-key": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+      "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+      "dev": true
+    },
+    "path-parse": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
+      "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw=="
+    },
+    "path-to-regexp": {
+      "version": "0.1.7",
+      "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
+      "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
+    },
+    "path-type": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz",
+      "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==",
+      "dev": true,
+      "requires": {
+        "pify": "^3.0.0"
+      },
+      "dependencies": {
+        "pify": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+          "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
+          "dev": true
+        }
+      }
+    },
+    "pathval": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz",
+      "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=",
+      "dev": true
+    },
+    "pdfjs-dist": {
+      "version": "2.7.570",
+      "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-2.7.570.tgz",
+      "integrity": "sha512-/ZkA1FwkEOyDaq11JhMLazdwQAA0F9uwrP7h/1L9Akt9KWh1G5/tkzS+bPuUELq2s2GDFnaT+kooN/aSjT7DXQ=="
+    },
+    "performance-now": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
+      "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
+    },
+    "picomatch": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.1.tgz",
+      "integrity": "sha512-ISBaA8xQNmwELC7eOjqFKMESB2VIqt4PPDD0nsS95b/9dZXvVKOlz9keMSnoGGKcOHXfTvDD6WMaRoSc9UuhRA==",
+      "dev": true
+    },
+    "pify": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/pify/-/pify-5.0.0.tgz",
+      "integrity": "sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA=="
+    },
+    "pkg-dir": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz",
+      "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=",
+      "dev": true,
+      "requires": {
+        "find-up": "^2.1.0"
+      }
+    },
+    "pkg-up": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz",
+      "integrity": "sha1-yBmscoBZpGHKscOImivjxJoATX8=",
+      "dev": true,
+      "requires": {
+        "find-up": "^2.1.0"
+      }
+    },
+    "pprof": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/pprof/-/pprof-3.0.0.tgz",
+      "integrity": "sha512-uPWbAhoH/zvq1kM3/Fd/wshb4D7sLlGap8t6uCTER4aZRWqqyPYgXzpjWbT0Unn5U25pEy2VREUu27nQ9o9VPA==",
+      "requires": {
+        "bindings": "^1.2.1",
+        "delay": "^4.0.1",
+        "findit2": "^2.2.3",
+        "nan": "^2.14.0",
+        "node-pre-gyp": "^0.16.0",
+        "p-limit": "^3.0.0",
+        "pify": "^5.0.0",
+        "protobufjs": "~6.10.0",
+        "source-map": "^0.7.3",
+        "split": "^1.0.1"
+      },
+      "dependencies": {
+        "@types/long": {
+          "version": "4.0.1",
+          "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz",
+          "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w=="
+        },
+        "@types/node": {
+          "version": "13.13.42",
+          "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.42.tgz",
+          "integrity": "sha512-g+w2QgbW7k2CWLOXzQXbO37a7v5P9ObPvYahKphdBLV5aqpbVZRhTpWCT0SMRqX1i30Aig791ZmIM2fJGL2S8A=="
+        },
+        "debug": {
+          "version": "3.2.7",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+          "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+          "requires": {
+            "ms": "^2.1.1"
+          }
+        },
+        "minimist": {
+          "version": "1.2.5",
+          "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
+          "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
+        },
+        "mkdirp": {
+          "version": "0.5.5",
+          "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
+          "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
+          "requires": {
+            "minimist": "^1.2.5"
+          }
+        },
+        "ms": {
+          "version": "2.1.3",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+          "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
+        },
+        "needle": {
+          "version": "2.6.0",
+          "resolved": "https://registry.npmjs.org/needle/-/needle-2.6.0.tgz",
+          "integrity": "sha512-KKYdza4heMsEfSWD7VPUIz3zX2XDwOyX2d+geb4vrERZMT5RMU6ujjaD+I5Yr54uZxQ2w6XRTAhHBbSCyovZBg==",
+          "requires": {
+            "debug": "^3.2.6",
+            "iconv-lite": "^0.4.4",
+            "sax": "^1.2.4"
+          }
+        },
+        "node-pre-gyp": {
+          "version": "0.16.0",
+          "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.16.0.tgz",
+          "integrity": "sha512-4efGA+X/YXAHLi1hN8KaPrILULaUn2nWecFrn1k2I+99HpoyvcOGEbtcOxpDiUwPF2ZANMJDh32qwOUPenuR1g==",
+          "requires": {
+            "detect-libc": "^1.0.2",
+            "mkdirp": "^0.5.3",
+            "needle": "^2.5.0",
+            "nopt": "^4.0.1",
+            "npm-packlist": "^1.1.6",
+            "npmlog": "^4.0.2",
+            "rc": "^1.2.7",
+            "rimraf": "^2.6.1",
+            "semver": "^5.3.0",
+            "tar": "^4.4.2"
+          }
+        },
+        "protobufjs": {
+          "version": "6.10.2",
+          "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.10.2.tgz",
+          "integrity": "sha512-27yj+04uF6ya9l+qfpH187aqEzfCF4+Uit0I9ZBQVqK09hk/SQzKa2MUqUpXaVa7LOFRg1TSSr3lVxGOk6c0SQ==",
+          "requires": {
+            "@protobufjs/aspromise": "^1.1.2",
+            "@protobufjs/base64": "^1.1.2",
+            "@protobufjs/codegen": "^2.0.4",
+            "@protobufjs/eventemitter": "^1.1.0",
+            "@protobufjs/fetch": "^1.1.0",
+            "@protobufjs/float": "^1.0.2",
+            "@protobufjs/inquire": "^1.1.0",
+            "@protobufjs/path": "^1.1.2",
+            "@protobufjs/pool": "^1.1.0",
+            "@protobufjs/utf8": "^1.1.0",
+            "@types/long": "^4.0.1",
+            "@types/node": "^13.7.0",
+            "long": "^4.0.0"
+          }
+        },
+        "source-map": {
+          "version": "0.7.3",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
+          "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ=="
+        }
+      }
+    },
+    "prelude-ls": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+      "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+      "dev": true
+    },
+    "prepend-http": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz",
+      "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=",
+      "dev": true
+    },
+    "prettier": {
+      "version": "2.3.2",
+      "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.2.tgz",
+      "integrity": "sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ==",
+      "dev": true
+    },
+    "prettier-linter-helpers": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
+      "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
+      "dev": true,
+      "requires": {
+        "fast-diff": "^1.1.2"
+      }
+    },
+    "pretty-ms": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-7.0.1.tgz",
+      "integrity": "sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q==",
+      "requires": {
+        "parse-ms": "^2.1.0"
+      }
+    },
+    "process-nextick-args": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
+      "integrity": "sha1-o31zL0JxtKsa0HDTVQjoKQeI/6o="
+    },
+    "progress": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
+      "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
+      "dev": true
+    },
+    "prom-client": {
+      "version": "11.5.3",
+      "resolved": "https://registry.npmjs.org/prom-client/-/prom-client-11.5.3.tgz",
+      "integrity": "sha512-iz22FmTbtkyL2vt0MdDFY+kWof+S9UB/NACxSn2aJcewtw+EERsen0urSkZ2WrHseNdydsvcxCTAnPcSMZZv4Q==",
+      "requires": {
+        "tdigest": "^0.1.1"
+      }
+    },
+    "protobufjs": {
+      "version": "6.8.8",
+      "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.8.8.tgz",
+      "integrity": "sha512-AAmHtD5pXgZfi7GMpllpO3q1Xw1OYldr+dMUlAnffGTAhqkg72WdmSY71uKBF/JuyiKs8psYbtKrhi0ASCD8qw==",
+      "requires": {
+        "@protobufjs/aspromise": "^1.1.2",
+        "@protobufjs/base64": "^1.1.2",
+        "@protobufjs/codegen": "^2.0.4",
+        "@protobufjs/eventemitter": "^1.1.0",
+        "@protobufjs/fetch": "^1.1.0",
+        "@protobufjs/float": "^1.0.2",
+        "@protobufjs/inquire": "^1.1.0",
+        "@protobufjs/path": "^1.1.2",
+        "@protobufjs/pool": "^1.1.0",
+        "@protobufjs/utf8": "^1.1.0",
+        "@types/long": "^4.0.0",
+        "@types/node": "^10.1.0",
+        "long": "^4.0.0"
+      }
+    },
+    "proxy-addr": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz",
+      "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==",
+      "requires": {
+        "forwarded": "~0.1.2",
+        "ipaddr.js": "1.9.1"
+      }
+    },
+    "psl": {
+      "version": "1.7.0",
+      "resolved": "https://registry.npmjs.org/psl/-/psl-1.7.0.tgz",
+      "integrity": "sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ=="
+    },
+    "pstree.remy": {
+      "version": "1.1.8",
+      "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz",
+      "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==",
+      "dev": true
+    },
+    "pump": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
+      "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
+      "requires": {
+        "end-of-stream": "^1.1.0",
+        "once": "^1.3.1"
+      }
+    },
+    "pumpify": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-2.0.1.tgz",
+      "integrity": "sha512-m7KOje7jZxrmutanlkS1daj1dS6z6BgslzOXmcSEpIlCxM3VJH7lG5QLeck/6hgF6F4crFf01UtQmNsJfweTAw==",
+      "requires": {
+        "duplexify": "^4.1.1",
+        "inherits": "^2.0.3",
+        "pump": "^3.0.0"
+      },
+      "dependencies": {
+        "duplexify": {
+          "version": "4.1.1",
+          "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.1.tgz",
+          "integrity": "sha512-DY3xVEmVHTv1wSzKNbwoU6nVjzI369Y6sPoqfYr0/xlx3IdX2n94xIszTcjPO8W8ZIv0Wb0PXNcjuZyT4wiICA==",
+          "requires": {
+            "end-of-stream": "^1.4.1",
+            "inherits": "^2.0.3",
+            "readable-stream": "^3.1.1",
+            "stream-shift": "^1.0.0"
+          }
+        },
+        "readable-stream": {
+          "version": "3.6.0",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+          "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+          "requires": {
+            "inherits": "^2.0.3",
+            "string_decoder": "^1.1.1",
+            "util-deprecate": "^1.0.1"
+          }
+        }
+      }
+    },
+    "punycode": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+      "integrity": "sha1-tYsBCsQMIsVldhbI0sLALHv0eew="
+    },
+    "pupa": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz",
+      "integrity": "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==",
+      "dev": true,
+      "requires": {
+        "escape-goat": "^2.0.0"
+      }
+    },
+    "qs": {
+      "version": "6.7.0",
+      "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
+      "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
+    },
+    "ramda": {
+      "version": "0.27.1",
+      "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.27.1.tgz",
+      "integrity": "sha512-PgIdVpn5y5Yns8vqb8FzBUEYn98V3xcPgawAkkgj0YJ0qDsnHCiNmZYfOGMgOvoB0eWFLpYbhxUR3mxfDIMvpw==",
+      "dev": true
+    },
+    "randombytes": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
+      "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
+      "dev": true,
+      "requires": {
+        "safe-buffer": "^5.1.0"
+      }
+    },
+    "range-parser": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+      "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
+    },
+    "raven": {
+      "version": "2.6.4",
+      "resolved": "https://registry.npmjs.org/raven/-/raven-2.6.4.tgz",
+      "integrity": "sha512-6PQdfC4+DQSFncowthLf+B6Hr0JpPsFBgTVYTAOq7tCmx/kR4SXbeawtPch20+3QfUcQDoJBLjWW1ybvZ4kXTw==",
+      "requires": {
+        "cookie": "0.3.1",
+        "md5": "^2.2.1",
+        "stack-trace": "0.0.10",
+        "timed-out": "4.0.1",
+        "uuid": "3.3.2"
+      },
+      "dependencies": {
+        "stack-trace": {
+          "version": "0.0.10",
+          "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
+          "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA="
+        },
+        "uuid": {
+          "version": "3.3.2",
+          "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
+          "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA=="
+        }
+      }
+    },
+    "raw-body": {
+      "version": "2.4.0",
+      "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
+      "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
+      "requires": {
+        "bytes": "3.1.0",
+        "http-errors": "1.7.2",
+        "iconv-lite": "0.4.24",
+        "unpipe": "1.0.0"
+      },
+      "dependencies": {
+        "iconv-lite": {
+          "version": "0.4.24",
+          "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+          "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+          "requires": {
+            "safer-buffer": ">= 2.1.2 < 3"
+          }
+        }
+      }
+    },
+    "rc": {
+      "version": "1.2.8",
+      "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
+      "integrity": "sha1-zZJL9SAKB1uDwYjNa54hG3/A0+0=",
+      "requires": {
+        "deep-extend": "^0.6.0",
+        "ini": "~1.3.0",
+        "minimist": "^1.2.0",
+        "strip-json-comments": "~2.0.1"
+      },
+      "dependencies": {
+        "minimist": {
+          "version": "1.2.0",
+          "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+          "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
+        }
+      }
+    },
+    "read-pkg": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz",
+      "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=",
+      "dev": true,
+      "requires": {
+        "load-json-file": "^4.0.0",
+        "normalize-package-data": "^2.3.2",
+        "path-type": "^3.0.0"
+      }
+    },
+    "read-pkg-up": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz",
+      "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=",
+      "dev": true,
+      "requires": {
+        "find-up": "^2.0.0",
+        "read-pkg": "^3.0.0"
+      }
+    },
+    "readable-stream": {
+      "version": "2.3.6",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+      "integrity": "sha1-sRwn2IuP8fvgcGQ8+UsMea4bCq8=",
+      "requires": {
+        "core-util-is": "~1.0.0",
+        "inherits": "~2.0.3",
+        "isarray": "~1.0.0",
+        "process-nextick-args": "~2.0.0",
+        "safe-buffer": "~5.1.1",
+        "string_decoder": "~1.1.1",
+        "util-deprecate": "~1.0.1"
+      }
+    },
+    "readdirp": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.2.0.tgz",
+      "integrity": "sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ==",
+      "dev": true,
+      "requires": {
+        "picomatch": "^2.0.4"
+      }
+    },
+    "regexpp": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz",
+      "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==",
+      "dev": true
+    },
+    "registry-auth-token": {
+      "version": "4.2.1",
+      "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz",
+      "integrity": "sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw==",
+      "dev": true,
+      "requires": {
+        "rc": "^1.2.8"
+      }
+    },
+    "registry-url": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz",
+      "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==",
+      "dev": true,
+      "requires": {
+        "rc": "^1.2.8"
+      }
+    },
+    "request": {
+      "version": "2.88.2",
+      "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
+      "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==",
+      "requires": {
+        "aws-sign2": "~0.7.0",
+        "aws4": "^1.8.0",
+        "caseless": "~0.12.0",
+        "combined-stream": "~1.0.6",
+        "extend": "~3.0.2",
+        "forever-agent": "~0.6.1",
+        "form-data": "~2.3.2",
+        "har-validator": "~5.1.3",
+        "http-signature": "~1.2.0",
+        "is-typedarray": "~1.0.0",
+        "isstream": "~0.1.2",
+        "json-stringify-safe": "~5.0.1",
+        "mime-types": "~2.1.19",
+        "oauth-sign": "~0.9.0",
+        "performance-now": "^2.1.0",
+        "qs": "~6.5.2",
+        "safe-buffer": "^5.1.2",
+        "tough-cookie": "~2.5.0",
+        "tunnel-agent": "^0.6.0",
+        "uuid": "^3.3.2"
+      },
+      "dependencies": {
+        "qs": {
+          "version": "6.5.2",
+          "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
+          "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
+        }
+      }
+    },
+    "require-directory": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+      "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
+      "dev": true
+    },
+    "require-from-string": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
+      "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+      "dev": true
+    },
+    "require-in-the-middle": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-5.1.0.tgz",
+      "integrity": "sha512-M2rLKVupQfJ5lf9OvqFGIT+9iVLnTmjgbOmpil12hiSQNn5zJTKGPoIisETNjfK+09vP3rpm1zJajmErpr2sEQ==",
+      "requires": {
+        "debug": "^4.1.1",
+        "module-details-from-path": "^1.0.3",
+        "resolve": "^1.12.0"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "4.3.1",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
+          "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
+          "requires": {
+            "ms": "2.1.2"
+          }
+        },
+        "ms": {
+          "version": "2.1.2",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+          "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+        }
+      }
+    },
+    "require-like": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/require-like/-/require-like-0.1.2.tgz",
+      "integrity": "sha1-rW8wwTvs15cBDEaK+ndcDAprR/o=",
+      "dev": true
+    },
+    "resolve": {
+      "version": "1.12.0",
+      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz",
+      "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==",
+      "requires": {
+        "path-parse": "^1.0.6"
+      }
+    },
+    "resolve-from": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+      "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+      "dev": true
+    },
+    "responselike": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz",
+      "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=",
+      "dev": true,
+      "requires": {
+        "lowercase-keys": "^1.0.0"
+      }
+    },
+    "retry-as-promised": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-3.2.0.tgz",
+      "integrity": "sha512-CybGs60B7oYU/qSQ6kuaFmRd9sTZ6oXSc0toqePvV74Ac6/IFZSI1ReFQmtCN+uvW1Mtqdwpvt/LGOiCBAY2Mg==",
+      "requires": {
+        "any-promise": "^1.3.0"
+      }
+    },
+    "retry-request": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-4.1.1.tgz",
+      "integrity": "sha512-BINDzVtLI2BDukjWmjAIRZ0oglnCAkpP2vQjM3jdLhmT62h0xnQgciPwBRDAvHqpkPT2Wo1XuUyLyn6nbGrZQQ==",
+      "requires": {
+        "debug": "^4.1.1",
+        "through2": "^3.0.1"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "4.1.1",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+          "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+          "requires": {
+            "ms": "^2.1.1"
+          }
+        },
+        "ms": {
+          "version": "2.1.2",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+          "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+        }
+      }
+    },
+    "rimraf": {
+      "version": "2.6.2",
+      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz",
+      "integrity": "sha1-LtgVDSShbqhlHm1u8PR8QVjOejY=",
+      "requires": {
+        "glob": "^7.0.5"
+      }
+    },
+    "safe-buffer": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+      "integrity": "sha1-mR7GnSluAxN0fVm9/St0XDX4go0="
+    },
+    "safe-json-stringify": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz",
+      "integrity": "sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg==",
+      "optional": true
+    },
+    "safer-buffer": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+      "integrity": "sha1-RPoWGwGHuVSd2Eu5GAL5vYOFzWo="
+    },
+    "sandboxed-module": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/sandboxed-module/-/sandboxed-module-2.0.3.tgz",
+      "integrity": "sha1-x+VFkzm7y6KMUwPusz9ug4e/upY=",
+      "dev": true,
+      "requires": {
+        "require-like": "0.1.2",
+        "stack-trace": "0.0.9"
+      }
+    },
+    "sax": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
+      "integrity": "sha1-KBYjTiN4vdxOU1T6tcqold9xANk="
+    },
+    "semver": {
+      "version": "5.6.0",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz",
+      "integrity": "sha1-fnQlb7qknHWqfHogXMInmcrIAAQ="
+    },
+    "semver-diff": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz",
+      "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==",
+      "dev": true,
+      "requires": {
+        "semver": "^6.3.0"
+      },
+      "dependencies": {
+        "semver": {
+          "version": "6.3.0",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+          "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+          "dev": true
+        }
+      }
+    },
+    "send": {
+      "version": "0.17.1",
+      "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
+      "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
+      "requires": {
+        "debug": "2.6.9",
+        "depd": "~1.1.2",
+        "destroy": "~1.0.4",
+        "encodeurl": "~1.0.2",
+        "escape-html": "~1.0.3",
+        "etag": "~1.8.1",
+        "fresh": "0.5.2",
+        "http-errors": "~1.7.2",
+        "mime": "1.6.0",
+        "ms": "2.1.1",
+        "on-finished": "~2.3.0",
+        "range-parser": "~1.2.1",
+        "statuses": "~1.5.0"
+      },
+      "dependencies": {
+        "ms": {
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+          "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
+        }
+      }
+    },
+    "sequelize": {
+      "version": "5.21.5",
+      "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-5.21.5.tgz",
+      "integrity": "sha512-n9hR5K4uQGmBGK/Y/iqewCeSFmKVsd0TRnh0tfoLoAkmXbKC4tpeK96RhKs7d+TTMtrJlgt2TNLVBaAxEwC4iw==",
+      "requires": {
+        "bluebird": "^3.5.0",
+        "cls-bluebird": "^2.1.0",
+        "debug": "^4.1.1",
+        "dottie": "^2.0.0",
+        "inflection": "1.12.0",
+        "lodash": "^4.17.15",
+        "moment": "^2.24.0",
+        "moment-timezone": "^0.5.21",
+        "retry-as-promised": "^3.2.0",
+        "semver": "^6.3.0",
+        "sequelize-pool": "^2.3.0",
+        "toposort-class": "^1.0.1",
+        "uuid": "^3.3.3",
+        "validator": "^10.11.0",
+        "wkx": "^0.4.8"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "4.1.1",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+          "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+          "requires": {
+            "ms": "^2.1.1"
+          }
+        },
+        "moment": {
+          "version": "2.24.0",
+          "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz",
+          "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg=="
+        },
+        "ms": {
+          "version": "2.1.2",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+          "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+        },
+        "semver": {
+          "version": "6.3.0",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+          "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
+        }
+      }
+    },
+    "sequelize-pool": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/sequelize-pool/-/sequelize-pool-2.3.0.tgz",
+      "integrity": "sha512-Ibz08vnXvkZ8LJTiUOxRcj1Ckdn7qafNZ2t59jYHMX1VIebTAOYefWdRYFt6z6+hy52WGthAHAoLc9hvk3onqA=="
+    },
+    "serialize-javascript": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz",
+      "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==",
+      "dev": true,
+      "requires": {
+        "randombytes": "^2.1.0"
+      }
+    },
+    "serve-static": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
+      "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==",
+      "requires": {
+        "encodeurl": "~1.0.2",
+        "escape-html": "~1.0.3",
+        "parseurl": "~1.3.3",
+        "send": "0.17.1"
+      }
+    },
+    "set-blocking": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
+      "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
+    },
+    "setprototypeof": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
+      "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
+    },
+    "shebang-command": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+      "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+      "dev": true,
+      "requires": {
+        "shebang-regex": "^3.0.0"
+      }
+    },
+    "shebang-regex": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+      "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+      "dev": true
+    },
+    "shimmer": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.0.tgz",
+      "integrity": "sha1-+Wb3VVeJdj502IQRk2haXnhzZmU="
+    },
+    "signal-exit": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
+      "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0="
+    },
+    "sinon": {
+      "version": "9.0.1",
+      "resolved": "https://registry.npmjs.org/sinon/-/sinon-9.0.1.tgz",
+      "integrity": "sha512-iTTyiQo5T94jrOx7X7QLBZyucUJ2WvL9J13+96HMfm2CGoJYbIPqRfl6wgNcqmzk0DI28jeGx5bUTXizkrqBmg==",
+      "dev": true,
+      "requires": {
+        "@sinonjs/commons": "^1.7.0",
+        "@sinonjs/fake-timers": "^6.0.0",
+        "@sinonjs/formatio": "^5.0.1",
+        "@sinonjs/samsam": "^5.0.3",
+        "diff": "^4.0.2",
+        "nise": "^4.0.1",
+        "supports-color": "^7.1.0"
+      },
+      "dependencies": {
+        "diff": {
+          "version": "4.0.2",
+          "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
+          "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
+          "dev": true
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
+        "supports-color": {
+          "version": "7.1.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
+          "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
+        }
+      }
+    },
+    "slice-ansi": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz",
+      "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==",
+      "dev": true,
+      "requires": {
+        "ansi-styles": "^4.0.0",
+        "astral-regex": "^2.0.0"
+      },
+      "dependencies": {
+        "ansi-styles": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+          "dev": true
+        }
+      }
+    },
+    "snakecase-keys": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/snakecase-keys/-/snakecase-keys-3.2.0.tgz",
+      "integrity": "sha512-WTJ0NhCH/37J+PU3fuz0x5b6TvtWQChTcKPOndWoUy0pteKOe0hrHMzSRsJOWSIP48EQkzUEsgQPmrG3W8pFNQ==",
+      "requires": {
+        "map-obj": "^4.0.0",
+        "to-snake-case": "^1.0.0"
+      }
+    },
+    "source-map": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
+    },
+    "source-map-support": {
+      "version": "0.5.19",
+      "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz",
+      "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==",
+      "requires": {
+        "buffer-from": "^1.0.0",
+        "source-map": "^0.6.0"
+      }
+    },
+    "spdx-correct": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz",
+      "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==",
+      "dev": true,
+      "requires": {
+        "spdx-expression-parse": "^3.0.0",
+        "spdx-license-ids": "^3.0.0"
+      }
+    },
+    "spdx-exceptions": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz",
+      "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==",
+      "dev": true
+    },
+    "spdx-expression-parse": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
+      "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
+      "dev": true,
+      "requires": {
+        "spdx-exceptions": "^2.1.0",
+        "spdx-license-ids": "^3.0.0"
+      }
+    },
+    "spdx-license-ids": {
+      "version": "3.0.9",
+      "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.9.tgz",
+      "integrity": "sha512-Ki212dKK4ogX+xDo4CtOZBVIwhsKBEfsEEcwmJfLQzirgc2jIWdzg40Unxz/HzEUqM1WFzVlQSMF9kZZ2HboLQ==",
+      "dev": true
+    },
+    "split": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz",
+      "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==",
+      "requires": {
+        "through": "2"
+      }
+    },
+    "split-ca": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/split-ca/-/split-ca-1.0.1.tgz",
+      "integrity": "sha1-bIOv82kvphJW4M0ZfgXp3hV2kaY="
+    },
+    "sprintf-js": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+      "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
+      "dev": true
+    },
+    "sqlite3": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-4.1.1.tgz",
+      "integrity": "sha512-CvT5XY+MWnn0HkbwVKJAyWEMfzpAPwnTiB3TobA5Mri44SrTovmmh499NPQP+gatkeOipqPlBLel7rn4E/PCQg==",
+      "requires": {
+        "nan": "^2.12.1",
+        "node-pre-gyp": "^0.11.0",
+        "request": "^2.87.0"
+      }
+    },
+    "sqlstring": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.1.tgz",
+      "integrity": "sha1-R1OT/56RR5rqYtyvDKPRSYOn+0A="
+    },
+    "ssh2": {
+      "version": "0.8.8",
+      "resolved": "https://registry.npmjs.org/ssh2/-/ssh2-0.8.8.tgz",
+      "integrity": "sha512-egJVQkf3sbjECTY6rCeg8rgV/fab6S/7E5kpYqHT3Fe/YpfJbLYeA1qTcB2d+LRUUAjqKi7rlbfWkaP66YdpAQ==",
+      "requires": {
+        "ssh2-streams": "~0.4.9"
+      }
+    },
+    "ssh2-streams": {
+      "version": "0.4.9",
+      "resolved": "https://registry.npmjs.org/ssh2-streams/-/ssh2-streams-0.4.9.tgz",
+      "integrity": "sha512-glMQKeYKuA+rLaH16fJC3nZMV1BWklbxuYCR4C5/LlBSf2yaoNRpPU7Ul702xsi5nvYjIx9XDkKBJwrBjkDynw==",
+      "requires": {
+        "asn1": "~0.2.0",
+        "bcrypt-pbkdf": "^1.0.2",
+        "streamsearch": "~0.1.2"
+      }
+    },
+    "sshpk": {
+      "version": "1.16.1",
+      "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz",
+      "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==",
+      "requires": {
+        "asn1": "~0.2.3",
+        "assert-plus": "^1.0.0",
+        "bcrypt-pbkdf": "^1.0.0",
+        "dashdash": "^1.12.0",
+        "ecc-jsbn": "~0.1.1",
+        "getpass": "^0.1.1",
+        "jsbn": "~0.1.0",
+        "safer-buffer": "^2.0.2",
+        "tweetnacl": "~0.14.0"
+      }
+    },
+    "stack-trace": {
+      "version": "0.0.9",
+      "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.9.tgz",
+      "integrity": "sha1-qPbq7KkGdMMz58Q5U/J1tFFRBpU=",
+      "dev": true
+    },
+    "statuses": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
+      "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
+    },
+    "stream-events": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz",
+      "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==",
+      "requires": {
+        "stubs": "^3.0.0"
+      }
+    },
+    "stream-shift": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz",
+      "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI="
+    },
+    "streamsearch": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz",
+      "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo="
+    },
+    "string-width": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
+      "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+      "requires": {
+        "code-point-at": "^1.0.0",
+        "is-fullwidth-code-point": "^1.0.0",
+        "strip-ansi": "^3.0.0"
+      }
+    },
+    "string.prototype.trimend": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz",
+      "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==",
+      "dev": true,
+      "requires": {
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.3"
+      }
+    },
+    "string.prototype.trimstart": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz",
+      "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==",
+      "dev": true,
+      "requires": {
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.3"
+      }
+    },
+    "string_decoder": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+      "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=",
+      "requires": {
+        "safe-buffer": "~5.1.0"
+      }
+    },
+    "strip-ansi": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+      "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+      "requires": {
+        "ansi-regex": "^2.0.0"
+      }
+    },
+    "strip-bom": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+      "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
+      "dev": true
+    },
+    "strip-json-comments": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
+      "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo="
+    },
+    "stubs": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz",
+      "integrity": "sha1-6NK6H6nJBXAwPAMLaQD31fiavls="
+    },
+    "supports-color": {
+      "version": "5.5.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+      "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+      "dev": true,
+      "requires": {
+        "has-flag": "^3.0.0"
+      }
+    },
+    "table": {
+      "version": "6.7.1",
+      "resolved": "https://registry.npmjs.org/table/-/table-6.7.1.tgz",
+      "integrity": "sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg==",
+      "dev": true,
+      "requires": {
+        "ajv": "^8.0.1",
+        "lodash.clonedeep": "^4.5.0",
+        "lodash.truncate": "^4.4.2",
+        "slice-ansi": "^4.0.0"
+      },
+      "dependencies": {
+        "ajv": {
+          "version": "8.6.1",
+          "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.1.tgz",
+          "integrity": "sha512-42VLtQUOLefAvKFAQIxIZDaThq6om/PrfP0CYk3/vn+y4BMNkKnbli8ON2QCiHov4KkzOSJ/xSoBJdayiiYvVQ==",
+          "dev": true,
+          "requires": {
+            "fast-deep-equal": "^3.1.1",
+            "require-from-string": "^2.0.2",
+            "uri-js": "^4.2.2"
+          }
+        }
+      }
+    },
+    "tar": {
+      "version": "4.4.8",
+      "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.8.tgz",
+      "integrity": "sha1-sZ7sP94qluZGZt+f20DFyhvDdH0=",
+      "requires": {
+        "chownr": "^1.1.1",
+        "fs-minipass": "^1.2.5",
+        "minipass": "^2.3.4",
+        "minizlib": "^1.1.1",
+        "mkdirp": "^0.5.0",
+        "safe-buffer": "^5.1.2",
+        "yallist": "^3.0.2"
+      }
+    },
+    "tar-fs": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.0.0.tgz",
+      "integrity": "sha512-vaY0obB6Om/fso8a8vakQBzwholQ7v5+uy+tF3Ozvxv1KNezmVQAiWtcNmMHFSFPqL3dJA8ha6gdtFbfX9mcxA==",
+      "requires": {
+        "chownr": "^1.1.1",
+        "mkdirp": "^0.5.1",
+        "pump": "^3.0.0",
+        "tar-stream": "^2.0.0"
+      }
+    },
+    "tar-stream": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.2.tgz",
+      "integrity": "sha512-UaF6FoJ32WqALZGOIAApXx+OdxhekNMChu6axLJR85zMMjXKWFGjbIRe+J6P4UnRGg9rAwWvbTT0oI7hD/Un7Q==",
+      "requires": {
+        "bl": "^4.0.1",
+        "end-of-stream": "^1.4.1",
+        "fs-constants": "^1.0.0",
+        "inherits": "^2.0.3",
+        "readable-stream": "^3.1.1"
+      },
+      "dependencies": {
+        "readable-stream": {
+          "version": "3.6.0",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+          "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+          "requires": {
+            "inherits": "^2.0.3",
+            "string_decoder": "^1.1.1",
+            "util-deprecate": "^1.0.1"
+          }
+        }
+      }
+    },
+    "tdigest": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/tdigest/-/tdigest-0.1.1.tgz",
+      "integrity": "sha1-Ljyyw56kSeVdHmzZEReszKRYgCE=",
+      "requires": {
+        "bintrees": "1.0.1"
+      }
+    },
+    "teeny-request": {
+      "version": "6.0.3",
+      "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-6.0.3.tgz",
+      "integrity": "sha512-TZG/dfd2r6yeji19es1cUIwAlVD8y+/svB1kAC2Y0bjEyysrfbO8EZvJBRwIE6WkwmUoB7uvWLwTIhJbMXZ1Dw==",
+      "requires": {
+        "http-proxy-agent": "^4.0.0",
+        "https-proxy-agent": "^5.0.0",
+        "node-fetch": "^2.2.0",
+        "stream-events": "^1.0.5",
+        "uuid": "^7.0.0"
+      },
+      "dependencies": {
+        "uuid": {
+          "version": "7.0.3",
+          "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz",
+          "integrity": "sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg=="
+        }
+      }
+    },
+    "term-size": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz",
+      "integrity": "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==",
+      "dev": true
+    },
+    "text-table": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+      "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
+      "dev": true
+    },
+    "through": {
+      "version": "2.3.8",
+      "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
+      "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
+    },
+    "through2": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz",
+      "integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==",
+      "requires": {
+        "readable-stream": "2 || 3"
+      }
+    },
+    "timed-out": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz",
+      "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8="
+    },
+    "timekeeper": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/timekeeper/-/timekeeper-2.2.0.tgz",
+      "integrity": "sha512-W3AmPTJWZkRwu+iSNxPIsLZ2ByADsOLbbLxe46UJyWj3mlYLlwucKiq+/dPm0l9wTzqoF3/2PH0AGFCebjq23A==",
+      "dev": true
+    },
+    "to-no-case": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/to-no-case/-/to-no-case-1.0.2.tgz",
+      "integrity": "sha1-xyKQcWTvaxeBMsjmmTAhLRtKoWo="
+    },
+    "to-readable-stream": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz",
+      "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==",
+      "dev": true
+    },
+    "to-regex-range": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+      "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+      "dev": true,
+      "requires": {
+        "is-number": "^7.0.0"
+      }
+    },
+    "to-snake-case": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/to-snake-case/-/to-snake-case-1.0.0.tgz",
+      "integrity": "sha1-znRpE4l5RgGah+Yu366upMYIq4w=",
+      "requires": {
+        "to-space-case": "^1.0.0"
+      }
+    },
+    "to-space-case": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/to-space-case/-/to-space-case-1.0.0.tgz",
+      "integrity": "sha1-sFLar7Gysp3HcM6gFj5ewOvJ/Bc=",
+      "requires": {
+        "to-no-case": "^1.0.0"
+      }
+    },
+    "toidentifier": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
+      "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
+    },
+    "toposort-class": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/toposort-class/-/toposort-class-1.0.1.tgz",
+      "integrity": "sha1-f/0feMi+KMO6Rc1OGj9e4ZO9mYg="
+    },
+    "touch": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz",
+      "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==",
+      "dev": true,
+      "requires": {
+        "nopt": "~1.0.10"
+      },
+      "dependencies": {
+        "nopt": {
+          "version": "1.0.10",
+          "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz",
+          "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=",
+          "dev": true,
+          "requires": {
+            "abbrev": "1"
+          }
+        }
+      }
+    },
+    "tough-cookie": {
+      "version": "2.5.0",
+      "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
+      "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
+      "requires": {
+        "psl": "^1.1.28",
+        "punycode": "^2.1.1"
+      }
+    },
+    "tsconfig-paths": {
+      "version": "3.10.1",
+      "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.10.1.tgz",
+      "integrity": "sha512-rETidPDgCpltxF7MjBZlAFPUHv5aHH2MymyPvh+vEyWAED4Eb/WeMbsnD/JDr4OKPOA1TssDHgIcpTN5Kh0p6Q==",
+      "dev": true,
+      "requires": {
+        "json5": "^2.2.0",
+        "minimist": "^1.2.0",
+        "strip-bom": "^3.0.0"
+      },
+      "dependencies": {
+        "minimist": {
+          "version": "1.2.5",
+          "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
+          "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
+          "dev": true
+        }
+      }
+    },
+    "tunnel-agent": {
+      "version": "0.6.0",
+      "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
+      "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
+      "requires": {
+        "safe-buffer": "^5.0.1"
+      }
+    },
+    "tweetnacl": {
+      "version": "0.14.5",
+      "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
+      "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
+    },
+    "type-check": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+      "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+      "dev": true,
+      "requires": {
+        "prelude-ls": "^1.2.1"
+      }
+    },
+    "type-detect": {
+      "version": "4.0.8",
+      "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
+      "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
+      "dev": true
+    },
+    "type-fest": {
+      "version": "0.8.1",
+      "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
+      "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==",
+      "dev": true
+    },
+    "type-is": {
+      "version": "1.6.18",
+      "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
+      "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
+      "requires": {
+        "media-typer": "0.3.0",
+        "mime-types": "~2.1.24"
+      }
+    },
+    "typedarray": {
+      "version": "0.0.6",
+      "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
+      "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
+    },
+    "typedarray-to-buffer": {
+      "version": "3.1.5",
+      "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz",
+      "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==",
+      "dev": true,
+      "requires": {
+        "is-typedarray": "^1.0.0"
+      }
+    },
+    "unbox-primitive": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz",
+      "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==",
+      "dev": true,
+      "requires": {
+        "function-bind": "^1.1.1",
+        "has-bigints": "^1.0.1",
+        "has-symbols": "^1.0.2",
+        "which-boxed-primitive": "^1.0.2"
+      },
+      "dependencies": {
+        "has-symbols": {
+          "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz",
+          "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==",
+          "dev": true
+        }
+      }
+    },
+    "undefsafe": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.3.tgz",
+      "integrity": "sha512-nrXZwwXrD/T/JXeygJqdCO6NZZ1L66HrxM/Z7mIq2oPanoN0F1nLx3lwJMu6AwJY69hdixaFQOuoYsMjE5/C2A==",
+      "dev": true,
+      "requires": {
+        "debug": "^2.2.0"
+      }
+    },
+    "underscore": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz",
+      "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag="
+    },
+    "unique-string": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz",
+      "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==",
+      "dev": true,
+      "requires": {
+        "crypto-random-string": "^2.0.0"
+      }
+    },
+    "universalify": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
+      "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ=="
+    },
+    "unpipe": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+      "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
+    },
+    "update-notifier": {
+      "version": "4.1.3",
+      "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-4.1.3.tgz",
+      "integrity": "sha512-Yld6Z0RyCYGB6ckIjffGOSOmHXj1gMeE7aROz4MG+XMkmixBX4jUngrGXNYz7wPKBmtoD4MnBa2Anu7RSKht/A==",
+      "dev": true,
+      "requires": {
+        "boxen": "^4.2.0",
+        "chalk": "^3.0.0",
+        "configstore": "^5.0.1",
+        "has-yarn": "^2.1.0",
+        "import-lazy": "^2.1.0",
+        "is-ci": "^2.0.0",
+        "is-installed-globally": "^0.3.1",
+        "is-npm": "^4.0.0",
+        "is-yarn-global": "^0.3.0",
+        "latest-version": "^5.0.0",
+        "pupa": "^2.0.1",
+        "semver-diff": "^3.1.1",
+        "xdg-basedir": "^4.0.0"
+      },
+      "dependencies": {
+        "ansi-styles": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+          "requires": {
+            "color-convert": "^2.0.1"
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
+        },
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
+        }
+      }
+    },
+    "uri-js": {
+      "version": "4.2.2",
+      "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
+      "integrity": "sha1-lMVA4f93KVbiKZUHwBCupsiDjrA=",
+      "requires": {
+        "punycode": "^2.1.0"
+      }
+    },
+    "url-parse-lax": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz",
+      "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=",
+      "dev": true,
+      "requires": {
+        "prepend-http": "^2.0.0"
+      }
+    },
+    "util-deprecate": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+      "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
+    },
+    "utils-merge": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
+      "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
+    },
+    "uuid": {
+      "version": "3.3.3",
+      "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz",
+      "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ=="
+    },
+    "v8-compile-cache": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz",
+      "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==",
+      "dev": true
+    },
+    "v8-profiler-node8": {
+      "version": "6.1.1",
+      "resolved": "https://registry.npmjs.org/v8-profiler-node8/-/v8-profiler-node8-6.1.1.tgz",
+      "integrity": "sha512-mKS7TXRRYi70hvbv5c1tk9AbuqNrtbLc+jFLlsZ2TpaC1l5lWryBlDLZKJ1JP6hjSbMEjW1ucjWLSaKsaPnGXg==",
+      "requires": {
+        "nan": "^2.14.0",
+        "node-pre-gyp": "^0.13.0"
+      },
+      "dependencies": {
+        "node-pre-gyp": {
+          "version": "0.13.0",
+          "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.13.0.tgz",
+          "integrity": "sha512-Md1D3xnEne8b/HGVQkZZwV27WUi1ZRuZBij24TNaZwUPU3ZAFtvT6xxJGaUVillfmMKnn5oD1HoGsp2Ftik7SQ==",
+          "requires": {
+            "detect-libc": "^1.0.2",
+            "mkdirp": "^0.5.1",
+            "needle": "^2.2.1",
+            "nopt": "^4.0.1",
+            "npm-packlist": "^1.1.6",
+            "npmlog": "^4.0.2",
+            "rc": "^1.2.7",
+            "rimraf": "^2.6.1",
+            "semver": "^5.3.0",
+            "tar": "^4"
+          }
+        }
+      }
+    },
+    "validate-npm-package-license": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
+      "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
+      "dev": true,
+      "requires": {
+        "spdx-correct": "^3.0.0",
+        "spdx-expression-parse": "^3.0.0"
+      }
+    },
+    "validator": {
+      "version": "10.11.0",
+      "resolved": "https://registry.npmjs.org/validator/-/validator-10.11.0.tgz",
+      "integrity": "sha512-X/p3UZerAIsbBfN/IwahhYaBbY68EN/UQBWHtsbXGT5bfrH/p4NQzUCG1kF/rtKaNpnJ7jAu6NGTdSNtyNIXMw=="
+    },
+    "vary": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+      "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
+    },
+    "verror": {
+      "version": "1.10.0",
+      "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
+      "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
+      "requires": {
+        "assert-plus": "^1.0.0",
+        "core-util-is": "1.0.2",
+        "extsprintf": "^1.2.0"
+      }
+    },
+    "walkdir": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/walkdir/-/walkdir-0.4.1.tgz",
+      "integrity": "sha512-3eBwRyEln6E1MSzcxcVpQIhRG8Q1jLvEqRmCZqS3dsfXEDR/AhOF4d+jHg1qvDCpYaVRZjENPQyrVxAkQqxPgQ=="
+    },
+    "which": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+      "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+      "dev": true,
+      "requires": {
+        "isexe": "^2.0.0"
+      }
+    },
+    "which-boxed-primitive": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz",
+      "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==",
+      "dev": true,
+      "requires": {
+        "is-bigint": "^1.0.1",
+        "is-boolean-object": "^1.1.0",
+        "is-number-object": "^1.0.4",
+        "is-string": "^1.0.5",
+        "is-symbol": "^1.0.3"
+      }
+    },
+    "wide-align": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
+      "integrity": "sha1-rgdOa9wMFKQx6ATmJFScYzsABFc=",
+      "requires": {
+        "string-width": "^1.0.2 || 2"
+      }
+    },
+    "widest-line": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz",
+      "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==",
+      "dev": true,
+      "requires": {
+        "string-width": "^4.0.0"
+      },
+      "dependencies": {
+        "ansi-regex": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
+          "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
+          "dev": true
+        },
+        "is-fullwidth-code-point": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+          "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+          "dev": true
+        },
+        "string-width": {
+          "version": "4.2.2",
+          "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz",
+          "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==",
+          "dev": true,
+          "requires": {
+            "emoji-regex": "^8.0.0",
+            "is-fullwidth-code-point": "^3.0.0",
+            "strip-ansi": "^6.0.0"
+          }
+        },
+        "strip-ansi": {
+          "version": "6.0.0",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
+          "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
+          "dev": true,
+          "requires": {
+            "ansi-regex": "^5.0.0"
+          }
+        }
+      }
+    },
+    "wkx": {
+      "version": "0.4.8",
+      "resolved": "https://registry.npmjs.org/wkx/-/wkx-0.4.8.tgz",
+      "integrity": "sha512-ikPXMM9IR/gy/LwiOSqWlSL3X/J5uk9EO2hHNRXS41eTLXaUFEVw9fn/593jW/tE5tedNg8YjT5HkCa4FqQZyQ==",
+      "requires": {
+        "@types/node": "*"
+      }
+    },
+    "word-wrap": {
+      "version": "1.2.3",
+      "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
+      "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
+      "dev": true
+    },
+    "workerpool": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.0.tgz",
+      "integrity": "sha512-toV7q9rWNYha963Pl/qyeZ6wG+3nnsyvolaNUS8+R5Wtw6qJPTxIlOP1ZSvcGhEJw+l3HMMmtiNo9Gl61G4GVg==",
+      "dev": true
+    },
+    "wrap-ansi": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+      "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+      "dev": true
+    },
+    "wrappy": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+      "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
+    },
+    "wrench": {
+      "version": "1.5.9",
+      "resolved": "https://registry.npmjs.org/wrench/-/wrench-1.5.9.tgz",
+      "integrity": "sha1-QRaRxjqbJTGxcAJnJ5veyiOyFCo="
+    },
+    "write-file-atomic": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz",
+      "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==",
+      "dev": true,
+      "requires": {
+        "imurmurhash": "^0.1.4",
+        "is-typedarray": "^1.0.0",
+        "signal-exit": "^3.0.2",
+        "typedarray-to-buffer": "^3.1.5"
+      }
+    },
+    "xdg-basedir": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz",
+      "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==",
+      "dev": true
+    },
+    "y18n": {
+      "version": "5.0.8",
+      "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+      "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+      "dev": true
+    },
+    "yallist": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz",
+      "integrity": "sha1-tLBJ4xS+VF486AIjbWzSLNkcPek="
+    },
+    "yargs": {
+      "version": "16.2.0",
+      "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
+      "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
+      "dev": true,
+      "requires": {
+        "cliui": "^7.0.2",
+        "escalade": "^3.1.1",
+        "get-caller-file": "^2.0.5",
+        "require-directory": "^2.1.1",
+        "y18n": "^5.0.5",
+        "yargs-parser": "^20.2.2"
+      }
+    },
+    "yargs-parser": {
+      "version": "20.2.4",
+      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz",
+      "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==",
+      "dev": true
+    },
+    "yargs-unparser": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz",
+      "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==",
+      "dev": true,
+      "requires": {
+        "camelcase": "^6.0.0",
+        "decamelize": "^4.0.0",
+        "flat": "^5.0.2",
+        "is-plain-obj": "^2.1.0"
+      },
+      "dependencies": {
+        "camelcase": {
+          "version": "6.2.0",
+          "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz",
+          "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==",
+          "dev": true
+        }
+      }
+    },
+    "yn": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
+      "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q=="
+    },
+    "yocto-queue": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+      "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="
+    }
+  }
+}

+ 67 - 0
services/clsi/package.json

@@ -0,0 +1,67 @@
+{
+  "name": "node-clsi",
+  "description": "A Node.js implementation of the CLSI LaTeX web-API",
+  "version": "0.1.4",
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/sharelatex/clsi-sharelatex.git"
+  },
+  "scripts": {
+    "start": "node $NODE_APP_OPTIONS app.js",
+    "test:acceptance:_run": "mocha --recursive --reporter spec --timeout 15000 --exit $@ test/acceptance/js",
+    "test:acceptance": "npm run test:acceptance:_run -- --grep=$MOCHA_GREP",
+    "test:unit:_run": "mocha --recursive --reporter spec $@ test/unit/js",
+    "test:unit": "npm run test:unit:_run -- --grep=$MOCHA_GREP",
+    "nodemon": "nodemon --config nodemon.json",
+    "lint": "eslint --max-warnings 0 --format unix .",
+    "format": "prettier --list-different $PWD/'**/*.js'",
+    "format:fix": "prettier --write $PWD/'**/*.js'",
+    "lint:fix": "eslint --fix ."
+  },
+  "author": "James Allen <james@sharelatex.com>",
+  "dependencies": {
+    "@overleaf/metrics": "^3.5.1",
+    "@overleaf/o-error": "^3.3.1",
+    "@overleaf/settings": "^2.1.1",
+    "async": "3.2.0",
+    "body-parser": "^1.19.0",
+    "bunyan": "^1.8.15",
+    "diskusage": "^1.1.3",
+    "dockerode": "^3.1.0",
+    "express": "^4.17.1",
+    "fs-extra": "^10.0.0",
+    "heapdump": "^0.3.15",
+    "lockfile": "^1.0.4",
+    "lodash": "^4.17.21",
+    "logger-sharelatex": "^2.2.0",
+    "mysql": "^2.18.1",
+    "p-limit": "^3.1.0",
+    "pdfjs-dist": "^2.7.570",
+    "request": "^2.88.2",
+    "send": "^0.17.1",
+    "sequelize": "^5.21.5",
+    "sqlite3": "^4.1.1",
+    "v8-profiler-node8": "^6.1.1",
+    "wrench": "~1.5.9"
+  },
+  "devDependencies": {
+    "chai": "^4.2.0",
+    "chai-as-promised": "^7.1.1",
+    "eslint": "^7.21.0",
+    "eslint-config-prettier": "^8.1.0",
+    "eslint-config-standard": "^16.0.2",
+    "eslint-plugin-chai-expect": "^2.2.0",
+    "eslint-plugin-chai-friendly": "^0.6.0",
+    "eslint-plugin-import": "^2.22.1",
+    "eslint-plugin-mocha": "^8.0.0",
+    "eslint-plugin-node": "^11.1.0",
+    "eslint-plugin-prettier": "^3.1.2",
+    "eslint-plugin-promise": "^4.2.1",
+    "mocha": "^8.3.2",
+    "nodemon": "^2.0.7",
+    "prettier": "^2.2.1",
+    "sandboxed-module": "^2.0.3",
+    "sinon": "~9.0.1",
+    "timekeeper": "2.2.0"
+  }
+}

+ 3 - 0
services/clsi/patch-texlive-dockerfile

@@ -0,0 +1,3 @@
+FROM quay.io/sharelatex/texlive-full:2017.1
+
+# RUN usermod -u 1001 tex

+ 12 - 0
services/clsi/scripts/demo-pdfjs-Xref.js

@@ -0,0 +1,12 @@
+const fs = require('fs')
+const { parseXrefTable } = require('../app/lib/pdfjs/parseXrefTable')
+
+const pdfPath = process.argv[2]
+
+async function main() {
+  const size = (await fs.promises.stat(pdfPath)).size
+  const xRefEntries = await parseXrefTable(pdfPath, size)
+  console.log('Xref entries', xRefEntries)
+}
+
+main().catch(console.error)

+ 836 - 0
services/clsi/seccomp/clsi-profile.json

@@ -0,0 +1,836 @@
+{
+    "defaultAction": "SCMP_ACT_ERRNO",
+    "architectures": [
+        "SCMP_ARCH_X86_64",
+        "SCMP_ARCH_X86",
+        "SCMP_ARCH_X32"
+    ],
+    "syscalls": [
+        {
+            "name": "access",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "arch_prctl",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "brk",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "chdir",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "chmod",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "clock_getres",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "clock_gettime",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "clock_nanosleep",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "clone",
+            "action": "SCMP_ACT_ALLOW",
+            "args": [
+                {
+                    "index": 0,
+                    "value": 2080505856,
+                    "valueTwo": 0,
+                    "op": "SCMP_CMP_MASKED_EQ"
+                }
+            ]
+        },
+        {
+            "name": "close",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "copy_file_range",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "creat",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "dup",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "dup2",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "dup3",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "execve",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "execveat",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "exit",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "exit_group",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "faccessat",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "fadvise64",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "fadvise64_64",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "fallocate",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "fchdir",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "fchmod",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "fchmodat",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "fcntl",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "fcntl64",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "fdatasync",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "fork",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "fstat",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "fstat64",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "fstatat64",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "fstatfs",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "fstatfs64",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "fsync",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "ftruncate",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "ftruncate64",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "futex",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "futimesat",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "getcpu",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "getcwd",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "getdents",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "getdents64",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "getegid",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "getegid32",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "geteuid",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "geteuid32",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "getgid",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "getgid32",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "getgroups",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "getgroups32",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "getpgid",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "getpgrp",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "getpid",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "getppid",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "getpriority",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "getresgid",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "getresgid32",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "getresuid",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "getresuid32",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "getrlimit",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "get_robust_list",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "getrusage",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "getsid",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "gettid",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "getuid",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "getuid32",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "ioctl",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "kill",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "_llseek",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "lseek",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "lstat",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "lstat64",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "madvise",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "mkdir",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "mkdirat",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "mmap",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "mmap2",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "mprotect",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "mremap",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "munmap",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "newfstatat",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "open",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "openat",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "pause",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "pipe",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "pipe2",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "prctl",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "pread64",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "preadv",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "prlimit64",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "pwrite64",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "pwritev",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "read",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "readlink",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "readlinkat",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "readv",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "rename",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "renameat",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "renameat2",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "restart_syscall",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "rmdir",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "rt_sigaction",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "rt_sigpending",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "rt_sigprocmask",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "rt_sigqueueinfo",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "rt_sigreturn",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "rt_sigsuspend",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "rt_sigtimedwait",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "rt_tgsigqueueinfo",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "sched_getaffinity",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "sched_getparam",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "sched_get_priority_max",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "sched_get_priority_min",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "sched_getscheduler",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "sched_rr_get_interval",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "sched_yield",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "sendfile",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "sendfile64",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "setgroups",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "setgroups32",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "set_robust_list",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "set_tid_address",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "sigaltstack",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "stat",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "stat64",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "statfs",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "statfs64",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "sync",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "sync_file_range",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "syncfs",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "sysinfo",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "tgkill",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "timer_create",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "timer_delete",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "timer_getoverrun",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "timer_gettime",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "timer_settime",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "times",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "tkill",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "truncate",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "truncate64",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "umask",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "uname",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "unlink",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "unlinkat",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "utime",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "utimensat",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "utimes",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "vfork",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "vhangup",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "wait4",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "waitid",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "write",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "writev",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "pread",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "setgid",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "setuid",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "capget",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "capset",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+            "name": "fchown",
+            "action": "SCMP_ACT_ALLOW",
+            "args": []
+        },
+        {
+          "name": "gettimeofday",
+          "action": "SCMP_ACT_ALLOW",
+          "args": []
+        }, {
+          "name": "epoll_pwait",
+          "action": "SCMP_ACT_ALLOW",
+          "args": []
+        }
+    ]
+}

+ 66 - 0
services/clsi/src/synctex.c

@@ -0,0 +1,66 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+
+#include "synctex/synctex_parser.h"
+
+
+void print_usage() {
+  fprintf (stderr, "Usage: synctex code <synctex_file> <filename> <line> <column>\n");
+  fprintf (stderr, "       synctex pdf  <synctex_file> <page> <h> <v>\n");
+}
+
+int main(int argc, char *argv[], char *envp[]) {
+  synctex_scanner_t scanner;
+
+  if (argc < 6 || (strcmp(argv[1], "code") != 0 && strcmp(argv[1], "pdf") != 0))  {
+    print_usage();
+    return EXIT_FAILURE;
+  }
+
+  const char* direction = argv[1];
+  const char* synctex_file = argv[2];
+
+  scanner = synctex_scanner_new_with_output_file(synctex_file, NULL, 1);
+
+  if(!(scanner = synctex_scanner_parse(scanner))) {
+    fprintf (stderr, "Could not parse output file\n");
+    return EXIT_FAILURE;
+  }
+
+  if (strcmp(direction, "code") == 0) {
+    const char* name = argv[3];
+    int line = atoi(argv[4]);
+    int column = atoi(argv[5]);
+
+    if(synctex_display_query(scanner, name, line, column) > 0) {
+      synctex_node_t node;
+      while((node = synctex_next_result(scanner))) {
+        int page     = synctex_node_page(node);
+        float h      = synctex_node_box_visible_h(node);
+        float v      = synctex_node_box_visible_v(node);
+        float width  = synctex_node_box_visible_width(node);
+        float height = synctex_node_box_visible_height(node);
+        printf ("NODE\t%d\t%.2f\t%.2f\t%.2f\t%.2f\n", page, h, v, width, height);
+      }
+    }
+  } else if (strcmp(direction, "pdf") == 0) {
+    int page = atoi(argv[3]);
+    float h = atof(argv[4]);
+    float v = atof(argv[5]);
+
+    if(synctex_edit_query(scanner, page, h, v) > 0) {
+      synctex_node_t node;
+      while((node = synctex_next_result(scanner))) {
+        int tag          = synctex_node_tag(node);
+        const char* name = synctex_scanner_get_name(scanner, tag);
+        int line         = synctex_node_line(node);
+        int column       = synctex_node_column(node);
+        printf ("NODE\t%s\t%d\t%d\n", name, line, column);
+      }
+    }
+  }
+
+  return 0;
+}

+ 4249 - 0
services/clsi/src/synctex/synctex_parser.c

@@ -0,0 +1,4249 @@
+/* 
+Copyright (c) 2008, 2009, 2010 , 2011 jerome DOT laurens AT u-bourgogne DOT fr
+
+This file is part of the SyncTeX package.
+
+Latest Revision: Tue Jun 14 08:23:30 UTC 2011
+
+Version: 1.16
+
+See synctex_parser_readme.txt for more details
+
+License:
+--------
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE
+
+Except as contained in this notice, the name of the copyright holder  
+shall not be used in advertising or otherwise to promote the sale,  
+use or other dealings in this Software without prior written  
+authorization from the copyright holder.
+
+Acknowledgments:
+----------------
+The author received useful remarks from the pdfTeX developers, especially Hahn The Thanh,
+and significant help from XeTeX developer Jonathan Kew
+
+Nota Bene:
+----------
+If you include or use a significant part of the synctex package into a software,
+I would appreciate to be listed as contributor and see "SyncTeX" highlighted.
+
+Version 1
+Thu Jun 19 09:39:21 UTC 2008
+
+*/
+
+/*  We assume that high level application like pdf viewers will want
+ *  to embed this code as is. We assume that they also have locale.h and setlocale.
+ *  For other tools such as TeXLive tools, you must define SYNCTEX_USE_LOCAL_HEADER,
+ *  when building. You also have to create and customize synctex_parser_local.h to fit your system.
+ *  In particular, the HAVE_LOCALE_H and HAVE_SETLOCALE macros should be properly defined.
+ *  With this design, you should not need to edit this file. */
+
+#   if defined(SYNCTEX_USE_LOCAL_HEADER)
+#       include "synctex_parser_local.h"
+#   else
+#       define HAVE_LOCALE_H 1
+#       define HAVE_SETLOCALE 1
+#       if defined(_MSC_VER) 
+#          define SYNCTEX_INLINE __inline
+#       else
+#          define SYNCTEX_INLINE inline
+#       endif
+#   endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+
+#if defined(HAVE_LOCALE_H)
+#include <locale.h>
+#endif
+
+/*  The data is organized in a graph with multiple entries.
+ *  The root object is a scanner, it is created with the contents on a synctex file.
+ *  Each leaf of the tree is a synctex_node_t object.
+ *  There are 3 subtrees, two of them sharing the same leaves.
+ *  The first tree is the list of input records, where input file names are associated with tags.
+ *  The second tree is the box tree as given by TeX when shipping pages out.
+ *  First level objects are sheets, containing boxes, glues, kerns...
+ *  The third tree allows to browse leaves according to tag and line.
+ */
+
+#include "synctex_parser.h"
+#include "synctex_parser_utils.h"
+
+/*  These are the possible extensions of the synctex file */
+const char * synctex_suffix = ".synctex";
+const char * synctex_suffix_gz = ".gz";
+
+/*  each synctex node has a class */
+typedef struct __synctex_class_t _synctex_class_t;
+typedef _synctex_class_t * synctex_class_t;
+
+
+/*  synctex_node_t is a pointer to a node
+ *  _synctex_node is the target of the synctex_node_t pointer
+ *  It is a pseudo object oriented program.
+ *  class is a pointer to the class object the node belongs to.
+ *  implementation is meant to contain the private data of the node
+ *  basically, there are 2 kinds of information: navigation information and
+ *  synctex information. Both will depend on the type of the node,
+ *  thus different nodes will have different private data.
+ *  There is no inheritancy overhead.
+ */
+typedef union _synctex_info_t {
+	int    INT;
+	char * PTR;
+} synctex_info_t;
+
+struct _synctex_node {
+	synctex_class_t class;
+	synctex_info_t * implementation;
+};
+
+/*  Each node of the tree, except the scanner itself belongs to a class.
+ *  The class object is just a struct declaring the owning scanner
+ *  This is a pointer to the scanner as root of the tree.
+ *  The type is used to identify the kind of node.
+ *  The class declares pointers to a creator and a destructor method.
+ *  The log and display fields are used to log and display the node.
+ *  display will also display the child, sibling and parent sibling.
+ *  parent, child and sibling are used to navigate the tree,
+ *  from TeX box hierarchy point of view.
+ *  The friend field points to a method which allows to navigate from friend to friend.
+ *  A friend is a node with very close tag and line numbers.
+ *  Finally, the info field point to a method giving the private node info offset.
+ */
+
+typedef synctex_node_t *(*_synctex_node_getter_t)(synctex_node_t);
+typedef synctex_info_t *(*_synctex_info_getter_t)(synctex_node_t);
+
+struct __synctex_class_t {
+	synctex_scanner_t scanner;
+	int type;
+	synctex_node_t (*new)(synctex_scanner_t scanner);
+	void (*free)(synctex_node_t);
+	void (*log)(synctex_node_t);
+	void (*display)(synctex_node_t);
+	_synctex_node_getter_t parent;
+	_synctex_node_getter_t child;
+	_synctex_node_getter_t sibling;
+	_synctex_node_getter_t friend;
+	_synctex_node_getter_t next_box;
+	_synctex_info_getter_t info;
+};
+
+#	ifdef SYNCTEX_NOTHING
+#       pragma mark -
+#       pragma mark Abstract OBJECTS and METHODS
+#   endif
+
+/*  These macros are shortcuts
+ *  This macro checks if a message can be sent.
+ */
+#   define SYNCTEX_CAN_PERFORM(NODE,SELECTOR)\
+		(NULL!=((((NODE)->class))->SELECTOR))
+
+/*  This macro is some kind of objc_msg_send.
+ *  It takes care of sending the proper message if possible.
+ */
+#   define SYNCTEX_MSG_SEND(NODE,SELECTOR) if (NODE && SYNCTEX_CAN_PERFORM(NODE,SELECTOR)) {\
+		(*((((NODE)->class))->SELECTOR))(NODE);\
+	}
+
+/*  read only safe getter
+ */
+#   define SYNCTEX_GET(NODE,SELECTOR)((NODE && SYNCTEX_CAN_PERFORM(NODE,SELECTOR))?SYNCTEX_GETTER(NODE,SELECTOR)[0]:(NULL))
+
+/*  read/write getter
+ */
+#   define SYNCTEX_GETTER(NODE,SELECTOR)\
+		((synctex_node_t *)((*((((NODE)->class))->SELECTOR))(NODE)))
+
+#   define SYNCTEX_FREE(NODE) SYNCTEX_MSG_SEND(NODE,free);
+
+/*  Parent getter and setter
+ */
+#   define SYNCTEX_PARENT(NODE) SYNCTEX_GET(NODE,parent)
+#   define SYNCTEX_SET_PARENT(NODE,NEW_PARENT) if (NODE && NEW_PARENT && SYNCTEX_CAN_PERFORM(NODE,parent)){\
+		SYNCTEX_GETTER(NODE,parent)[0]=NEW_PARENT;\
+	}
+
+/*  Child getter and setter
+ */
+#   define SYNCTEX_CHILD(NODE) SYNCTEX_GET(NODE,child)
+#   define SYNCTEX_SET_CHILD(NODE,NEW_CHILD) if (NODE && NEW_CHILD){\
+		SYNCTEX_GETTER(NODE,child)[0]=NEW_CHILD;\
+		SYNCTEX_GETTER(NEW_CHILD,parent)[0]=NODE;\
+	}
+
+/*  Sibling getter and setter
+ */
+#   define SYNCTEX_SIBLING(NODE) SYNCTEX_GET(NODE,sibling)
+#   define SYNCTEX_SET_SIBLING(NODE,NEW_SIBLING) if (NODE && NEW_SIBLING) {\
+		SYNCTEX_GETTER(NODE,sibling)[0]=NEW_SIBLING;\
+		if (SYNCTEX_CAN_PERFORM(NEW_SIBLING,parent) && SYNCTEX_CAN_PERFORM(NODE,parent)) {\
+			SYNCTEX_GETTER(NEW_SIBLING,parent)[0]=SYNCTEX_GETTER(NODE,parent)[0];\
+		}\
+	}
+/*  Friend getter and setter. A friend is a kern, math, glue or void box node which tag and line numbers are similar.
+ *  This is a first filter on the nodes that avoids testing all of them.
+ *  Friends are used mainly in forward synchronization aka from source to output.
+ */
+#   define SYNCTEX_FRIEND(NODE) SYNCTEX_GET(NODE,friend)
+#   define SYNCTEX_SET_FRIEND(NODE,NEW_FRIEND) if (NODE && NEW_FRIEND){\
+		SYNCTEX_GETTER(NODE,friend)[0]=NEW_FRIEND;\
+	}
+
+/*  Next box getter and setter. The box tree can be traversed from one horizontal box to the other.
+ *  Navigation starts with the deeper boxes.
+ */
+#   define SYNCTEX_NEXT_HORIZ_BOX(NODE) SYNCTEX_GET(NODE,next_box)
+#   define SYNCTEX_SET_NEXT_HORIZ_BOX(NODE,NEXT_BOX) if (NODE && NEXT_BOX){\
+		SYNCTEX_GETTER(NODE,next_box)[0]=NEXT_BOX;\
+	}
+
+void _synctex_free_node(synctex_node_t node);
+void _synctex_free_leaf(synctex_node_t node);
+
+/*  A node is meant to own its child and sibling.
+ *  It is not owned by its parent, unless it is its first child.
+ *  This destructor is for all nodes with children.
+ */
+void _synctex_free_node(synctex_node_t node) {
+	if (node) {
+		(*((node->class)->sibling))(node);
+		SYNCTEX_FREE(SYNCTEX_SIBLING(node));
+		SYNCTEX_FREE(SYNCTEX_CHILD(node));
+		free(node);
+	}
+	return;
+}
+
+/*  A node is meant to own its child and sibling.
+ *  It is not owned by its parent, unless it is its first child.
+ *  This destructor is for nodes with no child.
+ */
+void _synctex_free_leaf(synctex_node_t node) {
+	if (node) {
+		SYNCTEX_FREE(SYNCTEX_SIBLING(node));
+		free(node);
+	}
+	return;
+}
+#	ifdef	__SYNCTEX_WORK__
+#		include "/usr/include/zlib.h"
+#	else
+#		include <zlib.h>
+#	endif
+
+/*  The synctex scanner is the root object.
+ *  Is is initialized with the contents of a text file or a gzipped file.
+ *  The buffer_? are first used to parse the text.
+ */
+struct __synctex_scanner_t {
+	gzFile file;                  /*  The (possibly compressed) file */
+	char * buffer_cur;            /*  current location in the buffer */
+	char * buffer_start;          /*  start of the buffer */
+	char * buffer_end;            /*  end of the buffer */
+	char * output_fmt;            /*  dvi or pdf, not yet used */
+	char * output;                /*  the output name used to create the scanner */
+	char * synctex;               /*  the .synctex or .synctex.gz name used to create the scanner */
+	int version;                  /*  1, not yet used */
+	struct {
+		unsigned has_parsed:1;		/*  Whether the scanner has parsed its underlying synctex file. */
+		unsigned reserved:sizeof(unsigned)-1;	/*  alignment */
+	} flags;
+	int pre_magnification;        /*  magnification from the synctex preamble */
+	int pre_unit;                 /*  unit from the synctex preamble */
+	int pre_x_offset;             /*  X offste from the synctex preamble */
+	int pre_y_offset;             /*  Y offset from the synctex preamble */
+	int count;                    /*  Number of records, from the synctex postamble */
+	float unit;                   /*  real unit, from synctex preamble or post scriptum */
+	float x_offset;               /*  X offset, from synctex preamble or post scriptum */
+	float y_offset;               /*  Y Offset, from synctex preamble or post scriptum */
+	synctex_node_t sheet;         /*  The first sheet node, its siblings are the other sheet nodes */
+	synctex_node_t input;         /*  The first input node, its siblings are the other input nodes */
+	int number_of_lists;          /*  The number of friend lists */
+	synctex_node_t * lists_of_friends;/*  The friend lists */
+	_synctex_class_t class[synctex_node_number_of_types]; /*  The classes of the nodes of the scanner */
+};
+
+/*  SYNCTEX_CUR, SYNCTEX_START and SYNCTEX_END are convenient shortcuts
+ */
+#   define SYNCTEX_CUR (scanner->buffer_cur)
+#   define SYNCTEX_START (scanner->buffer_start)
+#   define SYNCTEX_END (scanner->buffer_end)
+
+#	ifdef SYNCTEX_NOTHING
+#       pragma mark -
+#       pragma mark OBJECTS, their creators and destructors.
+#   endif
+
+/*  Here, we define the indices for the different informations.
+ *  They are used to declare the size of the implementation.
+ *  For example, if one object uses SYNCTEX_HORIZ_IDX is its size,
+ *  then its info will contain a tag, line, column, horiz but no width nor height nor depth
+ */
+
+/*  The sheet is a first level node.
+ *  It has no parent (the parent is the scanner itself)
+ *  Its sibling points to another sheet.
+ *  Its child points to its first child, in general a box.
+ *  A sheet node contains only one synctex information: the page.
+ *  This is the 1 based page index as given by TeX.
+ */
+/*  The next macros are used to access the node info
+ *  SYNCTEX_INFO(node) points to the first synctex integer or pointer data of node
+ *  SYNCTEX_INFO(node)[index] is the information at index
+ *  for example, the page of a sheet is stored in SYNCTEX_INFO(sheet)[SYNCTEX_PAGE_IDX]
+ */
+#   define SYNCTEX_INFO(NODE) ((*((((NODE)->class))->info))(NODE))
+#   define SYNCTEX_PAGE_IDX 0
+#   define SYNCTEX_PAGE(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_PAGE_IDX].INT
+
+/*  This macro defines implementation offsets
+ *  It is only used for pointer values
+ */
+#   define SYNCTEX_MAKE_GET(SYNCTEX_GETTER,OFFSET)\
+synctex_node_t * SYNCTEX_GETTER (synctex_node_t node);\
+synctex_node_t * SYNCTEX_GETTER (synctex_node_t node) {\
+	return node?(synctex_node_t *)((&((node)->implementation))+OFFSET):NULL;\
+}
+SYNCTEX_MAKE_GET(_synctex_implementation_0,0)
+SYNCTEX_MAKE_GET(_synctex_implementation_1,1)
+SYNCTEX_MAKE_GET(_synctex_implementation_2,2)
+SYNCTEX_MAKE_GET(_synctex_implementation_3,3)
+SYNCTEX_MAKE_GET(_synctex_implementation_4,4)
+SYNCTEX_MAKE_GET(_synctex_implementation_5,5)
+
+typedef struct {
+	synctex_class_t class;
+	synctex_info_t implementation[3+SYNCTEX_PAGE_IDX+1];/*  child, sibling, next box,
+	                         *  SYNCTEX_PAGE_IDX */
+} synctex_sheet_t;
+
+synctex_node_t _synctex_new_sheet(synctex_scanner_t scanner);
+void _synctex_display_sheet(synctex_node_t sheet);
+void _synctex_log_sheet(synctex_node_t sheet);
+
+static _synctex_class_t synctex_class_sheet = {
+	NULL,                       /*  No scanner yet */
+	synctex_node_type_sheet,    /*  Node type */
+	&_synctex_new_sheet,        /*  creator */
+	&_synctex_free_node,        /*  destructor */
+	&_synctex_log_sheet,        /*  log */
+	&_synctex_display_sheet,    /*  display */
+	NULL,                       /*  No parent */
+	&_synctex_implementation_0, /*  child */
+	&_synctex_implementation_1, /*  sibling */
+	NULL,                       /*  No friend */
+	&_synctex_implementation_2, /*  Next box */
+	(_synctex_info_getter_t)&_synctex_implementation_3  /*  info */
+};
+
+/*  sheet node creator */
+synctex_node_t _synctex_new_sheet(synctex_scanner_t scanner) {
+	synctex_node_t node = _synctex_malloc(sizeof(synctex_sheet_t));
+	if (node) {
+		node->class = scanner?scanner->class+synctex_node_type_sheet:(synctex_class_t)&synctex_class_sheet;
+	}
+	return node;
+}
+
+/*  A box node contains navigation and synctex information
+ *  There are different kind of boxes.
+ *  Only horizontal boxes are treated differently because of their visible size.
+ */
+#   define SYNCTEX_TAG_IDX 0
+#   define SYNCTEX_LINE_IDX (SYNCTEX_TAG_IDX+1)
+#   define SYNCTEX_COLUMN_IDX (SYNCTEX_LINE_IDX+1)
+#   define SYNCTEX_HORIZ_IDX (SYNCTEX_COLUMN_IDX+1)
+#   define SYNCTEX_VERT_IDX (SYNCTEX_HORIZ_IDX+1)
+#   define SYNCTEX_WIDTH_IDX (SYNCTEX_VERT_IDX+1)
+#   define SYNCTEX_HEIGHT_IDX (SYNCTEX_WIDTH_IDX+1)
+#   define SYNCTEX_DEPTH_IDX (SYNCTEX_HEIGHT_IDX+1)
+/*  the corresponding info accessors */
+#   define SYNCTEX_TAG(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_TAG_IDX].INT
+#   define SYNCTEX_LINE(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_LINE_IDX].INT
+#   define SYNCTEX_COLUMN(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_COLUMN_IDX].INT
+#   define SYNCTEX_HORIZ(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_HORIZ_IDX].INT
+#   define SYNCTEX_VERT(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_VERT_IDX].INT
+#   define SYNCTEX_WIDTH(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_WIDTH_IDX].INT
+#   define SYNCTEX_HEIGHT(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_HEIGHT_IDX].INT
+#   define SYNCTEX_DEPTH(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_DEPTH_IDX].INT
+#   define SYNCTEX_ABS_WIDTH(NODE) ((SYNCTEX_WIDTH(NODE)>0?SYNCTEX_WIDTH(NODE):-SYNCTEX_WIDTH(NODE)))
+#   define SYNCTEX_ABS_HEIGHT(NODE) ((SYNCTEX_HEIGHT(NODE)>0?SYNCTEX_HEIGHT(NODE):-SYNCTEX_HEIGHT(NODE)))
+#   define SYNCTEX_ABS_DEPTH(NODE) ((SYNCTEX_DEPTH(NODE)>0?SYNCTEX_DEPTH(NODE):-SYNCTEX_DEPTH(NODE)))
+
+typedef struct {
+	synctex_class_t class;
+	synctex_info_t implementation[5+SYNCTEX_DEPTH_IDX+1]; /*  parent,child,sibling,friend,next box,
+						        *  SYNCTEX_TAG,SYNCTEX_LINE,SYNCTEX_COLUMN,
+								*  SYNCTEX_HORIZ,SYNCTEX_VERT,SYNCTEX_WIDTH,SYNCTEX_HEIGHT,SYNCTEX_DEPTH */
+} synctex_vert_box_node_t;
+
+synctex_node_t _synctex_new_vbox(synctex_scanner_t scanner);
+void _synctex_log_box(synctex_node_t sheet);
+void _synctex_display_vbox(synctex_node_t node);
+
+/*  These are static class objects, each scanner will make a copy of them and setup the scanner field.
+ */
+static _synctex_class_t synctex_class_vbox = {
+	NULL,                       /*  No scanner yet */
+	synctex_node_type_vbox,     /*  Node type */
+	&_synctex_new_vbox,         /*  creator */
+	&_synctex_free_node,        /*  destructor */
+	&_synctex_log_box,          /*  log */
+	&_synctex_display_vbox,     /*  display */
+	&_synctex_implementation_0, /*  parent */
+	&_synctex_implementation_1, /*  child */
+	&_synctex_implementation_2, /*  sibling */
+	&_synctex_implementation_3, /*  friend */
+	&_synctex_implementation_4, /*  next box */
+	(_synctex_info_getter_t)&_synctex_implementation_5
+};
+
+/*  vertical box node creator */
+synctex_node_t _synctex_new_vbox(synctex_scanner_t scanner) {
+	synctex_node_t node = _synctex_malloc(sizeof(synctex_vert_box_node_t));
+	if (node) {
+		node->class = scanner?scanner->class+synctex_node_type_vbox:(synctex_class_t)&synctex_class_vbox;
+	}
+	return node;
+}
+
+#   define SYNCTEX_HORIZ_V_IDX (SYNCTEX_DEPTH_IDX+1)
+#   define SYNCTEX_VERT_V_IDX (SYNCTEX_HORIZ_V_IDX+1)
+#   define SYNCTEX_WIDTH_V_IDX (SYNCTEX_VERT_V_IDX+1)
+#   define SYNCTEX_HEIGHT_V_IDX (SYNCTEX_WIDTH_V_IDX+1)
+#   define SYNCTEX_DEPTH_V_IDX (SYNCTEX_HEIGHT_V_IDX+1)
+/*  the corresponding info accessors */
+#   define SYNCTEX_HORIZ_V(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_HORIZ_V_IDX].INT
+#   define SYNCTEX_VERT_V(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_VERT_V_IDX].INT
+#   define SYNCTEX_WIDTH_V(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_WIDTH_V_IDX].INT
+#   define SYNCTEX_HEIGHT_V(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_HEIGHT_V_IDX].INT
+#   define SYNCTEX_DEPTH_V(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_DEPTH_V_IDX].INT
+#   define SYNCTEX_ABS_WIDTH_V(NODE) ((SYNCTEX_WIDTH_V(NODE)>0?SYNCTEX_WIDTH_V(NODE):-SYNCTEX_WIDTH_V(NODE)))
+#   define SYNCTEX_ABS_HEIGHT_V(NODE) ((SYNCTEX_HEIGHT_V(NODE)>0?SYNCTEX_HEIGHT_V(NODE):-SYNCTEX_HEIGHT_V(NODE)))
+#   define SYNCTEX_ABS_DEPTH_V(NODE) ((SYNCTEX_DEPTH_V(NODE)>0?SYNCTEX_DEPTH_V(NODE):-SYNCTEX_DEPTH_V(NODE)))
+
+/*  Horizontal boxes must contain visible size, because 0 width does not mean emptiness */
+typedef struct {
+	synctex_class_t class;
+	synctex_info_t implementation[5+SYNCTEX_DEPTH_V_IDX+1]; /*parent,child,sibling,friend,next box,
+						*  SYNCTEX_TAG,SYNCTEX_LINE,SYNCTEX_COLUMN,
+						*  SYNCTEX_HORIZ,SYNCTEX_VERT,SYNCTEX_WIDTH,SYNCTEX_HEIGHT,SYNCTEX_DEPTH,
+						*  SYNCTEX_HORIZ_V,SYNCTEX_VERT_V,SYNCTEX_WIDTH_V,SYNCTEX_HEIGHT_V,SYNCTEX_DEPTH_V*/
+} synctex_horiz_box_node_t;
+
+synctex_node_t _synctex_new_hbox(synctex_scanner_t scanner);
+void _synctex_display_hbox(synctex_node_t node);
+void _synctex_log_horiz_box(synctex_node_t sheet);
+
+
+static _synctex_class_t synctex_class_hbox = {
+	NULL,                       /*  No scanner yet */
+	synctex_node_type_hbox,     /*  Node type */
+	&_synctex_new_hbox,         /*  creator */
+	&_synctex_free_node,        /*  destructor */
+	&_synctex_log_horiz_box,    /*  log */
+	&_synctex_display_hbox,     /*  display */
+	&_synctex_implementation_0, /*  parent */
+	&_synctex_implementation_1, /*  child */
+	&_synctex_implementation_2, /*  sibling */
+	&_synctex_implementation_3, /*  friend */
+	&_synctex_implementation_4, /*  next box */
+	(_synctex_info_getter_t)&_synctex_implementation_5
+};
+
+/*  horizontal box node creator */
+synctex_node_t _synctex_new_hbox(synctex_scanner_t scanner) {
+	synctex_node_t node = _synctex_malloc(sizeof(synctex_horiz_box_node_t));
+	if (node) {
+		node->class = scanner?scanner->class+synctex_node_type_hbox:(synctex_class_t)&synctex_class_hbox;
+	}
+	return node;
+}
+
+/*  This void box node implementation is either horizontal or vertical
+ *  It does not contain a child field.
+ */
+typedef struct {
+	synctex_class_t class;
+	synctex_info_t implementation[3+SYNCTEX_DEPTH_IDX+1]; /*  parent,sibling,friend,
+	                  *  SYNCTEX_TAG,SYNCTEX_LINE,SYNCTEX_COLUMN,
+					  *  SYNCTEX_HORIZ,SYNCTEX_VERT,SYNCTEX_WIDTH,SYNCTEX_HEIGHT,SYNCTEX_DEPTH*/
+} synctex_void_box_node_t;
+
+synctex_node_t _synctex_new_void_vbox(synctex_scanner_t scanner);
+void _synctex_log_void_box(synctex_node_t sheet);
+void _synctex_display_void_vbox(synctex_node_t node);
+
+static _synctex_class_t synctex_class_void_vbox = {
+	NULL,                       /*  No scanner yet */
+	synctex_node_type_void_vbox,/*  Node type */
+	&_synctex_new_void_vbox,    /*  creator */
+	&_synctex_free_node,        /*  destructor */
+	&_synctex_log_void_box,     /*  log */
+	&_synctex_display_void_vbox,/*  display */
+	&_synctex_implementation_0, /*  parent */
+	NULL,                       /*  No child */
+	&_synctex_implementation_1, /*  sibling */
+	&_synctex_implementation_2, /*  friend */
+	NULL,						/*  No next box */
+	(_synctex_info_getter_t)&_synctex_implementation_3
+};
+
+/*  vertical void box node creator */
+synctex_node_t _synctex_new_void_vbox(synctex_scanner_t scanner) {
+	synctex_node_t node = _synctex_malloc(sizeof(synctex_void_box_node_t));
+	if (node) {
+		node->class = scanner?scanner->class+synctex_node_type_void_vbox:(synctex_class_t)&synctex_class_void_vbox;
+	}
+	return node;
+}
+
+synctex_node_t _synctex_new_void_hbox(synctex_scanner_t scanner);
+void _synctex_display_void_hbox(synctex_node_t node);
+
+static _synctex_class_t synctex_class_void_hbox = {
+	NULL,                       /*  No scanner yet */
+	synctex_node_type_void_hbox,/*  Node type */
+	&_synctex_new_void_hbox,    /*  creator */
+	&_synctex_free_node,        /*  destructor */
+	&_synctex_log_void_box,     /*  log */
+	&_synctex_display_void_hbox,/*  display */
+	&_synctex_implementation_0, /*  parent */
+	NULL,                       /*  No child */
+	&_synctex_implementation_1, /*  sibling */
+	&_synctex_implementation_2, /*  friend */
+	NULL,						/*  No next box */
+	(_synctex_info_getter_t)&_synctex_implementation_3
+};
+
+/*  horizontal void box node creator */
+synctex_node_t _synctex_new_void_hbox(synctex_scanner_t scanner) {
+	synctex_node_t node = _synctex_malloc(sizeof(synctex_void_box_node_t));
+	if (node) {
+		node->class = scanner?scanner->class+synctex_node_type_void_hbox:(synctex_class_t)&synctex_class_void_hbox;
+	}
+	return node;
+}
+
+/*  The medium nodes correspond to kern, glue, penalty and math nodes.  */
+typedef struct {
+	synctex_class_t class;
+	synctex_info_t implementation[3+SYNCTEX_WIDTH_IDX+1]; /*  parent,sibling,friend,
+	                  *  SYNCTEX_TAG,SYNCTEX_LINE,SYNCTEX_COLUMN,
+					  *  SYNCTEX_HORIZ,SYNCTEX_VERT,SYNCTEX_WIDTH */
+} synctex_medium_node_t;
+
+#define SYNCTEX_IS_BOX(NODE)\
+	((NODE->class->type == synctex_node_type_vbox)\
+	|| (NODE->class->type == synctex_node_type_void_vbox)\
+	|| (NODE->class->type == synctex_node_type_hbox)\
+	|| (NODE->class->type == synctex_node_type_void_hbox))
+	
+#define SYNCTEX_HAS_CHILDREN(NODE) (NODE && SYNCTEX_CHILD(NODE))
+	
+void _synctex_log_medium_node(synctex_node_t node);
+
+/*  math node creator */
+synctex_node_t _synctex_new_math(synctex_scanner_t scanner);
+void _synctex_display_math(synctex_node_t node);
+
+static _synctex_class_t synctex_class_math = {
+	NULL,                       /*  No scanner yet */
+	synctex_node_type_math,     /*  Node type */
+	&_synctex_new_math,         /*  creator */
+	&_synctex_free_leaf,        /*  destructor */
+	&_synctex_log_medium_node,  /*  log */
+	&_synctex_display_math,     /*  display */
+	&_synctex_implementation_0, /*  parent */
+	NULL,                       /*  No child */
+	&_synctex_implementation_1, /*  sibling */
+	&_synctex_implementation_2, /*  friend */
+	NULL,                       /*  No next box */
+	(_synctex_info_getter_t)&_synctex_implementation_3
+};
+
+synctex_node_t _synctex_new_math(synctex_scanner_t scanner) {
+	synctex_node_t node = _synctex_malloc(sizeof(synctex_medium_node_t));
+	if (node) {
+		node->class = scanner?scanner->class+synctex_node_type_math:(synctex_class_t)&synctex_class_math;
+	}
+	return node;
+}
+
+/*  kern node creator */
+synctex_node_t _synctex_new_kern(synctex_scanner_t scanner);
+void _synctex_display_kern(synctex_node_t node);
+
+static _synctex_class_t synctex_class_kern = {
+	NULL,                       /*  No scanner yet */
+	synctex_node_type_kern,     /*  Node type */
+	&_synctex_new_kern,         /*  creator */
+	&_synctex_free_leaf,        /*  destructor */
+	&_synctex_log_medium_node,  /*  log */
+	&_synctex_display_kern,     /*  display */
+	&_synctex_implementation_0, /*  parent */
+	NULL,                       /*  No child */
+	&_synctex_implementation_1, /*  sibling */
+	&_synctex_implementation_2, /*  friend */
+	NULL,                       /*  No next box */
+	(_synctex_info_getter_t)&_synctex_implementation_3
+};
+
+synctex_node_t _synctex_new_kern(synctex_scanner_t scanner) {
+	synctex_node_t node = _synctex_malloc(sizeof(synctex_medium_node_t));
+	if (node) {
+		node->class = scanner?scanner->class+synctex_node_type_kern:(synctex_class_t)&synctex_class_kern;
+	}
+	return node;
+}
+
+/*  The small nodes correspond to glue and boundary nodes.  */
+typedef struct {
+	synctex_class_t class;
+	synctex_info_t implementation[3+SYNCTEX_VERT_IDX+1]; /*  parent,sibling,friend,
+	                  *  SYNCTEX_TAG,SYNCTEX_LINE,SYNCTEX_COLUMN,
+					  *  SYNCTEX_HORIZ,SYNCTEX_VERT */
+} synctex_small_node_t;
+
+void _synctex_log_small_node(synctex_node_t node);
+/*  glue node creator */
+synctex_node_t _synctex_new_glue(synctex_scanner_t scanner);
+void _synctex_display_glue(synctex_node_t node);
+
+static _synctex_class_t synctex_class_glue = {
+	NULL,                       /*  No scanner yet */
+	synctex_node_type_glue,     /*  Node type */
+	&_synctex_new_glue,         /*  creator */
+	&_synctex_free_leaf,        /*  destructor */
+	&_synctex_log_medium_node,  /*  log */
+	&_synctex_display_glue,     /*  display */
+	&_synctex_implementation_0, /*  parent */
+	NULL,                       /*  No child */
+	&_synctex_implementation_1, /*  sibling */
+	&_synctex_implementation_2, /*  friend */
+	NULL,                       /*  No next box */
+	(_synctex_info_getter_t)&_synctex_implementation_3
+};
+synctex_node_t _synctex_new_glue(synctex_scanner_t scanner) {
+	synctex_node_t node = _synctex_malloc(sizeof(synctex_medium_node_t));
+	if (node) {
+		node->class = scanner?scanner->class+synctex_node_type_glue:(synctex_class_t)&synctex_class_glue;
+	}
+	return node;
+}
+
+/*  boundary node creator */
+synctex_node_t _synctex_new_boundary(synctex_scanner_t scanner);
+void _synctex_display_boundary(synctex_node_t node);
+
+static _synctex_class_t synctex_class_boundary = {
+	NULL,                       /*  No scanner yet */
+	synctex_node_type_boundary,     /*  Node type */
+	&_synctex_new_boundary, /*  creator */
+	&_synctex_free_leaf,        /*  destructor */
+	&_synctex_log_small_node,   /*  log */
+	&_synctex_display_boundary,     /*  display */
+	&_synctex_implementation_0, /*  parent */
+	NULL,                       /*  No child */
+	&_synctex_implementation_1, /*  sibling */
+	&_synctex_implementation_2, /*  friend */
+	NULL,                       /*  No next box */
+	(_synctex_info_getter_t)&_synctex_implementation_3
+};
+
+synctex_node_t _synctex_new_boundary(synctex_scanner_t scanner) {
+	synctex_node_t node = _synctex_malloc(sizeof(synctex_small_node_t));
+	if (node) {
+		node->class = scanner?scanner->class+synctex_node_type_boundary:(synctex_class_t)&synctex_class_boundary;
+	}
+	return node;
+}
+
+#   define SYNCTEX_NAME_IDX (SYNCTEX_TAG_IDX+1)
+#   define SYNCTEX_NAME(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_NAME_IDX].PTR
+
+/*  Input nodes only know about their sibling, which is another input node.
+ *  The synctex information is the SYNCTEX_TAG and SYNCTEX_NAME*/
+typedef struct {
+	synctex_class_t class;
+	synctex_info_t implementation[1+SYNCTEX_NAME_IDX+1]; /*  sibling,
+	                          *  SYNCTEX_TAG,SYNCTEX_NAME */
+} synctex_input_t;
+
+synctex_node_t _synctex_new_input(synctex_scanner_t scanner);
+void _synctex_free_input(synctex_node_t node);
+void _synctex_display_input(synctex_node_t node);
+void _synctex_log_input(synctex_node_t sheet);
+
+static _synctex_class_t synctex_class_input = {
+	NULL,                       /*  No scanner yet */
+	synctex_node_type_input,    /*  Node type */
+	&_synctex_new_input,        /*  creator */
+	&_synctex_free_input,       /*  destructor */
+	&_synctex_log_input,        /*  log */
+	&_synctex_display_input,    /*  display */
+	NULL,                       /*  No parent */
+	NULL,                       /*  No child */
+	&_synctex_implementation_0, /*  sibling */
+	NULL,                       /*  No friend */
+	NULL,                       /*  No next box */
+	(_synctex_info_getter_t)&_synctex_implementation_1
+};
+
+synctex_node_t _synctex_new_input(synctex_scanner_t scanner) {
+	synctex_node_t node = _synctex_malloc(sizeof(synctex_input_t));
+	if (node) {
+		node->class = scanner?scanner->class+synctex_node_type_input:(synctex_class_t)&synctex_class_input;
+	}
+	return node;
+}
+void _synctex_free_input(synctex_node_t node){
+	if (node) {
+		SYNCTEX_FREE(SYNCTEX_SIBLING(node));
+		free(SYNCTEX_NAME(node));
+		free(node);
+	}
+}
+#	ifdef SYNCTEX_NOTHING
+#       pragma mark -
+#       pragma mark Navigation
+#   endif
+synctex_node_t synctex_node_parent(synctex_node_t node)
+{
+	return SYNCTEX_PARENT(node);
+}
+synctex_node_t synctex_node_sheet(synctex_node_t node)
+{
+	while(node && node->class->type != synctex_node_type_sheet) {
+		node = SYNCTEX_PARENT(node);
+	}
+	/*  exit the while loop either when node is NULL or node is a sheet */
+	return node;
+}
+synctex_node_t synctex_node_child(synctex_node_t node)
+{
+	return SYNCTEX_CHILD(node);
+}
+synctex_node_t synctex_node_sibling(synctex_node_t node)
+{
+	return SYNCTEX_SIBLING(node);
+}
+synctex_node_t synctex_node_next(synctex_node_t node) {
+	if (SYNCTEX_CHILD(node)) {
+		return SYNCTEX_CHILD(node);
+	}
+sibling:
+	if (SYNCTEX_SIBLING(node)) {
+		return SYNCTEX_SIBLING(node);
+	}
+	if ((node = SYNCTEX_PARENT(node))) {
+		if (node->class->type == synctex_node_type_sheet) {/*  EXC_BAD_ACCESS? */
+			return NULL;
+		}
+		goto sibling;
+	}
+	return NULL;
+}
+#	ifdef SYNCTEX_NOTHING
+#       pragma mark -
+#       pragma mark CLASS
+#   endif
+
+/*  Public node accessor: the type  */
+synctex_node_type_t synctex_node_type(synctex_node_t node) {
+	if (node) {
+		return (((node)->class))->type;
+	}
+	return synctex_node_type_error;
+}
+
+/*  Public node accessor: the human readable type  */
+const char * synctex_node_isa(synctex_node_t node) {
+static const char * isa[synctex_node_number_of_types] =
+		{"Not a node","input","sheet","vbox","void vbox","hbox","void hbox","kern","glue","math","boundary"};
+	return isa[synctex_node_type(node)];
+}
+
+#	ifdef SYNCTEX_NOTHING
+#       pragma mark -
+#       pragma mark SYNCTEX_LOG
+#   endif
+
+#   define SYNCTEX_LOG(NODE) SYNCTEX_MSG_SEND(NODE,log)
+
+/*  Public node logger  */
+void synctex_node_log(synctex_node_t node) {
+	SYNCTEX_LOG(node);
+}
+
+#   define SYNCTEX_DISPLAY(NODE) SYNCTEX_MSG_SEND(NODE,display)
+
+void synctex_node_display(synctex_node_t node) {
+	SYNCTEX_DISPLAY(node);
+}
+
+void _synctex_display_input(synctex_node_t node) {
+	printf("....Input:%i:%s\n",
+		SYNCTEX_TAG(node),
+		SYNCTEX_NAME(node));
+	SYNCTEX_DISPLAY(SYNCTEX_SIBLING(node));
+}
+
+void _synctex_log_sheet(synctex_node_t sheet) {
+	if (sheet) {
+		printf("%s:%i\n",synctex_node_isa(sheet),SYNCTEX_PAGE(sheet));
+		printf("SELF:%p",(void *)sheet);
+		printf(" SYNCTEX_PARENT:%p",(void *)SYNCTEX_PARENT(sheet));
+		printf(" SYNCTEX_CHILD:%p",(void *)SYNCTEX_CHILD(sheet));
+		printf(" SYNCTEX_SIBLING:%p",(void *)SYNCTEX_SIBLING(sheet));
+		printf(" SYNCTEX_FRIEND:%p\n",(void *)SYNCTEX_FRIEND(sheet));
+	}
+}
+
+void _synctex_log_small_node(synctex_node_t node) {
+	printf("%s:%i,%i:%i,%i\n",
+		synctex_node_isa(node),
+		SYNCTEX_TAG(node),
+		SYNCTEX_LINE(node),
+		SYNCTEX_HORIZ(node),
+		SYNCTEX_VERT(node));
+	printf("SELF:%p",(void *)node);
+	printf(" SYNCTEX_PARENT:%p",(void *)SYNCTEX_PARENT(node));
+	printf(" SYNCTEX_CHILD:%p",(void *)SYNCTEX_CHILD(node));
+	printf(" SYNCTEX_SIBLING:%p",(void *)SYNCTEX_SIBLING(node));
+	printf(" SYNCTEX_FRIEND:%p\n",(void *)SYNCTEX_FRIEND(node));
+}
+
+void _synctex_log_medium_node(synctex_node_t node) {
+	printf("%s:%i,%i:%i,%i:%i\n",
+		synctex_node_isa(node),
+		SYNCTEX_TAG(node),
+		SYNCTEX_LINE(node),
+		SYNCTEX_HORIZ(node),
+		SYNCTEX_VERT(node),
+		SYNCTEX_WIDTH(node));
+	printf("SELF:%p",(void *)node);
+	printf(" SYNCTEX_PARENT:%p",(void *)SYNCTEX_PARENT(node));
+	printf(" SYNCTEX_CHILD:%p",(void *)SYNCTEX_CHILD(node));
+	printf(" SYNCTEX_SIBLING:%p",(void *)SYNCTEX_SIBLING(node));
+	printf(" SYNCTEX_FRIEND:%p\n",(void *)SYNCTEX_FRIEND(node));
+}
+
+void _synctex_log_void_box(synctex_node_t node) {
+	printf("%s",synctex_node_isa(node));
+	printf(":%i",SYNCTEX_TAG(node));
+	printf(",%i",SYNCTEX_LINE(node));
+	printf(",%i",0);
+	printf(":%i",SYNCTEX_HORIZ(node));
+	printf(",%i",SYNCTEX_VERT(node));
+	printf(":%i",SYNCTEX_WIDTH(node));
+	printf(",%i",SYNCTEX_HEIGHT(node));
+	printf(",%i",SYNCTEX_DEPTH(node));
+	printf("\nSELF:%p",(void *)node);
+	printf(" SYNCTEX_PARENT:%p",(void *)SYNCTEX_PARENT(node));
+	printf(" SYNCTEX_CHILD:%p",(void *)SYNCTEX_CHILD(node));
+	printf(" SYNCTEX_SIBLING:%p",(void *)SYNCTEX_SIBLING(node));
+	printf(" SYNCTEX_FRIEND:%p\n",(void *)SYNCTEX_FRIEND(node));
+}
+
+void _synctex_log_box(synctex_node_t node) {
+	printf("%s",synctex_node_isa(node));
+	printf(":%i",SYNCTEX_TAG(node));
+	printf(",%i",SYNCTEX_LINE(node));
+	printf(",%i",0);
+	printf(":%i",SYNCTEX_HORIZ(node));
+	printf(",%i",SYNCTEX_VERT(node));
+	printf(":%i",SYNCTEX_WIDTH(node));
+	printf(",%i",SYNCTEX_HEIGHT(node));
+	printf(",%i",SYNCTEX_DEPTH(node));
+	printf("\nSELF:%p",(void *)node);
+	printf(" SYNCTEX_PARENT:%p",(void *)SYNCTEX_PARENT(node));
+	printf(" SYNCTEX_CHILD:%p",(void *)SYNCTEX_CHILD(node));
+	printf(" SYNCTEX_SIBLING:%p",(void *)SYNCTEX_SIBLING(node));
+	printf(" SYNCTEX_FRIEND:%p\n",(void *)SYNCTEX_FRIEND(node));
+}
+
+void _synctex_log_horiz_box(synctex_node_t node) {
+	printf("%s",synctex_node_isa(node));
+	printf(":%i",SYNCTEX_TAG(node));
+	printf(",%i",SYNCTEX_LINE(node));
+	printf(",%i",0);
+	printf(":%i",SYNCTEX_HORIZ(node));
+	printf(",%i",SYNCTEX_VERT(node));
+	printf(":%i",SYNCTEX_WIDTH(node));
+	printf(",%i",SYNCTEX_HEIGHT(node));
+	printf(",%i",SYNCTEX_DEPTH(node));
+	printf("/%i",SYNCTEX_HORIZ_V(node));
+	printf(",%i",SYNCTEX_VERT_V(node));
+	printf(":%i",SYNCTEX_WIDTH_V(node));
+	printf(",%i",SYNCTEX_HEIGHT_V(node));
+	printf(",%i",SYNCTEX_DEPTH_V(node));
+	printf("\nSELF:%p",(void *)node);
+	printf(" SYNCTEX_PARENT:%p",(void *)SYNCTEX_PARENT(node));
+	printf(" SYNCTEX_CHILD:%p",(void *)SYNCTEX_CHILD(node));
+	printf(" SYNCTEX_SIBLING:%p",(void *)SYNCTEX_SIBLING(node));
+	printf(" SYNCTEX_FRIEND:%p\n",(void *)SYNCTEX_FRIEND(node));
+}
+
+void _synctex_log_input(synctex_node_t node) {
+	printf("%s",synctex_node_isa(node));
+	printf(":%i",SYNCTEX_TAG(node));
+	printf(",%s",SYNCTEX_NAME(node));
+	printf(" SYNCTEX_SIBLING:%p",(void *)SYNCTEX_SIBLING(node));
+}
+
+void _synctex_display_sheet(synctex_node_t sheet) {
+	if (sheet) {
+		printf("....{%i\n",SYNCTEX_PAGE(sheet));
+		SYNCTEX_DISPLAY(SYNCTEX_CHILD(sheet));
+		printf("....}\n");
+		SYNCTEX_DISPLAY(SYNCTEX_SIBLING(sheet));
+	}
+}
+
+void _synctex_display_vbox(synctex_node_t node) {
+	printf("....[%i,%i:%i,%i:%i,%i,%i\n",
+		SYNCTEX_TAG(node),
+		SYNCTEX_LINE(node),
+		SYNCTEX_HORIZ(node),
+		SYNCTEX_VERT(node),
+		SYNCTEX_WIDTH(node),
+		SYNCTEX_HEIGHT(node),
+		SYNCTEX_DEPTH(node));
+	SYNCTEX_DISPLAY(SYNCTEX_CHILD(node));
+	printf("....]\n");
+	SYNCTEX_DISPLAY(SYNCTEX_SIBLING(node));
+}
+
+void _synctex_display_hbox(synctex_node_t node) {
+	printf("....(%i,%i:%i,%i:%i,%i,%i\n",
+		SYNCTEX_TAG(node),
+		SYNCTEX_LINE(node),
+		SYNCTEX_HORIZ(node),
+		SYNCTEX_VERT(node),
+		SYNCTEX_WIDTH(node),
+		SYNCTEX_HEIGHT(node),
+		SYNCTEX_DEPTH(node));
+	SYNCTEX_DISPLAY(SYNCTEX_CHILD(node));
+	printf("....)\n");
+	SYNCTEX_DISPLAY(SYNCTEX_SIBLING(node));
+}
+
+void _synctex_display_void_vbox(synctex_node_t node) {
+	printf("....v%i,%i;%i,%i:%i,%i,%i\n",
+		SYNCTEX_TAG(node),
+		SYNCTEX_LINE(node),
+		SYNCTEX_HORIZ(node),
+		SYNCTEX_VERT(node),
+		SYNCTEX_WIDTH(node),
+		SYNCTEX_HEIGHT(node),
+		SYNCTEX_DEPTH(node));
+	SYNCTEX_DISPLAY(SYNCTEX_SIBLING(node));
+}
+
+void _synctex_display_void_hbox(synctex_node_t node) {
+	printf("....h%i,%i:%i,%i:%i,%i,%i\n",
+		SYNCTEX_TAG(node),
+		SYNCTEX_LINE(node),
+		SYNCTEX_HORIZ(node),
+		SYNCTEX_VERT(node),
+		SYNCTEX_WIDTH(node),
+		SYNCTEX_HEIGHT(node),
+		SYNCTEX_DEPTH(node));
+	SYNCTEX_DISPLAY(SYNCTEX_SIBLING(node));
+}
+
+void _synctex_display_glue(synctex_node_t node) {
+	printf("....glue:%i,%i:%i,%i\n",
+		SYNCTEX_TAG(node),
+		SYNCTEX_LINE(node),
+		SYNCTEX_HORIZ(node),
+		SYNCTEX_VERT(node));
+	SYNCTEX_DISPLAY(SYNCTEX_SIBLING(node));
+}
+
+void _synctex_display_math(synctex_node_t node) {
+	printf("....math:%i,%i:%i,%i\n",
+		SYNCTEX_TAG(node),
+		SYNCTEX_LINE(node),
+		SYNCTEX_HORIZ(node),
+		SYNCTEX_VERT(node));
+	SYNCTEX_DISPLAY(SYNCTEX_SIBLING(node));
+}
+
+void _synctex_display_kern(synctex_node_t node) {
+	printf("....kern:%i,%i:%i,%i:%i\n",
+		SYNCTEX_TAG(node),
+		SYNCTEX_LINE(node),
+		SYNCTEX_HORIZ(node),
+		SYNCTEX_VERT(node),
+		SYNCTEX_WIDTH(node));
+	SYNCTEX_DISPLAY(SYNCTEX_SIBLING(node));
+}
+
+void _synctex_display_boundary(synctex_node_t node) {
+	printf("....boundary:%i,%i:%i,%i\n",
+		SYNCTEX_TAG(node),
+		SYNCTEX_LINE(node),
+		SYNCTEX_HORIZ(node),
+		SYNCTEX_VERT(node));
+	SYNCTEX_DISPLAY(SYNCTEX_SIBLING(node));
+}
+
+#	ifdef SYNCTEX_NOTHING
+#       pragma mark -
+#       pragma mark SCANNER
+#   endif
+
+/*  Here are gathered all the possible status that the next scanning functions will return.
+ *  All these functions return a status, and pass their result through pointers.
+ *  Negative values correspond to errors.
+ *  The management of the buffer is causing some significant overhead.
+ *  Every function that may access the buffer returns a status related to the buffer and file state.
+ *  status >= SYNCTEX_STATUS_OK means the function worked as expected
+ *  status < SYNCTEX_STATUS_OK means the function did not work as expected
+ *  status == SYNCTEX_STATUS_NOT_OK means the function did not work as expected but there is still some material to parse.
+ *  status == SYNCTEX_STATUS_EOF means the function did not work as expected and there is no more material.
+ *  status<SYNCTEX_STATUS_EOF means an error
+ */
+typedef int synctex_status_t;
+/*  When the end of the synctex file has been reached: */
+#   define SYNCTEX_STATUS_EOF 0
+/*  When the function could not return the value it was asked for: */
+#   define SYNCTEX_STATUS_NOT_OK (SYNCTEX_STATUS_EOF+1)
+/*  When the function returns the value it was asked for: */
+#   define SYNCTEX_STATUS_OK (SYNCTEX_STATUS_NOT_OK+1)
+/*  Generic error: */
+#   define SYNCTEX_STATUS_ERROR -1
+/*  Parameter error: */
+#   define SYNCTEX_STATUS_BAD_ARGUMENT -2
+
+#   define SYNCTEX_FILE (scanner->file)
+
+/*  Actually, the minimum buffer size is driven by integer and float parsing.
+ *  ±0.123456789e123
+ */
+#   define SYNCTEX_BUFFER_MIN_SIZE 16
+#   define SYNCTEX_BUFFER_SIZE 32768
+
+#	ifdef SYNCTEX_NOTHING
+#       pragma mark -
+#       pragma mark Prototypes
+#   endif
+void _synctex_log_void_box(synctex_node_t node);
+void _synctex_log_box(synctex_node_t node);
+void _synctex_log_horiz_box(synctex_node_t node);
+void _synctex_log_input(synctex_node_t node);
+synctex_status_t _synctex_buffer_get_available_size(synctex_scanner_t scanner, size_t * size_ptr);
+synctex_status_t _synctex_next_line(synctex_scanner_t scanner);
+synctex_status_t _synctex_match_string(synctex_scanner_t scanner, const char * the_string);
+synctex_status_t _synctex_decode_int(synctex_scanner_t scanner, int* value_ref);
+synctex_status_t _synctex_decode_string(synctex_scanner_t scanner, char ** value_ref);
+synctex_status_t _synctex_scan_input(synctex_scanner_t scanner);
+synctex_status_t _synctex_scan_preamble(synctex_scanner_t scanner);
+synctex_status_t _synctex_scan_float_and_dimension(synctex_scanner_t scanner, float * value_ref);
+synctex_status_t _synctex_scan_post_scriptum(synctex_scanner_t scanner);
+int _synctex_scan_postamble(synctex_scanner_t scanner);
+synctex_status_t _synctex_setup_visible_box(synctex_node_t box);
+synctex_status_t _synctex_horiz_box_setup_visible(synctex_node_t node,int h, int v);
+synctex_status_t _synctex_scan_sheet(synctex_scanner_t scanner, synctex_node_t parent);
+synctex_status_t _synctex_scan_nested_sheet(synctex_scanner_t scanner);
+synctex_status_t _synctex_scan_content(synctex_scanner_t scanner);
+int synctex_scanner_pre_x_offset(synctex_scanner_t scanner);
+int synctex_scanner_pre_y_offset(synctex_scanner_t scanner);
+const char * synctex_scanner_get_output_fmt(synctex_scanner_t scanner);
+int _synctex_node_is_box(synctex_node_t node);
+int _synctex_bail(void);
+
+/*  Try to ensure that the buffer contains at least size bytes.
+ *  Passing a huge size argument means the whole buffer length.
+ *  Passing a null size argument means return the available buffer length, without reading the file.
+ *  In that case, the return status is always SYNCTEX_STATUS_OK unless the given scanner is NULL,
+ *  in which case, SYNCTEX_STATUS_BAD_ARGUMENT is returned.
+ *  The value returned in size_ptr is the number of bytes now available in the buffer.
+ *  This is a nonnegative integer, it may take the value 0.
+ *  It is the responsibility of the caller to test whether this size is conforming to its needs.
+ *  Negative values may return in case of error, actually
+ *  when there was an error reading the synctex file. */
+synctex_status_t _synctex_buffer_get_available_size(synctex_scanner_t scanner, size_t * size_ptr) {
+  	size_t available = 0;
+	if (NULL == scanner || NULL == size_ptr) {
+		return SYNCTEX_STATUS_BAD_ARGUMENT;
+	}
+#   define size (* size_ptr)
+	if (size>SYNCTEX_BUFFER_SIZE){
+		size = SYNCTEX_BUFFER_SIZE;
+	}
+	available = SYNCTEX_END - SYNCTEX_CUR; /*  available is the number of unparsed chars in the buffer */
+	if (size<=available) {
+		/*  There are already sufficiently many characters in the buffer */
+		size = available;
+		return SYNCTEX_STATUS_OK;
+	}
+	if (SYNCTEX_FILE) {
+		/*  Copy the remaining part of the buffer to the beginning,
+		 *  then read the next part of the file */
+		int already_read = 0;
+		if (available) {
+			memmove(SYNCTEX_START, SYNCTEX_CUR, available);
+		}
+		SYNCTEX_CUR = SYNCTEX_START + available; /*  the next character after the move, will change. */
+		/*  Fill the buffer up to its end */
+		already_read = gzread(SYNCTEX_FILE,(void *)SYNCTEX_CUR,SYNCTEX_BUFFER_SIZE - available);
+		if (already_read>0) {
+			/*  We assume that 0<already_read<=SYNCTEX_BUFFER_SIZE - available, such that
+			 *  SYNCTEX_CUR + already_read = SYNCTEX_START + available  + already_read <= SYNCTEX_START + SYNCTEX_BUFFER_SIZE */
+			SYNCTEX_END = SYNCTEX_CUR + already_read;
+			/*  If the end of the file was reached, all the required SYNCTEX_BUFFER_SIZE - available
+			 *  may not be filled with values from the file.
+			 *  In that case, the buffer should stop properly after already_read characters. */
+			* SYNCTEX_END = '\0';
+			SYNCTEX_CUR = SYNCTEX_START;
+			size = SYNCTEX_END - SYNCTEX_CUR; /* == old available + already_read*/
+			return SYNCTEX_STATUS_OK; /*  May be available is less than size, the caller will have to test. */
+		} else if (0>already_read) {
+			/*  There is a possible error in reading the file */
+			int errnum = 0;
+			const char * error_string = gzerror(SYNCTEX_FILE, &errnum);
+			if (Z_ERRNO == errnum) {
+				/*  There is an error in zlib caused by the file system */
+				_synctex_error("gzread error from the file system (%i)",errno);
+                return SYNCTEX_STATUS_ERROR;
+			} else if (errnum) {
+				_synctex_error("gzread error (%i:%i,%s)",already_read,errnum,error_string);
+                return SYNCTEX_STATUS_ERROR;
+			}
+		}
+        /*  Nothing was read, we are at the end of the file. */
+        gzclose(SYNCTEX_FILE);
+        SYNCTEX_FILE = NULL;
+        SYNCTEX_END = SYNCTEX_CUR;
+        SYNCTEX_CUR = SYNCTEX_START;
+        * SYNCTEX_END = '\0';/*  Terminate the string properly.*/
+        size = SYNCTEX_END - SYNCTEX_CUR;
+        return SYNCTEX_STATUS_EOF; /*  there might be a bit of text left */
+    }
+	/*  We cannot enlarge the buffer because the end of the file was reached. */
+	size = available;
+ 	return SYNCTEX_STATUS_EOF;
+#   undef size
+}
+
+/*  Used when parsing the synctex file.
+ *  Advance to the next character starting a line.
+ *  Actually, only '\n' is recognized as end of line marker.
+ *  On normal completion, the returned value is the number of unparsed characters available in the buffer.
+ *  In general, it is a positive value, 0 meaning that the end of file was reached.
+ *  -1 is returned in case of error, actually because there was an error while feeding the buffer.
+ *  When the function returns with no error, SYNCTEX_CUR points to the first character of the next line, if any.
+ *  J. Laurens: Sat May 10 07:52:31 UTC 2008
+ */
+synctex_status_t _synctex_next_line(synctex_scanner_t scanner) {
+	synctex_status_t status = SYNCTEX_STATUS_OK;
+	size_t available = 0;
+	if (NULL == scanner) {
+		return SYNCTEX_STATUS_BAD_ARGUMENT;
+	}
+infinite_loop:
+	while(SYNCTEX_CUR<SYNCTEX_END) {
+		if (*SYNCTEX_CUR == '\n') {
+			++SYNCTEX_CUR;
+			available = 1;
+			return _synctex_buffer_get_available_size(scanner, &available);
+		}
+		++SYNCTEX_CUR;
+	}
+	/*  Here, we have SYNCTEX_CUR == SYNCTEX_END, such that the next call to _synctex_buffer_get_available_size
+	 *  will read another bunch of synctex file. Little by little, we advance to the end of the file. */
+	available = 1;
+	status = _synctex_buffer_get_available_size(scanner, &available);
+	if (status<=0) {
+		return status;
+	}
+	goto infinite_loop;
+}
+
+/*  Scan the given string.
+ *  Both scanner and the_string must not be NULL, and the_string must not be 0 length.
+ *  SYNCTEX_STATUS_OK is returned if the string is found,
+ *  SYNCTEX_STATUS_EOF is returned when the EOF is reached,
+ *  SYNCTEX_STATUS_NOT_OK is returned is the string is not found,
+ *  an error status is returned otherwise.
+ *  This is a critical method because buffering renders things more difficult.
+ *  The given string might be as long as the maximum size_t value.
+ *  As side effect, the buffer state may have changed if the given argument string can't fit into the buffer.
+ */
+synctex_status_t _synctex_match_string(synctex_scanner_t scanner, const char * the_string) {
+	size_t tested_len = 0; /*  the number of characters at the beginning of the_string that match */
+	size_t remaining_len = 0; /*  the number of remaining characters of the_string that should match */
+	size_t available = 0;
+	synctex_status_t status = 0;
+	if (NULL == scanner || NULL == the_string) {
+		return SYNCTEX_STATUS_BAD_ARGUMENT;
+	}
+	remaining_len = strlen(the_string); /*  All the_string should match */
+	if (0 == remaining_len) {
+		return SYNCTEX_STATUS_BAD_ARGUMENT;
+	}
+	/*  How many characters available in the buffer? */
+	available = remaining_len;
+	status = _synctex_buffer_get_available_size(scanner,&available);
+	if (status<SYNCTEX_STATUS_EOF) {
+		return status;
+	}
+	/*  Maybe we have less characters than expected because the buffer is too small. */
+	if (available>=remaining_len) {
+		/*  The buffer is sufficiently big to hold the expected number of characters. */
+		if (strncmp((char *)SYNCTEX_CUR,the_string,remaining_len)) {
+			return SYNCTEX_STATUS_NOT_OK;
+		}
+return_OK:
+		/*  Advance SYNCTEX_CUR to the next character after the_string. */
+		SYNCTEX_CUR += remaining_len;
+		return SYNCTEX_STATUS_OK;
+	} else if (strncmp((char *)SYNCTEX_CUR,the_string,available)) {
+			/*  No need to goo further, this is not the expected string in the buffer. */
+			return SYNCTEX_STATUS_NOT_OK;
+	} else if (SYNCTEX_FILE) {
+		/*  The buffer was too small to contain remaining_len characters.
+		 *  We have to cut the string into pieces. */
+		z_off_t offset = 0L;
+		/*  the first part of the string is found, advance the_string to the next untested character. */
+		the_string += available;
+		/*  update the remaining length and the parsed length. */
+		remaining_len -= available;
+		tested_len += available;
+		SYNCTEX_CUR += available; /*  We validate the tested characters. */
+		if (0 == remaining_len) {
+			/*  Nothing left to test, we have found the given string, we return the length. */
+			return tested_len;
+		}
+		/*  We also have to record the current state of the file cursor because
+		 *  if the_string does not match, all this should be a totally blank operation,
+		 *  for which the file and buffer states should not be modified at all.
+		 *  In fact, the states of the buffer before and after this function are in general different
+		 *  but they are totally equivalent as long as the values of the buffer before SYNCTEX_CUR
+		 *  can be safely discarded.  */
+		offset = gztell(SYNCTEX_FILE);
+		/*  offset now corresponds to the first character of the file that was not buffered. */
+		available = SYNCTEX_CUR - SYNCTEX_START; /*  available can be used as temporary placeholder. */
+		/*  available now corresponds to the number of chars that where already buffered and
+		 *  that match the head of the_string. If in fine the_string does not match, all these chars must be recovered
+		 *  because the buffer contents is completely replaced by _synctex_buffer_get_available_size.
+		 *  They were buffered from offset-len location in the file. */
+		offset -= available;
+more_characters:
+		/*  There is still some work to be done, so read another bunch of file.
+		 *  This is the second call to _synctex_buffer_get_available_size,
+		 *  which means that the actual contents of the buffer will be discarded.
+		 *  We will definitely have to recover the previous state in case we do not find the expected string. */
+		available = remaining_len;
+		status = _synctex_buffer_get_available_size(scanner,&available);
+		if (status<SYNCTEX_STATUS_EOF) {
+			return status; /*  This is an error, no need to go further. */
+		}
+		if (available==0) {
+			/*  Missing characters: recover the initial state of the file and return. */
+return_NOT_OK:
+			if (offset != gzseek(SYNCTEX_FILE,offset,SEEK_SET)) {
+				/*  This is a critical error, we could not recover the previous state. */
+				_synctex_error("can't seek file");
+				return SYNCTEX_STATUS_ERROR;
+			}
+			/*  Next time we are asked to fill the buffer,
+			 *  we will read a complete bunch of text from the file. */
+			SYNCTEX_CUR = SYNCTEX_END;
+			return SYNCTEX_STATUS_NOT_OK;
+		}
+		if (available<remaining_len) {
+			/*  We'll have to loop one more time. */
+			if (strncmp((char *)SYNCTEX_CUR,the_string,available)) {
+				/*  This is not the expected string, recover the previous state and return. */
+				goto return_NOT_OK;
+			}
+			/*  Advance the_string to the first untested character. */
+			the_string += available;
+			/*  update the remaining length and the parsed length. */
+			remaining_len -= available;
+			tested_len += available;
+			SYNCTEX_CUR += available; /*  We validate the tested characters. */
+			if (0 == remaining_len) {
+				/*  Nothing left to test, we have found the given string. */
+				return SYNCTEX_STATUS_OK;
+			}
+			goto more_characters;
+		}
+		/*  This is the last step. */
+		if (strncmp((char *)SYNCTEX_CUR,the_string,remaining_len)) {
+			/*  This is not the expected string, recover the previous state and return. */
+			goto return_NOT_OK;
+		}
+		goto return_OK;
+	} else {
+		/*  The buffer can't contain the given string argument, and the EOF was reached */
+		return SYNCTEX_STATUS_EOF;
+	}
+}
+
+/*  Used when parsing the synctex file.
+ *  Decode an integer.
+ *  First, field separators, namely ':' and ',' characters are skipped
+ *  The returned value is negative if there is an unrecoverable error.
+ *  It is SYNCTEX_STATUS_NOT_OK if an integer could not be parsed, for example
+ *  if the characters at the current cursor position are not digits or
+ *  if the end of the file has been reached.
+ *  It is SYNCTEX_STATUS_OK if an int has been successfully parsed.
+ *  The given scanner argument must not be NULL, on the contrary, value_ref may be NULL.
+ */
+synctex_status_t _synctex_decode_int(synctex_scanner_t scanner, int* value_ref) {
+	char * ptr = NULL;
+	char * end = NULL;
+	int result = 0;
+	size_t available = 0;
+	synctex_status_t status = 0;
+	if (NULL == scanner) {
+		 return SYNCTEX_STATUS_BAD_ARGUMENT;
+	}
+	available = SYNCTEX_BUFFER_MIN_SIZE;
+	status = _synctex_buffer_get_available_size(scanner, &available);
+	if (status<SYNCTEX_STATUS_EOF) {
+		return status;/*  Forward error. */
+	}
+	if (available==0) {
+		return SYNCTEX_STATUS_EOF;/*  it is the end of file. */
+	}
+	ptr = SYNCTEX_CUR;
+	if (*ptr==':' || *ptr==',') {
+		++ptr;
+		--available;
+		if (available==0) {
+			return SYNCTEX_STATUS_NOT_OK;/*  It is not possible to scan an int */
+		}
+	}
+	result = (int)strtol(ptr, &end, 10);
+	if (end>ptr) {
+		SYNCTEX_CUR = end;
+		if (value_ref) {
+			* value_ref = result;
+		}
+		return SYNCTEX_STATUS_OK;/*  Successfully scanned an int */
+	}	
+	return SYNCTEX_STATUS_NOT_OK;/*  Could not scan an int */
+}
+
+/*  The purpose of this function is to read a string.
+ *  A string is an array of characters from the current parser location
+ *  and before the next '\n' character.
+ *  If a string was properly decoded, it is returned in value_ref and
+ *  the cursor points to the new line marker.
+ *  The returned string was alloced on the heap, the caller is the owner and
+ *  is responsible to free it in due time.
+ *  If no string is parsed, * value_ref is undefined.
+ *  The maximum length of a string that a scanner can decode is platform dependent, namely UINT_MAX.
+ *  If you just want to blindly parse the file up to the end of the current line,
+ *  use _synctex_next_line instead.
+ *  On return, the scanner cursor is unchanged if a string could not be scanned or
+ *  points to the terminating '\n' character otherwise. As a consequence,
+ *  _synctex_next_line is necessary after.
+ *  If either scanner or value_ref is NULL, it is considered as an error and
+ *  SYNCTEX_STATUS_BAD_ARGUMENT is returned.
+ */
+synctex_status_t _synctex_decode_string(synctex_scanner_t scanner, char ** value_ref) {
+	char * end = NULL;
+	size_t current_size = 0;
+	size_t new_size = 0;
+	size_t len = 0;/*  The number of bytes to copy */
+	size_t available = 0;
+	synctex_status_t status = 0;
+	if (NULL == scanner || NULL == value_ref) {
+		return SYNCTEX_STATUS_BAD_ARGUMENT;
+	}
+	/*  The buffer must at least contain one character: the '\n' end of line marker */
+	if (SYNCTEX_CUR>=SYNCTEX_END) {
+		available = 1;
+		status = _synctex_buffer_get_available_size(scanner,&available);
+		if (status < 0) {
+			return status;
+		}
+		if (0 == available) {
+			return SYNCTEX_STATUS_EOF;
+		}
+	}
+	/*  Now we are sure that there is at least one available character, either because
+	 *  SYNCTEX_CUR was already < SYNCTEX_END, or because the buffer has been properly filled. */
+	/*  end will point to the next unparsed '\n' character in the file, when mapped to the buffer. */
+	end = SYNCTEX_CUR;
+	* value_ref = NULL;/*  Initialize, it will be realloc'ed */
+	/*  We scan all the characters up to the next '\n' */
+next_character:
+	if (end<SYNCTEX_END) {
+		if (*end == '\n') {
+			/*  OK, we found where to stop */
+			len = end - SYNCTEX_CUR;
+			if (current_size>UINT_MAX-len-1) {
+				/*  But we have reached the limit: we do not have current_size+len+1>UINT_MAX.
+				 *  We return the missing amount of memory.
+				 *  This will never occur in practice. */
+				return UINT_MAX-len-1 - current_size;
+			}
+			new_size = current_size+len;
+			/*  We have current_size+len+1<=UINT_MAX
+			 *  or equivalently new_size<UINT_MAX,
+			 *  where we have assumed that len<UINT_MAX */
+			if ((* value_ref = realloc(* value_ref,new_size+1)) != NULL) {
+				if (memcpy((*value_ref)+current_size,SYNCTEX_CUR,len)) {
+					(* value_ref)[new_size]='\0'; /*  Terminate the string */
+					SYNCTEX_CUR += len;/*  Advance to the terminating '\n' */
+					return SYNCTEX_STATUS_OK;
+				}
+				free(* value_ref);
+				* value_ref = NULL;
+				_synctex_error("could not copy memory (1).");
+				return SYNCTEX_STATUS_ERROR;
+			}
+			_synctex_error("could not allocate memory (1).");
+			return SYNCTEX_STATUS_ERROR;
+		} else {
+			++end;
+			goto next_character;
+		}
+	} else {
+		/*  end == SYNCTEX_END */
+		len = SYNCTEX_END - SYNCTEX_CUR;
+		if (current_size>UINT_MAX-len-1) {
+			/*  We have reached the limit. */
+			_synctex_error("limit reached (missing %i).",current_size-(UINT_MAX-len-1));
+			return SYNCTEX_STATUS_ERROR;
+		}
+		new_size = current_size+len;
+		if ((* value_ref = realloc(* value_ref,new_size+1)) != NULL) {
+			if (memcpy((*value_ref)+current_size,SYNCTEX_CUR,len)) {
+				(* value_ref)[new_size]='\0'; /*  Terminate the string */
+				SYNCTEX_CUR = SYNCTEX_END;/*  Advance the cursor to the end of the bufer */
+				return SYNCTEX_STATUS_OK;
+			}
+			free(* value_ref);
+			* value_ref = NULL;
+			_synctex_error("could not copy memory (2).");
+			return SYNCTEX_STATUS_ERROR;
+		}
+		/*  Huge memory problem */
+		_synctex_error("could not allocate memory (2).");
+		return SYNCTEX_STATUS_ERROR;
+	}
+}
+
+/*  Used when parsing the synctex file.
+ *  Read an Input record.
+ */
+synctex_status_t _synctex_scan_input(synctex_scanner_t scanner) {
+	synctex_status_t status = 0;
+	size_t available = 0;
+	synctex_node_t input = NULL;
+	if (NULL == scanner) {
+		return SYNCTEX_STATUS_BAD_ARGUMENT;
+	}
+	status = _synctex_match_string(scanner,"Input:");
+	if (status<SYNCTEX_STATUS_OK) {
+		return status;
+	}
+	/*  Create a node */
+	input = _synctex_new_input(scanner);
+	if (NULL == input) {
+		_synctex_error("could not create an input node.");
+		return SYNCTEX_STATUS_ERROR;
+	}
+	/*  Decode the synctag  */
+	status = _synctex_decode_int(scanner,&(SYNCTEX_TAG(input)));
+	if (status<SYNCTEX_STATUS_OK) {
+		_synctex_error("bad format of input node.");
+		SYNCTEX_FREE(input);
+		return status;
+	}
+	/*  The next character is a field separator, we expect one character in the buffer. */
+	available = 1;
+	status = _synctex_buffer_get_available_size(scanner, &available);
+	if (status<=SYNCTEX_STATUS_ERROR) {
+		return status;
+	}
+	if (0 == available) {
+		return SYNCTEX_STATUS_EOF;
+	}
+	/*  We can now safely advance to the next character, stepping over the field separator. */
+	++SYNCTEX_CUR;
+	--available;
+	/*  Then we scan the file name */
+	status = _synctex_decode_string(scanner,&(SYNCTEX_NAME(input)));
+	if (status<SYNCTEX_STATUS_OK) {
+		SYNCTEX_FREE(input);
+		return status;
+	}
+	/*  Prepend this input node to the input linked list of the scanner */
+	SYNCTEX_SET_SIBLING(input,scanner->input);
+	scanner->input = input;
+	return _synctex_next_line(scanner);/*  read the line termination character, if any */
+	/*  Now, set up the path */
+}
+
+typedef synctex_status_t (*synctex_decoder_t)(synctex_scanner_t,void *);
+
+synctex_status_t _synctex_scan_named(synctex_scanner_t scanner,const char * name,void * value_ref,synctex_decoder_t decoder);
+
+/*  Used when parsing the synctex file.
+ *  Read one of the settings.
+ *  On normal completion, returns SYNCTEX_STATUS_OK.
+ *  On error, returns SYNCTEX_STATUS_ERROR.
+ *  Both arguments must not be NULL.
+ *  On return, the scanner points to the next character after the decoded object whatever it is.
+ *  It is the responsibility of the caller to prepare the scanner for the next line.
+ */
+synctex_status_t _synctex_scan_named(synctex_scanner_t scanner,const char * name,void * value_ref,synctex_decoder_t decoder) {
+	synctex_status_t status = 0;
+	if (NULL == scanner || NULL == name || NULL == value_ref || NULL == decoder) {
+		return SYNCTEX_STATUS_BAD_ARGUMENT;
+	}
+not_found:
+	status = _synctex_match_string(scanner,name);
+	if (status<SYNCTEX_STATUS_NOT_OK) {
+		return status;
+	} else if (status == SYNCTEX_STATUS_NOT_OK) {
+		status = _synctex_next_line(scanner);
+		if (status<SYNCTEX_STATUS_OK) {
+			return status;
+		}
+		goto not_found;
+	}
+	/*  A line is found, scan the value */
+	return (*decoder)(scanner,value_ref);
+}
+
+/*  Used when parsing the synctex file.
+ *  Read the preamble.
+ */
+synctex_status_t _synctex_scan_preamble(synctex_scanner_t scanner) {
+	synctex_status_t status = 0;
+	if (NULL == scanner) {
+		return SYNCTEX_STATUS_BAD_ARGUMENT;
+	}
+	status = _synctex_scan_named(scanner,"SyncTeX Version:",&(scanner->version),(synctex_decoder_t)&_synctex_decode_int);
+	if (status<SYNCTEX_STATUS_OK) {
+		return status;
+	}
+	status = _synctex_next_line(scanner);
+	if (status<SYNCTEX_STATUS_OK) {
+		return status;
+	}
+	/*  Read all the input records */
+	do {
+		status = _synctex_scan_input(scanner);
+		if (status<SYNCTEX_STATUS_NOT_OK) {
+			return status;
+		}
+	} while(status == SYNCTEX_STATUS_OK);
+	/*  the loop exits when status == SYNCTEX_STATUS_NOT_OK */
+	/*  Now read all the required settings. */
+	status = _synctex_scan_named(scanner,"Output:",&(scanner->output_fmt),(synctex_decoder_t)&_synctex_decode_string);
+	if (status<SYNCTEX_STATUS_NOT_OK) {
+		return status;
+	}
+	status = _synctex_next_line(scanner);
+	if (status<SYNCTEX_STATUS_OK) {
+		return status;
+	}
+	status = _synctex_scan_named(scanner,"Magnification:",&(scanner->pre_magnification),(synctex_decoder_t)&_synctex_decode_int);
+	if (status<SYNCTEX_STATUS_OK) {
+		return status;
+	}
+	status = _synctex_next_line(scanner);
+	if (status<SYNCTEX_STATUS_OK) {
+		return status;
+	}
+	status = _synctex_scan_named(scanner,"Unit:",&(scanner->pre_unit),(synctex_decoder_t)&_synctex_decode_int);
+	if (status<SYNCTEX_STATUS_OK) {
+		return status;
+	}
+	status = _synctex_next_line(scanner);
+	if (status<SYNCTEX_STATUS_OK) {
+		return status;
+	}
+	status = _synctex_scan_named(scanner,"X Offset:",&(scanner->pre_x_offset),(synctex_decoder_t)&_synctex_decode_int);
+	if (status<SYNCTEX_STATUS_OK) {
+		return status;
+	}
+	status = _synctex_next_line(scanner);
+	if (status<SYNCTEX_STATUS_OK) {
+		return status;
+	}
+	status = _synctex_scan_named(scanner,"Y Offset:",&(scanner->pre_y_offset),(synctex_decoder_t)&_synctex_decode_int);
+	if (status<SYNCTEX_STATUS_OK) {
+		return status;
+	}
+	return _synctex_next_line(scanner);
+}
+
+/*  parse a float with a dimension */
+synctex_status_t _synctex_scan_float_and_dimension(synctex_scanner_t scanner, float * value_ref) {
+	synctex_status_t status = 0;
+	char * endptr = NULL;
+	float f = 0;
+#ifdef HAVE_SETLOCALE
+	char * loc = setlocale(LC_NUMERIC, NULL);
+#endif
+	size_t available = 0;
+	if (NULL == scanner || NULL == value_ref) {
+		return SYNCTEX_STATUS_BAD_ARGUMENT;
+	}
+	available = SYNCTEX_BUFFER_MIN_SIZE;
+	status = _synctex_buffer_get_available_size(scanner, &available);
+	if (status<SYNCTEX_STATUS_EOF) {
+		_synctex_error("problem with float.");
+		return status;
+	}
+#ifdef HAVE_SETLOCALE
+	setlocale(LC_NUMERIC, "C");
+#endif
+	f = strtod(SYNCTEX_CUR,&endptr);
+#ifdef HAVE_SETLOCALE
+	setlocale(LC_NUMERIC, loc);
+#endif
+	if (endptr == SYNCTEX_CUR) {
+		_synctex_error("a float was expected.");
+		return SYNCTEX_STATUS_ERROR;
+	}
+	SYNCTEX_CUR = endptr;
+	if ((status = _synctex_match_string(scanner,"in")) >= SYNCTEX_STATUS_OK) {
+		f *= 72.27f*65536;
+	} else if (status<SYNCTEX_STATUS_EOF) {
+report_unit_error:
+		_synctex_error("problem with unit.");
+		return status;
+	} else if ((status = _synctex_match_string(scanner,"cm")) >= SYNCTEX_STATUS_OK) {
+		f *= 72.27f*65536/2.54f;
+	} else if (status<0) {
+		goto report_unit_error;
+	} else if ((status = _synctex_match_string(scanner,"mm")) >= SYNCTEX_STATUS_OK) {
+		f *= 72.27f*65536/25.4f;
+	} else if (status<0) {
+		goto report_unit_error;
+	} else if ((status = _synctex_match_string(scanner,"pt")) >= SYNCTEX_STATUS_OK) {
+		f *= 65536.0f;
+	} else if (status<0) {
+		goto report_unit_error;
+	} else if ((status = _synctex_match_string(scanner,"bp")) >= SYNCTEX_STATUS_OK) {
+		f *= 72.27f/72*65536.0f;
+	}  else if (status<0) {
+		goto report_unit_error;
+	} else if ((status = _synctex_match_string(scanner,"pc")) >= SYNCTEX_STATUS_OK) {
+		f *= 12.0*65536.0f;
+	}  else if (status<0) {
+		goto report_unit_error;
+	} else if ((status = _synctex_match_string(scanner,"sp")) >= SYNCTEX_STATUS_OK) {
+		f *= 1.0f;
+	}  else if (status<0) {
+		goto report_unit_error;
+	} else if ((status = _synctex_match_string(scanner,"dd")) >= SYNCTEX_STATUS_OK) {
+		f *= 1238.0f/1157*65536.0f;
+	}  else if (status<0) {
+		goto report_unit_error;
+	} else if ((status = _synctex_match_string(scanner,"cc")) >= SYNCTEX_STATUS_OK) {
+		f *= 14856.0f/1157*65536;
+	} else if (status<0) {
+		goto report_unit_error;
+	} else if ((status = _synctex_match_string(scanner,"nd")) >= SYNCTEX_STATUS_OK) {
+		f *= 685.0f/642*65536;
+	}  else if (status<0) {
+		goto report_unit_error;
+	} else if ((status = _synctex_match_string(scanner,"nc")) >= SYNCTEX_STATUS_OK) {
+		f *= 1370.0f/107*65536;
+	} else if (status<0) {
+		goto report_unit_error;
+	}
+	*value_ref = f;
+	return SYNCTEX_STATUS_OK;
+}
+
+/*  parse the post scriptum
+ *  SYNCTEX_STATUS_OK is returned on completion
+ *  a negative error is returned otherwise */
+synctex_status_t _synctex_scan_post_scriptum(synctex_scanner_t scanner) {
+	synctex_status_t status = 0;
+	char * endptr = NULL;
+#ifdef HAVE_SETLOCALE
+	char * loc = setlocale(LC_NUMERIC, NULL);
+#endif
+	if (NULL == scanner) {
+		return SYNCTEX_STATUS_BAD_ARGUMENT;
+	}
+	/*  Scan the file until a post scriptum line is found */
+post_scriptum_not_found:
+	status = _synctex_match_string(scanner,"Post scriptum:");
+	if (status<SYNCTEX_STATUS_NOT_OK) {
+		return status;
+	}
+	if (status == SYNCTEX_STATUS_NOT_OK) {
+		status = _synctex_next_line(scanner);
+		if (status<SYNCTEX_STATUS_EOF) {
+			return status;
+		} else if (status<SYNCTEX_STATUS_OK) {
+			return SYNCTEX_STATUS_OK;/*  The EOF is found, we have properly scanned the file */
+		}
+		goto post_scriptum_not_found;
+	}
+	/*  We found the name, advance to the next line. */
+next_line:
+	status = _synctex_next_line(scanner);
+	if (status<SYNCTEX_STATUS_EOF) {
+		return status;
+	} else if (status<SYNCTEX_STATUS_OK) {
+		return SYNCTEX_STATUS_OK;/*  The EOF is found, we have properly scanned the file */
+	}
+	/*  Scanning the information */
+	status = _synctex_match_string(scanner,"Magnification:");
+	if (status == SYNCTEX_STATUS_OK ) {
+#ifdef HAVE_SETLOCALE
+		setlocale(LC_NUMERIC, "C");
+#endif
+		scanner->unit = strtod(SYNCTEX_CUR,&endptr);
+#ifdef HAVE_SETLOCALE
+		setlocale(LC_NUMERIC, loc);
+#endif
+		if (endptr == SYNCTEX_CUR) {
+			_synctex_error("bad magnification in the post scriptum, a float was expected.");
+			return SYNCTEX_STATUS_ERROR;
+		}
+		if (scanner->unit<=0) {
+			_synctex_error("bad magnification in the post scriptum, a positive float was expected.");
+			return SYNCTEX_STATUS_ERROR;
+		}
+		SYNCTEX_CUR = endptr;
+		goto next_line;
+	}
+	if (status<SYNCTEX_STATUS_EOF){
+report_record_problem:
+		_synctex_error("Problem reading the Post Scriptum records");
+		return status; /*  echo the error. */
+	}
+	status = _synctex_match_string(scanner,"X Offset:");
+	if (status == SYNCTEX_STATUS_OK) {
+		status = _synctex_scan_float_and_dimension(scanner, &(scanner->x_offset));
+		if (status<SYNCTEX_STATUS_OK) {
+			_synctex_error("problem with X offset in the Post Scriptum.");
+			return status;
+		}
+		goto next_line;
+	} else if (status<SYNCTEX_STATUS_EOF){
+		goto report_record_problem;
+	}
+	status = _synctex_match_string(scanner,"Y Offset:");
+	if (status==SYNCTEX_STATUS_OK) {
+		status = _synctex_scan_float_and_dimension(scanner, &(scanner->y_offset));
+		if (status<SYNCTEX_STATUS_OK) {
+			_synctex_error("problem with Y offset in the Post Scriptum.");
+			return status;
+		}
+		goto next_line;
+	} else if (status<SYNCTEX_STATUS_EOF){
+		goto report_record_problem;
+	}
+	goto next_line;
+}
+
+/*  SYNCTEX_STATUS_OK is returned if the postamble is read
+ *  SYNCTEX_STATUS_NOT_OK is returned if the postamble is not at the current location
+ *  a negative error otherwise
+ *  The postamble comprises the post scriptum section.
+ */
+int _synctex_scan_postamble(synctex_scanner_t scanner) {
+	int status = 0;
+	if (NULL == scanner) {
+		return SYNCTEX_STATUS_BAD_ARGUMENT;
+	}
+	status = _synctex_match_string(scanner,"Postamble:");
+	if (status < SYNCTEX_STATUS_OK) {
+		return status;
+	}
+count_again:
+	status = _synctex_next_line(scanner);
+	if (status < SYNCTEX_STATUS_OK) {
+		return status;
+	}
+	status = _synctex_scan_named(scanner,"Count:",&(scanner->count),(synctex_decoder_t)&_synctex_decode_int);
+	if (status < SYNCTEX_STATUS_EOF) {
+		return status; /*  forward the error */
+	} else if (status < SYNCTEX_STATUS_OK) { /*  No Count record found */
+		status = _synctex_next_line(scanner); /*  Advance one more line */
+		if (status<SYNCTEX_STATUS_OK) {
+			return status;
+		}
+		goto count_again;
+	}
+	/*  Now we scan the last part of the SyncTeX file: the Post Scriptum section. */
+	return _synctex_scan_post_scriptum(scanner);
+}
+
+/*  Horizontal boxes also have visible size.
+ *  Visible size are bigger than real size.
+ *  For example 0 width boxes may contain text.
+ *  At creation time, the visible size is set to the values of the real size.
+ */
+synctex_status_t _synctex_setup_visible_box(synctex_node_t box) {
+	if (box) {
+		switch(box->class->type) {
+			case synctex_node_type_hbox:
+				if (SYNCTEX_INFO(box) != NULL) {
+					SYNCTEX_HORIZ_V(box)  = SYNCTEX_HORIZ(box);
+					SYNCTEX_VERT_V(box)   = SYNCTEX_VERT(box);
+					SYNCTEX_WIDTH_V(box)  = SYNCTEX_WIDTH(box);
+					SYNCTEX_HEIGHT_V(box) = SYNCTEX_HEIGHT(box);
+					SYNCTEX_DEPTH_V(box)  = SYNCTEX_DEPTH(box);
+					return SYNCTEX_STATUS_OK;
+				}
+				return SYNCTEX_STATUS_ERROR;
+		}
+	}
+	return SYNCTEX_STATUS_BAD_ARGUMENT;
+}
+
+/*  This method is sent to an horizontal box to setup the visible size
+ *  Some box have 0 width but do contain text material.
+ *  With this method, one can enlarge the box to contain the given point (h,v).
+ */
+synctex_status_t _synctex_horiz_box_setup_visible(synctex_node_t node,int h, int v) {
+#	ifdef __DARWIN_UNIX03
+#       pragma unused(v)
+#   endif
+	int itsBtm, itsTop;
+	if (NULL == node || node->class->type != synctex_node_type_hbox) {
+		return SYNCTEX_STATUS_BAD_ARGUMENT;
+	}
+	if (SYNCTEX_WIDTH_V(node)<0) {
+		itsBtm = SYNCTEX_HORIZ_V(node);
+		itsTop = SYNCTEX_HORIZ_V(node)-SYNCTEX_WIDTH_V(node);
+		if (h<itsBtm) {
+			SYNCTEX_HORIZ_V(node) = h;
+			SYNCTEX_WIDTH_V(node) = SYNCTEX_HORIZ_V(node) - itsTop;
+		} else if (h>itsTop) {
+			SYNCTEX_WIDTH_V(node) = SYNCTEX_HORIZ_V(node) - h;
+		}
+	} else {
+		itsBtm = SYNCTEX_HORIZ_V(node);
+		itsTop = SYNCTEX_HORIZ_V(node)+SYNCTEX_WIDTH_V(node);
+		if (h<itsBtm) {
+			SYNCTEX_HORIZ_V(node) = h;
+			SYNCTEX_WIDTH_V(node) = itsTop - SYNCTEX_HORIZ_V(node);
+		} else if (h>itsTop) {
+			SYNCTEX_WIDTH_V(node) = h - SYNCTEX_HORIZ_V(node);
+		}
+	}
+	return SYNCTEX_STATUS_OK;
+}
+
+/*  Here are the control characters that strat each line of the synctex output file.
+ *  Their values define the meaning of the line.
+ */
+#   define SYNCTEX_CHAR_BEGIN_SHEET '{'
+#   define SYNCTEX_CHAR_END_SHEET   '}'
+#   define SYNCTEX_CHAR_BEGIN_VBOX  '['
+#   define SYNCTEX_CHAR_END_VBOX    ']'
+#   define SYNCTEX_CHAR_BEGIN_HBOX  '('
+#   define SYNCTEX_CHAR_END_HBOX    ')'
+#   define SYNCTEX_CHAR_ANCHOR      '!'
+#   define SYNCTEX_CHAR_VOID_VBOX   'v'
+#   define SYNCTEX_CHAR_VOID_HBOX   'h'
+#   define SYNCTEX_CHAR_KERN        'k'
+#   define SYNCTEX_CHAR_GLUE        'g'
+#   define SYNCTEX_CHAR_MATH        '$'
+#   define SYNCTEX_CHAR_BOUNDARY    'x'
+
+#   define SYNCTEX_RETURN(STATUS) return STATUS;
+
+/*  Used when parsing the synctex file. A '{' character has just been parsed.
+ *  The purpose is to gobble everything until the closing '}'.
+ *  Actually only one nesting depth has been observed when using the clip option
+ *  of \includegraphics option. Here we use arbitrary level of depth.
+ */
+synctex_status_t _synctex_scan_nested_sheet(synctex_scanner_t scanner) {
+    unsigned int depth = 0;
+deeper:
+    ++depth;
+    if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
+        _synctex_error("Unexpected end of nested sheet (1).");
+        SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+    }
+scan_next_line:
+    if (SYNCTEX_CUR<SYNCTEX_END) {
+		if (*SYNCTEX_CUR == SYNCTEX_CHAR_END_SHEET) {
+			++SYNCTEX_CUR;
+			if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
+				_synctex_error("Unexpected end of nested sheet (2).");
+				SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+			}
+            if (--depth>0) {
+                goto scan_next_line;
+            } else {
+            	SYNCTEX_RETURN(SYNCTEX_STATUS_OK);
+            }
+		} else if (*SYNCTEX_CUR == SYNCTEX_CHAR_BEGIN_SHEET) {
+			++SYNCTEX_CUR;
+			goto deeper;
+            
+		} else if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
+            _synctex_error("Unexpected end of nested sheet (3).");
+            SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+        }
+    }
+    _synctex_error("Unexpected end of nested sheet (4).");
+    SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+}
+
+/*  Used when parsing the synctex file.
+ *  The sheet argument is a newly created sheet node that will hold the contents.
+ *  Something is returned in case of error.
+ */
+synctex_status_t _synctex_scan_sheet(synctex_scanner_t scanner, synctex_node_t sheet) {
+	synctex_node_t parent = sheet;
+	synctex_node_t child = NULL;
+	synctex_node_t sibling = NULL;
+	synctex_node_t box = sheet;
+	int friend_index = 0;
+	synctex_info_t * info = NULL;
+	synctex_status_t status = 0;
+	size_t available = 0;
+	if ((NULL == scanner) || (NULL == sheet)) {
+		return SYNCTEX_STATUS_BAD_ARGUMENT;
+	}
+	/*  We MUST start with a box, so at this level, the unique possibility is '[', '(' or "}". */
+prepare_loop:
+	if (SYNCTEX_CUR<SYNCTEX_END) {
+		if (*SYNCTEX_CUR == SYNCTEX_CHAR_BEGIN_VBOX) {
+scan_vbox:
+			++SYNCTEX_CUR;
+			if ((child = _synctex_new_vbox(scanner)) && (info = SYNCTEX_INFO(child))) {
+#               define SYNCTEX_DECODE_FAILED(WHAT) \
+					(_synctex_decode_int(scanner,&(info[WHAT].INT))<SYNCTEX_STATUS_OK)
+				if (SYNCTEX_DECODE_FAILED(SYNCTEX_TAG_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_LINE_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_HORIZ_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_VERT_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_WIDTH_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_HEIGHT_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_DEPTH_IDX)
+						|| _synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
+					_synctex_error("Bad vbox record.");
+					SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+				}
+				SYNCTEX_SET_CHILD(parent,child);
+				parent = child;
+				child = NULL;
+				goto child_loop;/*  next created node will be a child */
+			} else {
+				_synctex_error("Can't create vbox record.");
+				SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+			}
+		} else if (*SYNCTEX_CUR == SYNCTEX_CHAR_BEGIN_HBOX) {
+scan_hbox:
+			++SYNCTEX_CUR;
+			if ((child = _synctex_new_hbox(scanner)) && (info = SYNCTEX_INFO(child))) {
+				if (SYNCTEX_DECODE_FAILED(SYNCTEX_TAG_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_LINE_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_HORIZ_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_VERT_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_WIDTH_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_HEIGHT_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_DEPTH_IDX)
+						|| _synctex_setup_visible_box(child)<SYNCTEX_STATUS_OK
+						|| _synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
+					_synctex_error("Bad hbox record.");
+					SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+				}
+				_synctex_horiz_box_setup_visible(parent,SYNCTEX_HORIZ(child),SYNCTEX_VERT(child));
+				_synctex_horiz_box_setup_visible(parent,SYNCTEX_HORIZ(child)+SYNCTEX_ABS_WIDTH(child),SYNCTEX_VERT(child));
+				SYNCTEX_SET_CHILD(parent,child);
+				parent = child;
+				child = NULL;
+				goto child_loop;/*  next created node will be a child */
+			} else {
+				_synctex_error("Can't create hbox record.");
+				SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+			}
+		} else if (*SYNCTEX_CUR == SYNCTEX_CHAR_END_SHEET) {
+scan_teehs:
+			++SYNCTEX_CUR;
+			if (NULL == parent || parent->class->type != synctex_node_type_sheet
+					|| _synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
+				_synctex_error("Unexpected end of sheet.");
+				SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+			}
+			SYNCTEX_RETURN(SYNCTEX_STATUS_OK);
+		} else if (*SYNCTEX_CUR == SYNCTEX_CHAR_BEGIN_SHEET) {
+			/*  Addendum to version 1.10 to manage nested sheets  */
+			++SYNCTEX_CUR;
+			if (_synctex_scan_nested_sheet(scanner)<SYNCTEX_STATUS_OK) {
+				_synctex_error("Unexpected nested sheet.");
+				SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+			}
+			goto prepare_loop;
+		} else if (*SYNCTEX_CUR == SYNCTEX_CHAR_ANCHOR) {
+scan_anchor:
+			++SYNCTEX_CUR;
+			if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
+				_synctex_error("Missing anchor.");
+				SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+			}
+			goto prepare_loop;
+		} else {
+			/*  _synctex_error("Ignored record %c\n",*SYNCTEX_CUR); */
+			++SYNCTEX_CUR;
+			if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
+				_synctex_error("Unexpected end.");
+				SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+			}
+			goto prepare_loop;
+		}
+	} else {
+		available = 1;
+		status = _synctex_buffer_get_available_size(scanner,&available);
+		 if (status<SYNCTEX_STATUS_OK && available>0){
+			_synctex_error("Uncomplete sheet(0)");
+			SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+		} else {
+			goto prepare_loop;
+		}
+	}
+	_synctex_bail();
+/*  The child loop means that we go do one level, when we just created a box node,
+ *  the next node created is a child of this box. */
+child_loop:
+	if (SYNCTEX_CUR<SYNCTEX_END) {
+		if (*SYNCTEX_CUR == SYNCTEX_CHAR_BEGIN_VBOX) {
+			goto scan_vbox;
+		} else if (*SYNCTEX_CUR == SYNCTEX_CHAR_END_VBOX) {
+scan_xobv:
+			++SYNCTEX_CUR;
+			if (NULL != parent && parent->class->type == synctex_node_type_vbox) {
+				#define SYNCTEX_UPDATE_BOX_FRIEND(NODE)\
+				friend_index = ((SYNCTEX_INFO(NODE))[SYNCTEX_TAG_IDX].INT+(SYNCTEX_INFO(NODE))[SYNCTEX_LINE_IDX].INT)%(scanner->number_of_lists);\
+				SYNCTEX_SET_FRIEND(NODE,(scanner->lists_of_friends)[friend_index]);\
+				(scanner->lists_of_friends)[friend_index] = NODE;
+				if (NULL == SYNCTEX_CHILD(parent)) {
+					/*  only void boxes are friends */
+					SYNCTEX_UPDATE_BOX_FRIEND(parent);
+				}
+				child = parent;
+				parent = SYNCTEX_PARENT(child);
+			} else {
+				_synctex_error("Unexpected end of vbox, ignored.");
+			}
+			if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
+				_synctex_error("Uncomplete sheet.");
+				SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+			}
+			goto sibling_loop;
+		} else if (*SYNCTEX_CUR == SYNCTEX_CHAR_BEGIN_HBOX) {
+			goto scan_hbox;
+		} else if (*SYNCTEX_CUR == SYNCTEX_CHAR_END_HBOX) {
+scan_xobh:
+			++SYNCTEX_CUR;
+			if ((parent) && parent->class->type == synctex_node_type_hbox) {
+				if (NULL == child) {
+					/*  Only boxes with no children are friends,
+					 *  boxes with children are indirectly friends through one of their descendants. */
+					SYNCTEX_UPDATE_BOX_FRIEND(parent);
+				}
+				/*  setting the next horizontal box at the end ensures that a child is recorded before any of its ancestors. */
+				SYNCTEX_SET_NEXT_HORIZ_BOX(box,parent);
+				box = parent;
+				child = parent;
+				parent = SYNCTEX_PARENT(child);
+			} else {
+				_synctex_error("Unexpected enf of hbox, ignored.");
+			}
+			if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
+				_synctex_error("Uncomplete sheet.");
+				SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+			}
+			goto sibling_loop;
+		} else if (*SYNCTEX_CUR == SYNCTEX_CHAR_VOID_VBOX) {
+			++SYNCTEX_CUR;
+			if (NULL != (child = _synctex_new_void_vbox(scanner))
+					&& NULL != (info = SYNCTEX_INFO(child))) {
+				if (SYNCTEX_DECODE_FAILED(SYNCTEX_TAG_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_LINE_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_HORIZ_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_VERT_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_WIDTH_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_HEIGHT_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_DEPTH_IDX)
+						|| _synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
+					_synctex_error("Bad void vbox record.");
+					SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+				}
+				SYNCTEX_SET_CHILD(parent,child);
+				#define SYNCTEX_UPDATE_FRIEND(NODE)\
+				friend_index = (info[SYNCTEX_TAG_IDX].INT+info[SYNCTEX_LINE_IDX].INT)%(scanner->number_of_lists);\
+				SYNCTEX_SET_FRIEND(NODE,(scanner->lists_of_friends)[friend_index]);\
+				(scanner->lists_of_friends)[friend_index] = NODE;
+				SYNCTEX_UPDATE_FRIEND(child);
+				goto sibling_loop;
+			} else {
+				_synctex_error("Can't create vbox record.");
+				SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+			}
+		} else if (*SYNCTEX_CUR == SYNCTEX_CHAR_VOID_HBOX) {
+			++SYNCTEX_CUR;
+			if (NULL != (child = _synctex_new_void_hbox(scanner))
+					&& NULL != (info = SYNCTEX_INFO(child))) {
+				if (SYNCTEX_DECODE_FAILED(SYNCTEX_TAG_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_LINE_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_HORIZ_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_VERT_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_WIDTH_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_HEIGHT_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_DEPTH_IDX)
+						|| _synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
+					_synctex_error("Bad void hbox record.");
+					SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+				}
+				SYNCTEX_SET_CHILD(parent,child);
+				SYNCTEX_UPDATE_FRIEND(child);
+				_synctex_horiz_box_setup_visible(parent,SYNCTEX_HORIZ(child),SYNCTEX_VERT(child));
+				_synctex_horiz_box_setup_visible(parent,SYNCTEX_HORIZ(child)+SYNCTEX_ABS_WIDTH(child),SYNCTEX_VERT(child));
+				goto sibling_loop;
+			} else {
+				_synctex_error("Can't create void hbox record.");
+				SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+			}
+		} else if (*SYNCTEX_CUR == SYNCTEX_CHAR_KERN) {
+			++SYNCTEX_CUR;
+			if (NULL != (child = _synctex_new_kern(scanner))
+					&& NULL != (info = SYNCTEX_INFO(child))) {
+				if (SYNCTEX_DECODE_FAILED(SYNCTEX_TAG_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_LINE_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_HORIZ_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_VERT_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_WIDTH_IDX)
+						|| _synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
+					_synctex_error("Bad kern record.");
+					SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+				}
+				SYNCTEX_SET_CHILD(parent,child);
+				SYNCTEX_UPDATE_FRIEND(child);
+				_synctex_horiz_box_setup_visible(parent,SYNCTEX_HORIZ(child),SYNCTEX_VERT(child));
+				_synctex_horiz_box_setup_visible(parent,SYNCTEX_HORIZ(child)-SYNCTEX_WIDTH(child),SYNCTEX_VERT(child));
+				goto sibling_loop;
+			} else {
+				_synctex_error("Can't create kern record.");
+				SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+			}
+		} else if (*SYNCTEX_CUR == SYNCTEX_CHAR_GLUE) {
+			++SYNCTEX_CUR;
+			if (NULL != (child = _synctex_new_glue(scanner))
+					&& NULL != (info = SYNCTEX_INFO(child))) {
+				if (SYNCTEX_DECODE_FAILED(SYNCTEX_TAG_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_LINE_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_HORIZ_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_VERT_IDX)
+						|| _synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
+					_synctex_error("Bad glue record.");
+					SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+				}
+				SYNCTEX_SET_CHILD(parent,child);
+				_synctex_horiz_box_setup_visible(parent,SYNCTEX_HORIZ(child),SYNCTEX_VERT(child));
+				SYNCTEX_UPDATE_FRIEND(child);
+				goto sibling_loop;
+			} else {
+				_synctex_error("Can't create glue record.");
+				SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+			}
+		} else if (*SYNCTEX_CUR == SYNCTEX_CHAR_MATH) {
+			++SYNCTEX_CUR;
+			if (NULL != (child = _synctex_new_math(scanner))
+					&& NULL != (info = SYNCTEX_INFO(child))) {
+				if (SYNCTEX_DECODE_FAILED(SYNCTEX_TAG_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_LINE_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_HORIZ_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_VERT_IDX)
+						|| _synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
+					_synctex_error("Bad math record.");
+					SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+				}
+				SYNCTEX_SET_CHILD(parent,child);
+				_synctex_horiz_box_setup_visible(parent,SYNCTEX_HORIZ(child),SYNCTEX_VERT(child));
+				SYNCTEX_UPDATE_FRIEND(child);
+				goto sibling_loop;
+			} else {
+				_synctex_error("Can't create math record.");
+				SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+			}
+		} else if (*SYNCTEX_CUR == SYNCTEX_CHAR_BOUNDARY) {
+			++SYNCTEX_CUR;
+			if (NULL != (child = _synctex_new_boundary(scanner))
+					&& NULL != (info = SYNCTEX_INFO(child))) {
+				if (SYNCTEX_DECODE_FAILED(SYNCTEX_TAG_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_LINE_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_HORIZ_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_VERT_IDX)
+						|| _synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
+					_synctex_error("Bad boundary record.");
+					SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+				}
+				SYNCTEX_SET_CHILD(parent,child);
+				_synctex_horiz_box_setup_visible(parent,SYNCTEX_HORIZ(child),SYNCTEX_VERT(child));
+				SYNCTEX_UPDATE_FRIEND(child);
+				goto sibling_loop;
+			} else {
+				_synctex_error("Can't create math record.");
+				SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+			}
+		} else if (*SYNCTEX_CUR == SYNCTEX_CHAR_END_SHEET) {
+			goto scan_teehs;
+		} else if (*SYNCTEX_CUR == SYNCTEX_CHAR_BEGIN_SHEET) {
+			/*  Addendum to version 1.10 to manage nested sheets  */
+			++SYNCTEX_CUR;
+			if (_synctex_scan_nested_sheet(scanner)<SYNCTEX_STATUS_OK) {
+				_synctex_error("Unexpected nested sheet.");
+				SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+			}
+			goto child_loop;
+		} else if (*SYNCTEX_CUR == SYNCTEX_CHAR_ANCHOR) {
+			goto scan_anchor;
+		} else {
+			/*  _synctex_error("Ignored record %c\n",*SYNCTEX_CUR); */
+			++SYNCTEX_CUR;
+			if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
+				_synctex_error("Unexpected end.");
+				SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+			}
+			goto child_loop;
+		}
+	} else {
+		available = 1;
+		status = _synctex_buffer_get_available_size(scanner,&available);
+		 if (status<SYNCTEX_STATUS_OK && available>0){
+			_synctex_error("Uncomplete sheet(0)");
+			SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+		} else {
+			goto child_loop;
+		}
+	}
+	_synctex_bail();
+/*  The vertical loop means that we are on the same level, for example when we just ended a box.
+ *  If a node is created now, it will be a sibling of the current node, sharing the same parent. */
+sibling_loop:
+	if (SYNCTEX_CUR<SYNCTEX_END) {
+		if (*SYNCTEX_CUR == SYNCTEX_CHAR_BEGIN_VBOX) {
+			++SYNCTEX_CUR;
+			if (NULL != (sibling = _synctex_new_vbox(scanner))
+					&& NULL != (info = SYNCTEX_INFO(sibling))) {
+				if (SYNCTEX_DECODE_FAILED(SYNCTEX_TAG_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_LINE_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_HORIZ_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_VERT_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_WIDTH_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_HEIGHT_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_DEPTH_IDX)
+						|| _synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
+					_synctex_error("Bad vbox record (2).");
+					SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+				}
+				SYNCTEX_SET_SIBLING(child,sibling);
+				parent = sibling;
+				child = NULL;
+				goto child_loop;
+			} else {
+				_synctex_error("Can't create vbox record (2).");
+				SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+			}
+		} else if (*SYNCTEX_CUR == SYNCTEX_CHAR_END_VBOX) {
+			goto scan_xobv;
+		} else if (*SYNCTEX_CUR == SYNCTEX_CHAR_BEGIN_HBOX) {
+			++SYNCTEX_CUR;
+			if (NULL != (sibling = _synctex_new_hbox(scanner)) &&
+					NULL != (info = SYNCTEX_INFO(sibling))) {
+				if (SYNCTEX_DECODE_FAILED(SYNCTEX_TAG_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_LINE_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_HORIZ_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_VERT_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_WIDTH_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_HEIGHT_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_DEPTH_IDX)
+						|| _synctex_setup_visible_box(sibling)<SYNCTEX_STATUS_OK
+						|| _synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
+					_synctex_error("Bad hbox record (2).");
+					SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+				}
+				SYNCTEX_SET_SIBLING(child,sibling);
+				child = sibling;
+				_synctex_horiz_box_setup_visible(parent,SYNCTEX_HORIZ(child),SYNCTEX_VERT(child));
+				_synctex_horiz_box_setup_visible(parent,SYNCTEX_HORIZ(child)+SYNCTEX_ABS_WIDTH(child),SYNCTEX_VERT(child));
+				parent = child;
+				child = NULL;
+				goto child_loop;
+			} else {
+				_synctex_error("Can't create hbox record (2).");
+				SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+			}
+		} else if (*SYNCTEX_CUR == SYNCTEX_CHAR_END_HBOX) {
+			goto scan_xobh;
+		} else if (*SYNCTEX_CUR == SYNCTEX_CHAR_VOID_VBOX) {
+			++SYNCTEX_CUR;
+			if (NULL != (sibling = _synctex_new_void_vbox(scanner)) &&
+					NULL != (info = SYNCTEX_INFO(sibling))) {
+				if (SYNCTEX_DECODE_FAILED(SYNCTEX_TAG_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_LINE_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_HORIZ_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_VERT_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_WIDTH_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_HEIGHT_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_DEPTH_IDX)
+						|| _synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
+					_synctex_error("Bad void vbox record (2).");
+					SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+				}
+				SYNCTEX_SET_SIBLING(child,sibling);
+				child = sibling;
+				SYNCTEX_UPDATE_FRIEND(child);
+				goto sibling_loop;
+			} else {
+				_synctex_error("can't create void vbox record (2).");
+				SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+			}
+		} else if (*SYNCTEX_CUR == SYNCTEX_CHAR_VOID_HBOX) {
+			++SYNCTEX_CUR;
+			if (NULL != (sibling = _synctex_new_void_hbox(scanner)) &&
+					NULL != (info = SYNCTEX_INFO(sibling))) {
+				if (SYNCTEX_DECODE_FAILED(SYNCTEX_TAG_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_LINE_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_HORIZ_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_VERT_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_WIDTH_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_HEIGHT_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_DEPTH_IDX)
+						|| _synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
+					_synctex_error("Bad void hbox record (2).");
+					SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+				}
+				SYNCTEX_SET_SIBLING(child,sibling);
+				child = sibling;
+				SYNCTEX_UPDATE_FRIEND(child);
+				_synctex_horiz_box_setup_visible(parent,SYNCTEX_HORIZ(child),SYNCTEX_VERT(child));
+				_synctex_horiz_box_setup_visible(parent,SYNCTEX_HORIZ(child)+SYNCTEX_ABS_WIDTH(child),SYNCTEX_VERT(child));
+				goto sibling_loop;
+			} else {
+				_synctex_error("can't create void hbox record (2).");
+				SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+			}
+		} else if (*SYNCTEX_CUR == SYNCTEX_CHAR_KERN) {
+			++SYNCTEX_CUR;
+			if (NULL != (sibling = _synctex_new_kern(scanner))
+					&& NULL != (info = SYNCTEX_INFO(sibling))) {
+				if (SYNCTEX_DECODE_FAILED(SYNCTEX_TAG_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_LINE_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_HORIZ_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_VERT_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_WIDTH_IDX)
+						|| _synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
+					_synctex_error("Bad kern record (2).");
+					SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+				}
+				SYNCTEX_SET_SIBLING(child,sibling);
+				child = sibling;
+				SYNCTEX_UPDATE_FRIEND(child);
+				_synctex_horiz_box_setup_visible(parent,SYNCTEX_HORIZ(child),SYNCTEX_VERT(child));
+				_synctex_horiz_box_setup_visible(parent,SYNCTEX_HORIZ(child)-SYNCTEX_WIDTH(child),SYNCTEX_VERT(child));
+				goto sibling_loop;
+			} else {
+				_synctex_error("Can't create kern record (2).");
+				SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+			}
+		} else if (*SYNCTEX_CUR == SYNCTEX_CHAR_GLUE) {
+			++SYNCTEX_CUR;
+			if (NULL != (sibling = _synctex_new_glue(scanner))
+					&& NULL != (info = SYNCTEX_INFO(sibling))) {
+				if (SYNCTEX_DECODE_FAILED(SYNCTEX_TAG_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_LINE_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_HORIZ_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_VERT_IDX)
+						|| _synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
+					_synctex_error("Bad glue record (2).");
+					SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+				}
+				SYNCTEX_SET_SIBLING(child,sibling);
+				child = sibling;
+				SYNCTEX_UPDATE_FRIEND(child);
+				_synctex_horiz_box_setup_visible(parent,SYNCTEX_HORIZ(child),SYNCTEX_VERT(child));
+				goto sibling_loop;
+			} else {
+				_synctex_error("Can't create glue record (2).");
+				SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+			}
+		} else if (*SYNCTEX_CUR == SYNCTEX_CHAR_MATH) {
+			++SYNCTEX_CUR;
+			if (NULL != (sibling = _synctex_new_math(scanner))
+					&& NULL != (info = SYNCTEX_INFO(sibling))) {
+				if (SYNCTEX_DECODE_FAILED(SYNCTEX_TAG_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_LINE_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_HORIZ_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_VERT_IDX)
+						|| _synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
+					_synctex_error("Bad math record (2).");
+					SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+				}
+				SYNCTEX_SET_SIBLING(child,sibling);
+				child = sibling;
+				SYNCTEX_UPDATE_FRIEND(child);
+				_synctex_horiz_box_setup_visible(parent,SYNCTEX_HORIZ(child),SYNCTEX_VERT(child));
+				goto sibling_loop;
+			} else {
+				_synctex_error("Can't create math record (2).");
+				SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+			}
+		} else if (*SYNCTEX_CUR == SYNCTEX_CHAR_BOUNDARY) {
+			++SYNCTEX_CUR;
+			if (NULL != (sibling = _synctex_new_boundary(scanner))
+					&& NULL != (info = SYNCTEX_INFO(sibling))) {
+				if (SYNCTEX_DECODE_FAILED(SYNCTEX_TAG_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_LINE_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_HORIZ_IDX)
+						|| SYNCTEX_DECODE_FAILED(SYNCTEX_VERT_IDX)
+						|| _synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
+					_synctex_error("Bad boundary record (2).");
+					SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+				}
+				SYNCTEX_SET_SIBLING(child,sibling);
+				child = sibling;
+				SYNCTEX_UPDATE_FRIEND(child);
+				_synctex_horiz_box_setup_visible(parent,SYNCTEX_HORIZ(child),SYNCTEX_VERT(child));
+				goto sibling_loop;
+			} else {
+				_synctex_error("Can't create boundary record (2).");
+				SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+			}
+		} else if (*SYNCTEX_CUR == SYNCTEX_CHAR_END_SHEET) {
+			goto scan_teehs;
+		} else if (*SYNCTEX_CUR == SYNCTEX_CHAR_ANCHOR) {
+			++SYNCTEX_CUR;
+			if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
+				_synctex_error("Missing anchor (2).");
+				SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+			}
+			goto sibling_loop;
+		} else {
+			++SYNCTEX_CUR;
+			/* _synctex_error("Ignored record %c(2)\n",*SYNCTEX_CUR); */
+			if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
+				SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+			}
+			goto sibling_loop;
+		}
+	} else {
+		available = 1;
+		status = _synctex_buffer_get_available_size(scanner,&available);
+		if (status<SYNCTEX_STATUS_OK && available>0){
+			goto sibling_loop;
+		} else {
+			_synctex_error("Uncomplete sheet(2)");
+			SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+		}
+	}
+#   undef SYNCTEX_DECODE_FAILED
+}
+
+/*  Used when parsing the synctex file
+ */
+synctex_status_t _synctex_scan_content(synctex_scanner_t scanner) {
+	synctex_node_t sheet = NULL;
+	synctex_status_t status = 0;
+	if (NULL == scanner) {
+		return SYNCTEX_STATUS_BAD_ARGUMENT;
+	}
+	/*  set up the lists of friends */
+	if (NULL == scanner->lists_of_friends) {
+		scanner->number_of_lists = 1024;
+		scanner->lists_of_friends = (synctex_node_t *)_synctex_malloc(scanner->number_of_lists*sizeof(synctex_node_t));
+		if (NULL == scanner->lists_of_friends) {
+			_synctex_error("malloc:2");
+			return SYNCTEX_STATUS_ERROR;
+		}
+	}
+	/*  Find where this section starts */
+content_not_found:
+	status = _synctex_match_string(scanner,"Content:");
+	if (status<SYNCTEX_STATUS_EOF) {
+		return status;
+	}
+	if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
+		_synctex_error("Uncomplete Content.");
+		return SYNCTEX_STATUS_ERROR;
+	}
+	if (status == SYNCTEX_STATUS_NOT_OK) {
+		goto content_not_found;
+	}
+next_sheet:
+	if (*SYNCTEX_CUR != SYNCTEX_CHAR_BEGIN_SHEET) {
+		status = _synctex_scan_postamble(scanner);
+		if (status < SYNCTEX_STATUS_EOF) {
+			_synctex_error("Bad content.");
+			return status;
+		}
+		if (status<SYNCTEX_STATUS_OK) {
+			status = _synctex_next_line(scanner);
+			if (status < SYNCTEX_STATUS_OK) {
+				_synctex_error("Bad content.");
+				return status;
+			}
+			goto next_sheet;
+		}
+		return SYNCTEX_STATUS_OK;
+	}
+	++SYNCTEX_CUR;
+	/*  Create a new sheet node */
+	sheet = _synctex_new_sheet(scanner);
+	status = _synctex_decode_int(scanner,&(SYNCTEX_PAGE(sheet)));
+	if (status<SYNCTEX_STATUS_OK) {
+		_synctex_error("Missing sheet number.");
+bail:
+		SYNCTEX_FREE(sheet);
+		return SYNCTEX_STATUS_ERROR;
+	}
+	status = _synctex_next_line(scanner);
+	if (status<SYNCTEX_STATUS_OK) {
+		_synctex_error("Uncomplete file.");
+		goto bail;
+	}
+	status = _synctex_scan_sheet(scanner,sheet);
+	if (status<SYNCTEX_STATUS_OK) {
+		_synctex_error("Bad sheet content.");
+		goto bail;
+	}
+	SYNCTEX_SET_SIBLING(sheet,scanner->sheet);
+	scanner->sheet = sheet;
+	sheet = NULL;
+	/*  Now read the list of Inputs between 2 sheets. */
+	do {
+		status = _synctex_scan_input(scanner);
+		if (status<SYNCTEX_STATUS_EOF) {
+			_synctex_error("Bad input section.");
+			goto bail;
+		}
+	}
+	while(status >= SYNCTEX_STATUS_OK);
+	goto next_sheet;
+}
+
+int _synctex_open(const char * output, const char * build_directory, char ** synctex_name_ref, gzFile * file_ref, synctex_bool_t add_quotes, synctex_io_mode_t * io_modeRef);
+
+/*  Where the synctex scanner is created. */
+synctex_scanner_t synctex_scanner_new_with_output_file(const char * output, const char * build_directory, int parse) {
+	gzFile file = NULL;
+	char * synctex = NULL;
+	synctex_scanner_t scanner = NULL;
+	synctex_io_mode_t io_mode = 0;
+	/*  Here we assume that int are smaller than void * */
+	if (sizeof(int)>sizeof(void*)) {
+		_synctex_error("INTERNAL INCONSISTENCY: int's are unexpectedly bigger than pointers, bailing out.");
+		return NULL;
+	}
+	/*  We ensure that SYNCTEX_BUFFER_SIZE < UINT_MAX, I don't know if it makes sense... */
+	if (SYNCTEX_BUFFER_SIZE >= UINT_MAX) {
+		_synctex_error("SyncTeX BUG: Internal inconsistency, bad SYNCTEX_BUFFER_SIZE (1)");
+		return NULL;
+	}
+	/*  for integers: */
+	if (SYNCTEX_BUFFER_SIZE < SYNCTEX_BUFFER_MIN_SIZE) {
+		_synctex_error("SyncTeX BUG: Internal inconsistency, bad SYNCTEX_BUFFER_SIZE (2)");
+		return NULL;
+	}
+	/*  now open the synctex file */
+	if (_synctex_open(output,build_directory,&synctex,&file,synctex_ADD_QUOTES,&io_mode) || !file) {
+		if (_synctex_open(output,build_directory,&synctex,&file,synctex_DONT_ADD_QUOTES,&io_mode) || !file) {
+			return NULL;
+		}
+	}
+	scanner = (synctex_scanner_t)_synctex_malloc(sizeof(_synctex_scanner_t));
+	if (NULL == scanner) {
+		_synctex_error("SyncTeX: malloc problem");
+		free(synctex);
+		gzclose(file);
+		return NULL;
+	}
+	/*  make a private copy of output for the scanner */
+	if (NULL == (scanner->output = (char *)malloc(strlen(output)+1))){
+		_synctex_error("!  synctex_scanner_new_with_output_file: Memory problem (2), scanner's output is not reliable.");
+	} else if (scanner->output != strcpy(scanner->output,output)) {
+		_synctex_error("!  synctex_scanner_new_with_output_file: Copy problem, scanner's output is not reliable.");
+	}
+	scanner->synctex = synctex;/*  Now the scanner owns synctex */
+	SYNCTEX_FILE = file;
+	return parse? synctex_scanner_parse(scanner):scanner;
+}
+
+int __synctex_open(const char * output, char ** synctex_name_ref, gzFile * file_ref, synctex_bool_t add_quotes, synctex_io_mode_t * io_mode_ref);
+
+/*	This functions opens the file at the "output" given location.
+ *  It manages the problem of quoted filenames that appear with pdftex and filenames containing the space character.
+ *  In TeXLive 2008, the synctex file created with pdftex did contain unexpected quotes.
+ *	This function will remove them if possible.
+ *  All the reference arguments will take a value on return. They must be non NULL.
+ *	0 on success, non 0 on error. */
+int __synctex_open(const char * output, char ** synctex_name_ref, gzFile * file_ref, synctex_bool_t add_quotes, synctex_io_mode_t * io_mode_ref) {
+	if (synctex_name_ref && file_ref && io_mode_ref) {
+        /*  1 local variables that uses dynamic memory */
+        char * synctex_name = NULL;
+        gzFile the_file = NULL;
+        char * quoteless_synctex_name = NULL;
+		size_t size = 0;
+        synctex_io_mode_t io_mode = *io_mode_ref;
+		const char * mode = _synctex_get_io_mode_name(io_mode);
+		/*  now create the synctex file name */
+		size = strlen(output)+strlen(synctex_suffix)+strlen(synctex_suffix_gz)+1;
+		synctex_name = (char *)malloc(size);
+		if (NULL == synctex_name) {
+			_synctex_error("!  __synctex_open: Memory problem (1)\n");
+			return 1;
+		}
+		/*  we have reserved for synctex enough memory to copy output (including its 2 eventual quotes), both suffices,
+		 *  including the terminating character. size is free now. */
+		if (synctex_name != strcpy(synctex_name,output)) {
+			_synctex_error("!  __synctex_open: Copy problem\n");
+return_on_error:
+			free(synctex_name);
+			free(quoteless_synctex_name);
+			return 2;
+		}
+		/*  remove the last path extension if any */
+		_synctex_strip_last_path_extension(synctex_name);
+		if (!strlen(synctex_name)) {
+			goto return_on_error;		
+		}
+		/*  now insert quotes. */
+		if (add_quotes) {
+			char * quoted = NULL;
+			if (_synctex_copy_with_quoting_last_path_component(synctex_name,&quoted,size) || (NULL == quoted)) {
+				/*	There was an error or quoting does not make sense: */
+				goto return_on_error;
+			}
+			quoteless_synctex_name = synctex_name;
+			synctex_name = quoted;
+		}
+		/*	Now add to synctex_name the first path extension. */
+		if (synctex_name != strcat(synctex_name,synctex_suffix)){
+			_synctex_error("!  __synctex_open: Concatenation problem (can't add suffix '%s')\n",synctex_suffix);
+			goto return_on_error;
+		}
+		/*	Add to quoteless_synctex_name as well, if relevant. */
+		if (quoteless_synctex_name && (quoteless_synctex_name != strcat(quoteless_synctex_name,synctex_suffix))){
+			free(quoteless_synctex_name);
+			quoteless_synctex_name = NULL;
+		}
+		if (NULL == (the_file = gzopen(synctex_name,mode))) {
+			/*  Could not open this file */
+			if (errno != ENOENT) {
+				/*  The file does exist, this is a lower level error, I can't do anything. */
+				_synctex_error("SyncTeX: could not open %s, error %i\n",synctex_name,errno);
+				goto return_on_error;
+			}
+			/*  Apparently, there is no uncompressed synctex file. Try the compressed version */
+			if (synctex_name != strcat(synctex_name,synctex_suffix_gz)){
+				_synctex_error("!  __synctex_open: Concatenation problem (can't add suffix '%s')\n",synctex_suffix_gz);
+				goto return_on_error;
+			}
+			io_mode |= synctex_io_gz_mask;
+			mode = _synctex_get_io_mode_name(io_mode); /* the file is a compressed and is a binary file, this caused errors on Windows */
+			/*	Add the suffix to the quoteless_synctex_name as well. */
+			if (quoteless_synctex_name && (quoteless_synctex_name != strcat(quoteless_synctex_name,synctex_suffix_gz))){
+				free(quoteless_synctex_name);
+				quoteless_synctex_name = NULL;
+			}
+			if (NULL == (the_file = gzopen(synctex_name,mode))) {
+				/*  Could not open this file */
+				if (errno != ENOENT) {
+					/*  The file does exist, this is a lower level error, I can't do anything. */
+					_synctex_error("SyncTeX: could not open %s, error %i\n",synctex_name,errno);
+				}
+				goto return_on_error;
+			}
+		}
+		/*	At this point, the file is properly open.
+		 *  If we are in the add_quotes mode, we change the file name by removing the quotes. */
+		if (quoteless_synctex_name) {
+			gzclose(the_file);
+			if (rename(synctex_name,quoteless_synctex_name)) {
+				_synctex_error("SyncTeX: could not rename %s to %s, error %i\n",synctex_name,quoteless_synctex_name,errno);
+				/*	We could not rename, reopen the file with the quoted name. */
+				if (NULL == (the_file = gzopen(synctex_name,mode))) {
+					/*  No luck, could not re open this file, something has happened meanwhile */
+					if (errno != ENOENT) {
+						/*  The file does not exist any more, it has certainly be removed somehow
+                         *  this is a lower level error, I can't do anything. */
+						_synctex_error("SyncTeX: could not open again %s, error %i\n",synctex_name,errno);
+					}
+					goto return_on_error;
+				}
+			} else {
+                /*  The file has been successfully renamed */
+				if (NULL == (the_file = gzopen(quoteless_synctex_name,mode))) {
+					/*  Could not open this file */
+					if (errno != ENOENT) {
+						/*  The file does exist, this is a lower level error, I can't do anything. */
+						_synctex_error("SyncTeX: could not open renamed %s, error %i\n",quoteless_synctex_name,errno);
+					}
+					goto return_on_error;
+				}
+				/*  The quote free file name should replace the old one:*/
+				free(synctex_name);
+				synctex_name = quoteless_synctex_name;
+				quoteless_synctex_name = NULL;
+			}
+		}
+        /*  The operation is successfull, return the arguments by value.    */
+        * file_ref = the_file;
+        * io_mode_ref = io_mode;
+        * synctex_name_ref = synctex_name;
+		return 0;
+	}
+	return 3;	/*	Bad parameter.	*/
+}
+
+/*	Opens the ouput file, taking into account the eventual build_directory.
+ *	0 on success, non 0 on error. */
+int _synctex_open(const char * output, const char * build_directory, char ** synctex_name_ref, gzFile * file_ref, synctex_bool_t add_quotes, synctex_io_mode_t * io_mode_ref) {
+#	define synctex_name (*synctex_name_ref)
+#	define the_file (*file_ref)
+	int result = __synctex_open(output,synctex_name_ref,file_ref,add_quotes,io_mode_ref);
+	if ((result || !*file_ref) && build_directory && strlen(build_directory)) {
+		char * build_output;
+		const char *lpc;
+		size_t size;
+		synctex_bool_t is_absolute;
+		build_output = NULL;
+		lpc = _synctex_last_path_component(output);
+		size = strlen(build_directory)+strlen(lpc)+2;   /*  One for the '/' and one for the '\0'.   */
+		is_absolute = _synctex_path_is_absolute(build_directory);
+		if (!is_absolute) {
+			size += strlen(output);
+		}
+		if ((build_output = (char *)malloc(size))) {
+			if (is_absolute) {
+				build_output[0] = '\0';
+			} else {
+				if (build_output != strcpy(build_output,output)) {
+					return -4;
+				}
+				build_output[lpc-output]='\0';
+			}
+			if (build_output == strcat(build_output,build_directory)) {
+				/*	Append a path separator if necessary. */
+				if (!SYNCTEX_IS_PATH_SEPARATOR(build_output[strlen(build_directory)-1])) {
+					if (build_output != strcat(build_output,"/")) {
+						return -2;
+					}
+				}
+				/*	Append the last path component of the output. */
+				if (build_output != strcat(build_output,lpc)) {
+					return -3;
+				}
+				return __synctex_open(build_output,synctex_name_ref,file_ref,add_quotes,io_mode_ref);
+			}
+		}
+		return -1;
+	}
+	return result;
+#	undef synctex_name
+#	undef the_file
+}
+
+/*  The scanner destructor
+ */
+void synctex_scanner_free(synctex_scanner_t scanner) {
+	if (NULL == scanner) {
+		return;
+	}
+	if (SYNCTEX_FILE) {
+		gzclose(SYNCTEX_FILE);
+		SYNCTEX_FILE = NULL;
+	}
+	SYNCTEX_FREE(scanner->sheet);
+	SYNCTEX_FREE(scanner->input);
+	free(SYNCTEX_START);
+	free(scanner->output_fmt);
+	free(scanner->output);
+	free(scanner->synctex);
+	free(scanner->lists_of_friends);
+	free(scanner);
+}
+
+/*  Where the synctex scanner parses the contents of the file. */
+synctex_scanner_t synctex_scanner_parse(synctex_scanner_t scanner) {
+	synctex_status_t status = 0;
+	if (!scanner || scanner->flags.has_parsed) {
+		return scanner;
+	}
+	scanner->flags.has_parsed=1;
+	scanner->pre_magnification = 1000;
+	scanner->pre_unit = 8192;
+	scanner->pre_x_offset = scanner->pre_y_offset = 578;
+	/*  initialize the offset with a fake unprobable value,
+	 *  If there is a post scriptum section, this value will be overriden by the real life value */
+	scanner->x_offset = scanner->y_offset = 6.027e23f;
+	scanner->class[synctex_node_type_sheet] = synctex_class_sheet;
+	scanner->class[synctex_node_type_input] = synctex_class_input;
+	(scanner->class[synctex_node_type_input]).scanner = scanner;
+	(scanner->class[synctex_node_type_sheet]).scanner = scanner;
+	scanner->class[synctex_node_type_vbox] = synctex_class_vbox;
+	(scanner->class[synctex_node_type_vbox]).scanner = scanner;
+	scanner->class[synctex_node_type_void_vbox] = synctex_class_void_vbox;
+	(scanner->class[synctex_node_type_void_vbox]).scanner = scanner;
+	scanner->class[synctex_node_type_hbox] = synctex_class_hbox;
+	(scanner->class[synctex_node_type_hbox]).scanner = scanner;
+	scanner->class[synctex_node_type_void_hbox] = synctex_class_void_hbox;
+	(scanner->class[synctex_node_type_void_hbox]).scanner = scanner;
+	scanner->class[synctex_node_type_kern] = synctex_class_kern;
+	(scanner->class[synctex_node_type_kern]).scanner = scanner;
+	scanner->class[synctex_node_type_glue] = synctex_class_glue;
+	(scanner->class[synctex_node_type_glue]).scanner = scanner;
+	scanner->class[synctex_node_type_math] = synctex_class_math;
+	(scanner->class[synctex_node_type_math]).scanner = scanner;
+	scanner->class[synctex_node_type_boundary] = synctex_class_boundary;
+	(scanner->class[synctex_node_type_boundary]).scanner = scanner;
+	SYNCTEX_START = (char *)malloc(SYNCTEX_BUFFER_SIZE+1); /*  one more character for null termination */
+	if (NULL == SYNCTEX_START) {
+		_synctex_error("SyncTeX: malloc error");
+		synctex_scanner_free(scanner);
+		return NULL;
+	}
+	SYNCTEX_END = SYNCTEX_START+SYNCTEX_BUFFER_SIZE;
+	/*  SYNCTEX_END always points to a null terminating character.
+	 *  Maybe there is another null terminating character between SYNCTEX_CUR and SYNCTEX_END-1.
+	 *  At least, we are sure that SYNCTEX_CUR points to a string covering a valid part of the memory. */
+	*SYNCTEX_END = '\0';
+	SYNCTEX_CUR = SYNCTEX_END;
+	status = _synctex_scan_preamble(scanner);
+	if (status<SYNCTEX_STATUS_OK) {
+		_synctex_error("SyncTeX Error: Bad preamble\n");
+bailey:
+		synctex_scanner_free(scanner);
+		return NULL;
+	}
+	status = _synctex_scan_content(scanner);
+	if (status<SYNCTEX_STATUS_OK) {
+		_synctex_error("SyncTeX Error: Bad content\n");
+		goto bailey;
+	}
+	/*  Everything is finished, free the buffer, close the file */
+	free((void *)SYNCTEX_START);
+	SYNCTEX_START = SYNCTEX_CUR = SYNCTEX_END = NULL;
+	gzclose(SYNCTEX_FILE);
+	SYNCTEX_FILE = NULL;
+	/*  Final tuning: set the default values for various parameters */
+	/*  1 pre_unit = (scanner->pre_unit)/65536 pt = (scanner->pre_unit)/65781.76 bp
+	 * 1 pt = 65536 sp */
+	if (scanner->pre_unit<=0) {
+		scanner->pre_unit = 8192;
+	}
+	if (scanner->pre_magnification<=0) {
+		scanner->pre_magnification = 1000;
+	}
+	if (scanner->unit <= 0) {
+		/*  no post magnification */
+		scanner->unit = scanner->pre_unit / 65781.76;/*  65781.76 or 65536.0*/
+	} else {
+		/*  post magnification */
+		scanner->unit *= scanner->pre_unit / 65781.76;
+	}
+	scanner->unit *= scanner->pre_magnification / 1000.0;
+	if (scanner->x_offset > 6e23) {
+		/*  no post offset */
+		scanner->x_offset = scanner->pre_x_offset * (scanner->pre_unit / 65781.76);
+		scanner->y_offset = scanner->pre_y_offset * (scanner->pre_unit / 65781.76);
+	} else {
+		/*  post offset */
+		scanner->x_offset /= 65781.76f;
+		scanner->y_offset /= 65781.76f;
+	}
+	return scanner;
+	#undef SYNCTEX_FILE
+}
+
+/*  Scanner accessors.
+ */
+int synctex_scanner_pre_x_offset(synctex_scanner_t scanner){
+	return scanner?scanner->pre_x_offset:0;
+}
+int synctex_scanner_pre_y_offset(synctex_scanner_t scanner){
+	return scanner?scanner->pre_y_offset:0;
+}
+int synctex_scanner_x_offset(synctex_scanner_t scanner){
+	return scanner?scanner->x_offset:0;
+}
+int synctex_scanner_y_offset(synctex_scanner_t scanner){
+	return scanner?scanner->y_offset:0;
+}
+float synctex_scanner_magnification(synctex_scanner_t scanner){
+	return scanner?scanner->unit:1;
+}
+void synctex_scanner_display(synctex_scanner_t scanner) {
+	if (NULL == scanner) {
+		return;
+	}
+	printf("The scanner:\noutput:%s\noutput_fmt:%s\nversion:%i\n",scanner->output,scanner->output_fmt,scanner->version);
+	printf("pre_unit:%i\nx_offset:%i\ny_offset:%i\n",scanner->pre_unit,scanner->pre_x_offset,scanner->pre_y_offset);
+	printf("count:%i\npost_magnification:%f\npost_x_offset:%f\npost_y_offset:%f\n",
+		scanner->count,scanner->unit,scanner->x_offset,scanner->y_offset);
+	printf("The input:\n");
+	SYNCTEX_DISPLAY(scanner->input);
+	if (scanner->count<1000) {
+		printf("The sheets:\n");
+		SYNCTEX_DISPLAY(scanner->sheet);
+		printf("The friends:\n");
+		if (scanner->lists_of_friends) {
+			int i = scanner->number_of_lists;
+			synctex_node_t node;
+			while(i--) {
+				printf("Friend index:%i\n",i);
+				node = (scanner->lists_of_friends)[i];
+				while(node) {
+					printf("%s:%i,%i\n",
+						synctex_node_isa(node),
+						SYNCTEX_TAG(node),
+						SYNCTEX_LINE(node)
+					);
+					node = SYNCTEX_FRIEND(node);
+				}
+			}
+		}
+	} else {
+		printf("SyncTeX Warning: Too many objects\n");
+	}
+}
+/*  Public*/
+const char * synctex_scanner_get_name(synctex_scanner_t scanner,int tag) {
+	synctex_node_t input = NULL;
+	if (NULL == scanner) {
+		return NULL;
+	}
+	input = scanner->input;
+	do {
+		if (tag == SYNCTEX_TAG(input)) {
+			return (SYNCTEX_NAME(input));
+		}
+	} while((input = SYNCTEX_SIBLING(input)) != NULL);
+	return NULL;
+}
+
+int _synctex_scanner_get_tag(synctex_scanner_t scanner,const char * name);
+int _synctex_scanner_get_tag(synctex_scanner_t scanner,const char * name) {
+	synctex_node_t input = NULL;
+	if (NULL == scanner) {
+		return 0;
+	}
+	input = scanner->input;
+	do {
+		if (_synctex_is_equivalent_file_name(name,(SYNCTEX_NAME(input)))) {
+			return SYNCTEX_TAG(input);
+		}
+	} while((input = SYNCTEX_SIBLING(input)) != NULL);
+	return 0;
+}
+
+int synctex_scanner_get_tag(synctex_scanner_t scanner,const char * name) {
+	size_t char_index = strlen(name);
+	if ((scanner = synctex_scanner_parse(scanner)) && (0 < char_index)) {
+		/*  the name is not void */
+		char_index -= 1;
+		if (!SYNCTEX_IS_PATH_SEPARATOR(name[char_index])) {
+			/*  the last character of name is not a path separator */
+			int result = _synctex_scanner_get_tag(scanner,name);
+			if (result) {
+				return result;
+			} else {
+				/*  the given name was not the one known by TeX
+				 *  try a name relative to the enclosing directory of the scanner->output file */
+				const char * relative = name;
+				const char * ptr = scanner->output;
+				while((strlen(relative) > 0) && (strlen(ptr) > 0) && (*relative == *ptr))
+				{
+					relative += 1;
+					ptr += 1;
+				}
+				/*  Find the last path separator before relative */
+				while(relative > name) {
+					if (SYNCTEX_IS_PATH_SEPARATOR(*(relative-1))) {
+						break;
+					}
+					relative -= 1;
+				}
+				if ((relative > name) && (result = _synctex_scanner_get_tag(scanner,relative))) {
+					return result;
+				}
+				if (SYNCTEX_IS_PATH_SEPARATOR(name[0])) {
+					/*  No tag found for the given absolute name,
+					 *  Try each relative path starting from the shortest one */
+					while(0<char_index) {
+						char_index -= 1;
+						if (SYNCTEX_IS_PATH_SEPARATOR(name[char_index])
+								&& (result = _synctex_scanner_get_tag(scanner,name+char_index+1))) {
+							return result;
+						}
+					}
+				}
+			}
+			return result;
+		}
+	}
+	return 0;
+}
+synctex_node_t synctex_scanner_input(synctex_scanner_t scanner) {
+	return scanner?scanner->input:NULL;
+}
+const char * synctex_scanner_get_output_fmt(synctex_scanner_t scanner) {
+	return NULL != scanner && scanner->output_fmt?scanner->output_fmt:"";
+}
+const char * synctex_scanner_get_output(synctex_scanner_t scanner) {
+	return NULL != scanner && scanner->output?scanner->output:"";
+}
+const char * synctex_scanner_get_synctex(synctex_scanner_t scanner) {
+	return NULL != scanner && scanner->synctex?scanner->synctex:"";
+}
+#	ifdef SYNCTEX_NOTHING
+#       pragma mark -
+#       pragma mark Public node attributes
+#   endif
+int synctex_node_h(synctex_node_t node){
+	if (!node) {
+		return 0;
+	}
+	return SYNCTEX_HORIZ(node);
+}
+int synctex_node_v(synctex_node_t node){
+	if (!node) {
+		return 0;
+	}
+	return SYNCTEX_VERT(node);
+}
+int synctex_node_width(synctex_node_t node){
+	if (!node) {
+		return 0;
+	}
+	return SYNCTEX_WIDTH(node);
+}
+int synctex_node_box_h(synctex_node_t node){
+	if (!node) {
+		return 0;
+	}
+	if (SYNCTEX_IS_BOX(node)) {
+result:
+		return SYNCTEX_HORIZ(node);
+	}
+	if ((node = SYNCTEX_PARENT(node)) && (node->class->type != synctex_node_type_sheet)) {
+		goto result;
+	}
+	return 0;
+}
+int synctex_node_box_v(synctex_node_t node){
+	if (!node) {
+		return 0;
+	}
+	if (SYNCTEX_IS_BOX(node)) {
+result:
+		return SYNCTEX_VERT(node);
+	}
+	if ((node = SYNCTEX_PARENT(node)) && (node->class->type != synctex_node_type_sheet)) {
+		goto result;
+	}
+	return 0;
+}
+int synctex_node_box_width(synctex_node_t node){
+	if (!node) {
+		return 0;
+	}
+	if (SYNCTEX_IS_BOX(node)) {
+result:
+		return SYNCTEX_WIDTH(node);
+	}
+	if ((node = SYNCTEX_PARENT(node)) && (node->class->type != synctex_node_type_sheet)) {
+		goto result;
+	}
+	return 0;
+}
+int synctex_node_box_height(synctex_node_t node){
+	if (!node) {
+		return 0;
+	}
+	if (SYNCTEX_IS_BOX(node)) {
+result:
+		return SYNCTEX_HEIGHT(node);
+	}
+	if ((node = SYNCTEX_PARENT(node)) && (node->class->type != synctex_node_type_sheet)) {
+		goto result;
+	}
+	return 0;
+}
+int synctex_node_box_depth(synctex_node_t node){
+	if (!node) {
+		return 0;
+	}
+	if (SYNCTEX_IS_BOX(node)) {
+result:
+		return SYNCTEX_DEPTH(node);
+	}
+	if ((node = SYNCTEX_PARENT(node)) && (node->class->type != synctex_node_type_sheet)) {
+		goto result;
+	}
+	return 0;
+}
+#	ifdef SYNCTEX_NOTHING
+#       pragma mark -
+#       pragma mark Public node visible attributes
+#   endif
+float synctex_node_visible_h(synctex_node_t node){
+	if (!node) {
+		return 0;
+	}
+	return SYNCTEX_HORIZ(node)*node->class->scanner->unit+node->class->scanner->x_offset;
+}
+float synctex_node_visible_v(synctex_node_t node){
+	if (!node) {
+		return 0;
+	}
+	return SYNCTEX_VERT(node)*node->class->scanner->unit+node->class->scanner->y_offset;
+}
+float synctex_node_visible_width(synctex_node_t node){
+	if (!node) {
+		return 0;
+	}
+	return SYNCTEX_WIDTH(node)*node->class->scanner->unit;
+}
+float synctex_node_box_visible_h(synctex_node_t node){
+	if (!node) {
+		return 0;
+	}
+	switch(node->class->type) {
+		case synctex_node_type_vbox:
+		case synctex_node_type_void_vbox:
+		case synctex_node_type_void_hbox:
+			return SYNCTEX_HORIZ(node)*node->class->scanner->unit+node->class->scanner->x_offset;
+		case synctex_node_type_hbox:
+result:
+			return SYNCTEX_HORIZ_V(node)*node->class->scanner->unit+node->class->scanner->x_offset;
+	}
+	if ((node = SYNCTEX_PARENT(node)) && (node->class->type != synctex_node_type_sheet)) {
+		goto result;
+	}
+	return 0;
+}
+float synctex_node_box_visible_v(synctex_node_t node){
+	if (!node) {
+		return 0;
+	}
+	switch(node->class->type) {
+		case synctex_node_type_vbox:
+		case synctex_node_type_void_vbox:
+		case synctex_node_type_void_hbox:
+			return SYNCTEX_VERT(node)*node->class->scanner->unit+node->class->scanner->y_offset;
+		case synctex_node_type_hbox:
+result:
+			return SYNCTEX_VERT_V(node)*node->class->scanner->unit+node->class->scanner->y_offset;
+	}
+	if ((node = SYNCTEX_PARENT(node)) && (node->class->type != synctex_node_type_sheet)) {
+		goto result;
+	}
+	return 0;
+}
+float synctex_node_box_visible_width(synctex_node_t node){
+	if (!node) {
+		return 0;
+	}
+	switch(node->class->type) {
+		case synctex_node_type_vbox:
+		case synctex_node_type_void_vbox:
+		case synctex_node_type_void_hbox:
+			return SYNCTEX_WIDTH(node)*node->class->scanner->unit;
+		case synctex_node_type_hbox:
+result:
+			return SYNCTEX_WIDTH_V(node)*node->class->scanner->unit;
+	}
+	if ((node = SYNCTEX_PARENT(node)) && (node->class->type != synctex_node_type_sheet)) {
+		goto result;
+	}
+	return 0;
+}
+float synctex_node_box_visible_height(synctex_node_t node){
+	if (!node) {
+		return 0;
+	}
+	switch(node->class->type) {
+		case synctex_node_type_vbox:
+		case synctex_node_type_void_vbox:
+		case synctex_node_type_void_hbox:
+			return SYNCTEX_HEIGHT(node)*node->class->scanner->unit;
+		case synctex_node_type_hbox:
+result:
+			return SYNCTEX_HEIGHT_V(node)*node->class->scanner->unit;
+	}
+	if ((node = SYNCTEX_PARENT(node)) && (node->class->type != synctex_node_type_sheet)) {
+		goto result;
+	}
+	return 0;
+}
+float synctex_node_box_visible_depth(synctex_node_t node){
+	if (!node) {
+		return 0;
+	}
+	switch(node->class->type) {
+		case synctex_node_type_vbox:
+		case synctex_node_type_void_vbox:
+		case synctex_node_type_void_hbox:
+			return SYNCTEX_DEPTH(node)*node->class->scanner->unit;
+		case synctex_node_type_hbox:
+result:
+			return SYNCTEX_DEPTH_V(node)*node->class->scanner->unit;
+	}
+	if ((node = SYNCTEX_PARENT(node)) && (node->class->type != synctex_node_type_sheet)) {
+		goto result;
+	}
+	return 0;
+}
+#	ifdef SYNCTEX_NOTHING
+#       pragma mark -
+#       pragma mark Other public node attributes
+#   endif
+
+int synctex_node_page(synctex_node_t node){
+	synctex_node_t parent = NULL;
+	if (!node) {
+		return -1;
+	}
+	parent = SYNCTEX_PARENT(node);
+	while(parent) {
+		node = parent;
+		parent = SYNCTEX_PARENT(node);
+	}
+	if (node->class->type == synctex_node_type_sheet) {
+		return SYNCTEX_PAGE(node);
+	}
+	return -1;
+}
+int synctex_node_tag(synctex_node_t node) {
+	return node?SYNCTEX_TAG(node):-1;
+}
+int synctex_node_line(synctex_node_t node) {
+	return node?SYNCTEX_LINE(node):-1;
+}
+int synctex_node_column(synctex_node_t node) {
+#	ifdef __DARWIN_UNIX03
+#       pragma unused(node)
+#   endif
+	return -1;
+}
+#	ifdef SYNCTEX_NOTHING
+#       pragma mark -
+#       pragma mark Sheet
+#   endif
+
+synctex_node_t synctex_sheet_content(synctex_scanner_t scanner,int page) {
+	if (scanner) {
+		synctex_node_t sheet = scanner->sheet;
+		while(sheet) {
+			if (page == SYNCTEX_PAGE(sheet)) {
+				return SYNCTEX_CHILD(sheet);
+			}
+			sheet = SYNCTEX_SIBLING(sheet);
+		}
+	}
+	return NULL;
+}
+
+#	ifdef SYNCTEX_NOTHING
+#       pragma mark -
+#       pragma mark Query
+#   endif
+
+int synctex_display_query(synctex_scanner_t scanner,const char * name,int line,int column) {
+#	ifdef __DARWIN_UNIX03
+#       pragma unused(column)
+#   endif
+	int tag = synctex_scanner_get_tag(scanner,name);
+	size_t size = 0;
+	int friend_index = 0;
+	int max_line = 0;
+	synctex_node_t node = NULL;
+	if (tag == 0) {
+		printf("SyncTeX Warning: No tag for %s\n",name);
+		return -1;
+	}
+	free(SYNCTEX_START);
+	SYNCTEX_CUR = SYNCTEX_END = SYNCTEX_START = NULL;
+	max_line = line < INT_MAX-scanner->number_of_lists ? line+scanner->number_of_lists:INT_MAX;
+	while(line<max_line) {
+		/*  This loop will only be performed once for advanced viewers */
+		friend_index = (tag+line)%(scanner->number_of_lists);
+		if ((node = (scanner->lists_of_friends)[friend_index])) {
+			do {
+				if ((synctex_node_type(node)>=synctex_node_type_boundary)
+					&& (tag == SYNCTEX_TAG(node))
+						&& (line == SYNCTEX_LINE(node))) {
+					if (SYNCTEX_CUR == SYNCTEX_END) {
+						size += 16;
+						SYNCTEX_END = realloc(SYNCTEX_START,size*sizeof(synctex_node_t *));
+						SYNCTEX_CUR += SYNCTEX_END - SYNCTEX_START;
+						SYNCTEX_START = SYNCTEX_END;
+						SYNCTEX_END = SYNCTEX_START + size*sizeof(synctex_node_t *);
+					}			
+					*(synctex_node_t *)SYNCTEX_CUR = node;
+					SYNCTEX_CUR += sizeof(synctex_node_t);
+				}
+			} while((node = SYNCTEX_FRIEND(node)));
+			if (SYNCTEX_START == NULL) {
+				/*  We did not find any matching boundary, retry with glue or kern */
+				node = (scanner->lists_of_friends)[friend_index];/*  no need to test it again, already done */
+				do {
+					if ((synctex_node_type(node)>=synctex_node_type_kern)
+						&& (tag == SYNCTEX_TAG(node))
+							&& (line == SYNCTEX_LINE(node))) {
+						if (SYNCTEX_CUR == SYNCTEX_END) {
+							size += 16;
+							SYNCTEX_END = realloc(SYNCTEX_START,size*sizeof(synctex_node_t *));
+							SYNCTEX_CUR += SYNCTEX_END - SYNCTEX_START;
+							SYNCTEX_START = SYNCTEX_END;
+							SYNCTEX_END = SYNCTEX_START + size*sizeof(synctex_node_t *);
+						}			
+						*(synctex_node_t *)SYNCTEX_CUR = node;
+						SYNCTEX_CUR += sizeof(synctex_node_t);
+					}
+				} while((node = SYNCTEX_FRIEND(node)));
+				if (SYNCTEX_START == NULL) {
+					/*  We did not find any matching glue or kern, retry with boxes */
+					node = (scanner->lists_of_friends)[friend_index];/*  no need to test it again, already done */
+					do {
+						if ((tag == SYNCTEX_TAG(node))
+								&& (line == SYNCTEX_LINE(node))) {
+							if (SYNCTEX_CUR == SYNCTEX_END) {
+								size += 16;
+								SYNCTEX_END = realloc(SYNCTEX_START,size*sizeof(synctex_node_t *));
+								SYNCTEX_CUR += SYNCTEX_END - SYNCTEX_START;
+								SYNCTEX_START = SYNCTEX_END;
+								SYNCTEX_END = SYNCTEX_START + size*sizeof(synctex_node_t *);
+							}			
+							*(synctex_node_t *)SYNCTEX_CUR = node;
+							SYNCTEX_CUR += sizeof(synctex_node_t);
+						}
+					} while((node = SYNCTEX_FRIEND(node)));
+				}
+			}
+			SYNCTEX_END = SYNCTEX_CUR;
+			/*  Now reverse the order to have nodes in display order, and keep just a few nodes */
+			if ((SYNCTEX_START) && (SYNCTEX_END))
+			{
+				synctex_node_t * start_ref = (synctex_node_t *)SYNCTEX_START;
+				synctex_node_t * end_ref   = (synctex_node_t *)SYNCTEX_END;
+				end_ref -= 1;
+				while(start_ref < end_ref) {
+					node = *start_ref;
+					*start_ref = *end_ref;
+					*end_ref = node;
+					start_ref += 1;
+					end_ref -= 1;
+				}
+				/*  Basically, we keep the first node for each parent.
+				 *  More precisely, we keep only nodes that are not descendants of
+				 *  their predecessor's parent. */
+				start_ref = (synctex_node_t *)SYNCTEX_START;
+				end_ref   = (synctex_node_t *)SYNCTEX_START;
+		next_end:
+				end_ref += 1; /*  we allways have start_ref<= end_ref*/
+				if (end_ref < (synctex_node_t *)SYNCTEX_END) {
+					node = *end_ref;
+					while((node = SYNCTEX_PARENT(node))) {
+						if (SYNCTEX_PARENT(*start_ref) == node) {
+							goto next_end;
+						}
+					}
+					start_ref += 1;
+					*start_ref = *end_ref;
+					goto next_end;
+				}
+				start_ref += 1;
+                SYNCTEX_END = (char *)start_ref;
+                SYNCTEX_CUR = NULL;// added on behalf of Jose Alliste
+				return (SYNCTEX_END-SYNCTEX_START)/sizeof(synctex_node_t);// added on behalf Jan Sundermeyer
+            }
+			SYNCTEX_CUR = NULL;
+			// return (SYNCTEX_END-SYNCTEX_START)/sizeof(synctex_node_t); removed on behalf Jan Sundermeyer
+		}
+#       if defined(__SYNCTEX_STRONG_DISPLAY_QUERY__)
+		break;
+#       else
+		++line;
+#       endif
+	}
+	return 0;
+}
+
+synctex_node_t synctex_next_result(synctex_scanner_t scanner) {
+	if (NULL == SYNCTEX_CUR) {
+		SYNCTEX_CUR = SYNCTEX_START;
+	} else {
+		SYNCTEX_CUR+=sizeof(synctex_node_t);
+	}
+	if (SYNCTEX_CUR<SYNCTEX_END) {
+		return *(synctex_node_t*)SYNCTEX_CUR;
+	} else {
+		return NULL;
+	}
+}
+
+/*  This struct records a point in TeX coordinates.*/
+typedef struct {
+	int h;
+	int v;
+} synctex_point_t;
+
+/*  This struct records distances, the left one is positive or 0 and the right one is negative or 0.
+ *  When comparing the locations of 2 different graphical objects on the page, we will have to also record the
+ *  horizontal distance as signed to keep track of the typesetting order.*/
+typedef struct {
+	int left;
+	int right;
+} synctex_distances_t;
+
+typedef struct {
+	synctex_point_t left;
+	synctex_point_t right;
+} synctex_offsets_t;
+
+
+typedef struct {
+	synctex_node_t left;
+	synctex_node_t right;
+} synctex_node_set_t;
+
+/*  The smallest container between two has the smallest width or height.
+ *  This comparison is used when there are 2 overlapping boxes that contain the hit point.
+ *  For ConTeXt, the problem appears at each page.
+ *  The chosen box is the one with the smallest height, then the smallest width. */
+SYNCTEX_INLINE static synctex_node_t _synctex_smallest_container(synctex_node_t node, synctex_node_t other_node);
+
+/*  Returns the distance between the hit point hitPoint=(H,V) and the given node. */
+synctex_bool_t _synctex_point_in_box(synctex_point_t hitPoint, synctex_node_t node, synctex_bool_t visible);
+int _synctex_node_distance_to_point(synctex_point_t hitPoint, synctex_node_t node, synctex_bool_t visible);
+
+/*  The best container is the deeper box that contains the hit point (H,V).
+ *  _synctex_eq_deepest_container starts with node whereas
+ *  _synctex_box_child_deepest starts with node's children, if any
+ *  if node is not a box, or a void box, NULL is returned.
+ *  We traverse the node tree in a deep first manner and stop as soon as a result is found. */
+static synctex_node_t _synctex_eq_deepest_container(synctex_point_t hitPoint,synctex_node_t node, synctex_bool_t visible);
+
+/*  Once a best container is found, the closest children are the closest nodes to the left or right of the hit point.
+ *  Only horizontal and vertical offsets are used to compare the positions of the nodes. */
+SYNCTEX_INLINE static int _synctex_eq_get_closest_children_in_box(synctex_point_t hitPoint, synctex_node_t node, synctex_node_set_t*  bestNodesRef,synctex_distances_t*  bestDistancesRef, synctex_bool_t visible);
+
+/*  The closest container is the box that is the one closest to the given point.
+ *  The "visible" version takes into account the visible dimensions instead of the real ones given by TeX. */
+SYNCTEX_INLINE static synctex_node_t _synctex_eq_closest_child(synctex_point_t hitPoint,synctex_node_t node, synctex_bool_t visible);
+
+#define SYNCTEX_MASK_LEFT 1
+#define SYNCTEX_MASK_RIGHT 2
+
+int synctex_edit_query(synctex_scanner_t scanner,int page,float h,float v) {
+	synctex_node_t sheet = NULL;
+	synctex_node_t node = NULL; /*  placeholder */
+	synctex_node_t other_node = NULL; /*  placeholder */
+	synctex_point_t hitPoint = {0,0}; /*  placeholder */
+	synctex_node_set_t bestNodes = {NULL,NULL}; /*  holds the best node */
+	synctex_distances_t bestDistances = {INT_MAX,INT_MAX}; /*  holds the best distances for the best node */
+	synctex_node_t bestContainer = NULL; /*  placeholder */
+	if (NULL == (scanner = synctex_scanner_parse(scanner)) || 0 >= scanner->unit) {/*  scanner->unit must be >0 */
+		return 0;
+	}
+	/*  Convert the given point to scanner integer coordinates */
+	hitPoint.h = (h-scanner->x_offset)/scanner->unit;
+	hitPoint.v = (v-scanner->y_offset)/scanner->unit;
+	/*  We will store in the scanner's buffer the result of the query. */
+	free(SYNCTEX_START);
+	SYNCTEX_START = SYNCTEX_END = SYNCTEX_CUR = NULL;
+	/*  Find the proper sheet */
+	sheet = scanner->sheet;
+	while((sheet) && SYNCTEX_PAGE(sheet) != page) {
+		sheet = SYNCTEX_SIBLING(sheet);
+	}
+	if (NULL == sheet) {
+		return -1;
+	}
+	/*  Now sheet points to the sheet node with proper page number */
+	/*  Here is how we work:
+	 *  At first we do not consider the visible box dimensions. This will cover the most frequent cases.
+	 *  Then we try with the visible box dimensions.
+	 *  We try to find a non void box containing the hit point.
+	 *  We browse all the horizontal boxes until we find one containing the hit point. */
+	if ((node = SYNCTEX_NEXT_HORIZ_BOX(sheet))) {
+		do {
+			if (_synctex_point_in_box(hitPoint,node,synctex_YES)) {
+				/*  Maybe the hitPoint belongs to a contained vertical box. */
+end:
+				/*  This trick is for catching overlapping boxes */
+				if ((other_node = SYNCTEX_NEXT_HORIZ_BOX(node))) {
+					do {
+						if (_synctex_point_in_box(hitPoint,other_node,synctex_YES)) {
+							node = _synctex_smallest_container(other_node,node); 
+						}
+					} while((other_node = SYNCTEX_NEXT_HORIZ_BOX(other_node)));
+				}
+                /*  node is the smallest horizontal box that contains hitPoint. */
+				if ((bestContainer = _synctex_eq_deepest_container(hitPoint,node,synctex_YES))) {
+					node = bestContainer;
+				}
+				_synctex_eq_get_closest_children_in_box(hitPoint,node,&bestNodes,&bestDistances,synctex_YES);
+				if (bestNodes.right && bestNodes.left) {
+					if ((SYNCTEX_TAG(bestNodes.right)!=SYNCTEX_TAG(bestNodes.left))
+							|| (SYNCTEX_LINE(bestNodes.right)!=SYNCTEX_LINE(bestNodes.left))
+								|| (SYNCTEX_COLUMN(bestNodes.right)!=SYNCTEX_COLUMN(bestNodes.left))) {
+						if ((SYNCTEX_START = malloc(2*sizeof(synctex_node_t)))) {
+							if (bestDistances.left>bestDistances.right) {
+								((synctex_node_t *)SYNCTEX_START)[0] = bestNodes.right;
+								((synctex_node_t *)SYNCTEX_START)[1] = bestNodes.left;
+							} else {
+								((synctex_node_t *)SYNCTEX_START)[0] = bestNodes.left;
+								((synctex_node_t *)SYNCTEX_START)[1] = bestNodes.right;
+							}
+							SYNCTEX_END = SYNCTEX_START + 2*sizeof(synctex_node_t);
+							SYNCTEX_CUR = NULL;
+							return (SYNCTEX_END-SYNCTEX_START)/sizeof(synctex_node_t);
+						}
+						return SYNCTEX_STATUS_ERROR;
+					}
+					/*  both nodes have the same input coordinates
+					 *  We choose the one closest to the hit point  */
+					if (bestDistances.left>bestDistances.right) {
+						bestNodes.left = bestNodes.right;
+					}
+					bestNodes.right = NULL;
+				} else if (bestNodes.right) {
+					bestNodes.left = bestNodes.right;
+				} else if (!bestNodes.left){
+					bestNodes.left = node;
+				}
+				if ((SYNCTEX_START = malloc(sizeof(synctex_node_t)))) {
+					* (synctex_node_t *)SYNCTEX_START = bestNodes.left;
+					SYNCTEX_END = SYNCTEX_START + sizeof(synctex_node_t);
+					SYNCTEX_CUR = NULL;
+					return (SYNCTEX_END-SYNCTEX_START)/sizeof(synctex_node_t);
+				}
+				return SYNCTEX_STATUS_ERROR;
+			}
+		} while ((node = SYNCTEX_NEXT_HORIZ_BOX(node)));
+		/*  All the horizontal boxes have been tested,
+		 *  None of them contains the hit point.
+		 */
+	}
+	/*  We are not lucky */
+	if ((node = SYNCTEX_CHILD(sheet))) {
+		goto end;
+	}
+	return 0;
+}
+
+#	ifdef SYNCTEX_NOTHING
+#       pragma mark -
+#       pragma mark Utilities
+#   endif
+
+int _synctex_bail(void) {
+		_synctex_error("SyncTeX ERROR\n");
+		return -1;
+}
+/*  Rougly speaking, this is:
+ *  node's h coordinate - hitPoint's h coordinate.
+ *  If node is to the right of the hit point, then this distance is positive,
+ *  if node is to the left of the hit point, this distance is negative.*/
+int _synctex_point_h_distance(synctex_point_t hitPoint, synctex_node_t node, synctex_bool_t visible);
+int _synctex_point_h_distance(synctex_point_t hitPoint, synctex_node_t node, synctex_bool_t visible) {
+	if (node) {
+		int min,med,max;
+		switch(node->class->type) {
+			/*  The distance between a point and a box is special.
+			 *  It is not the euclidian distance, nor something similar.
+			 *  We have to take into account the particular layout,
+			 *  and the box hierarchy.
+			 *  Given a box, there are 9 regions delimited by the lines of the edges of the box.
+			 *  The origin being at the top left corner of the page,
+			 *  we also give names to the vertices of the box.
+			 *
+			 *   1 | 2 | 3
+			 *  ---A---B--->
+			 *   4 | 5 | 6
+			 *  ---C---D--->
+			 *   7 | 8 | 9
+			 *     v   v
+			 */
+			case synctex_node_type_hbox:
+				/*  getting the box bounds, taking into account negative width, height and depth. */
+				min = visible?SYNCTEX_HORIZ_V(node):SYNCTEX_HORIZ(node);
+				max = min + (visible?SYNCTEX_ABS_WIDTH_V(node):SYNCTEX_ABS_WIDTH(node));
+				/*  We allways have min <= max */
+				if (hitPoint.h<min) {
+					return min - hitPoint.h; /*  regions 1+4+7, result is > 0 */
+				} else if (hitPoint.h>max) {
+					return max - hitPoint.h; /*  regions 3+6+9, result is < 0 */
+				} else {
+					return 0; /*  regions 2+5+8, inside the box, except for vertical coordinates */
+				}
+				break;
+			case synctex_node_type_vbox:
+			case synctex_node_type_void_vbox:
+			case synctex_node_type_void_hbox:
+				/*  getting the box bounds, taking into account negative width, height and depth.
+				 *  For these boxes, no visible dimension available */
+				min = SYNCTEX_HORIZ(node);
+				max = min + SYNCTEX_ABS_WIDTH(node);
+				/*  We allways have min <= max */
+				if (hitPoint.h<min) {
+					return min - hitPoint.h; /*  regions 1+4+7, result is > 0 */
+				} else if (hitPoint.h>max) {
+					return max - hitPoint.h; /*  regions 3+6+9, result is < 0 */
+				} else {
+					return 0; /*  regions 2+5+8, inside the box, except for vertical coordinates */
+				}
+				break;
+			case synctex_node_type_kern:
+				/*  IMPORTANT NOTICE: the location of the kern is recorded AFTER the move.
+				 *  The distance to the kern is very special,
+				 *  in general, there is no text material in the kern,
+				 *  this is why we compute the offset relative to the closest edge of the kern.*/
+				max = SYNCTEX_WIDTH(node);
+				if (max<0) {
+					min = SYNCTEX_HORIZ(node);
+					max = min - max;
+				} else {
+					min = -max;
+					max = SYNCTEX_HORIZ(node);
+					min += max;
+				}
+				med = (min+max)/2;
+				/*  positive kern: '.' means text, '>' means kern offset
+				 *      .............
+				 *                   min>>>>med>>>>max
+				 *                                    ...............
+				 *  negative kern: '.' means text, '<' means kern offset
+				 *      ............................
+				 *                 min<<<<med<<<<max
+				 *                 .................................
+				 *  Actually, we do not take into account negative widths.
+				 *  There is a problem for such situation when there is efectively overlapping text.
+				 *  But this should be extremely rare. I guess that in that case, many different choices
+				 *  could be made, one being in contradiction of the other.
+				 *  It means that the best choice should be made according to the situation that occurs
+				 *  most frequently.
+				 */
+				if (hitPoint.h<min) {
+					return min - hitPoint.h + 1; /*  penalty to ensure other nodes are chosen first in case of overlapping ones */
+				} else if (hitPoint.h>max) {
+					return max - hitPoint.h - 1; /*  same kind of penalty */
+				} else if (hitPoint.h>med) {
+					/*  do things like if the node had 0 width and was placed at the max edge + 1*/
+					return max - hitPoint.h + 1; /*  positive, the kern is to the right of the hitPoint */
+				} else {
+					return min - hitPoint.h - 1; /*  negative, the kern is to the left of the hitPoint */
+				}
+			case synctex_node_type_glue:
+			case synctex_node_type_math:
+				return SYNCTEX_HORIZ(node) - hitPoint.h;
+		}
+	}
+	return INT_MAX;/*  We always assume that the node is faraway to the right*/
+}
+/*  Rougly speaking, this is:
+ *  node's v coordinate - hitPoint's v coordinate.
+ *  If node is at the top of the hit point, then this distance is positive,
+ *  if node is at the bottom of the hit point, this distance is negative.*/
+int _synctex_point_v_distance(synctex_point_t hitPoint, synctex_node_t node,synctex_bool_t visible);
+int _synctex_point_v_distance(synctex_point_t hitPoint, synctex_node_t node,synctex_bool_t visible) {
+#	ifdef __DARWIN_UNIX03
+#       pragma unused(visible)
+#   endif
+	if (node) {
+		int min,max;
+		switch(node->class->type) {
+			/*  The distance between a point and a box is special.
+			 *  It is not the euclidian distance, nor something similar.
+			 *  We have to take into account the particular layout,
+			 *  and the box hierarchy.
+			 *  Given a box, there are 9 regions delimited by the lines of the edges of the box.
+			 *  The origin being at the top left corner of the page,
+			 *  we also give names to the vertices of the box.
+			 *
+			 *   1 | 2 | 3
+			 *  ---A---B--->
+			 *   4 | 5 | 6
+			 *  ---C---D--->
+			 *   7 | 8 | 9
+			 *     v   v
+			 */
+			case synctex_node_type_hbox:
+				/*  getting the box bounds, taking into account negative width, height and depth. */
+				min = SYNCTEX_VERT_V(node);
+				max = min + SYNCTEX_ABS_DEPTH_V(node);
+				min -= SYNCTEX_ABS_HEIGHT_V(node);
+				/*  We allways have min <= max */
+				if (hitPoint.v<min) {
+					return min - hitPoint.v; /*  regions 1+2+3, result is > 0 */
+				} else if (hitPoint.v>max) {
+					return max - hitPoint.v; /*  regions 7+8+9, result is < 0 */
+				} else {
+					return 0; /*  regions 4.5.6, inside the box, except for horizontal coordinates */
+				}
+				break;
+			case synctex_node_type_vbox:
+			case synctex_node_type_void_vbox:
+			case synctex_node_type_void_hbox:
+				/*  getting the box bounds, taking into account negative width, height and depth. */
+				min = SYNCTEX_VERT(node);
+				max = min + SYNCTEX_ABS_DEPTH(node);
+				min -= SYNCTEX_ABS_HEIGHT(node);
+				/*  We allways have min <= max */
+				if (hitPoint.v<min) {
+					return min - hitPoint.v; /*  regions 1+2+3, result is > 0 */
+				} else if (hitPoint.v>max) {
+					return max - hitPoint.v; /*  regions 7+8+9, result is < 0 */
+				} else {
+					return 0; /*  regions 4.5.6, inside the box, except for horizontal coordinates */
+				}
+				break;
+			case synctex_node_type_kern:
+			case synctex_node_type_glue:
+			case synctex_node_type_math:
+				return SYNCTEX_VERT(node) - hitPoint.v;
+		}
+	}
+	return INT_MAX;/*  We always assume that the node is faraway to the top*/
+}
+
+SYNCTEX_INLINE static synctex_node_t _synctex_smallest_container(synctex_node_t node, synctex_node_t other_node) {
+	float height, other_height;
+	if (SYNCTEX_ABS_WIDTH(node)<SYNCTEX_ABS_WIDTH(other_node)) {
+		return node;
+	}
+	if (SYNCTEX_ABS_WIDTH(node)>SYNCTEX_ABS_WIDTH(other_node)) {
+		return other_node;
+	}
+	height = SYNCTEX_ABS_DEPTH(node) + SYNCTEX_ABS_HEIGHT(node);
+	other_height = SYNCTEX_ABS_DEPTH(other_node) + SYNCTEX_ABS_HEIGHT(other_node);
+	if (height<other_height) {
+		return node;
+	}
+	if (height>other_height) {
+		return other_node;
+	}
+	return node;
+}
+
+synctex_bool_t _synctex_point_in_box(synctex_point_t hitPoint, synctex_node_t node, synctex_bool_t visible) {
+	if (node) {
+		if (0 == _synctex_point_h_distance(hitPoint,node,visible)
+				&& 0 == _synctex_point_v_distance(hitPoint,node,visible)) {
+			return synctex_YES;
+		}
+	}
+	return synctex_NO;	
+}
+
+int _synctex_node_distance_to_point(synctex_point_t hitPoint, synctex_node_t node, synctex_bool_t visible) {
+#	ifdef __DARWIN_UNIX03
+#       pragma unused(visible)
+#   endif
+	int result = INT_MAX; /*  when the distance is meaning less (sheet, input...)  */
+	if (node) {
+		int minH,maxH,minV,maxV;
+		switch(node->class->type) {
+			/*  The distance between a point and a box is special.
+			 *  It is not the euclidian distance, nor something similar.
+			 *  We have to take into account the particular layout,
+			 *  and the box hierarchy.
+			 *  Given a box, there are 9 regions delimited by the lines of the edges of the box.
+			 *  The origin being at the top left corner of the page,
+			 *  we also give names to the vertices of the box.
+			 *
+			 *   1 | 2 | 3
+			 *  ---A---B--->
+			 *   4 | 5 | 6
+			 *  ---C---D--->
+			 *   7 | 8 | 9
+			 *     v   v
+			 *  In each region, there is a different formula.
+			 *  In the end we have a continuous distance which may not be a mathematical distance but who cares. */
+			case synctex_node_type_vbox:
+			case synctex_node_type_void_vbox:
+			case synctex_node_type_hbox:
+			case synctex_node_type_void_hbox:
+				/*  getting the box bounds, taking into account negative widths. */
+				minH = SYNCTEX_HORIZ(node);
+				maxH = minH + SYNCTEX_ABS_WIDTH(node);
+				minV = SYNCTEX_VERT(node);
+				maxV = minV + SYNCTEX_ABS_DEPTH(node);
+				minV -= SYNCTEX_ABS_HEIGHT(node);
+				/*  In what region is the point hitPoint=(H,V) ? */
+				if (hitPoint.v<minV) {
+					if (hitPoint.h<minH) {
+						/*  This is region 1. The distance to the box is the L1 distance PA. */
+						result = minV - hitPoint.v + minH - hitPoint.h;/*  Integer overflow? probability epsilon */
+					} else if (hitPoint.h<=maxH) {
+						/*  This is region 2. The distance to the box is the geometrical distance to the top edge.  */
+						result = minV - hitPoint.v;
+					} else {
+						/*  This is region 3. The distance to the box is the L1 distance PB. */
+						result = minV - hitPoint.v + hitPoint.h - maxH;
+					}
+				} else if (hitPoint.v<=maxV) {
+					if (hitPoint.h<minH) {
+						/*  This is region 4. The distance to the box is the geometrical distance to the left edge.  */
+						result = minH - hitPoint.h;
+					} else if (hitPoint.h<=maxH) {
+						/*  This is region 4. We are inside the box.  */
+						result = 0;
+					} else {
+						/*  This is region 6. The distance to the box is the geometrical distance to the right edge.  */
+						result = hitPoint.h - maxH;
+					}
+				} else {
+					if (hitPoint.h<minH) {
+						/*  This is region 7. The distance to the box is the L1 distance PC. */
+						result = hitPoint.v - maxV + minH - hitPoint.h;
+					} else if (hitPoint.h<=maxH) {
+						/*  This is region 8. The distance to the box is the geometrical distance to the top edge.  */
+						result = hitPoint.v - maxV;
+					} else {
+						/*  This is region 9. The distance to the box is the L1 distance PD. */
+						result = hitPoint.v - maxV + hitPoint.h - maxH;
+					}
+				}
+				break;
+			case synctex_node_type_kern:
+				maxH = SYNCTEX_WIDTH(node);
+				if (maxH<0) {
+					minH = SYNCTEX_HORIZ(node);
+					maxH = minH - maxH;
+				} else {
+					minH = -maxH;
+					maxH = SYNCTEX_HORIZ(node);
+					minH += maxH;
+				}
+				minV = SYNCTEX_VERT(node);
+				if (hitPoint.h<minH) {
+					if (hitPoint.v>minV) {
+						result = hitPoint.v - minV + minH - hitPoint.h;
+					} else {
+						result = minV - hitPoint.v + minH - hitPoint.h;
+					}
+				} else if (hitPoint.h>maxH) {
+					if (hitPoint.v>minV) {
+						result = hitPoint.v - minV + hitPoint.h - maxH;
+					} else {
+						result = minV - hitPoint.v + hitPoint.h - maxH;
+					}
+				} else if (hitPoint.v>minV) {
+					result = hitPoint.v - minV;
+				} else {
+					result = minV - hitPoint.v;
+				}
+				break;
+			case synctex_node_type_glue:
+			case synctex_node_type_math:
+				minH = SYNCTEX_HORIZ(node);
+				minV = SYNCTEX_VERT(node);
+				if (hitPoint.h<minH) {
+					if (hitPoint.v>minV) {
+						result = hitPoint.v - minV + minH - hitPoint.h;
+					} else {
+						result = minV - hitPoint.v + minH - hitPoint.h;
+					}
+				} else if (hitPoint.v>minV) {
+					result = hitPoint.v - minV + hitPoint.h - minH;
+				} else {
+					result = minV - hitPoint.v + hitPoint.h - minH;
+				}
+				break;
+		}
+	}
+	return result;
+}
+
+static synctex_node_t _synctex_eq_deepest_container(synctex_point_t hitPoint,synctex_node_t node, synctex_bool_t visible) {
+	if (node) {
+		synctex_node_t result = NULL;
+		synctex_node_t child = NULL;
+		switch(node->class->type) {
+			case synctex_node_type_vbox:
+			case synctex_node_type_hbox:
+				/*  test the deep nodes first */
+				if ((child = SYNCTEX_CHILD(node))) {
+					do {
+						if ((result = _synctex_eq_deepest_container(hitPoint,child,visible))) {
+							return result;
+						}
+					} while((child = SYNCTEX_SIBLING(child)));
+				}
+				/*  is the hit point inside the box? */
+				if (_synctex_point_in_box(hitPoint,node,visible)) {
+					/*  for vboxes we try to use some node inside.
+					 *  Walk through the list of siblings until we find the closest one.
+					 *  Only consider siblings with children. */
+					if ((node->class->type == synctex_node_type_vbox) && (child = SYNCTEX_CHILD(node))) {
+						int bestDistance = INT_MAX;
+						do {
+							if (SYNCTEX_CHILD(child)) {
+								int distance = _synctex_node_distance_to_point(hitPoint,child,visible);
+								if (distance < bestDistance) {
+									bestDistance = distance;
+									node = child;
+								}
+							}
+						} while((child = SYNCTEX_SIBLING(child)));
+					}
+					return node;
+				}
+		}
+	}
+	return NULL;
+}
+
+/*  Compares the locations of the hitPoint with the locations of the various nodes contained in the box.
+ *  As it is an horizontal box, we only compare horizontal coordinates. */
+SYNCTEX_INLINE static int __synctex_eq_get_closest_children_in_hbox(synctex_point_t hitPoint, synctex_node_t node, synctex_node_set_t*  bestNodesRef,synctex_distances_t*  bestDistancesRef, synctex_bool_t visible);
+SYNCTEX_INLINE static int __synctex_eq_get_closest_children_in_hbox(synctex_point_t hitPoint, synctex_node_t node, synctex_node_set_t*  bestNodesRef,synctex_distances_t*  bestDistancesRef, synctex_bool_t visible) {
+	int result = 0;
+	if ((node = SYNCTEX_CHILD(node))) {
+		do {
+			int off7 = _synctex_point_h_distance(hitPoint,node,visible);
+			if (off7 > 0) {
+				/*  node is to the right of the hit point.
+				 *  We compare node and the previously recorded one, through the recorded distance.
+				 *  If the nodes have the same tag, prefer the one with the smallest line number,
+				 *  if the nodes also have the same line number, prefer the one with the smallest column. */
+				if (bestDistancesRef->right > off7) {
+					bestDistancesRef->right = off7;
+					bestNodesRef->right = node;
+					result |= SYNCTEX_MASK_RIGHT;
+				} else if (bestDistancesRef->right == off7 && bestNodesRef->right) {
+					if (SYNCTEX_TAG(bestNodesRef->right) == SYNCTEX_TAG(node)
+						&& (SYNCTEX_LINE(bestNodesRef->right) > SYNCTEX_LINE(node)
+							|| (SYNCTEX_LINE(bestNodesRef->right) == SYNCTEX_LINE(node)
+								&& SYNCTEX_COLUMN(bestNodesRef->right) > SYNCTEX_COLUMN(node)))) {
+						bestNodesRef->right = node;
+						result |= SYNCTEX_MASK_RIGHT;
+					}
+				}
+			} else if (off7 == 0) {
+				/*  hitPoint is inside node. */ 
+				bestDistancesRef->left = bestDistancesRef->right = 0;
+				bestNodesRef->left = node;
+				bestNodesRef->right = NULL;
+				result |= SYNCTEX_MASK_LEFT;
+			} else { /*  here off7 < 0, hitPoint is to the right of node */
+				off7 = -off7;
+				if (bestDistancesRef->left > off7) {
+					bestDistancesRef->left = off7;
+					bestNodesRef->left = node;
+					result |= SYNCTEX_MASK_LEFT;
+				} else if (bestDistancesRef->left == off7 && bestNodesRef->left) {
+					if (SYNCTEX_TAG(bestNodesRef->left) == SYNCTEX_TAG(node)
+						&& (SYNCTEX_LINE(bestNodesRef->left) > SYNCTEX_LINE(node)
+							|| (SYNCTEX_LINE(bestNodesRef->left) == SYNCTEX_LINE(node)
+								&& SYNCTEX_COLUMN(bestNodesRef->left) > SYNCTEX_COLUMN(node)))) {
+						bestNodesRef->left = node;
+						result |= SYNCTEX_MASK_LEFT;
+					}
+				}
+			}
+		} while((node = SYNCTEX_SIBLING(node)));
+		if (result & SYNCTEX_MASK_LEFT) {
+			/*  the left node is new, try to narrow the result */
+			if ((node = _synctex_eq_deepest_container(hitPoint,bestNodesRef->left,visible))) {
+				bestNodesRef->left = node;
+			} 
+			if ((node = _synctex_eq_closest_child(hitPoint,bestNodesRef->left,visible))) {
+				bestNodesRef->left = node;
+			} 
+		}
+		if (result & SYNCTEX_MASK_RIGHT) {
+			/*  the right node is new, try to narrow the result */
+			if ((node = _synctex_eq_deepest_container(hitPoint,bestNodesRef->right,visible))) {
+				bestNodesRef->right = node;
+			} 
+			if ((node = _synctex_eq_closest_child(hitPoint,bestNodesRef->right,visible))) {
+				bestNodesRef->right = node;
+			} 
+		}
+	}
+	return result;
+}
+SYNCTEX_INLINE static int __synctex_eq_get_closest_children_in_vbox(synctex_point_t hitPoint, synctex_node_t node, synctex_node_set_t*  bestNodesRef,synctex_distances_t*  bestDistancesRef,synctex_bool_t visible);
+SYNCTEX_INLINE static int __synctex_eq_get_closest_children_in_vbox(synctex_point_t hitPoint, synctex_node_t node, synctex_node_set_t*  bestNodesRef,synctex_distances_t*  bestDistancesRef,synctex_bool_t visible) {
+	int result = 0;
+	if ((node = SYNCTEX_CHILD(node))) {
+		do {
+			int off7 = _synctex_point_v_distance(hitPoint,node,visible);/*  this is what makes the difference with the h version above */
+			if (off7 > 0) {
+				/*  node is to the top of the hit point (below because TeX is oriented from top to bottom.
+				 *  We compare node and the previously recorded one, through the recorded distance.
+				 *  If the nodes have the same tag, prefer the one with the smallest line number,
+				 *  if the nodes also have the same line number, prefer the one with the smallest column. */
+				if (bestDistancesRef->right > off7) {
+					bestDistancesRef->right = off7;
+					bestNodesRef->right = node;
+					result |= SYNCTEX_MASK_RIGHT;
+				} else if (bestDistancesRef->right == off7 && bestNodesRef->right) {
+					if (SYNCTEX_TAG(bestNodesRef->right) == SYNCTEX_TAG(node)
+						&& (SYNCTEX_LINE(bestNodesRef->right) > SYNCTEX_LINE(node)
+							|| (SYNCTEX_LINE(bestNodesRef->right) == SYNCTEX_LINE(node)
+								&& SYNCTEX_COLUMN(bestNodesRef->right) > SYNCTEX_COLUMN(node)))) {
+						bestNodesRef->right = node;
+						result |= SYNCTEX_MASK_RIGHT;
+					}
+				}
+			} else if (off7 == 0) {
+				bestDistancesRef->left = bestDistancesRef->right = 0;
+				bestNodesRef->left = node;
+				bestNodesRef->right = NULL;
+				result |= SYNCTEX_MASK_LEFT;
+			} else { /*  here off7 < 0 */
+				off7 = -off7;
+				if (bestDistancesRef->left > off7) {
+					bestDistancesRef->left = off7;
+					bestNodesRef->left = node;
+					result |= SYNCTEX_MASK_LEFT;
+				} else if (bestDistancesRef->left == off7 && bestNodesRef->left) {
+					if (SYNCTEX_TAG(bestNodesRef->left) == SYNCTEX_TAG(node)
+						&& (SYNCTEX_LINE(bestNodesRef->left) > SYNCTEX_LINE(node)
+							|| (SYNCTEX_LINE(bestNodesRef->left) == SYNCTEX_LINE(node)
+								&& SYNCTEX_COLUMN(bestNodesRef->left) > SYNCTEX_COLUMN(node)))) {
+						bestNodesRef->left = node;
+						result |= SYNCTEX_MASK_LEFT;
+					}
+				}
+			}
+		} while((node = SYNCTEX_SIBLING(node)));
+		if (result & SYNCTEX_MASK_LEFT) {
+			/*  the left node is new, try to narrow the result */
+			if ((node = _synctex_eq_deepest_container(hitPoint,bestNodesRef->left,visible))) {
+				bestNodesRef->left = node;
+			} 
+			if ((node = _synctex_eq_closest_child(hitPoint,bestNodesRef->left,visible))) {
+				bestNodesRef->left = node;
+			} 
+		}
+		if (result & SYNCTEX_MASK_RIGHT) {
+			/*  the right node is new, try to narrow the result */
+			if ((node = _synctex_eq_deepest_container(hitPoint,bestNodesRef->right,visible))) {
+				bestNodesRef->right = node;
+			} 
+			if ((node = _synctex_eq_closest_child(hitPoint,bestNodesRef->right,visible))) {
+				bestNodesRef->right = node;
+			} 
+		}
+	}
+	return result;
+}
+SYNCTEX_INLINE static int _synctex_eq_get_closest_children_in_box(synctex_point_t hitPoint, synctex_node_t node, synctex_node_set_t*  bestNodesRef,synctex_distances_t*  bestDistancesRef,synctex_bool_t visible) {
+	if (node) {
+		switch(node->class->type) {
+			case synctex_node_type_hbox:
+				return __synctex_eq_get_closest_children_in_hbox(hitPoint, node, bestNodesRef, bestDistancesRef,visible);
+			case synctex_node_type_vbox:
+				return __synctex_eq_get_closest_children_in_vbox(hitPoint, node, bestNodesRef, bestDistancesRef,visible);
+		}
+	}
+	return 0;
+}
+
+SYNCTEX_INLINE static synctex_node_t __synctex_eq_closest_child(synctex_point_t hitPoint, synctex_node_t node,int*  distanceRef, synctex_bool_t visible);
+SYNCTEX_INLINE static synctex_node_t __synctex_eq_closest_child(synctex_point_t hitPoint, synctex_node_t node,int*  distanceRef, synctex_bool_t visible) {
+	synctex_node_t best_node = NULL;
+	if ((node = SYNCTEX_CHILD(node))) {
+		do {
+			int distance = _synctex_node_distance_to_point(hitPoint,node,visible);
+			synctex_node_t candidate = NULL;
+			if (distance<=*distanceRef) {
+				*distanceRef = distance;
+				best_node = node;
+			}
+			switch(node->class->type) {
+				case synctex_node_type_vbox:
+				case synctex_node_type_hbox:
+					if ((candidate = __synctex_eq_closest_child(hitPoint,node,distanceRef,visible))) {
+						best_node = candidate;
+					}
+			}
+		} while((node = SYNCTEX_SIBLING(node)));
+	}
+	return best_node;
+}
+SYNCTEX_INLINE static synctex_node_t _synctex_eq_closest_child(synctex_point_t hitPoint,synctex_node_t node, synctex_bool_t visible) {
+	if (node) {
+		switch(node->class->type) {
+			case synctex_node_type_hbox:
+			case synctex_node_type_vbox:
+			{
+				int best_distance = INT_MAX;
+				synctex_node_t best_node = __synctex_eq_closest_child(hitPoint,node,&best_distance,visible);
+				if ((best_node)) {
+					synctex_node_t child = NULL;
+					switch(best_node->class->type) {
+						case synctex_node_type_vbox:
+						case synctex_node_type_hbox:
+							if ((child = SYNCTEX_CHILD(best_node))) {
+								best_distance = _synctex_node_distance_to_point(hitPoint,child,visible);
+								while((child = SYNCTEX_SIBLING(child))) {
+									int distance = _synctex_node_distance_to_point(hitPoint,child,visible);
+									if (distance<=best_distance) {
+										best_distance = distance;
+										best_node = child;
+									}
+								}
+							}
+					}
+				}
+				return best_node;
+			}
+		}
+	}
+	return NULL;
+}
+
+#	ifdef SYNCTEX_NOTHING
+#       pragma mark -
+#       pragma mark Updater
+#   endif
+
+typedef int (*synctex_fprintf_t)(void *, const char * , ...); /*  print formatted to either FILE *  or gzFile */
+
+#   define SYNCTEX_BITS_PER_BYTE 8
+
+struct __synctex_updater_t {
+    void *file;                 /*  the foo.synctex or foo.synctex.gz I/O identifier  */
+	synctex_fprintf_t fprintf;  /*  either fprintf or gzprintf */
+	int length;                 /*  the number of chars appended */
+    struct _flags {
+        unsigned int no_gz:1;   /*  Whether zlib is used or not */
+        unsigned int reserved:SYNCTEX_BITS_PER_BYTE*sizeof(int)-1; /*  Align */
+	} flags;
+};
+#   define SYNCTEX_FILE updater->file
+#   define SYNCTEX_NO_GZ ((updater->flags).no_gz)
+#   define SYNCTEX_fprintf (*(updater->fprintf))
+
+synctex_updater_t synctex_updater_new_with_output_file(const char * output, const char * build_directory) {
+	synctex_updater_t updater = NULL;
+	char * synctex = NULL;
+	synctex_io_mode_t io_mode = 0;
+	const char * mode = NULL;
+	/*  prepare the updater, the memory is the only one dynamically allocated */
+	updater = (synctex_updater_t)_synctex_malloc(sizeof(synctex_updater_t));
+	if (NULL == updater) {
+		_synctex_error("!  synctex_updater_new_with_file: malloc problem");
+		return NULL;
+	}
+	if (_synctex_open(output,build_directory,&synctex,&SYNCTEX_FILE,synctex_ADD_QUOTES,&io_mode)
+		&& _synctex_open(output,build_directory,&synctex,&SYNCTEX_FILE,synctex_DONT_ADD_QUOTES,&io_mode)) {
+return_on_error:
+		free(updater);
+        updater = NULL;
+		return NULL;
+	}
+	/*  OK, the file exists, we close it and reopen it with the correct mode.
+     *  The receiver is now the owner of the "synctex" variable. */
+	gzclose(SYNCTEX_FILE);
+	SYNCTEX_FILE = NULL;
+	SYNCTEX_NO_GZ = (io_mode&synctex_io_gz_mask)?synctex_NO:synctex_YES;
+    mode = _synctex_get_io_mode_name(io_mode|synctex_io_append_mask);/* either "a" or "ab", depending on the file extension */
+	if (SYNCTEX_NO_GZ) {
+		if (NULL == (SYNCTEX_FILE = (void *)fopen(synctex,mode))) {
+no_write_error:
+			_synctex_error("!  synctex_updater_new_with_file: Can't append to %s",synctex);
+			free(synctex);
+			goto return_on_error;
+		}
+		updater->fprintf = (synctex_fprintf_t)(&fprintf);
+	} else {
+		if (NULL == (SYNCTEX_FILE = (void *)gzopen(synctex,mode))) {
+			goto no_write_error;
+		}
+		updater->fprintf = (synctex_fprintf_t)(&gzprintf);
+	}
+	printf("SyncTeX: updating %s...",synctex);
+	free(synctex);
+	return updater;
+}
+
+
+void synctex_updater_append_magnification(synctex_updater_t updater, char * magnification){
+	if (NULL==updater) {
+		return;
+	}
+	if (magnification && strlen(magnification)) {
+		updater->length += SYNCTEX_fprintf(SYNCTEX_FILE,"Magnification:%s\n",magnification);
+	}
+}
+
+void synctex_updater_append_x_offset(synctex_updater_t updater, char * x_offset){
+	if (NULL==updater) {
+		return;
+	}
+	if (x_offset && strlen(x_offset)) {
+		updater->length += SYNCTEX_fprintf(SYNCTEX_FILE,"X Offset:%s\n",x_offset);
+	}
+}
+
+void synctex_updater_append_y_offset(synctex_updater_t updater, char * y_offset){
+	if (NULL==updater) {
+		return;
+	}
+	if (y_offset && strlen(y_offset)) {
+		updater->length += SYNCTEX_fprintf(SYNCTEX_FILE,"Y Offset:%s\n",y_offset);
+	}
+}
+
+void synctex_updater_free(synctex_updater_t updater){
+	if (NULL==updater) {
+		return;
+	}
+	if (updater->length>0) {
+		SYNCTEX_fprintf(SYNCTEX_FILE,"!%i\n",updater->length);
+	}
+	if (SYNCTEX_NO_GZ) {
+		fclose((FILE *)SYNCTEX_FILE);
+	} else {
+		gzclose((gzFile)SYNCTEX_FILE);
+	}
+	free(updater);
+	printf("... done.\n");
+	return;
+}

+ 346 - 0
services/clsi/src/synctex/synctex_parser.h

@@ -0,0 +1,346 @@
+/* 
+Copyright (c) 2008, 2009, 2010 , 2011 jerome DOT laurens AT u-bourgogne DOT fr
+
+This file is part of the SyncTeX package.
+
+Latest Revision: Tue Jun 14 08:23:30 UTC 2011
+
+Version: 1.16
+
+See synctex_parser_readme.txt for more details
+
+License:
+--------
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE
+
+Except as contained in this notice, the name of the copyright holder  
+shall not be used in advertising or otherwise to promote the sale,  
+use or other dealings in this Software without prior written  
+authorization from the copyright holder.
+
+Acknowledgments:
+----------------
+The author received useful remarks from the pdfTeX developers, especially Hahn The Thanh,
+and significant help from XeTeX developer Jonathan Kew
+
+Nota Bene:
+----------
+If you include or use a significant part of the synctex package into a software,
+I would appreciate to be listed as contributor and see "SyncTeX" highlighted.
+
+Version 1
+Thu Jun 19 09:39:21 UTC 2008
+
+*/
+
+#ifndef __SYNCTEX_PARSER__
+#   define __SYNCTEX_PARSER__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*  synctex_node_t is the type for all synctex nodes.
+ *  The synctex file is parsed into a tree of nodes, either sheet, boxes, math nodes... */
+typedef struct _synctex_node *  synctex_node_t;
+
+/*  The main synctex object is a scanner
+ *  Its implementation is considered private.
+ *  The basic workflow is
+ * - create a "synctex scanner" with the contents of a file
+ * - perform actions on that scanner like display or edit queries
+ * - free the scanner when the work is done
+ */
+typedef struct __synctex_scanner_t _synctex_scanner_t;
+typedef _synctex_scanner_t *  synctex_scanner_t;
+
+/*  This is the designated method to create a new synctex scanner object.
+ *  output is the pdf/dvi/xdv file associated to the synctex file.
+ *  If necessary, it can be the tex file that originated the synctex file
+ *  but this might cause problems if the \jobname has a custom value.
+ *  Despite this method can accept a relative path in practice,
+ *  you should only pass a full path name.
+ *  The path should be encoded by the underlying file system,
+ *  assuming that it is based on 8 bits characters, including UTF8,
+ *  not 16 bits nor 32 bits.
+ *  The last file extension is removed and replaced by the proper extension.
+ *  Then the private method _synctex_scanner_new_with_contents_of_file is called.
+ *  NULL is returned in case of an error or non existent file.
+ *  Once you have a scanner, use the synctex_display_query and synctex_edit_query below.
+ *	The new "build_directory" argument is available since version 1.5.
+ *	It is the directory where all the auxiliary stuff is created.
+ *	Sometimes, the synctex output file and the pdf, dvi or xdv files are not created in the same directory.
+ *	This is the case in MikTeX (I will include this into TeX Live).
+ *	This directory path can be nil, it will be ignored then.
+ *	It can be either absolute or relative to the directory of the output pdf (dvi or xdv) file.
+ *	If no synctex file is found in the same directory as the output file, then we try to find one in the build directory.
+ *  Please note that this new "build_directory" is provided as a convenient argument but should not be used.
+ *  In fact, this is implempented as a work around of a bug in MikTeX where the synctex file does not follow the pdf file.
+ *  The new "parse" argument is available since version 1.5. In general, use 1.
+ *  Use 0 only if you do not want to parse the content but just check the existence.
+ */
+synctex_scanner_t synctex_scanner_new_with_output_file(const char * output, const char * build_directory, int parse);
+
+/*  This is the designated method to delete a synctex scanner object.
+ *  Frees all the memory, you must call it when you are finished with the scanner.
+ */
+void synctex_scanner_free(synctex_scanner_t scanner);
+
+/*  Send this message to force the scanner to parse the contents of the synctex output file.
+ *  Nothing is performed if the file was already parsed.
+ *  In each query below, this message is sent, but if you need to access information more directly,
+ *  you must be sure that the parsing did occur.
+ *  Usage:
+ *		if((my_scanner = synctex_scanner_parse(my_scanner))) {
+ *			continue with my_scanner...
+ *		} else {
+ *			there was a problem
+ *		}
+ */
+synctex_scanner_t synctex_scanner_parse(synctex_scanner_t scanner);
+
+/*  The main entry points.
+ *  Given the file name, a line and a column number, synctex_display_query returns the number of nodes
+ *  satisfying the contrain. Use code like
+ *
+ *     if(synctex_display_query(scanner,name,line,column)>0) {
+ *         synctex_node_t node;
+ *         while((node = synctex_next_result(scanner))) {
+ *             // do something with node
+ *             ...
+ *         }
+ *     }
+ *
+ *  For example, one can
+ * - highlight each resulting node in the output, using synctex_node_h and synctex_node_v
+ * - highlight all the rectangles enclosing those nodes, using synctex_box_... functions
+ * - highlight just the character using that information
+ *
+ *  Given the page and the position in the page, synctex_edit_query returns the number of nodes
+ *  satisfying the contrain. Use code like
+ *
+ *     if(synctex_edit_query(scanner,page,h,v)>0) {
+ *         synctex_node_t node;
+ *         while(node = synctex_next_result(scanner)) {
+ *             // do something with node
+ *             ...
+ *         }
+ *     }
+ *
+ *  For example, one can
+ * - highlight each resulting line in the input,
+ * - highlight just the character using that information
+ *
+ *  page is 1 based
+ *  h and v are coordinates in 72 dpi unit, relative to the top left corner of the page.
+ *  If you make a new query, the result of the previous one is discarded.
+ *  If one of this function returns a non positive integer,
+ *  it means that an error occurred.
+ *
+ *  Both methods are conservative, in the sense that matching is weak.
+ *  If the exact column number is not found, there will be an answer with the whole line.
+ *
+ *  Sumatra-PDF, Skim, iTeXMac2 and Texworks are examples of open source software that use this library.
+ *  You can browse their code for a concrete implementation.
+ */
+int synctex_display_query(synctex_scanner_t scanner,const char *  name,int line,int column);
+int synctex_edit_query(synctex_scanner_t scanner,int page,float h,float v);
+synctex_node_t synctex_next_result(synctex_scanner_t scanner);
+
+/*  Display all the information contained in the scanner object.
+ *  If the records are too numerous, only the first ones are displayed.
+ *  This is mainly for informatinal purpose to help developers.
+ */
+void synctex_scanner_display(synctex_scanner_t scanner);
+
+/*  The x and y offset of the origin in TeX coordinates. The magnification
+   These are used by pdf viewers that want to display the real box size.
+   For example, getting the horizontal coordinates of a node would require
+   synctex_node_box_h(node)*synctex_scanner_magnification(scanner)+synctex_scanner_x_offset(scanner)
+   Getting its TeX width would simply require
+   synctex_node_box_width(node)*synctex_scanner_magnification(scanner)
+   but direct methods are available for that below.
+ */
+int synctex_scanner_x_offset(synctex_scanner_t scanner);
+int synctex_scanner_y_offset(synctex_scanner_t scanner);
+float synctex_scanner_magnification(synctex_scanner_t scanner);
+
+/*  Managing the input file names.
+ *  Given a tag, synctex_scanner_get_name will return the corresponding file name.
+ *  Conversely, given a file name, synctex_scanner_get_tag will retur, the corresponding tag.
+ *  The file name must be the very same as understood by TeX.
+ *  For example, if you \input myDir/foo.tex, the file name is myDir/foo.tex.
+ *  No automatic path expansion is performed.
+ *  Finally, synctex_scanner_input is the first input node of the scanner.
+ *  To browse all the input node, use a loop like
+ *
+ *     if((input_node = synctex_scanner_input(scanner))){
+ *         do {
+ *             blah
+ *         } while((input_node=synctex_node_sibling(input_node)));
+ *     }
+ *
+ *  The output is the name that was used to create the scanner.
+ *  The synctex is the real name of the synctex file,
+ *  it was obtained from output by setting the proper file extension.
+ */
+const char * synctex_scanner_get_name(synctex_scanner_t scanner,int tag);
+int synctex_scanner_get_tag(synctex_scanner_t scanner,const char * name);
+synctex_node_t synctex_scanner_input(synctex_scanner_t scanner);
+const char * synctex_scanner_get_output(synctex_scanner_t scanner);
+const char * synctex_scanner_get_synctex(synctex_scanner_t scanner);
+
+/*  Browsing the nodes
+ *  parent, child and sibling are standard names for tree nodes.
+ *  The parent is one level higher, the child is one level deeper,
+ *  and the sibling is at the same level.
+ *  The sheet of a node is the first ancestor, it is of type sheet.
+ *  A node and its sibling have the same parent.
+ *  A node is the parent of its child.
+ *  A node is either the child of its parent,
+ *  or belongs to the sibling chain of its parent's child.
+ *  The next node is either the child, the sibling or the parent's sibling,
+ *  unless the parent is a sheet.
+ *  This allows to navigate through all the nodes of a given sheet node:
+ *
+ *     synctex_node_t node = sheet;
+ *     while((node = synctex_node_next(node))) {
+ *         // do something with node
+ *     }
+ *
+ *  With synctex_sheet_content, you can retrieve the sheet node given the page.
+ *  The page is 1 based, according to TeX standards.
+ *  Conversely synctex_node_sheet allows to retrieve the sheet containing a given node.
+ */
+synctex_node_t synctex_node_parent(synctex_node_t node);
+synctex_node_t synctex_node_sheet(synctex_node_t node);
+synctex_node_t synctex_node_child(synctex_node_t node);
+synctex_node_t synctex_node_sibling(synctex_node_t node);
+synctex_node_t synctex_node_next(synctex_node_t node);
+synctex_node_t synctex_sheet_content(synctex_scanner_t scanner,int page);
+
+/*  These are the types of the synctex nodes */
+typedef enum {
+	synctex_node_type_error = 0,
+	synctex_node_type_input,
+	synctex_node_type_sheet,
+	synctex_node_type_vbox,
+	synctex_node_type_void_vbox,
+	synctex_node_type_hbox,
+	synctex_node_type_void_hbox,
+	synctex_node_type_kern,
+	synctex_node_type_glue,
+	synctex_node_type_math,
+	synctex_node_type_boundary,
+	synctex_node_number_of_types
+} synctex_node_type_t;
+
+/*  synctex_node_type gives the type of a given node,
+ *  synctex_node_isa gives the same information as a human readable text. */
+synctex_node_type_t synctex_node_type(synctex_node_t node);
+const char * synctex_node_isa(synctex_node_t node);
+
+/*  This is primarily used for debugging purpose.
+ *  The second one logs information for the node and recursively displays information for its next node */
+void synctex_node_log(synctex_node_t node);
+void synctex_node_display(synctex_node_t node);
+
+/*  Given a node, access to its tag, line and column.
+ *  The line and column numbers are 1 based.
+ *  The latter is not yet fully supported in TeX, the default implementation returns 0 which means the whole line.
+ *  When the tag is known, the scanner of the node will give the corresponding file name.
+ *  When the tag is known, the scanner of the node will give the name.
+ */
+int synctex_node_tag(synctex_node_t node);
+int synctex_node_line(synctex_node_t node);
+int synctex_node_column(synctex_node_t node);
+
+/*  This is the page where the node appears.
+ *  This is a 1 based index as given by TeX.
+ */
+int synctex_node_page(synctex_node_t node);
+
+/*  For quite all nodes, horizontal, vertical coordinates, and width.
+ *  These are expressed in TeX small points coordinates, with origin at the top left corner.
+ */
+int synctex_node_h(synctex_node_t node);
+int synctex_node_v(synctex_node_t node);
+int synctex_node_width(synctex_node_t node);
+
+/*  For all nodes, dimensions of the enclosing box.
+ *  These are expressed in TeX small points coordinates, with origin at the top left corner.
+ *  A box is enclosing itself.
+ */
+int synctex_node_box_h(synctex_node_t node);
+int synctex_node_box_v(synctex_node_t node);
+int synctex_node_box_width(synctex_node_t node);
+int synctex_node_box_height(synctex_node_t node);
+int synctex_node_box_depth(synctex_node_t node);
+
+/*  For quite all nodes, horizontal, vertical coordinates, and width.
+ *  The visible dimensions are bigger than real ones to compensate 0 width boxes
+ *  that do contain nodes.
+ *  These are expressed in page coordinates, with origin at the top left corner.
+ *  A box is enclosing itself.
+ */
+float synctex_node_visible_h(synctex_node_t node);
+float synctex_node_visible_v(synctex_node_t node);
+float synctex_node_visible_width(synctex_node_t node);
+/*  For all nodes, visible dimensions of the enclosing box.
+ *  A box is enclosing itself.
+ *  The visible dimensions are bigger than real ones to compensate 0 width boxes
+ *  that do contain nodes.
+ */
+float synctex_node_box_visible_h(synctex_node_t node);
+float synctex_node_box_visible_v(synctex_node_t node);
+float synctex_node_box_visible_width(synctex_node_t node);
+float synctex_node_box_visible_height(synctex_node_t node);
+float synctex_node_box_visible_depth(synctex_node_t node);
+
+/*  The main synctex updater object.
+ *  This object is used to append information to the synctex file.
+ *  Its implementation is considered private.
+ *  It is used by the synctex command line tool to take into account modifications
+ *  that could occur while postprocessing files by dvipdf like filters.
+ */
+typedef struct __synctex_updater_t _synctex_updater_t;
+typedef _synctex_updater_t * synctex_updater_t;
+
+/*  Designated initializer.
+ *  Once you are done with your whole job,
+ *  free the updater */
+synctex_updater_t synctex_updater_new_with_output_file(const char * output, const char * directory);
+
+/*  Use the next functions to append records to the synctex file,
+ *  no consistency tests made on the arguments */
+void synctex_updater_append_magnification(synctex_updater_t updater, char *  magnification);
+void synctex_updater_append_x_offset(synctex_updater_t updater, char *  x_offset);
+void synctex_updater_append_y_offset(synctex_updater_t updater, char *  y_offset);
+
+/*  You MUST free the updater, once everything is properly appended */
+void synctex_updater_free(synctex_updater_t updater);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 45 - 0
services/clsi/src/synctex/synctex_parser_local.h

@@ -0,0 +1,45 @@
+/* 
+Copyright (c) 2008, 2009, 2010 , 2011 jerome DOT laurens AT u-bourgogne DOT fr
+
+This file is part of the SyncTeX package.
+
+Latest Revision: Tue Jun 14 08:23:30 UTC 2011
+
+Version: 1.16
+
+See synctex_parser_readme.txt for more details
+
+License:
+--------
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE
+
+Except as contained in this notice, the name of the copyright holder  
+shall not be used in advertising or otherwise to promote the sale,  
+use or other dealings in this Software without prior written  
+authorization from the copyright holder.
+
+*/
+
+/* This local header file is for TEXLIVE, use your own header to fit your system */
+#   include <w2c/c-auto.h> /* for inline && HAVE_xxx */
+/*	No inlining for synctex tool in texlive. */
+#	define SYNCTEX_INLINE

+ 141 - 0
services/clsi/src/synctex/synctex_parser_readme.txt

@@ -0,0 +1,141 @@
+This file is part of the SyncTeX package.
+
+The Synchronization TeXnology named SyncTeX is a new feature
+of recent TeX engines designed by Jerome Laurens.
+It allows to synchronize between input and output, which means to
+navigate from the source document to the typeset material and vice versa.
+More informations on http://itexmac2.sourceforge.net/SyncTeX.html
+
+This package is mainly for developers, it mainly contains the following files:
+
+synctex_parser_readme.txt
+synctex_parser_version.txt
+synctex_parser_utils.c
+synctex_parser_utils.h
+synctex_parser_local.h
+synctex_parser.h
+synctex_parser.c
+
+The file you are reading contains more informations about the SyncTeX parser history.
+
+In order to support SyncTeX in a viewer, it is sufficient to include
+in the source the files synctex_parser.h and synctex_parser.c.
+The synctex parser usage is described in synctex_parser.h header file.
+
+The other files are used by tex engines or by the synctex command line utility:
+
+ChangeLog
+README.txt
+am
+man1
+man5
+synctex-common.h
+synctex-convert.sh
+synctex-e-mem.ch0
+synctex-e-mem.ch1
+synctex-e-rec.ch0
+synctex-e-rec.ch1
+synctex-etex.h
+synctex-mem.ch0
+synctex-mem.ch1
+synctex-mem.ch2
+synctex-pdf-rec.ch2
+synctex-pdftex.h
+synctex-rec.ch0
+synctex-rec.ch1
+synctex-rec.ch2
+synctex-tex.h
+synctex-xe-mem.ch2
+synctex-xe-rec.ch2
+synctex-xe-rec.ch3
+synctex-xetex.h
+synctex.c
+synctex.defines
+synctex.h
+synctex_main.c
+tests
+
+
+Version:
+--------
+This is version 1, which refers to the synctex output file format.
+The files are identified by a build number.
+In order to help developers to automatically manage the version and build numbers
+and download the parser only when necessary, the synctex_parser.version
+is an ASCII text file just containing the current version and build numbers.
+
+History:
+--------
+1.1: Thu Jul 17 09:28:13 UTC 2008
+- First official version available in TeXLive 2008 DVD.
+  Unfortunately, the backwards synchronization is not working properly mainly for ConTeXt users, see below.
+1.2: Tue Sep  2 10:28:32 UTC 2008
+- Correction for ConTeXt support in the edit query.
+  The previous method was assuming that TeX boxes do not overlap,
+  which is reasonable for LaTeX but not for ConTeXt.
+  This assumption is no longer considered.
+1.3: Fri Sep  5 09:39:57 UTC 2008
+- Local variable "read" renamed to "already_read" to avoid conflicts.
+- "inline" compiler directive renamed to "SYNCTEX_INLINE" for code support and maintenance
+- _synctex_error cannot be inlined due to variable arguments (thanks Christiaan Hofman)
+- Correction in the display query, extra boundary nodes are used for a more precise forwards synchronization
+1.4: Fri Sep 12 08:12:34 UTC 2008
+- For an unknown reason, the previous version was not the real 1.3 (as used in iTeXMac2 build 747).
+  As a consequence, a crash was observed.
+- Some typos are fixed.
+1.6: Mon Nov  3 20:20:02 UTC 2008
+- The bug that prevented synchronization with compressed files on windows has been fixed.
+- New interface to allow system specific customization.
+- Note that some APIs have changed.
+1.8: Mer  8 jul 2009 11:32:38 UTC
+Note that version 1.7 was delivered privately.
+- bug fix: synctex was causing a memory leak in pdftex and xetex, thus some processing speed degradation
+- bug fix: the synctex command line tool was broken when updating a .synctex file
+- enhancement: better accuracy of the synchronization process
+- enhancement: the pdf output file and the associated .synctex file no longer need to live in the same directory.
+               The new -d option of the synctex command line tool manages this situation.
+               This is handy when using something like tex -output-directory=DIR ...
+1.9: Wed Nov  4 11:52:35 UTC 2009
+- Various typo fixed
+- OutputDebugString replaced by OutputDebugStringA to deliberately disable unicode preprocessing
+- New conditional created because OutputDebugStringA is only available since Windows 2K professional
+1.10: Sun Jan  10 10:12:32 UTC 2010 
+- Bug fix in synctex_parser.c to solve a synchronization problem with amsmath's gather environment.
+  Concerns the synctex tool.
+1.11: Sun Jan  17 09:12:31 UTC 2010
+- Bug fix in synctex_parser.c, function synctex_node_box_visible_v: 'x' replaced by 'y'.
+  Only 3rd party tools are concerned.
+1.12: Mon Jul 19 21:52:10 UTC 2010
+- Bug fix in synctex_parser.c, function __synctex_open: the io_mode was modified even in case of a non zero return,
+causing a void .synctex.gz file to be created even if it was not expected. Reported by Marek Kasik concerning a bug on evince.
+1.13: Fri Mar 11 07:39:12 UTC 2011
+- Bug fix in synctex_parser.c, better synchronization as suggested by Jan Sundermeyer (near line 3388).
+- Stronger code design in synctex_parser_utils.c, function _synctex_get_name (really neutral behavior).
+  Only 3rd party tools are concerned.
+1.14: Fri Apr 15 19:10:57 UTC 2011
+- taking output_directory into account
+- Replaced FOPEN_WBIN_MODE by FOPEN_W_MODE when opening the text version of the .synctex file.
+- Merging with LuaTeX's version of synctex.c
+1.15: Fri Jun 10 14:10:17 UTC 2011
+This concerns the synctex command line tool and 3rd party developers.
+TeX and friends are not concerned by these changes.
+- Bug fixed in _synctex_get_io_mode_name, sometimes the wrong mode was returned
+- Support for LuaTeX convention of './' file prefixing
+1.16: Tue Jun 14 08:23:30 UTC 2011
+This concerns the synctex command line tool and 3rd party developers.
+TeX and friends are not concerned by these changes.
+- Better forward search (thanks Jose Alliste)
+- Support for LuaTeX convention of './' file prefixing now for everyone, not only for Windows
+
+Acknowledgments:
+----------------
+The author received useful remarks from the pdfTeX developers, especially Hahn The Thanh,
+and significant help from XeTeX developer Jonathan Kew
+
+Nota Bene:
+----------
+If you include or use a significant part of the synctex package into a software,
+I would appreciate to be listed as contributor and see "SyncTeX" highlighted.
+
+Copyright (c) 2008-2011 jerome DOT laurens AT u-bourgogne DOT fr
+

+ 479 - 0
services/clsi/src/synctex/synctex_parser_utils.c

@@ -0,0 +1,479 @@
+/* 
+Copyright (c) 2008, 2009, 2010 , 2011 jerome DOT laurens AT u-bourgogne DOT fr
+
+This file is part of the SyncTeX package.
+
+Latest Revision: Tue Jun 14 08:23:30 UTC 2011
+
+Version: 1.16
+
+See synctex_parser_readme.txt for more details
+
+License:
+--------
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE
+
+Except as contained in this notice, the name of the copyright holder  
+shall not be used in advertising or otherwise to promote the sale,  
+use or other dealings in this Software without prior written  
+authorization from the copyright holder.
+
+*/
+
+/*  In this file, we find all the functions that may depend on the operating system. */
+
+#include <synctex_parser_utils.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include <limits.h>
+#include <ctype.h>
+#include <string.h>
+
+#include <sys/stat.h>
+
+#if defined(_WIN32) || defined(__WIN32__) || defined(__TOS_WIN__) || defined(__WINDOWS__)
+#define SYNCTEX_WINDOWS 1
+#endif
+
+#ifdef _WIN32_WINNT_WINXP
+#define SYNCTEX_RECENT_WINDOWS 1
+#endif
+
+#ifdef SYNCTEX_WINDOWS
+#include <windows.h>
+#endif
+
+void *_synctex_malloc(size_t size) {
+	void * ptr = malloc(size);
+	if(ptr) {
+/*  There used to be a switch to use bzero because it is more secure. JL */
+		memset(ptr,0, size);
+	}
+	return (void *)ptr;
+}
+
+int _synctex_error(const char * reason,...) {
+	va_list arg;
+	int result;
+	va_start (arg, reason);
+#	ifdef SYNCTEX_RECENT_WINDOWS
+	{/*	This code is contributed by William Blum.
+        As it does not work on some older computers,
+        the _WIN32 conditional here is replaced with a SYNCTEX_RECENT_WINDOWS one.
+        According to http://msdn.microsoft.com/en-us/library/aa363362(VS.85).aspx
+        Minimum supported client	Windows 2000 Professional
+        Minimum supported server	Windows 2000 Server
+        People running Windows 2K standard edition will not have OutputDebugStringA.
+        JL.*/
+		char *buff;
+		size_t len;
+		OutputDebugStringA("SyncTeX ERROR: ");
+		len = _vscprintf(reason, arg) + 1;
+		buff = (char*)malloc( len * sizeof(char) );
+		result = vsprintf(buff, reason, arg) +strlen("SyncTeX ERROR: ");
+		OutputDebugStringA(buff);
+		OutputDebugStringA("\n");
+		free(buff);
+	}
+#   else
+	result = fprintf(stderr,"SyncTeX ERROR: ");
+	result += vfprintf(stderr, reason, arg);
+	result += fprintf(stderr,"\n");
+#   endif
+	va_end (arg);
+	return result;
+}
+
+/*  strip the last extension of the given string, this string is modified! */
+void _synctex_strip_last_path_extension(char * string) {
+	if(NULL != string){
+		char * last_component = NULL;
+		char * last_extension = NULL;
+		char * next = NULL;
+		/*  first we find the last path component */
+		if(NULL == (last_component = strstr(string,"/"))){
+			last_component = string;
+		} else {
+			++last_component;
+			while((next = strstr(last_component,"/"))){
+				last_component = next+1;
+			}
+		}
+#       ifdef	SYNCTEX_WINDOWS
+		/*  On Windows, the '\' is also a path separator. */
+		while((next = strstr(last_component,"\\"))){
+			last_component = next+1;
+		}
+#       endif
+		/*  then we find the last path extension */
+		if((last_extension = strstr(last_component,"."))){
+			++last_extension;
+			while((next = strstr(last_extension,"."))){
+				last_extension = next+1;
+			}
+			--last_extension;/*  back to the "." */
+			if(last_extension>last_component){/*  filter out paths like ....my/dir/.hidden"*/
+				last_extension[0] = '\0';
+			}
+		}
+	}
+}
+
+const char * synctex_ignore_leading_dot_slash(const char * name)
+{
+    while(SYNCTEX_IS_DOT(*name) && SYNCTEX_IS_PATH_SEPARATOR(name[1])) {
+        name += 2;
+        while (SYNCTEX_IS_PATH_SEPARATOR(*name)) {
+            ++name;
+        }
+    }
+    return name;
+}
+
+/*  Compare two file names, windows is sometimes case insensitive... */
+synctex_bool_t _synctex_is_equivalent_file_name(const char *lhs, const char *rhs) {
+    /*  Remove the leading regex '(\./+)*' in both rhs and lhs */
+    lhs = synctex_ignore_leading_dot_slash(lhs);
+    rhs = synctex_ignore_leading_dot_slash(rhs);
+#	if SYNCTEX_WINDOWS
+    /*  On Windows, filename should be compared case insensitive.
+	 *  The characters '/' and '\' are both valid path separators.
+	 *  There will be a very serious problem concerning UTF8 because
+	 *  not all the characters must be toupper...
+	 *  I would like to have URL's instead of filenames. */
+next_character:
+	if(SYNCTEX_IS_PATH_SEPARATOR(*lhs)) {/*  lhs points to a path separator */
+		if(!SYNCTEX_IS_PATH_SEPARATOR(*rhs)) {/*  but not rhs */
+			return synctex_NO;
+		}
+	} else if(SYNCTEX_IS_PATH_SEPARATOR(*rhs)) {/*  rhs points to a path separator but not lhs */
+		return synctex_NO;
+	} else if(toupper(*lhs) != toupper(*rhs)){/*  uppercase do not match */
+		return synctex_NO;
+	} else if (!*lhs) {/*  lhs is at the end of the string */
+		return *rhs ? synctex_NO : synctex_YES;
+	} else if(!*rhs) {/*  rhs is at the end of the string but not lhs */
+		return synctex_NO;
+	}
+	++lhs;
+	++rhs;
+	goto next_character;
+#	else
+    return 0 == strcmp(lhs,rhs)?synctex_YES:synctex_NO;
+#	endif
+}
+
+synctex_bool_t _synctex_path_is_absolute(const char * name) {
+	if(!strlen(name)) {
+		return synctex_NO;
+	}
+#	if SYNCTEX_WINDOWS
+	if(strlen(name)>2) {
+		return (name[1]==':' && SYNCTEX_IS_PATH_SEPARATOR(name[2]))?synctex_YES:synctex_NO;
+	}
+	return synctex_NO;
+#	else
+    return SYNCTEX_IS_PATH_SEPARATOR(name[0])?synctex_YES:synctex_NO;
+#	endif
+}
+
+/*  We do not take care of UTF-8 */
+const char * _synctex_last_path_component(const char * name) {
+	const char * c = name+strlen(name);
+	if(c>name) {
+		if(!SYNCTEX_IS_PATH_SEPARATOR(*c)) {
+			do {
+				--c;
+				if(SYNCTEX_IS_PATH_SEPARATOR(*c)) {
+					return c+1;
+				}
+			} while(c>name);
+		}
+		return c;/* the last path component is the void string*/
+	}
+	return c;
+}
+
+int _synctex_copy_with_quoting_last_path_component(const char * src, char ** dest_ref, size_t size) {
+  const char * lpc;
+  if(src && dest_ref) {
+#		define dest (*dest_ref)
+		dest = NULL;	/*	Default behavior: no change and sucess. */
+		lpc = _synctex_last_path_component(src);
+		if(strlen(lpc)) {
+			if(strchr(lpc,' ') && lpc[0]!='"' && lpc[strlen(lpc)-1]!='"') {
+				/*	We are in the situation where adding the quotes is allowed.	*/
+				/*	Time to add the quotes.	*/
+				/*  Consistency test: we must have dest+size>dest+strlen(dest)+2
+				 *	or equivalently: strlen(dest)+2<size (see below) */
+				if(strlen(src)<size) {
+					if((dest = (char *)malloc(size+2))) {
+						char * dpc = dest + (lpc-src);	/*	dpc is the last path component of dest.	*/
+						if(dest != strncpy(dest,src,size)) {
+							_synctex_error("!  _synctex_copy_with_quoting_last_path_component: Copy problem");
+							free(dest);
+							dest = NULL;/*  Don't forget to reinitialize. */
+							return -2;
+						}
+						memmove(dpc+1,dpc,strlen(dpc)+1);	/*	Also move the null terminating character. */
+						dpc[0]='"';
+						dpc[strlen(dpc)+1]='\0';/*	Consistency test */
+						dpc[strlen(dpc)]='"';
+						return 0;	/*	Success. */
+					}
+					return -1;	/*	Memory allocation error.	*/
+				}
+				_synctex_error("!  _synctex_copy_with_quoting_last_path_component: Internal inconsistency");
+				return -3;
+			}
+			return 0;	/*	Success. */
+		}
+		return 0;	/*	No last path component. */
+#		undef dest
+	}
+	return 1; /*  Bad parameter, this value is subject to changes. */
+}
+
+/*  The client is responsible of the management of the returned string, if any. */
+char * _synctex_merge_strings(const char * first,...);
+
+char * _synctex_merge_strings(const char * first,...) {
+	va_list arg;
+	size_t size = 0;
+	const char * temp;
+	/*   First retrieve the size necessary to store the merged string */
+	va_start (arg, first);
+	temp = first;
+	do {
+		size_t len = strlen(temp);
+		if(UINT_MAX-len<size) {
+			_synctex_error("!  _synctex_merge_strings: Capacity exceeded.");
+			return NULL;
+		}
+		size+=len;
+	} while( (temp = va_arg(arg, const char *)) != NULL);
+	va_end(arg);
+	if(size>0) {
+		char * result = NULL;
+		++size;
+		/*  Create the memory storage */
+		if(NULL!=(result = (char *)malloc(size))) {
+			char * dest = result;
+			va_start (arg, first);
+			temp = first;
+			do {
+				if((size = strlen(temp))>0) {
+					/*  There is something to merge */
+					if(dest != strncpy(dest,temp,size)) {
+						_synctex_error("!  _synctex_merge_strings: Copy problem");
+						free(result);
+						result = NULL;
+						return NULL;
+					}
+					dest += size;
+				}
+			} while( (temp = va_arg(arg, const char *)) != NULL);
+			va_end(arg);
+			dest[0]='\0';/*  Terminate the merged string */
+			return result;
+		}
+		_synctex_error("!  _synctex_merge_strings: Memory problem");
+		return NULL;
+	}
+	return NULL;	
+}
+
+/*  The purpose of _synctex_get_name is to find the name of the synctex file.
+ *  There is a list of possible filenames from which we return the most recent one and try to remove all the others.
+ *  With two runs of pdftex or xetex we are sure the the synctex file is really the most appropriate.
+ */
+int _synctex_get_name(const char * output, const char * build_directory, char ** synctex_name_ref, synctex_io_mode_t * io_mode_ref)
+{
+	if(output && synctex_name_ref && io_mode_ref) {
+		/*  If output is already absolute, we just have to manage the quotes and the compress mode */
+		size_t size = 0;
+        char * synctex_name = NULL;
+        synctex_io_mode_t io_mode = *io_mode_ref;
+		const char * base_name = _synctex_last_path_component(output); /*  do not free, output is the owner. base name of output*/
+		/*  Do we have a real base name ? */
+		if(strlen(base_name)>0) {
+			/*  Yes, we do. */
+			const char * temp = NULL;
+			char * core_name = NULL; /*  base name of output without path extension. */
+			char * dir_name = NULL; /*  dir name of output */
+			char * quoted_core_name = NULL;
+			char * basic_name = NULL;
+			char * gz_name = NULL;
+			char * quoted_name = NULL;
+			char * quoted_gz_name = NULL;
+			char * build_name = NULL;
+			char * build_gz_name = NULL;
+			char * build_quoted_name = NULL;
+			char * build_quoted_gz_name = NULL;
+			struct stat buf;
+			time_t the_time = 0;
+			/*  Create core_name: let temp point to the dot before the path extension of base_name;
+			 *  We start form the \0 terminating character and scan the string upward until we find a dot.
+			 *  The leading dot is not accepted. */
+			if((temp = strrchr(base_name,'.')) && (size = temp - base_name)>0) {
+				/*  There is a dot and it is not at the leading position    */
+				if(NULL == (core_name = (char *)malloc(size+1))) {
+					_synctex_error("!  _synctex_get_name: Memory problem 1");
+					return -1;
+				}
+				if(core_name != strncpy(core_name,base_name,size)) {
+					_synctex_error("!  _synctex_get_name: Copy problem 1");
+					free(core_name);
+					dir_name = NULL;
+					return -2;
+				}
+				core_name[size] = '\0';
+			} else {
+				/*  There is no path extension,
+				 *  Just make a copy of base_name */
+				core_name = _synctex_merge_strings(base_name);
+			}
+			/*  core_name is properly set up, owned by "self". */
+			/*  creating dir_name. */
+			size = strlen(output)-strlen(base_name);
+			if(size>0) {
+				/*  output contains more than one path component */
+				if(NULL == (dir_name = (char *)malloc(size+1))) {
+					_synctex_error("!  _synctex_get_name: Memory problem");
+					free(core_name);
+					dir_name = NULL;
+					return -1;
+				}
+				if(dir_name != strncpy(dir_name,output,size)) {
+					_synctex_error("!  _synctex_get_name: Copy problem");
+					free(dir_name);
+					dir_name = NULL;
+					free(core_name);
+					dir_name = NULL;
+					return -2;
+				}
+				dir_name[size] = '\0';
+			}
+			/*  dir_name is properly set up. It ends with a path separator, if non void. */
+			/*  creating quoted_core_name. */
+			if(strchr(core_name,' ')) {
+				quoted_core_name = _synctex_merge_strings("\"",core_name,"\"");
+			}
+			/*  quoted_core_name is properly set up. */
+			if(dir_name &&strlen(dir_name)>0) {
+				basic_name = _synctex_merge_strings(dir_name,core_name,synctex_suffix,NULL);
+				if(quoted_core_name && strlen(quoted_core_name)>0) {
+					quoted_name = _synctex_merge_strings(dir_name,quoted_core_name,synctex_suffix,NULL);
+				}
+			} else {
+				basic_name = _synctex_merge_strings(core_name,synctex_suffix,NULL);
+				if(quoted_core_name && strlen(quoted_core_name)>0) {
+					quoted_name = _synctex_merge_strings(quoted_core_name,synctex_suffix,NULL);
+				}
+			}
+			if(!_synctex_path_is_absolute(output) && build_directory && (size = strlen(build_directory))) {
+				temp = build_directory + size - 1;
+				if(_synctex_path_is_absolute(temp)) {
+					build_name = _synctex_merge_strings(build_directory,basic_name,NULL);
+					if(quoted_core_name && strlen(quoted_core_name)>0) {
+						build_quoted_name = _synctex_merge_strings(build_directory,quoted_name,NULL);
+					}
+				} else {
+					build_name = _synctex_merge_strings(build_directory,"/",basic_name,NULL);
+					if(quoted_core_name && strlen(quoted_core_name)>0) {
+						build_quoted_name = _synctex_merge_strings(build_directory,"/",quoted_name,NULL);
+					}
+				}
+			}
+			if(basic_name) {
+				gz_name = _synctex_merge_strings(basic_name,synctex_suffix_gz,NULL);
+			}
+			if(quoted_name) {
+				quoted_gz_name = _synctex_merge_strings(quoted_name,synctex_suffix_gz,NULL);
+			}
+			if(build_name) {
+				build_gz_name = _synctex_merge_strings(build_name,synctex_suffix_gz,NULL);
+			}
+			if(build_quoted_name) {
+				build_quoted_gz_name = _synctex_merge_strings(build_quoted_name,synctex_suffix_gz,NULL);
+			}
+			/*  All the others names are properly set up... */
+			/*  retain the most recently modified file */
+#			define TEST(FILENAME,COMPRESS_MODE) \
+			if(FILENAME) {\
+				if (stat(FILENAME, &buf)) { \
+					free(FILENAME);\
+					FILENAME = NULL;\
+				} else if (buf.st_mtime>the_time) { \
+                    the_time=buf.st_mtime; \
+                    synctex_name = FILENAME; \
+                    if (COMPRESS_MODE) { \
+                        io_mode |= synctex_io_gz_mask; \
+                    } else { \
+                        io_mode &= ~synctex_io_gz_mask; \
+                    } \
+				} \
+			}
+			TEST(basic_name,synctex_DONT_COMPRESS);
+			TEST(gz_name,synctex_COMPRESS);
+			TEST(quoted_name,synctex_DONT_COMPRESS);
+			TEST(quoted_gz_name,synctex_COMPRESS);
+			TEST(build_name,synctex_DONT_COMPRESS);
+			TEST(build_gz_name,synctex_COMPRESS);
+			TEST(build_quoted_name,synctex_DONT_COMPRESS);
+			TEST(build_quoted_gz_name,synctex_COMPRESS);
+#			undef TEST
+			/*  Free all the intermediate filenames, except the one that will be used as returned value. */
+#			define CLEAN_AND_REMOVE(FILENAME) \
+			if(FILENAME && (FILENAME!=synctex_name)) {\
+				remove(FILENAME);\
+				printf("synctex tool info: %s removed\n",FILENAME);\
+				free(FILENAME);\
+				FILENAME = NULL;\
+			}
+			CLEAN_AND_REMOVE(basic_name);
+			CLEAN_AND_REMOVE(gz_name);
+			CLEAN_AND_REMOVE(quoted_name);
+			CLEAN_AND_REMOVE(quoted_gz_name);
+			CLEAN_AND_REMOVE(build_name);
+			CLEAN_AND_REMOVE(build_gz_name);
+			CLEAN_AND_REMOVE(build_quoted_name);
+			CLEAN_AND_REMOVE(build_quoted_gz_name);
+#			undef CLEAN_AND_REMOVE
+            /* set up the returned values */
+            * synctex_name_ref = synctex_name;
+            * io_mode_ref = io_mode;
+			return 0;
+		}
+		return -1;/*  bad argument */
+	}
+	return -2;
+}
+
+const char * _synctex_get_io_mode_name(synctex_io_mode_t io_mode) {
+    static const char * synctex_io_modes[4] = {"r","rb","a","ab"}; 
+    unsigned index = ((io_mode & synctex_io_gz_mask)?1:0) + ((io_mode & synctex_io_append_mask)?2:0);// bug pointed out by Jose Alliste
+    return synctex_io_modes[index];
+}

+ 141 - 0
services/clsi/src/synctex/synctex_parser_utils.h

@@ -0,0 +1,141 @@
+/* 
+Copyright (c) 2008, 2009, 2010, 2011 jerome DOT laurens AT u-bourgogne DOT fr
+
+This file is part of the SyncTeX package.
+
+Latest Revision: Tue Jun 14 08:23:30 UTC 2011
+
+Version: 1.16
+
+See synctex_parser_readme.txt for more details
+
+License:
+--------
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE
+
+Except as contained in this notice, the name of the copyright holder  
+shall not be used in advertising or otherwise to promote the sale,  
+use or other dealings in this Software without prior written  
+authorization from the copyright holder.
+
+*/
+
+/*  The utilities declared here are subject to conditional implementation.
+ *  All the operating system special stuff goes here.
+ *  The problem mainly comes from file name management: path separator, encoding...
+ */
+
+#	define synctex_bool_t int
+#	define synctex_YES -1
+#	define synctex_ADD_QUOTES -1
+#	define synctex_COMPRESS -1
+#	define synctex_NO 0
+#	define synctex_DONT_ADD_QUOTES 0
+#	define synctex_DONT_COMPRESS 0
+
+#ifndef __SYNCTEX_PARSER_UTILS__
+#   define __SYNCTEX_PARSER_UTILS__
+
+#include <stdlib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#	if _WIN32
+#		define SYNCTEX_IS_PATH_SEPARATOR(c) ('/' == c || '\\' == c)
+#	else
+#		define SYNCTEX_IS_PATH_SEPARATOR(c) ('/' == c)
+#	endif
+    
+#	if _WIN32
+#		define SYNCTEX_IS_DOT(c) ('.' == c)
+#	else
+#		define SYNCTEX_IS_DOT(c) ('.' == c)
+#	endif
+    
+/*  This custom malloc functions initializes to 0 the newly allocated memory.
+ *  There is no bzero function on windows. */
+void *_synctex_malloc(size_t size);
+
+/*  This is used to log some informational message to the standard error stream.
+ *  On Windows, the stderr stream is not exposed and another method is used.
+ *	The return value is the number of characters printed.	*/
+int _synctex_error(const char * reason,...);
+
+/*  strip the last extension of the given string, this string is modified!
+ *  This function depends on the OS because the path separator may differ.
+ *  This should be discussed more precisely. */
+void _synctex_strip_last_path_extension(char * string);
+
+/*  Compare two file names, windows is sometimes case insensitive...
+ *  The given strings may differ stricto sensu, but represent the same file name.
+ *  It might not be the real way of doing things.
+ *  The return value is an undefined non 0 value when the two file names are equivalent.
+ *  It is 0 otherwise. */
+synctex_bool_t _synctex_is_equivalent_file_name(const char *lhs, const char *rhs);
+
+/*	Description forthcoming.*/
+synctex_bool_t _synctex_path_is_absolute(const char * name);
+
+/*	Description forthcoming...*/
+const char * _synctex_last_path_component(const char * name);
+
+/*	If the core of the last path component of src is not already enclosed with double quotes ('"')
+ *  and contains a space character (' '), then a new buffer is created, the src is copied and quotes are added.
+ *	In all other cases, no destination buffer is created and the src is not copied.
+ *  0 on success, which means no error, something non 0 means error, mainly due to memory allocation failure, or bad parameter.
+ *  This is used to fix a bug in the first version of pdftex with synctex (1.40.9) for which names with spaces
+ *  were not managed in a standard way.
+ *  On success, the caller owns the buffer pointed to by dest_ref (is any) and
+ *  is responsible of freeing the memory when done.
+ *	The size argument is the size of the src buffer. On return the dest_ref points to a buffer sized size+2.*/
+int _synctex_copy_with_quoting_last_path_component(const char * src, char ** dest_ref, size_t size);
+
+/*  These are the possible extensions of the synctex file */
+extern const char * synctex_suffix;
+extern const char * synctex_suffix_gz;
+
+typedef unsigned int synctex_io_mode_t;
+
+typedef enum {
+	synctex_io_append_mask = 1,
+    synctex_io_gz_mask = synctex_io_append_mask<<1
+} synctex_io_mode_masks_t;
+
+typedef enum {
+	synctex_compress_mode_none = 0,
+	synctex_compress_mode_gz = 1
+} synctex_compress_mode_t;
+
+int _synctex_get_name(const char * output, const char * build_directory, char ** synctex_name_ref, synctex_io_mode_t * io_mode_ref);
+
+/*  returns the correct mode required by fopen and gzopen from the given io_mode */
+const char * _synctex_get_io_mode_name(synctex_io_mode_t io_mode);
+
+const char * synctex_ignore_leading_dot_slash(const char * name);
+    
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 1 - 0
services/clsi/src/synctex/synctex_parser_version.txt

@@ -0,0 +1 @@
+1.16

+ 34 - 0
services/clsi/synctex.profile

@@ -0,0 +1,34 @@
+include /etc/firejail/disable-common.inc
+include /etc/firejail/disable-devel.inc
+# include /etc/firejail/disable-mgmt.inc  ## removed in 0.9.40
+# include /etc/firejail/disable-secret.inc ## removed in 0.9.40
+
+read-only /bin
+blacklist /boot
+blacklist /dev
+read-only /etc
+blacklist /home # blacklisted for synctex
+read-only /lib
+read-only /lib64
+blacklist /media
+blacklist /mnt
+blacklist /opt
+blacklist /root
+read-only /run
+blacklist /sbin
+blacklist /selinux
+blacklist /src
+blacklist /sys
+read-only /usr
+
+caps.drop all
+noroot
+nogroups
+net none
+private-tmp
+private-dev
+shell none
+seccomp
+nonewprivs
+
+

+ 117 - 0
services/clsi/test/acceptance/fixtures/examples/asymptote/main.tex

@@ -0,0 +1,117 @@
+\documentclass[12pt]{article}
+
+% Use this form to include EPS (latex) or PDF (pdflatex) files:
+\usepackage{asymptote}
+
+% Use this form with latex or pdflatex to include inline LaTeX code by default:
+%\usepackage[inline]{asymptote}
+
+% Use this form with latex or pdflatex to create PDF attachments by default:
+%\usepackage[attach]{asymptote}
+
+% Enable this line to support the attach option:
+%\usepackage[dvips]{attachfile2}
+
+\begin{document}
+
+% Optional subdirectory for asy files (no spaces):
+\def\asydir{}
+
+\begin{asydef}
+// Global Asymptote definitions can be put here.
+import three;
+usepackage("bm");
+texpreamble("\def\V#1{\bm{#1}}");
+// One can globally override the default toolbar settings here:
+// settings.toolbar=true;
+\end{asydef}
+
+Here is a venn diagram produced with Asymptote, drawn to width 4cm:
+
+\def\A{A}
+\def\B{\V{B}}
+
+%\begin{figure}
+\begin{center}
+\begin{asy}
+size(4cm,0);
+pen colour1=red;
+pen colour2=green;
+
+pair z0=(0,0);
+pair z1=(-1,0);
+pair z2=(1,0);
+real r=1.5;
+path c1=circle(z1,r);
+path c2=circle(z2,r);
+fill(c1,colour1);
+fill(c2,colour2);
+
+picture intersection=new picture;
+fill(intersection,c1,colour1+colour2);
+clip(intersection,c2);
+
+add(intersection);
+
+draw(c1);
+draw(c2);
+
+//draw("$\A$",box,z1);              // Requires [inline] package option.
+//draw(Label("$\B$","$B$"),box,z2); // Requires [inline] package option.
+draw("$A$",box,z1);            
+draw("$\V{B}$",box,z2);
+
+pair z=(0,-2);
+real m=3;
+margin BigMargin=Margin(0,m*dot(unit(z1-z),unit(z0-z)));
+
+draw(Label("$A\cap B$",0),conj(z)--z0,Arrow,BigMargin);
+draw(Label("$A\cup B$",0),z--z0,Arrow,BigMargin);
+draw(z--z1,Arrow,Margin(0,m));
+draw(z--z2,Arrow,Margin(0,m));
+
+shipout(bbox(0.25cm));
+\end{asy}
+%\caption{Venn diagram}\label{venn}
+\end{center}
+%\end{figure}
+
+Each graph is drawn in its own environment. One can specify the width
+and height to \LaTeX\ explicitly. This 3D example can be viewed
+interactively either with Adobe Reader or Asymptote's fast OpenGL-based
+renderer. To support {\tt latexmk}, 3D figures should specify
+\verb+inline=true+. It is sometimes desirable to embed 3D files as annotated
+attachments; this requires the \verb+attach=true+ option as well as the
+\verb+attachfile2+ \LaTeX\ package.
+\begin{center}
+\begin{asy}[height=4cm,inline=true,attach=false,viewportwidth=\linewidth]
+currentprojection=orthographic(5,4,2);
+draw(unitcube,blue);
+label("$V-E+F=2$",(0,1,0.5),3Y,blue+fontsize(17pt));
+\end{asy}
+\end{center}
+
+One can also scale the figure to the full line width:
+\begin{center}
+\begin{asy}[width=\the\linewidth,inline=true]
+pair z0=(0,0);
+pair z1=(2,0);
+pair z2=(5,0);
+pair zf=z1+0.75*(z2-z1);
+
+draw(z1--z2);
+dot(z1,red+0.15cm);
+dot(z2,darkgreen+0.3cm);
+label("$m$",z1,1.2N,red);
+label("$M$",z2,1.5N,darkgreen);
+label("$\hat{\ }$",zf,0.2*S,fontsize(24pt)+blue);
+
+pair s=-0.2*I;
+draw("$x$",z0+s--z1+s,N,red,Arrows,Bars,PenMargins);
+s=-0.5*I;
+draw("$\bar{x}$",z0+s--zf+s,blue,Arrows,Bars,PenMargins);
+s=-0.95*I;
+draw("$X$",z0+s--z2+s,darkgreen,Arrows,Bars,PenMargins);
+\end{asy}
+\end{center}
+\end{document}

BIN
services/clsi/test/acceptance/fixtures/examples/asymptote/output.pdf


+ 9 - 0
services/clsi/test/acceptance/fixtures/examples/biber_bibliography/bibliography.bib

@@ -0,0 +1,9 @@
+@book{DouglasAdams,
+  title={The Hitchhiker's Guide to the Galaxy},
+  author={Adams, Douglas},
+  isbn={9781417642595},
+  url={http://books.google.com/books?id=W-xMPgAACAAJ},
+  year={1995},
+  publisher={San Val}
+}
+

+ 12 - 0
services/clsi/test/acceptance/fixtures/examples/biber_bibliography/main.tex

@@ -0,0 +1,12 @@
+\documentclass{article}
+
+\usepackage[backend=biber]{biblatex}
+\addbibresource{bibliography.bib}
+
+\begin{document}
+
+The meaning of life, the universe and everything is 42 \cite{DouglasAdams}
+
+\printbibliography
+
+\end{document}

+ 48 - 0
services/clsi/test/acceptance/fixtures/examples/biber_bibliography/output.bbl

@@ -0,0 +1,48 @@
+% $ biblatex auxiliary file $
+% $ biblatex version 1.5 $
+% $ biber version 0.9.3 $
+% Do not modify the above lines!
+%
+% This is an auxiliary file used by the 'biblatex' package.
+% This file may safely be deleted. It will be recreated by
+% biber or bibtex as required.
+%
+\begingroup
+\makeatletter
+\@ifundefined{ver@biblatex.sty}
+  {\@latex@error
+     {Missing 'biblatex' package}
+     {The bibliography requires the 'biblatex' package.}
+      \aftergroup\endinput}
+  {}
+\endgroup
+
+
+\refsection{0}
+  \entry{DouglasAdams}{book}{}
+    \name{labelname}{1}{}{%
+      {{}{Adams}{A\bibinitperiod}{Douglas}{D\bibinitperiod}{}{}{}{}}%
+    }
+    \name{author}{1}{}{%
+      {{}{Adams}{A\bibinitperiod}{Douglas}{D\bibinitperiod}{}{}{}{}}%
+    }
+    \list{publisher}{1}{%
+      {San Val}%
+    }
+    \strng{namehash}{AD1}
+    \strng{fullhash}{AD1}
+    \field{sortinit}{A}
+    \field{isbn}{9781417642595}
+    \field{title}{The Hitchhiker's Guide to the Galaxy}
+    \field{year}{1995}
+    \verb{url}
+    \verb http://books.google.com/books?id=W-xMPgAACAAJ
+    \endverb
+  \endentry
+
+  \lossort
+  \endlossort
+
+\endrefsection
+\endinput
+

BIN
services/clsi/test/acceptance/fixtures/examples/biber_bibliography/output.pdf


+ 84 - 0
services/clsi/test/acceptance/fixtures/examples/biber_bibliography/output.run.xml

@@ -0,0 +1,84 @@
+<?xml version="1.0" standalone="yes"?>
+<!-- logreq request file -->
+<!-- logreq version 1.0 / dtd version 1.0 -->
+<!-- Do not edit this file! -->
+<!DOCTYPE requests [
+  <!ELEMENT requests (internal | external)*>
+  <!ELEMENT internal (generic, (provides | requires)*)>
+  <!ELEMENT external (generic, cmdline?, input?, output?, (provides | requires)*)>
+  <!ELEMENT cmdline (binary, (option | infile | outfile)*)>
+  <!ELEMENT input (file)+>
+  <!ELEMENT output (file)+>
+  <!ELEMENT provides (file)+>
+  <!ELEMENT requires (file)+>
+  <!ELEMENT generic (#PCDATA)>
+  <!ELEMENT binary (#PCDATA)>
+  <!ELEMENT option (#PCDATA)>
+  <!ELEMENT infile (#PCDATA)>
+  <!ELEMENT outfile (#PCDATA)>
+  <!ELEMENT file (#PCDATA)>
+  <!ATTLIST requests
+    version CDATA #REQUIRED
+  >
+  <!ATTLIST internal
+    package CDATA #REQUIRED
+    priority (9) #REQUIRED
+    active (0 | 1) #REQUIRED
+  >
+  <!ATTLIST external
+    package CDATA #REQUIRED
+    priority (1 | 2 | 3 | 4 | 5 | 6 | 7 | 8) #REQUIRED
+    active (0 | 1) #REQUIRED
+  >
+  <!ATTLIST provides
+    type (static | dynamic | editable) #REQUIRED
+  >
+  <!ATTLIST requires
+    type (static | dynamic | editable) #REQUIRED
+  >
+  <!ATTLIST file
+    type CDATA #IMPLIED
+  >
+]>
+<requests version="1.0">
+  <internal package="biblatex" priority="9" active="0">
+    <generic>latex</generic>
+    <provides type="dynamic">
+      <file>output.bcf</file>
+    </provides>
+    <requires type="dynamic">
+      <file>output.bbl</file>
+    </requires>
+    <requires type="static">
+      <file>blx-compat.def</file>
+      <file>biblatex.def</file>
+      <file>numeric.bbx</file>
+      <file>standard.bbx</file>
+      <file>numeric.cbx</file>
+      <file>biblatex.cfg</file>
+      <file>english.lbx</file>
+    </requires>
+  </internal>
+  <external package="biblatex" priority="5" active="0">
+    <generic>biber</generic>
+    <cmdline>
+      <binary>biber</binary>
+      <infile>output</infile>
+    </cmdline>
+    <input>
+      <file>output.bcf</file>
+    </input>
+    <output>
+      <file>output.bbl</file>
+    </output>
+    <provides type="dynamic">
+      <file>output.bbl</file>
+    </provides>
+    <requires type="dynamic">
+      <file>output.bcf</file>
+    </requires>
+    <requires type="editable">
+      <file>bibliography.bib</file>
+    </requires>
+  </external>
+</requests>

BIN
services/clsi/test/acceptance/fixtures/examples/epstopdf/image-eps-converted-to.pdf


+ 6673 - 0
services/clsi/test/acceptance/fixtures/examples/epstopdf/image.eps

@@ -0,0 +1,6673 @@
+%!PS-Adobe-3.0 EPSF-1.2
+%%BoundingBox: 0 0 432 268
+%%HiResBoundingBox: 0 0 432 268
+%%Creator: (Wolfram Mathematica 8.0 for Linux x86 (64-bit) (February 23, 2011))
+%%CreationDate: (Monday, October 8, 2012)(15:03:46)
+%%Title: Clipboard
+%%DocumentNeededResources: font Times-Roman
+%%DocumentSuppliedResources: font Times-Roman-MISO
+%%+ font Mathematica2
+%%+ font Mathematica1
+%%DocumentNeededFonts: Times-Roman
+%%DocumentSuppliedFonts: Times-Roman-MISO
+%%+ Mathematica2
+%%+ Mathematica1
+%%DocumentFonts: Times-Roman
+%%+ Times-Roman-MISO
+%%+ Mathematica2
+%%+ Mathematica1
+%%EndComments
+/p{gsave}bind def
+/P{grestore}bind def
+/g{setgray}bind def
+/r{setrgbcolor}bind def
+/k{setcmykcolor}bind def
+/w{setlinewidth}bind def
+/np{newpath}bind def
+/m{moveto}bind def
+/Mr{rmoveto}bind def
+/Mx{currentpoint exch pop moveto}bind def
+/My{currentpoint pop exch moveto}bind def
+/X{0 rmoveto}bind def
+/Y{0 exch rmoveto}bind def
+/N{currentpoint 3 -1 roll show moveto}bind def
+/L{lineto}bind def
+/rL{rlineto}bind def
+/C{curveto}bind def
+/cp{closepath}bind def
+/F{eofill}bind def
+/f{fill}bind def
+/s{stroke}bind def
+/S{show}bind def
+/tri{p 9 6 roll r 6 4 roll m 4 2 roll L L cp F P}bind def
+/Msf{findfont exch scalefont[1 0 0 -1 0 0]makefont setfont}bind def
+1 -1 scale 0 -267.698 translate
+-35.28 -2.88 translate
+[1 0 0 1 0 0 ]  concat
+1 w
+0 g
+0 g
+[ ] 0 setdash
+2 setlinecap
+0 setlinejoin
+10 setmiterlimit
+
+%%MathematicaCell
+%Cell[BoxData[
+% GraphicsBox[{{}, {}, 
+%   {GrayLevel[0], LineBox[CompressedData["
+%1:eJw113dcTf8fB/B7697q7tW0KSoJWRnp/ba3rCR7kxmSzKy+KFlFvqSMjB++
+%ESqKrlVEKJoy2nvv3S/O+5x/7uPce8/n83m/nu+zeq7cOmeNBofDqeVyOH8+
+%2a0l/9EWydgK9V7d4EmZe47Cm8LwOycM2X1fGDzdYkfqz3L1024exbOfBoC3
+%r26ui2c5/R4EpUfSFFnDytWX/v2z/Q9yO+VvM84uo/8Hg9/5EpPBp8vUJbP/
+%fBMCXy8fN5GPKqPjn4DGhdnXo/JK1cKUG86jheEwxHn2wDHnSmm8Z7DcMeyV
+%7+hStfni069rzZ7D73FreM8KSmj8KKiZGhB1z6dEHdRxdMqNl+C31zh+u20J
+%zfcK1nC8d2oVFqsH/93egLor7NzkU0zzvwWPY/98uDG6WP2m1qxjhmhwtBjk
+%dye/iNbzDkRHH6e5ny1Sz/k74XvY7J513WxUEa0vFhJ+vlgTkFOoztzzZ8AP
+%YDckMqHAu5DWGwf9rDutaBhWqN72Z3nOn2DLwfSeGb8LaP2fobqXp8v54wVq
+%RiMedJ1rPhpaFVA98XBvmqp6Y1q+evLfCRIgKtrukO+hfKovAa4/0ex0wDxf
+%/We2xae/wuWhSVqYkEf1foM7E+x2fnPLU/8tJ+Ub9MHHQYN75FH9iZCe2ui9
+%4F2uuvuf5XRLghFBj47Yb8mlPJLAyiiZ11kvV71u7Z8tGcxC4sIeROZQPikQ
+%nNGyS29ljvrPaLrBKXB/zgzOGJ0cyisVnGs38MYEZ6vr/gxXmwpxOW67tedl
+%U35pMMcjofO/jVlq278DfoegKVuPNQVkUZ7f4RpHXT5wfJb67/I80mF2ZJu1
+%cWEm5fsDHKa6JZd5Z6o/xf3ZfoDBO+2sPYMyKe+fYGHW3z0pOUOt93eBv+DB
+%/IdHm/ZkUP6/4Mi8AfcqumWo/w63+DfofvIPWuzxmzwy4HDdvBtr4n5RPRng
+%XyQ/26D8RT4ZMPzRQcuhjj+pvgxocL11Ky/gB3llQtVuy7W2OelUbyYkKzUb
+%upink18mhO8U4sXN36n+LKgLm1j3X0gaeWaB1zML89G1qZRHFmim+1ksHZ5K
+%vtlgedzAUmNvCuWTDQ3HXh8c/iKZvLPBRX+wU01bEuWVA6+K352eiUnknwMT
+%axKOmR9KpPxywHbtC6ejr75RP+RCoPeYCnfuN8ozF3oOWGKVjV+pP3LBe7L5
+%piz3BMo3Dxx73Vu6Niqe+iUPxjkazH/X8IXyzoM+e25rp+74TP2TD+LzNpol
+%pXGUfz4kRp4W8dZ9pH7KhytWwuBOv2PJowD+O3xdbDv/PXkUQOigqX62cTHk
+%UQAmpw6MmzEmmjwKID7AsmxD6BvyKAQn433ymWavyaMQ9Dj2C8ZeekkehfD2
+%tIXdFv0o8iiCLxaLbU3qItRn/noUgeSth6vXt3DyKAKT2d+7fgx+Qh7FUOYz
+%5qzF8RDyKAazMPW0oKj75FEMtptcBlWtu0UeJTCxx4K8oZKr5FEChh625dcX
+%+ZBHCdxdculc2bQ95FEK/jDse0aTGzAepXDat36zh6EPMB6lEOMyLXttSyAw
+%HmXw0vl9VIrLLWA8yiDsQt/zKyvuA+NRBjv2/d43+GkIMB7lUHT/5ubmpifA
+%eJTD2mtGW4vgKTAe5fB4Z5evQccigfGoAOVTaPVLiAKmngqY/6hX1yGyV8D4
+%VEBui2yWT9hrYOqrAGPprh6ey94C41UBT9Mc+z4XxABTbwVYR3y/ZB36Dhi/
+%Crh4w3aAYmUsMPVXgmNgbMYq+UdgPCvBzrn8foY6jvKohF1ulbIi58/A+FbC
+%qfuxDgGKeMqnEmpM34d7FscD410J2DMwyigmgfKqgsnmb18fvPYVGP8qyP+i
+%eXn3/m+UXxVk1g8+OnVhIjDnZxWU1nmvz7FOojyroIeBPsdSPxmY/qiCWTF9
+%JVibTPlWwzDfRG2DpBRg+qUaTjQei30Zmkp5V8O1LS5aQ/zSgOmfanAvvXnN
+%fvd3yr8a7G59+7JjcTow/VQNMr7DyVn4gzxq4Oo/D/X5vX+SRw30CbVN8hT+
+%Io8aWFESNqam4hd51MCExLtuY1J/k0cN3LLSbRmxPIM8asBzeCet0MQM8qiB
+%xasjHkZMySSPWoiyEMg7qTPJoxby2zPkrUOyyKMWLhklHVp0L4s8aiFlYNjQ
+%vr2yyaMWTljpGO/5N5s8asG5x7V9qMghjzp4963x2KkTOeRRB5EuJi12Grnk
+%UQeF/fc1B+3NJY+O/4+32L6lNpc86mCRzYR5UVvzyKMO9DZbrbtYlEce9WA1
+%w8COvzafPOoh3SfIODcznzzqwe12cr91SwvIox5SRvB2uKYXkEc98DhqF6lj
+%IXnUw9beZyJtUgrJowFsrQ6d0ZpfRB4NYHrH/+TapCLyaIAuOL1mtn0xeTTA
+%GuPw70VJxeTRABNkzUUqhxLyaACvueemlaeWkEcDOFnuMly9qJQ8GsFlgFfM
+%gV+l5NEI+U+NetquLCOPRngeGBIYmFdGHo2wcvfdxR82lpNHI5zRh4MnK8vJ
+%oxG4C3e46NhUkEcTNMd1Tw5wryCPJpi5wunA1TcV5NEEPv2vtpRoVwJzvWyC
+%hNniq7emV5JHE+j1eBv69GwleTTBucifASNTKsmjGQ6uu2yt37WKPJph027d
+%liWrqsijGZZWO1vp3K0ij2ZIWrG+uVtlFXk0w5uBsu6Bw6vJoxnEvr7uFw5W
+%k0cLZAa39qx4X00eLdCw/9iod4oa8miBzg+mjuIvqiGPFni9sK/u26Aa8miB
+%Al/97Y1lNeTRAv945ufdGFFLHi3Qpbvry89Ha8mjFbJ6eHq7xdeSRyu4t5vZ
+%X+9SRx6t8HFCVbC9Ux15tMJJ19DlHmF15NEKBUP6rQJePXm0wrlbvnoec+rJ
+%ow2GN6beXXKtnjzaIEQ7Qvmhop482mD5z+vvorGBPNrApmT4yTlnG8ijDfQq
+%YhoOZjWQRxsIRxUOnz2kkTzaoelrk27YP43k0Q6orNB8m9ZIHu2w1HNA0X7L
+%JvJoB0Mb7tisQ03k0Q4nnwiKeSlN5NEOrolmkc/6NZMHB0+MWM0dc6SZ6uFg
+%7LMxG3Z/byYfDm6x7mLnOqiF6uOg79Tpd829WsiLgzNttkwMz2mBv+Wu5eC3
+%2QNna0Mr+XFw67ivTf0vtdL1gYPJni0OpnWt5MlBz/itIby5bZQHB1sbMyRR
+%D9vIl4Mng7Y/Wydrp3w4+GOPlMvf2k7eHDwaGWJx6ks75cXF5DI/5+OGHGT8
+%ueh/JmX++ykcZPLj4nHJw1On9nKQ6QcujnPtZ5PxHweZPLmYMH6oV0QGB5n+
+%4OL7L1PajHW5yOTLRRvp2tM9J3OR6RcuvjN3mfxkHxeZvLnIyX8mKwzhItM/
+%XLR7Glv1NJ+LTP5c3Ou/b/LgbhrI9BMXh4UZzZ9nr4EMhwbKe4SeMfXWQMZD
+%A9/FH4OwaA1kPDRwxL86ri1tGsh4aGBk9AfkjtBExkMDd7rFhH/boYlM/2ng
+%4ATDJ1seaCLj0THvBndRdrEmMh4a2Du8LXiYOQ8ZDw18dd4yfPs6HjIeGmia
+%1DnC7xYPGQ8NtN/c//GDPB4yHho4LPTEwxhTPjIeGhj4wmluhRMfGQ9NLJ7n
+%mGv8H588NHHZ7mzu/ko+eWjij+WDrPjWWuShiUM9n54O3a9FHproO9Y5/0m0
+%FnloomCt/IGmTJs8NHG00ZFz9x21yUMTM0cbX4i9qU0empi9YeGrzdXa5KGJ
+%Xvo9TS+P1SEPTaxVVV3c76NDHprYMNZ2uixPhzx4+NEnJXbXSAF58DBb97z7
+%xzMC8uBh2zLR5oGFAvLg4QHhp+f3xgnJg4cRws6zVl8VkgcPraICnnu0C8mD
+%h14jQjcPXyEiDx7GpEws8owWkQcPV1zemBzdT0wePFzjsF5Hx09MHjzUuXtS
+%5ceTkAcPJzs6TQ/YKSEPHgZO/xLhXCQhDz4mdptgumiVlDz4uPCrVpRPhpQ8
+%+HjQZoT/uZUy8uBjr415hx8UysiDj6tc5vYeOUpOHny89DlzY8RROXnw8YP4
+%2ODrCXLy6Dh+bFe1XS8FefCx3n/ZZ4tdCvLg45ul2VYv4xXkwccGvW3hdwco
+%yYOPe54UTHjkqyQPLbzbdNf7AkdFHlrIUWPfEBcVeXT0kUty5/FVKvLQQr7R
+%zaMz9uqSR0dfRW7eoKfUIw8tdO/WLXFihB55aGGd1ZKK9W765KGF41+08r7P
+%NSAPLTzbeGeVaIEheWihmb5sW85FI/LQwtHDQspjbDuThxbqTQ8JHBXclTy0
+%8NGVKzfH2fUkD208suB8pPknQ/LQxrkbyiR29/TIQxtTp7aeKF+rSx7aOOrC
+%4WEXL6nIQxsrrU5OsNVSkYc2Opd6f15yTkke2rhlkNmQe4OV5NFxXpgZWaZm
+%sx7a+CT3WZoikPXQxvCAL9Y/VrIe2vhc5jRvuaWCPDr2HbF5eaucPHQwNt7k
+%dUGHN+Ohg8szu38bf1dOHjroOoNTnO0hJw8dNLh9xX/Wajl56ODH7Zn+AyfI
+%yUMHHS8WbhlqJicPHayrWcPvLpEjc//UwWNdgnq45cvIQwc1LfudCQmXkYcO
+%9p/le+K+p4w8dNBUEuTfbbmMPHTQwS6g5JO1jDx00LKqzOSEQkYeAlyvv2CP
+%WSl7fgjwXtXzlkMf2PNDgJ2Gvm1w/p+UPAQIid39v5+QkocAz37gLb2+UUoe
+%AnQ03/Xp3kwpeQhwEH93Yf4gKXkIULVkY9dRhlLyEODI1Z+X+LdJyEOAe0VR
+%B6pyJeQhwJeuv+v6f5aQhwBX5R4QTA2XkIcQ+00ct2nwNQl5CHFSXO2vn14S
+%8hCi/y/dk2N3SchDiI0ub9bbr5KQhxCD7zSaSmdJyEOIptmFtStGS8hDiP8z
+%vvlmmoWEPIRosnSx6K2RhDyEuDVm966XOhLyEGL/5o2FoxvY65UQy276/BhW
+%ICYPIVb0vOMXlComDyH+1+p95UismDxE+LzfgvNxEWLyEKEgPUCx976YPEQY
+%O2uA8mSAmDxEGCbuv6X5jJg8RLguUKV8e0RMHiKcK3jUNcdVTB4i9JiQmDp/
+%g5g8Ova33QgwXComDxFW/HvZ1XyOmDxE+H2cfOGRiWLyEOHPuV0/m4wSk4cI
+%Rw4yGS4cKCYPMb7rbJ08qLeYPMQoWBQ86XInMXmI0eHZiU0oF5OHGL3HDbja
+%U0tMHmI06GPUbNMiIg8xrkz1MThdJSIPMYYeLQs1KBSRhxiN6nPOJv5m7x9i
+%PHTWI/p1sog8xHhsAudK1icReYjx48pae6uO+w3jIcbS10cy7j8XkYcY7beN
+%z5j/REQeErzdq+Rh3/si8pDgogMWm0yDROQhQcPN9tEz/EXkIcFrpVMXXfYV
+%kYcE3Ve7L5Z6i8hDgvsWHku//cfhr4cET9WPX7n6gIg8JPhTc/GC8btE5CFB
+%jwdPJkzscGQ8JDgqLkTo5CQiDwka3NnW9f5KEXlIMLXx8TtpRx8wHlI0Gfrg
+%/ml7EXlIMfRmulV/OxF5SHH1ovT9RZNF5CHFSdue178eKyIPKb5clnwkzIb1
+%6LhObBxhGT2M9ZCiw+ry96UDWQ8pfnXPnzDQgvWQ4qgVQi/P3qyHFH3vnX/R
+%2p31kOJQ3dTo451YDykG8swCzfVYDynumvhQniVjPWR4e2LdqYdC1kOGd/wm
+%p/ryWQ8Z1h+OifHmsB4yDGyy5V5uFpKHDAsXqVIj64TkIcOf+d2tKyuF5CHD
+%AylXDEeUCslDhpv2nfjtWyAkDxmeOjjzCDdHSB4yPGuVevvgbyF5yHDv1AVN
+%inQhecjwtYk793GykDzk2NX8Ysuar0KqR46iVP5Q889C8pFj3kuDSS2xQqpP
+%joYDWuf/ihaSlxxf+PUoi38lpHrleOOkdvjXF0Lyk6Pa521i1jMh1S/Hx+1G
+%CZww9vlMjm2Lp6RZPmLzkGNBj/bY9cFCZN5nOu476/YWP7zL5iNHq7DgG1q3
+%2ec3OVaevW3qdIPNS47lccqA1EAhPT/IMZ0/ycTen81PjsZDV+/4eVFI/SDH
+%6c5vmredZ/OU48iN7p7Kc0LqDzl6XXU2Up9i85Xjhv5Nuq5eQuoXOQb5BPUb
+%cZzNW47TJA8rtDyE1D9yjPfwP/T7EJu/HOf17rv69QEh9ZMcz7Xoj324l/VQ
+%YJ5Gb7M7bqyHAuHBsCF3d7IeCkx4fu126HbWQ4G2ZxsHfdzKeijQftr1psJN
+%rIcCM8y/LFFuYD0UaB0SrTt+HeuhwHqfrF0HVrMeCtTvMz3z1QrWQ4FCboNS
+%uox9flZgnx4tUSsXsx4KfHuv2kvtyHoocMWXDVEmDqyHAo2CTZeem8d6KNBj
+%qnO99hzWQ4EH7B9P9LBjPRToGHFHXziD9VBgb4d9tRemsh4KvHz1XTeLyayH
+%Ant8CV8XM4H1UOCMkVYjncaxHgpcN6abg2oM69FR75Tvs97ash4KxM3JbXtt
+%WA8Fpr25fWXkSNZDidf1lpa3W7MeSuzbuVfEx6GshxI3Ogi0AwazHko85/Xo
+%k6sV66FE25/LdtoPYD2U6PZsQdJIS9ZDiQOv6Bv0sWA9lCi4yNtoYM56KHFl
+%U6yt1JT1UOJUt4hRot6shxI3KOSPJMashxLN93vl6PVkPZTY/L6+xLg766HE
+%IapVUuuurEfHfHe+JNh1Zj2UOCWiJGyzEeuhxBXVRulnDFgPJf7PetaiZ3qs
+%R8f+4bSwPBXroURV1to4QyXrocQj/V2WzJKzHkq8O2DNvpNS1kOJfcIUNz+J
+%WQ8lFojPfFWKWA8lBqgn9VoiYD1UKHTg6t/XZj1UOPfEleh2Puuhwp+xr7Y7
+%8FgPFcZ3VWs+0WA9On4/cmO2Lpf1UGFfYd85u9vZ90cV3ihKm5/VKiAPFdrc
+%mxNm1yIgDxXqn71l/KpJQB4qPFUxy2FYo4A8VDhoZoDmw3oBeagwwSZpgGWd
+%gDxU6HqWj8E1AvJQ4dV1Rw8PrhaQhwp/VxQufV4pII+O96i4M7mTKwTkoUK/
+%DTv6p5UJyEOFlY+G/LOpVEAeKtxxcYAbr0RAHh3vJZHL1gQWCchDhbc1upWP
+%7nhfZjxUqO57Lfp3voA8VLjxznOlR56APFR474X0i2WugDxUuDpzSmJatoA8
+%dPHMBqeU41kC/D+QSguY
+%     "]]}, 
+%   {GrayLevel[0], LineBox[CompressedData["
+%1:eJw113dczfsfB/BzTvPMzoispGGEzJtrv98qZFzRtSJZyYpCXDKvkK3749rE
+%jcxKSEY5lAZpUlFJpb3XaY9fnPfn+0+P+n7P5/N5v57v8/30MV7lZr+Gx+Fw
+%VFwO5+dPdg3+Y5N4+KVq5W79wOm5nofA0/+3/IGu7PdzEN594CYdqFY+73u4
+%bN7z6/Cs6XVygozdvwX6g/qU18VWKS9f+nndg0fWhkbCzVX0fCCk7Bv4qFZe
+%pSyf9/MvwdBjiWr0ndBK+vxT6BD6xJg4VioF6X7ukwShMKbG1HA1p5LGewHZ
+%++0H7btVoTR3PBOhGhQGO2rTps+3raDxX4P+DdstzWXlyltdn073ewOhltbC
+%ZWfKab63kL88dvbVUeXK0b+uSEiV5ReeTy2j+d/B1ee6G5bvLFNGqgZ1zRAF
+%O5xbi+t7ldF6YuBqovuk2eGlSvtfE8ZCg8fgHNflpbS+9xAbPnuZE7dUmev5
+%c8APwLkqCRD6ldB6P0LyuaUuJ2xKlFt+Ls89Hqwnx71NKCim9SdA2Lp9JzKP
+%FCvVGkmg52Y18uHAYqonCYKa3nNmxBYpbX9NkAzXJAc2PlhXRPUlwy3jzsI4
+%3SLlz9kcz6RA5tkn7hF3C6neT2CYE3l5l22h8lc56Z8gZmfeAlVRAdX/GdxP
+%PHIY712gNPq5nL6pUGIc6jhlQAHlkQq/dwwM1YvKV651+XmlQdEq8YQbq/Mp
+%n3TQ+K3kdQM3X/lzNP3AdEjs7GfW7cYPyusLRINQ0jTph7Lh53CqL3Aq4suQ
+%S5l5lN9XUOVmFPJ25Skn/xowA/zPXTQf2j2P8syA50NHfen+JFf5a3mHM6Fk
+%pgKi7HIp3yxQZedNtizPUcZ//HllQYjzNff1R3Mo728wKLf4nINZjrLbrwVm
+%w8fBR5b3P/Cd8s+GaTsvhotCs5W/hnP8Dk5X27ttrPhGHjkw0WnO10mm36ie
+%HFgd86Jn4OIs8smBsOJpIT6nMqm+HDjyg38t+20GeeXCbW2vRc9UX6neXPC2
+%2u/FM/9KfrnA3eSv/W7pF6o/Dza+Oe7deiqdPPOg7MDX9TeUaZRHHvydM2hj
+%YnUq+f6AKzGlzm7GqZTPD5iXYM7bN+8zef+AJ2KTqsoDnyivfPhkG+SQEpRC
+%/vlwPim0b7/sZMovH2xsj1/OFyZTPxRAwve5vbnjkijPAugR4NA8LC6B+qMA
+%rlVsSZ9lGk/5FsK9U3H3eZ5x1C+FIGlW/uuZ+J7yLuwab8GecNNY6p8iGO9+
+%4EzhjmjKvwgcbTSuPIt9R/1UBDGWv3lb9owkj2Lw06yb4r3uLXkUQ7NjBkdv
+%k5I8iqFu3o8HI7XDyKMYcFl/j9arz8mjBB79+8a1bUQIeZTAyqiAxNa3weRR
+%Am8MF036cuMheZRCt1PtYX7Pbyt9fnmUwoDjh5J/O3eNPEohzF0ybo3DGfIo
+%A6ltcsLzU0vJowwKl/+5aPPxo6D2KINhTwt+t553GdQe5ZAoVsaeHn0L1B7l
+%0G3btXHHpj4AtUc51C31i9g/LBjUHhVQNfNA46vwp6D2qADz/unbP9o9B7VH
+%BdhscL75Kf8VqD0q4UyNM7zYqwS1RyW0FS7vcffkW1B7VEJvQWa3MY6RoPao
+%gjEF5+18LaJA7VEFpx/vbjvOiQG1RxV8Xvjmo0NqLKg9qkF6LNBP+eAD1VMN
+%a68EO1sd+ghqn2qwG7Dkej+nBKqvGvz17Zf0MkwCtVc1bPg2vL1EmEz1VsPY
+%1QLfqNZkUPtVQ+VX5Qf/8hSqvwYMBla+cMn+BGrPGuAG7p1skfyZ8qiByg+T
+%fpS9SwW1bw2sjO5Xe/tFGuVTA6/GTHA7F5QOau8aMJlqZ+3l/4XyqoXIurm3
+%V1//Cmr/Whi4fyLf5UIG5VcL1teK3Lz/yQT197MWjF2PZoeczKI8a+HJvVnv
+%dY99A3V/1MLmiKySE0eyKd866FXzunrm4e+g7pc6GBXTuf+SVQ7lXQfc2kWB
+%+pE5oO6fOrjbvqZ4ik0u5d/1/O4B3lrRudRPdRDwMbl9v20eedRDbIXy7am4
+%PPKoh8yLgZZWdj/Iox7e7L5w6+anH+RRD2aen4fdXJxPHvVQ5j55vEd2PnnU
+%g2dzg26+cwF51EPtjUdT+OUF5KECYWhP5+JtheShAvel//O93lZIHl33VU4h
+%zYeLyEMFE6Lt+hpJi8lDBa17/+gluVJMHioY1So/8W5ACXk0QIjDswKbJyXk
+%0QBr37y56Iul5NEAW+L8xsQllJJHA7xvaX4Wv6yMPBogvnlQw7OKMvJoAGXd
+%UdHefeXk0QivfDJmWEoryKMRhnlJL3z+r4I8GiHgjPNchzGV5NEI/w4LPZH+
+%oZI8GmGUym6X3Yoq8mgEh2aVLKKhijyaYE5q1jKT6dXk0QQlw8w+LztbTR5N
+%IE57eXFUTjV5NMEGvVozT4sa8miCfO302fN315BHE3zof8Ip4n0NeTTB5rTT
+%VyJ61JJHM3B2Xk+yX1dLHs2QluP7ZF9XX6o9mqGh3qvZll9HHs1gpJ138fGS
+%OvJohhGpbSvCH9aRRzMknvxTd3NnHXm0AHSGXky0ryePFhgzufVOhn89ebTA
+%FIXlf0da60H9vmwBB72ipsa5KvJogVjf7lLjOyryaIFkc9tGfoeKPFrBcOti
+%8aMFDeTRCrevX3GxCGwgj1boF5dvtFmnkTxaISfwndOBlY3k0QomRgY/Noc1
+%kkcrHHlwr8aiRxN5tEF+5KrEDI8m8mgDX/25591TmsijDXQ0C3PLRzSTRxtE
+%X4j0W+nTTB5t0DvJ1zClupk82mBZvHTxNPsW8mgD/rT3CyKftpBHOzxtfHd/
+%aY9W8miHovSG97y9reTRDkGizVMS81rJox3mbDuT/nZGG3m0w4qNx4wKgtvI
+%ox0875Q9ntm7nTw6IPjI56aWw+3k0QFXt3OOltW0k0cHuKnyAu2Xd5BHBwQd
+%uh5mkNBBHh0wanODxazJneTRAaNzd5xvDeokj04oWeJZcEnGQbVHJ7wr2nRE
+%PJuDao9O+G+tcdRcbw6qPTqhbrGv65ZIDqo9OkF/4PUH2zhcVHt0wgl/XbcN
+%k7lIxw8UJI05u2gvF9X1cBCl9scwnItqHw6aTkgy+62Di+r6OFh4PmCbFfJQ
+%7cXBD/d77v3Li4e/ynXhYNbMBSPjY3io9uOg2bkZWavEGqh+P3DQ4KzK64/5
+%Gqj25ODMx3O27L+qgeo8OCjteBpiXqiBal8O8rYlr5w+UhPV+XAQ7jcpyvZq
+%otqbg8YbRm0e+1ET1XlxUf/FX74j+2ih2p+Lnat6DO3cpEX5cfFl8FiLgLda
+%qO4HLh4VGQ9ebqBNeXLxRVzfxQPctFHdH1z8ZJQ01uK9NuXLRV9pypgL/XVQ
+%3S9dOXlM2Hf0kA7lzcVlwvVLpIU6qO4fLjYt9xi3c6Yu5c9FLc+P2U3Buqju
+%Jy7+I9g47mofPnnwcNhL311hx/nkwcPoDudTfm188uBhvJ5BwPGtAvLg4d8O
+%UWbPywXkwUOx8/LprzYKUd1/PBxfHFflXyUkDx6+CVq3a+guEXnw8O7Cjffu
+%6orJg4cbfWIL4q6LyYOHt5Pfz5AF9SYPHnb7LBoSy+1DHjwMM3QOaV3chzx4
+%GGR6rLdhSB/y0MC/0/Iza7obkocGZk8JsRXuNyQPDVz1oXTUkkpD8tBANIve
+%2telL3lo4Pht33pvKOpLHhp4v8+2qhEeRuShgTcW/jN/rE4/8tDAThPrP244
+%9iMPDZQs3DHHLqwfeWjg14FBwQJzY/LQQM9v376Y3jEmDw08qLI/vWKSCXlo
+%otuIJIlzowl5aOLIwRn+O8pNyaPr/iGRbdX8/uShiQZZ3+dO6DGIPDTxYWd8
+%jCKjJ3loYknACkHIoO7koYmPex61bXymTx6aOH5MmMAxWUEemijWd/3DaKiC
+%PDTR8L1NYa+HcvLQxChweatvIycPTVyy0ju6tlxGHpr4+ftTU/xPRh5aGP17
+%4m7jlTLy0MIBPoOzjg2UkYcWLo25u1G7XkoeWniJJ+x5LUpKHlo4u1D/wror
+%UvLQwo+9dwYleEjJQwtv3g84NXWelDy0MP+CXbTbCCl5aGFa6qvF9TIpeWhh
+%k7zVtVeZHnlooXsfk+jqED3y0EJN/81nZYf0yEMba819Xcct0CMPbXwdHjWF
+%a65HHtoY9Ubv3ymdEvLQRp6Z7+176RLy0MaRL+6M7PtYQh7aaOx83Hn9aQl5
+%aGNV+czgTa4S8tDGluKpjzVnS8hDGz1qb+jqW0jIQxtNjZdpe+pJyKNrfQOu
+%pPSvE5OHNmrsSQpsSxeThzb2EY/dXRYuJg8dLHGqds+7JSYPHdTP0f6ccFJM
+%Hjro8yfY+G8Xk4cOLg/33Dx/uZg8dNBb9iQ2aYaYPHTQSDJimNRSTB466G/S
+%mqpjLCYPHTTfGaJ5VywmDx107N1rZ2WLiDx0cGSywcfEYhF56GD47/XdbNNF
+%5KGDzQ+99s+OFpGHLmbrTHzyKUREHrpYzdsd8e22iDx0cfSGqMmrzovIQxeH
+%9c7hO3iLyEMXX05J7/F6p4g8dLG5V8m8UxtE5KGLi55effDOUYTq/VMXvxqp
+%+jrYichDF2M8suNmW4nIQxc5OS79L1qKyEMX2wyyD04wF5GHLh75Ni1wmKGI
+%PHQxooeBdJtMRB58/LtgtCNPW0QefMy//1dxVouQPPgYv2b31tau96nag4+v
+%XgSecCoQkgcfl5ekGWhkCsmDj896xRgXJgnJg4+Dd52M1IoRkgcfT1gIuCvC
+%heTBx3spQcl1T4TkwcdvB9Ymvr4vJA8+2rqeLgq7KSQPPl6/sci++qKQPARo
+%5yX0/NNHSB4CXLpte1WRt5A8BHhO5BR2b7+QPAT4tv2R34W/hOQhwDcnF395
+%7Mb2DwFqu64c27iW7R8CDDgy+tnqFULyEODU0szHzYuF5CHA0nqN5KfzhOQh
+%wPXNrYfPzxSShwCXcS1DL1sLyUOAbgdbPkVMFJKHAEuMv6tEY4TkIURJdIVo
+%13AheQgx2DsmX9OceQix0Op+aJAJ8+iqqxjSPfswDyFO/2qdv6o78xCi6Vfj
+%UBcp8xDioI8Hg70FzEOIs2dGit9pMg8hjjBJutirU0AeQuzu1/j2RLOAPLpy
+%0fJw6V4vIA8h5o/au+ZFpYA8RJhitX7dthIBeYhwT8PVy9PyBeQhQutX872G
+%f2f7uQgnjHs/aEQG289FuNEsbfTUVAF5iNDC/4iPe5KAPET4wcZD+1GcgDxE
+%6DVP8kQjRkAeInx18bnTxggBeYhQ3zH0ZmG4gDxEqMed+JvHCwF5iNCy4mu/
+%biEC8hChysMuOPqRgDzEaFw9euvxhwLyEOPShA6bFXcF5CHGPtutA6fdEpCH
+%GJ10Ok5MvCEgj6733mj9xdZXBeQhRpdHdpccLgrIQ4yTps4u3X9OQB5iHLiE
+%0/+pj4A8xJguWu/SdJJ5iDFlgK/FzGPMo+s9G9Gt495h5iFGf4HLgx4HmYcE
+%b1yO/ufcPuYhweDq0j2Gu5mHBL+9rjj55C/mIcFdaXXOCz2YhwR3xjsO0trC
+%PCRYse+k6s0m5iFBt14Bhkc3MA8JDrsksF26lnlIcHPGWfPxzsxDgsbufZ+a
+%rWQeEuSb/q+ppxPzkGBBt57+PZcyDwmKeesHmy1mHnq4qseQK+MWMA893JOn
+%cXixPfPQw5PWAScP2jEPPbxzQFUQMpt56OH7IXOSamcwDz18azD56LjpzEMP
+%df13NR+1YR56qCzjD86bwjz08EuMz1MbYB56aJt57a+gicxDD+tdtf82Hc88
+%9DDZ82xf39+ZhxT3h5z+x9SS1SPFpcX3W4JGMR8pxqc8gqkjWH1STH3s0TvP
+%gnlJ0fSDzcIjQ1i9UozrnDxplDnzk6JzxSyrwgGsfik6ZZ22v2nGPKXoenXo
+%59UmLA8pZo9qX2jRT0DnGSnaD18wqMOQ5SPFT52Gw1J7M28pHtedavekJ8tL
+%irEjxuZeNBDQ/w9SfL3uqMehbiw/Kc77WuC4Q8H6QYobrrQI3GQsTynOsLm4
+%c5Me6w8pHtq1ymarmOUrxYhYTYO9QtYvUsw38Lx9is/yluKQXe9e+umw/pHi
+%rPMh+W+0WP5SNJ46uyNPg/WTFDO+WFQJeMxDhqtj/XljOcxDhhkV3kUbOth5
+%RIaH95Q5/td1HlF7yPBskEonp4WdT2RoLbr+p0kznzxk6PV41sv1jXzykGFm
+%wZX1z1R88pBh9/DWYzr1fPKQYa0lRC6r5ZOHDDfYTk57Xs2n75sMeZYPAntU
+%8clDht3qvYP3VPDJQ4Z7jYOHFnbti2oPGbbt8Hr5ZymfPGRoar9hX1Qxnzxk
+%2KNuz/cJRXzykKEiup/LswI+eciwMbNshmU+nzy66onIcnyexycPGRY6KEWQ
+%yycPGTqvtDr64TufPGRd++W6IodsPnl0Pe+4Pqk8i08eMrx+uWz6oUw+echQ
+%33SWX98Mdh6U4z1RfHb4Fz55yFFof7f7inTmIceTcycaaacxDznGrYvTfPSZ
+%echxzb31lss+MQ85BuS2TpekMA85Jtne3hSRxDzkODzlwlLPROYhx/xdlncs
+%E5hH13p8EuPqPjIPOe6LmjE+JI55yHGuR3CO5wfmIcd0T9v/Wb1nHnLsNcRv
+%oSSWechxXuTV4qxo5iHHPSr90YFRzKPr+VkNd7zeMQ854qL+q5dEMg85rm41
+%0bWMYB5y5G7b1iF/yzzkeG7B/rJaJfPoWp972j9pr5mHHHdecVgWFs485Cg7
+%fbrzVhjzkGPaC9l2n1fMQ4HXE9eH73vJPBRoNUU0x+0F81Dg6CVyyernzEOB
+%X6sbZQ6hzEOBKxx5bfOeMQ8F7iqT1c4OYR4KfPhxnfGsp8yj69z3tDhx5hPm
+%ocC+kdut/njMPBTIvdB9rX0w81DgmS9xdQ6PmIcCqw6dPLc6iHko0GtC761u
+%gcxDgTNqHhzeG8A8FLjK23n86YfMQ4ELx0zk3HzAPLrGFy6c8ew+81DgvLuu
+%ofH3mIcC1xTuVRXeZR4KNLvnNIB3l3kocOe3yQeN7jCPrvWIXGPBn3ko8Nje
+%KcNX3mYeCjRasrvl0C3mocDl/LOj7vsxD31U8aOPp/zHx/8Dq98lgg==
+%     "]]}, 
+%   {GrayLevel[0], LineBox[CompressedData["
+%1:eJw92XVYVNvXwPGhmWKKDhEMbEUM1Ktrid3YCihiKxa2qIAoBnaDioqg2Ipd
+%iIFSiiAlId3dHe/8nLXf+ec+4zDnnL0++3tm5rlmyzfPWaXM4XCKVTic//2X
+%PbQjVt3cYFYdulf78aRs1+3g/sXs10Yue34Ivlu9ilpRXRX6potX6ew3p6D3
+%mIPB81Kq6PULcCLT8uSYz1WhV3z/PeDzLT+b7vfY31+HuqG1aZyzVaFls//3
+%L/6w03WFS8Ju9v5AMHs28NatZVWhvOSALaN5QbCxuKlu5WR2vHtw9IJdTI9B
+%VaG9HU5/qe/1ENLfKIen67HjP4Zpa0yFxp2VoYHydycHPIX7g7qt3xhTSecL
+%hulPXSKe+1WGWv17PIfsgLEJRRsq6fwv4Ph7nrbhf5WhX+t7yc/wEj41GBkO
+%4lfS9byGjdHN0aNTK0LnKE4Ih78b3xp5r4Ku7y04mTSt67e7IjTb9X8HfAfn
+%qnfacSdV0PV+gKqCRz9zdCpCXf53eVtCYAVf9jMor5yu/yPsteqMdHheHqrQ
+%+ASeE0MylDzLaT2f4Exd3RV/2/LQyf9O8Bly7xvs72taTuv7DEFfs/Y9LC8L
+%/d/Z5KcEbtZSf+MPZbTeryAJG3d0t3dZ6L/lJH+F/FuvdvxYVEbrD4PBwxY3
+%yyzKQk3/dzldvoGp46os2/pSmsc3cJDdtNj5tTR0zer/Pb5D65m2Bu+zpTSf
+%cICEdKubjqWh/zua9uNwcNjeZdT1/qU0rwiYe0IWeaq1JLThf4erj4CUA15n
+%N0SW0PwiIWxa4f1xl0tCx/w7YBSYHjlkpLyqhOYZBQ4TT4z4OLgk9N/leUXD
+%U6zf6sgpofn+gKRLQzM5McWhP3/8ewDf763D8avFNO+f0PbXeKXGuuJQnX8X
+%GAPqNjONXYYV0/xjIM7JqiFWpTj03+EcfkGBXmhit7gi8oiFoea/nddeL6L1
+%xEJiqvYdX+ci8omFlFNZbh+ti2h9seD+ZKJ7sloRecXBDY/pq9N+F9J64+CY
+%hQ8/+UYh+cWBe9SQ/z5vKKT1/4bvzt+KL44oJM/fwD9oc9tZvZDm8RsuRK8d
+%1iu+gHzjgXMoKiPxRgHNJx6W+IU/2r2hgLzjYYX/8yz+iAKaVwKExV/tf0Kt
+%gPwTYM3Er5eUf+fT/BLA5dZI8fLr+bQfEsFpc9cL79bn0zwTwbjlV1nHsHza
+%H4mQvN5NNkoln+abBEe2XJ239Fce7ZckaIjUXbXvah7NOwlS2/5YHFqTR/sn
+%GVotM57vs8qj+SfDHu1xHWs7c2k/JYNd9439JkXnkscfaEvj6Usu55LHH2j0
+%MkqOXp5LHn/A13995p4BueTxB56knjxq0JJDHilwf6nG5wffcsgjBRYdsftt
+%cTaHPFLgs9vTyEsOOeSRCvYf9h4rtMgJPfPPIxU0jvNVR9dmk0cqSIzUc90+
+%ZpNHGvwYtS324bFs8kgD2xubT36fl00eaVBQ9X1/smk2eaSDU6uKb0JJFnmk
+%Q0+uWuOHl1nkkQ53annfrnpkkcdfUDq+bZXTtCzy+AtN+3+46OtmkcdfaNlj
+%cufG50zyyICzi7UHpCzIJI8M8I/aYZRcmkEeGZAuWWr52yODPDJB9PSkNFAn
+%gzwywW30om5T7/8lj0wYZlh0+euYv+SRBWbFaR0Yn07ryYLgbNNLm9ekk08W
+%TEm6vdK9NY3WlwXlPw3/XDydRl5ZcG7bss0e3dJovVlw1K/oVP/XqeSXBd+G
+%PJ4cPTWV1p8NxVMKbC0zUsgzG1JOO0494pJC88iGiPx7NrdUU8g3Gy6Kxzje
+%vvyH5pMN6a1DL13q84e8s0HT0UAyISSZ5pUDw/bsX58/K5n8cyBsQ6bvvJwk
+%ml8OOK5v331mexL1mQOcHxDwQz2J5pkDHaOFWaE+ibQ/cmDtfjO/q30Sab65
+%cMBlgWzShwTaL7mwdUzLk6QZCTTvXNihrZ3aLzOe9k8ubN7/Jsp5SzzNPxf0
+%cnydzynF037KheHdIq9uOPebPPJgdYKpBXT7TR55cClx4tqE53HkkQf+eht/
+%TB0fRx55oPJSctAnIZY88uDswgHWL1bGkkceCPaa2B8u/kUeeTBw29fBNZq/
+%yCMfbv1NO2vTK4Y88iEvob/n6kk/ySMfVreUCKtW/SCPfNCf/eQU/1A0eeTD
+%3uhlBma3osgjH8oadgmuh0aSRwHIek/vofQ3gjwKAB1GDMpqDiePArDy3cs7
+%oxtOHgWgE/9+/73B38mjAAbqlZU+nvmNPAogWtnSqff6MPIohNJC7Y/WXl/J
+%oxCiFoS/HHTzC3kUgsEj9ef+7z6TRyGI+L0CTiZ8Io9CODOieOGZ9FDyKISc
+%Xe5/F2t9JI8i6Dzq0aMJP5BHEfxYfyqqzuUdeRTB+T32vLRbb8ijCIKsH327
+%HfeKPIrAvjN+XDfOS/IogvXBJqKSAc/Jowhix63hdtgHk0cxOP83dMgXyRPy
+%KAbPoaf7DVv7gDyK4b8dU5/afAwij2JY/evarHeSQPIohlvzB00dsOomeRSD
+%o/vxzqz1vuRRArsTZaVzNM6RRwl8nvJshfYUL/IogafF2fnCn+voflkCHwev
+%0zgVtQUUHiWwMtHiid64I6DwKIG6D0fX+krPg8KjFP7Embw9e+wKKDxKYdPQ
+%8eMfKvmDwqMU5t6o5Ol+DgSFRykkcDpDAw7cBYVHKchOtK6WTHgICo9SeDKA
+%Z/pK9SkoPMpAd+noUVGFwaDwKINIx7KNrtHPQeFRBpaW81ISnr4EhUcZpGWU
+%ds649BoUHmWQ9KjT5sL+t6DwKIO+r3q1c1a9B4VHGTzLyf4weWYIKDzK4Rtu
+%MRpoHQoKj3IYZZf3adnZT6DwKIe/qycen3LkMyg8yqFL1igfbbcvoPAoBxU/
+%G+G77V9B4SF//ip+KW4IA4VHBbgOM88UrvwGCo8KmOXzNPazw3dQeFTAgfqw
+%9PL54aDwqICffYpCvGdFkEcFfM/ZZL17SiR5VMDxSV8Tt46PIo9KaEjs8mgG
+%RJNHJej8mvV8+qgf5FEJu0KMcvyG/ySPSpjvn198dkgMeVRC+fvY86qDf5FH
+%JVzYaFAxQD+WPKqgclvtm02XY2k9VTAtN6mbnl4c+VRB7dlrZXqX42h9VTB0
+%4fusgXq/yasKmhcmX313+Tf8W+7qKjgU+2D8Lv148quCHqoLQ375xoPi/iA/
+%vn1/tzFGCeRZBUPumZ9W9UugeVTBjA+LDfRME8m3ClaHR5697p9I86mCbDuz
+%Oyu7J5F3FaTd/V1+PiiJ5lUNE23umRX0SSb/ajhuONbmiPx7jGJ+1eCghIa+
+%g//QfqgG3xNPHgte/6F5VkP4l+3hBaNSaH9Uw9NUx3Fln1JovtXwsNqzfMfE
+%VNov8ufvT2k6/EileVfD/KcvLh6ck0b7pxoM9Mr366ek0fyr4aeT3ZZWx3Ta
+%T9VQHBI1aW5hOnnUgFr40sdKm/+SRw28nhxwR6nxL3nUwKrOX3FH3DPIowaK
+%DI/lzdLMJI8aWBIWl3jgbCbtvxq4Mi8x6I4wizxqYNyJn2qX52SRRw3EHxUM
+%iricRR7y4wVd0xz5N4s8aiBn9Phbf82zyaMGVKpGiB+szSaPGkh92f3Yg8fZ
+%5FEDvMXqFcV12eRRC5xxO1rmjMohj1r4e33qT65nDnnUwtnYX5dqInLIoxbm
+%LbpiqinOJY9aGKqnKVi8MJc8aqFQ8mtp4fVc8qgFu6v+V98V5JJHLaRr3Twe
+%PSCPPGrB4m3IQY1deeRRCy3LNqqvD80jj1pQ43HaLDXzyUN+PXfjK6xn55NH
+%HTgdWbLK7ko+edSBpvY9i4jcfPKog3737p850L+APOrgS5vw0rZdBeRRB/7O
+%2c+DPheQRx0E1AgdtQSF5FEHPl39e6csKCSPOkj/o4Qp/oXkUQfz/Fc/E5UX
+%kkcd3LbS+3DRuog86kB0eELEskNF5FEHPd1X9F0SW0QedRAvOvr9rXExedRD
+%2LvqyCXrismjHjbN21jR81UxedTDt4DYpctUSsijHsww7VyYbQl51EPkWusd
+%ftdLyKMeYkK6C26WlZBHPdiEZvatGVlKHvWwIXj7voPHSsmjHob0DN22608p
+%edRD/rzaO18tysijHjr87p5evKuMPOoh/rvznOnhZeTRABt87ilt0isnjwb4
+%41I+qHZNOXk0gJnJjx0Rb8rJowEe1RzrVcStII8GaN88qmKRfQV5NED+144/
+%Ux9VkEcDTOAZzdzWWUEeDfC8JHWj6pxK8miA99WDs7MDK8mjAQ7ZbpCNaKok
+%jwYI/FRlGNejijwaADr2rI2ZVUUeDXBqit7Quj1V5NEIz5qrtNYEVpFHI1zx
+%+vAzPKaKPBrhq75j8IHmKvJohIvaSeUruleTRyNYmxuZuc6qJo9GKFvNmRDq
+%Wk0ejTAxqb3PpDvV5NEI+guqVIrjqsmjEZRTalzT26vJoxHMqkdpN/euIY9G
+%2P22Z8qwBTXk0QidX3LLP3nWkEcTcLtnmxx+UkMeTbDQ4eqU1ek15NEEM/c5
+%V5zm1pJHE3iPW8ipH1ZLHk1g+ZRvfnJlLXk0gavLI32Hc7Xk0QQ8aC3a8KmW
+%Pj+bIKET1j+vqCWPJlCa9PLGCJM68miCQwYTZqpOryOPJtBqfSZrknem8GiC
+%GXYux0bcryOPJkgZ3e/E55Q68mgG7V+/9m3g1pNHM9TxdtusH1FPHs0Q5587
+%JmhdPXk0w6TdoweNvlJPHs2w+/Sjbs1R9eTRDKbJzq86WuvJoxlcZLOU5/Rv
+%II9mUNpbujhnaQN5NMPZiMaAkDMN5NEMWZMHL6j40kAezZAU8mPIKvk+Ung0
+%Q2FHjptJr0byaIGVPXtv7WnfSB4tMMVr5FuXU43k0QL9HhWNNZY7KzxaYETl
+%S2N+QyN5tMDYqEvR0/o0kUcLvBo/7savpU3k0QIbe8R+DDvfRB4t0PfdzB6N
+%EU3k0QI7Fm5o9OpoIo8WCLFYOF8/Q4oKjxaIfXJO716pFBUeLaD5JF40q1mK
+%Co8WkG36XifUkKHCoxVarG0PpWvLUOHRChmaHZN/mstQ4dEKCw+Z7Pw5SIYK
+%j1b45N1rS9EYGSo8WsFhsINJlxkyVHi0wpqVnGtb7WWo8GiFQ5udshPWyVDh
+%0QptGxdtddgtQ4VHK1SEZ5nUHpahwqMVat2dBPcuylDh0QqOlrsa9gTKUOEh
+%//vpD6rXPZehwqMN7IOXzHX9IkOFRxvYuukdvhYnQ4VHG2SmvHEpzJKhwqMN
+%rH4ccJxYJUOFRxv0O9/o+aNThgqPNjgwrO7ZZpE2KjzaYGHiEu3hptqo8GiD
+%8+laa7oO1EaFRxu8v1jxujtoo8KjDUz6en6ZOUubPNogfH/ieD9HbfJoAyja
+%0SLdok0ebVAx8uqpJx7a5NEO626UqK09q00e7VBuUpc46ZY2ebTDD2fJ7kXP
+%tcmjHQb7LjA4FqZNHu1wYULt4cJEbfJoh9l/zu7dXqhNHu2g17Ww26BmbfJo
+%h2U3eF15fB3yaAdlw/fnzU10yEP+3MZnxcqBOuTRDk3LqnwSx+qQRzvMeZw4
+%fc88HfLogEQ3t6Xz1uiQRwcsueMhXemqQx4dMNxNsvjxSR3y6ICSEaeDevvr
+%kEcHWOxy6Z33Qoc8OqBVvfJBSoQOeXTA/uH3tsn+6pBHB+ztlr/0WLUOeXRA
+%26WGmxPUdcmjA5Tm9soZY6RLHh3gZDq2+PAgXfLogFmrHs/Qm6hLHh1gddNA
+%udFelzw64YptnorJVl3y6IQ+1UOWXTuqSx6dsPH4VgOXG7rk0QkhLuU9/F7p
+%kkcnpFefi+kVo0senaAzwfqdYYEueXSC2q4wvmOHLnl0wpi/Wp56enrk0Qmn
+%BY5lYwbpkYf8+ZGlvIQpeuTRCcsObXuXukKPPDpBd9vgCkc3PfLg4L4bhfdd
+%fPVoPRz8kHxCvf2FHvlwMPY294xxnB6tj4NuEw8e+16uR14cNEy2tdTh69N6
+%Odir4Ty3vZc++XEw7WTKxzuT9Gn9HDR+YN2larU+eXIwtCxGWH1Yn+bBwQil
+%P88ig/RR8XuGg9OcvVLXRurTfDiof23AqOJSffLmYPLNgPEzRQY0Lw4K04Xd
+%Aq0MUPH9gYOcZuONnYsMaH4cDBKHa69wN6D9wMFxyz28iu8Y0Dw5uGLtg+sn
+%fhnQ/uDgn54R8TubDWi+HDzQ2NF6s7sh7RcOWnIe2JrONqR5c1DdL+Zoi5sh
+%7R8Obgy4u6zbY0OaPwffzEk79iPDkPYTB20M1B8ki43IQwknfM4Zaz3eiDyU
+%8HAT72L8biPyUEL19xdFux8bkYcSbsxtHPktz4g8lLDzmajFxMiYPJQwymRD
+%6WtbY/JQwqS5QeXcQ8bkoYRio/jMzmfG5KGED8x9z0xKNSYPJZzqeMSgvs6Y
+%elPCaxNmDBe2GZOHEo7fYdTVqMCYPJTQPnZyq/NdY/JQwkpf9Z+bRxqTh/z9
+%vMC0l1eMyEMJhzyueFUXb0geSsiPFJ01b2YeSmh0vXfJTWPmoYQyk6FqxtP1
+%yUMJ7XY9Vg47qUceSng0+kLj8Fxd8lDC0dkvSmymsZ7lr7tVeZwMY/cnJWyo
+%35//aLoOeShh1/aYyxOztclDGTt8Bl1K+//7rzKqOz/MaeqtTR7K2Ffz0Zf6
+%VPb5qIy/3+/9r/Ac+3xRxm6hya2/bdnnpTKGe7wWWGizzxtltKn3GV6eJiUP
+%ZdQN6xfWFiQlD2V0KDJKu7RLSh7KOMki3mX6VCl5KOOOlgwDblcpeSjj9skD
+%T65pkpCHMp5eobPVNF5CHsp4Yu2CublPJOQhX1+QqdGDUxLyUMZXt4/b9N0s
+%IQ9l1MozzbKdLSEPZSzh+quVDpGQhzLe7TjSu8hAQh7y+Ri+knTjSMhDvl7t
+%tEzfQjF5KKOSUso6vVgxeShjd+eFPrffislDGfd/abUyDhSThzL2i6vftOW0
+%mDxUcIy7hZ/PXjF5qGBj4rTxXmvF5KGCz6fbv7VcICYPFZxpO7DywHgxeahg
+%c8VNHXcrMXmo4M//bjiKuonJQwVbu8WsHCkTk4cKtvCvDstWEZOHChbVP1Dq
+%XSIiDxW0sbkzY9dPEXmooJnOw1sfgkXkoYJ8lWnd4i+JyEMF7bc0xETuE5GH
+%Cm6/8PTjiRUi8lDB9NERyWrTROShgufn3JwxzEpEHipo+XLsXB1jEXmo4M0Y
+%8UM/NRF5qOCms35XvlVqkYcKBjtZHz2YqkUeKqjT8Wzat29a5KGCNeu7jrwd
+%rEUeKmg4++bkTj8t8lDBBKt7L7K8tchDBeFQ7dzRu7XIQxWnDgmMEa/WIg9V
+%jPT2rbadp0UeqvjNs2O/yjgt8lDFDM/T3C6DtchDFc8Ihx/yMdMiD1XUv3Gj
+%p6tEizxU0a7s/In3SlrkoYondYLHz6oRkocq2jxetnxsrpA8VNGZa831ShCS
+%h/x1/dm3TL8LyUMVh/f7O0PtjZA8VNEqqN9ly/tC8lDFiVHCF8+uCclDFR3a
+%ElN3nhaShyre6Ts02sNTSB6qaB2f6xmzQ0geqhhq+Mhr8ToheaiiZnjnDosl
+%QvJQxcMfDacMnC0kD1W8cWe01dYJQvJQxc04e2PFCCF5yOc5v9zjzgAheaji
+%f86LOk53E5KHKo4Xhbk80xeShxq+//7KkaclJA813DvtcffjKkLyUMPO1LGb
+%xzQLyEMNh8ycsbd7pYA81FBzafC4ofkC8lDDZTqLTm9NE5CHGpZ83dUtM05A
+%Hmo4OfI+uEQIyEMNt3vUDx0UKiAPNfRatq639JWAPNRw4+s6E9NHAvJQw1yl
+%J5NnBwrIQw1/Lr3RHnhVQB5quDXe84b5eQF5qOH333bdQrwF5KGGK/8ILT08
+%BeShhhseZoU4ugrIQw1jq9+kLdsqIA81NNo91v3QegF5qOGLVhXJ9+UC8pDP
+%79jYwB72AvJQw69P6sJuzRWQhxo+5R+fNma6gDzUcMfb/a4N4wXkoYZ/Ays6
+%I0cLyEMdDVTjo94ME5CHOoYtGZjyaaCAPNSxxwZeeVYv5qGOylayRV3MmYc6
+%Kk3bULDNiHmo49PDe3pkaTMPdczUNLBbo8U81LFzrOC8iibzUMcgt4jVL5WY
+%hzqeqNRxcW/lk4c6JmXMVXes55OHOu6rThsxr5JPHur4M2BM45JiPnmo4xKx
+%uoVrLp881DEw6q7347988lDHN00bDjQm88lDHR1XPmmx/c0nD3UM2TGr5f0P
+%Pnmo41uLfPPR4XzyUMf3XScm/vjMJw91HL0xw3jzBz55qOOaWbUt3V/zyUMd
+%v0wzby8P5pOHOvZ0con48pBPHvJ5Tc4/8zCITx4aOL0gyuz2LT55aKBHbdzG
+%h3588tBAFeWOO999+OShgYPuDl1Xdp5PHhq4T999TI/TfPLQwAdrLSY7e/PJ
+%QwMbi5ZqhnjxyUMDV6dzLc0P8MlDAys7V/c6u49PHhqobpPqJN7NPDSwn8Y5
+%U79tzEMDNV+knB+6mXloYEfTy81/1zMPDYztkht/ZjXz0MDD4Sufzl7OPDRQ
+%X9u0xWQp89DANYMMZzcuZh4amOxU/Tp9PvPQwL4R9f4/ZzMPDez9dtLh6BnM
+%QwOPTg6I+z2FeWigmv3EcQUTmIcGcvu7RKjaMA8NLDFvshw4hnlo4NZjQZdW
+%jmQemphpvsY7YBjz0MQV6v0elg9mHppoNeveoLEDmYcmzk0/GHijL/PQxEXh
+%uhyNXsxDfrzpAR57ujMPTUzqca9fXVfmoYmhb2o/7DZhHppoPO+JqaYh89DE
+%6w45//nrMg9NvBf6MBNkzEMTF98Z3a9UxDw08THf8JafgHloom5tSuICLp9+
+%f8rPd6StzEideWhi3TqxZrEy89BED8f5Az508shDEwNd327wbeORhybqhF2f
+%tq+ZRx7y9RmE9Hdu4JGHJg7Jq3uyrJZHHpo4zcZCd0kVjzw0ce/X+Sucynnk
+%IZ+v8fwZziU88tDESouOcrdCHnlw0aBvbqRvHo88uHi83XTju2weeXBxwfTq
+%1twMHnlwcbLm9q/SdB55cDHt5rlBU1N45MFFvp2T9+EkHnlw8dQmA6/oeB55
+%cDH8nN4unTgeeXAx0/Ne56oYHnlw8cql5SEfo3nkwcVjmya3GkfyyIOLXX0m
+%9T3wnUceXGzWbU8s/cojDy6WdB8w3+Ezjzy4+GDw+te/P/LIg4vWx9OCZ33g
+%kQcX3wgmmce/ZR5c1N8cKXR4zTy4GCBy8C5+wTy4uK1gw4V9z5gHF98GCLrq
+%PWUeXOzhHVf48hHz4OKQ08eP2D1gHlzc3vKsWfUe8+Bi28bAvy/uMA8epsVX
+%aq8NZB487Mrv9sbsFvPg4f2tKh8zbzAPHkbFb7UN9GMePMzQnvZjw1XmwUO9
+%BVuPjfJlHjx0nucUL7zMPHj4ouBeVP4F5sFD4cQTsz+dYx48PLX4euutM8yD
+%hzu52hVHTjEP+XUc0lbfdoJ58NDnQr27kzfz4GGu/lWzeUeZBw9DHk57PP0w
+%8+BhdPnpBZMPMQ8eFo7JmjDFk3nwcNecFS0zPJgHD7+nxzfNd2Me8jnv1lJe
+%vo958HDgJf3D21yZBw9t1+isOrabefBQypEW+O9kHjycXTN04aftzIOHomsj
+%qrK2Mg8+ui0xDldzYR7y+9Cd8V0HbmYefAy4UHnJfiPzkN8nNrhPOuHMPOT3
+%sbbi+Z/WMQ8+frC/kdO0hnnw8b86s19Wq5mH/L543/zPtpXMg4/L591seLmc
+%efAx92fuxbZlzIOPYpHD3YmOzIOPEVM6Us4vYR58FF1WM8y3Zx58XNX+NNPa
+%jnnw8XifAMGZRcyDjxMnqF8pWcA8+HhS2WP2lPnMQ35fvJJ69f5c5sHH4RyL
+%3lpzmAcf0xd9qdlhyzz4eFWUbJY9k3nwceDjZp+ZM5gHH826aPf+NI158NGg
+%aM9Iq6nMQ/658tYp+v5k5iFAs0dzw7tNYh4C1Lh3a5P/BOYhQLcOpzVm45mH
+%ALd0fNALsGEeApxun7Cl91jmIcC7pTJRMDAPATrH2W3+bwzzEGBS6C7zqP+Y
+%hwA3+ElLFo9iHgL8o5TBqRjBPOTfe0uyfQ9ZMw8BxvYP5pkMZx4C7GNjFfZ6
+%KPMQoP9Tw/L5Q5iH/Hvfw8fRDYOZhwBfF6X4+FoyDwEO2NeUPGYQ8xDgs9zZ
+%WDCAeQhwtNeGfWf7Mw8BRs8O7j6mH/MQoM+kdQ3lfZiHAPe47hx4ozfzEKD9
+%1GPFc3sxD/n36h7h/XgWzEOAPz73bP3cg3kIMSanydWtO/MQ4sUTGVUjuzEP
+%IX7RXb6t1Yx5CLFS1yL3Q1fmIUT3wDfOB0yZh/x30/4818ldmIcQh5760CYy
+%YR5CPPSpMyPNiHkI0XLryJV3DZmHEBOnJq3bZcA8hPj2L+f1ZH3mIcSI+Dgv
+%Iz3mIcQFrqHSah3mIUTrETFvw7WZhxB7Buzz8JcxDyHOzR9lsF/KPITYlqGe
+%YCdhHkKc/t3+ywgx85DPozM92FDEPIR4xpQf2S5kHvLX7wXeyhEwDyEunryU
+%E8VnHkI8V9hd/TmPeQgxuM/PD35c5iH/3edxcI23JvPQwhGnFlvs0WAe8t/5
+%9SZX1qkzDy2M9HB+YqfGPOS/s9+VBc1UZR5aaF615MJ4Feahhd57M9tHKTMP
+%Law+btIyRIl5aKGL2oIRgzjMQwvPLVxr2a+TSx5aqFbb9rpPB5c8tDCxxapv
+%33YueWhh6YjlZv3auOShhYVHuv4a2MolDy3c7jkwf0gLlzy08EzQMruRzVzy
+%0EK35W6fbJq45KGFuW0DNKc3cslDC78Vx6QtbOCShxYKVk1atFL+uavw0MJ3
+%JywOba3jkocWFl+J6eJZyyUP+Xq9hxy8UMMlDy0cr+kfcqeaSx5a+OitrOhd
+%FZc8tLDPz+9r4iq55CFC3cSka0UVXPIQYR/D1c5K8ucKDxHOv6xhZ1TOJQ8R
+%ThumNmF4GZc8RPh+bMvcefLvDQoPEc749DhnawmXPESo4pq8/1wxlzxE2COm
+%0fFFEZc8RLindO/VpELmIcIt3OdzWwuYhwgLB9w50bWAeYjQr8T29cR85iG/
+%3guD52zOYx4i/Har+bNPLvMQ4X2Tl9Zfc5iHCO0GaAytzGYeIqySFv0yzmYe
+%Imzt+Nx3Wpb8e9M/DxF6fGtw35vJPER40v7gvocZzEOEO9y2xGX8ZR4ilIQc
+%PSL9yzxEuOqS7bpJ6cxDhJrfbdfuT2MeIowcYnTlRSrzEOPie38sylNoPWpi
+%HDql++yeKcxHjJMD9qx3+sNV/L4SiTFj9PrVfsnMS4zCLocbUpNovQZiHJQb
+%9Uk/ifmJsf/GH6sXJdL6u4lxn3FXb58E5ilG28lvrFPjaR4DxGjS093TJJ75
+%yq/vcOVJp9+0X63FCHc44XfimLcYI3vN61keS/OyEaPHU9fbVrHMX4x/0qe4
+%7/tF85suRpedvZ98i2H7QYwOa5MniWNongvFeHez5lr7n2x/iFG6qSwj6AfN
+%10mMu33E3vXRXPr/M2LcmfLOcXw0zdtZjKFFtyaej2L7R4xTQxKm5kXS/HeI
+%Uav+cvbQSLafxDjs774DRyPIw0OMF6dKPNPD2f4So9L7oNGDwsnDW4xL736x
+%PvydPE6LUfy08EP6N/K4IMa9nQNdhnxj+0+Mzlofu58II4/rYoz1v7Qr/yvb
+%j2Lsod3zGH4lj7tiNL/qtfXqF7Y/xVgbURTR+Jk8notxTop9+bzP7P4hn2fR
+%nsPPPpFHiBi9v8ycLfnE9q8Yx7ROt3YJJY9wMdpMKNz7+yPbz2J8bRmYN/gj
+%ecSJ8Zn3tbGXQtj+FqOeS6R58wfySJfP67j7tSUf2H4Xo07WyxVf3pNHgRjD
+%v3Gn9XrP9r8Y59343n7qHXlUiTH54CnlhresBzHyup8/svQtebSIceIky6jw
+%N6wPCfImunEGv2F9SHB5w+kF116zPiT4yajIU/M160OCI3RTi7a/Yn1I8P6B
+%rvNyXrI+JHhmXvDbWS9ZHxLcmnd7QOgL1ocEN2+z2j/gBetDgpUOlrIbz1kf
+%EhRJTs8TP2d9SHD6l/SOA89YHxLM1vXIqwtmfUjQRFY+Ym0w60OCQ66V6aY/
+%ZX3Ir0fXPmX2U9aHBK8Orov+/oT1IcGVfNmm0U9YHxJU7Xzj+OIx60OC5Q+l
+%yX0fsz4k6DRo95/AR+x+KsFeTU+bTR6xPiRombYs+vJD1ocEfU+UBkgfsj4k
+%aBqy/NnJB6wPCe538+XzHrA+JDiH093/8H3Wh/z6ZitPUr3P+pA/r3dw8rzH
+%+pBghVvIcqV7rA8Jrg3r2ONxl/UhwdATlQ2cu6wPCRZPNz1+IIj1IcHMEVwP
+%5SDWhwTx5gMrrzusDwmm3+NlaNxhfUiwR9rXTO/brA8J7ozO6SW6zfqQ4MJ+
+%M36dD2R9SHCFZuZ4g0DWhwSl16q8bgSwPiTodUWrqEcA60OC9kX3vz68xfqQ
+%yH9PRlUNucX6kOAMfdd+If6sDwk6SAP6TfQnjwIJTvjoZhZ7k/UhwbFGz/Xt
+%brI+JBhXku6fd4P1Id9PE/c1b77B+pBgxrQ67bbrrA8pug2fa3f0OutDik/s
+%dq7Quc76kOLMhcEzA/xYH1I8YCrbaenH+pCiclBYbug11ocUc0x9fs26xvqQ
+%4rqI/g8yr7I+pNhRa6W+5SrrQ37+RWeclK6yPqTo02XbwPNXWB9SvGp+jtvj
+%CutDig8Ww7nXvqwPKR4/kxs71Zf1IcXYniYLM3xYH1IcvXhb3VYf1of89cu9
+%MjR8WB9SBM/lln6XWR9StN07c7bVZdaHFIc2+a2JvMT6kGJJxeHJyy6xPqS4
+%I8o2o/Ei60OKl/PcY09fZH1IsV4y06zXRdaH/HifNJs/XWB9SDFyx9M3dhdY
+%H/J5j7C2rTvP+pDiO9kjvdPnWR9SXPGr5mDv86wPKf52F+K3c6wPKQq2meg6
+%nWN9SHHMqoCX7WdZH1LMU3Oec+Us60OKTpsLXYafZX1IcXF08qLEM6wPKe50
+%SF+77QzrQ4oG5zdNlp5hfcjnYf94TvBp1ocU9z27mmZ7mvUhxe4xfl2rT7E+
+%pNimOePz2VOsD7m/Wr+8wadYH3JfTumwhJOsDykeGzR0z86TrA8pJrmHb9I/
+%yfqQ4vh5zRbvT7A+pFho88pu6QnWhxS//ne9WukE60OKfbgD228fZ31IMfPS
+%lZCpx1kfUvQYc+ZXpTfrQ4YXu3wruODN+pDhbmn/8yO9WR8y/HZhQlvWMdaH
+%DMdm7j5/5BjrQ4ZvRjvYDTjG+pDhzqcLkhOPsj5kqPLMInX/UdaHDCP9Hkzs
+%eZT1IcOS1idhMUdYHzJU35aXt+sI60OGsn6qaHaE9SHDISk7rKIPsz5k2K1o
+%ydYdh1kfMpxnOrzG9DDrQ4ZDBeP+RHmxPmRoXmG/facX60O+fjX1ueZerA8Z
+%+k7Xt4s5xPqQYbD5r7+uh1gfMrwQvJHb6xDrQ4b97cVWiQdZHzIcPmvBE8+D
+%rA8Z3r/829jyIOtDhjru+ksyPVkfMsxcN8zllCfrQz7Pvu8jRnuyPmR4t8fg
+%JeUHWB/yeW1sWuB3gPUhw6kfg/rNOMD6kKGueVpnuwfrQ4bTnxkrPfFgfchw
+%WlwNd5kH60OG1qXyu4wH60OGASPqm766sz5kuMP53eqd7qwPGbo+K2ro5c76
+%kGGb106zdDfWh3z+41f7nHZjfchQeewUu3FurA8Zbnru4dq4n/Uhwz/n3oc9
+%2M/6kOHxVe+PLNvP+pCh1bCJ3jr7WR8yTHjbMjl6H+tDhhmZCYke+1gfMvy1
+%wSV/2D7Wh3y/nMsYV76X9SHDo9OOBwfsZX3I3/986F27vawP+d/r6CyU7mV9
+%aGNE04ukSFcu/h+i+QLA
+%     "]]}, 
+%   {GrayLevel[0], LineBox[CompressedData["
+%1:eJw113lczNsbB/BpmmavZokkW7cI19ZybZfOI5VoVymKJCJbiVC3ouxLJJWI
+%uOLey00JP1uYuAiFlLQo2hftm/b6DXOe7z+9pu/M+Z7n8z5zzjM6a3yXrmMy
+%GIxOJQbjx1+8Ul26/o4JaJf9oZG8qCxoP7EvejblzHx8HU3Kp3Tv3KfSLrs/
+%5kC9w/0EYvLPcO7Gt230/hUSCL43zWPaZOfO/riukUvnfW4OX9lG359MLp2e
+%4fRVr03W4PDjP6nkOLep5s+GVvr5O8R/ZtyTlXdaZfz8RL/5/HvkN9fQC5rB
+%rXS8B0R9W1JLxsJW2ST3k886Jz4i3df91u0UtNLxn5CuwPe3dXNbZFfkn85P
+%TCdX6xcp551roc97SsaeXSA7sKZFZvTz+o+cu2fBNpjcQp//nGw94u32sqJZ
+%9l/nRPkTXpBj6y1fGm5vpvPJIAe1JiYnMZtlS38+8BVJ3xWvPzOqic7vNXmX
+%EvnwjU6TrCzox4BviOGo0dt9UhvpfLPIRH1zkfaCRtm2H9Pze0vYlebXKrIb
+%6PzfkfS0E4bpqxtkCo1sovf0TMejlnpaTzYxStlZk763Xmb58wEfiEWWzrh2
+%UT2t7wMZ+76vYOaf32Q/nuZ+Moes+8e++ILBN1pvLulmDSpNe1Yn+1lOfi7J
+%2q+kW7C0jtb/kahbvitKrqiVjf0xnTF55G7pwekXd9TSPPLIxOEub9JUamXr
+%vX9cn8iI7SY3hWdqaD75ZJnBKub+iTWyH6NpJOeTzZuAO/FhNc2rgESvbe6u
+%saqWff8xXGcBufAx51FuSRXNr5DYvXROH+ZXJTP5OWARmRzfp7qDWUXzLCLW
+%He4uddGVsp/TO/CZBCzmBH3Sr6T5FpOh/7UEZD2skL3N+nEVE6dLHZ6DNhU0
+%7xIyI377/ZCyctmwnxP8QtiJLrGTAspp/l/I4/P321R45bKfw7l/JfY7Pe5r
+%XyijHqVkzICdT4JBGa2nlFyws3TUfllKfUpJsdLhEqZbKa2vlPS/D9zlWPqV
+%epURrdyQ2z6Cr7TeMtKt22sTP/ML9Ssjq13q/FevKaH1l5M7ET3VshPF1LOc
+%kDuSwZK0zzSPcjLX0C00qK6I+lYQ3U3SeDfNIppPBdGPdB78xaKQeleQw8KO
+%x3sCCmhelWTqNqdxm6/mU/9K4mhePz4z7xPNr5K4f+v9PMj+RNdDFel6wdRM
+%nJ1H86wi1zh3H52OZoNifVSR624L1xgks0GRbzXZ8i79iyyDDYr1Uk36NijX
+%TStjgyLvamL6yND/VC8bFOunhlxcJ/izUMoBRf41JInt/og3lQOK9VRDLryI
+%mDDVggMKj1rSvuLwQkMPDig8asn1lPDrWrs5oPCoJbuW/6vaFskBhUct0Q4X
+%cFKvcUDhUUfMohOuOT3jgMKjjqiP6tSsLeKAwqOOpAdUqy1r54DC4xvRyOmX
+%3RZwIfKnxzfya5ZdbKcuFxQe38jbm0kfpfO4oPCoJ5n9jp1jnbig8Kgn+5M4
+%9fzNXFB41JNAPe7Bin1cUHg0kKIzyeHR8VxQeDSQB8Xx18ff5oLCo4Esn9v6
+%9NIbLig8GoljSnrGYBkXFB6NJOiwh/eMHi71aCRXhwseWol41KOJbMn0Zi3S
+%51GPJpImWOM8zIRHPZqIzW1OZoYTj3o0k4TR0+4s38SjHs3kaKnV8PQwHvVo
+%JiwDHw+lOB71aCFWMIM1NplH62khUS9reRrPedSnhVgbGJ//XMij9bUQVePI
+%wh3NPOrVQiKs1OrqWHxabwt54B+UPmUkn/q1kFcjWx1Np/Np/a3EyPjRhwlm
+%fOrZSlK5r5vyXfk0j1bCuFbW5rCFT31bSalhsvf5MD7Np5W0rPhNIymGT71b
+%SXH1dM2Qa3yaVxvxbw3J037Mp/5thPVCemR3Np/m10ZeXnTNiKngg+L72Ubu
+%elZxt37n0zzbSO7iPZUqPAFdH21EZ6bqTXttAc23nZjbs7udpgroemknc097
+%zFcnApp3O3kxm1+xw15A1087+T1jUVCkp4Dm305m/hK/aqm/gK6ndhJ0+qHa
+%w3AB9eggy2sdZ72NElCPDtJQlKsTdllAPToIz1qq/y5VQD06CPu37G1P0gXU
+%o4McKw2fZ/FeQD06SEZtZLRXiYB6dJCjG7duEdULqEcnCf3rz9k23QLq0UkK
+%HErbR6gIqUcn+Tt+dukmsZB6dJKsC8b3zEYLqUcn6XltrHt5opB6dJI6rvX6
+%UCMh9fhOVmUM+uTNF1KP7yQyqOvBP4uE1OM7ebvdM6zNXkg9vhPd3P2bUpcL
+%qcd38m6q/5QyTyH1+E469l2Yvd9HSD26SB37zalYPyH16CLrJ++ql+4SUo8u
+%EqezIHgwWEg9ukiQC+uEY7iQenSRoqilT6SHhNSji+zZ4Fv7+zEh9egmH9Sm
+%2r0/IaQe3US3503em1NC6tFNJp2McjOIFlKPbjLayrKOGSukHt3k5DdG3IIz
+%QurRTbQttkqb5K8VHt0E3L5bCeKE1KOH7LnrnHNOfl/h0UP+47jMuxSLHj1E
+%dbxgkXYMevSQvcabWBqn0aOHzDDfueB4JHr0kD7fuqDDEejRS3Z1nq/jH0WP
+%XmJw+Z3WiIPo0UtsjmnF3AsT0v2yl7wemPBLQzB69JJTUu20tF3o0UsKTQaL
+%J/mjRx/JC1N78Ptm9Ogjz6Z5CLrXoUcfMbw4RcvLAz36yGpXJdtwV/ToI9yC
+%m87uDujRR9JWbwvrWIwe/eTS+3y2gyl69JMvo6bl75yLHv3Ex+PTk42G6NFP
+%vubv/NNwMnr0E795re45OujRT9gr8sfba6FHPzENeTGYJEKPAfIhKHKoiYMe
+%A2TNeda/2kMC6jFAjpjxcmZ+F1CPATL54f63CxoF1GOA/JUOdxdUCqjHAPFe
+%/+z4/M8C6jFIApqjOUY5AuoxSMIuGSye8FpAPQZJV9XacaPk32+FxyDpfMxa
+%NeyegHoMkqf3v/2nkYz71SCJPrabMe4q7ldDJDUmjGt4HverIeIoOKu+7DTu
+%V0Pk1OQRJYeO4n41RPr3Maa+DsP9aojcC7YyHBWI+9UQmfXilvp+P9yvGDBB
+%i1U7uB7rYUD7pkTdUx64fzHAMMi9ZI4L1seASZ4uen22uJ8x4KXppLAPFgL4
+%Wa43A/ROTD3z0gT3NwbUloqO58wU0P2BAQEaVqo903C/Y0BC2JK7s/QxDwbY
+%JwiXxY7F/U8+fuHBa6ojMB8G9J4rq7oswv2QATc812gv5WFeSlAyh9czion7
+%oxLMspycxOrj0/yUABqjVTkdeH4pQaL5yJLxjXyapxI0vp/DW12N55kSJI3R
+%s7n7lU/zVYIp1e6dEwvxfFMCW/u7pXdy+DRvJQh+ZfjOPQvPOyXYslY8oPOS
+%T/NXgm0HC58y0vH8U4KeUCLpesCnHky4t/d4GP8OnodMmLUuzW9mMp96MCGx
+%57ZO8D94PjLBOfn60+LLfOrBhJXR622WXsDzkgm/ivcwq8/wqQcTtG1SNkdE
+%4fnJhNa7anE2EXzqwYSooazB8YfxPGXCwemZK4ftw/6ACQsjR87QCuVTDyY8
+%VdeuNw7EfoEJSapm1T47+NRDGeIfx7Ym+2L/oAy/eV2yUd2EHspwYkdeQag3
+%eiiDkW6OWGUNeihDjdGXlxdXoocyjH2X3WazHD2U4Y1txr9qzuihDJ8fP3lW
+%YY8eyuA/+GHtG2v0UAb192eVn1uihzIE2M43yjZDD2X41M1PagD0YIF63PhC
+%7fnowYJXGr7PV8xBDxa8WP5r/7+/oQcL7G61HxUaogcLtoYdbv9jGnqwYPtS
+%k4yByejBgojzR8Mi9NGDBcPZA+ZT9NCDBQZeB1IKx6EHCz5/U74YOxo9WLCs
+%rt3McyR6sKDu2j3vOZrowYKKPz6+HquBHirQo9PDlIrRQwUCnM9HaaihhwoE
+%dwY80BGghwq8rPaYO4+LHiqwt1LNxksFPVQg+6lgZCwTPVRgR88vbz8O8aiH
+%ChBPVcexA9i/qkBJlaZbQC+PesjvP9FOyuviUQ8V2FXtc9q0k0c9VODwHKWZ
+%D9qw32XDgb73z+e2YH/NBqXogIEXjdj/sqE0EhLc63nUgw2XN6Sf7K/FfpgN
+%1rKBwqvV2I+zIS2WMcG1Evtj+Xhz42KGl/OoBxt87w0zK/6K/TIbTB24XddL
+%sH9nQ9jhb9Xhn3nUgw3m1o4Fa+T9tsKDDR990q4syedRDzZ0ufZdn5eH/T4H
+%1mjV+M3M5VEPDri0n1SZ/YFHPTjwNeBxKrznUQ8OJIZYDjq8xd8HHAi0LXyz
+%MZNHPTiw+9n220df86gHB1zvORWnZqAHByygmXx9gR4cyDArDBz2HD04kKSf
+%luvwDD04cPNzqUtMOnrI75cHZ355gh5cuKW82Xv6Y/Tgwh33tcUH0tCDC5wl
+%3ZnlD9CDC8Yfnn02v48eXJCcDhtMvoseXHC9IRga/T/04ELyQG9M1G0ePT+5
+%ULv5oonqLfTgQlTK/7wjbqIHF/bYjd8iTUEPLixq+scj4QZ6cCEwM9FrWhJ6
+%yOfj98Du+XX04EHXaaWkVdfQgwftgWNeD/6NHjzY57t7deJf6MGDzQWMYuur
+%6MEDr4sZ4r5E9OCBVbzm8OTL6MGDML249+v+RA8ehLfpOOlcQg8eHKpLaitL
+%QA8e6OU97//rAnrwYHLMQnO/8+jBAzuN2Ffz49GDD51Z043Uz6EHH4pWm9hV
+%xqEHH2xcSooen0EPPviOq4qMj0UPPvz++Oyp4Bj04EOL+Pkjz2j04ANv7Mnz
+%S06jBx/e39qvNysKPfhwNXFzoP4p9ODDwI2cbdqR6MGH7sTqPZKT6MGHhfXb
+%U4Qn0IMPnBmH7vIj0EMAq41/1RMeRw95H+HsrSk6hh7yvm3oYq/mUfQQwOxr
+%Goa6R9BDAAvVHhobHEYPAUStdHY0PYQeAvC3t/BadhA95H3Rp7YNWw6ghwDq
+%0z1WHNqPHvK+4tIo3Sv70EMAWrmRr/8LRw/550e/elgVhh5CMPUKuCAIQw8h
+%KJtHjTHaix5CmB5XHr5yD3oIoWMo6tvRUPSQ/66asD7vQQh6CKH1jmpCQzB6
+%yPve/KHfdYLRQwgjuLVrXf9ADyE070v5cCoIPYTAE1otfxuIHvLXHZJPgkD0
+%EELgUNYU693oIYSWRP+CE7vQQxWKwtR4H3eihyo8d24pH7kTPVTB0WNayNoA
+%9FCFsoHWTyk70EMV3EadujOwHT1UITmj09lmO3qowqHbK9IT/NFDFe6daE5s
+%24YeqpB9vcHccht6qAIj2/fART/0UAWf6kSVHl/0UIUd43LWO/mihxp4KU27
+%mroVPdSgpj/EUbQVPdRgha5+yLYt6KEGUxP4IR83o4caXPnlatjszeihBluP
+%hYYnbEIPNfBxel7F3oQearDD8V2o30b0UAP/htBzn33QQw02xD6KsPRBD/l8
+%0pd8u7sBPdRgVohV44QN6KEGUZ1/x8StRw91UJ+496lgPXqoQ16/m/9eb/RQ
+%h1yp6Oz3deihDtlHvlpuXYce6mDIH7GxZi16qIPrScsBz7XooQ6/apV1lHih
+%hzqUelUGunmhhzpIh91eWLQGPdRBP6NUe8Ua9JC/P+1V2WdP9FCH56OFD1d5
+%oocIGkbYnilfjfWIwFaTKd6wGn1E4P6x41GTB9YngtEtYck7PdBLBKmGUZVD
+%q7BeETzYdsLh6Cr0E4Gnw+bKYauwfvn9xBdNl1eipwgkqcojDVZiHiIw0ro1
+%IJPv44rfMyKwV7pB7N0xHxFsX5yQXOaG3iLomLbXdIcb5iWCvz6Gf+W4Yf8g
+%Am1vm8rzKzA/EUQZ5hwwXIHrQQRZDDXH18sxTxEEly177rkc14cI9qyWru11
+%xXxFkGtmuyDaFdeLCFaGul+f7op5i2B+t8+iTBdcPyJghbWKNrhg/iLYrR+s
+%z3bB9SSCfQdYrleXoYcY1vXPyzFbhh5iOJq+p6bKGT3EUN68J+uQM3qIQbfY
+%Y/RkZ/QQg1bqf5ffOqGHGD7pbyzb5oQeYuAtyPDVdEIPMawqGHPrsSN6iGFj
+%+pHCtY7oIYZXupeyhI74fZO/n6/E/99S9BCD2vclh1ctRQ8xGD0ouMVdih5i
+%MEzJHrjtgB5iEPeWtXs4oIe83mEuSUIH9BBDQo/6uIf26CGff2ZjyAZ79BDD
+%jTDueE179BDD6+E1rS/t0EMMA8t6L++yQw8xtB62ZE6yQw/5eAHLm4ps0UMM
+%Q580QyJs0UMMe6b9dgxs0UMCV3b01HXYoIcEbK80ZF+zQQ8JtAUVqnjYoIcE
+%fHXemAyzQQ8JnHKZfSvTGj3k9x9ywvZZo4cE1v7x7tbv1ughgUxVu/Z2K/SQ
+%wNQPX1JuWKGHBIxkxhnrrdBDAmXujdN/sUIPCSwxWGBesgQ9JHDd5eTss0vQ
+%QwLvh5+86rwEPSTAMTh/TLIEPSTwarePT/Zi9JDAv4bxlScWo4cEsk5PLbBZ
+%jB4SsOHMSRIuRg8JpD4dpvfWEj0k8LHZ+EiEJXpIYNSdDeNsLdFDAj47Hqap
+%W6KHBLT0nmrnLEIPCVSY6dZHL0IPKVwNFX5zWYQeUri7eIqR9iL0kAJX/1dG
+%qQV6SOHI5CneVyzQQwqhST1nfSzQQz7eVqn1dAv0kAJ71ZOTneboIYXHN71M
+%HpmjhxQcnGM37DNHDymEWNx3tDJHDynMGLHAV2qOHlK4b7x8eLEZekjB7eMx
+%vatm6CEFE+ulwVvN0EMKKddI0Gwz9JCC8dncCqYZekjB1W+4yduF6CGFywfW
+%jDq7ED2kwGl69mjtQvSQwuDe2rIZC9FDCtWnvEIHTNFD/vwt7/a+MUUPKVjH
+%6XLiTNFDCn9kjvmwzhQ9pFD4yiXfyBQ9NGCbumUmU/76/4f9glI=
+%     "]]}},
+%  AspectRatio->NCache[GoldenRatio^(-1), 0.6180339887498948],
+%  Axes->None,
+%  AxesOrigin->{0, 0},
+%  Epilog->{
+%    InsetBox[
+%     BoxData[
+%      FormBox[
+%       InterpretationBox[
+%        Cell[
+%         BoxData[
+%          FormBox["\"|q| = 0.02\"", TraditionalForm]], "Text", "TR"], 
+%        Text["|q| = 0.02"]], TraditionalForm]], {0.8, 4}, BaseStyle \
+%-> 14], 
+%    InsetBox[
+%     BoxData[
+%      FormBox[
+%       InterpretationBox[
+%        Cell[
+%         BoxData[
+%          FormBox["\"|q| = 0.05\"", TraditionalForm]], "Text", "TR"], 
+%        Text["|q| = 0.05"]], TraditionalForm]], {1.67, 6.4}, \
+%BaseStyle -> 14], 
+%    InsetBox[
+%     BoxData[
+%      FormBox[
+%       InterpretationBox[
+%        Cell[
+%         BoxData[
+%          FormBox["\"|q| = 0.07\"", TraditionalForm]], "Text", "TR"], 
+%        Text["|q| = 0.07"]], TraditionalForm]], {2.85, 12}, \
+%BaseStyle -> 14], 
+%    InsetBox[
+%     BoxData[
+%      FormBox[
+%       InterpretationBox[
+%        Cell[
+%         BoxData[
+%          FormBox["\"|q| = 0.1\"", TraditionalForm]], "Text", "TR"], \
+%
+%        Text["|q| = 0.1"]], TraditionalForm]], {3.2, 7}, BaseStyle \
+%-> 14], Null},
+%  Frame->True,
+%  FrameLabel->{
+%    FormBox["\"Impact parameter, b\"", TraditionalForm], 
+%    FormBox["\"Spatial rotation, \[Chi]\"", TraditionalForm]},
+%  FrameStyle->{{14, 
+%     GrayLevel[1]}, {14, 
+%     GrayLevel[1]}},
+%  FrameTicks->{Automatic, {{0, 
+%      FormBox["0", TraditionalForm]}, {
+%      NCache[Pi, 3.141592653589793], 
+%      FormBox["\[Pi]", TraditionalForm]}, {
+%      NCache[2 Pi, 6.283185307179586], 
+%      FormBox[
+%       RowBox[{"2", " ", "\[Pi]"}], TraditionalForm]}, {
+%      NCache[3 Pi, 9.42477796076938], 
+%      FormBox[
+%       RowBox[{"3", " ", "\[Pi]"}], TraditionalForm]}, {
+%      NCache[4 Pi, 12.566370614359172`], 
+%      FormBox[
+%       RowBox[{"4", " ", "\[Pi]"}], TraditionalForm]}}},
+%  FrameTicksStyle->{16, 16},
+%  ImageSize->600,
+%  PlotRange->{All, All},
+%  PlotRangeClipping->True,
+%  PlotRangePadding->{Automatic, Automatic},
+%  TicksStyle->16]], "Output",
+% CellChangeTimes->{3.556953242036603*^9, {3.556953596625984*^9, \
+%3.556953702375863*^9}, {3.556953796337514*^9, 3.556953956593231*^9}, \
+%{3.556954020687426*^9, 3.556954030706046*^9}, {
+%   3.558693364370013*^9, 3.5586933829491863`*^9}, \
+%{3.55869345851305*^9, 3.5586934742140837`*^9}}]
+%%EndMathematicaCell
+p
+np 33 1 m
+33 273 L
+469 273 L
+469 1 L
+cp
+clip np
+p
+np 35 3 m
+35 271 L
+467 271 L
+467 3 L
+cp
+clip np
+3.239 setmiterlimit
+p
+np 70 3 m
+70 238 L
+450 238 L
+450 3 L
+cp
+clip np
+P
+p
+np 70 3 m
+70 238 L
+450 238 L
+450 3 L
+cp
+clip np
+P
+p
+np 70 3 m
+70 238 L
+450 238 L
+450 3 L
+cp
+clip np
+0 g
+0.36 w
+[ ] 0 setdash
+3.25 setmiterlimit
+78.19 226.892 m
+78.919 226.724 L
+79.648 226.555 L
+80.376 226.386 L
+81.105 226.217 L
+81.834 226.048 L
+82.562 225.878 L
+83.291 225.707 L
+84.02 225.537 L
+84.749 225.366 L
+85.477 225.194 L
+86.206 225.022 L
+86.935 224.85 L
+87.663 224.678 L
+88.392 224.505 L
+89.121 224.331 L
+89.849 224.158 L
+90.578 223.984 L
+91.307 223.809 L
+92.035 223.635 L
+92.764 223.459 L
+93.493 223.284 L
+94.221 223.108 L
+94.95 222.932 L
+95.679 222.755 L
+96.407 222.578 L
+97.136 222.4 L
+97.865 222.222 L
+98.593 222.044 L
+99.322 221.865 L
+100.051 221.686 L
+100.779 221.507 L
+101.508 221.327 L
+102.237 221.147 L
+102.965 220.966 L
+103.694 220.785 L
+104.423 220.604 L
+105.152 220.422 L
+105.88 220.24 L
+106.609 220.057 L
+107.338 219.874 L
+108.066 219.691 L
+108.795 219.507 L
+109.524 219.323 L
+110.252 219.138 L
+110.981 218.953 L
+111.71 218.767 L
+112.438 218.581 L
+113.167 218.395 L
+113.896 218.208 L
+114.624 218.021 L
+115.353 217.833 L
+116.082 217.645 L
+116.81 217.457 L
+117.539 217.268 L
+118.268 217.078 L
+118.996 216.889 L
+119.725 216.698 L
+120.454 216.508 L
+121.182 216.316 L
+121.911 216.125 L
+122.64 215.933 L
+123.368 215.74 L
+124.097 215.547 L
+124.826 215.354 L
+125.554 215.16 L
+126.283 214.965 L
+127.012 214.77 L
+127.741 214.575 L
+128.469 214.379 L
+129.198 214.183 L
+129.927 213.986 L
+130.655 213.789 L
+131.384 213.591 L
+132.113 213.392 L
+132.841 213.194 L
+133.57 212.994 L
+134.299 212.794 L
+135.027 212.594 L
+135.756 212.393 L
+136.485 212.192 L
+137.213 211.99 L
+137.942 211.787 L
+138.671 211.584 L
+139.399 211.38 L
+140.128 211.176 L
+140.857 210.972 L
+141.585 210.766 L
+142.314 210.56 L
+143.043 210.354 L
+143.771 210.147 L
+144.5 209.939 L
+145.229 209.731 L
+145.957 209.522 L
+146.686 209.313 L
+147.415 209.103 L
+148.144 208.893 L
+148.872 208.681 L
+149.601 208.47 L
+150.33 208.257 L
+151.058 208.044 L
+151.787 207.83 L
+152.516 207.616 L
+153.244 207.401 L
+153.973 207.185 L
+154.702 206.969 L
+155.43 206.752 L
+156.159 206.534 L
+156.888 206.316 L
+157.616 206.097 L
+158.345 205.877 L
+159.074 205.656 L
+159.802 205.435 L
+160.531 205.213 L
+161.26 204.99 L
+161.988 204.767 L
+162.717 204.542 L
+163.446 204.317 L
+164.174 204.092 L
+164.903 203.865 L
+165.632 203.638 L
+166.36 203.409 L
+167.089 203.181 L
+167.818 202.951 L
+168.547 202.72 L
+169.275 202.489 L
+170.004 202.256 L
+170.733 202.023 L
+171.461 201.789 L
+172.19 201.554 L
+172.919 201.318 L
+173.647 201.081 L
+174.376 200.844 L
+175.105 200.605 L
+175.833 200.365 L
+176.562 200.125 L
+177.291 199.883 L
+178.019 199.641 L
+178.748 199.397 L
+179.477 199.153 L
+180.205 198.907 L
+180.934 198.661 L
+181.663 198.413 L
+182.391 198.164 L
+183.12 197.914 L
+183.849 197.664 L
+184.577 197.412 L
+185.306 197.158 L
+186.035 196.904 L
+186.763 196.649 L
+187.492 196.392 L
+188.221 196.134 L
+188.95 195.875 L
+189.678 195.615 L
+190.407 195.353 L
+191.136 195.091 L
+191.864 194.826 L
+192.593 194.561 L
+193.322 194.294 L
+194.05 194.026 L
+194.779 193.757 L
+195.508 193.486 L
+196.236 193.214 L
+196.965 192.94 L
+197.694 192.665 L
+198.422 192.388 L
+199.151 192.11 L
+199.88 191.83 L
+200.608 191.549 L
+201.337 191.266 L
+202.066 190.981 L
+202.794 190.695 L
+203.523 190.408 L
+204.252 190.118 L
+204.98 189.827 L
+205.709 189.534 L
+206.438 189.239 L
+207.166 188.942 L
+207.895 188.644 L
+208.624 188.343 L
+209.353 188.041 L
+210.081 187.737 L
+210.81 187.43 L
+211.539 187.122 L
+212.267 186.811 L
+212.996 186.499 L
+213.725 186.184 L
+214.453 185.867 L
+215.182 185.548 L
+215.911 185.226 L
+216.639 184.903 L
+217.368 184.576 L
+218.097 184.248 L
+218.825 183.917 L
+219.554 183.583 L
+220.283 183.247 L
+221.011 182.908 L
+221.74 182.566 L
+222.469 182.221 L
+223.197 181.874 L
+223.926 181.524 L
+224.655 181.171 L
+225.383 180.815 L
+226.112 180.455 L
+226.841 180.093 L
+227.569 179.727 L
+228.298 179.358 L
+229.027 178.986 L
+229.756 178.61 L
+230.484 178.23 L
+231.213 177.847 L
+231.942 177.46 L
+232.67 177.069 L
+233.399 176.674 L
+234.128 176.276 L
+234.856 175.872 L
+235.585 175.465 L
+236.314 175.053 L
+237.042 174.637 L
+237.771 174.216 L
+238.5 173.79 L
+239.228 173.359 L
+239.957 172.924 L
+240.686 172.482 L
+241.414 172.036 L
+242.143 171.584 L
+242.872 171.126 L
+243.6 170.662 L
+244.329 170.192 L
+245.058 169.716 L
+245.786 169.233 L
+246.515 168.744 L
+247.244 168.247 L
+247.972 167.743 L
+248.701 167.232 L
+249.43 166.713 L
+250.159 166.186 L
+250.887 165.65 L
+251.616 165.106 L
+252.345 164.553 L
+253.073 163.99 L
+253.802 163.417 L
+254.531 162.834 L
+255.259 162.241 L
+255.988 161.636 L
+256.717 161.02 L
+257.445 160.391 L
+258.174 159.75 L
+258.903 159.095 L
+259.631 158.426 L
+260.36 157.743 L
+261.089 157.044 L
+261.817 156.328 L
+262.546 155.595 L
+263.275 154.844 L
+264.003 154.074 L
+264.732 153.283 L
+265.461 152.471 L
+266.189 151.635 L
+266.918 150.774 L
+267.647 149.887 L
+268.375 148.972 L
+269.104 148.026 L
+269.833 147.047 L
+270.561 146.033 L
+271.29 144.98 L
+272.019 143.886 L
+272.748 142.746 L
+273.476 141.556 L
+274.205 140.31 L
+274.934 139.004 L
+275.662 137.63 L
+276.391 136.179 L
+277.12 134.642 L
+277.848 133.005 L
+278.577 131.258 L
+279.306 129.378 L
+280.034 127.344 L
+280.763 125.125 L
+281.492 122.683 L
+282.22 119.959 L
+282.949 116.88 L
+283.678 113.326 L
+284.406 109.112 L
+285.135 103.916 L
+285.864 97.083 L
+286.592 86.986 L
+287.321 66.511 L
+288.05 107.215 L
+288.778 120.201 L
+289.507 125.455 L
+290.236 128.764 L
+290.964 131.167 L
+291.693 133.043 L
+292.422 134.577 L
+293.151 135.87 L
+293.879 136.983 L
+294.608 137.958 L
+295.337 138.825 L
+296.065 139.602 L
+296.794 140.306 L
+297.523 140.948 L
+298.251 141.537 L
+298.98 142.081 L
+299.709 142.585 L
+300.437 143.054 L
+301.166 143.493 L
+301.895 143.904 L
+302.623 144.291 L
+303.352 144.655 L
+304.081 145 L
+304.809 145.327 L
+305.538 145.636 L
+306.267 145.931 L
+306.995 146.212 L
+307.724 146.479 L
+308.453 146.735 L
+309.181 146.98 L
+309.91 147.214 L
+310.639 147.439 L
+311.367 147.655 L
+312.096 147.863 L
+312.825 148.062 L
+313.554 148.255 L
+314.282 148.44 L
+315.011 148.619 L
+315.74 148.792 L
+316.468 148.959 L
+317.197 149.12 L
+317.926 149.276 L
+318.654 149.427 L
+319.383 149.574 L
+320.112 149.716 L
+320.84 149.853 L
+321.569 149.987 L
+322.298 150.117 L
+323.026 150.243 L
+323.755 150.366 L
+324.484 150.485 L
+325.212 150.601 L
+325.941 150.713 L
+326.67 150.823 L
+327.398 150.93 L
+328.127 151.035 L
+328.856 151.136 L
+329.584 151.235 L
+330.313 151.332 L
+331.042 151.426 L
+331.77 151.518 L
+332.499 151.608 L
+333.228 151.696 L
+333.957 151.782 L
+334.685 151.866 L
+335.414 151.948 L
+336.143 152.028 L
+336.871 152.106 L
+337.6 152.183 L
+338.329 152.258 L
+339.057 152.331 L
+339.786 152.403 L
+340.515 152.474 L
+341.243 152.542 L
+341.972 152.61 L
+342.701 152.676 L
+343.429 152.741 L
+344.158 152.805 L
+344.887 152.867 L
+345.615 152.928 L
+346.344 152.988 L
+347.073 153.047 L
+347.801 153.105 L
+348.53 153.161 L
+349.259 153.217 L
+349.987 153.271 L
+350.716 153.325 L
+351.445 153.377 L
+352.173 153.429 L
+352.902 153.48 L
+353.631 153.53 L
+354.36 153.579 L
+355.088 153.627 L
+355.817 153.674 L
+356.546 153.721 L
+357.274 153.766 L
+358.003 153.811 L
+358.732 153.855 L
+359.46 153.899 L
+360.189 153.942 L
+360.918 153.984 L
+361.646 154.025 L
+362.375 154.066 L
+363.104 154.106 L
+363.832 154.145 L
+364.561 154.184 L
+365.29 154.222 L
+366.018 154.26 L
+366.747 154.297 L
+367.476 154.333 L
+368.204 154.369 L
+368.933 154.405 L
+369.662 154.44 L
+370.39 154.474 L
+371.119 154.508 L
+371.848 154.541 L
+372.576 154.574 L
+373.305 154.606 L
+374.034 154.638 L
+374.763 154.669 L
+375.491 154.7 L
+376.22 154.731 L
+376.949 154.761 L
+377.677 154.791 L
+378.406 154.82 L
+379.135 154.849 L
+379.863 154.877 L
+380.592 154.905 L
+381.321 154.933 L
+382.049 154.96 L
+382.778 154.987 L
+383.507 155.014 L
+384.235 155.04 L
+384.964 155.066 L
+385.693 155.092 L
+386.421 155.117 L
+387.15 155.142 L
+387.879 155.166 L
+388.607 155.19 L
+389.336 155.214 L
+390.065 155.238 L
+390.793 155.261 L
+391.522 155.284 L
+392.251 155.307 L
+392.979 155.33 L
+393.708 155.352 L
+394.437 155.374 L
+395.166 155.395 L
+395.894 155.417 L
+396.623 155.438 L
+397.352 155.458 L
+398.08 155.479 L
+398.809 155.499 L
+399.538 155.519 L
+400.266 155.539 L
+400.995 155.559 L
+401.724 155.578 L
+402.452 155.597 L
+403.181 155.616 L
+403.91 155.635 L
+404.638 155.653 L
+405.367 155.672 L
+406.096 155.69 L
+406.824 155.708 L
+407.553 155.725 L
+408.282 155.743 L
+409.01 155.76 L
+409.739 155.777 L
+410.468 155.794 L
+411.196 155.811 L
+411.925 155.827 L
+412.654 155.843 L
+413.382 155.859 L
+414.111 155.875 L
+414.84 155.891 L
+415.568 155.907 L
+416.297 155.922 L
+417.026 155.937 L
+417.755 155.952 L
+418.483 155.967 L
+419.212 155.982 L
+419.941 155.997 L
+420.669 156.011 L
+421.398 156.025 L
+422.127 156.04 L
+422.855 156.054 L
+423.584 156.067 L
+424.313 156.081 L
+425.041 156.095 L
+425.77 156.108 L
+426.499 156.121 L
+427.227 156.135 L
+427.956 156.148 L
+428.685 156.16 L
+429.413 156.173 L
+430.142 156.186 L
+430.871 156.198 L
+431.599 156.211 L
+432.328 156.223 L
+433.057 156.235 L
+433.785 156.247 L
+434.514 156.259 L
+435.243 156.271 L
+435.971 156.282 L
+436.7 156.294 L
+437.429 156.305 L
+438.158 156.316 L
+438.886 156.328 L
+439.615 156.339 L
+440.344 156.35 L
+441.072 156.36 L
+441.801 156.371 L
+442.53 156.382 L
+s
+78.19 227.244 m
+78.919 227.062 L
+79.648 226.879 L
+80.376 226.696 L
+81.105 226.513 L
+81.834 226.329 L
+82.562 226.144 L
+83.291 225.96 L
+84.02 225.774 L
+84.749 225.589 L
+85.477 225.403 L
+86.206 225.216 L
+86.935 225.029 L
+87.663 224.842 L
+88.392 224.654 L
+89.121 224.465 L
+89.849 224.277 L
+90.578 224.088 L
+91.307 223.898 L
+92.035 223.708 L
+92.764 223.518 L
+93.493 223.327 L
+94.221 223.135 L
+94.95 222.944 L
+95.679 222.751 L
+96.407 222.559 L
+97.136 222.366 L
+97.865 222.172 L
+98.593 221.978 L
+99.322 221.783 L
+100.051 221.589 L
+100.779 221.393 L
+101.508 221.197 L
+102.237 221.001 L
+102.965 220.804 L
+103.694 220.607 L
+104.423 220.409 L
+105.152 220.211 L
+105.88 220.013 L
+106.609 219.813 L
+107.338 219.614 L
+108.066 219.414 L
+108.795 219.213 L
+109.524 219.012 L
+110.252 218.811 L
+110.981 218.609 L
+111.71 218.406 L
+112.438 218.203 L
+113.167 217.999 L
+113.896 217.795 L
+114.624 217.591 L
+115.353 217.386 L
+116.082 217.18 L
+116.81 216.974 L
+117.539 216.767 L
+118.268 216.56 L
+118.996 216.352 L
+119.725 216.144 L
+120.454 215.935 L
+121.182 215.726 L
+121.911 215.516 L
+122.64 215.305 L
+123.368 215.094 L
+124.097 214.883 L
+124.826 214.67 L
+125.554 214.458 L
+126.283 214.244 L
+127.012 214.03 L
+127.741 213.816 L
+128.469 213.6 L
+129.198 213.385 L
+129.927 213.168 L
+130.655 212.951 L
+131.384 212.733 L
+132.113 212.515 L
+132.841 212.296 L
+133.57 212.076 L
+134.299 211.856 L
+135.027 211.635 L
+135.756 211.413 L
+136.485 211.191 L
+137.213 210.968 L
+137.942 210.744 L
+138.671 210.52 L
+139.399 210.294 L
+140.128 210.068 L
+140.857 209.842 L
+141.585 209.614 L
+142.314 209.386 L
+143.043 209.157 L
+143.771 208.927 L
+144.5 208.697 L
+145.229 208.466 L
+145.957 208.234 L
+146.686 208.001 L
+147.415 207.767 L
+148.144 207.532 L
+148.872 207.297 L
+149.601 207.06 L
+150.33 206.823 L
+151.058 206.585 L
+151.787 206.346 L
+152.516 206.106 L
+153.244 205.865 L
+153.973 205.623 L
+154.702 205.381 L
+155.43 205.137 L
+156.159 204.892 L
+156.888 204.646 L
+157.616 204.4 L
+158.345 204.152 L
+159.074 203.903 L
+159.802 203.653 L
+160.531 203.402 L
+161.26 203.15 L
+161.988 202.897 L
+162.717 202.643 L
+163.446 202.387 L
+164.174 202.131 L
+164.903 201.873 L
+165.632 201.614 L
+166.36 201.354 L
+167.089 201.092 L
+167.818 200.83 L
+168.547 200.565 L
+169.275 200.3 L
+170.004 200.033 L
+170.733 199.765 L
+171.461 199.496 L
+172.19 199.225 L
+172.919 198.953 L
+173.647 198.68 L
+174.376 198.405 L
+175.105 198.128 L
+175.833 197.85 L
+176.562 197.57 L
+177.291 197.289 L
+178.019 197.006 L
+178.748 196.722 L
+179.477 196.435 L
+180.205 196.148 L
+180.934 195.858 L
+181.663 195.567 L
+182.391 195.274 L
+183.12 194.979 L
+183.849 194.682 L
+184.577 194.383 L
+185.306 194.083 L
+186.035 193.78 L
+186.763 193.475 L
+187.492 193.169 L
+188.221 192.86 L
+188.95 192.549 L
+189.678 192.236 L
+190.407 191.921 L
+191.136 191.603 L
+191.864 191.284 L
+192.593 190.961 L
+193.322 190.637 L
+194.05 190.31 L
+194.779 189.98 L
+195.508 189.648 L
+196.236 189.313 L
+196.965 188.976 L
+197.694 188.635 L
+198.422 188.292 L
+199.151 187.946 L
+199.88 187.598 L
+200.608 187.246 L
+201.337 186.891 L
+202.066 186.533 L
+202.794 186.171 L
+203.523 185.807 L
+204.252 185.438 L
+204.98 185.067 L
+205.709 184.692 L
+206.438 184.313 L
+207.166 183.93 L
+207.895 183.544 L
+208.624 183.153 L
+209.353 182.759 L
+210.081 182.36 L
+210.81 181.957 L
+211.539 181.549 L
+212.267 181.137 L
+212.996 180.72 L
+213.725 180.299 L
+214.453 179.872 L
+215.182 179.44 L
+215.911 179.003 L
+216.639 178.56 L
+217.368 178.112 L
+218.097 177.657 L
+218.825 177.197 L
+219.554 176.73 L
+220.283 176.257 L
+221.011 175.777 L
+221.74 175.29 L
+222.469 174.796 L
+223.197 174.295 L
+223.926 173.785 L
+224.655 173.268 L
+225.383 172.742 L
+226.112 172.207 L
+226.841 171.662 L
+227.569 171.109 L
+228.298 170.546 L
+229.027 169.971 L
+229.756 169.387 L
+230.484 168.79 L
+231.213 168.182 L
+231.942 167.561 L
+232.67 166.927 L
+233.399 166.28 L
+234.128 165.617 L
+234.856 164.94 L
+235.585 164.246 L
+236.314 163.535 L
+237.042 162.806 L
+237.771 162.058 L
+238.5 161.289 L
+239.228 160.499 L
+239.957 159.686 L
+240.686 158.848 L
+241.414 157.985 L
+242.143 157.093 L
+242.872 156.17 L
+243.6 155.216 L
+244.329 154.226 L
+245.058 153.198 L
+245.786 152.13 L
+246.515 151.016 L
+247.244 149.854 L
+247.972 148.638 L
+248.701 95.265 L
+249.43 93.925 L
+250.159 92.513 L
+250.887 91.021 L
+251.616 89.437 L
+252.345 87.751 L
+253.073 85.948 L
+253.802 84.008 L
+254.531 81.914 L
+255.259 79.635 L
+255.988 77.137 L
+256.717 74.37 L
+257.445 71.271 L
+258.174 67.749 L
+258.903 63.665 L
+259.631 58.799 L
+260.36 52.774 L
+261.089 44.836 L
+261.817 33.16 L
+262.546 10.25 L
+263.275 102.86 L
+264.003 118.171 L
+264.732 124.159 L
+265.461 127.877 L
+266.189 130.553 L
+266.918 132.626 L
+267.647 134.311 L
+268.375 135.722 L
+269.104 136.932 L
+269.833 137.986 L
+270.561 138.919 L
+271.29 139.753 L
+272.019 140.505 L
+272.748 141.189 L
+273.476 141.814 L
+274.205 142.389 L
+274.934 142.921 L
+275.662 143.415 L
+276.391 143.875 L
+277.12 144.305 L
+277.848 144.709 L
+278.577 145.088 L
+279.306 145.446 L
+280.034 145.784 L
+280.763 146.105 L
+281.492 146.409 L
+282.22 146.698 L
+282.949 146.973 L
+283.678 147.236 L
+284.406 147.486 L
+285.135 147.726 L
+285.864 147.955 L
+286.592 148.175 L
+287.321 148.386 L
+288.05 148.589 L
+288.778 148.784 L
+289.507 148.972 L
+290.236 149.152 L
+290.964 149.327 L
+291.693 149.495 L
+292.422 149.657 L
+293.151 149.814 L
+293.879 149.965 L
+294.608 150.112 L
+295.337 150.254 L
+296.065 150.392 L
+296.794 150.525 L
+297.523 150.654 L
+298.251 150.78 L
+298.98 150.901 L
+299.709 151.02 L
+300.437 151.135 L
+301.166 151.246 L
+301.895 151.355 L
+302.623 151.46 L
+303.352 151.563 L
+304.081 151.663 L
+304.809 151.761 L
+305.538 151.856 L
+306.267 151.949 L
+306.995 152.039 L
+307.724 152.127 L
+308.453 152.213 L
+309.181 152.297 L
+309.91 152.379 L
+310.639 152.459 L
+311.367 152.537 L
+312.096 152.613 L
+312.825 152.688 L
+313.554 152.761 L
+314.282 152.832 L
+315.011 152.902 L
+315.74 152.97 L
+316.468 153.037 L
+317.197 153.102 L
+317.926 153.166 L
+318.654 153.228 L
+319.383 153.29 L
+320.112 153.35 L
+320.84 153.409 L
+321.569 153.466 L
+322.298 153.523 L
+323.026 153.578 L
+323.755 153.633 L
+324.484 153.686 L
+325.212 153.738 L
+325.941 153.789 L
+326.67 153.84 L
+327.398 153.889 L
+328.127 153.937 L
+328.856 153.985 L
+329.584 154.032 L
+330.313 154.078 L
+331.042 154.123 L
+331.77 154.167 L
+332.499 154.21 L
+333.228 154.253 L
+333.957 154.295 L
+334.685 154.336 L
+335.414 154.376 L
+336.143 154.416 L
+336.871 154.455 L
+337.6 154.494 L
+338.329 154.532 L
+339.057 154.569 L
+339.786 154.606 L
+340.515 154.642 L
+341.243 154.677 L
+341.972 154.712 L
+342.701 154.746 L
+343.429 154.78 L
+344.158 154.813 L
+344.887 154.846 L
+345.615 154.878 L
+346.344 154.91 L
+347.073 154.941 L
+347.801 154.972 L
+348.53 155.002 L
+349.259 155.032 L
+349.987 155.061 L
+350.716 155.09 L
+351.445 155.118 L
+352.173 155.146 L
+352.902 155.174 L
+353.631 155.201 L
+354.36 155.228 L
+355.088 155.255 L
+355.817 155.281 L
+356.546 155.306 L
+357.274 155.332 L
+358.003 155.357 L
+358.732 155.381 L
+359.46 155.406 L
+360.189 155.43 L
+360.918 155.453 L
+361.646 155.476 L
+362.375 155.499 L
+363.104 155.522 L
+363.832 155.544 L
+364.561 155.567 L
+365.29 155.588 L
+366.018 155.61 L
+366.747 155.631 L
+367.476 155.652 L
+368.204 155.672 L
+368.933 155.693 L
+369.662 155.713 L
+370.39 155.733 L
+371.119 155.752 L
+371.848 155.772 L
+372.576 155.791 L
+373.305 155.81 L
+374.034 155.828 L
+374.763 155.847 L
+375.491 155.865 L
+376.22 155.883 L
+376.949 155.9 L
+377.677 155.918 L
+378.406 155.935 L
+379.135 155.952 L
+379.863 155.969 L
+380.592 155.986 L
+381.321 156.002 L
+382.049 156.018 L
+382.778 156.034 L
+383.507 156.05 L
+384.235 156.066 L
+384.964 156.081 L
+385.693 156.097 L
+386.421 156.112 L
+387.15 156.127 L
+387.879 156.141 L
+388.607 156.156 L
+389.336 156.171 L
+390.065 156.185 L
+390.793 156.199 L
+391.522 156.213 L
+392.251 156.227 L
+392.979 156.24 L
+393.708 156.254 L
+394.437 156.267 L
+395.166 156.28 L
+395.894 156.293 L
+396.623 156.306 L
+397.352 156.319 L
+398.08 156.331 L
+398.809 156.344 L
+399.538 156.356 L
+400.266 156.368 L
+400.995 156.38 L
+401.724 156.392 L
+402.452 156.404 L
+403.181 156.416 L
+403.91 156.427 L
+404.638 156.439 L
+405.367 156.45 L
+406.096 156.461 L
+406.824 156.472 L
+407.553 156.483 L
+408.282 156.494 L
+409.01 156.505 L
+409.739 156.515 L
+410.468 156.526 L
+411.196 156.536 L
+411.925 156.546 L
+412.654 156.557 L
+413.382 156.567 L
+414.111 156.577 L
+414.84 156.587 L
+415.568 156.596 L
+416.297 156.606 L
+417.026 156.616 L
+417.755 156.625 L
+418.483 156.634 L
+419.212 156.644 L
+419.941 156.653 L
+420.669 156.662 L
+421.398 156.671 L
+422.127 156.68 L
+422.855 156.689 L
+423.584 156.697 L
+424.313 156.706 L
+425.041 156.715 L
+425.77 156.723 L
+426.499 156.732 L
+427.227 156.74 L
+427.956 156.748 L
+428.685 156.756 L
+429.413 156.764 L
+430.142 156.772 L
+430.871 156.78 L
+431.599 156.788 L
+432.328 156.796 L
+433.057 156.804 L
+433.785 156.811 L
+434.514 156.819 L
+435.243 156.826 L
+435.971 156.834 L
+436.7 156.841 L
+437.429 156.849 L
+438.158 156.856 L
+438.886 156.863 L
+439.615 156.87 L
+440.344 156.877 L
+441.072 156.884 L
+441.801 156.891 L
+442.53 156.898 L
+s
+78.19 227.796 m
+78.555 227.699 L
+78.919 227.602 L
+79.283 227.504 L
+79.648 227.407 L
+80.012 227.309 L
+80.376 227.211 L
+80.741 227.113 L
+81.105 227.016 L
+81.469 226.917 L
+81.834 226.819 L
+82.198 226.721 L
+82.562 226.622 L
+82.927 226.524 L
+83.291 226.425 L
+83.655 226.327 L
+84.02 226.228 L
+84.384 226.129 L
+84.749 226.03 L
+85.113 225.93 L
+85.477 225.831 L
+85.842 225.732 L
+86.206 225.632 L
+86.57 225.532 L
+86.935 225.433 L
+87.299 225.333 L
+87.663 225.233 L
+88.028 225.133 L
+88.392 225.032 L
+88.756 224.932 L
+89.121 224.832 L
+89.485 224.731 L
+89.849 224.63 L
+90.214 224.53 L
+90.578 224.429 L
+90.942 224.328 L
+91.307 224.227 L
+91.671 224.125 L
+92.035 224.024 L
+92.4 223.923 L
+92.764 223.821 L
+93.128 223.719 L
+93.493 223.618 L
+93.857 223.516 L
+94.221 223.414 L
+94.586 223.311 L
+94.95 223.209 L
+95.314 223.107 L
+95.679 223.004 L
+96.043 222.902 L
+96.407 222.799 L
+96.772 222.696 L
+97.136 222.593 L
+97.5 222.49 L
+97.865 222.387 L
+98.229 222.284 L
+98.593 222.18 L
+98.958 222.077 L
+99.322 221.973 L
+99.686 221.869 L
+100.051 221.765 L
+100.415 221.661 L
+100.779 221.557 L
+101.144 221.453 L
+101.508 221.349 L
+101.872 221.244 L
+102.237 221.14 L
+102.601 221.035 L
+102.965 220.93 L
+103.33 220.825 L
+103.694 220.72 L
+104.058 220.615 L
+104.423 220.51 L
+104.787 220.404 L
+105.152 220.299 L
+105.516 220.193 L
+105.88 220.087 L
+106.245 219.981 L
+106.609 219.875 L
+106.973 219.769 L
+107.338 219.663 L
+107.702 219.556 L
+108.066 219.45 L
+108.431 219.343 L
+108.795 219.236 L
+109.159 219.129 L
+109.524 219.022 L
+109.888 218.915 L
+110.252 218.808 L
+110.617 218.7 L
+110.981 218.593 L
+111.345 218.485 L
+111.71 218.377 L
+112.074 218.269 L
+112.438 218.161 L
+112.803 218.053 L
+113.167 217.945 L
+113.531 217.836 L
+113.896 217.727 L
+114.26 217.619 L
+114.624 217.51 L
+114.989 217.401 L
+115.353 217.291 L
+115.717 217.182 L
+116.082 217.073 L
+116.446 216.963 L
+116.81 216.853 L
+117.175 216.743 L
+117.539 216.633 L
+117.903 216.523 L
+118.268 216.413 L
+118.632 216.303 L
+118.996 216.192 L
+119.361 216.081 L
+119.725 215.97 L
+120.089 215.859 L
+120.454 215.748 L
+120.818 215.637 L
+121.182 215.525 L
+121.547 215.414 L
+121.911 215.302 L
+122.275 215.19 L
+122.64 215.078 L
+123.004 214.965 L
+123.368 214.853 L
+123.733 214.74 L
+124.097 214.628 L
+124.461 214.515 L
+124.826 214.402 L
+125.19 214.288 L
+125.554 214.175 L
+125.919 214.061 L
+126.283 213.948 L
+126.648 213.834 L
+127.012 213.72 L
+127.376 213.605 L
+127.741 213.491 L
+128.105 213.376 L
+128.469 213.262 L
+128.834 213.147 L
+129.198 213.032 L
+129.562 212.916 L
+129.927 212.801 L
+130.291 212.685 L
+130.655 212.57 L
+131.02 212.454 L
+131.384 212.337 L
+131.748 212.221 L
+132.113 212.105 L
+132.477 211.988 L
+132.841 211.871 L
+133.206 211.754 L
+133.57 211.636 L
+133.934 211.519 L
+134.299 211.401 L
+134.663 211.283 L
+135.027 211.165 L
+135.392 211.047 L
+135.756 210.928 L
+136.12 210.81 L
+136.485 210.691 L
+136.849 210.572 L
+137.213 210.452 L
+137.578 210.332 L
+137.942 210.213 L
+138.306 210.093 L
+138.671 209.973 L
+139.035 209.852 L
+139.399 209.732 L
+139.764 209.611 L
+140.128 209.49 L
+140.492 209.368 L
+140.857 209.247 L
+141.221 209.125 L
+141.585 209.003 L
+141.95 208.881 L
+142.314 208.758 L
+142.678 208.635 L
+143.043 208.513 L
+143.407 208.389 L
+143.771 208.266 L
+144.136 208.142 L
+144.5 208.018 L
+144.864 207.894 L
+145.229 207.77 L
+145.593 207.645 L
+145.957 207.52 L
+146.322 207.395 L
+146.686 207.269 L
+147.051 207.143 L
+147.415 207.017 L
+147.779 206.891 L
+148.144 206.764 L
+148.508 206.637 L
+148.872 206.51 L
+149.237 206.383 L
+149.601 206.255 L
+149.965 206.127 L
+150.33 205.999 L
+150.694 205.87 L
+151.058 205.741 L
+151.423 205.612 L
+151.787 205.483 L
+152.151 205.353 L
+152.516 205.223 L
+152.88 205.092 L
+153.244 204.962 L
+153.609 204.831 L
+153.973 204.699 L
+154.337 204.567 L
+154.702 204.435 L
+155.066 204.303 L
+155.43 204.17 L
+155.795 204.037 L
+156.159 203.904 L
+156.523 203.77 L
+156.888 203.636 L
+157.252 203.502 L
+157.616 203.367 L
+157.981 203.232 L
+158.345 203.096 L
+158.709 202.961 L
+159.074 202.824 L
+159.438 202.688 L
+159.802 202.551 L
+160.167 202.414 L
+160.531 202.276 L
+160.895 202.138 L
+161.26 201.999 L
+161.624 201.86 L
+161.988 201.721 L
+162.353 201.581 L
+162.717 201.441 L
+163.081 201.301 L
+163.446 201.16 L
+163.81 201.018 L
+164.174 200.877 L
+164.539 200.735 L
+164.903 200.592 L
+165.267 200.449 L
+165.632 200.305 L
+165.996 200.161 L
+166.36 200.017 L
+166.725 199.872 L
+167.089 199.727 L
+167.454 199.581 L
+167.818 199.435 L
+168.182 199.288 L
+168.547 199.141 L
+168.911 198.993 L
+169.275 198.845 L
+169.64 198.696 L
+170.004 198.547 L
+170.368 198.397 L
+170.733 198.247 L
+171.097 198.097 L
+171.461 197.945 L
+171.826 197.793 L
+172.19 197.641 L
+172.554 197.488 L
+172.919 197.335 L
+173.283 197.181 L
+173.647 197.026 L
+174.012 196.871 L
+174.376 196.716 L
+174.74 196.559 L
+175.105 196.403 L
+175.469 196.245 L
+175.833 196.087 L
+176.198 195.929 L
+176.562 195.77 L
+176.926 195.61 L
+177.291 195.449 L
+177.655 195.288 L
+178.019 195.126 L
+178.384 194.964 L
+178.748 194.801 L
+179.112 194.637 L
+179.477 194.473 L
+179.841 194.308 L
+180.205 194.142 L
+180.57 193.976 L
+180.934 193.809 L
+181.298 193.641 L
+181.663 193.472 L
+182.027 193.304 L
+182.391 193.134 L
+182.756 192.963 L
+183.12 192.791 L
+183.484 192.62 L
+183.849 192.447 L
+184.213 192.273 L
+184.577 192.098 L
+184.942 191.923 L
+185.306 191.747 L
+185.67 191.57 L
+186.035 191.392 L
+186.399 191.214 L
+186.763 191.035 L
+187.128 190.854 L
+187.492 190.673 L
+187.856 190.491 L
+188.221 190.309 L
+188.585 190.125 L
+188.95 189.94 L
+189.314 189.755 L
+189.678 189.568 L
+190.043 189.381 L
+190.407 189.192 L
+190.771 189.003 L
+191.136 188.813 L
+191.5 188.622 L
+191.864 188.429 L
+192.229 188.236 L
+192.593 188.042 L
+192.957 187.847 L
+193.322 187.65 L
+193.686 187.453 L
+194.05 187.254 L
+194.415 187.054 L
+194.779 186.854 L
+195.143 186.652 L
+195.508 186.449 L
+195.872 186.244 L
+196.236 186.039 L
+196.601 185.832 L
+196.965 185.625 L
+197.329 185.415 L
+197.694 185.205 L
+198.058 184.994 L
+198.422 184.781 L
+198.787 184.567 L
+199.151 184.351 L
+199.515 184.134 L
+199.88 131.817 L
+200.244 131.597 L
+200.608 131.376 L
+200.973 131.153 L
+201.337 130.93 L
+201.701 130.704 L
+202.066 130.477 L
+202.43 130.249 L
+202.794 130.019 L
+203.159 129.787 L
+203.523 129.554 L
+203.887 129.318 L
+204.252 129.082 L
+204.616 128.844 L
+204.98 128.604 L
+205.345 128.362 L
+205.709 128.118 L
+206.073 127.873 L
+206.438 127.625 L
+206.802 127.376 L
+207.166 127.125 L
+207.531 126.872 L
+207.895 126.616 L
+208.259 126.358 L
+208.624 126.099 L
+208.988 125.837 L
+209.353 125.573 L
+209.717 125.307 L
+210.081 125.038 L
+210.446 124.767 L
+210.81 124.494 L
+211.174 124.217 L
+211.539 123.939 L
+211.903 123.657 L
+212.267 123.373 L
+212.632 123.086 L
+212.996 122.797 L
+213.36 122.504 L
+213.725 122.208 L
+214.089 121.91 L
+214.453 121.608 L
+214.818 121.302 L
+215.182 120.994 L
+215.546 120.681 L
+215.911 120.366 L
+216.275 120.046 L
+216.639 119.723 L
+217.004 119.395 L
+217.368 119.064 L
+217.732 118.728 L
+218.097 118.389 L
+218.461 118.044 L
+218.825 117.695 L
+219.19 117.34 L
+219.554 116.982 L
+219.918 116.617 L
+220.283 116.248 L
+220.647 115.872 L
+221.011 115.491 L
+221.376 115.103 L
+221.74 114.71 L
+222.104 114.31 L
+222.469 113.903 L
+222.833 113.488 L
+223.197 113.067 L
+223.562 112.637 L
+223.926 112.199 L
+224.29 111.753 L
+224.655 111.297 L
+225.019 110.833 L
+225.383 110.358 L
+225.748 109.874 L
+226.112 109.378 L
+226.476 108.871 L
+226.841 108.353 L
+227.205 107.821 L
+227.569 107.276 L
+227.934 106.717 L
+228.298 106.144 L
+228.662 105.556 L
+229.027 104.951 L
+229.391 104.329 L
+229.756 103.689 L
+230.12 103.031 L
+230.484 102.352 L
+230.849 101.653 L
+231.213 100.932 L
+231.577 100.19 L
+231.942 99.426 L
+232.306 98.638 L
+232.67 97.827 L
+233.035 96.995 L
+233.399 96.143 L
+233.763 95.275 L
+234.128 94.395 L
+234.492 93.515 L
+234.856 92.647 L
+235.221 91.816 L
+235.585 91.055 L
+235.949 90.419 L
+236.314 89.987 L
+236.678 89.872 L
+237.042 90.228 L
+237.407 91.227 L
+237.771 93.008 L
+238.135 95.595 L
+238.5 98.813 L
+238.864 102.357 L
+239.228 105.928 L
+239.593 109.305 L
+239.957 112.388 L
+240.321 115.15 L
+240.686 117.611 L
+241.05 119.793 L
+241.414 121.734 L
+241.779 123.473 L
+242.143 125.035 L
+242.507 126.442 L
+242.872 127.722 L
+243.236 128.889 L
+243.6 129.959 L
+243.965 130.941 L
+244.329 131.852 L
+244.693 132.693 L
+245.058 133.477 L
+245.422 134.21 L
+245.786 134.895 L
+246.151 135.537 L
+246.515 136.14 L
+246.879 136.711 L
+247.244 137.249 L
+247.608 137.759 L
+247.972 138.243 L
+248.337 138.702 L
+248.701 139.14 L
+249.065 139.557 L
+249.43 139.955 L
+249.794 140.335 L
+250.159 140.699 L
+250.523 141.048 L
+250.887 141.382 L
+251.252 141.704 L
+251.616 142.013 L
+251.98 142.31 L
+252.345 142.596 L
+252.709 142.871 L
+253.073 143.138 L
+253.438 143.394 L
+253.802 143.643 L
+254.166 143.882 L
+254.531 144.115 L
+254.895 144.339 L
+255.259 144.557 L
+255.624 144.768 L
+255.988 144.972 L
+256.352 145.171 L
+256.717 145.364 L
+257.081 145.551 L
+257.445 145.733 L
+257.81 145.91 L
+258.174 146.082 L
+258.538 146.25 L
+258.903 146.413 L
+259.267 146.572 L
+259.631 146.727 L
+259.996 146.878 L
+260.36 147.025 L
+260.724 147.169 L
+261.089 147.309 L
+261.453 147.446 L
+261.817 147.579 L
+262.182 147.71 L
+262.546 147.837 L
+262.91 147.962 L
+263.275 148.084 L
+263.639 148.203 L
+264.003 148.32 L
+264.368 148.434 L
+264.732 148.546 L
+265.096 148.655 L
+265.461 148.763 L
+265.825 148.868 L
+266.189 148.97 L
+266.554 149.071 L
+266.918 149.17 L
+267.282 149.267 L
+267.647 149.362 L
+268.011 149.455 L
+268.375 149.547 L
+268.74 149.636 L
+269.104 149.724 L
+269.468 149.811 L
+269.833 149.896 L
+270.197 149.979 L
+270.561 150.061 L
+270.926 150.141 L
+271.29 150.22 L
+271.655 150.298 L
+272.019 150.374 L
+272.383 150.449 L
+272.748 150.523 L
+273.112 150.595 L
+273.476 150.667 L
+273.841 150.737 L
+274.205 150.806 L
+274.569 150.874 L
+274.934 150.941 L
+275.298 151.006 L
+275.662 151.071 L
+276.027 151.135 L
+276.391 151.198 L
+276.755 151.259 L
+277.12 151.32 L
+277.484 151.38 L
+277.848 151.439 L
+278.213 151.497 L
+278.577 151.554 L
+278.941 151.611 L
+279.306 151.666 L
+279.67 151.721 L
+280.034 151.775 L
+280.399 151.828 L
+280.763 151.881 L
+281.127 151.932 L
+281.492 151.983 L
+281.856 152.034 L
+282.22 152.083 L
+282.585 152.132 L
+282.949 152.181 L
+283.313 152.228 L
+283.678 152.275 L
+284.042 152.321 L
+284.406 152.367 L
+284.771 152.412 L
+285.135 152.457 L
+285.499 152.501 L
+285.864 152.544 L
+286.228 152.587 L
+286.592 152.63 L
+286.957 152.671 L
+287.321 152.713 L
+287.685 152.753 L
+288.05 152.794 L
+288.414 152.833 L
+288.778 152.872 L
+289.143 152.911 L
+289.507 152.949 L
+289.871 152.987 L
+290.236 153.025 L
+290.6 153.062 L
+290.964 153.098 L
+291.329 153.134 L
+291.693 153.17 L
+292.058 153.205 L
+292.422 153.24 L
+292.786 153.274 L
+293.151 153.308 L
+293.515 153.342 L
+293.879 153.375 L
+294.244 153.408 L
+294.608 153.44 L
+294.972 153.473 L
+295.337 153.504 L
+295.701 153.536 L
+296.065 153.567 L
+296.43 153.597 L
+296.794 153.628 L
+297.158 153.658 L
+297.523 153.688 L
+297.887 153.717 L
+298.251 153.746 L
+298.616 153.775 L
+298.98 153.803 L
+299.344 153.832 L
+299.709 153.86 L
+300.073 153.887 L
+300.437 153.914 L
+300.802 153.941 L
+301.166 153.968 L
+301.53 153.995 L
+301.895 154.021 L
+302.259 154.047 L
+302.623 154.072 L
+302.988 154.098 L
+303.352 154.123 L
+303.716 154.148 L
+304.081 154.173 L
+304.445 154.197 L
+304.809 154.221 L
+305.174 154.245 L
+305.538 154.269 L
+305.902 154.292 L
+306.267 154.315 L
+306.631 154.338 L
+306.995 154.361 L
+307.36 154.384 L
+307.724 154.406 L
+308.088 154.428 L
+308.453 154.45 L
+308.817 154.472 L
+309.181 154.493 L
+309.546 154.515 L
+309.91 154.536 L
+310.274 154.557 L
+310.639 154.578 L
+311.003 154.598 L
+311.367 154.618 L
+311.732 154.639 L
+312.096 154.659 L
+312.461 154.678 L
+312.825 154.698 L
+313.189 154.717 L
+313.554 154.737 L
+313.918 154.756 L
+314.282 154.775 L
+314.647 154.793 L
+315.011 154.812 L
+315.375 154.83 L
+315.74 154.849 L
+316.104 154.867 L
+316.468 154.885 L
+316.833 154.902 L
+317.197 154.92 L
+317.561 154.938 L
+317.926 154.955 L
+318.29 154.972 L
+318.654 154.989 L
+319.019 155.006 L
+319.383 155.023 L
+319.747 155.039 L
+320.112 155.056 L
+320.476 155.072 L
+320.84 155.088 L
+321.205 155.104 L
+321.569 155.12 L
+321.933 155.136 L
+322.298 155.152 L
+322.662 155.167 L
+323.026 155.183 L
+323.391 155.198 L
+323.755 155.213 L
+324.119 155.228 L
+324.484 155.243 L
+324.848 155.258 L
+325.212 155.272 L
+325.577 155.287 L
+325.941 155.301 L
+326.305 155.316 L
+326.67 155.33 L
+327.034 155.344 L
+327.398 155.358 L
+327.763 155.372 L
+328.127 155.386 L
+328.491 155.399 L
+328.856 155.413 L
+329.22 155.426 L
+329.584 155.439 L
+329.949 155.453 L
+330.313 155.466 L
+330.677 155.479 L
+331.042 155.492 L
+331.406 155.505 L
+331.77 155.517 L
+332.135 155.53 L
+332.499 155.542 L
+332.864 155.555 L
+333.228 155.567 L
+333.592 155.579 L
+333.957 155.592 L
+334.321 155.604 L
+334.685 155.616 L
+335.05 155.628 L
+335.414 155.639 L
+335.778 155.651 L
+336.143 155.663 L
+336.507 155.674 L
+336.871 155.686 L
+337.236 155.697 L
+337.6 155.708 L
+337.964 155.719 L
+338.329 155.731 L
+338.693 155.742 L
+339.057 155.753 L
+339.422 155.763 L
+339.786 155.774 L
+340.15 155.785 L
+340.515 155.796 L
+340.879 155.806 L
+341.243 155.817 L
+341.608 155.827 L
+341.972 155.838 L
+342.336 155.848 L
+342.701 155.858 L
+343.065 155.868 L
+343.429 155.878 L
+343.794 155.888 L
+344.158 155.898 L
+344.522 155.908 L
+344.887 155.918 L
+345.251 155.927 L
+345.615 155.937 L
+345.98 155.947 L
+346.344 155.956 L
+346.708 155.966 L
+347.073 155.975 L
+347.437 155.984 L
+347.801 155.994 L
+348.166 156.003 L
+348.53 156.012 L
+348.894 156.021 L
+349.259 156.03 L
+349.623 156.039 L
+349.987 156.048 L
+350.352 156.057 L
+350.716 156.065 L
+351.08 156.074 L
+351.445 156.083 L
+351.809 156.091 L
+352.173 156.1 L
+352.538 156.108 L
+352.902 156.117 L
+353.266 156.125 L
+353.631 156.133 L
+353.995 156.142 L
+354.36 156.15 L
+354.724 156.158 L
+355.088 156.166 L
+355.453 156.174 L
+355.817 156.182 L
+356.181 156.19 L
+356.546 156.198 L
+356.91 156.206 L
+357.274 156.214 L
+357.639 156.221 L
+358.003 156.229 L
+358.367 156.237 L
+358.732 156.244 L
+359.096 156.252 L
+359.46 156.259 L
+359.825 156.267 L
+360.189 156.274 L
+360.553 156.282 L
+360.918 156.289 L
+361.282 156.296 L
+361.646 156.303 L
+362.011 156.311 L
+362.375 156.318 L
+362.739 156.325 L
+363.104 156.332 L
+363.468 156.339 L
+363.832 156.346 L
+364.197 156.353 L
+364.561 156.36 L
+364.925 156.366 L
+365.29 156.373 L
+365.654 156.38 L
+366.018 156.387 L
+366.383 156.393 L
+366.747 156.4 L
+367.111 156.407 L
+367.476 156.413 L
+367.84 156.42 L
+368.204 156.426 L
+368.569 156.433 L
+368.933 156.439 L
+369.297 156.445 L
+369.662 156.452 L
+370.026 156.458 L
+370.39 156.464 L
+370.755 156.47 L
+371.119 156.476 L
+371.483 156.483 L
+371.848 156.489 L
+372.212 156.495 L
+372.576 156.501 L
+372.941 156.507 L
+373.305 156.513 L
+373.669 156.519 L
+374.034 156.524 L
+374.398 156.53 L
+374.763 156.536 L
+375.127 156.542 L
+375.491 156.548 L
+375.856 156.553 L
+376.22 156.559 L
+376.584 156.565 L
+376.949 156.57 L
+377.313 156.576 L
+377.677 156.581 L
+378.042 156.587 L
+378.406 156.592 L
+378.77 156.598 L
+379.135 156.603 L
+379.499 156.609 L
+379.863 156.614 L
+380.228 156.619 L
+380.592 156.625 L
+380.956 156.63 L
+381.321 156.635 L
+381.685 156.64 L
+382.049 156.646 L
+382.414 156.651 L
+382.778 156.656 L
+383.142 156.661 L
+383.507 156.666 L
+383.871 156.671 L
+384.235 156.676 L
+384.6 156.681 L
+384.964 156.686 L
+385.328 156.691 L
+385.693 156.696 L
+386.057 156.701 L
+386.421 156.706 L
+386.786 156.71 L
+387.15 156.715 L
+387.514 156.72 L
+387.879 156.725 L
+388.243 156.729 L
+388.607 156.734 L
+388.972 156.739 L
+389.336 156.743 L
+389.7 156.748 L
+390.065 156.753 L
+390.429 156.757 L
+390.793 156.762 L
+391.158 156.766 L
+391.522 156.771 L
+391.886 156.775 L
+392.251 156.78 L
+392.615 156.784 L
+392.979 156.788 L
+393.344 156.793 L
+393.708 156.797 L
+394.072 156.802 L
+394.437 156.806 L
+394.801 156.81 L
+395.166 156.814 L
+395.53 156.819 L
+395.894 156.823 L
+396.259 156.827 L
+396.623 156.831 L
+396.987 156.835 L
+397.352 156.84 L
+397.716 156.844 L
+398.08 156.848 L
+398.445 156.852 L
+398.809 156.856 L
+399.173 156.86 L
+399.538 156.864 L
+399.902 156.868 L
+400.266 156.872 L
+400.631 156.876 L
+400.995 156.88 L
+401.359 156.884 L
+401.724 156.887 L
+402.088 156.891 L
+402.452 156.895 L
+402.817 156.899 L
+403.181 156.903 L
+403.545 156.907 L
+403.91 156.91 L
+404.274 156.914 L
+404.638 156.918 L
+405.003 156.921 L
+405.367 156.925 L
+405.731 156.929 L
+406.096 156.933 L
+406.46 156.936 L
+406.824 156.94 L
+407.189 156.943 L
+407.553 156.947 L
+407.917 156.95 L
+408.282 156.954 L
+408.646 156.958 L
+409.01 156.961 L
+409.375 156.965 L
+409.739 156.968 L
+410.103 156.972 L
+410.468 156.975 L
+410.832 156.978 L
+411.196 156.982 L
+411.561 156.985 L
+411.925 156.989 L
+412.289 156.992 L
+412.654 156.995 L
+413.018 156.999 L
+413.382 157.002 L
+413.747 157.005 L
+414.111 157.008 L
+414.475 157.012 L
+414.84 157.015 L
+415.204 157.018 L
+415.568 157.021 L
+415.933 157.025 L
+416.297 157.028 L
+416.662 157.031 L
+417.026 157.034 L
+417.39 157.037 L
+417.755 157.04 L
+418.119 157.043 L
+418.483 157.047 L
+418.848 157.05 L
+419.212 157.053 L
+419.576 157.056 L
+419.941 157.059 L
+420.305 157.062 L
+420.669 157.065 L
+421.034 157.068 L
+421.398 157.071 L
+421.762 157.074 L
+422.127 157.077 L
+422.491 157.08 L
+422.855 157.083 L
+423.22 157.085 L
+423.584 157.088 L
+423.948 157.091 L
+424.313 157.094 L
+424.677 157.097 L
+425.041 157.1 L
+425.406 157.103 L
+425.77 157.105 L
+426.134 157.108 L
+426.499 157.111 L
+426.863 157.114 L
+427.227 157.117 L
+427.592 157.119 L
+427.956 157.122 L
+428.32 157.125 L
+428.685 157.127 L
+429.049 157.13 L
+429.413 157.133 L
+429.778 157.136 L
+430.142 157.138 L
+430.506 157.141 L
+430.871 157.143 L
+431.235 157.146 L
+431.599 157.149 L
+431.964 157.151 L
+432.328 157.154 L
+432.692 157.156 L
+433.057 157.159 L
+433.421 157.162 L
+433.785 157.164 L
+434.15 157.167 L
+434.514 157.169 L
+434.878 157.172 L
+435.243 157.174 L
+435.607 157.177 L
+435.971 157.179 L
+436.336 157.182 L
+436.7 157.184 L
+437.065 157.187 L
+437.429 157.189 L
+437.793 157.191 L
+438.158 157.194 L
+438.522 157.196 L
+438.886 157.199 L
+439.251 157.201 L
+439.615 157.203 L
+439.979 157.206 L
+440.344 157.208 L
+440.708 157.21 L
+441.072 157.213 L
+441.437 157.215 L
+441.801 157.217 L
+442.165 157.22 L
+442.53 157.222 L
+s
+78.19 230.21 m
+78.919 229.983 L
+79.648 229.756 L
+80.376 229.529 L
+81.105 229.302 L
+81.834 229.074 L
+82.562 228.847 L
+83.291 228.619 L
+84.02 228.391 L
+84.749 228.163 L
+85.477 227.935 L
+86.206 227.707 L
+86.935 227.479 L
+87.663 227.251 L
+88.392 227.023 L
+89.121 226.795 L
+89.849 226.567 L
+90.578 226.339 L
+91.307 226.111 L
+92.035 225.884 L
+92.764 225.656 L
+93.493 225.429 L
+94.221 225.201 L
+94.95 224.974 L
+95.679 224.747 L
+96.407 224.52 L
+97.136 224.294 L
+97.865 224.067 L
+98.593 223.841 L
+99.322 223.615 L
+100.051 223.39 L
+100.779 223.164 L
+101.508 222.939 L
+102.237 222.714 L
+102.965 222.49 L
+103.694 222.266 L
+104.423 222.042 L
+105.152 221.819 L
+105.88 221.596 L
+106.609 221.373 L
+107.338 221.151 L
+108.066 220.929 L
+108.795 220.707 L
+109.524 220.486 L
+110.252 220.265 L
+110.981 220.045 L
+111.71 219.825 L
+112.438 219.606 L
+113.167 219.387 L
+113.896 219.169 L
+114.624 218.951 L
+115.353 218.733 L
+116.082 218.517 L
+116.81 218.3 L
+117.539 218.085 L
+118.268 217.869 L
+118.996 217.655 L
+119.725 217.44 L
+120.454 217.227 L
+121.182 217.014 L
+121.911 216.801 L
+122.64 216.59 L
+123.368 216.378 L
+124.097 216.168 L
+124.826 215.958 L
+125.554 215.748 L
+126.283 215.539 L
+127.012 163.232 L
+127.741 163.025 L
+128.469 162.818 L
+129.198 162.612 L
+129.927 162.406 L
+130.655 162.201 L
+131.384 161.997 L
+132.113 161.794 L
+132.841 161.591 L
+133.57 161.39 L
+134.299 161.188 L
+135.027 160.988 L
+135.756 160.789 L
+136.485 160.59 L
+137.213 160.392 L
+137.942 160.194 L
+138.671 159.998 L
+139.399 159.803 L
+140.128 159.608 L
+140.857 159.414 L
+141.585 159.221 L
+142.314 159.029 L
+143.043 158.839 L
+143.771 158.648 L
+144.5 158.459 L
+145.229 158.271 L
+145.957 158.084 L
+146.686 157.898 L
+147.415 157.714 L
+148.144 157.53 L
+148.872 157.347 L
+149.601 157.166 L
+150.33 156.985 L
+151.058 156.806 L
+151.787 156.628 L
+152.516 156.452 L
+153.244 156.277 L
+153.973 156.103 L
+154.702 155.931 L
+155.43 155.761 L
+156.159 155.591 L
+156.888 155.424 L
+157.616 155.258 L
+158.345 155.094 L
+159.074 154.931 L
+159.802 154.771 L
+160.531 154.612 L
+161.26 154.455 L
+161.988 154.3 L
+162.717 154.148 L
+163.446 153.997 L
+164.174 153.849 L
+164.903 153.703 L
+165.632 153.56 L
+166.36 153.419 L
+167.089 153.28 L
+167.818 153.145 L
+168.547 153.012 L
+169.275 152.882 L
+170.004 152.754 L
+170.733 152.631 L
+171.461 152.51 L
+172.19 152.392 L
+172.919 152.278 L
+173.647 152.167 L
+174.376 152.06 L
+175.105 151.956 L
+175.833 151.856 L
+176.562 151.76 L
+177.291 151.668 L
+178.019 151.58 L
+178.748 151.497 L
+179.477 151.417 L
+180.205 151.342 L
+180.934 151.271 L
+181.663 151.205 L
+182.391 151.143 L
+183.12 151.086 L
+183.849 151.033 L
+184.577 150.985 L
+185.306 150.942 L
+186.035 150.904 L
+186.763 150.87 L
+187.492 150.841 L
+188.221 150.817 L
+188.95 150.797 L
+189.678 150.783 L
+190.407 150.773 L
+191.136 150.767 L
+191.864 150.766 L
+192.593 150.77 L
+193.322 150.778 L
+194.05 150.79 L
+194.779 150.806 L
+195.508 150.827 L
+196.236 150.851 L
+196.965 150.88 L
+197.694 150.911 L
+198.422 150.947 L
+199.151 150.986 L
+199.88 151.028 L
+200.608 151.073 L
+201.337 151.121 L
+202.066 151.172 L
+202.794 151.225 L
+203.523 151.281 L
+204.252 151.339 L
+204.98 151.398 L
+205.709 151.46 L
+206.438 151.524 L
+207.166 151.589 L
+207.895 151.656 L
+208.624 151.723 L
+209.353 151.792 L
+210.081 151.862 L
+210.81 151.933 L
+211.539 152.005 L
+212.267 152.077 L
+212.996 152.149 L
+213.725 152.222 L
+214.453 152.295 L
+215.182 152.368 L
+215.911 152.441 L
+216.639 152.515 L
+217.368 152.588 L
+218.097 152.661 L
+218.825 152.733 L
+219.554 152.806 L
+220.283 152.877 L
+221.011 152.949 L
+221.74 153.019 L
+222.469 153.09 L
+223.197 153.159 L
+223.926 153.228 L
+224.655 153.296 L
+225.383 153.364 L
+226.112 153.431 L
+226.841 153.497 L
+227.569 153.562 L
+228.298 153.626 L
+229.027 153.69 L
+229.756 153.753 L
+230.484 153.815 L
+231.213 153.876 L
+231.942 153.936 L
+232.67 153.995 L
+233.399 154.053 L
+234.128 154.111 L
+234.856 154.167 L
+235.585 154.223 L
+236.314 154.278 L
+237.042 154.332 L
+237.771 154.385 L
+238.5 154.437 L
+239.228 154.488 L
+239.957 154.539 L
+240.686 154.589 L
+241.414 154.637 L
+242.143 154.685 L
+242.872 154.733 L
+243.6 154.779 L
+244.329 154.824 L
+245.058 154.869 L
+245.786 154.913 L
+246.515 154.957 L
+247.244 154.999 L
+247.972 155.041 L
+248.701 155.082 L
+249.43 155.122 L
+250.159 155.162 L
+250.887 155.201 L
+251.616 155.239 L
+252.345 155.276 L
+253.073 155.313 L
+253.802 155.35 L
+254.531 155.385 L
+255.259 155.42 L
+255.988 155.455 L
+256.717 155.488 L
+257.445 155.521 L
+258.174 155.554 L
+258.903 155.586 L
+259.631 155.618 L
+260.36 155.648 L
+261.089 155.679 L
+261.817 155.709 L
+262.546 155.738 L
+263.275 155.767 L
+264.003 155.795 L
+264.732 155.823 L
+265.461 155.85 L
+266.189 155.877 L
+266.918 155.904 L
+267.647 155.93 L
+268.375 155.955 L
+269.104 155.98 L
+269.833 156.005 L
+270.561 156.029 L
+271.29 156.053 L
+272.019 156.077 L
+272.748 156.1 L
+273.476 156.122 L
+274.205 156.145 L
+274.934 156.167 L
+275.662 156.188 L
+276.391 156.209 L
+277.12 156.23 L
+277.848 156.251 L
+278.577 156.271 L
+279.306 156.291 L
+280.034 156.31 L
+280.763 156.33 L
+281.492 156.349 L
+282.22 156.367 L
+282.949 156.385 L
+283.678 156.404 L
+284.406 156.421 L
+285.135 156.439 L
+285.864 156.456 L
+286.592 156.473 L
+287.321 156.49 L
+288.05 156.506 L
+288.778 156.522 L
+289.507 156.538 L
+290.236 156.554 L
+290.964 156.569 L
+291.693 156.584 L
+292.422 156.599 L
+293.151 156.614 L
+293.879 156.628 L
+294.608 156.643 L
+295.337 156.657 L
+296.065 156.671 L
+296.794 156.684 L
+297.523 156.698 L
+298.251 156.711 L
+298.98 156.724 L
+299.709 156.737 L
+300.437 156.75 L
+301.166 156.762 L
+301.895 156.774 L
+302.623 156.787 L
+303.352 156.799 L
+304.081 156.81 L
+304.809 156.822 L
+305.538 156.833 L
+306.267 156.845 L
+306.995 156.856 L
+307.724 156.867 L
+308.453 156.878 L
+309.181 156.888 L
+309.91 156.899 L
+310.639 156.909 L
+311.367 156.92 L
+312.096 156.93 L
+312.825 156.94 L
+313.554 156.949 L
+314.282 156.959 L
+315.011 156.969 L
+315.74 156.978 L
+316.468 156.988 L
+317.197 156.997 L
+317.926 157.006 L
+318.654 157.015 L
+319.383 157.024 L
+320.112 157.032 L
+320.84 157.041 L
+321.569 157.049 L
+322.298 157.058 L
+323.026 157.066 L
+323.755 157.074 L
+324.484 157.082 L
+325.212 157.09 L
+325.941 157.098 L
+326.67 157.106 L
+327.398 157.113 L
+328.127 157.121 L
+328.856 157.128 L
+329.584 157.136 L
+330.313 157.143 L
+331.042 157.15 L
+331.77 157.157 L
+332.499 157.164 L
+333.228 157.171 L
+333.957 157.178 L
+334.685 157.185 L
+335.414 157.192 L
+336.143 157.198 L
+336.871 157.205 L
+337.6 157.211 L
+338.329 157.217 L
+339.057 157.224 L
+339.786 157.23 L
+340.515 157.236 L
+341.243 157.242 L
+341.972 157.248 L
+342.701 157.254 L
+343.429 157.26 L
+344.158 157.266 L
+344.887 157.271 L
+345.615 157.277 L
+346.344 157.283 L
+347.073 157.288 L
+347.801 157.294 L
+348.53 157.299 L
+349.259 157.304 L
+349.987 157.31 L
+350.716 157.315 L
+351.445 157.32 L
+352.173 157.325 L
+352.902 157.33 L
+353.631 157.335 L
+354.36 157.34 L
+355.088 157.345 L
+355.817 157.35 L
+356.546 157.354 L
+357.274 157.359 L
+358.003 157.364 L
+358.732 157.368 L
+359.46 157.373 L
+360.189 157.377 L
+360.918 157.382 L
+361.646 157.386 L
+362.375 157.391 L
+363.104 157.395 L
+363.832 157.399 L
+364.561 157.403 L
+365.29 157.408 L
+366.018 157.412 L
+366.747 157.416 L
+367.476 157.42 L
+368.204 157.424 L
+368.933 157.428 L
+369.662 157.432 L
+370.39 157.436 L
+371.119 157.439 L
+371.848 157.443 L
+372.576 157.447 L
+373.305 157.451 L
+374.034 157.454 L
+374.763 157.458 L
+375.491 157.462 L
+376.22 157.465 L
+376.949 157.469 L
+377.677 157.472 L
+378.406 157.476 L
+379.135 157.479 L
+379.863 157.482 L
+380.592 157.486 L
+381.321 157.489 L
+382.049 157.492 L
+382.778 157.496 L
+383.507 157.499 L
+384.235 157.502 L
+384.964 157.505 L
+385.693 157.508 L
+386.421 157.512 L
+387.15 157.515 L
+387.879 157.518 L
+388.607 157.521 L
+389.336 157.524 L
+390.065 157.527 L
+390.793 157.53 L
+391.522 157.532 L
+392.251 157.535 L
+392.979 157.538 L
+393.708 157.541 L
+394.437 157.544 L
+395.166 157.547 L
+395.894 157.549 L
+396.623 157.552 L
+397.352 157.555 L
+398.08 157.557 L
+398.809 157.56 L
+399.538 157.563 L
+400.266 157.565 L
+400.995 157.568 L
+401.724 157.57 L
+402.452 157.573 L
+403.181 157.575 L
+403.91 157.578 L
+404.638 157.58 L
+405.367 157.583 L
+406.096 157.585 L
+406.824 157.587 L
+407.553 157.59 L
+408.282 157.592 L
+409.01 157.594 L
+409.739 157.597 L
+410.468 157.599 L
+411.196 157.601 L
+411.925 157.603 L
+412.654 157.606 L
+413.382 157.608 L
+414.111 157.61 L
+414.84 157.612 L
+415.568 157.614 L
+416.297 157.616 L
+417.026 157.619 L
+417.755 157.621 L
+418.483 157.623 L
+419.212 157.625 L
+419.941 157.627 L
+420.669 157.629 L
+421.398 157.631 L
+422.127 157.633 L
+422.855 157.635 L
+423.584 157.637 L
+424.313 157.639 L
+425.041 157.64 L
+425.77 157.642 L
+426.499 157.644 L
+427.227 157.646 L
+427.956 157.648 L
+428.685 157.65 L
+429.413 157.652 L
+430.142 157.653 L
+430.871 157.655 L
+431.599 157.657 L
+432.328 157.659 L
+433.057 157.66 L
+433.785 157.662 L
+434.514 157.664 L
+435.243 157.666 L
+435.971 157.667 L
+436.7 157.669 L
+437.429 157.671 L
+438.158 157.672 L
+438.886 157.674 L
+439.615 157.675 L
+440.344 157.677 L
+441.072 157.679 L
+441.801 157.68 L
+442.53 157.682 L
+s
+P
+0 g
+0.144 w
+[ ] 0 setdash
+3.25 setmiterlimit
+450.12 237.508 m
+70.6 237.508 L
+s
+70.6 237.508 m
+70.6 2.952 L
+s
+1 g
+[ ] 0 setdash
+70.6 2.952 m
+450.12 2.952 L
+s
+450.12 2.952 m
+450.12 237.508 L
+s
+0 g
+[ ] 0 setdash
+p
+0 setlinecap
+78.19 237.508 m
+78.19 234.611 L
+s
+P
+p
+np 74 239 m
+74 253 L
+82 253 L
+82 239 L
+cp
+clip np
+p
+1 w
+0 g
+0 g
+[ ] 0 setdash
+2 setlinecap
+0 setlinejoin
+10 setmiterlimit
+[1 0 0 1 75.19 239.668 ]  concat
+1 w
+[ ] 0 setdash
+p
+np -2.19 -1.668 m
+-2.19 14.332 L
+7.81 14.332 L
+7.81 -1.668 L
+cp
+clip np
+/MISOfy
+{
+	/newfontname exch def
+	/oldfontname exch def
+	oldfontname findfont
+	dup length dict begin
+		{1 index/FID ne{def}{pop pop}ifelse}forall
+		/Encoding ISOLatin1Encoding def
+		currentdict
+	end
+	newfontname exch definefont pop
+}def
+%%IncludeResource: font Times-Roman
+%%IncludeFont: Times-Roman
+%%BeginResource: font Times-Roman-MISO
+%%BeginFont: Times-Roman-MISO
+/Times-Roman /Times-Roman-MISO MISOfy
+%%EndFont
+%%EndResource
+%%IncludeResource: font Times-Roman-MISO
+%%IncludeFont: Times-Roman-MISO
+11.52 /Times-Roman-MISO Msf
+0 10.5 m
+(0) N
+P
+[1 0 0 1 -75.19 -239.668 ]  concat
+1 w
+[ ] 0 setdash
+P
+P
+[ ] 0 setdash
+p
+0 setlinecap
+92.764 237.508 m
+92.764 235.77 L
+s
+P
+p
+0 setlinecap
+107.338 237.508 m
+107.338 235.77 L
+s
+P
+p
+0 setlinecap
+121.911 237.508 m
+121.911 235.77 L
+s
+P
+p
+0 setlinecap
+136.485 237.508 m
+136.485 235.77 L
+s
+P
+p
+0 setlinecap
+151.058 237.508 m
+151.058 234.611 L
+s
+P
+p
+np 147 239 m
+147 253 L
+155 253 L
+155 239 L
+cp
+clip np
+p
+1 w
+0 g
+0 g
+[ ] 0 setdash
+2 setlinecap
+0 setlinejoin
+10 setmiterlimit
+[1 0 0 1 148.058 239.668 ]  concat
+1 w
+[ ] 0 setdash
+p
+np -2.058 -1.668 m
+-2.058 14.332 L
+7.942 14.332 L
+7.942 -1.668 L
+cp
+clip np
+%%IncludeResource: font Times-Roman-MISO
+%%IncludeFont: Times-Roman-MISO
+11.52 /Times-Roman-MISO Msf
+0 10.5 m
+(1) N
+P
+[1 0 0 1 -148.058 -239.668 ]  concat
+1 w
+[ ] 0 setdash
+P
+P
+[ ] 0 setdash
+p
+0 setlinecap
+165.632 237.508 m
+165.632 235.77 L
+s
+P
+p
+0 setlinecap
+180.205 237.508 m
+180.205 235.77 L
+s
+P
+p
+0 setlinecap
+194.779 237.508 m
+194.779 235.77 L
+s
+P
+p
+0 setlinecap
+209.353 237.508 m
+209.353 235.77 L
+s
+P
+p
+0 setlinecap
+223.926 237.508 m
+223.926 234.611 L
+s
+P
+p
+np 220 239 m
+220 253 L
+228 253 L
+228 239 L
+cp
+clip np
+p
+1 w
+0 g
+0 g
+[ ] 0 setdash
+2 setlinecap
+0 setlinejoin
+10 setmiterlimit
+[1 0 0 1 220.926 239.668 ]  concat
+1 w
+[ ] 0 setdash
+p
+np -1.926 -1.668 m
+-1.926 14.332 L
+8.074 14.332 L
+8.074 -1.668 L
+cp
+clip np
+%%IncludeResource: font Times-Roman-MISO
+%%IncludeFont: Times-Roman-MISO
+11.52 /Times-Roman-MISO Msf
+0 10.5 m
+(2) N
+P
+[1 0 0 1 -220.926 -239.668 ]  concat
+1 w
+[ ] 0 setdash
+P
+P
+[ ] 0 setdash
+p
+0 setlinecap
+238.5 237.508 m
+238.5 235.77 L
+s
+P
+p
+0 setlinecap
+253.073 237.508 m
+253.073 235.77 L
+s
+P
+p
+0 setlinecap
+267.647 237.508 m
+267.647 235.77 L
+s
+P
+p
+0 setlinecap
+282.22 237.508 m
+282.22 235.77 L
+s
+P
+p
+0 setlinecap
+296.794 237.508 m
+296.794 234.611 L
+s
+P
+p
+np 293 239 m
+293 253 L
+301 253 L
+301 239 L
+cp
+clip np
+p
+1 w
+0 g
+0 g
+[ ] 0 setdash
+2 setlinecap
+0 setlinejoin
+10 setmiterlimit
+[1 0 0 1 293.794 239.668 ]  concat
+1 w
+[ ] 0 setdash
+p
+np -1.794 -1.668 m
+-1.794 14.332 L
+8.206 14.332 L
+8.206 -1.668 L
+cp
+clip np
+%%IncludeResource: font Times-Roman-MISO
+%%IncludeFont: Times-Roman-MISO
+11.52 /Times-Roman-MISO Msf
+0 10.5 m
+(3) N
+P
+[1 0 0 1 -293.794 -239.668 ]  concat
+1 w
+[ ] 0 setdash
+P
+P
+[ ] 0 setdash
+p
+0 setlinecap
+311.367 237.508 m
+311.367 235.77 L
+s
+P
+p
+0 setlinecap
+325.941 237.508 m
+325.941 235.77 L
+s
+P
+p
+0 setlinecap
+340.515 237.508 m
+340.515 235.77 L
+s
+P
+p
+0 setlinecap
+355.088 237.508 m
+355.088 235.77 L
+s
+P
+p
+0 setlinecap
+369.662 237.508 m
+369.662 234.611 L
+s
+P
+p
+np 365 239 m
+365 253 L
+374 253 L
+374 239 L
+cp
+clip np
+p
+1 w
+0 g
+0 g
+[ ] 0 setdash
+2 setlinecap
+0 setlinejoin
+10 setmiterlimit
+[1 0 0 1 366.287 239.668 ]  concat
+1 w
+[ ] 0 setdash
+p
+np -2.287 -1.668 m
+-2.287 14.332 L
+8.713 14.332 L
+8.713 -1.668 L
+cp
+clip np
+%%IncludeResource: font Times-Roman-MISO
+%%IncludeFont: Times-Roman-MISO
+11.52 /Times-Roman-MISO Msf
+0.75 10.5 m
+(4) N
+P
+[1 0 0 1 -366.287 -239.668 ]  concat
+1 w
+[ ] 0 setdash
+P
+P
+[ ] 0 setdash
+p
+0 setlinecap
+384.235 237.508 m
+384.235 235.77 L
+s
+P
+p
+0 setlinecap
+398.809 237.508 m
+398.809 235.77 L
+s
+P
+p
+0 setlinecap
+413.382 237.508 m
+413.382 235.77 L
+s
+P
+p
+0 setlinecap
+427.956 237.508 m
+427.956 235.77 L
+s
+P
+p
+0 setlinecap
+442.53 237.508 m
+442.53 234.611 L
+s
+P
+p
+np 439 239 m
+439 253 L
+447 253 L
+447 239 L
+cp
+clip np
+p
+1 w
+0 g
+0 g
+[ ] 0 setdash
+2 setlinecap
+0 setlinejoin
+10 setmiterlimit
+[1 0 0 1 439.53 239.668 ]  concat
+1 w
+[ ] 0 setdash
+p
+np -1.53 -1.668 m
+-1.53 14.332 L
+8.47 14.332 L
+8.47 -1.668 L
+cp
+clip np
+%%IncludeResource: font Times-Roman-MISO
+%%IncludeFont: Times-Roman-MISO
+11.52 /Times-Roman-MISO Msf
+0 10.5 m
+(5) N
+P
+[1 0 0 1 -439.53 -239.668 ]  concat
+1 w
+[ ] 0 setdash
+P
+P
+[ ] 0 setdash
+p
+0 setlinecap
+70.6 210.041 m
+73.497 210.041 L
+s
+P
+p
+np 61 203 m
+61 217 L
+69 217 L
+69 203 L
+cp
+clip np
+p
+1 w
+0 g
+0 g
+[ ] 0 setdash
+2 setlinecap
+0 setlinejoin
+10 setmiterlimit
+[1 0 0 1 62.44 203.666 ]  concat
+1 w
+[ ] 0 setdash
+p
+np -2.44 -1.666 m
+-2.44 14.334 L
+7.56 14.334 L
+7.56 -1.666 L
+cp
+clip np
+%%IncludeResource: font Times-Roman-MISO
+%%IncludeFont: Times-Roman-MISO
+11.52 /Times-Roman-MISO Msf
+0 10.5 m
+(0) N
+P
+[1 0 0 1 -62.44 -203.666 ]  concat
+1 w
+[ ] 0 setdash
+P
+P
+[ ] 0 setdash
+p
+0 setlinecap
+70.6 157.941 m
+73.497 157.941 L
+s
+P
+p
+np 61 151 m
+61 165 L
+69 165 L
+69 151 L
+cp
+clip np
+p
+1 w
+0 g
+0 g
+[ ] 0 setdash
+2 setlinecap
+0 setlinejoin
+10 setmiterlimit
+[1 0 0 1 61.69 151.566 ]  concat
+1 w
+[ ] 0 setdash
+p
+np -1.69 -1.566 m
+-1.69 14.434 L
+8.31 14.434 L
+8.31 -1.566 L
+cp
+clip np
+%%BeginResource: font Mathematica1
+%%BeginFont: Mathematica1
+%!PS-AdobeFont-1.0: Mathematica1 001.000
+%%CreationDate: 8/26/01 at 4:07 PM
+%%VMusage: 1024 31527  
+% Mathematica typeface design by Andre Kuzniarek, with Gregg Snyder and Stephen Wolfram. Copyright \(c\) 1996-2001 Wolfram Research, Inc. [http://www.wolfram.com]. All rights reserved. [Font version 2.00]
+% ADL: 800 200 0
+%%EndComments
+FontDirectory/Mathematica1 known{/Mathematica1 findfont dup/UniqueID known{dup
+/UniqueID get 5095641 eq exch/FontType get 1 eq and}{pop false}ifelse
+{save true}{false}ifelse}{false}ifelse
+20 dict begin
+/FontInfo 16 dict dup begin
+  /version (001.000) readonly def
+  /FullName (Mathematica1) readonly def
+  /FamilyName (Mathematica1) readonly def
+  /Weight (Medium) readonly def
+  /ItalicAngle 0 def
+  /isFixedPitch false def
+  /UnderlinePosition -133 def
+  /UnderlineThickness 20 def
+  /Notice (Mathematica typeface design by Andre Kuzniarek, with Gregg Snyder and Stephen Wolfram. Copyright \(c\) 1996-2001 Wolfram Research, Inc. [http://www.wolfram.com]. All rights reserved. [Font version 2.00]) readonly def
+  /em 1000 def
+  /ascent 800 def
+  /descent 200 def
+end readonly def
+/FontName /Mathematica1 def
+/Encoding 256 array
+dup 0/NUL put
+dup 1/Eth put
+dup 2/eth put
+dup 3/Lslash put
+dup 4/lslash put
+dup 5/Scaron put
+dup 6/scaron put
+dup 7/Yacute put
+dup 8/yacute put
+dup 9/HT put
+dup 10/LF put
+dup 11/Thorn put
+dup 12/thorn put
+dup 13/CR put
+dup 14/Zcaron put
+dup 15/zcaron put
+dup 16/DLE put
+dup 17/DC1 put
+dup 18/DC2 put
+dup 19/DC3 put
+dup 20/DC4 put
+dup 21/onehalf put
+dup 22/onequarter put
+dup 23/onesuperior put
+dup 24/threequarters put
+dup 25/threesuperior put
+dup 26/twosuperior put
+dup 27/brokenbar put
+dup 28/minus put
+dup 29/multiply put
+dup 30/RS put
+dup 31/US put
+dup 32/Space put
+dup 33/Exclamation put
+dup 34/ForAll put
+dup 35/NumberSign put
+dup 36/Exists put
+dup 37/Percent put
+dup 38/Ampersand put
+dup 39/SmallMember put
+dup 40/LParen put
+dup 41/RParen put
+dup 42/Star put
+dup 43/Plus put
+dup 44/Comma put
+dup 45/Minus put
+dup 46/Period put
+dup 47/Slash put
+dup 48/Zero put
+dup 49/One put
+dup 50/Two put
+dup 51/Three put
+dup 52/Four put
+dup 53/Five put
+dup 54/Six put
+dup 55/Seven put
+dup 56/Eight put
+dup 57/Nine put
+dup 58/Colon put
+dup 59/SemiColon put
+dup 60/Less put
+dup 61/Equal put
+dup 62/Greater put
+dup 63/Question put
+dup 64/TildeFullEqual put
+dup 65/CapAlpha put
+dup 66/CapBeta put
+dup 67/CapChi put
+dup 68/CapDelta put
+dup 69/CapEpsilon put
+dup 70/CapPhi put
+dup 71/CapGamma put
+dup 72/CapEta put
+dup 73/CapIota put
+dup 74/CurlyTheta put
+dup 75/CapKappa put
+dup 76/CapLambda put
+dup 77/CapMu put
+dup 78/CapNu put
+dup 79/CapOmicron put
+dup 80/CapPi put
+dup 81/CapTheta put
+dup 82/CapRho put
+dup 83/CapSigma put
+dup 84/CapTau put
+dup 85/CapUpsilon put
+dup 86/FinalSigma put
+dup 87/CapOmega put
+dup 88/CapXi put
+dup 89/CapPsi put
+dup 90/CapZeta put
+dup 91/LBracket put
+dup 92/Therefore put
+dup 93/RBracket put
+dup 94/Perpendicular put
+dup 95/Underbar put
+dup 96/Hat put
+dup 97/Alpha put
+dup 98/Beta put
+dup 99/Chi put
+dup 100/Delta put
+dup 101/Epsilon put
+dup 102/Phi put
+dup 103/Gamma put
+dup 104/Eta put
+dup 105/Iota put
+dup 106/CurlyPhi put
+dup 107/Kappa put
+dup 108/Lambda put
+dup 109/Mu put
+dup 110/Nu put
+dup 111/Omicron put
+dup 112/Pi put
+dup 113/Theta put
+dup 114/Rho put
+dup 115/Sigma put
+dup 116/Tau put
+dup 117/Upsilon put
+dup 118/CurlyPi put
+dup 119/Omega put
+dup 120/Xi put
+dup 121/Psi put
+dup 122/Zeta put
+dup 123/LBrace put
+dup 124/VertBar put
+dup 125/RBrace put
+dup 126/Tilde put
+dup 127/DEL put
+dup 128/FractionBarExt put
+dup 129/EscapeChar put
+dup 130/SelectPlaceholder put
+dup 131/Placeholder put
+dup 132/Continuation put
+dup 133/Skeleton put
+dup 134/LSkeleton put
+dup 135/RSkeleton put
+dup 136/Spacer put
+dup 137/Cross put
+dup 138/DblEqual put
+dup 139/Grave put
+dup 140/Acute put
+dup 141/DoubleAcute put
+dup 142/OverTilde put
+dup 143/OverBar put
+dup 144/DblUpDownArrow put
+dup 145/DblUpExtens1 put
+dup 146/DblLongLArrow put
+dup 147/DblExtens put
+dup 148/DblLongRArrow put
+dup 149/DblLRArrow2 put
+dup 150/DblLongLRArrow put
+dup 151/UpDownArrow put
+dup 152/LongLArrow put
+dup 153/LongRArrow put
+dup 154/LongLRArrow put
+dup 155/ColonEqual put
+dup 156/Diamond2 put
+dup 157/NotSquareSprsetEqual put
+dup 158/AtSign put
+dup 159/Solidmedsqr put
+dup 160/OverDot put
+dup 161/CurlyCapUpsilon put
+dup 162/Prime put
+dup 163/LessEqual put
+dup 164/Fraction put
+dup 165/Infinity put
+dup 166/RuleDelayed put
+dup 167/ClubSuit put
+dup 168/DiamondSuit put
+dup 169/HeartSuit put
+dup 170/SpadeSuit put
+dup 171/LRArrow put
+dup 172/LArrow put
+dup 173/UpArrow put
+dup 174/RArrow put
+dup 175/DownArrow put
+dup 176/Degree put
+dup 177/PlusMinus put
+dup 178/DoublePrime put
+dup 179/GreaterEqual put
+dup 180/Multiply put
+dup 181/Proportional put
+dup 182/PartialDiff put
+dup 183/Bullet put
+dup 184/Divide put
+dup 185/NotEqual put
+dup 186/Equivalence put
+dup 187/Approxequal put
+dup 188/Ellipsis put
+dup 189/ArrowVertEx put
+dup 190/ArrowHorizEx put
+dup 191/CarriageReturn put
+dup 192/Aleph put
+dup 193/IFraktur put
+dup 194/RFraktur put
+dup 195/Weierstrass put
+dup 196/CircleMultiply put
+dup 197/CirclePlus put
+dup 198/EmptySet put
+dup 199/Union put
+dup 200/Intersection put
+dup 201/ProperSuperset put
+dup 202/NbSpace put
+dup 203/NotSubset put
+dup 204/ProperSubset put
+dup 205/ReflexSubset put
+dup 206/Element put
+dup 207/NotElement put
+dup 208/Angle put
+dup 209/Gradient put
+dup 210/RegTM put
+dup 211/Copyright put
+dup 212/TM put
+dup 213/Product put
+dup 214/Radical put
+dup 215/DotMath put
+dup 216/LogicalNot put
+dup 217/Wedge put
+dup 218/Vee put
+dup 219/DblLRArrow put
+dup 220/DblLArrow put
+dup 221/DblUpArrow put
+dup 222/DblRArrow put
+dup 223/DblDownArrow put
+dup 224/Lozenge put
+dup 225/LAngle put
+dup 226/Diffd put
+dup 227/Expe put
+dup 228/Imagi put
+dup 229/Sum put
+dup 230/LParenTop put
+dup 231/LParenEx put
+dup 232/LParenBot put
+dup 233/LBracketTop put
+dup 234/LBracketEx put
+dup 235/LBracketBot put
+dup 236/LBraceTop put
+dup 237/LBraceMid put
+dup 238/LBraceBot put
+dup 239/BraceEx put
+dup 240/Slot put
+dup 241/RAngle put
+dup 242/Intergral put
+dup 243/IntegralTop put
+dup 244/IntegralEx put
+dup 245/IntegralBot put
+dup 246/RParenTop put
+dup 247/RParenEx put
+dup 248/RParenBot put
+dup 249/RBracketTop put
+dup 250/RBracketEx put
+dup 251/RBracketBot put
+dup 252/RBraceTop put
+dup 253/RBraceMid put
+dup 254/RBraceBot put
+dup 255/Wolf put
+ readonly def
+/PaintType 0 def
+/FontType 1 def
+/StrokeWidth 0 def
+/FontMatrix[0.001 0 0 0.001 0 0]readonly def
+/UniqueID 5095641 def
+/FontBBox{-120 -220 1544 923}readonly def
+currentdict end
+currentfile eexec
+D8061D93A8246509E76A3EC656E953B7C22E43117F5A3BC2421790057C314DAE3EFBFF49F45DA34B
+424A961BE670A194E7E4BF0FF295DEE23134A14A7C08B6602621D885EE631B1D8D3003CF2093E039
+4D77FCEFCA8BA8965D1783DCAD9EFE6C7E420CF7B898933309A89F84721ADE7F3AE4443C5EAE8155
+759A9EB6F738F7BA81C192EE45DAD00F398FFD6904111BBD91BFEE328004F2A18BCCD98DCDB2CE23
+961B00F204E50EA022D00CE2B68E653626D4BB5AFA334A0D657307416FAF7AA8C43ED4DC541F1B7B
+B7500B3F423D9D369F8192FD00A59FD5E6ABC70F788FB70976CC1907DDC309F4B690AA2D2BF12CAE
+C493958CC0E76CE9EB5FF8BD1F1650F659E5C123EE455B7D77C39952C212AF319BF19A91E36DE52F
+0EF84B602704BD6C7C94E1B0E067210DB919F6231755A2CC5D5FE129279B43A2E2CCD27F56F00B05
+C8AC10AB07FABBEFB3509088301FE78CAF8B440C5BA2FFE06BBFCD066046618F3B6AA2E4B17B296E
+1F3F1560E19CBAD54E16E4B7A65622E468F6BDF97C50277E355D1DD8843D0A449A147FFBA071BA99
+CF70D7576DC18B96FEAF8070BF25F3A3141F873241EF4A07F332306B56F1D5909A4F233A9DB3A08E
+E43DF38DD6DB2D6DAB908967A907303EE1FA04F048FA6EBC531738C170B8B0F095FF3B05D14C2BDC
+272F7EDA8926A77D9CDA49A90AE1387A51A24ECDB2E4E287B0F95A83AD2EC0310F9B6F396AC10479
+835035FD5D4C84D91917FE8A8755C976504AB1A830ED516B5F325EA4ADFD115900D23039A2BC84EE
+D21CC21E2BBE29A5E0CF28BE047CBD515DF7785F37DDD4474042B102A1F28193BB8567FF6FDEF811
+25CE9A504BE5011C0010DCEBCF321824C9DA249D8DB11F79298F7B674CEDB6F33C111F8B0115E407
+99E0FE1B6CE9F6B2A3EED1827A9CEB453D643FE5CE2540DCBCAF7B2EA2C8F0AE9434D4BAAEAB3488
+FEC7541D57179BDEAA0BB6145EA783E73E70E0AA71A4FA58E81EB510E9FD2CF4ACFBF28E48CA313C
+CF5ED2BE032B7C9A07ABBEC9CCD8D5AC9F775D36BECC252B9FE01B6AA79A70E22478904DADDA08BB
+7CA3B66F3A83AEEBA37406127353790348AE5FBD144ABD8E1B32D1BCC70E2BC22A63E854D38317E6
+BB97C52A6C9B0C6AB5C336CE2D417A714825DCD237F7486C33925A995CD8AD3B359176C4CA775FE2
+2C6F8A7C7343F31F39D1B9A5A744973BF65D655DDB59E3495B28DBE2A877EBB32A22A4AB8EB13C67
+02B239F6932E2DC8B4B88915923B1C2AFF2F876A31F765B91747D5A858BD632B0BE4F135AC484016
+AE1BC01E44B6653A58EE1380B6DF24AEB73220A36FA8FDE9A152C16E049D2F247A9AA71CD2DF3D9E
+653054FAF518BBC1EEB43200DB0BACA93FEA9445FA5F32A99926C4F1F2370B2E3E03CEFBEECE5D5C
+DE299FE1641A9CE0C90E42E7C465DF8C19C153EA76C11791F8E112853E708CD0F6EFC22E44808A44
+3686442645D643E7118D128BF34188FD313E53B8E951E605E96825738C4DC893D942C145C3E1153F
+CDED16D1EE10374626F45781461FFC94C77D431BCF167FD29136A0B369631E139A59B06AC5281B3D
+52470B38237B469C0E49CBE74C82A940F8AAD10E05C5DD2A8F3218C4BE40DCED0982770869190845
+D2E8BA2A1A2F9CF16DDDE418B6E2013C3258FBE9AFCDACCD57B01C4FEF3526C06FD5BAB6F195C557
+23A96FA3E5CDB2ADC9AA8989DF78F163F19E26F3B9EAF60D44B745FCA49B09D9CE5CC16D1E2C78C5
+A28F14D3374956E9721C0542A01A731A3D5A771C7B51FB72960BB22C99BC8F216530AA86C1A0346B
+3D986EF1DF68BCC46EC4C809C6D54FB9C50F225ABA2C96965F4DE77E2C5D131318231C6773F0976C
+DBD016B803C38B8923822BDF160FB87BBE744D0953EDEBDE87394282B9950C89F58824D731355E8F
+A3CE364F742E3337A983BD1219CE8787CFA6925B560001F6F78343647821D408B60965966C16C454
+394D33268483F51984614FD9964CCE5F1AA4AB12144874A72D3FE97F4416ABE4213B4EDCA9ECF73A
+937B562F978E059705E561A6D0C8B89B59B4CAB2248BFC4A6D3D98F89FF38A4D1C3E5A4570E2E2E8
+D328772E11DEA2116C260B13328045139A819F5489A2C302F311898230CD26DD03E6CE3BE75EDB86
+0D982FBC8E1E24D1F83E8CA64174495B2F32BDF0505FC96E9C65FDB0EB0C4ADA410F0A1C4BB4D551
+93B1AA5EA1F988882A56413F77CF24FF6516CD5095077BA566116797895FD7EA616D10510957025A
+1DA05064FD9176303A248C94CE2A9875C03C7D8755A1A8010B712B64BAD73BEA4195D51A328F076D
+12C0E52E87D98A5269D157D544CD7B4E1CAAEA6BDD59F08D0C5FBB84A6B099BECF8BEB721A874BAA
+1BD1F8CDB5ED5CD3B49A763EAA36D33837667A6643B83537EF5515DBF7659E28692E0ACEB48FD051
+45534A443092E0A510B05B5B51B402CB3372A4E8BAF98A7FEB6D8BEF6B364A5EA0F3FDF301A44EE2
+3485D4493E7B4C33E0E352202931188D156983C40F7A4B615B6F5281B33FB32A2E6A0F7AE84BEA2C
+5C3635D7DA17371D608847EB402270B83293EC09E4B1D85996C1CC81196BE590EC9EF0F258364746
+BC4763976FDDB3707D37AFCBDC0F4EB2B1502F137EBBB1541B992EAD43B5D64CCDF505FF2F127115
+4857C69B689A11760979F6D2BB032CF0DCCBB33D2B6F6D6BB29A1D0E371AA5869A408225C0AFF523
+CEFB08A7D3D17DF9064010363196CC569C436E720B1B4573CDAE1CD93A10FD2D4ACB14E47046B5B7
+66B75B40BA97027FEC4218980B002FAB60A9F1F5A37861B9A63F696732F8127B2C6F036BF32311B8
+FF08A489E88218092D57A99C8981EF8CBBD09DF49CC836B9C1968692D1FB551F47619F020289F1A3
+D3898A96DC1C7D39A21B49C2D0DD9508CB8A8BD6A5EB40FF86F555FA32017B67AEC07BECD659E8C4
+8DD1D43E8D1EE08A117479015F78BF20D3318224572D9C90884856E4307A8AFFC83EDD317B008484
+3BBE8EB2A4E2D70077A639FE3637C3DCF87C67F1BE62E63CC67BCEF8EBD07E030524A53DD440F2A1
+3A019B7AA89E155AAD1497974258A01DA45DE1D94BB9F925290FE9BDDA29EA3F2BF1E64DF7EBCFC4
+23AB2C7310D9D87A5CA673EE95189135E1B134B431B231428FF2BF64C8F155CBDDAD17BCB524CF7E
+ABD66B75705BFFB1DDB27B72D681D7AA19FAD0FF23EEF028B606DD20D58588AACB299A3CF38372A8
+E7494A65227C1E4A231AC7843962B8A4A7BDD90F3F8B83277E696F3514139BB8DD207641B62286F0
+0E32C7FAD7029DC0E092564C7CE1BC5240FCBFB00A06F075D9536F0326CFFBA958BA7A1A11AA047B
+B14E7DE16761BB0FEF19ABE85154359807C339961B9695CDED59E489CA4D9BB75D86D9EDDF0502BC
+0B4EC36B1D71FCD4E03881ECEC703E5DA23B3F5DB98CB8DAED81D5BA20B844A92A4B92FE5B082952
+6837237C7F994786878404BE744D0953C676E52CB05FCE193E8827F977B31929E36E320E770A6F1E
+972B84AFA21E01719759DF0132C5CF9692BAA487E86E8453C09FF97642600D1024ED5D6C7D5B387D
+CB5E6147D20B4A14D7D485B5747D765C5A1CA40B9416DC4EF5DC08F43F0706B27B1C90E901556E1C
+EFF304EA8DF8D727E4B7E7CEAD14E4FC7E76002DBC37B92BD0E58AF29DA7DA91E6F99DADF1A9CBDD
+04648E2F5B811F2AF0729646B17D32D7EF25AD47E267EE53A61A7CD744C9ABFDB1EDB71C0705412B
+15769188CA1601AF0395796FAC0E2139EF90FAA65D27AAEEEE319D2831A56CE82203523097D0574D
+22742D17E519D95560B8445B5B29033BF3454B94F20C23EBE8B90DDF26D903F1D66CB39D3A64579D
+39D136C537CCD9FF2D6ACE5F79DE696C7330C0C4EA179D7911B7B67858D86CEE0833AB1E105B1993
+2A9BD572C41E78FB4A2A2421514DC2066E2F56101A9027F4BBA5D48E5DA9218E81CE46B95B50380F
+560C67A5D547A8D32610AECECBB6F5A09DF44994F6DAC64F7399C07E182033BC1B9A32B69C41FDFC
+7E1DCDDF273F045F154D727AFEE3CDB816CF2ECDB6733C579276353DD59C9D2AFA455935C1FCD0AB
+7D57F9DD79FBCC7A45E5E5A501FF17EE1C5FF0823C6FDE29C60F85F6786940D8E3540E891B5BF7F5
+D36C57AC3AD359BFAB12B215E7FC94B933386C250E6B236506FA3D151ABAD893A817772071C09144
+6E5FB23A17591A4CECAA46DD46E3C41B4578F21D8894A59B72FAF6F88EE6E514FBD2FE69447D2B59
+9A6AA8BC5C1FD5C25E50BFB5CE5DBF5AD5771BC42FCC3706B6E9F7E4FAAFF2E63ED1684C5A4C136D
+609D03E31EBCF31E864AAA74D8DDBCA52F85CCF14AB739CC55617EFC964D6CC6988AA45245B19CE2
+B63CB865DF1F1DA4A200A4A043C5CB706CD303EB31C32866ED92077AB11BF136D158840EAC67E7A1
+1BC2BFDCD5894AF735D677E1AC98BF3F19F69AF75355F168632037F6EDEBF61BE5854057AD05972C
+7DA8D37AE65D35738547A9D835C6351D8D732F8C0DC49D7605F00A6A6045559D3A0B0CC21DFDD75E
+2FCF25522001741EBBEB5CC97DDBD9DDCE490FE3CB186C101940DD02CACB685ECCB8C1DEDCDD4156
+F5F9F6B3CA53EC6E7E8A2F8159451CD5479D91BFBF91C6B32A1E0053017369330EAD2DDE323BCAC5
+EEC91A595473F447355B1BDFB873D0B538BF54AFB8EAADE5309E1B74283218F59272E59619D66405
+E74B5A9D6323CB76AF00FB27FD984F740601248C7206F59EF7FF4E95AF95327D12C47D2D34CBFF33
+29F28236831D7F0FD9D633B940D8282E1F1D5D95616CD839C6B41A7195A22B7A320864E48943CE99
+C68E3591A97689986A6536C3A1C37DA9838FF71A8C1A03094537C95D3585DF5AD9292874D8D05720
+030133B02E2754DA24712B52D6D8871422C709532B09D147EC8ACD861FA5392A82A6C2981347E327
+1986017B8315822B5FCB19A1B31BF6909E0D6490EC823434BFCE031045D20FFC675A73EBD7A39A50
+44C3B40906186CCF6E8502CD7915F053BC8CF6BE9FDD9A0330AE943D5C9A11D60D43F5BBE8A045EF
+CDB7D19C0053F4F082303442C35C432E0DA4408C5917D24A6658DB807BD754AF140CE84A34F79851
+9323060D60F4EAC0E82D3E17042BB6729C69A8B8774904C6214C2EB016C528DC1428DB41075AA6C5
+4E4D849901838C6B6DADF7B67CD0CBC6EE1B415B44654D89397274D4A6AD2BA69DD81567F5B802F2
+684DD3516ECA0F6D66046EDA0B2B38F420A238D67D98676925ECBE6711D64DAE5DBE8AC5473A2EE9
+97AE65F0715A2E5DB2C75F20D9939EF58692EDA3AEA81F25AEC888327CFA6CC13D496714E63A1A87
+11FC50250D6D23FC7A8017038281F659A685ED7F1BB1ADBF8833ABC6DBEC0D96D08334E58C67E0F9
+0046132F0D4FBCB9CDF52EE74561498B42701AB826A6DD77F46C14753748E1EC66F4BD3583FCB4F1
+DC91050CF18B0D51BC366549261282779FC9694A7B987973C0D59F65CFF3CDB88D23952E46F5EEC1
+BDA0DC354188C11B0FA191F5C11A45BB9093701B33B8E7AC1A8621793D5326E92CDD3A76FB9B67D6
+860567B9CEE7B9F9056C6E9971F642DC0BCC1D2882B5BDF1B1CDCAA3FC61775A74E70CDFC128DE0F
+9606091BB8E53905108EE77A1D3C506550FCFCAE454B020379BE3A7D6C62D162697AF1383F7BC8F2
+34FD616324C1E56E3E440277862CAB2C02395D9937816117F71A6828344182E6B5AF2799E29A6611
+9C0543E135300E44746EF2EBA57C4EABB1A15D8AC6D037F4BA2BE1EB4D1A7496F9711FC67E56D4D0
+FDA4D810B5383A72ACA15872DE9F3A9890E33759CE4DA493691BCA47A92E42D950DF588C3574D6FC
+72B5AF7DDE915E3F5925B3E97DEBE6A4208CE7337754045607679296C4EEEA3A3EF1915E85EB1A32
+F1BBADB2904B09806631E20BBF5E57AF11BC784C75BF049B4BC7E479F1D4AE7754CBED7B11ED80A5
+2DD0006FAE5CC23A7C234CF628B42F4104A490E7D59E8B1F2A1395D37641C478FF8CB9FB602B29FD
+3E48D6087CAEE48B523887F27F69DB32BF80B760B030A6766F8F9B19DE70704DAF81D3DED2BF663D
+89B5BD8AF253BB8FA59DF84A52FDED83D2737532B6D66AFB9EF0446ACD44BFAB797AB9FDB47F2E18
+8F0A55887853772EBFD029B5FA0AFBAF10A88D464BD6F634C5839B230D583E36671A51DDB1EBF471
+8ABB84D057729D514751B0EEF4F756B91DEDAD7D9AD529B3886008F1F32645F3A205F304B2A8D808
+D37A38E389B8587E8D3654DC374568FCEBBA160395BE1D132B1ACB434164525FBF250A5AA30F3520
+F0F2A75F9D1B7C6EAB0223C209A120D894D6ECA336B57B7F2AB0E2A94E6616D7539010C700311966
+7B4A9EB3A812FEF4D100AB4C036401D21DDF8AEB393074638D154418D3A7AE51CD1F0C2D5CF4B475
+3B582D5071F91E6D3AFBFB09EAABBEAB2A60A5D388D16232939B5483CF7D5C1C9D05DDC85B266F39
+6F61E179AB9FAB644040014293EB53F2B50E8751F9D92D1DAE8DC89594905A39357C0098265FBD24
+E407F9A22027E36D5608FAF15BD9E354228E3BA943EC30301ABB2CB105F3B6D57C001EBF8328A8CA
+318A7B1B999AE8BF9E2FD530B669640116149B622EB3C0A8CCDE62449DE9D39E0F5E5E01CBBF4F5E
+52B33A2BD60B52FA4A5CE98B7313FE4C3FA30FA07DE4516109C3EAEE97ABE430C505647DD8B3DBF2
+BB4B3A806B14A9E0F629863F15D12A1CA62E8E055FA6ACABDE1926D3231CAC271C30A3F7AAC6084D
+D88628B943052B618E4E39148600AC1EDB57B0835C291F73D29C51FCA3C5EFB1DB18A5CA47433B45
+C57EB3CB28AEBC68E4171E6DE357793B0FD2A1F4E76180248017112674DAD7ACA6ECAAF209CA174A
+5603CEA5CE528F20919443900636E2FC79C303EA7B467A3F001EA8CB849BCF28BF40A3364C08ABC9
+B5D935CFEDA827A8C3FE2F6ABA54017D2AD0393A05AE21F7AE1F58AE1E992B5C889219DA157FA7EE
+92E291DE2F9DFC244F2CF0FDCEFCACC487F0EA830985B687556D5AF53B81814DE9CE0C6C430DCBCE
+51EBC6091B4E0227732E07DF20D59E9EED4D8A83761CED84CCE244BFD6A39103A2B48A684AEC9210
+5C94495D54FD6690AF857E5A3915E6A15689F1816F75F1FC8D5769B240639B339EBE54BC6D84794D
+50F4EBE892530351984C6F8BEBE097CD46F4FED7D46E32830A16F77C13F13D17003737482F02D1B6
+F04C57B4C2B1929AA315B4BE1C7C9CB55F8981508546B67E4EBF84B6026C355C5E4E07CD98C95F07
+56F6643FB1DD7D8C77C4AF4C4F23602DD3F61D1C46583071AC460E74084F7F7CF812BC53975CAAF8
+B3C1160B5D6864AF08A1733FA893CE4248C8F8B46AEFCCF317DC726BC1E5F2445E462E0591BEAAEA
+49AD8E4E2D3CF07F900EC46D596E9CDB3A8710A0B93CE5DA9D35E612596A6374F35AED0EF55DC46A
+8E14A91163B87417259DE926BBC3FC5423FF0AE2AA6D740BFFD26981A57C8C1D97FB04A90A567296
+B07437F94C8FFF4709213DD5D8862A777CF3F97723F43A4F913F4A30F7554ACDAE34713654E21731
+C594C3718C0547FCDAF7BB1620B2D6BB19E65596A585290CC43F50B34A2FE6EB1E495ACFFB16DFEE
+8784B66FCB9359600412969E2BDA330C421C68345A797C49728450A6CF41C926AE8EBBE80BD97863
+593C3E8AB5415A8BA625957F242378948F5C7EA9C2641312E8E46955FE5C1A1824C71D3B0C9FD211
+7CC965DA7C21D257587F291AB7C594459A11B977A278C74CF5E6598B0C75ABBB2FC1B3D167D7E31D
+B519F4A0BDA650A3FE7F1A281DB9B69B57A23656BD820B22D0E30B07361FE7C90933E24A32B6DE2D
+F240635D543315226E32487F264AFE83EFEAC74117321A82A92F4FC7B259F26DBE74C966B4CB3F4E
+3499E30B9B326F72D73919D9FA9024AAC0B95B6751AD4CE569CC3DDFC399428DF1298FB1733FFCE6
+240FB3BE3A2837E1A66E08B784CDD131B5A61423508000785CDC5610CE4DA1DD314131767C8857E3
+3D9741EF3FD7B8D0AF0371CFFA3DCF74FF2D3B421AC0339EBC05FB4B61A2F46D6BD1144747AD148B
+7968D7DF4D0F742AB39B0A04A88D66F5CF9876970879879872BFDA0C56C253DE5C72016B4990CEBB
+2455DCDEC7465EE7C7E1C557B570E9F3EF3E31A22DC8AB9B17F34F48E135BE7820ACE383DB7C5B05
+8A9DC855C6850E49AB7D7D00ED2D23C50AEE671B11752F0A682EFE179CECBFAB47B76676AC0E8FD1
+0A47353D3AC3477A59B0F3CAF22849DE97AAC8B79935F7C57F3052DE7E13BA0FE7CEC4685C86E841
+EA8C7D6734D0FEEFF538CC2AA1414EC9126637E169FBE4ECAFDFA29A08451B25954F0094710195E1
+69E0A6004419E432E9319BE2AEC8D93407B25B758336946C6E30032657DD857BE9C0A05F487423D2
+0C2772D4416D27FEB5CCC971DDEDFE9B8C2D7DF9DEC90D0868D5DD18850BE567A04D08310883D9B2
+D50B817D0690F8550E238C1139C0A117B48199B1B4D489C9E52C58E0CA75E6C386ADD447D8AE52D1
+D979FD52A50D82BBCB8867B5D49C19BDEC414929DB67A00AF7C6482A87E46BD11D8E4E52783E5845
+FB2CC7439F99FF6552C7288354B436B1C361AB8C2932F77B14A50A7407FC0BCC29662D710248CA46
+AC42A03FBBEF9C972783942F3721BD32BDA3273D1E353D9E013D4CFF630BFE4E7C2963FECFE350A2
+860421D17D6ACA888FA26403726A0E16BD10638D8511A2C8A36C99E9B72A422038E1149BF88A7CA1
+B2DB196333F3AD32D6FE28F18FE1ADA06FD25E4597936A924E71C188B9E8779BDBA928A182042C96
+F6A14A6FAB5C1A819DB8E9374E32F77393734A65606809E90D188F749180A3E6CA7AD8044E168677
+15FDFF350D70B14454B692715DC3AE2CAA561FB953B478E873EB19C41A8D83288451E4E910D5988F
+33D83C5974DD0EE4DF2E6C849C500D0714038ECB1E9336D71D852889F2FBCA6769949699BE6BBF19
+A9318CCD7C845D0EC7FF3CFD871328CF7B45E6BBBBD16094E8ABE4D3789DEAD2C930AC8D810C911C
+03AF2BDA4EBA62810F19B4506592005ACFF16EB58E33D6A71EA4DAD28A2D7B10FF89ACAB4BCC0F96
+E622EBA20347AE04C62A298257D1623977D185BB46B42CCDD733F72B37072B8DFAA5D7FF77E35618
+3F6D25EE1D951F7EBFBEA8FA17A859B8545BDB212A6BFE9C3078C32124C4E5D4DA09404E40498F76
+7B7164C13E12BF006EE8DE408070296EF0A08AF759206DB3C445BF49EAB18ECDE1FEDEFFAB653FDC
+B13FA4649B35D12266FD42D0498331560E96F54D4238678F70233F56097E1944FC671D6BB1AB66CD
+E0E9DC24349E44D67C36B3E3A00B07755749A597DF31C25F28D55779841BD3F6221BCDE389852DD4
+590E075C1298B36181D9F64BDCB54A556C05A9EF661EA1CC7C7549A3E1CCF79B87A6E71C3ACDECC9
+C4EFB05B643744407029258B8225DBF960DE529EEC262D1D3113F6CDDBCF4BDAB706BF085C0FF2EE
+E8420CF755E46B053B774DF37C5683F8432EEC183C96176BFB24B3F478FACACBF1FCB73D83D4D857
+2D0F15783C8AE95D7CE959D794FDE1D973867D8F30A301598BDB7F2824B2629D64B88F0FF4498B6F
+3728CF8916EA884C5930677E7481F90C85ED41DD28AA00E714D3A4F2CC136E417A4C591C7944C409
+70D2BCBE410A940937C3CAA118FA32317444B401968B8ECB2F0B3C8DAF6D4886C2015000A71FDAD4
+066B82312A4CD1E49A9ACFA05C3E7CA5A5CB3FA713CA0AD9E66A34730A36612C72D1F803D4CB1501
+9184FA2FDB3E5D067BC64B29299D5531565264B70FFFF86F5A5B03848E55D73337650208D21F35BB
+D5C14748CBE17EB3A7E02BE53136DC69E8740C597CE28472CAEEB96EF2A5752CF17CFBB82F6C104F
+2BBB50C216C49E8AB7E661207E1742B35392752051A1E409BEDCDA2654CB5155B770C8C5E4A2968A
+A35CF1924D0C74A8D23AB811B7DCE60F1EBC1494A295C8D670E84B7B886A6064151065BD96F2D364
+7BA049A444CF16EB6432CAFCC70FF2E8B354F55A192C94BF08D156856A6512403F5140DF4C8D254E
+DA59B2B3ADEE19A35E9A61202B711193A7E7BA8EF427152157DA5A51084EA46510864E1CD7B4FD11
+16E74D7BA769ABCFAC556BBA7CC528C18003A2AE059CC97C18263500667F6A9A8C421F2ABDD73EAD
+82D9D08940DEE1688D4AA200ED80C3AFEF6A4979154D99478D4F4EB3738333E8581A755190D87BE3
+09A319ED170A107512F056E4C619D4BB1615BA6C0680117610A26462F267B76F9DBC9A7A3AC08C9A
+512A44697082A2718B62FD1D565010AC96E1006F958070AB7567A33E1FF7BD450681DF6BD4EBD265
+CF12726A7EFDEFBB9BA1E596BC5C2940A4FC9DE60116C9082138F1C672977F4AA6B7986ADABBB2B0
+651A7B0C06C4BD405C58B8C81BE075997E118E10FC008AD1F9ACF9F6AAC6C9E3F6DC7FCB838720E8
+D0D9BB99F203EEA2566F18232711E832810F10DD4E2DE44A0A81603EB1162E1BDB60AA1E2D771EC2
+E6E2C3B39508E5CA03A1E2A7F2860BC6F0B57240880DF54C325F4729EEFA1D2753D57F055CDFCA5C
+E9C6630980C7121FC21E2F7223E6111C18FFDA0A0A7643A213DE9525AE138814C4183BF03A26A36F
+EE9199821464C845A746945413629DC53F5A2B0D8CE9282B580ED662F2F07398D6A8B7093CFCC6F5
+E0F5A7926D2862AD1CCACB84D85902038551EE3EAED02AC7754E3B65818C530A0252C049A68862DC
+A459DDD937F7BA64DB16AC86885B68AF46422D7C4923070E20CBAAC9F14E43979C5D5AC4C9321016
+7CCC52E7DA272146D68D8F61DB4D60063E74F6673B41ACB601DEEB1DF73517A8C5388F00E8A9D236
+9C85DBFE4C1E9864AB52E09F465EE98C194151E66CB98E6981EFFCADBC48532E9795290CF745FDA9
+CB7FD40BB77C148A8B90C0CA50176103E2ECCAA57030F7C0882F1E891F9EEBA077AA4992FAE38C75
+5470A4C35454EBAB45271DD76E4DBB2D9147817F7D3FB800EA136D3F27C84B4D45ACEAD13D3C91EE
+BD6836AC04F95E578D1B7B8CE5B718E42FD1BBE91EF9A7873F0C6DC59AD231D08CEB4AE312F83F1A
+9310155D2C4F216A6FC72385C899B5390EBADE7CF7BEB028F73DD76EDEEF639E9EDE034ACB25BA57
+8C7BEC3581FEE0B221F7340570489596FC60EC805405E0D2ACF0687A62A75358B3878972D4C039D9
+07D868DD00C5F7F3342384C09B01F1926F7D37C2B862FC516361253CBBDAB354898B31B1FE58F773
+61517F2C2E106708AB659D95CE3E9E7777B7327DE01AE2D1E0C84B9EE3887C094C149700CB095D5D
+A5FEAF1AA9027AF257576A7370D79FF4DB98511AA27C5C083FA0CA60A3A5E66B8DA9A22FE5DD3DDF
+C529BEA71E83881D8B010D140AD686DBEC9AF4B331086F6556B695CAB14BF24B5FE64382E6F9BC21
+5E9EC40C0914B1E27BC25F43E6F555B551726A8D9CD590D5AD3732777EF3B00CBAA89F8E8E0E0347
+7D45B00181A7B196FD4919F110C0F2C0F1B41231051AB5BC8F8025ED00C10F2C2F51E97B622A4E84
+E6AADA59F896F83EFADE779B37FACC56BDCA1892A3BD965B65D51B55AC1D73ABCD16D0EADE55C0BD
+3C1BE9FDB323E9FBC9835C63F757051E29E773D3491706DEEBAA0F1182F0E02D0CB701661B30770D
+94E240E1358195781006E18CBFC2D83F229A89C3066E35CAE1384A0266D5A37824A089E855F11209
+9F723AF03BC1C8B1C0BCFFDEBE3EF73A40BF83F5E038B63267DE5413B88D30155E62EDCFA35C0047
+0178E5558CDA2B30C4EE2A9854C93E0E484D4363E3614E5BE829FAEAE51935386D20DBFC00B42952
+7F56FB045EC4D97B3D649415045337AF44BCF4AD9B9F9BF3EA72151DB616FF8F6B13EF66516D9640
+67460FF123C7EA525A97F1D04BDE9D3D495602620659F6E5DCF1AFC5360D1C371BDF9984C4A7B462
+180A3CAA7098E0FB0BDCE694806BA466883BD28D77DB4CFB6635BB7DB45B4D83AAD4260A4CA0D411
+0E251AE7476A95327BD6AC1AC88F85CCB705FBD09993B9E2990D51C37F1110F78B887C54E4EFDA80
+4ADAE5D81477913B6938FE1B39913C6582021A1ACA834500D9D75C9942CE2375D0A2A73805751EC0
+970D6FA62D4354337A43D85DEA6C6F3334F40221FC473DD79344D028FAC689645963B371A55CDA92
+F6BC989F4F1880AC11D8A056DF7A0EE60471800126526488B95B785698D2AC488CC027D31190ECE2
+54F80757DC9B4FF18171409C457F5FC58DD1D43E8D1EE08A6AA2F374B7C245B7A21287DC333BCB1E
+EB498A2BD723EE6BB30B76E574773F70A61F1E163A25941531C049ADEDDB59AE75B7A6725D997752
+10ED356DD93D0F0AD7EE6495F07869C570125125BC84946F6AA1152CA18FCAD3B68004A7F7AFC6E0
+9EE6E1B365A4DA15DA95AB980A566DEC7394F385FE041B3B7F46D62424F4726B55DCB4BD3625CA68
+1221CE01DAE78E24F8045EF21F85C13D27D1F0F1B589F936F62F699B93EF05930C6F5730B0AFDB7A
+7A829F2ECBF0DD196ED66C7E4E4E7E05B565BB98385F017D01B1305856DB747C3634BF05DAB08836
+029674ED3A4053CC3DC696F758F86970F94034B4F0DFEAA4DBDE449800DB040B55E7FC83E6B5584F
+A00F7C70ED18524037CCB48A22A5A69506A825DED81D98FE93177DEEFD42B485013D4C4184CD690D
+714530B56E355FB01BC92DD90A7AE8095748A872F79121A28E965E6149F37C9D5F4DF64574F605E1
+B929174AE0CF533F77EBA0E10862BBAC46BEBF583B951BD0BFC5128B08CD998DE897033E7CA8B198
+625D865701394543845CDB7A45BF82DD8B6319658E28B34FD319E38E650869742BD2672623ED9810
+8DF14CE5C911AE0AF2035B4F9CC0D1D84176CF3AEBC4834E8BBF07B7720521C4E6C081A962FE29E0
+700C4F4ECFE92C39BEDD64C3DDF52959A4102CC242C35F56139643F22613D675F970CFDF8C6A57BE
+9D168C9CDF93915F66A5CB37DDB9B8B729F8131391F9D6EADC8BDD5C13D22A0EF967622F3F7C08DC
+C422E3D41B3BDA6B4520C98FD08262303E042DF37B9911C1447F3DC8A854973323E1C380A02DACDF
+8A53A0D1EDE9BF34A50E8B76C5AD5B15F8E25B2783BCF4B1247309E9B70CC951CF3D98C1A25E9CB7
+11235352F3BA91FABA828F2D7D91F0FFC50852860C531C20A5FAAFBCE1197CA367F0F84DEB86A8FF
+A9FF4C252EB2633AA2BDAB30F2094721F482CF926DA3299452177B79251B311AA60D4CC82F5A9F50
+E942703877AF1C10CD92DCFD16CF19BC7314FDA5A85284BDE964DE2BEE782F4D52D17FD2084E0A95
+59EBD5AADCC74A6DE64C1F799385F5EC2E2F5F869F78F7289A26B03A9FD906934C3A6BA4A7B36E7C
+3B77A7581BE9CD62B49C34572A870053CBA85DCDB9FDDE407B29CB3D02AD1C752B54DBB995DF0F8F
+CB117CF8500B97849275A4625EF9937AFD7C8751B9B021E778F7DE9A9B191BFC470823FB8EA919BA
+DB8B68755DD83C6905B118FA18FAAE216E2494FDEE9C1125C3941092C592DEC7A5B0C763E7E0D3CF
+DA02AF9FFCD234A01D75C661603820C37E9A681356A6DB424F5F991FACCFF78EAE3518C0747C35E0
+8EDEA2E108CBBFFA0B2D3BFD612B5743AC95CC4A0580A6739BE4EDE6CB1E8B2F4CB5C6FA4572329A
+06080E0085748067462F3EAEBCAD734DDA18BF206EAEFE9F882807694B80A63AF2F40625E98DF55F
+BE08AEEEC2C1BFBC16F1BB4D391B17122EFB5FB539005A613EF5C9F154BD50F05862F813F2083CEA
+149FEDC651191259BA4FAA658A42AF151B03A7B553AA79726A44AF9175A724E0D65CE84F41F3B7B0
+E0B684656EA56B4E7E654946AEFABDABCC4F3876B3C3A67373F4133FA8498DCFEBDC27476FBB28C4
+003FBFB063267FEAB2B2BB8DC647345803B2723DBA8905AB0741137C43768B434CE118954AE55DD6
+61AAA1BB60B87ADE97D4197211A0C70CDD7855783B7C570FD742AE5A5940A3D90F9EFF687C4F1E4A
+D3C4F5C3B9FF20B61C0076B9FF060EB99860F31C41B5AEC0D2C4DE626C6059678DFA35BAC5F2F171
+A3BD00065AB2007EABA499E4E7F30EB994F8BA757FF9BB0C9A2E3E384BC1DD4B62036E2D70819AD0
+A535816761800CFEA50681AFBF0057E69A0CDBB4BAAFB8B15F7727BE1169BDD4FAF692F9CEC12893
+E4E4425DE4CB20F330951EB5A7FBB0FC10DE78A7F9B0EF6FA629CA13E8C2F616A1BD42B1E26E2A0B
+533DEA701AB7BA93526E2439755FB9BD5CB076C3309E73807A5935AF6CDBBDABD5DD7B036324130B
+8BC40163FA0999364553CFBE351509F8A5167F205E228ECD66EC9375D0E0713563AE75B6C23423AE
+63EB67167F2F4512BEFFE96B90913E6920F1F8B7139F1CAC9E38482B6CD26576776A01103FDEB164
+A176023370D08906E2EF27E1E8B6F6C27EC67A86EA36A6C82B681385F3A60AD53A8512E0880D7ACB
+5567F2654731CCC1796C47E18DD6CCE30F65E27DDC5A3E56BFA0460DFC3F9FF1E98B7BDA9DDCC615
+718D7C6CD8DC1270E70FDD4973B287396E2B85ADFCC83C71DBEBB97346E858CFDA78713C0EDEFEF6
+B84984D719C4729C0A3F2A7422DFFBB2AA5FE61891D3D16CDC1BA4A84E7A74B0076FEBE0C2C74F4B
+B9314E5246D7E67DE23466D47C8AA93AC4859B347D8CE31FCFB4D41137B51C41BF19D626A59D0999
+FF2A4FA5FE6FA60E029494EF99C8B00700E6E752F4F9ED52F2AF5845563ED8AA5D4E19F82DC0F87E
+255ADA53AC62E3D7BC788EAA135431DFF49F2D3ACB61798353B27409D149FD635690F8AD324804DE
+A99D12B02F15D9C6DAA01BE2C1512BB8DBE86EB31D7034866C10558C008D69DAD8830745F2BEFC2F
+FCD957D0FEC30BFEC54F3C440F3A99BFDD7C6D0D657402A064F2656694E5F5A5524CF4A7A2AD4625
+5DE9D2E9916DB9DC2C39986A221C31F89A1884ADBF7DD62D4EBD47957E7A359F2ACFD38E073E8502
+5F907941ED233EE3582AA955CEF67A8ECE6D8B301EF37B7D40ED84FA9DD604C74C8E870F9C26A2D4
+DEC8F03563D29E1DFB974CA191D4696D877A468082951B02A88884B9B760961D9C37154F32D54512
+4F0E4357B68547CAE9CDB571089752D7881613E7FD8DAA8CFB98CA9E930B48B78AE13523E43A3568
+7B42DD2F0A99034ECA1DD782DA692EFF6AC99D6734DF1AED3616B198E6C242EA7A9954B7337ABA3D
+13EBF06B95E16F19047AB0EDBAB6A8928D81003E593C1F134B0E2B8C250EA251B59CD04905F57016
+1662514225C393C42BCC172DD23D8871908522CFA5CE113EC05F39E4583EBDEB5DA425E4627A4A2B
+D5C511F9C9C155BC81D0EFAFB0D0F2E96BD49A5C942933336EDF9AE0CDCBB159761DFC50F6180FB5
+024D2E5C2A20233AFF282FD3B1AAE9B65D2989BB8176AA8C7A1F58E43A9AF3A6D0168CADB6930706
+C4F974282D4A23F71B0A41C75086DC1C45CB98ED68ED0E4FC62807EDEF13C6C85741B11FA957D791
+D92B750F3B7BDFCA7E148149E55EDED66700483C4D5BFC3973580F7199FD99CE6B358B508FFF5DF1
+78A5E495977D851B0B06DC7F6B38388D5C94BC8934584D8EE2F4E0CCD3332A737BC066F042B14931
+57BE93622E346FC6B293B8DA0D3EED02508AD2183454FD4D5D21235268834B257EA8B06117F67589
+3E0505E64709FDE03F2D5C82B163C29629EEBF5D408547AC363758D8D134AD7B9A55AD9C7D90B67E
+6DF3AAE6867759D2A75993265118BF6C5A261C6D80EF039A0163BCF8E6B40E643D1BF87BD2592BFD
+6A41EFDF9CFC2E6DDCA83FEC28B5EEEA2E363C20AFB4A13B5EEB2CA3BAEB2CA6F4824AF1A59E4CBD
+884CA4C62852A095E4B13CD018669AF3898DFC4840C874B97408C811E431163887E5BB8CEAC12FA4
+A1EC50E8305137437441AE0FDF8FA4DFFFC77C05FCCC41696E40889A02DC4B1267D5B892647D6AFB
+BA549F737543652D3C3A051E36BDB692DD7EA0D8964EEC27BCAE8EF5FA853C54D67A8ABEF7B01FB5
+AA473DF48CFBD3D697C9B55C852773A35B9A7C97908DB701AB66DCFB274A824B60E275E1CB341147
+36B26E50EFB1DF01F01688E09E096533E95B3AF5D09D7823DED38487C58B4F10D6AC76EB48731CED
+78AB098C452AC83CCEDFE4E8E4AEB4A93A715306A096F262BFDE5036F38A3B733B1A0134904A3EE0
+8A9F7C2723FB5D0535C5B57CB80C29E292A49AF936DAC66CDE5C01640490109E679FBDC13F892438
+D70CAFB12909FD2ABFEAB23ABF6D129F5628B36FA00548ACCC39C8312030DBB87364DA44FACF3818
+D4C8ACFE3302B1487D5CFED16E17B05CE9889219C13C9DEA28C9BAE5D788578C309CB7781244E30B
+7DFFFAF5A9F594B8781F849EB20B1F3A346C2D697CFFEA6AB4134DD46C86BD0F89AB3EE9DBB2F87E
+988D906C21A43E5ADE58BFE82D4D4009A39EA3D1E779FC374FF21B86BE48AA4A33186DFA0F97BBB3
+218CE963643EF2A35788D613DFF934139B3EEA36377E67A713D20BD3DF04720AB49834E3FCD78908
+1FB726CF642A5B87D5D23609661F7D694EA0256F3EA0DBAB7C2CF79CF89CA0FC9D25281EE0FC16B0
+D089DC5B156462343A375F1EA2682A53F70A1F239D58080F4F884DBFFA68CC6D63365A8CC308DC5C
+BC2387BF3D3107C95FF4DDC3A902B31C3F414355748B10518EBE00C92682CFA413FD071A16B8D129
+4021B0ECC5025E33F6116C89C7B983C6BFC31C5C8D7FB5E5E81D3AC500123CC05B3C8DE01357E192
+0DCFD172EB4B488CEB9E1ED5FA1D235C96FAD22B319239FDBA08ECA2C5C1192B4D7A797ECE135228
+6BBF1E59AB3B54B8886E67A82AD971DFD1EB21CC5E3512CA922F9B870A48E6DC94F94181E422D274
+2D3A14FCB3939FC8C1D62CAF79033D6EF4DCC93751BDABE588BF5D97B52AFC5084C5BC17246FF977
+7AA4D738BB9B15E534ABFD68848B879A9840EEF4774734F0BACED5E7B6177DFD430E0497E36D1077
+7654F351348BCEAAA18C3B362B2791A006782C25C9D544CF1594EA623BB4C782C6AABCA95F9CBB1A
+8C86318834E1430376406D2B6CD5AB09644361B83AFF66C96CF549C2D309F7439254C6C3A5B210E8
+23F83647FF420BE66901C317349C1B305014EE7E9F90DDA917E3F853F1A8AF3DF1528A81C50B76A0
+F02E933229C2743BFA639003025697612BFD8575DCEA0BA5FFF805EEB4D9FBFA8D2014BC239E9D5C
+4C87E36D1C83E010B92F43C06733976BC84AAF1C05C0A0CF45CA7746ED7E1DF5A12F2401C0FFBEAB
+EFA199A7299E4BE5089C2CD83E7838F163F6284FC299B213513F803E93ABD8D759595DBF513D68BF
+96031B9FB95A945B7C9153B0B315436C850FA5F1415AA2C9565F6FA39E9F5C5FB265CEEEA8C98E4D
+00A72CE7F9F6677DCA7E58C1A8C111A9C6C44781867AA5FC71F36486AEE73FB81C03BF4EC728E43D
+75564244AACA3D66B6D36DFD38332AD05F150D4972FD475FD087E13C9312D5A17A83411B45740153
+81CB568CF85BD66428FF9EA2C07E7BF8D0AF4469AA367DAC0230650036240634AB81766E832EBA8A
+2D8BFA402DA804D264757E74B3465EE21A1EA1C92929444DAB2EC83050AD169F257B77D3F4B9BA61
+B11361F5DA6DD2DD933E101B64F9DC82945A2D421807F09F3E587D4B13BE0FDD6D7133CE890C3AC7
+1D0880418880362E27635986795E2E8426A0A7D7E8E5C41317209D957B53B6CB9E4EE7C3EEAF3315
+E006B7FD90E7A58FAE5289AE513D7751201459BE029563B58D967AD24E90DE5E96357D37E86CAEB1
+6059CD8593F92617AF636C7D32E2B074A40B6A1C40828313C8DF1BCBB002DF276D424519EEE2F234
+FF9B9B27126996834BEFB6E05A7BFE958B4AFC810E8B77F0EDDCD43E81549154F81E282276A7133C
+23650ACB159EFEA53ABABAE1C1CCEF5642898A5605A285205DB40DF6C768029D8CAF85C520AA66CE
+5BF1E0A0520CC94917FCC3118953403A1B16312096DBD56883F03E78A14315A5F97345E13C363C06
+3A01D49492349D60A9114A77BB9FD48FC0C3AC76D190204DC69C3ED4A265B8148F2C5F2E147A504E
+4F373637C065FB894446031F78C4BDEA68088681E0C5099CAB1D13833FA87AB96E511013A9B3D806
+E71EF6E0A1442C91FC2A1795A13145ACBBF5D18880695EF11832ACD328BDE6E0A7308B12759D12C8
+6B558CDA038590787704BC1BF49EE8C788C41594332624D56082ED8627EC110233CA328D2A0BEDAE
+3511719EDAA726F338D324D1577593948A8B9F0300F27FD4420638C6972EEE2D6248B87643275DB7
+69F72E8F86803184035F6A539A7CDF43A79886ECF110ECD7053FC04EB5E51B3C7625B3BF95C0F5EC
+044FB7226281BC723988AC2498ACC0489DF0BFD1DA82D04FE3ACF6B63EB269BA9489F8D5D07DA9A3
+AD04BA924B99B9C1EA64AFE7BB2886513EA6730462D4FB5DD82659C7DB7687F4CD8E006581A15EA7
+715E274C9B89F66F1ACE9C2AE7698FCF7479A04F2208DEBA6DE801A6D184A8A9AF6BC1B0E37CEE3B
+323DC4EF93EAA8219F946DB9F4D9C133C6CE0FCC6884F9C2F3A816C4ABEF44DD6256E7BC4574600E
+1D825080660BF6858B415648258399839118F3C11410C1C29B3C208A3B54AD5D7484708DCFBCA04C
+849F2AAB79E4D96328D990C63DE05DC8E804DDAE255F94FC3D56270BAFF6F86190796F91BFA018C4
+FE4FAEC3F1ABE8ADE43D0DA18E710BC1F419F77DEBAACD3BE011BB93E111B18CCEDA8EB0352934F9
+690F3E73D71655191F150BC3788677D1FE46070FDB354BCCAC8C179009553A7D67C87518131A4D8B
+4FFF85FB9485C9F30F4CD31EECBC4A44CC267F6C57AA05A11C6FCC09B5CCC83F189F6A32F8EA56F2
+2D20DA4D4008F08EFB1487675CCAE22BC9494441682F4E46839F0F4D2D16AD58AD0886C60C925DB9
+C7D9AA1A7FF41C94B6289E1B72382789006F40B99B78B05ED1FB1F715CE4C0A1078AAC02EFBE6306
+F53F5F7E73DAD249995DAEBF17E5F55082CC6885A54F93F1A935E0389FE54E8B1B6C5ED19D483620
+A697873D5F18CE9A48E3C2C1C871FD4739A78782D8112602DDF8D4FC497C459067A6B118AA998740
+6C8DE97C2F09CB9D388D341EAEC0A5BB4BBBED92BE59B273C77F3D6965418669BFCE0C43D5C86275
+D8E658BE1893DA8E698DC858CA459711969B2CBF4CE294071EA572496575CC35CAF57ED49C2FF1AC
+CC21E19D189B7C2A1ABB1AEAD7185675413C224CC4C0E1AF4EB76BA9F44148A95D8609838B967784
+2391DECB30BB0FEB92CC890F224FD2E9E9D34466D6091443CB69E845D4419F9A04664706FC8D2D15
+9002422367F39CA1B1CB1A6C32A65F46230CA2376C3E5125CDFD367514E087E59873EEE569B7F376
+227DB126060F063662F118C7BD01946BD04172147B601BABA952A01E0AD31AB1147D48FC2C3F52BB
+75D9618E6F03F1F1EA393AF0E8474025F451616C4ED236ED831E14F40BEF5B86806B73ED64AFE7B1
+2A3C1F5036BB9AE79862EDAF13BDFFF06C94939AFDA1A749364BEE73449520111CF56181527F9568
+F3189652FA7FFDF4BA1086DC5992C6E0282B6F88D7CB73F485F8A27A77453C151B0BA40E294653A9
+73298EBECE8132B440ED4B4437283356B79CADA8198512A45044A7AF04CD02CD4DF7F47D5D1E7FE9
+52C346B01D03D1F69904D1AB5E8C433B0615E88CF4D01B3C96361F82B5CF7CB4D92FF3971E44F0C3
+317D3C5B0BC8DF4BCD4DC63474D0E0B5BEFF3177E2722D4AD4AC4B4AB6269EE948BBAB6019ECF2F5
+846A3D215F6C0D999D489215D4328875DE21F2CA243CF184280B229ECA4B8A9C5290973503AE5883
+03C67DDECC577F12B41F0D4DA266772867F9D93E1863BF76C6AEA5DE3FC6567EECE93D96F717E39E
+DF536ABDFEC14DF6748DC90A2CDF6066246DF69D2745D2944123AB3A6ADCFBE7C74EA8E8D712AE86
+F76B3059178E78FB2FBE8D1F25831C70F58FB6B5922B371A27501E7463E01C844A2226CBA263B570
+4E5C4D3E50FE31435437E1ED39E6E3BF47B4E2C4588274F044B3294E7B2BFE302E76EFA3CB74108D
+A6CFBBBF383F5C456128ADB5E667E1F7ECB4C3E00AB8937769E5A2830520E9FC0A1DA1662F881ECC
+DD7967647B5841D8FDE1DF7C9F5475523F236005EEC0DF307BFCEF379355C30A83EDC96D6086E224
+388DC7B5E951B819347AC5A1F9FBE7EF1907726EE7E972AA1DDECBF7F72658C20FDE99FEE686D7E5
+01A7759DEF55169938F34978A6BD4DB49E494883F67E868A9AB177FA8E6F81157A95A03B4D9DB572
+EC1CFF33B450BC13E00830BBA20AFF928CCE04B4F79F3795DB54A4A8B5A2F3CB323194990050CBBB
+C7CD32103E0911160FF4DFF135A77DD0CC15867B994CC88E1EC10E3A097D329DBB90FBB62981FF61
+C2521F9AB4B9393C6764E5B4361D0FBB1938456CE437142F0AAD9341588BD15EA0F6EDDACF12A62E
+C025F3294AB1AC45719C5EFEE94067390D579AED4D1D36041D358CF1A24446176DCC808CE2D6CE02
+7A2C2F6E517A5ADAC722EE94A1710BF61254DB4693B30225C12B9C4F856E1D24075327017D6B288B
+B52EDE713B3710778A565EFF6C89656BE3C5F590F6ECA600390C1BFDE9B0EEBC2E4FBB9E0E2F405B
+39738F7969F64E8228494B298C3FFF4C7DED00B0EEC336B7EFEAA892C4A80CE9ABAAB4318FB34348
+93AAE6A90FF00B892D1DEE254DACDB268A6308E91FB628A98989956958C9634896B878CF93F4E0AB
+A0170C1B7BE2A0C4A0D514D7BBBC4CA114349D4D4985E96DB7E2ABB752C7828A9E36D9B0B4551DCD
+D878C06C3C68D2C214EC8121F6675D8D03545606B582B09B76B6D8608DFCED5C4A721F7008FC2014
+1D877353E8BA5DEB1CB61F7C956D4A9F8CFFC8C4FF81B2660AE4BE45F7A63141BAEDEA05C43CAC2F
+A04163ACECAEF90F61E0473E5CBF1F1994076B6A72CB5C33B17DD57E2632F7C6DEF7837F8A939055
+CF357795865D86C7745DE54C6079C791850B20C0C7349472FB6018521DD5924ED1505A1B8C8F9CF4
+C892CB40795C4ADBA3CC11C8A52A1DCFA8FD334D7F3C344CB4057E80A4B66AF6A97799F8DE817CDD
+0202870BBF42E76C9BABD2D9B66D10F1A68388AF1A511887FFC50EC7D07581D4C4FC3F6C4F7EFCA9
+4799D74132B5EA25DF0C9557902C7EF1E04E612D9E40DBA459513E584C3A3EE5614ACEBA165E07A7
+CA3394969C2CF1FFF28B1C7DA85451270DF0FB71DE22A03FC2F17531FD59B12B55DEA7F5F56B0DF9
+34C96E26124342571BD04FAE6A12C6EB0E21F06275605FABE91C6EDCF55B298BC4CC52891DD90360
+AA5FDC150004BAE65225FFC42D13026F9C6FE343D7CE2F52229B4846F6E23BB2BBDB6DCF60F07A74
+8F19F74A1168DC5C67BEF840A3C68CA7A4D8CE7F94610F4CEE989459D0CDF1B194C63A2B82479746
+03A89150C4C6AC67AE3A1341F9516887C6BA254F81C5B552C527765A52ED5C4FC45D575F606E465F
+7C2EDD2F5927319BE737D48099C333BBA84486F5F8CC0B32052DB6E57DC55A68019788DEABF8D649
+A531C1880D07E425D55D4DD4F3966B2FBD2A0B55E5C429051DCF0E3B9CB1DE6A5B3DA05DDBBDD3CC
+1C81877AED2BD272FC7A10707C07DAA54FD6CD37E15C247E3100A1A0C527727B73D45C8E02798C44
+1864A2D1D30FAF94121F0CADA24753221C85DD5B43E5F00FCAF73A1E531D9453307581C6FA28BB5C
+54D93F149D3871D2E017E6E7FFF7B0BEC71B83AD12AF353CD13311D3A6F16C51938986C9B6A24EAA
+06B8BE6DF27A5090B3B120D1D1E064C6C1745536C6B0AE5C899C3DCA91BC38B7E900D9614F291B9D
+BAA85AFBBBB57D58C3E1FA8713CCA1BF4EED469773EB4B9605125C08A8F7E998E37BD893F3533232
+ECDF47D26E8B2A0437220FAA760DC8E90FCBEF59AF6C1C55FE1A28A4C98E2A67DD5A7E55BD4FE272
+15533A56561F0D80989BFE15B321976CDEF26FD6530EF7A368A7239CC55D7AE2B8F0E980DF63CFE6
+F562F3ADD0AB906B60682BA447CE4A86E6D5538E13C6847791D8A16F5BA29E5840847A7E33AFF57E
+BBEC1A5B329A461FD0E858DE5163A2120BA12839C3A216C44F364452A2D6401EB549791012C4B65F
+4FEECA2B73B2D88CA49B44493802B01A23321470A2593A8F8ADE3F88D87247851561372137E11D10
+11A733C671C71D33EC939B05060C73697EE577A8F2BAE08309585887E5F314BCC642BA2715B51E0B
+4D093F6B11CD37BD9728EE90A0C92D15BD1105637052F89B417F6F36340588601C9C2BE9526D01B2
+E88EEAFA300E38B0EB5E2B54341533B31DA1193588974DC054FFBDF374960D28F0C8C1AB8505CF5C
+64988DB86E17213EF0D9D6D52ADB1BCEBCD02B5E16F0866D21D7C0FE108D551E695CEAFBB83AADC7
+362727C47BF24C482EAD6F122F1F35923DE5D6A248BB36433D044F73C944E6CE4D9FBDF0417ED53C
+8F56B6D389519E7A539D6BE9444A4747957FEABCFFDCA5FA54BFF46F637B3A3299988929CDE008E9
+E3C4CCA97054A822C4AAD01ECF9861AF6238E6643358B0EA141B0E161C6ABCB45F38740B344D4112
+9D4E898DAEA8F2065263D68D97966FA24BF88E61BD86CF2C7BFD1F058FCF04AEB88F3A30C9D446C6
+3611112CCF30F163C103D6C7C5AF946ADD33B50A58FCD8EC612268F7E119BDF387104F22E4C2CAE2
+DAC407F206186F12D93BA87711FB05E6E96F17DE333305196FA7C33DC06255828744B590FA0E67CC
+E4B0375276FC7AB9B324978B15FC228DAC17F955C7B3D441C540192145FBD002EE20FBDF6F397F95
+336C0A056609E28430E123A432EE91594E20A8D9E5774FE8768C84CC040A706D8D3590BBC9AFE373
+4A5536162846B6B7BB7B248924F1DAFD768B9546BD2A2CFA1203D6A3FF45C8EBDE2355F01744484C
+DA9FC337CF7D9D54106407F54FEEA81B77BFEBEE088F344CA2E537644B615D8D6B6A79D03F6CBD9B
+573FBA87EB00410B5251A29007CFB60E711F642C19847F58333B48F66B6758E4ABE524A4671B0635
+C491341B3CDEE650CB9F774A5B6FB92AA70FEC7D9A057084214D81EA5A36A7F8EBE9922A70F2B102
+121736E0BFB178A08ADC0A58E2A9DB347FB9B0BE707C038CC19A5519C3FA9AAEF660653086191E59
+320D0E696218CE1E8EA7DB5FB3318EEC98C130F3F1C45D0D2401223421FF99BD6E1546873C6F12C6
+F45D2E1C3EE41634F5A415FC8338A18EF2299EFCECEE00F9025E79610168548BF2A52623A479EEAE
+CC55BE5172B0CF07F9B04E2B2AC08A2BC839DFBA9680F2BB2E438519BF3B434C71B4AD9E64262C76
+9F6C1AD174FCFF3F41B5BD7652AB296C543F323F0A16E88C6D1FD3025C33794551DCAED303B57A87
+CD1E7FA46E16ED357CE0FEBAAD2678A4D84E4D6B1635F412465C0F3E7246407BDDF934F0E2E0F5D3
+EFE318D7D63A7B6BD0CD5556B6DA811D521408EF336CCE2D2B6777AA472A9EE0DB9FE0D6914059FE
+25EFF5D7E3D2A6EB96B23669118B9EFFEE5B7FB8F1EEEA355998BB48430156F14766FD77605BBF80
+CDEF19879E8F8C07B6998AF145B0AED86FA94A952F2F49D2891D41AB0184EFA8616B139E640A3A9B
+D808BDF79E283B0CC4686935D0D96630D590A5F4A7C71C05E110BF3CDB5150D8CAC418AB25419BCF
+DD5EF0A2015305561CA26494E267BA89892AD21E0BFF44D48E330694A1CE12183B9A7E4E25D78EA3
+498EDF9A22DD7718ABD06DE2C28D7762F609A1E9A5C40A878FB8AB33A60383152A119B9FA077B109
+90AF19C261CE43AFC116593C30BC27EE4D8CBCA0C0298BB84327FDBDD93F073B1E06F71933C0323A
+6E7FD2AABA783E9BC995C4AED621434B82B34424B768B4427EE65228E612581B0B8A7AAC3149788D
+FDD106A6AB93E01771802AD93B63EC386B057690E5C34A409421E532C6614EC61A0197C13EAEE438
+35ED9BB38CF811E39CF8F154684E3D8B12E3B673152B82DD9B15A2A68A6163D2CCD72D3117F7C24C
+F1575671832ECC4AC6B912882C0231050D60ACDC7D6F36C6BB4E32B6019B32D1DA08C7ACFC1DF451
+3A338FACC16D297C56C6138FB02FC7FB5BF7B9DE4C61B63C6B37B0F9CB42E6FF86693BAE1CEBB60C
+9C15CB6222F547E0D0776BC5545A73A2ED3C7799F0CA3C2D5EE17849DFB15C6ECF7C2846AD10870A
+00E0810F35A57770EEE9D49D05B54500DF164A02FFC324CCFDB5F828AF7307DDFBBE647E98909C73
+A3CC6BFB360042823C678EE6ACA0D658C12776F2A573656073E4F40505F5CB4A3D340B034B0FBC29
+C3B6B055D23F0BD47E44B441D430B8703883AF8EBA79081D528AF5646340A27291472B8E1F19C8BB
+B4AD17C7EA1FCFCC7B52E6EBAE0BC8204AA52C08B3A63B2F07FBFF20092139143A24130191C41D2D
+69077D71FC204C5FD41275DCEBEF7E5701BFF6C0A4217F6F60C2E37697C7F1C35D2451B040BA0D28
+0C9D23AAEF592BFFB436165C314C3CB75223D15694B6EF312CBCE8035A1A9172365FCCD119CD5DCC
+569B84C6BBC5AC9CAB6942096034523671156FAE2012F6A24A001DCB2F35A8A031A2366CA98F1E52
+944B58FBF1852710CEE0116BD2C7D68DB956B15FB6AAA147155E9F179E67357F231F8252D728AF12
+49DAFCF6AB4EBC8637E1BA10D27555D2FAAC9EEE5E51C8BBC793ECC6011C4FEDDF7116E719147927
+0BC11D5EDD9215A4E8087A6A16A591BF7ADFDB69C4A03361C0DD078017DC5851BBF60E06A86C6C0B
+E08087C99F4F9002ED5206534913353AE16C4F358BC1564A442CAE506A107D1FBC0B9ED99AFC6633
+209BEDBBB681CCE475645E92155285C00FD6985216CCE60064946F94778F7AA85ED87F5762C20FE9
+DC007954281BD6FCA8554D2A0CA5B76A3ED42EE5F44F3B276E574F64B20E1AD489753903ECD20B4B
+EC88ABD1E1CF5D06AA1815AB771E350EB6D04078EC04616B977CDA8CE88C483DDEE9F28D58366D3C
+224C41D19E550B5ECB9775F569C2D391F61C4667A9BD11C69A88473AA7884B823F762195CC403823
+690A32087893C29C63100A5935842F6B612C95EC9B5F07459608786310C8AE65DBBDCFED21B43191
+874E20D08F12E1384DCE1A990CA5F07DE36BD012DC9EC558FE7CC44494F48CA3BCBF1F1F11BB98BA
+EBBF8691F8590F84AD849923E656860EFC0EC27496FE6D6C185791E3261027DEEE4C57032547F94E
+7D593F7885526AAF054BB850C02E863D831ECFEE61A781B89867139889A362F95F48A837ADB955C9
+939F609D2EBBD56775151D2EA4B09D38FC1D824A952EE7E52849260DE61F07333076CF887A3AC2F4
+CD088C29E47715C5242E2366CB493B2FEB38C19FC159EC50EAE88409CF0A30C0A6823C45D0532C2A
+72E45A17916F6CEEB475C4D4A19372AE271326697CCCAFBB43D92C25A052797186FE8314FB41FD94
+2A4B24548866BAC19A83BCD2E6979FB3C7B70B075DCDAB3EF6B6181A00A98AE73B6D968EE2DDEF07
+503A92E4FE1E0B67A90A2F351D600DF960101A15808AA99B3C680A8F50D5CBBC96D98A3A2AD5F14C
+43857F4CB9DF9372FE74B5D6CA9CD801667399E778B56EF702A56F0F393137B20BF11BCFF9DFC0F0
+4E67649D07A2A5F4C42C9A929231D5BACFB6B53210E9FE311642D8BAA7358E6A7370B6CE921765B3
+68A354B42C8877E59227146409DA83E407657BEB475329F228CDFDD11BC73123234649AEF0E2E9DA
+76C12AF91713828368FB778905A6B7150258695D4D9DF6111E1B28A9462002D7C476FE44A9B13F32
+9FF84930D213787932A1BA01EA608ABE7054FFDAAE2176EB960005E5407D7C1D39AEF8EAD8683A50
+D93398C584312EE4A12C07E9D55AE9981D7EF57D66499CA93EA653EDDA1BECB494662E54CD7ED8A3
+0B2A8522CAB12B2751F7E9B3B66CA0B5C8905E0F3A51F68C96E9C02F10FE515FD6133D3ED298D15E
+8B1649F3D13341BDADDA7FCB838720E8D0D9DD92D0A241A0CD8E25D7B313DDAA2F25B543BB0E7965
+402ED0F24AD146E49919ABD9604ECFEA5A7A49E58664DCEFDC893F5D722CF24A44D26369F5D86569
+9632141348086A80AE528EDAFFF9D9986D5665DBCD375DF221D5EF1757D79361E5A5BCBE333B6C9C
+1CF46794A7C7C776477023BB298C970F6DFF9AE6C7ADDA8CEBE73B07117BFED6CED2A36548081C79
+BDA9A70C8FC5FA16D0EFD3E9F869DD5DBDF6E70CDA4217935FEFE577B64DA1DDBADB9A092B9D1E3C
+E1FD3B6E0117DDED155313A5DB7D8742C3409FC18B9743F09ADEC49BFF2FDBBEF9D5FA7110266287
+7C65E3A1421BA56E258A49D76C436C97AE2116F772CA5EEA5726BF17AD5D5CE37DC5F45235730E90
+A1A1E3087132C820EB9E0E311500F2CA193C72E2ACCB4D77ADFA34E268B014FFAB5DD1BE7187251A
+A69B7BB3A517796BACEFB9ACE0114FAAEFB0D9BCA028A52F5037291232A04C2353B9663BFCBEC2D9
+30CB007954B8C6396003214262AA9CA9FA876B4191313FD1831D664863C4A19946AEB8F4E21C468B
+9C94B8CAA407A74AF418DFB60D46BEE5029C884950D3BE8A58023DA864E9AB34005337D335B0B35A
+91DDEB53A54C3233150E27225B0800C841E489E9EA6C12F5D95112BB723C3E88AAEDA3A942D06242
+2CD80B04DDF024ED79224E166086C5770EB10C389D0BF0327B2F753358D9BD552C04EA52BA154EEE
+5A84C51FBBFC79055AD0F1243E489F82FD3D1A3D2CCB24A02DB3F306767564A2451DA405BAFFED98
+20241A82C6F4F6E1D2A36FD954540EF3C091247120F5E1B362D1EBCBF80951A3158B1547E7A0CCFE
+40C03F992BAD00496C32BBE9B0A06EE9554EA3FAA247A7399A37D05329CADB006F679B58F2E5969B
+13A5516A9F6949505A64B7B3FE5E748B9B9662C05E6C5CB79D64FD72C54F557ADE72880B3F9D8718
+58D5EA3FEF2BD3FCFD670794AEA6144D13782BA89648C98E7CF5160089DB9798E49BE2DEBC2669C1
+0C42AE1AD7C1FF4921EF465300977E057C0AEE61579460F56E51B6EAFC1F7F41D669ADE9610EE777
+055F927444F971121CD76A55A273CEBB481B89D6B78D0066ECE31BE3EEF65CE9FD22D22EFD6E3ED4
+536E6439782AD53550CEF1A903125F228A3E2F1AA3342B0551C59924149358F8E941969125F6E54C
+8B2C068F57FE0DF91912CD71BE9492768191F4F6B70BA45C72409D4AC6E9619E8A34398E6A66E984
+55F1793D8A2C0DF30EABE2B19679318AE09014262DFBB9AED5D637653FBA1C10821908ED6E088910
+AA033E2E0798A630966D29F84845A9C937C27A268CAB8BA1AE9B32E12B52EE4B64BE5388AD32035C
+7F98904F052B31D7C4819E27BCBC597E8503A5614E0DC2BBE5C51922980E3F492B61BEEB169C3EB6
+AB9F1DA82EA6BCFBCC16199923C8399B0495EE9A9E1749DAD9FB289FF3FCCF34F55DF7D94F91EF31
+B3B4C8074C567C4FAB337A337BCC2F459075244F665B079C159AAB83781E465F5259D41313183EC5
+F5F53BFB4797942F93EAD6CBF9255F9B4DD0748A3BC6BF36ACBE0127AF68EA6566B65430D0595FD2
+A7A2FF74055BF2E70BFFF2B79131F1871CEDEFB495FF914B88770654F9E5556AF535E1B9AF812004
+99288AA39D1388ACCB5B11B13596B550F2746B2A83B076F4F606F7580156E54FBF6976AA4CFC9581
+0A7FAC41A1635FDCFD6F7CD6BF738190D7F9FEAAB8C0B7CE38DD1459E2465C3B75625C8BAFE3B60A
+F66ED183965AB9681A8174C44311D8EE36E468A8FFE2035B7C5AB6A372FE37FF627F4697C0D19F7C
+8A1E356F829BBF2B8F4A95A49F85CA16ADDCA5C0817D6A4F7C4EAEE908DA13E0C89C9AEFF6D1D7B5
+BCD1BBF672B46C4960720CD1C74E70C78784CBFA8930576AD4067D406A0FCE9248DCE6D610D7EF0E
+97F3F9CEA1D08DDA90DFF0C484DD32265FD13A2571513F361DE3061F2EA76886112AC7589B290220
+E34610277CF81535CD628CA688CA812275D7B9909757DD519F1FC89FE4D0AFE5FDB999323A470C6A
+A0D9BA9CC92BAC24514E7CB3A3EAD2A70271EA8B02B2DDFEC6CE803F1B761D9BB7099FFCBF918D8E
+B602946FB0DC14F0FD1289037B15A4A96A4605BBE53BCEA9112BF9746F3BDDC06CCCD808C62F2B41
+D4F508EEFB03AD22E2E154888DC63C6BEC6A21D1851A5C82397FA49BD163BA8A72527827E4B6F50F
+585325219FAA9B3A2CE14A0134C19DAE50373A0F9E6DDC8205467242D11A3989D17730C8169964B9
+CC9549BE20A84FD9137DD9C9F7DEBAC3A41345DFEB0AEDCD7ED408A909000FE8D9CAC85D93052256
+7A2C9312769DC85F902D4A5F5766EA3C561549F1F2B4C5269276946809ECA26B6F51DB4CBDA9E668
+BE1023D8962C0DBCACD5BBC9E5F61C459B825036E1322737C0F196C0DF93DA76011DF2CA06F7B383
+8C672A802EA24A474ACF7A51F2DE867ECAF5674B1053CD5419F5FAC20584A3F7565D7CE584CD395B
+1968B622FE3C68DAD2F0E33274DFDF03B5A8EA047B077DA1316DF487C91ECEA84E9B417EA25EC9CF
+1F1CEDD7A1C2CB0D51A58BD8F9772FAED8D553141ADAB148AFDA200479CD04C0FFD1478EBC618303
+5437A5BC1AEE3218E9B27D21656EB9F31BD4E7D3186C89DF251207E8B67265585083111BC1AFD4D6
+A2773629147A29CBB4BBC3935B83392487858E0D18FFC96E57C83C4C6744C8E0DBF001DFD64B660C
+CC8064907AA4BECC12376A1EB55EDF655CBBB4744C1A6166590B9572073A2AB577EF446B80567241
+389F990AE6C90B286EB48454166BBF264422FF2A387FA0B413F2295B6B188A64927DF702C232CCAE
+59A2CCD1D109840A464BAC74A45B865A6667C901D86F771C4A36421308551F532497990BC15DA648
+6E322566C210C517DAADDBC24DACB39CA41611B9F961E4696D1FEC46E71C608DFDEEF19ECDD88724
+24E1BEF7176B0AFA4888BDB4C56C8690AFD03428F32740DFBC4BE22B7583DC47BBA42DF4E83C14AA
+29F4E79397BBDCD2EC43338BA1F0D2CA9DA6E64A065FCB0073ACD0D86E46EEBF1454C9C172C6BAA5
+0F37FA19756B05405763387237794E9FF74E6749BF6CE5EE9145413950D342DBE5B0EFDE57163127
+2A8D060E935547E1FB0FB1FD60400FF856D027671CCAECFF7E215BE61E8A77E9BF5C72C2C1E4501A
+A11B2F8574BD823574EC9598D579A9C5C525867F4BAAAA78DDA0E5BD7AC832DEA41328A507874A85
+90B7F133E743471D4FA27DFAC39C6F99E233C913183B2A039CBEB5CB3BF8825A92D83D266246D5B3
+CC7B11469E611E260C6ED16D17C9693B13B78E3F0DE2F0ABBE73FD6AE1ED25F57B4894254FFDB332
+0E65ADA53669B95CA28BB4BD166507E9D8F12727E46D2B9593186C090764FE8A95F1594291FD551C
+A96E4CBE1D6FD42852B2B65E7F10C4F17707F930DD934E2A2ACBDFA40E04EABC1E54632D67AB7D5C
+00DC103A3D11DCF78B6771D98ABD5CCA0B253283C67B0863C80D1A78FE6D5422568207509FDB1946
+92706C7A211B29955F6354092C9732DB2ABA8CDAF407FF25B40BF9D73317D5985E19E6C12B6AB5F5
+DD59328905F822E1EBEB87C4E386EF20CEB7EAF842700F09BDC50D7AE6442D1C93804D56FE194785
+968373A154E1F426BF76692CE3E4474360ACB9FEBD3191B8EE909E7C224EF90EEF36CD660AC9A642
+4DB5BB20B8835D365D35A442ECB2444F30466C3323001A087639B73848E3EE275406CFFB495ECDB4
+52F2A357F45D2D32BB22CAF9F9C2C44741A5341B29BB6C4322287A2A3891E1E853B976E17DD9306F
+D98BDE8A0C97ABDCC7C708BD1BF49B524CD0DCECB40DDC557E9C90A1EDDEE57BBABC4C338F08A625
+E03C1C1A20DEA709051EB1A3264D7002D6DD2BB7B622AF93C7DF54F49EFA5FBA58F115A049A91875
+A2476C19F9133D0BE1B07C8102746163CEA0A98F9B62EA59C9CED7AD808DD52DDF8687933AE52A7D
+9D7081150A812801D3FA78F90C4933E5DF09C991325324DEC6ED364ADF73DFC7B78953B51FBA2F81
+F264C2A94139289CEBF7ECD6D8FEE9E579BE231FE1ABBCC07B4BF1884CABDCEBFC56610A5E2B7510
+37FE804511FB5443F7B21290E38855F6DE5EE38960E02481DC57F83CFCB87C8FF1CB196CDC19B2B4
+6C7C72B64D7D45C678851ED7E2860DE7DB772BE7B33C84ADF7E6F9B2063D1263CDF55E6220753ACD
+67C4701EBDC0799AF47AC58A0CCE796384A50DE6A814DD994B372630E64E5FDFF57393522F689DF7
+81C44599B21AB1C214D4ACF94F07CFEE79C628C85378B96D9007ACD4225E14DAD8C70984DC596DF1
+96BC814E7346217C94F172FA40B3DFC73C30E6E530DDE4A91F6A5166AE19EAA3D2CC3D9D6917BFD3
+1EFAC19C236463AF8D7FB7D9B3A4D6FB6D962C59B296E988561957A3C3D11F33ACF69F5768A2DA30
+462BA9AFE67D3C41A1E8368099361F50D4F6368963A48C75F1590804E7918A02EB9DA5F60F49828C
+95AC60E9D86DEB7699C35037669C02D408269D6BF481EC745E4EC630F68FF5168BE5E0BDF875EEC1
+5EB9CAA9CBED1CE2A0EB4CE97C14E10114087DCBE5DE1DB6A070CBC6B68EE141B7D62ED320F7B74C
+07D9762E0F5C8AF0779D38B7677ED65B5DD2F83ED06F041B5701228EFEAA0FFC031E44D6D68B274A
+45FF9EB62FDA007CDC98B86831E9A668098364B421E1FE2A45D85824612A521777C6177316F0C6DB
+D9847A2F61326E74A3114EAC3ABAEE456156DF125E4EDAD6BDB66E7DA6370C90ADC4AF4EC1958170
+C32D5AAF3B45BF749EE1DFAD8A5D0C03973B7C1589EAE4E67F6C87213C721E4A33466F3F54253545
+5014417CFFC9AF461CF695F5E1D7A37E4C7680A5624AA11EEA65C59BB38A96734E4A4A238C636638
+9EB8083D5AD753FA7F74EDF10054315EDD53DE61D48846F70AA9272626E23BA3DAB7CCEE819B0A88
+9CE775AA7F5205C95A19AAF475729D2C0BCAD9AB5459380A5F034C108A64B80621820D779582A198
+14058DFDCC6C2D6461AC6F98C64AABA53A2EF77CEF612EA766FE15498F56F54106D70D0F8DD4134C
+8C64B282E79A96F15D4BBE0297885ACF775BA5009006315637D60AD86D25A3CA6AC5BF5B4DB95B1A
+672A8C589D89101809D838236560F7690E329784C1D335203BFED3A258CD79467D16B6DA06A282BC
+4B9669986CB50C54DADE3D5B20FD5CD9FDDE60AF4B5D8D9C50E77CF50DF3B36F37F381FE7E28A719
+91C8199D18258C09BD0440F5E258A5D2E22C6CFBFDC5F91228992E86532F4C0A50067BCB2D4387B2
+9FEFB8043D7BC44A322FD3629D7D92B6EEBA21B035DAC884110BDC6B22CF59B08C195BBA1C682E5F
+8CF32F479937D329FE8EC6E9A3A3AA63AE95EEB766E0EACEEAD4DCF46617888AE687009112732A7A
+8FA8419A4652075EDDF83E291613B66BA793D3858CEA093C79B89B0C93E11CAAB86AD1980C401713
+0B6A02545DE484FDA6E4DFBCEBBC848751B2A8821CB3F7AC96D2EA9B83B575AB60CECA41EA567EE0
+8ADAC8F1060255057527485D6B12A26D2E7EF6892865FD65FEF28F8B46249C4F19341089D59DAF82
+582CAD1A3B53C763A0E57134A57C4A24AB4FD358592ADB01BE8F1D9FAE9CA97A3BC3A0E2939913E0
+62960CC1D29ECEF9E61BAC20E92F13C49078E7F1562C9D7C01C2B300ECD6ADCAC9AA0C1F1BA30401
+0573F79D158C66D91A8D987C5239C9D4FEED1DC5C9586C4129C33E7C737C624EE9BD1EDE38E9F72A
+78DC19E144B5AABF3B0FB72216815E371A5D6452684965CB7208B87CE5A27E444820E03F80AD2DF4
+E280D25D9BDE41E719BB2C939E25B7965DB162CA8665CBFE65DFDC7992A508D54FE4EE8454C11482
+7186A280D3D9EDF397640A809E3646675AB6621B9CDF42808E2F19859AC975CF70F41D2B3D22F8E0
+C180126F4A12122B207A150BC8E1819962DAEBB821DE59B7EEF8FF9219474597AF859E353B4B713F
+5F1350F4049DCE99627492E396038B10A2F8E82AB679F65BE14A166E4502921BA44CCFE04E5DD3FE
+B04BA47C3F7A3E7DBF7D55328AD1E96092C73EDB16C3325537295DF768E2633D01304EA0B01379AA
+DAFE6CCA2AB3E246768C5AC841A458A2F69141A9A9D716D129B26643F1BBC95D8CD0ED44148F7C6A
+F370201E454F4AFF7C11A8080508FEDE242343E7ADDD5A8AAF079C50765A79BD25FBBDC59EDF980F
+FE0B60435692C49D20AC22F8BD1FCA3F3D8200CA26D61A30212558B54671E63BC09776A3AE7A906F
+CA63281F080A00500C42C02B8F82357A3EBF572CFC4BEBA3FE86537DDDF23A861C240A31778A131E
+F638C1DBB7E87A45487C092D23FD9FB2C162C3253636ED263B141178FCA9F2D5BE2D81F99B2872C4
+B3C08749CE18C9C00E75DA6B3E4C6A6DD79EBA731618EF5B5E767BB40429D8FEEC6ABFE9975746FB
+3A82A03A07B7D0DA8BAECFD1F72AFE17EB0C0CF100A2D48F4449A42C482BB1D64C19F8FEA73F5BA1
+8FC9BFEE3F640C39C081669A9B0EE3FF6B2BDC0A726301EF0E2259E65BE59275C76E7A3F1C76C3A3
+3AD0E2F79198DCA39C482B4E1196238DBA937692CF319436830BBF441C0A8A04161DC0814B5DF049
+83099439EF421B6D37D351D4A988A8DFE93D796B2E8B2564C602F79E6162F56F933196A0DCE91051
+600BAFFD9D439A91F17C20CD6A9104B553D823A0317E3C8344E9F0B2560AD583BF876AE307892500
+2177AF61BDBD745CEC2B2A7931D27423D2667584AE0D6CAA2B281EB9271ADE86C493791AC9A2265B
+8324F764D20DE65F6267439F1574DB02C600E771D3C743D420FA6F8BF01A977B91AF035E0D5412AB
+0B85D6ED3ED9D0345ED434F9B95FD911A9D3828CD162686E6ECCD2B6A5B1104F0E6838AAC7FB3FC7
+09F08F8E82B4BFCB55D8984771F9F4D339EE7FF391448C7807A436A78B6A487D3A5A86F314B302FA
+C5BE1C1DB9844F975619D615D1C7A20EC2B144797E0648CF5C044C8DD1699EEA3AC9D7E3BAA54A85
+11A932623D5ED3F0A9C3028CA439AC395F58DEAF1C0354A169D9AC7F380900D9828C3ECBB975F6FE
+6BD79EA4BEA9B71F3A9B1D2EAF8F1E475B4FB99758192EBCDA21B47EA33F57C58E904DA260C801AE
+CF457C84592B81CFA96C10E448D705D24871F3D1AD1FE004406C8070A54FA3747788C5B55E9462E6
+F51378BF3F848360542CAD2D5FF9EDAC84C164DFEC115A2F3D873760EAE58CC8F361B37E6CE076EF
+325A1C54DFF84DB95DB7DB5B56C48ED15C5426E6153D8A94B1A35B22EAB1D7B871097DF12C093BA5
+538957AB7D0AB2E39A2D1AECB91F0A693B8F6A00601929B6C55AAE8A227CAB6404FEBCC8BC4EEEE0
+ADD4CFD7DEC225170A0063457918B9875AD7F022A8F9932ADE316E4126EAB75C1D0B2B9F44E85F1C
+54755D1301345665EA630C8E885A8D9AB069DEA2CC6D12E4A0BF6E80C3AE12BC7ABE507422A5D92B
+65F70B4A472DD945EBB960880EC4C34E4C206C260FDF86A997D7D399A0219A51E6F8BC1189EC0ED0
+B8686F0243BFAFD829979747442AB1DA8282F10F4C37C4E6C88D231460DA3BF34C23A4755E2F8F21
+F6D138C86B6091CB3BCE5ED170242C4CC87C010FE63DA9A2ECBDD85311DA8572AD67E40F744D2EC9
+6607701D9790574F051C859EF29291AFA301C809E0A5513DF9A7FC2DB1776DBD4A2CB622BD17811B
+C0E584A8D89D4BEB17C2D0AA8BF31CD7E2B36C0E1888409788E30EAC2E46AFA1CFE457D3A19C28C5
+80669CB82CF36BF40DFAFED78F99A5D5DF189CE293413FFBA9E8D2457986C68483242AD767D5F026
+1CE303153C35D5DEB1789448088B51C48A9B8E903BBC69ED7FB0129CCE33A0337198F11DBA94FDAE
+AD74BBC5E519F559094E15B03AF3EFA89DC38B4278C68D2AEEEBE4B28B351D7B72F52D94480D8CBC
+06544645F20800B8284A4E7C8FAD59D0AA8EC8EC0551912CBC62694A5DD15C469BBE614FB187C084
+838D676E5A1CC8DFA8676758EB7D53FD2BA8F1C434F4F70AED8F2CE27EED2F6A82E20E30EACF9BF0
+CE28B4BF7D556815A887D777B7D75F60D2C410AA2D378B7BFAA6C3A5BF0A5EF80D3E35A0CF393A5E
+BC27A895D712C6F004F1A03F9FCF493973F33E6FB106407DFD992AB0E9E36EF395663A6EEFFD71BB
+2DAD9DFE6AA4685F307B8FD69BB2F74E672666414A71FA479B37D3DEF6AEAF80FA14821C65F7ABBE
+5CC6DD49002216E23B3D22C282F3DE0077EA81017E79D8C3BDB61EC031CFB13FA981120A6C518F4B
+4C39C9DD5AC6EDA29ABA527C9948BC3162EF65E8251C9D4E3494C6A51A41D489F8BC89250128EBD7
+0EFD01B9F03C40B6E0384364F2571F7D969016A959E4243D7AB728F0F030B8E2FB456D8E47A85898
+313B0E91C68C7A8A7226603427AC6DCB5C69924FD026E90CB0EE89AC42D874BB15AFC981079E9DC7
+1271C3E461265AF327D4AAB4AEFAB6A9DC3F84DCE7C7868D4719006344B9055C8BA952A2250B8041
+E5EF519BB788201124BE90AECAC2293A310D9565E1EA4AEB99F7ED7106649169E8EBF3DCB210706A
+F2FA65158D3059DFBD7AB5B00DBB06B016F73FEDB3AE1C1CC03BC2D94B143BFBD759338EB181B1F6
+841F7B539D8D126FC5B31B244A3A00ABBB121A98F73314D1F1683F513C3CCC9A0C2180824ACE5D45
+E454062CE1217F7AFBDC60A6022B0134EDA6C5D787DE8A5925BEB374F64D612329B9AC17B6EC03F8
+56CFEE502C1FC3AAC7B6F251E8DE4147DCB1580185C406F38E6B729307EADFF6D7A0E824C9830093
+F0C4FECEEFC7FF839306FEFEB780FFBD8879F22AFDADCA4C0767B8EEAE5DB869D7BD91355F0255E1
+CAC68A81A276967F233624DE0D2082B89E2C2D1DB0F8BE0B2BD7051C4DE9B1564AA6CBA03E50386F
+D685943D1EDDF5BE0EB34418C48E5EA645CBA552A0444B254E82E9E84A5B7BD0064D5EA87B1716C1
+4EF7FD14E2187FCE16FF6ECBC0A6588B30B4329626F024BDBA65457028668BC6750A4DD668D017CC
+EF35CD9CF45EC73885FC91B8AD78920919B4747A710D25BB850B558C5CCF7170C20012DD0F2874A6
+699DF53EDC6C75716A5B6B06C98F4904BAD22B18C77B3EEF32DE3F45A8AC12383DA16DF6F06CD923
+D344CFC2253ECF97A52F2DC6A22539231E29A324F851265BA82BE566E42167247CEBD914EBE42EAB
+E189E74FE1FF17ADA8A5F47DC9866CF86694DEE28EED333B0A8AE380557820E3F1D32B05AE27FFE1
+68267FADD310C4EE550F71704EF71F4C669952F30E485AF37561884066C0E3425C748BBAC77CC7A9
+87EC336B8BEBA44358BEA4ECFEA32494A8006E9852C4BFCB1C0F00ACC247F1F8E3F3B34539C67638
+64527F9F885132DCACBFCB1DC80EE3B2A711145DDA1D3B5F83144E2CD698D43A02B0DEB706C78D75
+A24FA39B9374E1155969FAD7D5FB477F4C3C7440DBFE1BD1DC877360E8690A6D3A64EF0A1313A520
+C49320295302227CE5625E134EA442A537505D206EE46E7E218BDA38F7B6BA124ACA0E949725CEEB
+961EF8B0727E3A78287E61E65A84AC14D1014A213259A48F04C634C1F04C73B4110D0DB491250CCD
+7DAE323CC8BD29BEF605DD92AA5480009DEAD1256E5C9FFB18362D63B022C2A378D15ADE2C6AE603
+1DF385CF090226F0AF889464584A9EA63B884453DE231058C5661C4F7DCF7505D7B1E48C78AA5799
+8FF39084FFF1CD18CB2989D867F2E085B291FE52367B4E8667E5CDA4F41DDBC7129DA5BDC39549E0
+8B67D9C9E4C332D91FE261728521245CBF51870F24C42F5814F393719A9E126C5693C0E776E4D8F3
+0F0288537B8E9A7C5DA4F931FC83F96A1950B1DF26C0448B646D3BF42C0429E2BD0164703FC71833
+19DDEA75CE65D9DFFE8369787FE664306D843421C2C0C3A5B210E823F8366161A96CA88D95FD6486
+5F9E5B0D53DCEE4A98AC10A8FF1BC34A46DB6016E973C9FA298D3F58228E3D9FCCCF9632556CE2E7
+F9C81E05FDB0FC1071E69A4C158E742D0B8939CA3DE33A4BE8D01D679C81C6BA9CF396A6768CA626
+05D796AA5C323E3A6208F2DA5000FC4462ED81AD0044D9959E40E4764562ADA76E33EC63271966F1
+7A13AD376A54FFA40FA247896A22C1C4A4CE51094CA1892A0B9904EB762839AAE1EDB5A0A5A69343
+CCA45F88F61926B23E6C85688862B1D10F1ED827FCCE2A427E49D960DBEE0D39EDE08D2565C6F5AD
+66CA83DAC1467C38F742132F715DA7E9757A5D067FDB4A8859D148473F302BF6ECB9B7EA5C0DF851
+8F58D4D076625B92BC8201028064950D70CD40492DA83B98FFD79BD3DCD9CEE95178B6DEA9AB10B3
+4C61A74B49C3697CF714F56A9B1E366B04500C9A67704AF6F721851EA4566B96E220E7B526873F94
+E6013EA8C489639D291167BCAEFB661C96C27DC7BCD5B317C1F101CD8DF2FB29F6162B5ECE903C0D
+5570C8C6325F05218850E4A5B5F94C7CE334BADDFC5DB98EF5333B4A92129F41D046D0F5EB6F342C
+F362BC8E45511FCF328E2CE2D4C579516307AEC400BA207071C887F5185AF536A6827307D7698095
+E8A6F8C19812A11C32AD207FBC353172C8DF626C5C05CD24C171B3C8DAB270A74AC48E35F0A8849C
+E0934B93D56C93599F5235EC441B5232257C4C6767C7B5A35830010E60E56AFBE9199B42E725A216
+C49D45A3C554767DF192F04D1A192D5CE196831EB7A56201A4A96CD44DD084729774334F698E1902
+614405347A37A4C753C0211D7F24D5265CF033E3C4D30E61886CF781D2AC30385E6021A892AB116D
+1F5505635BF5C915D0300ACA8AC32CAA0F3639550C4AEB390DA7B86E53B6BB0E4819626757BA9986
+BCA4B4B18F6BF3706577A3BE146F2585025D5A36EB14E587C5469EEEB622D31FD32EBFA812B9CCDC
+CFF7D17D00D9F21BF8871661F20E89A25A4543068EC509D0DF7DEA2A24FF68957943BE0FC7BD319D
+92A575E12E6FB8112D72A100FEF3EE85972304BBE07FC1026CCC068E8A414F641116E51F76009C5A
+F9E664BCCF93C943CFF2115235BB3290F935AA3FAB4F2E73FCDB9FA5E1866EFE278FFE7EA9DD6F89
+38CD5E5F4F31F6C659AF7EFF5B291AFB86521678B42C99275024EDAD04E5929D201A36F4A6C4DCD7
+2E349D6B9A1F90B123DEDE3DA50EDF929F9285747CD504C8F4A73D522F312D623BD65A35419C0729
+30976129B2D99B32D1DEDD81D64F7879CEDDFF04BBBF1286E07BCE651E2DEC190DFB9D25F3C1EEC8
+D7277752BB7CCC313A0FEEBF0EFFA2A189861AD4EA13AD4FDBD5160DC273F12525815F0D693C1413
+AFE4FBE09BCBD71E16C33E6CF8D18732447FFDCC2E4AF9E270D725C1D262D96E72538CA3309BCAF2
+35A3FAFAB27D265C583E6F6F062B555C97DFD89AC46E6154670ADCA13FD99F7A4B9B8BB224D43764
+8CC9DB76E177F562862C912D8CBC248A4C628FF2D0C9688611CED3DFB89064B988F16655633B7CC7
+AFAE34B2937528EA0814ED245D6B0D2AEF87A5B63E2BD7E4F7D9CFE372C295A0891F97C3855C97EC
+C03C7C2A231704BED419612255F8B2C9262D48CB4FB46D63CC9697A210FA9D7BFC3BAB7E46172D3F
+52A4AF9C4114BC72A5C7CBA042FA633AFD5E404F29408D4485837E55E6F9702F04C7AB410C351039
+6A78C8672F8EAD53BB9CBCED63FB9E72E7238CC88FB4E7C48C3DE3E4B80E277B952727916A4127A2
+5CC1413C390F4DAFD5253B07BC96DAE8CF4DA08330DEE580CBC1E12B75A661819E96B018D47A8B71
+B6BFAFC5E3CBFE68E5193417B6E730E6A2820838D22049BE6BB64B74AA13779D46519965ED80D5FC
+30B0A6F73D26DEBD8150B3D27B3F135F608D59BB1632AD9C2E11177FAF54CBD1C4E58D58C395BAE8
+AD7C7AEEB0E1C3F0C0B7A5E7142E9A1DCCC8B4EF56C319D4A4F750857D2FD180F871772B9CC69FB9
+B222F83C3ACCB66125AF6848B2EFDCE3D2284FA5844641FB32F701FBF32F1D2F2E2233B66E36CCD1
+49FCB3FCDB6EA04367D11624717D73D9128EA7D9AABB8658BE9E9986E532
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+cleartomark{restore}if
+
+%%EndFont
+%%EndResource
+11.52 /Mathematica1 Msf
+0.75 10.5 m
+(p) N
+P
+[1 0 0 1 -61.69 -151.566 ]  concat
+1 w
+[ ] 0 setdash
+P
+P
+[ ] 0 setdash
+p
+0 setlinecap
+70.6 105.842 m
+73.497 105.842 L
+s
+P
+p
+np 52 98 m
+52 113 L
+69 113 L
+69 98 L
+cp
+clip np
+p
+1 w
+0 g
+0 g
+[ ] 0 setdash
+2 setlinecap
+0 setlinejoin
+10 setmiterlimit
+[1 0 0 1 53.44 99.467 ]  concat
+1 w
+[ ] 0 setdash
+p
+np -2.44 -2.467 m
+-2.44 14.533 L
+16.56 14.533 L
+16.56 -2.467 L
+cp
+clip np
+%%IncludeResource: font Times-Roman-MISO
+%%IncludeFont: Times-Roman-MISO
+11.52 /Times-Roman-MISO Msf
+0 10.5 m
+(2) N
+11.52 /Mathematica1 Msf
+9 10.5 m
+(p) N
+P
+[1 0 0 1 -53.44 -99.467 ]  concat
+1 w
+[ ] 0 setdash
+P
+P
+[ ] 0 setdash
+p
+0 setlinecap
+70.6 53.742 m
+73.497 53.742 L
+s
+P
+p
+np 52 46 m
+52 61 L
+69 61 L
+69 46 L
+cp
+clip np
+p
+1 w
+0 g
+0 g
+[ ] 0 setdash
+2 setlinecap
+0 setlinejoin
+10 setmiterlimit
+[1 0 0 1 53.44 47.367 ]  concat
+1 w
+[ ] 0 setdash
+p
+np -2.44 -2.367 m
+-2.44 14.633 L
+16.56 14.633 L
+16.56 -2.367 L
+cp
+clip np
+%%IncludeResource: font Times-Roman-MISO
+%%IncludeFont: Times-Roman-MISO
+11.52 /Times-Roman-MISO Msf
+0 10.5 m
+(3) N
+11.52 /Mathematica1 Msf
+9 10.5 m
+(p) N
+P
+[1 0 0 1 -53.44 -47.367 ]  concat
+1 w
+[ ] 0 setdash
+P
+P
+1 g
+[ ] 0 setdash
+p
+0 setlinecap
+78.19 2.952 m
+78.19 5.849 L
+s
+P
+p
+0 setlinecap
+92.764 2.952 m
+92.764 4.69 L
+s
+P
+p
+0 setlinecap
+107.338 2.952 m
+107.338 4.69 L
+s
+P
+p
+0 setlinecap
+121.911 2.952 m
+121.911 4.69 L
+s
+P
+p
+0 setlinecap
+136.485 2.952 m
+136.485 4.69 L
+s
+P
+p
+0 setlinecap
+151.058 2.952 m
+151.058 5.849 L
+s
+P
+p
+0 setlinecap
+165.632 2.952 m
+165.632 4.69 L
+s
+P
+p
+0 setlinecap
+180.205 2.952 m
+180.205 4.69 L
+s
+P
+p
+0 setlinecap
+194.779 2.952 m
+194.779 4.69 L
+s
+P
+p
+0 setlinecap
+209.353 2.952 m
+209.353 4.69 L
+s
+P
+p
+0 setlinecap
+223.926 2.952 m
+223.926 5.849 L
+s
+P
+p
+0 setlinecap
+238.5 2.952 m
+238.5 4.69 L
+s
+P
+p
+0 setlinecap
+253.073 2.952 m
+253.073 4.69 L
+s
+P
+p
+0 setlinecap
+267.647 2.952 m
+267.647 4.69 L
+s
+P
+p
+0 setlinecap
+282.22 2.952 m
+282.22 4.69 L
+s
+P
+p
+0 setlinecap
+296.794 2.952 m
+296.794 5.849 L
+s
+P
+p
+0 setlinecap
+311.367 2.952 m
+311.367 4.69 L
+s
+P
+p
+0 setlinecap
+325.941 2.952 m
+325.941 4.69 L
+s
+P
+p
+0 setlinecap
+340.515 2.952 m
+340.515 4.69 L
+s
+P
+p
+0 setlinecap
+355.088 2.952 m
+355.088 4.69 L
+s
+P
+p
+0 setlinecap
+369.662 2.952 m
+369.662 5.849 L
+s
+P
+p
+0 setlinecap
+384.235 2.952 m
+384.235 4.69 L
+s
+P
+p
+0 setlinecap
+398.809 2.952 m
+398.809 4.69 L
+s
+P
+p
+0 setlinecap
+413.382 2.952 m
+413.382 4.69 L
+s
+P
+p
+0 setlinecap
+427.956 2.952 m
+427.956 4.69 L
+s
+P
+p
+0 setlinecap
+442.53 2.952 m
+442.53 5.849 L
+s
+P
+p
+0 setlinecap
+450.12 210.041 m
+447.223 210.041 L
+s
+P
+p
+np 451 203 m
+451 217 L
+459 217 L
+459 203 L
+cp
+clip np
+p
+1 w
+1 g
+0 g
+[ ] 0 setdash
+2 setlinecap
+0 setlinejoin
+10 setmiterlimit
+[1 0 0 1 452.28 203.666 ]  concat
+1 w
+[ ] 0 setdash
+p
+np -2.28 -1.666 m
+-2.28 14.334 L
+7.72 14.334 L
+7.72 -1.666 L
+cp
+clip np
+%%IncludeResource: font Times-Roman-MISO
+%%IncludeFont: Times-Roman-MISO
+11.52 /Times-Roman-MISO Msf
+1 g
+0 10.5 m
+(0) N
+P
+[1 0 0 1 -452.28 -203.666 ]  concat
+1 w
+[ ] 0 setdash
+P
+P
+[ ] 0 setdash
+p
+0 setlinecap
+450.12 157.941 m
+447.223 157.941 L
+s
+P
+p
+np 451 151 m
+451 165 L
+460 165 L
+460 151 L
+cp
+clip np
+p
+1 w
+1 g
+0 g
+[ ] 0 setdash
+2 setlinecap
+0 setlinejoin
+10 setmiterlimit
+[1 0 0 1 452.28 151.566 ]  concat
+1 w
+[ ] 0 setdash
+p
+np -2.28 -1.566 m
+-2.28 14.434 L
+8.72 14.434 L
+8.72 -1.566 L
+cp
+clip np
+11.52 /Mathematica1 Msf
+1 g
+0.75 10.5 m
+(p) N
+P
+[1 0 0 1 -452.28 -151.566 ]  concat
+1 w
+[ ] 0 setdash
+P
+P
+[ ] 0 setdash
+p
+0 setlinecap
+450.12 105.842 m
+447.223 105.842 L
+s
+P
+p
+np 451 98 m
+451 113 L
+468 113 L
+468 98 L
+cp
+clip np
+p
+1 w
+1 g
+0 g
+[ ] 0 setdash
+2 setlinecap
+0 setlinejoin
+10 setmiterlimit
+[1 0 0 1 452.28 99.467 ]  concat
+1 w
+[ ] 0 setdash
+p
+np -2.28 -2.467 m
+-2.28 14.533 L
+16.72 14.533 L
+16.72 -2.467 L
+cp
+clip np
+%%IncludeResource: font Times-Roman-MISO
+%%IncludeFont: Times-Roman-MISO
+11.52 /Times-Roman-MISO Msf
+1 g
+0 10.5 m
+(2) N
+11.52 /Mathematica1 Msf
+9 10.5 m
+(p) N
+P
+[1 0 0 1 -452.28 -99.467 ]  concat
+1 w
+[ ] 0 setdash
+P
+P
+[ ] 0 setdash
+p
+0 setlinecap
+450.12 53.742 m
+447.223 53.742 L
+s
+P
+p
+np 451 46 m
+451 61 L
+468 61 L
+468 46 L
+cp
+clip np
+p
+1 w
+1 g
+0 g
+[ ] 0 setdash
+2 setlinecap
+0 setlinejoin
+10 setmiterlimit
+[1 0 0 1 452.28 47.367 ]  concat
+1 w
+[ ] 0 setdash
+p
+np -2.28 -2.367 m
+-2.28 14.633 L
+16.72 14.633 L
+16.72 -2.367 L
+cp
+clip np
+%%IncludeResource: font Times-Roman-MISO
+%%IncludeFont: Times-Roman-MISO
+11.52 /Times-Roman-MISO Msf
+1 g
+0 10.5 m
+(3) N
+11.52 /Mathematica1 Msf
+9 10.5 m
+(p) N
+P
+[1 0 0 1 -452.28 -47.367 ]  concat
+1 w
+[ ] 0 setdash
+P
+P
+p
+np 210 257 m
+210 272 L
+310 272 L
+310 257 L
+cp
+clip np
+p
+1 w
+1 g
+0 g
+[ ] 0 setdash
+2 setlinecap
+0 setlinejoin
+10 setmiterlimit
+[1 0 0 1 211.235 257.578 ]  concat
+1 w
+[ ] 0 setdash
+p
+np -2.235 -1.578 m
+-2.235 15.422 L
+99.765 15.422 L
+99.765 -1.578 L
+cp
+clip np
+%%IncludeResource: font Times-Roman-MISO
+%%IncludeFont: Times-Roman-MISO
+10.08 /Times-Roman-MISO Msf
+p
+0 9 m
+(I) N
+P
+p
+3.75 9 m
+(m) N
+P
+p
+12.75 9 m
+(p) N
+P
+p
+19.5 9 m
+(a) N
+P
+p
+24.75 9 m
+(c) N
+P
+p
+30 9 m
+(t) N
+P
+p
+36.75 9 m
+(p) N
+P
+p
+43.5 9 m
+(a) N
+P
+p
+48.75 9 m
+(r) N
+P
+p
+53.25 9 m
+(a) N
+P
+p
+58.5 9 m
+(m) N
+P
+p
+67.5 9 m
+(e) N
+P
+p
+72.75 9 m
+(t) N
+P
+p
+76.5 9 m
+(e) N
+P
+p
+81.75 9 m
+(r) N
+P
+86.25 9 m
+(,) N
+92.25 9 m
+(b) N
+P
+[1 0 0 1 -211.235 -257.578 ]  concat
+1 w
+[ ] 0 setdash
+P
+P
+p
+np 34 75 m
+34 165 L
+49 165 L
+49 75 L
+cp
+clip np
+p
+1 w
+1 g
+0 g
+[ ] 0 setdash
+2 setlinecap
+0 setlinejoin
+10 setmiterlimit
+[0 -1 1 0 35.28 164.105 ]  concat
+1 w
+[ ] 0 setdash
+p
+np -1.895 -2.28 m
+-1.895 14.72 L
+90.105 14.72 L
+90.105 -2.28 L
+cp
+clip np
+%%IncludeResource: font Times-Roman-MISO
+%%IncludeFont: Times-Roman-MISO
+10.08 /Times-Roman-MISO Msf
+p
+0 9 m
+(S) N
+P
+p
+6 9 m
+(p) N
+P
+p
+12.75 9 m
+(a) N
+P
+p
+18 9 m
+(t) N
+P
+p
+21.75 9 m
+(i) N
+P
+p
+24.75 9 m
+(a) N
+P
+p
+30 9 m
+(l) N
+P
+p
+36 9 m
+(r) N
+P
+p
+40.5 9 m
+(o) N
+P
+p
+46.5 9 m
+(t) N
+P
+p
+50.25 9 m
+(a) N
+P
+p
+55.5 9 m
+(t) N
+P
+p
+59.25 9 m
+(i) N
+P
+p
+62.25 9 m
+(o) N
+P
+p
+68.25 9 m
+(n) N
+P
+75 9 m
+(,) N
+10.08 /Mathematica1 Msf
+81.75 9 m
+(c) N
+%%IncludeResource: font Times-Roman-MISO
+%%IncludeFont: Times-Roman-MISO
+10.08 /Times-Roman-MISO Msf
+P
+[0 1 -1 0 164.105 -35.28 ]  concat
+1 w
+[ ] 0 setdash
+P
+P
+p
+np 70 3 m
+70 238 L
+450 238 L
+450 3 L
+cp
+clip np
+p
+np 117 136 m
+117 152 L
+155 152 L
+155 136 L
+cp
+clip np
+p
+1 w
+1 g
+0 g
+[ ] 0 setdash
+2 setlinecap
+0 setlinejoin
+10 setmiterlimit
+[1 0 0 1 118.485 136.705 ]  concat
+1 w
+[ ] 0 setdash
+p
+np -2.485 -1.705 m
+-2.485 16.295 L
+37.515 16.295 L
+37.515 -1.705 L
+cp
+clip np
+p
+np -0.485 0.295 m
+-0.485 13.295 L
+35.515 13.295 L
+35.515 0.295 L
+cp
+clip np
+[1 0 0 1 0 0 ]  concat
+1 w
+[ ] 0 setdash
+p
+np -2.485 -1.705 m
+-2.485 15.295 L
+37.515 15.295 L
+37.515 -1.705 L
+cp
+clip np
+%%IncludeResource: font Times-Roman-MISO
+%%IncludeFont: Times-Roman-MISO
+9.72 /Times-Roman-MISO Msf
+%%BeginResource: font Mathematica2
+%%BeginFont: Mathematica2
+%!PS-AdobeFont-1.0: Mathematica2 001.000
+%%CreationDate: 8/28/01 at 12:01 AM
+%%VMusage: 1024 29061  
+% Mathematica typeface design by Andre Kuzniarek. Copyright \(c\) 1996-2001 Wolfram Research, Inc. [http://www.wolfram.com]. All rights reserved. [Font version 2.00]
+% ADL: 800 200 0
+%%EndComments
+FontDirectory/Mathematica2 known{/Mathematica2 findfont dup/UniqueID known{dup
+/UniqueID get 5095653 eq exch/FontType get 1 eq and}{pop false}ifelse
+{save true}{false}ifelse}{false}ifelse
+20 dict begin
+/FontInfo 16 dict dup begin
+  /version (001.000) readonly def
+  /FullName (Mathematica2) readonly def
+  /FamilyName (Mathematica2) readonly def
+  /Weight (Medium) readonly def
+  /ItalicAngle 0 def
+  /isFixedPitch false def
+  /UnderlinePosition -133 def
+  /UnderlineThickness 20 def
+  /Notice (Mathematica typeface design by Andre Kuzniarek. Copyright \(c\) 1996-2001 Wolfram Research, Inc. [http://www.wolfram.com]. All rights reserved. [Font version 2.00]) readonly def
+  /em 1000 def
+  /ascent 800 def
+  /descent 200 def
+end readonly def
+/FontName /Mathematica2 def
+/Encoding 256 array
+dup 0/NUL put
+dup 1/Eth put
+dup 2/eth put
+dup 3/Lslash put
+dup 4/lslash put
+dup 5/Scaron put
+dup 6/scaron put
+dup 7/Yacute put
+dup 8/yacute put
+dup 9/HT put
+dup 10/LF put
+dup 11/Thorn put
+dup 12/thorn put
+dup 13/CR put
+dup 14/Zcaron put
+dup 15/zcaron put
+dup 16/DLE put
+dup 17/DC1 put
+dup 18/DC2 put
+dup 19/DC3 put
+dup 20/DC4 put
+dup 21/onehalf put
+dup 22/onequarter put
+dup 23/onesuperior put
+dup 24/threequarters put
+dup 25/threesuperior put
+dup 26/twosuperior put
+dup 27/brokenbar put
+dup 28/minus put
+dup 29/multiply put
+dup 30/RS put
+dup 31/US put
+dup 32/Space put
+dup 33/Radical1Extens put
+dup 34/Radical2 put
+dup 35/Radical2Extens put
+dup 36/Radical3 put
+dup 37/Radical3Extens put
+dup 38/Radical4 put
+dup 39/Radical4Extens put
+dup 40/Radical5 put
+dup 41/Radical5VertExtens put
+dup 42/Radical5Top put
+dup 43/Radical5Extens put
+dup 44/FixedFreeRadical1 put
+dup 45/FixedFreeRadical2 put
+dup 46/FixedFreeRadical3 put
+dup 47/FixedFreeRadical4 put
+dup 48/TexRad1 put
+dup 49/TexRad2 put
+dup 50/TexRad3 put
+dup 51/TexRad4 put
+dup 52/TexRad5 put
+dup 53/TexRad5VertExt put
+dup 54/TexRad5Top put
+dup 55/TexRadExtens put
+dup 56/LBrace1 put
+dup 57/LBrace2 put
+dup 58/LBrace3 put
+dup 59/LBrace4 put
+dup 60/RBrace1 put
+dup 61/RBrace2 put
+dup 62/RBrace3 put
+dup 63/RBrace4 put
+dup 64/LBracket1 put
+dup 65/LBracket2 put
+dup 66/LBracket3 put
+dup 67/LBracket4 put
+dup 68/RBracket1 put
+dup 69/RBracket2 put
+dup 70/RBracket3 put
+dup 71/RBracket4 put
+dup 72/LParen1 put
+dup 73/LParen2 put
+dup 74/LParen3 put
+dup 75/LParen4 put
+dup 76/RParen1 put
+dup 77/RParen2 put
+dup 78/RParen3 put
+dup 79/RParen4 put
+dup 80/DblLBracket1 put
+dup 81/DblLBracket2 put
+dup 82/DblLBracket3 put
+dup 83/DblLBracket4 put
+dup 84/DblRBracket1 put
+dup 85/DblRBracket2 put
+dup 86/DblRBracket3 put
+dup 87/DblRBracket4 put
+dup 88/LAngleBracket1 put
+dup 89/LAngleBracket2 put
+dup 90/LAngleBracket3 put
+dup 91/LAngleBracket4 put
+dup 92/RAngleBracket1 put
+dup 93/RAngleBracket2 put
+dup 94/RAngleBracket3 put
+dup 95/RAngleBracket4 put
+dup 96/LCeiling1 put
+dup 97/LCeiling2 put
+dup 98/LCeiling3 put
+dup 99/LCeiling4 put
+dup 100/LFloor1 put
+dup 101/LFloor2 put
+dup 102/LFloor3 put
+dup 103/LFloor4 put
+dup 104/LFlrClngExtens put
+dup 105/LParenTop put
+dup 106/LParenExtens put
+dup 107/LParenBottom put
+dup 108/LBraceTop put
+dup 109/LBraceMiddle put
+dup 110/LBraceBottom put
+dup 111/BraceExtens put
+dup 112/RCeiling1 put
+dup 113/RCeiling2 put
+dup 114/RCeiling3 put
+dup 115/RCeiling4 put
+dup 116/RFloor1 put
+dup 117/RFloor2 put
+dup 118/RFloor3 put
+dup 119/RFloor4 put
+dup 120/RFlrClngExtens put
+dup 121/RParenTop put
+dup 122/RParenExtens put
+dup 123/RParenBottom put
+dup 124/RBraceTop put
+dup 125/RBraceMiddle put
+dup 126/RBraceBottom put
+dup 127/DEL put
+dup 128/LBracketTop put
+dup 129/LBracketExtens put
+dup 130/LBracketBottom put
+dup 131/RBracketTop put
+dup 132/RBracketExtens put
+dup 133/RBracketBottom put
+dup 134/DblLBracketBottom put
+dup 135/DblLBracketExtens put
+dup 136/DblLBracketTop put
+dup 137/DblRBracketBottom put
+dup 138/DblRBracketExtens put
+dup 139/DblRBracketTop put
+dup 140/LeftHook put
+dup 141/HookExt put
+dup 142/RightHook put
+dup 143/Radical1 put
+dup 144/Slash1 put
+dup 145/Slash2 put
+dup 146/Slash3 put
+dup 147/Slash4 put
+dup 148/BackSlash1 put
+dup 149/BackSlash2 put
+dup 150/BackSlash3 put
+dup 151/BackSlash4 put
+dup 152/ContourIntegral put
+dup 153/DblContInteg put
+dup 154/CntrClckwContInteg put
+dup 155/ClckwContInteg put
+dup 156/SquareContInteg put
+dup 157/UnionPlus put
+dup 158/SquareIntersection put
+dup 159/SquareUnion put
+dup 160/LBracketBar1 put
+dup 161/LBracketBar2 put
+dup 162/LBracketBar3 put
+dup 163/LBracketBar4 put
+dup 164/RBracketBar1 put
+dup 165/RBracketBar2 put
+dup 166/RBracketBar3 put
+dup 167/RBracketBar4 put
+dup 168/ContourIntegral2 put
+dup 169/DblContInteg2 put
+dup 170/CntrClckwContInteg2 put
+dup 171/ClckwContInteg2 put
+dup 172/SquareContInteg2 put
+dup 173/UnionPlus2 put
+dup 174/SquareIntersection2 put
+dup 175/SquareUnion2 put
+dup 176/DblLBracketBar1 put
+dup 177/DblLBracketBar2 put
+dup 178/DblLBracketBar3 put
+dup 179/DblLBracketBar4 put
+dup 180/DblRBracketBar1 put
+dup 181/DblRBracketBar2 put
+dup 182/DblRBracketBar3 put
+dup 183/DblRBracketBar4 put
+dup 184/ContourIntegral3 put
+dup 185/DblContInteg3 put
+dup 186/CntrClckwContInteg3 put
+dup 187/ClckwContInteg3 put
+dup 188/SquareContInteg3 put
+dup 189/UnionPlus3 put
+dup 190/SquareIntersection3 put
+dup 191/SquareUnion3 put
+dup 192/DblBar1 put
+dup 193/DblBar2 put
+dup 194/DblBar3 put
+dup 195/DblBar4 put
+dup 196/BarExt put
+dup 197/DblBarExt put
+dup 198/OverCircle put
+dup 199/Hacek put
+dup 200/VertBar1 put
+dup 201/VertBar2 put
+dup 202/Nbspace put
+dup 203/VertBar3 put
+dup 204/VertBar4 put
+dup 205/FIntegral put
+dup 206/FIntegral2 put
+dup 207/FIntegral3 put
+dup 208/OverDoubleDot put
+dup 209/OverTripleDot put
+dup 210/OverLVector put
+dup 211/OverRVector put
+dup 212/OverLRVector put
+dup 213/OverLArrow put
+dup 214/OverArrowVectExt put
+dup 215/OverRArrow put
+dup 216/OverLRArrow put
+dup 217/Integral put
+dup 218/Summation put
+dup 219/Product put
+dup 220/Intersection put
+dup 221/Union put
+dup 222/LogicalOr put
+dup 223/LogicalAnd put
+dup 224/Integral1 put
+dup 225/Integral2 put
+dup 226/Sum1 put
+dup 227/Sum2 put
+dup 228/Product1 put
+dup 229/Product2 put
+dup 230/Union1 put
+dup 231/Union2 put
+dup 232/Intersect1 put
+dup 233/Intersect2 put
+dup 234/Or1 put
+dup 235/Or2 put
+dup 236/And1 put
+dup 237/And2 put
+dup 238/SmallVee put
+dup 239/SmallWedge put
+dup 240/DoubleGrave put
+dup 241/Breve put
+dup 242/DownBreve put
+dup 243/OverTilde put
+dup 244/Tilde2 put
+dup 245/Tilde3 put
+dup 246/Tilde4 put
+dup 247/BackQuote put
+dup 248/DblBackQuote put
+dup 249/Quote put
+dup 250/DblQuote put
+dup 251/VertBar put
+dup 252/DblVertBar put
+dup 253/VertBarExten put
+dup 254/DblVertBarExten put
+dup 255/Coproduct put
+ readonly def
+/PaintType 0 def
+/FontType 1 def
+/StrokeWidth 0 def
+/FontMatrix[0.001 0 0 0.001 0 0]readonly def
+/UniqueID 5095653 def
+/FontBBox{-13 -4075 2499 2436}readonly def
+currentdict end
+currentfile eexec
+D8061D93A8246509E76A3EC656E953B7C22E43117F5A3BC2421790057C314DAE3EFBFF49F45DD7CD
+91B890E4155C4895C5126A36B01A58FDB2004471266DA05A0931953736AD8B3DEB3BCB2A24BC816A
+C1C90A1577C96B9096D6F51F9E21E625ADF6C3A49867A632A605C117E6325C820121799F412E226B
+EFE61F2813676F172CBD7EC10FF1EFBB92DF3A88E9378921BBD00E6024CC08EF057CECD09B824E0A
+CCDAA4644296DE34D19D779A21C30666026829D38FB35A2284CAED23C8B913E7B28BB3DA7C8CE390
+4C0BAE30B0287680CCCAB2E6D4CAB2E7D786ABF54068028FD7D94FAC236094761B62B7E76F68D2BE
+58C23AF85001950EFC1A9C1BB71520B78DDF6AA0058D25D041E86D01878DF56A5C48D74DCB2BBD68
+D75C94A3CE878484D28049331CE3D4364B40FAA2C754E8F443D244C5BC44B1C7868E36EAF4F7EF1F
+6CB81E6CF63FABD65C29A991EB7D724DA06535AE43F3D0E2D04F6113B493C15463B4CEBFB72AB879
+7E4645F9CC0BB17E02A9626BEA4B4259F798B53B18DF2ACCF2B86BF2209CF0265DE0A46869333F98
+CCF70BF2C9239A0ABD2E97923AA5695BAFEA31E27B8F532BAA45F2980A11D069265A5312A260A627
+A765C4A08897E5C500990AE6FDA4CD6D575905468417297380EB6400CB2CF001C4B8EB79811CD8D7
+C173A337922B99DAB1048D5D03C78F78F36FEE31673D8C5FF8AD689A63AEA055CA705DB47D3AF965
+73571985E62F63866018C96EC4CA7E735C9294D8C81C03806D23CB87C0C08F86F5FA68CFC9AE48F6
+958AE016DCE4D60EB64AEAAD59D8A2592BC398BCA479FBC2F0C20C3E7F730481494C88781A6A9E0E
+4F47A94619A3841FAC76A4FB252EB6FB43628AE1A4944539B1DFF672940AA5E93FFACFAC04624EF6
+7ED9C691788F0004DB7FFD1C995F2C52C0042F02F5C789F85D9E51716F3B4EDB21D4B9E660E4A892
+B747201EEC6DD6A8881FA3039664061094D1892108A2AD068D7F0251BFA72D874ECB2F42D27CC343
+052156F6A3A66D2CA6DAEF046A433FD54FEB4410315690971D0F43363EC0119B21F4BE3DFDC8C28D
+BF5D68F145D3AC932EE62C32DFDEB1C48C2455392193268C893093BF911645986607DD13D8285895
+1854A7FF81FC98ADD44742907818B3C8E3187371BD9FE6EF5B4315E82C359BF2EA91D2894EE7FD9A
+734BF2745D7FE8D08D29DA2A03176C992E11F1AADCE219D5E33F325BCFF4521D6D04E61B913B6C41
+740AF8BD9EA83F3AE7C4E5402366A812B1724431EE78D2028317B975E91941B591CC6C97740C453C
+C7D7AB3CE97AE2F4DFAB6B9C8810A52A276BEAABD687BDA0971EE623A40B7AD6A8E69ED8BE63959D
+3DCF8799E2505AC7F7B0E2FFECB0B08027A6266B7F96311B0AD78B7B6C78392AA062A73FDF179FEC
+7F748929D060289F899FE417D4028FF332204FE04146BB130EF05FB57AF4BF9337EF71094DC5922E
+3EF2D6A9F8257AF242019C349B65C2A3972ACA842D14EAB6A8D287456C9D7D295198F4AB7632EE43
+7D006B8124A80BBF26C1B902379D7F90080B51F982630E27B6031CE20930C273EA5D7FF0FC7E996E
+67B072796CD96A7739444D21FE10DE7B57D143E4155EEE1B89F32EBCBF4C64D6D3FA1C46E06C9E9F
+F99BC9BBCC61A400C7DBF812C42AAED863FE9EE3B02E731D1809F3CAB941252DE486BFE641F60F60
+C788F68519A6B04A4D6F50F02F93C6B8774B960C1FE893373D2AC2D865C487CFFE7669E1F1E73630
+7D34C15835F0453C8B0C1AE352CE1F27065F1082E6C84F86330F76B39C246315D6996AB69F81A020
+30D92CCB4C2A5AA4F4B3CAC88B2C8C9C621294A5EAB6AC3778DB99BD2F411735DC1421861417B4FD
+399B365AEA45185A3D9D687546E36BB73994FB7FA3EE890AE3734BD9381B0E7AE514E8C517B87268
+7364C38D0A8379039F33336799205F2F54BBF7C2E9B30B27BCFB9FF2CD64F5D700F2455EE66B6252
+6E79ED2B0E5FF9732281CA50D27A93F259B6D4B5C7F856BB7D4F2E0F7741FA2419BBAF86C372E34D
+59BC7AABC4CEF4F47EE40E118AB95A4863E16C55824D34002D59844D1F51B5DC6FB6BB5D595C4404
+1E05A84FD453A129279F894D726F6CD53BA3E234518324C5F715DAE6E7B5695326FC0C9B6CA2B53D
+B25EC76BE9662388058629E70DC7BD861E188F1FF32A754A455195163CB4754D116D24E6A6300B67
+1862625E260442DEA2505E36D5F7AA4AD1FEB3B42632E382959C7E84569B2A790A0D0A208C2D4816
+AD28046B42C7A4797D424277AD9425C04DB87DCF112AE431CFFF6E4FFA979E947502AE5E1755C112
+0AE2361888B956F3F513A5975680E6E8374D8BF26C32AADC826D729E9026B14A68BC3D065E11C697
+D4D474CF963AFE083DD7D9278A0C27447E25AD70DD40B2EBAB8040164E11CD75AE3125C29806DEF4
+AD1B989F7001E227685DEF6EBE3287DE43BBA5FE2123A0EC835AECF879C13F7CFDC409901F291E89
+06C26E42B189862AFAE029F03D86D35E44E318FE16537E2B73E4F39F1E6945A3A6432438DCB6D2B2
+09814B5228D08165568617C279499ECA1B88C90300F1C88C45D4BE3DC726A59E24B46C5B2FF228C6
+E6645819C6F1D5B05737BE7353F4787EE52A21DC47A44F3C44406E79BBFDDC164682B02F4C39300D
+12EF37A58E317FC1B8CE58E04BE666ED5DA75DBF752BEDDA4C7491E4D6922BCCA9CF421CE6751002
+8638EF643119841F423626D6B19A5D2CFB193B093D7646913F64614C098E5F5FF9422EBA75FA9AA8
+4F8ED114AEAB011E6F0727FB96F26BECBBAFE3AA8D0ABC5A8E9906B6CBB9E03F8CC4FCA97C884B83
+7CC33C619CD3195C55633B72D3F2D45561CD226F42B859B8099812D591886FA851107A185169FA7C
+944248DE28642FA3043FF3B60236BFD781257C6FE4D56174AD16ABBF9659C05F08673A70496A0787
+C187D4367CB0CF48BD9A4FE0E481273E4909A1092626A13917DCBDE920028B06E094F5B28887B990
+32521E1720B75EB284AA6FFE53FA5CD5B903F951FCF7B33CC981FE7BCC4BDF4907ACC3AA60B69969
+A9AF204C84EC8C0F5DCB8A85E39EA9F2D4B67095A44CA0C8B072D7A61F3015D502B1A0F660C22221
+3231986D5E8C04AECBAFE999D1735A80051C06CA279D0FF6215633FB7A706454DA7236DB17AD72EE
+1F6044A26A0EB77AB3BCE823E3F5E0DD31ACB029A1D17665FF16E5D1ACDDFD83CAEE1D48666D7BC6
+DADC34D317C335855D118892CBD32412F5870C3D2E599A46AA997A5E2BBDD3001C2957D81345DBED
+583B72C4FB356F0C872A31A35087775EF18601930C0874EEA1ACB3ED3690EF447926439CC383087C
+C9D5C6EB21EDF5941CB4E99FDA434A91676D76DC1A1BD801EECA6B0A88370B7005D41A1253CF8217
+1285986DC302B51123DBA9733BDEF0361AE0580FE6FBA5F29CF1438801586559D7436436CFE33E6A
+D6EFA850BB8C9382E1A068246F370388186DC278F31F770C2C96881AC6E40823B24D6D536514A2C7
+AF3D159621080442592CAC03D6967BCBDB38FCA1A45F76F1A10027F1DCC076533C6AFC097FBCF0DA
+A0078BE0828247F938AF76A87EFC14D9F5444CBCDCE637E2325D86B9D99D1ED70F44194E19F6C7A9
+9E415DC8A6E484DAAE52AAC1307A5353E8F649A35214B3F43DB5F3DB3ED06580A570E60B3E52679F
+F90A3B00D4EB4DFBCF0D2F9C5545A6DE10BCC849A0BA9E18F29C6F09ED0F0DD48AD37B7925654433
+A6D02965A3813BA2EAB2E6C2004ADD216DAE99471EE518BD0DA0F534F1512A8E26286B07FEDE71E6
+0A5A057A22AEF095A8B4752F54C04CB8BC170F3D6B725B83A6780176194B21BA906E37B7923E2548
+604F8DB18E0A3E1B5FF04D00898C29C6033DAC54637CF5B068291559D8526D5201F3503FBA4EE12D
+D7A6CF6271618F41FE08384521CD771FA80364D747430A071EE3D3ABDB5400DD36A0285430D537FA
+F6EF8ACAF85C250D6652F327B2BD3B29E1C64E6E66C788FF1D9C3AC6DD38691CDECD0F3FF4079BAD
+A2BC0CBE14AA3FCC38E3F31B3298A6995C87B34A7245ABA2C968F908D8337860507569C370654635
+570615F987531551414B5CCAF7F4D0B38F701619C553E746BD90655294270560046A925A021C98F9
+3EA8FF5B5B8A0D05AD483E6DDC5257635308C6C0FE9182D0E4FB011A00981A7B95DB5BF5A82F8B1E
+B68E8822F8B1B7CF01AF11302B44307F3A71D5EB3465F793CAEB1E72D2C63E3D264380A75FF1DDA5
+00B5F82B04179EA9DAC10731FDEDF5913EFDEDDF5799D2A86EF2D16C0B52D99FCEAD392E9226AA6D
+3D11D29054130C8602F703CB1EBDAAA089A02C0EBD53343A7B297836CB63E4B2C33C8760ECEB15E5
+6B59D4B296B8B724244D37651C3CB862C7135D62B2692F5A27B9374C5C3C5399E5C4DCCD76572294
+F742B1F545B736BF4C82D8F4E2941CD52C0B944261DD4CCF8A968B646662F3D187557206FF165F3C
+0D3D5CA1E428D61D7936E1D00C5377A047EE80E0A5612F7FDEBB8B224270ED23A031A049E8676516
+BF66EBAFCF3F9D4975B0F212FB7A914EE45640972B61AE8E60E602DC7C20758BC07A159B08862F16
+6302D8CBEF03C4B0C73BD8504EB5B14DBC64FBDDC867FE51F76099769A7BD4FA4CF4096EAAAFD55F
+9A20F6D4B84D8FD139112A43204026F15F9FF5AB6729537CCDA60369C24D7EFF4B6B971EBF0BD277
+A9AD1BF1066508A0A7DD9A8D45447A39B92D6B0F7DA4BEC2689D25F35C8C490891F053F90DEE5E2D
+8C9D7FD1E23D0F1C5F57921BDB13BC9B8C3CED4FC42C4DDBF0706985A0DDABCC683FF5EA8416C225
+ABD219024432E3972C3314C30A672FD21523C83D93B2AC8D1DF023EEB1BD74E825FCD19873E63A45
+F6935E6685CF5EF472191B976F9EED2A4765E1B21B46EE1C4CB90AE89DA48E52BC4EDBAC2C855A67
+CB0BE9160F3126151CD171081192D0D6CB27E4EB2D725F31AE95FB283149F53F22BD8E414354D4BB
+56724057601FE4BF34A5B188C00B0E550639CD796CC66EF895AA5315BEAD49B5639EF0878CDF2CA4
+271027678693EA212D0C11A6EA52F748AD0F62A0336BEC8497EE933EEC461E461CCD2F5291B980E2
+8B7D66690B10EEBE22B092179396EEF5D00E42E6CB73BAD4485F2063AEA6B6207E136ABB925332C2
+60F12D8B60B211A9BB15F37F42F53AC2559F5A8397DDD491D314B6DB0B50E41F0AA0A42FFDD5B9F3
+FBD8EFB7E343C06F793DA6BBEE4FAAFB233C31EAA3AD701B1F1C4F2FB05A7647439D19CC856C7D98
+EB3375B3ED2255FA33D9ACB87C1937E0B7F34C9B299C8D2A7F85D41141C598F9505C72B5AC2DE9BD
+E24CDAE1DEE416786B93D4EE713E437D2C4A3251A296B785C81A8232F98ADD04B3D2B41394F8BDEA
+7B602A555EDBD51A088C2277D8E86B08A0D05CB6142E33E973BB3F2CE841D323ABE6FBBF83B84272
+220F569DE23264AB672C2590D4527A29510E7F289DC2193E66FF23D83703E27E9E81572E7534B1DA
+510BB846576E8A39D9CF44483F297C92317ED8E46237B3D73844B3B19D37831B44EC116CBAC3F75B
+B67928C4D4E741EC95E96FAD74D852220F4F1A8FDCD273E0F6E77F09EFD5723CCA1398A021FAE947
+9CAC5922DAC8E2F46704BC216C7BCC1575A455CCE2C2A080B9FDCD0758D9D921EEB6DF96C64A31D1
+C9BEA80F48461857ED7DB635A3BABB3BB6155E835E605B09E06A2AAF6BF3EA70B8C0E098CD1A818E
+30B9A4AADC284EE2B87E945B717FA73AFF5FB788E51827F6FBE319ADDD059614B493ECCE718789A2
+EB0F117EC811EC38A3F4EDEACA660612BD247425A3FB2E6022CC14FDF69B6660B37FCD4359F1BA54
+D12B1F478D76CF898824762C25A026B01C94752F6F3724C31AE788CFE992D9CA152902EEBC4AD8B7
+A5F0E68A5A0408A2F0BA71CE0D94B4A905B35F2781D7E7A2712DC62E87518BFE72A5BC4A9F36A4B3
+B1494B0C4C14705203762E0CD0B28BE31234449C7655B5D6165D7CC4A16431F7A8ECA58D25711E98
+4FF2CE123C05AF9A65D478B73739715DE62A199D47BAC65785EE1DD25AF91868F91D037C0AD754BA
+CE3DC4B67F1FDCA8FD9FA39796EFA9C975DBFAA99DB70624B3563408D0048E3AAC6B4F228DC0AC08
+B9C2B63657EEDB53B46D157426A3B4B4B8CC5B4F30BC24CF9BED442DB51F3C7A0656DFBEFA401E1E
+0823065499C69D51C477360FD13ACA8896A8117789C4561F3D85F3A80D18E39F1D6BF76C7876922A
+1038ADAFD53F2D092369B356D0CA3FE6A27D7B9BD3985C78424C21E60F6BB46408013DFD7A30D320
+EAD9AC6E5FD36655AC6706666A76F1E426640C4B0BE692E4879991EA9EDF4596C0DDF43D4E370360
+D91E8B2839D368DA2A910AA6092337E2E20DEECF43D583CF164881079ED5A492B5EFCC1CAF91512E
+0FEA8140CA3E2553733D6F743728ACAC3E643394015967DAC8839D5A804503A45DBC539FB8656D75
+2F00EECF73E7EC8746CB13F438CAFD554C01150048F758573903B0B3260AEDD78BC2EE87D201E219
+486315A4C01D95DAAB54335A4F2CAFC3F43F12A9574CD2DECCBC1858406C701EE62C281A55B729DC
+EBBE74FDFF275A0A7B8B22C7490187D1839F4FF271723C140095F28221C4145C9061F9A5B3EDF8D2
+9E0DA04D9A8AF6ECD42DB897DD5C9318D636FAB698554BD9EF9B0902BFD8C96CB958773A3C4A5FCE
+8A334C673206C39277E45AB50DA2661F89D621AF057CF1A7ECDE344DC7658514B4C655937E7BE010
+B0694B069FF64D5582E3A4B7F6AF6C96D056ABB20CC883AB25A9BEABB18A84F0258CA3E4F33FFB77
+9841F5970DB447969FE9C6BFDB066ACBC040648D74F553EE434BADC353450A3792EEF9CFDB2FBCD6
+07153F2EF73C1BCCE3784609F26C80193BAEF766E7CC7C33A4CAB862E6E01FC1CDF11E2FBF25FE1D
+308CFF9CD924893861BABF78F762F3CADD3E0BEB38F6157CD08F1B8767606427C4A631AFC9488E6D
+4D1A8F4B51ED48582BCD3089BE037ECFF18DF6175EC317EA56D4FDE37288F089532C13F7B3C1EF7D
+333E7FAF8B49D95F535F60889CD7245E5CB0BEBFDAE8F7A0AC1AB7DA18F2BC06267B27403F1BAD9F
+DF5F13254E96C294C4568EC7154479570E514A55208D19A4538959F7C5B2A0C9CFE4B4A40300F248
+5943C6AAB753F3F0E551727B8DA8E75305A9CE623757B36FB7D34D13CB14EE561C404CDB2D88F375
+2BBFD9FDBCC92CF110D6C35629E3040D995CD25B90BED2CE79BBDC846EAA321B1BC46DFF7845380F
+BF08782D6A31EC7D41F251786FDE403A39626D6052D5189CFBB3DCFF09E81C09D0CE7D571F979509
+79B26AA4F6D07F97A33522306AD692D4D960CEF1CEA3D251A841E2A23C7AE3EA44B3F31E79808F22
+B6ED20DEE4186927394624E22D37E873B660BB8DE6FFAE058DD5817A3BBD68963D055D406F253701
+D940B5E58DAB01FDFF1C626D0D35E8E7995D37057DD07F6D4D45F152A141269A7FB81433652209B2
+B23D69BB5D8D367D95D4A94B2C2924FB83F50F651458CABCB65009912F00F485A924A61A0008DB44
+D478CAFDB7D9524204947703B822755A1301FE163A8248C8AED7073362D918D097597B24A3B579DF
+FE00F47776428D2B992E1F5FAD6190ADD1C789BB9A913BB1B938EDE357BB436F734477F7BF332E36
+7C380D049AED8687B4C2AB6EB813E719718C9CE62296C24F783B16E9635A7E4402840BD26D3A0DA5
+78050C49E6D8239EBE9E053719BE94CF6D8C7942AE1660F28B21B3C1E6E15459C1FEEA4FAAE64AA7
+165824F7B939E28E51009FB778E370C6001B3F546EBB76454912D730A3F8C7F6F9EC01B9F90A2E5E
+58EFF9EA99BE57A0154147C2B7A7C7E493E37C9BD0ECDEAD4AA4DBFF95D7E451246C9D4C30E71F4D
+76441A297A1223FD2F8E017B10C108B0F0F67385060E9C18133A6264AB2B445F6DBCE86CA803357D
+749244A6FFD8FF8AD37EBAF3787F3702764C5EE2CA7547D7A9FED5AECDD2064F7C767078579DE13C
+F135CB05561B15BD9803646611422353774984D386BAD640C5EED157569356A17BB6233EB298960B
+8E34209562AE170A08D15F3A29967DE067F6AD183BA1EB49A99F4899031A01410D7311BB9B7A984E
+BD6A303D44CF42B40F12769D44160583BCD68C68F823DDC0D73150083404B12AAA68E97206053C6D
+23FF0620231D3570A089520E928E980F56A273092DF94EB3A99FBFD877B58860231B2A761DC91A41
+A427778B06F07D4F78924FF986F837C1437B44EAD5E7C56B9CE9CCFC0F6ABDBFDBDE4A32E3FFF336
+7F7194DA20D038CC44C4413B2CAC15C05B22758569D1008EA057DCDCF4A324C924021B35B10ED632
+BBE921BE2E34795951DDA394FABF3EDCEB99B3CA15D6579559F0BBECF8DF6E6DAE427DF75657AEDC
+FE180A88DDA445A5A5E239B364B8884714B0ECE259F32F1742DBAC0BFA9A1052E2B14E632B56A474
+F2C9DCA9B5FD8D62A39227CA8C208DC69E5F543A745A913641950AE0DCCE02D20D374B652E2CC41B
+F0417F99C2EFCE1C23204261FD1BCED5A1E8AD4736C5F23E14482D766390B1C62A320F751CA13454
+8DBA0B08E4BA0A0CA5F6DC765F9520D15D895792BE580133B92EF3691B95331DC76A551C4AE9AB10
+24D7EFC4A02B5654057224C3433A2AD6859E3E4C40F8F0B089C846002C75ABD667C68606D7300B7D
+0569753AC074BE6943AD20018835A6EA28B99C983BE3BEA9B742A76F2D7A2A26B35664D24FFBF656
+EA28D525A78298C898C0BC2DDB56FA37151AF5A08B28226CE6BF09726C37F1B0BD39DB144CBB5494
+5DC48C374BA8716F6C9A4626C95B6993DB2CCD59A7430E3A3B2E6CCAB9A801760B8548C8447D129A
+01EDF187435162EC13A65C943CE9EA547C3694F88F9706AF29F5357EE59500EC080E7FB844E8021D
+907EE02C344DDCB78559AD7FDA31A3031D5CA80C004DBC04BE54B38752D49DFD19F1124C059ED68F
+6E85E1A3A848F98707D7413ED3DEEEA823D958CCE720B85303CF208AEBB9B799653EBE4DD16186CB
+F8C0301AAC6271EF9E2CF6732A6CB8548B7CAF2399287D6AEBD5ACC7C9D0DEB85BE38150072A0184
+51D3F1A8ECD784AF8149BF932E0422EDFC608B20B46D80D3EB68D746E1EF40423CD6FA218C9F569A
+3442B0E70A2D464DC59CAEBC9607333D7B8FB90349676207AACEEE5ACE8E0E9F9C5896751ED4DA00
+95D68C421D8754D665D3D58C682AAB1DD72EF9050894EB680110C3E7F02C3551D73C63CDE8B45E5C
+453BC9AC1FB3145CB6F0141B8E4928351FCE66F2A5AD534E5DD8BD901CEBFEB963DE2787095D7755
+81E588D3A165BD55B51F039992567B85FD3AE92C7526E33B44B8149E57BF7E57579E37611AA29DC5
+9EC94F583181201638BD4BBEEA23BB9EF067CFEC2E436D726F401EBA897480AEF7E38B9798C6CD42
+182C43B2BFCA7D8B6B696544F6B00C7B7D0D2C70D955304A4FC8D97E317C01113404129D480AF8E8
+EC0075A94859D5A79DF5F3FDC2EEF4F0BC1113D2C92DAB9859E9944DFAF557DF43AAF42B4FADE1BB
+F5AD6728F884F0D4E7671787F1A3500B00E62929147C72FED37CC222EE991630EC9AF614160872D1
+BF4666DF3B682373AB1CE36FB87C42597FF1F96D3D7B07DC8755C2304AE69B955FD2547D316E16C0
+458BEEAD37B904BC27DE764240E67A68ED5FB57BA1F9C7C4C8F2BFF745F3E6FC732FD5E37CC1DED3
+6EDE20B06FD832673AC78DFB7655D7358CA03332A18241D96BB0A45D16BF2A2B80D0A68C74C8DAB3
+F18936EF73085EEACA9B24B18EB7DFFA75C707C947E17736EB7B608C4AB90ABB53A53F44D8661485
+5D60E36CA31704053CC453F2A50B878AFCE0361EC30444F5D6009ACB5D9673E5536B11A02B659252
+A64923E738F494D6264824392234FCED4D66E0342D03189778D41AEFD907272A919AAF40066A304C
+6D7831F90B347CB8EACCAC6A726B40BE4671D6A0A591DC36A30ABBF91F9E780137D80EAD43BD49AF
+690A3789F39D3EBFEA9CC64B82D601B88805B6FDAC3C84C61638DFF1E391DC74FE4D08A0683BC5D4
+E6634F82F4DA357742764FFB2B8D264275F82052921F7647BD8709857BB1C98C205D13EE51C14E9A
+DAD1324562267D1931B5143A2ABD173C745B7272A6FECD532B5F189C8749DE0ECD3A6B1799C1834A
+414554EA6972309C48DAB44A9DC41D8B28361E89CCE4DE8AD6058469D2F603E7AA62631E80C01535
+539580E124A24E9387E3E0E58A63AFB29944207BE5929455A150AA58E27EC885CCF019CABE1B8769
+0AA7FD1F4166DF820A324FA0FE3B59F8B767BFE029A7E3ECED513A6CC622AA8CE96563219EE328CE
+BD649EE99E5F108FD57646926CBA30BE3AA8E00EB4CCA282AA35C0742410477E2E5771DAB74E4181
+D91DBCF25DF36BDBDFC5AB6C73A982A390416A23C8DA10655906878AF714C3478C8A0C7534F6022B
+80925A069F63834539B18D9CBE67844520A195019C15F8F858E91CC50DE001EDB52C89F06035473A
+022A718893BF7F6FC0B2E6CD4C1CB26E98C1A191EA5429BAE831F464971180F5EC2CC6E6F8F9EDB8
+2E2A7CA8C5656BFBDD618F7D31635A23C73F330EC76F4543C9795600F8EA45DF62BF4E071FFE3758
+2DADBF252F2A3EB399F61BEAE10BE0FEA537C272CE850026872F4BDFE5921245147B71DAFDC8EE88
+C509B6D8AC249557725FC5D14198A2DC889A4A9ED045870F767906A226826AC93FF1E09D75B4DF79
+8FD690C5146175EF2CBED8F03C9DEEBD95AABA69E98E25A98CC96820CF1C684F02E7739F525B12C2
+72613012143FC26919B800342903624AB29219E6266716E451C9D60A4FA010B8D26B56A4C91AE1C2
+ED778E35E66B49C4DE64021894C2B39E7C883518B06E04D198B7D056A24C3E65BC9E30BF2F73F2DE
+21E676A37E2AFD625220831F598E93BCBE098AD73FB6EA5CBD9D17EFBE6EE35FE4EE93BD3A75A2F7
+118EACBCCB82216DF70F56C2E82F77F072093824C6ADB800C66F0F26BF7AE513A490AC3DCF648DF8
+2E54567ECB9D6FE66E908748B7D5090910EC99EB9B222E92B058F3EF34A11918D6FCDDBE2B9C27D7
+DB19AD215E7B327689E0597963E6EC907A23A0EBFCDF86ACDC349CD8983EE83144B5B8489545AE2D
+ACCDC8E553FF4A1601F42CF82A90D571E36193BDF4A7893B2637DDC0C73EC0C21BDC4BE1D02BD257
+F4C240DD6AC53A321E13FD2EF4343B11B9C8152EC02EA6C4DBF7F66C9356647A948CA9E26672BD7F
+9B828FE109600B1638806DBB95DA6AD0F78050FB13AA580139C7198B1673D4AF9BB610A83A709D3B
+7D7B01AFFC0A740F03A5E2E3EB7AF145E3338D46D03A83FB82DD6487A52F9494A89717FB500A26AB
+C949C51FE25DEE8F78E199AA53EC1DDF9D767D8FDA77FA32F19200BDC539F00A23DEF166D97F0DF6
+64489D932D115523CED7460212BB35D887FC3507C2592ECF622FEA49AE2F04299A547ACEF75EB0C8
+8ABDFA7A042D5EE4C88D716B00E79C40173A3F7044546D093B988463623DC1649FC6CD645D01E102
+1AAD11911190D0B07C0A66AE7F9F9CDCD0D841A976A8298C26A0850FF4FD42EDECC37B97A19F7240
+3413098A81025E4451850EAF180299667A392B7D2E96C4068CE110CC3CE15C6E58CBB11CE21A3E9D
+FDC88ECF14A8F2D422E1CFCDDEA320DF5CAF93E6F9AFACBADCAEFBF542775D07EBF09A96F0162438
+62662AB782A464DC7A96BAC2B0F0F043E83690C3B24432F61293A1C5B3699605EEE8339AB079BA1B
+A7C65ED392B6E94FF817CC25AD32E89C95A0667F124F26B11AF5B45A9AEDE4F443429ED30130D7C4
+68C940A7C538ACBDEEF77BC084F8A24FD0060BB9CC12A710DB9DF03CD381FB6E76F79D3DE40DEA4D
+FEC56ECAADEAD68DF4492DBAE69EF1663E2CF90614871094BF6F0E1C9FA0EBB2D34923A19A557BE9
+54D914F35BA044FC800D822D88B5E70CAC27D6D56C66AD6CC3C7647DC679C8D3E1D39AA8282BCD27
+982428F5FAAB76EB16BCD26A1685C044E3C7B87B3A1685279DED690D76C0F1C52B76FD13C419165E
+754BDD7FEA75E26DFE2B916DD0CD40301CCC945683C8E1F49A03A0DCE1974A76B754BF04D36C2693
+969FE4C6C39D60D995738F1DE0ED6A7E0B80B40BC14B440B6B8F1085E83995E224BFF4EEC6F67EAB
+103B4BB6D21F9741932DFFBE85C0BA3D2AF925D670318D1157FACAE9C09B3AAB5B1FCFC889348207
+8D5A3F7787C699C420C9BF0883D3B8B3D7753E9A146175245CA9E2EE04FBE258B6E42334EF141A41
+D68ABA286864E72F0E4ADF41C1C96E60E69320E79211984A734392C870D72B8C236AD631672AB9F0
+FE48EF2611740799DF5B3339BD49697C4DFC0557C1022AAF15C24FDC54FBDEE2129EC70473A17EEF
+D202EE43A1B5C7B78A299B6EC8BC7595FDA6BD0BD22E025E8FFD89115448D99FD27BAEB680A22C59
+7295E33201199E9E1E38AF099926344D1B7CA626166CFFBA2F3D1C15AD63F0C6035A5B9BC5AD644B
+3D5636C2FF3B9403AFFC3AF0460B328C390D3B416C7880A0DFF10BF512BBB933081FAF4B2E06C093
+E80950F08BDEF07D56BD042433CB0A0C72E1F17692C9F5C7AA97C31AFEFA39233C62D5430F11DD31
+478E86A36A8AD3817B0AB397C8D6935960D905994ECD2AA8299D248AA274AE0FD1F1377F0443B37E
+67DE63151184DB5EDDB4DEB9CCAC76896BEBE32E30E0F3C2557429FBD4B85ADE7829428C5CC95CBE
+018C17BF38FE4B26B0AB736FEF35F6E3DACF0BEBB3B795D7982075B75D87324AC28D7E5B446F04F1
+0A001FF191A5FDD10B4729E57578FC42C26D473F22C70D5629AE05FC33E50A4EBA2C5D4D63B1D147
+9ED7B8FD7A0D824413D06437118C788543A21520653572608F9172CB1D1AC529280AADAEBB5A4E30
+AF99A58EDF2952BEEA29F366FB6FE7A804DFB1D116B73B45033E9E7E9767A9F41F2FAA76F97411D6
+420FB211B4BECF6C785FFEEBD90AB932E82EB0AEC7ABFA4A7AEE53E2482617576EB28BB2A3C7044E
+15F0B6521F3B073021C3CE55890951E041EFA38937F2C0683BAD1AF255CF3747AF2F0B8A92BBE44D
+88F7768D35B8F4EAEF0AADA3A42E26E3E3EC25E286C40808665B80C6265716DEEFAE3A86C9B54D34
+74285F3BA2946302A065B800EC7A9694B0B715BC86D3EEB649FAB8A47D170550D9213E8B8E9367CD
+FC8527955263AB2AA55FB7ADB7DA9A4E727E3E69D9C7946369CC078DD7751DCEA1C0601C57F4B5E4
+48BAD7F5F8A919632178C77B7B5F95E402DD808AD59EDC020D82399DBD3A9D9F3FD089B0909C171A
+940673E361F5728A89DB3E2CD0AE2009A6D64FD85ACEF62F8B42F009BBE11EA0AC14525C2ED72E03
+0DDF4F670D18B94C07C027509F281F7B59D854A5E46F4DC544BB320C427642D84B914A2E5043B1B6
+FC94802BE925FF260775F6D17A5C55C5A5D5931F88E3D7E8485D8026545CDED1DC96D1ED7E0088CA
+ECBFEB99F110A5CCDF7EF3B4190F3DA6ADCD5A46DB27C0F076B558667A1B9ED967363799307E77B0
+9859909B4E86A27639DF3A8C0B193502FD22C0298AE0398E7D33C3039D7878922AA9997C93F29655
+5F5D7BF3A34D46BA5592FE2DAC5A32DD9945852962457B81DE4B6B9050660EEE1A0D95785D824A5B
+DEABACAC8A8A90E7303093F0DFE57ACDF0EF2843DD7497B1B80AE7E883735D9BD689C84A61DE8127
+3E2DCA2F64B00B62F0FA3D3B5359241525434847763059927565F4D98CB8AD1842E31488E4C1DC58
+4BEEAFFE1D3E76AA2E6C81CE2DA9F00DD32841412695C8EE17EA60499E99B97D30C32DDB0B9E473C
+E65C259949129A4682DDE5DEAC0611464650236934D7C57D1EF7E8B5D9E5D7458F0FCA9795853710
+F37B5C24E39D7EE92B2D4066D533A662AE2B063B741559B24AACF24DAB6FB6786F639ABD8B34C7E7
+AF20E5FC999BA74AD93CD821B545C2531C506719605A64FC06DA8907550087A4599EFA621DDFEC17
+B904B6115BF94AAFDC56F3570065D75DADA1AB177F4C333A04A0119A89BD209DB0CDBC5DA0C8B99F
+EFF54B2F4FB4BF95AC573EBE6D5CC8110E6387365CCECA5630F5105C887DD5803DC1376986456634
+C3B3BBC235A72AF168CD5B350E0A8BBC303A2CFC37FF627F4697C0D19BEAE28FC3996E967CEAC4FC
+8D9D309E2FA65172E899285BAD3F8B45B38C9C2BCE94C31911079850A040C08789EE377B9E652A10
+01EE4F44420757358E183D48EED67C3008E6F05C3971C732B98ABC24271527B267D8B10735CB1FBE
+773E33FA51B5B472E402676E3590C7BE28BFFDE77AC34544718A20833C9891A176AA3A62D686E072
+7AB2150A1E77FAD5012D0299593B0222CA38CED2B9953B1E5893F176132F1197609D04F2F1D647B6
+F44B2EB0AD94211733F226B570E9D963AF9A6DF04FDFA26C0BDF31EDC41DA521F9D0090A9FA5DD13
+B9D977329F6412815A8C52C3290DD42EDBD312592DACBE0BFDEA209F389DE8E4B5D8ED51B46F1557
+C2B50098C2262D3DB298E12C0AC3E03B82CD2807CE04E109ADD00EB181D701E4BC3622DE8A86E977
+3D6C4AEB830F3283BCCEA663AFAB740B546C3577E9182EFE660AB380F0504590CEEC89313A608A29
+9F9DFFE22DA6296EA3E39857D7229885C78F097E7E7845E6C908A0570D4ED0AE320DFADB7AF87E5D
+F85AFCD1B271B672813C0A0E2EFBAC5275807ACD3A2F09EAB95DE6F571E3F3C827FB3EA9DE89DEB5
+4B8B14770305B23EDE569571D0BB8BAF4811E12A0DD5BA4809818D2FE088DC1CD4BE72EECB36C819
+AC25B41BADFA27D5839D548CEED7DD18F5F2BF69EFCAC0ECD4FD942995E930C147E1A7AD93628180
+E62F20F3779824324C5A1C35ECEF68DE30BF5DFDB5DDEBF66CBC459B2C7FBCF0ADC0274D137BE67E
+B22FA117C85CF7D52BBBB4CA5F9A6F73AFC23BF2E23B4B1EEACD33DAA3187F1D104843876EB44322
+67EDDDED02B6A507D13E3B9F012FCB8C9F0D14D951415BCFB20B3211B5B9860B0C6826BE597F2F9C
+94DA2788E65107C5CC77CE1265E33DDE9378AF9019E7E68522997470A52088253FDCA09AF9F071C2
+988CEBDB46B7F7C8D08B5467A1B3FA0EFC552C5E20D4B9D565AFEF9B520FA2F544E802EB8E8E0C76
+21FF63F4A5C0C59F94E9E1731D8F3C80C772805DE264C7501E705BB21EC37A1379BEF8B8A9E50EB5
+6FE9CF9C10C9D25CBDC68124D2133B2DB6348175537EF872CCB8B1787330A5ACFEA87E2BE6615DFE
+442EC74BBED30021A0437ED3E9DBA6EC49A824F0374B446271DE6E1B16AC74816F6216BAB7329725
+8CFBA83C178F5EC009C57404391898BCC3314411F129F12D68218AB6D0BCCE2E5AA9AA1D5FEE1E2F
+0AFAA4BCC3D226C5512B456CA8F28DE54858F18DE2B30AB4FA02840859988BDE7ECECB4EB0002523
+C6EC40BAC2E7ABC411329F803DE2DCE1EEB354E4E6771E4328ADEE4E713AD248BF4F91108C52B169
+140F33D5C56F1EA2240E7E182C060187E29020139C373B4A6CC4D2156F7C15590D3C07C98535853F
+4DF901EA9F2C66510C190D9456EE037DCDAA428D433CC2231B2B627CE2B5304B6A4630576BC48984
+66D7A8BB75D53ECA10C74D434A4E50B1A706ED6C7BA274A9CAD5D929B9BB8A631825A9C32A8F468D
+578507DF2291DEAB6338ECC92CE8664D4B1C211A4CCE186679B6C71ACD5655B97ED8E552B09C1C85
+387749406C549057DEFC059CC85639203160B8FF05A48F7D5C4F452B111891846A521674C0E2734D
+50B8C7E7B5D9F438C58DB139A6509DE3495388E0D7AD24F64FE73707C7BFB8CB06FA0E0C41346B98
+220E007E28515428C1874AC996819F16CB152C16F89CCDB3F9C83070AD90337F1823AC0A48B72749
+C6C29A8FDE1EC2E76B0D29FF711891EC81D0ED0B3349E9FDC413047731D70C33E57D2C4B637C8FCA
+B027CADCB3E11F94F61CF3A56E4D90E8550F456BB90638DB6118229C9B74C9533508F343C0EB422F
+87627EFB562C7730E1A804E3E4DC80FC0F199CE210045ECB1E3313C3364F78A500A8ACBFCAE0F7D6
+56FDC8B1BB95262A32ED7562A62EE5CF989235D1E641D726F34D215242D44A946662EE94E765A3C8
+75557732FB4DE1CC2699202802D4A5D99C621478C1C6D583FEE8CBDAF54C73C8C17BC73F1B414EEA
+BD901409B83E98D62749F9E742FCA7C632C176D323E72FAB241D143950B87AFCA5B7B75936FC6638
+1FD0E537C30D744E970A08636D27AE7A277F3838BEB7D1BF821F331D483FCEE4EF9FF98F350B5B3E
+CF2D6A5BBE95862FD5DEA6D3A5322DE6723B5201FF81EB1D1BC5BD0F77CC1737837655BE23E0662E
+AFDAB16BC0635F40DA516BEBA8128C414D6EB5F5AF5F98B8C0129606FCF536181E7A8ECA563BBFDF
+0AC65F1932F1DF20DDD6739F7B1EFEFFE282FB6DF35222E8148FB5968BC7E40A582D7B146E374270
+D3D014849E898E91997288BE44220B6B6E431B7AE3F2F7C5BF3E3444F5088F9F42B7F676EA14671B
+C70A84B77BC275E82516040F7B9DDC582C0FE335852A83C31BE3B3F716F17253AE57372D14951A2B
+58F69C2DF7B93052823311E4208A72C10D0625869BC5F3808D231E86CD259824D7E6C7669013CC55
+B61E4C20C0041C35BBD7F1C291EE7A3CAE016A8C90C56F22C515375252FC3E188B80940816EA5117
+88A2FC7AEEEEDAB9E0A33F2C82D886F9BE029BFA1348DAD16874751460DC149CAB5189D061E7C804
+1939D80A03BB529E3580A235F8C37EE25C503BECB9C31CB91904BFF4460837A1165A7474C83B5773
+5945BE04F3FAC3381310E4BEF8D8D4A7343B75D23BEFC58060C48BCEB52566A021C197ADCE5FA025
+1AD31CF2F90CF6A72122C38FEEACE6BE36176B1A990DBC42A33F0BC0090799C2F6C8AE6990725562
+B07725B3DD15C9011205C28700DF82AE4F00F9842DDEA3BB5C15D3A4CDCD18E692427505D7B24CEB
+40CD7AE0D81A4C83A0F9ED579F924FCB19D9D017E60C6500CC64572E0161EBA57EBC11A5932F24FE
+9F1AF444B3C657AD162BD39682D23D6355EF5D84440A124138CEAC85C926BDF484AD7B976D9A44AC
+6015C25354DCD72A889474F31B8BD3CB7371B95A9729FF0E46EA3644477AA4C11FF5913D917B7742
+065F0593482B6E6EEC9EE63633A6A963819F3E6A2920136B17C982353F1F071B3D585DD24B22DE9E
+EFB2B34F776DA85F85E9E107D16960AD67046476ADEC509FCFC64E8AAA6427935FC231C817A21C71
+6DCCE088EA4186DFF0A3A7A1C367615B8E7462DA176C9F9EA893DD04E29DFBF88679991AAB79A172
+48C06E2BCF3B91C108E09331FB57D94BE85EDCC66DA8225FF4B38E12F8563398E62622EBD2EAB098
+691EDED4D5D7AFC360AD6F263C043DAF483DA05CF70DD8BA8F96D2A5A87043DFACFBBBB3F8A378E2
+A896897DD48D8B13888A023AE6AD778DE5FA991907E731E5C5B0A3DABDC360D77CC71C59C34506C6
+39C21BA9FF7A9CF322C21B457D62F20448BA19CB765C4C0AE7CAD9A972F60ED60A21C92AB665537F
+EAC8738AF8A27D3946F759162731F62C0A657CED40C66B8A9941EC2559074279CE0F6E10BE3F44C1
+D517E10D85EDA6BB6D097F4DF55A3DB7D50679675A781FA1FFFD6F1A8349B2870C8114A05F5F7645
+3B38446D57ED63FF8731661F0FEA79033E4C8B5CFA29CEC43355780C5E2EE86CCD449577EBDC0140
+47AA5CEC980CEA8200867212DDDAC234BEFC9FDFFDB43DD32F44883EC6F2963D4D28171E19E144FE
+1BE2B8FCE99016691A00E4EA594F94E973E899D14DC44486BA6B4278DCB292FA5C7E6D73068A3BA5
+1557C3F072547E7F2F869D4E9AC03514276EBDB0920FA04E67E2934A250B1A502A8D06A25037CE59
+920D0E136C02D5DDEE2EDBE31A38BA32C4122AF89F295ADDF579FBFC72391283DC1914E9322A63E9
+44A280ABE7CEFA54ED2EF42B79FA97EF21EE83EF20CE34850FB66C378EC7C08B2BAB924F58FB8123
+FEFB43A385BC1EB922AD8360750FC1B0D8A303AD19286308B7A39A5086A50A8FDF7D60188342A9F6
+42286540945790524174800F8C44ED71306ACC3437FB49D8FC43FBF8E88103A76B4A92D95DB9B45A
+FA067E31EAEFE6BC818D11D7CB8566BAB418B596A7494FC7326AB3CC029D010917B305CE585B194B
+C5415088BC7AC7852A6D52EABD223E2634DCB29080217F6755B023C591F08C7E5D72267664136639
+9766EDF511FA744675D473AB37BED0ABD92E04049AA9008014C5EEC1B0443C6C86302CF6A3C38BDC
+B8D9E0538E349BD930707DFB002700B5EB427192AD6105E01B8C2FE488CF617E9EACE73EFE3BAFD0
+F87CB57E02AD31627E6439188006655D6B992B393F4858254C2106FE9A3F0EF7347347C66C94A999
+D49527A2A177EE20A08BF594FF1E08CB091D22A8C1569320EB5145FE4A2674151750620B5EEC822F
+A48C5F8B565DE0E18AD05F99827FF24D3BE03B007774F25284844D0F1F8F95643646960F303A831D
+4C2C4ED9E0664C1D705137C157671D62179B47FA4A6441DAE99C2F7C8765C4C931EF345CD8F92D11
+E290B2FDEB1B0FAB4BA661A4511F75768808AC1DF2FE79BC285B976D364ED25C64EDEE62E8E035A4
+B79344D55B1E7E2B43AF1CF94ABFC8D0CA5E89240EEA231464449B831F1B3F9EF01AD07B38F0B402
+712A0346892A1DBBBCDBCC827220CE7F492CD7471FAD35B43E71CA14F1F1A9CEA740C4E1A98337B1
+5EB97B10AD57DAA9E9ED134CBB4614321D548C6A71D8ED95900E7947A7E4331D7DF3EF367F6DE8F3
+113A7DDBEA0F741F9C189B8B586B83671475A492AAF9994D884FEF3A646ED4F272668DDDB05EA230
+399223AE63088D636AE6AC7CE2DA06CA6AADE9272FECE86D0EEA8290B927B17450DA6F34A3D566E2
+096300CD8D5A34139489228ECFEB104714FF907A6E1D3DE35BC0FCCF45A2781AFC5562CCDB627E06
+F23DDBEBD4212F36C332C4A5A9498032213DA7C3FD03FC4832D1F2AC9EFEE3B840BF8356A16E14CE
+989C37E6234CAC7A215DAE7C4DE2E2B6D9A876F709422113B503556A4BBD0ADC107B6B639F1BAC9C
+6FAC7F4092D23C04EB8684C9D0A5F184160CB660CF6E8672ABE1AFA596EA86890DD22D0A406B2118
+9BB626943F378227132475A25710B75E5B3BB2ED7DC0412A0A079E2AC311ED55AD8E7B2A1A55FC84
+E62D61398511B70877F3DDDFEE5D033A9ACB66899021951378EAFB9E9A799AC9686FBC2E9E9B3A51
+6DA9DECFDE87FA4BC042A5D26C2117E29AEE8840B18361B7B38B21493401E931B431EAB1A3371628
+EF13BE5A0C64A3B9F8B6A29D209884706D2A9AC85B86E3839706269366EC7E53F31BA4219F741E55
+21E42F0A10B7B29E839B924AD90861FDFE3A3D446D1E32C06E66A5D7187A63E590000D1356718648
+7C99BC31088356C9ED29BD80C61E442B81E15657AB191E90EE77A6804C762C45D3C1937EA17454CB
+D76D781A0C96A0914B9DE3D3984BAF6075D3A25AC69BEB34CC413D26C39824B83F853DF864C269F6
+8443970933FEF93208BAE4DB3BBE90DD3CABF6EAAD2F6CE664CFA05999FB1CC406A3502DAA6C8145
+3C69CD218B18BF0B9654FC3637EBAB8007A2EEA6ABDD11FB338E89FEC84B344857804D028F849E44
+5982E294384A442390AD36D7C4182EDD7A05BE6744FCBD3BBE6FAF4796063CF42499F2DEC302AEA7
+B64FEF6DD74F278FB5C2897CFBB87FE03538B2739E25EDEB806142E0030F7413018EC1833840BF37
+269E21DDFF67B8059BDC83DE9C6461F3EC2BC2224D2585AFA2AABEFE7D3B2A899C3E08F00AF2A707
+55C145C6BFFAC8F96B54EE682C3C8CE01BB44EB574E1C721974F236E8A2AA28DF0A4AD285EC4DE58
+DBD3A2FAC8A20173AE84CBF877559ECCEC64D9448F8CA0FE5272BEA6543738D5EDD54B73AED3C4CD
+172F91C4F70616B36D37B1252D355820DA88536C829B1542C1EF4F76375360E7123525744F55001E
+71F5AB1E5B39A5248CCC6789647F1FAE5E989A8A8ECAA2A9116ED69DC9B9AC25A743F70AC4B62D72
+5F120D94431528EF8C74611F7529BE325AF84663989C219B70274584D1EC4485E11AEFFE4A29F534
+6912577C010FC753DB47204BFD9E504027B335EEBD952FD299E5562407234C5DBF0B839D1010AE10
+5D56766F87910E44AC7968842833A302882AB481FE0AD444911F7EB8555DBB9F3D062E9E9FF3B529
+4E9A3B3AED4AECE34C816BE7FBCDAEBA33FDC491C725F13801A6BE162F7F8E99491E8BF77F2D2010
+D9FDD78BA2606A28810CB5D88CE00218D49609C62E96BBD36110DD611C3573EA341FD2AF6FE0C932
+58565D4A6C88CDA3951E287168A478F5EE074BAA9676EF5911D8EDF8D66A7E8F3BED1BB786F480F4
+423C3D3CDED2E80AB76CF5CE5A53B092A44FE52806D8EA72033BD3E7BCF1DA9C870108F32D8987FC
+25E7D873985C3C4882A5CDFE7A8B51F3754842FD6F1FF6235DBB88F04989BB2F2816A476B513C10F
+AB6F81CF115EF5044D3366A4E192EF68D385B6B6F4BDA7D14D9627F40843035CA80DE49F9DB52CD8
+714F769DF97F73E3152E8DE34D8C37163D485750F8F4E37AE8A3C3BF54D97BC3B2978985B557F9E2
+9F0537CB743EBFA0B7864BBEA9C126F13C02CECFDA50A8019F90900F409B6D700CBB9EEE18FD1952
+D496DBFDBAC800FB83B37C705367F182D91B21C7F6C242D8735A75343C84DBFFE583337DE2A95890
+660584B513C5BEB7A0926BCA7B7DC3ED4D080CC3F1264A4215ACD35DCC62D896B4354F2F7254444A
+7235E0E3D53D02583710DBFF2CD55AE5E61D25ED1B3C3B6708E5BB308A3D658F043C26B881C949C1
+0940AF6BDFD2DB0D544DE119BC7F7B03451E61FE18845000D350AD6D04A09D8E3E999E6DAC6AB73F
+818A11EAD345EAED03BA083A6EEE7E9CA8CFB760FCBC88B8DBE0887F79AB430913604F15272F9C73
+DAD19D591B40CB7863414A8FAB21C41F80A4BB0A3AFD9D4B1322487429149470DE62F305906F1244
+2AE20521BE034F159A7E7EA211A2FC6193AF59CBDD4B43207BCDE8697DD515459F80F8EDB982C97C
+05ED3996E03891DD7EEAD505F6A71A924CC1CEF29053ABC8F0B5F56D0DA1249F317406822C225863
+ECEED46BE0072EDDFDF5F63DC8E94FC119087A66E394A653D5AC774407B006B35C406E7EE4385565
+78290F8CB8B131B88BD78CB87A11CDA44C5A199BC71388A81F2F30E2C003094E793969673E8D0906
+1F4A3FAB9B14C52EC89BACA1C52703F000A967EEC445E5423D3BCB9253D91AA64BB26727C8461FA7
+FF61022B4C6A9E793901D3407487B4962A16B564CAC93D7AA28A22C28318F69770E12DF9D6CFFF17
+09EE0604890191217696AA52630231FE11153761310A72D60E6925AD6B9D63A66047F32B9425C91E
+57505CFFE42A90185451297C2CAD408B0CA4F8E923EC26A3D5D66448550AFA3CE3BE9296C8149878
+F853F2A7C3B2E98899C8F9B47B1405F96D2B22E9F1CD62A2945AB62F67AB0297982809A829826BC0
+F24A7777508EE0A71BA7588663D8118E3BAC936A61FF4A628EE96C0B9AE05072A5A4307E68EF2C3E
+97A46EB31A2A7D4A33CE9C44E1111D73D9D3C6A0F22F50D8C22153C2CBB5BE187C0F2F37708237D6
+FEBC445843CF88F4C3A249B39DC971CA003A78028F8A2CB3BFBD2C26CFE457A9561350AF93E60295
+D21E1C2024312B1F2F76F2EAEB822CA72412F860F5A87DF705C2F82E681AB9AF45A19023E02538B6
+9E0F273BB4811F07D153029BF988D3EB66358D987895B96B5EC4C24F3409C1EE1B5978B1EC8F3E2E
+75FDF2BEDB0AF0CC66481FA98ADBA8DF4D8C8EB800F88C7776BACEE6F61095FC2CA891574F309E1C
+0C27E37D8B03472DB3FB8D6668E286501118F7DADBF1106687AEC6DC4C162D22E3BD0BD42DBED782
+BA4707ED5181DAA9ACED58FB358DA0AA9B63AB4394D42360C3F5082A6F168CF41D381073A1D99CA3
+3BD97F62446D62059BF4A8616A8809147B739400362748EDCF39FFD2BF2C16D4632C06A5D43B48D5
+0D0B98E4449B4F1D39659DCCE8AC72A2E8F32487FF8686E550299F37A6353A8E558D4A8B074C2F5B
+864DBC8FA3391E3B5135FC738E4C13868D67234489B6AE382792FD5214D5F9E5249AD65C433C22A4
+D63FA7A36A48A339F443AB39C23D2025741186C8B18AAE9952D41E25E930D6905B29F85A4589E9D3
+95FF040E3F72FBF29650D4DA2B6FA27F60DAC4169DC9764021F4E7E094FA25F6B5AF25602C593266
+ADED528EAE6C967E66F0BA05F258E34CF6AA5488D3F7406C2AA9D9FD9C533E827F26862D348B5223
+A690E40953F337352C8245F1861A19E5326490F2A9742917B5546884E8036DE0874363F812CF6FF9
+574DFD5BA77A1DD7C5778B2B9A2E22F2482744E2CE9323E590C60FF02412CC7AB0EC30D0EC857D27
+A4E3D8B35FD69FE28287B2160AC0BCF645A0654403D26B05041654B17D82928044BBAC40871BC3FD
+D8EFC3207928BDE5D66926ED199017B223AAEBA563F2723AFFAB737F6482DD269F44ECCA3B32FA03
+FFCE3ED882B449BEE196F59E6616EF1A2F08B42B1A184727D5BD96DF83972BE1B5F8CF098F61B84A
+B5BBCBF231E099CFC07D4748D43F129D123FD8051628564931E43A70BC09BF20AE2C0ABB009014AF
+89753F91ACB574C5A218A47A02DA3AE44D3F688F9D96076AED9EDE7388B2935C01FD400BA7EC9574
+96E317C6931E3ED7078A53CB4CEE4E56311F4D8A368AE981606AD7E9DB0EA2A10E079476D9881596
+8C9675D9CF15E29B9328600FECCC02B484D8749B4154D69CF90997AD650D881735AA71289AE93FB5
+8C56686C1F9FB0E696CC0285A1A870373E5DCAC285A1B4FE903CC30B8ACAD2E4613873ED77D813D2
+9FA4984A7530F5B046A71011D97CDE4BAE9648AD54537A1D87A5AF1B92560DD7064F3EDD3CCFA7A4
+FBD6166D067945F103C13B3019540C3FE706804A7A00E1D28A0C26A40AC5A8845E39631A00099CE9
+88C3F12C8954EA84BF268D99E2E13726BF9C63474A5FC874CBF723BD48A5461177789E11B4F1CC63
+0FF4D60DE4F01422C1029E664782232A8BEFC38CD058135C79E015A55BA933AD8D446A81051D956E
+429779863DBA60F4258DFC62D1DC3CC3862C5D310579417FEF4D7642ACBA8BA5284DC5833F581150
+ACF91709D6B3A41395960197AB43E63B8C6C2F745DEB4647E298341F3E3023D7DA22509340E28140
+97A0C193493711E60C5DA99C464D8DFC83A61291BD0E13AB6A42C81253C0F1B37E71E7706BF2D662
+76C60A3A7DECCC58E56524A200F8C6C0512FAD2FEB51F5C24109E284FADFFD8484A7A053E8EA544F
+AFC1C7BA21E4824866090E542B8BB0BE603060A36D8A8CD64EC92D6037B438EA6A7F3A23E9608F29
+02E8DB10F2854D89E2CE6F093E6804299305BFF3D1601C2830F793B92CC2542A6CB0E9DA602E00FF
+DEF234702D0DEFCD270F2984642F13B818F65BD407B61227A94AB11E7ACE8D40849808AB3A7E6EFE
+B2D4F3B9FCD233B8497B35299DF28F651B0B2960B4545BF2C05952229EB1CF676DA761995413051E
+885A529957538A8B2C6BBDCEDB2A3F4104004B880624C2F55B544EEE8DD29231386492598C2BE995
+6E7BA78FA75FAEBED43807FAF072839BAA02333D38BF4A59B1F3ACEA5C7BC188C0BE8A8BCF2069BB
+E36BDB2203EAC19D249C4ACFBF8F9717111B7F4637D631D058EAE0CD4E4E8A9F0ACE1BE19B3A241C
+9C6DCE2513690473D8F4A59E1A7FCC926885CC324086981FE0C6AADB8F48116822C59B1E93F53829
+77CC88B82FEEDD2B4CC6095691FFA1419D5A6850D3576C4E705C676850D0BCAB84D52791355DCC7D
+70A7F5CF603B4D0D7F0CF4F25AEF44F21A2D627D45D306DE0BE1C794AB90DD1EECAB2D61A115D3D4
+AFAD1914E808B16BC868FD48208E1F915B0E8ECFD59AA5895CD7ACF95E0DCE87DD8B12F683C8EFD9
+625CD388337C262013626A06ED60556FA70A8D425D36B48E430758AB4CAD34A656F457A60E4DF933
+07C4D5B2698501E6D1F270CFF70E47DE38A5FCA2B04D7BEC3A2945EB93C49F1D366835581B42F43C
+C99D71F13F45A9FF8D12E26C43E1B994BE5AB44E5309B0F936D78C93169D666DDE6D18E33A5E016F
+32278897FD7E76BAFE39498C6A849F4A6D882D109C40B42488059554CC95530FBBAB2591DCFADED7
+3EE5F2F2BBF17F4A131B8126F9E0AEB4B379CEE4EAD92A1BB29E3789EC19671E77558B4DE961629F
+28B49DB4D8FAAF541D23205844EAA801FEA468D26F32BF9CAC30BCA244246A55F600BFBB61C5E8C4
+10CA07319DD094770DFFC1CB700DF67097F61C46036353C8AB3A5E5198445A194BF189E20490E970
+7D2C03C1A003BC782A66841AE5DDEED2297BB6DD019C98A66A8F279748DA39C85CE2082D09210EAF
+CA995591A1A3DCA52EFC9A752DAE0CFB125127DA2932AAAAD7E9850AD78D48304260B4C270EFF12B
+160DEF2C2B8E30E8C137975AD20046F37A72F1355F31D878334D96313D330C24EE7350D4042AAE2F
+A345EDCEB133121D4B39645B1D6114D0597C3301B4CA56DA2B4A457D7A51BD13B7AC61FE6E1CD451
+6253F606FD4B57E9F4895CAC93691EEBC2AC992CC5D122DD3FC6A9EC8FD337CD402F03F901CE7986
+2F4F4E4500617DA0F913E357BED3ED04F49FD61FD1C66606CB231C3B7A5042C7C07EAB2E02BE8CF6
+AEE5E16B4AB725B5FB5D01EAD4887E365BC2ACA579BD80E0AA686E4A08DFFC70F99132353E3D5898
+5205807271753BA3DB7264C4567DF5FE999514F28E1DD6D3E966D8810978B140F8DD9BB259078A47
+013BCF247C37F543C0899B532F34843CA56F18F688B42A12DDE2A90CA457860540B6FA138F753DAB
+E7331188ED6F535480FBFE934F68EFB1C9C16D4F11EDB35F944EAE63751101928EDD0E7AFE64D7C8
+5E9CAFCBA88450DEF9122A245FC1ECCF9EF8DD94EE6A70CF16ECAB39B52597AB1E8C47B6DA4FF0DE
+C7D0FABC84DDCC8C652DD7C941DB3FDCC5F0542A8F433AF9FDF4D393E123884E1DD5D359D46DAC61
+0694020BAE84B3BD4C068E3BC871BE21DA13571BB61387E207926769236776B5B31A4A462902966C
+DC3D92BE171F10EF8395D2402F0C492A3FE55979CB903CFF2CD2319CE4B46481489E798A131635CB
+2E70147193FE3C8F4570FD01BE10E004B17341C4DB8B029BCBBC45D31227A684E5F38F5D6F0821C3
+ED13D31DBCFF51BD759C84A98145FC86D82F871D2D83F43C3DD7FE9A064120338A7BC63A2C60667E
+25B50EA1C267174B334F4437856295A6B826F54C3EA9ED39CA6909A0F6D9669F1E75A7A05CFBE7B4
+2C330668E311872177F0BE3A9B3EAE611EB48721AAC2F10C3CCD897CEA8A136E5E10C2337BC5EEB2
+FBC1A3646A6B6792CA3946BA5D4D135D929547042A2F0D0A202A4D86E3F7098C823E7AE4331CEC6B
+607F6AA434180B4153F6B10DCC914A92D6D0934551BC9DDB3C78065B2177B264216AF5524D798AE6
+2A90D00A70CB36C5B9950499163C2C1B04339FA76D28E03A4D0C80FCF7BF29B8A188C67EAB6A4BE7
+8C07713C7EE09043301B5BFD60222DD0D0943180AFB286D2953A8A12661986A4812E2C0DE5B3E703
+DAB34AF0E9306A5711D286AD09A3C6AB80841491BD0E5A1D1ABE1D600CB494BC17EC4F74B45870F6
+AC41EFE16BB6C87F382DFA4B2B8DDC9C2E912FD139A2FFD5C92D836F3D749EEB985C62A376849751
+A6AC56B1F679A85FDAD9448DC7A4CFE323AFB540408547C53297DB6FE9A0B08901BC285997934A2A
+1772090FBA175CF4660764B87A21A738519D5B619840D15F45DF3589E8B80A6DDF1F395B65345869
+58C7060DC131700DFF6E25962494583085E6F8BAEA557A5666E6634E4704D0C07BB0A2EB228A7BEF
+EB890B4EEC638303B8005BCCE922CF3A7AE627206F2946A142B0095FF960BB8B8F9C975B6FF07479
+2D5C3DFA125B7BE7A8356D8B44E264AF6AA582DB84BA09D2FFDC2213903FE8FE16DE5EF61E518DE0
+6A29D98E217038A4CA4D219E4F114858CA493AEF0CD6495A7C5EF1ADA06AB543051B1A5213952D46
+648BE06D15B1728768BB853CE32943AB0988D172227780CE82D1A1D297D0D6ECC51B290E156645B6
+9BD54699940AB17EBE10EDF258BBA6BFEA39F4F0B066FF6B3FA16C7C72F2565CC028F249BAA4B488
+B48A2513DCC5D1E205FB874BCFE45612DF4EDAF815CDD53CBB80842B429B1AFA32D35EA58E17F4E7
+252C2C9A737AAECFC25FD8CA5520D3EA38AF71C61B88F31FB53A7F5369305D63495ADABA455C3C4C
+35D9FE423A00CFBB278CD482D3FD33BEEAC1F359AD9B6AA17D60CC46FAB670ABFB3B2B4161B9EB9D
+949A06CC3B734F63DE821FE0B8EA065B6F79C0601E46E8CEB6E2DE0C052771E0EC7012063F1AC46C
+3FC454767469225AA266784DD77256C6FDE25D7D857FCEF2562AEBE38A9A47063AEF91449723E680
+B36D824BB95BE95C9802F7AD4DF1AF37B4777A1E116A7EEF770F0A499BC9F9675A775503091D4EBD
+F55118782E3E54AEBC67A0545E8D75BA5A482C6CEC50595D45AF041664689AA4338C1428870CDC15
+BFEEF788C1D1E87B3389103225E2619120B129FDF048DF3F5DDAFF1CE87428E6BFA591B91D82720C
+E4A72FBB952403435248657ECD5456CA814FA0ECBE70BC3D391AEA0A195AE8FBE92D054AD0F3E549
+841ACDCE59DCAA9DAC20348A1D05DF7B4127C75F4C6607EF8501F741CBC96AD8D429A4CA8034CA52
+9AB673EE706F1FF51DD93F45802031EEBF8F3AD29D2A74888B1CB344F88ACEB9B4DEA13ECAE0A3B7
+1FF5CAA38D0DC484B96E90251D56732B85DB8A8DBF10E0A5921121CFDFDB6EA9CA45B98584401C3F
+346BD026DA2A17E75CEEB74E48667F9BF5C538623E88021F0C24EC7D082C3E1FFDE5A6F68F0F3F6D
+2B61547C4C14614118F7942851B090D6C8D80BA06D9139CB5AEB0EF77DEF2376C0BF602FB1083178
+27E86DDCC00622438FD7A268C0252CA2E20DA887A9D5ED251FD5CCE811E514D702206E413DEE7528
+3A6AEF793F5E3E3B0B10E59226102DF2A7347A95B96E39C6B6ABD1FB4B7CDC0813D7390E2819EE50
+7E5458E06D43CC3BF16E40CEC4E64909615CBFCD233B5D316BC8B8B46243CDB7F2FBECA2F208D0A7
+985048E88A5E685A9EE8C3A351FFCFB522C1EE41A8E4EF0A080C0D110D2A0D8A980AF1F604D1CD52
+3CF41D08A45EB809AF11F530C138F256FFD49BC20F77005D004AC85FD563E8E8BA5F79CC60FDE714
+1416F4C14FFF281ECE2BE1D167B7B1ECE17790454F795B056CE4DF1D08F25F0E5E2A16CFA066AF97
+F3A80015068FA2A1D65F329A5CF7856114A77EE7295C7929CA27796D8C51B31347CBC7C4EEF1E75D
+12237C558BE5B65DCAA4CDDCB419E543B1785DF8B225C2E9778839123E6F85E53D925C6D2FC0327A
+EF9CA13D1F2F9B3E510A1A4190D3057227621788AFB6567132238BE86559BCA006B85AFBB8AFE5D7
+A8CE58AF731F2550108C6B8CD63632184B1E218F01881CB94CEBFFC310F263565A39C83C874AE474
+AA213F7DD9CB3B8446A3E47E81BACF7B16E4A80899BF2D30C28F9DE0678DF364589DA6A454398E30
+21A92AD1D8B0DC494D96A06C7568E34547F0E68829E5D15F6500ECD7403B40B5CC2A479D0F8CE3E8
+709861608AED046339BE055BAED1A6BD2311CF918E74378D893710D42A769671326E947F108A38C2
+3F92EF5C6A50EFE36E858671F17151C719B9C7D60E8E429F088616D616080DC676760BE83A3F5229
+6C4B13CA3818E3FEB56D51DB5979F28063F7D537C15493D57CAA1E55438C01C794336D9F21520484
+FE5B5BAEAD7121E3CCC5086E2F2191DBEE9527CA61CE85CC0A4D99EA29169B17A10B3D4372EA493D
+48CF572E570CF48B3B7512268A9AC4CC161F97695D19412C36F1D67522E272B1C18B6122A355B43C
+37636E71462503C43C1AEF5ACE34A6442075B59892BA54E9B5A99B9733BAF64161450F8ECF4FB2B2
+DA6F9AAEBC827CD9AEF94BB463935C26E1DD5DDCA5BA87E0EFB62D9D3226DC28427EC097B68C9EE1
+E47D125F884F8656C8AD55D625A8F7F1A306383A5036A12E63B6FAC4A215AC88725611EBA74B6770
+B20F45356879DB807E9C9105D14C83104B2DCB4F274A2F37152425935B3F2D1633E4444540F7411B
+24C17055850C9528D912F9DD40984CA1B3BE277F82198BC99B80233B8AAB674A44B42B9123E886B7
+B3161D48B4AA24970BB75A983569690BFD4B92E6ECB410DDCA14A3BF59057397FF1AF159BE230216
+FF2F7009C3A3DDD81F2BD1F5C10C2C2F30A8095D7371427AC9D527AC6794CF8593303F786F21CCBE
+36DFBE01B0ADD78DB3C9483D9DB09A9E2577A3A4296E7BD09C41F5C91B29C9D7E69EF06B5BDB66E0
+853C3DB133769BC8858121A350CADC1D61C77C1A313833D077938AB2E10EF970590256479B1549B0
+401A9C5E97F12A269A305B372F0EFC035B06AE3E2187D6E3A62DE3B4E8075F0ECF5BE32A5B97A4AC
+23A7335C4AB29917129C08250DBD1D5BC123C92E821BBB1A7598E32DDDF1876C6FBB7B858FD317D7
+6022955595338AAD2BFB3B17DA11380E7473A5F566C6329E0F38D9A67E68FD4B8CFCC82FC0BD7BA3
+9A25D5BD0952CC71CF0BD7B5EE184A8E62FBB366D95AB051BC3A80E9F6C27EC100E00C4A002291B0
+4D758A9683657FCB07A2AA8B07DF585CD4205B4DC55330E8A2A367CBE966E84618ACFCAAF81C8A9D
+3A3165FE5A623720A0C11CBDD5CA200100DD2C970A09B124D98987D6A2CA7CBB40DA5B585B86FCB3
+8E96F54C64A921D7A5D2F805C67339DC4FC026FB2EE533BB9700F66F4AD58949B3DB75D770C4D7C5
+C23DC720D78124A7ABF30CDFF10C708219903431C7B1A19317341CEA22AA11A588F8AF9BFA4AAC40
+31EF22F13BA597BD1E5958CEA741274197113CB0B7975424C17F73733ED30BDC0170CD0084BB7DC1
+8F031BEF2A6C44D0EA0EBB84E31D5B4C50B63DF0AF96AAF33E3C986D4B82D161E2F9049DE9710EF8
+A11BA0E08266A3547F20567E582EDB47633772D37FCCD119AF5B11F338098F185D5B055975F56092
+CC6979FF49487D69E08A4E8754FD51D29410F56E0F3EFB9C3B86253B3B0A39E9D217604F664CC696
+634D216524F976D066034DDE8EDF5E8F9644337C50C7D8E2230B38E0EE88C53DEEDB3363477E32CB
+88B3ACB6931B9717FC93F8D1A9DDDA3A7D8A56783F2B651A1DE216493C34DCD17A7635D21CEC0770
+751424B599B00CE950E60D8A8B3444F1629220DF073137E2ADE1B6243C1C69BD5E10450B948BFCBA
+46A841B98BCE7C86E120789C73C87E581868021E68F78CB903AA6D494331174CBF944221DEA9E567
+A2542BD399C5E471D972FAB7E8950AEFF3D91A1296E685B0BFF91367C2703DCAB10AB236DE603E3E
+993C4C9020EEBFE89726AB9FB92AF9D371D7188448CAB9FD2704506C39F0842E36EB8B9AAFB1F26F
+CD366E66725AD28B19FA57537757EC71B04B451F056C3AEBFA04D1C0CABC9492B6B8D239EEC36776
+D6B515ED43A66AB4342BE4C8B2027F2D008EA231A24B472DA8352A05DDEE31CBC3577E5814DD2D4E
+C17A216A7C8FFB27012346C9ED12F7819B96A5B135B6196E888C9AC73D7D4B7DC370E2FBFA17FD74
+0BF516CFF69294493900BFD63721A537BEFB31C5262567103F9CEA4E8DB02964B983E1AF190ECCEF
+41F552C45E9B94E29AE3F129687EC35719F591C1987A08DCED3B822344DF81A70AE78E14A81EF1A1
+A77FD5D19F7D35B7C12A473EE11C655E15DC5D3C94F226CBDA85377338BFCEA18359176CA7EED622
+84F9015F2D592E27F1037C95570CFDCA3B9B90D35DB8C341434BBF04C0E692D4C2C3F59EF386CD1C
+5A8C783198EBE42C89E3B64C662F7875325982E9299C18DBD1FD2257DD964F9F9DDEEBFE56E4AE9E
+8163E8C58581BFD5818BF396CCDE3A66F58E17BDD262D63E5D9AE0D0E43C304CCAC4776B87D40F6B
+5AD7E625B2065FAABA81AAF2E2D32E0ACAB12DB2A9FE9C6160FBA7DE5AF019810AD9C64B2E6567B0
+F0108E8EDEC635F4BA88BDEBD3561CDBBB9063B4D19C493E0CA4F551255A7DF6BCE96C17A5DD4877
+7F654F4F114CF29ECDB9779F63EECDC04E1FA06E48701547A8F96483B4195C0DE90B0FD1B95B5F12
+5C43BB973D0B6414A994595359289B5FFEE4A08D684C2E7E3F417585581318D54DF2EEAE7CF36E2C
+A1A70EA76154B88C1E4FFDF5EBC28ABE264B40583F2DE1F761DC25DF242396AE502CC63EFD6EE284
+DB37B0DF36A8833ED3EA5A1DB372C8E1F3203E6E945A53D83B87250CD72C851F68A602E7BCF92DDC
+E600D518069F8900F3BCA434D21694229572D55F307E0FAFA3DAEA864BAA19F227097963CE83882D
+22FB7F1AA3FE558A3669BB7FA37C0A591B4BF3E2A2FF85BFEDE5E424E0AE337EABDFD67BBF38E160
+09A687D2E7C1882DB79304D722EACA233EFA489B10E8D4D404D12A5A4683FDE377A9A952CCAB257E
+ABB408D75FF130206DC3447F26E74D1D00354313E340878917177DBD2AA5A2A70E2087D551DA5181
+6FAD07F6E826DB0BF21560BB26906D3100E12AA3801D4A8BDCBF1A0A8D58F1054EE2A0DB04ADDF3F
+EAF1FD6E322BBEE274A4995EFFEAAD0F24D9FB45730F6BA0F42D88BE4AEBE9777F8AF508DED61024
+EF955B26ACEC51AF5C21BAD7AD93CC0C9DBE03C0FE9637A3099E5EF329051C87FFF70042D788F21F
+4DE645FEBF58374F7E38A9AFE3DE4D2888DD807A09169DA8516DFE37591C122A4798343C1A8121C7
+430F244EE5B19A21897A4423E21CE0492E75C9320E37BD65F1EACA7FC6FE032842E4D985E666E633
+AE5CC62B3449404672B284EA5C6A01E927787104ECCE1354D1C0E5AA2452B7B12937B946B74EFA98
+8C5C79EEE5ACDCFC994CAA853AFA08EB9E180C5E898FDDF8903EB0863E98A4FA537207C154CE5B98
+EF4E97FB6A1CBB07C45D34221EED26998EC864475D231FF76E830A7C0900C4B5FCB980E3F67F4EAE
+3DD8086CFA1874E65C424646C7A8F84272DAB04A5984FCE3F4B89380B3A5C6F04EA3B683CC224913
+9209136336AC9349AA89690B5DE0D93E92F996BA7E346B043EBD45A35FA297DB70E9A6250B138674
+FDDFA609BE11EBA8106EEE3923B4E2657CF7A82443C25022A5DED862E8E3A5BFA7B8AC1C1B804B23
+C08A7D8FE594140E8432C1A5E183FE361B6B8C763054458798B756A6CD2535FF4667C634DF78AFBC
+C87D40DCD56FFFA12CFC6EDC6A8AF41AF657E0137320624F1895D9AA84AABAC5F3653E977B054C52
+92B06FEAAAC0E5C7FD45B6654D0D9CCDF292A8489E1E6CD929F0AF25B9604CE33BD25662133CEE99
+CDBBD00F865431193767480700CA019D694B38D695CC29F732B5B39B6CE654F1AA18AC3223B60E55
+3FC3C3E114E6A2E59617BFD58AD8E429200B59E035DB7D67C8DC6C72001D005924670ECF7A06FF5F
+7B3DD5556DDDE1BE5163F7CD6FC969033EBBAD85856522ECA91F2A200A75BDDA69070240D86C2D7D
+959F1DBE45D488A96F308269E8262327A410D057446F596418DF4A12267A7A3FF5ADEA0846896D3A
+EFE3D5C72387F7EEEE809BCCD23D1126B86C45A0F66404FAF617D03379A2E44865051E92B4C835F7
+21492147DE3DCCE47EAD192D3F10A5DD459634D6C253F4D09DD98E7DDC836EED8DE08A78A6F6FB93
+84565CF6DA8D4C948144272D5F8787643B9B550F96E42A8CFC0F1C1F2B14D83201199E9E1F5BC0E6
+51E39E16A744930F4409C61CDF9F456C7EAC5C62BD22CE0DDCCDF3755DC670FDC8FEBF09F241BF95
+1A1694BD70B9695A15653F719DF4374AFC6C27F58DD144253BDF86FC1BB3D4FEAEEB9196BCEC7168
+AF1488AB7072751FA24C6642B87DCADF4B2427631E7D7FA39D126410189CD8E24CF0A490FDC7045A
+A9A83D02CAB57B9C27211FE1805080FBCEF23B86CDB19885A574C589BDB15C4CB3651FA3D2D6B619
+4449B16D927047E376A84F9423562CD548D192AD0A57E0D5B41010D1C7D87929DB6F456B0A073DF9
+A8220BEB3D4DA01518B5C85C82ABCC821BEEC1965283DABF6AF3AC55ABDDAEF1B53E6119DBB4A114
+44FB1769F9891B5DC931ADFEC8ECEFFA179427FCB08C1B149EDA70717C5E9322F3CE9405438481F3
+90968A38061EDAA2223147D6143877E4C4E645EC0A9803A933116D610491FDC639DF69B418156772
+E14A428864982D3230EFFAA1D2D3388B0F70D8FE202CDD6CB0E97B3290ECCF4A1A69D93C11D3D735
+FF0B5A9A161A86CA4F16B7C231B8BF71D5083697B2F24C0BDAA6E83E5DD2222DA248D4ED3B5509CC
+7CA40D8D5A2507A1D40FC1AE9486343CA2B2C65B6CED9B1F4C0535ADF7BECCE45E1DB74C7417B06D
+897C6ECDD05F068857D931B3D960103628675BE2FB82DA774ED3786670D8C2BA4ECC36C203C7F7E0
+9C97A09E3B2E2C3EC30B53A8825519B664D09F149AB73F183A4A22C8F20E2052F76649AB881BA28C
+6C3C3950EA9B3961F87F6AEFF24DD7B4E10DE28C15DCB27F593F98CBC9CDE19360C5531B2A0C6DC1
+8FAE2832AF49BF9522C068F3036516F711AADEE496EE4E3EFA2F55EA4891DEC6D8A868E0B0017076
+3040CA4D42A73A71057DFE315FF8625BBD74F41E5CA77B7DF096C2F4F0D51B1184CA9F2C8517FCB3
+956C44873D630E94EAA1D2D1451B332770172B2D7A21ECAC864C612235C0B39ADF55EF074F2073FA
+D3B54A66B07AEA1FB495A9F7269BD1C07692B0B762DA6881EE6B265ACED0BF0794C0397F8D8B13FF
+275BE77561067E2C1FA536131184682BFD32384C6488012D29E8E55838E5A5AA7C40C20AB6B03BFD
+BF4EFD2001A612100D585BAC1C77C1CB1ADAF2B000E09686091AB7D1AB6ADE395A03580BD78E961D
+14D052E7D4A61227534B55FACAFC4E8F9326DE35BA53A463F7D94B705698300771185DB19E78FABC
+D0AE4FCE005B79C795B692F2D9C00B6A61D4B343C35E417BBA169EE82A2E4AE204A126B08A94191C
+6E5B5E8328A147BD6ED5A5AD5C9143A50C47789DCFA699720336AFFBD6B1646D8C35139B0DC34340
+C76C7E4FD72DABF80BF64BA04742D07B380E20A678EBCB19057F2346FEC653F1302992279DCCD2E8
+902B2B8F78435AF3400DAE319E94E3148BA88C056701D524DD67FF368C85EC6366F31689A62FFC03
+4BE436588ECBB8B41E8C43112F3B65F50D20A5EC51A26FE899E74731B01C7771F75B76070DC0B223
+1845BE9C09670E65C4DF54C0FB36511B735251AEEBD13FDC0FCFC3134D8DA826E4100521CFEAB202
+B83267EB3F69AEEA25FDE1E9C86407E38AB4CA2D1B91607EFC96DDC5BB10FCD46DEAA5FFED196959
+12302ED55DFACFDC6F22C1528AE3A9902B75F46A489352C87B0F5056FD149DDE67219A3C3D1DD875
+BAD81AD443E0F90DC79D979E10B4262DD20B255B04BBBE5C988B23667135CC61D988928F5F07753A
+C76807203942F6B62A178A1E5CBBFBBAEBE735D09B747BF00C360F2E2B6C9F2261F7DB018EDF40B1
+3DB4B7962D4D2F858E1FA6376F21ED9CEA4BEE07FFE27F136BF60352AFB71CDBFF7E65F6B4624E16
+C6E5AB7FF60B4711A178FEE691DD8D362B14175A69070FBC5B9EB15248E4DFDA3C9453335D0E1EB2
+7714E4624C7215A73B53402B02CF7AA05A14CE1E5AE161B75F218BF0EE1215D3CD925D5ACB7DBD2E
+BD8E4405433F432C491999794C814D5B384B7A1C0AB28A11CBD0A6EA12678508CF86232309B0BBF7
+EEA929FB7951F5A6B367C5AC57DD7D3B3C1E2188DD4664D14FBC32B1A1EB53D7EE7F55F4D5C2D014
+528EBB7F0E595F7618721451EE6B83EB240A6C4D33377893D4EF542F47EB2845A09759D5554C74DC
+5E9109FFC8C929CF1AC446D8149957720EE4FB670D3CA61378549DB992126B23618CF49361D6D4B1
+C73C3D37E4A4465ABB349CFA34E9351D1192E366267EDF02DE432ABC80792B0CFD41FFD0AAA42E63
+3F5B2177A351D33477C636CA573CB02C07F7F7A41C9F1BC4C112BD6459DD130757D2BD6F47495C3F
+92E99522871DAC2865
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+cleartomark{restore}if
+
+%%EndFont
+%%EndResource
+9.72 /Mathematica2 Msf
+0 9 m
+(\310) N
+%%IncludeResource: font Times-Roman-MISO
+%%IncludeFont: Times-Roman-MISO
+9.72 /Times-Roman-MISO Msf
+2.25 9 m
+(q) N
+9.72 /Mathematica2 Msf
+7.5 9 m
+(\310) N
+9.72 /Mathematica1 Msf
+12 9 m
+(=) N
+%%IncludeResource: font Times-Roman-MISO
+%%IncludeFont: Times-Roman-MISO
+9.72 /Times-Roman-MISO Msf
+p
+20.25 9 m
+(0) N
+P
+p
+24.75 9 m
+(.) N
+P
+p
+27 9 m
+(0) N
+P
+p
+31.5 9 m
+(2) N
+P
+P
+[1 0 0 1 0 0 ]  concat
+1 w
+[ ] 0 setdash
+1 w
+0 g
+0 g
+[ ] 0 setdash
+2 setlinecap
+0 setlinejoin
+10 setmiterlimit
+P
+P
+[1 0 0 1 -118.485 -136.705 ]  concat
+1 w
+[ ] 0 setdash
+P
+P
+p
+np 181 96 m
+181 112 L
+219 112 L
+219 96 L
+cp
+clip np
+p
+1 w
+1 g
+0 g
+[ ] 0 setdash
+2 setlinecap
+0 setlinejoin
+10 setmiterlimit
+[1 0 0 1 181.505 96.904 ]  concat
+1 w
+[ ] 0 setdash
+p
+np -1.505 -1.904 m
+-1.505 16.096 L
+38.495 16.096 L
+38.495 -1.904 L
+cp
+clip np
+p
+np 0.495 0.0956 m
+0.495 13.096 L
+37.495 13.096 L
+37.495 0.0956 L
+cp
+clip np
+[1 0 0 1 0 0 ]  concat
+1 w
+[ ] 0 setdash
+p
+np -1.505 -0.904 m
+-1.505 15.096 L
+38.495 15.096 L
+38.495 -0.904 L
+cp
+clip np
+%%IncludeResource: font Times-Roman-MISO
+%%IncludeFont: Times-Roman-MISO
+9.72 /Times-Roman-MISO Msf
+9.72 /Mathematica2 Msf
+0 9 m
+(\310) N
+%%IncludeResource: font Times-Roman-MISO
+%%IncludeFont: Times-Roman-MISO
+9.72 /Times-Roman-MISO Msf
+2.25 9 m
+(q) N
+9.72 /Mathematica2 Msf
+7.5 9 m
+(\310) N
+9.72 /Mathematica1 Msf
+12 9 m
+(=) N
+%%IncludeResource: font Times-Roman-MISO
+%%IncludeFont: Times-Roman-MISO
+9.72 /Times-Roman-MISO Msf
+p
+20.25 9 m
+(0) N
+P
+p
+24.75 9 m
+(.) N
+P
+p
+27 9 m
+(0) N
+P
+p
+31.5 9 m
+(5) N
+P
+P
+[1 0 0 1 0 0 ]  concat
+1 w
+[ ] 0 setdash
+1 w
+0 g
+0 g
+[ ] 0 setdash
+2 setlinecap
+0 setlinejoin
+10 setmiterlimit
+P
+P
+[1 0 0 1 -181.505 -96.904 ]  concat
+1 w
+[ ] 0 setdash
+P
+P
+p
+np 267 3 m
+267 19 L
+305 19 L
+305 3 L
+cp
+clip np
+p
+1 w
+1 g
+0 g
+[ ] 0 setdash
+2 setlinecap
+0 setlinejoin
+10 setmiterlimit
+[1 0 0 1 267.864 4.035 ]  concat
+1 w
+[ ] 0 setdash
+p
+np -1.864 -2.035 m
+-1.864 15.965 L
+38.136 15.965 L
+38.136 -2.035 L
+cp
+clip np
+p
+np 0.136 -0.0353 m
+0.136 12.965 L
+36.136 12.965 L
+36.136 -0.0353 L
+cp
+clip np
+[1 0 0 1 0 0 ]  concat
+1 w
+[ ] 0 setdash
+p
+np -1.864 -1.035 m
+-1.864 14.965 L
+38.136 14.965 L
+38.136 -1.035 L
+cp
+clip np
+%%IncludeResource: font Times-Roman-MISO
+%%IncludeFont: Times-Roman-MISO
+9.72 /Times-Roman-MISO Msf
+9.72 /Mathematica2 Msf
+0 9 m
+(\310) N
+%%IncludeResource: font Times-Roman-MISO
+%%IncludeFont: Times-Roman-MISO
+9.72 /Times-Roman-MISO Msf
+2.25 9 m
+(q) N
+9.72 /Mathematica2 Msf
+7.5 9 m
+(\310) N
+9.72 /Mathematica1 Msf
+12 9 m
+(=) N
+%%IncludeResource: font Times-Roman-MISO
+%%IncludeFont: Times-Roman-MISO
+9.72 /Times-Roman-MISO Msf
+p
+20.25 9 m
+(0) N
+P
+p
+24.75 9 m
+(.) N
+P
+p
+27 9 m
+(0) N
+P
+p
+31.5 9 m
+(7) N
+P
+P
+[1 0 0 1 0 0 ]  concat
+1 w
+[ ] 0 setdash
+1 w
+0 g
+0 g
+[ ] 0 setdash
+2 setlinecap
+0 setlinejoin
+10 setmiterlimit
+P
+P
+[1 0 0 1 -267.864 -4.035 ]  concat
+1 w
+[ ] 0 setdash
+P
+P
+p
+np 295 86 m
+295 102 L
+328 102 L
+328 86 L
+cp
+clip np
+p
+1 w
+1 g
+0 g
+[ ] 0 setdash
+2 setlinecap
+0 setlinejoin
+10 setmiterlimit
+[1 0 0 1 295.617 86.954 ]  concat
+1 w
+[ ] 0 setdash
+p
+np -1.617 -1.954 m
+-1.617 16.046 L
+33.383 16.046 L
+33.383 -1.954 L
+cp
+clip np
+p
+np 0.383 0.0458 m
+0.383 13.046 L
+32.383 13.046 L
+32.383 0.0458 L
+cp
+clip np
+[1 0 0 1 0 0 ]  concat
+1 w
+[ ] 0 setdash
+p
+np -1.617 -0.954 m
+-1.617 15.046 L
+33.383 15.046 L
+33.383 -0.954 L
+cp
+clip np
+%%IncludeResource: font Times-Roman-MISO
+%%IncludeFont: Times-Roman-MISO
+9.72 /Times-Roman-MISO Msf
+9.72 /Mathematica2 Msf
+0 9 m
+(\310) N
+%%IncludeResource: font Times-Roman-MISO
+%%IncludeFont: Times-Roman-MISO
+9.72 /Times-Roman-MISO Msf
+2.25 9 m
+(q) N
+9.72 /Mathematica2 Msf
+7.5 9 m
+(\310) N
+9.72 /Mathematica1 Msf
+12 9 m
+(=) N
+%%IncludeResource: font Times-Roman-MISO
+%%IncludeFont: Times-Roman-MISO
+9.72 /Times-Roman-MISO Msf
+p
+20.25 9 m
+(0) N
+P
+p
+24.75 9 m
+(.) N
+P
+p
+27 9 m
+(1) N
+P
+P
+[1 0 0 1 0 0 ]  concat
+1 w
+[ ] 0 setdash
+1 w
+0 g
+0 g
+[ ] 0 setdash
+2 setlinecap
+0 setlinejoin
+10 setmiterlimit
+P
+P
+[1 0 0 1 -295.617 -86.954 ]  concat
+1 w
+[ ] 0 setdash
+P
+P
+P
+P
+P
+1 w
+0 g
+0 g
+[ ] 0 setdash
+2 setlinecap
+0 setlinejoin
+10 setmiterlimit
+%Trailer
+%EOF

+ 10 - 0
services/clsi/test/acceptance/fixtures/examples/epstopdf/main.tex

@@ -0,0 +1,10 @@
+\documentclass{article}
+
+\usepackage{graphicx}
+\usepackage{epstopdf}
+
+\begin{document}
+
+\includegraphics[width=\textwidth]{image}
+
+\end{document}

BIN
services/clsi/test/acceptance/fixtures/examples/epstopdf/output.pdf


+ 28 - 0
services/clsi/test/acceptance/fixtures/examples/feynmf/main.tex

@@ -0,0 +1,28 @@
+\documentclass[a4paper]{article}
+\usepackage{feynmf}
+
+\begin{document}
+
+\setlength{\unitlength}{1mm}
+
+\begin{fmffile}{diagram}
+
+\begin{center}
+\begin{fmfgraph*}(41,17)
+\fmfleftn{i}{2}
+\fmfrightn{o}{2}
+\fmflabel{$g_2$}{i1}
+\fmflabel{$g_1$}{i2}
+\fmflabel{$p_2$}{o1}
+\fmflabel{$p_1$}{o2}
+\fmf{quark}{i1,v1}
+\fmf{quark}{i2,v1}
+\fmfblob{.35w}{v1}
+\fmf{quark}{v1,o1}
+\fmf{quark}{v1,o2}
+\end{fmfgraph*}
+\end{center}
+
+\end{fmffile}
+
+\end{document}

BIN
services/clsi/test/acceptance/fixtures/examples/feynmf/output.pdf


+ 28 - 0
services/clsi/test/acceptance/fixtures/examples/feynmp/main.tex

@@ -0,0 +1,28 @@
+\documentclass[a4paper]{article}
+\usepackage{feynmp}
+
+\begin{document}
+
+\setlength{\unitlength}{1mm}
+
+\begin{fmffile}{diagram}
+
+\begin{center}
+\begin{fmfgraph*}(41,17)
+\fmfleftn{i}{2}
+\fmfrightn{o}{2}
+\fmflabel{$g_2$}{i1}
+\fmflabel{$g_1$}{i2}
+\fmflabel{$p_2$}{o1}
+\fmflabel{$p_1$}{o2}
+\fmf{quark}{i1,v1}
+\fmf{quark}{i2,v1}
+\fmfblob{.35w}{v1}
+\fmf{quark}{v1,o1}
+\fmf{quark}{v1,o2}
+\end{fmfgraph*}
+\end{center}
+
+\end{fmffile}
+
+\end{document}

+ 3 - 0
services/clsi/test/acceptance/fixtures/examples/feynmp/options.json

@@ -0,0 +1,3 @@
+{
+	"compiler": "latex"
+}

BIN
services/clsi/test/acceptance/fixtures/examples/feynmp/output.pdf


+ 12 - 0
services/clsi/test/acceptance/fixtures/examples/fontawesome/main.tex

@@ -0,0 +1,12 @@
+\documentclass{article}
+\usepackage{fontawesome}
+
+\begin{document}
+Cloud \faCloud
+
+Cog \faCog
+
+Database \faDatabase
+
+Leaf \faLeaf
+\end{document}

BIN
services/clsi/test/acceptance/fixtures/examples/fontawesome/output.pdf


+ 16 - 0
services/clsi/test/acceptance/fixtures/examples/fontawesome_xelatex/main.tex

@@ -0,0 +1,16 @@
+\documentclass{article}
+\usepackage{fontspec}
+\defaultfontfeatures{Extension = .otf} % this is needed because
+                                       % fontawesome package loads by
+                                       % font name only
+\usepackage{fontawesome}
+
+\begin{document}
+Cloud \faCloud
+
+Cog \faCog
+
+Database \faDatabase
+
+Leaf \faLeaf
+\end{document}

+ 3 - 0
services/clsi/test/acceptance/fixtures/examples/fontawesome_xelatex/options.json

@@ -0,0 +1,3 @@
+{
+	"compiler": "xelatex"
+}

BIN
services/clsi/test/acceptance/fixtures/examples/fontawesome_xelatex/output.pdf


+ 17 - 0
services/clsi/test/acceptance/fixtures/examples/glossaries/main.tex

@@ -0,0 +1,17 @@
+\documentclass{article}
+
+\usepackage{glossaries}
+\makeglossaries
+
+\newglossaryentry{Physics}{
+  name=Physics,
+  description={is the study of stuff}
+}
+
+\begin{document}
+
+To solve various problems in \Gls{Physics} it can useful to express any arbitrary piecewise-smooth function as a Fourier Series composed of multiple sine and cosine funcions.
+
+\printglossaries
+
+\end{document}

+ 7 - 0
services/clsi/test/acceptance/fixtures/examples/glossaries/output.glg

@@ -0,0 +1,7 @@
+This is makeindex, version 2.15 [TeX Live 2011] (kpathsea + Thai support).
+Scanning style file ./output.ist...........................done (27 attributes redefined, 0 ignored).
+Scanning input file output.glo....done (1 entries accepted, 0 rejected).
+Sorting entries...done (0 comparisons).
+Generating output file output.gls....done (6 lines written, 0 warnings).
+Output written in output.gls.
+Transcript written in output.glg.

+ 1 - 0
services/clsi/test/acceptance/fixtures/examples/glossaries/output.glo

@@ -0,0 +1 @@
+\glossaryentry{Physics?\glossaryentryfield{Physics}{\glsnamefont{Physics}}{is the study of stuff}{\relax }|setentrycounter[]{page}\glsnumberformat}{1}

+ 6 - 0
services/clsi/test/acceptance/fixtures/examples/glossaries/output.gls

@@ -0,0 +1,6 @@
+\glossarysection[\glossarytoctitle]{\glossarytitle}\glossarypreamble
+\begin{theglossary}\glossaryheader
+\glsgroupheading{P}\relax \glsresetentrylist %
+\glossaryentryfield{Physics}{\glsnamefont{Physics}}{is the study of stuff}{\relax }{\glossaryentrynumbers{\relax 
+		\setentrycounter[]{page}\glsnumberformat{1}}}%
+\end{theglossary}\glossarypostamble

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác