Merge branch 'master' into 9998-no-count-items-available
authorTom Clegg <tom@curoverse.com>
Mon, 16 Jan 2017 16:13:32 +0000 (11:13 -0500)
committerTom Clegg <tom@curoverse.com>
Mon, 16 Jan 2017 16:13:32 +0000 (11:13 -0500)
Conflicts:
sdk/go/arvados/resource_list.go
services/keep-balance/collection.go

432 files changed:
.gitignore
README.md
apps/workbench/Gemfile
apps/workbench/Gemfile.lock
apps/workbench/app/assets/javascripts/work_unit_component.js
apps/workbench/app/controllers/application_controller.rb
apps/workbench/app/controllers/container_requests_controller.rb
apps/workbench/app/controllers/pipeline_instances_controller.rb
apps/workbench/app/controllers/projects_controller.rb
apps/workbench/app/controllers/work_units_controller.rb
apps/workbench/app/models/container_work_unit.rb
apps/workbench/app/models/proxy_work_unit.rb
apps/workbench/app/views/application/_extra_tab_line_buttons.html.erb [new file with mode: 0644]
apps/workbench/app/views/application/_title_and_buttons.html.erb
apps/workbench/app/views/container_requests/_extra_tab_line_buttons.html.erb [new file with mode: 0644]
apps/workbench/app/views/container_requests/_show_inputs.html.erb
apps/workbench/app/views/pipeline_instances/_running_component.html.erb
apps/workbench/app/views/pipeline_instances/_show_components_running.html.erb
apps/workbench/app/views/projects/_show_dashboard.html.erb
apps/workbench/app/views/projects/_show_processes.html.erb [new file with mode: 0644]
apps/workbench/app/views/projects/show.html.erb
apps/workbench/config/application.default.yml
apps/workbench/config/routes.rb
apps/workbench/lib/tasks/config_dump.rake [new file with mode: 0644]
apps/workbench/test/controllers/container_requests_controller_test.rb
apps/workbench/test/controllers/disabled_api_test.rb
apps/workbench/test/controllers/projects_controller_test.rb
apps/workbench/test/diagnostics/container_request_test.rb [new file with mode: 0644]
apps/workbench/test/diagnostics/pipeline_test.rb
apps/workbench/test/diagnostics_test_helper.rb
apps/workbench/test/integration/application_layout_test.rb
apps/workbench/test/integration/container_requests_test.rb
apps/workbench/test/integration/pipeline_instances_test.rb
apps/workbench/test/integration/projects_test.rb
apps/workbench/test/integration/work_units_test.rb
apps/workbench/test/performance/browsing_test.rb
apps/workbench/test/unit/work_unit_test.rb
backports/deb-fuse/fpm-info.sh [new file with mode: 0644]
backports/deb-libfuse-dev/fpm-info.sh [new file with mode: 0644]
backports/python-llfuse/fpm-info.sh
backports/rpm-fuse-devel/fpm-info.sh [new file with mode: 0644]
backports/rpm-fuse/fpm-info.sh [new file with mode: 0644]
build/README
build/build-dev-docker-jobs-image.sh [new file with mode: 0755]
build/libcloud-pin
build/package-build-dockerfiles/Makefile
build/package-build-dockerfiles/centos6/Dockerfile [deleted file]
build/package-build-dockerfiles/debian7/Dockerfile [deleted file]
build/package-test-dockerfiles/centos6/Dockerfile [deleted file]
build/package-test-dockerfiles/centos6/localrepo.repo [deleted file]
build/package-test-dockerfiles/centos7/Dockerfile
build/package-test-dockerfiles/debian7/Dockerfile [deleted file]
build/package-test-dockerfiles/debian8/Dockerfile
build/package-test-dockerfiles/ubuntu1204/Dockerfile
build/package-test-dockerfiles/ubuntu1404/Dockerfile
build/package-testing/test-packages-centos6.sh [deleted symlink]
build/rails-package-scripts/postinst.sh
build/run-build-docker-images.sh
build/run-build-packages-all-targets.sh
build/run-build-packages-one-target.sh
build/run-build-packages-sso.sh
build/run-build-packages.sh
build/run-build-test-packages-one-target.sh
build/run-library.sh
build/run-tests.sh
doc/_config.yml
doc/_includes/_container_runtime_constraints.liquid
doc/_includes/_container_scheduling_parameters.liquid [new file with mode: 0644]
doc/_includes/_crunch1only_begin.liquid [new file with mode: 0644]
doc/_includes/_crunch1only_end.liquid [new file with mode: 0644]
doc/_includes/_notebox_begin_warning.liquid [new file with mode: 0644]
doc/_includes/_pipeline_deprecation_notice.liquid
doc/_includes/_what_is_cwl.liquid [new file with mode: 0644]
doc/_layouts/default.html.liquid
doc/api/authentication.html.textile.liquid [deleted file]
doc/api/crunch-scripts.html.textile.liquid
doc/api/execution.html.textile.liquid [new file with mode: 0644]
doc/api/index.html.textile.liquid
doc/api/methods.html.textile.liquid
doc/api/methods/api_client_authorizations.html.textile.liquid
doc/api/methods/api_clients.html.textile.liquid
doc/api/methods/authorized_keys.html.textile.liquid
doc/api/methods/collections.html.textile.liquid
doc/api/methods/container_requests.html.textile.liquid
doc/api/methods/containers.html.textile.liquid
doc/api/methods/groups.html.textile.liquid
doc/api/methods/humans.html.textile.liquid
doc/api/methods/job_tasks.html.textile.liquid
doc/api/methods/jobs.html.textile.liquid
doc/api/methods/keep_disks.html.textile.liquid
doc/api/methods/keep_services.html.textile.liquid
doc/api/methods/links.html.textile.liquid
doc/api/methods/logs.html.textile.liquid
doc/api/methods/nodes.html.textile.liquid
doc/api/methods/pipeline_instances.html.textile.liquid
doc/api/methods/pipeline_templates.html.textile.liquid
doc/api/methods/repositories.html.textile.liquid
doc/api/methods/specimens.html.textile.liquid
doc/api/methods/traits.html.textile.liquid
doc/api/methods/users.html.textile.liquid
doc/api/methods/virtual_machines.html.textile.liquid
doc/api/methods/workflows.html.textile.liquid
doc/api/permission-model.html.textile.liquid
doc/api/requests.html.textile.liquid [new file with mode: 0644]
doc/api/resources.html.textile.liquid
doc/api/schema/ApiClient.html.textile.liquid [deleted file]
doc/api/schema/ApiClientAuthorization.html.textile.liquid [deleted file]
doc/api/schema/AuthorizedKey.html.textile.liquid [deleted file]
doc/api/schema/Collection.html.textile.liquid [deleted file]
doc/api/schema/Container.html.textile.liquid [deleted file]
doc/api/schema/ContainerRequest.html.textile.liquid [deleted file]
doc/api/schema/Group.html.textile.liquid [deleted file]
doc/api/schema/Human.html.textile.liquid [deleted file]
doc/api/schema/Job.html.textile.liquid [deleted file]
doc/api/schema/JobTask.html.textile.liquid [deleted file]
doc/api/schema/KeepDisk.html.textile.liquid [deleted file]
doc/api/schema/KeepService.html.textile.liquid [deleted file]
doc/api/schema/Link.html.textile.liquid [deleted file]
doc/api/schema/Log.html.textile.liquid [deleted file]
doc/api/schema/Node.html.textile.liquid [deleted file]
doc/api/schema/PipelineInstance.html.textile.liquid [deleted file]
doc/api/schema/PipelineTemplate.html.textile.liquid [deleted file]
doc/api/schema/Repository.html.textile.liquid [deleted file]
doc/api/schema/Specimen.html.textile.liquid [deleted file]
doc/api/schema/Trait.html.textile.liquid [deleted file]
doc/api/schema/User.html.textile.liquid [deleted file]
doc/api/schema/VirtualMachine.html.textile.liquid [deleted file]
doc/api/schema/Workflow.html.textile.liquid [deleted file]
doc/api/storage.html.textile.liquid [new file with mode: 0644]
doc/api/tokens.html.textile.liquid [new file with mode: 0644]
doc/images/Arvados_Permissions.svg [new file with mode: 0644]
doc/images/Crunch_dispatch.svg [new file with mode: 0644]
doc/images/Keep_manifests.svg [new file with mode: 0644]
doc/images/Keep_reading_writing_block.svg [new file with mode: 0644]
doc/images/Keep_rendezvous_hashing.svg [new file with mode: 0644]
doc/images/Session_Establishment.svg [new file with mode: 0644]
doc/images/upload-using-workbench.png
doc/images/workbench-dashboard.png
doc/images/workbench-move-selected.png
doc/install/crunch2-slurm/install-prerequisites.html.textile.liquid
doc/install/install-keep-web.html.textile.liquid
doc/install/install-keepproxy.html.textile.liquid
doc/install/install-ws.html.textile.liquid [new file with mode: 0644]
doc/sdk/cli/install.html.textile.liquid
doc/sdk/go/example.html.textile.liquid [new file with mode: 0644]
doc/sdk/go/index.html.textile.liquid
doc/sdk/index.html.textile.liquid
doc/sdk/java/example.html.textile.liquid [new file with mode: 0644]
doc/sdk/java/index.html.textile.liquid
doc/sdk/perl/example.html.textile.liquid [new file with mode: 0644]
doc/sdk/perl/index.html.textile.liquid
doc/sdk/python/events.html.textile.liquid
doc/sdk/python/example.html.textile.liquid [new file with mode: 0644]
doc/sdk/python/sdk-python.html.textile.liquid
doc/sdk/ruby/example.html.textile.liquid [new file with mode: 0644]
doc/sdk/ruby/index.html.textile.liquid
doc/user/cwl/cwl-extensions.html.textile.liquid [new file with mode: 0644]
doc/user/cwl/cwl-run-options.html.textile.liquid [new file with mode: 0644]
doc/user/cwl/cwl-runner.html.textile.liquid
doc/user/cwl/cwl-style.html.textile.liquid
doc/user/examples/crunch-examples.html.textile.liquid
doc/user/getting_started/workbench.html.textile.liquid
doc/user/reference/job-pipeline-ref.html.textile.liquid
doc/user/topics/arv-copy.html.textile.liquid
doc/user/topics/arv-docker.html.textile.liquid
doc/user/topics/arv-run.html.textile.liquid
doc/user/topics/run-command.html.textile.liquid
doc/user/topics/running-pipeline-command-line.html.textile.liquid
doc/user/tutorials/running-external-program.html.textile.liquid
doc/user/tutorials/tutorial-pipeline-workbench.html.textile.liquid [deleted file]
doc/user/tutorials/tutorial-submit-job.html.textile.liquid
doc/user/tutorials/tutorial-workflow-workbench.html.textile.liquid [new file with mode: 0644]
doc/user/tutorials/writing-cwl-workflow.html.textile.liquid [new file with mode: 0644]
sdk/cli/bin/crunch-job
sdk/cli/test/test_arv-keep-get.rb
sdk/cli/test/test_arv-keep-put.rb
sdk/cwl/arvados_cwl/__init__.py
sdk/cwl/arvados_cwl/arvcontainer.py
sdk/cwl/arvados_cwl/arvdocker.py
sdk/cwl/arvados_cwl/arvjob.py
sdk/cwl/arvados_cwl/arvworkflow.py
sdk/cwl/arvados_cwl/crunch_script.py
sdk/cwl/arvados_cwl/done.py
sdk/cwl/arvados_cwl/fsaccess.py
sdk/cwl/arvados_cwl/pathmapper.py
sdk/cwl/arvados_cwl/runner.py
sdk/cwl/setup.py
sdk/cwl/tests/test_container.py
sdk/cwl/tests/test_job.py
sdk/cwl/tests/test_make_output.py
sdk/cwl/tests/test_submit.py
sdk/cwl/tests/test_urljoin.py [new file with mode: 0644]
sdk/cwl/tests/wf/expect_arvworkflow.cwl [new file with mode: 0644]
sdk/cwl/tests/wf/expect_packed.cwl
sdk/cwl/tests/wf/scatter2_subwf.cwl
sdk/cwl/tests/wf/submit_keepref_wf.cwl [new file with mode: 0644]
sdk/dev-jobs.dockerfile [new file with mode: 0644]
sdk/go/arvados/client.go
sdk/go/arvados/collection.go
sdk/go/arvados/container.go
sdk/go/arvados/log.go [new file with mode: 0644]
sdk/go/arvados/resource_list.go
sdk/go/arvadosclient/arvadosclient.go
sdk/go/config/load.go
sdk/go/crunchrunner/crunchrunner.go
sdk/go/ctxlog/log.go [new file with mode: 0644]
sdk/go/httpserver/id_generator.go [new file with mode: 0644]
sdk/go/httpserver/request_limiter.go
sdk/go/keepclient/keepclient.go
sdk/go/keepclient/keepclient_test.go
sdk/go/logger/logger.go [deleted file]
sdk/go/logger/util.go [deleted file]
sdk/go/stats/duration.go [new file with mode: 0644]
sdk/go/stats/duration_test.go [new file with mode: 0644]
sdk/go/util/util.go [deleted file]
sdk/python/arvados/_version.py [new file with mode: 0644]
sdk/python/arvados/arvfile.py
sdk/python/arvados/collection.py
sdk/python/arvados/commands/arv_copy.py
sdk/python/arvados/commands/keepdocker.py
sdk/python/arvados/commands/ls.py
sdk/python/arvados/commands/put.py
sdk/python/arvados/commands/run.py
sdk/python/arvados/commands/ws.py
sdk/python/arvados/keep.py
sdk/python/arvados/util.py
sdk/python/bin/arv-get
sdk/python/bin/arv-normalize
sdk/python/setup.py
sdk/python/tests/arvados_testutil.py
sdk/python/tests/nginx.conf
sdk/python/tests/run_test_server.py
sdk/python/tests/test_arv_copy.py [new file with mode: 0644]
sdk/python/tests/test_arv_keepdocker.py [new file with mode: 0644]
sdk/python/tests/test_arv_ls.py
sdk/python/tests/test_arv_normalize.py [new file with mode: 0644]
sdk/python/tests/test_arv_put.py
sdk/python/tests/test_arv_run.py [new file with mode: 0644]
sdk/python/tests/test_arv_ws.py
sdk/python/tests/test_arvfile.py
sdk/python/tests/test_collections.py
sdk/python/tests/test_events.py
sdk/python/tests/test_keep_client.py
services/api/Gemfile
services/api/Gemfile.lock
services/api/app/controllers/application_controller.rb
services/api/app/controllers/arvados/v1/collections_controller.rb
services/api/app/controllers/arvados/v1/container_requests_controller.rb
services/api/app/controllers/arvados/v1/containers_controller.rb
services/api/app/controllers/arvados/v1/groups_controller.rb
services/api/app/controllers/arvados/v1/schema_controller.rb
services/api/app/controllers/arvados/v1/user_agreements_controller.rb
services/api/app/controllers/arvados/v1/users_controller.rb
services/api/app/controllers/arvados/v1/virtual_machines_controller.rb
services/api/app/controllers/database_controller.rb
services/api/app/middlewares/arvados_api_token.rb
services/api/app/middlewares/rack_socket.rb
services/api/app/models/arvados_model.rb
services/api/app/models/blob.rb
services/api/app/models/collection.rb
services/api/app/models/commit_ancestor.rb
services/api/app/models/container.rb
services/api/app/models/container_request.rb
services/api/app/models/job.rb
services/api/app/models/link.rb
services/api/app/models/log.rb
services/api/app/models/node.rb
services/api/app/models/pipeline_instance.rb
services/api/app/models/repository.rb
services/api/app/models/user.rb
services/api/config/application.default.yml
services/api/config/application.rb
services/api/config/boot.rb
services/api/config/initializers/inflections.rb
services/api/config/initializers/load_config.rb
services/api/config/initializers/preload_all_models.rb
services/api/config/routes.rb
services/api/db/migrate/20161111143147_add_scheduling_parameters_to_container.rb [new file with mode: 0644]
services/api/db/migrate/20161115171221_add_output_and_log_uuid_to_container_request.rb [new file with mode: 0644]
services/api/db/migrate/20161115174218_add_output_and_log_uuids_to_container_request_search_index.rb [new file with mode: 0644]
services/api/db/migrate/20161213172944_full_text_search_indexes.rb [new file with mode: 0644]
services/api/db/migrate/20161222153434_split_expiry_to_trash_and_delete.rb [new file with mode: 0644]
services/api/db/migrate/20161223090712_add_output_name_to_container_requests.rb [new file with mode: 0644]
services/api/db/migrate/20170102153111_add_output_name_to_container_request_search_index.rb [new file with mode: 0644]
services/api/db/migrate/20170105160301_add_output_name_to_cr_fts_index.rb [new file with mode: 0644]
services/api/db/migrate/20170105160302_set_finished_at_on_finished_pipeline_instances.rb [new file with mode: 0644]
services/api/db/structure.sql
services/api/lib/create_permission_view.sql [new file with mode: 0644]
services/api/lib/create_superuser_token.rb
services/api/lib/crunch_dispatch.rb
services/api/lib/current_api_client.rb
services/api/lib/eventbus.rb
services/api/lib/load_param.rb
services/api/lib/salvage_collection.rb
services/api/lib/sweep_trashed_collections.rb [new file with mode: 0644]
services/api/lib/tasks/config_dump.rake [new file with mode: 0644]
services/api/script/arvados-git-sync.rb
services/api/script/migrate-gitolite-to-uuid-storage.rb
services/api/test/factories/user.rb
services/api/test/fixtures/collections.yml
services/api/test/fixtures/container_requests.yml
services/api/test/fixtures/jobs.yml
services/api/test/fixtures/pipeline_templates.yml
services/api/test/fixtures/workflows.yml
services/api/test/functional/arvados/v1/api_client_authorizations_controller_test.rb
services/api/test/functional/arvados/v1/collections_controller_test.rb
services/api/test/functional/arvados/v1/container_requests_controller_test.rb [new file with mode: 0644]
services/api/test/functional/arvados/v1/filters_test.rb
services/api/test/functional/arvados/v1/groups_controller_test.rb
services/api/test/functional/arvados/v1/jobs_controller_test.rb
services/api/test/functional/arvados/v1/links_controller_test.rb
services/api/test/functional/arvados/v1/repositories_controller_test.rb
services/api/test/functional/arvados/v1/schema_controller_test.rb
services/api/test/functional/arvados/v1/users_controller_test.rb
services/api/test/functional/arvados/v1/virtual_machines_controller_test.rb
services/api/test/helpers/users_test_helper.rb
services/api/test/integration/collections_api_test.rb
services/api/test/integration/collections_performance_test.rb
services/api/test/integration/cross_origin_test.rb
services/api/test/integration/database_reset_test.rb
services/api/test/integration/select_test.rb
services/api/test/integration/user_sessions_test.rb
services/api/test/integration/websocket_test.rb
services/api/test/test_helper.rb
services/api/test/unit/app_version_test.rb
services/api/test/unit/arvados_model_test.rb
services/api/test/unit/authorized_key_test.rb
services/api/test/unit/collection_performance_test.rb
services/api/test/unit/collection_test.rb
services/api/test/unit/commit_test.rb
services/api/test/unit/container_request_test.rb
services/api/test/unit/container_test.rb
services/api/test/unit/create_superuser_token_test.rb
services/api/test/unit/fail_jobs_test.rb
services/api/test/unit/job_test.rb
services/api/test/unit/log_test.rb
services/api/test/unit/node_test.rb
services/api/test/unit/owner_test.rb
services/api/test/unit/permission_test.rb
services/api/test/unit/pipeline_instance_test.rb
services/api/test/unit/salvage_collection_test.rb
services/api/test/unit/user_test.rb
services/api/test/websocket_runner.rb [deleted file]
services/arv-git-httpd/main.go
services/arv-web/arv-web.py
services/crunch-dispatch-slurm/crunch-dispatch-slurm.go
services/crunch-dispatch-slurm/crunch-dispatch-slurm_test.go
services/crunch-dispatch-slurm/squeue.go
services/crunch-run/crunchrun.go
services/crunch-run/crunchrun_test.go
services/crunchstat/crunchstat.go
services/crunchstat/crunchstat_test.go
services/datamanager/collection/collection.go [deleted file]
services/datamanager/collection/collection_test.go [deleted file]
services/datamanager/collection/testing.go [deleted file]
services/datamanager/datamanager.go [deleted file]
services/datamanager/datamanager_test.go [deleted file]
services/datamanager/experimental/datamanager.py [deleted file]
services/datamanager/experimental/datamanager_test.py [deleted file]
services/datamanager/keep/keep.go [deleted file]
services/datamanager/keep/keep_test.go [deleted file]
services/datamanager/loggerutil/loggerutil.go [deleted file]
services/datamanager/summary/canonical_string.go [deleted file]
services/datamanager/summary/file.go [deleted file]
services/datamanager/summary/pull_list.go [deleted file]
services/datamanager/summary/pull_list_test.go [deleted file]
services/datamanager/summary/summary.go [deleted file]
services/datamanager/summary/summary_test.go [deleted file]
services/datamanager/summary/trash_list.go [deleted file]
services/datamanager/summary/trash_list_test.go [deleted file]
services/fuse/arvados_fuse/__init__.py
services/fuse/arvados_fuse/_version.py [new file with mode: 0644]
services/fuse/arvados_fuse/command.py
services/fuse/setup.py
services/fuse/tests/mount_test_base.py
services/fuse/tests/test_command_args.py
services/fuse/tests/test_mount.py
services/fuse/tests/test_retry.py
services/keep-balance/balance.go
services/keep-balance/balance_run_test.go
services/keep-balance/collection.go
services/keep-balance/keep-balance.service
services/keep-balance/main.go
services/keep-balance/usage.go
services/keep-web/main.go
services/keepproxy/keepproxy.go
services/keepstore/azure_blob_volume.go
services/keepstore/azure_blob_volume_test.go
services/keepstore/bufferpool.go
services/keepstore/config.go
services/keepstore/config_test.go
services/keepstore/count.go [new file with mode: 0644]
services/keepstore/handler_test.go
services/keepstore/handlers.go
services/keepstore/keepstore.go
services/keepstore/logging_router.go
services/keepstore/pipe_adapters.go [new file with mode: 0644]
services/keepstore/pull_worker.go
services/keepstore/s3_volume.go
services/keepstore/s3_volume_test.go
services/keepstore/stats_ticker.go [new file with mode: 0644]
services/keepstore/trash_worker.go
services/keepstore/usage.go
services/keepstore/volume.go
services/keepstore/volume_unix.go
services/keepstore/volume_unix_test.go
services/nodemanager/arvnodeman/_version.py [new file with mode: 0644]
services/nodemanager/arvnodeman/computenode/driver/gce.py
services/nodemanager/arvnodeman/launcher.py
services/nodemanager/setup.py
services/nodemanager/tests/test_arguments.py [new file with mode: 0644]
services/nodemanager/tests/testutil.py
services/ws/arvados-ws.service [new file with mode: 0644]
services/ws/config.go [new file with mode: 0644]
services/ws/doc.go [new file with mode: 0644]
services/ws/event.go [new file with mode: 0644]
services/ws/event_source.go [new file with mode: 0644]
services/ws/handler.go [new file with mode: 0644]
services/ws/main.go [new file with mode: 0644]
services/ws/permission.go [new file with mode: 0644]
services/ws/router.go [new file with mode: 0644]
services/ws/session.go [new file with mode: 0644]
services/ws/session_v0.go [new file with mode: 0644]
services/ws/session_v1.go [new file with mode: 0644]
tools/arvbash/arvbash.sh [new file with mode: 0755]
tools/arvbox/lib/arvbox/docker/Dockerfile.base
tools/arvbox/lib/arvbox/docker/api-setup.sh
tools/arvbox/lib/arvbox/docker/common.sh
tools/arvbox/lib/arvbox/docker/createusers.sh
tools/arvbox/lib/arvbox/docker/service/api/run-service
tools/arvbox/lib/arvbox/docker/service/ready/run-service
tools/arvbox/lib/arvbox/docker/service/sso/run-service

index d5c4b3715e800c300a8b29f7c9742fb5a7b2d62d..77b98999bca91ede8ff5aab46f04473ba1f2cd99 100644 (file)
@@ -22,3 +22,8 @@ sdk/java/target
 *.class
 sdk/java/log
 tmp
+sdk/cli/binstubs/
+sdk/cwl/arvados_cwl/_version.py
+services/api/config/arvados-clients.yml
+*#*
+.DS_Store
index cf09171b024716b4a57a412746c7d1a9decbb87e..419ca15957cc4cd2aa1b00048dd61feb2c6375f3 100644 (file)
--- a/README.md
+++ b/README.md
@@ -59,6 +59,7 @@ contributers to Arvados.
 ## Development
 
 [![Build Status](https://ci.curoverse.com/buildStatus/icon?job=run-tests)](https://ci.curoverse.com/job/run-tests/)
+[![Go Report Card](https://goreportcard.com/badge/github.com/curoverse/arvados)](https://goreportcard.com/report/github.com/curoverse/arvados)
 
 The Arvados public bug tracker is located at https://dev.arvados.org/projects/arvados/issues
 
index 20d64d17a16dc87b2a076c13b2ad6c356b0a041a..eac4fdf601c8d4033db80e99b72c71a02cfa4e0e 100644 (file)
@@ -1,6 +1,6 @@
 source 'https://rubygems.org'
 
-gem 'rails', '~> 4.1.0'
+gem 'rails', '~> 4.1'
 gem 'arvados', '>= 0.1.20150511150219'
 
 gem 'activerecord-nulldb-adapter'
@@ -19,7 +19,7 @@ gem 'coffee-rails'
 # in production environments by default.
 group :assets do
   gem 'sass-rails'
-  gem 'uglifier', '>= 1.0.3'
+  gem 'uglifier', '~> 2.0'
 
   # See https://github.com/sstephenson/execjs#readme for more supported runtimes
   gem 'therubyracer', :platforms => :ruby
@@ -33,7 +33,7 @@ group :development do
 end
 
 group :test, :diagnostics, :performance do
-  gem 'minitest', '>= 5.0.0'
+  gem 'minitest', '~> 5.0'
   gem 'selenium-webdriver'
   gem 'capybara'
   gem 'poltergeist'
index a8431a7dfd373d0357053df0e06df901498ca0ca..0abe868ccf75bac683e44529ce939a421e772d45 100644 (file)
@@ -92,7 +92,7 @@ GEM
     deep_merge (1.0.1)
     docile (1.1.5)
     erubis (2.7.0)
-    execjs (2.2.2)
+    execjs (2.7.0)
     extlib (0.9.16)
     faraday (0.9.2)
       multipart-post (>= 1.2, < 3)
@@ -123,7 +123,7 @@ GEM
       signet (~> 0.7)
     headless (1.0.2)
     highline (1.6.21)
-    httpclient (2.6.0.1)
+    httpclient (2.8.2.4)
     i18n (0.7.0)
     jquery-rails (3.1.2)
       railties (>= 3.0, < 5.0)
@@ -158,7 +158,7 @@ GEM
       metaclass (~> 0.0.1)
     morrisjs-rails (0.5.1)
       railties (> 3.1, < 5)
-    multi_json (1.12.0)
+    multi_json (1.12.1)
     multipart-post (2.0.0)
     net-scp (1.2.1)
       net-ssh (>= 2.6.5)
@@ -257,7 +257,7 @@ GEM
     tilt (1.4.1)
     tzinfo (1.2.2)
       thread_safe (~> 0.1)
-    uglifier (2.7.0)
+    uglifier (2.7.2)
       execjs (>= 0.3.0)
       json (>= 1.8.0)
     websocket (1.2.2)
@@ -292,7 +292,7 @@ DEPENDENCIES
   less-rails
   lograge
   logstash-event
-  minitest (>= 5.0.0)
+  minitest (~> 5.0)
   mocha
   morrisjs-rails
   multi_json
@@ -301,7 +301,7 @@ DEPENDENCIES
   piwik_analytics
   poltergeist
   rack-mini-profiler
-  rails (~> 4.1.0)
+  rails (~> 4.1)
   rails-perftest
   raphael-rails
   ruby-debug-passenger
@@ -316,8 +316,8 @@ DEPENDENCIES
   sshkey
   themes_for_rails!
   therubyracer
-  uglifier (>= 1.0.3)
+  uglifier (~> 2.0)
   wiselinks
 
 BUNDLED WITH
-   1.12.1
+   1.13.2
index baff0e8de1ee474d938e6686e8cd8d285d7728c0..e63aecde70a4c48ac0d61b6e066513967a0e6928 100644 (file)
@@ -1,18 +1,16 @@
 $(document).
-    on('click', '.component-detail-panel', function(event) {
-      var href = $($(event.target).attr('href'));
-      if ($(href).attr("class").split(' ').indexOf("in") == -1) {
-        return;   // collapsed; nothing more to do
-      }
-
+  on('click', '.component-detail-panel', function(event) {
+    var href = $($(event.target).attr('href'));
+    if ($(href).hasClass("in")) {
       var content_div = href.find('.work-unit-component-detail-body');
       content_div.html('<div class="spinner spinner-32px col-sm-1"></div>');
       var content_url = href.attr('content-url');
       var action_data = href.attr('action-data');
       $.ajax(content_url, {dataType: 'html', type: 'POST', data: {action_data: action_data}}).
-          done(function(data, status, jqxhr) {
-              content_div.html(data);
-          }).fail(function(jqxhr, status, error) {
-              content_div.html(error);
-          });
-      });
+        done(function(data, status, jqxhr) {
+          content_div.html(data);
+        }).fail(function(jqxhr, status, error) {
+          content_div.html(error);
+        });
+    }
+  });
index c9ce8ce0b748a9473d2cd5f80739d070f1f8aef5..ee3ac4d6810588b7d630705a5efe4cd6e08bd6ae 100644 (file)
@@ -907,7 +907,7 @@ class ApplicationController < ActionController::Base
   # from the top three levels.
   # That is: get toplevel projects under home, get subprojects of
   # these projects, and so on until we hit the limit.
-  def my_wanted_projects user, page_size=100
+  def my_wanted_projects(user, page_size=100)
     return @my_wanted_projects if @my_wanted_projects
 
     from_top = []
@@ -922,7 +922,7 @@ class ApplicationController < ActionController::Base
       break if current_level.results.size == 0
       @too_many_projects = true if current_level.items_available > current_level.results.size
       from_top.concat current_level.results
-      uuids = current_level.results.collect { |x| x.uuid }
+      uuids = current_level.results.collect(&:uuid)
       depth += 1
       if depth >= 3
         @reached_level_limit = true
@@ -933,12 +933,12 @@ class ApplicationController < ActionController::Base
   end
 
   helper_method :my_wanted_projects_tree
-  def my_wanted_projects_tree user, page_size=100
-    build_my_wanted_projects_tree user, page_size
+  def my_wanted_projects_tree(user, page_size=100)
+    build_my_wanted_projects_tree(user, page_size)
     [@my_wanted_projects_tree, @too_many_projects, @reached_level_limit]
   end
 
-  def build_my_wanted_projects_tree user, page_size=100
+  def build_my_wanted_projects_tree(user, page_size=100)
     return @my_wanted_projects_tree if @my_wanted_projects_tree
 
     parent_of = {user.uuid => 'me'}
index b67d100887c838917ee4a2fc6ba8ac2871893cd3..b286a9456e14e3a2538df122c31e6071fab33558 100644 (file)
@@ -59,4 +59,32 @@ class ContainerRequestsController < ApplicationController
     end
   end
 
+  def copy
+    src = @object
+
+    @object = ContainerRequest.new
+
+    @object.command = src.command
+    @object.container_image = src.container_image
+    @object.cwd = src.cwd
+    @object.description = src.description
+    @object.environment = src.environment
+    @object.mounts = src.mounts
+    @object.name = src.name
+    @object.output_path = src.output_path
+    @object.priority = 1
+    @object.properties[:template_uuid] = src.properties[:template_uuid]
+    @object.runtime_constraints = src.runtime_constraints
+    @object.scheduling_parameters = src.scheduling_parameters
+    @object.state = 'Uncommitted'
+    @object.use_existing = false
+
+    # set owner_uuid to that of source, provided it is a project and writable by current user
+    current_project = Group.find(src.owner_uuid) rescue nil
+    if (current_project && current_project.writable_by.andand.include?(current_user.uuid))
+      @object.owner_uuid = src.owner_uuid
+    end
+
+    super
+  end
 end
index c5fbda0cf349177801a0bcbbd75c7c95634b56ef..83fe0dda4645a0437a962aa95e9572c9c897afe2 100644 (file)
@@ -53,7 +53,7 @@ class PipelineInstancesController < ApplicationController
     end
     @object.state = 'New'
 
-    # set owner_uuid to that of source, provided it is a project and wriable by current user
+    # set owner_uuid to that of source, provided it is a project and writable by current user
     current_project = Group.find(source.owner_uuid) rescue nil
     if (current_project && current_project.writable_by.andand.include?(current_user.uuid))
       @object.owner_uuid = source.owner_uuid
index 0a2044a0e23e96b741d77658dfa91057fe57bdfa..273f9d0c8063c178489ca93972cf64c0b39bbc18 100644 (file)
@@ -55,8 +55,10 @@ class ProjectsController < ApplicationController
     pane_list = []
 
     procs = ["arvados#containerRequest"]
+    procs_pane_name = 'Processes'
     if PipelineInstance.api_exists?(:index)
       procs << "arvados#pipelineInstance"
+      procs_pane_name = 'Pipelines_and_processes'
     end
 
     workflows = ["arvados#workflow"]
@@ -76,7 +78,7 @@ class ProjectsController < ApplicationController
       }
     pane_list <<
       {
-        :name => 'Pipelines_and_processes',
+        :name => procs_pane_name,
         :filters => [%w(uuid is_a) + [procs]]
       }
     pane_list <<
@@ -147,10 +149,10 @@ class ProjectsController < ApplicationController
         link.destroy
       end
 
-      # If this object has the 'expires_at' attribute, then simply mark it
-      # expired.
-      if item.attributes.include?("expires_at")
-        item.update_attributes expires_at: Time.now
+      # If this object has the 'trash_at' attribute, then simply mark it
+      # as trash.
+      if item.attributes.include?("trash_at")
+        item.update_attributes trash_at: Time.now
         @removed_uuids << item.uuid
       elsif item.owner_uuid == @object.uuid
         # Object is owned by this project. Remove it from the project by
@@ -159,7 +161,7 @@ class ProjectsController < ApplicationController
           item.update_attributes owner_uuid: current_user.uuid
           @removed_uuids << item.uuid
         rescue ArvadosApiClient::ApiErrorResponseException => e
-          if e.message.include? '_owner_uuid_name_unique'
+          if e.message.include? '_owner_uuid_'
             rename_to = item.name + ' removed from ' +
                         (@object.name ? @object.name : @object.uuid) +
                         ' at ' + Time.now.to_s
index 3b611aa25b74e28663d9b7ecc2b0647670f066c8..550bdb7e953f7fe47a899cbe674a39ed457a9529 100644 (file)
@@ -57,7 +57,7 @@ class WorkUnitsController < ApplicationController
       workflow = Workflow.find? template_uuid
       if workflow.definition
         begin
-          wf_json = YAML::load(workflow.definition)
+          wf_json = ActiveSupport::HashWithIndifferentAccess.new YAML::load(workflow.definition)
         rescue => e
           logger.error "Error converting definition yaml to json: #{e.message}"
           raise ArgumentError, "Error converting definition yaml to json: #{e.message}"
@@ -77,11 +77,21 @@ class WorkUnitsController < ApplicationController
       attrs['cwd'] = "/var/spool/cwl"
       attrs['output_path'] = "/var/spool/cwl"
 
+      input_defaults = {}
+      if wf_json
+        inputs = get_cwl_inputs(wf_json)
+        inputs.each do |input|
+          if input[:default]
+            input_defaults[cwl_shortname(input[:id])] = input[:default]
+          end
+        end
+      end
+
       # mounts
       mounts = {
         "/var/lib/cwl/cwl.input.json" => {
           "kind" => "json",
-          "content" => {}
+          "content" => input_defaults
         },
         "stdout" => {
           "kind" => "file",
index 88aab306cedc8b9ea5a8a94a27cc394f2022780b..ed82f18036c1025bffb16e5267701045039bb167 100644 (file)
@@ -99,12 +99,21 @@ class ContainerWorkUnit < ProxyWorkUnit
   end
 
   def log_collection
-    get_combined(:log)
+    if @proxied.is_a?(ContainerRequest)
+      get(:log_uuid)
+    else
+      get(:log)
+    end
   end
 
   def outputs
     items = []
-    items << get_combined(:output) if get_combined(:output)
+    if @proxied.is_a?(ContainerRequest)
+      out = get(:output_uuid)
+    else
+      out = get(:output)
+    end
+    items << out if out
     items
   end
 
index 48bc3a04bc95dd41915e317449e7287ed4e42bce..42304843d32d3c998235adb523819fb3ae080cdd 100644 (file)
@@ -187,9 +187,15 @@ class ProxyWorkUnit < WorkUnit
   end
 
   def cputime
-    if state_label != "Queued"
+    if children.any?
+      children.map { |c|
+        c.cputime
+      }.reduce(:+) || 0
+    else
       if started_at
-        (runtime_constraints.andand[:min_nodes] || 1) * ((finished_at || Time.now()) - started_at)
+        (runtime_constraints.andand[:min_nodes] || 1).to_i * ((finished_at || Time.now()) - started_at)
+      else
+        0
       end
     end
   end
@@ -272,20 +278,7 @@ class ProxyWorkUnit < WorkUnit
       end
       resp << " for "
 
-      cpu_time = 0
-      if children.any?
-        cpu_time = children.map { |c|
-          if c.started_at
-             (c.runtime_constraints.andand[:min_nodes] || 1) * ((c.finished_at || Time.now()) - c.started_at)
-          else
-            0
-          end
-        }.reduce(:+) || 0
-      else
-        if started_at
-          cpu_time = (runtime_constraints.andand[:min_nodes] || 1) * ((finished_at || Time.now()) - started_at)
-        end
-      end
+      cpu_time = cputime
 
       resp << ApplicationController.helpers.render_time(runningtime, false)
       if (walltime - runningtime) > 0
diff --git a/apps/workbench/app/views/application/_extra_tab_line_buttons.html.erb b/apps/workbench/app/views/application/_extra_tab_line_buttons.html.erb
new file mode 100644 (file)
index 0000000..e69de29
index 398f248a39bc478b016b64537a310a6b578bb9b8..46a949aa12d083ac1baeb1138e5395030889dd77 100644 (file)
@@ -13,6 +13,9 @@
 
 <% if @object.class.goes_in_projects? && @object.uuid != current_user.andand.uuid # Not the "Home" project %>
   <% content_for :tab_line_buttons do %>
+    <% if current_user.andand.is_active %>
+      <%= render partial: 'extra_tab_line_buttons' %>
+    <% end %>
     <% if current_user.andand.is_active && @object.class.copies_to_projects? %>
       <%= link_to(
           choose_projects_path(
diff --git a/apps/workbench/app/views/container_requests/_extra_tab_line_buttons.html.erb b/apps/workbench/app/views/container_requests/_extra_tab_line_buttons.html.erb
new file mode 100644 (file)
index 0000000..662309f
--- /dev/null
@@ -0,0 +1,10 @@
+<% if @object.state == 'Final' %>
+  <%= link_to(copy_container_request_path('id' => @object.uuid),
+      class: 'btn btn-primary',
+      title: 'Re-run',
+      data: {toggle: :tooltip, placement: :top}, title: 'This will make a copy and take you there. You can then make any needed changes and run it',
+      method: :post,
+      ) do %>
+    <i class="fa fa-fw fa-play"></i> Re-run
+  <% end %>
+<% end %>
index a6c4bffacd2fc1add6043a16226e28bbae15affa..b2fb245454aae2ead67ca1851ba5f57700678512 100644 (file)
@@ -1,22 +1,30 @@
-<% n_inputs = cwl_inputs_required(@object, get_cwl_inputs(@object.mounts[:"/var/lib/cwl/workflow.json"][:content]), [:mounts, :"/var/lib/cwl/cwl.input.json", :content]) %>
+<%
+n_inputs = if @object.mounts[:"/var/lib/cwl/workflow.json"] && @object.mounts[:"/var/lib/cwl/cwl.input.json"]
+             cwl_inputs_required(@object, get_cwl_inputs(@object.mounts[:"/var/lib/cwl/workflow.json"][:content]), [:mounts, :"/var/lib/cwl/cwl.input.json", :content])
+           else
+             0
+           end
+%>
 
 <% content_for :pi_input_form do %>
 <form role="form" style="width:60%">
   <div class="form-group">
-    <% workflow = @object.mounts[:"/var/lib/cwl/workflow.json"][:content] %>
-    <% inputs = get_cwl_inputs(workflow) %>
-    <% inputs.each do |input| %>
-      <label for="#input-<%= cwl_shortname(input[:id]) %>">
-        <%= input[:label] || cwl_shortname(input[:id]) %>
-      </label>
-      <div>
-        <p class="form-control-static">
-          <%= render_cwl_input @object, input, [:mounts, :"/var/lib/cwl/cwl.input.json", :content] %>
+    <% workflow = @object.mounts[:"/var/lib/cwl/workflow.json"].andand[:content] %>
+    <% if workflow %>
+      <% inputs = get_cwl_inputs(workflow) %>
+      <% inputs.each do |input| %>
+        <label for="#input-<%= cwl_shortname(input[:id]) %>">
+          <%= input[:label] || cwl_shortname(input[:id]) %>
+        </label>
+        <div>
+          <p class="form-control-static">
+            <%= render_cwl_input @object, input, [:mounts, :"/var/lib/cwl/cwl.input.json", :content] %>
+          </p>
+        </div>
+        <p class="help-block">
+          <%= input[:doc] %>
         </p>
-      </div>
-      <p class="help-block">
-        <%= input[:doc] %>
-      </p>
+      <% end %>
     <% end %>
   </div>
 </form>
index ded535ef3ad5109e81a33ea1fd9815cde8ac6905..06ed01ee6efd71282f6c1e647bdd542c717b1569 100644 (file)
@@ -48,7 +48,7 @@
           <div class="col-md-3">
             <% if current_job[:started_at] %>
               <% walltime = ((if current_job[:finished_at] then current_job[:finished_at] else Time.now() end) - current_job[:started_at]) %>
-              <% cputime = (current_job[:runtime_constraints].andand[:min_nodes] || 1) *
+              <% cputime = (current_job[:runtime_constraints].andand[:min_nodes] || 1).to_i *
                            ((current_job[:finished_at] || Time.now()) - current_job[:started_at]) %>
               <%= render_runtime(walltime, false) %>
               <% if cputime > 0 %> / <%= render_runtime(cputime, false) %> (<%= (cputime/walltime).round(1) %>&Cross;)<% end %>
index 4343f2e57b5adbb64dfb0fbabe177b9d7f937b7a..a4eb6ffb2abad2b959c9bfe48718d5f951227b59 100644 (file)
@@ -66,7 +66,7 @@
     <%
         cputime = pipeline_jobs.map { |j|
         if j[:job][:started_at]
-          (j[:job][:runtime_constraints].andand[:min_nodes] || 1) * ((j[:job][:finished_at] || Time.now()) - j[:job][:started_at])
+          (j[:job][:runtime_constraints].andand[:min_nodes] || 1).to_i * ((j[:job][:finished_at] || Time.now()) - j[:job][:started_at])
         else
           0
         end
index e0093bf6de320a3aacd471e8dbc3c4128983aed5..ab6eb16f5153862061a40c6b59f85bd89819c4f5 100644 (file)
   preload_links_for_objects(collection_pdhs + collection_uuids)
 %>
 
+<%
+  if !PipelineInstance.api_exists?(:index)
+    recent_procs_title = 'Recent processes'
+    run_proc_title = 'Choose a workflow to run:'
+  else
+    recent_procs_title = 'Recent pipelines and processes'
+    run_proc_title = 'Choose a pipeline or workflow to run:'
+  end
+%>
+
   <div class="row">
     <div class="col-md-6">
       <div class="panel panel-default" style="min-height: 10.5em">
         <div class="panel-heading">
-          <span class="panel-title">Recent pipelines and processes</span>
+          <span class="panel-title"><%=recent_procs_title%></span>
           <% if current_user.andand.is_active %>
             <span class="pull-right recent-processes-actions">
               <span>
                 <%= link_to(
                 choose_work_unit_templates_path(
-                  title: 'Choose a pipeline or workflow to run:',
+                  title: run_proc_title,
                   action_name: 'Next: choose inputs <i class="fa fa-fw fa-arrow-circle-right"></i>',
                   action_href: work_units_path,
                   action_method: 'post',
                   action_data: {'selection_param' => 'work_unit[template_uuid]', 'work_unit[owner_uuid]' => current_user.uuid, 'success' => 'redirect-to-created-object'}.to_json),
                 { class: "btn btn-primary btn-xs", remote: true }) do %>
-                  <i class="fa fa-fw fa-gear"></i> Run a pipeline...
+                  <i class="fa fa-fw fa-gear"></i> Run a process...
                 <% end %>
               </span>
               <span>
diff --git a/apps/workbench/app/views/projects/_show_processes.html.erb b/apps/workbench/app/views/projects/_show_processes.html.erb
new file mode 100644 (file)
index 0000000..71f6a89
--- /dev/null
@@ -0,0 +1,5 @@
+<%= render_pane 'tab_contents', to_string: true, locals: {
+      limit: 50,
+      filters: [['uuid', 'is_a', ["arvados#containerRequest"]]],
+      sortable_columns: { 'name' => 'container_requests.name', 'description' => 'container_requests.description' }
+    }.merge(local_assigns) %>
index e52d826cf60da778f9b343d1d44578c6cc5b0c7f..56055645170b8640b69c5691352ca858c45205ce 100644 (file)
@@ -9,6 +9,16 @@
   </h2>
 <% end %>
 
+<%
+  if !PipelineInstance.api_exists?(:index)
+    run_proc_title = 'Choose a workflow to run:'
+    run_proc_hover = 'Run a workflow in this project'
+  else
+    run_proc_title = 'Choose a pipeline or workflow to run:'
+    run_proc_hover = 'Run a pipeline or workflow in this project'
+  end
+%>
+
 <% content_for :tab_line_buttons do %>
   <% if @object.editable? %>
     <div class="btn-group btn-group-sm">
     </div>
     <%= link_to(
           choose_work_unit_templates_path(
-            title: 'Choose a pipeline or workflow to run:',
+            title: run_proc_title,
             action_name: 'Next: choose inputs <i class="fa fa-fw fa-arrow-circle-right"></i>',
             action_href: work_units_path,
             action_method: 'post',
             action_data: {'selection_param' => 'work_unit[template_uuid]', 'work_unit[owner_uuid]' => @object.uuid, 'success' => 'redirect-to-created-object'}.to_json),
-          { class: "btn btn-primary btn-sm", remote: true, title: "Run a pipeline or workflow in this project" }) do %>
-      <i class="fa fa-fw fa-gear"></i> Run a pipeline...
+          { class: "btn btn-primary btn-sm", remote: true, title: run_proc_hover }) do %>
+      <i class="fa fa-fw fa-gear"></i> Run a process...
     <% end %>
     <%= link_to projects_path({'project[owner_uuid]' => @object.uuid, 'options' => {'ensure_unique_name' => true}}), method: :post, title: "Add a subproject to this project", class: 'btn btn-sm btn-primary' do %>
       <i class="fa fa-fw fa-plus"></i>
index e4e27829866a0748558efdd639496b4a420fb510..c2dcba80942a68d5107918a81aa723de5d24db9e 100644 (file)
@@ -26,6 +26,11 @@ diagnostics:
     pipeline_2:
       template_uuid: zzzzz-p5p6p-1xbobfobk94ppbv
       input_paths: [zzzzz-4zz18-nz98douzhaa3jh2, zzzzz-4zz18-gpw9o5wpcti3nib]
+  container_requests_to_test:
+    container_request_1:
+      workflow_uuid: zzzzz-7fd4e-60e96shgwspt4mw
+      input_paths: []
+      max_wait_seconds: 10
 
 # Below is a sample setting for performance testing.
 # Configure workbench URL as "arvados_workbench_url"
index 7c2312c1cee57c7ffef86ad300539d8c575db5bf..21cb7c40bc7755c22dc026a9f23c917369614149 100644 (file)
@@ -26,6 +26,7 @@ ArvadosWorkbench::Application.routes.draw do
   resources :containers
   resources :container_requests do
     post 'cancel', :on => :member
+    post 'copy', on: :member
   end
   get '/virtual_machines/:id/webshell/:login' => 'virtual_machines#webshell', :as => :webshell_virtual_machine
   resources :authorized_keys
diff --git a/apps/workbench/lib/tasks/config_dump.rake b/apps/workbench/lib/tasks/config_dump.rake
new file mode 100644 (file)
index 0000000..c7e0214
--- /dev/null
@@ -0,0 +1,6 @@
+namespace :config do
+  desc 'Show site configuration'
+  task dump: :environment do
+    puts $application_config.to_yaml
+  end
+end
index 8dbbbd07c11d5add20dc91f2402ed015e8d43d2a..70e042cd3dcad21a38de4b639343bef6ab7c5098 100644 (file)
@@ -29,4 +29,31 @@ class ContainerRequestsControllerTest < ActionController::TestCase
     assert_includes @response.body, '<div id="event_log_div"'
     assert_select 'Download the log', false
   end
+
+  test "completed container request offers re-run option" do
+    use_token 'active'
+
+    uuid = api_fixture('container_requests')['completed']['uuid']
+
+    get :show, {id: uuid}, session_for(:active)
+    assert_response :success
+
+   assert_includes @response.body, "href=\"/container_requests/#{uuid}/copy\""
+  end
+
+  test "container request copy" do
+    completed_cr = api_fixture('container_requests')['completed']
+    post(:copy,
+         {
+           id: completed_cr['uuid']
+         },
+         session_for(:active))
+    assert_response 302
+    copied_cr = assigns(:object)
+    assert_not_nil copied_cr
+    assert_equal 'Uncommitted', copied_cr[:state]
+    assert_equal "Copy of #{completed_cr['name']}", copied_cr['name']
+    assert_equal completed_cr['cmd'], copied_cr['cmd']
+    assert_equal completed_cr['runtime_constraints']['ram'], copied_cr['runtime_constraints'][:ram]
+  end
 end
index a41d87f31ab34187804bbfb25beb12da310f9aea..47276c02e835419cf89601e78c153f4f5431df21 100644 (file)
@@ -12,6 +12,7 @@ class DisabledApiTest < ActionController::TestCase
     get :index, {}, session_for(:active)
     assert_includes @response.body, "zzzzz-xvhdp-cr4runningcntnr" # expect crs
     assert_not_includes @response.body, "zzzzz-d1hrv-"   # expect no pipelines
+    assert_includes @response.body, "Run a process"
   end
 
   [
@@ -33,6 +34,7 @@ class DisabledApiTest < ActionController::TestCase
   end
 
   [
+    :admin,
     :active,
     nil,
   ].each do |user|
@@ -58,6 +60,7 @@ class DisabledApiTest < ActionController::TestCase
       assert_includes resp, "href=\"#Pipelines_and_processes\""
       assert_includes resp, "href=\"#Workflows\""
       assert_not_includes resp, "href=\"#Pipeline_templates\""
+      assert_includes @response.body, "Run a process" if user == :admin
     end
   end
 end
index d31d6e3458a94f629bc21329ba3fa5db1b79061e..1b19f2f4936a6cfd8c3b82cd1f8ebaaf32db49fb 100644 (file)
@@ -101,8 +101,9 @@ class ProjectsControllerTest < ActionController::TestCase
   end
 
   test "project admin can remove collections from the project" do
-    # Deleting an object that supports 'expires_at' should make it
-    # completely inaccessible to API queries, not simply moved out of the project.
+    # Deleting an object that supports 'trash_at' should make it
+    # completely inaccessible to API queries, not simply moved out of
+    # the project.
     coll_key = "collection_to_remove_from_subproject"
     coll_uuid = api_fixture("collections")[coll_key]["uuid"]
     delete(:remove_item,
@@ -116,12 +117,12 @@ class ProjectsControllerTest < ActionController::TestCase
 
     use_token :subproject_admin
     assert_raise ArvadosApiClient::NotFoundException do
-      Collection.find(coll_uuid)
+      Collection.find(coll_uuid, cache: false)
     end
   end
 
   test "project admin can remove items from project other than collections" do
-    # An object which does not have an expired_at field (e.g. Specimen)
+    # An object which does not have an trash_at field (e.g. Specimen)
     # should be implicitly moved to the user's Home project when removed.
     specimen_uuid = api_fixture('specimens', 'in_asubproject')['uuid']
     delete(:remove_item,
@@ -490,27 +491,28 @@ class ProjectsControllerTest < ActionController::TestCase
     ["user1_with_load", 2, ["project_with_10_collections"], "project_with_2_pipelines_and_60_crs"],
     ["admin", 5, ["anonymously_accessible_project", "subproject_in_anonymous_accessible_project"], "aproject"],
   ].each do |user, page_size, tree_segment, unexpected|
+    # Note: this test is sensitive to database collation. It passes
+    # with en_US.UTF-8.
     test "build my projects tree for #{user} user and verify #{unexpected} is omitted" do
       use_token user
-      ctrl = ProjectsController.new
-
-      current_user = User.find(api_fixture('users')[user]['uuid'])
 
-      my_tree = ctrl.send :my_wanted_projects_tree, current_user, page_size
+      tree, _, _ = @controller.send(:my_wanted_projects_tree,
+                                    User.current,
+                                    page_size)
 
       tree_segment_at_depth_1 = api_fixture('groups')[tree_segment[0]]
       tree_segment_at_depth_2 = api_fixture('groups')[tree_segment[1]] if tree_segment[1]
 
-      tree_nodes = {}
-      my_tree[0].each do |x|
-        tree_nodes[x[:object]['uuid']] = x[:depth]
+      node_depth = {}
+      tree.each do |x|
+        node_depth[x[:object]['uuid']] = x[:depth]
       end
 
-      assert_equal(1, tree_nodes[tree_segment_at_depth_1['uuid']])
-      assert_equal(2, tree_nodes[tree_segment_at_depth_2['uuid']]) if tree_segment[1]
+      assert_equal(1, node_depth[tree_segment_at_depth_1['uuid']])
+      assert_equal(2, node_depth[tree_segment_at_depth_2['uuid']]) if tree_segment[1]
 
       unexpected_project = api_fixture('groups')[unexpected]
-      assert_nil(tree_nodes[unexpected_project['uuid']])
+      assert_nil(node_depth[unexpected_project['uuid']], node_depth.inspect)
     end
   end
 
diff --git a/apps/workbench/test/diagnostics/container_request_test.rb b/apps/workbench/test/diagnostics/container_request_test.rb
new file mode 100644 (file)
index 0000000..257159a
--- /dev/null
@@ -0,0 +1,49 @@
+require 'diagnostics_test_helper'
+
+# This test assumes that the configured workflow_uuid corresponds to a cwl workflow.
+# Ex: configure a workflow using the steps below and use the resulting workflow uuid:
+#   > cd arvados/doc/user/cwl/bwa-mem
+#   > arvados-cwl-runner --create-workflow bwa-mem.cwl bwa-mem-input.yml
+
+class ContainerRequestTest < DiagnosticsTest
+  crs_to_test = Rails.configuration.container_requests_to_test.andand.keys
+
+  setup do
+    need_selenium 'to make websockets work'
+  end
+
+  crs_to_test.andand.each do |cr_to_test|
+    test "run container_request: #{cr_to_test}" do
+      cr_config = Rails.configuration.container_requests_to_test[cr_to_test]
+
+      visit_page_with_token 'active'
+
+      find('.btn', text: 'Run a process').click
+
+      within('.modal-dialog') do
+        page.find_field('Search').set cr_config['workflow_uuid']
+        wait_for_ajax
+        find('.selectable', text: 'bwa-mem.cwl').click
+        find('.btn', text: 'Next: choose inputs').click
+      end
+
+      page.assert_selector('a.disabled,button.disabled', text: 'Run') if cr_config['input_paths'].any?
+
+      # Choose input for the workflow
+      cr_config['input_paths'].each do |look_for|
+        select_input look_for
+      end
+      wait_for_ajax
+
+      # All needed input are already filled in. Run this workflow now
+      page.assert_no_selector('a.disabled,button.disabled', text: 'Run')
+      find('a,button', text: 'Run').click
+
+      # container_request is running. Run button is no longer available.
+      page.assert_no_selector('a', text: 'Run')
+
+      # Wait for container_request run to complete
+      wait_until_page_has 'completed', cr_config['max_wait_seconds']
+    end
+  end
+end
index d038222cf0cf58278818bd087e288a4e1c11b52c..11d0e42629af4593d59c41ffddba31535fdce6f2 100644 (file)
@@ -42,54 +42,11 @@ class PipelineTest < DiagnosticsTest
       find('a,button', text: 'Components').click
       find('a,button', text: 'Run').click
 
-      # Pipeline is running. We have a "Stop" button instead now.
+      # Pipeline is running. We have a "Pause" button instead now.
       page.assert_selector 'a,button', text: 'Pause'
 
       # Wait for pipeline run to complete
       wait_until_page_has 'completed', pipeline_config['max_wait_seconds']
     end
   end
-
-  def select_input look_for
-    inputs_needed = page.all('.btn', text: 'Choose')
-    return if (!inputs_needed || !inputs_needed.any?)
-
-    look_for_uuid = nil
-    look_for_file = nil
-    if look_for.andand.index('/').andand.>0
-      partitions = look_for.partition('/')
-      look_for_uuid = partitions[0]
-      look_for_file = partitions[2]
-    else
-      look_for_uuid = look_for
-      look_for_file = nil
-    end
-
-    assert_triggers_dom_event 'shown.bs.modal' do
-      inputs_needed[0].click
-    end
-
-    within('.modal-dialog') do
-      if look_for_uuid
-        fill_in('Search', with: look_for_uuid, exact: true)
-        wait_for_ajax
-      end
-             
-      page.all('.selectable').first.click
-      wait_for_ajax
-      # ajax reload is wiping out input selection after search results; so, select again.
-      page.all('.selectable').first.click
-      wait_for_ajax
-
-      if look_for_file
-        wait_for_ajax
-        within('.collection_files_name', text: look_for_file) do
-          find('.fa-file').click
-        end
-      end
-      
-      find('button', text: 'OK').click
-      wait_for_ajax
-    end
-  end
 end
index 3587721edae7bc6e96778efceaaedfadddbeacd3..46b961ae11923e7db4b94e4e78215c407390099c 100644 (file)
@@ -21,6 +21,49 @@ class DiagnosticsTest < ActionDispatch::IntegrationTest
     visit page_with_token(tokens[token_name], (workbench_url + path))
   end
 
+  def select_input look_for
+    inputs_needed = page.all('.btn', text: 'Choose')
+    return if (!inputs_needed || !inputs_needed.any?)
+
+    look_for_uuid = nil
+    look_for_file = nil
+    if look_for.andand.index('/').andand.>0
+      partitions = look_for.partition('/')
+      look_for_uuid = partitions[0]
+      look_for_file = partitions[2]
+    else
+      look_for_uuid = look_for
+      look_for_file = nil
+    end
+
+    assert_triggers_dom_event 'shown.bs.modal' do
+      inputs_needed[0].click
+    end
+
+    within('.modal-dialog') do
+      if look_for_uuid
+        fill_in('Search', with: look_for_uuid, exact: true)
+        wait_for_ajax
+      end
+
+      page.all('.selectable').first.click
+      wait_for_ajax
+      # ajax reload is wiping out input selection after search results; so, select again.
+      page.all('.selectable').first.click
+      wait_for_ajax
+
+      if look_for_file
+        wait_for_ajax
+        within('.collection_files_name', text: look_for_file) do
+          find('.fa-file').click
+        end
+      end
+
+      find('button', text: 'OK').click
+      wait_for_ajax
+    end
+  end
+
   # Looks for the text_to_look_for for up to the max_time provided
   def wait_until_page_has text_to_look_for, max_time=30
     max_time = 30 if (!max_time || (max_time.to_s != max_time.to_i.to_s))
index c4eb941b08894bb4cf58c88f75ba7e721e120f3f..b49cbf91c7f52f701ab7d898b866ed3619c9c5c6 100644 (file)
@@ -251,12 +251,12 @@ class ApplicationLayoutTest < ActionDispatch::IntegrationTest
 
       assert_text 'Recent pipelines and processes' # seeing dashboard now
       within('.recent-processes-actions') do
-        assert page.has_link?('Run a pipeline')
+        assert page.has_link?('Run a process')
         assert page.has_link?('All processes')
       end
 
       within('.recent-processes') do
-        assert_text 'pipeline_with_job'
+        assert_text 'running'
 
         within('.row-zzzzz-xvhdp-cr4runningcntnr') do
           assert_text 'requester_for_running_cr'
@@ -269,7 +269,7 @@ class ApplicationLayoutTest < ActionDispatch::IntegrationTest
 
         assert_text 'completed container request'
         within('.row-zzzzz-xvhdp-cr4completedctr')do
-          assert page.has_link? '1f4b0bc7583c2a7f9102c395f4ffc5e3+45'
+          assert page.has_link? 'foo_file'
         end
       end
 
index df6584ebb6490cedac2fe439a1a77110a9feeb84..bd3a813f72af4b8ea77c35568b6f737a1389237d 100644 (file)
@@ -96,4 +96,17 @@ class ContainerRequestsTest < ActionDispatch::IntegrationTest
     wait_for_ajax
     assert_text 'This container is queued'
   end
+
+  test "Run button enabled when workflow is empty and no inputs are needed" do
+    visit page_with_token("active")
+
+    find('.btn', text: 'Run a process').click
+    within('.modal-dialog') do
+      find('.selectable', text: 'Valid workflow with no definition yaml').click
+      find('.btn', text: 'Next: choose inputs').click
+    end
+
+    assert_text 'This workflow does not need any further inputs'
+    page.assert_selector 'a', text: 'Run'
+  end
 end
index 171580bbaa2bc9816a9ba1061e40142d0487c8e9..338280684ecd3a07a5f8e5f244c528a57d7b51b5 100644 (file)
@@ -391,7 +391,7 @@ class PipelineInstancesTest < ActionDispatch::IntegrationTest
     collection = api_fixture('collections', collection_fixture)
 
     # create a pipeline instance
-    find('.btn', text: 'Run a pipeline').click
+    find('.btn', text: 'Run a process').click
     within('.modal-dialog') do
       find('.selectable', text: template_name).click
       find('.btn', text: 'Next: choose inputs').click
index e5877aca6d1e88824b2575ba571eda21de403ee3..8a47a53bd8171616f13481d8c4c19cae306f7f3c 100644 (file)
@@ -558,7 +558,6 @@ class ProjectsTest < ActionDispatch::IntegrationTest
   end
 
   test "add new project using projects dropdown" do
-    # verify that selection options are disabled on the project until an item is selected
     visit page_with_token 'active', '/'
 
     # Add a new project
@@ -566,13 +565,6 @@ class ProjectsTest < ActionDispatch::IntegrationTest
     click_link 'Add a new project'
     assert_text 'New project'
     assert_text 'No description provided'
-
-    # Add one more new project
-    find("#projects-menu").click
-    click_link 'Add a new project'
-    match = /New project \(\d\)/.match page.text
-    assert match, 'Expected project name not found'
-    assert_text 'No description provided'
   end
 
   test "first tab loads data when visiting other tab directly" do
@@ -746,7 +738,7 @@ class ProjectsTest < ActionDispatch::IntegrationTest
       project = api_fixture('groups')['aproject']
       visit page_with_token 'active', '/projects/' + project['uuid']
 
-      find('.btn', text: 'Run a pipeline').click
+      find('.btn', text: 'Run a process').click
 
       # in the chooser, verify preview and click Next button
       within('.modal-dialog') do
index f04616dd383ac49f927a544fb2e7d372c30b8acb..5b5848ee7766580003ee10be1c28ba944070bf36 100644 (file)
@@ -109,8 +109,8 @@ class WorkUnitsTest < ActionDispatch::IntegrationTest
   end
 
   [
-    ['Two Part Pipeline Template', 'part-one', 'Provide a value for the following'],
-    ['Workflow with input specifications', 'this workflow has inputs specified', 'Provide a value for the following'],
+    ['Pipeline with default input specifications', 'part-one', 'Provide values for the following'],
+    ['Workflow with default input specifications', 'this workflow has inputs specified', 'Provide a value for the following'],
   ].each do |template_name, preview_txt, process_txt|
     test "run a process using template #{template_name} from dashboard" do
       visit page_with_token('admin')
@@ -118,7 +118,7 @@ class WorkUnitsTest < ActionDispatch::IntegrationTest
 
       within('.recent-processes-actions') do
         assert page.has_link?('All processes')
-        find('a', text: 'Run a pipeline').click
+        find('a', text: 'Run a process').click
       end
 
       # in the chooser, verify preview and click Next button
@@ -131,6 +131,10 @@ class WorkUnitsTest < ActionDispatch::IntegrationTest
       # in the process page now
       assert_text process_txt
       assert_selector 'a', text: template_name
+
+      assert_equal "Set value for ex_string_def", find('div.form-group > div > p.form-control-static > a', text: "hello-testing-123")[:"data-title"]
+
+      page.assert_selector 'a.disabled,button.disabled', text: 'Run'
     end
   end
 
index d068ee2aaf08f3e7451ae817599e7dcaaca0d535..dcfd7d86e050532319662f13666db55dd69bf06c 100644 (file)
@@ -19,7 +19,7 @@ class BrowsingTest < WorkbenchPerformanceTest
   test "home page" do
     visit_page_with_token
     assert_text 'Dashboard'
-    assert_selector 'a', text: 'Run a pipeline'
+    assert_selector 'a', text: 'Run a process'
   end
 
   test "search for hash" do
index 0ff38140e1a6f976ffc5b0da27e73b43415b4d54..564a5d3b5b4068025fb90297637ce0dd87ee4232 100644 (file)
@@ -53,11 +53,11 @@ class WorkUnitTest < ActiveSupport::TestCase
   end
 
   [
-    [Job, 'running_job_with_components', 1, 1, nil],
-    [Job, 'queued', nil, nil, 1],
-    [PipelineInstance, 'pipeline_in_running_state', 1, 1, nil],
-    [PipelineInstance, 'has_component_with_completed_jobs', 60, 60, nil],
-  ].each do |type, fixture, walltime, cputime, queuedtime|
+    [Job, 'running_job_with_components', 1, 1, nil, true],
+    [Job, 'queued', nil, 0, 1, false],
+    [PipelineInstance, 'pipeline_in_running_state', 1, 1, nil, false],
+    [PipelineInstance, 'has_component_with_completed_jobs', 60, 60, nil, true],
+  ].each do |type, fixture, walltime, cputime, queuedtime, cputime_more_than_walltime|
     test "times for #{fixture}" do
       use_token 'active'
       obj = find_fixture(type, fixture)
@@ -80,6 +80,8 @@ class WorkUnitTest < ActiveSupport::TestCase
       else
         assert_equal queuedtime, wu.queuedtime
       end
+
+      assert_equal cputime_more_than_walltime, (wu.cputime > wu.walltime) if wu.cputime and wu.walltime
     end
   end
 
diff --git a/backports/deb-fuse/fpm-info.sh b/backports/deb-fuse/fpm-info.sh
new file mode 100644 (file)
index 0000000..6f2b022
--- /dev/null
@@ -0,0 +1,5 @@
+case "$TARGET" in
+    ubuntu1204)
+        fpm_depends+=('libfuse2 = 2.9.2-5')
+        ;;
+esac
diff --git a/backports/deb-libfuse-dev/fpm-info.sh b/backports/deb-libfuse-dev/fpm-info.sh
new file mode 100644 (file)
index 0000000..6f2b022
--- /dev/null
@@ -0,0 +1,5 @@
+case "$TARGET" in
+    ubuntu1204)
+        fpm_depends+=('libfuse2 = 2.9.2-5')
+        ;;
+esac
index 9fc00987e6f9561f37528368264adf15718b3afd..401a8367a7bf2c2a6bc9aeed1fa0b0cd156c6efe 100644 (file)
@@ -1,11 +1,19 @@
 case "$TARGET" in
+    centos6)
+        build_depends+=('fuse-libs' 'fuse-devel')
+        fpm_depends+=(glibc 'fuse-libs = 2.9.2-5' 'fuse = 2.9.2-5')
+        ;;
     centos*)
         build_depends+=('fuse-devel')
         fpm_depends+=(glibc fuse-libs)
         ;;
+    ubuntu1204)
+        build_depends+=(libfuse2 libfuse-dev)
+        fpm_depends+=(libc6 python-contextlib2 'libfuse2 = 2.9.2-5' 'fuse = 2.9.2-5')
+        ;;
     debian* | ubuntu*)
         build_depends+=('libfuse-dev')
-        fpm_depends+=(libc6 libfuse2)
+        fpm_depends+=(libc6 'libfuse2 > 2.9.0' 'fuse > 2.9.0')
         ;;
 esac
 
diff --git a/backports/rpm-fuse-devel/fpm-info.sh b/backports/rpm-fuse-devel/fpm-info.sh
new file mode 100644 (file)
index 0000000..89cdebe
--- /dev/null
@@ -0,0 +1,5 @@
+case "$TARGET" in
+    centos6)
+        fpm_depends+=('fuse-libs = 2.9.2-5')
+        ;;
+esac
diff --git a/backports/rpm-fuse/fpm-info.sh b/backports/rpm-fuse/fpm-info.sh
new file mode 100644 (file)
index 0000000..89cdebe
--- /dev/null
@@ -0,0 +1,5 @@
+case "$TARGET" in
+    centos6)
+        fpm_depends+=('fuse-libs = 2.9.2-5')
+        ;;
+esac
index 418254457dd41e61f1e39bf698b297fcf59dcc26..4c67839a1006693f3d70f8adaf2823e4fa4f11e5 100644 (file)
@@ -23,7 +23,11 @@ run-build-packages-python-and-ruby.sh    Build Python and Ruby packages suitable
 
 run-build-docker-images.sh               Build arvbox Docker images.
 
-run-build-docker-jobs-image.sh           Build arvados/jobs Docker image.
+run-build-docker-jobs-image.sh           Build arvados/jobs Docker image
+                                         (uses published debian packages)
+
+build-dev-docker-jobs-image.sh           Build developer arvados/jobs Docker image
+                                         (uses local git tree)
 
 run-library.sh                           A library of functions shared by the
                                          various scripts in this
diff --git a/build/build-dev-docker-jobs-image.sh b/build/build-dev-docker-jobs-image.sh
new file mode 100755 (executable)
index 0000000..5a6e777
--- /dev/null
@@ -0,0 +1,63 @@
+#!/bin/bash
+
+read -rd "\000" helpmessage <<EOF
+Build an arvados/jobs Docker image from local git tree.
+
+Intended for use by developers working on arvados-python-client or
+arvados-cwl-runner and need to run a crunch job with a custom package
+version.  Also supports building custom cwltool if CWLTOOL is set.
+
+Syntax:
+        WORKSPACE=/path/to/arvados $(basename $0)
+
+WORKSPACE=path         Path to the Arvados source tree to build packages from
+CWLTOOL=path           (optional) Path to cwltool git repository.
+
+EOF
+
+set -e
+
+if [[ -z "$WORKSPACE" ]] ; then
+    echo "$helpmessage"
+    echo
+    echo "Must set WORKSPACE"
+    exit 1
+fi
+
+if [[ -z "$ARVADOS_API_HOST" || -z "$ARVADOS_API_TOKEN" ]] ; then
+    echo "$helpmessage"
+    echo
+    echo "Must set ARVADOS_API_HOST and ARVADOS_API_TOKEN"
+    exit 1
+fi
+
+cd "$WORKSPACE"
+
+(cd sdk/python && python setup.py sdist)
+sdk=$(cd sdk/python/dist && ls -t arvados-python-client-*.tar.gz | head -n1)
+
+(cd sdk/cwl && python setup.py sdist)
+runner=$(cd sdk/cwl/dist && ls -t arvados-cwl-runner-*.tar.gz | head -n1)
+
+rm -rf sdk/cwl/cwltool_dist
+mkdir -p sdk/cwl/cwltool_dist
+if [[ -n "$CWLTOOL" ]] ; then
+    (cd "$CWLTOOL" && python setup.py sdist)
+    cwltool=$(cd "$CWLTOOL/dist" && ls -t cwltool-*.tar.gz | head -n1)
+    cp "$CWLTOOL/dist/$cwltool" $WORKSPACE/sdk/cwl/cwltool_dist
+fi
+
+. build/run-library.sh
+
+python_sdk_ts=$(cd sdk/python && timestamp_from_git)
+cwl_runner_ts=$(cd sdk/cwl && timestamp_from_git)
+
+if [[ $python_sdk_ts -gt $cwl_runner_ts ]]; then
+    gittag=$(git log --first-parent --max-count=1 --format=format:%H sdk/python)
+else
+    gittag=$(git log --first-parent --max-count=1 --format=format:%H sdk/cwl)
+fi
+
+docker build --build-arg sdk=$sdk --build-arg runner=$runner --build-arg cwltool=$cwltool -f "$WORKSPACE/sdk/dev-jobs.dockerfile" -t arvados/jobs:$gittag "$WORKSPACE/sdk"
+echo arv-keepdocker arvados/jobs $gittag
+arv-keepdocker arvados/jobs $gittag
index 3fa07e6576b787801ef9557792412c85b9d061c8..0cbfcd894274bd43a37f830aa7dc02dbdd15f28a 100644 (file)
@@ -1 +1 @@
-LIBCLOUD_PIN=0.20.2.dev1
\ No newline at end of file
+LIBCLOUD_PIN=0.20.2.dev2
\ No newline at end of file
index 18694ed1844af6cb2fa87dd5f12b863df6402e7c..9987e9e0eb8565e7211c70a29384befd5d9ab2d5 100644 (file)
@@ -1,17 +1,9 @@
-all: centos6/generated centos7/generated debian7/generated debian8/generated ubuntu1204/generated ubuntu1404/generated
-
-centos6/generated: common-generated-all
-       test -d centos6/generated || mkdir centos6/generated
-       cp -rlt centos6/generated common-generated/*
+all: centos7/generated debian8/generated ubuntu1204/generated ubuntu1404/generated
 
 centos7/generated: common-generated-all
        test -d centos7/generated || mkdir centos7/generated
        cp -rlt centos7/generated common-generated/*
 
-debian7/generated: common-generated-all
-       test -d debian7/generated || mkdir debian7/generated
-       cp -rlt debian7/generated common-generated/*
-
 debian8/generated: common-generated-all
        test -d debian8/generated || mkdir debian8/generated
        cp -rlt debian8/generated common-generated/*
diff --git a/build/package-build-dockerfiles/centos6/Dockerfile b/build/package-build-dockerfiles/centos6/Dockerfile
deleted file mode 100644 (file)
index 8ea81f4..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-FROM centos:6
-MAINTAINER Brett Smith <brett@curoverse.com>
-
-# Install build dependencies provided in base distribution
-RUN yum -q -y install make automake gcc gcc-c++ libyaml-devel patch readline-devel zlib-devel libffi-devel openssl-devel bzip2 libtool bison sqlite-devel rpm-build git perl-ExtUtils-MakeMaker libattr-devel nss-devel libcurl-devel which tar unzip scl-utils centos-release-scl postgresql-devel
-
-# Install golang binary
-ADD generated/go1.7.1.linux-amd64.tar.gz /usr/local/
-RUN ln -s /usr/local/go/bin/go /usr/local/bin/
-
-# Install RVM
-RUN gpg --keyserver pool.sks-keyservers.net --recv-keys D39DC0E3 && \
-    curl -L https://get.rvm.io | bash -s stable && \
-    /usr/local/rvm/bin/rvm install 2.1 && \
-    /usr/local/rvm/bin/rvm alias create default ruby-2.1 && \
-    /usr/local/rvm/bin/rvm-exec default gem install bundler && \
-    /usr/local/rvm/bin/rvm-exec default gem install cure-fpm --version 1.6.0b
-
-# Need to "touch" RPM database to workaround bug in interaction between
-# overlayfs and yum (https://bugzilla.redhat.com/show_bug.cgi?id=1213602)
-RUN touch /var/lib/rpm/* && yum -q -y install python27 python33
-RUN scl enable python33 "easy_install-3.3 pip" && scl enable python27 "easy_install-2.7 pip"
-
-# fpm requires ffi which now wants xz-libs-5 which isn't packaged for centos6
-# but the library from xz-libs-4.999 appears to be good enough.
-RUN ln -s /usr/lib64/liblzma.so.0 /usr/lib64/lzma.so.5
-
-RUN cd /tmp && \
-    (curl -OLf 'http://pkgs.repoforge.org/rpmforge-release/rpmforge-release-0.5.3-1.el6.rf.x86_64.rpm' || \
-     curl -OLf 'http://repoforge.eecs.wsu.edu/redhat/el6/en/x86_64/rpmforge/RPMS/rpmforge-release-0.5.3-1.el6.rf.x86_64.rpm') && \
-    rpm -ivh rpmforge-release-0.5.3-1.el6.rf.x86_64.rpm && \
-    sed -i 's/enabled = 0/enabled = 1/' /etc/yum.repos.d/rpmforge.repo
-
-RUN touch /var/lib/rpm/* && yum install --assumeyes git
-
-ENV WORKSPACE /arvados
-CMD ["scl", "enable", "python33", "python27", "/usr/local/rvm/bin/rvm-exec default bash /jenkins/run-build-packages.sh --target centos6"]
diff --git a/build/package-build-dockerfiles/debian7/Dockerfile b/build/package-build-dockerfiles/debian7/Dockerfile
deleted file mode 100644 (file)
index 7632c94..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-FROM debian:wheezy
-MAINTAINER Ward Vandewege <ward@curoverse.com>
-
-# Install dependencies and set up system.
-RUN /usr/bin/apt-get update && /usr/bin/apt-get install -q -y python2.7-dev python3 python-setuptools python3-setuptools libcurl4-gnutls-dev curl git procps libattr1-dev libfuse-dev libpq-dev python-pip unzip
-
-# Install RVM
-RUN gpg --keyserver pool.sks-keyservers.net --recv-keys D39DC0E3 && \
-    curl -L https://get.rvm.io | bash -s stable && \
-    /usr/local/rvm/bin/rvm install 2.1 && \
-    /usr/local/rvm/bin/rvm alias create default ruby-2.1 && \
-    /usr/local/rvm/bin/rvm-exec default gem install bundler && \
-    /usr/local/rvm/bin/rvm-exec default gem install cure-fpm --version 1.6.0b
-
-# Install golang binary
-ADD generated/go1.7.1.linux-amd64.tar.gz /usr/local/
-RUN ln -s /usr/local/go/bin/go /usr/local/bin/
-
-ENV WORKSPACE /arvados
-CMD ["/usr/local/rvm/bin/rvm-exec", "default", "bash", "/jenkins/run-build-packages.sh", "--target", "debian7"]
diff --git a/build/package-test-dockerfiles/centos6/Dockerfile b/build/package-test-dockerfiles/centos6/Dockerfile
deleted file mode 100644 (file)
index d38507a..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-FROM centos:6
-MAINTAINER Peter Amstutz <peter.amstutz@curoverse.com>
-
-RUN yum -q install --assumeyes scl-utils centos-release-scl \
-    which tar
-
-# Install RVM
-RUN touch /var/lib/rpm/* && \
-    gpg --keyserver pool.sks-keyservers.net --recv-keys D39DC0E3 && \
-    curl -L https://get.rvm.io | bash -s stable && \
-    /usr/local/rvm/bin/rvm install 2.1 && \
-    /usr/local/rvm/bin/rvm alias create default ruby-2.1 && \
-    /usr/local/rvm/bin/rvm-exec default gem install bundle && \
-    /usr/local/rvm/bin/rvm-exec default gem install cure-fpm --version 1.6.0b
-
-RUN cd /tmp && \
-    (curl -OLf 'http://pkgs.repoforge.org/rpmforge-release/rpmforge-release-0.5.3-1.el6.rf.x86_64.rpm' || \
-     curl -OLf 'http://repoforge.eecs.wsu.edu/redhat/el6/en/x86_64/rpmforge/RPMS/rpmforge-release-0.5.3-1.el6.rf.x86_64.rpm') && \
-    rpm -ivh rpmforge-release-0.5.3-1.el6.rf.x86_64.rpm && \
-    sed -i 's/enabled = 0/enabled = 1/' /etc/yum.repos.d/rpmforge.repo
-
-COPY localrepo.repo /etc/yum.repos.d/localrepo.repo
diff --git a/build/package-test-dockerfiles/centos6/localrepo.repo b/build/package-test-dockerfiles/centos6/localrepo.repo
deleted file mode 100644 (file)
index ac6b898..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-[localrepo]
-name=Arvados Test
-baseurl=file:///arvados/packages/centos6
-gpgcheck=0
-enabled=1
index 6bc40bffa56fac9bdffe85ccce65416ae016aaac..06a39ca397979e022028e727ba563253bbaa777d 100644 (file)
@@ -7,8 +7,8 @@ RUN yum -q -y install scl-utils centos-release-scl which tar
 RUN touch /var/lib/rpm/* && \
     gpg --keyserver pool.sks-keyservers.net --recv-keys D39DC0E3 && \
     curl -L https://get.rvm.io | bash -s stable && \
-    /usr/local/rvm/bin/rvm install 2.1 && \
-    /usr/local/rvm/bin/rvm alias create default ruby-2.1 && \
+    /usr/local/rvm/bin/rvm install 2.3 && \
+    /usr/local/rvm/bin/rvm alias create default ruby-2.3 && \
     /usr/local/rvm/bin/rvm-exec default gem install bundle && \
     /usr/local/rvm/bin/rvm-exec default gem install cure-fpm --version 1.6.0b
 
diff --git a/build/package-test-dockerfiles/debian7/Dockerfile b/build/package-test-dockerfiles/debian7/Dockerfile
deleted file mode 100644 (file)
index c9a2fdc..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-FROM debian:7
-MAINTAINER Peter Amstutz <peter.amstutz@curoverse.com>
-
-# Install RVM
-RUN apt-get update && apt-get -y install curl procps && \
-    gpg --keyserver pool.sks-keyservers.net --recv-keys D39DC0E3 && \
-    curl -L https://get.rvm.io | bash -s stable && \
-    /usr/local/rvm/bin/rvm install 2.1 && \
-    /usr/local/rvm/bin/rvm alias create default ruby-2.1
-
-# udev daemon can't start in a container, so don't try.
-RUN mkdir -p /etc/udev/disabled
-
-RUN echo "deb file:///arvados/packages/debian7/ /" >>/etc/apt/sources.list
index cde18472333cdc59e9421a24fecc5f10d1611d38..c6bc8f4f98c0cd8ea9a102b358a8d2da57329e47 100644 (file)
@@ -2,11 +2,12 @@ FROM debian:8
 MAINTAINER Peter Amstutz <peter.amstutz@curoverse.com>
 
 # Install RVM
-RUN apt-get update && apt-get -y install curl && \
+RUN apt-get update && \
+    DEBIAN_FRONTEND=noninteractive apt-get -y install --no-install-recommends curl ca-certificates && \
     gpg --keyserver pool.sks-keyservers.net --recv-keys D39DC0E3 && \
     curl -L https://get.rvm.io | bash -s stable && \
-    /usr/local/rvm/bin/rvm install 2.1 && \
-    /usr/local/rvm/bin/rvm alias create default ruby-2.1
+    /usr/local/rvm/bin/rvm install 2.3 && \
+    /usr/local/rvm/bin/rvm alias create default ruby-2.3
 
 # udev daemon can't start in a container, so don't try.
 RUN mkdir -p /etc/udev/disabled
index 0cb77c8f8a92a2bbc18bd9005b7a4246d66b48a4..f6223715e1e43793fd71906856fc02aa1f661533 100644 (file)
@@ -2,13 +2,14 @@ FROM ubuntu:precise
 MAINTAINER Peter Amstutz <peter.amstutz@curoverse.com>
 
 # Install RVM
-RUN apt-get update && apt-get -y install curl && \
+RUN apt-get update && \
+    DEBIAN_FRONTEND=noninteractive apt-get -y install --no-install-recommends curl ca-certificates && \
     gpg --keyserver pool.sks-keyservers.net --recv-keys D39DC0E3 && \
     curl -L https://get.rvm.io | bash -s stable && \
-    /usr/local/rvm/bin/rvm install 2.1 && \
-    /usr/local/rvm/bin/rvm alias create default ruby-2.1
+    /usr/local/rvm/bin/rvm install 2.3 && \
+    /usr/local/rvm/bin/rvm alias create default ruby-2.3
 
 # udev daemon can't start in a container, so don't try.
 RUN mkdir -p /etc/udev/disabled
 
-RUN echo "deb file:///arvados/packages/ubuntu1204/ /" >>/etc/apt/sources.list
\ No newline at end of file
+RUN echo "deb file:///arvados/packages/ubuntu1204/ /" >>/etc/apt/sources.list
index 6c4d0e9b51f3c3a7f6acd27e4eec0f7c7cf413ad..f7ee48644df5ecd8afb57c85f0cd3e47ee49386b 100644 (file)
@@ -2,13 +2,14 @@ FROM ubuntu:trusty
 MAINTAINER Peter Amstutz <peter.amstutz@curoverse.com>
 
 # Install RVM
-RUN apt-get update && apt-get -y install curl && \
+RUN apt-get update && \
+    DEBIAN_FRONTEND=noninteractive apt-get -y install --no-install-recommends curl ca-certificates && \
     gpg --keyserver pool.sks-keyservers.net --recv-keys D39DC0E3 && \
     curl -L https://get.rvm.io | bash -s stable && \
-    /usr/local/rvm/bin/rvm install 2.1 && \
-    /usr/local/rvm/bin/rvm alias create default ruby-2.1
+    /usr/local/rvm/bin/rvm install 2.3 && \
+    /usr/local/rvm/bin/rvm alias create default ruby-2.3
 
 # udev daemon can't start in a container, so don't try.
 RUN mkdir -p /etc/udev/disabled
 
-RUN echo "deb file:///arvados/packages/ubuntu1404/ /" >>/etc/apt/sources.list
\ No newline at end of file
+RUN echo "deb file:///arvados/packages/ubuntu1404/ /" >>/etc/apt/sources.list
diff --git a/build/package-testing/test-packages-centos6.sh b/build/package-testing/test-packages-centos6.sh
deleted file mode 120000 (symlink)
index 64ef604..0000000
+++ /dev/null
@@ -1 +0,0 @@
-rpm-common-test-packages.sh
\ No newline at end of file
index 17454ef7065ac3a17af6e813308f73d14435e7b2..e019170d71d24a6323e3b4eaee856c30cebb5768 100644 (file)
@@ -122,14 +122,14 @@ setup_conffile() {
 
 prepare_database() {
   DB_MIGRATE_STATUS=`$COMMAND_PREFIX bundle exec rake db:migrate:status 2>&1 || true`
-  if echo $DB_MIGRATE_STATUS | grep -qF 'Schema migrations table does not exist yet.'; then
+  if echo "$DB_MIGRATE_STATUS" | grep -qF 'Schema migrations table does not exist yet.'; then
       # The database exists, but the migrations table doesn't.
       run_and_report "Setting up database" $COMMAND_PREFIX bundle exec \
                      rake "$RAILSPKG_DATABASE_LOAD_TASK" db:seed
-  elif echo $DB_MIGRATE_STATUS | grep -q '^database: '; then
+  elif echo "$DB_MIGRATE_STATUS" | grep -q '^database: '; then
       run_and_report "Running db:migrate" \
                      $COMMAND_PREFIX bundle exec rake db:migrate
-  elif echo $DB_MIGRATE_STATUS | grep -q 'database .* does not exist'; then
+  elif echo "$DB_MIGRATE_STATUS" | grep -q 'database .* does not exist'; then
       if ! run_and_report "Running db:setup" \
            $COMMAND_PREFIX bundle exec rake db:setup 2>/dev/null; then
           echo "Warning: unable to set up database." >&2
index a7dc30cfaedce168fab2ac3ec2d70d3e70bf297b..73f1e2dc46c12db55efc3e2aa81a7eec8ed63d8f 100755 (executable)
@@ -148,7 +148,7 @@ title "uploading images"
 
 timer_reset
 
-if [[ "$ECODE" != "0" ]]; then
+if [[ "$EXITCODE" != "0" ]]; then
     title "upload arvados images SKIPPED because build failed"
 else
     if [[ $upload == true ]]; then
index f1a1e1c4b3fa76eb5173109f071ee5b19f39b0b7..a4dd9a6ab8642e1e12b4a24e10ce3dd0d8c9b993 100755 (executable)
@@ -83,7 +83,7 @@ cd $(dirname $0)
 
 FINAL_EXITCODE=0
 
-for dockerfile_path in $(find -name Dockerfile); do
+for dockerfile_path in $(find -name Dockerfile | grep package-build-dockerfiles); do
     if ./run-build-packages-one-target.sh --target "$(basename $(dirname "$dockerfile_path"))" --command "$COMMAND" $DEBUG $TEST_PACKAGES $ONLY_TEST ; then
         true
     else
index adcb87f34d79b6e344317e75fa8fca3897d166cc..348dd7b982631623241b125f43fe01aab788d1ba 100755 (executable)
@@ -7,14 +7,16 @@ Syntax:
         WORKSPACE=/path/to/arvados $(basename $0) [options]
 
 --target <target>
-    Distribution to build packages for (default: debian7)
+    Distribution to build packages for (default: debian8)
 --command
     Build command to execute (default: use built-in Docker image command)
 --test-packages
     Run package install test script "test-packages-$target.sh"
 --debug
     Output debug information (default: false)
---only-test
+--only-build <package>
+    Build only a specific package
+--only-test <package>
     Test only a specific package
 
 WORKSPACE=path         Path to the Arvados source tree to build packages from
@@ -40,13 +42,13 @@ if ! [[ -d "$WORKSPACE" ]]; then
 fi
 
 PARSEDOPTS=$(getopt --name "$0" --longoptions \
-    help,debug,test-packages,target:,command:,only-test: \
+    help,debug,test-packages,target:,command:,only-test:,only-build: \
     -- "" "$@")
 if [ $? -ne 0 ]; then
     exit 1
 fi
 
-TARGET=debian7
+TARGET=debian8
 COMMAND=
 DEBUG=
 
@@ -62,8 +64,12 @@ while [ $# -gt 0 ]; do
             TARGET="$2"; shift
             ;;
         --only-test)
+            test_packages=1
             packages="$2"; shift
             ;;
+        --only-build)
+            ONLY_BUILD="$2"; shift
+            ;;
         --debug)
             DEBUG=" --debug"
             ;;
@@ -121,7 +127,6 @@ popd
 
 if test -z "$packages" ; then
     packages="arvados-api-server
-        arvados-data-manager
         arvados-docker-cleaner
         arvados-git-httpd
         arvados-node-manager
@@ -140,10 +145,6 @@ if test -z "$packages" ; then
         libarvados-perl"
 
     case "$TARGET" in
-        centos6)
-            packages="$packages python27-python-arvados-fuse
-                  python27-python-arvados-python-client python27-python-arvados-cwl-runner"
-            ;;
         *)
             packages="$packages python-arvados-fuse
                   python-arvados-python-client python-arvados-cwl-runner"
@@ -169,6 +170,9 @@ docker_volume_args=(
 
 if [[ -n "$test_packages" ]]; then
     for p in $packages ; do
+        if [[ -n "$ONLY_BUILD" ]] && [[ "$p" != "$ONLY_BUILD" ]]; then
+            continue
+        fi
         echo
         echo "START: $p test on $IMAGE" >&2
         if docker run --rm \
@@ -191,6 +195,7 @@ else
     if docker run --rm \
         "${docker_volume_args[@]}" \
         --env ARVADOS_DEBUG=1 \
+        --env "ONLY_BUILD=$ONLY_BUILD" \
         "$IMAGE" $COMMAND
     then
         echo
index b5dcdfce53b9e41518fe18d981af80815c47cde5..264f27d12b0202a9267a548a73e684460f8f5aa3 100755 (executable)
@@ -14,7 +14,7 @@ Options:
 --debug
     Output debug information (default: false)
 --target
-    Distribution to build packages for (default: debian7)
+    Distribution to build packages for (default: debian8)
 
 WORKSPACE=path         Path to the Arvados SSO source tree to build packages from
 
@@ -22,7 +22,7 @@ EOF
 
 EXITCODE=0
 DEBUG=${ARVADOS_DEBUG:-0}
-TARGET=debian7
+TARGET=debian8
 
 PARSEDOPTS=$(getopt --name "$0" --longoptions \
     help,build-bundle-packages,debug,target: \
@@ -68,9 +68,6 @@ if [[ "$DEBUG" != 0 ]]; then
 fi
 
 case "$TARGET" in
-    debian7)
-        FORMAT=deb
-        ;;
     debian8)
         FORMAT=deb
         ;;
@@ -80,7 +77,7 @@ case "$TARGET" in
     ubuntu1404)
         FORMAT=deb
         ;;
-    centos6|centos7)
+    centos7)
         FORMAT=rpm
         ;;
     *)
index 320f9d445c3a052a62bf5b8560b2080c98b06904..79614246e2f2c3341d18d177dd835f319fde904f 100755 (executable)
@@ -15,8 +15,10 @@ Options:
     Build api server and workbench packages with vendor/bundle included
 --debug
     Output debug information (default: false)
---target
-    Distribution to build packages for (default: debian7)
+--target <target>
+    Distribution to build packages for (default: debian8)
+--only-build <package>
+    Build only a specific package (or $ONLY_BUILD from environment)
 --command
     Build command to execute (defaults to the run command defined in the
     Docker image)
@@ -27,11 +29,11 @@ EOF
 
 EXITCODE=0
 DEBUG=${ARVADOS_DEBUG:-0}
-TARGET=debian7
+TARGET=debian8
 COMMAND=
 
 PARSEDOPTS=$(getopt --name "$0" --longoptions \
-    help,build-bundle-packages,debug,target: \
+    help,build-bundle-packages,debug,target:,only-build: \
     -- "" "$@")
 if [ $? -ne 0 ]; then
     exit 1
@@ -48,6 +50,9 @@ while [ $# -gt 0 ]; do
         --target)
             TARGET="$2"; shift
             ;;
+        --only-build)
+            ONLY_BUILD="$2"; shift
+            ;;
         --debug)
             DEBUG=1
             ;;
@@ -96,38 +101,27 @@ PYTHON3_INSTALL_LIB=lib/python$PYTHON3_VERSION/dist-packages
 ## End Debian Python defaults.
 
 case "$TARGET" in
-    debian7)
-        FORMAT=deb
-        PYTHON_BACKPORTS=(python-gflags==2.0 google-api-python-client==1.4.2 \
-            oauth2client==1.5.2 pyasn1==0.1.7 pyasn1-modules==0.0.5 \
-            rsa uritemplate httplib2 ws4py pykka six  \
-            ciso8601 pycrypto backports.ssl_match_hostname llfuse==0.41.1 \
-            'pycurl<7.21.5' contextlib2 pyyaml 'rdflib>=4.2.0' \
-            shellescape mistune typing avro ruamel.ordereddict
-            cachecontrol requests)
-        PYTHON3_BACKPORTS=(docker-py==1.7.2 six requests websocket-client)
-        ;;
     debian8)
         FORMAT=deb
         PYTHON_BACKPORTS=(python-gflags==2.0 google-api-python-client==1.4.2 \
             oauth2client==1.5.2 pyasn1==0.1.7 pyasn1-modules==0.0.5 \
-            rsa uritemplate httplib2 ws4py pykka six  \
+            rsa uritemplate httplib2 ws4py pykka six \
             ciso8601 pycrypto backports.ssl_match_hostname llfuse==0.41.1 \
             'pycurl<7.21.5' pyyaml 'rdflib>=4.2.0' \
             shellescape mistune typing avro ruamel.ordereddict
-            cachecontrol)
-        PYTHON3_BACKPORTS=(docker-py==1.7.2 six requests websocket-client)
+            cachecontrol junit-xml==1.7 futures==3.0.5)
+        PYTHON3_BACKPORTS=(docker-py==1.7.2 six requests websocket-client==0.37.0)
         ;;
     ubuntu1204)
         FORMAT=deb
         PYTHON_BACKPORTS=(python-gflags==2.0 google-api-python-client==1.4.2 \
             oauth2client==1.5.2 pyasn1==0.1.7 pyasn1-modules==0.0.5 \
-            rsa uritemplate httplib2 ws4py pykka six  \
+            rsa uritemplate httplib2 ws4py pykka six \
             ciso8601 pycrypto backports.ssl_match_hostname llfuse==0.41.1 \
             contextlib2 'pycurl<7.21.5' pyyaml 'rdflib>=4.2.0' \
             shellescape mistune typing avro isodate ruamel.ordereddict
-            cachecontrol requests)
-        PYTHON3_BACKPORTS=(docker-py==1.7.2 six requests websocket-client)
+            cachecontrol requests junit-xml==1.7 futures==3.0.5)
+        PYTHON3_BACKPORTS=(docker-py==1.7.2 six requests websocket-client==0.37.0)
         ;;
     ubuntu1404)
         FORMAT=deb
@@ -135,29 +129,8 @@ case "$TARGET" in
             google-api-python-client==1.4.2 six uritemplate oauth2client==1.5.2 httplib2 \
             rsa 'pycurl<7.21.5' backports.ssl_match_hostname pyyaml 'rdflib>=4.2.0' \
             shellescape mistune typing avro ruamel.ordereddict
-            cachecontrol)
-        PYTHON3_BACKPORTS=(docker-py==1.7.2 requests websocket-client)
-        ;;
-    centos6)
-        FORMAT=rpm
-        PYTHON2_PACKAGE=$(rpm -qf "$(which python$PYTHON2_VERSION)" --queryformat '%{NAME}\n')
-        PYTHON2_PKG_PREFIX=$PYTHON2_PACKAGE
-        PYTHON2_PREFIX=/opt/rh/python27/root/usr
-        PYTHON2_INSTALL_LIB=lib/python$PYTHON2_VERSION/site-packages
-        PYTHON3_PACKAGE=$(rpm -qf "$(which python$PYTHON3_VERSION)" --queryformat '%{NAME}\n')
-        PYTHON3_PKG_PREFIX=$PYTHON3_PACKAGE
-        PYTHON3_PREFIX=/opt/rh/python33/root/usr
-        PYTHON3_INSTALL_LIB=lib/python$PYTHON3_VERSION/site-packages
-        PYTHON_BACKPORTS=(python-gflags==2.0 google-api-python-client==1.4.2 \
-            oauth2client==1.5.2 pyasn1==0.1.7 pyasn1-modules==0.0.5 \
-            rsa uritemplate httplib2 ws4py pykka six  \
-            ciso8601 pycrypto backports.ssl_match_hostname 'pycurl<7.21.5' \
-            python-daemon llfuse==0.41.1 'pbr<1.0' pyyaml \
-            'rdflib>=4.2.0' shellescape mistune typing avro requests \
-            isodate pyparsing sparqlwrapper html5lib==0.9999999 keepalive \
-            ruamel.ordereddict cachecontrol)
-        PYTHON3_BACKPORTS=(docker-py==1.7.2 six requests websocket-client)
-        export PYCURL_SSL_LIBRARY=nss
+            cachecontrol junit-xml==1.7 futures==3.0.5)
+        PYTHON3_BACKPORTS=(docker-py==1.7.2 requests websocket-client==0.37.0)
         ;;
     centos7)
         FORMAT=rpm
@@ -172,11 +145,11 @@ case "$TARGET" in
             oauth2client==1.5.2 pyasn1==0.1.7 pyasn1-modules==0.0.5 \
             rsa uritemplate httplib2 ws4py pykka  \
             ciso8601 pycrypto 'pycurl<7.21.5' \
-            python-daemon==2.1.1 llfuse==0.41.1 'pbr<1.0' pyyaml \
+            python-daemon==2.1.1 llfuse==0.41.1 'pbr<1.0' pyyaml contextlib2 \
             'rdflib>=4.2.0' shellescape mistune typing avro \
             isodate pyparsing sparqlwrapper html5lib==0.9999999 keepalive \
-            ruamel.ordereddict cachecontrol)
-        PYTHON3_BACKPORTS=(docker-py==1.7.2 six requests websocket-client)
+            ruamel.ordereddict cachecontrol junit-xml==1.7 futures==3.0.5)
+        PYTHON3_BACKPORTS=(docker-py==1.7.2 six requests websocket-client==0.37.0)
         export PYCURL_SSL_LIBRARY=nss
         ;;
     *)
@@ -248,6 +221,7 @@ fi
 # Perl packages
 debug_echo -e "\nPerl packages\n"
 
+if [[ -z "$ONLY_BUILD" ]] || [[ "libarvados-perl" = "$ONLY_BUILD" ]] ; then
 cd "$WORKSPACE/sdk/perl"
 
 if [[ -e Makefile ]]; then
@@ -263,6 +237,7 @@ perl Makefile.PL INSTALL_BASE=install >"$STDOUT_IF_DEBUG" && \
     "Curoverse, Inc." dir "$(version_from_git)" install/man/=/usr/share/man \
     "$WORKSPACE/LICENSE-2.0.txt=/usr/share/doc/libarvados-perl/LICENSE-2.0.txt" && \
     mv --no-clobber libarvados-perl*.$FORMAT "$WORKSPACE/packages/$TARGET/"
+fi
 
 # Ruby gems
 debug_echo -e "\nRuby gems\n"
@@ -368,38 +343,6 @@ if [[ $TARGET =~ ubuntu1204 ]]; then
         "$WORKSPACE/packages/$TARGET/libfuse-dev_2.9.2-5_amd64.deb"
     apt-get -y --no-install-recommends -f install
     rm -rf $LIBFUSE_DIR
-elif [[ $TARGET =~ centos6 ]]; then
-    # port fuse 2.9.2 to centos 6
-    # install tools to build rpm from source
-    yum install -y rpm-build redhat-rpm-config
-    LIBFUSE_DIR=$(mktemp -d)
-    (
-        cd "$LIBFUSE_DIR"
-        # download fuse 2.9.2 centos 7 source rpm
-        file="fuse-2.9.2-6.el7.src.rpm" && curl -L -o "${file}" "http://vault.centos.org/7.2.1511/os/Source/SPackages/${file}"
-        (
-            # modify source rpm spec to remove conflict on filesystem version
-            mkdir -p /root/rpmbuild/SOURCES
-            cd /root/rpmbuild/SOURCES
-            rpm2cpio ${LIBFUSE_DIR}/fuse-2.9.2-6.el7.src.rpm | cpio -i
-            perl -pi -e 's/Conflicts:\s*filesystem.*//g' fuse.spec
-        )
-        # build rpms from source
-        rpmbuild -bb /root/rpmbuild/SOURCES/fuse.spec
-        rm -f fuse-2.9.2-6.el7.src.rpm
-        # move built RPMs to LIBFUSE_DIR
-        mv "/root/rpmbuild/RPMS/x86_64/fuse-2.9.2-6.el6.x86_64.rpm" ${LIBFUSE_DIR}/
-        mv "/root/rpmbuild/RPMS/x86_64/fuse-libs-2.9.2-6.el6.x86_64.rpm" ${LIBFUSE_DIR}/
-        mv "/root/rpmbuild/RPMS/x86_64/fuse-devel-2.9.2-6.el6.x86_64.rpm" ${LIBFUSE_DIR}/
-        rm -rf /root/rpmbuild
-    )
-    fpm_build "$LIBFUSE_DIR/fuse-libs-2.9.2-6.el6.x86_64.rpm" fuse-libs "Centos Developers" rpm "2.9.2" --iteration 5
-    fpm_build "$LIBFUSE_DIR/fuse-2.9.2-6.el6.x86_64.rpm" fuse "Centos Developers" rpm "2.9.2" --iteration 5 --no-auto-depends
-    fpm_build "$LIBFUSE_DIR/fuse-devel-2.9.2-6.el6.x86_64.rpm" fuse-devel "Centos Developers" rpm "2.9.2" --iteration 5 --no-auto-depends
-    yum install -y \
-        "$WORKSPACE/packages/$TARGET/fuse-libs-2.9.2-5.x86_64.rpm" \
-        "$WORKSPACE/packages/$TARGET/fuse-2.9.2-5.x86_64.rpm" \
-        "$WORKSPACE/packages/$TARGET/fuse-devel-2.9.2-5.x86_64.rpm"
 fi
 
 # Go binaries
@@ -417,8 +360,6 @@ package_go_binary services/crunch-run crunch-run \
     "Supervise a single Crunch container"
 package_go_binary services/crunchstat crunchstat \
     "Gather cpu/memory/network statistics of running Crunch jobs"
-package_go_binary services/datamanager arvados-data-manager \
-    "Ensure block replication levels, report disk usage, and determine which blocks should be deleted when space is needed"
 package_go_binary services/keep-balance keep-balance \
     "Rebalance and garbage-collect data blocks stored in Arvados Keep"
 package_go_binary services/keepproxy keepproxy \
@@ -427,6 +368,8 @@ package_go_binary services/keepstore keepstore \
     "Keep storage daemon, accessible to clients on the LAN"
 package_go_binary services/keep-web keep-web \
     "Static web hosting service for user data stored in Arvados Keep"
+package_go_binary services/ws arvados-ws \
+    "Arvados Websocket server"
 package_go_binary tools/keep-block-check keep-block-check \
     "Verify that all data from one set of Keep servers to another was copied"
 package_go_binary tools/keep-rsync keep-rsync \
@@ -443,7 +386,7 @@ package_go_binary tools/keep-exercise keep-exercise \
 # 2014-05-15
 cd $WORKSPACE/packages/$TARGET
 rm -rf "$WORKSPACE/sdk/python/build"
-fpm_build $WORKSPACE/sdk/python "${PYTHON2_PKG_PREFIX}-arvados-python-client" 'Curoverse, Inc.' 'python' "$(awk '($1 == "Version:"){print $2}' $WORKSPACE/sdk/python/arvados_python_client.egg-info/PKG-INFO)" "--url=https://arvados.org" "--description=The Arvados Python SDK" --deb-recommends=git
+fpm_build $WORKSPACE/sdk/python "${PYTHON2_PKG_PREFIX}-arvados-python-client" 'Curoverse, Inc.' 'python' "$(awk '($1 == "Version:"){print $2}' $WORKSPACE/sdk/python/arvados_python_client.egg-info/PKG-INFO)" "--url=https://arvados.org" "--description=The Arvados Python SDK" --depends "${PYTHON2_PKG_PREFIX}-setuptools" --deb-recommends=git
 
 # cwl-runner
 cd $WORKSPACE/packages/$TARGET
@@ -467,21 +410,23 @@ fpm_build lockfile "" "" python 0.12.2 --epoch 1
 # So we build this thing separately.
 #
 # Ward, 2016-03-17
-fpm_build schema_salad "" "" python 1.18.20161005190847 --depends "${PYTHON2_PKG_PREFIX}-lockfile >= 1:0.12.2-2"
+saladversion=$(cat "$WORKSPACE/sdk/cwl/setup.py" | grep schema-salad== | sed "s/.*==\(.*\)'.*/\1/")
+fpm_build schema_salad "" "" python $saladversion --depends "${PYTHON2_PKG_PREFIX}-lockfile >= 1:0.12.2-2"
 
 # And schema_salad now depends on ruamel-yaml, which apparently has a braindead setup.py that requires special arguments to build (otherwise, it aborts with 'error: you have to install with "pip install ."'). Sigh.
 # Ward, 2016-05-26
-fpm_build ruamel.yaml "" "" python 0.12.4 --python-setup-py-arguments "--single-version-externally-managed"
+fpm_build ruamel.yaml "" "" python 0.13.7 --python-setup-py-arguments "--single-version-externally-managed"
 
 # Dependency of cwltool.  Fpm doesn't produce a package with the correct version
 # number unless we build it explicitly
-fpm_build cwltest "" "" python 1.0.20160907111242
+fpm_build cwltest "" "" python 1.0.20161227194859 --depends "${PYTHON2_PKG_PREFIX}-futures >= 3.0.5" --iteration 3
 
 # And for cwltool we have the same problem as for schema_salad. Ward, 2016-03-17
-fpm_build cwltool "" "" python 1.0.20161107145355
+cwltoolversion=$(cat "$WORKSPACE/sdk/cwl/setup.py" | grep cwltool== | sed "s/.*==\(.*\)'.*/\1/")
+fpm_build cwltool "" "" python $cwltoolversion
 
 # FPM eats the trailing .0 in the python-rdflib-jsonld package when built with 'rdflib-jsonld>=0.3.0'. Force the version. Ward, 2016-03-25
-fpm_build rdflib-jsonld "" "" python 0.3.0
+fpm_build rdflib-jsonld "" "" python 0.4.0
 
 # The PAM module
 if [[ $TARGET =~ debian|ubuntu ]]; then
@@ -495,17 +440,17 @@ fi
 # not omit the python- prefix first.
 cd $WORKSPACE/packages/$TARGET
 rm -rf "$WORKSPACE/services/fuse/build"
-fpm_build $WORKSPACE/services/fuse "${PYTHON2_PKG_PREFIX}-arvados-fuse" 'Curoverse, Inc.' 'python' "$(awk '($1 == "Version:"){print $2}' $WORKSPACE/services/fuse/arvados_fuse.egg-info/PKG-INFO)" "--url=https://arvados.org" "--description=The Keep FUSE driver"
+fpm_build $WORKSPACE/services/fuse "${PYTHON2_PKG_PREFIX}-arvados-fuse" 'Curoverse, Inc.' 'python' "$(awk '($1 == "Version:"){print $2}' $WORKSPACE/services/fuse/arvados_fuse.egg-info/PKG-INFO)" "--url=https://arvados.org" "--description=The Keep FUSE driver" --depends "${PYTHON2_PKG_PREFIX}-setuptools"
 
 # The node manager
 cd $WORKSPACE/packages/$TARGET
 rm -rf "$WORKSPACE/services/nodemanager/build"
-fpm_build $WORKSPACE/services/nodemanager arvados-node-manager 'Curoverse, Inc.' 'python' "$(awk '($1 == "Version:"){print $2}' $WORKSPACE/services/nodemanager/arvados_node_manager.egg-info/PKG-INFO)" "--url=https://arvados.org" "--description=The Arvados node manager"
+fpm_build $WORKSPACE/services/nodemanager arvados-node-manager 'Curoverse, Inc.' 'python' "$(awk '($1 == "Version:"){print $2}' $WORKSPACE/services/nodemanager/arvados_node_manager.egg-info/PKG-INFO)" "--url=https://arvados.org" "--description=The Arvados node manager" --depends "${PYTHON2_PKG_PREFIX}-setuptools"
 
 # The Docker image cleaner
 cd $WORKSPACE/packages/$TARGET
 rm -rf "$WORKSPACE/services/dockercleaner/build"
-fpm_build $WORKSPACE/services/dockercleaner arvados-docker-cleaner 'Curoverse, Inc.' 'python3' "$(awk '($1 == "Version:"){print $2}' $WORKSPACE/services/dockercleaner/arvados_docker_cleaner.egg-info/PKG-INFO)" "--url=https://arvados.org" "--description=The Arvados Docker image cleaner"
+fpm_build $WORKSPACE/services/dockercleaner arvados-docker-cleaner 'Curoverse, Inc.' 'python3' "$(awk '($1 == "Version:"){print $2}' $WORKSPACE/services/dockercleaner/arvados_docker_cleaner.egg-info/PKG-INFO)" "--url=https://arvados.org" "--description=The Arvados Docker image cleaner" --depends "${PYTHON3_PKG_PREFIX}-websocket-client = 0.37.0" --iteration 3
 
 # The Arvados crunchstat-summary tool
 cd $WORKSPACE/packages/$TARGET
@@ -539,6 +484,11 @@ esac
 
 for deppkg in "${PYTHON_BACKPORTS[@]}"; do
     outname=$(echo "$deppkg" | sed -e 's/^python-//' -e 's/[<=>].*//' -e 's/_/-/g' -e "s/^/${PYTHON2_PKG_PREFIX}-/")
+
+    if [[ -n "$ONLY_BUILD" ]] && [[ "$outname" != "$ONLY_BUILD" ]] ; then
+        continue
+    fi
+
     case "$deppkg" in
         httplib2|google-api-python-client)
             # Work around 0640 permissions on some package files.
@@ -588,6 +538,7 @@ handle_rails_package arvados-api-server "$WORKSPACE/services/api" \
     --license="GNU Affero General Public License, version 3.0"
 
 # Build the workbench server package
+if [[ -z "$ONLY_BUILD" ]] || [[ "arvados-workbench" = "$ONLY_BUILD" ]] ; then
 (
     set -e
     cd "$WORKSPACE/apps/workbench"
@@ -612,6 +563,7 @@ handle_rails_package arvados-api-server "$WORKSPACE/services/api" \
     # Remove generated configuration files so they don't go in the package.
     rm config/application.yml config/environments/production.rb
 )
+fi
 
 if [[ "$?" != "0" ]]; then
   echo "ERROR: Asset precompilation failed"
index ff6bad4a71fed426b9f7087e317d98d3ef93ee43..46b8133466509b799724361f410872ff315a2228 100755 (executable)
@@ -7,7 +7,7 @@ Syntax:
         WORKSPACE=/path/to/arvados $(basename $0) [options]
 
 --target <target>
-    Distribution to build packages for (default: debian7)
+    Distribution to build packages for (default: debian8)
 --upload
     If the build and test steps are successful, upload the packages
     to a remote apt repository (default: false)
@@ -39,7 +39,7 @@ if [ $? -ne 0 ]; then
     exit 1
 fi
 
-TARGET=debian7
+TARGET=debian8
 UPLOAD=0
 
 eval set -- "$PARSEDOPTS"
index f0b120f6bf1e4e011a69f9f811ee67ad55624938..23e09d43d519e85957cb7eb9a230b7dc6ea864cf 100755 (executable)
@@ -69,6 +69,10 @@ handle_ruby_gem() {
     local gem_version="$(nohash_version_from_git)"
     local gem_src_dir="$(pwd)"
 
+    if [[ -n "$ONLY_BUILD" ]] && [[ "$gem_name" != "$ONLY_BUILD" ]] ; then
+        return 0
+    fi
+
     if ! [[ -e "${gem_name}-${gem_version}.gem" ]]; then
         find -maxdepth 1 -name "${gem_name}-*.gem" -delete
 
@@ -84,6 +88,10 @@ package_go_binary() {
     local description="$1"; shift
     local license_file="${1:-agpl-3.0.txt}"; shift
 
+    if [[ -n "$ONLY_BUILD" ]] && [[ "$prog" != "$ONLY_BUILD" ]] ; then
+        return 0
+    fi
+
     debug_echo "package_go_binary $src_path as $prog"
 
     local basename="${src_path##*/}"
@@ -143,6 +151,11 @@ _build_rails_package_scripts() {
 
 handle_rails_package() {
     local pkgname="$1"; shift
+
+    if [[ -n "$ONLY_BUILD" ]] && [[ "$pkgname" != "$ONLY_BUILD" ]] ; then
+        return 0
+    fi
+
     local srcdir="$1"; shift
     local license_path="$1"; shift
     local scripts_dir="$(mktemp --tmpdir -d "$pkgname-XXXXXXXX.scripts")" && \
@@ -165,7 +178,7 @@ handle_rails_package() {
     local -a pos_args=("$srcdir/=$railsdir" "$pkgname" "Curoverse, Inc." dir
                        "$(cat "$version_file")")
     local license_arg="$license_path=$railsdir/$(basename "$license_path")"
-    local -a switches=(--iteration=6
+    local -a switches=(--iteration=7
                        --after-install "$scripts_dir/postinst"
                        --before-remove "$scripts_dir/prerm"
                        --after-remove "$scripts_dir/postrm")
@@ -208,6 +221,10 @@ fpm_build () {
   VERSION=$1
   shift
 
+  if [[ -n "$ONLY_BUILD" ]] && [[ "$PACKAGE_NAME" != "$ONLY_BUILD" ]] && [[ "$PACKAGE" != "$ONLY_BUILD" ]] ; then
+      return 0
+  fi
+
   local default_iteration_value="$(default_iteration "$PACKAGE" "$VERSION")"
 
   case "$PACKAGE_TYPE" in
@@ -289,7 +306,7 @@ fpm_build () {
   declare -a fpm_dirs=(
       # source dir part of 'dir' package ("/source=/dest" => "/source"):
       "${PACKAGE%%=/*}"
-      # backports ("llfuse==0.41.1" => "backports/python-llfuse")
+      # backports ("llfuse>=1.0" => "backports/python-llfuse")
       "${WORKSPACE}/backports/${PACKAGE_TYPE}-${PACKAGE%%[<=>]*}")
   if [[ -n "$PACKAGE_NAME" ]]; then
       fpm_dirs+=("${WORKSPACE}/backports/${PACKAGE_NAME}")
index 8959cfbe09c3ea7ac6ded2142b626259787d2121..4b6c81395f0925ca9a27c226acfaf3d30774d524 100755 (executable)
@@ -22,6 +22,8 @@ Options:
 --leave-temp   Do not remove GOPATH, virtualenv, and other temp dirs at exit.
                Instead, show the path to give as --temp to reuse them in
                subsequent invocations.
+--repeat N     Repeat each install/test step until it succeeds N times.
+--retry        Prompt to retry if an install or test suite fails.
 --skip-install Do not run any install steps. Just run tests.
                You should provide GOPATH, GEMHOME, and VENVDIR options
                from a previous invocation if you use this option.
@@ -79,6 +81,7 @@ services/nodemanager
 services/crunch-run
 services/crunch-dispatch-local
 services/crunch-dispatch-slurm
+services/ws
 sdk/cli
 sdk/pam
 sdk/python
@@ -90,6 +93,7 @@ sdk/go/httpserver
 sdk/go/manifest
 sdk/go/blockdigest
 sdk/go/streamer
+sdk/go/stats
 sdk/go/crunchrunner
 sdk/cwl
 tools/crunchstat-summary
@@ -117,7 +121,7 @@ GEMHOME=
 PERLINSTALLBASE=
 
 short=
-skip_install=
+only_install=
 temp=
 temp_preserve=
 
@@ -156,6 +160,12 @@ sanity_checks() {
     echo -n 'virtualenv: '
     virtualenv --version \
         || fatal "No virtualenv. Try: apt-get install virtualenv (on ubuntu: python-virtualenv)"
+    echo -n 'ruby: '
+    ruby -v \
+        || fatal "No ruby. Install >=2.1.9 (using rbenv, rvm, or source)"
+    echo -n 'bundler: '
+    bundle version \
+        || fatal "No bundler. Try: gem install bundler"
     echo -n 'go: '
     go version \
         || fatal "No go binary. See http://golang.org/doc/install"
@@ -211,14 +221,7 @@ do
             exit 1
             ;;
         --skip)
-            skipwhat="$1"; shift
-            if [[ "$skipwhat" == "apps/workbench" ]]; then
-              skip["apps/workbench_units"]=1
-              skip["apps/workbench_functionals"]=1
-              skip["apps/workbench_integration"]=1
-            else
-              skip[$skipwhat]=1
-            fi
+            skip[$1]=1; shift
             ;;
         --only)
             only="$1"; skip[$1]=""; shift
@@ -227,10 +230,9 @@ do
             short=1
             ;;
         --skip-install)
-            skip_install=1
+            only_install=nothing
             ;;
         --only-install)
-            skip_install=1
             only_install="$1"; shift
             ;;
         --temp)
@@ -240,6 +242,9 @@ do
         --leave-temp)
             temp_preserve=1
             ;;
+        --repeat)
+            repeat=$((${1}+0)); shift
+            ;;
         --retry)
             retry=1
             ;;
@@ -264,15 +269,18 @@ start_api() {
         && eval $(python sdk/python/tests/run_test_server.py start --auth admin) \
         && export ARVADOS_TEST_API_HOST="$ARVADOS_API_HOST" \
         && export ARVADOS_TEST_API_INSTALLED="$$" \
+        && python sdk/python/tests/run_test_server.py start_ws \
+        && python sdk/python/tests/run_test_server.py start_nginx \
         && (env | egrep ^ARVADOS)
 }
 
 start_nginx_proxy_services() {
-    echo 'Starting keepproxy, keep-web, arv-git-httpd, and nginx ssl proxy...'
+    echo 'Starting keepproxy, keep-web, ws, arv-git-httpd, and nginx ssl proxy...'
     cd "$WORKSPACE" \
         && python sdk/python/tests/run_test_server.py start_keep_proxy \
         && python sdk/python/tests/run_test_server.py start_keep-web \
         && python sdk/python/tests/run_test_server.py start_arv-git-httpd \
+        && python sdk/python/tests/run_test_server.py start_ws \
         && python sdk/python/tests/run_test_server.py start_nginx \
         && export ARVADOS_TEST_PROXY_SERVICES=1
 }
@@ -283,12 +291,15 @@ stop_services() {
         cd "$WORKSPACE" \
             && python sdk/python/tests/run_test_server.py stop_nginx \
             && python sdk/python/tests/run_test_server.py stop_arv-git-httpd \
+            && python sdk/python/tests/run_test_server.py stop_ws \
             && python sdk/python/tests/run_test_server.py stop_keep-web \
             && python sdk/python/tests/run_test_server.py stop_keep_proxy
     fi
     if [[ -n "$ARVADOS_TEST_API_HOST" ]]; then
         unset ARVADOS_TEST_API_HOST
         cd "$WORKSPACE" \
+            && python sdk/python/tests/run_test_server.py stop_nginx \
+            && python sdk/python/tests/run_test_server.py stop_ws \
             && python sdk/python/tests/run_test_server.py stop
     fi
 }
@@ -443,13 +454,10 @@ pip freeze 2>/dev/null | egrep ^apache-libcloud==$LIBCLOUD_PIN \
     || pip install --pre --ignore-installed https://github.com/curoverse/libcloud/archive/apache-libcloud-$LIBCLOUD_PIN.zip >/dev/null \
     || fatal "pip install apache-libcloud failed"
 
-# This will help people who reuse --temp dirs when we upgrade to llfuse 0.42
-if egrep -q 'llfuse.*>= *0\.42' "$WORKSPACE/services/fuse/setup.py"; then
-    # Uninstall old llfuse, because services/fuse "pip install" won't
-    # upgrade it by default.
-    if pip freeze | egrep '^llfuse==0\.41\.'; then
-        yes | pip uninstall 'llfuse<0.42'
-    fi
+# Uninstall old llfuse (<1.0), because services/fuse "pip install"
+# won't upgrade it by default.
+if pip freeze | egrep '^llfuse==0'; then
+    yes | pip uninstall 'llfuse<1.0'
 fi
 
 # Deactivate Python 2 virtualenv
@@ -486,136 +494,151 @@ then
 fi
 
 retry() {
-    while ! ${@} && [[ "$retry" == 1 ]]
+    remain="${repeat}"
+    while :
     do
-        read -p 'Try again? [Y/n] ' x
-        if [[ "$x" != "y" ]] && [[ "$x" != "" ]]
-        then
+        if ${@}; then
+            if [[ "$remain" -gt 1 ]]; then
+                remain=$((${remain}-1))
+                title "Repeating ${remain} more times"
+            else
+                break
+            fi
+        elif [[ "$retry" == 1 ]]; then
+            read -p 'Try again? [Y/n] ' x
+            if [[ "$x" != "y" ]] && [[ "$x" != "" ]]
+            then
+                break
+            fi
+        else
             break
         fi
     done
 }
 
 do_test() {
-    retry do_test_once ${@}
+    case "${1}" in
+        apps/workbench_units | apps/workbench_functional | apps/workbench_integration)
+            suite=apps/workbench
+            ;;
+        *)
+            suite="${1}"
+            ;;
+    esac
+    if [[ -z "${skip[$suite]}" && -z "${skip[$1]}" && \
+                (-z "${only}" || "${only}" == "${suite}" || \
+                 "${only}" == "${1}") ]]; then
+        retry do_test_once ${@}
+    else
+        title "Skipping ${1} tests"
+    fi
 }
 
 do_test_once() {
     unset result
-    to_test=$1
-    if (( [[ "$only" == "apps/workbench" ]] ) &&
-       ( [[ "$to_test" == "apps/workbench_units" ]] || [[ "$to_test" == "apps/workbench_functionals" ]] ||
-         [[ "$to_test" == "apps/workbench_integration" ]])); then
-      to_test="apps/workbench"
-    fi
-    if [[ -z "${skip[$1]}" ]] && ( [[ -z "$only" ]] || [[ "$only" == "$to_test" ]] )
+
+    title "Running $1 tests"
+    timer_reset
+    if [[ "$2" == "go" ]]
     then
-        title "Running $1 tests"
-        timer_reset
-        if [[ "$2" == "go" ]]
-        then
-            covername="coverage-$(echo "$1" | sed -e 's/\//_/g')"
-            coverflags=("-covermode=count" "-coverprofile=$WORKSPACE/tmp/.$covername.tmp")
-            # We do "go get -t" here to catch compilation errors
-            # before trying "go test". Otherwise, coverage-reporting
-            # mode makes Go show the wrong line numbers when reporting
-            # compilation errors.
-            go get -t "git.curoverse.com/arvados.git/$1" || return 1
-            cd "$WORKSPACE/$1" || return 1
-            gofmt -e -d . | egrep . && result=1
-            if [[ -n "${testargs[$1]}" ]]
-            then
-                # "go test -check.vv giturl" doesn't work, but this
-                # does:
-                cd "$WORKSPACE/$1" && go test ${short:+-short} ${testargs[$1]}
-            else
-                # The above form gets verbose even when testargs is
-                # empty, so use this form in such cases:
-                go test ${short:+-short} ${coverflags[@]} "git.curoverse.com/arvados.git/$1"
-            fi
-            result=${result:-$?}
-            if [[ -f "$WORKSPACE/tmp/.$covername.tmp" ]]
-            then
-                go tool cover -html="$WORKSPACE/tmp/.$covername.tmp" -o "$WORKSPACE/tmp/$covername.html"
-                rm "$WORKSPACE/tmp/.$covername.tmp"
-            fi
-        elif [[ "$2" == "pip" ]]
+        covername="coverage-$(echo "$1" | sed -e 's/\//_/g')"
+        coverflags=("-covermode=count" "-coverprofile=$WORKSPACE/tmp/.$covername.tmp")
+        # We do "go get -t" here to catch compilation errors
+        # before trying "go test". Otherwise, coverage-reporting
+        # mode makes Go show the wrong line numbers when reporting
+        # compilation errors.
+        go get -t "git.curoverse.com/arvados.git/$1" || return 1
+        cd "$WORKSPACE/$1" || return 1
+        gofmt -e -d . | egrep . && result=1
+        if [[ -n "${testargs[$1]}" ]]
         then
-            tries=0
-            cd "$WORKSPACE/$1" && while :
-            do
-                tries=$((${tries}+1))
-                # $3 can name a path directory for us to use, including trailing
-                # slash; e.g., the bin/ subdirectory of a virtualenv.
-                "${3}python" setup.py ${short:+--short-tests-only} test ${testargs[$1]}
-                result=$?
-                if [[ ${tries} < 3 && ${result} == 137 ]]
-                then
-                    printf '\n*****\n%s tests killed -- retrying\n*****\n\n' "$1"
-                    continue
-                else
-                    break
-                fi
-            done
-        elif [[ "$2" != "" ]]
-        then
-            "test_$2"
+            # "go test -check.vv giturl" doesn't work, but this
+            # does:
+            cd "$WORKSPACE/$1" && go test ${short:+-short} ${testargs[$1]}
         else
-            "test_$1"
+            # The above form gets verbose even when testargs is
+            # empty, so use this form in such cases:
+            go test ${short:+-short} ${coverflags[@]} "git.curoverse.com/arvados.git/$1"
         fi
         result=${result:-$?}
-        checkexit $result "$1 tests"
-        title "End of $1 tests (`timer`)"
-        return $result
+        if [[ -f "$WORKSPACE/tmp/.$covername.tmp" ]]
+        then
+            go tool cover -html="$WORKSPACE/tmp/.$covername.tmp" -o "$WORKSPACE/tmp/$covername.html"
+            rm "$WORKSPACE/tmp/.$covername.tmp"
+        fi
+    elif [[ "$2" == "pip" ]]
+    then
+        tries=0
+        cd "$WORKSPACE/$1" && while :
+        do
+            tries=$((${tries}+1))
+            # $3 can name a path directory for us to use, including trailing
+            # slash; e.g., the bin/ subdirectory of a virtualenv.
+            "${3}python" setup.py ${short:+--short-tests-only} test ${testargs[$1]}
+            result=$?
+            if [[ ${tries} < 3 && ${result} == 137 ]]
+            then
+                printf '\n*****\n%s tests killed -- retrying\n*****\n\n' "$1"
+                continue
+            else
+                break
+            fi
+        done
+    elif [[ "$2" != "" ]]
+    then
+        "test_$2"
     else
-        title "Skipping $1 tests"
+        "test_$1"
     fi
+    result=${result:-$?}
+    checkexit $result "$1 tests"
+    title "End of $1 tests (`timer`)"
+    return $result
 }
 
 do_install() {
-    retry do_install_once ${@}
+    if [[ -z "${only_install}" || "${only_install}" == "${1}" ]]; then
+        retry do_install_once ${@}
+    else
+        title "Skipping $1 install"
+    fi
 }
 
 do_install_once() {
-    if [[ -z "$skip_install" || (-n "$only_install" && "$only_install" == "$1") ]]
+    title "Running $1 install"
+    timer_reset
+    if [[ "$2" == "go" ]]
     then
-        title "Running $1 install"
-        timer_reset
-        if [[ "$2" == "go" ]]
-        then
-            go get -t "git.curoverse.com/arvados.git/$1"
-        elif [[ "$2" == "pip" ]]
-        then
-            # $3 can name a path directory for us to use, including trailing
-            # slash; e.g., the bin/ subdirectory of a virtualenv.
-
-            # Need to change to a different directory after creating
-            # the source dist package to avoid a pip bug.
-            # see https://arvados.org/issues/5766 for details.
-
-            # Also need to install twice, because if it believes the package is
-            # already installed, pip it won't install it.  So the first "pip
-            # install" ensures that the dependencies are met, the second "pip
-            # install" ensures that we've actually installed the local package
-            # we just built.
-            cd "$WORKSPACE/$1" \
-                && "${3}python" setup.py sdist rotate --keep=1 --match .tar.gz \
-                && cd "$WORKSPACE" \
-                && "${3}pip" install --quiet "$WORKSPACE/$1/dist"/*.tar.gz \
-                && "${3}pip" install --quiet --no-deps --ignore-installed "$WORKSPACE/$1/dist"/*.tar.gz
-        elif [[ "$2" != "" ]]
-        then
-            "install_$2"
-        else
-            "install_$1"
-        fi
-        result=$?
-        checkexit $result "$1 install"
-        title "End of $1 install (`timer`)"
-        return $result
+        go get -t "git.curoverse.com/arvados.git/$1"
+    elif [[ "$2" == "pip" ]]
+    then
+        # $3 can name a path directory for us to use, including trailing
+        # slash; e.g., the bin/ subdirectory of a virtualenv.
+
+        # Need to change to a different directory after creating
+        # the source dist package to avoid a pip bug.
+        # see https://arvados.org/issues/5766 for details.
+
+        # Also need to install twice, because if it believes the package is
+        # already installed, pip it won't install it.  So the first "pip
+        # install" ensures that the dependencies are met, the second "pip
+        # install" ensures that we've actually installed the local package
+        # we just built.
+        cd "$WORKSPACE/$1" \
+            && "${3}python" setup.py sdist rotate --keep=1 --match .tar.gz \
+            && cd "$WORKSPACE" \
+            && "${3}pip" install --quiet "$WORKSPACE/$1/dist"/*.tar.gz \
+            && "${3}pip" install --quiet --no-deps --ignore-installed "$WORKSPACE/$1/dist"/*.tar.gz
+    elif [[ "$2" != "" ]]
+    then
+        "install_$2"
     else
-        title "Skipping $1 install"
+        "install_$1"
     fi
+    result=$?
+    checkexit $result "$1 install"
+    title "End of $1 install (`timer`)"
+    return $result
 }
 
 bundle_install_trylocal() {
@@ -750,6 +773,7 @@ gostuff=(
     sdk/go/manifest
     sdk/go/streamer
     sdk/go/crunchrunner
+    sdk/go/stats
     lib/crunchstat
     services/arv-git-httpd
     services/crunchstat
@@ -758,13 +782,10 @@ gostuff=(
     sdk/go/keepclient
     services/keep-balance
     services/keepproxy
-    services/datamanager/summary
-    services/datamanager/collection
-    services/datamanager/keep
-    services/datamanager
     services/crunch-dispatch-local
     services/crunch-dispatch-slurm
     services/crunch-run
+    services/ws
     tools/keep-block-check
     tools/keep-exercise
     tools/keep-rsync
index 5ed5af5cb58c3757fe36758b5d8bea8fa6d4413c..c2891d7cff1b4f20701839186e7a5deced1f3245 100644 (file)
@@ -28,9 +28,9 @@ navbar:
     - Welcome:
       - user/index.html.textile.liquid
       - user/getting_started/community.html.textile.liquid
-    - Run a pipeline using Workbench:
+    - Run a workflow using Workbench:
       - user/getting_started/workbench.html.textile.liquid
-      - user/tutorials/tutorial-pipeline-workbench.html.textile.liquid
+      - user/tutorials/tutorial-workflow-workbench.html.textile.liquid
     - Access an Arvados virtual machine:
       - user/getting_started/vm-login-with-webshell.html.textile.liquid
       - user/getting_started/ssh-access-unix.html.textile.liquid
@@ -43,23 +43,25 @@ navbar:
       - user/tutorials/tutorial-keep-mount.html.textile.liquid
       - user/topics/keep.html.textile.liquid
       - user/topics/arv-copy.html.textile.liquid
-    - Using Common Workflow Language:
+    - Running workflows at the command line:
       - user/cwl/cwl-runner.html.textile.liquid
-      - user/cwl/cwl-style.html.textile.liquid
-    - Working on the command line:
+      - user/cwl/cwl-run-options.html.textile.liquid
       - user/topics/running-pipeline-command-line.html.textile.liquid
       - user/topics/arv-run.html.textile.liquid
     - Working with git repositories:
       - user/tutorials/add-new-repository.html.textile.liquid
       - user/tutorials/git-arvados-guide.html.textile.liquid
-    - Develop an Arvados pipeline:
+    - Develop an Arvados workflow:
       - user/tutorials/intro-crunch.html.textile.liquid
+      - user/tutorials/writing-cwl-workflow.html.textile.liquid
+      - user/cwl/cwl-style.html.textile.liquid
+      - user/cwl/cwl-extensions.html.textile.liquid
+      - user/topics/arv-docker.html.textile.liquid
       - user/tutorials/running-external-program.html.textile.liquid
       - user/topics/crunch-tools-overview.html.textile.liquid
       - user/tutorials/tutorial-firstscript.html.textile.liquid
       - user/tutorials/tutorial-submit-job.html.textile.liquid
       - user/topics/tutorial-parallel.html.textile.liquid
-      - user/topics/arv-docker.html.textile.liquid
     - Develop a web service:
       - user/topics/arv-web.html.textile.liquid
     - Reference:
@@ -78,78 +80,67 @@ navbar:
       - sdk/index.html.textile.liquid
     - Python:
       - sdk/python/sdk-python.html.textile.liquid
+      - sdk/python/example.html.textile.liquid
       - sdk/python/python.html.textile.liquid
       - sdk/python/crunch-utility-libraries.html.textile.liquid
       - sdk/python/events.html.textile.liquid
+    - CLI:
+      - sdk/cli/install.html.textile.liquid
+      - sdk/cli/index.html.textile.liquid
+      - sdk/cli/reference.html.textile.liquid
+      - sdk/cli/subcommands.html.textile.liquid
+    - Go:
+      - sdk/go/index.html.textile.liquid
+      - sdk/go/example.html.textile.liquid
     - Perl:
       - sdk/perl/index.html.textile.liquid
+      - sdk/perl/example.html.textile.liquid
     - Ruby:
       - sdk/ruby/index.html.textile.liquid
+      - sdk/ruby/example.html.textile.liquid
     - Java:
       - sdk/java/index.html.textile.liquid
-    - Go:
-      - sdk/go/index.html.textile.liquid
-    - CLI:
-      - sdk/cli/index.html.textile.liquid
-      - sdk/cli/install.html.textile.liquid
-      - sdk/cli/reference.html.textile.liquid
-      - sdk/cli/subcommands.html.textile.liquid
+      - sdk/java/example.html.textile.liquid
   api:
     - Concepts:
       - api/index.html.textile.liquid
-      - api/authentication.html.textile.liquid
+      - api/tokens.html.textile.liquid
+      - api/requests.html.textile.liquid
       - api/methods.html.textile.liquid
       - api/resources.html.textile.liquid
-      - api/crunch-scripts.html.textile.liquid
       - api/permission-model.html.textile.liquid
-    - API Methods:
+      - api/storage.html.textile.liquid
+      - api/execution.html.textile.liquid
+    - Permission and authentication:
       - api/methods/api_client_authorizations.html.textile.liquid
       - api/methods/api_clients.html.textile.liquid
       - api/methods/authorized_keys.html.textile.liquid
-      - api/methods/collections.html.textile.liquid
-      - api/methods/container_requests.html.textile.liquid
-      - api/methods/containers.html.textile.liquid
       - api/methods/groups.html.textile.liquid
-      - api/methods/humans.html.textile.liquid
-      - api/methods/jobs.html.textile.liquid
-      - api/methods/job_tasks.html.textile.liquid
-      - api/methods/keep_disks.html.textile.liquid
+      - api/methods/users.html.textile.liquid
+    - System resources:
       - api/methods/keep_services.html.textile.liquid
       - api/methods/links.html.textile.liquid
       - api/methods/logs.html.textile.liquid
       - api/methods/nodes.html.textile.liquid
+      - api/methods/virtual_machines.html.textile.liquid
+      - api/methods/keep_disks.html.textile.liquid
+    - Data management:
+      - api/methods/collections.html.textile.liquid
+      - api/methods/repositories.html.textile.liquid
+    - Container engine:
+      - api/methods/container_requests.html.textile.liquid
+      - api/methods/containers.html.textile.liquid
+      - api/methods/workflows.html.textile.liquid
+    - Jobs engine (deprecated):
+      - api/crunch-scripts.html.textile.liquid
+      - api/methods/jobs.html.textile.liquid
+      - api/methods/job_tasks.html.textile.liquid
       - api/methods/pipeline_instances.html.textile.liquid
       - api/methods/pipeline_templates.html.textile.liquid
-      - api/methods/repositories.html.textile.liquid
+    - Metadata for bioinformatics:
+      - api/methods/humans.html.textile.liquid
       - api/methods/specimens.html.textile.liquid
       - api/methods/traits.html.textile.liquid
-      - api/methods/users.html.textile.liquid
-      - api/methods/virtual_machines.html.textile.liquid
-      - api/methods/workflows.html.textile.liquid
-    - Schema:
-      - api/schema/ApiClientAuthorization.html.textile.liquid
-      - api/schema/ApiClient.html.textile.liquid
-      - api/schema/AuthorizedKey.html.textile.liquid
-      - api/schema/Collection.html.textile.liquid
-      - api/schema/Container.html.textile.liquid
-      - api/schema/ContainerRequest.html.textile.liquid
-      - api/schema/Group.html.textile.liquid
-      - api/schema/Human.html.textile.liquid
-      - api/schema/Job.html.textile.liquid
-      - api/schema/JobTask.html.textile.liquid
-      - api/schema/KeepDisk.html.textile.liquid
-      - api/schema/KeepService.html.textile.liquid
-      - api/schema/Link.html.textile.liquid
-      - api/schema/Log.html.textile.liquid
-      - api/schema/Node.html.textile.liquid
-      - api/schema/PipelineInstance.html.textile.liquid
-      - api/schema/PipelineTemplate.html.textile.liquid
-      - api/schema/Repository.html.textile.liquid
-      - api/schema/Specimen.html.textile.liquid
-      - api/schema/Trait.html.textile.liquid
-      - api/schema/User.html.textile.liquid
-      - api/schema/VirtualMachine.html.textile.liquid
-      - api/schema/Workflow.html.textile.liquid
   installguide:
     - Overview:
       - install/index.html.textile.liquid
@@ -168,12 +159,12 @@ navbar:
       - install/configure-azure-blob-storage.html.textile.liquid
       - install/install-keepproxy.html.textile.liquid
       - install/install-keep-web.html.textile.liquid
-    - Install Crunch v2 on SLURM:
+    - Containers API support on SLURM:
       - install/crunch2-slurm/install-prerequisites.html.textile.liquid
       - install/crunch2-slurm/install-compute-node.html.textile.liquid
       - install/crunch2-slurm/install-dispatch.html.textile.liquid
       - install/crunch2-slurm/install-test.html.textile.liquid
-    - Install Crunch v1:
+    - Jobs API support (deprecated):
       - install/install-crunch-dispatch.html.textile.liquid
       - install/install-compute-node.html.textile.liquid
     - Helpful hints:
index 849db42e47827c7a3cc2ddea8a28f36d3434979e..d505bfd9e0ec9981b84a19f046ea8260f34577e4 100644 (file)
@@ -8,4 +8,3 @@ table(table table-bordered table-condensed).
 |vcpus|integer|Number of cores to be used to run this process.|Optional. However, a ContainerRequest that is in "Committed" state must provide this.|
 |keep_cache_ram|integer|Number of keep cache bytes to be used to run this process.|Optional.|
 |API|boolean|When set, ARVADOS_API_HOST and ARVADOS_API_TOKEN will be set, and container will have networking enabled to access the Arvados API server.|Optional.|
-|partition|array of strings|Specify the names of one or more compute partitions that may run this container.  If not provided, the system chooses where to run the container.|Optional.|
diff --git a/doc/_includes/_container_scheduling_parameters.liquid b/doc/_includes/_container_scheduling_parameters.liquid
new file mode 100644 (file)
index 0000000..ee2ca07
--- /dev/null
@@ -0,0 +1,7 @@
+Scheduling parameters
+
+Parameters to be passed to the container scheduler (e.g., SLURM) when running a container.
+
+table(table table-bordered table-condensed).
+|_. Key|_. Type|_. Description|_. Notes|
+|partitions|array of strings|The names of one or more compute partitions that may run this container. If not provided, the system will choose where to run the container.|Optional.|
diff --git a/doc/_includes/_crunch1only_begin.liquid b/doc/_includes/_crunch1only_begin.liquid
new file mode 100644 (file)
index 0000000..5c08d1e
--- /dev/null
@@ -0,0 +1,2 @@
+{% include 'notebox_begin_warning' %}
+This section assumes the legacy Jobs API is available. Some newer installations have already disabled the Jobs API in favor of the Containers API.
diff --git a/doc/_includes/_crunch1only_end.liquid b/doc/_includes/_crunch1only_end.liquid
new file mode 100644 (file)
index 0000000..f8b437a
--- /dev/null
@@ -0,0 +1 @@
+{% include 'notebox_end' %}
diff --git a/doc/_includes/_notebox_begin_warning.liquid b/doc/_includes/_notebox_begin_warning.liquid
new file mode 100644 (file)
index 0000000..9f97e74
--- /dev/null
@@ -0,0 +1,2 @@
+<div class="alert alert-block alert-warning">
+  <h4>Note:</h4>
index 682511f70c37ac9f2d31960adc8a3d07a712efd5..a2747b0a5916b76c34df8466a9a08069676feb3c 100644 (file)
@@ -1,3 +1,3 @@
-{% include 'notebox_begin' %}
+{% include 'notebox_begin_warning' %}
 Arvados pipeline templates are deprecated.  The recommended way to develop new workflows for Arvados is using the "Common Workflow Language":{{site.baseurl}}/user/cwl/cwl-runner.html.
 {% include 'notebox_end' %}
diff --git a/doc/_includes/_what_is_cwl.liquid b/doc/_includes/_what_is_cwl.liquid
new file mode 100644 (file)
index 0000000..d7b890d
--- /dev/null
@@ -0,0 +1 @@
+The "Common Workflow Language (CWL)":http://commonwl.org is a multi-vendor open standard for describing analysis tools and workflows that are portable across a variety of platforms.  CWL is the recommended way to develop and run workflows for Arvados.  Arvados supports the "CWL v1.0":http://commonwl.org/v1.0 specification.
index ed232c67734f700a6d686352ed26b7e7a314b021..4c4e2542fa86d5ecf6cae8f0757d4195473ef206 100644 (file)
@@ -2,7 +2,7 @@
 <html>
   <head>
     <meta charset="utf-8">
-    <title>{% unless page.title == "Arvados | Documentation" %} Arvados | Documentation | {% endunless %}{{ page.title }}</title>
+    <title>{% unless page.title == "Arvados | Documentation" %} Arvados {% if page.navmenu %}| {{ page.navmenu }} {% endif %} | {% endunless %}{{ page.title }}</title>
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <meta name="description" content="">
     <meta name="author" content="">
diff --git a/doc/api/authentication.html.textile.liquid b/doc/api/authentication.html.textile.liquid
deleted file mode 100644 (file)
index cbf7553..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
----
-layout: default
-navsection: api
-navmenu: Concepts
-title: Authentication
-
-...
-
-
-
-Every API request (except the authentication API itself) includes an @access_token@ parameter.
-
-table(table table-bordered table-condensed).
-|Name|Type|Description|
-|access_token|string|Access token returned by OAuth 2.0 authorization procedure|
-
-Many resources contain "actor" attributes like @modified_by@.  An @access_token@ uniquely identifies a client (application or project) and an end-user.
-
-table(table table-bordered table-condensed).
-|Name|Type|Description|
-|modified_by_client_uuid|string|ID of API client|
-|modified_by_user_uuid|string|ID of authenticated user|
-
-h2. Authorizing a client application
-
-The Arvados API uses the "OAuth 2.0 protocol":http://tools.ietf.org/html/draft-ietf-oauth-v2-22 for authentication and authorization.
-
-h3. Register your client application
-
-Before an application can run on an Arvados cloud, it needs to be registered with the cloud. 
-
-That registration yields a @client_id@ and a @client_secret@. 
-
-h3. Obtain an access code
-
-A client obtains an access code by means of a standard Oauth 2.0 flow. The access code is granted to it by an authorized user. The client requests one or more scopes, which translate to a set of requested permissions (reading, writing, etc). Unless the access is to be short-lived, a refresh token is also granted to the application. 
-
-h3. Refresh the access code (optional)
-
-Access codes have a limited lifetime. A refresh token allows an application to request a new access token.
index 98634cdd9ba11ba1f110eaadb8c9554c7b5d847a..4642ed540e0b89cf16f0341de5da12d89e1284f0 100644 (file)
@@ -20,8 +20,8 @@ A task's context is provided in environment variables.
 
 table(table table-bordered table-condensed).
 |Environment variable|Description|
-|@JOB_UUID@|UUID of the current "Job":schema/Job.html|
-|@TASK_UUID@|UUID of the current "JobTask":schema/JobTask.html|
+|@JOB_UUID@|UUID of the current "Job":methods/jobs.html|
+|@TASK_UUID@|UUID of the current "JobTask":methods/job_tasks.html|
 |@ARVADOS_API_HOST@|Hostname and port number of API server|
 |@ARVADOS_API_TOKEN@|Authentication token to use with API calls made by the current task|
 
@@ -43,4 +43,3 @@ task = arvados.api().job_tasks().get(uuid=os.environ['TASK_UUID']).execute()
 $sys.stderr.write("current task sequence number is %d"
                   % task['sequence'])
 </pre>
-
diff --git a/doc/api/execution.html.textile.liquid b/doc/api/execution.html.textile.liquid
new file mode 100644 (file)
index 0000000..973b472
--- /dev/null
@@ -0,0 +1,27 @@
+---
+layout: default
+navsection: api
+title: Computing with Crunch
+...
+
+Crunch is the name for the Arvados system for managing computation.  It provides an abstract API to various clouds and HPC resource allocation and scheduling systems, and integrates closely with Keep storage and the Arvados permission system.
+
+h2. Container API
+
+Note: although the preferred API for Arvados going forward, the Container API may not yet be available on all installations.
+
+# To submit work, create a "container request":{{site.baseurl}}/api/methods/container_requests.html in the @Committed@ state.
+# The system will fufill the container request by creating or reusing a "Container object":{{site.baseurl}}/api/methods/containers.html and assigning it to the @container_uuid@ field.  If the same request has been submitted in the past, it may reuse an existing container.  The reuse behavior can be suppressed with @use_existing: false@ in the container request.
+# The dispatcher process will notice a new container in @Queued@ state and submit a container executor to the underlying work queuing system (such as SLURM).
+# The container executes.  Upon termination the container goes into the  @Complete@ state.  If the container execution was interrupted or lost due to system failure, it will go into the @Cancelled@ state.
+# When the container associated with the container request is completed, the container request will go into the @Final@ state.
+# The @output_uuid@ field of the container request contains the uuid of output collection produced by container request.
+
+!{{site.baseurl}}/images/Crunch_dispatch.svg!
+
+h2. Job API (deprecated)
+
+# To submit work, create a "job":{{site.baseurl}}/api/methods/jobs.html .  If the same job has been submitted in the past, it will return an existing job in @Completed@ state.
+# The dispatcher process will notice a new job in @Queued@ state and attempt to allocate nodes to run the job.
+# The job executes.
+# Retrieve the @output@ field with the portable data hash of the collection with the output files of the job.
index 81b2c1ce6a482418f8302fde597e0ff1bdce300b..d90a35efa40d30ae9abdc17611434c42a1c710a4 100644 (file)
@@ -5,46 +5,12 @@ title: API Reference
 
 ...
 
+This reference describes the semantics of Arvados resources and how to programatically access Arvados via its REST API.  Each resource listed in this section is exposed on the Arvados API server under the @/arvados/v1/@ path prefix, for example, @https://{{ site.arvados_api_host }}/arvados/v1/collections@.
 
+h2. Discovery document
 
-h2. Concepts
+The API server publishes a machine-readable description of its endpoints and some additional site configuration values via a JSON-formatted discovery document.  This is available at @/discovery/v1/apis/arvados/v1/rest@, for example @https://{{ site.arvados_api_host }}/discovery/v1/apis/arvados/v1/rest@.  Some Arvados SDKs use the discovery document to generate language bindings.
 
-* Each API uses the same "authentication mechanism":authentication.html.
-* Resources in requests and responses adhere to a "common structure":resources.html.
-* API transactions use common "REST methods":methods.html.
-* API transactions are subject to a "permission model":permission-model.html.
-* "Job tasks":schema/JobTask.html use some special API features.
+h2. Workbench examples
 
-h2. Resources
-
-h3. Generic Resources
-
-* "Collection":schema/Collection.html
-* "Job":schema/Job.html
-* "JobTask":schema/JobTask.html
-* "Link":schema/Link.html
-* "Log":schema/Log.html
-* "PipelineTemplate":schema/PipelineTemplate.html
-* "PipelineInstance":schema/PipelineInstance.html
-* "Group":schema/Group.html
-* "Human":schema/Human.html
-* "Specimen":schema/Specimen.html
-* "Trait":schema/Trait.html
-* "User":schema/User.html
-
-h3. Authentication
-
-These Arvados resources govern authorization and "authentication":authentication.html:
-
-* "ApiClient":schema/ApiClient.html
-* "ApiClientAuthorization":schema/ApiClientAuthorization.html
-* "AuthorizedKey":schema/AuthorizedKey.html
-
-h3. Arvados Infrastructure
-
-These resources govern the Arvados infrastructure itself: Git repositories, Keep disks, active nodes, etc.
-
-* "KeepDisk":schema/KeepDisk.html
-* "Node":schema/Node.html
-* "Repository":schema/Repository.html
-* "VirtualMachine":schema/VirtualMachine.html
+Many Arvados Workbench pages, under the the *Advanced* tab, provide examples of API and SDK use for accessing the current resource .
index 2d530d147385a5ba529814f53f912c7b1f515126..e731cc7102f37600f450e13691b196f0f2486340 100644 (file)
@@ -2,41 +2,73 @@
 layout: default
 navsection: api
 navmenu: Concepts
-title: REST methods
+title: Common resource methods
 
 ...
 
+The following methods are available for most resources.  Some resources may limit who can perform certain operations.  Consult documentation for individual resource types for details.
 
+The methods are relative to the base URI, e.g. @/arvados/v1/resource_type@.  For arguments specifying a *Location* of @path@, the value of the argument is incorporated into the path portion of the URI.  For example, a @uuid@ of @aaaaa-bbbbb-ccccccccccccccc@ in a path position yields a URI of @/arvados/v1/resource_type/aaaaa-bbbbb-ccccccccccccccc@.
 
-(using Group as an example)
+Arguments specifying a *Location* of @query@ are incorporated into the query portion of the URI or request body.  For example, @/arvados/v1/resource_type?resource_type={}@.
 
-h2(#index). Index, list, search
+h2. create
 
-<pre>
-GET https://{{ site.arvados_api_host }}/arvados/v1/groups?filters=[["owner_uuid","=","xyzzy-tpzed-a4lcehql0dv2u25"]]
+The @create@ method creates a new object of the specified type.  Note that:
 
-POST https://{{ site.arvados_api_host }}/arvados/v1/groups
-_method=GET
-filters=[["owner_uuid","=","xyzzy-tpzed-a4lcehql0dv2u25"]]
-</pre>
+* Only the listed attributes (and "standard metadata":resources.html) are set
+* Unset attributes will get default values
+* The attributes of a given resource type are fixed (you cannot introduce new toplevel attributes)
 
-&rarr; Group resource list
+This method corresponds to the HTTP request @POST /arvados/v1/resource_type@.  A successful create call returns a copy of the new object.
+
+Arguments:
+
+table(table table-bordered table-condensed).
+|_. Argument |_. Type |_. Description |_. Location |
+|{resource_type}|object|Name is the singular form of the resource type, e.g. for the "collections" resource, this argument is "collection"|query||
+
+h2. delete
+
+The @delete@ method deletes an object of the specified type.  It corresponds to the HTTP request @DELETE /arvados/v1/resource_type/uuid@.  A successful delete call returns a copy of the deleted object.
+
+Arguments:
+
+table(table table-bordered table-condensed).
+|_. Argument |_. Type |_. Description |_. Location |
+{background:#ccffcc}.|uuid|string|The UUID of the object in question.|path|
+
+h2. get
+
+The @get@ method gets a single object with the specified @uuid@.  It corresponds to the HTTP request @GET /arvados/v1/resource_type/uuid@.
+
+Arguments:
+
+table(table table-bordered table-condensed).
+|_. Argument |_. Type |_. Description |_. Location |
+{background:#ccffcc}.|uuid|string|The UUID of the object in question.|path|
+
+h2(#index). list
+
+The @list@ method requests an list of resources of that type.  It corresponds to the HTTP request @GET /arvados/v1/resource_type@.  All resources support "list" method unless otherwise noted.
+
+Arguments:
 
 table(table table-bordered table-condensed).
-|*Parameter name*|*Value*|*Description*|
-|limit   |integer|Maximum number of resources to return.|
-|offset  |integer|Skip the first 'offset' resources that match the given filter conditions.|
-|filters |array  |Conditions for selecting resources to return (see below).|
+|_. Argument |_. Type |_. Description |_. Location |
+|limit   |integer|Maximum number of resources to return.  If not provided, server will provide a default limit.  Server may also impose a maximum number of records that can be returned in a single request.|query|
+|offset  |integer|Skip the first 'offset' number of resources that would be returned under the given filter conditions.|query|
+|filters |array  |"Conditions for selecting resources to return.":#filters|query|
 |order   |array  |Attributes to use as sort keys to determine the order resources are returned, each optionally followed by @asc@ or @desc@ to indicate ascending or descending order.
 Example: @["head_uuid asc","modified_at desc"]@
-Default: @["created_at desc"]@|
+Default: @["created_at desc"]@|query|
 |select  |array  |Set of attributes to include in the response.
 Example: @["head_uuid","tail_uuid"]@
-Default: all available attributes, minus "manifest_text" in the case of collections.|
+Default: all available attributes.  As a special case, collections do not return "manifest_text" unless explicitly selected.|query|
 |distinct|boolean|@true@: (default) do not return duplicate objects
-@false@: permitted to return duplicates|
+@false@: permitted to return duplicates|query|
 
-h3. Filters
+h3(#filters). Available list method filters
 
 The value of the @filters@ parameter is an array of conditions. The @list@ method returns only the resources that satisfy all of the given conditions. In other words, the conjunction @AND@ is implicit.
 
@@ -58,45 +90,23 @@ table(table table-bordered table-condensed).
 |@in@, @not in@|array of strings|@["script_version","in",["master","d00220fb38d4b85ca8fc28a8151702a2b9d1dec5"]]@|
 |@is_a@|string|@["head_uuid","is_a","arvados#pipelineInstance"]@|
 
-h2. Create
-
-<pre>
-POST https://{{ site.arvados_api_host }}/arvados/v1/groups
-group={"name":"fresh new group"}
-</pre>
-
-&rarr; Group resource
-
-h2. Delete
-
-<pre>
-DELETE https://{{ site.arvados_api_host }}/arvados/v1/groups/xyzzy-ldvyl-vyydjeplwaa6emg
-</pre>
+h3. Results of list method
 
-&rarr; Group resource
+A successful call to list will return the following object.
 
-h2. Update
-
-<pre>
-PUT https://{{ site.arvados_api_host }}/arvados/v1/groups/xyzzy-ldvyl-vyydjeplwaa6emg
-group={"uuid":"xyzzy-ldvyl-vyydjeplwaa6emg", "name":"Important group"}
-</pre>
-
-&rarr; Group resource
-
-<pre>
-PUT https://{{ site.arvados_api_host }}/arvados/v1/groups/xyzzy-ldvyl-vyydjeplwaa6emg
-group[uuid]=xyzzy-ldvyl-vyydjeplwaa6emg
-group[name]=Important group
-</pre>
-
-&rarr; Group resource
+table(table table-bordered table-condensed).
+|_. Attribute |_. Type |_. Description |
+|kind|string|type of objects returned|
+|offset|integer|query offset in effect|
+|limit|integer|query limit in effect|
+|items|array|actual query payload, an array of resource objects|
+|items_available|integer|total items available matching query|
 
-More appropriate (but not yet implemented):
+h2. update
 
-<pre>
-PATCH https://{{ site.arvados_api_host }}/arvados/v1/groups/xyzzy-ldvyl-vyydjeplwaa6emg
-group={"uuid":"xyzzy-ldvyl-vyydjeplwaa6emg", "name":"Important group"}
-</pre>
+The @update@ method updates fields on the object with the specified @uuid@.  It corresponds to the HTTP request @PUT /arvados/v1/resource_type/uuid@.  Note that only the listed attributes (and "standard metadata":resources.html) are updated, unset attributes will retain their previous values, and the attributes of a given resource type are fixed (you cannot introduce new toplevel attributes).  Also note that updates replace the value of the attribute, so if an attribute has an object value, the entire object is replaced.  A successful update call returns the updated copy of the object.
 
-&rarr; Group resource
+table(table table-bordered table-condensed).
+|_. Argument |_. Type |_. Description |_. Location |
+{background:#ccffcc}.|uuid|string|The UUID of the resource in question.|path||
+|{resource_type}|object||query||
index 7af9711b9d17e95d3a524c6e895446f8b8f17058..ab49610ab168332a78f3fdc9f69066105c4bf3b4 100644 (file)
@@ -6,24 +6,50 @@ title: "api_client_authorizations"
 
 ...
 
-See "REST methods for working with Arvados resources":{{site.baseurl}}/api/methods.html
-
 API endpoint base: @https://{{ site.arvados_api_host }}/arvados/v1/api_client_authorizations@
 
-Required arguments are displayed in %{background:#ccffcc}green%.
+Object type: @gj3su@
+
+Example UUID: @zzzzz-gj3su-0123456789abcde@
+
+h2. Resource
+
+The @api_client_authorizations@ resource stores the API tokens that have been issued to permit access the API server.
+
+An ApiClientAuthorization is *not* a generic Arvados resource.  The full list of properties that belong to an ApiClientAuthorization is:
+
+table(table table-bordered table-condensed).
+|_. Attribute|_. Type|_. Description|_. Example|
+|uuid|string|An identifier used to refer to the token without exposing the actual token.||
+|api_token|string|The actual token string that is expected in the Authorization header.||
+|api_client_id|integer|-||
+|user_id|integer|-||
+|created_by_ip_address|string|-||
+|last_used_by_ip_address|string|The network address of the most recent client using this token.||
+|last_used_at|datetime|Timestamp of the most recent request using this token.||
+|expires_at|datetime|Time at which the token is no longer valid.  May be set to a time in the past in order to immediately expire a token.||
+|owner_uuid|string|The user associated with the token.  All operations using this token are checked against the permissions of this user.||
+|scopes|array|A list of resources this token is allowed to access.  A scope of ["all"] allows all resources.  See "API Authorization":{{site.baseurl}}/api/tokens.html#scopes for details.||
+
+h2. Methods
+
+See "Common resource methods":{{site.baseurl}}/api/methods.html for more information about @create@, @delete@, @get@, @list@, and @update@.
 
+Required arguments are displayed in %{background:#ccffcc}green%.
 
-h2. create
+h3(#create). create
 
 Create a new ApiClientAuthorization.
 
+Regular users may only create self-owned API tokens, but may provide a restricted "scope":{{site.baseurl}}/api/tokens.html#scopes .  Administrators may create API tokens corresponding to any user.
+
 Arguments:
 
 table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 |api_client_authorization|object||query||
 
-h2. create_system_auth
+h3. create_system_auth
 
 create_system_auth api_client_authorizations
 
@@ -34,7 +60,7 @@ table(table table-bordered table-condensed).
 |api_client_id|integer||query||
 |scopes|array||query||
 
-h2. delete
+h3. delete
 
 Delete an existing ApiClientAuthorization.
 
@@ -44,9 +70,9 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 {background:#ccffcc}.|uuid|string|The UUID of the ApiClientAuthorization in question.|path||
 
-h2. get
+h3. get
 
-Gets a ApiClientAuthorization's metadata by UUID.
+Gets an ApiClientAuthorization's metadata by UUID.
 
 Arguments:
 
@@ -54,19 +80,13 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 {background:#ccffcc}.|uuid|string|The UUID of the ApiClientAuthorization in question.|path||
 
-h2. list
+h3. list
 
 List api_client_authorizations.
 
-Arguments:
-
-table(table table-bordered table-condensed).
-|_. Argument |_. Type |_. Description |_. Location |_. Example |
-|limit|integer (default 100)|Maximum number of api_client_authorizations to return.|query||
-|order|string|Order in which to return matching api_client_authorizations.|query||
-|filters|array|Conditions for filtering api_client_authorizations.|query||
+See "common resource list method.":{{site.baseurl}}/api/methods.html#index
 
-h2. update
+h3. update
 
 Update attributes of an existing ApiClientAuthorization.
 
index 056cc30812c767ea06b8ec53310dc1c8bc362fa0..f0224fbc4813a6367967d5328b4eb19d34cb31cd 100644 (file)
@@ -6,13 +6,31 @@ title: "api_clients"
 
 ...
 
-See "REST methods for working with Arvados resources":{{site.baseurl}}/api/methods.html
-
 API endpoint base: @https://{{ site.arvados_api_host }}/arvados/v1/api_clients@
 
+Object type: @ozdt8@
+
+Example UUID: @zzzzz-ozdt8-0123456789abcde@
+
+h2. Resource
+
+The "api_clients" resource determines if web applications that have gone through the browser login flow may create or list API tokens.
+
+Each ApiClient has, in addition to the "Common resource fields":{{site.baseurl}}/api/resources.html:
+
+table(table table-bordered table-condensed).
+|_. Attribute|_. Type|_. Description|_. Example|
+|name|string|||
+|url_prefix|string|||
+|is_trusted|boolean|Trusted by users to handle their API tokens (ApiClientAuthorizations).||
+
+h2. Methods
+
+See "Common resource methods":{{site.baseurl}}/api/methods.html for more information about @create@, @delete@, @get@, @list@, and @update@.
+
 Required arguments are displayed in %{background:#ccffcc}green%.
 
-h2. create
+h3. create
 
 Create a new ApiClient.
 
@@ -22,7 +40,7 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 |api_client|object||query||
 
-h2. delete
+h3. delete
 
 Delete an existing ApiClient.
 
@@ -32,7 +50,7 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 {background:#ccffcc}.|uuid|string|The UUID of the ApiClient in question.|path||
 
-h2. get
+h3. get
 
 Gets a ApiClient's metadata by UUID.
 
@@ -42,19 +60,13 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 {background:#ccffcc}.|uuid|string|The UUID of the ApiClient in question.|path||
 
-h2. list
+h3. list
 
 List api_clients.
 
-Arguments:
-
-table(table table-bordered table-condensed).
-|_. Argument |_. Type |_. Description |_. Location |_. Example |
-|limit|integer (default 100)|Maximum number of api_clients to return.|query||
-|order|string|Order in which to return matching api_clients.|query||
-|filters|array|Conditions for filtering api_clients.|query||
+See "common resource list method.":{{site.baseurl}}/api/methods.html#index
 
-h2. update
+h3. update
 
 Update attributes of an existing ApiClient.
 
index 9727c5730c54375917e0f36ce800ae93bdfd72cf..2912ba83d136193018e909e240dec91fb3d7fa04 100644 (file)
@@ -6,14 +6,33 @@ title: "authorized_keys"
 
 ...
 
-See "REST methods for working with Arvados resources":{{site.baseurl}}/api/methods.html
-
 API endpoint base: @https://{{ site.arvados_api_host }}/arvados/v1/authorized_keys@
 
-Required arguments are displayed in %{background:#ccffcc}green%.
+Object type: @fngyi@
+
+Example UUID: @zzzzz-fngyi-0123456789abcde@
+
+h2. Resource
+
+The authorized_keys resource stores SSH public keys which grant access to virtual machines or git repositories on the Arvados cluster as the user in @authorized_user_uuid@.
+
+Each AuthorizedKey has, in addition to the "Common resource fields":{{site.baseurl}}/api/resources.html:
+
+table(table table-bordered table-condensed).
+|_. Attribute|_. Type|_. Description|_. Example|
+|name|string|A name to help the user manage their keys.||
+|key_type|string|Public key type, currently only supports "SSH"||
+|authorized_user_uuid|string|The user to which this key belongs.  Authentication using this key authenticates as this user.||
+|public_key|text|The actual public key material, e.g. from @~/.ssh/id_rsa.pub@||
+|expires_at|datetime|Expiration date after which the key is no longer valid.||
 
+h2. Methods
 
-h2. create
+See "Common resource methods":{{site.baseurl}}/api/methods.html for more information about @create@, @delete@, @get@, @list@, and @update@.
+
+Required arguments are displayed in %{background:#ccffcc}green%.
+
+h3. create
 
 Create a new AuthorizedKey.
 
@@ -23,7 +42,7 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 |authorized_key|object||query||
 
-h2. delete
+h3. delete
 
 Delete an existing AuthorizedKey.
 
@@ -33,7 +52,7 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 {background:#ccffcc}.|uuid|string|The UUID of the AuthorizedKey in question.|path||
 
-h2. get
+h3. get
 
 Gets a AuthorizedKey's metadata by UUID.
 
@@ -43,19 +62,13 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 {background:#ccffcc}.|uuid|string|The UUID of the AuthorizedKey in question.|path||
 
-h2. list
+h3. list
 
 List authorized_keys.
 
-Arguments:
-
-table(table table-bordered table-condensed).
-|_. Argument |_. Type |_. Description |_. Location |_. Example |
-|limit|integer (default 100)|Maximum number of authorized_keys to return.|query||
-|order|string|Order in which to return matching authorized_keys.|query||
-|filters|array|Conditions for filtering authorized_keys.|query||
+See "common resource list method.":{{site.baseurl}}/api/methods.html#index
 
-h2. update
+h3. update
 
 Update attributes of an existing AuthorizedKey.
 
index f5e685e2e9be44a1d740defff0a1ffb0e60da80f..808125a9e676972d70aa9a75017951cf708b0063 100644 (file)
@@ -6,13 +6,45 @@ title: "collections"
 
 ...
 
-See "REST methods for working with Arvados resources":{{site.baseurl}}/api/methods.html
-
 API endpoint base: @https://{{ site.arvados_api_host }}/arvados/v1/collections@
 
+Object type: @4zz18@
+
+Example UUID: @zzzzz-4zz18-0123456789abcde@
+
+h2. Resource
+
+Collections describe sets of files in terms of data blocks stored in Keep.  See "storage in Keep":{{site.baseurl}}/api/storage.html for details.
+
+Each collection has, in addition to the "Common resource fields":{{site.baseurl}}/api/resources.html:
+
+table(table table-bordered table-condensed).
+|_. Attribute|_. Type|_. Description|_. Example|
+|name|string|||
+|description|text|||
+|portable_data_hash|string|The MD5 sum of the manifest text stripped of block hints other than the size hint.||
+|manifest_text|text|||
+|replication_desired|number|Minimum storage replication level desired for each data block referenced by this collection. A value of @null@ signifies that the site default replication level (typically 2) is desired.|@2@|
+|replication_confirmed|number|Replication level most recently confirmed by the storage system. This field is null when a collection is first created, and is reset to null when the manifest_text changes in a way that introduces a new data block. An integer value indicates the replication level of the _least replicated_ data block in the collection.|@2@, null|
+|replication_confirmed_at|datetime|When replication_confirmed was confirmed. If replication_confirmed is null, this field is also null.||
+
+h3. Conditions of creating a Collection
+
+The @portable_data_hash@ and @manifest_text@ attributes must be provided when creating a Collection. The cryptographic digest of the supplied @manifest_text@ must match the supplied @portable_data_hash@.
+
+h3. Side effects of creating a Collection
+
+Referenced blocks are protected from garbage collection in Keep.
+
+Data can be shared with other users via the Arvados permission model.
+
+h2. Methods
+
+See "Common resource methods":{{site.baseurl}}/api/methods.html for more information about @create@, @delete@, @get@, @list@, and @update@.
+
 Required arguments are displayed in %{background:#ccffcc}green%.
 
-h2. create
+h3. create
 
 Create a new Collection.
 
@@ -22,7 +54,7 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 |collection|object||query||
 
-h2. delete
+h3. delete
 
 Delete an existing Collection.
 
@@ -32,7 +64,7 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 {background:#ccffcc}.|uuid|string|The UUID of the Collection in question.|path||
 
-h2. get
+h3. get
 
 Gets a Collection's metadata by UUID.
 
@@ -42,22 +74,15 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 {background:#ccffcc}.|uuid|string|The UUID of the Collection in question.|path||
 
-h2. list
+h3. list
 
 List collections.
 
-Arguments:
-
-table(table table-bordered table-condensed).
-|_. Argument |_. Type |_. Description |_. Location |_. Example |
-|limit|integer (default 100)|Maximum number of collections to return.|query||
-|order|string|Order in which to return matching collections.|query||
-|filters|array|Conditions for filtering collections.|query||
-|select|array|Data fields to return in the result list.|query|@["uuid", "manifest_text"]@|
+See "common resource list method.":{{site.baseurl}}/api/methods.html#index
 
 Note: Because adding access tokens to manifests can be computationally expensive, the @manifest_text@ field is not included in results by default.  If you need it, pass a @select@ parameter that includes @manifest_text@.
 
-h2. update
+h3. update
 
 Update attributes of an existing Collection.
 
index 2603079560e8a857c60c1e16c20b43b1476c46fe..05a8cf56a8f48f24ae9eefa6de9ce69e108a76ff 100644 (file)
@@ -6,70 +6,131 @@ title: "container_requests"
 
 ...
 
-See "REST methods for working with Arvados resources":{{site.baseurl}}/api/methods.html
-
 API endpoint base: @https://{{ site.arvados_api_host }}/arvados/v1/container_requests@
 
+Object type: @xvhdp@
+
+Example UUID: @zzzzz-xvhdp-0123456789abcde@
+
+h2. Resource
+
+A container request is a request for the Arvados cluster to perform some computational work.  See "computing with Crunch":{{site.baseurl}}/api/execution.html for details.
+
+Each ContainerRequest offers the following attributes, in addition to the "Common resource fields":{{site.baseurl}}/api/resources.html:
+
+All attributes are optional, unless otherwise marked as required.
+
+table(table table-bordered table-condensed).
+|_. Attribute|_. Type|_. Description|_. Notes|
+|name|string|The name of the container_request.||
+|description|string|The description of the container_request.||
+|properties|hash|Client-defined structured data that does not affect how the container is run.||
+|state|string|The allowed states are "Uncommitted", "Committed", and "Final".|Once a request is Committed, the only attributes that can be modified are priority, container_uuid, and container_count_max. A request in the "Final" state cannot have any of its functional parts modified (i.e., only name, description, and properties fields can be modified).|
+|requesting_container_uuid|string|The uuid of the parent container that created this container_request, if any. Represents a process tree.|The priority of this container_request is inherited from the parent container, if the parent container is cancelled, this container_request will be cancelled as well.|
+|container_uuid|string|The uuid of the container that satisfies this container_request. The system may return a preexisting Container that matches the container request criteria. See "Container reuse":#container_reuse for more details.|Container reuse is the default behavior, but may be disabled with @use_existing: false@ to always create a new container.|
+|container_count_max|integer|Maximum number of containers to start, i.e., the maximum number of "attempts" to be made.||
+|mounts|hash|Objects to attach to the container's filesystem and stdin/stdout.|See "Mount types":#mount_types for more details.|
+|runtime_constraints|hash|Restrict the container's access to compute resources and the outside world.|Required when in "Committed" state. e.g.,<pre><code>{
+  "ram":12000000000,
+  "vcpus":2,
+  "API":true
+}</code></pre>See "Runtime constraints":#runtime_constraints for more details.|
+|scheduling_parameters|hash|Parameters to be passed to the container scheduler when running this container.|e.g.,<pre><code>{
+"partitions":["fastcpu","vfastcpu"]
+}</code></pre>See "Scheduling parameters":#scheduling_parameters for more details.|
+|container_image|string|Portable data hash of a collection containing the docker image to run the container.|Required.|
+|environment|hash|Environment variables and values that should be set in the container environment (@docker run --env@). This augments and (when conflicts exist) overrides environment variables given in the image's Dockerfile.||
+|cwd|string|Initial working directory, given as an absolute path (in the container) or a path relative to the WORKDIR given in the image's Dockerfile.|Required.|
+|command|array of strings|Command to execute in the container.|Required. e.g., @["echo","hello"]@|
+|output_path|string|Path to a directory or file inside the container that should be preserved as container's output when it finishes. This path must be, or be inside, one of the mount targets. For best performance, point output_path to a writable collection mount.|Required.|
+|priority|integer|Higher value means spend more resources on this container_request, i.e., go ahead of other queued containers, bring up more nodes etc.|Priority 0 means a container should not be run on behalf of this request. Clients are expected to submit container requests with zero priority in order to preview the container that will be used to satisfy it. Priority can be null if and only if state!="Committed".|
+|expires_at|datetime|After this time, priority is considered to be zero.|Not yet implemented.|
+|use_existing|boolean|If possible, use an existing (non-failed) container to satisfy the request instead of creating a new one.|Default is true|
+|log_uuid|string|Log collection containing log messages provided by the scheduler and crunch processes.|Null if the container has not yet completed.|
+|output_uuid|string|Output collection created when the container finished successfully.|Null if the container has failed or not yet completed.|
+|filters|string|Additional constraints for satisfying the container_request, given in the same form as the filters parameter accepted by the container_requests.list API.|
+
+h2(#mount_types). {% include 'mount_types' %}
+
+h2(#runtime_constraints). {% include 'container_runtime_constraints' %}
+
+h2(#scheduling_parameters). {% include 'container_scheduling_parameters' %}
+
+h2(#container_reuse). Container reuse
+
+When a container request is "Committed", the system will try to find and reuse any preexisting Container with the same exact command, cwd, environment, output_path, container_image, mounts, and runtime_constraints as this container request. The serialized fields environment, mounts and runtime_constraints are sorted to facilitate comparison.
+
+The system will use the following scheme to determine which Container to consider for reuse: A Container with the same exact command, cwd, environment, output_path, container_image, mounts, and runtime_constraints as this container request and,
+* The oldest successfully finished container, i.e., in state "Complete" with exit_code of 0. If matching containers with different outputs are found, the system will forgo reusing any of these finished containers and instead look for suitable containers in other states
+* The oldest "Running" container with the highest progress, i.e., the container that is most likely to finish first
+* The oldest "Locked" container with the highest priority, i.e., the container that is most likely to start first
+* The oldest "Queued" container with the highest priority, i.e, the container that is most likely to start first
+
+h2(#cancel_container). Canceling a container request
+
+A container request may be canceled by setting it's priority to 0, using an update call.
+
+When a container request is canceled, it will still reflect the state of the Container it is associated with via the container_uuid attribute. If that Container is being reused by any other container_requests that are still active, i.e., not yet canceled, that Container may continue to run or be scheduled to run by the system in future. However, if no other container_requests are using that Contianer, then the Container will get canceled as well.
+
+h2. Methods
+
+See "Common resource methods":{{site.baseurl}}/api/methods.html for more information about @create@, @delete@, @get@, @list@, and @update@.
+
 Required arguments are displayed in %{background:#ccffcc}green%.
 
 h2(#create). create
 
-Create a new ContainerRequest.
+Create a new container request.
 
 Arguments:
 
 table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
-{background:#ccffcc}.|container_request|object|See "ContainerRequest resource":{{site.baseurl}}/api/schema/ContainerRequest.html|request body||
+{background:#ccffcc}.|container_request|object|Container request resource.|request body||
+
 
 The request body must include the required attributes command, container_image, cwd, and output_path. It can also inlcude other attributes such as environment, mounts, and runtime_constraints.
 
-h2. delete
+h3. delete
 
-Delete an existing ContainerRequest.
+Delete an existing container request.
 
 Arguments:
 
 table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
-{background:#ccffcc}.|uuid|string|The UUID of the ContainerRequest in question.|path||
+{background:#ccffcc}.|uuid|string|The UUID of the container request in question.|path||
 
-h2. get
+h3. get
 
-Get a ContainerRequest's metadata by UUID.
+Get a container request's metadata by UUID.
 
 Arguments:
 
 table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
-{background:#ccffcc}.|uuid|string|The UUID of the ContainerRequest in question.|path||
+{background:#ccffcc}.|uuid|string|The UUID of the container request in question.|path||
 
-h2. list
+h3. list
 
 List container_requests.
 
-Arguments:
-
-table(table table-bordered table-condensed).
-|_. Argument |_. Type |_. Description |_. Location |_. Example |
-|limit|integer (default 100)|Maximum number of container_requests to return.|query||
-|order|string|Order in which to return matching container_requests.|query||
-|filters|array|Conditions for filtering container_requests.|query||
+See "common resource list method.":{{site.baseurl}}/api/methods.html#index
 
-See the create method documentation for more information about ContainerRequest-specific filters.
+See the create method documentation for more information about container request-specific filters.
 
-h2. update
+h3. update
 
-Update attributes of an existing ContainerRequest.
+Update attributes of an existing container request.
 
 Arguments:
 
 table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
-{background:#ccffcc}.|uuid|string|The UUID of the ContainerRequest in question.|path||
+{background:#ccffcc}.|uuid|string|The UUID of the container request in question.|path||
 |container_request|object||query||
 
 {% include 'notebox_begin' %}
 Setting the priority of a committed container_request to 0 may cancel a running container assigned for it.
-See "Canceling a ContainerRequest":{{site.baseurl}}/api/schema/ContainerRequest.html#cancel_container for further details.
+See "Canceling a container request":{{site.baseurl}}/api/methods/container_requests.html#cancel_container for further details.
 {% include 'notebox_end' %}
index c39b0926c18b717f0c09b1cf4355e2f8d89ed67d..7eed8b0d30f84aa07dddbc46edf0a35773b63977 100644 (file)
@@ -6,10 +6,67 @@ title: "containers"
 
 ...
 
-See "REST methods for working with Arvados resources":{{site.baseurl}}/api/methods.html
-
 API endpoint base: @https://{{ site.arvados_api_host }}/arvados/v1/containers@
 
+Object type: @dz642@
+
+Example UUID: @zzzzz-dz642-0123456789abcde@
+
+h2. Resource
+
+A container is work order to be dispatched to an Arvados cluster to perform some computational work.  A container is created in response to a container request.  See "computing with Crunch":{{site.baseurl}}/api/execution.html for details.
+
+Each Container offers the following attributes, in addition to the "Common resource fields":{{site.baseurl}}/api/resources.html:
+
+table(table table-bordered table-condensed).
+|_. Attribute|_. Type|_. Description|_. Notes|
+|state|string|The allowed states are "Queued", "Locked", "Running", "Cancelled" and "Complete".|See "Container states":#container_states for more details.|
+|started_at|datetime|When this container started running.|Null if container has not yet started.|
+|finished_at|datetime|When this container finished.|Null if container has not yet finished.|
+|log|string|Portable data hash of the collection containing logs from a completed container run.|Null if the container is not yet finished.|
+|environment|hash|Environment variables and values that should be set in the container environment (@docker run --env@). This augments and (when conflicts exist) overrides environment variables given in the image's Dockerfile.|Must be equal to a ContainerRequest's environment in order to satisfy the ContainerRequest.|
+|cwd|string|Initial working directory.|Must be equal to a ContainerRequest's cwd in order to satisfy the ContainerRequest|
+|command|array of strings|Command to execute.| Must be equal to a ContainerRequest's command in order to satisfy the ContainerRequest.|
+|output_path|string|Path to a directory or file inside the container that should be preserved as this container's output when it finishes.|Must be equal to a ContainerRequest's output_path in order to satisfy the ContainerRequest.|
+|mounts|hash|Must contain the same keys as the ContainerRequest being satisfied. Each value must be within the range of values described in the ContainerRequest at the time the Container is assigned to the ContainerRequest.|See "Mount types":#mount_types for more details.|
+|runtime_constraints|hash|Compute resources, and access to the outside world, that are / were available to the container.
+Generally this will contain additional keys that are not present in any corresponding ContainerRequests: for example, even if no ContainerRequests specified constraints on the number of CPU cores, the number of cores actually used will be recorded here.|e.g.,
+<pre><code>{
+  "ram":12000000000,
+  "vcpus":2,
+  "API":true
+}</code></pre>See "Runtime constraints":#runtime_constraints for more details.|
+|scheduling_parameters|hash|Parameters to be passed to the container scheduler when running this container.|e.g.,<pre><code>{
+"partitions":["fastcpu","vfastcpu"]
+}</code></pre>See "Scheduling parameters":#scheduling_parameters for more details.|
+|output|string|Portable data hash of the output collection.|Null if the container is not yet finished.|
+|container_image|string|Portable data hash of a collection containing the docker image used to run the container.||
+|progress|number|A number between 0.0 and 1.0 describing the fraction of work done.||
+|priority|integer|Priority assigned by the system, taking into account the priorities of all associated ContainerRequests.||
+|exit_code|integer|Process exit code.|Null if state!="Complete"|
+|auth_uuid|string|UUID of a token to be passed into the container itself, used to access Keep-backed mounts, etc.|Null if state∉{"Locked","Running"}|
+|locked_by_uuid|string|UUID of a token, indicating which dispatch process changed state to Locked. If null, any token can be used to lock. If not null, only the indicated token can modify this container.|Null if state∉{"Locked","Running"}|
+
+h2(#container_states). Container states
+
+table(table table-bordered table-condensed).
+|_. State|_. Sgnificance|_. Allowed next|
+|Queued|Waiting for a dispatcher to lock it and try to run the container.|Locked, Cancelled|
+|Locked|A dispatcher has "taken" the container and is allocating resources for it. The container has not started yet.|Queued, Running, Cancelled|
+|Running|Resources have been allocated and the contained process has been started (or is about to start). Crunch-run _must_ set state to Running _before_ there is any possibility that user code will run in the container.|Complete, Cancelled|
+|Complete|Container was running, and the contained process/command has exited.|-|
+|Cancelled|The container did not run long enough to produce an exit code. This includes cases where the container didn't even start, cases where the container was interrupted/killed before it exited by itself (e.g., priority changed to 0), and cases where some problem prevented the system from capturing the contained process's exit status (exit code and output).|-|
+
+h2(#mount_types). {% include 'mount_types' %}
+
+h2(#runtime_constraints). {% include 'container_runtime_constraints' %}
+
+h2(#scheduling_parameters). {% include 'container_scheduling_parameters' %}
+
+h2. Methods
+
+See "Common resource methods":{{site.baseurl}}/api/methods.html for more information about @create@, @delete@, @get@, @list@, and @update@.
+
 Required arguments are displayed in %{background:#ccffcc}green%.
 
 h2(#create). create
@@ -20,9 +77,9 @@ Arguments:
 
 table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
-{background:#ccffcc}.|container|object|See "Container resource":{{site.baseurl}}/api/schema/Container.html|request body||
+{background:#ccffcc}.|container|object|Container resource|request body||
 
-h2. delete
+h3. delete
 
 Delete an existing Container.
 
@@ -32,7 +89,7 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 {background:#ccffcc}.|uuid|string|The UUID of the Container in question.|path||
 
-h2. get
+h3. get
 
 Get a Container's metadata by UUID.
 
@@ -42,21 +99,15 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 {background:#ccffcc}.|uuid|string|The UUID of the Container in question.|path||
 
-h2. list
+h3. list
 
 List containers.
 
-Arguments:
-
-table(table table-bordered table-condensed).
-|_. Argument |_. Type |_. Description |_. Location |_. Example |
-|limit|integer (default 100)|Maximum number of containers to return.|query||
-|order|string|Order in which to return matching containers.|query||
-|filters|array|Conditions for filtering containers.|query||
+See "common resource list method.":{{site.baseurl}}/api/methods.html#index
 
 See the create method documentation for more information about Container-specific filters.
 
-h2. update
+h3. update
 
 Update attributes of an existing Container.
 
@@ -67,7 +118,7 @@ table(table table-bordered table-condensed).
 {background:#ccffcc}.|uuid|string|The UUID of the Container in question.|path||
 |container|object||query||
 
-h2. auth
+h3. auth
 
 Get the api_client_authorization record indicated by this container's auth_uuid, which belongs to the container's locked_by_uuid.
 
index cd9633db427aa1807d4a600f6533225f543e4b34..7a15d20d5aaf3d5b1ff63aedf9f958d6e91efc3e 100644 (file)
@@ -3,18 +3,35 @@ layout: default
 navsection: api
 navmenu: API Methods
 title: "groups"
-
 ...
 
+API endpoint base: @https://{{ site.arvados_api_host }}/arvados/v1/groups@
 
-See "REST methods for working with Arvados resources":{{site.baseurl}}/api/methods.html
+Object type: @j7d0g@
 
-API endpoint base: @https://{{ site.arvados_api_host }}/arvados/v1/groups@
+Example UUID: @zzzzz-j7d0g-0123456789abcde@
 
-Required arguments are displayed in %{background:#ccffcc}green%.
+h2. Resource
+
+Groups provides a way to apply the same permissions to a set of Arvados objects.  See "permission model":{{site.baseurl}}/api/permission-model.html for details.
+
+Each Group has, in addition to the "Common resource fields":{{site.baseurl}}/api/resources.html:
+
+table(table table-bordered table-condensed).
+|_. Attribute|_. Type|_. Description|_. Example|
+|name|string|||
+|group_class|string|Type of group. This does not affect behavior, but determines how the group is presented in the user interface. For example, @project@ indicates that the group should be displayed by Workbench and arv-mount as a project for organizing and naming objects.|@"project"@
+null|
+|description|text|||
+|writable_by|array|List of UUID strings identifying Users and other Groups that have write permission for this Group.  Only users who are allowed to administer the Group will receive a full list.  Other users will receive a partial list that includes the Group's owner_uuid and (if applicable) their own user UUID.||
 
+h2. Methods
 
-h2. contents
+See "Common resource methods":{{site.baseurl}}/api/methods.html for more information about @create@, @delete@, @get@, @list@, and @update@.
+
+Required arguments are displayed in %{background:#ccffcc}green%.
+
+h3. contents
 
 Retrieve a list of items owned by the group.
 
@@ -31,7 +48,7 @@ Note: Because adding access tokens to manifests can be computationally expensive
 
 Note: Use filters with the attribute format @<item type>.<field name>@ to filter items of a specific type. For example: @["pipeline_instances.state", "=", "Complete"]@ to filter @pipeline_instances@ where @state@ is @Complete@. All other types of items owned by this group will be unimpacted by this filter and will still be included.
 
-h2. create
+h3. create
 
 Create a new Group.
 
@@ -41,7 +58,7 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 |group|object||query||
 
-h2. delete
+h3. delete
 
 Delete an existing Group.
 
@@ -51,7 +68,7 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 {background:#ccffcc}.|uuid|string|The UUID of the Group in question.|path||
 
-h2. get
+h3. get
 
 Gets a Group's metadata by UUID.
 
@@ -61,19 +78,13 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 {background:#ccffcc}.|uuid|string|The UUID of the Group in question.|path||
 
-h2. list
+h3. list
 
 List groups.
 
-Arguments:
-
-table(table table-bordered table-condensed).
-|_. Argument |_. Type |_. Description |_. Location |_. Example |
-|limit|integer (default 100)|Maximum number of groups to return.|query||
-|order|string|Order in which to return matching groups.|query||
-|filters|array|Conditions for filtering groups.|query||
+See "common resource list method.":{{site.baseurl}}/api/methods.html#index
 
-h2. show
+h3. show
 
 show groups
 
@@ -83,7 +94,7 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 {background:#ccffcc}.|uuid|string||path||
 
-h2. update
+h3. update
 
 Update attributes of an existing Group.
 
index 1d8c13e9dc41fe86c5f93f3dba78264f82e123c2..c4f2fd6215106aefd07d359c3bbebe7c3e709fdc 100644 (file)
@@ -6,14 +6,29 @@ title: "humans"
 
 ...
 
-See "REST methods for working with Arvados resources":{{site.baseurl}}/api/methods.html
-
 API endpoint base: @https://{{ site.arvados_api_host }}/arvados/v1/humans@
 
-Required arguments are displayed in %{background:#ccffcc}green%.
+Object type: @7a9it@
+
+Example UUID: @zzzzz-7a9it-0123456789abcde@
+
+h2. Resource
+
+A metadata record that may be used to represent a human subject.
+
+Each Human has, in addition to the "Common resource fields":{{site.baseurl}}/api/resources.html:
+
+table(table table-bordered table-condensed).
+|_. Attribute|_. Type|_. Description|_. Example|
+|properties|hash|||
 
+h2. Methods
 
-h2. create
+See "Common resource methods":{{site.baseurl}}/api/methods.html for more information about @create@, @delete@, @get@, @list@, and @update@.
+
+Required arguments are displayed in %{background:#ccffcc}green%.
+
+h3. create
 
 Create a new Human.
 
@@ -23,7 +38,7 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 |human|object||query||
 
-h2. delete
+h3. delete
 
 Delete an existing Human.
 
@@ -33,7 +48,7 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 {background:#ccffcc}.|uuid|string|The UUID of the Human in question.|path||
 
-h2. get
+h3. get
 
 Gets a Human's metadata by UUID.
 
@@ -43,19 +58,13 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 {background:#ccffcc}.|uuid|string|The UUID of the Human in question.|path||
 
-h2. list
+h3. list
 
 List humans.
 
-Arguments:
-
-table(table table-bordered table-condensed).
-|_. Argument |_. Type |_. Description |_. Location |_. Example |
-|limit|integer (default 100)|Maximum number of humans to return.|query||
-|order|string|Order in which to return matching humans.|query||
-|filters|array|Conditions for filtering humans.|query||
+See "common resource list method.":{{site.baseurl}}/api/methods.html#index
 
-h2. update
+h3. update
 
 Update attributes of an existing Human.
 
index 7b040d85b3c0b41841d50f99108257df72bcde62..4969abf9efab0eb35739c2c779060f5e27d63f7c 100644 (file)
@@ -6,14 +6,45 @@ title: "job_tasks"
 
 ...
 
-See "REST methods for working with Arvados resources":{{site.baseurl}}/api/methods.html
-
 API endpoint base: @https://{{ site.arvados_api_host }}/arvados/v1/job_tasks@
 
-Required arguments are displayed in %{background:#ccffcc}green%.
+Object type: @ot0gb@
+
+Example UUID: @zzzzz-ot0gb-0123456789abcde@
+
+h2. Resource
+
+Deprecated.
 
+A job task is a individually scheduled unit of work executed as part of an overall job.
 
-h2. create
+Each JobTask has, in addition to the "Common resource fields":{{site.baseurl}}/api/resources.html:
+
+table(table table-bordered table-condensed).
+|_. Attribute|_. Type|_. Description|_. Example|
+|sequence|integer|Execution sequence.
+A step cannot be run until all steps with lower sequence numbers have completed.
+Job steps with the same sequence number can be run in any order.||
+|parameters|hash|||
+|output|text|||
+|progress|float|||
+|success|boolean|Is null if the task has neither completed successfully nor failed permanently.||
+
+The following attributes should not be updated by anyone other than the job manager:
+
+table(table table-bordered table-condensed).
+|_. Attribute|_. Type|_. Description|_. Notes|
+|qsequence|integer|Order of arrival|0-based|
+|job_uuid|string|||
+|created_by_job_task_uuid|string|||
+
+h2. Methods
+
+See "Common resource methods":{{site.baseurl}}/api/methods.html for more information about @create@, @delete@, @get@, @list@, and @update@.
+
+Required arguments are displayed in %{background:#ccffcc}green%.
+
+h3. create
 
 Create a new JobTask.
 
@@ -23,7 +54,7 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 |job_task|object||query||
 
-h2. delete
+h3. delete
 
 Delete an existing JobTask.
 
@@ -33,7 +64,7 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 {background:#ccffcc}.|uuid|string|The UUID of the JobTask in question.|path||
 
-h2. get
+h3. get
 
 Gets a JobTask's metadata by UUID.
 
@@ -43,19 +74,13 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 {background:#ccffcc}.|uuid|string|The UUID of the JobTask in question.|path||
 
-h2. list
+h3. list
 
 List job_tasks.
 
-Arguments:
-
-table(table table-bordered table-condensed).
-|_. Argument |_. Type |_. Description |_. Location |_. Example |
-|limit|integer (default 100)|Maximum number of job_tasks to return.|query||
-|order|string|Order in which to return matching job_tasks.|query||
-|filters|array|Conditions for filtering job_tasks.|query||
+See "common resource list method.":{{site.baseurl}}/api/methods.html#index
 
-h2. update
+h3. update
 
 Update attributes of an existing JobTask.
 
index 90a3c4c72216136909ca6fe1f5c074d5298b440f..01776ab17895f005f28e0f849eec15cf96545151 100644 (file)
@@ -6,13 +6,76 @@ title: "jobs"
 
 ...
 
-See "REST methods for working with Arvados resources":{{site.baseurl}}/api/methods.html
-
 API endpoint base: @https://{{ site.arvados_api_host }}/arvados/v1/jobs@
 
+Object type: @8i9sb@
+
+Example UUID: @zzzzz-8i9sb-0123456789abcde@
+
+h2. Resource
+
+Deprecated.
+
+A job describes a work order to be executed by the Arvados cluster.
+
+Each job has, in addition to the "Common resource fields":{{site.baseurl}}/api/resources.html:
+
+table(table table-bordered table-condensed).
+|_. Attribute|_. Type|_. Description|_. Notes|
+|script|string|The filename of the job script.|This program will be invoked by Crunch for each job task. It is given as a path to an executable file, relative to the @/crunch_scripts@ directory in the Git tree specified by the _repository_ and _script_version_ attributes.|
+|script_parameters|hash|The input parameters for the job.|Conventionally, one of the parameters is called @"input"@. Typically, some parameter values are collection UUIDs. Ultimately, though, the significance of parameters is left entirely up to the script itself.|
+|repository|string|Git repository name or URL.|Source of the repository where the given script_version is to be found. This can be given as the name of a locally hosted repository, or as a publicly accessible URL starting with @git://@, @http://@, or @https://@.
+Examples:
+@yourusername/yourrepo@
+@https://github.com/curoverse/arvados.git@|
+|script_version|string|Git commit|During a **create** transaction, this is the Git branch, tag, or hash supplied by the client. Before the job starts, Arvados updates it to the full 40-character SHA-1 hash of the commit used by the job.
+See "Specifying Git versions":#script_version below for more detail about acceptable ways to specify a commit.|
+|cancelled_by_client_uuid|string|API client ID|Is null if job has not been cancelled|
+|cancelled_by_user_uuid|string|Authenticated user ID|Is null if job has not been cancelled|
+|cancelled_at|datetime|When job was cancelled|Is null if job has not been cancelled|
+|started_at|datetime|When job started running|Is null if job has not [yet] started|
+|finished_at|datetime|When job finished running|Is null if job has not [yet] finished|
+|running|boolean|Whether the job is running||
+|success|boolean|Whether the job indicated successful completion|Is null if job has not finished|
+|is_locked_by_uuid|string|UUID of the user who has locked this job|Is null if job is not locked. The system user locks the job when starting the job, in order to prevent job attributes from being altered.|
+|node_uuids|array|List of UUID strings for node objects that have been assigned to this job||
+|log|string|Collection UUID|Is null if the job has not finished. After the job runs, the given collection contains a text file with log messages provided by the @arv-crunch-job@ task scheduler as well as the standard error streams provided by the task processes.|
+|tasks_summary|hash|Summary of task completion states.|Example: @{"done":0,"running":4,"todo":2,"failed":0}@|
+|output|string|Collection UUID|Is null if the job has not finished.|
+|nondeterministic|boolean|The job is expected to produce different results if run more than once.|If true, this job will not be considered as a candidate for automatic re-use when submitting subsequent identical jobs.|
+|submit_id|string|Unique ID provided by client when job was submitted|Optional. This can be used by a client to make the "jobs.create":{{site.baseurl}}/api/methods/jobs.html#create method idempotent.|
+|priority|string|||
+|arvados_sdk_version|string|Git commit hash that specifies the SDK version to use from the Arvados repository|This is set by searching the Arvados repository for a match for the arvados_sdk_version runtime constraint.|
+|docker_image_locator|string|Portable data hash of the collection that contains the Docker image to use|This is set by searching readable collections for a match for the docker_image runtime constraint.|
+|runtime_constraints|hash|Constraints that must be satisfied by the job/task scheduler in order to run the job.|See below.|
+|components|hash|Name and uuid pairs representing the child work units of this job. The uuids can be of different object types.|Example components hash: @{"name1": "zzzzz-8i9sb-xyz...", "name2": "zzzzz-d1hrv-xyz...",}@|
+
+h3(#script_version). Specifying Git versions
+
+The script_version attribute and arvados_sdk_version runtime constraint are typically given as a branch, tag, or commit hash, but there are many more ways to specify a Git commit. The "specifying revisions" section of the "gitrevisions manual page":http://git-scm.com/docs/gitrevisions.html has a definitive list. Arvados accepts Git versions in any format listed there that names a single commit (not a tree, a blob, or a range of commits). However, some kinds of names can be expected to resolve differently in Arvados than they do in your local repository. For example, <code>HEAD@{1}</code> refers to the local reflog, and @origin/master@ typically refers to a remote branch: neither is likely to work as desired if given as a Git version.
+
+h3. Runtime constraints
+
+table(table table-bordered table-condensed).
+|_. Key|_. Type|_. Description|_. Implemented|
+|arvados_sdk_version|string|The Git version of the SDKs to use from the Arvados git repository.  See "Specifying Git versions":#script_version for more detail about acceptable ways to specify a commit.  If you use this, you must also specify a @docker_image@ constraint (see below).  In order to install the Python SDK successfully, Crunch must be able to find and run virtualenv inside the container.|&#10003;|
+|docker_image|string|The Docker image that this Job needs to run.  If specified, Crunch will create a Docker container from this image, and run the Job's script inside that.  The Keep mount and work directories will be available as volumes inside this container.  The image must be uploaded to Arvados using @arv keep docker@.  You may specify the image in any format that Docker accepts, such as @arvados/jobs@, @debian:latest@, or the Docker image id.  Alternatively, you may specify the portable data hash of the image Collection.|&#10003;|
+|min_nodes|integer||&#10003;|
+|max_nodes|integer|||
+|min_cores_per_node|integer|Require that each node assigned to this Job have the specified number of CPU cores|&#10003;|
+|min_ram_mb_per_node|integer|Require that each node assigned to this Job have the specified amount of real memory (in MiB)|&#10003;|
+|min_scratch_mb_per_node|integer|Require that each node assigned to this Job have the specified amount of scratch storage available (in MiB)|&#10003;|
+|max_tasks_per_node|integer|Maximum simultaneous tasks on a single node|&#10003;|
+|keep_cache_mb_per_task|integer|Size of file data buffer for per-task Keep directory ($TASK_KEEPMOUNT), in MiB.  Default is 256 MiB.  Increase this to reduce cache thrashing in situtations such as accessing multiple large (64+ MiB) files at the same time, or accessing different parts of a large file at the same time.|&#10003;|
+|min_ram_per_task|integer|Minimum real memory (KiB) per task||
+
+h2. Methods
+
+See "Common resource methods":{{site.baseurl}}/api/methods.html for more information about @create@, @delete@, @get@, @list@, and @update@.
+
 Required arguments are displayed in %{background:#ccffcc}green%.
 
-h2. cancel
+h3. cancel
 
 Cancel a job that is queued or running.
 
@@ -22,7 +85,7 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 {background:#ccffcc}.|uuid|string||path||
 
-h2(#create). create
+h3(#create). create
 
 Create a new Job.
 
@@ -30,7 +93,7 @@ Arguments:
 
 table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
-{background:#ccffcc}.|job|object|See "Job resource":{{site.baseurl}}/api/schema/Job.html|request body||
+{background:#ccffcc}.|job|object|Job resource|request body||
 |minimum_script_version |string     |Git branch, tag, or commit hash specifying the minimum acceptable script version (earliest ancestor) to consider when deciding whether to re-use a past job.[1]|query|@"c3e86c9"@|
 |exclude_script_versions|array of strings|Git commit branches, tags, or hashes to exclude when deciding whether to re-use a past job.|query|@["8f03c71","8f03c71"]@
 @["badtag1","badtag2"]@|
@@ -39,9 +102,9 @@ table(table table-bordered table-condensed).
 
 When a job is submitted to the queue using the **create** method, the @script_version@ attribute is updated to a full 40-character Git commit hash based on the current content of the specified repository. If @script_version@ cannot be resolved, the job submission is rejected.
 
-fn1. See the "note about specifying Git commits on the Job resource page":{{site.baseurl}}/api/schema/Job.html#script_version for more detail.
+fn1. See the "note about specifying Git commits":#script_version for more detail.
 
-h3. Specialized filters
+h4. Specialized filters
 
 Special filter operations are available for specific Job columns.
 
@@ -53,7 +116,7 @@ Special filter operations are available for specific Job columns.
 
 * @docker_image_locator@ @not in docker@ @SEARCH@<br>Negate the @in docker@ filter.
 
-h3. Reusing jobs
+h4. Reusing jobs
 
 Because Arvados records the exact version of the script, input parameters, and runtime environment that was used to run the job, if the script is deterministic (meaning that the same code version is guaranteed to produce the same outputs from the same inputs) then it is possible to re-use the results of past jobs, and avoid re-running the computation to save time.  Arvados uses the following algorithm to determine if a past job can be re-used:
 
@@ -68,7 +131,7 @@ notextile. <div class="spaced-out">
 
 </div>
 
-h3. Examples
+h4. Examples
 
 Run the script "crunch_scripts/hash.py" in the repository "you" using the "master" commit.  Arvados should re-use a previous job if the script_version of the previous job is the same as the current "master" commit. This works irrespective of whether the previous job was submitted using the name "master", a different branch name or tag indicating the same commit, a SHA-1 commit hash, etc.
 
@@ -156,7 +219,7 @@ Run the script "crunch_scripts/monte-carlo.py" in the repository "you/you" using
 }
 </pre></notextile>
 
-h2. delete
+h3. delete
 
 Delete an existing Job.
 
@@ -166,7 +229,7 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 {background:#ccffcc}.|uuid|string|The UUID of the Job in question.|path||
 
-h2. get
+h3. get
 
 Gets a Job's metadata by UUID.
 
@@ -176,21 +239,15 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 {background:#ccffcc}.|uuid|string|The UUID of the Job in question.|path||
 
-h2. list
+h3. list
 
 List jobs.
 
-Arguments:
-
-table(table table-bordered table-condensed).
-|_. Argument |_. Type |_. Description |_. Location |_. Example |
-|limit|integer (default 100)|Maximum number of jobs to return.|query||
-|order|string|Order in which to return matching jobs.|query||
-|filters|array|Conditions for filtering jobs.|query||
+See "common resource list method.":{{site.baseurl}}/api/methods.html#index
 
 See the create method documentation for more information about Job-specific filters.
 
-h2. log_tail_follow
+h3. log_tail_follow
 
 log_tail_follow jobs
 
@@ -201,7 +258,7 @@ table(table table-bordered table-condensed).
 {background:#ccffcc}.|uuid|string||path||
 |buffer_size|integer (default 8192)||query||
 
-h2. queue
+h3. queue
 
 Get the current job queue.
 
@@ -214,7 +271,7 @@ table(table table-bordered table-condensed).
 
 This method is equivalent to the "list method":#list, except that the results are restricted to queued jobs (i.e., jobs that have not yet been started or cancelled) and order defaults to queue priority.
 
-h2. update
+h3. update
 
 Update attributes of an existing Job.
 
index 5179ccc8b216e0fceca8a838effd92730bece093..421e942faa123f3602947fee7a50b48e11ac3f01 100644 (file)
@@ -2,17 +2,43 @@
 layout: default
 navsection: api
 navmenu: API Methods
-title: "keep_disks"
+title: "keep_disks (deprecated)"
 
 ...
 
-See "REST methods for working with Arvados resources":{{site.baseurl}}/api/methods.html
-
 API endpoint base: @https://{{ site.arvados_api_host }}/arvados/v1/keep_disks@
 
+Object type: @penuu@
+
+Example UUID: @zzzzz-penuu-0123456789abcde@
+
+h2. Resource
+
+Obsoleted by "keep_services":{{site.baseurl}}/api/methods/keep_services.html
+
+Each KeepDisk has, in addition to the "Common resource fields":{{site.baseurl}}/api/resources.html:
+
+table(table table-bordered table-condensed).
+|_. Attribute|_. Type|_. Description|_. Example|
+|ping_secret|string|||
+|node_uuid|string|||
+|filesystem_uuid|string|||
+|bytes_total|integer|||
+|bytes_free|integer|||
+|is_readable|boolean|||
+|is_writable|boolean|||
+|last_read_at|datetime|||
+|last_write_at|datetime|||
+|last_ping_at|datetime|||
+|keep_service_uuid|string|||
+
+h2. Methods
+
+See "Common resource methods":{{site.baseurl}}/api/methods.html for more information about @create@, @delete@, @get@, @list@, and @update@.
+
 Required arguments are displayed in %{background:#ccffcc}green%.
 
-h2. create
+h3. create
 
 Create a new KeepDisk.
 
@@ -22,7 +48,7 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 |keep_disk|object||query||
 
-h2. delete
+h3. delete
 
 Delete an existing KeepDisk.
 
@@ -32,7 +58,7 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 {background:#ccffcc}.|uuid|string|The UUID of the KeepDisk in question.|path||
 
-h2. get
+h3. get
 
 Gets a KeepDisk's metadata by UUID.
 
@@ -42,19 +68,13 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 {background:#ccffcc}.|uuid|string|The UUID of the KeepDisk in question.|path||
 
-h2. list
+h3. list
 
 List keep_disks.
 
-Arguments:
-
-table(table table-bordered table-condensed).
-|_. Argument |_. Type |_. Description |_. Location |_. Example |
-|limit|integer (default 100)|Maximum number of keep_disks to return.|query||
-|order|string|Order in which to return matching keep_disks.|query||
-|filters|array|Conditions for filtering keep_disks.|query||
+See "common resource list method.":{{site.baseurl}}/api/methods.html#index
 
-h2. ping
+h3. ping
 
 ping keep_disks
 
@@ -70,7 +90,7 @@ table(table table-bordered table-condensed).
 |service_host|string||query||
 |uuid|string||query||
 
-h2. update
+h3. update
 
 Update attributes of an existing KeepDisk.
 
index da6818b92da3bae0e87b1999fe6637ea392ea6d2..dadf783324cfffd3bb0f3edf1a46cb5ab135f048 100644 (file)
@@ -6,22 +6,36 @@ title: "keep_services"
 
 ...
 
-See "REST methods for working with Arvados resources":{{site.baseurl}}/api/methods.html
-
 API endpoint base: @https://{{ site.arvados_api_host }}/arvados/v1/keep_services@
 
-Required arguments are displayed in %{background:#ccffcc}green%.
+Object type: @bi6l4@
+
+Example UUID: @zzzzz-bi6l4-0123456789abcde@
+
+h2. Resource
+
+The keep_services resource keep clients to discover storage servers and proxies available on the cluster for persistent storage and retrieval of keep blocks.
+
+Each KeepService has, in addition to the "Common resource fields":{{site.baseurl}}/api/resources.html:
+
+table(table table-bordered table-condensed).
+|_. Attribute|_. Type|_. Description|_. Example|
+|service_host|string|hostname of the server||
+|service_port|integer|TCP port of the service||
+|service_ssl_flag|boolean|if the server uses SSL||
+|service_type|string|The service type, one of "disk", "blob" (cloud object store) or "proxy" (keepproxy)||
 
-h2. accessible
+h2. Methods
 
-Get a list of keep services that are accessible to the requesting client.  This
-is context-sensitive, for example providing the list of actual Keep servers
-when inside the cluster, but providing a proxy service if client contacts
-Arvados from outside the cluster.
+See "Common resource methods":{{site.baseurl}}/api/methods.html for more information about @create@, @delete@, @get@, @list@, and @update@.
 
-Takes no arguments.
+Required arguments are displayed in %{background:#ccffcc}green%.
+
+h3. accessible
 
-h2. create
+Get a list of keep services that are accessible to the requesting client.  Unlike @list@, this is context-sensitive based on the requester, for example providing the list of actual Keep servers when inside the cluster, but providing a proxy service if client contacts Arvados from outside the cluster.
+
+h3. create
 
 Create a new KeepService.
 
@@ -29,9 +43,9 @@ Arguments:
 
 table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
-|keep_disk|object||query||
+|keep_service|object||query||
 
-h2. delete
+h3. delete
 
 Delete an existing KeepService.
 
@@ -41,7 +55,7 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 {background:#ccffcc}.|uuid|string|The UUID of the KeepService in question.|path||
 
-h2. get
+h3. get
 
 Gets a KeepService's metadata by UUID.
 
@@ -51,19 +65,13 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 {background:#ccffcc}.|uuid|string|The UUID of the KeepService in question.|path||
 
-h2. list
+h3. list
 
 List keep_services.
 
-Arguments:
-
-table(table table-bordered table-condensed).
-|_. Argument |_. Type |_. Description |_. Location |_. Example |
-|limit|integer (default 100)|Maximum number of keep_services to return.|query||
-|order|string|Order in which to return matching keep_services.|query||
-|filters|array|Conditions for filtering keep_services.|query||
+See "common resource list method.":{{site.baseurl}}/api/methods.html#index
 
-h2. update
+h3. update
 
 Update attributes of an existing KeepService.
 
index 3c0bdf346d033a9cad6bb0a9adcb8cd3dedde6c9..d94a055150bc2ca6cd55c5c495db42caaf4ce6a9 100644 (file)
@@ -6,13 +6,50 @@ title: "links"
 
 ...
 
-See "REST methods for working with Arvados resources":{{site.baseurl}}/api/methods.html
-
 API endpoint base: @https://{{ site.arvados_api_host }}/arvados/v1/links@
 
+Object type: @o0j2j@
+
+Example UUID: @zzzzz-o0j2j-0123456789abcde@
+
+h2. Resource
+
+Links are an extensible way to describe relationships between Arvados objects and metadata about individual objects.
+
+Each link has, in addition to the "Common resource fields":{{site.baseurl}}/api/resources.html:
+
+table(table table-bordered table-condensed).
+|_. Attribute|_. Type|_. Description|
+|head_uuid|string|The object being described or acted on.|
+|tail_uuid|string|The origin or actor in the description or action (may be null).|
+|link_class|string|Type of link|
+|name|string|Primary value of the link.|
+|properties|hash|Additional information, expressed as a key&rarr;value hash. Key: string. Value: string, number, array, or hash.|
+
+h2. Link classes
+
+Some classes are pre-defined by convention and have standard meanings attached to names.
+
+h3. permission
+
+See "permission links":{{site.baseurl}}/api/permission-model.html#links section of the permission model.
+
+h3. tag
+
+A **tag** link describes an object using an unparsed plain text string. Tags can be used to annotate objects that are not editable, like collections and objects shared as read-only.
+
+table(table table-bordered table-condensed).
+|_. tail_type&rarr;head_type|_. name&rarr;head_uuid {properties}|
+|&rarr;Collection           | _tag name_ &rarr; _collection uuid_|
+|&rarr;Job                  | _tag name_ &rarr; _job uuid_|
+
+h2. Methods
+
+See "Common resource methods":{{site.baseurl}}/api/methods.html for more information about @create@, @delete@, @get@, @list@, and @update@.
+
 Required arguments are displayed in %{background:#ccffcc}green%.
 
-h2. create
+h3. create
 
 Create a new Link.
 
@@ -22,7 +59,7 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 |link|object||query||
 
-h2. delete
+h3. delete
 
 Delete an existing Link.
 
@@ -32,7 +69,7 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 {background:#ccffcc}.|uuid|string|The UUID of the Link in question.|path||
 
-h2. get
+h3. get
 
 Gets a Link's metadata by UUID.
 
@@ -42,29 +79,13 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 {background:#ccffcc}.|uuid|string|The UUID of the Link in question.|path||
 
-h2. list
+h3. list
 
 List links.
 
-Arguments:
-
-table(table table-bordered table-condensed).
-|_. Argument |_. Type |_. Description |_. Location |_. Example |
-|limit|integer (default 100)|Maximum number of links to return.|query||
-|order|string|Order in which to return matching links.|query||
-|filters|array|Conditions for filtering links.|query||
-
-h2. render_not_found
-
-render_not_found links
-
-Arguments:
-
-table(table table-bordered table-condensed).
-|_. Argument |_. Type |_. Description |_. Location |_. Example |
-{background:#ccffcc}.|a|string||path||
+See "common resource list method.":{{site.baseurl}}/api/methods.html#index
 
-h2. update
+h3. update
 
 Update attributes of an existing Link.
 
index c5895d78a211dab8170a4b21ffcf66b6c574e33f..e5380d89397b487172510742cf266cd9d67eefb5 100644 (file)
@@ -6,14 +6,39 @@ title: "logs"
 
 ...
 
-See "REST methods for working with Arvados resources":{{site.baseurl}}/api/methods.html
-
 API endpoint base: @https://{{ site.arvados_api_host }}/arvados/v1/logs@
 
-Required arguments are displayed in %{background:#ccffcc}green%.
+Object type: @57u5n@
+
+Example UUID: @zzzzz-57u5n-0123456789abcde@
+
+h2. Resource
+
+Each Log has, in addition to the "Common resource fields":{{site.baseurl}}/api/resources.html:
+
+table(table table-bordered table-condensed).
+|_. Attribute|_. Type|_. Description|_. Example|
+|object_uuid|string|The arvados object that is the subject of the log.||
+|event_at|datetime|||
+|event_type|string|A user-defined category or type for this event.|@LOGIN@|
+|summary|text|||
+|properties|hash|||
+
+h3. Creation
+
+Any user may create Log entries for any event they find useful. User-generated Logs have no intrinsic meaning to other users or to the Arvados system itself; it is up to each user to choose appropriate log event types and summaries for their project.
 
+h3. System Logs
 
-h2. create
+Arvados uses Logs to record creation, deletion, and updates of other Arvados resources.
+
+h2. Methods
+
+See "Common resource methods":{{site.baseurl}}/api/methods.html for more information about @create@, @delete@, @get@, @list@, and @update@.
+
+Required arguments are displayed in %{background:#ccffcc}green%.
+
+h3. create
 
 Create a new log entry.
 
@@ -23,7 +48,7 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 |log|object||query||
 
-h2. delete
+h3. delete
 
 Delete an existing log entry. This method can only be used by privileged (system administrator) users.
 
@@ -33,7 +58,7 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 {background:#ccffcc}.|uuid|string|The UUID of the log entry in question.|path||
 
-h2. get
+h3. get
 
 Retrieve a log entry.
 
@@ -43,19 +68,13 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 {background:#ccffcc}.|uuid|string|The UUID of the log entry in question.|path||
 
-h2. list
+h3. list
 
 List log entries.
 
-Arguments:
-
-table(table table-bordered table-condensed).
-|_. Argument |_. Type |_. Description |_. Location |_. Example |
-|limit|integer (default 100)|Maximum number of log entries to return.|query||
-|order|string|Order in which to return matching log entries.|query||
-|filters|array|Conditions for filtering log entries.|query||
+See "common resource list method.":{{site.baseurl}}/api/methods.html#index
 
-h2. update
+h3. update
 
 Update attributes of an existing log entry. This method can only be used by privileged (system administrator) users.
 
index 7aa5896ea5314e3535182fbe5ba194902d86bc43..aa21237bfa4004135dff544aa4ab39ec551222f6 100644 (file)
@@ -6,14 +6,36 @@ title: "nodes"
 
 ...
 
-See "REST methods for working with Arvados resources":{{site.baseurl}}/api/methods.html
-
 API endpoint base: @https://{{ site.arvados_api_host }}/arvados/v1/nodes@
 
-Required arguments are displayed in %{background:#ccffcc}green%.
+Object type: @7ekkf@
+
+Example UUID: @zzzzz-7ekkf-0123456789abcde@
+
+h2. Resource
+
+Node resources list compute nodes on which Crunch may schedule work.
+
+Each Node has, in addition to the "Common resource fields":{{site.baseurl}}/api/resources.html:
+
+table(table table-bordered table-condensed).
+|_. Attribute|_. Type|_. Description|_. Example|
+|slot_number|integer|||
+|hostname|string|||
+|domain|string|||
+|ip_address|string|||
+|job_uuid|string|The UUID of the job that this node is assigned to work on.  If you do not have permission to read the job, this will be null.||
+|first_ping_at|datetime|||
+|last_ping_at|datetime|||
+|info|hash|||
 
+h2. Methods
 
-h2. create
+See "Common resource methods":{{site.baseurl}}/api/methods.html for more information about @create@, @delete@, @get@, @list@, and @update@.
+
+Required arguments are displayed in %{background:#ccffcc}green%.
+
+h3. create
 
 Create a new Node.
 
@@ -23,7 +45,7 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 {background:#ccffcc}.|node|object||query||
 
-h2. delete
+h3. delete
 
 Delete an existing Node.
 
@@ -33,7 +55,7 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 {background:#ccffcc}.|uuid|string|The UUID of the Node in question.|path||
 
-h2. get
+h3. get
 
 Gets a Node's metadata by UUID.
 
@@ -43,21 +65,15 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 {background:#ccffcc}.|uuid|string|The UUID of the Node in question.|path||
 
-h2. list
+h3. list
 
 List nodes.
 
-Arguments:
-
-table(table table-bordered table-condensed).
-|_. Argument |_. Type |_. Description |_. Location |_. Example |
-|limit|integer (default 100)|Maximum number of nodes to return.|query||
-|order|string|Order in which to return matching nodes.|query||
-|filters|array|Conditions for filtering nodes.|query||
+See "common resource list method.":{{site.baseurl}}/api/methods.html#index
 
-h2. ping
+h3. ping
 
-ping nodes
+Process a ping from a compute node.
 
 Arguments:
 
@@ -66,7 +82,7 @@ table(table table-bordered table-condensed).
 {background:#ccffcc}.|ping_secret|string||query||
 {background:#ccffcc}.|uuid|string||path||
 
-h2. update
+h3. update
 
 Update attributes of an existing Node.
 
index d637a696eb83de397c9bae0b972b64b156e555b3..f4fb774cf48d3bc921877816f9d6ee155e5b9433 100644 (file)
@@ -6,14 +6,34 @@ title: "pipeline_instances"
 
 ...
 
-See "REST methods for working with Arvados resources":{{site.baseurl}}/api/methods.html
-
 API endpoint base: @https://{{ site.arvados_api_host }}/arvados/v1/pipeline_instances@
 
-Required arguments are displayed in %{background:#ccffcc}green%.
+Object type: @d1hrv@
+
+Example UUID: @zzzzz-d1hrv-0123456789abcde@
+
+h2. Resource
+
+Deprecated.  A pipeline instance is a collection of jobs managed by @aravdos-run-pipeline-instance@.
+
+Each PipelineInstance has, in addition to the "Common resource fields":{{site.baseurl}}/api/resources.html:
+
+table(table table-bordered table-condensed).
+|_. Attribute|_. Type|_. Description|_. Example|
+|pipeline_template_uuid|string|The "pipeline template":pipeline_templates.html that this instance was created from.||
+|name|string|||
+|components|hash|||
+|success|boolean|||
+|active|boolean|||
+|properties|Hash|||
 
+h2. Methods
 
-h2. create
+See "Common resource methods":{{site.baseurl}}/api/methods.html for more information about @create@, @delete@, @get@, @list@, and @update@.
+
+Required arguments are displayed in %{background:#ccffcc}green%.
+
+h3. create
 
 Create a new PipelineInstance.
 
@@ -23,7 +43,7 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 |pipeline_instance|object||query||
 
-h2. delete
+h3. delete
 
 Delete an existing PipelineInstance.
 
@@ -33,7 +53,7 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 {background:#ccffcc}.|uuid|string|The UUID of the PipelineInstance in question.|path||
 
-h2. get
+h3. get
 
 Gets a PipelineInstance's metadata by UUID.
 
@@ -43,19 +63,13 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 {background:#ccffcc}.|uuid|string|The UUID of the PipelineInstance in question.|path||
 
-h2. list
+h3. list
 
 List pipeline_instances.
 
-Arguments:
-
-table(table table-bordered table-condensed).
-|_. Argument |_. Type |_. Description |_. Location |_. Example |
-|limit|integer (default 100)|Maximum number of pipeline_instances to return.|query||
-|order|string|Order in which to return matching pipeline_instances.|query||
-|filters|array|Conditions for filtering pipeline_instances.|query||
+See "common resource list method.":{{site.baseurl}}/api/methods.html#index
 
-h2. update
+h3. update
 
 Update attributes of an existing PipelineInstance.
 
index 06684cc6df05ab1f32f17e362cc6b52f8ac2eeae..e8fb6a4c596a59718cae5cc2608476660e6caa8b 100644 (file)
@@ -6,14 +6,172 @@ title: "pipeline_templates"
 
 ...
 
-See "REST methods for working with Arvados resources":{{site.baseurl}}/api/methods.html
-
 API endpoint base: @https://{{ site.arvados_api_host }}/arvados/v1/pipeline_templates@
 
-Required arguments are displayed in %{background:#ccffcc}green%.
+Object type: @p5p6p@
+
+Example UUID: @zzzzz-p5p6p-0123456789abcde@
+
+h2. Resource
+
+Deprecated.  A pipeline template is a collection of jobs that can be instantiated as a pipeline_instance.
+
+Each PipelineTemplate has, in addition to the "Common resource fields":{{site.baseurl}}/api/resources.html:
+
+table(table table-bordered table-condensed).
+|_. Attribute|_. Type|_. Description|_. Example|
+|name|string|||
+|components|hash|||
+
+The pipeline template consists of "name" and "components".
+
+table(table table-bordered table-condensed).
+|_. Attribute    |_. Type |_. Accepted values                           |_. Required|_. Description|
+|name            |string  |any                                          |yes        |The human-readable name of the pipeline template.|
+|components      |object  |JSON object containing job submission objects|yes        |The component jobs that make up the pipeline, with the component name as the key. |
+
+h3. Components
 
+The components field of the pipeline template is a JSON object which describes the individual steps that make up the pipeline.  Each component is an Arvados job submission.  "Parameters for job submissions are described on the job method page.":{{site.baseurl}}/api/methods/jobs.html#create  In addition, a component can have the following parameters:
 
-h2. create
+table(table table-bordered table-condensed).
+|_. Attribute    |_. Type          |_. Accepted values |_. Required|_. Description|
+|output_name     |string or boolean|string or false    |no         |If a string is provided, use this name for the output collection of this component.  If the value is false, do not create a permanent output collection (an temporary intermediate collection will still be created).  If not provided, a default name will be assigned to the output.|
+
+h3. Script parameters
+
+When used in a pipeline, each parameter in the 'script_parameters' attribute of a component job can specify that the input parameter must be supplied by the user, or the input parameter should be linked to the output of another component.  To do this, the value of the parameter should be JSON object containing one of the following attributes:
+
+table(table table-bordered table-condensed).
+|_. Attribute    |_. Type |_. Accepted values                               |_. Description|
+|default         |any     |any                                              |The default value for this parameter.|
+|required        |boolean |true or false                                    |Specifies whether the parameter is required to have a value or not.|
+|dataclass       |string  |One of 'Collection', 'File' [1], 'number', or 'text' |Data type of this parameter.|
+|search_for      |string  |any string                                       |Substring to use as a default search string when choosing inputs.|
+|output_of       |string  |the name of another component in the pipeline    |Specifies that the value of this parameter should be set to the 'output' attribute of the job that corresponds to the specified component.|
+|title           |string  |any string                                       |User friendly title to display when choosing parameter values|
+|description     |string  |any string                                       |Extended text description for describing expected/valid values for the script parameter|
+|link_name       |string  |any string                                       |User friendly name to display for the parameter value instead of the actual parameter value|
+
+The 'output_of' parameter is especially important, as this is how components are actually linked together to form a pipeline.  Component jobs that depend on the output of other components do not run until the parent job completes and has produced output.  If the parent job fails, the entire pipeline fails.
+
+fn1. The 'File' type refers to a specific file within a Keep collection in the form 'collection_hash/filename', for example '887cd41e9c613463eab2f0d885c6dd96+83/bob.txt'.
+
+The 'search_for' parameter is meaningful only when input dataclass of type Collection or File is used. If a value is provided, this will be preloaded into the input data chooser dialog in Workbench. For example, if your input dataclass is a File and you are interested in a certain filename extention, you can preconfigure it in this attribute.
+
+h3. Examples
+
+This is a pipeline named "Filter MD5 hash values" with two components, "do_hash" and "filter".  The "input" script parameter of the "do_hash" component is required to be filled in by the user, and the expected data type is "Collection".  This also specifies that the "input" script parameter of the "filter" component is the output of "do_hash", so "filter" will not run until "do_hash" completes successfully.  When the pipeline runs, past jobs that meet the criteria described above may be substituted for either or both components to avoid redundant computation.
+
+<notextile><pre>
+{
+  "name": "Filter MD5 hash values",
+  "components": {
+    "do_hash": {
+      "script": "hash.py",
+      "repository": "<b>you</b>/<b>you</b>",
+      "script_version": "master",
+      "script_parameters": {
+        "input": {
+          "required": true,
+          "dataclass": "Collection",
+          "search_for": ".fastq.gz",
+          "title":"Please select a fastq file"
+        }
+      },
+    },
+    "filter": {
+      "script": "0-filter.py",
+      "repository": "<b>you</b>/<b>you</b>",
+      "script_version": "master",
+      "script_parameters": {
+        "input": {
+          "output_of": "do_hash"
+        }
+      },
+    }
+  }
+}
+</pre></notextile>
+
+This pipeline consists of three components.  The components "thing1" and "thing2" both depend on "cat_in_the_hat".  Once the "cat_in_the_hat" job is complete, both "thing1" and "thing2" can run in parallel, because they do not depend on each other.
+
+<notextile><pre>
+{
+  "name": "Wreck the house",
+  "components": {
+    "cat_in_the_hat": {
+      "script": "cat.py",
+      "repository": "<b>you</b>/<b>you</b>",
+      "script_version": "master",
+      "script_parameters": { }
+    },
+    "thing1": {
+      "script": "thing1.py",
+      "repository": "<b>you</b>/<b>you</b>",
+      "script_version": "master",
+      "script_parameters": {
+        "input": {
+          "output_of": "cat_in_the_hat"
+        }
+      },
+    },
+    "thing2": {
+      "script": "thing2.py",
+      "repository": "<b>you</b>/<b>you</b>",
+      "script_version": "master",
+      "script_parameters": {
+        "input": {
+          "output_of": "cat_in_the_hat"
+        }
+      },
+    },
+  }
+}
+</pre></notextile>
+
+This pipeline consists of three components.  The component "cleanup" depends on "thing1" and "thing2".  Both "thing1" and "thing2" are started immediately and can run in parallel, because they do not depend on each other, but "cleanup" cannot begin until both "thing1" and "thing2" have completed.
+
+<notextile><pre>
+{
+  "name": "Clean the house",
+  "components": {
+    "thing1": {
+      "script": "thing1.py",
+      "repository": "<b>you</b>/<b>you</b>",
+      "script_version": "master",
+      "script_parameters": { }
+    },
+    "thing2": {
+      "script": "thing2.py",
+      "repository": "<b>you</b>/<b>you</b>",
+      "script_version": "master",
+      "script_parameters": { }
+    },
+    "cleanup": {
+      "script": "cleanup.py",
+      "repository": "<b>you</b>/<b>you</b>",
+      "script_version": "master",
+      "script_parameters": {
+        "mess1": {
+          "output_of": "thing1"
+        },
+        "mess2": {
+          "output_of": "thing2"
+        }
+      }
+    }
+  }
+}
+</pre></notextile>
+
+h2. Methods
+
+See "Common resource methods":{{site.baseurl}}/api/methods.html for more information about @create@, @delete@, @get@, @list@, and @update@.
+
+Required arguments are displayed in %{background:#ccffcc}green%.
+
+h3. create
 
 Create a new PipelineTemplate.
 
@@ -23,7 +181,7 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 |pipeline_template|object||query||
 
-h2. delete
+h3. delete
 
 Delete an existing PipelineTemplate.
 
@@ -33,7 +191,7 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 {background:#ccffcc}.|uuid|string|The UUID of the PipelineTemplate in question.|path||
 
-h2. get
+h3. get
 
 Gets a PipelineTemplate's metadata by UUID.
 
@@ -43,19 +201,13 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 {background:#ccffcc}.|uuid|string|The UUID of the PipelineTemplate in question.|path||
 
-h2. list
+h3. list
 
 List pipeline_templates.
 
-Arguments:
-
-table(table table-bordered table-condensed).
-|_. Argument |_. Type |_. Description |_. Location |_. Example |
-|limit|integer (default 100)|Maximum number of pipeline_templates to return.|query||
-|order|string|Order in which to return matching pipeline_templates.|query||
-|filters|array|Conditions for filtering pipeline_templates.|query||
+See "common resource list method.":{{site.baseurl}}/api/methods.html#index
 
-h2. update
+h3. update
 
 Update attributes of an existing PipelineTemplate.
 
index 7bd8dd9d4216c2049083a2a0be9c62ec57b6c869..80dfc0b49c1877896aea0741e5b99cc2089854be 100644 (file)
@@ -6,14 +6,33 @@ title: "repositories"
 
 ...
 
-See "REST methods for working with Arvados resources":{{site.baseurl}}/api/methods.html
-
 API endpoint base: @https://{{ site.arvados_api_host }}/arvados/v1/repositories@
 
-Required arguments are displayed in %{background:#ccffcc}green%.
+Object type: @s0uqq@
+
+Example UUID: @zzzzz-s0uqq-0123456789abcde@
+
+h2. Resource
+
+The repositories resource lists git repositories managed by Arvados.
+
+Each Repository has, in addition to the "Common resource fields":{{site.baseurl}}/api/resources.html:
+
+table(table table-bordered table-condensed).
+|_. Attribute|_. Type|_. Description|_. Example|
+|name|string|The name of the repository on disk.  Repository names must begin with a letter and contain only alphanumerics.  Unless the repository is owned by the system user, the name must begin with the owner's username, then be separated from the base repository name with @/@.  You may not create a repository that is owned by a user without a username.|@username/project1@|
+|clone_urls|array|URLs from which the repository can be cloned. Read-only.|@["git@git.zzzzz.arvadosapi.com:foo/bar.git",
+ "https://git.zzzzz.arvadosapi.com/foo/bar.git"]@|
+|fetch_url|string|URL suggested as a fetch-url in git config. Deprecated. Read-only.||
+|push_url|string|URL suggested as a push-url in git config. Deprecated. Read-only.||
 
+h2. Methods
 
-h2. create
+See "Common resource methods":{{site.baseurl}}/api/methods.html for more information about @create@, @delete@, @get@, @list@, and @update@.
+
+Required arguments are displayed in %{background:#ccffcc}green%.
+
+h3. create
 
 Create a new Repository.
 
@@ -23,7 +42,7 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 |repository|object||query||
 
-h2. delete
+h3. delete
 
 Delete an existing Repository.
 
@@ -33,7 +52,7 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 {background:#ccffcc}.|uuid|string|The UUID of the Repository in question.|path||
 
-h2. get
+h3. get
 
 Gets a Repository's metadata by UUID.
 
@@ -43,7 +62,7 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 {background:#ccffcc}.|uuid|string|The UUID of the Repository in question.|path||
 
-h2. get_all_permissions
+h3. get_all_permissions
 
 get_all_permissions repositories
 
@@ -52,19 +71,13 @@ Arguments:
 table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 
-h2. list
+h3. list
 
 List repositories.
 
-Arguments:
-
-table(table table-bordered table-condensed).
-|_. Argument |_. Type |_. Description |_. Location |_. Example |
-|limit|integer (default 100)|Maximum number of repositories to return.|query||
-|order|string|Order in which to return matching repositories.|query||
-|filters|array|Conditions for filtering repositories.|query||
+See "common resource list method.":{{site.baseurl}}/api/methods.html#index
 
-h2. update
+h3. update
 
 Update attributes of an existing Repository.
 
index 6737c9bcae8b2ebb08ec54b7445065d2d011110a..652497d3f958096fca494b257bedcbabfa113bf5 100644 (file)
@@ -3,17 +3,32 @@ layout: default
 navsection: api
 navmenu: API Methods
 title: "specimens"
-
 ...
 
-See "REST methods for working with Arvados resources":{{site.baseurl}}/api/methods.html
-
 API endpoint base: @https://{{ site.arvados_api_host }}/arvados/v1/specimens@
 
-Required arguments are displayed in %{background:#ccffcc}green%.
+Object type: @j58dm@
+
+Example UUID: @zzzzz-j58dm-0123456789abcde@
+
+h2. Resource
+
+A metadata record that may be used to represent a biological specimen.
+
+Each Specimen has, in addition to the "Common resource fields":{{site.baseurl}}/api/resources.html:
+
+table(table table-bordered table-condensed).
+|_. Attribute|_. Type|_. Description|_. Example|
+|material|string|||
+|properties|hash|||
+
+h2. Methods
 
+See "Common resource methods":{{site.baseurl}}/api/methods.html for more information about @create@, @delete@, @get@, @list@, and @update@.
+
+Required arguments are displayed in %{background:#ccffcc}green%.
 
-h2. create
+h3. create
 
 Create a new Specimen.
 
@@ -23,7 +38,7 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 |specimen|object||query||
 
-h2. delete
+h3. delete
 
 Delete an existing Specimen.
 
@@ -33,7 +48,7 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 {background:#ccffcc}.|uuid|string|The UUID of the Specimen in question.|path||
 
-h2. get
+h3. get
 
 Gets a Specimen's metadata by UUID.
 
@@ -43,19 +58,13 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 {background:#ccffcc}.|uuid|string|The UUID of the Specimen in question.|path||
 
-h2. list
+h3. list
 
 List specimens.
 
-Arguments:
-
-table(table table-bordered table-condensed).
-|_. Argument |_. Type |_. Description |_. Location |_. Example |
-|limit|integer (default 100)|Maximum number of specimens to return.|query||
-|order|string|Order in which to return matching specimens.|query||
-|filters|array|Conditions for filtering specimens.|query||
+See "common resource list method.":{{site.baseurl}}/api/methods.html#index
 
-h2. update
+h3. update
 
 Update attributes of an existing Specimen.
 
index 9b19a0880b30d9f3ca22e9dc411a8df0a5132dfc..3f7d49f69101c045d5287a5d81587343d9dd96cc 100644 (file)
@@ -6,14 +6,30 @@ title: "traits"
 
 ...
 
-See "REST methods for working with Arvados resources":{{site.baseurl}}/api/methods.html
-
 API endpoint base: @https://{{ site.arvados_api_host }}/arvados/v1/traits@
 
-Required arguments are displayed in %{background:#ccffcc}green%.
+Object type: @q1cn2@
+
+Example UUID: @zzzzz-q1cn2-0123456789abcde@
+
+h2. Resource
+
+A metadata record that may be used to represent a genotype or phenotype trait.
+
+Each Trait has, in addition to the "Common resource fields":{{site.baseurl}}/api/resources.html:
+
+table(table table-bordered table-condensed).
+|_. Attribute|_. Type|_. Description|_. Example|
+|name|string|||
+|properties|hash|||
 
+h2. Methods
 
-h2. create
+See "Common resource methods":{{site.baseurl}}/api/methods.html for more information about @create@, @delete@, @get@, @list@, and @update@.
+
+Required arguments are displayed in %{background:#ccffcc}green%.
+
+h3. create
 
 Create a new Trait.
 
@@ -23,7 +39,7 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 |trait|object||query||
 
-h2. delete
+h3. delete
 
 Delete an existing Trait.
 
@@ -33,7 +49,7 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 {background:#ccffcc}.|uuid|string|The UUID of the Trait in question.|path||
 
-h2. get
+h3. get
 
 Gets a Trait's metadata by UUID.
 
@@ -43,19 +59,13 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 {background:#ccffcc}.|uuid|string|The UUID of the Trait in question.|path||
 
-h2. list
+h3. list
 
 List traits.
 
-Arguments:
-
-table(table table-bordered table-condensed).
-|_. Argument |_. Type |_. Description |_. Location |_. Example |
-|limit|integer (default 100)|Maximum number of traits to return.|query||
-|order|string|Order in which to return matching traits.|query||
-|filters|array|Conditions for filtering traits.|query||
+See "common resource list method.":{{site.baseurl}}/api/methods.html#index
 
-h2. update
+h3. update
 
 Update attributes of an existing Trait.
 
index 33f884b6cc7ffd9599343503a34db89ee90ff6ad..451205b48ee6b88351a3ca2927794007c2391b32 100644 (file)
@@ -3,17 +3,40 @@ layout: default
 navsection: api
 navmenu: API Methods
 title: "users"
-
 ...
 
-See "REST methods for working with Arvados resources":{{site.baseurl}}/api/methods.html
-
 API endpoint base: @https://{{ site.arvados_api_host }}/arvados/v1/users@
 
-Required arguments are displayed in %{background:#ccffcc}green%.
+Object type: @tpzed@
 
+Example UUID: @zzzzz-tpzed-0123456789abcde@
 
-h2. create
+h2. Resource
+
+Users represent individuals with access to the Arvados cluster.
+
+Each User has, in addition to the "Common resource fields":{{site.baseurl}}/api/resources.html:
+
+table(table table-bordered table-condensed).
+|_. Attribute|_. Type|_. Description|_. Example|
+|email|string|||
+|username|string|The username used for the user's git repositories and virtual machine logins.  Usernames must start with a letter, and contain only alphanumerics.  When a new user is created, a default username is set from their e-mail address.  Only administrators may change the username.||
+|first_name|string|||
+|last_name|string|||
+|identity_url|string|||
+|is_admin|boolean|||
+|prefs|hash|||
+|default_owner_uuid|string|||
+|is_active|boolean|||
+|writable_by|array|List of UUID strings identifying Groups and other Users that can modify this User object.  This will include the user's owner_uuid and, for administrators and users requesting their own User object, the requesting user's UUID.||
+
+h2. Methods
+
+See "Common resource methods":{{site.baseurl}}/api/methods.html for more information about @create@, @delete@, @get@, @list@, and @update@.
+
+Required arguments are displayed in %{background:#ccffcc}green%.
+
+h3. create
 
 Create a new User.
 
@@ -23,16 +46,16 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 |user|object||query||
 
-h2. current
+h3. current
 
-current users
+Get the user associated with the provided API token.
 
 Arguments:
 
 table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 
-h2. delete
+h3. delete
 
 Delete an existing User.
 
@@ -48,7 +71,7 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 {background:#ccffcc}.|uuid|string||path||
 
-h2. get
+h3. get
 
 Gets a User's metadata by UUID.
 
@@ -58,38 +81,22 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 {background:#ccffcc}.|uuid|string|The UUID of the User in question.|path||
 
-h2. list
+h3. list
 
 List users.
 
-Arguments:
-
-table(table table-bordered table-condensed).
-|_. Argument |_. Type |_. Description |_. Location |_. Example |
-|limit|integer (default 100)|Maximum number of users to return.|query||
-|order|string|Order in which to return matching users.|query||
-|filters|array|Conditions for filtering users.|query||
-
-h2. show
-
-show users
-
-Arguments:
-
-table(table table-bordered table-condensed).
-|_. Argument |_. Type |_. Description |_. Location |_. Example |
-{background:#ccffcc}.|uuid|string||path||
+See "common resource list method.":{{site.baseurl}}/api/methods.html#index
 
-h2. system
+h3. system
 
-system users
+Get the user record for the "system user.":{{site.baseurl}}/api/permission-model.html#system
 
 Arguments:
 
 table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 
-h2. update
+h3. update
 
 Update attributes of an existing User.
 
index 390abdc595893964893a151387432185a04ba9af..cad9bcbb1a785d587a93995e6a1ff22cd4286f23 100644 (file)
@@ -3,17 +3,31 @@ layout: default
 navsection: api
 navmenu: API Methods
 title: "virtual_machines"
-
 ...
 
-See "REST methods for working with Arvados resources":{{site.baseurl}}/api/methods.html
-
 API endpoint base: @https://{{ site.arvados_api_host }}/arvados/v1/virtual_machines@
 
-Required arguments are displayed in %{background:#ccffcc}green%.
+Object type: @2x53u@
+
+Example UUID: @zzzzz-2x53u-0123456789abcde@
+
+h2. Resource
+
+The virtual_machines resource lists compute resources in the Arvados cluster to which a user may log in to get an interactive shell (via ssh or webshell).
+
+Each VirtualMachine has, in addition to the "Common resource fields":{{site.baseurl}}/api/resources.html:
+
+table(table table-bordered table-condensed).
+|_. Attribute|_. Type|_. Description|_. Example|
+|hostname|string|||
+
+h2. Methods
 
+See "Common resource methods":{{site.baseurl}}/api/methods.html for more information about @create@, @delete@, @get@, @list@, and @update@.
+
+Required arguments are displayed in %{background:#ccffcc}green%.
 
-h2. create
+h3. create
 
 Create a new VirtualMachine.
 
@@ -23,7 +37,7 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 |virtual_machine|object||query||
 
-h2. delete
+h3. delete
 
 Delete an existing VirtualMachine.
 
@@ -33,7 +47,7 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 {background:#ccffcc}.|uuid|string|The UUID of the VirtualMachine in question.|path||
 
-h2. get
+h3. get
 
 Gets a VirtualMachine's metadata by UUID.
 
@@ -43,7 +57,7 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 {background:#ccffcc}.|uuid|string|The UUID of the VirtualMachine in question.|path||
 
-h2(#logins). logins
+h3(#logins). logins
 
 Get a list of SSH keys and account names that should be able to log in to a given virtual machine.
 
@@ -53,7 +67,7 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 {background:#ccffcc}.|uuid|string||path||
 
-The response is a "resource list":{{site.baseurl}}/api/resources.html#resourceList with @kind@ set to @"arvados#HashList"@. Each item is a hash with the following keys:
+The response is an object with the field @items@ containing an array of objects in the following format:
 
 table(table table-bordered table-condensed).
 |_. Key|_. Value type|_. Description|_. Example|
@@ -61,30 +75,24 @@ table(table table-bordered table-condensed).
 |hostname|string|Hostname of the virtual machine|@"shell.xyzzy.arvadosapi.com"@|
 |public_key|string|SSH public key|@"ssh-rsa AAAAB3NzaC1yc2E..."@|
 |user_uuid|string|UUID of the user who should be able to log in|@"xyzzy-tpzed-mv4d7dy7n91te11"@|
-|virtual_machine_uuid|string|UUID of the "VirtualMachine resource":{{site.baseurl}}/api/schema/VirtualMachine.html|@"xyzzy-2x53u-kvszmclnbjuv8xc"@|
-|authorized_key_uuid|string|UUID of the "AuthorizedKey resource":{{site.baseurl}}/api/schema/AuthorizedKey.html|@"xyzzy-fngyi-v9p0cyfmjxbio64"@|
+|virtual_machine_uuid|string|UUID of the "virtual machine resource":{{site.baseurl}}/api/methods/virtual_machines.html|@"zzzzz-2x53u-kvszmclnbjuv8xc"@|
+|authorized_key_uuid|string|UUID of the "authorized key resource":{{site.baseurl}}/api/methods/authorized_keys.html|@"zzzzz-fngyi-v9p0cyfmjxbio64"@|
 
-h2. get_all_logins
+h3. get_all_logins
 
-Get a list, for every virtual machine in the system, of SSH keys and account names that should be able to log in.
+Get a list of SSH keys and account names that should be able to log in for every virtual machine in the system.
 
 Arguments: none.
 
 The response has the same format as the response to the "logins method":#logins above.
 
-h2. list
+h3. list
 
 List virtual_machines.
 
-Arguments:
-
-table(table table-bordered table-condensed).
-|_. Argument |_. Type |_. Description |_. Location |_. Example |
-|limit|integer (default 100)|Maximum number of virtual_machines to return.|query||
-|order|string|Order in which to return matching virtual_machines.|query||
-|filters|array|Conditions for filtering virtual_machines.|query||
+See "common resource list method.":{{site.baseurl}}/api/methods.html#index
 
-h2. update
+h3. update
 
 Update attributes of an existing VirtualMachine.
 
index 95be013b621d92e5f534ba42ec94af96d3a56ed8..57b301a3b9022fb8deb2dfaf5a6fe2e6f6d0b0a1 100644 (file)
@@ -3,17 +3,33 @@ layout: default
 navsection: api
 navmenu: API Methods
 title: "workflows"
-
 ...
 
-See "REST methods for working with Arvados resources":{{site.baseurl}}/api/methods.html
-
 API endpoint base: @https://{{ site.arvados_api_host }}/arvados/v1/workflows@
 
-Required arguments are displayed in %{background:#ccffcc}green%.
+Object type: @7fd4e@
+
+Example UUID: @zzzzz-7fd4e-0123456789abcde@
+
+h2. Resource
+
+Stores a "Common Workflow Language":http://commonwl.org (CWL) computational workflow that can be searched for, browsed and executed (submitted to Crunch) from the workbench.
+
+Each Workflow offers the following optional attributes, in addition to the "Common resource fields":{{site.baseurl}}/api/resources.html:
+
+table(table table-bordered table-condensed).
+|_. Attribute|_. Type|_. Description|_. Example|
+|name|string|If not specified, will be set to any "name" from the "definition" attribute.||
+|description|string|If not specified, will be set to any "description" from the "definition" attribute.||
+|definition|string|A "Common Workflow Language" document.|Visit "Common Workflow Language":http://www.commonwl.org/ for details.|
+
+h2. Methods
 
+See "Common resource methods":{{site.baseurl}}/api/methods.html for more information about @create@, @delete@, @get@, @list@, and @update@.
+
+Required arguments are displayed in %{background:#ccffcc}green%.
 
-h2. create
+h3. create
 
 Create a new Workflow.
 
@@ -21,9 +37,9 @@ Arguments:
 
 table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
-{background:#ccffcc}.|workflow|object|See "Workflow resource":{{site.baseurl}}/api/schema/Workflow.html|request body||
+{background:#ccffcc}.|workflow|object|Workflow resource|request body||
 
-h2. delete
+h3. delete
 
 Delete an existing Workflow.
 
@@ -33,7 +49,7 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 {background:#ccffcc}.|uuid|string|The UUID of the Workflow in question.|path||
 
-h2. get
+h3. get
 
 Get a Workflow's metadata by UUID.
 
@@ -43,19 +59,13 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 {background:#ccffcc}.|uuid|string|The UUID of the Workflow in question.|path||
 
-h2. list
+h3. list
 
 List workflows.
 
-Arguments:
-
-table(table table-bordered table-condensed).
-|_. Argument |_. Type |_. Description |_. Location |_. Example |
-|limit|integer (default 100)|Maximum number of workflows to return.|query||
-|order|string|Order in which to return matching workflows.|query||
-|filters|array|Conditions for filtering workflows.|query||
+See "common resource list method.":{{site.baseurl}}/api/methods.html#index
 
-h2. update
+h3. update
 
 Update attributes of an existing Workflow.
 
index 8b085ee5aaffd1a99c27a0350a134a38f31a269b..62cd74d9d72c35d7a86c23e56f32e29fcfef422d 100644 (file)
@@ -3,123 +3,70 @@ layout: default
 navsection: api
 navmenu: Concepts
 title: "Permission model"
-
 ...
 
+* There are four levels of permission: *none*, *can_read*, *can_write*, and *can_manage*.
+** *none* is the default state when there are no other permission grants.
+*** the object is not included in any list query response.
+*** direct queries of the object by uuid return 404 Not Found.
+*** Link objects require valid identifiers in @head_uuid@ and @tail_uuid@, so an attempt to create a Link that references an unreadable object will return an error indicating the object is not found.
+** *can_read* grants read-only access to the record.  Attempting to update or delete the record returns an error.  *can_read* does not allow a reader to see any permission grants on the object except the object's owner_uuid and the reader's own permissions.
+** *can_write* permits changes to the record (but not permission links).  *can_write* permits the user to delete the object.  *can_write* also implies *can_read*.
+** *can_manage* permits the user to read, create, update and delete permission links whose @head_uuid@ is this object's @uuid@.  *can_manage* also implies *can_write* and *can_read*.
 
+h2. Ownership
 
-Each API transaction (read, write, create, etc.) is done on behalf of a person.
-
-* An end user, via a web app
-* The owner of an installed app
-
-A user (person) is permitted to act on an object if there is a path (series of permission Links) from the acting user to the object in which
-
-* Every intervening object is a Group, and
-* Every intervening permission Link allows the current action
-
-Special case: A permission path can also include intervening User objects if the links _to_ the Users are "can_manage" links.
-
-Each object has exactly one _owner_, which can be either a User or a Group.
-
-* If the owner of X is A, then A is permitted to do any action on X.
-
-h3. Tokens
-
-An authorization token is issued at a user's request, and supplied to an API client using some suitable mechanism (_e.g._, cookie or application config file for a web app; environment variable or .rc-file for a CLI app).
-
-A user can have multiple valid tokens at a given time.  At the user's option, a token can be restricted to a combination of
-
-* API client program
-* time interval
-* transaction type
-
-h3. System pseudo-user
-
-A privileged user account exists for the use of built-in Arvados system components.  This user manages system-wide shared objects which can't really be "owned" by any particular user, like
-
-* Jobs and job steps (because a given job can be "wanted" by multiple users)
-* Provenance metadata (because no user should be able to modify this directly)
-* Storage metadata like
-** redundancy verified as N&times; at time Y
-** contents of collections A and B are identical
-
-The system pseudo-user's uuid is @{siteprefix}-tpzed-000000000000000@.
-
-h2. Example scenarios
-
-h3. 1. Private objects
-
-Alfred stores 3 data Collections in Keep and adds them to a new Group.
-
-The Collections and the Group can only be seen by Alfred, administrators, and the system user.
-
-The data in the Collections can only be retrieved by Alfred, administrators, and the system user.
-
-h3. 2. Public objects
-
-George creates a "PGP public data" Group, and grants "read" permission to all users.
-
-* ...by adding a Link: "All users" Group _can_read_&rarr; "PGP public data" Group
-
-George stores 4 data Collections in Keep and adds them to the "PGP public data" Group
+* All Arvados objects have an @owner_uuid@ field. Valid uuid types for @owner_uuid@ are "User" and "Group".
+* The User or Group specified by @owner_uuid@ has *can_manage* permission on the object.
+** This permission is one way: A User or Group's @owner_uuid@ being equal to @X@ does not imply any permission for that User/Group to read, write, or manage an object whose @uuid@ is equal to @X@.
+* Applications should represent each object as belonging to, or being "inside", the Group/User referenced by its @owner_uuid@.
+** A "project" is a subtype of Group that is treated as a "Project" in Workbench, and as a directory by @arv-mount@.
+** A "role" is a subtype of Group that is treated in Workbench as a group of users who have permissions in common (typically an organizational group).
+* To change the @owner_uuid@ field, it is necessary to have @can_write@ permission on both the current owner and the new owner.
 
-* ...by adding a Link: Group _can_read_&rarr; Collection
+h2(#links). Permission links
 
-Anyone who can connect to Arvados can log in with a Google/OpenID account and read the data.
+A link object with
 
-h3. 3. Group-managed objects
+* @owner_uuid@ of the system user.
+* @link_class@ "permission"
+* @name@ one of *can_read*, *can_write* or *can_manage*
+* @head_uuid@ of some Arvados object
+* @tail_uuid@ of a User or Group
 
-Three lab members are working together on a project. All Specimens, Links, Jobs, etc. can be modified by any of the three lab members. _Other_ lab members, who are not working on this project, can view but not modify these objects.
+grants the @name@ permission for @tail_uuid@ accessing @head_uuid@
 
-h3. 4. Group-level administrator
+* If a User has *can_manage* permission on some object, this grants permission to read, create, update and delete permission links where the @head_uuid@ is the object under management.
 
-The Ashton Lab administrator, Alison, manages user accounts within her lab. She can enable and disable accounts, and exercise any permission that her lab members have.
+h3. Transitive permissions
 
-George has read-only access to the same set of accounts. This lets him see things like user activity and resource usage reports, without worrying about accidentally messing up anyone's data.
+Permissions can be obtained indirectly through Groups.
+* If a User X *can_read* Group A, and Group A *can_read* Object B, then User X *can_read* Object B.
+* Permissions are narrowed to the least powerful permission on the path.
+** If User X *can_write* Group A, and Group A *can_read* Object B, then User X *can_read* Object B.
+** If User X *can_read* Group A, and Group A *can_write* Object B, then User X *can_read* Object B.
 
-table(table table-bordered table-condensed).
-|Tail                   |Permission     |Head                      |Effect|
-|Group: Ashton Lab Admin|can_manage     |User: Lab Member 1        |Lab member 1 is in this administrative group|
-|Group: Ashton Lab Admin|can_manage     |User: Lab Member 2        |Lab member 2 is in this administrative group|
-|Group: Ashton Lab Admin|can_manage     |User: Lab Member 3        |Lab member 3 is in this administrative group|
-|Group: Ashton Lab Admin|can_manage     |User: Alison              |Alison is in this administrative group|
-|Group: Ashton Lab Admin|can_manage     |User: George              |George is in this administrative group|
-|Alison                 |can_manage     |Group: Ashton Lab Admin   |Alison can do everything the above lab members can do|
-|George                 |can_read       |Group: Ashton Lab Admin   |George can read everything the above lab members can read|
+h2. Group Membership
 
-h3. 5. Segregated roles
+Group membership is determined by whether the group has *can_read* permission on an object.  If a group G *can_read* an object A, then we say A is a member of G.
 
-Granwyth, at the Hulatberi Lab, sets up a Factory Robot which uses a hosted Arvados site to do work for the Hulatberi Lab.
+For some kinds of groups, like roles, it is natural for users who are members of a group to also have *can_manage* permission on the group, i.e., G *can_read* A  and A *can_manage* G ("A can do anything G can do"). However, this is not necessary: A can be a member of a group while being unable to even read it.
 
-Frank uploads a data Collection using Factory Robot's upload interface.  Factory Robot sets data owner to Hulatberi Lab.
+h2. Special cases
 
-Factory Robot processes the data using a pipeline.
+* Log table objects are additionally readable based on whether the User has *can_read* permission on @object_uuid@ (User can access log history about objects it can read).  To retain the integrity of the log, the log table should deny all update or delete operations.
+* Permission links where @tail_uuid@ is a User permit @can_read@ on the link by that user.  (User can discover her own permission grants.)
+* *can_read* on a Collection grants permission to read the blocks that make up the collection (API server returns signed blocks)
+* If User or Group X *can_FOO* Group A, and Group A *can_manage* User B, then X *can_FOO* _everything that User B can_FOO_.
 
-Factory Robot grants permission for anyone in the Ingeborg Lab (a Hulateberi Lab customer) to read the output of the pipeline, as well as the pipeline invocation details.  (Say, Ingeborg and Jill.)
+h2(#system). System user and group
 
-During and after processing, all members of the Hulatberi Lab (_e.g._, Mike) can inspect pipeline progress, read source/intermediate/output data, and modify objects.
+A privileged user account exists for the use by internal Arvados components.  This user manages system objects which should not be "owned" by any particular user.  The system user uuid is @{siteprefix}-tpzed-000000000000000@.
 
-Possible encoding:
+h2. Anoymous user and group
 
-table(table table-bordered table-condensed).
-|Tail           |Permission     |Head                      |Effect|
-|Frank          |(none)         |                          |Factory Robot uses only its own credentials during upload|
-|Granwyth       |can_manage     |User:    Factory Robot    |can revoke tokens, view activity... (needed?)|
-|Granwyth       |can_manage     |Group: Hulatberi Lab    |can grant group-write permission to Factory Robot|
-|Factory Robot  |can_write      |Group: Hulatberi Lab    |can add data, pipelines, jobs, etc. to the Lab group|
-|Mike           |can_write      |Group: Hulatberi Lab    |can edit/annotate/delete objects that belong to the Lab|
+An Arvado site may be configued to allow users to browse resources without requiring a log in.  In this case, permissions for non-logged-in users are associated with the "anonymous" user.  To make objects visible to the public, they can be shared with the "anonymous" group.  The anonymous user uuid is @{siteprefix}-tpzed-anonymouspublic@.  The anonymous group uuid is @{siteprefix}-j7d0g-anonymouspublic@.
 
-h3. Actions governed by permissions
+h2. Example
 
-table(table table-bordered table-condensed).
-|_Action_|_Permissions needed_|
-|Retrieve data from Keep|can_read (system-wide?)|
-|Store data in Keep|can_write (system-wide?)|
-|Add a Collection to Arvados|can_write (system-wide?)|
-|Run a job|can_run (system-wide?)|
-|See progress/result of a job|can_read (on job)|
-|Give group permissions to a user/group|can_manage (on group)|
-|Revoke group permissions from a user/group|can_manage (on group)|
-|Change owner of an object|can_manage (on object)|
-|Add an object to a group|can_write (on group)|
+!{{site.baseurl}}/images/Arvados_Permissions.svg!
diff --git a/doc/api/requests.html.textile.liquid b/doc/api/requests.html.textile.liquid
new file mode 100644 (file)
index 0000000..e720395
--- /dev/null
@@ -0,0 +1,349 @@
+---
+layout: default
+navsection: api
+navmenu: Concepts
+title: REST API syntax
+...
+
+Arvados exposes a REST API using standard HTTP requests.
+
+h3. HTTP Method
+
+Use @GET@ to request individual resources or lists of resources.
+
+Use @POST@ to create new resources.
+
+Use @PUT@ to update an existing resource.
+
+Use @DELETE@ to remove an existing resource.
+
+As a special case, a @POST@ with the query parameter @_method=GET@ will be treated as a GET request.  This makes it possible to issue @GET@ requests where the query string exceeds the maximum request URI length, by putting the query string in the body of the request.
+
+h3. Request URI
+
+The URI portion of the request identifies the specific resource to operate on.  For example, operations on "collections":{{site.baseurl}}/api/methods/collections.html use the @https://{{ site.arvados_api_host }}/arvados/v1/collections@ request URI prefix.
+
+h3. Authorization header
+
+Every request must include an API token.  This identifies the user making the request for the purposes of access control.  In addition, tokens may be further "restricted in scope":{{site.baseurl}}/api/methods/api_client_authorizations.html#scope to only access certain API endpoints.
+
+API requests must provide the API token using the @Authorization@ header in the following format:
+
+<pre>
+$ curl -v -H "Authorization: OAuth2 xxxxapitokenxxxx" https://192.168.5.2:8000/arvados/v1/collections
+> GET /arvados/v1/collections HTTP/1.1
+> ...
+> Authorization: OAuth2 xxxxapitokenxxxx
+> ...
+</pre>
+
+h3. Parameters
+
+Request parameters may be provided in one of two ways.  They may be provided in the "query" section of request URI, or they may be provided in the body of the request with application/x-www-form-urlencoded encoding.  If parameters are provided in both places, their values will be merged.  Parameter names must be unique.  If a parameter appears multiple times, the behavior is undefined.
+
+Structured and nested parameter values must be provided as urlencoded JSON.
+
+h3. Result
+
+Results are returned JSON-encoded in the response body.
+
+h3. Errors
+
+If a request cannot be fulfilled, the API will return 4xx or 5xx HTTP status code.  Be aware that the API server may return a 404 (Not Found) status for resources that exist but for which the client does not have read access.  The API will also return an error record:
+
+table(table table-bordered table-condensed).
+|*Parameter name*|*Value*|*Description*|
+|errors|array|An array of one or more error messages|
+|error_token|string|a unique identifier used to correlate the error in the API server logs|
+
+h2. Examples
+
+h3. Create a new record
+
+<pre>
+$ curl -v -X POST --data-urlencode 'collection={"name":"empty collection"}' -H "Authorization: OAuth2 oz0os4nyudswvglxhdlnrgnuelxptmj7qu7dpwvyz3g9ocqtr" https://192.168.5.2:8000/arvados/v1/collections | jq .
+> POST /arvados/v1/collections HTTP/1.1
+> User-Agent: curl/7.38.0
+> Host: 192.168.5.2:8000
+> Accept: */*
+> Authorization: OAuth2 oz0os4nyudswvglxhdlnrgnuelxptmj7qu7dpwvyz3g9ocqtr
+> Content-Length: 54
+> Content-Type: application/x-www-form-urlencoded
+>
+} [data not shown]
+< HTTP/1.1 200 OK
+< Content-Type: application/json; charset=utf-8
+< Transfer-Encoding: chunked
+< Connection: keep-alive
+< Status: 200 OK
+< Access-Control-Allow-Origin: *
+< Access-Control-Allow-Methods: GET, HEAD, PUT, POST, DELETE
+< Access-Control-Allow-Headers: Authorization
+< Access-Control-Max-Age: 86486400
+< X-UA-Compatible: IE=Edge,chrome=1
+< ETag: "2ec9ef5151c1f7a1486ad169c33ae462"
+< Cache-Control: max-age=0, private, must-revalidate
+< Set-Cookie: _server_session=BAh7BkkiD3Nlc3Npb25faWQGOgZFVEkiJTIwMjQ1NTE5YmEwMzU1MGZkMTBmYmY1YzllY2ZiMjFlBjsAVA%3D%3D--653bc9c20899d48ee8523e18d9a4c1cde0702577; path=/; HttpOnly
+< X-Request-Id: 56aa10bc49097f3b44d3ed946bf0e61e
+< X-Runtime: 0.049951
+< X-Powered-By: Phusion Passenger 4.0.41
+< Date: Fri, 28 Oct 2016 19:20:09 GMT
+< Server: nginx/1.4.7 + Phusion Passenger 4.0.41
+<
+{
+  "href": "/collections/962eh-4zz18-m1ma0mxxfg3mbcc",
+  "kind": "arvados#collection",
+  "etag": "c5ifrv1ox2tu6alb559ymtkb7",
+  "uuid": "962eh-4zz18-m1ma0mxxfg3mbcc",
+  "owner_uuid": "962eh-tpzed-000000000000000",
+  "created_at": "2016-10-28T19:20:09.320771531Z",
+  "modified_by_client_uuid": "962eh-ozdt8-lm5x8emraox8epg",
+  "modified_by_user_uuid": "962eh-tpzed-000000000000000",
+  "modified_at": "2016-10-28T19:20:09.319661000Z",
+  "name": "empty collection",
+  "description": null,
+  "properties": {},
+  "portable_data_hash": "d41d8cd98f00b204e9800998ecf8427e+0",
+  "manifest_text": "",
+  "replication_desired": null,
+  "replication_confirmed": null,
+  "replication_confirmed_at": null,
+  "expires_at": null
+}
+</pre>
+
+h3. Delete a record
+
+<pre>
+$ curl -X DELETE -v -H "Authorization: OAuth2 oz0os4nyudswvglxhdlnrgnuelxptmj7qu7dpwvyz3g9ocqtr" https://192.168.5.2:8000/arvados/v1/collections/962eh-4zz18-m1ma0mxxfg3mbcc | jq .
+> DELETE /arvados/v1/collections/962eh-4zz18-m1ma0mxxfg3mbcc HTTP/1.1
+> User-Agent: curl/7.38.0
+> Host: 192.168.5.2:8000
+> Accept: */*
+> Authorization: OAuth2 oz0os4nyudswvglxhdlnrgnuelxptmj7qu7dpwvyz3g9ocqtr
+>
+< HTTP/1.1 200 OK
+< Content-Type: application/json; charset=utf-8
+< Transfer-Encoding: chunked
+< Connection: keep-alive
+< Status: 200 OK
+< Access-Control-Allow-Origin: *
+< Access-Control-Allow-Methods: GET, HEAD, PUT, POST, DELETE
+< Access-Control-Allow-Headers: Authorization
+< Access-Control-Max-Age: 86486400
+< X-UA-Compatible: IE=Edge,chrome=1
+< ETag: "1e8f72802cf1a6d0a5c4a1ebbfcc46a9"
+< Cache-Control: max-age=0, private, must-revalidate
+< Set-Cookie: _server_session=BAh7BkkiD3Nlc3Npb25faWQGOgZFVEkiJTc2NDYyY2M0NTNlNmU3M2Y2M2E3YmFiMWQ1MTEyZGZkBjsAVA%3D%3D--d28c7dd640bd24e2b12f01e77088072138dcf145; path=/; HttpOnly
+< X-Request-Id: e66fd3ab825bdb87301f5456161fb641
+< X-Runtime: 0.028788
+< X-Powered-By: Phusion Passenger 4.0.41
+< Date: Fri, 28 Oct 2016 19:33:31 GMT
+< Server: nginx/1.4.7 + Phusion Passenger 4.0.41
+<
+{
+  "href": "/collections/962eh-4zz18-m1ma0mxxfg3mbcc",
+  "kind": "arvados#collection",
+  "etag": "c5ifrv1ox2tu6alb559ymtkb7",
+  "uuid": "962eh-4zz18-m1ma0mxxfg3mbcc",
+  "owner_uuid": "962eh-tpzed-000000000000000",
+  "created_at": "2016-10-28T19:20:09.320771000Z",
+  "modified_by_client_uuid": "962eh-ozdt8-lm5x8emraox8epg",
+  "modified_by_user_uuid": "962eh-tpzed-000000000000000",
+  "modified_at": "2016-10-28T19:20:09.319661000Z",
+  "name": "empty collection",
+  "description": null,
+  "properties": {},
+  "portable_data_hash": "d41d8cd98f00b204e9800998ecf8427e+0",
+  "manifest_text": "",
+  "replication_desired": null,
+  "replication_confirmed": null,
+  "replication_confirmed_at": null,
+  "expires_at": null
+}
+</pre>
+
+h3. Get a specific record
+
+<pre>
+$ curl -v -H "Authorization: OAuth2 oz0os4nyudswvglxhdlnrgnuelxptmj7qu7dpwvyz3g9ocqtr" https://192.168.5.2:8000/arvados/v1/collections/962eh-4zz18-xi32mpz2621o8km | jq .
+> GET /arvados/v1/collections/962eh-4zz18-xi32mpz2621o8km HTTP/1.1
+> User-Agent: curl/7.38.0
+> Host: 192.168.5.2:8000
+> Accept: */*
+> Authorization: OAuth2 oz0os4nyudswvglxhdlnrgnuelxptmj7qu7dpwvyz3g9ocqtr
+>
+< HTTP/1.1 200 OK
+< Content-Type: application/json; charset=utf-8
+< Transfer-Encoding: chunked
+< Connection: keep-alive
+< Status: 200 OK
+< Access-Control-Allow-Origin: *
+< Access-Control-Allow-Methods: GET, HEAD, PUT, POST, DELETE
+< Access-Control-Allow-Headers: Authorization
+< Access-Control-Max-Age: 86486400
+< X-UA-Compatible: IE=Edge,chrome=1
+< ETag: "fec2ddf433a352e5a2b5d356abd6d3d4"
+< Cache-Control: max-age=0, private, must-revalidate
+< X-Request-Id: 40b447507ff202ae9a0b0b3e0ebe98da
+< X-Runtime: 0.011404
+< X-Powered-By: Phusion Passenger 4.0.41
+< Date: Fri, 28 Oct 2016 18:59:09 GMT
+< Server: nginx/1.4.7 + Phusion Passenger 4.0.41
+<
+{
+  "href": "/collections/962eh-4zz18-xi32mpz2621o8km",
+  "kind": "arvados#collection",
+  "etag": "3mmn0s9e1z5s5opfofmtb9k8p",
+  "uuid": "962eh-4zz18-xi32mpz2621o8km",
+  "owner_uuid": "962eh-tpzed-000000000000000",
+  "created_at": "2016-10-27T14:47:43.792587000Z",
+  "modified_by_client_uuid": "962eh-ozdt8-lm5x8emraox8epg",
+  "modified_by_user_uuid": "962eh-tpzed-000000000000000",
+  "modified_at": "2016-10-27T14:47:43.792166000Z",
+  "name": "Saved at 2016-10-27 14:47:43 UTC by peter@debian",
+  "description": null,
+  "properties": {},
+  "portable_data_hash": "93a45073511646a5c3e2f4953fcf6f61+116",
+  "manifest_text": ". eff999f3b5158331eb44a9a93e3b36e1+67108864+Aad3839bea88bce22cbfe71cf4943de7dab3ea52a@5826180f db141bfd11f7da60dce9e5ee85a988b8+34038725+Ae8f48913fed782cbe463e0499ab37697ee06a2f8@5826180f 0:101147589:rna.SRR948778.bam\n",
+  "replication_desired": null,
+  "replication_confirmed": null,
+  "replication_confirmed_at": null,
+  "expires_at": null
+}
+</pre>
+
+h3. List records and filter by date
+
+(Note, return result is truncated).
+
+<pre>
+$ curl -v -G --data-urlencode 'filters=[["created_at",">","2016-11-08T21:38:24.124834000Z"]]' -H "Authorization: OAuth2 oz0os4nyudswvglxhdlnrgnuelxptmj7qu7dpwvyz3g9ocqtr" https://192.168.5.2:8000/arvados/v1/collections | jq .
+> GET /arvados/v1/collections?filters=%5B%5B%22uuid%22%2C%20%22%3D%22%2C%20%22962eh-4zz18-xi32mpz2621o8km%22%5D%5D HTTP/1.1
+> User-Agent: curl/7.38.0
+> Host: 192.168.5.2:8000
+> Accept: */*
+> Authorization: OAuth2 oz0os4nyudswvglxhdlnrgnuelxptmj7qu7dpwvyz3g9ocqtr
+>
+< HTTP/1.1 200 OK
+< Content-Type: application/json; charset=utf-8
+< Transfer-Encoding: chunked
+< Connection: keep-alive
+< Status: 200 OK
+< Access-Control-Allow-Origin: *
+< Access-Control-Allow-Methods: GET, HEAD, PUT, POST, DELETE
+< Access-Control-Allow-Headers: Authorization
+< Access-Control-Max-Age: 86486400
+< X-UA-Compatible: IE=Edge,chrome=1
+< ETag: "76345ef24952f073acc3a0c550241d4e"
+< Cache-Control: max-age=0, private, must-revalidate
+< X-Request-Id: d34b8ede4ffc707d8ed172dc2f47ff5e
+< X-Runtime: 0.012727
+< X-Powered-By: Phusion Passenger 4.0.41
+< Date: Fri, 28 Oct 2016 19:08:52 GMT
+< Server: nginx/1.4.7 + Phusion Passenger 4.0.41
+<
+{
+  "kind": "arvados#collectionList",
+  "etag": "",
+  "self_link": "",
+  "offset": 0,
+  "limit": 100,
+  "items": [
+    {
+      "href": "/collections/962eh-4zz18-ybggo9im899vv60",
+      "kind": "arvados#collection",
+      "etag": "bvgrrsg63zsenb9wnpnp0nsgl",
+      "uuid": "962eh-4zz18-ybggo9im899vv60",
+      "owner_uuid": "962eh-tpzed-000000000000000",
+      "created_at": "2016-11-08T21:47:36.937106000Z",
+      "modified_by_client_uuid": null,
+      "modified_by_user_uuid": "962eh-tpzed-000000000000000",
+      "modified_at": "2016-11-08T21:47:36.936625000Z",
+      "name": "Log from cwl-runner job 962eh-8i9sb-45jww0k15fi5ldd",
+      "description": null,
+      "properties": {},
+      "portable_data_hash": "a7820b94717eff86229927565fedbd72+85",
+      "replication_desired": null,
+      "replication_confirmed": null,
+      "replication_confirmed_at": null,
+      "expires_at": null
+    },
+   ...
+    {
+      "href": "/collections/962eh-4zz18-37i1tfl5de5ild9",
+      "kind": "arvados#collection",
+      "etag": "2fa07dx52lux8wa1loehwyrc5",
+      "uuid": "962eh-4zz18-37i1tfl5de5ild9",
+      "owner_uuid": "962eh-tpzed-000000000000000",
+      "created_at": "2016-11-08T21:38:46.717798000Z",
+      "modified_by_client_uuid": null,
+      "modified_by_user_uuid": "962eh-tpzed-000000000000000",
+      "modified_at": "2016-11-08T21:38:46.717409000Z",
+      "name": null,
+      "description": null,
+      "properties": {},
+      "portable_data_hash": "9d43d4c8328640446f6e252cda584e7e+54",
+      "replication_desired": null,
+      "replication_confirmed": null,
+      "replication_confirmed_at": null,
+      "expires_at": null
+    }
+  ],
+  "items_available": 99
+}
+</pre>
+
+h3. Update a field
+
+<pre>
+$ curl -v -X PUT --data-urlencode 'collection={"name":"rna.SRR948778.bam"}' -H "Authorization: OAuth2 oz0os4nyudswvglxhdlnrgnuelxptmj7qu7dpwvyz3g9ocqtr" https://192.168.5.2:8000/arvados/v1/collections/962eh-4zz18-xi32mpz2621o8km | jq .
+> PUT /arvados/v1/collections/962eh-4zz18-xi32mpz2621o8km HTTP/1.1
+> User-Agent: curl/7.38.0
+> Host: 192.168.5.2:8000
+> Accept: */*
+> Authorization: OAuth2 oz0os4nyudswvglxhdlnrgnuelxptmj7qu7dpwvyz3g9ocqtr
+> Content-Length: 53
+> Content-Type: application/x-www-form-urlencoded
+>
+} [data not shown]
+< HTTP/1.1 200 OK
+< Content-Type: application/json; charset=utf-8
+< Transfer-Encoding: chunked
+< Connection: keep-alive
+< Status: 200 OK
+< Access-Control-Allow-Origin: *
+< Access-Control-Allow-Methods: GET, HEAD, PUT, POST, DELETE
+< Access-Control-Allow-Headers: Authorization
+< Access-Control-Max-Age: 86486400
+< X-UA-Compatible: IE=Edge,chrome=1
+< ETag: "fbb50d2847426eab793e3fcf346ca9eb"
+< Cache-Control: max-age=0, private, must-revalidate
+< Set-Cookie: _server_session=BAh7BkkiD3Nlc3Npb25faWQGOgZFVEkiJWI3NjFjMzVjMGI5OGExYmNjZDg0ZTg5MjZhMzcwMDE1BjsAVA%3D%3D--0e005d71fad15cb366e47361c38474b7447ba155; path=/; HttpOnly
+< X-Request-Id: 76d3cb3c0995af6133b0a73a64f57354
+< X-Runtime: 0.030756
+< X-Powered-By: Phusion Passenger 4.0.41
+< Date: Fri, 28 Oct 2016 19:15:16 GMT
+< Server: nginx/1.4.7 + Phusion Passenger 4.0.41
+<
+{
+  "href": "/collections/962eh-4zz18-xi32mpz2621o8km",
+  "kind": "arvados#collection",
+  "etag": "51509hhxo9qqjxqewnoz1b7og",
+  "uuid": "962eh-4zz18-xi32mpz2621o8km",
+  "owner_uuid": "962eh-tpzed-000000000000000",
+  "created_at": "2016-10-27T14:47:43.792587000Z",
+  "modified_by_client_uuid": "962eh-ozdt8-lm5x8emraox8epg",
+  "modified_by_user_uuid": "962eh-tpzed-000000000000000",
+  "modified_at": "2016-10-28T19:15:16.137814000Z",
+  "name": "rna.SRR948778.bam",
+  "description": null,
+  "properties": {},
+  "portable_data_hash": "93a45073511646a5c3e2f4953fcf6f61+116",
+  "manifest_text": ". eff999f3b5158331eb44a9a93e3b36e1+67108864+Acca57af82cc18c5dfa47bdfd16e335fccd09dfa5@582618c4 db141bfd11f7da60dce9e5ee85a988b8+34038725+A7764f122f41f92c2d5bde1852fcdd1bea5f8bd78@582618c4 0:101147589:rna.SRR948778.bam\n",
+  "replication_desired": null,
+  "replication_confirmed": null,
+  "replication_confirmed_at": null,
+  "expires_at": null
+}
+</pre>
index a93b4ac114745c2de676f2cb14ee90ac0bc60227..7b36f113bf27c7fcfe6bed44c46b96fd21c0ea48 100644 (file)
@@ -2,48 +2,38 @@
 layout: default
 navsection: api
 navmenu: Concepts
-title: Resources
+title: Common resource fields
 
 ...
 
-
-
 This page describes the common attributes of Arvados resources.
 
-The list of resource types is on the "front page of the API Reference":./.
-
-h2. Object IDs
-
-Object IDs are alphanumeric strings, unique across all installations (each installation has a unique prefix to prevent collisions).
-
-h2(#resource). Attributes of resources
+h2(#resource). Resource
 
 table(table table-bordered table-condensed).
-|*Attribute*|*Type*|*Description*|*Example*|
-|uuid|string|universally unique object identifier|@mk2qn-4zz18-w3anr2hk2wgfpuo@|
+|_. Attribute |_. Type |_. Description |_. Example|
+|uuid|string|universally unique object identifier, set on @create@|@mk2qn-4zz18-w3anr2hk2wgfpuo@|
+|owner_uuid|string|UUID of owner (must be a User or Group), set on @create@, controls who may access the resource, ownership may be changed explicitly with @update@, see "permission model":{{site.baseurl}}/api/permission-model.html for details.|@mk2qn-tpzed-a4lcehql0dv2u25@|
+|created_at|datetime|When resource was created, set on @create@|@2013-01-21T22:17:39Z@|
+|modified_by_client_uuid|string|API client software which most recently modified the resource, set on @create@ and @update@|@mk2qn-ozdt8-vq8l5qkzj7pr7h7@|
+|modified_by_user_uuid|string|Authenticated user, on whose behalf the client was acting when modifying the resource, set on @create@ and @update@|@mk2qn-tpzed-a4lcehql0dv2u25@|
+|modified_at|datetime|When resource was last modified, set on @create@ and @update@|@2013-01-25T22:29:32Z@|
 |href|string|a URL that can be used to address this resource||
 |kind|string|@arvados#{resource_type}@|@arvados#collection@|
 |etag|string|The ETag[1] of the resource|@1xlmizzjq7wro3dlb2dirf505@|
-|self_link|string|||
-|owner_uuid|string|UUID of owner (typically User or Project)|@mk2qn-tpzed-a4lcehql0dv2u25@|
-|created_at|datetime|When resource was created|@2013-01-21T22:17:39Z@|
-|modified_by_client_uuid|string|API client software which most recently modified the resource|@mk2qn-ozdt8-vq8l5qkzj7pr7h7@|
-|modified_by_user_uuid|string|Authenticated user, on whose behalf the client was acting when modifying the resource|@mk2qn-tpzed-a4lcehql0dv2u25@|
-|modified_at|datetime|When resource was last modified|@2013-01-25T22:29:32Z@|
 
-h2(#resourceList). Attributes of resource lists
+h2. Object UUID
 
-table(table table-bordered table-condensed).
-|*Attribute*|*Type*|*Description*|*Example*|
-|kind|string|@arvados#{resource_type}List@|@arvados#projectList@|
-|etag|string|The ETag[1] of the resource list|@cd3o1wi9sf934saajykawrz2e@|
-|self_link|string|||
-|next_page_token|string|||
-|next_link|string|||
-|items[]|list|List of resources||
+Each object is assigned a UUID.  This has the format @aaaaa-bbbbb-ccccccccccccccc@.
+
+# The first field (@aaaaa@ in the example) is the site prefix.  This is unique to a specific Arvados installation.
+# The second field (@bbbbb@ in the example) is the object type.
+# The third field (@ccccccccccccccc@ in the example) uniquely identifies the object.
 
+h2. Timestamps
+
+All Arvados timestamps follow ISO 8601 datetime format with fractional seconds (microsecond precision).  All timestamps are UTC.  Date format: @YYYY-mm-ddTHH:MM:SS.SSSSZ@ example date: @2016-11-08T21:38:24.124834000Z@.
 
 h2. ETags
 
 fn1. Each response includes an ETag, a string which changes when the resource changes.  Clients can use this to check whether a resource has changed since they last retrieved it.  If a previous ETag is provided along with a request, and the resource has not changed since, the server may return a "not modified" response.
-
diff --git a/doc/api/schema/ApiClient.html.textile.liquid b/doc/api/schema/ApiClient.html.textile.liquid
deleted file mode 100644 (file)
index 0cda1af..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
----
-layout: default
-navsection: api
-navmenu: Schema
-title: ApiClient
-
-...
-
-
-An **ApiClient** represents a client program that can issue requests to the API server.
-
-h2. Methods
-
-See "api_clients":{{site.baseurl}}/api/methods/api_clients.html
-
-h2. Resource
-
-Each ApiClient has, in addition to the usual "attributes of Arvados resources":{{site.baseurl}}/api/resources.html:
-
-table(table table-bordered table-condensed).
-|_. Attribute|_. Type|_. Description|_. Example|
-|name|string|||
-|url_prefix|string|||
-|is_trusted|boolean|Trusted by users to handle their API tokens (ApiClientAuthorizations).||
diff --git a/doc/api/schema/ApiClientAuthorization.html.textile.liquid b/doc/api/schema/ApiClientAuthorization.html.textile.liquid
deleted file mode 100644 (file)
index dc9614a..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
----
-layout: default
-navsection: api
-navmenu: Schema
-title: ApiClientAuthorization
-
-...
-
-An **ApiClientAuthorization** represents an API client's authorization to make API requests on a user's behalf.
-
-h2. Methods
-
-See "api_client_authorizations":{{site.baseurl}}/api/methods/api_client_authorizations.html
-
-h2. Resource
-
-An ApiClientAuthorization is not a generic Arvados resource.  The full list of properties that belong to an ApiClientAuthorization is:
-
-table(table table-bordered table-condensed).
-|_. Attribute|_. Type|_. Description|_. Example|
-|api_token|string|||
-|api_client_id|integer|||
-|user_id|integer|||
-|created_by_ip_address|string|||
-|last_used_by_ip_address|string|||
-|last_used_at|datetime|||
-|expires_at|datetime|||
-|default_owner_uuid|string|||
-|scopes|array|||
diff --git a/doc/api/schema/AuthorizedKey.html.textile.liquid b/doc/api/schema/AuthorizedKey.html.textile.liquid
deleted file mode 100644 (file)
index d9f4354..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
----
-layout: default
-navsection: api
-navmenu: Schema
-title: AuthorizedKey
-...
-
-An **AuthorizedKey** represents the public part of an SSH authentication key which can be used to authorize transactions on behalf of the user.
-
-h2. Methods
-
-See "authorized_keys":{{site.baseurl}}/api/methods/authorized_keys.html
-
-h2. Resource
-
-Each AuthorizedKey has, in addition to the usual "attributes of Arvados resources":{{site.baseurl}}/api/resources.html:
-
-table(table table-bordered table-condensed).
-|_. Attribute|_. Type|_. Description|_. Example|
-|name|string|||
-|key_type|string|||
-|authorized_user_uuid|string|||
-|public_key|text|||
-|expires_at|datetime|||
diff --git a/doc/api/schema/Collection.html.textile.liquid b/doc/api/schema/Collection.html.textile.liquid
deleted file mode 100644 (file)
index 9aa783c..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
----
-layout: default
-navsection: api
-navmenu: Schema
-title: Collection
-
-...
-
-Note: This resource concerns indexing, usage accounting, and integrity checks for data stored in Arvados. Reading and writing the data _per se_ is achieved by the "Keep":{{site.baseurl}}/user/tutorials/tutorial-keep.html storage system.
-
-h2. Methods
-
-See "collections":{{site.baseurl}}/api/methods/collections.html
-
-h3. Conditions of creating a Collection
-
-The @uuid@ and @manifest_text@ attributes must be provided when creating a Collection. The cryptographic digest of the supplied @manifest_text@ must match the supplied @uuid@.
-
-h3. Side effects of creating a Collection
-
-Referenced data can be protected from garbage collection. See the section about "resources" links on the "Links":Link.html page.
-
-Data can be shared with other users via the Arvados permission model.
-
-Clients can request checks of data integrity and storage redundancy.
-
-h2. Resource
-
-Each collection has, in addition to the usual "attributes of Arvados resources":{{site.baseurl}}/api/resources.html:
-
-table(table table-bordered table-condensed).
-|_. Attribute|_. Type|_. Description|_. Example|
-|name|string|||
-|description|text|||
-|portable_data_hash|string|||
-|manifest_text|text|||
-|replication_desired|number|Minimum storage replication level desired for each data block referenced by this collection. A value of @null@ signifies that the site default replication level (typically 2) is desired.|@2@|
-|replication_confirmed|number|Replication level most recently confirmed by the storage system. This field is null when a collection is first created, and is reset to null when the manifest_text changes in a way that introduces a new data block. An integer value indicates the replication level of the _least replicated_ data block in the collection.|@2@, null|
-|replication_confirmed_at|datetime|When replication_confirmed was confirmed. If replication_confirmed is null, this field is also null.||
diff --git a/doc/api/schema/Container.html.textile.liquid b/doc/api/schema/Container.html.textile.liquid
deleted file mode 100644 (file)
index d29d420..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
----
-layout: default
-navsection: api
-navmenu: Schema
-title: Container
-
-...
-
-A Container:
-* Precisely describes the environment in which a Crunch2 process should run. For example, git trees, data collections, and docker images are stored as content addresses. This makes it possible to reason about the difference between two processes, and to replay a process at a different time and place.
-* Container records are created by the system to fulfill container requests.
-
-h2. Methods
-
-See "containers":{{site.baseurl}}/api/methods/containers.html
-
-h2. Resource
-
-Each Container offers the following attributes, in addition to the usual "attributes of Arvados resources":{{site.baseurl}}/api/resources.html:
-
-table(table table-bordered table-condensed).
-|_. Attribute|_. Type|_. Description|_. Notes|
-|state|string|The allowed states are "Queued", "Locked", "Running", "Cancelled" and "Complete".|See "Container states":#container_states for more details.|
-|started_at|datetime|When this container started running.|Null if container has not yet started.|
-|finished_at|datetime|When this container finished.|Null if container has not yet finished.|
-|log|string|Portable data hash of the collection containing logs from a completed container run.|Null if the container is not yet finished.|
-|environment|hash|Environment variables and values that should be set in the container environment (@docker run --env@). This augments and (when conflicts exist) overrides environment variables given in the image's Dockerfile.|Must be equal to a ContainerRequest's environment in order to satisfy the ContainerRequest.|
-|cwd|string|Initial working directory.|Must be equal to a ContainerRequest's cwd in order to satisfy the ContainerRequest|
-|command|array of strings|Command to execute.| Must be equal to a ContainerRequest's command in order to satisfy the ContainerRequest.|
-|output_path|string|Path to a directory or file inside the container that should be preserved as this container's output when it finishes.|Must be equal to a ContainerRequest's output_path in order to satisfy the ContainerRequest.|
-|mounts|hash|Must contain the same keys as the ContainerRequest being satisfied. Each value must be within the range of values described in the ContainerRequest at the time the Container is assigned to the ContainerRequest.|See "Mount types":#mount_types for more details.|
-|runtime_constraints|hash|Compute resources, and access to the outside world, that are / were available to the container.
-Generally this will contain additional keys that are not present in any corresponding ContainerRequests: for example, even if no ContainerRequests specified constraints on the number of CPU cores, the number of cores actually used will be recorded here.|e.g.,
-<pre><code>{
-  "ram":12000000000,
-  "vcpus":2,
-  "API":true
-}</code></pre>See "Runtime constraints":#runtime_constraints for more details.|
-|output|string|Portable data hash of the output collection.|Null if the container is not yet finished.|
-|container_image|string|Portable data hash of a collection containing the docker image used to run the container.||
-|progress|number|A number between 0.0 and 1.0 describing the fraction of work done.||
-|priority|integer|Priority assigned by the system, taking into account the priorities of all associated ContainerRequests.||
-|exit_code|integer|Process exit code.|Null if state!="Complete"|
-|auth_uuid|string|UUID of a token to be passed into the container itself, used to access Keep-backed mounts, etc.|Null if state∉{"Locked","Running"}|
-|locked_by_uuid|string|UUID of a token, indicating which dispatch process changed state to Locked. If null, any token can be used to lock. If not null, only the indicated token can modify this container.|Null if state∉{"Locked","Running"}|
-
-h2(#container_states). Container states
-
-table(table table-bordered table-condensed).
-|_. State|_. Sgnificance|_. Allowed next|
-|Queued|Waiting for a dispatcher to lock it and try to run the container.|Locked, Cancelled|
-|Locked|A dispatcher has "taken" the container and is allocating resources for it. The container has not started yet.|Queued, Running, Cancelled|
-|Running|Resources have been allocated and the contained process has been started (or is about to start). Crunch-run _must_ set state to Running _before_ there is any possibility that user code will run in the container.|Complete, Cancelled|
-|Complete|Container was running, and the contained process/command has exited.|-|
-|Cancelled|The container did not run long enough to produce an exit code. This includes cases where the container didn't even start, cases where the container was interrupted/killed before it exited by itself (e.g., priority changed to 0), and cases where some problem prevented the system from capturing the contained process's exit status (exit code and output).|-|
-
-h2(#mount_types). {% include 'mount_types' %}
-
-h2(#runtime_constraints). {% include 'container_runtime_constraints' %}
diff --git a/doc/api/schema/ContainerRequest.html.textile.liquid b/doc/api/schema/ContainerRequest.html.textile.liquid
deleted file mode 100644 (file)
index 48c624a..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
----
-layout: default
-navsection: api
-navmenu: Schema
-title: ContainerRequest
-
-...
-
-A ContainerRequest:
-* Is a client's expression of interest in knowing the outcome of a computational process.
-* The system is responsible for finding suitable containers and assigning them to container_requests.
-* The client's description of the ContainerRequest is less precise than of a Container: a ContainerRequest describes container constraints which can have different interpretations over time. For example, a ContainerRequest with a {"kind":"git_tree","commit_range":"abc123..master",...} mount might be satisfiable by any of several different source trees, and this set of satisfying source trees can change when the repository's "master" branch is updated.
-
-h2. Methods
-
-See "container_requests":{{site.baseurl}}/api/methods/container_requests.html
-
-h2. Resource
-
-Each ContainerRequest offers the following attributes, in addition to the usual "attributes of Arvados resources":{{site.baseurl}}/api/resources.html:
-
-All attributes are optional, unless otherwise marked as required.
-
-table(table table-bordered table-condensed).
-|_. Attribute|_. Type|_. Description|_. Notes|
-|name|string|The name of the container_request.||
-|description|string|The description of the container_request.||
-|properties|hash|Client-defined structured data that does not affect how the container is run.||
-|state|string|The allowed states are "Uncommitted", "Committed", and "Final".|Once a request is Committed, the only attributes that can be modified are priority, container_uuid, and container_count_max. A request in the "Final" state cannot have any of its functional parts modified (i.e., only name, description, and properties fields can be modified).|
-|requesting_container_uuid|string|The uuid of the parent container that created this container_request, if any. Represents a process tree.|The priority of this container_request is inherited from the parent container, if the parent container is cancelled, this container_request will be cancelled as well.|
-|container_uuid|string|The uuid of the container that satisfies this container_request. The system will find and reuse any preexisting Container that matches this ContainerRequest's criteria. See "Container reuse":#container_reuse for more details.|Currently, container reuse is the default behavior and a mechanism to skip reuse is not supported.|
-|container_count_max|integer|Maximum number of containers to start, i.e., the maximum number of "attempts" to be made.||
-|mounts|hash|Objects to attach to the container's filesystem and stdin/stdout.|See "Mount types":#mount_types for more details.|
-|runtime_constraints|hash|Restrict the container's access to compute resources and the outside world.|Required when in "Committed" state. e.g.,<pre><code>{
-  "ram":12000000000,
-  "vcpus":2,
-  "API":true
-}</code></pre>See "Runtime constraints":#runtime_constraints for more details.|
-|container_image|string|Portable data hash of a collection containing the docker image to run the container.|Required.|
-|environment|hash|Environment variables and values that should be set in the container environment (@docker run --env@). This augments and (when conflicts exist) overrides environment variables given in the image's Dockerfile.||
-|cwd|string|Initial working directory, given as an absolute path (in the container) or a path relative to the WORKDIR given in the image's Dockerfile.|Required.|
-|command|array of strings|Command to execute in the container.|Required. e.g., @["echo","hello"]@|
-|output_path|string|Path to a directory or file inside the container that should be preserved as container's output when it finishes. This path must be, or be inside, one of the mount targets. For best performance, point output_path to a writable collection mount.|Required.|
-|priority|integer|Higher value means spend more resources on this container_request, i.e., go ahead of other queued containers, bring up more nodes etc.|Priority 0 means a container should not be run on behalf of this request. Clients are expected to submit ContainerRequests with zero priority in order to prevew the container that will be used to satisfy it. Priority can be null if and only if state!="Committed".|
-|expires_at|datetime|After this time, priority is considered to be zero.|Not yet implemented.|
-|filters|string|Additional constraints for satisfying the container_request, given in the same form as the filters parameter accepted by the container_requests.list API.||
-
-h2(#mount_types). {% include 'mount_types' %}
-
-h2(#runtime_constraints). {% include 'container_runtime_constraints' %}
-
-h2(#container_reuse). Container reuse
-
-When a ContainerRequest is "Committed", the system will try to find and reuse any preexisting Container with the same exact command, cwd, environment, output_path, container_image, mounts, and runtime_constraints as this ContainerRequest. The serialized fields environment, mounts and runtime_constraints are sorted to facilitate comparison.
-
-The system will use the following scheme to determine which Container to consider for reuse: A Container with the same exact command, cwd, environment, output_path, container_image, mounts, and runtime_constraints as this ContainerRequest and,
-* The oldest successfully finished container, i.e., in state "Complete" with exit_code of 0. If matching containers with different outputs are found, the system will forgo reusing any of these finished containers and instead look for suitable containers in other states
-* The oldest "Running" container with the highest progress, i.e., the container that is most likely to finish first
-* The oldest "Locked" container with the highest priority, i.e., the container that is most likely to start first
-* The oldest "Queued" container with the highest priority, i.e, the container that is most likely to start first
-
-{% include 'notebox_begin' %}
-Currently, container reuse is the default behavior and a mechanism to skip reuse is not supported.
-{% include 'notebox_end' %}
-
-h2(#cancel_container). Canceling a ContainerRequest
-
-A ContainerRequest may be canceled by setting it's priority to 0, using an update call.
-
-When a ContainerRequest is canceled, it will still reflect the state of the Container it is associated with via the container_uuid attribute. If that Container is being reused by any other container_requests that are still active, i.e., not yet canceled, that Container may continue to run or be scheduled to run by the system in future. However, if no other container_requests are using that Contianer, then the Container will get canceled as well.
diff --git a/doc/api/schema/Group.html.textile.liquid b/doc/api/schema/Group.html.textile.liquid
deleted file mode 100644 (file)
index 2bf67eb..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
----
-layout: default
-navsection: api
-navmenu: Schema
-title: Group
-
-...
-
-A **Group** represents a set of objects. Groups allow you to organize content, define user roles, and apply permissions to sets of objects.
-
-h2. Methods
-
-See "groups":{{site.baseurl}}/api/methods/groups.html
-
-h2. Resource
-
-Each Group has, in addition to the usual "attributes of Arvados resources":{{site.baseurl}}/api/resources.html:
-
-table(table table-bordered table-condensed).
-|_. Attribute|_. Type|_. Description|_. Example|
-|name|string|||
-|group_class|string|Type of group. This does not affect behavior, but determines how the group is presented in the user interface. For example, @project@ indicates that the group should be displayed by Workbench and arv-mount as a project for organizing and naming objects.|@"project"@
-null|
-|description|text|||
-|writable_by|array|List of UUID strings identifying Users and other Groups that have write permission for this Group.  Only users who are allowed to administer the Group will receive a full list.  Other users will receive a partial list that includes the Group's owner_uuid and (if applicable) their own user UUID.||
diff --git a/doc/api/schema/Human.html.textile.liquid b/doc/api/schema/Human.html.textile.liquid
deleted file mode 100644 (file)
index 361e619..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
----
-layout: default
-navsection: api
-navmenu: Schema
-title: Human
-
-...
-
-h2. Methods
-
-See "humans":{{site.baseurl}}/api/methods/humans.html
-
-h2. Resource
-
-Each Human has, in addition to the usual "attributes of Arvados resources":{{site.baseurl}}/api/resources.html:
-
-table(table table-bordered table-condensed).
-|_. Attribute|_. Type|_. Description|_. Example|
-|properties|hash|||
diff --git a/doc/api/schema/Job.html.textile.liquid b/doc/api/schema/Job.html.textile.liquid
deleted file mode 100644 (file)
index 58b6a51..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
----
-layout: default
-navsection: api
-navmenu: Schema
-title: Job
-
-...
-
-Applications submit compute jobs when:
-* Provenance is important, i.e., it is worth recording how the output was produced; or
-* Computation time is significant; or
-* The job management features are convenient (failure detection/recovery, regression testing, etc).
-
-h2. Methods
-
-See "jobs":{{site.baseurl}}/api/methods/jobs.html
-
-h2. Resource
-
-Each job has, in addition to the usual "attributes of Arvados resources":{{site.baseurl}}/api/resources.html:
-
-table(table table-bordered table-condensed).
-|_. Attribute|_. Type|_. Description|_. Notes|
-|script|string|The filename of the job script.|This program will be invoked by Crunch for each job task. It is given as a path to an executable file, relative to the @/crunch_scripts@ directory in the Git tree specified by the _repository_ and _script_version_ attributes.|
-|script_parameters|hash|The input parameters for the job.|Conventionally, one of the parameters is called @"input"@. Typically, some parameter values are collection UUIDs. Ultimately, though, the significance of parameters is left entirely up to the script itself.|
-|repository|string|Git repository name or URL.|Source of the repository where the given script_version is to be found. This can be given as the name of a locally hosted repository, or as a publicly accessible URL starting with @git://@, @http://@, or @https://@.
-Examples:
-@yourusername/yourrepo@
-@https://github.com/curoverse/arvados.git@|
-|script_version|string|Git commit|During a **create** transaction, this is the Git branch, tag, or hash supplied by the client. Before the job starts, Arvados updates it to the full 40-character SHA-1 hash of the commit used by the job.
-See "Specifying Git versions":#script_version below for more detail about acceptable ways to specify a commit.|
-|cancelled_by_client_uuid|string|API client ID|Is null if job has not been cancelled|
-|cancelled_by_user_uuid|string|Authenticated user ID|Is null if job has not been cancelled|
-|cancelled_at|datetime|When job was cancelled|Is null if job has not been cancelled|
-|started_at|datetime|When job started running|Is null if job has not [yet] started|
-|finished_at|datetime|When job finished running|Is null if job has not [yet] finished|
-|running|boolean|Whether the job is running||
-|success|boolean|Whether the job indicated successful completion|Is null if job has not finished|
-|is_locked_by_uuid|string|UUID of the user who has locked this job|Is null if job is not locked. The system user locks the job when starting the job, in order to prevent job attributes from being altered.|
-|node_uuids|array|List of UUID strings for node objects that have been assigned to this job||
-|log|string|Collection UUID|Is null if the job has not finished. After the job runs, the given collection contains a text file with log messages provided by the @arv-crunch-job@ task scheduler as well as the standard error streams provided by the task processes.|
-|tasks_summary|hash|Summary of task completion states.|Example: @{"done":0,"running":4,"todo":2,"failed":0}@|
-|output|string|Collection UUID|Is null if the job has not finished.|
-|nondeterministic|boolean|The job is expected to produce different results if run more than once.|If true, this job will not be considered as a candidate for automatic re-use when submitting subsequent identical jobs.|
-|submit_id|string|Unique ID provided by client when job was submitted|Optional. This can be used by a client to make the "jobs.create":{{site.baseurl}}/api/methods/jobs.html#create method idempotent.|
-|priority|string|||
-|arvados_sdk_version|string|Git commit hash that specifies the SDK version to use from the Arvados repository|This is set by searching the Arvados repository for a match for the arvados_sdk_version runtime constraint.|
-|docker_image_locator|string|Portable data hash of the collection that contains the Docker image to use|This is set by searching readable collections for a match for the docker_image runtime constraint.|
-|runtime_constraints|hash|Constraints that must be satisfied by the job/task scheduler in order to run the job.|See below.|
-|components|hash|Name and uuid pairs representing the child work units of this job. The uuids can be of different object types.|Example components hash: @{"name1": "zzzzz-8i9sb-xyz...", "name2": "zzzzz-d1hrv-xyz...",}@|
-
-h3(#script_version). Specifying Git versions
-
-The script_version attribute and arvados_sdk_version runtime constraint are typically given as a branch, tag, or commit hash, but there are many more ways to specify a Git commit. The "specifying revisions" section of the "gitrevisions manual page":http://git-scm.com/docs/gitrevisions.html has a definitive list. Arvados accepts Git versions in any format listed there that names a single commit (not a tree, a blob, or a range of commits). However, some kinds of names can be expected to resolve differently in Arvados than they do in your local repository. For example, <code>HEAD@{1}</code> refers to the local reflog, and @origin/master@ typically refers to a remote branch: neither is likely to work as desired if given as a Git version.
-
-h3. Runtime constraints
-
-table(table table-bordered table-condensed).
-|_. Key|_. Type|_. Description|_. Implemented|
-|arvados_sdk_version|string|The Git version of the SDKs to use from the Arvados git repository.  See "Specifying Git versions":#script_version for more detail about acceptable ways to specify a commit.  If you use this, you must also specify a @docker_image@ constraint (see below).  In order to install the Python SDK successfully, Crunch must be able to find and run virtualenv inside the container.|&#10003;|
-|docker_image|string|The Docker image that this Job needs to run.  If specified, Crunch will create a Docker container from this image, and run the Job's script inside that.  The Keep mount and work directories will be available as volumes inside this container.  The image must be uploaded to Arvados using @arv keep docker@.  You may specify the image in any format that Docker accepts, such as @arvados/jobs@, @debian:latest@, or the Docker image id.  Alternatively, you may specify the portable data hash of the image Collection.|&#10003;|
-|min_nodes|integer||&#10003;|
-|max_nodes|integer|||
-|min_cores_per_node|integer|Require that each node assigned to this Job have the specified number of CPU cores|&#10003;|
-|min_ram_mb_per_node|integer|Require that each node assigned to this Job have the specified amount of real memory (in MiB)|&#10003;|
-|min_scratch_mb_per_node|integer|Require that each node assigned to this Job have the specified amount of scratch storage available (in MiB)|&#10003;|
-|max_tasks_per_node|integer|Maximum simultaneous tasks on a single node|&#10003;|
-|keep_cache_mb_per_task|integer|Size of file data buffer for per-task Keep directory ($TASK_KEEPMOUNT), in MiB.  Default is 256 MiB.  Increase this to reduce cache thrashing in situtations such as accessing multiple large (64+ MiB) files at the same time, or accessing different parts of a large file at the same time.|&#10003;|
-|min_ram_per_task|integer|Minimum real memory (KiB) per task||
diff --git a/doc/api/schema/JobTask.html.textile.liquid b/doc/api/schema/JobTask.html.textile.liquid
deleted file mode 100644 (file)
index fbd4343..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
----
-layout: default
-navsection: api
-navmenu: Schema
-title: JobTask
-
-...
-
-A Job Task is a well defined independently-computable portion of a "Job":Job.html.
-
-Job tasks are created two ways:
-* When a job starts, it is seeded with a job task with @sequence=0@ and an empty @parameters{}@ list.
-* Job task A can create additional job tasks B, C, D, which will belong to the same job. Tasks B, C, D will not be performed until job task A is complete. If job task A fails, tasks B, C, D will be deleted.
-
-Job tasks have particular update semantics:
-* Progress reporting: A job task should only be <code>PATCH</code>ed by a worker process which has been dispatched to work on that task and is reporting progress or completion status &mdash; and by the job manager itself.
-* Completion: When a job task process terminates, the task is considered complete only if its most recent @PATCH@ transaction had @progress=1.0@ and @success=true@.
-* Temporary failure: If a job task process terminates without updating @success@ to @true@ or @false@, it is assumed that the task failed but is worth re-attempting (at a different time, on a different node, etc).
-
-
-h2. Methods
-
-See "job_tasks":{{site.baseurl}}/api/methods/job_tasks.html
-
-h2. Resource
-
-Each JobTask has, in addition to the usual "attributes of Arvados resources":{{site.baseurl}}/api/resources.html:
-
-table(table table-bordered table-condensed).
-|_. Attribute|_. Type|_. Description|_. Example|
-|sequence|integer|Execution sequence.
-A step cannot be run until all steps with lower sequence numbers have completed.
-Job steps with the same sequence number can be run in any order.||
-|parameters|hash|||
-|output|text|||
-|progress|float|||
-|success|boolean|Is null if the task has neither completed successfully nor failed permanently.||
-
-The following attributes should not be updated by anyone other than the job manager:
-
-table(table table-bordered table-condensed).
-|_. Attribute|_. Type|_. Description|_. Notes|
-|qsequence|integer|Order of arrival|0-based|
-|job_uuid|string|||
-|created_by_job_task_uuid|string|||
-
-
diff --git a/doc/api/schema/KeepDisk.html.textile.liquid b/doc/api/schema/KeepDisk.html.textile.liquid
deleted file mode 100644 (file)
index c128c3e..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
----
-layout: default
-navsection: api
-navmenu: Schema
-title: KeepDisk
-
-...
-
-A **KeepDisk** is a filesystem volume used by a Keep storage server to store data blocks.
-
-h2. Methods
-
-See "keep_disks":{{site.baseurl}}/api/methods/keep_disks.html
-
-h2. Resource
-
-Each KeepDisk has, in addition to the usual "attributes of Arvados resources":{{site.baseurl}}/api/resources.html:
-
-table(table table-bordered table-condensed).
-|_. Attribute|_. Type|_. Description|_. Example|
-|ping_secret|string|||
-|node_uuid|string|||
-|filesystem_uuid|string|||
-|bytes_total|integer|||
-|bytes_free|integer|||
-|is_readable|boolean|||
-|is_writable|boolean|||
-|last_read_at|datetime|||
-|last_write_at|datetime|||
-|last_ping_at|datetime|||
-|keep_service_uuid|string|||
diff --git a/doc/api/schema/KeepService.html.textile.liquid b/doc/api/schema/KeepService.html.textile.liquid
deleted file mode 100644 (file)
index ac1d974..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
----
-layout: default
-navsection: api
-navmenu: Schema
-title: KeepService
-
-...
-
-A **KeepService** is a service endpoint that supports the Keep protocol.
-
-h2. Methods
-
-See "keep_services":{{site.baseurl}}/api/methods/keep_services.html
-
-h2. Resource
-
-Each KeepService has, in addition to the usual "attributes of Arvados resources":{{site.baseurl}}/api/resources.html:
-
-table(table table-bordered table-condensed).
-|_. Attribute|_. Type|_. Description|_. Example|
-|service_host|string|||
-|service_port|integer|||
-|service_ssl_flag|boolean|||
-|service_type|string|||
\ No newline at end of file
diff --git a/doc/api/schema/Link.html.textile.liquid b/doc/api/schema/Link.html.textile.liquid
deleted file mode 100644 (file)
index 4abfdbc..0000000
+++ /dev/null
@@ -1,83 +0,0 @@
----
-layout: default
-navsection: api
-navmenu: Schema
-title: Link
-
-...
-
-**Links** describe relationships between Arvados objects, and from objects to primitives.
-
-Links are directional: each metadata object has a tail (the "subject" being described), class, name, properties, and head (the "object" that describes the "subject").  A Link may describe a relationship between two objects in an Arvados database: e.g. a _permission_ link between a User and a Group defines the permissions that User has to read or modify the Group.  Other Links simply represent metadata for a single object, e.g. the _identifier_ Link, in which the _name_ property represents a human-readable identifier for the object at the link's head.
-
-For links that don't make sense to share between API clients, a _link_class_ that begins with @client@ (like @client.my_app_id@ or @client.my_app_id.anythinghere@) should be used.
-
-h2. Methods
-
-See "links":{{site.baseurl}}/api/methods/links.html
-
-h2. Resource
-
-Each link has, in addition to the usual "attributes of Arvados resources":{{site.baseurl}}/api/resources.html:
-
-table(table table-bordered table-condensed).
-|_. Attribute|_. Type|_. Description|
-|tail_uuid|string|Object UUID at the tail (start, source, origin) of this link|
-|link_class|string|Class (see below)|
-|name|string|Link type (see below)|
-|head_uuid|string|Object UUID at the head (end, destination, target) of this link|
-|properties|hash|Additional information, expressed as a key&rarr;value hash. Key: string. Value: string, number, array, or hash.|
-
-h2. Link classes
-
-Some classes are pre-defined by convention and have standard meanings attached to names.
-
-h3. provenance
-
-table(table table-bordered table-condensed).
-|_. tail_type&rarr;head_type|_. name&rarr;head_uuid {properties}|_. Notes|
-|&rarr;Collection  |provided &rarr; _collection uuid_
-{url&rarr;http://example.com/foo.tgz, retrieved_at&rarr;1352616640.000}||
-|Job&rarr;Collection     |provided &rarr; _collection uuid_||
-|Specimen&rarr;Collection|provided &rarr; _collection uuid_||
-|Human&rarr;Specimen     |provided &rarr; _specimen uuid_||
-|Human&rarr;Collection   |provided &rarr; _collection uuid_||
-
-h3. permission
-
-table(table table-bordered table-condensed).
-|_. tail_type&rarr;head_type|_. name&rarr;head_uuid {properties}|_. Notes|
-|User&rarr;Group  |{white-space:nowrap}. can_manage &rarr; _group uuid_|The User can read, write, and control permissions on the Group itself, every object owned by the Group, and every object on which the Group has _can_manage_ permission.|
-|User&rarr;Group  |can_read &rarr; _group uuid_  |The User can retrieve the Group itself and every object that is readable by the Group.|
-|User&rarr;Job|can_write &rarr; _job uuid_  |The User can read and update the Job. (This works for all objects, not just jobs.)|
-|User&rarr;Job|can_manage &rarr; _job uuid_  |The User can read, update, and change permissions for the Job. (This works for all objects, not just jobs.)|
-|Group&rarr;Job|can_manage &rarr; _job uuid_  |Anyone with _can_manage_ permission on the Group can also read, update, and change permissions for the Job. Anyone with _can_read_ permission on the Group can read the Job. (This works for all objects, not just jobs.)|
-
-h3. resources
-
-table(table table-bordered table-condensed).
-|_. tail_type&rarr;head_type|_. name&rarr;head_uuid {properties}|_. Notes|
-|User&rarr;Collection|wants &rarr; _collection uuid_    |Determines whether data can be deleted|
-|User&rarr;Job       |wants &rarr; _job uuid_    |Determines whether a job can be cancelled|
-
-h3. tag
-
-A **tag** link describes an object using an unparsed plain text string. Tags can be used to annotate objects that are not editable, like collections and objects shared as read-only.
-
-table(table table-bordered table-condensed).
-|_. tail_type&rarr;head_type|_. name&rarr;head_uuid {properties}|
-|&rarr;Collection           | _tag name_ &rarr; _collection uuid_|
-|&rarr;Job                  | _tag name_ &rarr; _job uuid_|
-
-h3. human_trait
-
-table(table table-bordered table-condensed).
-|_. tail_type&rarr;head_type|_. name&rarr;head_uuid {properties}|_. Notes|
-|Human&rarr;Trait  |measured &rarr; _trait uuid_ {value&rarr;1.83, unit&rarr;metre, measured_at&rarr;1352616640.000}||
-
-h3. identifier
-
-table(table table-bordered table-condensed).
-|_. tail_type&rarr;head_type|_. name&rarr;head_uuid {properties}|_. Notes|
-|&rarr;Human        |hu123456 &rarr; _human uuid_||
-
diff --git a/doc/api/schema/Log.html.textile.liquid b/doc/api/schema/Log.html.textile.liquid
deleted file mode 100644 (file)
index 425246a..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
----
-layout: default
-navsection: api
-navmenu: Schema
-title: Log
-
-...
-
-**Log** objects record events that occur in an Arvados cluster. Both user-written pipelines and the Arvados system itself may generate Log events.
-
-h2. Methods
-
-See "logs":{{site.baseurl}}/api/methods/logs.html
-
-h2. Creation
-
-Any user may create Log entries for any event they find useful. User-generated Logs have no intrinsic meaning to other users or to the Arvados system itself; it is up to each user to choose appropriate log event types and summaries for their project.
-
-h3. System Logs
-
-Arvados uses Logs to record creation, deletion, and updates of other Arvados resources.
-
-h2. Resource
-
-Each Log has, in addition to the usual "attributes of Arvados resources":{{site.baseurl}}/api/resources.html:
-
-table(table table-bordered table-condensed).
-|_. Attribute|_. Type|_. Description|_. Example|
-|object_uuid|string|||
-|event_at|datetime|||
-|event_type|string|A user-defined category or type for this event.|@LOGIN@|
-|summary|text|||
-|properties|hash|||
diff --git a/doc/api/schema/Node.html.textile.liquid b/doc/api/schema/Node.html.textile.liquid
deleted file mode 100644 (file)
index ff9e882..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
----
-layout: default
-navsection: api
-navmenu: Schema
-title: Node
-
-...
-
-A **Node** represents a host that can be used to run Crunch job tasks.
-
-h2. Methods
-
-See "nodes":{{site.baseurl}}/api/methods/nodes.html
-
-h2. Resource
-
-Each Node has, in addition to the usual "attributes of Arvados resources":{{site.baseurl}}/api/resources.html:
-
-table(table table-bordered table-condensed).
-|_. Attribute|_. Type|_. Description|_. Example|
-|slot_number|integer|||
-|hostname|string|||
-|domain|string|||
-|ip_address|string|||
-|job_uuid|string|The UUID of the job that this node is assigned to work on.  If you do not have permission to read the job, this will be null.||
-|first_ping_at|datetime|||
-|last_ping_at|datetime|||
-|info|hash|||
diff --git a/doc/api/schema/PipelineInstance.html.textile.liquid b/doc/api/schema/PipelineInstance.html.textile.liquid
deleted file mode 100644 (file)
index 75c7885..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
----
-layout: default
-navsection: api
-navmenu: Schema
-title: PipelineInstance
-
-...
-
-A **PipelineInstance** is the act or record of applying a pipeline template to a specific set of inputs; generally, a pipeline instance refers to a set of jobs that have been run to satisfy the pipeline components.
-
-h2. Methods
-
-See "pipeline_instances":{{site.baseurl}}/api/methods/pipeline_instances.html
-
-h2. Resource
-
-Each PipelineInstance has, in addition to the usual "attributes of Arvados resources":{{site.baseurl}}/api/resources.html:
-
-table(table table-bordered table-condensed).
-|_. Attribute|_. Type|_. Description|_. Example|
-|pipeline_template_uuid|string|||
-|name|string|||
-|components|hash|||
-|success|boolean|||
-|active|boolean|||
-|properties|Hash|||
diff --git a/doc/api/schema/PipelineTemplate.html.textile.liquid b/doc/api/schema/PipelineTemplate.html.textile.liquid
deleted file mode 100644 (file)
index 444960a..0000000
+++ /dev/null
@@ -1,161 +0,0 @@
----
-layout: default
-navsection: api
-navmenu: Schema
-title: PipelineTemplate
-...
-
-The pipeline template consists of "name" and "components".
-
-table(table table-bordered table-condensed).
-|_. Attribute    |_. Type |_. Accepted values                           |_. Required|_. Description|
-|name            |string  |any                                          |yes        |The human-readable name of the pipeline template.|
-|components      |object  |JSON object containing job submission objects|yes        |The component jobs that make up the pipeline, with the component name as the key. |
-
-h3. Components
-
-The components field of the pipeline template is a JSON object which describes the individual steps that make up the pipeline.  Each component is an Arvados job submission.  "Parameters for job submissions are described on the job method page.":{{site.baseurl}}/api/methods/jobs.html#create  In addition, a component can have the following parameters:
-
-table(table table-bordered table-condensed).
-|_. Attribute    |_. Type          |_. Accepted values |_. Required|_. Description|
-|output_name     |string or boolean|string or false    |no         |If a string is provided, use this name for the output collection of this component.  If the value is false, do not create a permanent output collection (an temporary intermediate collection will still be created).  If not provided, a default name will be assigned to the output.|
-
-h3. Script parameters
-
-When used in a pipeline, each parameter in the 'script_parameters' attribute of a component job can specify that the input parameter must be supplied by the user, or the input parameter should be linked to the output of another component.  To do this, the value of the parameter should be JSON object containing one of the following attributes:
-
-table(table table-bordered table-condensed).
-|_. Attribute    |_. Type |_. Accepted values                               |_. Description|
-|default         |any     |any                                              |The default value for this parameter.|
-|required        |boolean |true or false                                    |Specifies whether the parameter is required to have a value or not.|
-|dataclass       |string  |One of 'Collection', 'File' [1], 'number', or 'text' |Data type of this parameter.|
-|search_for      |string  |any string                                       |Substring to use as a default search string when choosing inputs.|
-|output_of       |string  |the name of another component in the pipeline    |Specifies that the value of this parameter should be set to the 'output' attribute of the job that corresponds to the specified component.|
-|title           |string  |any string                                       |User friendly title to display when choosing parameter values|
-|description     |string  |any string                                       |Extended text description for describing expected/valid values for the script parameter|
-|link_name       |string  |any string                                       |User friendly name to display for the parameter value instead of the actual parameter value|
-
-The 'output_of' parameter is especially important, as this is how components are actually linked together to form a pipeline.  Component jobs that depend on the output of other components do not run until the parent job completes and has produced output.  If the parent job fails, the entire pipeline fails.
-
-fn1. The 'File' type refers to a specific file within a Keep collection in the form 'collection_hash/filename', for example '887cd41e9c613463eab2f0d885c6dd96+83/bob.txt'.
-
-The 'search_for' parameter is meaningful only when input dataclass of type Collection or File is used. If a value is provided, this will be preloaded into the input data chooser dialog in Workbench. For example, if your input dataclass is a File and you are interested in a certain filename extention, you can preconfigure it in this attribute.
-
-h3. Examples
-
-This is a pipeline named "Filter MD5 hash values" with two components, "do_hash" and "filter".  The "input" script parameter of the "do_hash" component is required to be filled in by the user, and the expected data type is "Collection".  This also specifies that the "input" script parameter of the "filter" component is the output of "do_hash", so "filter" will not run until "do_hash" completes successfully.  When the pipeline runs, past jobs that meet the criteria described above may be substituted for either or both components to avoid redundant computation.
-
-<notextile><pre>
-{
-  "name": "Filter MD5 hash values",
-  "components": {
-    "do_hash": {
-      "script": "hash.py",
-      "repository": "<b>you</b>/<b>you</b>",
-      "script_version": "master",
-      "script_parameters": {
-        "input": {
-          "required": true,
-          "dataclass": "Collection",
-          "search_for": ".fastq.gz",
-          "title":"Please select a fastq file"
-        }
-      },
-    },
-    "filter": {
-      "script": "0-filter.py",
-      "repository": "<b>you</b>/<b>you</b>",
-      "script_version": "master",
-      "script_parameters": {
-        "input": {
-          "output_of": "do_hash"
-        }
-      },
-    }
-  }
-}
-</pre></notextile>
-
-This pipeline consists of three components.  The components "thing1" and "thing2" both depend on "cat_in_the_hat".  Once the "cat_in_the_hat" job is complete, both "thing1" and "thing2" can run in parallel, because they do not depend on each other.
-
-<notextile><pre>
-{
-  "name": "Wreck the house",
-  "components": {
-    "cat_in_the_hat": {
-      "script": "cat.py",
-      "repository": "<b>you</b>/<b>you</b>",
-      "script_version": "master",
-      "script_parameters": { }
-    },
-    "thing1": {
-      "script": "thing1.py",
-      "repository": "<b>you</b>/<b>you</b>",
-      "script_version": "master",
-      "script_parameters": {
-        "input": {
-          "output_of": "cat_in_the_hat"
-        }
-      },
-    },
-    "thing2": {
-      "script": "thing2.py",
-      "repository": "<b>you</b>/<b>you</b>",
-      "script_version": "master",
-      "script_parameters": {
-        "input": {
-          "output_of": "cat_in_the_hat"
-        }
-      },
-    },
-  }
-}
-</pre></notextile>
-
-This pipeline consists of three components.  The component "cleanup" depends on "thing1" and "thing2".  Both "thing1" and "thing2" are started immediately and can run in parallel, because they do not depend on each other, but "cleanup" cannot begin until both "thing1" and "thing2" have completed.
-
-<notextile><pre>
-{
-  "name": "Clean the house",
-  "components": {
-    "thing1": {
-      "script": "thing1.py",
-      "repository": "<b>you</b>/<b>you</b>",
-      "script_version": "master",
-      "script_parameters": { }
-    },
-    "thing2": {
-      "script": "thing2.py",
-      "repository": "<b>you</b>/<b>you</b>",
-      "script_version": "master",
-      "script_parameters": { }
-    },
-    "cleanup": {
-      "script": "cleanup.py",
-      "repository": "<b>you</b>/<b>you</b>",
-      "script_version": "master",
-      "script_parameters": {
-        "mess1": {
-          "output_of": "thing1"
-        },
-        "mess2": {
-          "output_of": "thing2"
-        }
-      }
-    }
-  }
-}
-</pre></notextile>
-
-h2. Methods
-
-See "pipeline_templates":{{site.baseurl}}/api/methods/pipeline_templates.html
-
-h2. Resource
-
-Each PipelineTemplate has, in addition to the usual "attributes of Arvados resources":{{site.baseurl}}/api/resources.html:
-
-table(table table-bordered table-condensed).
-|_. Attribute|_. Type|_. Description|_. Example|
-|name|string|||
-|components|hash|||
diff --git a/doc/api/schema/Repository.html.textile.liquid b/doc/api/schema/Repository.html.textile.liquid
deleted file mode 100644 (file)
index 0f9b25e..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
----
-layout: default
-navsection: api
-navmenu: Schema
-title: Repository
-
-...
-
-A **Repository** represents a git repository hosted in an Arvados installation.
-
-h2. Methods
-
-See "repositories":{{site.baseurl}}/api/methods/repositories.html
-
-h2. Resource
-
-Each Repository has, in addition to the usual "attributes of Arvados resources":{{site.baseurl}}/api/resources.html:
-
-table(table table-bordered table-condensed).
-|_. Attribute|_. Type|_. Description|_. Example|
-|name|string|The name of the repository on disk.  Repository names must begin with a letter and contain only alphanumerics.  Unless the repository is owned by the system user, the name must begin with the owner's username, then be separated from the base repository name with @/@.  You may not create a repository that is owned by a user without a username.|@username/project1@|
-|clone_urls|array|URLs from which the repository can be cloned. Read-only.|@["git@git.zzzzz.arvadosapi.com:foo/bar.git",
- "https://git.zzzzz.arvadosapi.com/foo/bar.git"]@|
-|fetch_url|string|URL suggested as a fetch-url in git config. Deprecated. Read-only.||
-|push_url|string|URL suggested as a push-url in git config. Deprecated. Read-only.||
diff --git a/doc/api/schema/Specimen.html.textile.liquid b/doc/api/schema/Specimen.html.textile.liquid
deleted file mode 100644 (file)
index 1a7e483..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
----
-layout: default
-navsection: api
-navmenu: Schema
-title: Specimen
-
-...
-
-A **Specimen** represents a tissue sample or similar material obtained from a human that has some biomedical significance or interest.
-
-h2. Methods
-
-See "specimens":{{site.baseurl}}/api/methods/specimens.html
-
-h2. Resource
-
-Each Specimen has, in addition to the usual "attributes of Arvados resources":{{site.baseurl}}/api/resources.html:
-
-table(table table-bordered table-condensed).
-|_. Attribute|_. Type|_. Description|_. Example|
-|material|string|||
-|properties|hash|||
diff --git a/doc/api/schema/Trait.html.textile.liquid b/doc/api/schema/Trait.html.textile.liquid
deleted file mode 100644 (file)
index 80c74ab..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
----
-layout: default
-navsection: api
-navmenu: Schema
-title: Trait
-
-...
-
-A **Trait** represents a measured or observed characteristic of a human.
-
-h2. Methods
-
-See "traits":{{site.baseurl}}/api/methods/traits.html
-
-h2. Resource
-
-Each Trait has, in addition to the usual "attributes of Arvados resources":{{site.baseurl}}/api/resources.html:
-
-table(table table-bordered table-condensed).
-|_. Attribute|_. Type|_. Description|_. Example|
-|name|string|||
-|properties|hash|||
diff --git a/doc/api/schema/User.html.textile.liquid b/doc/api/schema/User.html.textile.liquid
deleted file mode 100644 (file)
index 335b6c1..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
----
-layout: default
-navsection: api
-navmenu: Schema
-title: User
-
-...
-
-A **User** represents a person who interacts with Arvados via an ApiClient.
-
-h2. Methods
-
-See "users":{{site.baseurl}}/api/methods/users.html
-
-h2. Resource
-
-Each User has, in addition to the usual "attributes of Arvados resources":{{site.baseurl}}/api/resources.html:
-
-table(table table-bordered table-condensed).
-|_. Attribute|_. Type|_. Description|_. Example|
-|email|string|||
-|username|string|The username used for the user's git repositories and virtual machine logins.  Usernames must start with a letter, and contain only alphanumerics.  When a new user is created, a default username is set from their e-mail address.  Only administrators may change the username.||
-|first_name|string|||
-|last_name|string|||
-|identity_url|string|||
-|is_admin|boolean|||
-|prefs|hash|||
-|default_owner_uuid|string|||
-|is_active|boolean|||
-|writable_by|array|List of UUID strings identifying Groups and other Users that can modify this User object.  This will include the user's owner_uuid and, for administrators and users requesting their own User object, the requesting user's UUID.||
diff --git a/doc/api/schema/VirtualMachine.html.textile.liquid b/doc/api/schema/VirtualMachine.html.textile.liquid
deleted file mode 100644 (file)
index 1c6a4b6..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
----
-layout: default
-navsection: api
-navmenu: Schema
-title: VirtualMachine
-
-...
-
-A **VirtualMachine** represents a network host, running within an Arvados installation, on which Arvados users are given login accounts.
-
-h2. Methods
-
-See "virtual_machines":{{site.baseurl}}/api/methods/virtual_machines.html
-
-h2. Resource
-
-Each VirtualMachine has, in addition to the usual "attributes of Arvados resources":{{site.baseurl}}/api/resources.html:
-
-table(table table-bordered table-condensed).
-|_. Attribute|_. Type|_. Description|_. Example|
-|hostname|string|||
diff --git a/doc/api/schema/Workflow.html.textile.liquid b/doc/api/schema/Workflow.html.textile.liquid
deleted file mode 100644 (file)
index 05cd998..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
----
-layout: default
-navsection: api
-navmenu: Schema
-title: Workflow
-
-...
-
-A *Workflow* is a definition of work to be performed by a Crunch2 process. It defines the steps and inputs for the process.
-
-h2. Methods
-
-See "workflows":{{site.baseurl}}/api/methods/workflows.html
-
-h2. Resource
-
-Each Workflow offers the following optional attributes, in addition to the usual "attributes of Arvados resources":{{site.baseurl}}/api/resources.html:
-
-table(table table-bordered table-condensed).
-|_. Attribute|_. Type|_. Description|_. Example|
-|name|string|If not specified, will be set to any "name" from the "definition" attribute.||
-|description|string|If not specified, will be set to any "description" from the "definition" attribute.||
-|definition|string|A "Common Workflow Language" document.|Visit "Common Workflow Language":http://www.commonwl.org/ for details.|
diff --git a/doc/api/storage.html.textile.liquid b/doc/api/storage.html.textile.liquid
new file mode 100644 (file)
index 0000000..868e5cc
--- /dev/null
@@ -0,0 +1,170 @@
+---
+layout: default
+navsection: api
+title: Storage in Keep
+...
+
+Keep clients are applications such as @arv-get@, @arv-put@ and @arv-mount@ which store and retrieve data from Keep.  In doing so, these programs interact with both the API server (which stores file metadata in form of Collection objects) and individual Keep servers (which store the actual data blocks).
+
+!{{site.baseurl}}/images/Keep_reading_writing_block.svg!
+
+h2. Storing a file
+
+# The client discovers keep servers (or proxies) using the @accessible@ method on "keep_services":{{site.baseurl}}/api/methods/keep_services.html
+# Data is split into 64 MiB blocks and the MD5 hash is computed for each block.
+# The client uploads each block to one or more Keep servers, based on the number of desired replicas.  The priority order is determined using rendezvous hashing, described below.
+# The Keep server returns a block locator (the MD5 sum of the block) and a "signed token" which the client can use as proof of knowledge for the block.
+# The client constructs a @manifest@ which lists the blocks by MD5 hash and how to reassemble them into the original files.
+# The client creates a "collection":{{site.baseurl}}/api/methods/collections.html and provides the @manifest_text@
+# The API server accepts the collection after validating the signed tokens (proof of knowledge) for each block.
+
+!{{site.baseurl}}/images/Keep_manifests.svg!
+
+h2. Fetching a file
+
+# The client requests a @collection@ object including @manifest_text@ from the APIs server
+# The server adds "token signatures" to the @manifest_text@ and returns it to the client.
+# The client discovers keep servers (or proxies) using the @accessible@ method on "keep_services":{{site.baseurl}}/api/methods/keep_services.html
+# For each data block, the client chooses the highest priority server using rendezvous hashing, described below.
+# The client sends the data block request to the keep server, along with the token signature from the API which proves to Keep servers that the client is permitted to read a given block.
+# The server provides the block data after validating the token signature for the block (if the server does not have the block, it returns a 404 and the client tries the next highest priority server)
+
+!{{site.baseurl}}/images/Keep_rendezvous_hashing.svg!
+
+Each @keep_service@ resource has an assigned uuid.  To determine priority assignments of blocks to servers, for each keep service compute the MD5 sum of the string concatenation of the block locator (hex-coded hash part only) and service uuid, then sort this list in descending order.  Blocks are preferentially placed on servers with the highest weight.
+
+h2. Keep server API
+
+The Keep server is accessed via a simple HTTP REST API.
+
+*GET /blocklocator+size+A@token*
+
+Fetch the data block.  Response returns block contents.  If permission checking is enabled, requires a valid token hint.
+
+*PUT /blocklocator*
+
+Body: the block contents.  Responds the block locator consisting of MD5 sum of the data, block size, and signed token hint.
+
+*POST /*
+
+Body: the block contents.  Responds the block locator consisting of MD5 sum of the data, block size, and signed token hint.
+
+h2(#locator). Keep locator format
+
+BNF notation for a valid Keep locator string (with hints).  For example @d41d8cd98f00b204e9800998ecf8427e+0+Z+Ada39a3ee5e6b4b0d3255bfef95601890afd80709@53bed294@
+
+<pre>
+locator        ::= sized-digest hint*
+sized-digest   ::= digest size-hint
+digest         ::= <32 lowercase hexadecimal digits>
+size-hint      ::= "+" [0-9]+
+hint           ::= "+" hint-type hint-content
+hint-type      ::= [A-Z]+
+hint-content   ::= [A-Za-z0-9@_-]*
+sign-hint      ::= "+A" <40 lowercase hexadecimal digits> "@" sign-timestamp
+sign-timestamp ::= <8 lowercase hexadecimal digits>
+</pre>
+
+h3. Token signatures
+
+A token signature (sign-hint) provides proof-of-access for a data block.  It is computed by taking a SHA1 HMAC of the blob signing token (a shared secret between the API server and keep servers), block digest, current API token, expiration timestamp, and blob signature TTL.
+
+When communicating with the Keep store to fetch a block, or the API server to create or update a collection, the service computes the expected token signature for each block and compares it to the token signature that was presented by the client.  Keep clients receive valid block signatures when uploading a block to a keep store (getting back a signed token as proof of knowledge) or, from the API server, getting the manifest text of a collection on which the user has read permission.
+
+Security of a token signature is derived from the following characteristics:
+
+# Valid signatures can only be generated by entities that know the shared secret (the "blob signing token")
+# A signature can only be used by an entity that also know the API token that was used to generate it.
+# It expires after a set date (the expiration time, based on the "blob signature time-to-live (TTL)")
+
+h3. Regular expression to validate locator
+
+<pre>
+/^([0-9a-f]{32})\+([0-9]+)(\+[A-Z][-A-Za-z0-9@_]*)*$/
+</pre>
+
+h3. Valid locators
+
+table(table table-bordered table-condensed).
+|@d41d8cd98f00b204e9800998ecf8427e+0@|
+|@d41d8cd98f00b204e9800998ecf8427e+0+Z@|
+|<code>d41d8cd98f00b204e9800998ecf8427e+0+Z+Ada39a3ee5e6b4b0d3255bfef95601890afd80709@53bed294</code>|
+
+h3. Invalid locators
+
+table(table table-bordered table-condensed).
+||Why|
+|@d41d8cd98f00b204e9800998ecf8427e@|No size hint|
+|@d41d8cd98f00b204e9800998ecf8427e+Z+0@|Other hint before size hint|
+|@d41d8cd98f00b204e9800998ecf8427e+0+0@|Multiple size hints|
+|@d41d8cd98f00b204e9800998ecf8427e+0+z@|Hint does not start with uppercase letter|
+|@d41d8cd98f00b204e9800998ecf8427e+0+Zfoo*bar@|Hint contains invalid character @*@|
+
+h2. Manifest v1
+
+A manifest is utf-8 encoded text, consisting of zero or more newline-terminated streams.
+
+<pre>
+manifest       ::= stream*
+stream         ::= stream-name (" " locator)+ (" " file-segment)+ "\n"
+stream-name    ::= "." ("/" path-component)*
+path-component ::= <printable ASCII - (whitespace, "/")>+
+file-segment   ::= position ":" size ":" filename
+position       ::= [0-9]+
+size           ::= [0-9]+
+filename       ::= path-component ("/" path-component)*
+</pre>
+
+Notes:
+
+* The first token is the stream name, consisting of one or more path components, delimited by @"/"@.
+** The first path component is always @"."@.
+** No path component is empty.
+** No path component following the first one can be "." or "..".
+** The stream name never begins or ends with @"/"@.
+* The next N tokens are "keep locators":#locator
+** These describe the "data stream".  By logically concatenating the blocks in the order that they appear, we can refer to "positions" in the data stream.
+* File tokens come after the sequence of keep locators.
+** A file token has three parts, delimited by @":"@: position, size, filename.
+** Position and size are given in decimal
+** The position is the position in the data stream
+** The size is the count of bytes following the position in the data stream.  A file size may cross multiple blocks in the data stream.
+** Filename may contain @"/"@ characters, but must not start or end with @"/"@, and must not contain @"//"@.
+** Filename components (delimited by @"/"@) must not be @"."@ or @".."@.
+** There may be multiple file tokens.
+
+It is legal to have multiple file tokens in the manifest (possible across different streams) with the same combined path name @stream name + "/" + filename@.  This must be interpreted as a concatenation of file content, in the order that the file tokens appear in the manifest.
+
+Spaces are represented by the escape sequence @\040@.  Spaces in stream names and filenames must be translated when reading and writing manifests.  A manifest may not contain TAB characters, nor other ASCII whitespace characters or control codes other than the spaces or newlines used as delimiters specified above.  A manifest always ends with a newline -- except the empty (zero-length) string, which is a valid manifest.
+
+h3. Normalized manifest v1
+
+A normalized manifest is a manifest that meets the following additional restrictions:
+
+* Streams are in alphanumeric order.
+* Each stream name is unique within the manifest.
+* Files within a stream are listed in alphanumeric order.
+* Blocks within a stream are ordered based on order of file tokens of the stream.  A given block is listed at most once in a stream.
+* Filename must not contain @"/"@ (the stream name represents the path prefix)
+
+h3. Example manifests
+
+A manifest with four files in two directories:
+
+<pre>
+. 930625b054ce894ac40596c3f5a0d947+33 0:0:a 0:0:b 0:33:output.txt
+./c d41d8cd98f00b204e9800998ecf8427e+0 0:0:d
+</pre>
+
+The same manifest with permission signatures on each block:
+
+<pre>
+. 930625b054ce894ac40596c3f5a0d947+33+A1f27a35dd9af37191d63ad8eb8985624451e7b79@5835c8bc 0:0:a 0:0:b 0:33:output.txt
+./c d41d8cd98f00b204e9800998ecf8427e+0+A27117dcd30c013a6e85d6d74c9a50179a1446efa@5835c8bc 0:0:d
+</pre>
+
+A manifest containing a file consisting of multiple blocks and a space in the file name:
+
+<pre>
+. c449ed86671e4a34a8b8b9430850beba+67108864 09fcfea01c3a141b89dd0dcfa1b7768e+22534144 0:89643008:Docker\040image.tar
+</pre>
diff --git a/doc/api/tokens.html.textile.liquid b/doc/api/tokens.html.textile.liquid
new file mode 100644 (file)
index 0000000..72661c3
--- /dev/null
@@ -0,0 +1,63 @@
+---
+layout: default
+navsection: api
+title: API Authorization
+...
+
+All requests to the API server must have an API token.  API tokens can be issued by going though the login flow, or created via the API.  At this time, only browser based applications can perform login from email/password.  Command line applications and services must use an API token provided via the @ARVADOS_API_TOKEN@ environment variable or configuration file.
+
+h2. Browser login
+
+Browser based applications can perform log in via the following highlevel flow:
+
+# The web application presents a "login" link to @/login@ on the API server with a @return_to@ parameter provided in the query portion of the URL.  For example @https://{{ site.arvados_api_host }}/login?return_to=XXX@ , where  @return_to=XXX@ is the URL of the login page for the web application.
+# The "login" link takes the browser to the login page (this may involve several redirects)
+# The user logs in.  API server authenticates the user and issues a new API token.
+# The browser is redirected to the login page URL provided in @return_to=XXX@ with the addition of @?api_token=xxxxapitokenxxxx@.
+# The web application gets the login request with the included authorization token.
+
+!{{site.baseurl}}/images/Session_Establishment.svg!
+
+The "browser authentication process is documented in detail on the Arvados wiki.":https://dev.arvados.org/projects/arvados/wiki/Workbench_authentication_process
+
+h2. Creating tokens via the API
+
+The browser login method above issues a new token.  Using that token, it is possible to make API calls to create additional tokens.  To do so, use the @create@ method of the "API client authorizations":{{site.baseurl}}/api/methods/api_client_authorizations.html resource.
+
+h2. Trusted API clients
+
+The "api_clients":{{site.baseurl}}/api/methods/api_clients.html resource determines if web applications that have gone through the browser login flow may create or list API tokens.
+
+After the user has authenticated, but before an authorization token is issued and browser redirect sent (sending the browser back to the @return_to@ login page bearing @api_token@), the server strips the path and query portion from @return_to@ to get @url_prefix@.  The @url_prefix@ is used to find or create an ApiClient object.  The newly issued API client authorization (API token) is associated with this ApiClient object.
+
+API clients may be marked as "trusted" by making an API call to create or update an "api_clients":{{site.baseurl}}/api/methods/api_clients.html resource and set the @is_trusted@ flag to @true@. An authorization token associated with a "trusted" client is permitted to list authorization tokens on "API client authorizations":{{site.baseurl}}/api/methods/api_client_authorizations.html .
+
+A authorization token which is not associated with a trusted client may only use the @current@ method to query its own api_client_authorization object.  The "untrusted" token is forbidden performing any other operations on API client authorizations, such as listing other authorizations or creating new authorizations.
+
+Authorization tokens which are not issued via the browser login flow (created directly via the API) will not have an associated api client.  This means authorization tokens created via the API are always "untrusted".
+
+h2(#scopes). Scopes
+
+Scopes can restrict a token so it may only access certain resources.  This is in addition to normal permission checks for the user associated with the token.
+
+Each entry in scopes consists of a @request_method@ and @request_path@, where the @request_method@ is a HTTP method (one of @GET@, @POST@, @PUT@ or @DELETE@) and @request_path@ is the request URI.  A given request is permitted if it matches a scopes exactly, or the scope ends with @/@ and the request string is a prefix of the scope.
+
+As a special case, a scope of ["all"] allows all resources.
+
+h3. Scope examples
+
+A scope of @GET /arvados/v1/collections@ permits listing collections.
+
+* Requests with different methods, such as creating a new collection using @POST /arvados/v1/collections@, will be rejected.
+* Requests to access other resources, such as @GET /arvados/v1/groups@, will be rejected.
+* Be aware that requests for specific records, such as @GET /arvados/v1/collections/962eh-4zz18-xi32mpz2621o8km@ will also be rejected.  This is because the scope @GET /arvados/v1/collections@ does not end in @/@
+
+A scope of @GET /arvados/v1/collections/@ (with @/@ suffix) will permit access to individual collections.
+
+* The request @GET /arvados/v1/collections/962eh-4zz18-xi32mpz2621o8km@ will succeed
+* Be aware that requests for listing @GET /arvados/v1/collections@ (no @/@ suffix) will be rejected, because it is not a match with the rule @GET /arvados/v1/collections/@
+* A listing request @GET /arvados/v1/collections/@ will have the trailing @/@ suffix trimmed before the scope check, as a result it will not match the rule @GET /arvados/v1/collections/@.
+
+To allow both listing objects and requesting individual objects, include both in the scope: @["GET /arvados/v1/collections", "GET /arvados/v1/collections/"]@
+
+A narrow scope such as @GET /arvados/v1/collections/962eh-4zz18-xi32mpz2621o8km@ will disallow listing objects as well as disallow requesting any object other than those listed in the scope.
diff --git a/doc/images/Arvados_Permissions.svg b/doc/images/Arvados_Permissions.svg
new file mode 100644 (file)
index 0000000..018a172
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" standalone="yes"?>
+
+<svg version="1.1" viewBox="0.0 0.0 1381.4540682414697 465.73490813648294" fill="none" stroke="none" stroke-linecap="square" stroke-miterlimit="10" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><clipPath id="p.0"><path d="m0 0l1381.4541 0l0 465.7349l-1381.4541 0l0 -465.7349z" clip-rule="nonzero"></path></clipPath><g clip-path="url(#p.0)"><path fill="#000000" fill-opacity="0.0" d="m0 0l1381.4541 0l0 465.7349l-1381.4541 0z" fill-rule="nonzero"></path><path fill="#76a5af" d="m167.81102 247.17343l0 0c0 -5.809967 4.709915 -10.519882 10.519897 -10.519882l116.660995 0c2.790039 0 5.4658203 1.1083374 7.43869 3.0812073c1.9728699 1.9728546 3.0812073 4.648636 3.0812073 7.438675l0 42.07834c0 5.809967 -4.7099304 10.519897 -10.519897 10.519897l-116.660995 0c-5.8099823 0 -10.519897 -4.7099304 -10.519897 -10.519897z" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m167.81102 247.17343l0 0c0 -5.809967 4.709915 -10.519882 10.519897 -10.519882l116.660995 0c2.790039 0 5.4658203 1.1083374 7.43869 3.0812073c1.9728699 1.9728546 3.0812073 4.648636 3.0812073 7.438675l0 42.07834c0 5.809967 -4.7099304 10.519897 -10.519897 10.519897l-116.660995 0c-5.8099823 0 -10.519897 -4.7099304 -10.519897 -10.519897z" fill-rule="nonzero"></path><path fill="#000000" d="m226.62677 275.1326l0 -13.59375l4.421875 0q2.5 0 3.265625 0.203125q1.15625 0.296875 1.9375 1.328125q0.796875 1.015625 0.796875 2.640625q0 1.25 -0.453125 2.109375q-0.453125 0.859375 -1.15625 1.34375q-0.703125 0.484375 -1.421875 0.640625q-0.984375 0.203125 -2.84375 0.203125l-1.796875 0l0 5.125l-2.75 0zm2.75 -11.296875l0 3.859375l1.5 0q1.625 0 2.171875 -0.21875q0.546875 -0.21875 0.859375 -0.671875q0.3125 -0.453125 0.3125 -1.046875q0 -0.75 -0.4375 -1.234375q-0.4375 -0.484375 -1.09375 -0.59375q-0.5 -0.09375 -1.984375 -0.09375l-1.328125 0zm15.802948 11.296875l-2.609375 0l0 -9.828125q-1.4375 1.34375 -3.375 1.984375l0 -2.375q1.03125 -0.328125 2.21875 -1.25q1.203125 -0.9375 1.640625 -2.1875l2.125 0l0 13.65625z" fill-rule="nonzero"></path><path fill="#f3a7eb" fill-opacity="0.5269" d="m322.10236 83.18898l0 0c0 -19.803978 25.586761 -35.858273 57.149628 -35.858273l0 0c31.562836 0 57.149597 16.054295 57.149597 35.858273l0 0c0 19.80397 -25.586761 35.85826 -57.149597 35.85826l0 0c-31.562866 0 -57.149628 -16.05429 -57.149628 -35.85826z" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m322.10236 83.18898l0 0c0 -19.803978 25.586761 -35.858273 57.149628 -35.858273l0 0c31.562836 0 57.149597 16.054295 57.149597 35.858273l0 0c0 19.80397 -25.586761 35.85826 -57.149597 35.85826l0 0c-31.562866 0 -57.149628 -16.05429 -57.149628 -35.85826z" fill-rule="nonzero"></path><path fill="#000000" d="m368.687 76.51523l2.75 0l0 7.359375q0 1.75 0.109375 2.265625q0.171875 0.84375 0.828125 1.359375q0.671875 0.5 1.8125 0.5q1.171875 0 1.765625 -0.484375q0.59375 -0.484375 0.71875 -1.171875q0.125 -0.703125 0.125 -2.3125l0 -7.515625l2.734375 0l0 7.140625q0 2.4375 -0.21875 3.453125q-0.21875 1.015625 -0.828125 1.71875q-0.59375 0.6875 -1.59375 1.109375q-1.0 0.40625 -2.609375 0.40625q-1.953125 0 -2.96875 -0.453125q-1.0 -0.453125 -1.59375 -1.171875q-0.578125 -0.71875 -0.75 -1.5q-0.28125 -1.171875 -0.28125 -3.453125l0 -7.25zm19.59793 13.59375l-2.609375 0l0 -9.828125q-1.4375 1.34375 -3.375 1.984375l0 -2.375q1.03125 -0.328125 2.21875 -1.25q1.203125 -0.9375 1.640625 -2.1875l2.125 0l0 13.65625z" fill-rule="nonzero"></path><path fill="#f3a7eb" fill-opacity="0.5255" d="m730.10236 115.18898l0 0c0 -15.385696 20.7359 -27.858269 46.31494 -27.858269l0 0c25.579102 0 46.315002 12.472572 46.315002 27.858269l0 0c0 15.385696 -20.7359 27.858261 -46.315002 27.858261l0 0c-25.57904 0 -46.31494 -12.472565 -46.31494 -27.858261z" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m730.10236 115.18898l0 0c0 -15.385696 20.7359 -27.858269 46.31494 -27.858269l0 0c25.579102 0 46.315002 12.472572 46.315002 27.858269l0 0c0 15.385696 -20.7359 27.858261 -46.315002 27.858261l0 0c-25.57904 0 -46.31494 -12.472565 -46.31494 -27.858261z" fill-rule="nonzero"></path><path fill="#000000" d="m765.85236 108.51522l2.75 0l0 7.359375q0 1.75 0.109375 2.265625q0.171875 0.84375 0.828125 1.359375q0.671875 0.5 1.8125 0.5q1.171875 0 1.765625 -0.484375q0.59375 -0.484375 0.71875 -1.171875q0.125 -0.703125 0.125 -2.3125l0 -7.515625l2.734375 0l0 7.140625q0 2.4375 -0.21875 3.453125q-0.21875 1.015625 -0.828125 1.71875q-0.59375 0.6875 -1.59375 1.109375q-1.0 0.40625 -2.609375 0.40625q-1.953125 0 -2.96875 -0.453125q-1.0 -0.453125 -1.59375 -1.171875q-0.578125 -0.71875 -0.75 -1.5q-0.28125 -1.171875 -0.28125 -3.453125l0 -7.25zm21.722961 11.171875l0 2.421875l-9.140625 0q0.15625 -1.375 0.890625 -2.59375q0.75 -1.234375 2.9375 -3.265625q1.765625 -1.640625 2.15625 -2.234375q0.546875 -0.796875 0.546875 -1.59375q0 -0.875 -0.46875 -1.34375q-0.46875 -0.46875 -1.296875 -0.46875q-0.8125 0 -1.296875 0.5q-0.484375 0.484375 -0.5625 1.625l-2.59375 -0.25q0.234375 -2.15625 1.453125 -3.09375q1.21875 -0.9375 3.0625 -0.9375q2.015625 0 3.15625 1.09375q1.15625 1.078125 1.15625 2.6875q0 0.921875 -0.328125 1.75q-0.328125 0.828125 -1.046875 1.734375q-0.46875 0.609375 -1.703125 1.75q-1.234375 1.125 -1.5625 1.5q-0.328125 0.359375 -0.53125 0.71875l5.171875 0z" fill-rule="nonzero"></path><path fill="#f3a7eb" fill-opacity="0.5255" d="m738.10236 319.28348l0 0c0 -13.22876 17.880432 -23.952759 39.93701 -23.952759l0 0c22.05658 0 39.93701 10.723999 39.93701 23.952759l0 0c0 13.228729 -17.880432 23.952728 -39.93701 23.952728l0 0c-22.05658 0 -39.93701 -10.723999 -39.93701 -23.952728z" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m738.10236 319.28348l0 0c0 -13.22876 17.880432 -23.952759 39.93701 -23.952759l0 0c22.05658 0 39.93701 10.723999 39.93701 23.952759l0 0c0 13.228729 -17.880432 23.952728 -39.93701 23.952728l0 0c-22.05658 0 -39.93701 -10.723999 -39.93701 -23.952728z" fill-rule="nonzero"></path><path fill="#000000" d="m767.4744 312.6097l2.75 0l0 7.359375q0 1.75 0.109375 2.265625q0.171875 0.84375 0.828125 1.359375q0.671875 0.5 1.8125 0.5q1.171875 0 1.765625 -0.484375q0.59375 -0.484375 0.71875 -1.171875q0.125 -0.703125 0.125 -2.3125l0 -7.515625l2.734375 0l0 7.140625q0 2.4375 -0.21875 3.453125q-0.21875 1.015625 -0.828125 1.71875q-0.59375 0.6875 -1.59375 1.109375q-1.0 0.40625 -2.609375 0.40625q-1.953125 0 -2.96875 -0.453125q-1.0 -0.453125 -1.59375 -1.171875q-0.578125 -0.71875 -0.75 -1.5q-0.28125 -1.171875 -0.28125 -3.453125l0 -7.25zm12.832336 9.984375l2.515625 -0.3125q0.125 0.96875 0.65625 1.484375q0.53125 0.5 1.28125 0.5q0.796875 0 1.34375 -0.609375q0.5625 -0.609375 0.5625 -1.640625q0 -0.984375 -0.53125 -1.5625q-0.53125 -0.578125 -1.28125 -0.578125q-0.5 0 -1.203125 0.203125l0.28125 -2.125q1.0625 0.015625 1.609375 -0.46875q0.5625 -0.484375 0.5625 -1.296875q0 -0.6875 -0.40625 -1.09375q-0.40625 -0.40625 -1.078125 -0.40625q-0.671875 0 -1.140625 0.46875q-0.46875 0.46875 -0.578125 1.359375l-2.40625 -0.421875q0.25 -1.234375 0.75 -1.96875q0.515625 -0.734375 1.421875 -1.15625q0.90625 -0.421875 2.03125 -0.421875q1.90625 0 3.078125 1.21875q0.953125 1.0 0.953125 2.265625q0 1.796875 -1.953125 2.859375q1.15625 0.25 1.859375 1.125q0.703125 0.875 0.703125 2.109375q0 1.78125 -1.3125 3.046875q-1.296875 1.265625 -3.25 1.265625q-1.84375 0 -3.0625 -1.0625q-1.21875 -1.0625 -1.40625 -2.78125z" fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0" d="m379.25198 119.04724l-142.58269 117.60631" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m379.25198 119.04724l-137.95406 113.78847" fill-rule="evenodd"></path><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m240.24692 231.56151l-2.4498596 4.1618195l4.5518646 -1.6134033z" fill-rule="evenodd"></path><path fill="#76a5af" d="m55.811024 383.17343l0 0c0 -5.809967 4.709919 -10.519897 10.519894 -10.519897l116.660995 0c2.7900543 0 5.4658356 1.1083374 7.43869 3.0812073c1.9728699 1.9728699 3.0812073 4.648651 3.0812073 7.43869l0 42.07834c0 5.809967 -4.709915 10.519897 -10.519897 10.519897l-116.660995 0c-5.8099747 0 -10.519894 -4.7099304 -10.519894 -10.519897z" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m55.811024 383.17343l0 0c0 -5.809967 4.709919 -10.519897 10.519894 -10.519897l116.660995 0c2.7900543 0 5.4658356 1.1083374 7.43869 3.0812073c1.9728699 1.9728699 3.0812073 4.648651 3.0812073 7.43869l0 42.07834c0 5.809967 -4.709915 10.519897 -10.519897 10.519897l-116.660995 0c-5.8099747 0 -10.519894 -4.7099304 -10.519894 -10.519897z" fill-rule="nonzero"></path><path fill="#000000" d="m114.62677 411.1326l0 -13.59375l4.421875 0q2.5 0 3.265625 0.203125q1.15625 0.296875 1.9375 1.328125q0.796875 1.015625 0.796875 2.640625q0 1.25 -0.453125 2.109375q-0.453125 0.859375 -1.15625 1.34375q-0.703125 0.484375 -1.421875 0.640625q-0.984375 0.203125 -2.84375 0.203125l-1.796875 0l0 5.125l-2.75 0zm2.75 -11.296875l0 3.859375l1.5 0q1.625 0 2.171875 -0.21875q0.546875 -0.21875 0.859375 -0.671875q0.3125 -0.453125 0.3125 -1.046875q0 -0.75 -0.4375 -1.234375q-0.4375 -0.484375 -1.09375 -0.59375q-0.5 -0.09375 -1.984375 -0.09375l-1.328125 0zm17.927948 8.875l0 2.421875l-9.140625 0q0.15625 -1.375 0.890625 -2.59375q0.75 -1.234375 2.9375 -3.265625q1.765625 -1.640625 2.15625 -2.234375q0.546875 -0.796875 0.546875 -1.59375q0 -0.875 -0.46875 -1.34375q-0.46875 -0.46875 -1.296875 -0.46875q-0.8125 0 -1.296875 0.5q-0.484375 0.484375 -0.5625 1.625l-2.59375 -0.25q0.234375 -2.15625 1.453125 -3.09375q1.21875 -0.9375 3.0625 -0.9375q2.015625 0 3.15625 1.09375q1.15625 1.078125 1.15625 2.6875q0 0.921875 -0.328125 1.75q-0.328125 0.828125 -1.046875 1.734375q-0.46875 0.609375 -1.703125 1.75q-1.234375 1.125 -1.5625 1.5q-0.328125 0.359375 -0.53125 0.71875l5.171875 0z" fill-rule="nonzero"></path><path fill="#76a5af" d="m287.81104 383.17343l0 0c0 -5.809967 4.7099 -10.519897 10.519897 -10.519897l116.66098 0c2.790039 0 5.4658203 1.1083374 7.43869 3.0812073c1.9728699 1.9728699 3.0812073 4.648651 3.0812073 7.43869l0 42.07834c0 5.809967 -4.7099304 10.519897 -10.519897 10.519897l-116.66098 0c-5.8099976 0 -10.519897 -4.7099304 -10.519897 -10.519897z" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m287.81104 383.17343l0 0c0 -5.809967 4.7099 -10.519897 10.519897 -10.519897l116.66098 0c2.790039 0 5.4658203 1.1083374 7.43869 3.0812073c1.9728699 1.9728699 3.0812073 4.648651 3.0812073 7.43869l0 42.07834c0 5.809967 -4.7099304 10.519897 -10.519897 10.519897l-116.66098 0c-5.8099976 0 -10.519897 -4.7099304 -10.519897 -10.519897z" fill-rule="nonzero"></path><path fill="#000000" d="m346.62677 411.1326l0 -13.59375l4.421875 0q2.5 0 3.265625 0.203125q1.15625 0.296875 1.9375 1.328125q0.796875 1.015625 0.796875 2.640625q0 1.25 -0.453125 2.109375q-0.453125 0.859375 -1.15625 1.34375q-0.703125 0.484375 -1.421875 0.640625q-0.984375 0.203125 -2.84375 0.203125l-1.796875 0l0 5.125l-2.75 0zm2.75 -11.296875l0 3.859375l1.5 0q1.625 0 2.171875 -0.21875q0.546875 -0.21875 0.859375 -0.671875q0.3125 -0.453125 0.3125 -1.046875q0 -0.75 -0.4375 -1.234375q-0.4375 -0.484375 -1.09375 -0.59375q-0.5 -0.09375 -1.984375 -0.09375l-1.328125 0zm9.037323 7.6875l2.515625 -0.3125q0.125 0.96875 0.65625 1.484375q0.53125 0.5 1.28125 0.5q0.796875 0 1.34375 -0.609375q0.5625 -0.609375 0.5625 -1.640625q0 -0.984375 -0.53125 -1.5625q-0.53125 -0.578125 -1.28125 -0.578125q-0.5 0 -1.203125 0.203125l0.28125 -2.125q1.0625 0.015625 1.609375 -0.46875q0.5625 -0.484375 0.5625 -1.296875q0 -0.6875 -0.40625 -1.09375q-0.40625 -0.40625 -1.078125 -0.40625q-0.671875 0 -1.140625 0.46875q-0.46875 0.46875 -0.578125 1.359375l-2.40625 -0.421875q0.25 -1.234375 0.75 -1.96875q0.515625 -0.734375 1.421875 -1.15625q0.90625 -0.421875 2.03125 -0.421875q1.90625 0 3.078125 1.21875q0.953125 1.0 0.953125 2.265625q0 1.796875 -1.953125 2.859375q1.15625 0.25 1.859375 1.125q0.703125 0.875 0.703125 2.109375q0 1.78125 -1.3125 3.046875q-1.296875 1.265625 -3.25 1.265625q-1.84375 0 -3.0625 -1.0625q-1.21875 -1.0625 -1.40625 -2.78125z" fill-rule="nonzero"></path><path fill="#76a5af" d="m447.81104 247.17343l0 0c0 -5.809967 4.7099 -10.519882 10.519897 -10.519882l116.66101 0c2.790039 0 5.4658203 1.1083374 7.4386597 3.0812073c1.9728394 1.9728546 3.0812378 4.648636 3.0812378 7.438675l0 42.07834c0 5.809967 -4.709961 10.519897 -10.519897 10.519897l-116.66101 0c-5.8099976 0 -10.519897 -4.7099304 -10.519897 -10.519897z" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m447.81104 247.17343l0 0c0 -5.809967 4.7099 -10.519882 10.519897 -10.519882l116.66101 0c2.790039 0 5.4658203 1.1083374 7.4386597 3.0812073c1.9728394 1.9728546 3.0812378 4.648636 3.0812378 7.438675l0 42.07834c0 5.809967 -4.709961 10.519897 -10.519897 10.519897l-116.66101 0c-5.8099976 0 -10.519897 -4.7099304 -10.519897 -10.519897z" fill-rule="nonzero"></path><path fill="#000000" d="m506.62677 275.1326l0 -13.59375l4.421875 0q2.5 0 3.265625 0.203125q1.15625 0.296875 1.9375 1.328125q0.796875 1.015625 0.796875 2.640625q0 1.25 -0.453125 2.109375q-0.453125 0.859375 -1.15625 1.34375q-0.703125 0.484375 -1.421875 0.640625q-0.984375 0.203125 -2.84375 0.203125l-1.796875 0l0 5.125l-2.75 0zm2.75 -11.296875l0 3.859375l1.5 0q1.625 0 2.171875 -0.21875q0.546875 -0.21875 0.859375 -0.671875q0.3125 -0.453125 0.3125 -1.046875q0 -0.75 -0.4375 -1.234375q-0.4375 -0.484375 -1.09375 -0.59375q-0.5 -0.09375 -1.984375 -0.09375l-1.328125 0zm14.2404175 11.296875l0 -2.734375l-5.5625 0l0 -2.28125l5.890625 -8.640625l2.1875 0l0 8.625l1.6875 0l0 2.296875l-1.6875 0l0 2.734375l-2.515625 0zm0 -5.03125l0 -4.640625l-3.125 4.640625l3.125 0z" fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0" d="m730.10236 115.18898l-422.96063 128.06299" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="4.0,3.0,1.0,3.0" d="m730.10236 115.18898l-417.21808 126.324265" fill-rule="evenodd"></path><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m312.40564 239.93239l-3.864746 2.895935l4.8220215 0.26579285z" fill-rule="evenodd"></path><path fill="#000000" fill-opacity="0.0" d="m738.10236 319.28348l-152.59839 -51.08664" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="4.0,3.0,1.0,3.0" d="m738.10236 319.28348l-146.90881 -49.181885" fill-rule="evenodd"></path><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m591.7179 268.5353l-4.8276978 0.12564087l3.7789917 3.006958z" fill-rule="evenodd"></path><path fill="#000000" fill-opacity="0.0" d="m379.25198 119.04724l137.41733 117.60631" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m379.25198 119.04724l132.85886 113.70499" fill-rule="evenodd"></path><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m511.0368 234.00714l4.5217896 1.6958466l-2.3737793 -4.2056427z" fill-rule="evenodd"></path><path fill="#000000" fill-opacity="0.0" d="m236.66142 299.77167l-112.00001 72.88187" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m236.66142 299.77167l-106.97102 69.609375" fill-rule="evenodd"></path><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m128.7895 367.9966l-2.902771 3.8595886l4.704544 -1.0907593z" fill-rule="evenodd"></path><path fill="#000000" fill-opacity="0.0" d="m236.66142 299.77167l119.999985 72.88187" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m236.66142 299.77167l114.87175 69.76724" fill-rule="evenodd"></path><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m350.67575 370.95065l4.7361755 0.94400024l-3.0213318 -3.767517z" fill-rule="evenodd"></path><path fill="#000000" fill-opacity="0.0" d="m500.65353 107.34646l142.58267 0l0 47.90551l-142.58267 0z" fill-rule="nonzero"></path><path fill="#000000" d="m512.21606 122.95396l0 1.9375l-1.7969055 0l0 -1.53125q0 -1.25 0.296875 -1.796875q0.390625 -0.75 1.234375 -1.125l0.40628052 0.640625q-0.5000305 0.21875 -0.7500305 0.640625q-0.234375 0.421875 -0.265625 1.234375l0.8750305 0zm2.875 0l0 1.9375l-1.796875 0l0 -1.53125q0 -1.25 0.296875 -1.796875q0.390625 -0.75 1.234375 -1.125l0.40625 0.640625q-0.5 0.21875 -0.75 0.640625q-0.234375 0.421875 -0.265625 1.234375l0.875 0zm8.4626465 7.7031174l1.640625 0.21875q-0.265625 1.6875 -1.375 2.65625q-1.109375 0.953125 -2.734375 0.953125q-2.015625 0 -3.25 -1.3125q-1.21875 -1.328125 -1.21875 -3.796875q0 -1.5937424 0.515625 -2.7812424q0.53125 -1.203125 1.609375 -1.796875q1.09375 -0.609375 2.359375 -0.609375q1.609375 0 2.625 0.8125q1.015625 0.8125 1.3125 2.3125l-1.625 0.25q-0.234375 -1.0 -0.828125 -1.5q-0.59375 -0.5 -1.421875 -0.5q-1.265625 0 -2.0625 0.90625q-0.78125 0.90625 -0.78125 2.8593674q0 1.984375 0.765625 2.890625q0.765625 0.890625 1.984375 0.890625q0.984375 0 1.640625 -0.59375q0.65625 -0.609375 0.84375 -1.859375zm9.328125 2.390625q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34374237 0 -0.43749237q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.2343674q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm4.0788574 4.9375l0 -9.859367l1.5 0l0 1.40625q1.09375 -1.625 3.140625 -1.625q0.890625 0 1.640625 0.328125q0.75 0.3125 1.109375 0.84375q0.375 0.515625 0.53125 1.21875q0.09375 0.46875 0.09375 1.6249924l0 6.0625l-1.671875 0l0 -6.0q0 -1.0156174 -0.203125 -1.5156174q-0.1875 -0.515625 -0.6875 -0.8125q-0.5 -0.296875 -1.171875 -0.296875q-1.0625 0 -1.84375 0.671875q-0.765625 0.671875 -0.765625 2.5781174l0 5.375l-1.671875 0zm8.844482 3.78125l0 -1.21875l11.0625 0l0 1.21875l-11.0625 0zm13.735046 -3.78125l-3.015625 -9.859367l1.71875 0l1.5625 5.6874924l0.59375 2.125q0.03125 -0.15625 0.5 -2.03125l1.578125 -5.7812424l1.71875 0l1.46875 5.7187424l0.484375 1.890625l0.578125 -1.90625l1.6875 -5.7031174l1.625 0l-3.078125 9.859367l-1.734375 0l-1.578125 -5.90625l-0.375 -1.6718674l-2.0 7.5781174l-1.734375 0zm11.629211 0l0 -9.859367l1.5 0l0 1.5q0.578125 -1.046875 1.0625 -1.375q0.484375 -0.34375 1.078125 -0.34375q0.84375 0 1.71875 0.546875l-0.578125 1.546875q-0.609375 -0.359375 -1.234375 -0.359375q-0.546875 0 -0.984375 0.328125q-0.421875 0.328125 -0.609375 0.90625q-0.28125 0.8906174 -0.28125 1.9531174l0 5.15625l-1.671875 0zm6.2439575 -11.687492l0 -1.90625l1.671875 0l0 1.90625l-1.671875 0zm0 11.687492l0 -9.859367l1.671875 0l0 9.859367l-1.671875 0zm7.7854004 -1.5l0.234375 1.484375q-0.703125 0.140625 -1.265625 0.140625q-0.90625 0 -1.40625 -0.28125q-0.5 -0.296875 -0.703125 -0.75q-0.203125 -0.46875 -0.203125 -1.984375l0 -5.6562424l-1.234375 0l0 -1.3125l1.234375 0l0 -2.4375l1.65625 -1.0l0 3.4375l1.6875 0l0 1.3125l-1.6875 0l0 5.7499924q0 0.71875 0.078125 0.921875q0.09375 0.203125 0.296875 0.328125q0.203125 0.125 0.578125 0.125q0.265625 0 0.734375 -0.078125zm8.2771 -1.671875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.4843674 1.265625 -3.8593674q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.7968674q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.2343674 -0.625 -1.8593674q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.0468674zm8.672546 -5.9218674l0 -1.9375l1.78125 0l0 1.53125q0 1.234375 -0.28125 1.78125q-0.40625 0.75 -1.25 1.140625l-0.40625 -0.671875q0.5 -0.203125 0.75 -0.640625q0.25 -0.4375 0.265625 -1.203125l-0.859375 0zm2.875 0l0 -1.9375l1.78125 0l0 1.53125q0 1.234375 -0.28125 1.78125q-0.40625 0.75 -1.25 1.140625l-0.40625 -0.671875q0.5 -0.203125 0.75 -0.640625q0.25 -0.4375 0.28125 -1.203125l-0.875 0z" fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0" d="m643.4173 256.7638l120.0 0l0 41.95273l-120.0 0z" fill-rule="nonzero"></path><path fill="#000000" d="m654.9798 272.37128l0 1.9375l-1.796875 0l0 -1.53125q0 -1.25 0.296875 -1.796875q0.390625 -0.75 1.234375 -1.125l0.40625 0.640625q-0.5 0.21875 -0.75 0.640625q-0.234375 0.421875 -0.265625 1.234375l0.875 0zm2.875 0l0 1.9375l-1.796875 0l0 -1.53125q0 -1.25 0.296875 -1.796875q0.390625 -0.75 1.234375 -1.125l0.40625 0.640625q-0.5 0.21875 -0.75 0.640625q-0.234375 0.421875 -0.265625 1.234375l0.875 0zm8.4627075 7.703125l1.640625 0.21875q-0.265625 1.6875 -1.375 2.65625q-1.109375 0.953125 -2.734375 0.953125q-2.015625 0 -3.25 -1.3125q-1.21875 -1.328125 -1.21875 -3.796875q0 -1.59375 0.515625 -2.78125q0.53125 -1.203125 1.609375 -1.796875q1.09375 -0.609375 2.359375 -0.609375q1.609375 0 2.625 0.8125q1.015625 0.8125 1.3125 2.3125l-1.625 0.25q-0.234375 -1.0 -0.828125 -1.5q-0.59375 -0.5 -1.421875 -0.5q-1.265625 0 -2.0625 0.90625q-0.78125 0.90625 -0.78125 2.859375q0 1.984375 0.765625 2.890625q0.765625 0.890625 1.984375 0.890625q0.984375 0 1.640625 -0.59375q0.65625 -0.609375 0.84375 -1.859375zm9.328125 2.390625q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm4.0788574 4.9375l0 -9.859375l1.5 0l0 1.40625q1.09375 -1.625 3.140625 -1.625q0.890625 0 1.640625 0.328125q0.75 0.3125 1.109375 0.84375q0.375 0.515625 0.53125 1.21875q0.09375 0.46875 0.09375 1.625l0 6.0625l-1.671875 0l0 -6.0q0 -1.015625 -0.203125 -1.515625q-0.1875 -0.515625 -0.6875 -0.8125q-0.5 -0.296875 -1.171875 -0.296875q-1.0625 0 -1.84375 0.671875q-0.765625 0.671875 -0.765625 2.578125l0 5.375l-1.671875 0zm8.844421 3.78125l0 -1.21875l11.0625 0l0 1.21875l-11.0625 0zm11.891357 -3.78125l0 -9.859375l1.5 0l0 1.5q0.578125 -1.046875 1.0625 -1.375q0.484375 -0.34375 1.078125 -0.34375q0.84375 0 1.71875 0.546875l-0.578125 1.546875q-0.609375 -0.359375 -1.234375 -0.359375q-0.546875 0 -0.984375 0.328125q-0.421875 0.328125 -0.609375 0.90625q-0.28125 0.890625 -0.28125 1.953125l0 5.15625l-1.671875 0zm12.9783325 -3.171875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm15.547546 4.65625q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm10.469482 4.9375l0 -1.25q-0.9375 1.46875 -2.75 1.46875q-1.171875 0 -2.171875 -0.640625q-0.984375 -0.65625 -1.53125 -1.8125q-0.53125 -1.171875 -0.53125 -2.6875q0 -1.46875 0.484375 -2.671875q0.5 -1.203125 1.46875 -1.84375q0.984375 -0.640625 2.203125 -0.640625q0.890625 0 1.578125 0.375q0.703125 0.375 1.140625 0.984375l0 -4.875l1.65625 0l0 13.59375l-1.546875 0zm-5.28125 -4.921875q0 1.890625 0.796875 2.828125q0.8125 0.9375 1.890625 0.9375q1.09375 0 1.859375 -0.890625q0.765625 -0.890625 0.765625 -2.734375q0 -2.015625 -0.78125 -2.953125q-0.78125 -0.953125 -1.921875 -0.953125q-1.109375 0 -1.859375 0.90625q-0.75 0.90625 -0.75 2.859375zm8.828857 -6.875l0 -1.9375l1.78125 0l0 1.53125q0 1.234375 -0.28125 1.78125q-0.40625 0.75 -1.25 1.140625l-0.40625 -0.671875q0.5 -0.203125 0.75 -0.640625q0.25 -0.4375 0.265625 -1.203125l-0.859375 0zm2.875 0l0 -1.9375l1.78125 0l0 1.53125q0 1.234375 -0.28125 1.78125q-0.40625 0.75 -1.25 1.140625l-0.40625 -0.671875q0.5 -0.203125 0.75 -0.640625q0.25 -0.4375 0.28125 -1.203125l-0.875 0z" fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0" d="m559.7454 366.88452l114.299194 0l0 51.08661l-114.299194 0z" fill-rule="nonzero"></path><path fill="#000000" d="m571.3079 382.492l0 1.9375l-1.796875 0l0 -1.53125q0 -1.25 0.296875 -1.796875q0.390625 -0.75 1.234375 -1.125l0.40625 0.640625q-0.5 0.21875 -0.75 0.640625q-0.234375 0.421875 -0.265625 1.234375l0.875 0zm2.875 0l0 1.9375l-1.796875 0l0 -1.53125q0 -1.25 0.296875 -1.796875q0.390625 -0.75 1.234375 -1.125l0.40625 0.640625q-0.5 0.21875 -0.75 0.640625q-0.234375 0.421875 -0.265625 1.234375l0.875 0zm8.4626465 7.703125l1.640625 0.21875q-0.265625 1.6875 -1.375 2.65625q-1.109375 0.953125 -2.734375 0.953125q-2.015625 0 -3.25 -1.3125q-1.21875 -1.328125 -1.21875 -3.796875q0 -1.59375 0.515625 -2.78125q0.53125 -1.203125 1.609375 -1.796875q1.09375 -0.609375 2.359375 -0.609375q1.609375 0 2.625 0.8125q1.015625 0.8125 1.3125 2.3125l-1.625 0.25q-0.234375 -1.0 -0.828125 -1.5q-0.59375 -0.5 -1.421875 -0.5q-1.265625 0 -2.0625 0.90625q-0.78125 0.90625 -0.78125 2.859375q0 1.984375 0.765625 2.890625q0.765625 0.890625 1.984375 0.890625q0.984375 0 1.640625 -0.59375q0.65625 -0.609375 0.84375 -1.859375zm9.328125 2.390625q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm4.0788574 4.9375l0 -9.859375l1.5 0l0 1.40625q1.09375 -1.625 3.140625 -1.625q0.890625 0 1.640625 0.328125q0.75 0.3125 1.109375 0.84375q0.375 0.515625 0.53125 1.21875q0.09375 0.46875 0.09375 1.625l0 6.0625l-1.671875 0l0 -6.0q0 -1.015625 -0.203125 -1.515625q-0.1875 -0.515625 -0.6875 -0.8125q-0.5 -0.296875 -1.171875 -0.296875q-1.0625 0 -1.84375 0.671875q-0.765625 0.671875 -0.765625 2.578125l0 5.375l-1.671875 0zm8.844482 3.78125l0 -1.21875l11.0625 0l0 1.21875l-11.0625 0zm11.891296 -3.78125l0 -9.859375l1.5 0l0 1.5q0.578125 -1.046875 1.0625 -1.375q0.484375 -0.34375 1.078125 -0.34375q0.84375 0 1.71875 0.546875l-0.578125 1.546875q-0.609375 -0.359375 -1.234375 -0.359375q-0.546875 0 -0.984375 0.328125q-0.421875 0.328125 -0.609375 0.90625q-0.28125 0.890625 -0.28125 1.953125l0 5.15625l-1.671875 0zm12.9783325 -3.171875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm15.547607 4.65625q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm10.469421 4.9375l0 -1.25q-0.9375 1.46875 -2.75 1.46875q-1.171875 0 -2.171875 -0.640625q-0.984375 -0.65625 -1.53125 -1.8125q-0.53125 -1.171875 -0.53125 -2.6875q0 -1.46875 0.484375 -2.671875q0.5 -1.203125 1.46875 -1.84375q0.984375 -0.640625 2.203125 -0.640625q0.890625 0 1.578125 0.375q0.703125 0.375 1.140625 0.984375l0 -4.875l1.65625 0l0 13.59375l-1.546875 0zm-5.28125 -4.921875q0 1.890625 0.796875 2.828125q0.8125 0.9375 1.890625 0.9375q1.09375 0 1.859375 -0.890625q0.765625 -0.890625 0.765625 -2.734375q0 -2.015625 -0.78125 -2.953125q-0.78125 -0.953125 -1.921875 -0.953125q-1.109375 0 -1.859375 0.90625q-0.75 0.90625 -0.75 2.859375zm8.828857 -6.875l0 -1.9375l1.78125 0l0 1.53125q0 1.234375 -0.28125 1.78125q-0.40625 0.75 -1.25 1.140625l-0.40625 -0.671875q0.5 -0.203125 0.75 -0.640625q0.25 -0.4375 0.265625 -1.203125l-0.859375 0zm2.875 0l0 -1.9375l1.78125 0l0 1.53125q0 1.234375 -0.28125 1.78125q-0.40625 0.75 -1.25 1.140625l-0.40625 -0.671875q0.5 -0.203125 0.75 -0.640625q0.25 -0.4375 0.28125 -1.203125l-0.875 0z" fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0" d="m302.40683 310.40683l152.59842 0l0 41.95276l-152.59842 0z" fill-rule="nonzero"></path><path fill="#a61c00" d="m321.48495 332.3268l2.671875 0.84375q-0.609375 2.21875 -2.046875 3.3125q-1.421875 1.078125 -3.609375 1.078125q-2.703125 0 -4.453125 -1.84375q-1.734375 -1.859375 -1.734375 -5.078125q0 -3.390625 1.75 -5.265625q1.75 -1.875 4.609375 -1.875q2.5 0 4.046875 1.46875q0.9375 0.875 1.390625 2.5l-2.71875 0.65625q-0.234375 -1.0625 -1.0 -1.671875q-0.765625 -0.609375 -1.859375 -0.609375q-1.515625 0 -2.453125 1.09375q-0.9375 1.078125 -0.9375 3.5q0 2.578125 0.921875 3.6875q0.921875 1.09375 2.40625 1.09375q1.109375 0 1.890625 -0.6875q0.78125 -0.703125 1.125 -2.203125zm7.254181 5.0l-2.609375 0l0 -9.859375l2.421875 0l0 1.40625q0.625 -0.984375 1.109375 -1.296875q0.5 -0.328125 1.140625 -0.328125q0.890625 0 1.71875 0.5l-0.8125 2.265625q-0.65625 -0.421875 -1.21875 -0.421875q-0.546875 0 -0.9375 0.296875q-0.375 0.296875 -0.59375 1.09375q-0.21875 0.78125 -0.21875 3.296875l0 3.046875zm10.463409 -3.140625l2.609375 0.4375q-0.5 1.4375 -1.59375 2.1875q-1.078125 0.734375 -2.703125 0.734375q-2.5625 0 -3.796875 -1.671875q-0.96875 -1.34375 -0.96875 -3.40625q0 -2.4375 1.265625 -3.828125q1.28125 -1.390625 3.25 -1.390625q2.1875 0 3.453125 1.453125q1.28125 1.453125 1.234375 4.453125l-6.53125 0q0.015625 1.15625 0.625 1.8125q0.609375 0.640625 1.5 0.640625q0.609375 0 1.03125 -0.328125q0.421875 -0.34375 0.625 -1.09375zm0.15625 -2.625q-0.03125 -1.140625 -0.59375 -1.71875q-0.546875 -0.59375 -1.34375 -0.59375q-0.859375 0 -1.40625 0.625q-0.5625 0.609375 -0.546875 1.6875l3.890625 0zm6.469452 -1.078125l-2.359375 -0.4375q0.390625 -1.421875 1.359375 -2.109375q0.984375 -0.6875 2.90625 -0.6875q1.734375 0 2.59375 0.421875q0.859375 0.40625 1.203125 1.046875q0.34375 0.625 0.34375 2.328125l-0.03125 3.046875q0 1.296875 0.125 1.921875q0.125 0.609375 0.46875 1.3125l-2.578125 0q-0.09375 -0.265625 -0.25 -0.765625q-0.0625 -0.234375 -0.09375 -0.3125q-0.65625 0.65625 -1.421875 0.984375q-0.765625 0.3125 -1.625 0.3125q-1.515625 0 -2.40625 -0.8125q-0.875 -0.828125 -0.875 -2.09375q0 -0.84375 0.390625 -1.484375q0.40625 -0.65625 1.125 -1.0q0.71875 -0.359375 2.078125 -0.625q1.828125 -0.328125 2.53125 -0.625l0 -0.265625q0 -0.75 -0.375 -1.0625q-0.359375 -0.328125 -1.390625 -0.328125q-0.703125 0 -1.09375 0.28125q-0.390625 0.265625 -0.625 0.953125zm3.484375 2.109375q-0.5 0.171875 -1.59375 0.40625q-1.078125 0.234375 -1.40625 0.453125q-0.515625 0.359375 -0.515625 0.921875q0 0.546875 0.40625 0.953125q0.40625 0.390625 1.046875 0.390625q0.703125 0 1.34375 -0.46875q0.46875 -0.359375 0.625 -0.859375q0.09375 -0.34375 0.09375 -1.28125l0 -0.515625zm9.453857 -5.125l0 2.078125l-1.78125 0l0 3.984375q0 1.203125 0.046875 1.40625q0.0625 0.1875 0.234375 0.328125q0.1875 0.125 0.453125 0.125q0.359375 0 1.046875 -0.25l0.21875 2.015625q-0.90625 0.390625 -2.0625 0.390625q-0.703125 0 -1.265625 -0.234375q-0.5625 -0.234375 -0.828125 -0.609375q-0.265625 -0.375 -0.375 -1.015625q-0.078125 -0.453125 -0.078125 -1.84375l0 -4.296875l-1.203125 0l0 -2.078125l1.203125 0l0 -1.953125l2.609375 -1.515625l0 3.46875l1.78125 0zm7.400177 6.71875l2.609375 0.4375q-0.5 1.4375 -1.59375 2.1875q-1.078125 0.734375 -2.703125 0.734375q-2.5625 0 -3.796875 -1.671875q-0.96875 -1.34375 -0.96875 -3.40625q0 -2.4375 1.265625 -3.828125q1.28125 -1.390625 3.25 -1.390625q2.1875 0 3.453125 1.453125q1.28125 1.453125 1.234375 4.453125l-6.53125 0q0.015625 1.15625 0.625 1.8125q0.609375 0.640625 1.5 0.640625q0.609375 0 1.03125 -0.328125q0.421875 -0.34375 0.625 -1.09375zm0.15625 -2.625q-0.03125 -1.140625 -0.59375 -1.71875q-0.546875 -0.59375 -1.34375 -0.59375q-0.859375 0 -1.40625 0.625q-0.5625 0.609375 -0.546875 1.6875l3.890625 0zm13.563202 5.765625l-2.421875 0l0 -1.453125q-0.609375 0.84375 -1.4375 1.265625q-0.8125 0.40625 -1.640625 0.40625q-1.703125 0 -2.921875 -1.359375q-1.203125 -1.375 -1.203125 -3.828125q0 -2.5 1.171875 -3.796875q1.1875 -1.3125 2.984375 -1.3125q1.65625 0 2.859375 1.375l0 -4.890625l2.609375 0l0 13.59375zm-6.96875 -5.140625q0 1.578125 0.4375 2.28125q0.640625 1.015625 1.765625 1.015625q0.90625 0 1.53125 -0.765625q0.625 -0.765625 0.625 -2.28125q0 -1.703125 -0.609375 -2.4375q-0.609375 -0.75 -1.5625 -0.75q-0.9375 0 -1.5625 0.734375q-0.625 0.734375 -0.625 2.203125zm14.391785 5.140625l0 -13.59375l2.609375 0l0 4.890625q1.203125 -1.375 2.859375 -1.375q1.796875 0 2.96875 1.3125q1.1875 1.296875 1.1875 3.734375q0 2.53125 -1.203125 3.890625q-1.203125 1.359375 -2.921875 1.359375q-0.84375 0 -1.671875 -0.421875q-0.8125 -0.421875 -1.40625 -1.25l0 1.453125l-2.421875 0zm2.59375 -5.140625q0 1.53125 0.484375 2.265625q0.671875 1.03125 1.796875 1.03125q0.859375 0 1.46875 -0.734375q0.609375 -0.734375 0.609375 -2.328125q0 -1.6875 -0.609375 -2.421875q-0.609375 -0.75 -1.578125 -0.75q-0.9375 0 -1.5625 0.734375q-0.609375 0.71875 -0.609375 2.203125zm7.677246 -4.71875l2.78125 0l2.359375 7.0l2.296875 -7.0l2.703125 0l-3.484375 9.484375l-0.625 1.71875q-0.34375 0.859375 -0.65625 1.3125q-0.296875 0.46875 -0.703125 0.75q-0.40625 0.28125 -1.0 0.4375q-0.59375 0.15625 -1.328125 0.15625q-0.75 0 -1.46875 -0.15625l-0.234375 -2.046875q0.609375 0.125 1.09375 0.125q0.921875 0 1.34375 -0.53125q0.4375 -0.53125 0.671875 -1.359375l-3.75 -9.890625zm16.793396 -3.734375l2.75 0l0 7.359375q0 1.75 0.109375 2.265625q0.171875 0.84375 0.828125 1.359375q0.671875 0.5 1.8125 0.5q1.171875 0 1.765625 -0.484375q0.59375 -0.484375 0.71875 -1.171875q0.125 -0.703125 0.125 -2.3125l0 -7.515625l2.734375 0l0 7.140625q0 2.4375 -0.21875 3.453125q-0.21875 1.015625 -0.828125 1.71875q-0.59375 0.6875 -1.59375 1.109375q-1.0 0.40625 -2.609375 0.40625q-1.953125 0 -2.96875 -0.453125q-1.0 -0.453125 -1.59375 -1.171875q-0.578125 -0.71875 -0.75 -1.5q-0.28125 -1.171875 -0.28125 -3.453125l0 -7.25zm21.72293 11.171875l0 2.421875l-9.140625 0q0.15625 -1.375 0.890625 -2.59375q0.75 -1.234375 2.9375 -3.265625q1.765625 -1.640625 2.15625 -2.234375q0.546875 -0.796875 0.546875 -1.59375q0 -0.875 -0.46875 -1.34375q-0.46875 -0.46875 -1.296875 -0.46875q-0.8125 0 -1.296875 0.5q-0.484375 0.484375 -0.5625 1.625l-2.59375 -0.25q0.234375 -2.15625 1.453125 -3.09375q1.21875 -0.9375 3.0625 -0.9375q2.015625 0 3.15625 1.09375q1.15625 1.078125 1.15625 2.6875q0 0.921875 -0.328125 1.75q-0.328125 0.828125 -1.046875 1.734375q-0.46875 0.609375 -1.703125 1.75q-1.234375 1.125 -1.5625 1.5q-0.328125 0.359375 -0.53125 0.71875l5.171875 0z" fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0" d="m546.8373 393.3071l142.58264 0l0 41.95276l-142.58264 0z" fill-rule="nonzero"></path><path fill="#a61c00" d="m556.5248 415.8052l2.671875 -0.265625q0.234375 1.34375 0.96875 1.984375q0.75 0.625 2.0 0.625q1.328125 0 2.0 -0.5625q0.671875 -0.5625 0.671875 -1.3125q0 -0.484375 -0.28125 -0.8125q-0.28125 -0.34375 -0.984375 -0.59375q-0.484375 -0.171875 -2.203125 -0.59375q-2.203125 -0.546875 -3.09375 -1.34375q-1.265625 -1.125 -1.265625 -2.734375q0 -1.046875 0.59375 -1.953125q0.59375 -0.90625 1.703125 -1.375q1.109375 -0.46875 2.671875 -0.46875q2.5625 0 3.859375 1.125q1.296875 1.109375 1.359375 2.984375l-2.75 0.125q-0.171875 -1.046875 -0.75 -1.5q-0.578125 -0.46875 -1.75 -0.46875q-1.1875 0 -1.875 0.5q-0.4375 0.3125 -0.4375 0.84375q0 0.484375 0.421875 0.828125q0.515625 0.421875 2.515625 0.90625q2.0 0.46875 2.953125 0.984375q0.96875 0.5 1.515625 1.375q0.546875 0.875 0.546875 2.15625q0 1.171875 -0.65625 2.203125q-0.640625 1.015625 -1.828125 1.515625q-1.1875 0.484375 -2.96875 0.484375q-2.578125 0 -3.96875 -1.1875q-1.375 -1.1875 -1.640625 -3.46875zm15.7247925 -9.171875l0 5.0q1.25 -1.484375 3.015625 -1.484375q0.890625 0 1.609375 0.34375q0.734375 0.328125 1.09375 0.84375q0.375 0.515625 0.5 1.15625q0.140625 0.625 0.140625 1.953125l0 5.78125l-2.609375 0l0 -5.203125q0 -1.546875 -0.15625 -1.96875q-0.140625 -0.421875 -0.515625 -0.65625q-0.375 -0.25 -0.9375 -0.25q-0.65625 0 -1.171875 0.3125q-0.5 0.3125 -0.734375 0.953125q-0.234375 0.640625 -0.234375 1.875l0 4.9375l-2.609375 0l0 -13.59375l2.609375 0zm10.739746 6.75l-2.359375 -0.4375q0.390625 -1.421875 1.359375 -2.109375q0.984375 -0.6875 2.90625 -0.6875q1.734375 0 2.59375 0.421875q0.859375 0.40625 1.203125 1.046875q0.34375 0.625 0.34375 2.328125l-0.03125 3.046875q0 1.296875 0.125 1.921875q0.125 0.609375 0.46875 1.3125l-2.578125 0q-0.09375 -0.265625 -0.25 -0.765625q-0.0625 -0.234375 -0.09375 -0.3125q-0.65625 0.65625 -1.421875 0.984375q-0.765625 0.3125 -1.625 0.3125q-1.515625 0 -2.40625 -0.8125q-0.875 -0.828125 -0.875 -2.09375q0 -0.84375 0.390625 -1.484375q0.40625 -0.65625 1.125 -1.0q0.71875 -0.359375 2.078125 -0.625q1.828125 -0.328125 2.53125 -0.625l0 -0.265625q0 -0.75 -0.375 -1.0625q-0.359375 -0.328125 -1.390625 -0.328125q-0.703125 0 -1.09375 0.28125q-0.390625 0.265625 -0.625 0.953125zm3.484375 2.109375q-0.5 0.171875 -1.59375 0.40625q-1.078125 0.234375 -1.40625 0.453125q-0.515625 0.359375 -0.515625 0.921875q0 0.546875 0.40625 0.953125q0.40625 0.390625 1.046875 0.390625q0.703125 0 1.34375 -0.46875q0.46875 -0.359375 0.625 -0.859375q0.09375 -0.34375 0.09375 -1.28125l0 -0.515625zm7.4382324 4.734375l-2.609375 0l0 -9.859375l2.421875 0l0 1.40625q0.625 -0.984375 1.109375 -1.296875q0.5 -0.328125 1.140625 -0.328125q0.890625 0 1.71875 0.5l-0.8125 2.265625q-0.65625 -0.421875 -1.21875 -0.421875q-0.546875 0 -0.9375 0.296875q-0.375 0.296875 -0.59375 1.09375q-0.21875 0.78125 -0.21875 3.296875l0 3.046875zm10.463379 -3.140625l2.609375 0.4375q-0.5 1.4375 -1.59375 2.1875q-1.078125 0.734375 -2.703125 0.734375q-2.5625 0 -3.796875 -1.671875q-0.96875 -1.34375 -0.96875 -3.40625q0 -2.4375 1.265625 -3.828125q1.28125 -1.390625 3.25 -1.390625q2.1875 0 3.453125 1.453125q1.28125 1.453125 1.234375 4.453125l-6.53125 0q0.015625 1.15625 0.625 1.8125q0.609375 0.640625 1.5 0.640625q0.609375 0 1.03125 -0.328125q0.421875 -0.34375 0.625 -1.09375zm0.15625 -2.625q-0.03125 -1.140625 -0.59375 -1.71875q-0.546875 -0.59375 -1.34375 -0.59375q-0.859375 0 -1.40625 0.625q-0.5625 0.609375 -0.546875 1.6875l3.890625 0zm13.563232 5.765625l-2.421875 0l0 -1.453125q-0.609375 0.84375 -1.4375 1.265625q-0.8125 0.40625 -1.640625 0.40625q-1.703125 0 -2.921875 -1.359375q-1.203125 -1.375 -1.203125 -3.828125q0 -2.5 1.171875 -3.796875q1.1875 -1.3125 2.984375 -1.3125q1.65625 0 2.859375 1.375l0 -4.890625l2.609375 0l0 13.59375zm-6.96875 -5.140625q0 1.578125 0.4375 2.28125q0.640625 1.015625 1.765625 1.015625q0.90625 0 1.53125 -0.765625q0.625 -0.765625 0.625 -2.28125q0 -1.703125 -0.609375 -2.4375q-0.609375 -0.75 -1.5625 -0.75q-0.9375 0 -1.5625 0.734375q-0.625 0.734375 -0.625 2.203125zm14.391785 5.140625l0 -13.59375l2.609375 0l0 4.890625q1.203125 -1.375 2.859375 -1.375q1.796875 0 2.96875 1.3125q1.1875 1.296875 1.1875 3.734375q0 2.53125 -1.203125 3.890625q-1.203125 1.359375 -2.921875 1.359375q-0.84375 0 -1.671875 -0.421875q-0.8125 -0.421875 -1.40625 -1.25l0 1.453125l-2.421875 0zm2.59375 -5.140625q0 1.53125 0.484375 2.265625q0.671875 1.03125 1.796875 1.03125q0.859375 0 1.46875 -0.734375q0.609375 -0.734375 0.609375 -2.328125q0 -1.6875 -0.609375 -2.421875q-0.609375 -0.75 -1.578125 -0.75q-0.9375 0 -1.5625 0.734375q-0.609375 0.71875 -0.609375 2.203125zm7.677246 -4.71875l2.78125 0l2.359375 7.0l2.296875 -7.0l2.703125 0l-3.484375 9.484375l-0.625 1.71875q-0.34375 0.859375 -0.65625 1.3125q-0.296875 0.46875 -0.703125 0.75q-0.40625 0.28125 -1.0 0.4375q-0.59375 0.15625 -1.328125 0.15625q-0.75 0 -1.46875 -0.15625l-0.234375 -2.046875q0.609375 0.125 1.09375 0.125q0.921875 0 1.34375 -0.53125q0.4375 -0.53125 0.671875 -1.359375l-3.75 -9.890625zm16.793396 -3.734375l2.75 0l0 7.359375q0 1.75 0.109375 2.265625q0.171875 0.84375 0.828125 1.359375q0.671875 0.5 1.8125 0.5q1.171875 0 1.765625 -0.484375q0.59375 -0.484375 0.71875 -1.171875q0.125 -0.703125 0.125 -2.3125l0 -7.515625l2.734375 0l0 7.140625q0 2.4375 -0.21875 3.453125q-0.21875 1.015625 -0.828125 1.71875q-0.59375 0.6875 -1.59375 1.109375q-1.0 0.40625 -2.609375 0.40625q-1.953125 0 -2.96875 -0.453125q-1.0 -0.453125 -1.59375 -1.171875q-0.578125 -0.71875 -0.75 -1.5q-0.28125 -1.171875 -0.28125 -3.453125l0 -7.25zm21.7229 11.171875l0 2.421875l-9.140625 0q0.15625 -1.375 0.890625 -2.59375q0.75 -1.234375 2.9375 -3.265625q1.765625 -1.640625 2.15625 -2.234375q0.546875 -0.796875 0.546875 -1.59375q0 -0.875 -0.46875 -1.34375q-0.46875 -0.46875 -1.296875 -0.46875q-0.8125 0 -1.296875 0.5q-0.484375 0.484375 -0.5625 1.625l-2.59375 -0.25q0.234375 -2.15625 1.453125 -3.09375q1.21875 -0.9375 3.0625 -0.9375q2.015625 0 3.15625 1.09375q1.15625 1.078125 1.15625 2.6875q0 0.921875 -0.328125 1.75q-0.328125 0.828125 -1.046875 1.734375q-0.46875 0.609375 -1.703125 1.75q-1.234375 1.125 -1.5625 1.5q-0.328125 0.359375 -0.53125 0.71875l5.171875 0z" fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0" d="m514.8373 81.30708l142.58264 0l0 41.95276l-142.58264 0z" fill-rule="nonzero"></path><path fill="#a61c00" d="m524.5248 103.80521l2.671875 -0.265625q0.234375 1.34375 0.96875 1.984375q0.75 0.625 2.0 0.625q1.328125 0 2.0 -0.5625q0.671875 -0.5625 0.671875 -1.3125q0 -0.484375 -0.28125 -0.8125q-0.28125 -0.34375 -0.984375 -0.59375q-0.484375 -0.171875 -2.203125 -0.59375q-2.203125 -0.546875 -3.09375 -1.34375q-1.265625 -1.125 -1.265625 -2.734375q0 -1.046875 0.59375 -1.953125q0.59375 -0.90625 1.703125 -1.375q1.109375 -0.46875 2.671875 -0.46875q2.5625 0 3.859375 1.125q1.296875 1.109375 1.359375 2.984375l-2.75 0.125q-0.171875 -1.046875 -0.75 -1.5q-0.578125 -0.46875 -1.75 -0.46875q-1.1875 0 -1.875 0.5q-0.4375 0.3125 -0.4375 0.84375q0 0.484375 0.421875 0.828125q0.515625 0.421875 2.515625 0.90625q2.0 0.46875 2.953125 0.984375q0.96875 0.5 1.515625 1.375q0.546875 0.875 0.546875 2.15625q0 1.171875 -0.65625 2.203125q-0.640625 1.015625 -1.828125 1.515625q-1.1875 0.484375 -2.96875 0.484375q-2.578125 0 -3.96875 -1.1875q-1.375 -1.1875 -1.640625 -3.46875zm15.7247925 -9.171875l0 5.0q1.25 -1.484375 3.015625 -1.484375q0.890625 0 1.609375 0.34375q0.734375 0.328125 1.09375 0.84375q0.375 0.515625 0.5 1.15625q0.140625 0.625 0.140625 1.953125l0 5.78125l-2.609375 0l0 -5.203125q0 -1.546875 -0.15625 -1.96875q-0.140625 -0.421875 -0.515625 -0.65625q-0.375 -0.25 -0.9375 -0.25q-0.65625 0 -1.171875 0.3125q-0.5 0.3125 -0.734375 0.953125q-0.234375 0.640625 -0.234375 1.875l0 4.9375l-2.609375 0l0 -13.59375l2.609375 0zm10.739746 6.75l-2.359375 -0.4375q0.390625 -1.421875 1.359375 -2.109375q0.984375 -0.6875 2.90625 -0.6875q1.734375 0 2.59375 0.421875q0.859375 0.40625 1.203125 1.046875q0.34375 0.625 0.34375 2.328125l-0.03125 3.046875q0 1.296875 0.125 1.921875q0.125 0.609375 0.46875 1.3125l-2.578125 0q-0.09375 -0.265625 -0.25 -0.765625q-0.0625 -0.234375 -0.09375 -0.3125q-0.65625 0.65625 -1.421875 0.984375q-0.765625 0.3125 -1.625 0.3125q-1.515625 0 -2.40625 -0.8125q-0.875 -0.828125 -0.875 -2.09375q0 -0.84375 0.390625 -1.484375q0.40625 -0.65625 1.125 -1.0q0.71875 -0.359375 2.078125 -0.625q1.828125 -0.328125 2.53125 -0.625l0 -0.265625q0 -0.75 -0.375 -1.0625q-0.359375 -0.328125 -1.390625 -0.328125q-0.703125 0 -1.09375 0.28125q-0.390625 0.265625 -0.625 0.953125zm3.484375 2.109375q-0.5 0.171875 -1.59375 0.40625q-1.078125 0.234375 -1.40625 0.453125q-0.515625 0.359375 -0.515625 0.921875q0 0.546875 0.40625 0.953125q0.40625 0.390625 1.046875 0.390625q0.703125 0 1.34375 -0.46875q0.46875 -0.359375 0.625 -0.859375q0.09375 -0.34375 0.09375 -1.28125l0 -0.515625zm7.4382324 4.734375l-2.609375 0l0 -9.859375l2.421875 0l0 1.40625q0.625 -0.984375 1.109375 -1.296875q0.5 -0.328125 1.140625 -0.328125q0.890625 0 1.71875 0.5l-0.8125 2.265625q-0.65625 -0.421875 -1.21875 -0.421875q-0.546875 0 -0.9375 0.296875q-0.375 0.296875 -0.59375 1.09375q-0.21875 0.78125 -0.21875 3.296875l0 3.046875zm10.463379 -3.140625l2.609375 0.4375q-0.5 1.4375 -1.59375 2.1875q-1.078125 0.734375 -2.703125 0.734375q-2.5625 0 -3.796875 -1.671875q-0.96875 -1.34375 -0.96875 -3.40625q0 -2.4375 1.265625 -3.828125q1.28125 -1.390625 3.25 -1.390625q2.1875 0 3.453125 1.453125q1.28125 1.453125 1.234375 4.453125l-6.53125 0q0.015625 1.15625 0.625 1.8125q0.609375 0.640625 1.5 0.640625q0.609375 0 1.03125 -0.328125q0.421875 -0.34375 0.625 -1.09375zm0.15625 -2.625q-0.03125 -1.140625 -0.59375 -1.71875q-0.546875 -0.59375 -1.34375 -0.59375q-0.859375 0 -1.40625 0.625q-0.5625 0.609375 -0.546875 1.6875l3.890625 0zm13.563232 5.765625l-2.421875 0l0 -1.453125q-0.609375 0.84375 -1.4375 1.265625q-0.8125 0.40625 -1.640625 0.40625q-1.703125 0 -2.921875 -1.359375q-1.203125 -1.375 -1.203125 -3.828125q0 -2.5 1.171875 -3.796875q1.1875 -1.3125 2.984375 -1.3125q1.65625 0 2.859375 1.375l0 -4.890625l2.609375 0l0 13.59375zm-6.96875 -5.140625q0 1.578125 0.4375 2.28125q0.640625 1.015625 1.765625 1.015625q0.90625 0 1.53125 -0.765625q0.625 -0.765625 0.625 -2.28125q0 -1.703125 -0.609375 -2.4375q-0.609375 -0.75 -1.5625 -0.75q-0.9375 0 -1.5625 0.734375q-0.625 0.734375 -0.625 2.203125zm14.391785 5.140625l0 -13.59375l2.609375 0l0 4.890625q1.203125 -1.375 2.859375 -1.375q1.796875 0 2.96875 1.3125q1.1875 1.296875 1.1875 3.734375q0 2.53125 -1.203125 3.890625q-1.203125 1.359375 -2.921875 1.359375q-0.84375 0 -1.671875 -0.421875q-0.8125 -0.421875 -1.40625 -1.25l0 1.453125l-2.421875 0zm2.59375 -5.140625q0 1.53125 0.484375 2.265625q0.671875 1.03125 1.796875 1.03125q0.859375 0 1.46875 -0.734375q0.609375 -0.734375 0.609375 -2.328125q0 -1.6875 -0.609375 -2.421875q-0.609375 -0.75 -1.578125 -0.75q-0.9375 0 -1.5625 0.734375q-0.609375 0.71875 -0.609375 2.203125zm7.677246 -4.71875l2.78125 0l2.359375 7.0l2.296875 -7.0l2.703125 0l-3.484375 9.484375l-0.625 1.71875q-0.34375 0.859375 -0.65625 1.3125q-0.296875 0.46875 -0.703125 0.75q-0.40625 0.28125 -1.0 0.4375q-0.59375 0.15625 -1.328125 0.15625q-0.75 0 -1.46875 -0.15625l-0.234375 -2.046875q0.609375 0.125 1.09375 0.125q0.921875 0 1.34375 -0.53125q0.4375 -0.53125 0.671875 -1.359375l-3.75 -9.890625zm16.793396 -3.734375l2.75 0l0 7.359375q0 1.75 0.109375 2.265625q0.171875 0.84375 0.828125 1.359375q0.671875 0.5 1.8125 0.5q1.171875 0 1.765625 -0.484375q0.59375 -0.484375 0.71875 -1.171875q0.125 -0.703125 0.125 -2.3125l0 -7.515625l2.734375 0l0 7.140625q0 2.4375 -0.21875 3.453125q-0.21875 1.015625 -0.828125 1.71875q-0.59375 0.6875 -1.59375 1.109375q-1.0 0.40625 -2.609375 0.40625q-1.953125 0 -2.96875 -0.453125q-1.0 -0.453125 -1.59375 -1.171875q-0.578125 -0.71875 -0.75 -1.5q-0.28125 -1.171875 -0.28125 -3.453125l0 -7.25zm19.5979 13.59375l-2.609375 0l0 -9.828125q-1.4375 1.34375 -3.375 1.984375l0 -2.375q1.03125 -0.328125 2.21875 -1.25q1.203125 -0.9375 1.640625 -2.1875l2.125 0l0 13.65625z" fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0" d="m882.69293 27.729658l444.2834 0l0 407.5276l-444.2834 0z" fill-rule="nonzero"></path><path stroke="#c27ba0" stroke-width="2.0" stroke-linejoin="round" stroke-linecap="butt" d="m882.69293 27.729658l444.2834 0l0 407.5276l-444.2834 0z" fill-rule="nonzero"></path><path fill="#000000" d="m893.06793 54.64966l0 -13.59375l4.421875 0q2.5 0 3.265625 0.203125q1.15625 0.296875 1.9375 1.328125q0.796875 1.015625 0.796875 2.640625q0 1.25 -0.453125 2.109375q-0.453125 0.859375 -1.15625 1.34375q-0.703125 0.484375 -1.421875 0.640625q-0.984375 0.203125 -2.84375 0.203125l-1.796875 0l0 5.125l-2.75 0zm2.75 -11.296875l0 3.859375l1.5 0q1.625 0 2.171875 -0.21875q0.546875 -0.21875 0.859375 -0.671875q0.3125 -0.453125 0.3125 -1.046875q0 -0.75 -0.4375 -1.234375q-0.4375 -0.484375 -1.09375 -0.59375q-0.5 -0.09375 -1.984375 -0.09375l-1.328125 0zm15.3810425 8.15625l2.609375 0.4375q-0.5 1.4375 -1.59375 2.1875q-1.078125 0.734375 -2.703125 0.734375q-2.5625 0 -3.796875 -1.671875q-0.96875 -1.34375 -0.96875 -3.40625q0 -2.4375 1.265625 -3.828125q1.28125 -1.390625 3.25 -1.390625q2.1875 0 3.453125 1.453125q1.28125 1.453125 1.234375 4.453125l-6.53125 0q0.015625 1.15625 0.625 1.8125q0.609375 0.640625 1.5 0.640625q0.609375 0 1.03125 -0.328125q0.421875 -0.34375 0.625 -1.09375zm0.15625 -2.625q-0.03125 -1.140625 -0.59375 -1.71875q-0.546875 -0.59375 -1.34375 -0.59375q-0.859375 0 -1.40625 0.625q-0.5625 0.609375 -0.546875 1.6875l3.890625 0zm7.0163574 5.765625l-2.609375 0l0 -9.859375l2.421875 0l0 1.40625q0.625 -0.984375 1.109375 -1.296875q0.5 -0.328125 1.140625 -0.328125q0.890625 0 1.71875 0.5l-0.8125 2.265625q-0.65625 -0.421875 -1.21875 -0.421875q-0.546875 0 -0.9375 0.296875q-0.375 0.296875 -0.59375 1.09375q-0.21875 0.78125 -0.21875 3.296875l0 3.046875zm4.572754 -9.859375l2.40625 0l0 1.359375q1.28125 -1.578125 3.0625 -1.578125q0.953125 0 1.640625 0.390625q0.703125 0.390625 1.140625 1.1875q0.65625 -0.796875 1.40625 -1.1875q0.75 -0.390625 1.609375 -0.390625q1.078125 0 1.828125 0.453125q0.75 0.4375 1.125 1.28125q0.265625 0.640625 0.265625 2.046875l0 6.296875l-2.609375 0l0 -5.625q0 -1.46875 -0.265625 -1.90625q-0.359375 -0.546875 -1.109375 -0.546875q-0.546875 0 -1.03125 0.34375q-0.484375 0.328125 -0.703125 0.96875q-0.203125 0.640625 -0.203125 2.03125l0 4.734375l-2.609375 0l0 -5.40625q0 -1.4375 -0.140625 -1.84375q-0.140625 -0.421875 -0.4375 -0.625q-0.28125 -0.203125 -0.78125 -0.203125q-0.609375 0 -1.09375 0.328125q-0.484375 0.3125 -0.6875 0.9375q-0.203125 0.609375 -0.203125 2.03125l0 4.78125l-2.609375 0l0 -9.859375zm16.775879 -1.328125l0 -2.40625l2.609375 0l0 2.40625l-2.609375 0zm0 11.1875l0 -9.859375l2.609375 0l0 9.859375l-2.609375 0zm4.2770996 -2.8125l2.609375 -0.390625q0.171875 0.75 0.671875 1.15625q0.515625 0.390625 1.4375 0.390625q1.0 0 1.515625 -0.375q0.34375 -0.265625 0.34375 -0.703125q0 -0.296875 -0.1875 -0.484375q-0.1875 -0.1875 -0.875 -0.34375q-3.140625 -0.703125 -4.0 -1.265625q-1.15625 -0.796875 -1.15625 -2.21875q0 -1.28125 1.0 -2.15625q1.015625 -0.875 3.140625 -0.875q2.03125 0 3.0 0.65625q0.984375 0.65625 1.359375 1.953125l-2.453125 0.453125q-0.15625 -0.578125 -0.609375 -0.875q-0.4375 -0.3125 -1.25 -0.3125q-1.03125 0 -1.46875 0.296875q-0.296875 0.203125 -0.296875 0.515625q0 0.28125 0.25 0.484375q0.359375 0.25 2.4375 0.734375q2.078125 0.46875 2.90625 1.15625q0.828125 0.6875 0.828125 1.9375q0 1.359375 -1.140625 2.328125q-1.125 0.96875 -3.34375 0.96875q-2.015625 0 -3.1875 -0.8125q-1.171875 -0.8125 -1.53125 -2.21875zm10.375671 0l2.609375 -0.390625q0.171875 0.75 0.671875 1.15625q0.515625 0.390625 1.4375 0.390625q1.0 0 1.515625 -0.375q0.34375 -0.265625 0.34375 -0.703125q0 -0.296875 -0.1875 -0.484375q-0.1875 -0.1875 -0.875 -0.34375q-3.140625 -0.703125 -4.0 -1.265625q-1.15625 -0.796875 -1.15625 -2.21875q0 -1.28125 1.0 -2.15625q1.015625 -0.875 3.140625 -0.875q2.03125 0 3.0 0.65625q0.984375 0.65625 1.359375 1.953125l-2.453125 0.453125q-0.15625 -0.578125 -0.609375 -0.875q-0.4375 -0.3125 -1.25 -0.3125q-1.03125 0 -1.46875 0.296875q-0.296875 0.203125 -0.296875 0.515625q0 0.28125 0.25 0.484375q0.359375 0.25 2.4375 0.734375q2.078125 0.46875 2.90625 1.15625q0.828125 0.6875 0.828125 1.9375q0 1.359375 -1.140625 2.328125q-1.125 0.96875 -3.34375 0.96875q-2.015625 0 -3.1875 -0.8125q-1.171875 -0.8125 -1.53125 -2.21875zm11.281982 -8.375l0 -2.40625l2.609375 0l0 2.40625l-2.609375 0zm0 11.1875l0 -9.859375l2.609375 0l0 9.859375l-2.609375 0zm4.5895386 -5.0625q0 -1.296875 0.640625 -2.515625q0.640625 -1.21875 1.8125 -1.859375q1.171875 -0.640625 2.609375 -0.640625q2.25 0 3.671875 1.453125q1.421875 1.453125 1.421875 3.671875q0 2.234375 -1.4375 3.703125q-1.4375 1.46875 -3.625 1.46875q-1.359375 0 -2.59375 -0.609375q-1.21875 -0.609375 -1.859375 -1.796875q-0.640625 -1.1875 -0.640625 -2.875zm2.671875 0.140625q0 1.46875 0.6875 2.25q0.703125 0.765625 1.71875 0.765625q1.015625 0 1.703125 -0.765625q0.703125 -0.78125 0.703125 -2.265625q0 -1.453125 -0.703125 -2.234375q-0.6875 -0.78125 -1.703125 -0.78125q-1.015625 0 -1.71875 0.78125q-0.6875 0.78125 -0.6875 2.25zm18.286621 4.921875l-2.609375 0l0 -5.03125q0 -1.59375 -0.171875 -2.0625q-0.15625 -0.46875 -0.53125 -0.71875q-0.375 -0.265625 -0.90625 -0.265625q-0.6875 0 -1.234375 0.375q-0.53125 0.359375 -0.734375 0.984375q-0.1875 0.609375 -0.1875 2.25l0 4.46875l-2.609375 0l0 -9.859375l2.421875 0l0 1.453125q1.296875 -1.671875 3.25 -1.671875q0.859375 0 1.578125 0.3125q0.71875 0.3125 1.078125 0.796875q0.359375 0.484375 0.5 1.09375q0.15625 0.609375 0.15625 1.75l0 6.125zm1.5209961 -2.8125l2.609375 -0.390625q0.171875 0.75 0.671875 1.15625q0.515625 0.390625 1.4375 0.390625q1.0 0 1.515625 -0.375q0.34375 -0.265625 0.34375 -0.703125q0 -0.296875 -0.1875 -0.484375q-0.1875 -0.1875 -0.875 -0.34375q-3.140625 -0.703125 -4.0 -1.265625q-1.15625 -0.796875 -1.15625 -2.21875q0 -1.28125 1.0 -2.15625q1.015625 -0.875 3.140625 -0.875q2.03125 0 3.0 0.65625q0.984375 0.65625 1.359375 1.953125l-2.453125 0.453125q-0.15625 -0.578125 -0.609375 -0.875q-0.4375 -0.3125 -1.25 -0.3125q-1.03125 0 -1.46875 0.296875q-0.296875 0.203125 -0.296875 0.515625q0 0.28125 0.25 0.484375q0.359375 0.25 2.4375 0.734375q2.078125 0.46875 2.90625 1.15625q0.828125 0.6875 0.828125 1.9375q0 1.359375 -1.140625 2.328125q-1.125 0.96875 -3.34375 0.96875q-2.015625 0 -3.1875 -0.8125q-1.171875 -0.8125 -1.53125 -2.21875zm11.781982 -4.4375l0 -2.609375l2.609375 0l0 2.609375l-2.609375 0zm0 7.25l0 -2.609375l2.609375 0l0 2.609375l-2.609375 0z" fill-rule="nonzero"></path><path fill="#000000" d="m902.08356 85.05591l1.796875 0l0 7.84375q0 2.0625 -0.46875 3.265625q-0.453125 1.203125 -1.671875 1.96875q-1.203125 0.75 -3.171875 0.75q-1.90625 0 -3.125 -0.65625q-1.21875 -0.65625 -1.734375 -1.90625q-0.515625 -1.25 -0.515625 -3.421875l0 -7.84375l1.796875 0l0 7.84375q0 1.765625 0.328125 2.609375q0.328125 0.84375 1.125 1.296875q0.8125 0.453125 1.96875 0.453125q1.984375 0 2.828125 -0.890625q0.84375 -0.90625 0.84375 -3.46875l0 -7.84375zm10.1604 13.59375l-1.671875 0l0 -10.640625q-0.59375 0.578125 -1.578125 1.15625q-0.984375 0.5625 -1.765625 0.859375l0 -1.625q1.40625 -0.65625 2.453125 -1.59375q1.046875 -0.9375 1.484375 -1.8125l1.078125 0l0 13.65625zm9.090271 -4.078125l0 -1.6875l5.125 0l0 1.6875l-5.125 0zm12.255371 4.078125l0 -13.59375l5.125 0q1.359375 0 2.078125 0.125q1.0 0.171875 1.671875 0.640625q0.671875 0.46875 1.078125 1.3125q0.421875 0.84375 0.421875 1.84375q0 1.734375 -1.109375 2.9375q-1.09375 1.203125 -3.984375 1.203125l-3.484375 0l0 5.53125l-1.796875 0zm1.796875 -7.140625l3.515625 0q1.75 0 2.46875 -0.640625q0.734375 -0.65625 0.734375 -1.828125q0 -0.859375 -0.4375 -1.46875q-0.421875 -0.609375 -1.125 -0.796875q-0.453125 -0.125 -1.671875 -0.125l-3.484375 0l0 4.859375zm16.256104 7.140625l-1.671875 0l0 -10.640625q-0.59375 0.578125 -1.578125 1.15625q-0.984375 0.5625 -1.765625 0.859375l0 -1.625q1.40625 -0.65625 2.453125 -1.59375q1.046875 -0.9375 1.484375 -1.8125l1.078125 0l0 13.65625zm4.9850464 0l0 -1.90625l1.90625 0l0 1.90625q0 1.046875 -0.375 1.6875q-0.375 0.65625 -1.171875 1.0l-0.46875 -0.71875q0.53125 -0.21875 0.78125 -0.671875q0.25 -0.453125 0.28125 -1.296875l-0.953125 0zm10.147888 0l0 -13.59375l5.125 0q1.359375 0 2.078125 0.125q1.0 0.171875 1.671875 0.640625q0.671875 0.46875 1.078125 1.3125q0.421875 0.84375 0.421875 1.84375q0 1.734375 -1.109375 2.9375q-1.09375 1.203125 -3.984375 1.203125l-3.484375 0l0 5.53125l-1.796875 0zm1.796875 -7.140625l3.515625 0q1.75 0 2.46875 -0.640625q0.734375 -0.65625 0.734375 -1.828125q0 -0.859375 -0.4375 -1.46875q-0.421875 -0.609375 -1.125 -0.796875q-0.453125 -0.125 -1.671875 -0.125l-3.484375 0l0 4.859375zm18.740417 5.53125l0 1.609375l-8.984375 0q-0.015625 -0.609375 0.1875 -1.15625q0.34375 -0.921875 1.09375 -1.8125q0.765625 -0.890625 2.1875 -2.0625q2.21875 -1.8125 3.0 -2.875q0.78125 -1.0625 0.78125 -2.015625q0 -0.984375 -0.71875 -1.671875q-0.703125 -0.6875 -1.84375 -0.6875q-1.203125 0 -1.9375 0.734375q-0.71875 0.71875 -0.71875 2.0l-1.71875 -0.171875q0.171875 -1.921875 1.328125 -2.921875q1.15625 -1.015625 3.09375 -1.015625q1.953125 0 3.09375 1.09375q1.140625 1.078125 1.140625 2.6875q0 0.8125 -0.34375 1.609375q-0.328125 0.78125 -1.109375 1.65625q-0.765625 0.859375 -2.5625 2.390625q-1.5 1.265625 -1.9375 1.71875q-0.421875 0.4375 -0.703125 0.890625l6.671875 0zm2.5007324 1.609375l0 -1.90625l1.90625 0l0 1.90625q0 1.046875 -0.375 1.6875q-0.375 0.65625 -1.171875 1.0l-0.46875 -0.71875q0.53125 -0.21875 0.78125 -0.671875q0.25 -0.453125 0.28125 -1.296875l-0.953125 0zm10.147888 0l0 -13.59375l5.125 0q1.359375 0 2.078125 0.125q1.0 0.171875 1.671875 0.640625q0.671875 0.46875 1.078125 1.3125q0.421875 0.84375 0.421875 1.84375q0 1.734375 -1.109375 2.9375q-1.09375 1.203125 -3.984375 1.203125l-3.484375 0l0 5.53125l-1.796875 0zm1.796875 -7.140625l3.515625 0q1.75 0 2.46875 -0.640625q0.734375 -0.65625 0.734375 -1.828125q0 -0.859375 -0.4375 -1.46875q-0.421875 -0.609375 -1.125 -0.796875q-0.453125 -0.125 -1.671875 -0.125l-3.484375 0l0 4.859375zm9.9747925 3.546875l1.671875 -0.21875q0.28125 1.421875 0.96875 2.046875q0.703125 0.625 1.6875 0.625q1.1875 0 2.0 -0.8125q0.8125 -0.828125 0.8125 -2.03125q0 -1.140625 -0.765625 -1.890625q-0.75 -0.75 -1.90625 -0.75q-0.46875 0 -1.171875 0.1875l0.1875 -1.46875q0.15625 0.015625 0.265625 0.015625q1.0625 0 1.90625 -0.546875q0.859375 -0.5625 0.859375 -1.71875q0 -0.921875 -0.625 -1.515625q-0.609375 -0.609375 -1.59375 -0.609375q-0.96875 0 -1.625 0.609375q-0.640625 0.609375 -0.828125 1.84375l-1.671875 -0.296875q0.296875 -1.6875 1.375 -2.609375q1.09375 -0.921875 2.71875 -0.921875q1.109375 0 2.046875 0.484375q0.9375 0.46875 1.421875 1.296875q0.5 0.828125 0.5 1.75q0 0.890625 -0.46875 1.609375q-0.46875 0.71875 -1.40625 1.15625q1.21875 0.265625 1.875 1.15625q0.671875 0.875 0.671875 2.1875q0 1.78125 -1.296875 3.015625q-1.296875 1.234375 -3.28125 1.234375q-1.796875 0 -2.984375 -1.0625q-1.171875 -1.0625 -1.34375 -2.765625zm11.266357 3.59375l0 -1.90625l1.906189 0l0 1.90625q0 1.046875 -0.375 1.6875q-0.375 0.65625 -1.171814 1.0l-0.46875 -0.71875q0.53125 -0.21875 0.78125 -0.671875q0.25 -0.453125 0.28125 -1.296875l-0.953125 0zm10.147888 0l0 -13.59375l5.125 0q1.359375 0 2.078125 0.125q1.0 0.171875 1.671875 0.640625q0.671875 0.46875 1.078125 1.3125q0.421875 0.84375 0.421875 1.84375q0 1.734375 -1.109375 2.9375q-1.09375 1.203125 -3.984375 1.203125l-3.484375 0l0 5.53125l-1.796875 0zm1.796875 -7.140625l3.515625 0q1.75 0 2.46875 -0.640625q0.734375 -0.65625 0.734375 -1.828125q0 -0.859375 -0.4375 -1.46875q-0.421875 -0.609375 -1.125 -0.796875q-0.453125 -0.125 -1.671875 -0.125l-3.484375 0l0 4.859375zm15.318481 7.140625l0 -3.25l-5.90625 0l0 -1.53125l6.21875 -8.8125l1.359375 0l0 8.8125l1.84375 0l0 1.53125l-1.84375 0l0 3.25l-1.671875 0zm0 -4.78125l0 -6.140625l-4.25 6.140625l4.25 0zm10.027832 0.703125l0 -1.6875l5.125 0l0 1.6875l-5.125 0zm13.349121 -7.234375l0 1.9375l-1.796875 0l0 -1.53125q0 -1.25 0.296875 -1.796875q0.390625 -0.75 1.234375 -1.125l0.40625 0.640625q-0.5 0.21875 -0.75 0.640625q-0.234375 0.421875 -0.265625 1.234375l0.875 0zm2.875 0l0 1.9375l-1.796875 0l0 -1.53125q0 -1.25 0.296875 -1.796875q0.390625 -0.75 1.234375 -1.125l0.40625 0.640625q-0.5 0.21875 -0.75 0.640625q-0.234375 0.421875 -0.265625 1.234375l0.875 0zm8.4626465 7.703125l1.640625 0.21875q-0.265625 1.6875 -1.375 2.65625q-1.109375 0.953125 -2.734375 0.953125q-2.015625 0 -3.25 -1.3125q-1.21875 -1.328125 -1.21875 -3.796875q0 -1.59375 0.515625 -2.78125q0.53125 -1.203125 1.609375 -1.796875q1.09375 -0.609375 2.359375 -0.609375q1.609375 0 2.625 0.8125q1.015625 0.8125 1.3125 2.3125l-1.625 0.25q-0.234375 -1.0 -0.828125 -1.5q-0.59375 -0.5 -1.421875 -0.5q-1.265625 0 -2.0625 0.90625q-0.78125 0.90625 -0.78125 2.859375q0 1.984375 0.765625 2.890625q0.765625 0.890625 1.984375 0.890625q0.984375 0 1.640625 -0.59375q0.65625 -0.609375 0.84375 -1.859375zm9.328125 2.390625q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm4.0788574 4.9375l0 -9.859375l1.5 0l0 1.40625q1.09375 -1.625 3.140625 -1.625q0.890625 0 1.640625 0.328125q0.75 0.3125 1.109375 0.84375q0.375 0.515625 0.53125 1.21875q0.09375 0.46875 0.09375 1.625l0 6.0625l-1.671875 0l0 -6.0q0 -1.015625 -0.203125 -1.515625q-0.1875 -0.515625 -0.6875 -0.8125q-0.5 -0.296875 -1.171875 -0.296875q-1.0625 0 -1.84375 0.671875q-0.765625 0.671875 -0.765625 2.578125l0 5.375l-1.671875 0zm8.844482 3.78125l0 -1.21875l11.0625 0l0 1.21875l-11.0625 0zm11.906982 -3.78125l0 -9.859375l1.5 0l0 1.390625q0.453125 -0.71875 1.21875 -1.15625q0.78125 -0.453125 1.765625 -0.453125q1.09375 0 1.796875 0.453125q0.703125 0.453125 0.984375 1.28125q1.171875 -1.734375 3.046875 -1.734375q1.46875 0 2.25 0.8125q0.796875 0.8125 0.796875 2.5l0 6.765625l-1.671875 0l0 -6.203125q0 -1.0 -0.15625 -1.4375q-0.15625 -0.453125 -0.59375 -0.71875q-0.421875 -0.265625 -1.0 -0.265625q-1.03125 0 -1.71875 0.6875q-0.6875 0.6875 -0.6875 2.21875l0 5.71875l-1.671875 0l0 -6.40625q0 -1.109375 -0.40625 -1.65625q-0.40625 -0.5625 -1.34375 -0.5625q-0.703125 0 -1.3125 0.375q-0.59375 0.359375 -0.859375 1.078125q-0.265625 0.71875 -0.265625 2.0625l0 5.109375l-1.671875 0zm21.978271 -1.21875q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm4.0788574 4.9375l0 -9.859375l1.5 0l0 1.40625q1.09375 -1.625 3.140625 -1.625q0.890625 0 1.640625 0.328125q0.75 0.3125 1.109375 0.84375q0.375 0.515625 0.53125 1.21875q0.09375 0.46875 0.09375 1.625l0 6.0625l-1.671875 0l0 -6.0q0 -1.015625 -0.203125 -1.515625q-0.1875 -0.515625 -0.6875 -0.8125q-0.5 -0.296875 -1.171875 -0.296875q-1.0625 0 -1.84375 0.671875q-0.765625 0.671875 -0.765625 2.578125l0 5.375l-1.671875 0zm16.813232 -1.21875q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm3.7819824 5.75l1.609375 0.25q0.109375 0.75 0.578125 1.09375q0.609375 0.453125 1.6875 0.453125q1.171875 0 1.796875 -0.46875q0.625 -0.453125 0.859375 -1.28125q0.125 -0.515625 0.109375 -2.15625q-1.09375 1.296875 -2.71875 1.296875q-2.03125 0 -3.15625 -1.46875q-1.109375 -1.46875 -1.109375 -3.515625q0 -1.40625 0.515625 -2.59375q0.515625 -1.203125 1.484375 -1.84375q0.96875 -0.65625 2.265625 -0.65625q1.75 0 2.875 1.40625l0 -1.1875l1.546875 0l0 8.515625q0 2.3125 -0.46875 3.265625q-0.46875 0.96875 -1.484375 1.515625q-1.015625 0.5625 -2.5 0.5625q-1.765625 0 -2.859375 -0.796875q-1.078125 -0.796875 -1.03125 -2.390625zm1.375 -5.921875q0 1.953125 0.765625 2.84375q0.78125 0.890625 1.9375 0.890625q1.140625 0 1.921875 -0.890625q0.78125 -0.890625 0.78125 -2.78125q0 -1.8125 -0.8125 -2.71875q-0.796875 -0.921875 -1.921875 -0.921875q-1.109375 0 -1.890625 0.90625q-0.78125 0.890625 -0.78125 2.671875zm16.047607 1.9375l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm8.672607 -5.921875l0 -1.9375l1.78125 0l0 1.53125q0 1.234375 -0.28125 1.78125q-0.40625 0.75 -1.25 1.140625l-0.40625 -0.671875q0.5 -0.203125 0.75 -0.640625q0.25 -0.4375 0.265625 -1.203125l-0.859375 0zm2.875 0l0 -1.9375l1.78125 0l0 1.53125q0 1.234375 -0.28125 1.78125q-0.40625 0.75 -1.25 1.140625l-0.40625 -0.671875q0.5 -0.203125 0.75 -0.640625q0.25 -0.4375 0.28125 -1.203125l-0.875 0zm12.145996 15.796875q-1.375 -1.75 -2.328125 -4.078125q-0.953125 -2.34375 -0.953125 -4.84375q0 -2.21875 0.703125 -4.234375q0.84375 -2.34375 2.578125 -4.671875l1.203125 0q-1.125 1.921875 -1.484375 2.75q-0.5625 1.28125 -0.890625 2.671875q-0.40625 1.734375 -0.40625 3.484375q0 4.46875 2.78125 8.921875l-1.203125 0zm2.4001465 -8.921875q0 -2.734375 1.53125 -4.0625q1.265625 -1.09375 3.09375 -1.09375q2.03125 0 3.3125 1.34375q1.296875 1.328125 1.296875 3.671875q0 1.90625 -0.578125 3.0q-0.5625 1.078125 -1.65625 1.6875q-1.078125 0.59375 -2.375 0.59375q-2.0625 0 -3.34375 -1.328125q-1.28125 -1.328125 -1.28125 -3.8125zm1.71875 0q0 1.890625 0.828125 2.828125q0.828125 0.9375 2.078125 0.9375q1.25 0 2.0625 -0.9375q0.828125 -0.953125 0.828125 -2.890625q0 -1.828125 -0.828125 -2.765625q-0.828125 -0.9375 -2.0625 -0.9375q-1.25 0 -2.078125 0.9375q-0.828125 0.9375 -0.828125 2.828125zm11.110107 4.921875l-3.015625 -9.859375l1.71875 0l1.5625 5.6875l0.59375 2.125q0.03125 -0.15625 0.5 -2.03125l1.578125 -5.78125l1.71875 0l1.46875 5.71875l0.484375 1.890625l0.578125 -1.90625l1.6875 -5.703125l1.625 0l-3.078125 9.859375l-1.734375 0l-1.578125 -5.90625l-0.375 -1.671875l-2.0 7.578125l-1.734375 0zm11.644775 0l0 -9.859375l1.5 0l0 1.40625q1.09375 -1.625 3.140625 -1.625q0.890625 0 1.640625 0.328125q0.75 0.3125 1.109375 0.84375q0.375 0.515625 0.53125 1.21875q0.09375 0.46875 0.09375 1.625l0 6.0625l-1.671875 0l0 -6.0q0 -1.015625 -0.203125 -1.515625q-0.1875 -0.515625 -0.6875 -0.8125q-0.5 -0.296875 -1.171875 -0.296875q-1.0625 0 -1.84375 0.671875q-0.765625 0.671875 -0.765625 2.578125l0 5.375l-1.671875 0zm17.125732 -3.171875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm9.094482 5.875l0 -9.859375l1.5 0l0 1.5q0.578125 -1.046875 1.0625 -1.375q0.484375 -0.34375 1.078125 -0.34375q0.84375 0 1.71875 0.546875l-0.578125 1.546875q-0.609375 -0.359375 -1.234375 -0.359375q-0.546875 0 -0.984375 0.328125q-0.421875 0.328125 -0.609375 0.90625q-0.28125 0.890625 -0.28125 1.953125l0 5.15625l-1.671875 0zm7.3221436 4.0l-1.1875 0q2.765625 -4.453125 2.765625 -8.921875q0 -1.734375 -0.390625 -3.453125q-0.328125 -1.390625 -0.890625 -2.671875q-0.359375 -0.84375 -1.484375 -2.78125l1.1875 0q1.75 2.328125 2.578125 4.671875q0.71875 2.015625 0.71875 4.234375q0 2.5 -0.96875 4.84375q-0.953125 2.328125 -2.328125 4.078125z" fill-rule="nonzero"></path><path fill="#000000" d="m902.08356 129.05591l1.796875 0l0 7.84375q0 2.0625 -0.46875 3.265625q-0.453125 1.203125 -1.671875 1.96875q-1.203125 0.75 -3.171875 0.75q-1.90625 0 -3.125 -0.65625q-1.21875 -0.65625 -1.734375 -1.90625q-0.515625 -1.25 -0.515625 -3.421875l0 -7.84375l1.796875 0l0 7.84375q0 1.765625 0.328125 2.609375q0.328125 0.84375 1.125 1.296875q0.8125 0.453125 1.96875 0.453125q1.984375 0 2.828125 -0.890625q0.84375 -0.90625 0.84375 -3.46875l0 -7.84375zm12.644775 11.984375l0 1.609375l-8.984375 0q-0.015625 -0.609375 0.1875 -1.15625q0.34375 -0.921875 1.09375 -1.8125q0.765625 -0.890625 2.1875 -2.0625q2.21875 -1.8125 3.0 -2.875q0.78125 -1.0625 0.78125 -2.015625q0 -0.984375 -0.71875 -1.671875q-0.703125 -0.6875 -1.84375 -0.6875q-1.203125 0 -1.9375 0.734375q-0.71875 0.71875 -0.71875 2.0l-1.71875 -0.171875q0.171875 -1.921875 1.328125 -2.921875q1.15625 -1.015625 3.09375 -1.015625q1.953125 0 3.09375 1.09375q1.140625 1.078125 1.140625 2.6875q0 0.8125 -0.34375 1.609375q-0.328125 0.78125 -1.109375 1.65625q-0.765625 0.859375 -2.5625 2.390625q-1.5 1.265625 -1.9375 1.71875q-0.421875 0.4375 -0.703125 0.890625l6.671875 0zm6.605896 -2.46875l0 -1.6875l5.125 0l0 1.6875l-5.125 0zm12.255371 4.078125l0 -13.59375l5.125 0q1.359375 0 2.078125 0.125q1.0 0.171875 1.671875 0.640625q0.671875 0.46875 1.078125 1.3125q0.421875 0.84375 0.421875 1.84375q0 1.734375 -1.109375 2.9375q-1.09375 1.203125 -3.984375 1.203125l-3.484375 0l0 5.53125l-1.796875 0zm1.796875 -7.140625l3.515625 0q1.75 0 2.46875 -0.640625q0.734375 -0.65625 0.734375 -1.828125q0 -0.859375 -0.4375 -1.46875q-0.421875 -0.609375 -1.125 -0.796875q-0.453125 -0.125 -1.671875 -0.125l-3.484375 0l0 4.859375zm16.256104 7.140625l-1.671875 0l0 -10.640625q-0.59375 0.578125 -1.578125 1.15625q-0.984375 0.5625 -1.765625 0.859375l0 -1.625q1.40625 -0.65625 2.453125 -1.59375q1.046875 -0.9375 1.484375 -1.8125l1.078125 0l0 13.65625zm9.090271 -4.078125l0 -1.6875l5.125 0l0 1.6875l-5.125 0zm13.34906 -7.234375l0 1.9375l-1.796875 0l0 -1.53125q0 -1.25 0.296875 -1.796875q0.390625 -0.75 1.234375 -1.125l0.40625 0.640625q-0.5 0.21875 -0.75 0.640625q-0.234375 0.421875 -0.265625 1.234375l0.875 0zm2.875 0l0 1.9375l-1.796875 0l0 -1.53125q0 -1.25 0.296875 -1.796875q0.390625 -0.75 1.234375 -1.125l0.40625 0.640625q-0.5 0.21875 -0.75 0.640625q-0.234375 0.421875 -0.265625 1.234375l0.875 0zm8.4627075 7.703125l1.640625 0.21875q-0.265625 1.6875 -1.375 2.65625q-1.109375 0.953125 -2.734375 0.953125q-2.015625 0 -3.25 -1.3125q-1.21875 -1.328125 -1.21875 -3.796875q0 -1.59375 0.515625 -2.78125q0.53125 -1.203125 1.609375 -1.796875q1.09375 -0.609375 2.359375 -0.609375q1.609375 0 2.625 0.8125q1.015625 0.8125 1.3125 2.3125l-1.625 0.25q-0.234375 -1.0 -0.828125 -1.5q-0.59375 -0.5 -1.421875 -0.5q-1.265625 0 -2.0625 0.90625q-0.78125 0.90625 -0.78125 2.859375q0 1.984375 0.765625 2.890625q0.765625 0.890625 1.984375 0.890625q0.984375 0 1.640625 -0.59375q0.65625 -0.609375 0.84375 -1.859375zm9.328125 2.390625q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm4.0788574 4.9375l0 -9.859375l1.5 0l0 1.40625q1.09375 -1.625 3.140625 -1.625q0.890625 0 1.640625 0.328125q0.75 0.3125 1.109375 0.84375q0.375 0.515625 0.53125 1.21875q0.09375 0.46875 0.09375 1.625l0 6.0625l-1.671875 0l0 -6.0q0 -1.015625 -0.203125 -1.515625q-0.1875 -0.515625 -0.6875 -0.8125q-0.5 -0.296875 -1.171875 -0.296875q-1.0625 0 -1.84375 0.671875q-0.765625 0.671875 -0.765625 2.578125l0 5.375l-1.671875 0zm8.844421 3.78125l0 -1.21875l11.0625 0l0 1.21875l-11.0625 0zm13.735107 -3.78125l-3.015625 -9.859375l1.71875 0l1.5625 5.6875l0.59375 2.125q0.03125 -0.15625 0.5 -2.03125l1.578125 -5.78125l1.71875 0l1.46875 5.71875l0.484375 1.890625l0.578125 -1.90625l1.6875 -5.703125l1.625 0l-3.078125 9.859375l-1.734375 0l-1.578125 -5.90625l-0.375 -1.671875l-2.0 7.578125l-1.734375 0zm11.62915 0l0 -9.859375l1.5 0l0 1.5q0.578125 -1.046875 1.0625 -1.375q0.484375 -0.34375 1.078125 -0.34375q0.84375 0 1.71875 0.546875l-0.578125 1.546875q-0.609375 -0.359375 -1.234375 -0.359375q-0.546875 0 -0.984375 0.328125q-0.421875 0.328125 -0.609375 0.90625q-0.28125 0.890625 -0.28125 1.953125l0 5.15625l-1.671875 0zm6.2440186 -11.6875l0 -1.90625l1.671875 0l0 1.90625l-1.671875 0zm0 11.6875l0 -9.859375l1.671875 0l0 9.859375l-1.671875 0zm7.7854004 -1.5l0.234375 1.484375q-0.703125 0.140625 -1.265625 0.140625q-0.90625 0 -1.40625 -0.28125q-0.5 -0.296875 -0.703125 -0.75q-0.203125 -0.46875 -0.203125 -1.984375l0 -5.65625l-1.234375 0l0 -1.3125l1.234375 0l0 -2.4375l1.65625 -1.0l0 3.4375l1.6875 0l0 1.3125l-1.6875 0l0 5.75q0 0.71875 0.078125 0.921875q0.09375 0.203125 0.296875 0.328125q0.203125 0.125 0.578125 0.125q0.265625 0 0.734375 -0.078125zm8.2771 -1.671875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm8.672607 -5.921875l0 -1.9375l1.78125 0l0 1.53125q0 1.234375 -0.28125 1.78125q-0.40625 0.75 -1.25 1.140625l-0.40625 -0.671875q0.5 -0.203125 0.75 -0.640625q0.25 -0.4375 0.265625 -1.203125l-0.859375 0zm2.875 0l0 -1.9375l1.78125 0l0 1.53125q0 1.234375 -0.28125 1.78125q-0.40625 0.75 -1.25 1.140625l-0.40625 -0.671875q0.5 -0.203125 0.75 -0.640625q0.25 -0.4375 0.28125 -1.203125l-0.875 0zm12.145874 15.796875q-1.375 -1.75 -2.328125 -4.078125q-0.953125 -2.34375 -0.953125 -4.84375q0 -2.21875 0.703125 -4.234375q0.84375 -2.34375 2.578125 -4.671875l1.203125 0q-1.125 1.921875 -1.484375 2.75q-0.5625 1.28125 -0.890625 2.671875q-0.40625 1.734375 -0.40625 3.484375q0 4.46875 2.78125 8.921875l-1.203125 0zm2.3533936 -6.9375l1.65625 -0.265625q0.140625 1.0 0.765625 1.53125q0.640625 0.515625 1.78125 0.515625q1.15625 0 1.703125 -0.46875q0.5625 -0.46875 0.5625 -1.09375q0 -0.5625 -0.484375 -0.890625q-0.34375 -0.21875 -1.703125 -0.5625q-1.84375 -0.46875 -2.5625 -0.796875q-0.703125 -0.34375 -1.078125 -0.9375q-0.359375 -0.609375 -0.359375 -1.328125q0 -0.65625 0.296875 -1.21875q0.3125 -0.5625 0.828125 -0.9375q0.390625 -0.28125 1.0625 -0.484375q0.671875 -0.203125 1.4375 -0.203125q1.171875 0 2.046875 0.34375q0.875 0.328125 1.28125 0.90625q0.421875 0.5625 0.578125 1.515625l-1.625 0.21875q-0.109375 -0.75 -0.65625 -1.171875q-0.53125 -0.4375 -1.5 -0.4375q-1.15625 0 -1.640625 0.390625q-0.484375 0.375 -0.484375 0.875q0 0.328125 0.203125 0.59375q0.203125 0.265625 0.640625 0.4375q0.25 0.09375 1.46875 0.4375q1.765625 0.46875 2.46875 0.765625q0.703125 0.296875 1.09375 0.875q0.40625 0.578125 0.40625 1.4375q0 0.828125 -0.484375 1.578125q-0.484375 0.734375 -1.40625 1.140625q-0.921875 0.390625 -2.078125 0.390625q-1.921875 0 -2.9375 -0.796875q-1.0 -0.796875 -1.28125 -2.359375zm10.0 2.9375l0 -13.59375l1.671875 0l0 4.875q1.171875 -1.359375 2.953125 -1.359375q1.09375 0 1.890625 0.4375q0.8125 0.421875 1.15625 1.1875q0.359375 0.765625 0.359375 2.203125l0 6.25l-1.671875 0l0 -6.25q0 -1.25 -0.546875 -1.8125q-0.546875 -0.578125 -1.53125 -0.578125q-0.75 0 -1.40625 0.390625q-0.640625 0.375 -0.921875 1.046875q-0.28125 0.65625 -0.28125 1.8125l0 5.390625l-1.671875 0zm16.813232 -1.21875q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm4.0631104 4.9375l0 -9.859375l1.5 0l0 1.5q0.578125 -1.046875 1.0625 -1.375q0.484375 -0.34375 1.078125 -0.34375q0.84375 0 1.71875 0.546875l-0.578125 1.546875q-0.609375 -0.359375 -1.234375 -0.359375q-0.546875 0 -0.984375 0.328125q-0.421875 0.328125 -0.609375 0.90625q-0.28125 0.890625 -0.28125 1.953125l0 5.15625l-1.671875 0zm12.978394 -3.171875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm15.500732 5.875l0 -1.25q-0.9375 1.46875 -2.75 1.46875q-1.171875 0 -2.171875 -0.640625q-0.984375 -0.65625 -1.53125 -1.8125q-0.53125 -1.171875 -0.53125 -2.6875q0 -1.46875 0.484375 -2.671875q0.5 -1.203125 1.46875 -1.84375q0.984375 -0.640625 2.203125 -0.640625q0.890625 0 1.578125 0.375q0.703125 0.375 1.140625 0.984375l0 -4.875l1.65625 0l0 13.59375l-1.546875 0zm-5.28125 -4.921875q0 1.890625 0.796875 2.828125q0.8125 0.9375 1.890625 0.9375q1.09375 0 1.859375 -0.890625q0.765625 -0.890625 0.765625 -2.734375q0 -2.015625 -0.78125 -2.953125q-0.78125 -0.953125 -1.921875 -0.953125q-1.109375 0 -1.859375 0.90625q-0.75 0.90625 -0.75 2.859375zm15.99646 4.921875l-1.546875 0l0 -13.59375l1.65625 0l0 4.84375q1.0625 -1.328125 2.703125 -1.328125q0.90625 0 1.71875 0.375q0.8125 0.359375 1.328125 1.03125q0.53125 0.65625 0.828125 1.59375q0.296875 0.9375 0.296875 2.0q0 2.53125 -1.25 3.921875q-1.25 1.375 -3.0 1.375q-1.75 0 -2.734375 -1.453125l0 1.234375zm-0.015625 -5.0q0 1.765625 0.46875 2.5625q0.796875 1.28125 2.140625 1.28125q1.09375 0 1.890625 -0.9375q0.796875 -0.953125 0.796875 -2.84375q0 -1.921875 -0.765625 -2.84375q-0.765625 -0.921875 -1.84375 -0.921875q-1.09375 0 -1.890625 0.953125q-0.796875 0.953125 -0.796875 2.75zm8.766357 8.796875l-0.171875 -1.5625q0.546875 0.140625 0.953125 0.140625q0.546875 0 0.875 -0.1875q0.34375 -0.1875 0.5625 -0.515625q0.15625 -0.25 0.5 -1.25q0.046875 -0.140625 0.15625 -0.40625l-3.734375 -9.875l1.796875 0l2.046875 5.71875q0.40625 1.078125 0.71875 2.28125q0.28125 -1.15625 0.6875 -2.25l2.09375 -5.75l1.671875 0l-3.75 10.03125q-0.59375 1.625 -0.9375 2.234375q-0.4375 0.828125 -1.015625 1.203125q-0.578125 0.390625 -1.375 0.390625q-0.484375 0 -1.078125 -0.203125zm23.730103 -17.390625l1.796875 0l0 7.84375q0 2.0625 -0.46875 3.265625q-0.453125 1.203125 -1.671875 1.96875q-1.203125 0.75 -3.171875 0.75q-1.90625 0 -3.125 -0.65625q-1.21875 -0.65625 -1.734375 -1.90625q-0.515625 -1.25 -0.515625 -3.421875l0 -7.84375l1.796875 0l0 7.84375q0 1.765625 0.328125 2.609375q0.328125 0.84375 1.125 1.296875q0.8125 0.453125 1.96875 0.453125q1.984375 0 2.828125 -0.890625q0.84375 -0.90625 0.84375 -3.46875l0 -7.84375zm10.160522 13.59375l-1.671875 0l0 -10.640625q-0.59375 0.578125 -1.578125 1.15625q-0.984375 0.5625 -1.765625 0.859375l0 -1.625q1.40625 -0.65625 2.453125 -1.59375q1.046875 -0.9375 1.484375 -1.8125l1.078125 0l0 13.65625zm5.6413574 4.0l-1.1875 0q2.765625 -4.453125 2.765625 -8.921875q0 -1.734375 -0.390625 -3.453125q-0.328125 -1.390625 -0.890625 -2.671875q-0.359375 -0.84375 -1.484375 -2.78125l1.1875 0q1.75 2.328125 2.578125 4.671875q0.71875 2.015625 0.71875 4.234375q0 2.5 -0.96875 4.84375q-0.953125 2.328125 -2.328125 4.078125z" fill-rule="nonzero"></path><path fill="#000000" d="m902.08356 173.05591l1.796875 0l0 7.84375q0 2.0625 -0.46875 3.265625q-0.453125 1.203125 -1.671875 1.96875q-1.203125 0.75 -3.171875 0.75q-1.90625 0 -3.125 -0.65625q-1.21875 -0.65625 -1.734375 -1.90625q-0.515625 -1.25 -0.515625 -3.421875l0 -7.84375l1.796875 0l0 7.84375q0 1.765625 0.328125 2.609375q0.328125 0.84375 1.125 1.296875q0.8125 0.453125 1.96875 0.453125q1.984375 0 2.828125 -0.890625q0.84375 -0.90625 0.84375 -3.46875l0 -7.84375zm12.644775 11.984375l0 1.609375l-8.984375 0q-0.015625 -0.609375 0.1875 -1.15625q0.34375 -0.921875 1.09375 -1.8125q0.765625 -0.890625 2.1875 -2.0625q2.21875 -1.8125 3.0 -2.875q0.78125 -1.0625 0.78125 -2.015625q0 -0.984375 -0.71875 -1.671875q-0.703125 -0.6875 -1.84375 -0.6875q-1.203125 0 -1.9375 0.734375q-0.71875 0.71875 -0.71875 2.0l-1.71875 -0.171875q0.171875 -1.921875 1.328125 -2.921875q1.15625 -1.015625 3.09375 -1.015625q1.953125 0 3.09375 1.09375q1.140625 1.078125 1.140625 2.6875q0 0.8125 -0.34375 1.609375q-0.328125 0.78125 -1.109375 1.65625q-0.765625 0.859375 -2.5625 2.390625q-1.5 1.265625 -1.9375 1.71875q-0.421875 0.4375 -0.703125 0.890625l6.671875 0zm6.605896 -2.46875l0 -1.6875l5.125 0l0 1.6875l-5.125 0zm12.255371 4.078125l0 -13.59375l5.125 0q1.359375 0 2.078125 0.125q1.0 0.171875 1.671875 0.640625q0.671875 0.46875 1.078125 1.3125q0.421875 0.84375 0.421875 1.84375q0 1.734375 -1.109375 2.9375q-1.09375 1.203125 -3.984375 1.203125l-3.484375 0l0 5.53125l-1.796875 0zm1.796875 -7.140625l3.515625 0q1.75 0 2.46875 -0.640625q0.734375 -0.65625 0.734375 -1.828125q0 -0.859375 -0.4375 -1.46875q-0.421875 -0.609375 -1.125 -0.796875q-0.453125 -0.125 -1.671875 -0.125l-3.484375 0l0 4.859375zm18.740479 5.53125l0 1.609375l-8.984375 0q-0.015625 -0.609375 0.1875 -1.15625q0.34375 -0.921875 1.09375 -1.8125q0.765625 -0.890625 2.1875 -2.0625q2.21875 -1.8125 3.0 -2.875q0.78125 -1.0625 0.78125 -2.015625q0 -0.984375 -0.71875 -1.671875q-0.703125 -0.6875 -1.84375 -0.6875q-1.203125 0 -1.9375 0.734375q-0.71875 0.71875 -0.71875 2.0l-1.71875 -0.171875q0.171875 -1.921875 1.328125 -2.921875q1.15625 -1.015625 3.09375 -1.015625q1.953125 0 3.09375 1.09375q1.140625 1.078125 1.140625 2.6875q0 0.8125 -0.34375 1.609375q-0.328125 0.78125 -1.109375 1.65625q-0.765625 0.859375 -2.5625 2.390625q-1.5 1.265625 -1.9375 1.71875q-0.421875 0.4375 -0.703125 0.890625l6.671875 0zm6.605896 -2.46875l0 -1.6875l5.125 0l0 1.6875l-5.125 0zm13.34906 -7.234375l0 1.9375l-1.796875 0l0 -1.53125q0 -1.25 0.296875 -1.796875q0.390625 -0.75 1.234375 -1.125l0.40625 0.640625q-0.5 0.21875 -0.75 0.640625q-0.234375 0.421875 -0.265625 1.234375l0.875 0zm2.875 0l0 1.9375l-1.796875 0l0 -1.53125q0 -1.25 0.296875 -1.796875q0.390625 -0.75 1.234375 -1.125l0.40625 0.640625q-0.5 0.21875 -0.75 0.640625q-0.234375 0.421875 -0.265625 1.234375l0.875 0zm8.4627075 7.703125l1.640625 0.21875q-0.265625 1.6875 -1.375 2.65625q-1.109375 0.953125 -2.734375 0.953125q-2.015625 0 -3.25 -1.3125q-1.21875 -1.328125 -1.21875 -3.796875q0 -1.59375 0.515625 -2.78125q0.53125 -1.203125 1.609375 -1.796875q1.09375 -0.609375 2.359375 -0.609375q1.609375 0 2.625 0.8125q1.015625 0.8125 1.3125 2.3125l-1.625 0.25q-0.234375 -1.0 -0.828125 -1.5q-0.59375 -0.5 -1.421875 -0.5q-1.265625 0 -2.0625 0.90625q-0.78125 0.90625 -0.78125 2.859375q0 1.984375 0.765625 2.890625q0.765625 0.890625 1.984375 0.890625q0.984375 0 1.640625 -0.59375q0.65625 -0.609375 0.84375 -1.859375zm9.328125 2.390625q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm4.0788574 4.9375l0 -9.859375l1.5 0l0 1.40625q1.09375 -1.625 3.140625 -1.625q0.890625 0 1.640625 0.328125q0.75 0.3125 1.109375 0.84375q0.375 0.515625 0.53125 1.21875q0.09375 0.46875 0.09375 1.625l0 6.0625l-1.671875 0l0 -6.0q0 -1.015625 -0.203125 -1.515625q-0.1875 -0.515625 -0.6875 -0.8125q-0.5 -0.296875 -1.171875 -0.296875q-1.0625 0 -1.84375 0.671875q-0.765625 0.671875 -0.765625 2.578125l0 5.375l-1.671875 0zm8.844421 3.78125l0 -1.21875l11.0625 0l0 1.21875l-11.0625 0zm11.906982 -3.78125l0 -9.859375l1.5 0l0 1.390625q0.453125 -0.71875 1.21875 -1.15625q0.78125 -0.453125 1.765625 -0.453125q1.09375 0 1.796875 0.453125q0.703125 0.453125 0.984375 1.28125q1.171875 -1.734375 3.046875 -1.734375q1.46875 0 2.25 0.8125q0.796875 0.8125 0.796875 2.5l0 6.765625l-1.671875 0l0 -6.203125q0 -1.0 -0.15625 -1.4375q-0.15625 -0.453125 -0.59375 -0.71875q-0.421875 -0.265625 -1.0 -0.265625q-1.03125 0 -1.71875 0.6875q-0.6875 0.6875 -0.6875 2.21875l0 5.71875l-1.671875 0l0 -6.40625q0 -1.109375 -0.40625 -1.65625q-0.40625 -0.5625 -1.34375 -0.5625q-0.703125 0 -1.3125 0.375q-0.59375 0.359375 -0.859375 1.078125q-0.265625 0.71875 -0.265625 2.0625l0 5.109375l-1.671875 0zm21.978271 -1.21875q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm4.0788574 4.9375l0 -9.859375l1.5 0l0 1.40625q1.09375 -1.625 3.140625 -1.625q0.890625 0 1.640625 0.328125q0.75 0.3125 1.109375 0.84375q0.375 0.515625 0.53125 1.21875q0.09375 0.46875 0.09375 1.625l0 6.0625l-1.671875 0l0 -6.0q0 -1.015625 -0.203125 -1.515625q-0.1875 -0.515625 -0.6875 -0.8125q-0.5 -0.296875 -1.171875 -0.296875q-1.0625 0 -1.84375 0.671875q-0.765625 0.671875 -0.765625 2.578125l0 5.375l-1.671875 0zm16.813232 -1.21875q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm3.7819824 5.75l1.609375 0.25q0.109375 0.75 0.578125 1.09375q0.609375 0.453125 1.6875 0.453125q1.171875 0 1.796875 -0.46875q0.625 -0.453125 0.859375 -1.28125q0.125 -0.515625 0.109375 -2.15625q-1.09375 1.296875 -2.71875 1.296875q-2.03125 0 -3.15625 -1.46875q-1.109375 -1.46875 -1.109375 -3.515625q0 -1.40625 0.515625 -2.59375q0.515625 -1.203125 1.484375 -1.84375q0.96875 -0.65625 2.265625 -0.65625q1.75 0 2.875 1.40625l0 -1.1875l1.546875 0l0 8.515625q0 2.3125 -0.46875 3.265625q-0.46875 0.96875 -1.484375 1.515625q-1.015625 0.5625 -2.5 0.5625q-1.765625 0 -2.859375 -0.796875q-1.078125 -0.796875 -1.03125 -2.390625zm1.375 -5.921875q0 1.953125 0.765625 2.84375q0.78125 0.890625 1.9375 0.890625q1.140625 0 1.921875 -0.890625q0.78125 -0.890625 0.78125 -2.78125q0 -1.8125 -0.8125 -2.71875q-0.796875 -0.921875 -1.921875 -0.921875q-1.109375 0 -1.890625 0.90625q-0.78125 0.890625 -0.78125 2.671875zm16.047607 1.9375l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm8.672607 -5.921875l0 -1.9375l1.78125 0l0 1.53125q0 1.234375 -0.28125 1.78125q-0.40625 0.75 -1.25 1.140625l-0.40625 -0.671875q0.5 -0.203125 0.75 -0.640625q0.25 -0.4375 0.265625 -1.203125l-0.859375 0zm2.875 0l0 -1.9375l1.78125 0l0 1.53125q0 1.234375 -0.28125 1.78125q-0.40625 0.75 -1.25 1.140625l-0.40625 -0.671875q0.5 -0.203125 0.75 -0.640625q0.25 -0.4375 0.28125 -1.203125l-0.875 0zm12.145996 15.796875q-1.375 -1.75 -2.328125 -4.078125q-0.953125 -2.34375 -0.953125 -4.84375q0 -2.21875 0.703125 -4.234375q0.84375 -2.34375 2.578125 -4.671875l1.203125 0q-1.125 1.921875 -1.484375 2.75q-0.5625 1.28125 -0.890625 2.671875q-0.40625 1.734375 -0.40625 3.484375q0 4.46875 2.78125 8.921875l-1.203125 0zm2.3532715 -6.9375l1.65625 -0.265625q0.140625 1.0 0.765625 1.53125q0.640625 0.515625 1.78125 0.515625q1.15625 0 1.703125 -0.46875q0.5625 -0.46875 0.5625 -1.09375q0 -0.5625 -0.484375 -0.890625q-0.34375 -0.21875 -1.703125 -0.5625q-1.84375 -0.46875 -2.5625 -0.796875q-0.703125 -0.34375 -1.078125 -0.9375q-0.359375 -0.609375 -0.359375 -1.328125q0 -0.65625 0.296875 -1.21875q0.3125 -0.5625 0.828125 -0.9375q0.390625 -0.28125 1.0625 -0.484375q0.671875 -0.203125 1.4375 -0.203125q1.171875 0 2.046875 0.34375q0.875 0.328125 1.28125 0.90625q0.421875 0.5625 0.578125 1.515625l-1.625 0.21875q-0.109375 -0.75 -0.65625 -1.171875q-0.53125 -0.4375 -1.5 -0.4375q-1.15625 0 -1.640625 0.390625q-0.484375 0.375 -0.484375 0.875q0 0.328125 0.203125 0.59375q0.203125 0.265625 0.640625 0.4375q0.25 0.09375 1.46875 0.4375q1.765625 0.46875 2.46875 0.765625q0.703125 0.296875 1.09375 0.875q0.40625 0.578125 0.40625 1.4375q0 0.828125 -0.484375 1.578125q-0.484375 0.734375 -1.40625 1.140625q-0.921875 0.390625 -2.078125 0.390625q-1.921875 0 -2.9375 -0.796875q-1.0 -0.796875 -1.28125 -2.359375zm10.0 2.9375l0 -13.59375l1.671875 0l0 4.875q1.171875 -1.359375 2.953125 -1.359375q1.09375 0 1.890625 0.4375q0.8125 0.421875 1.15625 1.1875q0.359375 0.765625 0.359375 2.203125l0 6.25l-1.671875 0l0 -6.25q0 -1.25 -0.546875 -1.8125q-0.546875 -0.578125 -1.53125 -0.578125q-0.75 0 -1.40625 0.390625q-0.640625 0.375 -0.921875 1.046875q-0.28125 0.65625 -0.28125 1.8125l0 5.390625l-1.671875 0zm16.813232 -1.21875q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm4.0632324 4.9375l0 -9.859375l1.5 0l0 1.5q0.578125 -1.046875 1.0625 -1.375q0.484375 -0.34375 1.078125 -0.34375q0.84375 0 1.71875 0.546875l-0.578125 1.546875q-0.609375 -0.359375 -1.234375 -0.359375q-0.546875 0 -0.984375 0.328125q-0.421875 0.328125 -0.609375 0.90625q-0.28125 0.890625 -0.28125 1.953125l0 5.15625l-1.671875 0zm12.9782715 -3.171875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm15.500732 5.875l0 -1.25q-0.9375 1.46875 -2.75 1.46875q-1.171875 0 -2.171875 -0.640625q-0.984375 -0.65625 -1.53125 -1.8125q-0.53125 -1.171875 -0.53125 -2.6875q0 -1.46875 0.484375 -2.671875q0.5 -1.203125 1.46875 -1.84375q0.984375 -0.640625 2.203125 -0.640625q0.890625 0 1.578125 0.375q0.703125 0.375 1.140625 0.984375l0 -4.875l1.65625 0l0 13.59375l-1.546875 0zm-5.28125 -4.921875q0 1.890625 0.796875 2.828125q0.8125 0.9375 1.890625 0.9375q1.09375 0 1.859375 -0.890625q0.765625 -0.890625 0.765625 -2.734375q0 -2.015625 -0.78125 -2.953125q-0.78125 -0.953125 -1.921875 -0.953125q-1.109375 0 -1.859375 0.90625q-0.75 0.90625 -0.75 2.859375zm15.99646 4.921875l-1.546875 0l0 -13.59375l1.65625 0l0 4.84375q1.0625 -1.328125 2.703125 -1.328125q0.90625 0 1.71875 0.375q0.8125 0.359375 1.328125 1.03125q0.53125 0.65625 0.828125 1.59375q0.296875 0.9375 0.296875 2.0q0 2.53125 -1.25 3.921875q-1.25 1.375 -3.0 1.375q-1.75 0 -2.734375 -1.453125l0 1.234375zm-0.015625 -5.0q0 1.765625 0.46875 2.5625q0.796875 1.28125 2.140625 1.28125q1.09375 0 1.890625 -0.9375q0.796875 -0.953125 0.796875 -2.84375q0 -1.921875 -0.765625 -2.84375q-0.765625 -0.921875 -1.84375 -0.921875q-1.09375 0 -1.890625 0.953125q-0.796875 0.953125 -0.796875 2.75zm8.766357 8.796875l-0.171875 -1.5625q0.546875 0.140625 0.953125 0.140625q0.546875 0 0.875 -0.1875q0.34375 -0.1875 0.5625 -0.515625q0.15625 -0.25 0.5 -1.25q0.046875 -0.140625 0.15625 -0.40625l-3.734375 -9.875l1.796875 0l2.046875 5.71875q0.40625 1.078125 0.71875 2.28125q0.28125 -1.15625 0.6875 -2.25l2.09375 -5.75l1.671875 0l-3.75 10.03125q-0.59375 1.625 -0.9375 2.234375q-0.4375 0.828125 -1.015625 1.203125q-0.578125 0.390625 -1.375 0.390625q-0.484375 0 -1.078125 -0.203125zm23.730225 -17.390625l1.796875 0l0 7.84375q0 2.0625 -0.46875 3.265625q-0.453125 1.203125 -1.671875 1.96875q-1.203125 0.75 -3.171875 0.75q-1.90625 0 -3.125 -0.65625q-1.21875 -0.65625 -1.734375 -1.90625q-0.515625 -1.25 -0.515625 -3.421875l0 -7.84375l1.796875 0l0 7.84375q0 1.765625 0.328125 2.609375q0.328125 0.84375 1.125 1.296875q0.8125 0.453125 1.96875 0.453125q1.984375 0 2.828125 -0.890625q0.84375 -0.90625 0.84375 -3.46875l0 -7.84375zm10.1604 13.59375l-1.671875 0l0 -10.640625q-0.59375 0.578125 -1.578125 1.15625q-0.984375 0.5625 -1.765625 0.859375l0 -1.625q1.40625 -0.65625 2.453125 -1.59375q1.046875 -0.9375 1.484375 -1.8125l1.078125 0l0 13.65625zm9.090332 -4.078125l0 -1.6875l5.125 0l0 1.6875l-5.125 0zm11.739746 4.890625l1.609375 0.25q0.109375 0.75 0.578125 1.09375q0.609375 0.453125 1.6875 0.453125q1.171875 0 1.796875 -0.46875q0.625 -0.453125 0.859375 -1.28125q0.125 -0.515625 0.109375 -2.15625q-1.09375 1.296875 -2.71875 1.296875q-2.03125 0 -3.15625 -1.46875q-1.109375 -1.46875 -1.109375 -3.515625q0 -1.40625 0.515625 -2.59375q0.515625 -1.203125 1.484375 -1.84375q0.96875 -0.65625 2.265625 -0.65625q1.75 0 2.875 1.40625l0 -1.1875l1.546875 0l0 8.515625q0 2.3125 -0.46875 3.265625q-0.46875 0.96875 -1.484375 1.515625q-1.015625 0.5625 -2.5 0.5625q-1.765625 0 -2.859375 -0.796875q-1.078125 -0.796875 -1.03125 -2.390625zm1.375 -5.921875q0 1.953125 0.765625 2.84375q0.78125 0.890625 1.9375 0.890625q1.140625 0 1.921875 -0.890625q0.78125 -0.890625 0.78125 -2.78125q0 -1.8125 -0.8125 -2.71875q-0.796875 -0.921875 -1.921875 -0.921875q-1.109375 0 -1.890625 0.90625q-0.78125 0.890625 -0.78125 2.671875zm9.281982 5.109375l0 -9.859375l1.5 0l0 1.5q0.578125 -1.046875 1.0625 -1.375q0.484375 -0.34375 1.078125 -0.34375q0.84375 0 1.71875 0.546875l-0.578125 1.546875q-0.609375 -0.359375 -1.234375 -0.359375q-0.546875 0 -0.984375 0.328125q-0.421875 0.328125 -0.609375 0.90625q-0.28125 0.890625 -0.28125 1.953125l0 5.15625l-1.671875 0zm12.6657715 -1.21875q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm4.0788574 4.9375l0 -9.859375l1.5 0l0 1.40625q1.09375 -1.625 3.140625 -1.625q0.890625 0 1.640625 0.328125q0.75 0.3125 1.109375 0.84375q0.375 0.515625 0.53125 1.21875q0.09375 0.46875 0.09375 1.625l0 6.0625l-1.671875 0l0 -6.0q0 -1.015625 -0.203125 -1.515625q-0.1875 -0.515625 -0.6875 -0.8125q-0.5 -0.296875 -1.171875 -0.296875q-1.0625 0 -1.84375 0.671875q-0.765625 0.671875 -0.765625 2.578125l0 5.375l-1.671875 0zm14.031982 -1.5l0.234375 1.484375q-0.703125 0.140625 -1.265625 0.140625q-0.90625 0 -1.40625 -0.28125q-0.5 -0.296875 -0.703125 -0.75q-0.203125 -0.46875 -0.203125 -1.984375l0 -5.65625l-1.234375 0l0 -1.3125l1.234375 0l0 -2.4375l1.65625 -1.0l0 3.4375l1.6875 0l0 1.3125l-1.6875 0l0 5.75q0 0.71875 0.078125 0.921875q0.09375 0.203125 0.296875 0.328125q0.203125 0.125 0.578125 0.125q0.265625 0 0.734375 -0.078125zm0.85510254 -1.4375l1.65625 -0.265625q0.140625 1.0 0.765625 1.53125q0.640625 0.515625 1.78125 0.515625q1.15625 0 1.703125 -0.46875q0.5625 -0.46875 0.5625 -1.09375q0 -0.5625 -0.484375 -0.890625q-0.34375 -0.21875 -1.703125 -0.5625q-1.84375 -0.46875 -2.5625 -0.796875q-0.703125 -0.34375 -1.078125 -0.9375q-0.359375 -0.609375 -0.359375 -1.328125q0 -0.65625 0.296875 -1.21875q0.3125 -0.5625 0.828125 -0.9375q0.390625 -0.28125 1.0625 -0.484375q0.671875 -0.203125 1.4375 -0.203125q1.171875 0 2.046875 0.34375q0.875 0.328125 1.28125 0.90625q0.421875 0.5625 0.578125 1.515625l-1.625 0.21875q-0.109375 -0.75 -0.65625 -1.171875q-0.53125 -0.4375 -1.5 -0.4375q-1.15625 0 -1.640625 0.390625q-0.484375 0.375 -0.484375 0.875q0 0.328125 0.203125 0.59375q0.203125 0.265625 0.640625 0.4375q0.25 0.09375 1.46875 0.4375q1.765625 0.46875 2.46875 0.765625q0.703125 0.296875 1.09375 0.875q0.40625 0.578125 0.40625 1.4375q0 0.828125 -0.484375 1.578125q-0.484375 0.734375 -1.40625 1.140625q-0.921875 0.390625 -2.078125 0.390625q-1.921875 0 -2.9375 -0.796875q-1.0 -0.796875 -1.28125 -2.359375z" fill-rule="nonzero"></path><path fill="#000000" d="m892.94293 212.43091l0 -13.640625l1.53125 0l0 1.28125q0.53125 -0.75 1.203125 -1.125q0.6875 -0.375 1.640625 -0.375q1.265625 0 2.234375 0.65625q0.96875 0.640625 1.453125 1.828125q0.5 1.1875 0.5 2.59375q0 1.515625 -0.546875 2.734375q-0.546875 1.203125 -1.578125 1.84375q-1.03125 0.640625 -2.171875 0.640625q-0.84375 0 -1.515625 -0.34375q-0.65625 -0.359375 -1.078125 -0.890625l0 4.796875l-1.671875 0zm1.515625 -8.65625q0 1.90625 0.765625 2.8125q0.78125 0.90625 1.875 0.90625q1.109375 0 1.890625 -0.9375q0.796875 -0.9375 0.796875 -2.921875q0 -1.875 -0.78125 -2.8125q-0.765625 -0.9375 -1.84375 -0.9375q-1.0625 0 -1.890625 1.0q-0.8125 1.0 -0.8125 2.890625zm15.610046 1.703125l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm9.094482 5.875l0 -9.859375l1.5 0l0 1.5q0.578125 -1.046875 1.0625 -1.375q0.484375 -0.34375 1.078125 -0.34375q0.84375 0 1.71875 0.546875l-0.578125 1.546875q-0.609375 -0.359375 -1.234375 -0.359375q-0.546875 0 -0.984375 0.328125q-0.421875 0.328125 -0.609375 0.90625q-0.28125 0.890625 -0.28125 1.953125l0 5.15625l-1.671875 0zm6.2283325 0l0 -9.859375l1.5 0l0 1.390625q0.453125 -0.71875 1.21875 -1.15625q0.78125 -0.453125 1.765625 -0.453125q1.09375 0 1.796875 0.453125q0.703125 0.453125 0.984375 1.28125q1.171875 -1.734375 3.046875 -1.734375q1.46875 0 2.25 0.8125q0.796875 0.8125 0.796875 2.5l0 6.765625l-1.671875 0l0 -6.203125q0 -1.0 -0.15625 -1.4375q-0.15625 -0.453125 -0.59375 -0.71875q-0.421875 -0.265625 -1.0 -0.265625q-1.03125 0 -1.71875 0.6875q-0.6875 0.6875 -0.6875 2.21875l0 5.71875l-1.671875 0l0 -6.40625q0 -1.109375 -0.40625 -1.65625q-0.40625 -0.5625 -1.34375 -0.5625q-0.703125 0 -1.3125 0.375q-0.59375 0.359375 -0.859375 1.078125q-0.265625 0.71875 -0.265625 2.0625l0 5.109375l-1.671875 0zm15.5563965 -11.6875l0 -1.90625l1.671875 0l0 1.90625l-1.671875 0zm0 11.6875l0 -9.859375l1.671875 0l0 9.859375l-1.671875 0zm3.4573364 -2.9375l1.65625 -0.265625q0.140625 1.0 0.765625 1.53125q0.640625 0.515625 1.78125 0.515625q1.15625 0 1.703125 -0.46875q0.5625 -0.46875 0.5625 -1.09375q0 -0.5625 -0.484375 -0.890625q-0.34375 -0.21875 -1.703125 -0.5625q-1.84375 -0.46875 -2.5625 -0.796875q-0.703125 -0.34375 -1.078125 -0.9375q-0.359375 -0.609375 -0.359375 -1.328125q0 -0.65625 0.296875 -1.21875q0.3125 -0.5625 0.828125 -0.9375q0.390625 -0.28125 1.0625 -0.484375q0.671875 -0.203125 1.4375 -0.203125q1.171875 0 2.046875 0.34375q0.875 0.328125 1.28125 0.90625q0.421875 0.5625 0.578125 1.515625l-1.625 0.21875q-0.109375 -0.75 -0.65625 -1.171875q-0.53125 -0.4375 -1.5 -0.4375q-1.15625 0 -1.640625 0.390625q-0.484375 0.375 -0.484375 0.875q0 0.328125 0.203125 0.59375q0.203125 0.265625 0.640625 0.4375q0.25 0.09375 1.46875 0.4375q1.765625 0.46875 2.46875 0.765625q0.703125 0.296875 1.09375 0.875q0.40625 0.578125 0.40625 1.4375q0 0.828125 -0.484375 1.578125q-0.484375 0.734375 -1.40625 1.140625q-0.921875 0.390625 -2.078125 0.390625q-1.921875 0 -2.9375 -0.796875q-1.0 -0.796875 -1.28125 -2.359375zm9.328125 0l1.65625 -0.265625q0.140625 1.0 0.765625 1.53125q0.640625 0.515625 1.78125 0.515625q1.15625 0 1.703125 -0.46875q0.5625 -0.46875 0.5625 -1.09375q0 -0.5625 -0.484375 -0.890625q-0.34375 -0.21875 -1.703125 -0.5625q-1.84375 -0.46875 -2.5625 -0.796875q-0.703125 -0.34375 -1.078125 -0.9375q-0.359375 -0.609375 -0.359375 -1.328125q0 -0.65625 0.296875 -1.21875q0.3125 -0.5625 0.828125 -0.9375q0.390625 -0.28125 1.0625 -0.484375q0.671875 -0.203125 1.4375 -0.203125q1.171875 0 2.046875 0.34375q0.875 0.328125 1.28125 0.90625q0.421875 0.5625 0.578125 1.515625l-1.625 0.21875q-0.109375 -0.75 -0.65625 -1.171875q-0.53125 -0.4375 -1.5 -0.4375q-1.15625 0 -1.640625 0.390625q-0.484375 0.375 -0.484375 0.875q0 0.328125 0.203125 0.59375q0.203125 0.265625 0.640625 0.4375q0.25 0.09375 1.46875 0.4375q1.765625 0.46875 2.46875 0.765625q0.703125 0.296875 1.09375 0.875q0.40625 0.578125 0.40625 1.4375q0 0.828125 -0.484375 1.578125q-0.484375 0.734375 -1.40625 1.140625q-0.921875 0.390625 -2.078125 0.390625q-1.921875 0 -2.9375 -0.796875q-1.0 -0.796875 -1.28125 -2.359375zm10.015625 -8.75l0 -1.90625l1.671875 0l0 1.90625l-1.671875 0zm0 11.6875l0 -9.859375l1.671875 0l0 9.859375l-1.671875 0zm3.5042114 -4.921875q0 -2.734375 1.53125 -4.0625q1.265625 -1.09375 3.09375 -1.09375q2.03125 0 3.3125 1.34375q1.296875 1.328125 1.296875 3.671875q0 1.90625 -0.578125 3.0q-0.5625 1.078125 -1.65625 1.6875q-1.078125 0.59375 -2.375 0.59375q-2.0625 0 -3.34375 -1.328125q-1.28125 -1.328125 -1.28125 -3.8125zm1.71875 0q0 1.890625 0.828125 2.828125q0.828125 0.9375 2.078125 0.9375q1.25 0 2.0625 -0.9375q0.828125 -0.953125 0.828125 -2.890625q0 -1.828125 -0.828125 -2.765625q-0.828125 -0.9375 -2.0625 -0.9375q-1.25 0 -2.078125 0.9375q-0.828125 0.9375 -0.828125 2.828125zm9.281921 4.921875l0 -9.859375l1.5 0l0 1.40625q1.09375 -1.625 3.140625 -1.625q0.890625 0 1.640625 0.328125q0.75 0.3125 1.109375 0.84375q0.375 0.515625 0.53125 1.21875q0.09375 0.46875 0.09375 1.625l0 6.0625l-1.671875 0l0 -6.0q0 -1.015625 -0.203125 -1.515625q-0.1875 -0.515625 -0.6875 -0.8125q-0.5 -0.296875 -1.171875 -0.296875q-1.0625 0 -1.84375 0.671875q-0.765625 0.671875 -0.765625 2.578125l0 5.375l-1.671875 0zm19.215271 -1.5l0.234375 1.484375q-0.703125 0.140625 -1.265625 0.140625q-0.90625 0 -1.40625 -0.28125q-0.5 -0.296875 -0.703125 -0.75q-0.203125 -0.46875 -0.203125 -1.984375l0 -5.65625l-1.234375 0l0 -1.3125l1.234375 0l0 -2.4375l1.65625 -1.0l0 3.4375l1.6875 0l0 1.3125l-1.6875 0l0 5.75q0 0.71875 0.078125 0.921875q0.09375 0.203125 0.296875 0.328125q0.203125 0.125 0.578125 0.125q0.265625 0 0.734375 -0.078125zm0.9020996 -3.421875q0 -2.734375 1.53125 -4.0625q1.265625 -1.09375 3.09375 -1.09375q2.03125 0 3.3125 1.34375q1.296875 1.328125 1.296875 3.671875q0 1.90625 -0.578125 3.0q-0.5625 1.078125 -1.65625 1.6875q-1.078125 0.59375 -2.375 0.59375q-2.0625 0 -3.34375 -1.328125q-1.28125 -1.328125 -1.28125 -3.8125zm1.71875 0q0 1.890625 0.828125 2.828125q0.828125 0.9375 2.078125 0.9375q1.25 0 2.0625 -0.9375q0.828125 -0.953125 0.828125 -2.890625q0 -1.828125 -0.828125 -2.765625q-0.828125 -0.9375 -2.0625 -0.9375q-1.25 0 -2.078125 0.9375q-0.828125 0.9375 -0.828125 2.828125zm13.793396 1.984375l1.65625 -0.265625q0.140625 1.0 0.765625 1.53125q0.640625 0.515625 1.78125 0.515625q1.15625 0 1.703125 -0.46875q0.5625 -0.46875 0.5625 -1.09375q0 -0.5625 -0.484375 -0.890625q-0.34375 -0.21875 -1.703125 -0.5625q-1.84375 -0.46875 -2.5625 -0.796875q-0.703125 -0.34375 -1.078125 -0.9375q-0.359375 -0.609375 -0.359375 -1.328125q0 -0.65625 0.296875 -1.21875q0.3125 -0.5625 0.828125 -0.9375q0.390625 -0.28125 1.0625 -0.484375q0.671875 -0.203125 1.4375 -0.203125q1.171875 0 2.046875 0.34375q0.875 0.328125 1.28125 0.90625q0.421875 0.5625 0.578125 1.515625l-1.625 0.21875q-0.109375 -0.75 -0.65625 -1.171875q-0.53125 -0.4375 -1.5 -0.4375q-1.15625 0 -1.640625 0.390625q-0.484375 0.375 -0.484375 0.875q0 0.328125 0.203125 0.59375q0.203125 0.265625 0.640625 0.4375q0.25 0.09375 1.46875 0.4375q1.765625 0.46875 2.46875 0.765625q0.703125 0.296875 1.09375 0.875q0.40625 0.578125 0.40625 1.4375q0 0.828125 -0.484375 1.578125q-0.484375 0.734375 -1.40625 1.140625q-0.921875 0.390625 -2.078125 0.390625q-1.921875 0 -2.9375 -0.796875q-1.0 -0.796875 -1.28125 -2.359375zm10.0 2.9375l0 -13.59375l1.671875 0l0 4.875q1.171875 -1.359375 2.953125 -1.359375q1.09375 0 1.890625 0.4375q0.8125 0.421875 1.15625 1.1875q0.359375 0.765625 0.359375 2.203125l0 6.25l-1.671875 0l0 -6.25q0 -1.25 -0.546875 -1.8125q-0.546875 -0.578125 -1.53125 -0.578125q-0.75 0 -1.40625 0.390625q-0.640625 0.375 -0.921875 1.046875q-0.28125 0.65625 -0.28125 1.8125l0 5.390625l-1.671875 0zm16.813232 -1.21875q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm4.0632324 4.9375l0 -9.859375l1.5 0l0 1.5q0.578125 -1.046875 1.0625 -1.375q0.484375 -0.34375 1.078125 -0.34375q0.84375 0 1.71875 0.546875l-0.578125 1.546875q-0.609375 -0.359375 -1.234375 -0.359375q-0.546875 0 -0.984375 0.328125q-0.421875 0.328125 -0.609375 0.90625q-0.28125 0.890625 -0.28125 1.953125l0 5.15625l-1.671875 0zm12.9782715 -3.171875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm13.668335 0.953125q0 -2.734375 1.53125 -4.0625q1.265625 -1.09375 3.09375 -1.09375q2.03125 0 3.3125 1.34375q1.296875 1.328125 1.296875 3.671875q0 1.90625 -0.578125 3.0q-0.5625 1.078125 -1.65625 1.6875q-1.078125 0.59375 -2.375 0.59375q-2.0625 0 -3.34375 -1.328125q-1.28125 -1.328125 -1.28125 -3.8125zm1.71875 0q0 1.890625 0.828125 2.828125q0.828125 0.9375 2.078125 0.9375q1.25 0 2.0625 -0.9375q0.828125 -0.953125 0.828125 -2.890625q0 -1.828125 -0.828125 -2.765625q-0.828125 -0.9375 -2.0625 -0.9375q-1.25 0 -2.078125 0.9375q-0.828125 0.9375 -0.828125 2.828125zm12.938232 3.421875l0.234375 1.484375q-0.703125 0.140625 -1.265625 0.140625q-0.90625 0 -1.40625 -0.28125q-0.5 -0.296875 -0.703125 -0.75q-0.203125 -0.46875 -0.203125 -1.984375l0 -5.65625l-1.234375 0l0 -1.3125l1.234375 0l0 -2.4375l1.65625 -1.0l0 3.4375l1.6875 0l0 1.3125l-1.6875 0l0 5.75q0 0.71875 0.078125 0.921875q0.09375 0.203125 0.296875 0.328125q0.203125 0.125 0.578125 0.125q0.265625 0 0.734375 -0.078125zm1.5270996 1.5l0 -13.59375l1.671875 0l0 4.875q1.171875 -1.359375 2.953125 -1.359375q1.09375 0 1.890625 0.4375q0.8125 0.421875 1.15625 1.1875q0.359375 0.765625 0.359375 2.203125l0 6.25l-1.671875 0l0 -6.25q0 -1.25 -0.546875 -1.8125q-0.546875 -0.578125 -1.53125 -0.578125q-0.75 0 -1.40625 0.390625q-0.640625 0.375 -0.921875 1.046875q-0.28125 0.65625 -0.28125 1.8125l0 5.390625l-1.671875 0zm17.125732 -3.171875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm9.094482 5.875l0 -9.859375l1.5 0l0 1.5q0.578125 -1.046875 1.0625 -1.375q0.484375 -0.34375 1.078125 -0.34375q0.84375 0 1.71875 0.546875l-0.578125 1.546875q-0.609375 -0.359375 -1.234375 -0.359375q-0.546875 0 -0.984375 0.328125q-0.421875 0.328125 -0.609375 0.90625q-0.28125 0.890625 -0.28125 1.953125l0 5.15625l-1.671875 0zm5.5563965 -2.9375l1.65625 -0.265625q0.140625 1.0 0.765625 1.53125q0.640625 0.515625 1.78125 0.515625q1.15625 0 1.703125 -0.46875q0.5625 -0.46875 0.5625 -1.09375q0 -0.5625 -0.484375 -0.890625q-0.34375 -0.21875 -1.703125 -0.5625q-1.84375 -0.46875 -2.5625 -0.796875q-0.703125 -0.34375 -1.078125 -0.9375q-0.359375 -0.609375 -0.359375 -1.328125q0 -0.65625 0.296875 -1.21875q0.3125 -0.5625 0.828125 -0.9375q0.390625 -0.28125 1.0625 -0.484375q0.671875 -0.203125 1.4375 -0.203125q1.171875 0 2.046875 0.34375q0.875 0.328125 1.28125 0.90625q0.421875 0.5625 0.578125 1.515625l-1.625 0.21875q-0.109375 -0.75 -0.65625 -1.171875q-0.53125 -0.4375 -1.5 -0.4375q-1.15625 0 -1.640625 0.390625q-0.484375 0.375 -0.484375 0.875q0 0.328125 0.203125 0.59375q0.203125 0.265625 0.640625 0.4375q0.25 0.09375 1.46875 0.4375q1.765625 0.46875 2.46875 0.765625q0.703125 0.296875 1.09375 0.875q0.40625 0.578125 0.40625 1.4375q0 0.828125 -0.484375 1.578125q-0.484375 0.734375 -1.40625 1.140625q-0.921875 0.390625 -2.078125 0.390625q-1.921875 0 -2.9375 -0.796875q-1.0 -0.796875 -1.28125 -2.359375zm14.511353 0l1.65625 -0.265625q0.140625 1.0 0.765625 1.53125q0.640625 0.515625 1.78125 0.515625q1.15625 0 1.703125 -0.46875q0.5625 -0.46875 0.5625 -1.09375q0 -0.5625 -0.484375 -0.890625q-0.34375 -0.21875 -1.703125 -0.5625q-1.84375 -0.46875 -2.5625 -0.796875q-0.703125 -0.34375 -1.078125 -0.9375q-0.359375 -0.609375 -0.359375 -1.328125q0 -0.65625 0.296875 -1.21875q0.3125 -0.5625 0.828125 -0.9375q0.390625 -0.28125 1.0625 -0.484375q0.671875 -0.203125 1.4375 -0.203125q1.171875 0 2.046875 0.34375q0.875 0.328125 1.28125 0.90625q0.421875 0.5625 0.578125 1.515625l-1.625 0.21875q-0.109375 -0.75 -0.65625 -1.171875q-0.53125 -0.4375 -1.5 -0.4375q-1.15625 0 -1.640625 0.390625q-0.484375 0.375 -0.484375 0.875q0 0.328125 0.203125 0.59375q0.203125 0.265625 0.640625 0.4375q0.25 0.09375 1.46875 0.4375q1.765625 0.46875 2.46875 0.765625q0.703125 0.296875 1.09375 0.875q0.40625 0.578125 0.40625 1.4375q0 0.828125 -0.484375 1.578125q-0.484375 0.734375 -1.40625 1.140625q-0.921875 0.390625 -2.078125 0.390625q-1.921875 0 -2.9375 -0.796875q-1.0 -0.796875 -1.28125 -2.359375zm16.453125 2.9375l0 -1.453125q-1.140625 1.671875 -3.125 1.671875q-0.859375 0 -1.625 -0.328125q-0.75 -0.34375 -1.125 -0.84375q-0.359375 -0.5 -0.515625 -1.234375q-0.09375 -0.5 -0.09375 -1.5625l0 -6.109375l1.671875 0l0 5.46875q0 1.3125 0.09375 1.765625q0.15625 0.65625 0.671875 1.03125q0.515625 0.375 1.265625 0.375q0.75 0 1.40625 -0.375q0.65625 -0.390625 0.921875 -1.046875q0.28125 -0.671875 0.28125 -1.9375l0 -5.28125l1.671875 0l0 9.859375l-1.5 0zm10.360107 -3.609375l1.640625 0.21875q-0.265625 1.6875 -1.375 2.65625q-1.109375 0.953125 -2.734375 0.953125q-2.015625 0 -3.25 -1.3125q-1.21875 -1.328125 -1.21875 -3.796875q0 -1.59375 0.515625 -2.78125q0.53125 -1.203125 1.609375 -1.796875q1.09375 -0.609375 2.359375 -0.609375q1.609375 0 2.625 0.8125q1.015625 0.8125 1.3125 2.3125l-1.625 0.25q-0.234375 -1.0 -0.828125 -1.5q-0.59375 -0.5 -1.421875 -0.5q-1.265625 0 -2.0625 0.90625q-0.78125 0.90625 -0.78125 2.859375q0 1.984375 0.765625 2.890625q0.765625 0.890625 1.984375 0.890625q0.984375 0 1.640625 -0.59375q0.65625 -0.609375 0.84375 -1.859375zm2.890625 3.609375l0 -13.59375l1.671875 0l0 4.875q1.171875 -1.359375 2.953125 -1.359375q1.09375 0 1.890625 0.4375q0.8125 0.421875 1.15625 1.1875q0.359375 0.765625 0.359375 2.203125l0 6.25l-1.671875 0l0 -6.25q0 -1.25 -0.546875 -1.8125q-0.546875 -0.578125 -1.53125 -0.578125q-0.75 0 -1.40625 0.390625q-0.640625 0.375 -0.921875 1.046875q-0.28125 0.65625 -0.28125 1.8125l0 5.390625l-1.671875 0zm21.996582 -1.21875q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm3.4069824 2.0l1.65625 -0.265625q0.140625 1.0 0.765625 1.53125q0.640625 0.515625 1.78125 0.515625q1.15625 0 1.703125 -0.46875q0.5625 -0.46875 0.5625 -1.09375q0 -0.5625 -0.484375 -0.890625q-0.34375 -0.21875 -1.703125 -0.5625q-1.84375 -0.46875 -2.5625 -0.796875q-0.703125 -0.34375 -1.078125 -0.9375q-0.359375 -0.609375 -0.359375 -1.328125q0 -0.65625 0.296875 -1.21875q0.3125 -0.5625 0.828125 -0.9375q0.390625 -0.28125 1.0625 -0.484375q0.671875 -0.203125 1.4375 -0.203125q1.171875 0 2.046875 0.34375q0.875 0.328125 1.28125 0.90625q0.421875 0.5625 0.578125 1.515625l-1.625 0.21875q-0.109375 -0.75 -0.65625 -1.171875q-0.53125 -0.4375 -1.5 -0.4375q-1.15625 0 -1.640625 0.390625q-0.484375 0.375 -0.484375 0.875q0 0.328125 0.203125 0.59375q0.203125 0.265625 0.640625 0.4375q0.25 0.09375 1.46875 0.4375q1.765625 0.46875 2.46875 0.765625q0.703125 0.296875 1.09375 0.875q0.40625 0.578125 0.40625 1.4375q0 0.828125 -0.484375 1.578125q-0.484375 0.734375 -1.40625 1.140625q-0.921875 0.390625 -2.078125 0.390625q-1.921875 0 -2.9375 -0.796875q-1.0 -0.796875 -1.28125 -2.359375zm24.323853 -10.65625l1.796875 0l0 7.84375q0 2.0625 -0.46875 3.265625q-0.453125 1.203125 -1.671875 1.96875q-1.203125 0.75 -3.171875 0.75q-1.90625 0 -3.125 -0.65625q-1.21875 -0.65625 -1.734375 -1.90625q-0.515625 -1.25 -0.515625 -3.421875l0 -7.84375l1.796875 0l0 7.84375q0 1.765625 0.328125 2.609375q0.328125 0.84375 1.125 1.296875q0.8125 0.453125 1.96875 0.453125q1.984375 0 2.828125 -0.890625q0.84375 -0.90625 0.84375 -3.46875l0 -7.84375zm3.8792725 10.0l1.671875 -0.21875q0.28125 1.421875 0.96875 2.046875q0.703125 0.625 1.6875 0.625q1.1875 0 2.0 -0.8125q0.8125 -0.828125 0.8125 -2.03125q0 -1.140625 -0.765625 -1.890625q-0.75 -0.75 -1.90625 -0.75q-0.46875 0 -1.171875 0.1875l0.1875 -1.46875q0.15625 0.015625 0.265625 0.015625q1.0625 0 1.90625 -0.546875q0.859375 -0.5625 0.859375 -1.71875q0 -0.921875 -0.625 -1.515625q-0.609375 -0.609375 -1.59375 -0.609375q-0.96875 0 -1.625 0.609375q-0.640625 0.609375 -0.828125 1.84375l-1.671875 -0.296875q0.296875 -1.6875 1.375 -2.609375q1.09375 -0.921875 2.71875 -0.921875q1.109375 0 2.046875 0.484375q0.9375 0.46875 1.421875 1.296875q0.5 0.828125 0.5 1.75q0 0.890625 -0.46875 1.609375q-0.46875 0.71875 -1.40625 1.15625q1.21875 0.265625 1.875 1.15625q0.671875 0.875 0.671875 2.1875q0 1.78125 -1.296875 3.015625q-1.296875 1.234375 -3.28125 1.234375q-1.796875 0 -2.984375 -1.0625q-1.171875 -1.0625 -1.34375 -2.765625zm11.922607 7.59375l-1.1875 0q2.765625 -4.453125 2.765625 -8.921875q0 -1.734375 -0.390625 -3.453125q-0.328125 -1.390625 -0.890625 -2.671875q-0.359375 -0.84375 -1.484375 -2.78125l1.1875 0q1.75 2.328125 2.578125 4.671875q0.71875 2.015625 0.71875 4.234375q0 2.5 -0.96875 4.84375q-0.953125 2.328125 -2.328125 4.078125z" fill-rule="nonzero"></path><path fill="#000000" d="m902.08356 239.05591l1.796875 0l0 7.84375q0 2.0625 -0.46875 3.265625q-0.453125 1.203125 -1.671875 1.96875q-1.203125 0.75 -3.171875 0.75q-1.90625 0 -3.125 -0.65625q-1.21875 -0.65625 -1.734375 -1.90625q-0.515625 -1.25 -0.515625 -3.421875l0 -7.84375l1.796875 0l0 7.84375q0 1.765625 0.328125 2.609375q0.328125 0.84375 1.125 1.296875q0.8125 0.453125 1.96875 0.453125q1.984375 0 2.828125 -0.890625q0.84375 -0.90625 0.84375 -3.46875l0 -7.84375zm12.644775 11.984375l0 1.609375l-8.984375 0q-0.015625 -0.609375 0.1875 -1.15625q0.34375 -0.921875 1.09375 -1.8125q0.765625 -0.890625 2.1875 -2.0625q2.21875 -1.8125 3.0 -2.875q0.78125 -1.0625 0.78125 -2.015625q0 -0.984375 -0.71875 -1.671875q-0.703125 -0.6875 -1.84375 -0.6875q-1.203125 0 -1.9375 0.734375q-0.71875 0.71875 -0.71875 2.0l-1.71875 -0.171875q0.171875 -1.921875 1.328125 -2.921875q1.15625 -1.015625 3.09375 -1.015625q1.953125 0 3.09375 1.09375q1.140625 1.078125 1.140625 2.6875q0 0.8125 -0.34375 1.609375q-0.328125 0.78125 -1.109375 1.65625q-0.765625 0.859375 -2.5625 2.390625q-1.5 1.265625 -1.9375 1.71875q-0.421875 0.4375 -0.703125 0.890625l6.671875 0zm6.605896 -2.46875l0 -1.6875l5.125 0l0 1.6875l-5.125 0zm12.255371 4.078125l0 -13.59375l5.125 0q1.359375 0 2.078125 0.125q1.0 0.171875 1.671875 0.640625q0.671875 0.46875 1.078125 1.3125q0.421875 0.84375 0.421875 1.84375q0 1.734375 -1.109375 2.9375q-1.09375 1.203125 -3.984375 1.203125l-3.484375 0l0 5.53125l-1.796875 0zm1.796875 -7.140625l3.515625 0q1.75 0 2.46875 -0.640625q0.734375 -0.65625 0.734375 -1.828125q0 -0.859375 -0.4375 -1.46875q-0.421875 -0.609375 -1.125 -0.796875q-0.453125 -0.125 -1.671875 -0.125l-3.484375 0l0 4.859375zm9.9748535 3.546875l1.671875 -0.21875q0.28125 1.421875 0.96875 2.046875q0.703125 0.625 1.6875 0.625q1.1875 0 2.0 -0.8125q0.8125 -0.828125 0.8125 -2.03125q0 -1.140625 -0.765625 -1.890625q-0.75 -0.75 -1.90625 -0.75q-0.46875 0 -1.171875 0.1875l0.1875 -1.46875q0.15625 0.015625 0.265625 0.015625q1.0625 0 1.90625 -0.546875q0.859375 -0.5625 0.859375 -1.71875q0 -0.921875 -0.625 -1.515625q-0.609375 -0.609375 -1.59375 -0.609375q-0.96875 0 -1.625 0.609375q-0.640625 0.609375 -0.828125 1.84375l-1.671875 -0.296875q0.296875 -1.6875 1.375 -2.609375q1.09375 -0.921875 2.71875 -0.921875q1.109375 0 2.046875 0.484375q0.9375 0.46875 1.421875 1.296875q0.5 0.828125 0.5 1.75q0 0.890625 -0.46875 1.609375q-0.46875 0.71875 -1.40625 1.15625q1.21875 0.265625 1.875 1.15625q0.671875 0.875 0.671875 2.1875q0 1.78125 -1.296875 3.015625q-1.296875 1.234375 -3.28125 1.234375q-1.796875 0 -2.984375 -1.0625q-1.171875 -1.0625 -1.34375 -2.765625zm15.371521 -0.484375l0 -1.6875l5.125 0l0 1.6875l-5.125 0zm13.34906 -7.234375l0 1.9375l-1.796875 0l0 -1.53125q0 -1.25 0.296875 -1.796875q0.390625 -0.75 1.234375 -1.125l0.40625 0.640625q-0.5 0.21875 -0.75 0.640625q-0.234375 0.421875 -0.265625 1.234375l0.875 0zm2.875 0l0 1.9375l-1.796875 0l0 -1.53125q0 -1.25 0.296875 -1.796875q0.390625 -0.75 1.234375 -1.125l0.40625 0.640625q-0.5 0.21875 -0.75 0.640625q-0.234375 0.421875 -0.265625 1.234375l0.875 0zm8.4627075 7.703125l1.640625 0.21875q-0.265625 1.6875 -1.375 2.65625q-1.109375 0.953125 -2.734375 0.953125q-2.015625 0 -3.25 -1.3125q-1.21875 -1.328125 -1.21875 -3.796875q0 -1.59375 0.515625 -2.78125q0.53125 -1.203125 1.609375 -1.796875q1.09375 -0.609375 2.359375 -0.609375q1.609375 0 2.625 0.8125q1.015625 0.8125 1.3125 2.3125l-1.625 0.25q-0.234375 -1.0 -0.828125 -1.5q-0.59375 -0.5 -1.421875 -0.5q-1.265625 0 -2.0625 0.90625q-0.78125 0.90625 -0.78125 2.859375q0 1.984375 0.765625 2.890625q0.765625 0.890625 1.984375 0.890625q0.984375 0 1.640625 -0.59375q0.65625 -0.609375 0.84375 -1.859375zm9.328125 2.390625q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm4.0788574 4.9375l0 -9.859375l1.5 0l0 1.40625q1.09375 -1.625 3.140625 -1.625q0.890625 0 1.640625 0.328125q0.75 0.3125 1.109375 0.84375q0.375 0.515625 0.53125 1.21875q0.09375 0.46875 0.09375 1.625l0 6.0625l-1.671875 0l0 -6.0q0 -1.015625 -0.203125 -1.515625q-0.1875 -0.515625 -0.6875 -0.8125q-0.5 -0.296875 -1.171875 -0.296875q-1.0625 0 -1.84375 0.671875q-0.765625 0.671875 -0.765625 2.578125l0 5.375l-1.671875 0zm8.844421 3.78125l0 -1.21875l11.0625 0l0 1.21875l-11.0625 0zm13.735107 -3.78125l-3.015625 -9.859375l1.71875 0l1.5625 5.6875l0.59375 2.125q0.03125 -0.15625 0.5 -2.03125l1.578125 -5.78125l1.71875 0l1.46875 5.71875l0.484375 1.890625l0.578125 -1.90625l1.6875 -5.703125l1.625 0l-3.078125 9.859375l-1.734375 0l-1.578125 -5.90625l-0.375 -1.671875l-2.0 7.578125l-1.734375 0zm11.62915 0l0 -9.859375l1.5 0l0 1.5q0.578125 -1.046875 1.0625 -1.375q0.484375 -0.34375 1.078125 -0.34375q0.84375 0 1.71875 0.546875l-0.578125 1.546875q-0.609375 -0.359375 -1.234375 -0.359375q-0.546875 0 -0.984375 0.328125q-0.421875 0.328125 -0.609375 0.90625q-0.28125 0.890625 -0.28125 1.953125l0 5.15625l-1.671875 0zm6.2440186 -11.6875l0 -1.90625l1.671875 0l0 1.90625l-1.671875 0zm0 11.6875l0 -9.859375l1.671875 0l0 9.859375l-1.671875 0zm7.7854004 -1.5l0.234375 1.484375q-0.703125 0.140625 -1.265625 0.140625q-0.90625 0 -1.40625 -0.28125q-0.5 -0.296875 -0.703125 -0.75q-0.203125 -0.46875 -0.203125 -1.984375l0 -5.65625l-1.234375 0l0 -1.3125l1.234375 0l0 -2.4375l1.65625 -1.0l0 3.4375l1.6875 0l0 1.3125l-1.6875 0l0 5.75q0 0.71875 0.078125 0.921875q0.09375 0.203125 0.296875 0.328125q0.203125 0.125 0.578125 0.125q0.265625 0 0.734375 -0.078125zm8.2771 -1.671875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm8.672607 -5.921875l0 -1.9375l1.78125 0l0 1.53125q0 1.234375 -0.28125 1.78125q-0.40625 0.75 -1.25 1.140625l-0.40625 -0.671875q0.5 -0.203125 0.75 -0.640625q0.25 -0.4375 0.265625 -1.203125l-0.859375 0zm2.875 0l0 -1.9375l1.78125 0l0 1.53125q0 1.234375 -0.28125 1.78125q-0.40625 0.75 -1.25 1.140625l-0.40625 -0.671875q0.5 -0.203125 0.75 -0.640625q0.25 -0.4375 0.28125 -1.203125l-0.875 0zm12.145874 15.796875q-1.375 -1.75 -2.328125 -4.078125q-0.953125 -2.34375 -0.953125 -4.84375q0 -2.21875 0.703125 -4.234375q0.84375 -2.34375 2.578125 -4.671875l1.203125 0q-1.125 1.921875 -1.484375 2.75q-0.5625 1.28125 -0.890625 2.671875q-0.40625 1.734375 -0.40625 3.484375q0 4.46875 2.78125 8.921875l-1.203125 0zm3.0408936 -15.6875l0 -1.90625l1.671875 0l0 1.90625l-1.671875 0zm0 11.6875l0 -9.859375l1.671875 0l0 9.859375l-1.671875 0zm4.1291504 0l0 -9.859375l1.5 0l0 1.40625q1.09375 -1.625 3.140625 -1.625q0.890625 0 1.640625 0.328125q0.75 0.3125 1.109375 0.84375q0.375 0.515625 0.53125 1.21875q0.09375 0.46875 0.09375 1.625l0 6.0625l-1.671875 0l0 -6.0q0 -1.015625 -0.203125 -1.515625q-0.1875 -0.515625 -0.6875 -0.8125q-0.5 -0.296875 -1.171875 -0.296875q-1.0625 0 -1.84375 0.671875q-0.765625 0.671875 -0.765625 2.578125l0 5.375l-1.671875 0zm10.375732 0l0 -13.59375l1.671875 0l0 4.875q1.171875 -1.359375 2.953125 -1.359375q1.09375 0 1.890625 0.4375q0.8125 0.421875 1.15625 1.1875q0.359375 0.765625 0.359375 2.203125l0 6.25l-1.671875 0l0 -6.25q0 -1.25 -0.546875 -1.8125q-0.546875 -0.578125 -1.53125 -0.578125q-0.75 0 -1.40625 0.390625q-0.640625 0.375 -0.921875 1.046875q-0.28125 0.65625 -0.28125 1.8125l0 5.390625l-1.671875 0zm17.125732 -3.171875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm9.094482 5.875l0 -9.859375l1.5 0l0 1.5q0.578125 -1.046875 1.0625 -1.375q0.484375 -0.34375 1.078125 -0.34375q0.84375 0 1.71875 0.546875l-0.578125 1.546875q-0.609375 -0.359375 -1.234375 -0.359375q-0.546875 0 -0.984375 0.328125q-0.421875 0.328125 -0.609375 0.90625q-0.28125 0.890625 -0.28125 1.953125l0 5.15625l-1.671875 0zm6.2438965 -11.6875l0 -1.90625l1.671875 0l0 1.90625l-1.671875 0zm0 11.6875l0 -9.859375l1.671875 0l0 9.859375l-1.671875 0zm7.7854004 -1.5l0.234375 1.484375q-0.703125 0.140625 -1.265625 0.140625q-0.90625 0 -1.40625 -0.28125q-0.5 -0.296875 -0.703125 -0.75q-0.203125 -0.46875 -0.203125 -1.984375l0 -5.65625l-1.234375 0l0 -1.3125l1.234375 0l0 -2.4375l1.65625 -1.0l0 3.4375l1.6875 0l0 1.3125l-1.6875 0l0 5.75q0 0.71875 0.078125 0.921875q0.09375 0.203125 0.296875 0.328125q0.203125 0.125 0.578125 0.125q0.265625 0 0.734375 -0.078125zm8.2771 -1.671875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm15.500732 5.875l0 -1.25q-0.9375 1.46875 -2.75 1.46875q-1.171875 0 -2.171875 -0.640625q-0.984375 -0.65625 -1.53125 -1.8125q-0.53125 -1.171875 -0.53125 -2.6875q0 -1.46875 0.484375 -2.671875q0.5 -1.203125 1.46875 -1.84375q0.984375 -0.640625 2.203125 -0.640625q0.890625 0 1.578125 0.375q0.703125 0.375 1.140625 0.984375l0 -4.875l1.65625 0l0 13.59375l-1.546875 0zm-5.28125 -4.921875q0 1.890625 0.796875 2.828125q0.8125 0.9375 1.890625 0.9375q1.09375 0 1.859375 -0.890625q0.765625 -0.890625 0.765625 -2.734375q0 -2.015625 -0.78125 -2.953125q-0.78125 -0.953125 -1.921875 -0.953125q-1.109375 0 -1.859375 0.90625q-0.75 0.90625 -0.75 2.859375zm14.855835 4.921875l0 -8.546875l-1.484375 0l0 -1.3125l1.484375 0l0 -1.046875q0 -0.984375 0.171875 -1.46875q0.234375 -0.65625 0.84375 -1.046875q0.609375 -0.40625 1.703125 -0.40625q0.703125 0 1.5625 0.15625l-0.25 1.46875q-0.515625 -0.09375 -0.984375 -0.09375q-0.765625 0 -1.078125 0.328125q-0.3125 0.3125 -0.3125 1.203125l0 0.90625l1.921875 0l0 1.3125l-1.921875 0l0 8.546875l-1.65625 0zm4.7614746 0l0 -9.859375l1.5 0l0 1.5q0.578125 -1.046875 1.0625 -1.375q0.484375 -0.34375 1.078125 -0.34375q0.84375 0 1.71875 0.546875l-0.578125 1.546875q-0.609375 -0.359375 -1.234375 -0.359375q-0.546875 0 -0.984375 0.328125q-0.421875 0.328125 -0.609375 0.90625q-0.28125 0.890625 -0.28125 1.953125l0 5.15625l-1.671875 0zm5.6032715 -4.921875q0 -2.734375 1.53125 -4.0625q1.265625 -1.09375 3.09375 -1.09375q2.03125 0 3.3125 1.34375q1.296875 1.328125 1.296875 3.671875q0 1.90625 -0.578125 3.0q-0.5625 1.078125 -1.65625 1.6875q-1.078125 0.59375 -2.375 0.59375q-2.0625 0 -3.34375 -1.328125q-1.28125 -1.328125 -1.28125 -3.8125zm1.71875 0q0 1.890625 0.828125 2.828125q0.828125 0.9375 2.078125 0.9375q1.25 0 2.0625 -0.9375q0.828125 -0.953125 0.828125 -2.890625q0 -1.828125 -0.828125 -2.765625q-0.828125 -0.9375 -2.0625 -0.9375q-1.25 0 -2.078125 0.9375q-0.828125 0.9375 -0.828125 2.828125zm9.281982 4.921875l0 -9.859375l1.5 0l0 1.390625q0.453125 -0.71875 1.21875 -1.15625q0.78125 -0.453125 1.765625 -0.453125q1.09375 0 1.796875 0.453125q0.703125 0.453125 0.984375 1.28125q1.171875 -1.734375 3.046875 -1.734375q1.46875 0 2.25 0.8125q0.796875 0.8125 0.796875 2.5l0 6.765625l-1.671875 0l0 -6.203125q0 -1.0 -0.15625 -1.4375q-0.15625 -0.453125 -0.59375 -0.71875q-0.421875 -0.265625 -1.0 -0.265625q-1.03125 0 -1.71875 0.6875q-0.6875 0.6875 -0.6875 2.21875l0 5.71875l-1.671875 0l0 -6.40625q0 -1.109375 -0.40625 -1.65625q-0.40625 -0.5625 -1.34375 -0.5625q-0.703125 0 -1.3125 0.375q-0.59375 0.359375 -0.859375 1.078125q-0.265625 0.71875 -0.265625 2.0625l0 5.109375l-1.671875 0zm20.942871 0l0 -13.59375l5.125 0q1.359375 0 2.078125 0.125q1.0 0.171875 1.671875 0.640625q0.671875 0.46875 1.078125 1.3125q0.421875 0.84375 0.421875 1.84375q0 1.734375 -1.109375 2.9375q-1.09375 1.203125 -3.984375 1.203125l-3.484375 0l0 5.53125l-1.796875 0zm1.796875 -7.140625l3.515625 0q1.75 0 2.46875 -0.640625q0.734375 -0.65625 0.734375 -1.828125q0 -0.859375 -0.4375 -1.46875q-0.421875 -0.609375 -1.125 -0.796875q-0.453125 -0.125 -1.671875 -0.125l-3.484375 0l0 4.859375zm16.256104 7.140625l-1.671875 0l0 -10.640625q-0.59375 0.578125 -1.578125 1.15625q-0.984375 0.5625 -1.765625 0.859375l0 -1.625q1.40625 -0.65625 2.453125 -1.59375q1.046875 -0.9375 1.484375 -1.8125l1.078125 0l0 13.65625zm5.6412354 4.0l-1.1875 0q2.765625 -4.453125 2.765625 -8.921875q0 -1.734375 -0.390625 -3.453125q-0.328125 -1.390625 -0.890625 -2.671875q-0.359375 -0.84375 -1.484375 -2.78125l1.1875 0q1.75 2.328125 2.578125 4.671875q0.71875 2.015625 0.71875 4.234375q0 2.5 -0.96875 4.84375q-0.953125 2.328125 -2.328125 4.078125z" fill-rule="nonzero"></path><path fill="#000000" d="m902.08356 283.05588l1.796875 0l0 7.84375q0 2.0625 -0.46875 3.265625q-0.453125 1.203125 -1.671875 1.96875q-1.203125 0.75 -3.171875 0.75q-1.90625 0 -3.125 -0.65625q-1.21875 -0.65625 -1.734375 -1.90625q-0.515625 -1.25 -0.515625 -3.421875l0 -7.84375l1.796875 0l0 7.84375q0 1.765625 0.328125 2.609375q0.328125 0.84375 1.125 1.296875q0.8125 0.453125 1.96875 0.453125q1.984375 0 2.828125 -0.890625q0.84375 -0.90625 0.84375 -3.46875l0 -7.84375zm12.644775 11.984375l0 1.609375l-8.984375 0q-0.015625 -0.609375 0.1875 -1.15625q0.34375 -0.921875 1.09375 -1.8125q0.765625 -0.890625 2.1875 -2.0625q2.21875 -1.8125 3.0 -2.875q0.78125 -1.0625 0.78125 -2.015625q0 -0.984375 -0.71875 -1.671875q-0.703125 -0.6875 -1.84375 -0.6875q-1.203125 0 -1.9375 0.734375q-0.71875 0.71875 -0.71875 2.0l-1.71875 -0.171875q0.171875 -1.921875 1.328125 -2.921875q1.15625 -1.015625 3.09375 -1.015625q1.953125 0 3.09375 1.09375q1.140625 1.078125 1.140625 2.6875q0 0.8125 -0.34375 1.609375q-0.328125 0.78125 -1.109375 1.65625q-0.765625 0.859375 -2.5625 2.390625q-1.5 1.265625 -1.9375 1.71875q-0.421875 0.4375 -0.703125 0.890625l6.671875 0zm6.605896 -2.46875l0 -1.6875l5.125 0l0 1.6875l-5.125 0zm12.255371 4.078125l0 -13.59375l5.125 0q1.359375 0 2.078125 0.125q1.0 0.171875 1.671875 0.640625q0.671875 0.46875 1.078125 1.3125q0.421875 0.84375 0.421875 1.84375q0 1.734375 -1.109375 2.9375q-1.09375 1.203125 -3.984375 1.203125l-3.484375 0l0 5.53125l-1.796875 0zm1.796875 -7.140625l3.515625 0q1.75 0 2.46875 -0.640625q0.734375 -0.65625 0.734375 -1.828125q0 -0.859375 -0.4375 -1.46875q-0.421875 -0.609375 -1.125 -0.796875q-0.453125 -0.125 -1.671875 -0.125l-3.484375 0l0 4.859375zm15.3186035 7.140625l0 -3.25l-5.90625 0l0 -1.53125l6.21875 -8.8125l1.359375 0l0 8.8125l1.84375 0l0 1.53125l-1.84375 0l0 3.25l-1.671875 0zm0 -4.78125l0 -6.140625l-4.25 6.140625l4.25 0zm10.027771 0.703125l0 -1.6875l5.125 0l0 1.6875l-5.125 0zm12.03656 4.078125l0 -9.859375l1.5 0l0 1.40625q1.09375 -1.625 3.140625 -1.625q0.890625 0 1.640625 0.328125q0.75 0.3125 1.109375 0.84375q0.375 0.515625 0.53125 1.21875q0.09375 0.46875 0.09375 1.625l0 6.0625l-1.671875 0l0 -6.0q0 -1.015625 -0.203125 -1.515625q-0.1875 -0.515625 -0.6875 -0.8125q-0.5 -0.296875 -1.171875 -0.296875q-1.0625 0 -1.84375 0.671875q-0.765625 0.671875 -0.765625 2.578125l0 5.375l-1.671875 0zm9.750732 -4.921875q0 -2.734375 1.53125 -4.0625q1.265625 -1.09375 3.09375 -1.09375q2.03125 0 3.3125 1.34375q1.296875 1.328125 1.296875 3.671875q0 1.90625 -0.578125 3.0q-0.5625 1.078125 -1.65625 1.6875q-1.078125 0.59375 -2.375 0.59375q-2.0625 0 -3.34375 -1.328125q-1.28125 -1.328125 -1.28125 -3.8125zm1.71875 0q0 1.890625 0.828125 2.828125q0.828125 0.9375 2.078125 0.9375q1.25 0 2.0625 -0.9375q0.828125 -0.953125 0.828125 -2.890625q0 -1.828125 -0.828125 -2.765625q-0.828125 -0.9375 -2.0625 -0.9375q-1.25 0 -2.078125 0.9375q-0.828125 0.9375 -0.828125 2.828125zm9.281982 4.921875l0 -9.859375l1.5 0l0 1.40625q1.09375 -1.625 3.140625 -1.625q0.890625 0 1.640625 0.328125q0.75 0.3125 1.109375 0.84375q0.375 0.515625 0.53125 1.21875q0.09375 0.46875 0.09375 1.625l0 6.0625l-1.671875 0l0 -6.0q0 -1.015625 -0.203125 -1.515625q-0.1875 -0.515625 -0.6875 -0.8125q-0.5 -0.296875 -1.171875 -0.296875q-1.0625 0 -1.84375 0.671875q-0.765625 0.671875 -0.765625 2.578125l0 5.375l-1.671875 0zm17.125732 -3.171875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875z" fill-rule="nonzero"></path><path fill="#000000" d="m902.08356 327.05588l1.796875 0l0 7.84375q0 2.0625 -0.46875 3.265625q-0.453125 1.203125 -1.671875 1.96875q-1.203125 0.75 -3.171875 0.75q-1.90625 0 -3.125 -0.65625q-1.21875 -0.65625 -1.734375 -1.90625q-0.515625 -1.25 -0.515625 -3.421875l0 -7.84375l1.796875 0l0 7.84375q0 1.765625 0.328125 2.609375q0.328125 0.84375 1.125 1.296875q0.8125 0.453125 1.96875 0.453125q1.984375 0 2.828125 -0.890625q0.84375 -0.90625 0.84375 -3.46875l0 -7.84375zm3.8791504 10.0l1.671875 -0.21875q0.28125 1.421875 0.96875 2.046875q0.703125 0.625 1.6875 0.625q1.1875 0 2.0 -0.8125q0.8125 -0.828125 0.8125 -2.03125q0 -1.140625 -0.765625 -1.890625q-0.75 -0.75 -1.90625 -0.75q-0.46875 0 -1.171875 0.1875l0.1875 -1.46875q0.15625 0.015625 0.265625 0.015625q1.0625 0 1.90625 -0.546875q0.859375 -0.5625 0.859375 -1.71875q0 -0.921875 -0.625 -1.515625q-0.609375 -0.609375 -1.59375 -0.609375q-0.96875 0 -1.625 0.609375q-0.640625 0.609375 -0.828125 1.84375l-1.671875 -0.296875q0.296875 -1.6875 1.375 -2.609375q1.09375 -0.921875 2.71875 -0.921875q1.109375 0 2.046875 0.484375q0.9375 0.46875 1.421875 1.296875q0.5 0.828125 0.5 1.75q0 0.890625 -0.46875 1.609375q-0.46875 0.71875 -1.40625 1.15625q1.21875 0.265625 1.875 1.15625q0.671875 0.875 0.671875 2.1875q0 1.78125 -1.296875 3.015625q-1.296875 1.234375 -3.28125 1.234375q-1.796875 0 -2.984375 -1.0625q-1.171875 -1.0625 -1.34375 -2.765625zm15.371521 -0.484375l0 -1.6875l5.125 0l0 1.6875l-5.125 0zm12.255371 4.078125l0 -13.59375l5.125 0q1.359375 0 2.078125 0.125q1.0 0.171875 1.671875 0.640625q0.671875 0.46875 1.078125 1.3125q0.421875 0.84375 0.421875 1.84375q0 1.734375 -1.109375 2.9375q-1.09375 1.203125 -3.984375 1.203125l-3.484375 0l0 5.53125l-1.796875 0zm1.796875 -7.140625l3.515625 0q1.75 0 2.46875 -0.640625q0.734375 -0.65625 0.734375 -1.828125q0 -0.859375 -0.4375 -1.46875q-0.421875 -0.609375 -1.125 -0.796875q-0.453125 -0.125 -1.671875 -0.125l-3.484375 0l0 4.859375zm16.256104 7.140625l-1.671875 0l0 -10.640625q-0.59375 0.578125 -1.578125 1.15625q-0.984375 0.5625 -1.765625 0.859375l0 -1.625q1.40625 -0.65625 2.453125 -1.59375q1.046875 -0.9375 1.484375 -1.8125l1.078125 0l0 13.65625zm4.9850464 0l0 -1.90625l1.90625 0l0 1.90625q0 1.046875 -0.375 1.6875q-0.375 0.65625 -1.171875 1.0l-0.46875 -0.71875q0.53125 -0.21875 0.78125 -0.671875q0.25 -0.453125 0.28125 -1.296875l-0.953125 0zm10.147888 0l0 -13.59375l5.125 0q1.359375 0 2.078125 0.125q1.0 0.171875 1.671875 0.640625q0.671875 0.46875 1.078125 1.3125q0.421875 0.84375 0.421875 1.84375q0 1.734375 -1.109375 2.9375q-1.09375 1.203125 -3.984375 1.203125l-3.484375 0l0 5.53125l-1.796875 0zm1.796875 -7.140625l3.515625 0q1.75 0 2.46875 -0.640625q0.734375 -0.65625 0.734375 -1.828125q0 -0.859375 -0.4375 -1.46875q-0.421875 -0.609375 -1.125 -0.796875q-0.453125 -0.125 -1.671875 -0.125l-3.484375 0l0 4.859375zm9.9747925 3.546875l1.671875 -0.21875q0.28125 1.421875 0.96875 2.046875q0.703125 0.625 1.6875 0.625q1.1875 0 2.0 -0.8125q0.8125 -0.828125 0.8125 -2.03125q0 -1.140625 -0.765625 -1.890625q-0.75 -0.75 -1.90625 -0.75q-0.46875 0 -1.171875 0.1875l0.1875 -1.46875q0.15625 0.015625 0.265625 0.015625q1.0625 0 1.90625 -0.546875q0.859375 -0.5625 0.859375 -1.71875q0 -0.921875 -0.625 -1.515625q-0.609375 -0.609375 -1.59375 -0.609375q-0.96875 0 -1.625 0.609375q-0.640625 0.609375 -0.828125 1.84375l-1.671875 -0.296875q0.296875 -1.6875 1.375 -2.609375q1.09375 -0.921875 2.71875 -0.921875q1.109375 0 2.046875 0.484375q0.9375 0.46875 1.421875 1.296875q0.5 0.828125 0.5 1.75q0 0.890625 -0.46875 1.609375q-0.46875 0.71875 -1.40625 1.15625q1.21875 0.265625 1.875 1.15625q0.671875 0.875 0.671875 2.1875q0 1.78125 -1.296875 3.015625q-1.296875 1.234375 -3.28125 1.234375q-1.796875 0 -2.984375 -1.0625q-1.171875 -1.0625 -1.34375 -2.765625zm15.371521 -0.484375l0 -1.6875l5.125 0l0 1.6875l-5.125 0zm12.036621 4.078125l0 -9.859375l1.5 0l0 1.40625q1.09375 -1.625 3.140625 -1.625q0.890625 0 1.640625 0.328125q0.75 0.3125 1.109375 0.84375q0.375 0.515625 0.53125 1.21875q0.09375 0.46875 0.09375 1.625l0 6.0625l-1.671875 0l0 -6.0q0 -1.015625 -0.203125 -1.515625q-0.1875 -0.515625 -0.6875 -0.8125q-0.5 -0.296875 -1.171875 -0.296875q-1.0625 0 -1.84375 0.671875q-0.765625 0.671875 -0.765625 2.578125l0 5.375l-1.671875 0zm9.750732 -4.921875q0 -2.734375 1.53125 -4.0625q1.265625 -1.09375 3.09375 -1.09375q2.03125 0 3.3125 1.34375q1.296875 1.328125 1.296875 3.671875q0 1.90625 -0.578125 3.0q-0.5625 1.078125 -1.65625 1.6875q-1.078125 0.59375 -2.375 0.59375q-2.0625 0 -3.34375 -1.328125q-1.28125 -1.328125 -1.28125 -3.8125zm1.71875 0q0 1.890625 0.828125 2.828125q0.828125 0.9375 2.078125 0.9375q1.25 0 2.0625 -0.9375q0.828125 -0.953125 0.828125 -2.890625q0 -1.828125 -0.828125 -2.765625q-0.828125 -0.9375 -2.0625 -0.9375q-1.25 0 -2.078125 0.9375q-0.828125 0.9375 -0.828125 2.828125zm9.281982 4.921875l0 -9.859375l1.5 0l0 1.40625q1.09375 -1.625 3.140625 -1.625q0.890625 0 1.640625 0.328125q0.75 0.3125 1.109375 0.84375q0.375 0.515625 0.53125 1.21875q0.09375 0.46875 0.09375 1.625l0 6.0625l-1.671875 0l0 -6.0q0 -1.015625 -0.203125 -1.515625q-0.1875 -0.515625 -0.6875 -0.8125q-0.5 -0.296875 -1.171875 -0.296875q-1.0625 0 -1.84375 0.671875q-0.765625 0.671875 -0.765625 2.578125l0 5.375l-1.671875 0zm17.125732 -3.171875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875z" fill-rule="nonzero"></path><path fill="#000000" d="m902.08356 371.05588l1.796875 0l0 7.84375q0 2.0625 -0.46875 3.265625q-0.453125 1.203125 -1.671875 1.96875q-1.203125 0.75 -3.171875 0.75q-1.90625 0 -3.125 -0.65625q-1.21875 -0.65625 -1.734375 -1.90625q-0.515625 -1.25 -0.515625 -3.421875l0 -7.84375l1.796875 0l0 7.84375q0 1.765625 0.328125 2.609375q0.328125 0.84375 1.125 1.296875q0.8125 0.453125 1.96875 0.453125q1.984375 0 2.828125 -0.890625q0.84375 -0.90625 0.84375 -3.46875l0 -7.84375zm3.8791504 10.0l1.671875 -0.21875q0.28125 1.421875 0.96875 2.046875q0.703125 0.625 1.6875 0.625q1.1875 0 2.0 -0.8125q0.8125 -0.828125 0.8125 -2.03125q0 -1.140625 -0.765625 -1.890625q-0.75 -0.75 -1.90625 -0.75q-0.46875 0 -1.171875 0.1875l0.1875 -1.46875q0.15625 0.015625 0.265625 0.015625q1.0625 0 1.90625 -0.546875q0.859375 -0.5625 0.859375 -1.71875q0 -0.921875 -0.625 -1.515625q-0.609375 -0.609375 -1.59375 -0.609375q-0.96875 0 -1.625 0.609375q-0.640625 0.609375 -0.828125 1.84375l-1.671875 -0.296875q0.296875 -1.6875 1.375 -2.609375q1.09375 -0.921875 2.71875 -0.921875q1.109375 0 2.046875 0.484375q0.9375 0.46875 1.421875 1.296875q0.5 0.828125 0.5 1.75q0 0.890625 -0.46875 1.609375q-0.46875 0.71875 -1.40625 1.15625q1.21875 0.265625 1.875 1.15625q0.671875 0.875 0.671875 2.1875q0 1.78125 -1.296875 3.015625q-1.296875 1.234375 -3.28125 1.234375q-1.796875 0 -2.984375 -1.0625q-1.171875 -1.0625 -1.34375 -2.765625zm15.371521 -0.484375l0 -1.6875l5.125 0l0 1.6875l-5.125 0zm12.255371 4.078125l0 -13.59375l5.125 0q1.359375 0 2.078125 0.125q1.0 0.171875 1.671875 0.640625q0.671875 0.46875 1.078125 1.3125q0.421875 0.84375 0.421875 1.84375q0 1.734375 -1.109375 2.9375q-1.09375 1.203125 -3.984375 1.203125l-3.484375 0l0 5.53125l-1.796875 0zm1.796875 -7.140625l3.515625 0q1.75 0 2.46875 -0.640625q0.734375 -0.65625 0.734375 -1.828125q0 -0.859375 -0.4375 -1.46875q-0.421875 -0.609375 -1.125 -0.796875q-0.453125 -0.125 -1.671875 -0.125l-3.484375 0l0 4.859375zm18.740479 5.53125l0 1.609375l-8.984375 0q-0.015625 -0.609375 0.1875 -1.15625q0.34375 -0.921875 1.09375 -1.8125q0.765625 -0.890625 2.1875 -2.0625q2.21875 -1.8125 3.0 -2.875q0.78125 -1.0625 0.78125 -2.015625q0 -0.984375 -0.71875 -1.671875q-0.703125 -0.6875 -1.84375 -0.6875q-1.203125 0 -1.9375 0.734375q-0.71875 0.71875 -0.71875 2.0l-1.71875 -0.171875q0.171875 -1.921875 1.328125 -2.921875q1.15625 -1.015625 3.09375 -1.015625q1.953125 0 3.09375 1.09375q1.140625 1.078125 1.140625 2.6875q0 0.8125 -0.34375 1.609375q-0.328125 0.78125 -1.109375 1.65625q-0.765625 0.859375 -2.5625 2.390625q-1.5 1.265625 -1.9375 1.71875q-0.421875 0.4375 -0.703125 0.890625l6.671875 0zm6.605896 -2.46875l0 -1.6875l5.125 0l0 1.6875l-5.125 0zm13.34906 -7.234375l0 1.9375l-1.796875 0l0 -1.53125q0 -1.25 0.296875 -1.796875q0.390625 -0.75 1.234375 -1.125l0.40625 0.640625q-0.5 0.21875 -0.75 0.640625q-0.234375 0.421875 -0.265625 1.234375l0.875 0zm2.875 0l0 1.9375l-1.796875 0l0 -1.53125q0 -1.25 0.296875 -1.796875q0.390625 -0.75 1.234375 -1.125l0.40625 0.640625q-0.5 0.21875 -0.75 0.640625q-0.234375 0.421875 -0.265625 1.234375l0.875 0zm8.4627075 7.703125l1.640625 0.21875q-0.265625 1.6875 -1.375 2.65625q-1.109375 0.953125 -2.734375 0.953125q-2.015625 0 -3.25 -1.3125q-1.21875 -1.328125 -1.21875 -3.796875q0 -1.59375 0.515625 -2.78125q0.53125 -1.203125 1.609375 -1.796875q1.09375 -0.609375 2.359375 -0.609375q1.609375 0 2.625 0.8125q1.015625 0.8125 1.3125 2.3125l-1.625 0.25q-0.234375 -1.0 -0.828125 -1.5q-0.59375 -0.5 -1.421875 -0.5q-1.265625 0 -2.0625 0.90625q-0.78125 0.90625 -0.78125 2.859375q0 1.984375 0.765625 2.890625q0.765625 0.890625 1.984375 0.890625q0.984375 0 1.640625 -0.59375q0.65625 -0.609375 0.84375 -1.859375zm9.328125 2.390625q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm4.0788574 4.9375l0 -9.859375l1.5 0l0 1.40625q1.09375 -1.625 3.140625 -1.625q0.890625 0 1.640625 0.328125q0.75 0.3125 1.109375 0.84375q0.375 0.515625 0.53125 1.21875q0.09375 0.46875 0.09375 1.625l0 6.0625l-1.671875 0l0 -6.0q0 -1.015625 -0.203125 -1.515625q-0.1875 -0.515625 -0.6875 -0.8125q-0.5 -0.296875 -1.171875 -0.296875q-1.0625 0 -1.84375 0.671875q-0.765625 0.671875 -0.765625 2.578125l0 5.375l-1.671875 0zm8.844421 3.78125l0 -1.21875l11.0625 0l0 1.21875l-11.0625 0zm11.891357 -3.78125l0 -9.859375l1.5 0l0 1.5q0.578125 -1.046875 1.0625 -1.375q0.484375 -0.34375 1.078125 -0.34375q0.84375 0 1.71875 0.546875l-0.578125 1.546875q-0.609375 -0.359375 -1.234375 -0.359375q-0.546875 0 -0.984375 0.328125q-0.421875 0.328125 -0.609375 0.90625q-0.28125 0.890625 -0.28125 1.953125l0 5.15625l-1.671875 0zm12.9782715 -3.171875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm15.547607 4.65625q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm10.469482 4.9375l0 -1.25q-0.9375 1.46875 -2.75 1.46875q-1.171875 0 -2.171875 -0.640625q-0.984375 -0.65625 -1.53125 -1.8125q-0.53125 -1.171875 -0.53125 -2.6875q0 -1.46875 0.484375 -2.671875q0.5 -1.203125 1.46875 -1.84375q0.984375 -0.640625 2.203125 -0.640625q0.890625 0 1.578125 0.375q0.703125 0.375 1.140625 0.984375l0 -4.875l1.65625 0l0 13.59375l-1.546875 0zm-5.28125 -4.921875q0 1.890625 0.796875 2.828125q0.8125 0.9375 1.890625 0.9375q1.09375 0 1.859375 -0.890625q0.765625 -0.890625 0.765625 -2.734375q0 -2.015625 -0.78125 -2.953125q-0.78125 -0.953125 -1.921875 -0.953125q-1.109375 0 -1.859375 0.90625q-0.75 0.90625 -0.75 2.859375zm8.828857 -6.875l0 -1.9375l1.78125 0l0 1.53125q0 1.234375 -0.28125 1.78125q-0.40625 0.75 -1.25 1.140625l-0.40625 -0.671875q0.5 -0.203125 0.75 -0.640625q0.25 -0.4375 0.265625 -1.203125l-0.859375 0zm2.875 0l0 -1.9375l1.78125 0l0 1.53125q0 1.234375 -0.28125 1.78125q-0.40625 0.75 -1.25 1.140625l-0.40625 -0.671875q0.5 -0.203125 0.75 -0.640625q0.25 -0.4375 0.28125 -1.203125l-0.875 0zm12.145996 15.796875q-1.375 -1.75 -2.328125 -4.078125q-0.953125 -2.34375 -0.953125 -4.84375q0 -2.21875 0.703125 -4.234375q0.84375 -2.34375 2.578125 -4.671875l1.203125 0q-1.125 1.921875 -1.484375 2.75q-0.5625 1.28125 -0.890625 2.671875q-0.40625 1.734375 -0.40625 3.484375q0 4.46875 2.78125 8.921875l-1.203125 0zm2.3532715 -6.9375l1.65625 -0.265625q0.140625 1.0 0.765625 1.53125q0.640625 0.515625 1.78125 0.515625q1.15625 0 1.703125 -0.46875q0.5625 -0.46875 0.5625 -1.09375q0 -0.5625 -0.484375 -0.890625q-0.34375 -0.21875 -1.703125 -0.5625q-1.84375 -0.46875 -2.5625 -0.796875q-0.703125 -0.34375 -1.078125 -0.9375q-0.359375 -0.609375 -0.359375 -1.328125q0 -0.65625 0.296875 -1.21875q0.3125 -0.5625 0.828125 -0.9375q0.390625 -0.28125 1.0625 -0.484375q0.671875 -0.203125 1.4375 -0.203125q1.171875 0 2.046875 0.34375q0.875 0.328125 1.28125 0.90625q0.421875 0.5625 0.578125 1.515625l-1.625 0.21875q-0.109375 -0.75 -0.65625 -1.171875q-0.53125 -0.4375 -1.5 -0.4375q-1.15625 0 -1.640625 0.390625q-0.484375 0.375 -0.484375 0.875q0 0.328125 0.203125 0.59375q0.203125 0.265625 0.640625 0.4375q0.25 0.09375 1.46875 0.4375q1.765625 0.46875 2.46875 0.765625q0.703125 0.296875 1.09375 0.875q0.40625 0.578125 0.40625 1.4375q0 0.828125 -0.484375 1.578125q-0.484375 0.734375 -1.40625 1.140625q-0.921875 0.390625 -2.078125 0.390625q-1.921875 0 -2.9375 -0.796875q-1.0 -0.796875 -1.28125 -2.359375zm10.0 2.9375l0 -13.59375l1.671875 0l0 4.875q1.171875 -1.359375 2.953125 -1.359375q1.09375 0 1.890625 0.4375q0.8125 0.421875 1.15625 1.1875q0.359375 0.765625 0.359375 2.203125l0 6.25l-1.671875 0l0 -6.25q0 -1.25 -0.546875 -1.8125q-0.546875 -0.578125 -1.53125 -0.578125q-0.75 0 -1.40625 0.390625q-0.640625 0.375 -0.921875 1.046875q-0.28125 0.65625 -0.28125 1.8125l0 5.390625l-1.671875 0zm16.813232 -1.21875q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm4.0632324 4.9375l0 -9.859375l1.5 0l0 1.5q0.578125 -1.046875 1.0625 -1.375q0.484375 -0.34375 1.078125 -0.34375q0.84375 0 1.71875 0.546875l-0.578125 1.546875q-0.609375 -0.359375 -1.234375 -0.359375q-0.546875 0 -0.984375 0.328125q-0.421875 0.328125 -0.609375 0.90625q-0.28125 0.890625 -0.28125 1.953125l0 5.15625l-1.671875 0zm12.9782715 -3.171875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm15.500732 5.875l0 -1.25q-0.9375 1.46875 -2.75 1.46875q-1.171875 0 -2.171875 -0.640625q-0.984375 -0.65625 -1.53125 -1.8125q-0.53125 -1.171875 -0.53125 -2.6875q0 -1.46875 0.484375 -2.671875q0.5 -1.203125 1.46875 -1.84375q0.984375 -0.640625 2.203125 -0.640625q0.890625 0 1.578125 0.375q0.703125 0.375 1.140625 0.984375l0 -4.875l1.65625 0l0 13.59375l-1.546875 0zm-5.28125 -4.921875q0 1.890625 0.796875 2.828125q0.8125 0.9375 1.890625 0.9375q1.09375 0 1.859375 -0.890625q0.765625 -0.890625 0.765625 -2.734375q0 -2.015625 -0.78125 -2.953125q-0.78125 -0.953125 -1.921875 -0.953125q-1.109375 0 -1.859375 0.90625q-0.75 0.90625 -0.75 2.859375zm15.99646 4.921875l-1.546875 0l0 -13.59375l1.65625 0l0 4.84375q1.0625 -1.328125 2.703125 -1.328125q0.90625 0 1.71875 0.375q0.8125 0.359375 1.328125 1.03125q0.53125 0.65625 0.828125 1.59375q0.296875 0.9375 0.296875 2.0q0 2.53125 -1.25 3.921875q-1.25 1.375 -3.0 1.375q-1.75 0 -2.734375 -1.453125l0 1.234375zm-0.015625 -5.0q0 1.765625 0.46875 2.5625q0.796875 1.28125 2.140625 1.28125q1.09375 0 1.890625 -0.9375q0.796875 -0.953125 0.796875 -2.84375q0 -1.921875 -0.765625 -2.84375q-0.765625 -0.921875 -1.84375 -0.921875q-1.09375 0 -1.890625 0.953125q-0.796875 0.953125 -0.796875 2.75zm8.766357 8.796875l-0.171875 -1.5625q0.546875 0.140625 0.953125 0.140625q0.546875 0 0.875 -0.1875q0.34375 -0.1875 0.5625 -0.515625q0.15625 -0.25 0.5 -1.25q0.046875 -0.140625 0.15625 -0.40625l-3.734375 -9.875l1.796875 0l2.046875 5.71875q0.40625 1.078125 0.71875 2.28125q0.28125 -1.15625 0.6875 -2.25l2.09375 -5.75l1.671875 0l-3.75 10.03125q-0.59375 1.625 -0.9375 2.234375q-0.4375 0.828125 -1.015625 1.203125q-0.578125 0.390625 -1.375 0.390625q-0.484375 0 -1.078125 -0.203125zm23.730225 -17.390625l1.796875 0l0 7.84375q0 2.0625 -0.46875 3.265625q-0.453125 1.203125 -1.671875 1.96875q-1.203125 0.75 -3.171875 0.75q-1.90625 0 -3.125 -0.65625q-1.21875 -0.65625 -1.734375 -1.90625q-0.515625 -1.25 -0.515625 -3.421875l0 -7.84375l1.796875 0l0 7.84375q0 1.765625 0.328125 2.609375q0.328125 0.84375 1.125 1.296875q0.8125 0.453125 1.96875 0.453125q1.984375 0 2.828125 -0.890625q0.84375 -0.90625 0.84375 -3.46875l0 -7.84375zm12.644775 11.984375l0 1.609375l-8.984375 0q-0.015625 -0.609375 0.1875 -1.15625q0.34375 -0.921875 1.09375 -1.8125q0.765625 -0.890625 2.1875 -2.0625q2.21875 -1.8125 3.0 -2.875q0.78125 -1.0625 0.78125 -2.015625q0 -0.984375 -0.71875 -1.671875q-0.703125 -0.6875 -1.84375 -0.6875q-1.203125 0 -1.9375 0.734375q-0.71875 0.71875 -0.71875 2.0l-1.71875 -0.171875q0.171875 -1.921875 1.328125 -2.921875q1.15625 -1.015625 3.09375 -1.015625q1.953125 0 3.09375 1.09375q1.140625 1.078125 1.140625 2.6875q0 0.8125 -0.34375 1.609375q-0.328125 0.78125 -1.109375 1.65625q-0.765625 0.859375 -2.5625 2.390625q-1.5 1.265625 -1.9375 1.71875q-0.421875 0.4375 -0.703125 0.890625l6.671875 0zm3.1569824 5.609375l-1.1875 0q2.765625 -4.453125 2.765625 -8.921875q0 -1.734375 -0.390625 -3.453125q-0.328125 -1.390625 -0.890625 -2.671875q-0.359375 -0.84375 -1.484375 -2.78125l1.1875 0q1.75 2.328125 2.578125 4.671875q0.71875 2.015625 0.71875 4.234375q0 2.5 -0.96875 4.84375q-0.953125 2.328125 -2.328125 4.078125z" fill-rule="nonzero"></path><path fill="#000000" d="m902.08356 415.05588l1.796875 0l0 7.84375q0 2.0625 -0.46875 3.265625q-0.453125 1.203125 -1.671875 1.96875q-1.203125 0.75 -3.171875 0.75q-1.90625 0 -3.125 -0.65625q-1.21875 -0.65625 -1.734375 -1.90625q-0.515625 -1.25 -0.515625 -3.421875l0 -7.84375l1.796875 0l0 7.84375q0 1.765625 0.328125 2.609375q0.328125 0.84375 1.125 1.296875q0.8125 0.453125 1.96875 0.453125q1.984375 0 2.828125 -0.890625q0.84375 -0.90625 0.84375 -3.46875l0 -7.84375zm3.8791504 10.0l1.671875 -0.21875q0.28125 1.421875 0.96875 2.046875q0.703125 0.625 1.6875 0.625q1.1875 0 2.0 -0.8125q0.8125 -0.828125 0.8125 -2.03125q0 -1.140625 -0.765625 -1.890625q-0.75 -0.75 -1.90625 -0.75q-0.46875 0 -1.171875 0.1875l0.1875 -1.46875q0.15625 0.015625 0.265625 0.015625q1.0625 0 1.90625 -0.546875q0.859375 -0.5625 0.859375 -1.71875q0 -0.921875 -0.625 -1.515625q-0.609375 -0.609375 -1.59375 -0.609375q-0.96875 0 -1.625 0.609375q-0.640625 0.609375 -0.828125 1.84375l-1.671875 -0.296875q0.296875 -1.6875 1.375 -2.609375q1.09375 -0.921875 2.71875 -0.921875q1.109375 0 2.046875 0.484375q0.9375 0.46875 1.421875 1.296875q0.5 0.828125 0.5 1.75q0 0.890625 -0.46875 1.609375q-0.46875 0.71875 -1.40625 1.15625q1.21875 0.265625 1.875 1.15625q0.671875 0.875 0.671875 2.1875q0 1.78125 -1.296875 3.015625q-1.296875 1.234375 -3.28125 1.234375q-1.796875 0 -2.984375 -1.0625q-1.171875 -1.0625 -1.34375 -2.765625zm15.371521 -0.484375l0 -1.6875l5.125 0l0 1.6875l-5.125 0zm12.255371 4.078125l0 -13.59375l5.125 0q1.359375 0 2.078125 0.125q1.0 0.171875 1.671875 0.640625q0.671875 0.46875 1.078125 1.3125q0.421875 0.84375 0.421875 1.84375q0 1.734375 -1.109375 2.9375q-1.09375 1.203125 -3.984375 1.203125l-3.484375 0l0 5.53125l-1.796875 0zm1.796875 -7.140625l3.515625 0q1.75 0 2.46875 -0.640625q0.734375 -0.65625 0.734375 -1.828125q0 -0.859375 -0.4375 -1.46875q-0.421875 -0.609375 -1.125 -0.796875q-0.453125 -0.125 -1.671875 -0.125l-3.484375 0l0 4.859375zm15.3186035 7.140625l0 -3.25l-5.90625 0l0 -1.53125l6.21875 -8.8125l1.359375 0l0 8.8125l1.84375 0l0 1.53125l-1.84375 0l0 3.25l-1.671875 0zm0 -4.78125l0 -6.140625l-4.25 6.140625l4.25 0zm10.027771 0.703125l0 -1.6875l5.125 0l0 1.6875l-5.125 0zm13.34906 -7.234375l0 1.9375l-1.796875 0l0 -1.53125q0 -1.25 0.296875 -1.796875q0.390625 -0.75 1.234375 -1.125l0.40625 0.640625q-0.5 0.21875 -0.75 0.640625q-0.234375 0.421875 -0.265625 1.234375l0.875 0zm2.875 0l0 1.9375l-1.796875 0l0 -1.53125q0 -1.25 0.296875 -1.796875q0.390625 -0.75 1.234375 -1.125l0.40625 0.640625q-0.5 0.21875 -0.75 0.640625q-0.234375 0.421875 -0.265625 1.234375l0.875 0zm8.4627075 7.703125l1.640625 0.21875q-0.265625 1.6875 -1.375 2.65625q-1.109375 0.953125 -2.734375 0.953125q-2.015625 0 -3.25 -1.3125q-1.21875 -1.328125 -1.21875 -3.796875q0 -1.59375 0.515625 -2.78125q0.53125 -1.203125 1.609375 -1.796875q1.09375 -0.609375 2.359375 -0.609375q1.609375 0 2.625 0.8125q1.015625 0.8125 1.3125 2.3125l-1.625 0.25q-0.234375 -1.0 -0.828125 -1.5q-0.59375 -0.5 -1.421875 -0.5q-1.265625 0 -2.0625 0.90625q-0.78125 0.90625 -0.78125 2.859375q0 1.984375 0.765625 2.890625q0.765625 0.890625 1.984375 0.890625q0.984375 0 1.640625 -0.59375q0.65625 -0.609375 0.84375 -1.859375zm9.328125 2.390625q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm4.0788574 4.9375l0 -9.859375l1.5 0l0 1.40625q1.09375 -1.625 3.140625 -1.625q0.890625 0 1.640625 0.328125q0.75 0.3125 1.109375 0.84375q0.375 0.515625 0.53125 1.21875q0.09375 0.46875 0.09375 1.625l0 6.0625l-1.671875 0l0 -6.0q0 -1.015625 -0.203125 -1.515625q-0.1875 -0.515625 -0.6875 -0.8125q-0.5 -0.296875 -1.171875 -0.296875q-1.0625 0 -1.84375 0.671875q-0.765625 0.671875 -0.765625 2.578125l0 5.375l-1.671875 0zm8.844421 3.78125l0 -1.21875l11.0625 0l0 1.21875l-11.0625 0zm11.891357 -3.78125l0 -9.859375l1.5 0l0 1.5q0.578125 -1.046875 1.0625 -1.375q0.484375 -0.34375 1.078125 -0.34375q0.84375 0 1.71875 0.546875l-0.578125 1.546875q-0.609375 -0.359375 -1.234375 -0.359375q-0.546875 0 -0.984375 0.328125q-0.421875 0.328125 -0.609375 0.90625q-0.28125 0.890625 -0.28125 1.953125l0 5.15625l-1.671875 0zm12.9782715 -3.171875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm15.547607 4.65625q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm10.469482 4.9375l0 -1.25q-0.9375 1.46875 -2.75 1.46875q-1.171875 0 -2.171875 -0.640625q-0.984375 -0.65625 -1.53125 -1.8125q-0.53125 -1.171875 -0.53125 -2.6875q0 -1.46875 0.484375 -2.671875q0.5 -1.203125 1.46875 -1.84375q0.984375 -0.640625 2.203125 -0.640625q0.890625 0 1.578125 0.375q0.703125 0.375 1.140625 0.984375l0 -4.875l1.65625 0l0 13.59375l-1.546875 0zm-5.28125 -4.921875q0 1.890625 0.796875 2.828125q0.8125 0.9375 1.890625 0.9375q1.09375 0 1.859375 -0.890625q0.765625 -0.890625 0.765625 -2.734375q0 -2.015625 -0.78125 -2.953125q-0.78125 -0.953125 -1.921875 -0.953125q-1.109375 0 -1.859375 0.90625q-0.75 0.90625 -0.75 2.859375zm8.828857 -6.875l0 -1.9375l1.78125 0l0 1.53125q0 1.234375 -0.28125 1.78125q-0.40625 0.75 -1.25 1.140625l-0.40625 -0.671875q0.5 -0.203125 0.75 -0.640625q0.25 -0.4375 0.265625 -1.203125l-0.859375 0zm2.875 0l0 -1.9375l1.78125 0l0 1.53125q0 1.234375 -0.28125 1.78125q-0.40625 0.75 -1.25 1.140625l-0.40625 -0.671875q0.5 -0.203125 0.75 -0.640625q0.25 -0.4375 0.28125 -1.203125l-0.875 0zm12.145996 15.796875q-1.375 -1.75 -2.328125 -4.078125q-0.953125 -2.34375 -0.953125 -4.84375q0 -2.21875 0.703125 -4.234375q0.84375 -2.34375 2.578125 -4.671875l1.203125 0q-1.125 1.921875 -1.484375 2.75q-0.5625 1.28125 -0.890625 2.671875q-0.40625 1.734375 -0.40625 3.484375q0 4.46875 2.78125 8.921875l-1.203125 0zm2.3532715 -6.9375l1.65625 -0.265625q0.140625 1.0 0.765625 1.53125q0.640625 0.515625 1.78125 0.515625q1.15625 0 1.703125 -0.46875q0.5625 -0.46875 0.5625 -1.09375q0 -0.5625 -0.484375 -0.890625q-0.34375 -0.21875 -1.703125 -0.5625q-1.84375 -0.46875 -2.5625 -0.796875q-0.703125 -0.34375 -1.078125 -0.9375q-0.359375 -0.609375 -0.359375 -1.328125q0 -0.65625 0.296875 -1.21875q0.3125 -0.5625 0.828125 -0.9375q0.390625 -0.28125 1.0625 -0.484375q0.671875 -0.203125 1.4375 -0.203125q1.171875 0 2.046875 0.34375q0.875 0.328125 1.28125 0.90625q0.421875 0.5625 0.578125 1.515625l-1.625 0.21875q-0.109375 -0.75 -0.65625 -1.171875q-0.53125 -0.4375 -1.5 -0.4375q-1.15625 0 -1.640625 0.390625q-0.484375 0.375 -0.484375 0.875q0 0.328125 0.203125 0.59375q0.203125 0.265625 0.640625 0.4375q0.25 0.09375 1.46875 0.4375q1.765625 0.46875 2.46875 0.765625q0.703125 0.296875 1.09375 0.875q0.40625 0.578125 0.40625 1.4375q0 0.828125 -0.484375 1.578125q-0.484375 0.734375 -1.40625 1.140625q-0.921875 0.390625 -2.078125 0.390625q-1.921875 0 -2.9375 -0.796875q-1.0 -0.796875 -1.28125 -2.359375zm10.0 2.9375l0 -13.59375l1.671875 0l0 4.875q1.171875 -1.359375 2.953125 -1.359375q1.09375 0 1.890625 0.4375q0.8125 0.421875 1.15625 1.1875q0.359375 0.765625 0.359375 2.203125l0 6.25l-1.671875 0l0 -6.25q0 -1.25 -0.546875 -1.8125q-0.546875 -0.578125 -1.53125 -0.578125q-0.75 0 -1.40625 0.390625q-0.640625 0.375 -0.921875 1.046875q-0.28125 0.65625 -0.28125 1.8125l0 5.390625l-1.671875 0zm16.813232 -1.21875q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm4.0632324 4.9375l0 -9.859375l1.5 0l0 1.5q0.578125 -1.046875 1.0625 -1.375q0.484375 -0.34375 1.078125 -0.34375q0.84375 0 1.71875 0.546875l-0.578125 1.546875q-0.609375 -0.359375 -1.234375 -0.359375q-0.546875 0 -0.984375 0.328125q-0.421875 0.328125 -0.609375 0.90625q-0.28125 0.890625 -0.28125 1.953125l0 5.15625l-1.671875 0zm12.9782715 -3.171875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm15.500732 5.875l0 -1.25q-0.9375 1.46875 -2.75 1.46875q-1.171875 0 -2.171875 -0.640625q-0.984375 -0.65625 -1.53125 -1.8125q-0.53125 -1.171875 -0.53125 -2.6875q0 -1.46875 0.484375 -2.671875q0.5 -1.203125 1.46875 -1.84375q0.984375 -0.640625 2.203125 -0.640625q0.890625 0 1.578125 0.375q0.703125 0.375 1.140625 0.984375l0 -4.875l1.65625 0l0 13.59375l-1.546875 0zm-5.28125 -4.921875q0 1.890625 0.796875 2.828125q0.8125 0.9375 1.890625 0.9375q1.09375 0 1.859375 -0.890625q0.765625 -0.890625 0.765625 -2.734375q0 -2.015625 -0.78125 -2.953125q-0.78125 -0.953125 -1.921875 -0.953125q-1.109375 0 -1.859375 0.90625q-0.75 0.90625 -0.75 2.859375zm15.99646 4.921875l-1.546875 0l0 -13.59375l1.65625 0l0 4.84375q1.0625 -1.328125 2.703125 -1.328125q0.90625 0 1.71875 0.375q0.8125 0.359375 1.328125 1.03125q0.53125 0.65625 0.828125 1.59375q0.296875 0.9375 0.296875 2.0q0 2.53125 -1.25 3.921875q-1.25 1.375 -3.0 1.375q-1.75 0 -2.734375 -1.453125l0 1.234375zm-0.015625 -5.0q0 1.765625 0.46875 2.5625q0.796875 1.28125 2.140625 1.28125q1.09375 0 1.890625 -0.9375q0.796875 -0.953125 0.796875 -2.84375q0 -1.921875 -0.765625 -2.84375q-0.765625 -0.921875 -1.84375 -0.921875q-1.09375 0 -1.890625 0.953125q-0.796875 0.953125 -0.796875 2.75zm8.766357 8.796875l-0.171875 -1.5625q0.546875 0.140625 0.953125 0.140625q0.546875 0 0.875 -0.1875q0.34375 -0.1875 0.5625 -0.515625q0.15625 -0.25 0.5 -1.25q0.046875 -0.140625 0.15625 -0.40625l-3.734375 -9.875l1.796875 0l2.046875 5.71875q0.40625 1.078125 0.71875 2.28125q0.28125 -1.15625 0.6875 -2.25l2.09375 -5.75l1.671875 0l-3.75 10.03125q-0.59375 1.625 -0.9375 2.234375q-0.4375 0.828125 -1.015625 1.203125q-0.578125 0.390625 -1.375 0.390625q-0.484375 0 -1.078125 -0.203125zm23.730225 -17.390625l1.796875 0l0 7.84375q0 2.0625 -0.46875 3.265625q-0.453125 1.203125 -1.671875 1.96875q-1.203125 0.75 -3.171875 0.75q-1.90625 0 -3.125 -0.65625q-1.21875 -0.65625 -1.734375 -1.90625q-0.515625 -1.25 -0.515625 -3.421875l0 -7.84375l1.796875 0l0 7.84375q0 1.765625 0.328125 2.609375q0.328125 0.84375 1.125 1.296875q0.8125 0.453125 1.96875 0.453125q1.984375 0 2.828125 -0.890625q0.84375 -0.90625 0.84375 -3.46875l0 -7.84375zm10.1604 13.59375l-1.671875 0l0 -10.640625q-0.59375 0.578125 -1.578125 1.15625q-0.984375 0.5625 -1.765625 0.859375l0 -1.625q1.40625 -0.65625 2.453125 -1.59375q1.046875 -0.9375 1.484375 -1.8125l1.078125 0l0 13.65625zm5.6413574 4.0l-1.1875 0q2.765625 -4.453125 2.765625 -8.921875q0 -1.734375 -0.390625 -3.453125q-0.328125 -1.390625 -0.890625 -2.671875q-0.359375 -0.84375 -1.484375 -2.78125l1.1875 0q1.75 2.328125 2.578125 4.671875q0.71875 2.015625 0.71875 4.234375q0 2.5 -0.96875 4.84375q-0.953125 2.328125 -2.328125 4.078125z" fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0" d="m748.18634 137.59842l-557.7638 242.01575" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="4.0,3.0,1.0,3.0" d="m748.18634 137.59842l-552.2596 239.62747" fill-rule="evenodd"></path><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m195.26929 375.71063l-3.5056152 3.3216248l4.8205566 -0.2911377z" fill-rule="evenodd"></path><path fill="#000000" fill-opacity="0.0" d="m634.56696 151.3307l152.59839 0l0 41.95276l-152.59839 0z" fill-rule="nonzero"></path><path fill="#000000" d="m646.12946 166.9382l0 1.9375l-1.796875 0l0 -1.53125q0 -1.25 0.296875 -1.796875q0.390625 -0.75 1.234375 -1.125l0.40625 0.640625q-0.5 0.21875 -0.75 0.640625q-0.234375 0.421875 -0.265625 1.234375l0.875 0zm2.875 0l0 1.9375l-1.796875 0l0 -1.53125q0 -1.25 0.296875 -1.796875q0.390625 -0.75 1.234375 -1.125l0.40625 0.640625q-0.5 0.21875 -0.75 0.640625q-0.234375 0.421875 -0.265625 1.234375l0.875 0zm8.4626465 7.703125l1.640625 0.21875q-0.265625 1.6875 -1.375 2.65625q-1.109375 0.953125 -2.734375 0.953125q-2.015625 0 -3.25 -1.3125q-1.21875 -1.328125 -1.21875 -3.796875q0 -1.59375 0.515625 -2.78125q0.53125 -1.203125 1.609375 -1.796875q1.09375 -0.609375 2.359375 -0.609375q1.609375 0 2.625 0.8125q1.015625 0.8125 1.3125 2.3125l-1.625 0.25q-0.234375 -1.0 -0.828125 -1.5q-0.59375 -0.5 -1.421875 -0.5q-1.265625 0 -2.0625 0.90625q-0.78125 0.90625 -0.78125 2.859375q0 1.984375 0.765625 2.890625q0.765625 0.890625 1.984375 0.890625q0.984375 0 1.640625 -0.59375q0.65625 -0.609375 0.84375 -1.859375zm9.328125 2.390625q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm4.0788574 4.9375l0 -9.859375l1.5 0l0 1.40625q1.09375 -1.625 3.140625 -1.625q0.890625 0 1.640625 0.328125q0.75 0.3125 1.109375 0.84375q0.375 0.515625 0.53125 1.21875q0.09375 0.46875 0.09375 1.625l0 6.0625l-1.671875 0l0 -6.0q0 -1.015625 -0.203125 -1.515625q-0.1875 -0.515625 -0.6875 -0.8125q-0.5 -0.296875 -1.171875 -0.296875q-1.0625 0 -1.84375 0.671875q-0.765625 0.671875 -0.765625 2.578125l0 5.375l-1.671875 0zm8.844482 3.78125l0 -1.21875l11.0625 0l0 1.21875l-11.0625 0zm11.906921 -3.78125l0 -9.859375l1.5 0l0 1.390625q0.453125 -0.71875 1.21875 -1.15625q0.78125 -0.453125 1.765625 -0.453125q1.09375 0 1.796875 0.453125q0.703125 0.453125 0.984375 1.28125q1.171875 -1.734375 3.046875 -1.734375q1.46875 0 2.25 0.8125q0.796875 0.8125 0.796875 2.5l0 6.765625l-1.671875 0l0 -6.203125q0 -1.0 -0.15625 -1.4375q-0.15625 -0.453125 -0.59375 -0.71875q-0.421875 -0.265625 -1.0 -0.265625q-1.03125 0 -1.71875 0.6875q-0.6875 0.6875 -0.6875 2.21875l0 5.71875l-1.671875 0l0 -6.40625q0 -1.109375 -0.40625 -1.65625q-0.40625 -0.5625 -1.34375 -0.5625q-0.703125 0 -1.3125 0.375q-0.59375 0.359375 -0.859375 1.078125q-0.265625 0.71875 -0.265625 2.0625l0 5.109375l-1.671875 0zm21.978333 -1.21875q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm4.0788574 4.9375l0 -9.859375l1.5 0l0 1.40625q1.09375 -1.625 3.140625 -1.625q0.890625 0 1.640625 0.328125q0.75 0.3125 1.109375 0.84375q0.375 0.515625 0.53125 1.21875q0.09375 0.46875 0.09375 1.625l0 6.0625l-1.671875 0l0 -6.0q0 -1.015625 -0.203125 -1.515625q-0.1875 -0.515625 -0.6875 -0.8125q-0.5 -0.296875 -1.171875 -0.296875q-1.0625 0 -1.84375 0.671875q-0.765625 0.671875 -0.765625 2.578125l0 5.375l-1.671875 0zm16.813171 -1.21875q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm3.7819824 5.75l1.609375 0.25q0.109375 0.75 0.578125 1.09375q0.609375 0.453125 1.6875 0.453125q1.171875 0 1.796875 -0.46875q0.625 -0.453125 0.859375 -1.28125q0.125 -0.515625 0.109375 -2.15625q-1.09375 1.296875 -2.71875 1.296875q-2.03125 0 -3.15625 -1.46875q-1.109375 -1.46875 -1.109375 -3.515625q0 -1.40625 0.515625 -2.59375q0.515625 -1.203125 1.484375 -1.84375q0.96875 -0.65625 2.265625 -0.65625q1.75 0 2.875 1.40625l0 -1.1875l1.546875 0l0 8.515625q0 2.3125 -0.46875 3.265625q-0.46875 0.96875 -1.484375 1.515625q-1.015625 0.5625 -2.5 0.5625q-1.765625 0 -2.859375 -0.796875q-1.078125 -0.796875 -1.03125 -2.390625zm1.375 -5.921875q0 1.953125 0.765625 2.84375q0.78125 0.890625 1.9375 0.890625q1.140625 0 1.921875 -0.890625q0.78125 -0.890625 0.78125 -2.78125q0 -1.8125 -0.8125 -2.71875q-0.796875 -0.921875 -1.921875 -0.921875q-1.109375 0 -1.890625 0.90625q-0.78125 0.890625 -0.78125 2.671875zm16.047607 1.9375l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm8.672607 -5.921875l0 -1.9375l1.78125 0l0 1.53125q0 1.234375 -0.28125 1.78125q-0.40625 0.75 -1.25 1.140625l-0.40625 -0.671875q0.5 -0.203125 0.75 -0.640625q0.25 -0.4375 0.265625 -1.203125l-0.859375 0zm2.875 0l0 -1.9375l1.78125 0l0 1.53125q0 1.234375 -0.28125 1.78125q-0.40625 0.75 -1.25 1.140625l-0.40625 -0.671875q0.5 -0.203125 0.75 -0.640625q0.25 -0.4375 0.28125 -1.203125l-0.875 0z" fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0" d="m602.8373 289.3071l142.58264 0l0 41.95276l-142.58264 0z" fill-rule="nonzero"></path><path fill="#a61c00" d="m612.5248 311.8052l2.671875 -0.265625q0.234375 1.34375 0.96875 1.984375q0.75 0.625 2.0 0.625q1.328125 0 2.0 -0.5625q0.671875 -0.5625 0.671875 -1.3125q0 -0.484375 -0.28125 -0.8125q-0.28125 -0.34375 -0.984375 -0.59375q-0.484375 -0.171875 -2.203125 -0.59375q-2.203125 -0.546875 -3.09375 -1.34375q-1.265625 -1.125 -1.265625 -2.734375q0 -1.046875 0.59375 -1.953125q0.59375 -0.90625 1.703125 -1.375q1.109375 -0.46875 2.671875 -0.46875q2.5625 0 3.859375 1.125q1.296875 1.109375 1.359375 2.984375l-2.75 0.125q-0.171875 -1.046875 -0.75 -1.5q-0.578125 -0.46875 -1.75 -0.46875q-1.1875 0 -1.875 0.5q-0.4375 0.3125 -0.4375 0.84375q0 0.484375 0.421875 0.828125q0.515625 0.421875 2.515625 0.90625q2.0 0.46875 2.953125 0.984375q0.96875 0.5 1.515625 1.375q0.546875 0.875 0.546875 2.15625q0 1.171875 -0.65625 2.203125q-0.640625 1.015625 -1.828125 1.515625q-1.1875 0.484375 -2.96875 0.484375q-2.578125 0 -3.96875 -1.1875q-1.375 -1.1875 -1.640625 -3.46875zm15.7247925 -9.171875l0 5.0q1.25 -1.484375 3.015625 -1.484375q0.890625 0 1.609375 0.34375q0.734375 0.328125 1.09375 0.84375q0.375 0.515625 0.5 1.15625q0.140625 0.625 0.140625 1.953125l0 5.78125l-2.609375 0l0 -5.203125q0 -1.546875 -0.15625 -1.96875q-0.140625 -0.421875 -0.515625 -0.65625q-0.375 -0.25 -0.9375 -0.25q-0.65625 0 -1.171875 0.3125q-0.5 0.3125 -0.734375 0.953125q-0.234375 0.640625 -0.234375 1.875l0 4.9375l-2.609375 0l0 -13.59375l2.609375 0zm10.739746 6.75l-2.359375 -0.4375q0.390625 -1.421875 1.359375 -2.109375q0.984375 -0.6875 2.90625 -0.6875q1.734375 0 2.59375 0.421875q0.859375 0.40625 1.203125 1.046875q0.34375 0.625 0.34375 2.328125l-0.03125 3.046875q0 1.296875 0.125 1.921875q0.125 0.609375 0.46875 1.3125l-2.578125 0q-0.09375 -0.265625 -0.25 -0.765625q-0.0625 -0.234375 -0.09375 -0.3125q-0.65625 0.65625 -1.421875 0.984375q-0.765625 0.3125 -1.625 0.3125q-1.515625 0 -2.40625 -0.8125q-0.875 -0.828125 -0.875 -2.09375q0 -0.84375 0.390625 -1.484375q0.40625 -0.65625 1.125 -1.0q0.71875 -0.359375 2.078125 -0.625q1.828125 -0.328125 2.53125 -0.625l0 -0.265625q0 -0.75 -0.375 -1.0625q-0.359375 -0.328125 -1.390625 -0.328125q-0.703125 0 -1.09375 0.28125q-0.390625 0.265625 -0.625 0.953125zm3.484375 2.109375q-0.5 0.171875 -1.59375 0.40625q-1.078125 0.234375 -1.40625 0.453125q-0.515625 0.359375 -0.515625 0.921875q0 0.546875 0.40625 0.953125q0.40625 0.390625 1.046875 0.390625q0.703125 0 1.34375 -0.46875q0.46875 -0.359375 0.625 -0.859375q0.09375 -0.34375 0.09375 -1.28125l0 -0.515625zm7.4382324 4.734375l-2.609375 0l0 -9.859375l2.421875 0l0 1.40625q0.625 -0.984375 1.109375 -1.296875q0.5 -0.328125 1.140625 -0.328125q0.890625 0 1.71875 0.5l-0.8125 2.265625q-0.65625 -0.421875 -1.21875 -0.421875q-0.546875 0 -0.9375 0.296875q-0.375 0.296875 -0.59375 1.09375q-0.21875 0.78125 -0.21875 3.296875l0 3.046875zm10.463379 -3.140625l2.609375 0.4375q-0.5 1.4375 -1.59375 2.1875q-1.078125 0.734375 -2.703125 0.734375q-2.5625 0 -3.796875 -1.671875q-0.96875 -1.34375 -0.96875 -3.40625q0 -2.4375 1.265625 -3.828125q1.28125 -1.390625 3.25 -1.390625q2.1875 0 3.453125 1.453125q1.28125 1.453125 1.234375 4.453125l-6.53125 0q0.015625 1.15625 0.625 1.8125q0.609375 0.640625 1.5 0.640625q0.609375 0 1.03125 -0.328125q0.421875 -0.34375 0.625 -1.09375zm0.15625 -2.625q-0.03125 -1.140625 -0.59375 -1.71875q-0.546875 -0.59375 -1.34375 -0.59375q-0.859375 0 -1.40625 0.625q-0.5625 0.609375 -0.546875 1.6875l3.890625 0zm13.563232 5.765625l-2.421875 0l0 -1.453125q-0.609375 0.84375 -1.4375 1.265625q-0.8125 0.40625 -1.640625 0.40625q-1.703125 0 -2.921875 -1.359375q-1.203125 -1.375 -1.203125 -3.828125q0 -2.5 1.171875 -3.796875q1.1875 -1.3125 2.984375 -1.3125q1.65625 0 2.859375 1.375l0 -4.890625l2.609375 0l0 13.59375zm-6.96875 -5.140625q0 1.578125 0.4375 2.28125q0.640625 1.015625 1.765625 1.015625q0.90625 0 1.53125 -0.765625q0.625 -0.765625 0.625 -2.28125q0 -1.703125 -0.609375 -2.4375q-0.609375 -0.75 -1.5625 -0.75q-0.9375 0 -1.5625 0.734375q-0.625 0.734375 -0.625 2.203125zm14.391785 5.140625l0 -13.59375l2.609375 0l0 4.890625q1.203125 -1.375 2.859375 -1.375q1.796875 0 2.96875 1.3125q1.1875 1.296875 1.1875 3.734375q0 2.53125 -1.203125 3.890625q-1.203125 1.359375 -2.921875 1.359375q-0.84375 0 -1.671875 -0.421875q-0.8125 -0.421875 -1.40625 -1.25l0 1.453125l-2.421875 0zm2.59375 -5.140625q0 1.53125 0.484375 2.265625q0.671875 1.03125 1.796875 1.03125q0.859375 0 1.46875 -0.734375q0.609375 -0.734375 0.609375 -2.328125q0 -1.6875 -0.609375 -2.421875q-0.609375 -0.75 -1.578125 -0.75q-0.9375 0 -1.5625 0.734375q-0.609375 0.71875 -0.609375 2.203125zm7.677246 -4.71875l2.78125 0l2.359375 7.0l2.296875 -7.0l2.703125 0l-3.484375 9.484375l-0.625 1.71875q-0.34375 0.859375 -0.65625 1.3125q-0.296875 0.46875 -0.703125 0.75q-0.40625 0.28125 -1.0 0.4375q-0.59375 0.15625 -1.328125 0.15625q-0.75 0 -1.46875 -0.15625l-0.234375 -2.046875q0.609375 0.125 1.09375 0.125q0.921875 0 1.34375 -0.53125q0.4375 -0.53125 0.671875 -1.359375l-3.75 -9.890625zm16.793396 -3.734375l2.75 0l0 7.359375q0 1.75 0.109375 2.265625q0.171875 0.84375 0.828125 1.359375q0.671875 0.5 1.8125 0.5q1.171875 0 1.765625 -0.484375q0.59375 -0.484375 0.71875 -1.171875q0.125 -0.703125 0.125 -2.3125l0 -7.515625l2.734375 0l0 7.140625q0 2.4375 -0.21875 3.453125q-0.21875 1.015625 -0.828125 1.71875q-0.59375 0.6875 -1.59375 1.109375q-1.0 0.40625 -2.609375 0.40625q-1.953125 0 -2.96875 -0.453125q-1.0 -0.453125 -1.59375 -1.171875q-0.578125 -0.71875 -0.75 -1.5q-0.28125 -1.171875 -0.28125 -3.453125l0 -7.25zm19.5979 13.59375l-2.609375 0l0 -9.828125q-1.4375 1.34375 -3.375 1.984375l0 -2.375q1.03125 -0.328125 2.21875 -1.25q1.203125 -0.9375 1.640625 -2.1875l2.125 0l0 13.65625z" fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0" d="m618.8373 177.30708l142.58264 0l0 41.95276l-142.58264 0z" fill-rule="nonzero"></path><path fill="#a61c00" d="m628.5248 199.8052l2.671875 -0.265625q0.234375 1.34375 0.96875 1.984375q0.75 0.625 2.0 0.625q1.328125 0 2.0 -0.5625q0.671875 -0.5625 0.671875 -1.3125q0 -0.484375 -0.28125 -0.8125q-0.28125 -0.34375 -0.984375 -0.59375q-0.484375 -0.171875 -2.203125 -0.59375q-2.203125 -0.546875 -3.09375 -1.34375q-1.265625 -1.125 -1.265625 -2.734375q0 -1.046875 0.59375 -1.953125q0.59375 -0.90625 1.703125 -1.375q1.109375 -0.46875 2.671875 -0.46875q2.5625 0 3.859375 1.125q1.296875 1.109375 1.359375 2.984375l-2.75 0.125q-0.171875 -1.046875 -0.75 -1.5q-0.578125 -0.46875 -1.75 -0.46875q-1.1875 0 -1.875 0.5q-0.4375 0.3125 -0.4375 0.84375q0 0.484375 0.421875 0.828125q0.515625 0.421875 2.515625 0.90625q2.0 0.46875 2.953125 0.984375q0.96875 0.5 1.515625 1.375q0.546875 0.875 0.546875 2.15625q0 1.171875 -0.65625 2.203125q-0.640625 1.015625 -1.828125 1.515625q-1.1875 0.484375 -2.96875 0.484375q-2.578125 0 -3.96875 -1.1875q-1.375 -1.1875 -1.640625 -3.46875zm15.7247925 -9.171875l0 5.0q1.25 -1.484375 3.015625 -1.484375q0.890625 0 1.609375 0.34375q0.734375 0.328125 1.09375 0.84375q0.375 0.515625 0.5 1.15625q0.140625 0.625 0.140625 1.953125l0 5.78125l-2.609375 0l0 -5.203125q0 -1.546875 -0.15625 -1.96875q-0.140625 -0.421875 -0.515625 -0.65625q-0.375 -0.25 -0.9375 -0.25q-0.65625 0 -1.171875 0.3125q-0.5 0.3125 -0.734375 0.953125q-0.234375 0.640625 -0.234375 1.875l0 4.9375l-2.609375 0l0 -13.59375l2.609375 0zm10.739746 6.75l-2.359375 -0.4375q0.390625 -1.421875 1.359375 -2.109375q0.984375 -0.6875 2.90625 -0.6875q1.734375 0 2.59375 0.421875q0.859375 0.40625 1.203125 1.046875q0.34375 0.625 0.34375 2.328125l-0.03125 3.046875q0 1.296875 0.125 1.921875q0.125 0.609375 0.46875 1.3125l-2.578125 0q-0.09375 -0.265625 -0.25 -0.765625q-0.0625 -0.234375 -0.09375 -0.3125q-0.65625 0.65625 -1.421875 0.984375q-0.765625 0.3125 -1.625 0.3125q-1.515625 0 -2.40625 -0.8125q-0.875 -0.828125 -0.875 -2.09375q0 -0.84375 0.390625 -1.484375q0.40625 -0.65625 1.125 -1.0q0.71875 -0.359375 2.078125 -0.625q1.828125 -0.328125 2.53125 -0.625l0 -0.265625q0 -0.75 -0.375 -1.0625q-0.359375 -0.328125 -1.390625 -0.328125q-0.703125 0 -1.09375 0.28125q-0.390625 0.265625 -0.625 0.953125zm3.484375 2.109375q-0.5 0.171875 -1.59375 0.40625q-1.078125 0.234375 -1.40625 0.453125q-0.515625 0.359375 -0.515625 0.921875q0 0.546875 0.40625 0.953125q0.40625 0.390625 1.046875 0.390625q0.703125 0 1.34375 -0.46875q0.46875 -0.359375 0.625 -0.859375q0.09375 -0.34375 0.09375 -1.28125l0 -0.515625zm7.4382324 4.734375l-2.609375 0l0 -9.859375l2.421875 0l0 1.40625q0.625 -0.984375 1.109375 -1.296875q0.5 -0.328125 1.140625 -0.328125q0.890625 0 1.71875 0.5l-0.8125 2.265625q-0.65625 -0.421875 -1.21875 -0.421875q-0.546875 0 -0.9375 0.296875q-0.375 0.296875 -0.59375 1.09375q-0.21875 0.78125 -0.21875 3.296875l0 3.046875zm10.463379 -3.140625l2.609375 0.4375q-0.5 1.4375 -1.59375 2.1875q-1.078125 0.734375 -2.703125 0.734375q-2.5625 0 -3.796875 -1.671875q-0.96875 -1.34375 -0.96875 -3.40625q0 -2.4375 1.265625 -3.828125q1.28125 -1.390625 3.25 -1.390625q2.1875 0 3.453125 1.453125q1.28125 1.453125 1.234375 4.453125l-6.53125 0q0.015625 1.15625 0.625 1.8125q0.609375 0.640625 1.5 0.640625q0.609375 0 1.03125 -0.328125q0.421875 -0.34375 0.625 -1.09375zm0.15625 -2.625q-0.03125 -1.140625 -0.59375 -1.71875q-0.546875 -0.59375 -1.34375 -0.59375q-0.859375 0 -1.40625 0.625q-0.5625 0.609375 -0.546875 1.6875l3.890625 0zm13.563232 5.765625l-2.421875 0l0 -1.453125q-0.609375 0.84375 -1.4375 1.265625q-0.8125 0.40625 -1.640625 0.40625q-1.703125 0 -2.921875 -1.359375q-1.203125 -1.375 -1.203125 -3.828125q0 -2.5 1.171875 -3.796875q1.1875 -1.3125 2.984375 -1.3125q1.65625 0 2.859375 1.375l0 -4.890625l2.609375 0l0 13.59375zm-6.96875 -5.140625q0 1.578125 0.4375 2.28125q0.640625 1.015625 1.765625 1.015625q0.90625 0 1.53125 -0.765625q0.625 -0.765625 0.625 -2.28125q0 -1.703125 -0.609375 -2.4375q-0.609375 -0.75 -1.5625 -0.75q-0.9375 0 -1.5625 0.734375q-0.625 0.734375 -0.625 2.203125zm14.391785 5.140625l0 -13.59375l2.609375 0l0 4.890625q1.203125 -1.375 2.859375 -1.375q1.796875 0 2.96875 1.3125q1.1875 1.296875 1.1875 3.734375q0 2.53125 -1.203125 3.890625q-1.203125 1.359375 -2.921875 1.359375q-0.84375 0 -1.671875 -0.421875q-0.8125 -0.421875 -1.40625 -1.25l0 1.453125l-2.421875 0zm2.59375 -5.140625q0 1.53125 0.484375 2.265625q0.671875 1.03125 1.796875 1.03125q0.859375 0 1.46875 -0.734375q0.609375 -0.734375 0.609375 -2.328125q0 -1.6875 -0.609375 -2.421875q-0.609375 -0.75 -1.578125 -0.75q-0.9375 0 -1.5625 0.734375q-0.609375 0.71875 -0.609375 2.203125zm7.677246 -4.71875l2.78125 0l2.359375 7.0l2.296875 -7.0l2.703125 0l-3.484375 9.484375l-0.625 1.71875q-0.34375 0.859375 -0.65625 1.3125q-0.296875 0.46875 -0.703125 0.75q-0.40625 0.28125 -1.0 0.4375q-0.59375 0.15625 -1.328125 0.15625q-0.75 0 -1.46875 -0.15625l-0.234375 -2.046875q0.609375 0.125 1.09375 0.125q0.921875 0 1.34375 -0.53125q0.4375 -0.53125 0.671875 -1.359375l-3.75 -9.890625zm16.793396 -3.734375l2.75 0l0 7.359375q0 1.75 0.109375 2.265625q0.171875 0.84375 0.828125 1.359375q0.671875 0.5 1.8125 0.5q1.171875 0 1.765625 -0.484375q0.59375 -0.484375 0.71875 -1.171875q0.125 -0.703125 0.125 -2.3125l0 -7.515625l2.734375 0l0 7.140625q0 2.4375 -0.21875 3.453125q-0.21875 1.015625 -0.828125 1.71875q-0.59375 0.6875 -1.59375 1.109375q-1.0 0.40625 -2.609375 0.40625q-1.953125 0 -2.96875 -0.453125q-1.0 -0.453125 -1.59375 -1.171875q-0.578125 -0.71875 -0.75 -1.5q-0.28125 -1.171875 -0.28125 -3.453125l0 -7.25zm19.5979 13.59375l-2.609375 0l0 -9.828125q-1.4375 1.34375 -3.375 1.984375l0 -2.375q1.03125 -0.328125 2.21875 -1.25q1.203125 -0.9375 1.640625 -2.1875l2.125 0l0 13.65625z" fill-rule="nonzero"></path><path fill="#76a5af" d="m167.81102 247.17343l0 0c0 -5.809967 4.709915 -10.519882 10.519897 -10.519882l116.660995 0c2.790039 0 5.4658203 1.1083374 7.43869 3.0812073c1.9728699 1.9728546 3.0812073 4.648636 3.0812073 7.438675l0 42.07834c0 5.809967 -4.7099304 10.519897 -10.519897 10.519897l-116.660995 0c-5.8099823 0 -10.519897 -4.7099304 -10.519897 -10.519897z" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m167.81102 247.17343l0 0c0 -5.809967 4.709915 -10.519882 10.519897 -10.519882l116.660995 0c2.790039 0 5.4658203 1.1083374 7.43869 3.0812073c1.9728699 1.9728546 3.0812073 4.648636 3.0812073 7.438675l0 42.07834c0 5.809967 -4.7099304 10.519897 -10.519897 10.519897l-116.660995 0c-5.8099823 0 -10.519897 -4.7099304 -10.519897 -10.519897z" fill-rule="nonzero"></path><path fill="#000000" d="m226.62677 275.1326l0 -13.59375l4.421875 0q2.5 0 3.265625 0.203125q1.15625 0.296875 1.9375 1.328125q0.796875 1.015625 0.796875 2.640625q0 1.25 -0.453125 2.109375q-0.453125 0.859375 -1.15625 1.34375q-0.703125 0.484375 -1.421875 0.640625q-0.984375 0.203125 -2.84375 0.203125l-1.796875 0l0 5.125l-2.75 0zm2.75 -11.296875l0 3.859375l1.5 0q1.625 0 2.171875 -0.21875q0.546875 -0.21875 0.859375 -0.671875q0.3125 -0.453125 0.3125 -1.046875q0 -0.75 -0.4375 -1.234375q-0.4375 -0.484375 -1.09375 -0.59375q-0.5 -0.09375 -1.984375 -0.09375l-1.328125 0zm15.802948 11.296875l-2.609375 0l0 -9.828125q-1.4375 1.34375 -3.375 1.984375l0 -2.375q1.03125 -0.328125 2.21875 -1.25q1.203125 -0.9375 1.640625 -2.1875l2.125 0l0 13.65625z" fill-rule="nonzero"></path><path fill="#f3a7eb" fill-opacity="0.5269" d="m322.10236 83.18898l0 0c0 -19.803978 25.586761 -35.858273 57.149628 -35.858273l0 0c31.562836 0 57.149597 16.054295 57.149597 35.858273l0 0c0 19.80397 -25.586761 35.85826 -57.149597 35.85826l0 0c-31.562866 0 -57.149628 -16.05429 -57.149628 -35.85826z" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m322.10236 83.18898l0 0c0 -19.803978 25.586761 -35.858273 57.149628 -35.858273l0 0c31.562836 0 57.149597 16.054295 57.149597 35.858273l0 0c0 19.80397 -25.586761 35.85826 -57.149597 35.85826l0 0c-31.562866 0 -57.149628 -16.05429 -57.149628 -35.85826z" fill-rule="nonzero"></path><path fill="#000000" d="m368.687 76.51523l2.75 0l0 7.359375q0 1.75 0.109375 2.265625q0.171875 0.84375 0.828125 1.359375q0.671875 0.5 1.8125 0.5q1.171875 0 1.765625 -0.484375q0.59375 -0.484375 0.71875 -1.171875q0.125 -0.703125 0.125 -2.3125l0 -7.515625l2.734375 0l0 7.140625q0 2.4375 -0.21875 3.453125q-0.21875 1.015625 -0.828125 1.71875q-0.59375 0.6875 -1.59375 1.109375q-1.0 0.40625 -2.609375 0.40625q-1.953125 0 -2.96875 -0.453125q-1.0 -0.453125 -1.59375 -1.171875q-0.578125 -0.71875 -0.75 -1.5q-0.28125 -1.171875 -0.28125 -3.453125l0 -7.25zm19.59793 13.59375l-2.609375 0l0 -9.828125q-1.4375 1.34375 -3.375 1.984375l0 -2.375q1.03125 -0.328125 2.21875 -1.25q1.203125 -0.9375 1.640625 -2.1875l2.125 0l0 13.65625z" fill-rule="nonzero"></path><path fill="#f3a7eb" fill-opacity="0.5255" d="m730.10236 115.18898l0 0c0 -15.385696 20.7359 -27.858269 46.31494 -27.858269l0 0c25.579102 0 46.315002 12.472572 46.315002 27.858269l0 0c0 15.385696 -20.7359 27.858261 -46.315002 27.858261l0 0c-25.57904 0 -46.31494 -12.472565 -46.31494 -27.858261z" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m730.10236 115.18898l0 0c0 -15.385696 20.7359 -27.858269 46.31494 -27.858269l0 0c25.579102 0 46.315002 12.472572 46.315002 27.858269l0 0c0 15.385696 -20.7359 27.858261 -46.315002 27.858261l0 0c-25.57904 0 -46.31494 -12.472565 -46.31494 -27.858261z" fill-rule="nonzero"></path><path fill="#000000" d="m765.85236 108.51522l2.75 0l0 7.359375q0 1.75 0.109375 2.265625q0.171875 0.84375 0.828125 1.359375q0.671875 0.5 1.8125 0.5q1.171875 0 1.765625 -0.484375q0.59375 -0.484375 0.71875 -1.171875q0.125 -0.703125 0.125 -2.3125l0 -7.515625l2.734375 0l0 7.140625q0 2.4375 -0.21875 3.453125q-0.21875 1.015625 -0.828125 1.71875q-0.59375 0.6875 -1.59375 1.109375q-1.0 0.40625 -2.609375 0.40625q-1.953125 0 -2.96875 -0.453125q-1.0 -0.453125 -1.59375 -1.171875q-0.578125 -0.71875 -0.75 -1.5q-0.28125 -1.171875 -0.28125 -3.453125l0 -7.25zm21.722961 11.171875l0 2.421875l-9.140625 0q0.15625 -1.375 0.890625 -2.59375q0.75 -1.234375 2.9375 -3.265625q1.765625 -1.640625 2.15625 -2.234375q0.546875 -0.796875 0.546875 -1.59375q0 -0.875 -0.46875 -1.34375q-0.46875 -0.46875 -1.296875 -0.46875q-0.8125 0 -1.296875 0.5q-0.484375 0.484375 -0.5625 1.625l-2.59375 -0.25q0.234375 -2.15625 1.453125 -3.09375q1.21875 -0.9375 3.0625 -0.9375q2.015625 0 3.15625 1.09375q1.15625 1.078125 1.15625 2.6875q0 0.921875 -0.328125 1.75q-0.328125 0.828125 -1.046875 1.734375q-0.46875 0.609375 -1.703125 1.75q-1.234375 1.125 -1.5625 1.5q-0.328125 0.359375 -0.53125 0.71875l5.171875 0z" fill-rule="nonzero"></path><path fill="#f3a7eb" fill-opacity="0.5255" d="m738.10236 319.28348l0 0c0 -13.22876 17.880432 -23.952759 39.93701 -23.952759l0 0c22.05658 0 39.93701 10.723999 39.93701 23.952759l0 0c0 13.228729 -17.880432 23.952728 -39.93701 23.952728l0 0c-22.05658 0 -39.93701 -10.723999 -39.93701 -23.952728z" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m738.10236 319.28348l0 0c0 -13.22876 17.880432 -23.952759 39.93701 -23.952759l0 0c22.05658 0 39.93701 10.723999 39.93701 23.952759l0 0c0 13.228729 -17.880432 23.952728 -39.93701 23.952728l0 0c-22.05658 0 -39.93701 -10.723999 -39.93701 -23.952728z" fill-rule="nonzero"></path><path fill="#000000" d="m767.4744 312.6097l2.75 0l0 7.359375q0 1.75 0.109375 2.265625q0.171875 0.84375 0.828125 1.359375q0.671875 0.5 1.8125 0.5q1.171875 0 1.765625 -0.484375q0.59375 -0.484375 0.71875 -1.171875q0.125 -0.703125 0.125 -2.3125l0 -7.515625l2.734375 0l0 7.140625q0 2.4375 -0.21875 3.453125q-0.21875 1.015625 -0.828125 1.71875q-0.59375 0.6875 -1.59375 1.109375q-1.0 0.40625 -2.609375 0.40625q-1.953125 0 -2.96875 -0.453125q-1.0 -0.453125 -1.59375 -1.171875q-0.578125 -0.71875 -0.75 -1.5q-0.28125 -1.171875 -0.28125 -3.453125l0 -7.25zm12.832336 9.984375l2.515625 -0.3125q0.125 0.96875 0.65625 1.484375q0.53125 0.5 1.28125 0.5q0.796875 0 1.34375 -0.609375q0.5625 -0.609375 0.5625 -1.640625q0 -0.984375 -0.53125 -1.5625q-0.53125 -0.578125 -1.28125 -0.578125q-0.5 0 -1.203125 0.203125l0.28125 -2.125q1.0625 0.015625 1.609375 -0.46875q0.5625 -0.484375 0.5625 -1.296875q0 -0.6875 -0.40625 -1.09375q-0.40625 -0.40625 -1.078125 -0.40625q-0.671875 0 -1.140625 0.46875q-0.46875 0.46875 -0.578125 1.359375l-2.40625 -0.421875q0.25 -1.234375 0.75 -1.96875q0.515625 -0.734375 1.421875 -1.15625q0.90625 -0.421875 2.03125 -0.421875q1.90625 0 3.078125 1.21875q0.953125 1.0 0.953125 2.265625q0 1.796875 -1.953125 2.859375q1.15625 0.25 1.859375 1.125q0.703125 0.875 0.703125 2.109375q0 1.78125 -1.3125 3.046875q-1.296875 1.265625 -3.25 1.265625q-1.84375 0 -3.0625 -1.0625q-1.21875 -1.0625 -1.40625 -2.78125z" fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0" d="m379.25198 119.04724l-142.58269 117.60631" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m379.25198 119.04724l-137.95406 113.78847" fill-rule="evenodd"></path><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m240.24692 231.56151l-2.4498596 4.1618195l4.5518646 -1.6134033z" fill-rule="evenodd"></path><path fill="#76a5af" d="m55.811024 383.17343l0 0c0 -5.809967 4.709919 -10.519897 10.519894 -10.519897l116.660995 0c2.7900543 0 5.4658356 1.1083374 7.43869 3.0812073c1.9728699 1.9728699 3.0812073 4.648651 3.0812073 7.43869l0 42.07834c0 5.809967 -4.709915 10.519897 -10.519897 10.519897l-116.660995 0c-5.8099747 0 -10.519894 -4.7099304 -10.519894 -10.519897z" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m55.811024 383.17343l0 0c0 -5.809967 4.709919 -10.519897 10.519894 -10.519897l116.660995 0c2.7900543 0 5.4658356 1.1083374 7.43869 3.0812073c1.9728699 1.9728699 3.0812073 4.648651 3.0812073 7.43869l0 42.07834c0 5.809967 -4.709915 10.519897 -10.519897 10.519897l-116.660995 0c-5.8099747 0 -10.519894 -4.7099304 -10.519894 -10.519897z" fill-rule="nonzero"></path><path fill="#000000" d="m114.62677 411.1326l0 -13.59375l4.421875 0q2.5 0 3.265625 0.203125q1.15625 0.296875 1.9375 1.328125q0.796875 1.015625 0.796875 2.640625q0 1.25 -0.453125 2.109375q-0.453125 0.859375 -1.15625 1.34375q-0.703125 0.484375 -1.421875 0.640625q-0.984375 0.203125 -2.84375 0.203125l-1.796875 0l0 5.125l-2.75 0zm2.75 -11.296875l0 3.859375l1.5 0q1.625 0 2.171875 -0.21875q0.546875 -0.21875 0.859375 -0.671875q0.3125 -0.453125 0.3125 -1.046875q0 -0.75 -0.4375 -1.234375q-0.4375 -0.484375 -1.09375 -0.59375q-0.5 -0.09375 -1.984375 -0.09375l-1.328125 0zm17.927948 8.875l0 2.421875l-9.140625 0q0.15625 -1.375 0.890625 -2.59375q0.75 -1.234375 2.9375 -3.265625q1.765625 -1.640625 2.15625 -2.234375q0.546875 -0.796875 0.546875 -1.59375q0 -0.875 -0.46875 -1.34375q-0.46875 -0.46875 -1.296875 -0.46875q-0.8125 0 -1.296875 0.5q-0.484375 0.484375 -0.5625 1.625l-2.59375 -0.25q0.234375 -2.15625 1.453125 -3.09375q1.21875 -0.9375 3.0625 -0.9375q2.015625 0 3.15625 1.09375q1.15625 1.078125 1.15625 2.6875q0 0.921875 -0.328125 1.75q-0.328125 0.828125 -1.046875 1.734375q-0.46875 0.609375 -1.703125 1.75q-1.234375 1.125 -1.5625 1.5q-0.328125 0.359375 -0.53125 0.71875l5.171875 0z" fill-rule="nonzero"></path><path fill="#76a5af" d="m447.81104 247.17343l0 0c0 -5.809967 4.7099 -10.519882 10.519897 -10.519882l116.66101 0c2.790039 0 5.4658203 1.1083374 7.4386597 3.0812073c1.9728394 1.9728546 3.0812378 4.648636 3.0812378 7.438675l0 42.07834c0 5.809967 -4.709961 10.519897 -10.519897 10.519897l-116.66101 0c-5.8099976 0 -10.519897 -4.7099304 -10.519897 -10.519897z" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m447.81104 247.17343l0 0c0 -5.809967 4.7099 -10.519882 10.519897 -10.519882l116.66101 0c2.790039 0 5.4658203 1.1083374 7.4386597 3.0812073c1.9728394 1.9728546 3.0812378 4.648636 3.0812378 7.438675l0 42.07834c0 5.809967 -4.709961 10.519897 -10.519897 10.519897l-116.66101 0c-5.8099976 0 -10.519897 -4.7099304 -10.519897 -10.519897z" fill-rule="nonzero"></path><path fill="#000000" d="m506.62677 275.1326l0 -13.59375l4.421875 0q2.5 0 3.265625 0.203125q1.15625 0.296875 1.9375 1.328125q0.796875 1.015625 0.796875 2.640625q0 1.25 -0.453125 2.109375q-0.453125 0.859375 -1.15625 1.34375q-0.703125 0.484375 -1.421875 0.640625q-0.984375 0.203125 -2.84375 0.203125l-1.796875 0l0 5.125l-2.75 0zm2.75 -11.296875l0 3.859375l1.5 0q1.625 0 2.171875 -0.21875q0.546875 -0.21875 0.859375 -0.671875q0.3125 -0.453125 0.3125 -1.046875q0 -0.75 -0.4375 -1.234375q-0.4375 -0.484375 -1.09375 -0.59375q-0.5 -0.09375 -1.984375 -0.09375l-1.328125 0zm14.2404175 11.296875l0 -2.734375l-5.5625 0l0 -2.28125l5.890625 -8.640625l2.1875 0l0 8.625l1.6875 0l0 2.296875l-1.6875 0l0 2.734375l-2.515625 0zm0 -5.03125l0 -4.640625l-3.125 4.640625l3.125 0z" fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0" d="m730.10236 115.18898l-422.96063 128.06299" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="4.0,3.0,1.0,3.0" d="m730.10236 115.18898l-417.21808 126.324265" fill-rule="evenodd"></path><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m312.40564 239.93239l-3.864746 2.895935l4.8220215 0.26579285z" fill-rule="evenodd"></path><path fill="#000000" fill-opacity="0.0" d="m738.10236 319.28348l-152.59839 -51.08664" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="4.0,3.0,1.0,3.0" d="m738.10236 319.28348l-146.90881 -49.181885" fill-rule="evenodd"></path><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m591.7179 268.5353l-4.8276978 0.12564087l3.7789917 3.006958z" fill-rule="evenodd"></path><path fill="#000000" fill-opacity="0.0" d="m379.25198 119.04724l137.41733 117.60631" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m379.25198 119.04724l132.85886 113.70499" fill-rule="evenodd"></path><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m511.0368 234.00714l4.5217896 1.6958466l-2.3737793 -4.2056427z" fill-rule="evenodd"></path><path fill="#000000" fill-opacity="0.0" d="m236.66142 299.77167l-112.00001 72.88187" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m236.66142 299.77167l-106.97102 69.609375" fill-rule="evenodd"></path><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m128.7895 367.9966l-2.902771 3.8595886l4.704544 -1.0907593z" fill-rule="evenodd"></path><path fill="#000000" fill-opacity="0.0" d="m236.66142 299.77167l119.999985 72.88187" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m236.66142 299.77167l114.87175 69.76724" fill-rule="evenodd"></path><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m350.67575 370.95065l4.7361755 0.94400024l-3.0213318 -3.767517z" fill-rule="evenodd"></path><path fill="#000000" fill-opacity="0.0" d="m749.7996 336.2206l-556.28345 68.0" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="4.0,3.0,1.0,3.0" d="m749.7996 336.2206l-550.32776 67.272" fill-rule="evenodd"></path><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m199.27144 401.8531l-4.3041534 2.190155l4.7049713 1.0888977z" fill-rule="evenodd"></path><path fill="#000000" fill-opacity="0.0" d="m559.7454 366.88452l114.299194 0l0 51.08661l-114.299194 0z" fill-rule="nonzero"></path><path fill="#000000" d="m571.3079 382.492l0 1.9375l-1.796875 0l0 -1.53125q0 -1.25 0.296875 -1.796875q0.390625 -0.75 1.234375 -1.125l0.40625 0.640625q-0.5 0.21875 -0.75 0.640625q-0.234375 0.421875 -0.265625 1.234375l0.875 0zm2.875 0l0 1.9375l-1.796875 0l0 -1.53125q0 -1.25 0.296875 -1.796875q0.390625 -0.75 1.234375 -1.125l0.40625 0.640625q-0.5 0.21875 -0.75 0.640625q-0.234375 0.421875 -0.265625 1.234375l0.875 0zm8.4626465 7.703125l1.640625 0.21875q-0.265625 1.6875 -1.375 2.65625q-1.109375 0.953125 -2.734375 0.953125q-2.015625 0 -3.25 -1.3125q-1.21875 -1.328125 -1.21875 -3.796875q0 -1.59375 0.515625 -2.78125q0.53125 -1.203125 1.609375 -1.796875q1.09375 -0.609375 2.359375 -0.609375q1.609375 0 2.625 0.8125q1.015625 0.8125 1.3125 2.3125l-1.625 0.25q-0.234375 -1.0 -0.828125 -1.5q-0.59375 -0.5 -1.421875 -0.5q-1.265625 0 -2.0625 0.90625q-0.78125 0.90625 -0.78125 2.859375q0 1.984375 0.765625 2.890625q0.765625 0.890625 1.984375 0.890625q0.984375 0 1.640625 -0.59375q0.65625 -0.609375 0.84375 -1.859375zm9.328125 2.390625q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm4.0788574 4.9375l0 -9.859375l1.5 0l0 1.40625q1.09375 -1.625 3.140625 -1.625q0.890625 0 1.640625 0.328125q0.75 0.3125 1.109375 0.84375q0.375 0.515625 0.53125 1.21875q0.09375 0.46875 0.09375 1.625l0 6.0625l-1.671875 0l0 -6.0q0 -1.015625 -0.203125 -1.515625q-0.1875 -0.515625 -0.6875 -0.8125q-0.5 -0.296875 -1.171875 -0.296875q-1.0625 0 -1.84375 0.671875q-0.765625 0.671875 -0.765625 2.578125l0 5.375l-1.671875 0zm8.844482 3.78125l0 -1.21875l11.0625 0l0 1.21875l-11.0625 0zm11.891296 -3.78125l0 -9.859375l1.5 0l0 1.5q0.578125 -1.046875 1.0625 -1.375q0.484375 -0.34375 1.078125 -0.34375q0.84375 0 1.71875 0.546875l-0.578125 1.546875q-0.609375 -0.359375 -1.234375 -0.359375q-0.546875 0 -0.984375 0.328125q-0.421875 0.328125 -0.609375 0.90625q-0.28125 0.890625 -0.28125 1.953125l0 5.15625l-1.671875 0zm12.9783325 -3.171875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm15.547607 4.65625q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm10.469421 4.9375l0 -1.25q-0.9375 1.46875 -2.75 1.46875q-1.171875 0 -2.171875 -0.640625q-0.984375 -0.65625 -1.53125 -1.8125q-0.53125 -1.171875 -0.53125 -2.6875q0 -1.46875 0.484375 -2.671875q0.5 -1.203125 1.46875 -1.84375q0.984375 -0.640625 2.203125 -0.640625q0.890625 0 1.578125 0.375q0.703125 0.375 1.140625 0.984375l0 -4.875l1.65625 0l0 13.59375l-1.546875 0zm-5.28125 -4.921875q0 1.890625 0.796875 2.828125q0.8125 0.9375 1.890625 0.9375q1.09375 0 1.859375 -0.890625q0.765625 -0.890625 0.765625 -2.734375q0 -2.015625 -0.78125 -2.953125q-0.78125 -0.953125 -1.921875 -0.953125q-1.109375 0 -1.859375 0.90625q-0.75 0.90625 -0.75 2.859375zm8.828857 -6.875l0 -1.9375l1.78125 0l0 1.53125q0 1.234375 -0.28125 1.78125q-0.40625 0.75 -1.25 1.140625l-0.40625 -0.671875q0.5 -0.203125 0.75 -0.640625q0.25 -0.4375 0.265625 -1.203125l-0.859375 0zm2.875 0l0 -1.9375l1.78125 0l0 1.53125q0 1.234375 -0.28125 1.78125q-0.40625 0.75 -1.25 1.140625l-0.40625 -0.671875q0.5 -0.203125 0.75 -0.640625q0.25 -0.4375 0.28125 -1.203125l-0.875 0z" fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0" d="m302.40683 310.40683l152.59842 0l0 41.95276l-152.59842 0z" fill-rule="nonzero"></path><path fill="#a61c00" d="m321.48495 332.3268l2.671875 0.84375q-0.609375 2.21875 -2.046875 3.3125q-1.421875 1.078125 -3.609375 1.078125q-2.703125 0 -4.453125 -1.84375q-1.734375 -1.859375 -1.734375 -5.078125q0 -3.390625 1.75 -5.265625q1.75 -1.875 4.609375 -1.875q2.5 0 4.046875 1.46875q0.9375 0.875 1.390625 2.5l-2.71875 0.65625q-0.234375 -1.0625 -1.0 -1.671875q-0.765625 -0.609375 -1.859375 -0.609375q-1.515625 0 -2.453125 1.09375q-0.9375 1.078125 -0.9375 3.5q0 2.578125 0.921875 3.6875q0.921875 1.09375 2.40625 1.09375q1.109375 0 1.890625 -0.6875q0.78125 -0.703125 1.125 -2.203125zm7.254181 5.0l-2.609375 0l0 -9.859375l2.421875 0l0 1.40625q0.625 -0.984375 1.109375 -1.296875q0.5 -0.328125 1.140625 -0.328125q0.890625 0 1.71875 0.5l-0.8125 2.265625q-0.65625 -0.421875 -1.21875 -0.421875q-0.546875 0 -0.9375 0.296875q-0.375 0.296875 -0.59375 1.09375q-0.21875 0.78125 -0.21875 3.296875l0 3.046875zm10.463409 -3.140625l2.609375 0.4375q-0.5 1.4375 -1.59375 2.1875q-1.078125 0.734375 -2.703125 0.734375q-2.5625 0 -3.796875 -1.671875q-0.96875 -1.34375 -0.96875 -3.40625q0 -2.4375 1.265625 -3.828125q1.28125 -1.390625 3.25 -1.390625q2.1875 0 3.453125 1.453125q1.28125 1.453125 1.234375 4.453125l-6.53125 0q0.015625 1.15625 0.625 1.8125q0.609375 0.640625 1.5 0.640625q0.609375 0 1.03125 -0.328125q0.421875 -0.34375 0.625 -1.09375zm0.15625 -2.625q-0.03125 -1.140625 -0.59375 -1.71875q-0.546875 -0.59375 -1.34375 -0.59375q-0.859375 0 -1.40625 0.625q-0.5625 0.609375 -0.546875 1.6875l3.890625 0zm6.469452 -1.078125l-2.359375 -0.4375q0.390625 -1.421875 1.359375 -2.109375q0.984375 -0.6875 2.90625 -0.6875q1.734375 0 2.59375 0.421875q0.859375 0.40625 1.203125 1.046875q0.34375 0.625 0.34375 2.328125l-0.03125 3.046875q0 1.296875 0.125 1.921875q0.125 0.609375 0.46875 1.3125l-2.578125 0q-0.09375 -0.265625 -0.25 -0.765625q-0.0625 -0.234375 -0.09375 -0.3125q-0.65625 0.65625 -1.421875 0.984375q-0.765625 0.3125 -1.625 0.3125q-1.515625 0 -2.40625 -0.8125q-0.875 -0.828125 -0.875 -2.09375q0 -0.84375 0.390625 -1.484375q0.40625 -0.65625 1.125 -1.0q0.71875 -0.359375 2.078125 -0.625q1.828125 -0.328125 2.53125 -0.625l0 -0.265625q0 -0.75 -0.375 -1.0625q-0.359375 -0.328125 -1.390625 -0.328125q-0.703125 0 -1.09375 0.28125q-0.390625 0.265625 -0.625 0.953125zm3.484375 2.109375q-0.5 0.171875 -1.59375 0.40625q-1.078125 0.234375 -1.40625 0.453125q-0.515625 0.359375 -0.515625 0.921875q0 0.546875 0.40625 0.953125q0.40625 0.390625 1.046875 0.390625q0.703125 0 1.34375 -0.46875q0.46875 -0.359375 0.625 -0.859375q0.09375 -0.34375 0.09375 -1.28125l0 -0.515625zm9.453857 -5.125l0 2.078125l-1.78125 0l0 3.984375q0 1.203125 0.046875 1.40625q0.0625 0.1875 0.234375 0.328125q0.1875 0.125 0.453125 0.125q0.359375 0 1.046875 -0.25l0.21875 2.015625q-0.90625 0.390625 -2.0625 0.390625q-0.703125 0 -1.265625 -0.234375q-0.5625 -0.234375 -0.828125 -0.609375q-0.265625 -0.375 -0.375 -1.015625q-0.078125 -0.453125 -0.078125 -1.84375l0 -4.296875l-1.203125 0l0 -2.078125l1.203125 0l0 -1.953125l2.609375 -1.515625l0 3.46875l1.78125 0zm7.400177 6.71875l2.609375 0.4375q-0.5 1.4375 -1.59375 2.1875q-1.078125 0.734375 -2.703125 0.734375q-2.5625 0 -3.796875 -1.671875q-0.96875 -1.34375 -0.96875 -3.40625q0 -2.4375 1.265625 -3.828125q1.28125 -1.390625 3.25 -1.390625q2.1875 0 3.453125 1.453125q1.28125 1.453125 1.234375 4.453125l-6.53125 0q0.015625 1.15625 0.625 1.8125q0.609375 0.640625 1.5 0.640625q0.609375 0 1.03125 -0.328125q0.421875 -0.34375 0.625 -1.09375zm0.15625 -2.625q-0.03125 -1.140625 -0.59375 -1.71875q-0.546875 -0.59375 -1.34375 -0.59375q-0.859375 0 -1.40625 0.625q-0.5625 0.609375 -0.546875 1.6875l3.890625 0zm13.563202 5.765625l-2.421875 0l0 -1.453125q-0.609375 0.84375 -1.4375 1.265625q-0.8125 0.40625 -1.640625 0.40625q-1.703125 0 -2.921875 -1.359375q-1.203125 -1.375 -1.203125 -3.828125q0 -2.5 1.171875 -3.796875q1.1875 -1.3125 2.984375 -1.3125q1.65625 0 2.859375 1.375l0 -4.890625l2.609375 0l0 13.59375zm-6.96875 -5.140625q0 1.578125 0.4375 2.28125q0.640625 1.015625 1.765625 1.015625q0.90625 0 1.53125 -0.765625q0.625 -0.765625 0.625 -2.28125q0 -1.703125 -0.609375 -2.4375q-0.609375 -0.75 -1.5625 -0.75q-0.9375 0 -1.5625 0.734375q-0.625 0.734375 -0.625 2.203125zm14.391785 5.140625l0 -13.59375l2.609375 0l0 4.890625q1.203125 -1.375 2.859375 -1.375q1.796875 0 2.96875 1.3125q1.1875 1.296875 1.1875 3.734375q0 2.53125 -1.203125 3.890625q-1.203125 1.359375 -2.921875 1.359375q-0.84375 0 -1.671875 -0.421875q-0.8125 -0.421875 -1.40625 -1.25l0 1.453125l-2.421875 0zm2.59375 -5.140625q0 1.53125 0.484375 2.265625q0.671875 1.03125 1.796875 1.03125q0.859375 0 1.46875 -0.734375q0.609375 -0.734375 0.609375 -2.328125q0 -1.6875 -0.609375 -2.421875q-0.609375 -0.75 -1.578125 -0.75q-0.9375 0 -1.5625 0.734375q-0.609375 0.71875 -0.609375 2.203125zm7.677246 -4.71875l2.78125 0l2.359375 7.0l2.296875 -7.0l2.703125 0l-3.484375 9.484375l-0.625 1.71875q-0.34375 0.859375 -0.65625 1.3125q-0.296875 0.46875 -0.703125 0.75q-0.40625 0.28125 -1.0 0.4375q-0.59375 0.15625 -1.328125 0.15625q-0.75 0 -1.46875 -0.15625l-0.234375 -2.046875q0.609375 0.125 1.09375 0.125q0.921875 0 1.34375 -0.53125q0.4375 -0.53125 0.671875 -1.359375l-3.75 -9.890625zm16.793396 -3.734375l2.75 0l0 7.359375q0 1.75 0.109375 2.265625q0.171875 0.84375 0.828125 1.359375q0.671875 0.5 1.8125 0.5q1.171875 0 1.765625 -0.484375q0.59375 -0.484375 0.71875 -1.171875q0.125 -0.703125 0.125 -2.3125l0 -7.515625l2.734375 0l0 7.140625q0 2.4375 -0.21875 3.453125q-0.21875 1.015625 -0.828125 1.71875q-0.59375 0.6875 -1.59375 1.109375q-1.0 0.40625 -2.609375 0.40625q-1.953125 0 -2.96875 -0.453125q-1.0 -0.453125 -1.59375 -1.171875q-0.578125 -0.71875 -0.75 -1.5q-0.28125 -1.171875 -0.28125 -3.453125l0 -7.25zm21.72293 11.171875l0 2.421875l-9.140625 0q0.15625 -1.375 0.890625 -2.59375q0.75 -1.234375 2.9375 -3.265625q1.765625 -1.640625 2.15625 -2.234375q0.546875 -0.796875 0.546875 -1.59375q0 -0.875 -0.46875 -1.34375q-0.46875 -0.46875 -1.296875 -0.46875q-0.8125 0 -1.296875 0.5q-0.484375 0.484375 -0.5625 1.625l-2.59375 -0.25q0.234375 -2.15625 1.453125 -3.09375q1.21875 -0.9375 3.0625 -0.9375q2.015625 0 3.15625 1.09375q1.15625 1.078125 1.15625 2.6875q0 0.921875 -0.328125 1.75q-0.328125 0.828125 -1.046875 1.734375q-0.46875 0.609375 -1.703125 1.75q-1.234375 1.125 -1.5625 1.5q-0.328125 0.359375 -0.53125 0.71875l5.171875 0z" fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0" d="m546.8373 393.3071l142.58264 0l0 41.95276l-142.58264 0z" fill-rule="nonzero"></path><path fill="#a61c00" d="m556.5248 415.8052l2.671875 -0.265625q0.234375 1.34375 0.96875 1.984375q0.75 0.625 2.0 0.625q1.328125 0 2.0 -0.5625q0.671875 -0.5625 0.671875 -1.3125q0 -0.484375 -0.28125 -0.8125q-0.28125 -0.34375 -0.984375 -0.59375q-0.484375 -0.171875 -2.203125 -0.59375q-2.203125 -0.546875 -3.09375 -1.34375q-1.265625 -1.125 -1.265625 -2.734375q0 -1.046875 0.59375 -1.953125q0.59375 -0.90625 1.703125 -1.375q1.109375 -0.46875 2.671875 -0.46875q2.5625 0 3.859375 1.125q1.296875 1.109375 1.359375 2.984375l-2.75 0.125q-0.171875 -1.046875 -0.75 -1.5q-0.578125 -0.46875 -1.75 -0.46875q-1.1875 0 -1.875 0.5q-0.4375 0.3125 -0.4375 0.84375q0 0.484375 0.421875 0.828125q0.515625 0.421875 2.515625 0.90625q2.0 0.46875 2.953125 0.984375q0.96875 0.5 1.515625 1.375q0.546875 0.875 0.546875 2.15625q0 1.171875 -0.65625 2.203125q-0.640625 1.015625 -1.828125 1.515625q-1.1875 0.484375 -2.96875 0.484375q-2.578125 0 -3.96875 -1.1875q-1.375 -1.1875 -1.640625 -3.46875zm15.7247925 -9.171875l0 5.0q1.25 -1.484375 3.015625 -1.484375q0.890625 0 1.609375 0.34375q0.734375 0.328125 1.09375 0.84375q0.375 0.515625 0.5 1.15625q0.140625 0.625 0.140625 1.953125l0 5.78125l-2.609375 0l0 -5.203125q0 -1.546875 -0.15625 -1.96875q-0.140625 -0.421875 -0.515625 -0.65625q-0.375 -0.25 -0.9375 -0.25q-0.65625 0 -1.171875 0.3125q-0.5 0.3125 -0.734375 0.953125q-0.234375 0.640625 -0.234375 1.875l0 4.9375l-2.609375 0l0 -13.59375l2.609375 0zm10.739746 6.75l-2.359375 -0.4375q0.390625 -1.421875 1.359375 -2.109375q0.984375 -0.6875 2.90625 -0.6875q1.734375 0 2.59375 0.421875q0.859375 0.40625 1.203125 1.046875q0.34375 0.625 0.34375 2.328125l-0.03125 3.046875q0 1.296875 0.125 1.921875q0.125 0.609375 0.46875 1.3125l-2.578125 0q-0.09375 -0.265625 -0.25 -0.765625q-0.0625 -0.234375 -0.09375 -0.3125q-0.65625 0.65625 -1.421875 0.984375q-0.765625 0.3125 -1.625 0.3125q-1.515625 0 -2.40625 -0.8125q-0.875 -0.828125 -0.875 -2.09375q0 -0.84375 0.390625 -1.484375q0.40625 -0.65625 1.125 -1.0q0.71875 -0.359375 2.078125 -0.625q1.828125 -0.328125 2.53125 -0.625l0 -0.265625q0 -0.75 -0.375 -1.0625q-0.359375 -0.328125 -1.390625 -0.328125q-0.703125 0 -1.09375 0.28125q-0.390625 0.265625 -0.625 0.953125zm3.484375 2.109375q-0.5 0.171875 -1.59375 0.40625q-1.078125 0.234375 -1.40625 0.453125q-0.515625 0.359375 -0.515625 0.921875q0 0.546875 0.40625 0.953125q0.40625 0.390625 1.046875 0.390625q0.703125 0 1.34375 -0.46875q0.46875 -0.359375 0.625 -0.859375q0.09375 -0.34375 0.09375 -1.28125l0 -0.515625zm7.4382324 4.734375l-2.609375 0l0 -9.859375l2.421875 0l0 1.40625q0.625 -0.984375 1.109375 -1.296875q0.5 -0.328125 1.140625 -0.328125q0.890625 0 1.71875 0.5l-0.8125 2.265625q-0.65625 -0.421875 -1.21875 -0.421875q-0.546875 0 -0.9375 0.296875q-0.375 0.296875 -0.59375 1.09375q-0.21875 0.78125 -0.21875 3.296875l0 3.046875zm10.463379 -3.140625l2.609375 0.4375q-0.5 1.4375 -1.59375 2.1875q-1.078125 0.734375 -2.703125 0.734375q-2.5625 0 -3.796875 -1.671875q-0.96875 -1.34375 -0.96875 -3.40625q0 -2.4375 1.265625 -3.828125q1.28125 -1.390625 3.25 -1.390625q2.1875 0 3.453125 1.453125q1.28125 1.453125 1.234375 4.453125l-6.53125 0q0.015625 1.15625 0.625 1.8125q0.609375 0.640625 1.5 0.640625q0.609375 0 1.03125 -0.328125q0.421875 -0.34375 0.625 -1.09375zm0.15625 -2.625q-0.03125 -1.140625 -0.59375 -1.71875q-0.546875 -0.59375 -1.34375 -0.59375q-0.859375 0 -1.40625 0.625q-0.5625 0.609375 -0.546875 1.6875l3.890625 0zm13.563232 5.765625l-2.421875 0l0 -1.453125q-0.609375 0.84375 -1.4375 1.265625q-0.8125 0.40625 -1.640625 0.40625q-1.703125 0 -2.921875 -1.359375q-1.203125 -1.375 -1.203125 -3.828125q0 -2.5 1.171875 -3.796875q1.1875 -1.3125 2.984375 -1.3125q1.65625 0 2.859375 1.375l0 -4.890625l2.609375 0l0 13.59375zm-6.96875 -5.140625q0 1.578125 0.4375 2.28125q0.640625 1.015625 1.765625 1.015625q0.90625 0 1.53125 -0.765625q0.625 -0.765625 0.625 -2.28125q0 -1.703125 -0.609375 -2.4375q-0.609375 -0.75 -1.5625 -0.75q-0.9375 0 -1.5625 0.734375q-0.625 0.734375 -0.625 2.203125zm14.391785 5.140625l0 -13.59375l2.609375 0l0 4.890625q1.203125 -1.375 2.859375 -1.375q1.796875 0 2.96875 1.3125q1.1875 1.296875 1.1875 3.734375q0 2.53125 -1.203125 3.890625q-1.203125 1.359375 -2.921875 1.359375q-0.84375 0 -1.671875 -0.421875q-0.8125 -0.421875 -1.40625 -1.25l0 1.453125l-2.421875 0zm2.59375 -5.140625q0 1.53125 0.484375 2.265625q0.671875 1.03125 1.796875 1.03125q0.859375 0 1.46875 -0.734375q0.609375 -0.734375 0.609375 -2.328125q0 -1.6875 -0.609375 -2.421875q-0.609375 -0.75 -1.578125 -0.75q-0.9375 0 -1.5625 0.734375q-0.609375 0.71875 -0.609375 2.203125zm7.677246 -4.71875l2.78125 0l2.359375 7.0l2.296875 -7.0l2.703125 0l-3.484375 9.484375l-0.625 1.71875q-0.34375 0.859375 -0.65625 1.3125q-0.296875 0.46875 -0.703125 0.75q-0.40625 0.28125 -1.0 0.4375q-0.59375 0.15625 -1.328125 0.15625q-0.75 0 -1.46875 -0.15625l-0.234375 -2.046875q0.609375 0.125 1.09375 0.125q0.921875 0 1.34375 -0.53125q0.4375 -0.53125 0.671875 -1.359375l-3.75 -9.890625zm16.793396 -3.734375l2.75 0l0 7.359375q0 1.75 0.109375 2.265625q0.171875 0.84375 0.828125 1.359375q0.671875 0.5 1.8125 0.5q1.171875 0 1.765625 -0.484375q0.59375 -0.484375 0.71875 -1.171875q0.125 -0.703125 0.125 -2.3125l0 -7.515625l2.734375 0l0 7.140625q0 2.4375 -0.21875 3.453125q-0.21875 1.015625 -0.828125 1.71875q-0.59375 0.6875 -1.59375 1.109375q-1.0 0.40625 -2.609375 0.40625q-1.953125 0 -2.96875 -0.453125q-1.0 -0.453125 -1.59375 -1.171875q-0.578125 -0.71875 -0.75 -1.5q-0.28125 -1.171875 -0.28125 -3.453125l0 -7.25zm21.7229 11.171875l0 2.421875l-9.140625 0q0.15625 -1.375 0.890625 -2.59375q0.75 -1.234375 2.9375 -3.265625q1.765625 -1.640625 2.15625 -2.234375q0.546875 -0.796875 0.546875 -1.59375q0 -0.875 -0.46875 -1.34375q-0.46875 -0.46875 -1.296875 -0.46875q-0.8125 0 -1.296875 0.5q-0.484375 0.484375 -0.5625 1.625l-2.59375 -0.25q0.234375 -2.15625 1.453125 -3.09375q1.21875 -0.9375 3.0625 -0.9375q2.015625 0 3.15625 1.09375q1.15625 1.078125 1.15625 2.6875q0 0.921875 -0.328125 1.75q-0.328125 0.828125 -1.046875 1.734375q-0.46875 0.609375 -1.703125 1.75q-1.234375 1.125 -1.5625 1.5q-0.328125 0.359375 -0.53125 0.71875l5.171875 0z" fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0" d="m602.8373 289.3071l142.58264 0l0 41.95276l-142.58264 0z" fill-rule="nonzero"></path><path fill="#a61c00" d="m612.5248 311.8052l2.671875 -0.265625q0.234375 1.34375 0.96875 1.984375q0.75 0.625 2.0 0.625q1.328125 0 2.0 -0.5625q0.671875 -0.5625 0.671875 -1.3125q0 -0.484375 -0.28125 -0.8125q-0.28125 -0.34375 -0.984375 -0.59375q-0.484375 -0.171875 -2.203125 -0.59375q-2.203125 -0.546875 -3.09375 -1.34375q-1.265625 -1.125 -1.265625 -2.734375q0 -1.046875 0.59375 -1.953125q0.59375 -0.90625 1.703125 -1.375q1.109375 -0.46875 2.671875 -0.46875q2.5625 0 3.859375 1.125q1.296875 1.109375 1.359375 2.984375l-2.75 0.125q-0.171875 -1.046875 -0.75 -1.5q-0.578125 -0.46875 -1.75 -0.46875q-1.1875 0 -1.875 0.5q-0.4375 0.3125 -0.4375 0.84375q0 0.484375 0.421875 0.828125q0.515625 0.421875 2.515625 0.90625q2.0 0.46875 2.953125 0.984375q0.96875 0.5 1.515625 1.375q0.546875 0.875 0.546875 2.15625q0 1.171875 -0.65625 2.203125q-0.640625 1.015625 -1.828125 1.515625q-1.1875 0.484375 -2.96875 0.484375q-2.578125 0 -3.96875 -1.1875q-1.375 -1.1875 -1.640625 -3.46875zm15.7247925 -9.171875l0 5.0q1.25 -1.484375 3.015625 -1.484375q0.890625 0 1.609375 0.34375q0.734375 0.328125 1.09375 0.84375q0.375 0.515625 0.5 1.15625q0.140625 0.625 0.140625 1.953125l0 5.78125l-2.609375 0l0 -5.203125q0 -1.546875 -0.15625 -1.96875q-0.140625 -0.421875 -0.515625 -0.65625q-0.375 -0.25 -0.9375 -0.25q-0.65625 0 -1.171875 0.3125q-0.5 0.3125 -0.734375 0.953125q-0.234375 0.640625 -0.234375 1.875l0 4.9375l-2.609375 0l0 -13.59375l2.609375 0zm10.739746 6.75l-2.359375 -0.4375q0.390625 -1.421875 1.359375 -2.109375q0.984375 -0.6875 2.90625 -0.6875q1.734375 0 2.59375 0.421875q0.859375 0.40625 1.203125 1.046875q0.34375 0.625 0.34375 2.328125l-0.03125 3.046875q0 1.296875 0.125 1.921875q0.125 0.609375 0.46875 1.3125l-2.578125 0q-0.09375 -0.265625 -0.25 -0.765625q-0.0625 -0.234375 -0.09375 -0.3125q-0.65625 0.65625 -1.421875 0.984375q-0.765625 0.3125 -1.625 0.3125q-1.515625 0 -2.40625 -0.8125q-0.875 -0.828125 -0.875 -2.09375q0 -0.84375 0.390625 -1.484375q0.40625 -0.65625 1.125 -1.0q0.71875 -0.359375 2.078125 -0.625q1.828125 -0.328125 2.53125 -0.625l0 -0.265625q0 -0.75 -0.375 -1.0625q-0.359375 -0.328125 -1.390625 -0.328125q-0.703125 0 -1.09375 0.28125q-0.390625 0.265625 -0.625 0.953125zm3.484375 2.109375q-0.5 0.171875 -1.59375 0.40625q-1.078125 0.234375 -1.40625 0.453125q-0.515625 0.359375 -0.515625 0.921875q0 0.546875 0.40625 0.953125q0.40625 0.390625 1.046875 0.390625q0.703125 0 1.34375 -0.46875q0.46875 -0.359375 0.625 -0.859375q0.09375 -0.34375 0.09375 -1.28125l0 -0.515625zm7.4382324 4.734375l-2.609375 0l0 -9.859375l2.421875 0l0 1.40625q0.625 -0.984375 1.109375 -1.296875q0.5 -0.328125 1.140625 -0.328125q0.890625 0 1.71875 0.5l-0.8125 2.265625q-0.65625 -0.421875 -1.21875 -0.421875q-0.546875 0 -0.9375 0.296875q-0.375 0.296875 -0.59375 1.09375q-0.21875 0.78125 -0.21875 3.296875l0 3.046875zm10.463379 -3.140625l2.609375 0.4375q-0.5 1.4375 -1.59375 2.1875q-1.078125 0.734375 -2.703125 0.734375q-2.5625 0 -3.796875 -1.671875q-0.96875 -1.34375 -0.96875 -3.40625q0 -2.4375 1.265625 -3.828125q1.28125 -1.390625 3.25 -1.390625q2.1875 0 3.453125 1.453125q1.28125 1.453125 1.234375 4.453125l-6.53125 0q0.015625 1.15625 0.625 1.8125q0.609375 0.640625 1.5 0.640625q0.609375 0 1.03125 -0.328125q0.421875 -0.34375 0.625 -1.09375zm0.15625 -2.625q-0.03125 -1.140625 -0.59375 -1.71875q-0.546875 -0.59375 -1.34375 -0.59375q-0.859375 0 -1.40625 0.625q-0.5625 0.609375 -0.546875 1.6875l3.890625 0zm13.563232 5.765625l-2.421875 0l0 -1.453125q-0.609375 0.84375 -1.4375 1.265625q-0.8125 0.40625 -1.640625 0.40625q-1.703125 0 -2.921875 -1.359375q-1.203125 -1.375 -1.203125 -3.828125q0 -2.5 1.171875 -3.796875q1.1875 -1.3125 2.984375 -1.3125q1.65625 0 2.859375 1.375l0 -4.890625l2.609375 0l0 13.59375zm-6.96875 -5.140625q0 1.578125 0.4375 2.28125q0.640625 1.015625 1.765625 1.015625q0.90625 0 1.53125 -0.765625q0.625 -0.765625 0.625 -2.28125q0 -1.703125 -0.609375 -2.4375q-0.609375 -0.75 -1.5625 -0.75q-0.9375 0 -1.5625 0.734375q-0.625 0.734375 -0.625 2.203125zm14.391785 5.140625l0 -13.59375l2.609375 0l0 4.890625q1.203125 -1.375 2.859375 -1.375q1.796875 0 2.96875 1.3125q1.1875 1.296875 1.1875 3.734375q0 2.53125 -1.203125 3.890625q-1.203125 1.359375 -2.921875 1.359375q-0.84375 0 -1.671875 -0.421875q-0.8125 -0.421875 -1.40625 -1.25l0 1.453125l-2.421875 0zm2.59375 -5.140625q0 1.53125 0.484375 2.265625q0.671875 1.03125 1.796875 1.03125q0.859375 0 1.46875 -0.734375q0.609375 -0.734375 0.609375 -2.328125q0 -1.6875 -0.609375 -2.421875q-0.609375 -0.75 -1.578125 -0.75q-0.9375 0 -1.5625 0.734375q-0.609375 0.71875 -0.609375 2.203125zm7.677246 -4.71875l2.78125 0l2.359375 7.0l2.296875 -7.0l2.703125 0l-3.484375 9.484375l-0.625 1.71875q-0.34375 0.859375 -0.65625 1.3125q-0.296875 0.46875 -0.703125 0.75q-0.40625 0.28125 -1.0 0.4375q-0.59375 0.15625 -1.328125 0.15625q-0.75 0 -1.46875 -0.15625l-0.234375 -2.046875q0.609375 0.125 1.09375 0.125q0.921875 0 1.34375 -0.53125q0.4375 -0.53125 0.671875 -1.359375l-3.75 -9.890625zm16.793396 -3.734375l2.75 0l0 7.359375q0 1.75 0.109375 2.265625q0.171875 0.84375 0.828125 1.359375q0.671875 0.5 1.8125 0.5q1.171875 0 1.765625 -0.484375q0.59375 -0.484375 0.71875 -1.171875q0.125 -0.703125 0.125 -2.3125l0 -7.515625l2.734375 0l0 7.140625q0 2.4375 -0.21875 3.453125q-0.21875 1.015625 -0.828125 1.71875q-0.59375 0.6875 -1.59375 1.109375q-1.0 0.40625 -2.609375 0.40625q-1.953125 0 -2.96875 -0.453125q-1.0 -0.453125 -1.59375 -1.171875q-0.578125 -0.71875 -0.75 -1.5q-0.28125 -1.171875 -0.28125 -3.453125l0 -7.25zm19.5979 13.59375l-2.609375 0l0 -9.828125q-1.4375 1.34375 -3.375 1.984375l0 -2.375q1.03125 -0.328125 2.21875 -1.25q1.203125 -0.9375 1.640625 -2.1875l2.125 0l0 13.65625z" fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0" d="m618.8373 177.30708l142.58264 0l0 41.95276l-142.58264 0z" fill-rule="nonzero"></path><path fill="#a61c00" d="m628.5248 199.8052l2.671875 -0.265625q0.234375 1.34375 0.96875 1.984375q0.75 0.625 2.0 0.625q1.328125 0 2.0 -0.5625q0.671875 -0.5625 0.671875 -1.3125q0 -0.484375 -0.28125 -0.8125q-0.28125 -0.34375 -0.984375 -0.59375q-0.484375 -0.171875 -2.203125 -0.59375q-2.203125 -0.546875 -3.09375 -1.34375q-1.265625 -1.125 -1.265625 -2.734375q0 -1.046875 0.59375 -1.953125q0.59375 -0.90625 1.703125 -1.375q1.109375 -0.46875 2.671875 -0.46875q2.5625 0 3.859375 1.125q1.296875 1.109375 1.359375 2.984375l-2.75 0.125q-0.171875 -1.046875 -0.75 -1.5q-0.578125 -0.46875 -1.75 -0.46875q-1.1875 0 -1.875 0.5q-0.4375 0.3125 -0.4375 0.84375q0 0.484375 0.421875 0.828125q0.515625 0.421875 2.515625 0.90625q2.0 0.46875 2.953125 0.984375q0.96875 0.5 1.515625 1.375q0.546875 0.875 0.546875 2.15625q0 1.171875 -0.65625 2.203125q-0.640625 1.015625 -1.828125 1.515625q-1.1875 0.484375 -2.96875 0.484375q-2.578125 0 -3.96875 -1.1875q-1.375 -1.1875 -1.640625 -3.46875zm15.7247925 -9.171875l0 5.0q1.25 -1.484375 3.015625 -1.484375q0.890625 0 1.609375 0.34375q0.734375 0.328125 1.09375 0.84375q0.375 0.515625 0.5 1.15625q0.140625 0.625 0.140625 1.953125l0 5.78125l-2.609375 0l0 -5.203125q0 -1.546875 -0.15625 -1.96875q-0.140625 -0.421875 -0.515625 -0.65625q-0.375 -0.25 -0.9375 -0.25q-0.65625 0 -1.171875 0.3125q-0.5 0.3125 -0.734375 0.953125q-0.234375 0.640625 -0.234375 1.875l0 4.9375l-2.609375 0l0 -13.59375l2.609375 0zm10.739746 6.75l-2.359375 -0.4375q0.390625 -1.421875 1.359375 -2.109375q0.984375 -0.6875 2.90625 -0.6875q1.734375 0 2.59375 0.421875q0.859375 0.40625 1.203125 1.046875q0.34375 0.625 0.34375 2.328125l-0.03125 3.046875q0 1.296875 0.125 1.921875q0.125 0.609375 0.46875 1.3125l-2.578125 0q-0.09375 -0.265625 -0.25 -0.765625q-0.0625 -0.234375 -0.09375 -0.3125q-0.65625 0.65625 -1.421875 0.984375q-0.765625 0.3125 -1.625 0.3125q-1.515625 0 -2.40625 -0.8125q-0.875 -0.828125 -0.875 -2.09375q0 -0.84375 0.390625 -1.484375q0.40625 -0.65625 1.125 -1.0q0.71875 -0.359375 2.078125 -0.625q1.828125 -0.328125 2.53125 -0.625l0 -0.265625q0 -0.75 -0.375 -1.0625q-0.359375 -0.328125 -1.390625 -0.328125q-0.703125 0 -1.09375 0.28125q-0.390625 0.265625 -0.625 0.953125zm3.484375 2.109375q-0.5 0.171875 -1.59375 0.40625q-1.078125 0.234375 -1.40625 0.453125q-0.515625 0.359375 -0.515625 0.921875q0 0.546875 0.40625 0.953125q0.40625 0.390625 1.046875 0.390625q0.703125 0 1.34375 -0.46875q0.46875 -0.359375 0.625 -0.859375q0.09375 -0.34375 0.09375 -1.28125l0 -0.515625zm7.4382324 4.734375l-2.609375 0l0 -9.859375l2.421875 0l0 1.40625q0.625 -0.984375 1.109375 -1.296875q0.5 -0.328125 1.140625 -0.328125q0.890625 0 1.71875 0.5l-0.8125 2.265625q-0.65625 -0.421875 -1.21875 -0.421875q-0.546875 0 -0.9375 0.296875q-0.375 0.296875 -0.59375 1.09375q-0.21875 0.78125 -0.21875 3.296875l0 3.046875zm10.463379 -3.140625l2.609375 0.4375q-0.5 1.4375 -1.59375 2.1875q-1.078125 0.734375 -2.703125 0.734375q-2.5625 0 -3.796875 -1.671875q-0.96875 -1.34375 -0.96875 -3.40625q0 -2.4375 1.265625 -3.828125q1.28125 -1.390625 3.25 -1.390625q2.1875 0 3.453125 1.453125q1.28125 1.453125 1.234375 4.453125l-6.53125 0q0.015625 1.15625 0.625 1.8125q0.609375 0.640625 1.5 0.640625q0.609375 0 1.03125 -0.328125q0.421875 -0.34375 0.625 -1.09375zm0.15625 -2.625q-0.03125 -1.140625 -0.59375 -1.71875q-0.546875 -0.59375 -1.34375 -0.59375q-0.859375 0 -1.40625 0.625q-0.5625 0.609375 -0.546875 1.6875l3.890625 0zm13.563232 5.765625l-2.421875 0l0 -1.453125q-0.609375 0.84375 -1.4375 1.265625q-0.8125 0.40625 -1.640625 0.40625q-1.703125 0 -2.921875 -1.359375q-1.203125 -1.375 -1.203125 -3.828125q0 -2.5 1.171875 -3.796875q1.1875 -1.3125 2.984375 -1.3125q1.65625 0 2.859375 1.375l0 -4.890625l2.609375 0l0 13.59375zm-6.96875 -5.140625q0 1.578125 0.4375 2.28125q0.640625 1.015625 1.765625 1.015625q0.90625 0 1.53125 -0.765625q0.625 -0.765625 0.625 -2.28125q0 -1.703125 -0.609375 -2.4375q-0.609375 -0.75 -1.5625 -0.75q-0.9375 0 -1.5625 0.734375q-0.625 0.734375 -0.625 2.203125zm14.391785 5.140625l0 -13.59375l2.609375 0l0 4.890625q1.203125 -1.375 2.859375 -1.375q1.796875 0 2.96875 1.3125q1.1875 1.296875 1.1875 3.734375q0 2.53125 -1.203125 3.890625q-1.203125 1.359375 -2.921875 1.359375q-0.84375 0 -1.671875 -0.421875q-0.8125 -0.421875 -1.40625 -1.25l0 1.453125l-2.421875 0zm2.59375 -5.140625q0 1.53125 0.484375 2.265625q0.671875 1.03125 1.796875 1.03125q0.859375 0 1.46875 -0.734375q0.609375 -0.734375 0.609375 -2.328125q0 -1.6875 -0.609375 -2.421875q-0.609375 -0.75 -1.578125 -0.75q-0.9375 0 -1.5625 0.734375q-0.609375 0.71875 -0.609375 2.203125zm7.677246 -4.71875l2.78125 0l2.359375 7.0l2.296875 -7.0l2.703125 0l-3.484375 9.484375l-0.625 1.71875q-0.34375 0.859375 -0.65625 1.3125q-0.296875 0.46875 -0.703125 0.75q-0.40625 0.28125 -1.0 0.4375q-0.59375 0.15625 -1.328125 0.15625q-0.75 0 -1.46875 -0.15625l-0.234375 -2.046875q0.609375 0.125 1.09375 0.125q0.921875 0 1.34375 -0.53125q0.4375 -0.53125 0.671875 -1.359375l-3.75 -9.890625zm16.793396 -3.734375l2.75 0l0 7.359375q0 1.75 0.109375 2.265625q0.171875 0.84375 0.828125 1.359375q0.671875 0.5 1.8125 0.5q1.171875 0 1.765625 -0.484375q0.59375 -0.484375 0.71875 -1.171875q0.125 -0.703125 0.125 -2.3125l0 -7.515625l2.734375 0l0 7.140625q0 2.4375 -0.21875 3.453125q-0.21875 1.015625 -0.828125 1.71875q-0.59375 0.6875 -1.59375 1.109375q-1.0 0.40625 -2.609375 0.40625q-1.953125 0 -2.96875 -0.453125q-1.0 -0.453125 -1.59375 -1.171875q-0.578125 -0.71875 -0.75 -1.5q-0.28125 -1.171875 -0.28125 -3.453125l0 -7.25zm19.5979 13.59375l-2.609375 0l0 -9.828125q-1.4375 1.34375 -3.375 1.984375l0 -2.375q1.03125 -0.328125 2.21875 -1.25q1.203125 -0.9375 1.640625 -2.1875l2.125 0l0 13.65625z" fill-rule="nonzero"></path><path fill="#76a5af" d="m287.81104 383.17343l0 0c0 -5.809967 4.7099 -10.519897 10.519897 -10.519897l116.66098 0c2.790039 0 5.4658203 1.1083374 7.43869 3.0812073c1.9728699 1.9728699 3.0812073 4.648651 3.0812073 7.43869l0 42.07834c0 5.809967 -4.7099304 10.519897 -10.519897 10.519897l-116.66098 0c-5.8099976 0 -10.519897 -4.7099304 -10.519897 -10.519897z" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m287.81104 383.17343l0 0c0 -5.809967 4.7099 -10.519897 10.519897 -10.519897l116.66098 0c2.790039 0 5.4658203 1.1083374 7.43869 3.0812073c1.9728699 1.9728699 3.0812073 4.648651 3.0812073 7.43869l0 42.07834c0 5.809967 -4.7099304 10.519897 -10.519897 10.519897l-116.66098 0c-5.8099976 0 -10.519897 -4.7099304 -10.519897 -10.519897z" fill-rule="nonzero"></path><path fill="#000000" d="m346.62677 411.1326l0 -13.59375l4.421875 0q2.5 0 3.265625 0.203125q1.15625 0.296875 1.9375 1.328125q0.796875 1.015625 0.796875 2.640625q0 1.25 -0.453125 2.109375q-0.453125 0.859375 -1.15625 1.34375q-0.703125 0.484375 -1.421875 0.640625q-0.984375 0.203125 -2.84375 0.203125l-1.796875 0l0 5.125l-2.75 0zm2.75 -11.296875l0 3.859375l1.5 0q1.625 0 2.171875 -0.21875q0.546875 -0.21875 0.859375 -0.671875q0.3125 -0.453125 0.3125 -1.046875q0 -0.75 -0.4375 -1.234375q-0.4375 -0.484375 -1.09375 -0.59375q-0.5 -0.09375 -1.984375 -0.09375l-1.328125 0zm9.037323 7.6875l2.515625 -0.3125q0.125 0.96875 0.65625 1.484375q0.53125 0.5 1.28125 0.5q0.796875 0 1.34375 -0.609375q0.5625 -0.609375 0.5625 -1.640625q0 -0.984375 -0.53125 -1.5625q-0.53125 -0.578125 -1.28125 -0.578125q-0.5 0 -1.203125 0.203125l0.28125 -2.125q1.0625 0.015625 1.609375 -0.46875q0.5625 -0.484375 0.5625 -1.296875q0 -0.6875 -0.40625 -1.09375q-0.40625 -0.40625 -1.078125 -0.40625q-0.671875 0 -1.140625 0.46875q-0.46875 0.46875 -0.578125 1.359375l-2.40625 -0.421875q0.25 -1.234375 0.75 -1.96875q0.515625 -0.734375 1.421875 -1.15625q0.90625 -0.421875 2.03125 -0.421875q1.90625 0 3.078125 1.21875q0.953125 1.0 0.953125 2.265625q0 1.796875 -1.953125 2.859375q1.15625 0.25 1.859375 1.125q0.703125 0.875 0.703125 2.109375q0 1.78125 -1.3125 3.046875q-1.296875 1.265625 -3.25 1.265625q-1.84375 0 -3.0625 -1.0625q-1.21875 -1.0625 -1.40625 -2.78125z" fill-rule="nonzero"></path></g></svg>
+
diff --git a/doc/images/Crunch_dispatch.svg b/doc/images/Crunch_dispatch.svg
new file mode 100644 (file)
index 0000000..aa63962
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" standalone="yes"?>
+
+<svg version="1.1" viewBox="0.0 0.0 960.0 540.0" fill="none" stroke="none" stroke-linecap="square" stroke-miterlimit="10" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><clipPath id="g115a30441b_0_35.0"><path d="m0 0l960.0 0l0 540.0l-960.0 0l0 -540.0z" clip-rule="nonzero"></path></clipPath><g clip-path="url(#g115a30441b_0_35.0)"><path fill="#ffffff" d="m0 0l960.0 0l0 540.0l-960.0 0z" fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0" d="m32.72441 46.721786l894.55115 0l0 60.125984l-894.55115 0z" fill-rule="nonzero"></path><path fill="#000000" d="m63.47441 82.28053l3.5 0.875q-1.09375 4.328125 -3.96875 6.59375q-2.859375 2.265625 -6.984375 2.265625q-4.28125 0 -6.96875 -1.734375q-2.6875 -1.75 -4.09375 -5.046875q-1.390625 -3.3125 -1.390625 -7.109375q0 -4.140625 1.578125 -7.21875q1.578125 -3.078125 4.5 -4.671875q2.921875 -1.609375 6.421875 -1.609375q3.96875 0 6.671875 2.03125q2.71875 2.015625 3.796875 5.6875l-3.453125 0.8125q-0.921875 -2.890625 -2.6875 -4.203125q-1.75 -1.328125 -4.40625 -1.328125q-3.046875 0 -5.09375 1.46875q-2.046875 1.453125 -2.890625 3.921875q-0.828125 2.46875 -0.828125 5.09375q0 3.375 0.984375 5.890625q0.984375 2.515625 3.0625 3.765625q2.078125 1.25 4.5 1.25q2.953125 0 4.984375 -1.6875q2.046875 -1.703125 2.765625 -5.046875zm7.613434 9.28125l0 -19.1875l2.921875 0l0 2.90625q1.125 -2.03125 2.0625 -2.6875q0.953125 -0.65625 2.09375 -0.65625q1.640625 0 3.34375 1.046875l-1.125 3.015625q-1.1875 -0.703125 -2.375 -0.703125q-1.078125 0 -1.921875 0.640625q-0.84375 0.640625 -1.203125 1.78125q-0.546875 1.734375 -0.546875 3.796875l0 10.046875l-3.25 0zm25.039932 0l0 -2.8125q-2.25 3.25 -6.09375 3.25q-1.6875 0 -3.171875 -0.65625q-1.46875 -0.65625 -2.1875 -1.640625q-0.703125 -0.984375 -1.0 -2.40625q-0.203125 -0.953125 -0.203125 -3.03125l0 -11.890625l3.265625 0l0 10.640625q0 2.546875 0.1875 3.4375q0.3125 1.28125 1.296875 2.015625q1.0 0.734375 2.46875 0.734375q1.453125 0 2.734375 -0.75q1.296875 -0.75 1.828125 -2.046875q0.53125 -1.296875 0.53125 -3.75l0 -10.28125l3.25 0l0 19.1875l-2.90625 0zm8.182007 0l0 -19.1875l2.921875 0l0 2.734375q2.125 -3.171875 6.109375 -3.171875q1.734375 0 3.1875 0.625q1.453125 0.625 2.171875 1.640625q0.734375 1.015625 1.015625 2.40625q0.1875 0.890625 0.1875 3.15625l0 11.796875l-3.25 0l0 -11.671875q0 -1.984375 -0.390625 -2.96875q-0.375 -0.984375 -1.34375 -1.5625q-0.953125 -0.59375 -2.265625 -0.59375q-2.078125 0 -3.59375 1.3125q-1.5 1.3125 -1.5 5.0l0 10.484375l-3.25 0zm33.275757 -7.03125l3.203125 0.421875q-0.515625 3.296875 -2.6875 5.171875q-2.15625 1.875 -5.296875 1.875q-3.9375 0 -6.328125 -2.578125q-2.390625 -2.578125 -2.390625 -7.375q0 -3.109375 1.015625 -5.4375q1.03125 -2.34375 3.140625 -3.5q2.109375 -1.171875 4.578125 -1.171875q3.125 0 5.109375 1.59375q2.0 1.578125 2.546875 4.484375l-3.15625 0.484375q-0.453125 -1.9375 -1.609375 -2.90625q-1.140625 -0.984375 -2.765625 -0.984375q-2.453125 0 -4.0 1.765625q-1.53125 1.765625 -1.53125 5.578125q0 3.859375 1.484375 5.625q1.484375 1.75 3.875 1.75q1.90625 0 3.1875 -1.171875q1.28125 -1.1875 1.625 -3.625zm6.1484375 7.03125l0 -26.484375l3.25 0l0 9.5q2.28125 -2.640625 5.75 -2.640625q2.125 0 3.703125 0.84375q1.578125 0.84375 2.25 2.328125q0.671875 1.46875 0.671875 4.296875l0 12.15625l-3.25 0l0 -12.15625q0 -2.4375 -1.0625 -3.546875q-1.046875 -1.109375 -2.984375 -1.109375q-1.4375 0 -2.71875 0.75q-1.265625 0.734375 -1.8125 2.03125q-0.546875 1.28125 -0.546875 3.53125l0 10.5l-3.25 0zm31.552963 0l0 -26.484375l9.125 0q3.078125 0 4.703125 0.375q2.28125 0.53125 3.890625 1.90625q2.09375 1.765625 3.125 4.53125q1.046875 2.75 1.046875 6.28125q0 3.015625 -0.703125 5.359375q-0.703125 2.328125 -1.8125 3.859375q-1.09375 1.515625 -2.40625 2.390625q-1.3125 0.875 -3.171875 1.328125q-1.84375 0.453125 -4.25 0.453125l-9.546875 0zm3.5 -3.125l5.65625 0q2.625 0 4.109375 -0.484375q1.484375 -0.484375 2.375 -1.375q1.25 -1.25 1.9375 -3.34375q0.703125 -2.109375 0.703125 -5.109375q0 -4.15625 -1.375 -6.390625q-1.359375 -2.234375 -3.3125 -2.984375q-1.40625 -0.546875 -4.53125 -0.546875l-5.5625 0l0 20.234375zm23.050934 -19.625l0 -3.734375l3.25 0l0 3.734375l-3.25 0zm0 22.75l0 -19.1875l3.25 0l0 19.1875l-3.25 0zm6.9806213 -5.734375l3.21875 -0.5q0.265625 1.9375 1.5 2.96875q1.234375 1.03125 3.46875 1.03125q2.234375 0 3.3125 -0.90625q1.09375 -0.921875 1.09375 -2.15625q0 -1.09375 -0.96875 -1.734375q-0.65625 -0.4375 -3.3125 -1.09375q-3.578125 -0.90625 -4.96875 -1.5625q-1.375 -0.671875 -2.09375 -1.828125q-0.703125 -1.171875 -0.703125 -2.578125q0 -1.28125 0.578125 -2.375q0.59375 -1.09375 1.59375 -1.8125q0.765625 -0.5625 2.078125 -0.953125q1.3125 -0.390625 2.8125 -0.390625q2.25 0 3.953125 0.65625q1.71875 0.65625 2.53125 1.765625q0.8125 1.109375 1.109375 2.96875l-3.171875 0.4375q-0.21875 -1.484375 -1.265625 -2.3125q-1.03125 -0.84375 -2.921875 -0.84375q-2.25 0 -3.203125 0.75q-0.953125 0.734375 -0.953125 1.734375q0 0.625 0.390625 1.140625q0.40625 0.515625 1.25 0.859375q0.484375 0.1875 2.875 0.828125q3.453125 0.921875 4.8125 1.515625q1.359375 0.578125 2.140625 1.703125q0.78125 1.125 0.78125 2.78125q0 1.625 -0.953125 3.0625q-0.953125 1.4375 -2.75 2.234375q-1.78125 0.78125 -4.03125 0.78125q-3.75 0 -5.703125 -1.546875q-1.953125 -1.5625 -2.5 -4.625zm19.960938 13.09375l0 -26.546875l2.96875 0l0 2.5q1.046875 -1.46875 2.359375 -2.203125q1.328125 -0.734375 3.203125 -0.734375q2.453125 0 4.328125 1.265625q1.890625 1.265625 2.84375 3.578125q0.953125 2.296875 0.953125 5.046875q0 2.9375 -1.0625 5.296875q-1.046875 2.359375 -3.0625 3.625q-2.015625 1.25 -4.234375 1.25q-1.625 0 -2.921875 -0.6875q-1.296875 -0.6875 -2.125 -1.734375l0 9.34375l-3.25 0zm2.953125 -16.84375q0 3.703125 1.5 5.484375q1.5 1.765625 3.625 1.765625q2.171875 0 3.703125 -1.828125q1.546875 -1.84375 1.546875 -5.6875q0 -3.671875 -1.515625 -5.5q-1.5 -1.828125 -3.59375 -1.828125q-2.078125 0 -3.671875 1.953125q-1.59375 1.9375 -1.59375 5.640625zm30.322617 7.125q-1.796875 1.53125 -3.46875 2.171875q-1.671875 0.625 -3.5937347 0.625q-3.15625 0 -4.859375 -1.546875q-1.6875 -1.546875 -1.6875 -3.953125q0 -1.40625 0.640625 -2.5625q0.640625 -1.171875 1.671875 -1.875q1.046875 -0.703125 2.34375 -1.0625q0.953125 -0.265625 2.890625 -0.5q3.9374847 -0.46875 5.7968597 -1.109375q0.015625 -0.671875 0.015625 -0.859375q0 -1.984375 -0.921875 -2.796875q-1.25 -1.09375 -3.703125 -1.09375q-2.2968597 0 -3.3906097 0.796875q-1.09375 0.796875 -1.609375 2.84375l-3.1875 -0.4375q0.4375 -2.03125 1.421875 -3.28125q1.0 -1.265625 2.875 -1.9375q1.890625 -0.6875 4.3593597 -0.6875q2.46875 0 4.0 0.578125q1.53125 0.578125 2.25 1.453125q0.734375 0.875 1.015625 2.21875q0.15625 0.828125 0.15625 3.0l0 4.328125q0 4.546875 0.203125 5.75q0.21875 1.1875 0.828125 2.296875l-3.390625 0q-0.5 -1.015625 -0.65625 -2.359375zm-0.265625 -7.265625q-1.765625 0.71875 -5.3125 1.21875q-1.9999847 0.296875 -2.8437347 0.65625q-0.828125 0.359375 -1.28125 1.0625q-0.4375 0.6875 -0.4375 1.53125q0 1.3125 0.984375 2.1875q0.984375 0.859375 2.875 0.859375q1.8749847 0 3.3437347 -0.828125q1.46875 -0.828125 2.15625 -2.25q0.515625 -1.09375 0.515625 -3.25l0 -1.1875zm15.619507 6.71875l0.46875 2.875q-1.375 0.28125 -2.46875 0.28125q-1.765625 0 -2.75 -0.5625q-0.96875 -0.5625 -1.375 -1.46875q-0.390625 -0.90625 -0.390625 -3.84375l0 -11.03125l-2.375 0l0 -2.53125l2.375 0l0 -4.75l3.234375 -1.953125l0 6.703125l3.28125 0l0 2.53125l-3.28125 0l0 11.21875q0 1.390625 0.171875 1.796875q0.171875 0.390625 0.5625 0.625q0.390625 0.234375 1.109375 0.234375q0.546875 0 1.4375 -0.125zm15.777191 -4.125l3.203125 0.421875q-0.515625 3.296875 -2.6875 5.171875q-2.15625 1.875 -5.2968445 1.875q-3.9375 0 -6.328125 -2.578125q-2.390625 -2.578125 -2.390625 -7.375q0 -3.109375 1.015625 -5.4375q1.03125 -2.34375 3.140625 -3.5q2.109375 -1.171875 4.578125 -1.171875q3.1249695 0 5.1093445 1.59375q2.0 1.578125 2.546875 4.484375l-3.15625 0.484375q-0.453125 -1.9375 -1.609375 -2.90625q-1.140625 -0.984375 -2.7655945 -0.984375q-2.453125 0 -4.0 1.765625q-1.53125 1.765625 -1.53125 5.578125q0 3.859375 1.484375 5.625q1.484375 1.75 3.875 1.75q1.9062195 0 3.1874695 -1.171875q1.28125 -1.1875 1.625 -3.625zm6.1484375 7.03125l0 -26.484375l3.25 0l0 9.5q2.28125 -2.640625 5.75 -2.640625q2.125 0 3.703125 0.84375q1.578125 0.84375 2.25 2.328125q0.671875 1.46875 0.671875 4.296875l0 12.15625l-3.25 0l0 -12.15625q0 -2.4375 -1.0625 -3.546875q-1.046875 -1.109375 -2.984375 -1.109375q-1.4375 0 -2.71875 0.75q-1.265625 0.734375 -1.8125 2.03125q-0.546875 1.28125 -0.546875 3.53125l0 10.5l-3.25 0z" fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0" d="m145.72684 103.48555l673.67993 0l0 505.26257l-673.67993 0z" fill-rule="nonzero"></path><g transform="matrix(0.7017498687664041 0.0 0.0 0.7017535433070867 145.72683779527557 103.48555144356955)"><clipPath id="g115a30441b_0_35.1"><path d="m-2.842171E-14 0l960.0 0l0 720.0l-960.0 0z" clip-rule="nonzero"></path></clipPath><image clip-path="url(#g115a30441b_0_35.1)" fill="#000" width="960.0" height="720.0" x="0.0" y="0.0" preserveAspectRatio="none" xlink:href="data:image/png;base64,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"></image></g></g></svg>
+
diff --git a/doc/images/Keep_manifests.svg b/doc/images/Keep_manifests.svg
new file mode 100644 (file)
index 0000000..66a8162
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" standalone="yes"?>
+
+<svg version="1.1" viewBox="0.0 0.0 960.0 540.0" fill="none" stroke="none" stroke-linecap="square" stroke-miterlimit="10" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><clipPath id="g1586814eb6_0_6.0"><path d="m0 0l960.0 0l0 540.0l-960.0 0l0 -540.0z" clip-rule="nonzero"></path></clipPath><g clip-path="url(#g1586814eb6_0_6.0)"><path fill="#ffffff" d="m0 0l960.0 0l0 540.0l-960.0 0z" fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0" d="m32.72441 46.721786l894.55115 0l0 60.125984l-894.55115 0z" fill-rule="nonzero"></path><path fill="#000000" d="m63.47441 82.28053l3.5 0.875q-1.09375 4.328125 -3.96875 6.59375q-2.859375 2.265625 -6.984375 2.265625q-4.28125 0 -6.96875 -1.734375q-2.6875 -1.75 -4.09375 -5.046875q-1.390625 -3.3125 -1.390625 -7.109375q0 -4.140625 1.578125 -7.21875q1.578125 -3.078125 4.5 -4.671875q2.921875 -1.609375 6.421875 -1.609375q3.96875 0 6.671875 2.03125q2.71875 2.015625 3.796875 5.6875l-3.453125 0.8125q-0.921875 -2.890625 -2.6875 -4.203125q-1.75 -1.328125 -4.40625 -1.328125q-3.046875 0 -5.09375 1.46875q-2.046875 1.453125 -2.890625 3.921875q-0.828125 2.46875 -0.828125 5.09375q0 3.375 0.984375 5.890625q0.984375 2.515625 3.0625 3.765625q2.078125 1.25 4.5 1.25q2.953125 0 4.984375 -1.6875q2.046875 -1.703125 2.765625 -5.046875zm6.441559 -0.3125q0 -5.328125 2.953125 -7.890625q2.484375 -2.140625 6.03125 -2.140625q3.96875 0 6.46875 2.59375q2.515625 2.59375 2.515625 7.171875q0 3.703125 -1.109375 5.828125q-1.109375 2.109375 -3.234375 3.296875q-2.125 1.171875 -4.640625 1.171875q-4.015625 0 -6.5 -2.578125q-2.484375 -2.59375 -2.484375 -7.453125zm3.34375 0q0 3.6875 1.59375 5.53125q1.609375 1.828125 4.046875 1.828125q2.421875 0 4.03125 -1.84375q1.609375 -1.84375 1.609375 -5.625q0 -3.5625 -1.625 -5.390625q-1.609375 -1.828125 -4.015625 -1.828125q-2.4375 0 -4.046875 1.828125q-1.59375 1.8125 -1.59375 5.5zm18.541382 9.59375l0 -26.484375l3.265625 0l0 26.484375l-3.265625 0zm8.293121 0l0 -26.484375l3.265625 0l0 26.484375l-3.265625 0zm21.511871 -6.171875l3.359375 0.40625q-0.796875 2.953125 -2.953125 4.578125q-2.140625 1.625 -5.484375 1.625q-4.21875 0 -6.6875 -2.59375q-2.453125 -2.59375 -2.453125 -7.28125q0 -4.828125 2.484375 -7.5q2.5 -2.6875 6.46875 -2.6875q3.859375 0 6.296875 2.625q2.4375 2.625 2.4375 7.375q0 0.28125 -0.015625 0.859375l-14.3125 0q0.171875 3.171875 1.78125 4.859375q1.609375 1.671875 4.015625 1.671875q1.78125 0 3.046875 -0.9375q1.265625 -0.953125 2.015625 -3.0zm-10.6875 -5.265625l10.71875 0q-0.21875 -2.421875 -1.234375 -3.625q-1.546875 -1.890625 -4.015625 -1.890625q-2.25 0 -3.78125 1.5q-1.515625 1.5 -1.6875 4.015625zm30.822632 4.40625l3.203125 0.421875q-0.515625 3.296875 -2.6875 5.171875q-2.15625 1.875 -5.296875 1.875q-3.9375 0 -6.328125 -2.578125q-2.390625 -2.578125 -2.390625 -7.375q0 -3.109375 1.015625 -5.4375q1.03125 -2.34375 3.140625 -3.5q2.109375 -1.171875 4.578125 -1.171875q3.125 0 5.109375 1.59375q2.0 1.578125 2.546875 4.484375l-3.15625 0.484375q-0.453125 -1.9375 -1.609375 -2.90625q-1.140625 -0.984375 -2.765625 -0.984375q-2.453125 0 -4.0 1.765625q-1.53125 1.765625 -1.53125 5.578125q0 3.859375 1.484375 5.625q1.484375 1.75 3.875 1.75q1.90625 0 3.1875 -1.171875q1.28125 -1.1875 1.625 -3.625zm13.2578125 4.125l0.46875 2.875q-1.375 0.28125 -2.46875 0.28125q-1.765625 0 -2.75 -0.5625q-0.96875 -0.5625 -1.375 -1.46875q-0.390625 -0.90625 -0.390625 -3.84375l0 -11.03125l-2.375 0l0 -2.53125l2.375 0l0 -4.75l3.234375 -1.953125l0 6.703125l3.28125 0l0 2.53125l-3.28125 0l0 11.21875q0 1.390625 0.171875 1.796875q0.171875 0.390625 0.5625 0.625q0.390625 0.234375 1.109375 0.234375q0.546875 0 1.4375 -0.125zm3.2772064 -19.84375l0 -3.734375l3.25 0l0 3.734375l-3.25 0zm0 22.75l0 -19.1875l3.25 0l0 19.1875l-3.25 0zm7.0743713 -9.59375q0 -5.328125 2.953125 -7.890625q2.484375 -2.140625 6.03125 -2.140625q3.96875 0 6.46875 2.59375q2.515625 2.59375 2.515625 7.171875q0 3.703125 -1.109375 5.828125q-1.109375 2.109375 -3.234375 3.296875q-2.125 1.171875 -4.640625 1.171875q-4.015625 0 -6.5 -2.578125q-2.484375 -2.59375 -2.484375 -7.453125zm3.34375 0q0 3.6875 1.59375 5.53125q1.609375 1.828125 4.046875 1.828125q2.421875 0 4.03125 -1.84375q1.609375 -1.84375 1.609375 -5.625q0 -3.5625 -1.625 -5.390625q-1.609375 -1.828125 -4.015625 -1.828125q-2.4375 0 -4.046875 1.828125q-1.59375 1.8125 -1.59375 5.5zm18.619507 9.59375l0 -19.1875l2.921875 0l0 2.734375q2.125 -3.171875 6.109375 -3.171875q1.734375 0 3.1875 0.625q1.453125 0.625 2.171875 1.640625q0.734375 1.015625 1.015625 2.40625q0.1875 0.890625 0.1875 3.15625l0 11.796875l-3.25 0l0 -11.671875q0 -1.984375 -0.390625 -2.96875q-0.375 -0.984375 -1.34375 -1.5625q-0.953125 -0.59375 -2.265625 -0.59375q-2.078125 0 -3.59375 1.3125q-1.5 1.3125 -1.5 5.0l0 10.484375l-3.25 0zm19.463257 -5.734375l3.21875 -0.5q0.265625 1.9375 1.5 2.96875q1.234375 1.03125 3.46875 1.03125q2.234375 0 3.3125 -0.90625q1.09375 -0.921875 1.09375 -2.15625q0 -1.09375 -0.96875 -1.734375q-0.65625 -0.4375 -3.3125 -1.09375q-3.578125 -0.90625 -4.96875 -1.5625q-1.375 -0.671875 -2.09375 -1.828125q-0.703125 -1.171875 -0.703125 -2.578125q0 -1.28125 0.578125 -2.375q0.59375 -1.09375 1.59375 -1.8125q0.765625 -0.5625 2.078125 -0.953125q1.3125 -0.390625 2.8125 -0.390625q2.25 0 3.953125 0.65625q1.71875 0.65625 2.53125 1.765625q0.8125 1.109375 1.109375 2.96875l-3.171875 0.4375q-0.21875 -1.484375 -1.265625 -2.3125q-1.03125 -0.84375 -2.921875 -0.84375q-2.25 0 -3.203125 0.75q-0.953125 0.734375 -0.953125 1.734375q0 0.625 0.390625 1.140625q0.40625 0.515625 1.25 0.859375q0.484375 0.1875 2.875 0.828125q3.453125 0.921875 4.8125 1.515625q1.359375 0.578125 2.140625 1.703125q0.78125 1.125 0.78125 2.78125q0 1.625 -0.953125 3.0625q-0.953125 1.4375 -2.75 2.234375q-1.78125 0.78125 -4.03125 0.78125q-3.75 0 -5.703125 -1.546875q-1.953125 -1.5625 -2.5 -4.625zm20.867188 -9.75l0 -3.703125l3.703125 0l0 3.703125l-3.703125 0zm0 15.484375l0 -3.703125l3.703125 0l0 3.703125l-3.703125 0zm20.148163 0l0 -26.484375l5.265625 0l6.28125 18.75q0.859375 2.625 1.265625 3.921875q0.4375 -1.4375 1.40625 -4.25l6.34375 -18.421875l4.703125 0l0 26.484375l-3.375 0l0 -22.171875l-7.6875 22.171875l-3.171875 0l-7.65625 -22.546875l0 22.546875l-3.375 0zm43.29773 -2.359375q-1.796875 1.53125 -3.46875 2.171875q-1.671875 0.625 -3.59375 0.625q-3.15625 0 -4.859375 -1.546875q-1.6875 -1.546875 -1.6875 -3.953125q0 -1.40625 0.640625 -2.5625q0.640625 -1.171875 1.671875 -1.875q1.046875 -0.703125 2.34375 -1.0625q0.953125 -0.265625 2.890625 -0.5q3.9375 -0.46875 5.796875 -1.109375q0.015625 -0.671875 0.015625 -0.859375q0 -1.984375 -0.921875 -2.796875q-1.25 -1.09375 -3.703125 -1.09375q-2.296875 0 -3.390625 0.796875q-1.09375 0.796875 -1.609375 2.84375l-3.1875 -0.4375q0.4375 -2.03125 1.421875 -3.28125q1.0 -1.265625 2.875 -1.9375q1.890625 -0.6875 4.359375 -0.6875q2.46875 0 4.0 0.578125q1.53125 0.578125 2.25 1.453125q0.734375 0.875 1.015625 2.21875q0.15625 0.828125 0.15625 3.0l0 4.328125q0 4.546875 0.203125 5.75q0.21875 1.1875 0.828125 2.296875l-3.390625 0q-0.5 -1.015625 -0.65625 -2.359375zm-0.265625 -7.265625q-1.765625 0.71875 -5.3125 1.21875q-2.0 0.296875 -2.84375 0.65625q-0.828125 0.359375 -1.28125 1.0625q-0.4375 0.6875 -0.4375 1.53125q0 1.3125 0.984375 2.1875q0.984375 0.859375 2.875 0.859375q1.875 0 3.34375 -0.828125q1.46875 -0.828125 2.15625 -2.25q0.515625 -1.09375 0.515625 -3.25l0 -1.1875zm8.510132 9.625l0 -19.1875l2.921875 0l0 2.734375q2.125 -3.171875 6.109375 -3.171875q1.734375 0 3.1875 0.625q1.453125 0.625 2.171875 1.640625q0.734375 1.015625 1.015625 2.40625q0.1875 0.890625 0.1875 3.15625l0 11.796875l-3.25 0l0 -11.671875q0 -1.984375 -0.390625 -2.96875q-0.375 -0.984375 -1.34375 -1.5625q-0.953125 -0.59375 -2.265625 -0.59375q-2.078125 0 -3.59375 1.3125q-1.5 1.3125 -1.5 5.0l0 10.484375l-3.25 0zm20.775757 -22.75l0 -3.734375l3.25 0l0 3.734375l-3.25 0zm0 22.75l0 -19.1875l3.25 0l0 19.1875l-3.25 0zm9.058746 0l0 -16.65625l-2.875 0l0 -2.53125l2.875 0l0 -2.046875q0 -1.921875 0.34375 -2.859375q0.46875 -1.265625 1.640625 -2.046875q1.1875 -0.796875 3.328125 -0.796875q1.375 0 3.03125 0.328125l-0.484375 2.828125q-1.015625 -0.171875 -1.921875 -0.171875q-1.484375 0 -2.09375 0.640625q-0.609375 0.625 -0.609375 2.359375l0 1.765625l3.734375 0l0 2.53125l-3.734375 0l0 16.65625l-3.234375 0zm22.730347 -6.171875l3.359375 0.40625q-0.796875 2.953125 -2.953125 4.578125q-2.140625 1.625 -5.484375 1.625q-4.21875 0 -6.6875 -2.59375q-2.453125 -2.59375 -2.453125 -7.28125q0 -4.828125 2.484375 -7.5q2.5 -2.6875 6.46875 -2.6875q3.859375 0 6.296875 2.625q2.4375 2.625 2.4375 7.375q0 0.28125 -0.015625 0.859375l-14.3125 0q0.171875 3.171875 1.78125 4.859375q1.609375 1.671875 4.015625 1.671875q1.78125 0 3.046875 -0.9375q1.265625 -0.953125 2.015625 -3.0zm-10.6875 -5.265625l10.71875 0q-0.21875 -2.421875 -1.234375 -3.625q-1.546875 -1.890625 -4.015625 -1.890625q-2.25 0 -3.78125 1.5q-1.515625 1.5 -1.6875 4.015625zm17.010132 5.703125l3.21875 -0.5q0.265625 1.9375 1.5 2.96875q1.234375 1.03125 3.46875 1.03125q2.234375 0 3.3125 -0.90625q1.09375 -0.921875 1.09375 -2.15625q0 -1.09375 -0.96875 -1.734375q-0.65625 -0.4375 -3.3125 -1.09375q-3.578125 -0.90625 -4.96875 -1.5625q-1.375 -0.671875 -2.09375 -1.828125q-0.703125 -1.171875 -0.703125 -2.578125q0 -1.28125 0.578125 -2.375q0.59375 -1.09375 1.59375 -1.8125q0.765625 -0.5625 2.078125 -0.953125q1.3125 -0.390625 2.8125 -0.390625q2.25 0 3.953125 0.65625q1.71875 0.65625 2.53125 1.765625q0.8125 1.109375 1.109375 2.96875l-3.171875 0.4375q-0.21875 -1.484375 -1.265625 -2.3125q-1.03125 -0.84375 -2.921875 -0.84375q-2.25 0 -3.203125 0.75q-0.953125 0.734375 -0.953125 1.734375q0 0.625 0.390625 1.140625q0.40625 0.515625 1.25 0.859375q0.484375 0.1875 2.875 0.828125q3.453125 0.921875 4.8125 1.515625q1.359375 0.578125 2.140625 1.703125q0.78125 1.125 0.78125 2.78125q0 1.625 -0.953125 3.0625q-0.953125 1.4375 -2.75 2.234375q-1.78125 0.78125 -4.03125 0.78125q-3.75 0 -5.703125 -1.546875q-1.953125 -1.5625 -2.5 -4.625zm27.070312 2.828125l0.46875 2.875q-1.375 0.28125 -2.46875 0.28125q-1.765625 0 -2.75 -0.5625q-0.96875 -0.5625 -1.375 -1.46875q-0.390625 -0.90625 -0.390625 -3.84375l0 -11.03125l-2.375 0l0 -2.53125l2.375 0l0 -4.75l3.234375 -1.953125l0 6.703125l3.28125 0l0 2.53125l-3.28125 0l0 11.21875q0 1.390625 0.171875 1.796875q0.171875 0.390625 0.5625 0.625q0.390625 0.234375 1.109375 0.234375q0.546875 0 1.4375 -0.125zm1.9647217 -2.828125l3.21875 -0.5q0.265625 1.9375 1.5 2.96875q1.234375 1.03125 3.46875 1.03125q2.234375 0 3.3125 -0.90625q1.09375 -0.921875 1.09375 -2.15625q0 -1.09375 -0.96875 -1.734375q-0.65625 -0.4375 -3.3125 -1.09375q-3.578125 -0.90625 -4.96875 -1.5625q-1.375 -0.671875 -2.09375 -1.828125q-0.703125 -1.171875 -0.703125 -2.578125q0 -1.28125 0.578125 -2.375q0.59375 -1.09375 1.59375 -1.8125q0.765625 -0.5625 2.078125 -0.953125q1.3125 -0.390625 2.8125 -0.390625q2.25 0 3.953125 0.65625q1.71875 0.65625 2.53125 1.765625q0.8125 1.109375 1.109375 2.96875l-3.171875 0.4375q-0.21875 -1.484375 -1.265625 -2.3125q-1.03125 -0.84375 -2.921875 -0.84375q-2.25 0 -3.203125 0.75q-0.953125 0.734375 -0.953125 1.734375q0 0.625 0.390625 1.140625q0.40625 0.515625 1.25 0.859375q0.484375 0.1875 2.875 0.828125q3.453125 0.921875 4.8125 1.515625q1.359375 0.578125 2.140625 1.703125q0.78125 1.125 0.78125 2.78125q0 1.625 -0.953125 3.0625q-0.953125 1.4375 -2.75 2.234375q-1.78125 0.78125 -4.03125 0.78125q-3.75 0 -5.703125 -1.546875q-1.953125 -1.5625 -2.5 -4.625z" fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0" d="m32.08399 481.27823l894.5512 0l0 46.015717l-894.5512 0z" fill-rule="nonzero"></path><path fill="#000000" d="m46.005863 508.1982l0 -12.0l-4.46875 0l0 -1.59375l10.765625 0l0 1.59375l-4.5 0l0 12.0l-1.796875 0zm14.474106 -3.171875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm15.547596 2.265625l1.640625 0.21875q-0.265625 1.6875 -1.375 2.65625q-1.109375 0.953125 -2.734375 0.953125q-2.015625 0 -3.25 -1.3125q-1.2187538 -1.328125 -1.2187538 -3.796875q0 -1.59375 0.5156288 -2.78125q0.53125 -1.203125 1.609375 -1.796875q1.09375 -0.609375 2.359375 -0.609375q1.609375 0 2.625 0.8125q1.015625 0.8125 1.3125 2.3125l-1.625 0.25q-0.234375 -1.0 -0.828125 -1.5q-0.59375 -0.5 -1.421875 -0.5q-1.265625 0 -2.0625 0.90625q-0.78125 0.90625 -0.78125 2.859375q0 1.984375 0.765625 2.890625q0.765625 0.890625 1.984375 0.890625q0.984375 0 1.640625 -0.59375q0.65625 -0.609375 0.84375 -1.859375zm2.890625 3.609375l0 -13.59375l1.671875 0l0 4.875q1.171875 -1.359375 2.953125 -1.359375q1.09375 0 1.890625 0.4375q0.8125 0.421875 1.15625 1.1875q0.359375 0.765625 0.359375 2.203125l0 6.25l-1.671875 0l0 -6.25q0 -1.25 -0.546875 -1.8125q-0.546875 -0.578125 -1.53125 -0.578125q-0.75 0 -1.40625 0.390625q-0.640625 0.375 -0.921875 1.046875q-0.28125 0.65625 -0.28125 1.8125l0 5.390625l-1.671875 0zm10.375717 0l0 -9.859375l1.5 0l0 1.40625q1.09375 -1.625 3.140625 -1.625q0.890625 0 1.640625 0.328125q0.75 0.3125 1.109375 0.84375q0.375 0.515625 0.53125 1.21875q0.09375 0.46875 0.09375 1.625l0 6.0625l-1.671875 0l0 -6.0q0 -1.015625 -0.203125 -1.515625q-0.1875 -0.515625 -0.6875 -0.8125q-0.5 -0.296875 -1.171875 -0.296875q-1.0625 0 -1.84375 0.671875q-0.765625 0.671875 -0.765625 2.578125l0 5.375l-1.671875 0zm10.391342 -11.6875l0 -1.90625l1.671875 0l0 1.90625l-1.671875 0zm0 11.6875l0 -9.859375l1.671875 0l0 9.859375l-1.671875 0zm10.566696 -3.609375l1.640625 0.21875q-0.265625 1.6875 -1.375 2.65625q-1.109375 0.953125 -2.734375 0.953125q-2.015625 0 -3.25 -1.3125q-1.21875 -1.328125 -1.21875 -3.796875q0 -1.59375 0.515625 -2.78125q0.53125 -1.203125 1.609375 -1.796875q1.09375 -0.609375 2.359375 -0.609375q1.609375 0 2.625 0.8125q1.015625 0.8125 1.3125 2.3125l-1.625 0.25q-0.234375 -1.0 -0.828125 -1.5q-0.59375 -0.5 -1.421875 -0.5q-1.265625 0 -2.0625 0.90625q-0.78125 0.90625 -0.78125 2.859375q0 1.984375 0.765625 2.890625q0.765625 0.890625 1.984375 0.890625q0.984375 0 1.640625 -0.59375q0.65625 -0.609375 0.84375 -1.859375zm9.328125 2.390625q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm4.047592 4.9375l0 -13.59375l1.671875 0l0 13.59375l-1.671875 0zm8.6875 -2.9375l1.6562424 -0.265625q0.140625 1.0 0.765625 1.53125q0.640625 0.515625 1.78125 0.515625q1.15625 0 1.703125 -0.46875q0.5625 -0.46875 0.5625 -1.09375q0 -0.5625 -0.484375 -0.890625q-0.34375 -0.21875 -1.703125 -0.5625q-1.84375 -0.46875 -2.5625 -0.796875q-0.7031174 -0.34375 -1.0781174 -0.9375q-0.359375 -0.609375 -0.359375 -1.328125q0 -0.65625 0.296875 -1.21875q0.3125 -0.5625 0.8281174 -0.9375q0.390625 -0.28125 1.0625 -0.484375q0.671875 -0.203125 1.4375 -0.203125q1.171875 0 2.046875 0.34375q0.875 0.328125 1.28125 0.90625q0.421875 0.5625 0.578125 1.515625l-1.625 0.21875q-0.109375 -0.75 -0.65625 -1.171875q-0.53125 -0.4375 -1.5 -0.4375q-1.15625 0 -1.640625 0.390625q-0.484375 0.375 -0.484375 0.875q0 0.328125 0.203125 0.59375q0.203125 0.265625 0.640625 0.4375q0.25 0.09375 1.46875 0.4375q1.765625 0.46875 2.46875 0.765625q0.703125 0.296875 1.09375 0.875q0.40625 0.578125 0.40625 1.4375q0 0.828125 -0.484375 1.578125q-0.484375 0.734375 -1.40625 1.140625q-0.921875 0.390625 -2.078125 0.390625q-1.921875 0 -2.9374924 -0.796875q-1.0 -0.796875 -1.28125 -2.359375zm9.999992 6.71875l0 -13.640625l1.53125 0l0 1.28125q0.53125 -0.75 1.203125 -1.125q0.6875 -0.375 1.640625 -0.375q1.265625 0 2.234375 0.65625q0.96875 0.640625 1.453125 1.828125q0.5 1.1875 0.5 2.59375q0 1.515625 -0.546875 2.734375q-0.546875 1.203125 -1.578125 1.84375q-1.03125 0.640625 -2.171875 0.640625q-0.84375 0 -1.515625 -0.34375q-0.65625 -0.359375 -1.078125 -0.890625l0 4.796875l-1.671875 0zm1.515625 -8.65625q0 1.90625 0.765625 2.8125q0.78125 0.90625 1.875 0.90625q1.109375 0 1.890625 -0.9375q0.796875 -0.9375 0.796875 -2.921875q0 -1.875 -0.78125 -2.8125q-0.765625 -0.9375 -1.84375 -0.9375q-1.0625 0 -1.890625 1.0q-0.8125 1.0 -0.8125 2.890625zm15.610092 1.703125l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm15.547592 2.265625l1.640625 0.21875q-0.265625 1.6875 -1.375 2.65625q-1.109375 0.953125 -2.734375 0.953125q-2.015625 0 -3.25 -1.3125q-1.21875 -1.328125 -1.21875 -3.796875q0 -1.59375 0.515625 -2.78125q0.53125 -1.203125 1.609375 -1.796875q1.09375 -0.609375 2.359375 -0.609375q1.609375 0 2.625 0.8125q1.015625 0.8125 1.3125 2.3125l-1.625 0.25q-0.234375 -1.0 -0.828125 -1.5q-0.59375 -0.5 -1.421875 -0.5q-1.265625 0 -2.0625 0.90625q-0.78125 0.90625 -0.78125 2.859375q0 1.984375 0.765625 2.890625q0.765625 0.890625 1.984375 0.890625q0.984375 0 1.640625 -0.59375q0.65625 -0.609375 0.84375 -1.859375zm2.21875 0.671875l1.65625 -0.265625q0.140625 1.0 0.765625 1.53125q0.640625 0.515625 1.78125 0.515625q1.15625 0 1.703125 -0.46875q0.5625 -0.46875 0.5625 -1.09375q0 -0.5625 -0.484375 -0.890625q-0.34375 -0.21875 -1.703125 -0.5625q-1.84375 -0.46875 -2.5625 -0.796875q-0.703125 -0.34375 -1.078125 -0.9375q-0.359375 -0.609375 -0.359375 -1.328125q0 -0.65625 0.296875 -1.21875q0.3125 -0.5625 0.828125 -0.9375q0.390625 -0.28125 1.0625 -0.484375q0.671875 -0.203125 1.4375 -0.203125q1.171875 0 2.046875 0.34375q0.875 0.328125 1.28125 0.90625q0.421875 0.5625 0.578125 1.515625l-1.625 0.21875q-0.109375 -0.75 -0.65625 -1.171875q-0.53125 -0.4375 -1.5 -0.4375q-1.15625 0 -1.640625 0.390625q-0.484375 0.375 -0.484375 0.875q0 0.328125 0.203125 0.59375q0.203125 0.265625 0.640625 0.4375q0.25 0.09375 1.46875 0.4375q1.765625 0.46875 2.46875 0.765625q0.703125 0.296875 1.09375 0.875q0.40625 0.578125 0.40625 1.4375q0 0.828125 -0.484375 1.578125q-0.484375 0.734375 -1.40625 1.140625q-0.921875 0.390625 -2.078125 0.390625q-1.921875 0 -2.9375 -0.796875q-1.0 -0.796875 -1.28125 -2.359375zm10.46875 -5.015625l0 -1.90625l1.90625 0l0 1.90625l-1.90625 0zm0 7.953125l0 -1.90625l1.90625 0l0 1.90625l-1.90625 0z" fill-rule="nonzero"></path><path fill="#0097a7" d="m186.46445 508.1982l0 -13.59375l1.671875 0l0 4.875q1.171875 -1.359375 2.953125 -1.359375q1.09375 0 1.890625 0.4375q0.8125 0.421875 1.15625 1.1875q0.359375 0.765625 0.359375 2.203125l0 6.25l-1.671875 0l0 -6.25q0 -1.25 -0.546875 -1.8125q-0.546875 -0.578125 -1.53125 -0.578125q-0.75 0 -1.40625 0.390625q-0.640625 0.375 -0.921875 1.046875q-0.28125 0.65625 -0.28125 1.8125l0 5.390625l-1.671875 0zm14.031967 -1.5l0.234375 1.484375q-0.703125 0.140625 -1.265625 0.140625q-0.90625 0 -1.40625 -0.28125q-0.5 -0.296875 -0.703125 -0.75q-0.203125 -0.46875 -0.203125 -1.984375l0 -5.65625l-1.234375 0l0 -1.3125l1.234375 0l0 -2.4375l1.65625 -1.0l0 3.4375l1.6875 0l0 1.3125l-1.6875 0l0 5.75q0 0.71875 0.078125 0.921875q0.09375 0.203125 0.296875 0.328125q0.203125 0.125 0.578125 0.125q0.265625 0 0.734375 -0.078125zm5.183304 0l0.234375 1.484375q-0.703125 0.140625 -1.265625 0.140625q-0.90625 0 -1.40625 -0.28125q-0.5 -0.296875 -0.703125 -0.75q-0.203125 -0.46875 -0.203125 -1.984375l0 -5.65625l-1.234375 0l0 -1.3125l1.234375 0l0 -2.4375l1.65625 -1.0l0 3.4375l1.6875 0l0 1.3125l-1.6875 0l0 5.75q0 0.71875 0.078125 0.921875q0.09375 0.203125 0.296875 0.328125q0.203125 0.125 0.578125 0.125q0.265625 0 0.734375 -0.078125zm1.5270538 5.28125l0 -13.640625l1.53125 0l0 1.28125q0.53125 -0.75 1.203125 -1.125q0.6875 -0.375 1.640625 -0.375q1.265625 0 2.234375 0.65625q0.96875 0.640625 1.453125 1.828125q0.5 1.1875 0.5 2.59375q0 1.515625 -0.546875 2.734375q-0.546875 1.203125 -1.578125 1.84375q-1.03125 0.640625 -2.171875 0.640625q-0.84375 0 -1.515625 -0.34375q-0.65625 -0.359375 -1.078125 -0.890625l0 4.796875l-1.671875 0zm1.515625 -8.65625q0 1.90625 0.765625 2.8125q0.78125 0.90625 1.875 0.90625q1.109375 0 1.890625 -0.9375q0.796875 -0.9375 0.796875 -2.921875q0 -1.875 -0.78125 -2.8125q-0.765625 -0.9375 -1.84375 -0.9375q-1.0625 0 -1.890625 1.0q-0.8125 1.0 -0.8125 2.890625zm8.188217 1.9375l1.65625 -0.265625q0.140625 1.0 0.765625 1.53125q0.640625 0.515625 1.78125 0.515625q1.15625 0 1.703125 -0.46875q0.5625 -0.46875 0.5625 -1.09375q0 -0.5625 -0.484375 -0.890625q-0.34375 -0.21875 -1.703125 -0.5625q-1.84375 -0.46875 -2.5625 -0.796875q-0.703125 -0.34375 -1.078125 -0.9375q-0.359375 -0.609375 -0.359375 -1.328125q0 -0.65625 0.296875 -1.21875q0.3125 -0.5625 0.828125 -0.9375q0.390625 -0.28125 1.0625 -0.484375q0.671875 -0.203125 1.4375 -0.203125q1.171875 0 2.046875 0.34375q0.875 0.328125 1.28125 0.90625q0.421875 0.5625 0.578125 1.515625l-1.625 0.21875q-0.109375 -0.75 -0.65625 -1.171875q-0.53125 -0.4375 -1.5 -0.4375q-1.15625 0 -1.640625 0.390625q-0.484375 0.375 -0.484375 0.875q0 0.328125 0.203125 0.59375q0.203125 0.265625 0.640625 0.4375q0.25 0.09375 1.46875 0.4375q1.765625 0.46875 2.46875 0.765625q0.703125 0.296875 1.09375 0.875q0.40625 0.578125 0.40625 1.4375q0 0.828125 -0.484375 1.578125q-0.484375 0.734375 -1.40625 1.140625q-0.921875 0.390625 -2.078125 0.390625q-1.921875 0 -2.9375 -0.796875q-1.0 -0.796875 -1.28125 -2.359375zm10.46875 -5.015625l0 -1.90625l1.90625 0l0 1.90625l-1.90625 0zm0 7.953125l0 -1.90625l1.90625 0l0 1.90625l-1.90625 0zm3.4645538 0.234375l3.9375 -14.0625l1.34375 0l-3.9375 14.0625l-1.34375 0zm5.183304 0l3.9375 -14.0625l1.34375 0l-3.9375 14.0625l-1.34375 0zm12.823929 -0.234375l0 -1.25q-0.9375 1.46875 -2.75 1.46875q-1.171875 0 -2.171875 -0.640625q-0.984375 -0.65625 -1.53125 -1.8125q-0.53125 -1.171875 -0.53125 -2.6875q0 -1.46875 0.484375 -2.671875q0.5 -1.203125 1.46875 -1.84375q0.984375 -0.640625 2.203125 -0.640625q0.890625 0 1.578125 0.375q0.703125 0.375 1.140625 0.984375l0 -4.875l1.65625 0l0 13.59375l-1.546875 0zm-5.28125 -4.921875q0 1.890625 0.796875 2.828125q0.8125 0.9375 1.890625 0.9375q1.09375 0 1.859375 -0.890625q0.765625 -0.890625 0.765625 -2.734375q0 -2.015625 -0.78125 -2.953125q-0.78125 -0.953125 -1.921875 -0.953125q-1.109375 0 -1.859375 0.90625q-0.75 0.90625 -0.75 2.859375zm16.016342 1.75l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm11.844482 5.875l-3.75 -9.859375l1.765625 0l2.125 5.90625q0.34375 0.953125 0.625 1.984375q0.21875 -0.78125 0.625 -1.875l2.1875 -6.015625l1.71875 0l-3.734375 9.859375l-1.5625 0zm7.0625 0l0 -1.90625l1.90625 0l0 1.90625l-1.90625 0zm11.152039 -1.21875q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm4.0632324 4.9375l0 -9.859375l1.5 0l0 1.5q0.578125 -1.046875 1.0625 -1.375q0.484375 -0.34375 1.078125 -0.34375q0.84375 0 1.71875 0.546875l-0.578125 1.546875q-0.609375 -0.359375 -1.234375 -0.359375q-0.546875 0 -0.984375 0.328125q-0.421875 0.328125 -0.609375 0.90625q-0.28125 0.890625 -0.28125 1.953125l0 5.15625l-1.671875 0zm8.9626465 0l-3.75 -9.859375l1.765625 0l2.125 5.90625q0.34375 0.953125 0.625 1.984375q0.21875 -0.78125 0.625 -1.875l2.1875 -6.015625l1.71875 0l-3.734375 9.859375l-1.5625 0zm13.03125 -1.21875q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm10.469482 4.9375l0 -1.25q-0.9375 1.46875 -2.75 1.46875q-1.171875 0 -2.171875 -0.640625q-0.984375 -0.65625 -1.53125 -1.8125q-0.53125 -1.171875 -0.53125 -2.6875q0 -1.46875 0.484375 -2.671875q0.5 -1.203125 1.46875 -1.84375q0.984375 -0.640625 2.203125 -0.640625q0.890625 0 1.578125 0.375q0.703125 0.375 1.140625 0.984375l0 -4.875l1.65625 0l0 13.59375l-1.546875 0zm-5.28125 -4.921875q0 1.890625 0.796875 2.828125q0.8125 0.9375 1.890625 0.9375q1.09375 0 1.859375 -0.890625q0.765625 -0.890625 0.765625 -2.734375q0 -2.015625 -0.78125 -2.953125q-0.78125 -0.953125 -1.921875 -0.953125q-1.109375 0 -1.859375 0.90625q-0.75 0.90625 -0.75 2.859375zm8.641357 0q0 -2.734375 1.53125 -4.0625q1.265625 -1.09375 3.09375 -1.09375q2.03125 0 3.3125 1.34375q1.296875 1.328125 1.296875 3.671875q0 1.90625 -0.578125 3.0q-0.5625 1.078125 -1.65625 1.6875q-1.078125 0.59375 -2.375 0.59375q-2.0625 0 -3.34375 -1.328125q-1.28125 -1.328125 -1.28125 -3.8125zm1.71875 0q0 1.890625 0.828125 2.828125q0.828125 0.9375 2.078125 0.9375q1.25 0 2.0625 -0.9375q0.828125 -0.953125 0.828125 -2.890625q0 -1.828125 -0.828125 -2.765625q-0.828125 -0.9375 -2.0625 -0.9375q-1.25 0 -2.078125 0.9375q-0.828125 0.9375 -0.828125 2.828125zm8.610077 1.984375l1.65625 -0.265625q0.140625 1.0 0.765625 1.53125q0.640625 0.515625 1.78125 0.515625q1.15625 0 1.703125 -0.46875q0.5625 -0.46875 0.5625 -1.09375q0 -0.5625 -0.484375 -0.890625q-0.34375 -0.21875 -1.703125 -0.5625q-1.84375 -0.46875 -2.5625 -0.796875q-0.703125 -0.34375 -1.078125 -0.9375q-0.359375 -0.609375 -0.359375 -1.328125q0 -0.65625 0.296875 -1.21875q0.3125 -0.5625 0.828125 -0.9375q0.390625 -0.28125 1.0625 -0.484375q0.671875 -0.203125 1.4375 -0.203125q1.171875 0 2.046875 0.34375q0.875 0.328125 1.28125 0.90625q0.421875 0.5625 0.578125 1.515625l-1.625 0.21875q-0.109375 -0.75 -0.65625 -1.171875q-0.53125 -0.4375 -1.5 -0.4375q-1.15625 0 -1.640625 0.390625q-0.484375 0.375 -0.484375 0.875q0 0.328125 0.203125 0.59375q0.203125 0.265625 0.640625 0.4375q0.25 0.09375 1.46875 0.4375q1.765625 0.46875 2.46875 0.765625q0.703125 0.296875 1.09375 0.875q0.40625 0.578125 0.40625 1.4375q0 0.828125 -0.484375 1.578125q-0.484375 0.734375 -1.40625 1.140625q-0.921875 0.390625 -2.078125 0.390625q-1.921875 0 -2.9375 -0.796875q-1.0 -0.796875 -1.28125 -2.359375zm10.46875 2.9375l0 -1.90625l1.90625 0l0 1.90625l-1.90625 0zm4.089569 -4.921875q0 -2.734375 1.53125 -4.0625q1.265625 -1.09375 3.09375 -1.09375q2.03125 0 3.3125 1.34375q1.296875 1.328125 1.296875 3.671875q0 1.90625 -0.578125 3.0q-0.5625 1.078125 -1.65625 1.6875q-1.078125 0.59375 -2.375 0.59375q-2.0625 0 -3.34375 -1.328125q-1.28125 -1.328125 -1.28125 -3.8125zm1.71875 0q0 1.890625 0.828125 2.828125q0.828125 0.9375 2.078125 0.9375q1.25 0 2.0625 -0.9375q0.828125 -0.953125 0.828125 -2.890625q0 -1.828125 -0.828125 -2.765625q-0.828125 -0.9375 -2.0625 -0.9375q-1.25 0 -2.078125 0.9375q-0.828125 0.9375 -0.828125 2.828125zm9.266327 4.921875l0 -9.859375l1.5 0l0 1.5q0.578125 -1.046875 1.0625 -1.375q0.484375 -0.34375 1.078125 -0.34375q0.84375 0 1.71875 0.546875l-0.578125 1.546875q-0.609375 -0.359375 -1.234375 -0.359375q-0.546875 0 -0.984375 0.328125q-0.421875 0.328125 -0.609375 0.90625q-0.28125 0.890625 -0.28125 1.953125l0 5.15625l-1.671875 0zm5.931427 0.8125l1.609375 0.25q0.109375 0.75 0.578125 1.09375q0.609375 0.453125 1.6875 0.453125q1.171875 0 1.796875 -0.46875q0.625 -0.453125 0.859375 -1.28125q0.125 -0.515625 0.109375 -2.15625q-1.09375 1.296875 -2.71875 1.296875q-2.03125 0 -3.15625 -1.46875q-1.109375 -1.46875 -1.109375 -3.515625q0 -1.40625 0.515625 -2.59375q0.515625 -1.203125 1.484375 -1.84375q0.96875 -0.65625 2.265625 -0.65625q1.75 0 2.875 1.40625l0 -1.1875l1.546875 0l0 8.515625q0 2.3125 -0.46875 3.265625q-0.46875 0.96875 -1.484375 1.515625q-1.015625 0.5625305 -2.5 0.5625305q-1.765625 0 -2.859375 -0.7969055q-1.078125 -0.796875 -1.03125 -2.390625zm1.375 -5.921875q0 1.953125 0.765625 2.84375q0.78125 0.890625 1.9375 0.890625q1.140625 0 1.921875 -0.890625q0.78125 -0.890625 0.78125 -2.78125q0 -1.8125 -0.8125 -2.71875q-0.796875 -0.921875 -1.921875 -0.921875q-1.109375 0 -1.890625 0.90625q-0.78125 0.890625 -0.78125 2.671875zm8.047607 5.34375l3.9375 -14.0625l1.34375 0l-3.9375 14.0625l-1.34375 0zm6.4332886 3.546875l0 -13.640625l1.53125 0l0 1.28125q0.53125 -0.75 1.203125 -1.125q0.6875 -0.375 1.640625 -0.375q1.265625 0 2.234375 0.65625q0.96875 0.640625 1.453125 1.828125q0.5 1.1875 0.5 2.59375q0 1.515625 -0.546875 2.734375q-0.546875 1.203125 -1.578125 1.84375q-1.03125 0.640625 -2.171875 0.640625q-0.84375 0 -1.515625 -0.34375q-0.65625 -0.359375 -1.078125 -0.890625l0 4.796875l-1.671875 0zm1.515625 -8.65625q0 1.90625 0.765625 2.8125q0.78125 0.90625 1.875 0.90625q1.109375 0 1.890625 -0.9375q0.796875 -0.9375 0.796875 -2.921875q0 -1.875 -0.78125 -2.8125q-0.765625 -0.9375 -1.84375 -0.9375q-1.0625 0 -1.890625 1.0q-0.8125 1.0 -0.8125 2.890625zm8.844482 4.875l0 -9.859375l1.5 0l0 1.5q0.578125 -1.046875 1.0625 -1.375q0.484375 -0.34375 1.078125 -0.34375q0.84375 0 1.71875 0.546875l-0.578125 1.546875q-0.609375 -0.359375 -1.234375 -0.359375q-0.546875 0 -0.984375 0.328125q-0.421875 0.328125 -0.609375 0.90625q-0.28125 0.890625 -0.28125 1.953125l0 5.15625l-1.671875 0zm5.6032715 -4.921875q0 -2.734375 1.53125 -4.0625q1.265625 -1.09375 3.09375 -1.09375q2.03125 0 3.3125 1.34375q1.296875 1.328125 1.296875 3.671875q0 1.90625 -0.578125 3.0q-0.5625 1.078125 -1.65625 1.6875q-1.078125 0.59375 -2.375 0.59375q-2.0625 0 -3.34375 -1.328125q-1.28125 -1.328125 -1.28125 -3.8125zm1.71875 0q0 1.890625 0.828125 2.828125q0.828125 0.9375 2.078125 0.9375q1.25 0 2.0625 -0.9375q0.828125 -0.953125 0.828125 -2.890625q0 -1.828125 -0.828125 -2.765625q-0.828125 -0.9375 -2.0625 -0.9375q-1.25 0 -2.078125 0.9375q-0.828125 0.9375 -0.828125 2.828125zm9.281982 -6.734375l0 -1.9375l1.65625 0l0 1.9375l-1.65625 0zm-2.125 15.4844055l0.3125 -1.4219055q0.5 0.125 0.796875 0.125q0.515625 0 0.765625 -0.34375q0.25 -0.328125 0.25 -1.6875l0 -10.359375l1.65625 0l0 10.390625q0 1.828125 -0.46875 2.546875q-0.59375 0.9219055 -2.0 0.9219055q-0.671875 0 -1.3125 -0.171875zm13.019836 -7.0000305l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm15.547577 2.265625l1.640625 0.21875q-0.265625 1.6875 -1.375 2.65625q-1.109375 0.953125 -2.734375 0.953125q-2.015625 0 -3.25 -1.3125q-1.21875 -1.328125 -1.21875 -3.796875q0 -1.59375 0.515625 -2.78125q0.53125 -1.203125 1.609375 -1.796875q1.09375 -0.609375 2.359375 -0.609375q1.609375 0 2.625 0.8125q1.015625 0.8125 1.3125 2.3125l-1.625 0.25q-0.234375 -1.0 -0.828125 -1.5q-0.59375 -0.5 -1.421875 -0.5q-1.265625 0 -2.0625 0.90625q-0.78125 0.90625 -0.78125 2.859375q0 1.984375 0.765625 2.890625q0.765625 0.890625 1.984375 0.890625q0.984375 0 1.640625 -0.59375q0.65625 -0.609375 0.84375 -1.859375zm6.546875 2.109375l0.234375 1.484375q-0.703125 0.140625 -1.265625 0.140625q-0.90625 0 -1.40625 -0.28125q-0.5 -0.296875 -0.703125 -0.75q-0.203125 -0.46875 -0.203125 -1.984375l0 -5.65625l-1.234375 0l0 -1.3125l1.234375 0l0 -2.4375l1.65625 -1.0l0 3.4375l1.6875 0l0 1.3125l-1.6875 0l0 5.75q0 0.71875 0.078125 0.921875q0.09375 0.203125 0.296875 0.328125q0.203125 0.125 0.578125 0.125q0.265625 0 0.734375 -0.078125zm0.8551941 -1.4375l1.65625 -0.265625q0.140625 1.0 0.765625 1.53125q0.640625 0.515625 1.78125 0.515625q1.15625 0 1.703125 -0.46875q0.5625 -0.46875 0.5625 -1.09375q0 -0.5625 -0.484375 -0.890625q-0.34375 -0.21875 -1.703125 -0.5625q-1.84375 -0.46875 -2.5625 -0.796875q-0.703125 -0.34375 -1.078125 -0.9375q-0.359375 -0.609375 -0.359375 -1.328125q0 -0.65625 0.296875 -1.21875q0.3125 -0.5625 0.828125 -0.9375q0.390625 -0.28125 1.0625 -0.484375q0.671875 -0.203125 1.4375 -0.203125q1.171875 0 2.046875 0.34375q0.875 0.328125 1.28125 0.90625q0.421875 0.5625 0.578125 1.515625l-1.625 0.21875q-0.109375 -0.75 -0.65625 -1.171875q-0.53125 -0.4375 -1.5 -0.4375q-1.15625 0 -1.640625 0.390625q-0.484375 0.375 -0.484375 0.875q0 0.328125 0.203125 0.59375q0.203125 0.265625 0.640625 0.4375q0.25 0.09375 1.46875 0.4375q1.765625 0.46875 2.46875 0.765625q0.703125 0.296875 1.09375 0.875q0.40625 0.578125 0.40625 1.4375q0 0.828125 -0.484375 1.578125q-0.484375 0.734375 -1.40625 1.140625q-0.921875 0.390625 -2.078125 0.390625q-1.921875 0 -2.9375 -0.796875q-1.0 -0.796875 -1.28125 -2.359375zm8.7499695 3.171875l3.9375 -14.0625l1.34375 0l-3.9375 14.0625l-1.34375 0zm12.870789 -1.453125q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm4.0632324 4.9375l0 -9.859375l1.5 0l0 1.5q0.578125 -1.046875 1.0625 -1.375q0.484375 -0.34375 1.078125 -0.34375q0.84375 0 1.71875 0.546875l-0.578125 1.546875q-0.609375 -0.359375 -1.234375 -0.359375q-0.546875 0 -0.984375 0.328125q-0.421875 0.328125 -0.609375 0.90625q-0.28125 0.890625 -0.28125 1.953125l0 5.15625l-1.671875 0zm8.962677 0l-3.75 -9.859375l1.765625 0l2.125 5.90625q0.34375 0.953125 0.625 1.984375q0.21875 -0.78125 0.625 -1.875l2.1875 -6.015625l1.71875 0l-3.734375 9.859375l-1.5625 0zm13.03125 -1.21875q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm10.469452 4.9375l0 -1.25q-0.9375 1.46875 -2.75 1.46875q-1.171875 0 -2.171875 -0.640625q-0.984375 -0.65625 -1.53125 -1.8125q-0.53125 -1.171875 -0.53125 -2.6875q0 -1.46875 0.484375 -2.671875q0.5 -1.203125 1.46875 -1.84375q0.984375 -0.640625 2.203125 -0.640625q0.890625 0 1.578125 0.375q0.703125 0.375 1.140625 0.984375l0 -4.875l1.65625 0l0 13.59375l-1.546875 0zm-5.28125 -4.921875q0 1.890625 0.796875 2.828125q0.8125 0.9375 1.890625 0.9375q1.09375 0 1.859375 -0.890625q0.765625 -0.890625 0.765625 -2.734375q0 -2.015625 -0.78125 -2.953125q-0.78125 -0.953125 -1.921875 -0.953125q-1.109375 0 -1.859375 0.90625q-0.75 0.90625 -0.75 2.859375zm8.641357 0q0 -2.734375 1.53125 -4.0625q1.265625 -1.09375 3.09375 -1.09375q2.03125 0 3.3125 1.34375q1.296875 1.328125 1.296875 3.671875q0 1.90625 -0.578125 3.0q-0.5625 1.078125 -1.65625 1.6875q-1.078125 0.59375 -2.375 0.59375q-2.0625 0 -3.34375 -1.328125q-1.28125 -1.328125 -1.28125 -3.8125zm1.71875 0q0 1.890625 0.828125 2.828125q0.828125 0.9375 2.078125 0.9375q1.25 0 2.0625 -0.9375q0.828125 -0.953125 0.828125 -2.890625q0 -1.828125 -0.828125 -2.765625q-0.828125 -0.9375 -2.0625 -0.9375q-1.25 0 -2.078125 0.9375q-0.828125 0.9375 -0.828125 2.828125zm8.610107 1.984375l1.65625 -0.265625q0.140625 1.0 0.765625 1.53125q0.640625 0.515625 1.7812805 0.515625q1.15625 0 1.703125 -0.46875q0.5625 -0.46875 0.5625 -1.09375q0 -0.5625 -0.484375 -0.890625q-0.34375 -0.21875 -1.703125 -0.5625q-1.8437805 -0.46875 -2.5625305 -0.796875q-0.703125 -0.34375 -1.078125 -0.9375q-0.359375 -0.609375 -0.359375 -1.328125q0 -0.65625 0.296875 -1.21875q0.3125 -0.5625 0.828125 -0.9375q0.390625 -0.28125 1.0625 -0.484375q0.671875 -0.203125 1.4375305 -0.203125q1.171875 0 2.046875 0.34375q0.875 0.328125 1.28125 0.90625q0.421875 0.5625 0.578125 1.515625l-1.625 0.21875q-0.109375 -0.75 -0.65625 -1.171875q-0.53125 -0.4375 -1.5 -0.4375q-1.1562805 0 -1.6406555 0.390625q-0.484375 0.375 -0.484375 0.875q0 0.328125 0.203125 0.59375q0.203125 0.265625 0.640625 0.4375q0.25 0.09375 1.4687805 0.4375q1.765625 0.46875 2.46875 0.765625q0.703125 0.296875 1.09375 0.875q0.40625 0.578125 0.40625 1.4375q0 0.828125 -0.484375 1.578125q-0.484375 0.734375 -1.40625 1.140625q-0.921875 0.390625 -2.078125 0.390625q-1.9219055 0 -2.9375305 -0.796875q-1.0 -0.796875 -1.28125 -2.359375zm8.7500305 3.171875l3.9375 -14.0625l1.34375 0l-3.9375 14.0625l-1.34375 0zm8.261414 -0.234375l-3.015625 -9.859375l1.71875 0l1.5625 5.6875l0.59375 2.125q0.03125 -0.15625 0.5 -2.03125l1.578125 -5.78125l1.71875 0l1.46875 5.71875l0.484375 1.890625l0.578125 -1.90625l1.6875 -5.703125l1.625 0l-3.078125 9.859375l-1.734375 0l-1.578125 -5.90625l-0.375 -1.671875l-2.0 7.578125l-1.734375 0zm11.660461 -11.6875l0 -1.90625l1.671875 0l0 1.90625l-1.671875 0zm0 11.6875l0 -9.859375l1.671875 0l0 9.859375l-1.671875 0zm4.1448364 0l0 -13.59375l1.671875 0l0 7.75l3.953125 -4.015625l2.15625 0l-3.765625 3.65625l4.140625 6.203125l-2.0625 0l-3.25 -5.03125l-1.171875 1.125l0 3.90625l-1.671875 0zm9.328125 -11.6875l0 -1.90625l1.671875 0l0 1.90625l-1.671875 0zm0 11.6875l0 -9.859375l1.671875 0l0 9.859375l-1.671875 0zm2.8791504 0.234375l3.9375 -14.0625l1.34375 0l-3.9375 14.0625l-1.34375 0zm6.5739746 -0.234375l0 -13.59375l1.796875 0l0 6.734375l6.765625 -6.734375l2.4375 0l-5.703125 5.5l5.953125 8.09375l-2.375 0l-4.84375 -6.890625l-2.234375 2.171875l0 4.71875l-1.796875 0zm19.052917 -3.171875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm15.860046 2.703125l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm9.110107 9.65625l0 -13.640625l1.53125 0l0 1.28125q0.53125 -0.75 1.203125 -1.125q0.6875 -0.375 1.640625 -0.375q1.265625 0 2.234375 0.65625q0.96875 0.640625 1.453125 1.828125q0.5 1.1875 0.5 2.59375q0 1.515625 -0.546875 2.734375q-0.546875 1.203125 -1.578125 1.84375q-1.03125 0.640625 -2.171875 0.640625q-0.84375 0 -1.515625 -0.34375q-0.65625 -0.359375 -1.078125 -0.890625l0 4.796875l-1.671875 0zm1.515625 -8.65625q0 1.90625 0.765625 2.8125q0.78125 0.90625 1.875 0.90625q1.109375 0 1.890625 -0.9375q0.796875 -0.9375 0.796875 -2.921875q0 -1.875 -0.78125 -2.8125q-0.765625 -0.9375 -1.84375 -0.9375q-1.0625 0 -1.890625 1.0q-0.8125 1.0 -0.8125 2.890625zm7.3288574 8.65625l0 -1.21875l11.0625 0l0 1.21875l-11.0625 0zm11.906982 -3.78125l0 -9.859375l1.5 0l0 1.390625q0.453125 -0.71875 1.21875 -1.15625q0.78125 -0.453125 1.765625 -0.453125q1.09375 0 1.796875 0.453125q0.703125 0.453125 0.984375 1.28125q1.171875 -1.734375 3.046875 -1.734375q1.46875 0 2.25 0.8125q0.796875 0.8125 0.796875 2.5l0 6.765625l-1.671875 0l0 -6.203125q0 -1.0 -0.15625 -1.4375q-0.15625 -0.453125 -0.59375 -0.71875q-0.421875 -0.265625 -1.0 -0.265625q-1.03125 0 -1.71875 0.6875q-0.6875 0.6875 -0.6875 2.21875l0 5.71875l-1.671875 0l0 -6.40625q0 -1.109375 -0.40625 -1.65625q-0.40625 -0.5625 -1.34375 -0.5625q-0.703125 0 -1.3125 0.375q-0.59375 0.359375 -0.859375 1.078125q-0.265625 0.71875 -0.265625 2.0625l0 5.109375l-1.671875 0zm21.978333 -1.21875q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm4.0787964 4.9375l0 -9.859375l1.5 0l0 1.40625q1.09375 -1.625 3.140625 -1.625q0.890625 0 1.640625 0.328125q0.75 0.3125 1.109375 0.84375q0.375 0.515625 0.53125 1.21875q0.09375 0.46875 0.09375 1.625l0 6.0625l-1.671875 0l0 -6.0q0 -1.015625 -0.203125 -1.515625q-0.1875 -0.515625 -0.6875 -0.8125q-0.5 -0.296875 -1.171875 -0.296875q-1.0625 0 -1.84375 0.671875q-0.765625 0.671875 -0.765625 2.578125l0 5.375l-1.671875 0zm10.391357 -11.6875l0 -1.90625l1.671875 0l0 1.90625l-1.671875 0zm0 11.6875l0 -9.859375l1.671875 0l0 9.859375l-1.671875 0zm4.5355225 0l0 -8.546875l-1.484375 0l0 -1.3125l1.484375 0l0 -1.046875q0 -0.984375 0.171875 -1.46875q0.234375 -0.65625 0.84375 -1.046875q0.609375 -0.40625 1.703125 -0.40625q0.703125 0 1.5625 0.15625l-0.25 1.46875q-0.515625 -0.09375 -0.984375 -0.09375q-0.765625 0 -1.078125 0.328125q-0.3125 0.3125 -0.3125 1.203125l0 0.90625l1.921875 0l0 1.3125l-1.921875 0l0 8.546875l-1.65625 0zm11.526978 -3.171875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm8.438232 2.9375l1.65625 -0.265625q0.140625 1.0 0.765625 1.53125q0.640625 0.515625 1.78125 0.515625q1.15625 0 1.703125 -0.46875q0.5625 -0.46875 0.5625 -1.09375q0 -0.5625 -0.484375 -0.890625q-0.34375 -0.21875 -1.703125 -0.5625q-1.84375 -0.46875 -2.5625 -0.796875q-0.703125 -0.34375 -1.078125 -0.9375q-0.359375 -0.609375 -0.359375 -1.328125q0 -0.65625 0.296875 -1.21875q0.3125 -0.5625 0.828125 -0.9375q0.390625 -0.28125 1.0625 -0.484375q0.671875 -0.203125 1.4375 -0.203125q1.171875 0 2.046875 0.34375q0.875 0.328125 1.28125 0.90625q0.421875 0.5625 0.578125 1.515625l-1.625 0.21875q-0.109375 -0.75 -0.65625 -1.171875q-0.53125 -0.4375 -1.5 -0.4375q-1.15625 0 -1.640625 0.390625q-0.484375 0.375 -0.484375 0.875q0 0.328125 0.203125 0.59375q0.203125 0.265625 0.640625 0.4375q0.25 0.09375 1.46875 0.4375q1.765625 0.46875 2.46875 0.765625q0.703125 0.296875 1.09375 0.875q0.40625 0.578125 0.40625 1.4375q0 0.828125 -0.484375 1.578125q-0.484375 0.734375 -1.40625 1.140625q-0.921875 0.390625 -2.078125 0.390625q-1.921875 0 -2.9375 -0.796875q-1.0 -0.796875 -1.28125 -2.359375zm13.65625 1.4375l0.234375 1.484375q-0.703125 0.140625 -1.265625 0.140625q-0.90625 0 -1.40625 -0.28125q-0.5 -0.296875 -0.703125 -0.75q-0.203125 -0.46875 -0.203125 -1.984375l0 -5.65625l-1.234375 0l0 -1.3125l1.234375 0l0 -2.4375l1.65625 -1.0l0 3.4375l1.6875 0l0 1.3125l-1.6875 0l0 5.75q0 0.71875 0.078125 0.921875q0.09375 0.203125 0.296875 0.328125q0.203125 0.125 0.578125 0.125q0.265625 0 0.734375 -0.078125zm-0.0041503906 5.28125l0 -1.21875l11.0625 0l0 1.21875l-11.0625 0zm12.313232 -3.78125l0 -8.546875l-1.484375 0l0 -1.3125l1.484375 0l0 -1.046875q0 -0.984375 0.171875 -1.46875q0.234375 -0.65625 0.84375 -1.046875q0.609375 -0.40625 1.703125 -0.40625q0.703125 0 1.5625 0.15625l-0.25 1.46875q-0.515625 -0.09375 -0.984375 -0.09375q-0.765625 0 -1.078125 0.328125q-0.3125 0.3125 -0.3125 1.203125l0 0.90625l1.921875 0l0 1.3125l-1.921875 0l0 8.546875l-1.65625 0zm4.1519775 -4.921875q0 -2.734375 1.53125 -4.0625q1.265625 -1.09375 3.09375 -1.09375q2.03125 0 3.3125 1.34375q1.296875 1.328125 1.296875 3.671875q0 1.90625 -0.578125 3.0q-0.5625 1.078125 -1.65625 1.6875q-1.078125 0.59375 -2.375 0.59375q-2.0625 0 -3.34375 -1.328125q-1.28125 -1.328125 -1.28125 -3.8125zm1.71875 0q0 1.890625 0.828125 2.828125q0.828125 0.9375 2.078125 0.9375q1.25 0 2.0625 -0.9375q0.828125 -0.953125 0.828125 -2.890625q0 -1.828125 -0.828125 -2.765625q-0.828125 -0.9375 -2.0625 -0.9375q-1.25 0 -2.078125 0.9375q-0.828125 0.9375 -0.828125 2.828125zm9.266357 4.921875l0 -9.859375l1.5 0l0 1.5q0.578125 -1.046875 1.0625 -1.375q0.484375 -0.34375 1.078125 -0.34375q0.84375 0 1.71875 0.546875l-0.578125 1.546875q-0.609375 -0.359375 -1.234375 -0.359375q-0.546875 0 -0.984375 0.328125q-0.421875 0.328125 -0.609375 0.90625q-0.28125 0.890625 -0.28125 1.953125l0 5.15625l-1.671875 0zm6.2283936 0l0 -9.859375l1.5 0l0 1.390625q0.453125 -0.71875 1.21875 -1.15625q0.78125 -0.453125 1.765625 -0.453125q1.09375 0 1.796875 0.453125q0.703125 0.453125 0.984375 1.28125q1.171875 -1.734375 3.046875 -1.734375q1.46875 0 2.25 0.8125q0.796875 0.8125 0.796875 2.5l0 6.765625l-1.671875 0l0 -6.203125q0 -1.0 -0.15625 -1.4375q-0.15625 -0.453125 -0.59375 -0.71875q-0.421875 -0.265625 -1.0 -0.265625q-1.03125 0 -1.71875 0.6875q-0.6875 0.6875 -0.6875 2.21875l0 5.71875l-1.671875 0l0 -6.40625q0 -1.109375 -0.40625 -1.65625q-0.40625 -0.5625 -1.34375 -0.5625q-0.703125 0 -1.3125 0.375q-0.59375 0.359375 -0.859375 1.078125q-0.265625 0.71875 -0.265625 2.0625l0 5.109375l-1.671875 0zm21.978271 -1.21875q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm7.7351074 3.4375l0.234375 1.484375q-0.703125 0.140625 -1.265625 0.140625q-0.90625 0 -1.40625 -0.28125q-0.5 -0.296875 -0.703125 -0.75q-0.203125 -0.46875 -0.203125 -1.984375l0 -5.65625l-1.234375 0l0 -1.3125l1.234375 0l0 -2.4375l1.65625 -1.0l0 3.4375l1.6875 0l0 1.3125l-1.6875 0l0 5.75q0 0.71875 0.078125 0.921875q0.09375 0.203125 0.296875 0.328125q0.203125 0.125 0.578125 0.125q0.265625 0 0.734375 -0.078125z" fill-rule="nonzero"></path><path stroke="#0097a7" stroke-width="1.3671875" stroke-linecap="butt" d="m185.21445 510.1761l560.99927 0" fill-rule="nonzero"></path><a xlink:href="https://www.google.com/url?q=https://dev.arvados.org/projects/arvados/wiki/Keep_manifest_format&amp;sa=D&amp;ust=1478895969188000&amp;usg=AFQjCNHMNIzr5ezz4laFKPqTOrFHC9sgsA" target="_blank" rel="noreferrer"><path fill="transparent" fill-opacity="0" d="m185.21445 512.15173l0 -20.84253l560.99927 0l0 20.84253z" fill-rule="evenodd"></path></a><path fill="#000000" fill-opacity="0.0" d="m178.12337 46.721786l600.2048 0l0 473.36218l-600.2048 0z" fill-rule="nonzero"></path><g transform="matrix(0.6538178477690288 0.0 0.0 0.6538152230971128 178.12336036745407 46.72178477690289)"><clipPath id="g1586814eb6_0_6.1"><path d="m0 1.4210855E-14l918.0 0l0 724.0l-918.0 0z" clip-rule="nonzero"></path></clipPath><image clip-path="url(#g1586814eb6_0_6.1)" fill="#000" width="918.0" height="724.0" x="0.0" y="0.0" preserveAspectRatio="none" xlink:href="data:image/png;base64,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"></image></g></g></svg>
+
diff --git a/doc/images/Keep_reading_writing_block.svg b/doc/images/Keep_reading_writing_block.svg
new file mode 100644 (file)
index 0000000..3333eab
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" standalone="yes"?>
+
+<svg version="1.1" viewBox="0.0 0.0 960.0 540.0" fill="none" stroke="none" stroke-linecap="square" stroke-miterlimit="10" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><clipPath id="g150061aa58_0_0.0"><path d="m0 0l960.0 0l0 540.0l-960.0 0l0 -540.0z" clip-rule="nonzero"></path></clipPath><g clip-path="url(#g150061aa58_0_0.0)"><path fill="#ffffff" d="m0 0l960.0 0l0 540.0l-960.0 0z" fill-rule="nonzero"></path><path fill="#eeeeee" d="m312.64752 161.47145c-1.304718 -6.8049164 2.9787598 -13.541397 11.032776 -17.350906c8.053986 -3.8095093 18.466064 -4.023941 26.817963 -0.5523224l0 0c2.9583435 -3.9569397 8.373169 -6.6889496 14.606506 -7.3696136c6.2333374 -0.6806488 12.55304 0.769989 17.047424 3.9131317l0 0c2.5202026 -3.5875702 7.46875 -5.9979553 13.08963 -6.375824c5.6208496 -0.37786865 11.1185 1.3302612 14.542084 4.5182495l0 0c4.5532227 -3.8025818 11.797455 -5.40361 18.598114 -4.110321c6.800659 1.2933044 11.936279 5.2486267 13.184662 10.1545105l0 0c5.5782166 1.0799713 10.224731 3.8253632 12.739075 7.5268555c2.5143433 3.7014923 2.6498718 7.995987 0.3715515 11.773926l0 0c5.4933167 5.074188 6.778412 11.835541 3.375702 17.760818c-3.40271 5.9252777 -10.982025 10.124237 -19.909393 11.029907c-0.062927246 5.561264 -4.359955 10.664215 -11.234833 13.341904c-6.874878 2.6776886 -15.253998 2.5119781 -21.907654 -0.43325806c-2.8341675 6.660553 -10.811493 11.56134 -20.485474 12.585007c-9.673981 1.0236511 -19.310303 -2.013321 -24.745697 -7.798828c-6.662628 2.851654 -14.657257 3.6731415 -22.18042 2.279129c-7.5231934 -1.3939972 -13.941193 -4.8860626 -17.806213 -9.688431l0 0c-6.808319 0.565506 -13.391052 -1.9381409 -16.481201 -6.268387c-3.0901794 -4.330246 -2.0299683 -9.565308 2.6544495 -13.107056l0 0c-6.073517 -2.53714 -9.172607 -7.571686 -7.6812134 -12.478317c1.491394 -4.9066315 7.2353516 -8.573486 14.236572 -9.08844z" fill-rule="nonzero"></path><path fill="#eeeeee" d="m347.71796 228.58267c0 1.2978516 -1.052124 2.3499603 -2.3499756 2.3499603c-1.297821 0 -2.349945 -1.0521088 -2.349945 -2.3499603c0 -1.2978363 1.052124 -2.349945 2.349945 -2.349945c1.2978516 0 2.3499756 1.0521088 2.3499756 2.349945z" fill-rule="nonzero"></path><path fill="#eeeeee" d="m351.94028 225.65413c0 2.5956879 -2.1042175 4.6999207 -4.69989 4.6999207c-2.5957031 0 -4.6999207 -2.1042328 -4.6999207 -4.6999207c0 -2.5956879 2.1042175 -4.6999054 4.6999207 -4.6999054c2.5956726 0 4.69989 2.1042175 4.69989 4.6999054z" fill-rule="nonzero"></path><path fill="#eeeeee" d="m358.6943 218.76582c0 3.8935394 -3.1563416 7.0498657 -7.0498657 7.0498657c-3.8935242 0 -7.0498657 -3.1563263 -7.0498657 -7.0498657c0 -3.8935394 3.1563416 -7.0498657 7.0498657 -7.0498657c3.8935242 0 7.0498657 3.1563263 7.0498657 7.0498657z" fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0" d="m315.61838 184.52805l0 0c-3.3113708 0.18397522 -6.6223755 -0.35913086 -9.488464 -1.5564117m17.860107 18.588898c-1.331543 0.3761902 -2.7273254 0.6266632 -4.151306 0.7449341m39.921875 7.4392548l0 0c-1.0014954 -1.0660095 -1.8399963 -2.2051392 -2.5012207 -3.3979645m48.75699 -5.0752716c-0.14724731 1.264389 -0.48202515 2.5141602 -0.99871826 3.7284546m20.857605 -26.765305l0 0c7.5073853 2.592987 12.245026 8.014023 12.17807 13.934677m16.547363 -28.776062c-1.2158203 2.0160675 -3.0718994 3.8045044 -5.422699 5.225067m-7.589813 -24.612915l0 0c0.20715332 0.81414795 0.30303955 1.6405334 0.28631592 2.4677124m-34.918304 -5.346176l0 0c0.7065735 -1.1384583 1.642334 -2.198471 2.7781372 -3.1470337m-28.761261 4.6465454l0 0c0.28826904 -0.9408264 0.74020386 -1.8525238 1.345459 -2.7141113m-31.83963 3.6362305l0 0c1.767273 0.7345886 3.4022217 1.6187592 4.8689575 2.6330566m-41.849762 18.061127l0 0c-0.3857727 -0.9061127 -0.6699219 -1.8324738 -0.84973145 -2.7703857" fill-rule="nonzero"></path><path stroke="#595959" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m312.64752 161.47145c-1.304718 -6.8049164 2.9787598 -13.541397 11.032776 -17.350906c8.053986 -3.8095093 18.466064 -4.023941 26.817963 -0.5523224l0 0c2.9583435 -3.9569397 8.373169 -6.6889496 14.606506 -7.3696136c6.2333374 -0.6806488 12.55304 0.769989 17.047424 3.9131317l0 0c2.5202026 -3.5875702 7.46875 -5.9979553 13.08963 -6.375824c5.6208496 -0.37786865 11.1185 1.3302612 14.542084 4.5182495l0 0c4.5532227 -3.8025818 11.797455 -5.40361 18.598114 -4.110321c6.800659 1.2933044 11.936279 5.2486267 13.184662 10.1545105l0 0c5.5782166 1.0799713 10.224731 3.8253632 12.739075 7.5268555c2.5143433 3.7014923 2.6498718 7.995987 0.3715515 11.773926l0 0c5.4933167 5.074188 6.778412 11.835541 3.375702 17.760818c-3.40271 5.9252777 -10.982025 10.124237 -19.909393 11.029907c-0.062927246 5.561264 -4.359955 10.664215 -11.234833 13.341904c-6.874878 2.6776886 -15.253998 2.5119781 -21.907654 -0.43325806c-2.8341675 6.660553 -10.811493 11.56134 -20.485474 12.585007c-9.673981 1.0236511 -19.310303 -2.013321 -24.745697 -7.798828c-6.662628 2.851654 -14.657257 3.6731415 -22.18042 2.279129c-7.5231934 -1.3939972 -13.941193 -4.8860626 -17.806213 -9.688431l0 0c-6.808319 0.565506 -13.391052 -1.9381409 -16.481201 -6.268387c-3.0901794 -4.330246 -2.0299683 -9.565308 2.6544495 -13.107056l0 0c-6.073517 -2.53714 -9.172607 -7.571686 -7.6812134 -12.478317c1.491394 -4.9066315 7.2353516 -8.573486 14.236572 -9.08844z" fill-rule="nonzero"></path><path stroke="#595959" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m347.71796 228.58267c0 1.2978516 -1.052124 2.3499603 -2.3499756 2.3499603c-1.297821 0 -2.349945 -1.0521088 -2.349945 -2.3499603c0 -1.2978363 1.052124 -2.349945 2.349945 -2.349945c1.2978516 0 2.3499756 1.0521088 2.3499756 2.349945z" fill-rule="nonzero"></path><path stroke="#595959" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m351.94028 225.65413c0 2.5956879 -2.1042175 4.6999207 -4.69989 4.6999207c-2.5957031 0 -4.6999207 -2.1042328 -4.6999207 -4.6999207c0 -2.5956879 2.1042175 -4.6999054 4.6999207 -4.6999054c2.5956726 0 4.69989 2.1042175 4.69989 4.6999054z" fill-rule="nonzero"></path><path stroke="#595959" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m358.6943 218.76582c0 3.8935394 -3.1563416 7.0498657 -7.0498657 7.0498657c-3.8935242 0 -7.0498657 -3.1563263 -7.0498657 -7.0498657c0 -3.8935394 3.1563416 -7.0498657 7.0498657 -7.0498657c3.8935242 0 7.0498657 3.1563263 7.0498657 7.0498657z" fill-rule="nonzero"></path><path stroke="#595959" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m315.61838 184.52805l0 0c-3.3113708 0.18397522 -6.6223755 -0.35913086 -9.488464 -1.5564117m17.860107 18.588898c-1.331543 0.3761902 -2.7273254 0.6266632 -4.151306 0.7449341m39.921875 7.4392548l0 0c-1.0014954 -1.0660095 -1.8399963 -2.2051392 -2.5012207 -3.3979645m48.75699 -5.0752716c-0.14724731 1.264389 -0.48202515 2.5141602 -0.99871826 3.7284546m20.857605 -26.765305l0 0c7.5073853 2.592987 12.245026 8.014023 12.17807 13.934677m16.547363 -28.776062c-1.2158203 2.0160675 -3.0718994 3.8045044 -5.422699 5.225067m-7.589813 -24.612915l0 0c0.20715332 0.81414795 0.30303955 1.6405334 0.28631592 2.4677124m-34.918304 -5.346176l0 0c0.7065735 -1.1384583 1.642334 -2.198471 2.7781372 -3.1470337m-28.761261 4.6465454l0 0c0.28826904 -0.9408264 0.74020386 -1.8525238 1.345459 -2.7141113m-31.83963 3.6362305l0 0c1.767273 0.7345886 3.4022217 1.6187592 4.8689575 2.6330566m-41.849762 18.061127l0 0c-0.3857727 -0.9061127 -0.6699219 -1.8324738 -0.84973145 -2.7703857" fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0" d="m32.72441 46.721786l894.55115 0l0 60.125984l-894.55115 0z" fill-rule="nonzero"></path><path fill="#000000" d="m44.63066 91.56178l0 -26.484375l11.75 0q3.53125 0 5.375 0.71875q1.84375 0.703125 2.9375 2.515625q1.109375 1.8125 1.109375 3.984375q0 2.828125 -1.828125 4.765625q-1.8125 1.921875 -5.625 2.453125q1.390625 0.671875 2.109375 1.3125q1.53125 1.40625 2.90625 3.53125l4.609375 7.203125l-4.40625 0l-3.5 -5.515625q-1.546875 -2.375 -2.546875 -3.640625q-0.984375 -1.265625 -1.765625 -1.765625q-0.78125 -0.515625 -1.59375 -0.71875q-0.609375 -0.125 -1.953125 -0.125l-4.078125 0l0 11.765625l-3.5 0zm3.5 -14.796875l7.546875 0q2.390625 0 3.75 -0.5q1.359375 -0.5 2.0625 -1.578125q0.703125 -1.09375 0.703125 -2.390625q0 -1.875 -1.375 -3.078125q-1.359375 -1.21875 -4.296875 -1.21875l-8.390625 0l0 8.765625zm36.12906 8.625l3.359375 0.40625q-0.796875 2.953125 -2.953125 4.578125q-2.140625 1.625 -5.484375 1.625q-4.21875 0 -6.6875 -2.59375q-2.453125 -2.59375 -2.453125 -7.28125q0 -4.828125 2.484375 -7.5q2.5 -2.6875 6.46875 -2.6875q3.859375 0 6.296875 2.625q2.4375 2.625 2.4375 7.375q0 0.28125 -0.015625 0.859375l-14.3125 0q0.171875 3.171875 1.78125 4.859375q1.609375 1.671875 4.015625 1.671875q1.78125 0 3.046875 -0.9375q1.265625 -0.953125 2.015625 -3.0zm-10.6875 -5.265625l10.71875 0q-0.21875 -2.421875 -1.234375 -3.625q-1.546875 -1.890625 -4.015625 -1.890625q-2.25 0 -3.78125 1.5q-1.515625 1.5 -1.6875 4.015625zm30.822632 9.078125q-1.796875 1.53125 -3.46875 2.171875q-1.671875 0.625 -3.59375 0.625q-3.15625 0 -4.859375 -1.546875q-1.6875 -1.546875 -1.6875 -3.953125q0 -1.40625 0.640625 -2.5625q0.640625 -1.171875 1.671875 -1.875q1.046875 -0.703125 2.34375 -1.0625q0.953125 -0.265625 2.890625 -0.5q3.9375 -0.46875 5.796875 -1.109375q0.015625 -0.671875 0.015625 -0.859375q0 -1.984375 -0.921875 -2.796875q-1.25 -1.09375 -3.703125 -1.09375q-2.296875 0 -3.390625 0.796875q-1.09375 0.796875 -1.609375 2.84375l-3.1875 -0.4375q0.4375 -2.03125 1.421875 -3.28125q1.0 -1.265625 2.875 -1.9375q1.890625 -0.6875 4.359375 -0.6875q2.46875 0 4.0 0.578125q1.53125 0.578125 2.25 1.453125q0.734375 0.875 1.015625 2.21875q0.15625 0.828125 0.15625 3.0l0 4.328125q0 4.546875 0.203125 5.75q0.21875 1.1875 0.828125 2.296875l-3.390625 0q-0.5 -1.015625 -0.65625 -2.359375zm-0.265625 -7.265625q-1.765625 0.71875 -5.3125 1.21875q-2.0 0.296875 -2.84375 0.65625q-0.828125 0.359375 -1.28125 1.0625q-0.4375 0.6875 -0.4375 1.53125q0 1.3125 0.984375 2.1875q0.984375 0.859375 2.875 0.859375q1.875 0 3.34375 -0.828125q1.46875 -0.828125 2.15625 -2.25q0.515625 -1.09375 0.515625 -3.25l0 -1.1875zm20.963257 9.625l0 -2.421875q-1.828125 2.859375 -5.375 2.859375q-2.28125 0 -4.21875 -1.265625q-1.921875 -1.265625 -2.984375 -3.53125q-1.046875 -2.265625 -1.046875 -5.21875q0 -2.875 0.953125 -5.203125q0.96875 -2.34375 2.875 -3.59375q1.921875 -1.25 4.28125 -1.25q1.734375 0 3.09375 0.734375q1.359375 0.734375 2.203125 1.90625l0 -9.5l3.234375 0l0 26.484375l-3.015625 0zm-10.28125 -9.578125q0 3.6875 1.546875 5.515625q1.5625 1.828125 3.671875 1.828125q2.125 0 3.609375 -1.75q1.5 -1.75 1.5 -5.3125q0 -3.953125 -1.515625 -5.78125q-1.515625 -1.84375 -3.734375 -1.84375q-2.171875 0 -3.625 1.765625q-1.453125 1.765625 -1.453125 5.578125zm18.603882 -13.171875l0 -3.734375l3.25 0l0 3.734375l-3.25 0zm0 22.75l0 -19.1875l3.25 0l0 19.1875l-3.25 0zm8.277496 0l0 -19.1875l2.921875 0l0 2.734375q2.125 -3.171875 6.109375 -3.171875q1.734375 0 3.1875 0.625q1.453125 0.625 2.171875 1.640625q0.734375 1.015625 1.015625 2.40625q0.1875 0.890625 0.1875 3.15625l0 11.796875l-3.25 0l0 -11.671875q0 -1.984375 -0.390625 -2.96875q-0.375 -0.984375 -1.34375 -1.5625q-0.953125 -0.59375 -2.265625 -0.59375q-2.078125 0 -3.59375 1.3125q-1.5 1.3125 -1.5 5.0l0 10.484375l-3.25 0zm20.166382 1.59375l3.15625 0.46875q0.203125 1.453125 1.109375 2.125q1.203125 0.90625 3.296875 0.90625q2.265625 0 3.484375 -0.90625q1.234375 -0.90625 1.671875 -2.53125q0.25 -0.984375 0.234375 -4.171875q-2.125 2.515625 -5.3125 2.515625q-3.953125 0 -6.125 -2.84375q-2.171875 -2.859375 -2.171875 -6.859375q0 -2.75 1.0 -5.0625q1.0 -2.328125 2.875 -3.59375q1.890625 -1.265625 4.4375 -1.265625q3.40625 0 5.609375 2.75l0 -2.3125l3.0 0l0 16.578125q0 4.484375 -0.921875 6.359375q-0.90625 1.875 -2.890625 2.953125q-1.96875 1.078125 -4.859375 1.078125q-3.4375 0 -5.5625 -1.546875q-2.109375 -1.53125 -2.03125 -4.640625zm2.6875 -11.53125q0 3.78125 1.5 5.515625q1.5 1.734375 3.765625 1.734375q2.234375 0 3.75 -1.71875q1.515625 -1.734375 1.515625 -5.421875q0 -3.515625 -1.5625 -5.296875q-1.5625 -1.796875 -3.765625 -1.796875q-2.171875 0 -3.6875 1.765625q-1.515625 1.75 -1.515625 5.21875zm16.228882 10.390625l7.671875 -27.390625l2.609375 0l-7.65625 27.390625l-2.625 0zm16.355331 -0.453125l-5.875 -19.1875l3.359375 0l3.046875 11.078125l1.140625 4.109375q0.078125 -0.296875 1.0 -3.953125l3.046875 -11.234375l3.34375 0l2.875 11.125l0.953125 3.671875l1.109375 -3.703125l3.28125 -11.09375l3.171875 0l-6.0 19.1875l-3.375 0l-3.0625 -11.484375l-0.734375 -3.28125l-3.890625 14.765625l-3.390625 0zm23.379059 0l0 -19.1875l2.921875 0l0 2.90625q1.125 -2.03125 2.0625 -2.6875q0.953125 -0.65625 2.09375 -0.65625q1.640625 0 3.34375 1.046875l-1.125 3.015625q-1.1875 -0.703125 -2.375 -0.703125q-1.078125 0 -1.921875 0.640625q-0.84375 0.640625 -1.203125 1.78125q-0.546875 1.734375 -0.546875 3.796875l0 10.046875l-3.25 0zm12.477432 -22.75l0 -3.734375l3.25 0l0 3.734375l-3.25 0zm0 22.75l0 -19.1875l3.25 0l0 19.1875l-3.25 0zm15.386871 -2.90625l0.46875 2.875q-1.375 0.28125 -2.46875 0.28125q-1.765625 0 -2.75 -0.5625q-0.96875 -0.5625 -1.375 -1.46875q-0.390625 -0.90625 -0.390625 -3.84375l0 -11.03125l-2.375 0l0 -2.53125l2.375 0l0 -4.75l3.234375 -1.953125l0 6.703125l3.28125 0l0 2.53125l-3.28125 0l0 11.21875q0 1.390625 0.171875 1.796875q0.171875 0.390625 0.5625 0.625q0.390625 0.234375 1.109375 0.234375q0.546875 0 1.4375 -0.125zm3.2772064 -19.84375l0 -3.734375l3.25 0l0 3.734375l-3.25 0zm0 22.75l0 -19.1875l3.25 0l0 19.1875l-3.25 0zm8.277496 0l0 -19.1875l2.921875 0l0 2.734375q2.125 -3.171875 6.109375 -3.171875q1.734375 0 3.1875 0.625q1.453125 0.625 2.171875 1.640625q0.734375 1.015625 1.015625 2.40625q0.1875 0.890625 0.1875 3.15625l0 11.796875l-3.25 0l0 -11.671875q0 -1.984375 -0.390625 -2.96875q-0.375 -0.984375 -1.34375 -1.5625q-0.953125 -0.59375 -2.265625 -0.59375q-2.078125 0 -3.59375 1.3125q-1.5 1.3125 -1.5 5.0l0 10.484375l-3.25 0zm20.166382 1.59375l3.15625 0.46875q0.203125 1.453125 1.109375 2.125q1.203125 0.90625 3.296875 0.90625q2.2655945 0 3.4843445 -0.90625q1.234375 -0.90625 1.671875 -2.53125q0.25 -0.984375 0.234375 -4.171875q-2.125 2.515625 -5.3124695 2.515625q-3.953125 0 -6.125 -2.84375q-2.171875 -2.859375 -2.171875 -6.859375q0 -2.75 1.0 -5.0625q1.0 -2.328125 2.875 -3.59375q1.890625 -1.265625 4.4375 -1.265625q3.4062195 0 5.6093445 2.75l0 -2.3125l3.0 0l0 16.578125q0 4.484375 -0.921875 6.359375q-0.90625 1.875 -2.890625 2.953125q-1.96875 1.078125 -4.8593445 1.078125q-3.4375 0 -5.5625 -1.546875q-2.109375 -1.53125 -2.03125 -4.640625zm2.6875 -11.53125q0 3.78125 1.5 5.515625q1.5 1.734375 3.765625 1.734375q2.2343445 0 3.7499695 -1.71875q1.515625 -1.734375 1.515625 -5.421875q0 -3.515625 -1.5625 -5.296875q-1.5625 -1.796875 -3.7655945 -1.796875q-2.171875 0 -3.6875 1.765625q-1.515625 1.75 -1.515625 5.21875zm41.552948 7.578125q-1.796875 1.53125 -3.46875 2.171875q-1.671875 0.625 -3.59375 0.625q-3.15625 0 -4.859375 -1.546875q-1.6875 -1.546875 -1.6875 -3.953125q0 -1.40625 0.640625 -2.5625q0.640625 -1.171875 1.671875 -1.875q1.046875 -0.703125 2.34375 -1.0625q0.953125 -0.265625 2.890625 -0.5q3.9375 -0.46875 5.796875 -1.109375q0.015625 -0.671875 0.015625 -0.859375q0 -1.984375 -0.921875 -2.796875q-1.25 -1.09375 -3.703125 -1.09375q-2.296875 0 -3.390625 0.796875q-1.09375 0.796875 -1.609375 2.84375l-3.1875 -0.4375q0.4375 -2.03125 1.421875 -3.28125q1.0 -1.265625 2.875 -1.9375q1.890625 -0.6875 4.359375 -0.6875q2.46875 0 4.0 0.578125q1.53125 0.578125 2.25 1.453125q0.734375 0.875 1.015625 2.21875q0.15625 0.828125 0.15625 3.0l0 4.328125q0 4.546875 0.203125 5.75q0.21875 1.1875 0.828125 2.296875l-3.390625 0q-0.5 -1.015625 -0.65625 -2.359375zm-0.265625 -7.265625q-1.765625 0.71875 -5.3125 1.21875q-2.0 0.296875 -2.84375 0.65625q-0.828125 0.359375 -1.28125 1.0625q-0.4375 0.6875 -0.4375 1.53125q0 1.3125 0.984375 2.1875q0.984375 0.859375 2.875 0.859375q1.875 0 3.34375 -0.828125q1.46875 -0.828125 2.15625 -2.25q0.515625 -1.09375 0.515625 -3.25l0 -1.1875zm21.881104 9.625l-3.015625 0l0 -26.484375l3.25 0l0 9.453125q2.0625 -2.59375 5.265625 -2.59375q1.765625 0 3.34375 0.71875q1.578125 0.71875 2.59375 2.015625q1.03125 1.28125 1.609375 3.109375q0.578125 1.828125 0.578125 3.90625q0 4.921875 -2.4375 7.625q-2.4375 2.6875 -5.859375 2.6875q-3.390625 0 -5.328125 -2.84375l0 2.40625zm-0.03125 -9.734375q0 3.453125 0.9375 4.984375q1.53125 2.515625 4.15625 2.515625q2.125 0 3.671875 -1.84375q1.5625 -1.859375 1.5625 -5.53125q0 -3.765625 -1.5 -5.546875q-1.484375 -1.796875 -3.59375 -1.796875q-2.125 0 -3.6875 1.859375q-1.546875 1.84375 -1.546875 5.359375zm17.713257 9.734375l0 -26.484375l3.265625 0l0 26.484375l-3.265625 0zm7.1681213 -9.59375q0 -5.328125 2.953125 -7.890625q2.484375 -2.140625 6.03125 -2.140625q3.96875 0 6.46875 2.59375q2.515625 2.59375 2.515625 7.171875q0 3.703125 -1.109375 5.828125q-1.109375 2.109375 -3.234375 3.296875q-2.125 1.171875 -4.640625 1.171875q-4.015625 0 -6.5 -2.578125q-2.484375 -2.59375 -2.484375 -7.453125zm3.34375 0q0 3.6875 1.59375 5.53125q1.609375 1.828125 4.046875 1.828125q2.421875 0 4.03125 -1.84375q1.609375 -1.84375 1.609375 -5.625q0 -3.5625 -1.625 -5.390625q-1.609375 -1.828125 -4.015625 -1.828125q-2.4375 0 -4.046875 1.828125q-1.59375 1.8125 -1.59375 5.5zm31.135132 2.5625l3.203125 0.421875q-0.515625 3.296875 -2.6875 5.171875q-2.15625 1.875 -5.296875 1.875q-3.9375 0 -6.328125 -2.578125q-2.390625 -2.578125 -2.390625 -7.375q0 -3.109375 1.015625 -5.4375q1.03125 -2.34375 3.140625 -3.5q2.109375 -1.171875 4.578125 -1.171875q3.125 0 5.109375 1.59375q2.0 1.578125 2.546875 4.484375l-3.15625 0.484375q-0.453125 -1.9375 -1.609375 -2.90625q-1.140625 -0.984375 -2.765625 -0.984375q-2.453125 0 -4.0 1.765625q-1.53125 1.765625 -1.53125 5.578125q0 3.859375 1.484375 5.625q1.484375 1.75 3.875 1.75q1.90625 0 3.1875 -1.171875q1.28125 -1.1875 1.625 -3.625zm6.1640625 7.03125l0 -26.484375l3.25 0l0 15.109375l7.703125 -7.8125l4.203125 0l-7.328125 7.125l8.078125 12.0625l-4.015625 0l-6.34375 -9.8125l-2.296875 2.203125l0 7.609375l-3.25 0z" fill-rule="nonzero"></path><path fill="#fff2cc" d="m440.2152 411.24408l75.1181 0l0 40.44095l-75.1181 0z" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m440.2152 411.24408l75.1181 0l0 40.44095l-75.1181 0z" fill-rule="nonzero"></path><path fill="#000000" d="m453.62442 438.38455l0 -13.59375l1.671875 0l0 7.75l3.953125 -4.015625l2.15625 0l-3.765625 3.65625l4.140625 6.203125l-2.0625 0l-3.25 -5.03125l-1.171875 1.125l0 3.90625l-1.671875 0zm16.0625 -3.171875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm15.860077 2.703125l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm9.110107 9.65625l0 -13.640625l1.53125 0l0 1.28125q0.53125 -0.75 1.203125 -1.125q0.6875 -0.375 1.640625 -0.375q1.265625 0 2.234375 0.65625q0.96875 0.640625 1.453125 1.828125q0.5 1.1875 0.5 2.59375q0 1.515625 -0.546875 2.734375q-0.546875 1.203125 -1.578125 1.84375q-1.03125 0.640625 -2.171875 0.640625q-0.84375 0 -1.515625 -0.34375q-0.65625 -0.359375 -1.078125 -0.890625l0 4.796875l-1.671875 0zm1.515625 -8.65625q0 1.90625 0.765625 2.8125q0.78125 0.90625 1.875 0.90625q1.109375 0 1.890625 -0.9375q0.796875 -0.9375 0.796875 -2.921875q0 -1.875 -0.78125 -2.8125q-0.765625 -0.9375 -1.84375 -0.9375q-1.0625 0 -1.890625 1.0q-0.8125 1.0 -0.8125 2.890625zm8.391327 -1.828125q0 -2.421875 0.5 -3.890625q0.5 -1.46875 1.46875 -2.265625q0.984375 -0.796875 2.46875 -0.796875q1.09375 0 1.921875 0.4375q0.828125 0.4375 1.359375 1.28125q0.546875 0.828125 0.84375 2.015625q0.3125 1.1875 0.3125 3.21875q0 2.390625 -0.5 3.859375q-0.484375 1.46875 -1.46875 2.28125q-0.96875 0.796875 -2.46875 0.796875q-1.96875 0 -3.078125 -1.40625q-1.359375 -1.703125 -1.359375 -5.53125zm1.71875 0q0 3.34375 0.78125 4.453125q0.796875 1.109375 1.9375 1.109375q1.15625 0 1.9375 -1.109375q0.78125 -1.125 0.78125 -4.453125q0 -3.359375 -0.78125 -4.46875q-0.78125 -1.109375 -1.953125 -1.109375q-1.15625 0 -1.828125 0.984375q-0.875 1.234375 -0.875 4.59375z" fill-rule="nonzero"></path><path fill="#fff2cc" d="m529.6798 411.24408l75.1181 0l0 40.44095l-75.1181 0z" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m529.6798 411.24408l75.1181 0l0 40.44095l-75.1181 0z" fill-rule="nonzero"></path><path fill="#000000" d="m543.089 438.38455l0 -13.59375l1.671875 0l0 7.75l3.953125 -4.015625l2.15625 0l-3.765625 3.65625l4.140625 6.203125l-2.0625 0l-3.25 -5.03125l-1.171875 1.125l0 3.90625l-1.671875 0zm16.0625 -3.171875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm15.860046 2.703125l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm9.110107 9.65625l0 -13.640625l1.53125 0l0 1.28125q0.53125 -0.75 1.203125 -1.125q0.6875 -0.375 1.640625 -0.375q1.265625 0 2.234375 0.65625q0.96875 0.640625 1.453125 1.828125q0.5 1.1875 0.5 2.59375q0 1.515625 -0.546875 2.734375q-0.546875 1.203125 -1.578125 1.84375q-1.03125 0.640625 -2.171875 0.640625q-0.84375 0 -1.515625 -0.34375q-0.65625 -0.359375 -1.078125 -0.890625l0 4.796875l-1.671875 0zm1.515625 -8.65625q0 1.90625 0.765625 2.8125q0.78125 0.90625 1.875 0.90625q1.109375 0 1.890625 -0.9375q0.796875 -0.9375 0.796875 -2.921875q0 -1.875 -0.78125 -2.8125q-0.765625 -0.9375 -1.84375 -0.9375q-1.0625 0 -1.890625 1.0q-0.8125 1.0 -0.8125 2.890625zm14.688232 4.875l-1.671875 0l0 -10.640625q-0.59375 0.578125 -1.578125 1.15625q-0.984375 0.5625 -1.765625 0.859375l0 -1.625q1.40625 -0.65625 2.453125 -1.59375q1.046875 -0.9375 1.484375 -1.8125l1.078125 0l0 13.65625z" fill-rule="nonzero"></path><path fill="#fff2cc" d="m619.14435 411.24408l75.1181 0l0 40.44095l-75.1181 0z" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m619.14435 411.24408l75.1181 0l0 40.44095l-75.1181 0z" fill-rule="nonzero"></path><path fill="#000000" d="m632.5535 438.38455l0 -13.59375l1.671875 0l0 7.75l3.953125 -4.015625l2.15625 0l-3.765625 3.65625l4.140625 6.203125l-2.0625 0l-3.25 -5.03125l-1.171875 1.125l0 3.90625l-1.671875 0zm16.0625 -3.171875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm15.860107 2.703125l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm9.110107 9.65625l0 -13.640625l1.53125 0l0 1.28125q0.53125 -0.75 1.203125 -1.125q0.6875 -0.375 1.640625 -0.375q1.265625 0 2.234375 0.65625q0.96875 0.640625 1.453125 1.828125q0.5 1.1875 0.5 2.59375q0 1.515625 -0.546875 2.734375q-0.546875 1.203125 -1.578125 1.84375q-1.03125 0.640625 -2.171875 0.640625q-0.84375 0 -1.515625 -0.34375q-0.65625 -0.359375 -1.078125 -0.890625l0 4.796875l-1.671875 0zm1.515625 -8.65625q0 1.90625 0.765625 2.8125q0.78125 0.90625 1.875 0.90625q1.109375 0 1.890625 -0.9375q0.796875 -0.9375 0.796875 -2.921875q0 -1.875 -0.78125 -2.8125q-0.765625 -0.9375 -1.84375 -0.9375q-1.0625 0 -1.890625 1.0q-0.8125 1.0 -0.8125 2.890625zm17.172546 3.265625l0 1.609375l-8.984375 0q-0.015625 -0.609375 0.1875 -1.15625q0.34375 -0.921875 1.09375 -1.8125q0.765625 -0.890625 2.1875 -2.0625q2.21875 -1.8125 3.0 -2.875q0.78125 -1.0625 0.78125 -2.015625q0 -0.984375 -0.71875 -1.671875q-0.703125 -0.6875 -1.84375 -0.6875q-1.203125 0 -1.9375 0.734375q-0.71875 0.71875 -0.71875 2.0l-1.71875 -0.171875q0.171875 -1.921875 1.328125 -2.921875q1.15625 -1.015625 3.09375 -1.015625q1.953125 0 3.09375 1.09375q1.140625 1.078125 1.140625 2.6875q0 0.8125 -0.34375 1.609375q-0.328125 0.78125 -1.109375 1.65625q-0.765625 0.859375 -2.5625 2.390625q-1.5 1.265625 -1.9375 1.71875q-0.421875 0.4375 -0.703125 0.890625l6.671875 0z" fill-rule="nonzero"></path><path fill="#fff2cc" d="m708.60895 411.24408l75.1181 0l0 40.44095l-75.1181 0z" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m708.60895 411.24408l75.1181 0l0 40.44095l-75.1181 0z" fill-rule="nonzero"></path><path fill="#000000" d="m722.0181 438.38455l0 -13.59375l1.671875 0l0 7.75l3.953125 -4.015625l2.15625 0l-3.765625 3.65625l4.140625 6.203125l-2.0625 0l-3.25 -5.03125l-1.171875 1.125l0 3.90625l-1.671875 0zm16.0625 -3.171875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm15.860046 2.703125l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm9.110107 9.65625l0 -13.640625l1.53125 0l0 1.28125q0.53125 -0.75 1.203125 -1.125q0.6875 -0.375 1.640625 -0.375q1.265625 0 2.234375 0.65625q0.96875 0.640625 1.453125 1.828125q0.5 1.1875 0.5 2.59375q0 1.515625 -0.546875 2.734375q-0.546875 1.203125 -1.578125 1.84375q-1.03125 0.640625 -2.171875 0.640625q-0.84375 0 -1.515625 -0.34375q-0.65625 -0.359375 -1.078125 -0.890625l0 4.796875l-1.671875 0zm1.515625 -8.65625q0 1.90625 0.765625 2.8125q0.78125 0.90625 1.875 0.90625q1.109375 0 1.890625 -0.9375q0.796875 -0.9375 0.796875 -2.921875q0 -1.875 -0.78125 -2.8125q-0.765625 -0.9375 -1.84375 -0.9375q-1.0625 0 -1.890625 1.0q-0.8125 1.0 -0.8125 2.890625zm8.406982 1.28125l1.671875 -0.21875q0.28125 1.421875 0.96875 2.046875q0.703125 0.625 1.6875 0.625q1.1875 0 2.0 -0.8125q0.8125 -0.828125 0.8125 -2.03125q0 -1.140625 -0.765625 -1.890625q-0.75 -0.75 -1.90625 -0.75q-0.46875 0 -1.171875 0.1875l0.1875 -1.46875q0.15625 0.015625 0.265625 0.015625q1.0625 0 1.90625 -0.546875q0.859375 -0.5625 0.859375 -1.71875q0 -0.921875 -0.625 -1.515625q-0.609375 -0.609375 -1.59375 -0.609375q-0.96875 0 -1.625 0.609375q-0.640625 0.609375 -0.828125 1.84375l-1.671875 -0.296875q0.296875 -1.6875 1.375 -2.609375q1.09375 -0.921875 2.71875 -0.921875q1.109375 0 2.046875 0.484375q0.9375 0.46875 1.421875 1.296875q0.5 0.828125 0.5 1.75q0 0.890625 -0.46875 1.609375q-0.46875 0.71875 -1.40625 1.15625q1.21875 0.265625 1.875 1.15625q0.671875 0.875 0.671875 2.1875q0 1.78125 -1.296875 3.015625q-1.296875 1.234375 -3.28125 1.234375q-1.796875 0 -2.984375 -1.0625q-1.171875 -1.0625 -1.34375 -2.765625z" fill-rule="nonzero"></path><path fill="#fff2cc" d="m798.0735 411.24408l75.1181 0l0 40.44095l-75.1181 0z" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m798.0735 411.24408l75.1181 0l0 40.44095l-75.1181 0z" fill-rule="nonzero"></path><path fill="#000000" d="m811.48267 438.38455l0 -13.59375l1.671875 0l0 7.75l3.953125 -4.015625l2.15625 0l-3.765625 3.65625l4.140625 6.203125l-2.0625 0l-3.25 -5.03125l-1.171875 1.125l0 3.90625l-1.671875 0zm16.0625 -3.171875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm15.860107 2.703125l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm9.110107 9.65625l0 -13.640625l1.53125 0l0 1.28125q0.53125 -0.75 1.203125 -1.125q0.6875 -0.375 1.640625 -0.375q1.265625 0 2.234375 0.65625q0.96875 0.640625 1.453125 1.828125q0.5 1.1875 0.5 2.59375q0 1.515625 -0.546875 2.734375q-0.546875 1.203125 -1.578125 1.84375q-1.03125 0.640625 -2.171875 0.640625q-0.84375 0 -1.515625 -0.34375q-0.65625 -0.359375 -1.078125 -0.890625l0 4.796875l-1.671875 0zm1.515625 -8.65625q0 1.90625 0.765625 2.8125q0.78125 0.90625 1.875 0.90625q1.109375 0 1.890625 -0.9375q0.796875 -0.9375 0.796875 -2.921875q0 -1.875 -0.78125 -2.8125q-0.765625 -0.9375 -1.84375 -0.9375q-1.0625 0 -1.890625 1.0q-0.8125 1.0 -0.8125 2.890625zm13.750671 4.875l0 -3.25l-5.90625 0l0 -1.53125l6.21875 -8.8125l1.359375 0l0 8.8125l1.84375 0l0 1.53125l-1.84375 0l0 3.25l-1.671875 0zm0 -4.78125l0 -6.140625l-4.25 6.140625l4.25 0z" fill-rule="nonzero"></path><path fill="#ead1dc" d="m260.48032 227.8727l75.1181 0l0 40.440964l-75.1181 0z" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m260.48032 227.8727l75.1181 0l0 40.440964l-75.1181 0z" fill-rule="nonzero"></path><path fill="#000000" d="m283.95062 251.4038l1.640625 0.21875q-0.265625 1.6875 -1.375 2.65625q-1.109375 0.953125 -2.734375 0.953125q-2.015625 0 -3.25 -1.3125q-1.21875 -1.328125 -1.21875 -3.796875q0 -1.59375 0.515625 -2.78125q0.53125 -1.203125 1.609375 -1.796875q1.09375 -0.609375 2.359375 -0.609375q1.609375 0 2.625 0.8125q1.015625 0.8125 1.3125 2.3125l-1.625 0.25q-0.234375 -1.0 -0.828125 -1.5q-0.59375 -0.5 -1.421875 -0.5q-1.265625 0 -2.0625 0.90625q-0.78125 0.90625 -0.78125 2.859375q0 1.984375 0.765625 2.890625q0.765625 0.890625 1.984375 0.890625q0.984375 0 1.640625 -0.59375q0.65625 -0.609375 0.84375 -1.859375zm2.859375 3.609375l0 -13.59375l1.671875 0l0 13.59375l-1.671875 0zm4.191681 -11.6875l0 -1.90625l1.671875 0l0 1.90625l-1.671875 0zm0 11.6875l0 -9.859375l1.671875 0l0 9.859375l-1.671875 0zm10.879211 -3.171875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm9.110077 5.875l0 -9.859375l1.5 0l0 1.40625q1.09375 -1.625 3.140625 -1.625q0.890625 0 1.640625 0.328125q0.75 0.3125 1.109375 0.84375q0.375 0.515625 0.53125 1.21875q0.09375 0.46875 0.09375 1.625l0 6.0625l-1.671875 0l0 -6.0q0 -1.015625 -0.203125 -1.515625q-0.1875 -0.515625 -0.6875 -0.8125q-0.5 -0.296875 -1.171875 -0.296875q-1.0625 0 -1.84375 0.671875q-0.765625 0.671875 -0.765625 2.578125l0 5.375l-1.671875 0zm14.031982 -1.5l0.234375 1.484375q-0.703125 0.140625 -1.265625 0.140625q-0.90625 0 -1.40625 -0.28125q-0.5 -0.296875 -0.703125 -0.75q-0.203125 -0.46875 -0.203125 -1.984375l0 -5.65625l-1.234375 0l0 -1.3125l1.234375 0l0 -2.4375l1.65625 -1.0l0 3.4375l1.6875 0l0 1.3125l-1.6875 0l0 5.75q0 0.71875 0.078125 0.921875q0.09375 0.203125 0.296875 0.328125q0.203125 0.125 0.578125 0.125q0.265625 0 0.734375 -0.078125z" fill-rule="nonzero"></path><path fill="#d9ead3" d="m65.75328 411.2454l75.1181 0l0 40.44095l-75.1181 0z" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m65.75328 411.2454l75.1181 0l0 40.44095l-75.1181 0z" fill-rule="nonzero"></path><path fill="#000000" d="m85.53747 438.38586l0 -1.25q-0.9375 1.46875 -2.75 1.46875q-1.171875 0 -2.171875 -0.640625q-0.984375 -0.65625 -1.53125 -1.8125q-0.53125 -1.171875 -0.53125 -2.6875q0 -1.46875 0.484375 -2.671875q0.5 -1.203125 1.46875 -1.84375q0.984375 -0.640625 2.203125 -0.640625q0.890625 0 1.578125 0.375q0.703125 0.375 1.140625 0.984375l0 -4.875l1.65625 0l0 13.59375l-1.546875 0zm-5.28125 -4.921875q0 1.890625 0.796875 2.828125q0.8125 0.9375 1.890625 0.9375q1.09375 0 1.859375 -0.890625q0.765625 -0.890625 0.765625 -2.734375q0 -2.015625 -0.78125 -2.953125q-0.78125 -0.953125 -1.921875 -0.953125q-1.109375 0 -1.859375 0.90625q-0.75 0.90625 -0.75 2.859375zm10.813217 4.921875l-1.546875 0l0 -13.59375l1.65625 0l0 4.84375q1.0625 -1.328125 2.703125 -1.328125q0.90625 0 1.71875 0.375q0.8125 0.359375 1.328125 1.03125q0.53125 0.65625 0.828125 1.59375q0.296875 0.9375 0.296875 2.0q0 2.53125 -1.25 3.921875q-1.25 1.375 -3.0 1.375q-1.75 0 -2.734375 -1.453125l0 1.234375zm-0.015625 -5.0q0 1.765625 0.46875 2.5625q0.796875 1.28125 2.140625 1.28125q1.09375 0 1.890625 -0.9375q0.796875 -0.953125 0.796875 -2.84375q0 -1.921875 -0.765625 -2.84375q-0.765625 -0.921875 -1.84375 -0.921875q-1.09375 0 -1.890625 0.953125q-0.796875 0.953125 -0.796875 2.75zm7.594467 5.234375l3.9375 -14.0625l1.34375 0l-3.9375 14.0625l-1.34375 0zm12.870804 -1.453125q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm4.078842 8.71875l0 -13.640625l1.53125 0l0 1.28125q0.53125 -0.75 1.203125 -1.125q0.6875 -0.375 1.640625 -0.375q1.265625 0 2.234375 0.65625q0.96875 0.640625 1.453125 1.828125q0.5 1.1875 0.5 2.59375q0 1.515625 -0.546875 2.734375q-0.546875 1.203125 -1.578125 1.84375q-1.03125 0.640625 -2.171875 0.640625q-0.84375 0 -1.515625 -0.34375q-0.65625 -0.359375 -1.078125 -0.890625l0 4.796875l-1.671875 0zm1.515625 -8.65625q0 1.90625 0.765625 2.8125q0.78125 0.90625 1.875 0.90625q1.109375 0 1.890625 -0.9375q0.796875 -0.9375 0.796875 -2.921875q0 -1.875 -0.78125 -2.8125q-0.765625 -0.9375 -1.84375 -0.9375q-1.0625 0 -1.890625 1.0q-0.8125 1.0 -0.8125 2.890625zm8.875717 -6.8125l0 -1.90625l1.671875 0l0 1.90625l-1.671875 0zm0 11.6875l0 -9.859375l1.671875 0l0 9.859375l-1.671875 0z" fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0" d="m298.03937 268.31366l-194.74016 142.92911" fill-rule="nonzero"></path><path stroke="#595959" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m298.03937 268.31366l-189.90314 139.379" fill-rule="evenodd"></path><path fill="#595959" stroke="#595959" stroke-width="1.0" stroke-linecap="butt" d="m107.15892 406.36108l-2.6811676 4.0167236l4.6357803 -1.3535767z" fill-rule="evenodd"></path><path fill="#000000" fill-opacity="0.0" d="m298.03937 268.31366l179.74805 142.92911" fill-rule="nonzero"></path><path stroke="#595959" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m298.03937 268.31366l175.05176 139.19482" fill-rule="evenodd"></path><path fill="#595959" stroke="#595959" stroke-width="1.0" stroke-linecap="butt" d="m472.0631 408.8013l4.5800476 1.5316162l-2.5240173 -4.117279z" fill-rule="evenodd"></path><path fill="#000000" fill-opacity="0.0" d="m298.03937 268.31366l358.6772 142.92911" fill-rule="nonzero"></path><path stroke="#595959" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m298.03937 268.31366l353.1034 140.70804" fill-rule="evenodd"></path><path fill="#595959" stroke="#595959" stroke-width="1.0" stroke-linecap="butt" d="m650.5313 410.5561l4.8271484 0.14550781l-3.604248 -3.2142944z" fill-rule="evenodd"></path><path fill="#000000" fill-opacity="0.0" d="m106.43307 301.02625l111.37008 0l0 32.409454l-111.37008 0z" fill-rule="nonzero"></path><path fill="#000000" d="m122.51119 327.94626l-1.671875 0l0 -10.640625q-0.59375 0.578125 -1.578125 1.15625q-0.984375 0.5625 -1.765625 0.859375l0 -1.625q1.40625 -0.65625 2.453125 -1.59375q1.046875 -0.9375 1.484375 -1.8125l1.078125 0l0 13.65625zm5.016342 0l0 -1.90625l1.9062576 0l0 1.90625l-1.9062576 0zm16.28849 0l0 -1.25q-0.9375 1.46875 -2.75 1.46875q-1.171875 0 -2.171875 -0.640625q-0.984375 -0.65625 -1.53125 -1.8125q-0.53125 -1.171875 -0.53125 -2.6875q0 -1.46875 0.484375 -2.671875q0.5 -1.203125 1.46875 -1.84375q0.984375 -0.640625 2.203125 -0.640625q0.890625 0 1.578125 0.375q0.703125 0.375 1.140625 0.984375l0 -4.875l1.65625 0l0 13.59375l-1.546875 0zm-5.28125 -4.921875q0 1.890625 0.796875 2.828125q0.8125 0.9375 1.890625 0.9375q1.09375 0 1.859375 -0.890625q0.765625 -0.890625 0.765625 -2.734375q0 -2.015625 -0.78125 -2.953125q-0.78125 -0.953125 -1.921875 -0.953125q-1.109375 0 -1.859375 0.90625q-0.75 0.90625 -0.75 2.859375zm9.281967 -6.765625l0 -1.90625l1.671875 0l0 1.90625l-1.671875 0zm0 11.6875l0 -9.859375l1.671875 0l0 9.859375l-1.671875 0zm3.4573212 -2.9375l1.65625 -0.265625q0.140625 1.0 0.765625 1.53125q0.640625 0.515625 1.78125 0.515625q1.15625 0 1.703125 -0.46875q0.5625 -0.46875 0.5625 -1.09375q0 -0.5625 -0.484375 -0.890625q-0.34375 -0.21875 -1.703125 -0.5625q-1.84375 -0.46875 -2.5625 -0.796875q-0.703125 -0.34375 -1.078125 -0.9375q-0.359375 -0.609375 -0.359375 -1.328125q0 -0.65625 0.296875 -1.21875q0.3125 -0.5625 0.828125 -0.9375q0.390625 -0.28125 1.0625 -0.484375q0.671875 -0.203125 1.4375 -0.203125q1.171875 0 2.046875 0.34375q0.875 0.328125 1.28125 0.90625q0.421875 0.5625 0.578125 1.515625l-1.625 0.21875q-0.109375 -0.75 -0.65625 -1.171875q-0.53125 -0.4375 -1.5 -0.4375q-1.15625 0 -1.640625 0.390625q-0.484375 0.375 -0.484375 0.875q0 0.328125 0.203125 0.59375q0.203125 0.265625 0.640625 0.4375q0.25 0.09375 1.46875 0.4375q1.765625 0.46875 2.46875 0.765625q0.703125 0.296875 1.09375 0.875q0.40625 0.578125 0.40625 1.4375q0 0.828125 -0.484375 1.578125q-0.484375 0.734375 -1.40625 1.140625q-0.921875 0.390625 -2.078125 0.390625q-1.921875 0 -2.9375 -0.796875q-1.0 -0.796875 -1.28125 -2.359375zm16.4375 -0.671875l1.640625 0.21875q-0.265625 1.6875 -1.375 2.65625q-1.109375 0.953125 -2.734375 0.953125q-2.015625 0 -3.25 -1.3125q-1.21875 -1.328125 -1.21875 -3.796875q0 -1.59375 0.515625 -2.78125q0.53125 -1.203125 1.609375 -1.796875q1.09375 -0.609375 2.359375 -0.609375q1.609375 0 2.625 0.8125q1.015625 0.8125 1.3125 2.3125l-1.625 0.25q-0.234375 -1.0 -0.828125 -1.5q-0.59375 -0.5 -1.421875 -0.5q-1.265625 0 -2.0625 0.90625q-0.78125 0.90625 -0.78125 2.859375q0 1.984375 0.765625 2.890625q0.765625 0.890625 1.984375 0.890625q0.984375 0 1.640625 -0.59375q0.65625 -0.609375 0.84375 -1.859375zm2.265625 -1.3125q0 -2.734375 1.53125 -4.0625q1.265625 -1.09375 3.09375 -1.09375q2.03125 0 3.3125 1.34375q1.296875 1.328125 1.296875 3.671875q0 1.90625 -0.578125 3.0q-0.5625 1.078125 -1.65625 1.6875q-1.078125 0.59375 -2.375 0.59375q-2.0625 0 -3.34375 -1.328125q-1.28125 -1.328125 -1.28125 -3.8125zm1.71875 0q0 1.890625 0.828125 2.828125q0.828125 0.9375 2.078125 0.9375q1.25 0 2.0625 -0.9375q0.828125 -0.953125 0.828125 -2.890625q0 -1.828125 -0.828125 -2.765625q-0.828125 -0.9375 -2.0625 -0.9375q-1.25 0 -2.078125 0.9375q-0.828125 0.9375 -0.828125 2.828125zm12.016342 4.921875l-3.75 -9.859375l1.765625 0l2.125 5.90625q0.34375 0.953125 0.625 1.984375q0.21875 -0.78125 0.625 -1.875l2.1875 -6.015625l1.71875 0l-3.734375 9.859375l-1.5625 0zm13.34375 -3.171875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm9.094467 5.875l0 -9.859375l1.5 0l0 1.5q0.578125 -1.046875 1.0625 -1.375q0.484375 -0.34375 1.078125 -0.34375q0.84375 0 1.71875 0.546875l-0.578125 1.546875q-0.609375 -0.359375 -1.234375 -0.359375q-0.546875 0 -0.984375 0.328125q-0.421875 0.328125 -0.609375 0.90625q-0.28125 0.890625 -0.28125 1.953125l0 5.15625l-1.671875 0z" fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0" d="m445.85303 301.02625l111.370056 0l0 32.409454l-111.370056 0z" fill-rule="nonzero"></path><path fill="#000000" d="m455.6499 324.3525l1.671875 -0.21875q0.28125 1.421875 0.96875 2.046875q0.703125 0.625 1.6875 0.625q1.1875 0 2.0 -0.8125q0.8125 -0.828125 0.8125 -2.03125q0 -1.140625 -0.765625 -1.890625q-0.75 -0.75 -1.90625 -0.75q-0.46875 0 -1.171875 0.1875l0.1875 -1.46875q0.15625 0.015625 0.265625 0.015625q1.0625 0 1.90625 -0.546875q0.859375 -0.5625 0.859375 -1.71875q0 -0.921875 -0.625 -1.515625q-0.609375 -0.609375 -1.59375 -0.609375q-0.96875 0 -1.625 0.609375q-0.640625 0.609375 -0.828125 1.84375l-1.671875 -0.296875q0.296875 -1.6875 1.375 -2.609375q1.09375 -0.921875 2.71875 -0.921875q1.109375 0 2.046875 0.484375q0.9375 0.46875 1.421875 1.296875q0.5 0.828125 0.5 1.75q0 0.890625 -0.46875 1.609375q-0.46875 0.71875 -1.40625 1.15625q1.21875 0.265625 1.875 1.15625q0.671875 0.875 0.671875 2.1875q0 1.78125 -1.296875 3.015625q-1.296875 1.234375 -3.28125 1.234375q-1.796875 0 -2.984375 -1.0625q-1.171875 -1.0625 -1.34375 -2.765625zm11.297577 3.59375l0 -1.90625l1.90625 0l0 1.90625l-1.90625 0zm9.600983 0.8125l1.609375 0.25q0.109375 0.75 0.578125 1.09375q0.609375 0.453125 1.6875 0.453125q1.171875 0 1.796875 -0.46875q0.625 -0.453125 0.859375 -1.28125q0.125 -0.515625 0.109375 -2.15625q-1.09375 1.296875 -2.71875 1.296875q-2.03125 0 -3.15625 -1.46875q-1.109375 -1.46875 -1.109375 -3.515625q0 -1.40625 0.515625 -2.59375q0.515625 -1.203125 1.484375 -1.84375q0.96875 -0.65625 2.265625 -0.65625q1.75 0 2.875 1.40625l0 -1.1875l1.546875 0l0 8.515625q0 2.3125 -0.46875 3.265625q-0.46875 0.96875 -1.484375 1.515625q-1.015625 0.5625 -2.5 0.5625q-1.765625 0 -2.859375 -0.796875q-1.078125 -0.796875 -1.03125 -2.390625zm1.375 -5.921875q0 1.953125 0.765625 2.84375q0.78125 0.890625 1.9375 0.890625q1.140625 0 1.921875 -0.890625q0.78125 -0.890625 0.78125 -2.78125q0 -1.8125 -0.8125 -2.71875q-0.796875 -0.921875 -1.921875 -0.921875q-1.109375 0 -1.890625 0.90625q-0.78125 0.890625 -0.78125 2.671875zm16.047607 1.9375l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm12.766327 4.375l0.234375 1.484375q-0.703125 0.140625 -1.265625 0.140625q-0.90625 0 -1.40625 -0.28125q-0.5 -0.296875 -0.703125 -0.75q-0.203125 -0.46875 -0.203125 -1.984375l0 -5.65625l-1.234375 0l0 -1.3125l1.234375 0l0 -2.4375l1.65625 -1.0l0 3.4375l1.6875 0l0 1.3125l-1.6875 0l0 5.75q0 0.71875 0.078125 0.921875q0.09375 0.203125 0.296875 0.328125q0.203125 0.125 0.578125 0.125q0.265625 0 0.734375 -0.078125zm0.2770691 1.734375l3.9375 -14.0625l1.34375 0l-3.9375 14.0625l-1.34375 0zm6.4332886 3.546875l0 -13.640625l1.53125 0l0 1.28125q0.53125 -0.75 1.203125 -1.125q0.6875 -0.375 1.640625 -0.375q1.265625 0 2.234375 0.65625q0.96875 0.640625 1.453125 1.828125q0.5 1.1875 0.5 2.59375q0 1.515625 -0.546875 2.734375q-0.546875 1.203125 -1.578125 1.84375q-1.03125 0.640625 -2.171875 0.640625q-0.84375 0 -1.515625 -0.34375q-0.65625 -0.359375 -1.078125 -0.890625l0 4.796875l-1.671875 0zm1.515625 -8.65625q0 1.90625 0.765625 2.8125q0.78125 0.90625 1.875 0.90625q1.109375 0 1.890625 -0.9375q0.796875 -0.9375 0.796875 -2.921875q0 -1.875 -0.78125 -2.8125q-0.765625 -0.9375 -1.84375 -0.9375q-1.0625 0 -1.890625 1.0q-0.8125 1.0 -0.8125 2.890625zm15.313232 4.875l0 -1.453125q-1.140625 1.671875 -3.125 1.671875q-0.859375 0 -1.625 -0.328125q-0.75 -0.34375 -1.125 -0.84375q-0.359375 -0.5 -0.515625 -1.234375q-0.09375 -0.5 -0.09375 -1.5625l0 -6.109375l1.671875 0l0 5.46875q0 1.3125 0.09375 1.765625q0.15625 0.65625 0.671875 1.03125q0.515625 0.375 1.265625 0.375q0.75 0 1.40625 -0.375q0.65625 -0.390625 0.921875 -1.046875q0.28125 -0.671875 0.28125 -1.9375l0 -5.28125l1.671875 0l0 9.859375l-1.5 0zm7.5788574 -1.5l0.234375 1.484375q-0.703125 0.140625 -1.265625 0.140625q-0.90625 0 -1.40625 -0.28125q-0.5 -0.296875 -0.703125 -0.75q-0.203125 -0.46875 -0.203125 -1.984375l0 -5.65625l-1.234375 0l0 -1.3125l1.234375 0l0 -2.4375l1.65625 -1.0l0 3.4375l1.6875 0l0 1.3125l-1.6875 0l0 5.75q0 0.71875 0.078125 0.921875q0.09375 0.203125 0.296875 0.328125q0.203125 0.125 0.578125 0.125q0.265625 0 0.734375 -0.078125z" fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0" d="m308.41995 146.15486l146.77164 0l0 32.40944l-146.77164 0z" fill-rule="nonzero"></path><path fill="#000000" d="m326.98245 171.46548l0 1.609375l-8.984375 0q-0.015625 -0.609375 0.1875 -1.15625q0.34375 -0.921875 1.09375 -1.8125q0.765625 -0.890625 2.1875 -2.0625q2.21875 -1.8125 3.0 -2.875q0.78125 -1.0625 0.78125 -2.015625q0 -0.984375 -0.71875 -1.671875q-0.703125 -0.6875 -1.84375 -0.6875q-1.203125 0 -1.9375 0.734375q-0.71875 0.71875 -0.71875 2.0l-1.71875 -0.171875q0.171875 -1.921875 1.328125 -2.921875q1.15625 -1.015625 3.09375 -1.015625q1.953125 0 3.09375 1.09375q1.140625 1.078125 1.140625 2.6875q0 0.8125 -0.34375 1.609375q-0.328125 0.78125 -1.109375 1.65625q-0.765625 0.859375 -2.5625 2.390625q-1.5 1.265625 -1.9375 1.71875q-0.421875 0.4375 -0.703125 0.890625l6.671875 0zm2.531952 1.609375l0 -1.90625l1.90625 0l0 1.90625l-1.90625 0zm9.882233 0l0 -9.859375l1.5 0l0 1.5q0.578125 -1.046875 1.0625 -1.375q0.484375 -0.34375 1.078125 -0.34375q0.84375 0 1.71875 0.546875l-0.578125 1.546875q-0.609375 -0.359375 -1.234375 -0.359375q-0.546875 0 -0.984375 0.328125q-0.421875 0.328125 -0.609375 0.90625q-0.28125 0.890625 -0.28125 1.953125l0 5.15625l-1.671875 0zm12.978302 -3.171875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm9.110107 5.875l0 -9.859375l1.5 0l0 1.40625q1.09375 -1.625 3.140625 -1.625q0.890625 0 1.640625 0.328125q0.75 0.3125 1.109375 0.84375q0.375 0.515625 0.53125 1.21875q0.09375 0.46875 0.09375 1.625l0 6.0625l-1.671875 0l0 -6.0q0 -1.015625 -0.203125 -1.515625q-0.1875 -0.515625 -0.6875 -0.8125q-0.5 -0.296875 -1.171875 -0.296875q-1.0625 0 -1.84375 0.671875q-0.765625 0.671875 -0.765625 2.578125l0 5.375l-1.671875 0zm16.766327 0l0 -1.25q-0.9375 1.46875 -2.75 1.46875q-1.171875 0 -2.171875 -0.640625q-0.984375 -0.65625 -1.53125 -1.8125q-0.53125 -1.171875 -0.53125 -2.6875q0 -1.46875 0.484375 -2.671875q0.5 -1.203125 1.46875 -1.84375q0.984375 -0.640625 2.203125 -0.640625q0.890625 0 1.578125 0.375q0.703125 0.375 1.140625 0.984375l0 -4.875l1.65625 0l0 13.59375l-1.546875 0zm-5.28125 -4.921875q0 1.890625 0.796875 2.828125q0.8125 0.9375 1.890625 0.9375q1.09375 0 1.859375 -0.890625q0.765625 -0.890625 0.765625 -2.734375q0 -2.015625 -0.78125 -2.953125q-0.78125 -0.953125 -1.921875 -0.953125q-1.109375 0 -1.859375 0.90625q-0.75 0.90625 -0.75 2.859375zm16.016357 1.75l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm8.235077 5.875l0 -1.359375l6.265625 -7.1875q-1.0625 0.046875 -1.875 0.046875l-4.015625 0l0 -1.359375l8.046875 0l0 1.109375l-5.34375 6.25l-1.015625 1.140625q1.109375 -0.078125 2.09375 -0.078125l4.5625 0l0 1.4375l-8.71875 0zm12.9375 0l-3.75 -9.859375l1.765625 0l2.125 5.90625q0.34375 0.953125 0.625 1.984375q0.21875 -0.78125 0.625 -1.875l2.1875 -6.015625l1.71875 0l-3.734375 9.859375l-1.5625 0zm5.96875 -4.921875q0 -2.734375 1.53125 -4.0625q1.265625 -1.09375 3.09375 -1.09375q2.03125 0 3.3125 1.34375q1.296875 1.328125 1.296875 3.671875q0 1.90625 -0.578125 3.0q-0.5625 1.078125 -1.65625 1.6875q-1.078125 0.59375 -2.375 0.59375q-2.0625 0 -3.34375 -1.328125q-1.28125 -1.328125 -1.28125 -3.8125zm1.71875 0q0 1.890625 0.828125 2.828125q0.828125 0.9375 2.078125 0.9375q1.25 0 2.0625 -0.9375q0.828125 -0.953125 0.828125 -2.890625q0 -1.828125 -0.828125 -2.765625q-0.828125 -0.9375 -2.0625 -0.9375q-1.25 0 -2.078125 0.9375q-0.828125 0.9375 -0.828125 2.828125zm15.735107 4.921875l0 -1.453125q-1.140625 1.671875 -3.125 1.671875q-0.859375 0 -1.625 -0.328125q-0.75 -0.34375 -1.125 -0.84375q-0.359375 -0.5 -0.515625 -1.234375q-0.09375 -0.5 -0.09375 -1.5625l0 -6.109375l1.671875 0l0 5.46875q0 1.3125 0.09375 1.765625q0.15625 0.65625 0.671875 1.03125q0.515625 0.375 1.265625 0.375q0.75 0 1.40625 -0.375q0.65625 -0.390625 0.921875 -1.046875q0.28125 -0.671875 0.28125 -1.9375l0 -5.28125l1.671875 0l0 9.859375l-1.5 0zm3.250702 -2.9375l1.65625 -0.265625q0.140625 1.0 0.765625 1.53125q0.640625 0.515625 1.78125 0.515625q1.15625 0 1.703125 -0.46875q0.5625 -0.46875 0.5625 -1.09375q0 -0.5625 -0.484375 -0.890625q-0.34375 -0.21875 -1.703125 -0.5625q-1.84375 -0.46875 -2.5625 -0.796875q-0.703125 -0.34375 -1.078125 -0.9375q-0.359375 -0.609375 -0.359375 -1.328125q0 -0.65625 0.296875 -1.21875q0.3125 -0.5625 0.828125 -0.9375q0.390625 -0.28125 1.0625 -0.484375q0.671875 -0.203125 1.4375 -0.203125q1.171875 0 2.046875 0.34375q0.875 0.328125 1.28125 0.90625q0.421875 0.5625 0.578125 1.515625l-1.625 0.21875q-0.109375 -0.75 -0.65625 -1.171875q-0.53125 -0.4375 -1.5 -0.4375q-1.15625 0 -1.640625 0.390625q-0.484375 0.375 -0.484375 0.875q0 0.328125 0.203125 0.59375q0.203125 0.265625 0.640625 0.4375q0.25 0.09375 1.46875 0.4375q1.765625 0.46875 2.46875 0.765625q0.703125 0.296875 1.09375 0.875q0.40625 0.578125 0.40625 1.4375q0 0.828125 -0.484375 1.578125q-0.484375 0.734375 -1.40625 1.140625q-0.921875 0.390625 -2.078125 0.390625q-1.921875 0 -2.9375 -0.796875q-1.0 -0.796875 -1.28125 -2.359375z" fill-rule="nonzero"></path></g></svg>
+
diff --git a/doc/images/Keep_rendezvous_hashing.svg b/doc/images/Keep_rendezvous_hashing.svg
new file mode 100644 (file)
index 0000000..0c5c04b
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" standalone="yes"?>
+
+<svg version="1.1" viewBox="0.0 0.0 960.0 540.0" fill="none" stroke="none" stroke-linecap="square" stroke-miterlimit="10" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><clipPath id="g150061aa58_0_20.0"><path d="m0 0l960.0 0l0 540.0l-960.0 0l0 -540.0z" clip-rule="nonzero"></path></clipPath><g clip-path="url(#g150061aa58_0_20.0)"><path fill="#ffffff" d="m0 0l960.0 0l0 540.0l-960.0 0z" fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0" d="m32.72441 46.721786l894.55115 0l0 60.125984l-894.55115 0z" fill-rule="nonzero"></path><path fill="#000000" d="m44.63066 91.56178l0 -26.484375l11.75 0q3.53125 0 5.375 0.71875q1.84375 0.703125 2.9375 2.515625q1.109375 1.8125 1.109375 3.984375q0 2.828125 -1.828125 4.765625q-1.8125 1.921875 -5.625 2.453125q1.390625 0.671875 2.109375 1.3125q1.53125 1.40625 2.90625 3.53125l4.609375 7.203125l-4.40625 0l-3.5 -5.515625q-1.546875 -2.375 -2.546875 -3.640625q-0.984375 -1.265625 -1.765625 -1.765625q-0.78125 -0.515625 -1.59375 -0.71875q-0.609375 -0.125 -1.953125 -0.125l-4.078125 0l0 11.765625l-3.5 0zm3.5 -14.796875l7.546875 0q2.390625 0 3.75 -0.5q1.359375 -0.5 2.0625 -1.578125q0.703125 -1.09375 0.703125 -2.390625q0 -1.875 -1.375 -3.078125q-1.359375 -1.21875 -4.296875 -1.21875l-8.390625 0l0 8.765625zm36.12906 8.625l3.359375 0.40625q-0.796875 2.953125 -2.953125 4.578125q-2.140625 1.625 -5.484375 1.625q-4.21875 0 -6.6875 -2.59375q-2.453125 -2.59375 -2.453125 -7.28125q0 -4.828125 2.484375 -7.5q2.5 -2.6875 6.46875 -2.6875q3.859375 0 6.296875 2.625q2.4375 2.625 2.4375 7.375q0 0.28125 -0.015625 0.859375l-14.3125 0q0.171875 3.171875 1.78125 4.859375q1.609375 1.671875 4.015625 1.671875q1.78125 0 3.046875 -0.9375q1.265625 -0.953125 2.015625 -3.0zm-10.6875 -5.265625l10.71875 0q-0.21875 -2.421875 -1.234375 -3.625q-1.546875 -1.890625 -4.015625 -1.890625q-2.25 0 -3.78125 1.5q-1.515625 1.5 -1.6875 4.015625zm18.307007 11.4375l0 -19.1875l2.921875 0l0 2.734375q2.125 -3.171875 6.109375 -3.171875q1.734375 0 3.1875 0.625q1.453125 0.625 2.171875 1.640625q0.734375 1.015625 1.015625 2.40625q0.1875 0.890625 0.1875 3.15625l0 11.796875l-3.25 0l0 -11.671875q0 -1.984375 -0.390625 -2.96875q-0.375 -0.984375 -1.34375 -1.5625q-0.953125 -0.59375 -2.265625 -0.59375q-2.078125 0 -3.59375 1.3125q-1.5 1.3125 -1.5 5.0l0 10.484375l-3.25 0zm33.213257 0l0 -2.421875q-1.828125 2.859375 -5.375 2.859375q-2.28125 0 -4.21875 -1.265625q-1.921875 -1.265625 -2.984375 -3.53125q-1.046875 -2.265625 -1.046875 -5.21875q0 -2.875 0.953125 -5.203125q0.96875 -2.34375 2.875 -3.59375q1.921875 -1.25 4.28125 -1.25q1.734375 0 3.09375 0.734375q1.359375 0.734375 2.203125 1.90625l0 -9.5l3.234375 0l0 26.484375l-3.015625 0zm-10.28125 -9.578125q0 3.6875 1.546875 5.515625q1.5625 1.828125 3.671875 1.828125q2.125 0 3.609375 -1.75q1.5 -1.75 1.5 -5.3125q0 -3.953125 -1.515625 -5.78125q-1.515625 -1.84375 -3.734375 -1.84375q-2.171875 0 -3.625 1.765625q-1.453125 1.765625 -1.453125 5.578125zm31.728882 3.40625l3.359375 0.40625q-0.796875 2.953125 -2.953125 4.578125q-2.140625 1.625 -5.484375 1.625q-4.21875 0 -6.6875 -2.59375q-2.453125 -2.59375 -2.453125 -7.28125q0 -4.828125 2.484375 -7.5q2.5 -2.6875 6.46875 -2.6875q3.859375 0 6.296875 2.625q2.4375 2.625 2.4375 7.375q0 0.28125 -0.015625 0.859375l-14.3125 0q0.171875 3.171875 1.78125 4.859375q1.609375 1.671875 4.015625 1.671875q1.78125 0 3.046875 -0.9375q1.265625 -0.953125 2.015625 -3.0zm-10.6875 -5.265625l10.71875 0q-0.21875 -2.421875 -1.234375 -3.625q-1.546875 -1.890625 -4.015625 -1.890625q-2.25 0 -3.78125 1.5q-1.515625 1.5 -1.6875 4.015625zm16.588257 11.4375l0 -2.640625l12.21875 -14.015625q-2.078125 0.109375 -3.671875 0.109375l-7.8125 0l0 -2.640625l15.671875 0l0 2.15625l-10.390625 12.171875l-2.0 2.21875q2.1875 -0.15625 4.09375 -0.15625l8.875 0l0 2.796875l-16.984375 0zm25.710938 0l-7.296875 -19.1875l3.4375 0l4.109375 11.484375q0.671875 1.875 1.234375 3.875q0.4375 -1.515625 1.203125 -3.65625l4.265625 -11.703125l3.34375 0l-7.265625 19.1875l-3.03125 0zm12.1328125 -9.59375q0 -5.328125 2.953125 -7.890625q2.484375 -2.140625 6.03125 -2.140625q3.96875 0 6.46875 2.59375q2.515625 2.59375 2.515625 7.171875q0 3.703125 -1.109375 5.828125q-1.109375 2.109375 -3.234375 3.296875q-2.125 1.171875 -4.640625 1.171875q-4.015625 0 -6.5 -2.578125q-2.484375 -2.59375 -2.484375 -7.453125zm3.34375 0q0 3.6875 1.59375 5.53125q1.609375 1.828125 4.046875 1.828125q2.421875 0 4.03125 -1.84375q1.609375 -1.84375 1.609375 -5.625q0 -3.5625 -1.625 -5.390625q-1.609375 -1.828125 -4.015625 -1.828125q-2.4375 0 -4.046875 1.828125q-1.59375 1.8125 -1.59375 5.5zm31.197632 9.59375l0 -2.8125q-2.25 3.25 -6.09375 3.25q-1.6875 0 -3.171875 -0.65625q-1.46875 -0.65625 -2.1875 -1.640625q-0.703125 -0.984375 -1.0 -2.40625q-0.203125 -0.953125 -0.203125 -3.03125l0 -11.890625l3.265625 0l0 10.640625q0 2.546875 0.1875 3.4375q0.3125 1.28125 1.296875 2.015625q1.0 0.734375 2.46875 0.734375q1.453125 0 2.734375 -0.75q1.296875 -0.75 1.828125 -2.046875q0.53125 -1.296875 0.53125 -3.75l0 -10.28125l3.25 0l0 19.1875l-2.90625 0zm6.885132 -5.734375l3.21875 -0.5q0.265625 1.9375 1.5 2.96875q1.234375 1.03125 3.46875 1.03125q2.234375 0 3.3125 -0.90625q1.09375 -0.921875 1.09375 -2.15625q0 -1.09375 -0.96875 -1.734375q-0.65625 -0.4375 -3.3125 -1.09375q-3.578125 -0.90625 -4.96875 -1.5625q-1.375 -0.671875 -2.09375 -1.828125q-0.703125 -1.171875 -0.703125 -2.578125q0 -1.28125 0.578125 -2.375q0.59375 -1.09375 1.59375 -1.8125q0.765625 -0.5625 2.078125 -0.953125q1.3125 -0.390625 2.8125 -0.390625q2.25 0 3.953125 0.65625q1.71875 0.65625 2.53125 1.765625q0.8125 1.109375 1.109375 2.96875l-3.171875 0.4375q-0.21875 -1.484375 -1.265625 -2.3125q-1.03125 -0.84375 -2.921875 -0.84375q-2.25 0 -3.203125 0.75q-0.953125 0.734375 -0.953125 1.734375q0 0.625 0.390625 1.140625q0.40625 0.515625 1.25 0.859375q0.484375 0.1875 2.875 0.828125q3.453125 0.921875 4.8125 1.515625q1.359375 0.578125 2.140625 1.703125q0.78125 1.125 0.78125 2.78125q0 1.625 -0.953125 3.0625q-0.953125 1.4375 -2.75 2.234375q-1.78125 0.78125 -4.03125 0.78125q-3.75 0 -5.703125 -1.546875q-1.953125 -1.5625 -2.5 -4.625zm30.331894 5.734375l0 -26.484375l3.25 0l0 9.5q2.28125 -2.640625 5.75 -2.640625q2.125 0 3.703125 0.84375q1.578125 0.84375 2.25 2.328125q0.671875 1.46875 0.671875 4.296875l0 12.15625l-3.25 0l0 -12.15625q0 -2.4375 -1.0625 -3.546875q-1.046875 -1.109375 -2.984375 -1.109375q-1.4375 0 -2.71875 0.75q-1.265625 0.734375 -1.8125 2.03125q-0.546875 1.28125 -0.546875 3.53125l0 10.5l-3.25 0zm33.275726 -2.359375q-1.796875 1.53125 -3.46875 2.171875q-1.671875 0.625 -3.5937195 0.625q-3.15625 0 -4.859375 -1.546875q-1.6875 -1.546875 -1.6875 -3.953125q0 -1.40625 0.640625 -2.5625q0.640625 -1.171875 1.671875 -1.875q1.046875 -0.703125 2.34375 -1.0625q0.953125 -0.265625 2.8905945 -0.5q3.9375 -0.46875 5.796875 -1.109375q0.015625 -0.671875 0.015625 -0.859375q0 -1.984375 -0.921875 -2.796875q-1.25 -1.09375 -3.703125 -1.09375q-2.2968445 0 -3.3905945 0.796875q-1.09375 0.796875 -1.609375 2.84375l-3.1875 -0.4375q0.4375 -2.03125 1.421875 -3.28125q1.0 -1.265625 2.875 -1.9375q1.890625 -0.6875 4.3593445 -0.6875q2.46875 0 4.0 0.578125q1.53125 0.578125 2.25 1.453125q0.734375 0.875 1.015625 2.21875q0.15625 0.828125 0.15625 3.0l0 4.328125q0 4.546875 0.203125 5.75q0.21875 1.1875 0.828125 2.296875l-3.390625 0q-0.5 -1.015625 -0.65625 -2.359375zm-0.265625 -7.265625q-1.765625 0.71875 -5.3125 1.21875q-1.9999695 0.296875 -2.8437195 0.65625q-0.828125 0.359375 -1.28125 1.0625q-0.4375 0.6875 -0.4375 1.53125q0 1.3125 0.984375 2.1875q0.984375 0.859375 2.8749695 0.859375q1.875 0 3.34375 -0.828125q1.46875 -0.828125 2.15625 -2.25q0.515625 -1.09375 0.515625 -3.25l0 -1.1875zm7.213257 3.890625l3.21875 -0.5q0.265625 1.9375 1.5 2.96875q1.234375 1.03125 3.46875 1.03125q2.234375 0 3.3125 -0.90625q1.09375 -0.921875 1.09375 -2.15625q0 -1.09375 -0.96875 -1.734375q-0.65625 -0.4375 -3.3125 -1.09375q-3.578125 -0.90625 -4.96875 -1.5625q-1.375 -0.671875 -2.09375 -1.828125q-0.703125 -1.171875 -0.703125 -2.578125q0 -1.28125 0.578125 -2.375q0.59375 -1.09375 1.59375 -1.8125q0.765625 -0.5625 2.078125 -0.953125q1.3125 -0.390625 2.8125 -0.390625q2.25 0 3.953125 0.65625q1.71875 0.65625 2.53125 1.765625q0.8125 1.109375 1.109375 2.96875l-3.171875 0.4375q-0.21875 -1.484375 -1.265625 -2.3125q-1.03125 -0.84375 -2.921875 -0.84375q-2.25 0 -3.203125 0.75q-0.953125 0.734375 -0.953125 1.734375q0 0.625 0.390625 1.140625q0.40625 0.515625 1.25 0.859375q0.484375 0.1875 2.875 0.828125q3.453125 0.921875 4.8125 1.515625q1.359375 0.578125 2.140625 1.703125q0.78125 1.125 0.78125 2.78125q0 1.625 -0.953125 3.0625q-0.953125 1.4375 -2.75 2.234375q-1.78125 0.78125 -4.03125 0.78125q-3.75 0 -5.703125 -1.546875q-1.953125 -1.5625 -2.5 -4.625zm19.960938 5.734375l0 -26.484375l3.25 0l0 9.5q2.28125 -2.640625 5.75 -2.640625q2.125 0 3.703125 0.84375q1.578125 0.84375 2.25 2.328125q0.671875 1.46875 0.671875 4.296875l0 12.15625l-3.25 0l0 -12.15625q0 -2.4375 -1.0625 -3.546875q-1.046875 -1.109375 -2.984375 -1.109375q-1.4375 0 -2.71875 0.75q-1.265625 0.734375 -1.8125 2.03125q-0.546875 1.28125 -0.546875 3.53125l0 10.5l-3.25 0zm20.775757 -22.75l0 -3.734375l3.25 0l0 3.734375l-3.25 0zm0 22.75l0 -19.1875l3.25 0l0 19.1875l-3.25 0zm8.277496 0l0 -19.1875l2.921875 0l0 2.734375q2.125 -3.171875 6.109375 -3.171875q1.734375 0 3.1875 0.625q1.453125 0.625 2.171875 1.640625q0.734375 1.015625 1.015625 2.40625q0.1875 0.890625 0.1875 3.15625l0 11.796875l-3.25 0l0 -11.671875q0 -1.984375 -0.390625 -2.96875q-0.375 -0.984375 -1.34375 -1.5625q-0.953125 -0.59375 -2.265625 -0.59375q-2.078125 0 -3.59375 1.3125q-1.5 1.3125 -1.5 5.0l0 10.484375l-3.25 0zm20.166382 1.59375l3.15625 0.46875q0.203125 1.453125 1.109375 2.125q1.203125 0.90625 3.296875 0.90625q2.265625 0 3.484375 -0.90625q1.234375 -0.90625 1.671875 -2.53125q0.25 -0.984375 0.234375 -4.171875q-2.125 2.515625 -5.3125 2.515625q-3.953125 0 -6.125 -2.84375q-2.171875 -2.859375 -2.171875 -6.859375q0 -2.75 1.0 -5.0625q1.0 -2.328125 2.875 -3.59375q1.890625 -1.265625 4.4375 -1.265625q3.40625 0 5.609375 2.75l0 -2.3125l3.0 0l0 16.578125q0 4.484375 -0.921875 6.359375q-0.90625 1.875 -2.890625 2.953125q-1.96875 1.078125 -4.859375 1.078125q-3.4375 0 -5.5625 -1.546875q-2.109375 -1.53125 -2.03125 -4.640625zm2.6875 -11.53125q0 3.78125 1.5 5.515625q1.5 1.734375 3.765625 1.734375q2.234375 0 3.75 -1.71875q1.515625 -1.734375 1.515625 -5.421875q0 -3.515625 -1.5625 -5.296875q-1.5625 -1.796875 -3.765625 -1.796875q-2.171875 0 -3.6875 1.765625q-1.515625 1.75 -1.515625 5.21875z" fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0" d="m32.72441 467.15222l894.55115 0l0 54.519714l-894.55115 0z" fill-rule="nonzero"></path><path fill="#595959" d="m167.41017 499.19223l-3.796875 -12.453125l2.171875 0l1.984375 7.1875l0.734375 2.671875q0.046875 -0.203125 0.640625 -2.5625l1.984375 -7.296875l2.171875 0l1.859375 7.21875l0.625 2.390625l0.71875 -2.40625l2.125 -7.203125l2.046875 0l-3.890625 12.453125l-2.1875 0l-1.984375 -7.453125l-0.46875 -2.125l-2.53125 9.578125l-2.203125 0zm23.566406 -4.015625l2.171875 0.28125q-0.515625 1.90625 -1.90625 2.96875q-1.390625 1.046875 -3.5625 1.046875q-2.734375 0 -4.34375 -1.671875q-1.59375 -1.6875 -1.59375 -4.734375q0 -3.140625 1.609375 -4.875q1.625 -1.734375 4.203125 -1.734375q2.5 0 4.078125 1.703125q1.59375 1.703125 1.59375 4.78125q0 0.1875 -0.015625 0.5625l-9.28125 0q0.109375 2.046875 1.15625 3.140625q1.046875 1.09375 2.609375 1.09375q1.15625 0 1.96875 -0.609375q0.828125 -0.609375 1.3125 -1.953125zm-6.9375 -3.40625l6.953125 0q-0.140625 -1.5625 -0.796875 -2.359375q-1.0 -1.21875 -2.609375 -1.21875q-1.453125 0 -2.453125 0.984375q-0.984375 0.96875 -1.09375 2.59375zm11.769531 -7.328125l0 -2.4375l2.109375 0l0 2.4375l-2.109375 0zm0 14.75l0 -12.453125l2.109375 0l0 12.453125l-2.109375 0zm4.9414062 1.03125l2.046875 0.3125q0.125 0.9375 0.71875 1.375q0.78125 0.59375 2.140625 0.59375q1.46875 0 2.265625 -0.59375q0.796875 -0.578125 1.078125 -1.640625q0.15625 -0.640625 0.140625 -2.703125q-1.375 1.625 -3.4375 1.625q-2.5625 0 -3.96875 -1.84375q-1.40625 -1.859375 -1.40625 -4.453125q0 -1.78125 0.640625 -3.28125q0.640625 -1.515625 1.859375 -2.328125q1.234375 -0.828125 2.890625 -0.828125q2.203125 0 3.625 1.78125l0 -1.5l1.953125 0l0 10.765625q0 2.90625 -0.59375 4.109375q-0.59375 1.21875 -1.875 1.921875q-1.28125 0.703125 -3.15625 0.703125q-2.234375 0 -3.609375 -1.0q-1.359375 -1.0 -1.3125 -3.015625zm1.734375 -7.484375q0 2.453125 0.96875 3.578125q0.984375 1.125 2.453125 1.125q1.453125 0 2.4375 -1.109375q0.984375 -1.125 0.984375 -3.515625q0 -2.28125 -1.015625 -3.4375q-1.015625 -1.171875 -2.453125 -1.171875q-1.40625 0 -2.390625 1.140625q-0.984375 1.140625 -0.984375 3.390625zm11.988281 6.453125l0 -17.1875l2.109375 0l0 6.171875q1.484375 -1.71875 3.734375 -1.71875q1.375 0 2.390625 0.546875q1.03125 0.546875 1.46875 1.515625q0.4375 0.953125 0.4375 2.78125l0 7.890625l-2.109375 0l0 -7.890625q0 -1.578125 -0.6875 -2.296875q-0.6875 -0.71875 -1.9375 -0.71875q-0.9375 0 -1.765625 0.484375q-0.828125 0.484375 -1.1875 1.3125q-0.34375 0.828125 -0.34375 2.296875l0 6.8125l-2.109375 0zm17.957031 -1.890625l0.3125 1.859375q-0.890625 0.203125 -1.59375 0.203125q-1.15625 0 -1.796875 -0.359375q-0.625 -0.375 -0.890625 -0.96875q-0.25 -0.59375 -0.25 -2.484375l0 -7.171875l-1.546875 0l0 -1.640625l1.546875 0l0 -3.078125l2.09375 -1.265625l0 4.34375l2.125 0l0 1.640625l-2.125 0l0 7.28125q0 0.90625 0.109375 1.171875q0.125 0.25 0.375 0.40625q0.25 0.140625 0.71875 0.140625q0.34375 0 0.921875 -0.078125zm19.835938 -8.21875l-11.34375 0l0 -1.96875l11.34375 0l0 1.96875zm0 5.21875l-11.34375 0l0 -1.96875l11.34375 0l0 1.96875zm9.5742035 4.890625l0 -17.1875l2.109375 0l0 6.171875q1.484375 -1.71875 3.734375 -1.71875q1.375 0 2.390625 0.546875q1.03125 0.546875 1.46875 1.515625q0.4375 0.953125 0.4375 2.78125l0 7.890625l-2.109375 0l0 -7.890625q0 -1.578125 -0.6875 -2.296875q-0.6875 -0.71875 -1.9375 -0.71875q-0.9375 0 -1.765625 0.484375q-0.828125 0.484375 -1.1875 1.3125q-0.34375 0.828125 -0.34375 2.296875l0 6.8125l-2.109375 0zm12.566406 -6.21875q0 -3.46875 1.921875 -5.125q1.609375 -1.390625 3.921875 -1.390625q2.5625 0 4.1875 1.6875q1.625 1.6875 1.625 4.640625q0 2.40625 -0.71875 3.78125q-0.71875 1.375 -2.09375 2.140625q-1.375 0.765625 -3.0 0.765625q-2.625 0 -4.234375 -1.671875q-1.609375 -1.6875 -1.609375 -4.828125zm2.171875 0q0 2.390625 1.03125 3.578125q1.046875 1.1875 2.640625 1.1875q1.5625 0 2.609375 -1.1875q1.046875 -1.203125 1.046875 -3.65625q0 -2.3125 -1.0625 -3.5q-1.046875 -1.1875 -2.59375 -1.1875q-1.59375 0 -2.640625 1.1875q-1.03125 1.1875 -1.03125 3.578125zm14.253906 6.21875l-3.796875 -12.453125l2.171875 0l1.984375 7.1875l0.734375 2.671875q0.046875 -0.203125 0.640625 -2.5625l1.984375 -7.296875l2.171875 0l1.859375 7.21875l0.625 2.390625l0.71875 -2.40625l2.125 -7.203125l2.046875 0l-3.890625 12.453125l-2.1875 0l-1.984375 -7.453125l-0.46875 -2.125l-2.53125 9.578125l-2.203125 0zm29.828125 -1.53125q-1.171875 0.984375 -2.265625 1.40625q-1.078125 0.40625 -2.3125 0.40625q-2.046875 0 -3.15625 -1.0q-1.09375 -1.0 -1.09375 -2.5625q0 -0.921875 0.40625 -1.671875q0.421875 -0.75 1.09375 -1.203125q0.671875 -0.46875 1.515625 -0.703125q0.625 -0.15625 1.875 -0.3125q2.5625 -0.3125 3.765625 -0.734375q0.015625 -0.421875 0.015625 -0.546875q0 -1.28125 -0.609375 -1.8125q-0.796875 -0.71875 -2.390625 -0.71875q-1.5 0 -2.203125 0.53125q-0.703125 0.515625 -1.046875 1.84375l-2.0625 -0.28125q0.28125 -1.328125 0.921875 -2.140625q0.640625 -0.8125 1.859375 -1.25q1.21875 -0.453125 2.828125 -0.453125q1.59375 0 2.59375 0.375q1.0 0.375 1.46875 0.953125q0.46875 0.5625 0.65625 1.4375q0.09375 0.53125 0.09375 1.9375l0 2.8125q0 2.9375 0.140625 3.71875q0.140625 0.78125 0.53125 1.5l-2.203125 0q-0.328125 -0.65625 -0.421875 -1.53125zm-0.171875 -4.71875q-1.15625 0.46875 -3.453125 0.796875q-1.296875 0.1875 -1.84375 0.421875q-0.53125 0.234375 -0.828125 0.6875q-0.28125 0.453125 -0.28125 1.0q0 0.84375 0.625 1.40625q0.640625 0.5625 1.875 0.5625q1.21875 0 2.171875 -0.53125q0.953125 -0.53125 1.390625 -1.453125q0.34375 -0.71875 0.34375 -2.109375l0 -0.78125zm5.3945312 11.015625l0 -17.21875l1.921875 0l0 1.625q0.6875 -0.953125 1.53125 -1.421875q0.859375 -0.484375 2.078125 -0.484375q1.59375 0 2.8125 0.828125q1.21875 0.8125 1.84375 2.3125q0.625 1.5 0.625 3.28125q0 1.90625 -0.6875 3.4375q-0.6875 1.53125 -2.0 2.34375q-1.296875 0.8125 -2.734375 0.8125q-1.0625 0 -1.90625 -0.4375q-0.828125 -0.453125 -1.375 -1.140625l0 6.0625l-2.109375 0zm1.921875 -10.921875q0 2.40625 0.96875 3.5625q0.96875 1.140625 2.359375 1.140625q1.40625 0 2.40625 -1.1875q1.0 -1.1875 1.0 -3.6875q0 -2.375 -0.984375 -3.5625q-0.96875 -1.1875 -2.328125 -1.1875q-1.359375 0 -2.390625 1.265625q-1.03125 1.25 -1.03125 3.65625zm11.425781 10.921875l0 -17.21875l1.921875 0l0 1.625q0.6875 -0.953125 1.53125 -1.421875q0.859375 -0.484375 2.078125 -0.484375q1.59375 0 2.8125 0.828125q1.21875 0.8125 1.84375 2.3125q0.625 1.5 0.625 3.28125q0 1.90625 -0.6875 3.4375q-0.6875 1.53125 -2.0 2.34375q-1.296875 0.8125 -2.734375 0.8125q-1.0625 0 -1.90625 -0.4375q-0.828125 -0.453125 -1.375 -1.140625l0 6.0625l-2.109375 0zm1.921875 -10.921875q0 2.40625 0.96875 3.5625q0.96875 1.140625 2.359375 1.140625q1.40625 0 2.40625 -1.1875q1.0 -1.1875 1.0 -3.6875q0 -2.375 -0.984375 -3.5625q-0.96875 -1.1875 -2.328125 -1.1875q-1.359375 0 -2.390625 1.265625q-1.03125 1.25 -1.03125 3.65625zm11.410156 6.15625l0 -12.453125l1.890625 0l0 1.890625q0.734375 -1.328125 1.34375 -1.75q0.625 -0.421875 1.359375 -0.421875q1.0625 0 2.171875 0.6875l-0.734375 1.953125q-0.765625 -0.453125 -1.546875 -0.453125q-0.6875 0 -1.25 0.421875q-0.546875 0.40625 -0.78125 1.140625q-0.34375 1.125 -0.34375 2.46875l0 6.515625l-2.109375 0zm7.2265625 -6.21875q0 -3.46875 1.921875 -5.125q1.609375 -1.390625 3.921875 -1.390625q2.5625 0 4.1875 1.6875q1.625 1.6875 1.625 4.640625q0 2.40625 -0.71875 3.78125q-0.71875 1.375 -2.09375 2.140625q-1.375 0.765625 -3.0 0.765625q-2.625 0 -4.234375 -1.671875q-1.609375 -1.6875 -1.609375 -4.828125zm2.171875 0q0 2.390625 1.03125 3.578125q1.046875 1.1875 2.640625 1.1875q1.5625 0 2.609375 -1.1875q1.046875 -1.203125 1.046875 -3.65625q0 -2.3125 -1.0625 -3.5q-1.046875 -1.1875 -2.59375 -1.1875q-1.59375 0 -2.640625 1.1875q-1.03125 1.1875 -1.03125 3.578125zm11.957031 10.984375l0 -17.21875l1.921875 0l0 1.625q0.6875 -0.953125 1.53125 -1.421875q0.859375 -0.484375 2.078125 -0.484375q1.59375 0 2.8125 0.828125q1.21875 0.8125 1.84375 2.3125q0.625 1.5 0.625 3.28125q0 1.90625 -0.6875 3.4375q-0.6875 1.53125 -2.0 2.34375q-1.296875 0.8125 -2.734375 0.8125q-1.0625 0 -1.90625 -0.4375q-0.828125 -0.453125 -1.375 -1.140625l0 6.0625l-2.109375 0zm1.921875 -10.921875q0 2.40625 0.96875 3.5625q0.96875 1.140625 2.359375 1.140625q1.40625 0 2.40625 -1.1875q1.0 -1.1875 1.0 -3.6875q0 -2.375 -0.984375 -3.5625q-0.96875 -1.1875 -2.328125 -1.1875q-1.359375 0 -2.390625 1.265625q-1.03125 1.25 -1.03125 3.65625zm11.410156 6.15625l0 -12.453125l1.890625 0l0 1.890625q0.734375 -1.328125 1.34375 -1.75q0.625 -0.421875 1.359375 -0.421875q1.0625 0 2.171875 0.6875l-0.734375 1.953125q-0.765625 -0.453125 -1.546875 -0.453125q-0.6875 0 -1.25 0.421875q-0.546875 0.40625 -0.78125 1.140625q-0.34375 1.125 -0.34375 2.46875l0 6.515625l-2.109375 0zm8.0234375 -14.75l0 -2.4375l2.109375 0l0 2.4375l-2.109375 0zm0 14.75l0 -12.453125l2.109375 0l0 12.453125l-2.109375 0zm13.441406 -1.53125q-1.171875 0.984375 -2.265625 1.40625q-1.078125 0.40625 -2.3125 0.40625q-2.046875 0 -3.15625 -1.0q-1.09375 -1.0 -1.09375 -2.5625q0 -0.921875 0.40625 -1.671875q0.421875 -0.75 1.09375 -1.203125q0.671875 -0.46875 1.515625 -0.703125q0.625 -0.15625 1.875 -0.3125q2.5625 -0.3125 3.765625 -0.734375q0.015625 -0.421875 0.015625 -0.546875q0 -1.28125 -0.609375 -1.8125q-0.796875 -0.71875 -2.390625 -0.71875q-1.5 0 -2.203125 0.53125q-0.703125 0.515625 -1.046875 1.84375l-2.0625 -0.28125q0.28125 -1.328125 0.921875 -2.140625q0.640625 -0.8125 1.859375 -1.25q1.21875 -0.453125 2.828125 -0.453125q1.59375 0 2.59375 0.375q1.0 0.375 1.46875 0.953125q0.46875 0.5625 0.65625 1.4375q0.09375 0.53125 0.09375 1.9375l0 2.8125q0 2.9375 0.140625 3.71875q0.140625 0.78125 0.53125 1.5l-2.203125 0q-0.328125 -0.65625 -0.421875 -1.53125zm-0.171875 -4.71875q-1.15625 0.46875 -3.453125 0.796875q-1.296875 0.1875 -1.84375 0.421875q-0.53125 0.234375 -0.828125 0.6875q-0.28125 0.453125 -0.28125 1.0q0 0.84375 0.625 1.40625q0.640625 0.5625 1.875 0.5625q1.21875 0 2.171875 -0.53125q0.953125 -0.53125 1.390625 -1.453125q0.34375 -0.71875 0.34375 -2.109375l0 -0.78125zm10.003906 4.359375l0.3125 1.859375q-0.890625 0.203125 -1.59375 0.203125q-1.15625 0 -1.796875 -0.359375q-0.625 -0.375 -0.890625 -0.96875q-0.25 -0.59375 -0.25 -2.484375l0 -7.171875l-1.546875 0l0 -1.640625l1.546875 0l0 -3.078125l2.09375 -1.265625l0 4.34375l2.125 0l0 1.640625l-2.125 0l0 7.28125q0 0.90625 0.109375 1.171875q0.125 0.25 0.375 0.40625q0.25 0.140625 0.71875 0.140625q0.34375 0 0.921875 -0.078125zm10.589844 -2.125l2.171875 0.28125q-0.515625 1.90625 -1.90625 2.96875q-1.390625 1.046875 -3.5625 1.046875q-2.734375 0 -4.34375 -1.671875q-1.59375 -1.6875 -1.59375 -4.734375q0 -3.140625 1.609375 -4.875q1.625 -1.734375 4.203125 -1.734375q2.5 0 4.078125 1.703125q1.59375 1.703125 1.59375 4.78125q0 0.1875 -0.015625 0.5625l-9.28125 0q0.109375 2.046875 1.15625 3.140625q1.046875 1.09375 2.609375 1.09375q1.15625 0 1.96875 -0.609375q0.828125 -0.609375 1.3125 -1.953125zm-6.9375 -3.40625l6.953125 0q-0.140625 -1.5625 -0.796875 -2.359375q-1.0 -1.21875 -2.609375 -1.21875q-1.453125 0 -2.453125 0.984375q-0.984375 0.96875 -1.09375 2.59375zm18.4375 -7.328125l0 -2.4375l2.109375 0l0 2.4375l-2.109375 0zm0 14.75l0 -12.453125l2.109375 0l0 12.453125l-2.109375 0zm4.4726562 -3.71875l2.09375 -0.328125q0.171875 1.25 0.96875 1.921875q0.8125 0.671875 2.25 0.671875q1.453125 0 2.15625 -0.59375q0.703125 -0.59375 0.703125 -1.390625q0 -0.71875 -0.625 -1.125q-0.421875 -0.28125 -2.15625 -0.71875q-2.3125 -0.578125 -3.21875 -1.0q-0.890625 -0.4375 -1.359375 -1.1875q-0.453125 -0.765625 -0.453125 -1.671875q0 -0.828125 0.375 -1.53125q0.390625 -0.71875 1.046875 -1.1875q0.484375 -0.359375 1.328125 -0.609375q0.859375 -0.265625 1.828125 -0.265625q1.46875 0 2.578125 0.421875q1.109375 0.421875 1.625 1.15625q0.53125 0.71875 0.734375 1.921875l-2.0625 0.28125q-0.140625 -0.96875 -0.8125 -1.5q-0.671875 -0.546875 -1.90625 -0.546875q-1.453125 0 -2.078125 0.484375q-0.625 0.484375 -0.625 1.125q0 0.40625 0.265625 0.734375q0.25 0.34375 0.8125 0.5625q0.3125 0.125 1.859375 0.546875q2.234375 0.59375 3.109375 0.984375q0.890625 0.375 1.390625 1.109375q0.515625 0.71875 0.515625 1.796875q0 1.046875 -0.625 1.984375q-0.609375 0.9375 -1.765625 1.453125q-1.15625 0.5 -2.625 0.5q-2.421875 0 -3.703125 -1.0q-1.265625 -1.015625 -1.625 -3.0zm18.667969 0l2.09375 -0.328125q0.171875 1.25 0.96875 1.921875q0.8125 0.671875 2.25 0.671875q1.453125 0 2.15625 -0.59375q0.703125 -0.59375 0.703125 -1.390625q0 -0.71875 -0.625 -1.125q-0.421875 -0.28125 -2.15625 -0.71875q-2.3125 -0.578125 -3.21875 -1.0q-0.890625 -0.4375 -1.359375 -1.1875q-0.453125 -0.765625 -0.453125 -1.671875q0 -0.828125 0.375 -1.53125q0.390625 -0.71875 1.046875 -1.1875q0.484375 -0.359375 1.328125 -0.609375q0.859375 -0.265625 1.828125 -0.265625q1.46875 0 2.578125 0.421875q1.109375 0.421875 1.625 1.15625q0.53125 0.71875 0.734375 1.921875l-2.0625 0.28125q-0.140625 -0.96875 -0.8125 -1.5q-0.671875 -0.546875 -1.90625 -0.546875q-1.453125 0 -2.078125 0.484375q-0.625 0.484375 -0.625 1.125q0 0.40625 0.265625 0.734375q0.25 0.34375 0.8125 0.5625q0.3125 0.125 1.859375 0.546875q2.234375 0.59375 3.109375 0.984375q0.890625 0.375 1.390625 1.109375q0.515625 0.71875 0.515625 1.796875q0 1.046875 -0.625 1.984375q-0.609375 0.9375 -1.765625 1.453125q-1.15625 0.5 -2.625 0.5q-2.421875 0 -3.703125 -1.0q-1.265625 -1.015625 -1.625 -3.0zm21.375 -0.296875l2.171875 0.28125q-0.515625 1.90625 -1.90625 2.96875q-1.390625 1.046875 -3.5625 1.046875q-2.734375 0 -4.34375 -1.671875q-1.59375 -1.6875 -1.59375 -4.734375q0 -3.140625 1.609375 -4.875q1.625 -1.734375 4.203125 -1.734375q2.5 0 4.078125 1.703125q1.59375 1.703125 1.59375 4.78125q0 0.1875 -0.015625 0.5625l-9.28125 0q0.109375 2.046875 1.15625 3.140625q1.046875 1.09375 2.609375 1.09375q1.15625 0 1.96875 -0.609375q0.828125 -0.609375 1.3125 -1.953125zm-6.9375 -3.40625l6.953125 0q-0.140625 -1.5625 -0.796875 -2.359375q-1.0 -1.21875 -2.609375 -1.21875q-1.453125 0 -2.453125 0.984375q-0.984375 0.96875 -1.09375 2.59375zm11.738281 7.421875l0 -12.453125l1.890625 0l0 1.890625q0.734375 -1.328125 1.34375 -1.75q0.625 -0.421875 1.359375 -0.421875q1.0625 0 2.171875 0.6875l-0.734375 1.953125q-0.765625 -0.453125 -1.546875 -0.453125q-0.6875 0 -1.25 0.421875q-0.546875 0.40625 -0.78125 1.140625q-0.34375 1.125 -0.34375 2.46875l0 6.515625l-2.109375 0zm11.4765625 0l-4.734375 -12.453125l2.21875 0l2.671875 7.453125q0.4375 1.21875 0.796875 2.515625q0.28125 -0.984375 0.78125 -2.375l2.765625 -7.59375l2.171875 0l-4.703125 12.453125l-1.96875 0zm17.0625 -4.015625l2.171875 0.28125q-0.515625 1.90625 -1.90625 2.96875q-1.390625 1.046875 -3.5625 1.046875q-2.734375 0 -4.34375 -1.671875q-1.59375 -1.6875 -1.59375 -4.734375q0 -3.140625 1.609375 -4.875q1.625 -1.734375 4.203125 -1.734375q2.5 0 4.078125 1.703125q1.59375 1.703125 1.59375 4.78125q0 0.1875 -0.015625 0.5625l-9.28125 0q0.109375 2.046875 1.15625 3.140625q1.046875 1.09375 2.609375 1.09375q1.15625 0 1.96875 -0.609375q0.828125 -0.609375 1.3125 -1.953125zm-6.9375 -3.40625l6.953125 0q-0.140625 -1.5625 -0.796875 -2.359375q-1.0 -1.21875 -2.609375 -1.21875q-1.453125 0 -2.453125 0.984375q-0.984375 0.96875 -1.09375 2.59375zm11.738281 7.421875l0 -12.453125l1.890625 0l0 1.890625q0.734375 -1.328125 1.34375 -1.75q0.625 -0.421875 1.359375 -0.421875q1.0625 0 2.171875 0.6875l-0.734375 1.953125q-0.765625 -0.453125 -1.546875 -0.453125q-0.6875 0 -1.25 0.421875q-0.546875 0.40625 -0.78125 1.140625q-0.34375 1.125 -0.34375 2.46875l0 6.515625l-2.109375 0zm16.332031 -14.296875l0 2.453125l-2.265625 0l0 -1.9375q0 -1.578125 0.375 -2.28125q0.5 -0.9375 1.5625 -1.40625l0.515625 0.8125q-0.640625 0.265625 -0.953125 0.8125q-0.296875 0.53125 -0.328125 1.546875l1.09375 0zm3.640625 0l0 2.453125l-2.265625 0l0 -1.9375q0 -1.578125 0.375 -2.28125q0.484375 -0.9375 1.5625 -1.40625l0.515625 0.8125q-0.65625 0.265625 -0.96875 0.8125q-0.296875 0.53125 -0.328125 1.546875l1.109375 0zm2.1953735 8.78125l2.140625 -0.1875q0.15625 1.28125 0.703125 2.109375q0.5625 0.828125 1.734375 1.34375q1.171875 0.5 2.640625 0.5q1.296875 0 2.296875 -0.375q1.0 -0.390625 1.484375 -1.0625q0.484375 -0.6875 0.484375 -1.484375q0 -0.796875 -0.46875 -1.40625q-0.46875 -0.609375 -1.546875 -1.015625q-0.6875 -0.265625 -3.0625 -0.828125q-2.359375 -0.578125 -3.3125 -1.078125q-1.234375 -0.640625 -1.84375 -1.59375q-0.59375 -0.96875 -0.59375 -2.140625q0 -1.3125 0.734375 -2.4375q0.75 -1.125 2.15625 -1.703125q1.421875 -0.59375 3.15625 -0.59375q1.90625 0 3.359375 0.609375q1.46875 0.609375 2.25 1.8125q0.796875 1.1875 0.84375 2.703125l-2.171875 0.171875q-0.171875 -1.640625 -1.1875 -2.46875q-1.015625 -0.828125 -3.0 -0.828125q-2.0625 0 -3.015625 0.765625q-0.9375 0.75 -0.9375 1.8125q0 0.921875 0.671875 1.515625q0.65625 0.609375 3.421875 1.234375q2.78125 0.625 3.8125 1.09375q1.5 0.6875 2.203125 1.75q0.71875 1.0625 0.71875 2.4375q0 1.375 -0.78125 2.59375q-0.78125 1.203125 -2.25 1.890625q-1.46875 0.671875 -3.3125 0.671875q-2.328125 0 -3.90625 -0.671875q-1.578125 -0.6875 -2.484375 -2.046875q-0.890625 -1.375 -0.9375 -3.09375zm15.9453125 -9.390625l0 -2.453125l2.265625 0l0 1.9375q0 1.5625 -0.359375 2.265625q-0.5 0.9375 -1.578125 1.421875l-0.515625 -0.828125q0.640625 -0.265625 0.953125 -0.8125q0.3125 -0.5625 0.34375 -1.53125l-1.109375 0zm3.640625 0l0 -2.453125l2.265625 0l0 1.9375q0 1.5625 -0.375 2.265625q-0.5 0.9375 -1.5625 1.421875l-0.515625 -0.828125q0.625 -0.265625 0.9375 -0.8125q0.3125 -0.5625 0.34375 -1.53125l-1.09375 0zm12.097656 14.90625l0 -10.8125l-1.875 0l0 -1.640625l1.875 0l0 -1.3125q0 -1.265625 0.21875 -1.875q0.296875 -0.8125 1.0625 -1.3125q0.78125 -0.515625 2.15625 -0.515625q0.890625 0 1.96875 0.203125l-0.3125 1.84375q-0.65625 -0.125 -1.25 -0.125q-0.953125 0 -1.359375 0.421875q-0.390625 0.40625 -0.390625 1.53125l0 1.140625l2.421875 0l0 1.640625l-2.421875 0l0 10.8125l-2.09375 0zm5.3710938 -6.21875q0 -3.46875 1.921875 -5.125q1.609375 -1.390625 3.921875 -1.390625q2.5625 0 4.1875 1.6875q1.625 1.6875 1.625 4.640625q0 2.40625 -0.71875 3.78125q-0.71875 1.375 -2.09375 2.140625q-1.375 0.765625 -3.0 0.765625q-2.625 0 -4.234375 -1.671875q-1.609375 -1.6875 -1.609375 -4.828125zm2.171875 0q0 2.390625 1.03125 3.578125q1.046875 1.1875 2.640625 1.1875q1.5625 0 2.609375 -1.1875q1.046875 -1.203125 1.046875 -3.65625q0 -2.3125 -1.0625 -3.5q-1.046875 -1.1875 -2.59375 -1.1875q-1.59375 0 -2.640625 1.1875q-1.03125 1.1875 -1.03125 3.578125zm11.941406 6.21875l0 -12.453125l1.890625 0l0 1.890625q0.734375 -1.328125 1.34375 -1.75q0.625 -0.421875 1.359375 -0.421875q1.0625 0 2.171875 0.6875l-0.734375 1.953125q-0.765625 -0.453125 -1.546875 -0.453125q-0.6875 0 -1.25 0.421875q-0.546875 0.40625 -0.78125 1.140625q-0.34375 1.125 -0.34375 2.46875l0 6.515625l-2.109375 0zm13.832031 -3.71875l2.09375 -0.328125q0.171875 1.25 0.96875 1.921875q0.8125 0.671875 2.25 0.671875q1.453125 0 2.15625 -0.59375q0.703125 -0.59375 0.703125 -1.390625q0 -0.71875 -0.625 -1.125q-0.421875 -0.28125 -2.15625 -0.71875q-2.3125 -0.578125 -3.21875 -1.0q-0.890625 -0.4375 -1.359375 -1.1875q-0.453125 -0.765625 -0.453125 -1.671875q0 -0.828125 0.375 -1.53125q0.390625 -0.71875 1.046875 -1.1875q0.484375 -0.359375 1.328125 -0.609375q0.859375 -0.265625 1.828125 -0.265625q1.46875 0 2.578125 0.421875q1.109375 0.421875 1.625 1.15625q0.53125 0.71875 0.734375 1.921875l-2.0625 0.28125q-0.140625 -0.96875 -0.8125 -1.5q-0.671875 -0.546875 -1.90625 -0.546875q-1.453125 0 -2.078125 0.484375q-0.625 0.484375 -0.625 1.125q0 0.40625 0.265625 0.734375q0.25 0.34375 0.8125 0.5625q0.3125 0.125 1.859375 0.546875q2.234375 0.59375 3.109375 0.984375q0.890625 0.375 1.390625 1.109375q0.515625 0.71875 0.515625 1.796875q0 1.046875 -0.625 1.984375q-0.609375 0.9375 -1.765625 1.453125q-1.15625 0.5 -2.625 0.5q-2.421875 0 -3.703125 -1.0q-1.265625 -1.015625 -1.625 -3.0zm17.453125 1.828125l0.3125 1.859375q-0.890625 0.203125 -1.59375 0.203125q-1.15625 0 -1.796875 -0.359375q-0.625 -0.375 -0.890625 -0.96875q-0.25 -0.59375 -0.25 -2.484375l0 -7.171875l-1.546875 0l0 -1.640625l1.546875 0l0 -3.078125l2.09375 -1.265625l0 4.34375l2.125 0l0 1.640625l-2.125 0l0 7.28125q0 0.90625 0.109375 1.171875q0.125 0.25 0.375 0.40625q0.25 0.140625 0.71875 0.140625q0.34375 0 0.921875 -0.078125zm1.2773438 -4.328125q0 -3.46875 1.921875 -5.125q1.609375 -1.390625 3.921875 -1.390625q2.5625 0 4.1875 1.6875q1.625 1.6875 1.625 4.640625q0 2.40625 -0.71875 3.78125q-0.71875 1.375 -2.09375 2.140625q-1.375 0.765625 -3.0 0.765625q-2.625 0 -4.234375 -1.671875q-1.609375 -1.6875 -1.609375 -4.828125zm2.171875 0q0 2.390625 1.03125 3.578125q1.046875 1.1875 2.640625 1.1875q1.5625 0 2.609375 -1.1875q1.046875 -1.203125 1.046875 -3.65625q0 -2.3125 -1.0625 -3.5q-1.046875 -1.1875 -2.59375 -1.1875q-1.59375 0 -2.640625 1.1875q-1.03125 1.1875 -1.03125 3.578125zm11.941406 6.21875l0 -12.453125l1.890625 0l0 1.890625q0.734375 -1.328125 1.34375 -1.75q0.625 -0.421875 1.359375 -0.421875q1.0625 0 2.171875 0.6875l-0.734375 1.953125q-0.765625 -0.453125 -1.546875 -0.453125q-0.6875 0 -1.25 0.421875q-0.546875 0.40625 -0.78125 1.140625q-0.34375 1.125 -0.34375 2.46875l0 6.515625l-2.109375 0zm8.0234375 -14.75l0 -2.4375l2.109375 0l0 2.4375l-2.109375 0zm0 14.75l0 -12.453125l2.109375 0l0 12.453125l-2.109375 0zm5.3164062 0l0 -12.453125l1.90625 0l0 1.78125q1.375 -2.0625 3.953125 -2.0625q1.125 0 2.0625 0.40625q0.953125 0.40625 1.421875 1.0625q0.46875 0.65625 0.65625 1.5625q0.125 0.578125 0.125 2.046875l0 7.65625l-2.109375 0l0 -7.578125q0 -1.28125 -0.25 -1.921875q-0.25 -0.640625 -0.875 -1.015625q-0.625 -0.390625 -1.46875 -0.390625q-1.34375 0 -2.328125 0.859375q-0.984375 0.859375 -0.984375 3.25l0 6.796875l-2.109375 0zm12.972656 1.03125l2.046875 0.3125q0.125 0.9375 0.71875 1.375q0.78125 0.59375 2.140625 0.59375q1.46875 0 2.265625 -0.59375q0.796875 -0.578125 1.078125 -1.640625q0.15625 -0.640625 0.140625 -2.703125q-1.375 1.625 -3.4375 1.625q-2.5625 0 -3.96875 -1.84375q-1.40625 -1.859375 -1.40625 -4.453125q0 -1.78125 0.640625 -3.28125q0.640625 -1.515625 1.859375 -2.328125q1.234375 -0.828125 2.890625 -0.828125q2.203125 0 3.625 1.78125l0 -1.5l1.953125 0l0 10.765625q0 2.90625 -0.59375 4.109375q-0.59375 1.21875 -1.875 1.921875q-1.28125 0.703125 -3.15625 0.703125q-2.234375 0 -3.609375 -1.0q-1.359375 -1.0 -1.3125 -3.015625zm1.734375 -7.484375q0 2.453125 0.96875 3.578125q0.984375 1.125 2.453125 1.125q1.453125 0 2.4375 -1.109375q0.984375 -1.125 0.984375 -3.515625q0 -2.28125 -1.015625 -3.4375q-1.015625 -1.171875 -2.453125 -1.171875q-1.40625 0 -2.390625 1.140625q-0.984375 1.140625 -0.984375 3.390625zm20.609375 6.453125l-1.953125 0l0 -17.1875l2.109375 0l0 6.140625q1.328125 -1.6875 3.40625 -1.6875q1.140625 0 2.171875 0.46875q1.03125 0.46875 1.6875 1.3125q0.65625 0.828125 1.03125 2.015625q0.375 1.171875 0.375 2.53125q0 3.1875 -1.578125 4.9375q-1.578125 1.75 -3.796875 1.75q-2.203125 0 -3.453125 -1.84375l0 1.5625zm-0.03125 -6.3125q0 2.234375 0.609375 3.234375q1.0 1.625 2.703125 1.625q1.375 0 2.375 -1.203125q1.015625 -1.203125 1.015625 -3.578125q0 -2.4375 -0.96875 -3.59375q-0.953125 -1.171875 -2.328125 -1.171875q-1.390625 0 -2.40625 1.203125q-1.0 1.203125 -1.0 3.484375zm11.378906 6.3125l0 -17.1875l2.109375 0l0 17.1875l-2.109375 0zm4.5976562 -6.21875q0 -3.46875 1.921875 -5.125q1.609375 -1.390625 3.921875 -1.390625q2.5625 0 4.1875 1.6875q1.625 1.6875 1.625 4.640625q0 2.40625 -0.71875 3.78125q-0.71875 1.375 -2.09375 2.140625q-1.375 0.765625 -3.0 0.765625q-2.625 0 -4.234375 -1.671875q-1.609375 -1.6875 -1.609375 -4.828125zm2.171875 0q0 2.390625 1.03125 3.578125q1.046875 1.1875 2.640625 1.1875q1.5625 0 2.609375 -1.1875q1.046875 -1.203125 1.046875 -3.65625q0 -2.3125 -1.0625 -3.5q-1.046875 -1.1875 -2.59375 -1.1875q-1.59375 0 -2.640625 1.1875q-1.03125 1.1875 -1.03125 3.578125zm20.082031 1.65625l2.078125 0.265625q-0.34375 2.15625 -1.75 3.375q-1.390625 1.203125 -3.4375 1.203125q-2.546875 0 -4.109375 -1.671875q-1.546875 -1.671875 -1.546875 -4.78125q0 -2.015625 0.671875 -3.53125q0.671875 -1.515625 2.03125 -2.265625q1.359375 -0.765625 2.96875 -0.765625q2.03125 0 3.3125 1.03125q1.296875 1.03125 1.65625 2.90625l-2.046875 0.328125q-0.296875 -1.265625 -1.046875 -1.890625q-0.734375 -0.640625 -1.796875 -0.640625q-1.59375 0 -2.59375 1.15625q-0.984375 1.140625 -0.984375 3.609375q0 2.5 0.953125 3.640625q0.96875 1.140625 2.515625 1.140625q1.234375 0 2.0625 -0.765625q0.84375 -0.765625 1.0625 -2.34375zm3.890625 4.5625l0 -17.1875l2.109375 0l0 9.796875l5.0 -5.0625l2.71875 0l-4.75 4.625l5.234375 7.828125l-2.59375 0l-4.125 -6.359375l-1.484375 1.421875l0 4.9375l-2.109375 0zm20.308594 -14.296875l0 2.453125l-2.265625 0l0 -1.9375q0 -1.578125 0.375 -2.28125q0.5 -0.9375 1.5625 -1.40625l0.515625 0.8125q-0.640625 0.265625 -0.953125 0.8125q-0.296875 0.53125 -0.328125 1.546875l1.09375 0zm3.640625 0l0 2.453125l-2.265625 0l0 -1.9375q0 -1.578125 0.375 -2.28125q0.484375 -0.9375 1.5625 -1.40625l0.515625 0.8125q-0.65625 0.265625 -0.96875 0.8125q-0.296875 0.53125 -0.328125 1.546875l1.109375 0zm2.8828125 14.296875l0 -17.1875l6.4375 0q1.96875 0 3.15625 0.53125q1.1875 0.515625 1.859375 1.609375q0.6875 1.078125 0.6875 2.265625q0 1.09375 -0.609375 2.078125q-0.59375 0.96875 -1.796875 1.5625q1.5625 0.453125 2.390625 1.5625q0.84375 1.09375 0.84375 2.59375q0 1.203125 -0.515625 2.25q-0.5 1.03125 -1.25 1.59375q-0.75 0.5625 -1.890625 0.859375q-1.125 0.28125 -2.765625 0.28125l-6.546875 0zm2.265625 -9.96875l3.71875 0q1.515625 0 2.171875 -0.1875q0.859375 -0.265625 1.296875 -0.859375q0.4375 -0.59375 0.4375 -1.5q0 -0.859375 -0.40625 -1.5q-0.40625 -0.65625 -1.171875 -0.890625q-0.765625 -0.25 -2.609375 -0.25l-3.4375 0l0 5.1875zm0 7.9375l4.28125 0q1.09375 0 1.546875 -0.078125q0.78125 -0.140625 1.3125 -0.46875q0.53125 -0.328125 0.859375 -0.953125q0.34375 -0.625 0.34375 -1.453125q0 -0.953125 -0.5 -1.65625q-0.484375 -0.71875 -1.359375 -1.0q-0.875 -0.296875 -2.515625 -0.296875l-3.96875 0l0 5.90625zm12.9921875 -12.875l0 -2.453125l2.265625 0l0 1.9375q0 1.5625 -0.359375 2.265625q-0.5 0.9375 -1.578125 1.421875l-0.515625 -0.828125q0.640625 -0.265625 0.953125 -0.8125q0.3125 -0.5625 0.34375 -1.53125l-1.109375 0zm3.640625 0l0 -2.453125l2.265625 0l0 1.9375q0 1.5625 -0.375 2.265625q-0.5 0.9375 -1.5625 1.421875l-0.515625 -0.828125q0.625 -0.265625 0.9375 -0.8125q0.3125 -0.5625 0.34375 -1.53125l-1.09375 0zm8.8671875 10.671875q-0.015625 -0.421875 -0.015625 -0.625q0 -1.25 0.359375 -2.15625q0.25 -0.671875 0.828125 -1.359375q0.421875 -0.515625 1.515625 -1.46875q1.09375 -0.96875 1.421875 -1.546875q0.328125 -0.578125 0.328125 -1.25q0 -1.234375 -0.96875 -2.15625q-0.953125 -0.9375 -2.34375 -0.9375q-1.34375 0 -2.25 0.84375q-0.90625 0.84375 -1.1875 2.625l-2.15625 -0.25q0.28125 -2.40625 1.734375 -3.671875q1.453125 -1.28125 3.828125 -1.28125q2.515625 0 4.015625 1.375q1.5 1.359375 1.5 3.3125q0 1.125 -0.53125 2.078125q-0.515625 0.9375 -2.0625 2.296875q-1.03125 0.921875 -1.34375 1.359375q-0.3125 0.421875 -0.46875 0.984375q-0.15625 0.5625 -0.171875 1.828125l-2.03125 0zm-0.125 4.234375l0 -2.40625l2.40625 0l0 2.40625l-2.40625 0z" fill-rule="nonzero"></path><path stroke="#9e9e9e" stroke-width="1.0" stroke-linecap="butt" d="m44.666668 199.10498l0 245.38583" fill-rule="nonzero"></path><path stroke="#9e9e9e" stroke-width="1.0" stroke-linecap="butt" d="m340.52756 199.10498l0 245.38583" fill-rule="nonzero"></path><path stroke="#9e9e9e" stroke-width="1.0" stroke-linecap="butt" d="m636.3884 199.10498l0 245.38583" fill-rule="nonzero"></path><path stroke="#9e9e9e" stroke-width="1.0" stroke-linecap="butt" d="m932.2493 199.10498l0 245.38583" fill-rule="nonzero"></path><path stroke="#9e9e9e" stroke-width="1.0" stroke-linecap="butt" d="m44.16798 199.60367l888.5801 0" fill-rule="nonzero"></path><path stroke="#9e9e9e" stroke-width="1.0" stroke-linecap="butt" d="m44.16798 240.80052l888.5801 0" fill-rule="nonzero"></path><path stroke="#9e9e9e" stroke-width="1.0" stroke-linecap="butt" d="m44.16798 280.80054l888.5801 0" fill-rule="nonzero"></path><path stroke="#9e9e9e" stroke-width="1.0" stroke-linecap="butt" d="m44.16798 320.80054l888.5801 0" fill-rule="nonzero"></path><path stroke="#9e9e9e" stroke-width="1.0" stroke-linecap="butt" d="m44.16798 360.80054l888.5801 0" fill-rule="nonzero"></path><path stroke="#9e9e9e" stroke-width="1.0" stroke-linecap="butt" d="m44.16798 402.39633l888.5801 0" fill-rule="nonzero"></path><path stroke="#9e9e9e" stroke-width="1.0" stroke-linecap="butt" d="m44.16798 443.99213l888.5801 0" fill-rule="nonzero"></path><path fill="#000000" d="m55.432293 226.52367l0 -13.59375l1.8125 0l0 13.59375l-1.8125 0zm4.886429 0l0 -13.59375l4.687496 0q1.578125 0 2.421875 0.1875q1.15625 0.265625 1.984375 0.96875q1.078125 0.921875 1.609375 2.34375q0.53125 1.40625 0.53125 3.21875q0 1.546875 -0.359375 2.75q-0.359375 1.1875 -0.921875 1.984375q-0.5625 0.78125 -1.234375 1.234375q-0.671875 0.4375 -1.625 0.671875q-0.953125 0.234375 -2.1875 0.234375l-4.906246 0zm1.796875 -1.609375l2.9062462 0q1.34375 0 2.109375 -0.25q0.765625 -0.25 1.21875 -0.703125q0.640625 -0.640625 1.0 -1.71875q0.359375 -1.078125 0.359375 -2.625q0 -2.125 -0.703125 -3.265625q-0.703125 -1.15625 -1.703125 -1.546875q-0.71875 -0.28125 -2.328125 -0.28125l-2.8593712 0l0 10.390625zm14.644817 5.609375q-1.375 -1.75 -2.328125 -4.078125q-0.953125 -2.34375 -0.953125 -4.84375q0 -2.21875 0.703125 -4.234375q0.84375 -2.34375 2.578125 -4.671875l1.203125 0q-1.125 1.921875 -1.484375 2.75q-0.5625 1.28125 -0.890625 2.671875q-0.40625 1.734375 -0.40625 3.484375q0 4.46875 2.78125 8.921875l-1.203125 0zm2.634552 -8.375l1.6875 -0.140625q0.125 1.015625 0.5625 1.671875q0.4375 0.65625 1.359375 1.0625q0.9375 0.40625 2.09375 0.40625q1.03125 0 1.8125 -0.3125q0.796875 -0.3125 1.1875 -0.84375q0.390625 -0.53125 0.390625 -1.15625q0 -0.640625 -0.375 -1.109375q-0.375 -0.484375 -1.234375 -0.8125q-0.546875 -0.21875 -2.421875 -0.65625q-1.875 -0.453125 -2.625 -0.859375q-0.96875 -0.515625 -1.453125 -1.265625q-0.46875 -0.75 -0.46875 -1.6875q0 -1.03125 0.578125 -1.921875q0.59375 -0.90625 1.703125 -1.359375q1.125 -0.46875 2.5 -0.46875q1.515625 0 2.671875 0.484375q1.15625 0.484375 1.765625 1.4375q0.625 0.9375 0.671875 2.140625l-1.71875 0.125q-0.140625 -1.28125 -0.953125 -1.9375q-0.796875 -0.671875 -2.359375 -0.671875q-1.625 0 -2.375 0.609375q-0.75 0.59375 -0.75 1.4375q0 0.734375 0.53125 1.203125q0.515625 0.46875 2.703125 0.96875q2.203125 0.5 3.015625 0.875q1.1875 0.546875 1.75 1.390625q0.578125 0.828125 0.578125 1.921875q0 1.09375 -0.625 2.0625q-0.625 0.953125 -1.796875 1.484375q-1.15625 0.53125 -2.609375 0.53125q-1.84375 0 -3.09375 -0.53125q-1.25 -0.546875 -1.96875 -1.625q-0.703125 -1.078125 -0.734375 -2.453125zm13.927948 8.375l-1.1875 0q2.765625 -4.453125 2.765625 -8.921875q0 -1.734375 -0.390625 -3.453125q-0.328125 -1.390625 -0.890625 -2.671875q-0.359375 -0.84375 -1.484375 -2.78125l1.1875 0q1.75 2.328125 2.578125 4.671875q0.71875 2.015625 0.71875 4.234375q0 2.5 -0.96875 4.84375q-0.953125 2.328125 -2.328125 4.078125z" fill-rule="nonzero"></path><path fill="#000000" d="m350.9338 226.52367l0 -13.59375l2.71875 0l3.21875 9.625q0.4375 1.34375 0.640625 2.015625q0.234375 -0.75 0.734375 -2.1875l3.25 -9.453125l2.421875 0l0 13.59375l-1.734375 0l0 -11.390625l-3.953125 11.390625l-1.625 0l-3.9375 -11.578125l0 11.578125l-1.734375 0zm15.603302 0l0 -13.59375l4.6875 0q1.578125 0 2.421875 0.1875q1.15625 0.265625 1.984375 0.96875q1.078125 0.921875 1.609375 2.34375q0.53125 1.40625 0.53125 3.21875q0 1.546875 -0.359375 2.75q-0.359375 1.1875 -0.921875 1.984375q-0.5625 0.78125 -1.234375 1.234375q-0.671875 0.4375 -1.625 0.671875q-0.953125 0.234375 -2.1875 0.234375l-4.90625 0zm1.796875 -1.609375l2.90625 0q1.34375 0 2.109375 -0.25q0.765625 -0.25 1.21875 -0.703125q0.640625 -0.640625 1.0 -1.71875q0.359375 -1.078125 0.359375 -2.625q0 -2.125 -0.703125 -3.265625q-0.703125 -1.15625 -1.703125 -1.546875q-0.71875 -0.28125 -2.328125 -0.28125l-2.859375 0l0 10.390625zm10.988586 -1.953125l1.765625 -0.15625q0.1875 1.28125 0.890625 1.9375q0.71875 0.640625 1.71875 0.640625q1.203125 0 2.03125 -0.90625q0.84375 -0.90625 0.84375 -2.421875q0 -1.421875 -0.8125 -2.25q-0.796875 -0.828125 -2.09375 -0.828125q-0.796875 0 -1.453125 0.375q-0.640625 0.359375 -1.015625 0.953125l-1.578125 -0.203125l1.328125 -7.0l6.765625 0l0 1.609375l-5.4375 0l-0.734375 3.640625q1.234375 -0.84375 2.578125 -0.84375q1.78125 0 3.0 1.234375q1.234375 1.234375 1.234375 3.171875q0 1.84375 -1.078125 3.1875q-1.3125 1.65625 -3.578125 1.65625q-1.859375 0 -3.03125 -1.03125q-1.171875 -1.046875 -1.34375 -2.765625zm14.031952 7.5625q-1.375 -1.75 -2.328125 -4.078125q-0.953125 -2.34375 -0.953125 -4.84375q0 -2.21875 0.703125 -4.234375q0.84375 -2.34375 2.578125 -4.671875l1.203125 0q-1.125 1.921875 -1.484375 2.75q-0.5625 1.28125 -0.890625 2.671875q-0.40625 1.734375 -0.40625 3.484375q0 4.46875 2.78125 8.921875l-1.203125 0zm3.165802 -4.0l0 -13.59375l5.109375 0q1.546875 0 2.484375 0.40625q0.953125 0.40625 1.484375 1.265625q0.53125 0.859375 0.53125 1.796875q0 0.875 -0.46875 1.65625q-0.46875 0.765625 -1.4375 1.234375q1.234375 0.359375 1.890625 1.234375q0.671875 0.875 0.671875 2.0625q0 0.953125 -0.40625 1.78125q-0.390625 0.8125 -0.984375 1.265625q-0.59375 0.4375 -1.5 0.671875q-0.890625 0.21875 -2.1875 0.21875l-5.1875 0zm1.796875 -7.890625l2.9375 0q1.203125 0 1.71875 -0.15625q0.6875 -0.203125 1.03125 -0.671875q0.359375 -0.46875 0.359375 -1.1875q0 -0.671875 -0.328125 -1.1875q-0.328125 -0.515625 -0.9375 -0.703125q-0.59375 -0.203125 -2.0625 -0.203125l-2.71875 0l0 4.109375zm0 6.28125l3.390625 0q0.875 0 1.21875 -0.0625q0.625 -0.109375 1.046875 -0.359375q0.421875 -0.265625 0.6875 -0.765625q0.265625 -0.5 0.265625 -1.140625q0 -0.765625 -0.390625 -1.328125q-0.390625 -0.5625 -1.078125 -0.78125q-0.6875 -0.234375 -1.984375 -0.234375l-3.15625 0l0 4.671875zm11.599823 5.609375l-1.1875 0q2.765625 -4.453125 2.765625 -8.921875q0 -1.734375 -0.390625 -3.453125q-0.328125 -1.390625 -0.890625 -2.671875q-0.359375 -0.84375 -1.484375 -2.78125l1.1875 0q1.75 2.328125 2.578125 4.671875q0.71875 2.015625 0.71875 4.234375q0 2.5 -0.96875 4.84375q-0.953125 2.328125 -2.328125 4.078125z" fill-rule="nonzero"></path><path fill="#000000" d="m648.46655 226.52367l-3.015625 -9.859375l1.71875 0l1.5625 5.6875l0.59375 2.125q0.03125 -0.15625 0.5 -2.03125l1.578125 -5.78125l1.71875 0l1.46875 5.71875l0.484375 1.890625l0.578125 -1.90625l1.6875 -5.703125l1.625 0l-3.078125 9.859375l-1.734375 0l-1.578125 -5.90625l-0.375 -1.671875l-2.0 7.578125l-1.734375 0zm18.394836 -3.171875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm9.125732 -5.8125l0 -1.90625l1.671875 0l0 1.90625l-1.671875 0zm0 11.6875l0 -9.859375l1.671875 0l0 9.859375l-1.671875 0zm3.8323364 0.8125l1.609375 0.25q0.109375 0.75 0.578125 1.09375q0.609375 0.453125 1.6875 0.453125q1.171875 0 1.796875 -0.46875q0.625 -0.453125 0.859375 -1.28125q0.125 -0.515625 0.109375 -2.15625q-1.09375 1.296875 -2.71875 1.296875q-2.03125 0 -3.15625 -1.46875q-1.109375 -1.46875 -1.109375 -3.515625q0 -1.40625 0.515625 -2.59375q0.515625 -1.203125 1.484375 -1.84375q0.96875 -0.65625 2.265625 -0.65625q1.75 0 2.875 1.40625l0 -1.1875l1.546875 0l0 8.515625q0 2.3125 -0.46875 3.265625q-0.46875 0.96875 -1.484375 1.515625q-1.015625 0.5625 -2.5 0.5625q-1.765625 0 -2.859375 -0.796875q-1.078125 -0.796875 -1.03125 -2.390625zm1.375 -5.921875q0 1.953125 0.765625 2.84375q0.78125 0.890625 1.9375 0.890625q1.140625 0 1.921875 -0.890625q0.78125 -0.890625 0.78125 -2.78125q0 -1.8125 -0.8125 -2.71875q-0.796875 -0.921875 -1.921875 -0.921875q-1.109375 0 -1.890625 0.90625q-0.78125 0.890625 -0.78125 2.671875zm9.297546 5.109375l0 -13.59375l1.671875 0l0 4.875q1.171875 -1.359375 2.953125 -1.359375q1.09375 0 1.890625 0.4375q0.8125 0.421875 1.15625 1.1875q0.359375 0.765625 0.359375 2.203125l0 6.25l-1.671875 0l0 -6.25q0 -1.25 -0.546875 -1.8125q-0.546875 -0.578125 -1.53125 -0.578125q-0.75 0 -1.40625 0.390625q-0.640625 0.375 -0.921875 1.046875q-0.28125 0.65625 -0.28125 1.8125l0 5.390625l-1.671875 0zm14.031982 -1.5l0.234375 1.484375q-0.703125 0.140625 -1.265625 0.140625q-0.90625 0 -1.40625 -0.28125q-0.5 -0.296875 -0.703125 -0.75q-0.203125 -0.46875 -0.203125 -1.984375l0 -5.65625l-1.234375 0l0 -1.3125l1.234375 0l0 -2.4375l1.65625 -1.0l0 3.4375l1.6875 0l0 1.3125l-1.6875 0l0 5.75q0 0.71875 0.078125 0.921875q0.09375 0.203125 0.296875 0.328125q0.203125 0.125 0.578125 0.125q0.265625 0 0.734375 -0.078125z" fill-rule="nonzero"></path><path fill="#000000" d="m61.447918 263.88052l-1.75 0l-3.421875 -3.9375l0 3.9375l-1.28125 0l0 -10.34375l1.28125 0l0 6.359375l3.296875 -3.375l1.6875 0l-3.453125 3.390625l3.640625 3.96875zm7.667446 -4.0625q0 0.265625 -0.015625 0.453125q0 0.1875 -0.015625 0.34375l-5.171871 0q0 1.125 0.6249962 1.734375q0.640625 0.59375 1.828125 0.59375q0.3125 0 0.640625 -0.015625q0.328125 -0.03125 0.625 -0.0625q0.296875 -0.046875 0.5625 -0.09375q0.28125 -0.0625 0.515625 -0.140625l0 1.046875q-0.515625 0.15625 -1.171875 0.234375q-0.65625 0.09375 -1.359375 0.09375q-0.9375 0 -1.625 -0.25q-0.6874962 -0.25 -1.1249962 -0.734375q-0.421875 -0.5 -0.640625 -1.203125q-0.203125 -0.703125 -0.203125 -1.59375q0 -0.78125 0.21875 -1.46875q0.21875 -0.703125 0.640625 -1.21875q0.4375 -0.53125 1.0624962 -0.828125q0.625 -0.3125 1.421875 -0.3125q0.765625 0 1.359375 0.25q0.59375 0.234375 1.0 0.6875q0.40625 0.4375 0.609375 1.078125q0.21875 0.625 0.21875 1.40625zm-1.328125 -0.1875q0.015625 -0.484375 -0.109375 -0.890625q-0.109375 -0.40625 -0.359375 -0.703125q-0.234375 -0.296875 -0.609375 -0.453125q-0.359375 -0.171875 -0.84375 -0.171875q-0.421875 0 -0.765625 0.171875q-0.34375 0.15625 -0.59375 0.453125q-0.25 0.28125 -0.40625 0.703125q-0.14062119 0.40625 -0.18749619 0.890625l3.8749962 0zm9.3862 0.1875q0 0.265625 -0.015625 0.453125q0 0.1875 -0.015625 0.34375l-5.171875 0q0 1.125 0.625 1.734375q0.640625 0.59375 1.828125 0.59375q0.3125 0 0.640625 -0.015625q0.328125 -0.03125 0.625 -0.0625q0.296875 -0.046875 0.5625 -0.09375q0.28125 -0.0625 0.515625 -0.140625l0 1.046875q-0.515625 0.15625 -1.171875 0.234375q-0.65625 0.09375 -1.359375 0.09375q-0.9375 0 -1.625 -0.25q-0.6875 -0.25 -1.125 -0.734375q-0.421875 -0.5 -0.640625 -1.203125q-0.203125 -0.703125 -0.203125 -1.59375q0 -0.78125 0.21875 -1.46875q0.21875 -0.703125 0.640625 -1.21875q0.4375 -0.53125 1.0625 -0.828125q0.625 -0.3125 1.421875 -0.3125q0.765625 0 1.359375 0.25q0.59375 0.234375 1.0 0.6875q0.40625 0.4375 0.609375 1.078125q0.21875 0.625 0.21875 1.40625zm-1.328125 -0.1875q0.015625 -0.484375 -0.109375 -0.890625q-0.109375 -0.40625 -0.359375 -0.703125q-0.234375 -0.296875 -0.609375 -0.453125q-0.359375 -0.171875 -0.84375 -0.171875q-0.421875 0 -0.765625 0.171875q-0.34375 0.15625 -0.59375 0.453125q-0.25 0.28125 -0.40625 0.703125q-0.140625 0.40625 -0.1875 0.890625l3.875 0zm9.4487 0.4375q0 0.984375 -0.28125 1.71875q-0.265625 0.734375 -0.75 1.21875q-0.484375 0.484375 -1.140625 0.734375q-0.65625 0.234375 -1.421875 0.234375q-0.359375 0 -0.703125 -0.046875q-0.34375 -0.03125 -0.703125 -0.125l0 3.078125l-1.28125 0l0 -10.359375l1.140625 0l0.078125 1.234375q0.546875 -0.75 1.171875 -1.046875q0.625 -0.3125 1.34375 -0.3125q0.625 0 1.09375 0.265625q0.484375 0.265625 0.8125 0.75q0.328125 0.46875 0.484375 1.15625q0.15625 0.671875 0.15625 1.5zm-1.296875 0.0625q0 -0.578125 -0.09375 -1.0625q-0.078125 -0.484375 -0.265625 -0.828125q-0.171875 -0.34375 -0.46875 -0.53125q-0.28125 -0.203125 -0.671875 -0.203125q-0.25 0 -0.5 0.078125q-0.25 0.078125 -0.515625 0.265625q-0.265625 0.171875 -0.5625 0.46875q-0.296875 0.296875 -0.625 0.734375l0 3.5625q0.34375 0.15625 0.734375 0.25q0.390625 0.078125 0.765625 0.078125q1.03125 0 1.609375 -0.703125q0.59375 -0.703125 0.59375 -2.109375zm9.5112 -1.03125q0 1.09375 -0.234375 2.0q-0.21875 0.90625 -0.671875 1.5625q-0.4375 0.640625 -1.109375 1.0q-0.65625 0.34375 -1.546875 0.34375q-0.765625 0 -1.40625 -0.28125q-0.625 -0.296875 -1.078125 -0.890625q-0.4375 -0.59375 -0.6875 -1.53125q-0.234375 -0.9375 -0.234375 -2.203125q0 -1.09375 0.21875 -2.0q0.234375 -0.921875 0.671875 -1.5625q0.453125 -0.65625 1.109375 -1.0q0.671875 -0.359375 1.5625 -0.359375q0.765625 0 1.390625 0.296875q0.625 0.28125 1.078125 0.890625q0.453125 0.59375 0.6875 1.53125q0.25 0.921875 0.25 2.203125zm-1.296875 0.046875q0 -0.25 -0.015625 -0.5q-0.015625 -0.25 -0.046875 -0.484375l-4.046875 3.015625q0.109375 0.375 0.28125 0.703125q0.171875 0.328125 0.40625 0.5625q0.234375 0.21875 0.53125 0.359375q0.3125 0.125 0.703125 0.125q0.5 0 0.90625 -0.234375q0.40625 -0.25 0.6875 -0.71875q0.28125 -0.484375 0.4375 -1.1875q0.15625 -0.71875 0.15625 -1.640625zm-4.375 -0.09375q0 0.234375 0 0.46875q0 0.21875 0.03125 0.421875l4.046875 -2.984375q-0.109375 -0.375 -0.28125 -0.6875q-0.171875 -0.3125 -0.40625 -0.53125q-0.234375 -0.21875 -0.53125 -0.34375q-0.296875 -0.125 -0.671875 -0.125q-0.5 0 -0.90625 0.25q-0.40625 0.234375 -0.703125 0.71875q-0.28125 0.46875 -0.4375 1.1875q-0.140625 0.703125 -0.140625 1.625z" fill-rule="nonzero"></path><path fill="#000000" d="m355.41818 263.88052l-0.03125 -0.984375q-0.59375 0.59375 -1.21875 0.859375q-0.609375 0.25 -1.296875 0.25q-0.625 0 -1.078125 -0.15625q-0.4375 -0.15625 -0.734375 -0.4375q-0.28125 -0.28125 -0.421875 -0.65625q-0.140625 -0.390625 -0.140625 -0.84375q0 -1.09375 0.828125 -1.71875q0.828125 -0.640625 2.4375 -0.640625l1.515625 0l0 -0.640625q0 -0.65625 -0.421875 -1.046875q-0.40625 -0.390625 -1.265625 -0.390625q-0.625 0 -1.234375 0.140625q-0.59375 0.140625 -1.234375 0.40625l0 -1.15625q0.234375 -0.09375 0.53125 -0.171875q0.296875 -0.078125 0.625 -0.140625q0.328125 -0.078125 0.6875 -0.109375q0.359375 -0.046875 0.734375 -0.046875q0.65625 0 1.1875 0.15625q0.546875 0.140625 0.90625 0.4375q0.375 0.296875 0.5625 0.75q0.203125 0.453125 0.203125 1.078125l0 5.0625l-1.140625 0zm-0.140625 -3.34375l-1.609375 0q-0.484375 0 -0.828125 0.09375q-0.34375 0.09375 -0.5625 0.265625q-0.21875 0.171875 -0.328125 0.421875q-0.09375 0.25 -0.09375 0.5625q0 0.203125 0.0625 0.40625q0.0625 0.1875 0.203125 0.34375q0.15625 0.140625 0.390625 0.234375q0.234375 0.078125 0.5625 0.078125q0.4375 0 1.0 -0.265625q0.578125 -0.265625 1.203125 -0.84375l0 -1.296875zm9.1987 3.078125q-0.5 0.1875 -1.03125 0.28125q-0.515625 0.09375 -1.078125 0.09375q-1.734375 0 -2.6875 -0.9375q-0.9375 -0.953125 -0.9375 -2.78125q0 -0.859375 0.265625 -1.5625q0.28125 -0.71875 0.765625 -1.21875q0.5 -0.515625 1.171875 -0.78125q0.6875 -0.28125 1.5 -0.28125q0.578125 0 1.078125 0.078125q0.5 0.078125 0.953125 0.265625l0 1.21875q-0.484375 -0.25 -0.984375 -0.359375q-0.484375 -0.125 -1.015625 -0.125q-0.484375 0 -0.921875 0.1875q-0.4375 0.1875 -0.765625 0.546875q-0.328125 0.34375 -0.53125 0.859375q-0.1875 0.5 -0.1875 1.140625q0 1.328125 0.640625 2.0q0.65625 0.65625 1.8125 0.65625q0.515625 0 1.0 -0.109375q0.5 -0.125 0.953125 -0.359375l0 1.1875zm8.620575 -3.546875q0 0.921875 -0.25 1.640625q-0.25 0.71875 -0.71875 1.21875q-0.46875 0.5 -1.140625 0.78125q-0.65625 0.265625 -1.484375 0.265625q-0.65625 0 -1.34375 -0.125q-0.671875 -0.125 -1.34375 -0.40625l0 -9.90625l1.28125 0l0 2.84375l-0.0625 1.359375q0.546875 -0.734375 1.171875 -1.03125q0.625 -0.3125 1.34375 -0.3125q0.625 0 1.09375 0.265625q0.484375 0.265625 0.8125 0.75q0.328125 0.46875 0.484375 1.15625q0.15625 0.671875 0.15625 1.5zm-1.296875 0.0625q0 -0.578125 -0.09375 -1.0625q-0.078125 -0.484375 -0.265625 -0.828125q-0.171875 -0.34375 -0.46875 -0.53125q-0.28125 -0.203125 -0.671875 -0.203125q-0.25 0 -0.5 0.078125q-0.25 0.078125 -0.515625 0.265625q-0.265625 0.171875 -0.5625 0.46875q-0.296875 0.296875 -0.625 0.734375l0 3.5625q0.359375 0.15625 0.75 0.25q0.390625 0.078125 0.75 0.078125q0.4375 0 0.828125 -0.140625q0.40625 -0.140625 0.703125 -0.46875q0.3125 -0.328125 0.484375 -0.859375q0.1875 -0.546875 0.1875 -1.34375zm2.6987 0.234375q0 -0.9375 0.25 -1.671875q0.265625 -0.734375 0.734375 -1.234375q0.46875 -0.5 1.125 -0.75q0.671875 -0.265625 1.484375 -0.265625q0.359375 0 0.6875 0.046875q0.34375 0.03125 0.671875 0.125l0 -3.078125l1.28125 0l0 10.34375l-1.140625 0l-0.03125 -1.390625q-0.546875 0.78125 -1.171875 1.15625q-0.609375 0.359375 -1.34375 0.359375q-0.625 0 -1.109375 -0.25q-0.46875 -0.265625 -0.796875 -0.75q-0.3125 -0.484375 -0.484375 -1.15625q-0.15625 -0.671875 -0.15625 -1.484375zm1.3125 -0.09375q0 1.34375 0.390625 2.0q0.390625 0.65625 1.109375 0.65625q0.484375 0 1.015625 -0.4375q0.53125 -0.4375 1.125 -1.28125l0 -3.421875q-0.3125 -0.140625 -0.6875 -0.21875q-0.375 -0.078125 -0.75 -0.078125q-1.046875 0 -1.625 0.671875q-0.578125 0.671875 -0.578125 2.109375zm13.245575 3.609375l-6.0 0l0 -1.1875l2.453125 0l0 -6.984375l-2.296875 1.25l-0.46875 -1.09375l3.046875 -1.59375l1.125 0l0 8.421875l2.140625 0l0 1.1875zm8.151825 -2.40625q0 0.609375 -0.25 1.09375q-0.25 0.46875 -0.703125 0.796875q-0.453125 0.3125 -1.0625 0.484375q-0.59375 0.15625 -1.3125 0.15625q-0.78125 0 -1.375 -0.171875q-0.59375 -0.171875 -1.0 -0.484375q-0.40625 -0.3125 -0.609375 -0.75q-0.203125 -0.4375 -0.203125 -0.953125q0 -0.875 0.484375 -1.515625q0.5 -0.640625 1.515625 -1.15625q-0.9375 -0.484375 -1.375 -1.0625q-0.421875 -0.578125 -0.421875 -1.328125q0 -0.46875 0.1875 -0.890625q0.1875 -0.4375 0.578125 -0.765625q0.390625 -0.34375 0.96875 -0.546875q0.578125 -0.203125 1.359375 -0.203125q0.734375 0 1.296875 0.15625q0.5625 0.15625 0.9375 0.453125q0.390625 0.296875 0.578125 0.71875q0.1875 0.40625 0.1875 0.921875q0 0.828125 -0.46875 1.421875q-0.46875 0.578125 -1.3125 1.015625q0.421875 0.21875 0.78125 0.484375q0.375 0.25 0.640625 0.5625q0.265625 0.3125 0.421875 0.703125q0.15625 0.390625 0.15625 0.859375zm-1.53125 -4.953125q0 -0.640625 -0.453125 -0.953125q-0.453125 -0.328125 -1.28125 -0.328125q-0.828125 0 -1.28125 0.3125q-0.453125 0.3125 -0.453125 0.9375q0 0.296875 0.109375 0.546875q0.109375 0.234375 0.328125 0.453125q0.234375 0.203125 0.578125 0.40625q0.34375 0.203125 0.828125 0.421875q0.828125 -0.390625 1.21875 -0.8125q0.40625 -0.421875 0.40625 -0.984375zm0.140625 5.046875q0 -0.265625 -0.09375 -0.515625q-0.078125 -0.265625 -0.3125 -0.515625q-0.21875 -0.25 -0.609375 -0.5q-0.375 -0.25 -0.96875 -0.5q-0.5 0.234375 -0.84375 0.46875q-0.328125 0.234375 -0.546875 0.484375q-0.203125 0.25 -0.296875 0.515625q-0.078125 0.265625 -0.078125 0.546875q0 0.328125 0.140625 0.59375q0.140625 0.25 0.390625 0.421875q0.25 0.171875 0.59375 0.265625q0.34375 0.09375 0.75 0.09375q0.390625 0 0.734375 -0.078125q0.34375 -0.09375 0.59375 -0.25q0.25 -0.171875 0.390625 -0.421875q0.15625 -0.25 0.15625 -0.609375zm2.85495 -1.203125q0 -0.9375 0.25 -1.671875q0.265625 -0.734375 0.734375 -1.234375q0.46875 -0.5 1.125 -0.75q0.671875 -0.265625 1.484375 -0.265625q0.359375 0 0.6875 0.046875q0.34375 0.03125 0.671875 0.125l0 -3.078125l1.28125 0l0 10.34375l-1.140625 0l-0.03125 -1.390625q-0.546875 0.78125 -1.171875 1.15625q-0.609375 0.359375 -1.34375 0.359375q-0.625 0 -1.109375 -0.25q-0.46875 -0.265625 -0.796875 -0.75q-0.3125 -0.484375 -0.484375 -1.15625q-0.15625 -0.671875 -0.15625 -1.484375zm1.3125 -0.09375q0 1.34375 0.390625 2.0q0.390625 0.65625 1.109375 0.65625q0.484375 0 1.015625 -0.4375q0.53125 -0.4375 1.125 -1.28125l0 -3.421875q-0.3125 -0.140625 -0.6875 -0.21875q-0.375 -0.078125 -0.75 -0.078125q-1.046875 0 -1.625 0.671875q-0.578125 0.671875 -0.578125 2.109375zm13.401825 -0.203125q0 0.921875 -0.25 1.640625q-0.25 0.71875 -0.71875 1.21875q-0.46875 0.5 -1.140625 0.78125q-0.65625 0.265625 -1.484375 0.265625q-0.65625 0 -1.34375 -0.125q-0.671875 -0.125 -1.34375 -0.40625l0 -9.90625l1.28125 0l0 2.84375l-0.0625 1.359375q0.546875 -0.734375 1.171875 -1.03125q0.625 -0.3125 1.34375 -0.3125q0.625 0 1.09375 0.265625q0.484375 0.265625 0.8125 0.75q0.328125 0.46875 0.484375 1.15625q0.15625 0.671875 0.15625 1.5zm-1.296875 0.0625q0 -0.578125 -0.09375 -1.0625q-0.078125 -0.484375 -0.265625 -0.828125q-0.171875 -0.34375 -0.46875 -0.53125q-0.28125 -0.203125 -0.671875 -0.203125q-0.25 0 -0.5 0.078125q-0.25 0.078125 -0.515625 0.265625q-0.265625 0.171875 -0.5625 0.46875q-0.296875 0.296875 -0.625 0.734375l0 3.5625q0.359375 0.15625 0.75 0.25q0.390625 0.078125 0.75 0.078125q0.4375 0 0.828125 -0.140625q0.40625 -0.140625 0.703125 -0.46875q0.3125 -0.328125 0.484375 -0.859375q0.1875 -0.546875 0.1875 -1.34375zm9.7612 1.640625l-1.640625 0l0 2.109375l-1.296875 0l0 -2.109375l-4.609375 0l0 -1.125l4.078125 -6.34375l1.828125 0l0 6.34375l1.640625 0l0 1.125zm-2.9375 -6.234375l-3.328125 5.109375l3.328125 0l0 -5.109375zm10.026825 8.078125q-0.5 0.1875 -1.03125 0.28125q-0.515625 0.09375 -1.078125 0.09375q-1.734375 0 -2.6875 -0.9375q-0.9375 -0.953125 -0.9375 -2.78125q0 -0.859375 0.265625 -1.5625q0.28125 -0.71875 0.765625 -1.21875q0.5 -0.515625 1.171875 -0.78125q0.6875 -0.28125 1.5 -0.28125q0.578125 0 1.078125 0.078125q0.5 0.078125 0.953125 0.265625l0 1.21875q-0.484375 -0.25 -0.984375 -0.359375q-0.484375 -0.125 -1.015625 -0.125q-0.484375 0 -0.921875 0.1875q-0.4375 0.1875 -0.765625 0.546875q-0.328125 0.34375 -0.53125 0.859375q-0.1875 0.5 -0.1875 1.140625q0 1.328125 0.640625 2.0q0.65625 0.65625 1.8125 0.65625q0.515625 0 1.0 -0.109375q0.5 -0.125 0.953125 -0.359375l0 1.1875zm8.058075 0q-0.5 0.1875 -1.03125 0.28125q-0.515625 0.09375 -1.078125 0.09375q-1.734375 0 -2.6875 -0.9375q-0.9375 -0.953125 -0.9375 -2.78125q0 -0.859375 0.265625 -1.5625q0.28125 -0.71875 0.765625 -1.21875q0.5 -0.515625 1.171875 -0.78125q0.6875 -0.28125 1.5 -0.28125q0.578125 0 1.078125 0.078125q0.5 0.078125 0.953125 0.265625l0 1.21875q-0.484375 -0.25 -0.984375 -0.359375q-0.484375 -0.125 -1.015625 -0.125q-0.484375 0 -0.921875 0.1875q-0.4375 0.1875 -0.765625 0.546875q-0.328125 0.34375 -0.53125 0.859375q-0.1875 0.5 -0.1875 1.140625q0 1.328125 0.640625 2.0q0.65625 0.65625 1.8125 0.65625q0.515625 0 1.0 -0.109375q0.5 -0.125 0.953125 -0.359375l0 1.1875zm8.558075 0.265625l-6.3125 0l0 -1.140625l2.46875 -2.46875q0.609375 -0.59375 0.984375 -1.03125q0.390625 -0.4375 0.59375 -0.796875q0.21875 -0.375 0.296875 -0.6875q0.078125 -0.328125 0.078125 -0.703125q0 -0.34375 -0.109375 -0.65625q-0.09375 -0.328125 -0.296875 -0.5625q-0.1875 -0.25 -0.5 -0.390625q-0.3125 -0.140625 -0.75 -0.140625q-0.609375 0 -1.109375 0.28125q-0.5 0.265625 -0.921875 0.6875l-0.703125 -0.828125q0.546875 -0.578125 1.25 -0.921875q0.703125 -0.34375 1.640625 -0.34375q0.640625 0 1.15625 0.1875q0.53125 0.1875 0.90625 0.546875q0.390625 0.359375 0.59375 0.890625q0.21875 0.515625 0.21875 1.15625q0 0.5625 -0.15625 1.03125q-0.140625 0.46875 -0.4375 0.9375q-0.296875 0.453125 -0.75 0.953125q-0.453125 0.5 -1.0625 1.09375l-1.734375 1.6875l4.65625 0l0 1.21875zm8.370575 -9.15625q-1.015625 -0.21875 -1.734375 -0.21875q-1.71875 0 -1.71875 1.796875l0 1.296875l3.21875 0l0 1.0625l-3.21875 0l0 5.21875l-1.296875 0l0 -5.21875l-2.359375 0l0 -1.0625l2.359375 0l0 -1.21875q0 -2.9375 3.0625 -2.9375q0.765625 0 1.6875 0.171875l0 1.109375zm-7.703125 1.796875l0 0zm15.4487 4.953125q0 0.609375 -0.25 1.09375q-0.25 0.46875 -0.703125 0.796875q-0.453125 0.3125 -1.0625 0.484375q-0.59375 0.15625 -1.3125 0.15625q-0.78125 0 -1.375 -0.171875q-0.59375 -0.171875 -1.0 -0.484375q-0.40625 -0.3125 -0.609375 -0.75q-0.203125 -0.4375 -0.203125 -0.953125q0 -0.875 0.484375 -1.515625q0.5 -0.640625 1.515625 -1.15625q-0.9375 -0.484375 -1.375 -1.0625q-0.421875 -0.578125 -0.421875 -1.328125q0 -0.46875 0.1875 -0.890625q0.1875 -0.4375 0.578125 -0.765625q0.390625 -0.34375 0.96875 -0.546875q0.578125 -0.203125 1.359375 -0.203125q0.734375 0 1.296875 0.15625q0.5625 0.15625 0.9375 0.453125q0.390625 0.296875 0.578125 0.71875q0.1875 0.40625 0.1875 0.921875q0 0.828125 -0.46875 1.421875q-0.46875 0.578125 -1.3125 1.015625q0.421875 0.21875 0.78125 0.484375q0.375 0.25 0.640625 0.5625q0.265625 0.3125 0.421875 0.703125q0.15625 0.390625 0.15625 0.859375zm-1.53125 -4.953125q0 -0.640625 -0.453125 -0.953125q-0.453125 -0.328125 -1.28125 -0.328125q-0.828125 0 -1.28125 0.3125q-0.453125 0.3125 -0.453125 0.9375q0 0.296875 0.109375 0.546875q0.109375 0.234375 0.328125 0.453125q0.234375 0.203125 0.578125 0.40625q0.34375 0.203125 0.828125 0.421875q0.828125 -0.390625 1.21875 -0.8125q0.40625 -0.421875 0.40625 -0.984375zm0.140625 5.046875q0 -0.265625 -0.09375 -0.515625q-0.078125 -0.265625 -0.3125 -0.515625q-0.21875 -0.25 -0.609375 -0.5q-0.375 -0.25 -0.96875 -0.5q-0.5 0.234375 -0.84375 0.46875q-0.328125 0.234375 -0.546875 0.484375q-0.203125 0.25 -0.296875 0.515625q-0.078125 0.265625 -0.078125 0.546875q0 0.328125 0.140625 0.59375q0.140625 0.25 0.390625 0.421875q0.25 0.171875 0.59375 0.265625q0.34375 0.09375 0.75 0.09375q0.390625 0 0.734375 -0.078125q0.34375 -0.09375 0.59375 -0.25q0.25 -0.171875 0.390625 -0.421875q0.15625 -0.25 0.15625 -0.609375zm9.1987 -0.75q0 0.71875 -0.3125 1.3125q-0.296875 0.578125 -0.84375 1.0q-0.53125 0.40625 -1.265625 0.640625q-0.734375 0.234375 -1.578125 0.234375q-0.21875 0 -0.46875 -0.015625q-0.234375 0 -0.484375 -0.015625q-0.234375 -0.015625 -0.46875 -0.046875q-0.234375 -0.015625 -0.421875 -0.046875l0 -1.15625q0.40625 0.09375 0.90625 0.140625q0.515625 0.046875 1.03125 0.046875q0.59375 0 1.0625 -0.140625q0.46875 -0.140625 0.796875 -0.390625q0.328125 -0.265625 0.5 -0.640625q0.171875 -0.375 0.171875 -0.828125q0 -0.90625 -0.640625 -1.3125q-0.640625 -0.40625 -1.84375 -0.40625l-1.8125 0l0 -4.890625l5.15625 0l0 1.125l-3.953125 0l0 2.6875l0.84375 0q0.6875 0 1.328125 0.125q0.65625 0.125 1.15625 0.4375q0.515625 0.296875 0.828125 0.828125q0.3125 0.515625 0.3125 1.3125zm7.808075 2.796875q-0.5 0.1875 -1.03125 0.28125q-0.515625 0.09375 -1.078125 0.09375q-1.734375 0 -2.6875 -0.9375q-0.9375 -0.953125 -0.9375 -2.78125q0 -0.859375 0.265625 -1.5625q0.28125 -0.71875 0.765625 -1.21875q0.5 -0.515625 1.171875 -0.78125q0.6875 -0.28125 1.5 -0.28125q0.578125 0 1.078125 0.078125q0.5 0.078125 0.953125 0.265625l0 1.21875q-0.484375 -0.25 -0.984375 -0.359375q-0.484375 -0.125 -1.015625 -0.125q-0.484375 0 -0.921875 0.1875q-0.4375 0.1875 -0.765625 0.546875q-0.328125 0.34375 -0.53125 0.859375q-0.1875 0.5 -0.1875 1.140625q0 1.328125 0.640625 2.0q0.65625 0.65625 1.8125 0.65625q0.515625 0 1.0 -0.109375q0.5 -0.125 0.953125 -0.359375l0 1.1875zm8.558075 -3.796875q0 0.265625 -0.015625 0.453125q0 0.1875 -0.015625 0.34375l-5.171875 0q0 1.125 0.625 1.734375q0.640625 0.59375 1.828125 0.59375q0.3125 0 0.640625 -0.015625q0.328125 -0.03125 0.625 -0.0625q0.296875 -0.046875 0.5625 -0.09375q0.28125 -0.0625 0.515625 -0.140625l0 1.046875q-0.515625 0.15625 -1.171875 0.234375q-0.65625 0.09375 -1.359375 0.09375q-0.9375 0 -1.625 -0.25q-0.6875 -0.25 -1.125 -0.734375q-0.421875 -0.5 -0.640625 -1.203125q-0.203125 -0.703125 -0.203125 -1.59375q0 -0.78125 0.21875 -1.46875q0.21875 -0.703125 0.640625 -1.21875q0.4375 -0.53125 1.0625 -0.828125q0.625 -0.3125 1.421875 -0.3125q0.765625 0 1.359375 0.25q0.59375 0.234375 1.0 0.6875q0.40625 0.4375 0.609375 1.078125q0.21875 0.625 0.21875 1.40625zm-1.328125 -0.1875q0.015625 -0.484375 -0.109375 -0.890625q-0.109375 -0.40625 -0.359375 -0.703125q-0.234375 -0.296875 -0.609375 -0.453125q-0.359375 -0.171875 -0.84375 -0.171875q-0.421875 0 -0.765625 0.171875q-0.34375 0.15625 -0.59375 0.453125q-0.25 0.28125 -0.40625 0.703125q-0.140625 0.40625 -0.1875 0.890625l3.875 0zm2.79245 0.734375q0 -0.9375 0.25 -1.671875q0.265625 -0.734375 0.734375 -1.234375q0.46875 -0.5 1.125 -0.75q0.671875 -0.265625 1.484375 -0.265625q0.359375 0 0.6875 0.046875q0.34375 0.03125 0.671875 0.125l0 -3.078125l1.28125 0l0 10.34375l-1.140625 0l-0.03125 -1.390625q-0.546875 0.78125 -1.171875 1.15625q-0.609375 0.359375 -1.34375 0.359375q-0.625 0 -1.109375 -0.25q-0.46875 -0.265625 -0.796875 -0.75q-0.3125 -0.484375 -0.484375 -1.15625q-0.15625 -0.671875 -0.15625 -1.484375zm1.3125 -0.09375q0 1.34375 0.390625 2.0q0.390625 0.65625 1.109375 0.65625q0.484375 0 1.015625 -0.4375q0.53125 -0.4375 1.125 -1.28125l0 -3.421875q-0.3125 -0.140625 -0.6875 -0.21875q-0.375 -0.078125 -0.75 -0.078125q-1.046875 0 -1.625 0.671875q-0.578125 0.671875 -0.578125 2.109375zm13.339325 -0.453125q0 0.265625 -0.015625 0.453125q0 0.1875 -0.015625 0.34375l-5.171875 0q0 1.125 0.625 1.734375q0.640625 0.59375 1.828125 0.59375q0.3125 0 0.640625 -0.015625q0.328125 -0.03125 0.625 -0.0625q0.296875 -0.046875 0.5625 -0.09375q0.28125 -0.0625 0.515625 -0.140625l0 1.046875q-0.515625 0.15625 -1.171875 0.234375q-0.65625 0.09375 -1.359375 0.09375q-0.9375 0 -1.625 -0.25q-0.6875 -0.25 -1.125 -0.734375q-0.421875 -0.5 -0.640625 -1.203125q-0.203125 -0.703125 -0.203125 -1.59375q0 -0.78125 0.21875 -1.46875q0.21875 -0.703125 0.640625 -1.21875q0.4375 -0.53125 1.0625 -0.828125q0.625 -0.3125 1.421875 -0.3125q0.765625 0 1.359375 0.25q0.59375 0.234375 1.0 0.6875q0.40625 0.4375 0.609375 1.078125q0.21875 0.625 0.21875 1.40625zm-1.328125 -0.1875q0.015625 -0.484375 -0.109375 -0.890625q-0.109375 -0.40625 -0.359375 -0.703125q-0.234375 -0.296875 -0.609375 -0.453125q-0.359375 -0.171875 -0.84375 -0.171875q-0.421875 0 -0.765625 0.171875q-0.34375 0.15625 -0.59375 0.453125q-0.25 0.28125 -0.40625 0.703125q-0.140625 0.40625 -0.1875 0.890625l3.875 0zm9.6987 -4.90625q-1.015625 -0.21875 -1.734375 -0.21875q-1.71875 0 -1.71875 1.796875l0 1.296875l3.21875 0l0 1.0625l-3.21875 0l0 5.21875l-1.296875 0l0 -5.21875l-2.359375 0l0 -1.0625l2.359375 0l0 -1.21875q0 -2.9375 3.0625 -2.9375q0.765625 0 1.6875 0.171875l0 1.109375zm-7.703125 1.796875l0 0zm15.558105 4.328125q0 0.65625 -0.234375 1.234375q-0.234375 0.578125 -0.6875 1.015625q-0.4375 0.421875 -1.0625 0.671875q-0.609375 0.234375 -1.359375 0.234375q-0.796875 0 -1.40625 -0.25q-0.609375 -0.25 -1.015625 -0.765625q-0.40625 -0.53125 -0.6250305 -1.328125q-0.203125 -0.796875 -0.203125 -1.890625q0 -0.734375 0.09375 -1.421875q0.09375 -0.6875 0.31253052 -1.296875q0.21875 -0.609375 0.578125 -1.109375q0.375 -0.5 0.921875 -0.859375q0.546875 -0.375 1.28125 -0.578125q0.734375 -0.203125 1.71875 -0.203125l0.9375 0l0 1.125l-1.015625 0q-0.859375 0 -1.5 0.203125q-0.625 0.203125 -1.046875 0.578125q-0.421875 0.375 -0.65625 0.90625q-0.21875 0.515625 -0.28125 1.171875l-0.03125 0.296875q0.46875 -0.265625 1.0625 -0.421875q0.609375 -0.171875 1.3125 -0.171875q0.71875 0 1.265625 0.21875q0.546875 0.203125 0.90625 0.578125q0.375 0.375 0.546875 0.90625q0.1875 0.53125 0.1875 1.15625zm-1.328125 0.078125q0 -0.4375 -0.109375 -0.796875q-0.109375 -0.359375 -0.34375 -0.59375q-0.21875 -0.25 -0.5625 -0.375q-0.34375 -0.140625 -0.828125 -0.140625q-0.28125 0 -0.578125 0.046875q-0.28125 0.046875 -0.5625 0.140625q-0.265625 0.09375 -0.515625 0.21875q-0.25 0.109375 -0.453125 0.234375q0 0.953125 0.125 1.59375q0.140625 0.625 0.390625 1.015625q0.265625 0.375 0.640625 0.53125q0.390625 0.15625 0.890625 0.15625q0.421875 0 0.765625 -0.125q0.34375 -0.140625 0.59375 -0.40625q0.25 -0.265625 0.390625 -0.640625q0.15625 -0.375 0.15625 -0.859375zm9.026794 -0.109375q0 0.71875 -0.3125 1.3125q-0.296875 0.578125 -0.84375 1.0q-0.53125 0.40625 -1.265625 0.640625q-0.734375 0.234375 -1.578125 0.234375q-0.21875 0 -0.46875 -0.015625q-0.234375 0 -0.484375 -0.015625q-0.234375 -0.015625 -0.46875 -0.046875q-0.234375 -0.015625 -0.421875 -0.046875l0 -1.15625q0.40625 0.09375 0.90625 0.140625q0.515625 0.046875 1.03125 0.046875q0.59375 0 1.0625 -0.140625q0.46875 -0.140625 0.796875 -0.390625q0.328125 -0.265625 0.5 -0.640625q0.171875 -0.375 0.171875 -0.828125q0 -0.90625 -0.640625 -1.3125q-0.640625 -0.40625 -1.84375 -0.40625l-1.8125 0l0 -4.890625l5.15625 0l0 1.125l-3.953125 0l0 2.6875l0.84375 0q0.6875 0 1.328125 0.125q0.65625 0.125 1.15625 0.4375q0.515625 0.296875 0.828125 0.828125q0.3125 0.515625 0.3125 1.3125zm8.776855 0.953125l-1.640625 0l0 2.109375l-1.296875 0l0 -2.109375l-4.609375 0l0 -1.125l4.078125 -6.34375l1.828125 0l0 6.34375l1.640625 0l0 1.125zm-2.9375 -6.234375l-3.328125 5.109375l3.328125 0l0 -5.109375zm10.839294 -0.8125q-1.015625 -0.21875 -1.734375 -0.21875q-1.71875 0 -1.71875 1.796875l0 1.296875l3.21875 0l0 1.0625l-3.21875 0l0 5.21875l-1.296875 0l0 -5.21875l-2.359375 0l0 -1.0625l2.359375 0l0 -1.21875q0 -2.9375 3.0625 -2.9375q0.765625 0 1.6875 0.171875l0 1.109375zm-7.703125 1.796875l0 0zm14.94873 7.09375q-0.5 0.1875 -1.03125 0.28125q-0.515625 0.09375 -1.078125 0.09375q-1.734375 0 -2.6875 -0.9375q-0.9375 -0.953125 -0.9375 -2.78125q0 -0.859375 0.265625 -1.5625q0.28125 -0.71875 0.765625 -1.21875q0.5 -0.515625 1.171875 -0.78125q0.6875 -0.28125 1.5 -0.28125q0.578125 0 1.078125 0.078125q0.5 0.078125 0.953125 0.265625l0 1.21875q-0.484375 -0.25 -0.984375 -0.359375q-0.484375 -0.125 -1.015625 -0.125q-0.484375 0 -0.921875 0.1875q-0.4375 0.1875 -0.765625 0.546875q-0.328125 0.34375 -0.53125 0.859375q-0.1875 0.5 -0.1875 1.140625q0 1.328125 0.640625 2.0q0.65625 0.65625 1.8125 0.65625q0.515625 0 1.0 -0.109375q0.5 -0.125 0.953125 -0.359375l0 1.1875zm8.058044 0q-0.5 0.1875 -1.03125 0.28125q-0.515625 0.09375 -1.078125 0.09375q-1.734375 0 -2.6875 -0.9375q-0.9375 -0.953125 -0.9375 -2.78125q0 -0.859375 0.265625 -1.5625q0.28125 -0.71875 0.765625 -1.21875q0.5 -0.515625 1.171875 -0.78125q0.6875 -0.28125 1.5 -0.28125q0.578125 0 1.078125 0.078125q0.5 0.078125 0.953125 0.265625l0 1.21875q-0.484375 -0.25 -0.984375 -0.359375q-0.484375 -0.125 -1.015625 -0.125q-0.484375 0 -0.921875 0.1875q-0.4375 0.1875 -0.765625 0.546875q-0.328125 0.34375 -0.53125 0.859375q-0.1875 0.5 -0.1875 1.140625q0 1.328125 0.640625 2.0q0.65625 0.65625 1.8125 0.65625q0.515625 0 1.0 -0.109375q0.5 -0.125 0.953125 -0.359375l0 1.1875zm8.058105 0q-0.5 0.1875 -1.03125 0.28125q-0.515625 0.09375 -1.078125 0.09375q-1.734375 0 -2.6875 -0.9375q-0.9375 -0.953125 -0.9375 -2.78125q0 -0.859375 0.265625 -1.5625q0.28125 -0.71875 0.765625 -1.21875q0.5 -0.515625 1.171875 -0.78125q0.6875 -0.28125 1.5 -0.28125q0.578125 0 1.078125 0.078125q0.5 0.078125 0.953125 0.265625l0 1.21875q-0.484375 -0.25 -0.984375 -0.359375q-0.484375 -0.125 -1.015625 -0.125q-0.484375 0 -0.921875 0.1875q-0.4375 0.1875 -0.765625 0.546875q-0.328125 0.34375 -0.53125 0.859375q-0.1875 0.5 -0.1875 1.140625q0 1.328125 0.640625 2.0q0.65625 0.65625 1.8125 0.65625q0.515625 0 1.0 -0.109375q0.5 -0.125 0.953125 -0.359375l0 1.1875zm9.026794 -1.84375l-1.640625 0l0 2.109375l-1.296875 0l0 -2.109375l-4.609375 0l0 -1.125l4.078125 -6.34375l1.828125 0l0 6.34375l1.640625 0l0 1.125zm-2.9375 -6.234375l-3.328125 5.109375l3.328125 0l0 -5.109375zm9.026855 8.34375l-0.03125 -0.984375q-0.59375 0.59375 -1.21875 0.859375q-0.609375 0.25 -1.296875 0.25q-0.625 0 -1.078125 -0.15625q-0.4375 -0.15625 -0.734375 -0.4375q-0.28125 -0.28125 -0.421875 -0.65625q-0.140625 -0.390625 -0.140625 -0.84375q0 -1.09375 0.828125 -1.71875q0.828125 -0.640625 2.4375 -0.640625l1.515625 0l0 -0.640625q0 -0.65625 -0.421875 -1.046875q-0.40625 -0.390625 -1.265625 -0.390625q-0.625 0 -1.234375 0.140625q-0.59375 0.140625 -1.234375 0.40625l0 -1.15625q0.234375 -0.09375 0.53125 -0.171875q0.296875 -0.078125 0.625 -0.140625q0.328125 -0.078125 0.6875 -0.109375q0.359375 -0.046875 0.734375 -0.046875q0.65625 0 1.1875 0.15625q0.546875 0.140625 0.90625 0.4375q0.375 0.296875 0.5625 0.75q0.203125 0.453125 0.203125 1.078125l0 5.0625l-1.140625 0zm-0.140625 -3.34375l-1.609375 0q-0.484375 0 -0.828125 0.09375q-0.34375 0.09375 -0.5625 0.265625q-0.21875 0.171875 -0.328125 0.421875q-0.09375 0.25 -0.09375 0.5625q0 0.203125 0.0625 0.40625q0.0625 0.1875 0.203125 0.34375q0.15625 0.140625 0.390625 0.234375q0.234375 0.078125 0.5625 0.078125q0.4375 0 1.0 -0.265625q0.578125 -0.265625 1.203125 -0.84375l0 -1.296875zm10.167419 1.234375l-1.640625 0l0 2.109375l-1.296875 0l0 -2.109375l-4.609375 0l0 -1.125l4.078125 -6.34375l1.828125 0l0 6.34375l1.640625 0l0 1.125zm-2.9375 -6.234375l-3.328125 5.109375l3.328125 0l0 -5.109375zm3.9331055 4.828125q0 -0.9375 0.25 -1.671875q0.265625 -0.734375 0.734375 -1.234375q0.46875 -0.5 1.125 -0.75q0.671875 -0.265625 1.484375 -0.265625q0.359375 0 0.6875 0.046875q0.34375 0.03125 0.671875 0.125l0 -3.078125l1.28125 0l0 10.34375l-1.140625 0l-0.03125 -1.390625q-0.546875 0.78125 -1.171875 1.15625q-0.609375 0.359375 -1.34375 0.359375q-0.625 0 -1.109375 -0.25q-0.46875 -0.265625 -0.796875 -0.75q-0.3125 -0.484375 -0.484375 -1.15625q-0.15625 -0.671875 -0.15625 -1.484375zm1.3125 -0.09375q0 1.34375 0.390625 2.0q0.390625 0.65625 1.109375 0.65625q0.484375 0 1.015625 -0.4375q0.53125 -0.4375 1.125 -1.28125l0 -3.421875q-0.3125 -0.140625 -0.6875 -0.21875q-0.375 -0.078125 -0.75 -0.078125q-1.046875 0 -1.625 0.671875q-0.578125 0.671875 -0.578125 2.109375zm13.339294 1.203125q0 0.609375 -0.25 1.09375q-0.25 0.46875 -0.703125 0.796875q-0.453125 0.3125 -1.0625 0.484375q-0.59375 0.15625 -1.3125 0.15625q-0.78125 0 -1.375 -0.171875q-0.59375 -0.171875 -1.0 -0.484375q-0.40625 -0.3125 -0.609375 -0.75q-0.203125 -0.4375 -0.203125 -0.953125q0 -0.875 0.484375 -1.515625q0.5 -0.640625 1.515625 -1.15625q-0.9375 -0.484375 -1.375 -1.0625q-0.421875 -0.578125 -0.421875 -1.328125q0 -0.46875 0.1875 -0.890625q0.1875 -0.4375 0.578125 -0.765625q0.390625 -0.34375 0.96875 -0.546875q0.578125 -0.203125 1.359375 -0.203125q0.734375 0 1.296875 0.15625q0.5625 0.15625 0.9375 0.453125q0.390625 0.296875 0.578125 0.71875q0.1875 0.40625 0.1875 0.921875q0 0.828125 -0.46875 1.421875q-0.46875 0.578125 -1.3125 1.015625q0.421875 0.21875 0.78125 0.484375q0.375 0.25 0.640625 0.5625q0.265625 0.3125 0.421875 0.703125q0.15625 0.390625 0.15625 0.859375zm-1.53125 -4.953125q0 -0.640625 -0.453125 -0.953125q-0.453125 -0.328125 -1.28125 -0.328125q-0.828125 0 -1.28125 0.3125q-0.453125 0.3125 -0.453125 0.9375q0 0.296875 0.109375 0.546875q0.109375 0.234375 0.328125 0.453125q0.234375 0.203125 0.578125 0.40625q0.34375 0.203125 0.828125 0.421875q0.828125 -0.390625 1.21875 -0.8125q0.40625 -0.421875 0.40625 -0.984375zm0.140625 5.046875q0 -0.265625 -0.09375 -0.515625q-0.078125 -0.265625 -0.3125 -0.515625q-0.21875 -0.25 -0.609375 -0.5q-0.375 -0.25 -0.96875 -0.5q-0.5 0.234375 -0.84375 0.46875q-0.328125 0.234375 -0.546875 0.484375q-0.203125 0.25 -0.296875 0.515625q-0.078125 0.265625 -0.078125 0.546875q0 0.328125 0.140625 0.59375q0.140625 0.25 0.390625 0.421875q0.25 0.171875 0.59375 0.265625q0.34375 0.09375 0.75 0.09375q0.390625 0 0.734375 -0.078125q0.34375 -0.09375 0.59375 -0.25q0.25 -0.171875 0.390625 -0.421875q0.15625 -0.25 0.15625 -0.609375z" fill-rule="nonzero"></path><path fill="#000000" d="m652.6853 263.88052l-6.0 0l0 -1.1875l2.453125 0l0 -6.984375l-2.296875 1.25l-0.46875 -1.09375l3.046875 -1.59375l1.125 0l0 8.421875l2.140625 0l0 1.1875zm8.151855 -2.40625q0 0.609375 -0.25 1.09375q-0.25 0.46875 -0.703125 0.796875q-0.453125 0.3125 -1.0625 0.484375q-0.59375 0.15625 -1.3125 0.15625q-0.78125 0 -1.375 -0.171875q-0.59375 -0.171875 -1.0 -0.484375q-0.40625 -0.3125 -0.609375 -0.75q-0.203125 -0.4375 -0.203125 -0.953125q0 -0.875 0.484375 -1.515625q0.5 -0.640625 1.515625 -1.15625q-0.9375 -0.484375 -1.375 -1.0625q-0.421875 -0.578125 -0.421875 -1.328125q0 -0.46875 0.1875 -0.890625q0.1875 -0.4375 0.578125 -0.765625q0.390625 -0.34375 0.96875 -0.546875q0.578125 -0.203125 1.359375 -0.203125q0.734375 0 1.296875 0.15625q0.5625 0.15625 0.9375 0.453125q0.390625 0.296875 0.578125 0.71875q0.1875 0.40625 0.1875 0.921875q0 0.828125 -0.46875 1.421875q-0.46875 0.578125 -1.3125 1.015625q0.421875 0.21875 0.78125 0.484375q0.375 0.25 0.640625 0.5625q0.265625 0.3125 0.421875 0.703125q0.15625 0.390625 0.15625 0.859375zm-1.53125 -4.953125q0 -0.640625 -0.453125 -0.953125q-0.453125 -0.328125 -1.28125 -0.328125q-0.828125 0 -1.28125 0.3125q-0.453125 0.3125 -0.453125 0.9375q0 0.296875 0.109375 0.546875q0.109375 0.234375 0.328125 0.453125q0.234375 0.203125 0.578125 0.40625q0.34375 0.203125 0.828125 0.421875q0.828125 -0.390625 1.21875 -0.8125q0.40625 -0.421875 0.40625 -0.984375zm0.140625 5.046875q0 -0.265625 -0.09375 -0.515625q-0.078125 -0.265625 -0.3125 -0.515625q-0.21875 -0.25 -0.609375 -0.5q-0.375 -0.25 -0.96875 -0.5q-0.5 0.234375 -0.84375 0.46875q-0.328125 0.234375 -0.546875 0.484375q-0.203125 0.25 -0.296875 0.515625q-0.078125 0.265625 -0.078125 0.546875q0 0.328125 0.140625 0.59375q0.140625 0.25 0.390625 0.421875q0.25 0.171875 0.59375 0.265625q0.34375 0.09375 0.75 0.09375q0.390625 0 0.734375 -0.078125q0.34375 -0.09375 0.59375 -0.25q0.25 -0.171875 0.390625 -0.421875q0.15625 -0.25 0.15625 -0.609375zm9.558044 -0.71875q0 0.65625 -0.234375 1.234375q-0.234375 0.578125 -0.6875 1.015625q-0.4375 0.421875 -1.0625 0.671875q-0.609375 0.234375 -1.359375 0.234375q-0.796875 0 -1.40625 -0.25q-0.609375 -0.25 -1.015625 -0.765625q-0.40625 -0.53125 -0.625 -1.328125q-0.203125 -0.796875 -0.203125 -1.890625q0 -0.734375 0.09375 -1.421875q0.09375 -0.6875 0.3125 -1.296875q0.21875 -0.609375 0.578125 -1.109375q0.375 -0.5 0.921875 -0.859375q0.546875 -0.375 1.28125 -0.578125q0.734375 -0.203125 1.71875 -0.203125l0.9375 0l0 1.125l-1.015625 0q-0.859375 0 -1.5 0.203125q-0.625 0.203125 -1.046875 0.578125q-0.421875 0.375 -0.65625 0.90625q-0.21875 0.515625 -0.28125 1.171875l-0.03125 0.296875q0.46875 -0.265625 1.0625 -0.421875q0.609375 -0.171875 1.3125 -0.171875q0.71875 0 1.265625 0.21875q0.546875 0.203125 0.90625 0.578125q0.375 0.375 0.546875 0.90625q0.1875 0.53125 0.1875 1.15625zm-1.328125 0.078125q0 -0.4375 -0.109375 -0.796875q-0.109375 -0.359375 -0.34375 -0.59375q-0.21875 -0.25 -0.5625 -0.375q-0.34375 -0.140625 -0.828125 -0.140625q-0.28125 0 -0.578125 0.046875q-0.28125 0.046875 -0.5625 0.140625q-0.265625 0.09375 -0.515625 0.21875q-0.25 0.109375 -0.453125 0.234375q0 0.953125 0.125 1.59375q0.140625 0.625 0.390625 1.015625q0.265625 0.375 0.640625 0.53125q0.390625 0.15625 0.890625 0.15625q0.421875 0 0.765625 -0.125q0.34375 -0.140625 0.59375 -0.40625q0.25 -0.265625 0.390625 -0.640625q0.15625 -0.375 0.15625 -0.859375zm9.214355 -2.375q0 1.40625 -0.34375 2.421875q-0.328125 1.0 -0.984375 1.65625q-0.65625 0.640625 -1.640625 0.953125q-0.96875 0.296875 -2.25 0.296875l-0.796875 0l0 -1.109375l0.890625 0q0.953125 0 1.640625 -0.1875q0.6875 -0.203125 1.140625 -0.5625q0.453125 -0.375 0.6875 -0.90625q0.25 -0.53125 0.3125 -1.21875l0.03125 -0.296875q-0.46875 0.28125 -1.078125 0.453125q-0.59375 0.15625 -1.296875 0.15625q-0.71875 0 -1.265625 -0.21875q-0.546875 -0.21875 -0.921875 -0.59375q-0.359375 -0.375 -0.546875 -0.890625q-0.171875 -0.53125 -0.171875 -1.15625q0 -0.65625 0.234375 -1.234375q0.25 -0.578125 0.6875 -1.0q0.4375 -0.4375 1.03125 -0.6875q0.609375 -0.25 1.34375 -0.25q0.71875 0 1.3125 0.234375q0.609375 0.234375 1.046875 0.765625q0.4375 0.515625 0.6875 1.359375q0.25 0.828125 0.25 2.015625zm-3.34375 -3.328125q-0.421875 0 -0.765625 0.140625q-0.34375 0.125 -0.609375 0.390625q-0.25 0.265625 -0.390625 0.640625q-0.140625 0.375 -0.140625 0.875q0 0.4375 0.09375 0.796875q0.109375 0.34375 0.328125 0.59375q0.234375 0.25 0.578125 0.390625q0.359375 0.125 0.84375 0.125q0.265625 0 0.546875 -0.046875q0.296875 -0.0625 0.5625 -0.140625q0.28125 -0.09375 0.53125 -0.203125q0.25 -0.125 0.453125 -0.265625q0 -0.9375 -0.140625 -1.5625q-0.140625 -0.640625 -0.40625 -1.015625q-0.265625 -0.390625 -0.640625 -0.546875q-0.375 -0.171875 -0.84375 -0.171875zm10.964294 8.390625q-0.5 0.1875 -1.03125 0.28125q-0.515625 0.09375 -1.078125 0.09375q-1.734375 0 -2.6875 -0.9375q-0.9375 -0.953125 -0.9375 -2.78125q0 -0.859375 0.265625 -1.5625q0.28125 -0.71875 0.765625 -1.21875q0.5 -0.515625 1.171875 -0.78125q0.6875 -0.28125 1.5 -0.28125q0.578125 0 1.078125 0.078125q0.5 0.078125 0.953125 0.265625l0 1.21875q-0.484375 -0.25 -0.984375 -0.359375q-0.484375 -0.125 -1.015625 -0.125q-0.484375 0 -0.921875 0.1875q-0.4375 0.1875 -0.765625 0.546875q-0.328125 0.34375 -0.53125 0.859375q-0.1875 0.5 -0.1875 1.140625q0 1.328125 0.640625 2.0q0.65625 0.65625 1.8125 0.65625q0.515625 0 1.0 -0.109375q0.5 -0.125 0.953125 -0.359375l0 1.1875zm8.870605 -8.890625q-1.015625 -0.21875 -1.734375 -0.21875q-1.71875 0 -1.71875 1.796875l0 1.296875l3.21875 0l0 1.0625l-3.21875 0l0 5.21875l-1.296875 0l0 -5.21875l-2.359375 0l0 -1.0625l2.359375 0l0 -1.21875q0 -2.9375 3.0625 -2.9375q0.765625 0 1.6875 0.171875l0 1.109375zm-7.703125 1.796875l0 0zm15.386169 -1.03125l-4.015625 8.390625l-1.453125 0l4.171875 -8.390625l-5.171875 0l0 -1.1875l6.46875 0l0 1.1875zm8.058105 3.0625q0 1.40625 -0.34375 2.421875q-0.328125 1.0 -0.984375 1.65625q-0.65625 0.640625 -1.640625 0.953125q-0.96875 0.296875 -2.25 0.296875l-0.796875 0l0 -1.109375l0.890625 0q0.953125 0 1.640625 -0.1875q0.6875 -0.203125 1.140625 -0.5625q0.453125 -0.375 0.6875 -0.90625q0.25 -0.53125 0.3125 -1.21875l0.03125 -0.296875q-0.46875 0.28125 -1.078125 0.453125q-0.59375 0.15625 -1.296875 0.15625q-0.71875 0 -1.265625 -0.21875q-0.546875 -0.21875 -0.921875 -0.59375q-0.359375 -0.375 -0.546875 -0.890625q-0.171875 -0.53125 -0.171875 -1.15625q0 -0.65625 0.234375 -1.234375q0.25 -0.578125 0.6875 -1.0q0.4375 -0.4375 1.03125 -0.6875q0.609375 -0.25 1.34375 -0.25q0.71875 0 1.3125 0.234375q0.609375 0.234375 1.046875 0.765625q0.4375 0.515625 0.6875 1.359375q0.25 0.828125 0.25 2.015625zm-3.34375 -3.328125q-0.421875 0 -0.765625 0.140625q-0.34375 0.125 -0.609375 0.390625q-0.25 0.265625 -0.390625 0.640625q-0.140625 0.375 -0.140625 0.875q0 0.4375 0.09375 0.796875q0.109375 0.34375 0.328125 0.59375q0.234375 0.25 0.578125 0.390625q0.359375 0.125 0.84375 0.125q0.265625 0 0.546875 -0.046875q0.296875 -0.0625 0.5625 -0.140625q0.28125 -0.09375 0.53125 -0.203125q0.25 -0.125 0.453125 -0.265625q0 -0.9375 -0.140625 -1.5625q-0.140625 -0.640625 -0.40625 -1.015625q-0.265625 -0.390625 -0.640625 -0.546875q-0.375 -0.171875 -0.84375 -0.171875zm11.683044 3.875q0 1.09375 -0.234375 2.0q-0.21875 0.90625 -0.671875 1.5625q-0.4375 0.640625 -1.109375 1.0q-0.65625 0.34375 -1.546875 0.34375q-0.765625 0 -1.40625 -0.28125q-0.625 -0.296875 -1.078125 -0.890625q-0.4375 -0.59375 -0.6875 -1.53125q-0.234375 -0.9375 -0.234375 -2.203125q0 -1.09375 0.21875 -2.0q0.234375 -0.921875 0.671875 -1.5625q0.453125 -0.65625 1.109375 -1.0q0.671875 -0.359375 1.5625 -0.359375q0.765625 0 1.390625 0.296875q0.625 0.28125 1.078125 0.890625q0.453125 0.59375 0.6875 1.53125q0.25 0.921875 0.25 2.203125zm-1.296875 0.046875q0 -0.25 -0.015625 -0.5q-0.015625 -0.25 -0.046875 -0.484375l-4.046875 3.015625q0.109375 0.375 0.28125 0.703125q0.171875 0.328125 0.40625 0.5625q0.234375 0.21875 0.53125 0.359375q0.3125 0.125 0.703125 0.125q0.5 0 0.90625 -0.234375q0.40625 -0.25 0.6875 -0.71875q0.28125 -0.484375 0.4375 -1.1875q0.15625 -0.71875 0.15625 -1.640625zm-4.375 -0.09375q0 0.234375 0 0.46875q0 0.21875 0.03125 0.421875l4.046875 -2.984375q-0.109375 -0.375 -0.28125 -0.6875q-0.171875 -0.3125 -0.40625 -0.53125q-0.234375 -0.21875 -0.53125 -0.34375q-0.296875 -0.125 -0.671875 -0.125q-0.5 0 -0.90625 0.25q-0.40625 0.234375 -0.703125 0.71875q-0.28125 0.46875 -0.4375 1.1875q-0.140625 0.703125 -0.140625 1.625zm13.97998 2.71875l-1.640625 0l0 2.109375l-1.296875 0l0 -2.109375l-4.609375 0l0 -1.125l4.078125 -6.34375l1.828125 0l0 6.34375l1.640625 0l0 1.125zm-2.9375 -6.234375l-3.328125 5.109375l3.328125 0l0 -5.109375zm10.526794 5.9375q0 0.609375 -0.25 1.09375q-0.25 0.46875 -0.703125 0.796875q-0.453125 0.3125 -1.0625 0.484375q-0.59375 0.15625 -1.3125 0.15625q-0.78125 0 -1.375 -0.171875q-0.59375 -0.171875 -1.0 -0.484375q-0.40625 -0.3125 -0.609375 -0.75q-0.203125 -0.4375 -0.203125 -0.953125q0 -0.875 0.484375 -1.515625q0.5 -0.640625 1.515625 -1.15625q-0.9375 -0.484375 -1.375 -1.0625q-0.421875 -0.578125 -0.421875 -1.328125q0 -0.46875 0.1875 -0.890625q0.1875 -0.4375 0.578125 -0.765625q0.390625 -0.34375 0.96875 -0.546875q0.578125 -0.203125 1.359375 -0.203125q0.734375 0 1.296875 0.15625q0.5625 0.15625 0.9375 0.453125q0.390625 0.296875 0.578125 0.71875q0.1875 0.40625 0.1875 0.921875q0 0.828125 -0.46875 1.421875q-0.46875 0.578125 -1.3125 1.015625q0.421875 0.21875 0.78125 0.484375q0.375 0.25 0.640625 0.5625q0.265625 0.3125 0.421875 0.703125q0.15625 0.390625 0.15625 0.859375zm-1.53125 -4.953125q0 -0.640625 -0.453125 -0.953125q-0.453125 -0.328125 -1.28125 -0.328125q-0.828125 0 -1.28125 0.3125q-0.453125 0.3125 -0.453125 0.9375q0 0.296875 0.109375 0.546875q0.109375 0.234375 0.328125 0.453125q0.234375 0.203125 0.578125 0.40625q0.34375 0.203125 0.828125 0.421875q0.828125 -0.390625 1.21875 -0.8125q0.40625 -0.421875 0.40625 -0.984375zm0.140625 5.046875q0 -0.265625 -0.09375 -0.515625q-0.078125 -0.265625 -0.3125 -0.515625q-0.21875 -0.25 -0.609375 -0.5q-0.375 -0.25 -0.96875 -0.5q-0.5 0.234375 -0.84375 0.46875q-0.328125 0.234375 -0.546875 0.484375q-0.203125 0.25 -0.296875 0.515625q-0.078125 0.265625 -0.078125 0.546875q0 0.328125 0.140625 0.59375q0.140625 0.25 0.390625 0.421875q0.25 0.171875 0.59375 0.265625q0.34375 0.09375 0.75 0.09375q0.390625 0 0.734375 -0.078125q0.34375 -0.09375 0.59375 -0.25q0.25 -0.171875 0.390625 -0.421875q0.15625 -0.25 0.15625 -0.609375zm9.44873 -0.09375q0 0.609375 -0.25 1.09375q-0.25 0.46875 -0.703125 0.796875q-0.453125 0.3125 -1.0625 0.484375q-0.59375 0.15625 -1.3125 0.15625q-0.78125 0 -1.375 -0.171875q-0.59375 -0.171875 -1.0 -0.484375q-0.40625 -0.3125 -0.609375 -0.75q-0.203125 -0.4375 -0.203125 -0.953125q0 -0.875 0.484375 -1.515625q0.5 -0.640625 1.515625 -1.15625q-0.9375 -0.484375 -1.375 -1.0625q-0.421875 -0.578125 -0.421875 -1.328125q0 -0.46875 0.1875 -0.890625q0.1875 -0.4375 0.578125 -0.765625q0.390625 -0.34375 0.96875 -0.546875q0.578125 -0.203125 1.359375 -0.203125q0.734375 0 1.296875 0.15625q0.5625 0.15625 0.9375 0.453125q0.390625 0.296875 0.578125 0.71875q0.1875 0.40625 0.1875 0.921875q0 0.828125 -0.46875 1.421875q-0.46875 0.578125 -1.3125 1.015625q0.421875 0.21875 0.78125 0.484375q0.375 0.25 0.640625 0.5625q0.265625 0.3125 0.421875 0.703125q0.15625 0.390625 0.15625 0.859375zm-1.53125 -4.953125q0 -0.640625 -0.453125 -0.953125q-0.453125 -0.328125 -1.28125 -0.328125q-0.828125 0 -1.28125 0.3125q-0.453125 0.3125 -0.453125 0.9375q0 0.296875 0.109375 0.546875q0.109375 0.234375 0.328125 0.453125q0.234375 0.203125 0.578125 0.40625q0.34375 0.203125 0.828125 0.421875q0.828125 -0.390625 1.21875 -0.8125q0.40625 -0.421875 0.40625 -0.984375zm0.140625 5.046875q0 -0.265625 -0.09375 -0.515625q-0.078125 -0.265625 -0.3125 -0.515625q-0.21875 -0.25 -0.609375 -0.5q-0.375 -0.25 -0.96875 -0.5q-0.5 0.234375 -0.84375 0.46875q-0.328125 0.234375 -0.546875 0.484375q-0.203125 0.25 -0.296875 0.515625q-0.078125 0.265625 -0.078125 0.546875q0 0.328125 0.140625 0.59375q0.140625 0.25 0.390625 0.421875q0.25 0.171875 0.59375 0.265625q0.34375 0.09375 0.75 0.09375q0.390625 0 0.734375 -0.078125q0.34375 -0.09375 0.59375 -0.25q0.25 -0.171875 0.390625 -0.421875q0.15625 -0.25 0.15625 -0.609375zm9.386169 -6.078125l-4.015625 8.390625l-1.453125 0l4.171875 -8.390625l-5.171875 0l0 -1.1875l6.46875 0l0 1.1875zm8.058105 3.0625q0 1.40625 -0.34375 2.421875q-0.328125 1.0 -0.984375 1.65625q-0.65625 0.640625 -1.640625 0.953125q-0.96875 0.296875 -2.25 0.296875l-0.796875 0l0 -1.109375l0.890625 0q0.953125 0 1.640625 -0.1875q0.6875 -0.203125 1.140625 -0.5625q0.453125 -0.375 0.6875 -0.90625q0.25 -0.53125 0.3125 -1.21875l0.03125 -0.296875q-0.46875 0.28125 -1.078125 0.453125q-0.59375 0.15625 -1.296875 0.15625q-0.71875 0 -1.265625 -0.21875q-0.546875 -0.21875 -0.921875 -0.59375q-0.359375 -0.375 -0.546875 -0.890625q-0.171875 -0.53125 -0.171875 -1.15625q0 -0.65625 0.234375 -1.234375q0.25 -0.578125 0.6875 -1.0q0.4375 -0.4375 1.03125 -0.6875q0.609375 -0.25 1.34375 -0.25q0.71875 0 1.3125 0.234375q0.609375 0.234375 1.046875 0.765625q0.4375 0.515625 0.6875 1.359375q0.25 0.828125 0.25 2.015625zm-3.34375 -3.328125q-0.421875 0 -0.765625 0.140625q-0.34375 0.125 -0.609375 0.390625q-0.25 0.265625 -0.390625 0.640625q-0.140625 0.375 -0.140625 0.875q0 0.4375 0.09375 0.796875q0.109375 0.34375 0.328125 0.59375q0.234375 0.25 0.578125 0.390625q0.359375 0.125 0.84375 0.125q0.265625 0 0.546875 -0.046875q0.296875 -0.0625 0.5625 -0.140625q0.28125 -0.09375 0.53125 -0.203125q0.25 -0.125 0.453125 -0.265625q0 -0.9375 -0.140625 -1.5625q-0.140625 -0.640625 -0.40625 -1.015625q-0.265625 -0.390625 -0.640625 -0.546875q-0.375 -0.171875 -0.84375 -0.171875zm11.276794 5.75q0 0.625 -0.265625 1.1875q-0.25 0.546875 -0.765625 0.96875q-0.515625 0.40625 -1.296875 0.640625q-0.765625 0.234375 -1.796875 0.234375q-0.578125 0 -1.03125 -0.03125q-0.453125 -0.03125 -0.84375 -0.09375l0 -1.140625q0.453125 0.078125 0.953125 0.125q0.515625 0.046875 1.03125 0.046875q0.71875 0 1.21875 -0.125q0.515625 -0.140625 0.84375 -0.375q0.328125 -0.25 0.46875 -0.59375q0.140625 -0.34375 0.140625 -0.765625q0 -0.40625 -0.171875 -0.6875q-0.171875 -0.296875 -0.5 -0.5q-0.3125 -0.203125 -0.765625 -0.296875q-0.4375 -0.09375 -0.953125 -0.09375l-1.09375 0l0 -1.046875l1.109375 0q0.421875 0 0.78125 -0.109375q0.359375 -0.125 0.609375 -0.328125q0.25 -0.21875 0.375 -0.53125q0.140625 -0.3125 0.140625 -0.703125q0 -0.765625 -0.46875 -1.109375q-0.46875 -0.359375 -1.375 -0.359375q-0.484375 0 -1.0 0.09375q-0.5 0.09375 -1.09375 0.28125l0 -1.109375q0.25 -0.09375 0.53125 -0.15625q0.28125 -0.078125 0.5625 -0.125q0.28125 -0.046875 0.5625 -0.0625q0.28125 -0.03125 0.53125 -0.03125q0.765625 0 1.34375 0.171875q0.578125 0.15625 0.96875 0.46875q0.390625 0.296875 0.578125 0.75q0.203125 0.4375 0.203125 0.984375q0 0.8125 -0.421875 1.375q-0.421875 0.5625 -1.15625 0.890625q0.375 0.046875 0.734375 0.234375q0.375 0.171875 0.65625 0.453125q0.296875 0.265625 0.46875 0.640625q0.1875 0.375 0.1875 0.828125zm8.183105 -2.421875q0 1.40625 -0.34375 2.421875q-0.328125 1.0 -0.984375 1.65625q-0.65625 0.640625 -1.640625 0.953125q-0.96875 0.296875 -2.25 0.296875l-0.796875 0l0 -1.109375l0.890625 0q0.953125 0 1.640625 -0.1875q0.6875 -0.203125 1.140625 -0.5625q0.453125 -0.375 0.6875 -0.90625q0.25 -0.53125 0.3125 -1.21875l0.03125 -0.296875q-0.46875 0.28125 -1.078125 0.453125q-0.59375 0.15625 -1.296875 0.15625q-0.71875 0 -1.265625 -0.21875q-0.546875 -0.21875 -0.921875 -0.59375q-0.359375 -0.375 -0.546875 -0.890625q-0.171875 -0.53125 -0.171875 -1.15625q0 -0.65625 0.234375 -1.234375q0.25 -0.578125 0.6875 -1.0q0.4375 -0.4375 1.03125 -0.6875q0.609375 -0.25 1.34375 -0.25q0.71875 0 1.3125 0.234375q0.609375 0.234375 1.046875 0.765625q0.4375 0.515625 0.6875 1.359375q0.25 0.828125 0.25 2.015625zm-3.34375 -3.328125q-0.421875 0 -0.765625 0.140625q-0.34375 0.125 -0.609375 0.390625q-0.25 0.265625 -0.390625 0.640625q-0.140625 0.375 -0.140625 0.875q0 0.4375 0.09375 0.796875q0.109375 0.34375 0.328125 0.59375q0.234375 0.25 0.578125 0.390625q0.359375 0.125 0.84375 0.125q0.265625 0 0.546875 -0.046875q0.296875 -0.0625 0.5625 -0.140625q0.28125 -0.09375 0.53125 -0.203125q0.25 -0.125 0.453125 -0.265625q0 -0.9375 -0.140625 -1.5625q-0.140625 -0.640625 -0.40625 -1.015625q-0.265625 -0.390625 -0.640625 -0.546875q-0.375 -0.171875 -0.84375 -0.171875zm11.573669 5.625q0 0.65625 -0.234375 1.234375q-0.234375 0.578125 -0.6875 1.015625q-0.4375 0.421875 -1.0625 0.671875q-0.609375 0.234375 -1.359375 0.234375q-0.796875 0 -1.40625 -0.25q-0.609375 -0.25 -1.015625 -0.765625q-0.40625 -0.53125 -0.625 -1.328125q-0.203125 -0.796875 -0.203125 -1.890625q0 -0.734375 0.09375 -1.421875q0.09375 -0.6875 0.3125 -1.296875q0.21875 -0.609375 0.578125 -1.109375q0.375 -0.5 0.921875 -0.859375q0.546875 -0.375 1.28125 -0.578125q0.734375 -0.203125 1.71875 -0.203125l0.9375 0l0 1.125l-1.015625 0q-0.859375 0 -1.5 0.203125q-0.625 0.203125 -1.046875 0.578125q-0.421875 0.375 -0.65625 0.90625q-0.21875 0.515625 -0.28125 1.171875l-0.03125 0.296875q0.46875 -0.265625 1.0625 -0.421875q0.609375 -0.171875 1.3125 -0.171875q0.71875 0 1.265625 0.21875q0.546875 0.203125 0.90625 0.578125q0.375 0.375 0.546875 0.90625q0.1875 0.53125 0.1875 1.15625zm-1.328125 0.078125q0 -0.4375 -0.109375 -0.796875q-0.109375 -0.359375 -0.34375 -0.59375q-0.21875 -0.25 -0.5625 -0.375q-0.34375 -0.140625 -0.828125 -0.140625q-0.28125 0 -0.578125 0.046875q-0.28125 0.046875 -0.5625 0.140625q-0.265625 0.09375 -0.515625 0.21875q-0.25 0.109375 -0.453125 0.234375q0 0.953125 0.125 1.59375q0.140625 0.625 0.390625 1.015625q0.265625 0.375 0.640625 0.53125q0.390625 0.15625 0.890625 0.15625q0.421875 0 0.765625 -0.125q0.34375 -0.140625 0.59375 -0.40625q0.25 -0.265625 0.390625 -0.640625q0.15625 -0.375 0.15625 -0.859375zm9.276855 0.546875q0 0.609375 -0.25 1.09375q-0.25 0.46875 -0.703125 0.796875q-0.453125 0.3125 -1.0625 0.484375q-0.59375 0.15625 -1.3125 0.15625q-0.78125 0 -1.375 -0.171875q-0.59375 -0.171875 -1.0 -0.484375q-0.40625 -0.3125 -0.609375 -0.75q-0.203125 -0.4375 -0.203125 -0.953125q0 -0.875 0.484375 -1.515625q0.5 -0.640625 1.515625 -1.15625q-0.9375 -0.484375 -1.375 -1.0625q-0.421875 -0.578125 -0.421875 -1.328125q0 -0.46875 0.1875 -0.890625q0.1875 -0.4375 0.578125 -0.765625q0.390625 -0.34375 0.96875 -0.546875q0.578125 -0.203125 1.359375 -0.203125q0.734375 0 1.296875 0.15625q0.5625 0.15625 0.9375 0.453125q0.390625 0.296875 0.578125 0.71875q0.1875 0.40625 0.1875 0.921875q0 0.828125 -0.46875 1.421875q-0.46875 0.578125 -1.3125 1.015625q0.421875 0.21875 0.78125 0.484375q0.375 0.25 0.640625 0.5625q0.265625 0.3125 0.421875 0.703125q0.15625 0.390625 0.15625 0.859375zm-1.53125 -4.953125q0 -0.640625 -0.453125 -0.953125q-0.453125 -0.328125 -1.28125 -0.328125q-0.828125 0 -1.28125 0.3125q-0.453125 0.3125 -0.453125 0.9375q0 0.296875 0.109375 0.546875q0.109375 0.234375 0.328125 0.453125q0.234375 0.203125 0.578125 0.40625q0.34375 0.203125 0.828125 0.421875q0.828125 -0.390625 1.21875 -0.8125q0.40625 -0.421875 0.40625 -0.984375zm0.140625 5.046875q0 -0.265625 -0.09375 -0.515625q-0.078125 -0.265625 -0.3125 -0.515625q-0.21875 -0.25 -0.609375 -0.5q-0.375 -0.25 -0.96875 -0.5q-0.5 0.234375 -0.84375 0.46875q-0.328125 0.234375 -0.546875 0.484375q-0.203125 0.25 -0.296875 0.515625q-0.078125 0.265625 -0.078125 0.546875q0 0.328125 0.140625 0.59375q0.140625 0.25 0.390625 0.421875q0.25 0.171875 0.59375 0.265625q0.34375 0.09375 0.75 0.09375q0.390625 0 0.734375 -0.078125q0.34375 -0.09375 0.59375 -0.25q0.25 -0.171875 0.390625 -0.421875q0.15625 -0.25 0.15625 -0.609375zm9.386169 -3.015625q0 1.40625 -0.34375 2.421875q-0.328125 1.0 -0.984375 1.65625q-0.65625 0.640625 -1.640625 0.953125q-0.96875 0.296875 -2.25 0.296875l-0.796875 0l0 -1.109375l0.890625 0q0.953125 0 1.640625 -0.1875q0.6875 -0.203125 1.140625 -0.5625q0.453125 -0.375 0.6875 -0.90625q0.25 -0.53125 0.3125 -1.21875l0.03125 -0.296875q-0.46875 0.28125 -1.078125 0.453125q-0.59375 0.15625 -1.296875 0.15625q-0.71875 0 -1.265625 -0.21875q-0.546875 -0.21875 -0.921875 -0.59375q-0.359375 -0.375 -0.546875 -0.890625q-0.171875 -0.53125 -0.171875 -1.15625q0 -0.65625 0.234375 -1.234375q0.25 -0.578125 0.6875 -1.0q0.4375 -0.4375 1.03125 -0.6875q0.609375 -0.25 1.34375 -0.25q0.71875 0 1.3125 0.234375q0.609375 0.234375 1.046875 0.765625q0.4375 0.515625 0.6875 1.359375q0.25 0.828125 0.25 2.015625zm-3.34375 -3.328125q-0.421875 0 -0.765625 0.140625q-0.34375 0.125 -0.609375 0.390625q-0.25 0.265625 -0.390625 0.640625q-0.140625 0.375 -0.140625 0.875q0 0.4375 0.09375 0.796875q0.109375 0.34375 0.328125 0.59375q0.234375 0.25 0.578125 0.390625q0.359375 0.125 0.84375 0.125q0.265625 0 0.546875 -0.046875q0.296875 -0.0625 0.5625 -0.140625q0.28125 -0.09375 0.53125 -0.203125q0.25 -0.125 0.453125 -0.265625q0 -0.9375 -0.140625 -1.5625q-0.140625 -0.640625 -0.40625 -1.015625q-0.265625 -0.390625 -0.640625 -0.546875q-0.375 -0.171875 -0.84375 -0.171875zm11.401855 3.328125q0 1.40625 -0.34375 2.421875q-0.328125 1.0 -0.984375 1.65625q-0.65625 0.640625 -1.640625 0.953125q-0.96875 0.296875 -2.25 0.296875l-0.796875 0l0 -1.109375l0.890625 0q0.953125 0 1.640625 -0.1875q0.6875 -0.203125 1.140625 -0.5625q0.453125 -0.375 0.6875 -0.90625q0.25 -0.53125 0.3125 -1.21875l0.03125 -0.296875q-0.46875 0.28125 -1.078125 0.453125q-0.59375 0.15625 -1.296875 0.15625q-0.71875 0 -1.265625 -0.21875q-0.546875 -0.21875 -0.921875 -0.59375q-0.359375 -0.375 -0.546875 -0.890625q-0.171875 -0.53125 -0.171875 -1.15625q0 -0.65625 0.234375 -1.234375q0.25 -0.578125 0.6875 -1.0q0.4375 -0.4375 1.03125 -0.6875q0.609375 -0.25 1.34375 -0.25q0.71875 0 1.3125 0.234375q0.609375 0.234375 1.046875 0.765625q0.4375 0.515625 0.6875 1.359375q0.25 0.828125 0.25 2.015625zm-3.34375 -3.328125q-0.421875 0 -0.765625 0.140625q-0.34375 0.125 -0.609375 0.390625q-0.25 0.265625 -0.390625 0.640625q-0.140625 0.375 -0.140625 0.875q0 0.4375 0.09375 0.796875q0.109375 0.34375 0.328125 0.59375q0.234375 0.25 0.578125 0.390625q0.359375 0.125 0.84375 0.125q0.265625 0 0.546875 -0.046875q0.296875 -0.0625 0.5625 -0.140625q0.28125 -0.09375 0.53125 -0.203125q0.25 -0.125 0.453125 -0.265625q0 -0.9375 -0.140625 -1.5625q-0.140625 -0.640625 -0.40625 -1.015625q-0.265625 -0.390625 -0.640625 -0.546875q-0.375 -0.171875 -0.84375 -0.171875zm11.401794 0.265625l-4.015625 8.390625l-1.453125 0l4.171875 -8.390625l-5.171875 0l0 -1.1875l6.46875 0l0 1.1875zm8.120605 5.984375q0 0.609375 -0.25 1.09375q-0.25 0.46875 -0.703125 0.796875q-0.453125 0.3125 -1.0625 0.484375q-0.59375 0.15625 -1.3125 0.15625q-0.78125 0 -1.375 -0.171875q-0.59375 -0.171875 -1.0 -0.484375q-0.40625 -0.3125 -0.609375 -0.75q-0.203125 -0.4375 -0.203125 -0.953125q0 -0.875 0.484375 -1.515625q0.5 -0.640625 1.515625 -1.15625q-0.9375 -0.484375 -1.375 -1.0625q-0.421875 -0.578125 -0.421875 -1.328125q0 -0.46875 0.1875 -0.890625q0.1875 -0.4375 0.578125 -0.765625q0.390625 -0.34375 0.96875 -0.546875q0.578125 -0.203125 1.359375 -0.203125q0.734375 0 1.296875 0.15625q0.5625 0.15625 0.9375 0.453125q0.390625 0.296875 0.578125 0.71875q0.1875 0.40625 0.1875 0.921875q0 0.828125 -0.46875 1.421875q-0.46875 0.578125 -1.3125 1.015625q0.421875 0.21875 0.78125 0.484375q0.375 0.25 0.640625 0.5625q0.265625 0.3125 0.421875 0.703125q0.15625 0.390625 0.15625 0.859375zm-1.53125 -4.953125q0 -0.640625 -0.453125 -0.953125q-0.453125 -0.328125 -1.28125 -0.328125q-0.828125 0 -1.28125 0.3125q-0.453125 0.3125 -0.453125 0.9375q0 0.296875 0.109375 0.546875q0.109375 0.234375 0.328125 0.453125q0.234375 0.203125 0.578125 0.40625q0.34375 0.203125 0.828125 0.421875q0.828125 -0.390625 1.21875 -0.8125q0.40625 -0.421875 0.40625 -0.984375zm0.140625 5.046875q0 -0.265625 -0.09375 -0.515625q-0.078125 -0.265625 -0.3125 -0.515625q-0.21875 -0.25 -0.609375 -0.5q-0.375 -0.25 -0.96875 -0.5q-0.5 0.234375 -0.84375 0.46875q-0.328125 0.234375 -0.546875 0.484375q-0.203125 0.25 -0.296875 0.515625q-0.078125 0.265625 -0.078125 0.546875q0 0.328125 0.140625 0.59375q0.140625 0.25 0.390625 0.421875q0.25 0.171875 0.59375 0.265625q0.34375 0.09375 0.75 0.09375q0.390625 0 0.734375 -0.078125q0.34375 -0.09375 0.59375 -0.25q0.25 -0.171875 0.390625 -0.421875q0.15625 -0.25 0.15625 -0.609375zm9.761169 -6.84375q-1.015625 -0.21875 -1.734375 -0.21875q-1.71875 0 -1.71875 1.796875l0 1.296875l3.21875 0l0 1.0625l-3.21875 0l0 5.21875l-1.296875 0l0 -5.21875l-2.359375 0l0 -1.0625l2.359375 0l0 -1.21875q0 -2.9375 3.0625 -2.9375q0.765625 0 1.6875 0.171875l0 1.109375zm-7.703125 1.796875l0 0zm15.19873 4.296875q0 0.71875 -0.3125 1.3125q-0.296875 0.578125 -0.84375 1.0q-0.53125 0.40625 -1.265625 0.640625q-0.734375 0.234375 -1.578125 0.234375q-0.21875 0 -0.46875 -0.015625q-0.234375 0 -0.484375 -0.015625q-0.234375 -0.015625 -0.46875 -0.046875q-0.234375 -0.015625 -0.421875 -0.046875l0 -1.15625q0.40625 0.09375 0.90625 0.140625q0.515625 0.046875 1.03125 0.046875q0.59375 0 1.0625 -0.140625q0.46875 -0.140625 0.796875 -0.390625q0.328125 -0.265625 0.5 -0.640625q0.171875 -0.375 0.171875 -0.828125q0 -0.90625 -0.640625 -1.3125q-0.640625 -0.40625 -1.84375 -0.40625l-1.8125 0l0 -4.890625l5.15625 0l0 1.125l-3.953125 0l0 2.6875l0.84375 0q0.6875 0 1.328125 0.125q0.65625 0.125 1.15625 0.4375q0.515625 0.296875 0.828125 0.828125q0.3125 0.515625 0.3125 1.3125zm6.8080444 3.0625l-0.03125 -0.984375q-0.59375 0.59375 -1.21875 0.859375q-0.609375 0.25 -1.296875 0.25q-0.625 0 -1.078125 -0.15625q-0.4375 -0.15625 -0.734375 -0.4375q-0.28125 -0.28125 -0.421875 -0.65625q-0.140625 -0.390625 -0.140625 -0.84375q0 -1.09375 0.828125 -1.71875q0.828125 -0.640625 2.4375 -0.640625l1.515625 0l0 -0.640625q0 -0.65625 -0.421875 -1.046875q-0.40625 -0.390625 -1.265625 -0.390625q-0.625 0 -1.234375 0.140625q-0.59375 0.140625 -1.234375 0.40625l0 -1.15625q0.234375 -0.09375 0.53125 -0.171875q0.296875 -0.078125 0.625 -0.140625q0.328125 -0.078125 0.6875 -0.109375q0.359375 -0.046875 0.734375 -0.046875q0.65625 0 1.1875 0.15625q0.546875 0.140625 0.90625 0.4375q0.375 0.296875 0.5625 0.75q0.203125 0.453125 0.203125 1.078125l0 5.0625l-1.140625 0zm-0.140625 -3.34375l-1.609375 0q-0.484375 0 -0.828125 0.09375q-0.34375 0.09375 -0.5625 0.265625q-0.21875 0.171875 -0.328125 0.421875q-0.09375 0.25 -0.09375 0.5625q0 0.203125 0.0625 0.40625q0.0625 0.1875 0.203125 0.34375q0.15625 0.140625 0.390625 0.234375q0.234375 0.078125 0.5625 0.078125q0.4375 0 1.0 -0.265625q0.578125 -0.265625 1.203125 -0.84375l0 -1.296875zm9.44873 0.28125q0 0.71875 -0.3125 1.3125q-0.296875 0.578125 -0.84375 1.0q-0.53125 0.40625 -1.265625 0.640625q-0.734375 0.234375 -1.578125 0.234375q-0.21875 0 -0.46875 -0.015625q-0.234375 0 -0.484375 -0.015625q-0.234375 -0.015625 -0.46875 -0.046875q-0.234375 -0.015625 -0.421875 -0.046875l0 -1.15625q0.40625 0.09375 0.90625 0.140625q0.515625 0.046875 1.03125 0.046875q0.59375 0 1.0625 -0.140625q0.46875 -0.140625 0.796875 -0.390625q0.328125 -0.265625 0.5 -0.640625q0.171875 -0.375 0.171875 -0.828125q0 -0.90625 -0.640625 -1.3125q-0.640625 -0.40625 -1.84375 -0.40625l-1.8125 0l0 -4.890625l5.15625 0l0 1.125l-3.953125 0l0 2.6875l0.84375 0q0.6875 0 1.328125 0.125q0.65625 0.125 1.15625 0.4375q0.515625 0.296875 0.828125 0.828125q0.3125 0.515625 0.3125 1.3125zm8.308044 -1.0q0 0.265625 -0.015625 0.453125q0 0.1875 -0.015625 0.34375l-5.171875 0q0 1.125 0.625 1.734375q0.640625 0.59375 1.828125 0.59375q0.3125 0 0.640625 -0.015625q0.328125 -0.03125 0.625 -0.0625q0.296875 -0.046875 0.5625 -0.09375q0.28125 -0.0625 0.515625 -0.140625l0 1.046875q-0.515625 0.15625 -1.171875 0.234375q-0.65625 0.09375 -1.359375 0.09375q-0.9375 0 -1.625 -0.25q-0.6875 -0.25 -1.125 -0.734375q-0.421875 -0.5 -0.640625 -1.203125q-0.203125 -0.703125 -0.203125 -1.59375q0 -0.78125 0.21875 -1.46875q0.21875 -0.703125 0.640625 -1.21875q0.4375 -0.53125 1.0625 -0.828125q0.625 -0.3125 1.421875 -0.3125q0.765625 0 1.359375 0.25q0.59375 0.234375 1.0 0.6875q0.40625 0.4375 0.609375 1.078125q0.21875 0.625 0.21875 1.40625zm-1.328125 -0.1875q0.015625 -0.484375 -0.109375 -0.890625q-0.109375 -0.40625 -0.359375 -0.703125q-0.234375 -0.296875 -0.609375 -0.453125q-0.359375 -0.171875 -0.84375 -0.171875q-0.421875 0 -0.765625 0.171875q-0.34375 0.15625 -0.59375 0.453125q-0.25 0.28125 -0.40625 0.703125q-0.140625 0.40625 -0.1875 0.890625l3.875 0zm8.88623 3.984375q-0.5 0.1875 -1.03125 0.28125q-0.515625 0.09375 -1.078125 0.09375q-1.734375 0 -2.6875 -0.9375q-0.9375 -0.953125 -0.9375 -2.78125q0 -0.859375 0.265625 -1.5625q0.28125 -0.71875 0.765625 -1.21875q0.5 -0.515625 1.171875 -0.78125q0.6875 -0.28125 1.5 -0.28125q0.578125 0 1.078125 0.078125q0.5 0.078125 0.953125 0.265625l0 1.21875q-0.484375 -0.25 -0.984375 -0.359375q-0.484375 -0.125 -1.015625 -0.125q-0.484375 0 -0.921875 0.1875q-0.4375 0.1875 -0.765625 0.546875q-0.328125 0.34375 -0.53125 0.859375q-0.1875 0.5 -0.1875 1.140625q0 1.328125 0.640625 2.0q0.65625 0.65625 1.8125 0.65625q0.515625 0 1.0 -0.109375q0.5 -0.125 0.953125 -0.359375l0 1.1875zm9.026794 -1.84375l-1.640625 0l0 2.109375l-1.296875 0l0 -2.109375l-4.609375 0l0 -1.125l4.078125 -6.34375l1.828125 0l0 6.34375l1.640625 0l0 1.125zm-2.9375 -6.234375l-3.328125 5.109375l3.328125 0l0 -5.109375zm10.745605 3.5625q0 1.09375 -0.234375 2.0q-0.21875 0.90625 -0.671875 1.5625q-0.4375 0.640625 -1.109375 1.0q-0.65625 0.34375 -1.546875 0.34375q-0.765625 0 -1.40625 -0.28125q-0.625 -0.296875 -1.078125 -0.890625q-0.4375 -0.59375 -0.6875 -1.53125q-0.234375 -0.9375 -0.234375 -2.203125q0 -1.09375 0.21875 -2.0q0.234375 -0.921875 0.671875 -1.5625q0.453125 -0.65625 1.109375 -1.0q0.671875 -0.359375 1.5625 -0.359375q0.765625 0 1.390625 0.296875q0.625 0.28125 1.078125 0.890625q0.453125 0.59375 0.6875 1.53125q0.25 0.921875 0.25 2.203125zm-1.296875 0.046875q0 -0.25 -0.015625 -0.5q-0.015625 -0.25 -0.046875 -0.484375l-4.046875 3.015625q0.109375 0.375 0.28125 0.703125q0.171875 0.328125 0.40625 0.5625q0.234375 0.21875 0.53125 0.359375q0.3125 0.125 0.703125 0.125q0.5 0 0.90625 -0.234375q0.40625 -0.25 0.6875 -0.71875q0.28125 -0.484375 0.4375 -1.1875q0.15625 -0.71875 0.15625 -1.640625zm-4.375 -0.09375q0 0.234375 0 0.46875q0 0.21875 0.03125 0.421875l4.046875 -2.984375q-0.109375 -0.375 -0.28125 -0.6875q-0.171875 -0.3125 -0.40625 -0.53125q-0.234375 -0.21875 -0.53125 -0.34375q-0.296875 -0.125 -0.671875 -0.125q-0.5 0 -0.90625 0.25q-0.40625 0.234375 -0.703125 0.71875q-0.28125 0.46875 -0.4375 1.1875q-0.140625 0.703125 -0.140625 1.625zm13.979919 2.71875l-1.640625 0l0 2.109375l-1.296875 0l0 -2.109375l-4.609375 0l0 -1.125l4.078125 -6.34375l1.828125 0l0 6.34375l1.640625 0l0 1.125zm-2.9375 -6.234375l-3.328125 5.109375l3.328125 0l0 -5.109375zm10.433105 8.34375l-6.0 0l0 -1.1875l2.453125 0l0 -6.984375l-2.296875 1.25l-0.46875 -1.09375l3.046875 -1.59375l1.125 0l0 8.421875l2.140625 0l0 1.1875z" fill-rule="nonzero"></path><path fill="#000000" d="m61.447918 303.88052l-1.75 0l-3.421875 -3.9375l0 3.9375l-1.28125 0l0 -10.34375l1.28125 0l0 6.359375l3.296875 -3.375l1.6875 0l-3.453125 3.390625l3.640625 3.96875zm7.667446 -4.0625q0 0.265625 -0.015625 0.453125q0 0.1875 -0.015625 0.34375l-5.171871 0q0 1.125 0.6249962 1.734375q0.640625 0.59375 1.828125 0.59375q0.3125 0 0.640625 -0.015625q0.328125 -0.03125 0.625 -0.0625q0.296875 -0.046875 0.5625 -0.09375q0.28125 -0.0625 0.515625 -0.140625l0 1.046875q-0.515625 0.15625 -1.171875 0.234375q-0.65625 0.09375 -1.359375 0.09375q-0.9375 0 -1.625 -0.25q-0.6874962 -0.25 -1.1249962 -0.734375q-0.421875 -0.5 -0.640625 -1.203125q-0.203125 -0.703125 -0.203125 -1.59375q0 -0.78125 0.21875 -1.46875q0.21875 -0.703125 0.640625 -1.21875q0.4375 -0.53125 1.0624962 -0.828125q0.625 -0.3125 1.421875 -0.3125q0.765625 0 1.359375 0.25q0.59375 0.234375 1.0 0.6875q0.40625 0.4375 0.609375 1.078125q0.21875 0.625 0.21875 1.40625zm-1.328125 -0.1875q0.015625 -0.484375 -0.109375 -0.890625q-0.109375 -0.40625 -0.359375 -0.703125q-0.234375 -0.296875 -0.609375 -0.453125q-0.359375 -0.171875 -0.84375 -0.171875q-0.421875 0 -0.765625 0.171875q-0.34375 0.15625 -0.59375 0.453125q-0.25 0.28125 -0.40625 0.703125q-0.14062119 0.40625 -0.18749619 0.890625l3.8749962 0zm9.3862 0.1875q0 0.265625 -0.015625 0.453125q0 0.1875 -0.015625 0.34375l-5.171875 0q0 1.125 0.625 1.734375q0.640625 0.59375 1.828125 0.59375q0.3125 0 0.640625 -0.015625q0.328125 -0.03125 0.625 -0.0625q0.296875 -0.046875 0.5625 -0.09375q0.28125 -0.0625 0.515625 -0.140625l0 1.046875q-0.515625 0.15625 -1.171875 0.234375q-0.65625 0.09375 -1.359375 0.09375q-0.9375 0 -1.625 -0.25q-0.6875 -0.25 -1.125 -0.734375q-0.421875 -0.5 -0.640625 -1.203125q-0.203125 -0.703125 -0.203125 -1.59375q0 -0.78125 0.21875 -1.46875q0.21875 -0.703125 0.640625 -1.21875q0.4375 -0.53125 1.0625 -0.828125q0.625 -0.3125 1.421875 -0.3125q0.765625 0 1.359375 0.25q0.59375 0.234375 1.0 0.6875q0.40625 0.4375 0.609375 1.078125q0.21875 0.625 0.21875 1.40625zm-1.328125 -0.1875q0.015625 -0.484375 -0.109375 -0.890625q-0.109375 -0.40625 -0.359375 -0.703125q-0.234375 -0.296875 -0.609375 -0.453125q-0.359375 -0.171875 -0.84375 -0.171875q-0.421875 0 -0.765625 0.171875q-0.34375 0.15625 -0.59375 0.453125q-0.25 0.28125 -0.40625 0.703125q-0.140625 0.40625 -0.1875 0.890625l3.875 0zm9.4487 0.4375q0 0.984375 -0.28125 1.71875q-0.265625 0.734375 -0.75 1.21875q-0.484375 0.484375 -1.140625 0.734375q-0.65625 0.234375 -1.421875 0.234375q-0.359375 0 -0.703125 -0.046875q-0.34375 -0.03125 -0.703125 -0.125l0 3.078125l-1.28125 0l0 -10.359375l1.140625 0l0.078125 1.234375q0.546875 -0.75 1.171875 -1.046875q0.625 -0.3125 1.34375 -0.3125q0.625 0 1.09375 0.265625q0.484375 0.265625 0.8125 0.75q0.328125 0.46875 0.484375 1.15625q0.15625 0.671875 0.15625 1.5zm-1.296875 0.0625q0 -0.578125 -0.09375 -1.0625q-0.078125 -0.484375 -0.265625 -0.828125q-0.171875 -0.34375 -0.46875 -0.53125q-0.28125 -0.203125 -0.671875 -0.203125q-0.25 0 -0.5 0.078125q-0.25 0.078125 -0.515625 0.265625q-0.265625 0.171875 -0.5625 0.46875q-0.296875 0.296875 -0.625 0.734375l0 3.5625q0.34375 0.15625 0.734375 0.25q0.390625 0.078125 0.765625 0.078125q1.03125 0 1.609375 -0.703125q0.59375 -0.703125 0.59375 -2.109375zm9.1987 3.75l-6.0 0l0 -1.1875l2.453125 0l0 -6.984375l-2.296875 1.25l-0.46875 -1.09375l3.046875 -1.59375l1.125 0l0 8.421875l2.140625 0l0 1.1875z" fill-rule="nonzero"></path><path fill="#000000" d="m355.41818 303.88052l-0.03125 -0.984375q-0.59375 0.59375 -1.21875 0.859375q-0.609375 0.25 -1.296875 0.25q-0.625 0 -1.078125 -0.15625q-0.4375 -0.15625 -0.734375 -0.4375q-0.28125 -0.28125 -0.421875 -0.65625q-0.140625 -0.390625 -0.140625 -0.84375q0 -1.09375 0.828125 -1.71875q0.828125 -0.640625 2.4375 -0.640625l1.515625 0l0 -0.640625q0 -0.65625 -0.421875 -1.046875q-0.40625 -0.390625 -1.265625 -0.390625q-0.625 0 -1.234375 0.140625q-0.59375 0.140625 -1.234375 0.40625l0 -1.15625q0.234375 -0.09375 0.53125 -0.171875q0.296875 -0.078125 0.625 -0.140625q0.328125 -0.078125 0.6875 -0.109375q0.359375 -0.046875 0.734375 -0.046875q0.65625 0 1.1875 0.15625q0.546875 0.140625 0.90625 0.4375q0.375 0.296875 0.5625 0.75q0.203125 0.453125 0.203125 1.078125l0 5.0625l-1.140625 0zm-0.140625 -3.34375l-1.609375 0q-0.484375 0 -0.828125 0.09375q-0.34375 0.09375 -0.5625 0.265625q-0.21875 0.171875 -0.328125 0.421875q-0.09375 0.25 -0.09375 0.5625q0 0.203125 0.0625 0.40625q0.0625 0.1875 0.203125 0.34375q0.15625 0.140625 0.390625 0.234375q0.234375 0.078125 0.5625 0.078125q0.4375 0 1.0 -0.265625q0.578125 -0.265625 1.203125 -0.84375l0 -1.296875zm9.1987 3.078125q-0.5 0.1875 -1.03125 0.28125q-0.515625 0.09375 -1.078125 0.09375q-1.734375 0 -2.6875 -0.9375q-0.9375 -0.953125 -0.9375 -2.78125q0 -0.859375 0.265625 -1.5625q0.28125 -0.71875 0.765625 -1.21875q0.5 -0.515625 1.171875 -0.78125q0.6875 -0.28125 1.5 -0.28125q0.578125 0 1.078125 0.078125q0.5 0.078125 0.953125 0.265625l0 1.21875q-0.484375 -0.25 -0.984375 -0.359375q-0.484375 -0.125 -1.015625 -0.125q-0.484375 0 -0.921875 0.1875q-0.4375 0.1875 -0.765625 0.546875q-0.328125 0.34375 -0.53125 0.859375q-0.1875 0.5 -0.1875 1.140625q0 1.328125 0.640625 2.0q0.65625 0.65625 1.8125 0.65625q0.515625 0 1.0 -0.109375q0.5 -0.125 0.953125 -0.359375l0 1.1875zm8.620575 -3.546875q0 0.921875 -0.25 1.640625q-0.25 0.71875 -0.71875 1.21875q-0.46875 0.5 -1.140625 0.78125q-0.65625 0.265625 -1.484375 0.265625q-0.65625 0 -1.34375 -0.125q-0.671875 -0.125 -1.34375 -0.40625l0 -9.90625l1.28125 0l0 2.84375l-0.0625 1.359375q0.546875 -0.734375 1.171875 -1.03125q0.625 -0.3125 1.34375 -0.3125q0.625 0 1.09375 0.265625q0.484375 0.265625 0.8125 0.75q0.328125 0.46875 0.484375 1.15625q0.15625 0.671875 0.15625 1.5zm-1.296875 0.0625q0 -0.578125 -0.09375 -1.0625q-0.078125 -0.484375 -0.265625 -0.828125q-0.171875 -0.34375 -0.46875 -0.53125q-0.28125 -0.203125 -0.671875 -0.203125q-0.25 0 -0.5 0.078125q-0.25 0.078125 -0.515625 0.265625q-0.265625 0.171875 -0.5625 0.46875q-0.296875 0.296875 -0.625 0.734375l0 3.5625q0.359375 0.15625 0.75 0.25q0.390625 0.078125 0.75 0.078125q0.4375 0 0.828125 -0.140625q0.40625 -0.140625 0.703125 -0.46875q0.3125 -0.328125 0.484375 -0.859375q0.1875 -0.546875 0.1875 -1.34375zm2.6987 0.234375q0 -0.9375 0.25 -1.671875q0.265625 -0.734375 0.734375 -1.234375q0.46875 -0.5 1.125 -0.75q0.671875 -0.265625 1.484375 -0.265625q0.359375 0 0.6875 0.046875q0.34375 0.03125 0.671875 0.125l0 -3.078125l1.28125 0l0 10.34375l-1.140625 0l-0.03125 -1.390625q-0.546875 0.78125 -1.171875 1.15625q-0.609375 0.359375 -1.34375 0.359375q-0.625 0 -1.109375 -0.25q-0.46875 -0.265625 -0.796875 -0.75q-0.3125 -0.484375 -0.484375 -1.15625q-0.15625 -0.671875 -0.15625 -1.484375zm1.3125 -0.09375q0 1.34375 0.390625 2.0q0.390625 0.65625 1.109375 0.65625q0.484375 0 1.015625 -0.4375q0.53125 -0.4375 1.125 -1.28125l0 -3.421875q-0.3125 -0.140625 -0.6875 -0.21875q-0.375 -0.078125 -0.75 -0.078125q-1.046875 0 -1.625 0.671875q-0.578125 0.671875 -0.578125 2.109375zm13.245575 3.609375l-6.0 0l0 -1.1875l2.453125 0l0 -6.984375l-2.296875 1.25l-0.46875 -1.09375l3.046875 -1.59375l1.125 0l0 8.421875l2.140625 0l0 1.1875zm8.151825 -2.40625q0 0.609375 -0.25 1.09375q-0.25 0.46875 -0.703125 0.796875q-0.453125 0.3125 -1.0625 0.484375q-0.59375 0.15625 -1.3125 0.15625q-0.78125 0 -1.375 -0.171875q-0.59375 -0.171875 -1.0 -0.484375q-0.40625 -0.3125 -0.609375 -0.75q-0.203125 -0.4375 -0.203125 -0.953125q0 -0.875 0.484375 -1.515625q0.5 -0.640625 1.515625 -1.15625q-0.9375 -0.484375 -1.375 -1.0625q-0.421875 -0.578125 -0.421875 -1.328125q0 -0.46875 0.1875 -0.890625q0.1875 -0.4375 0.578125 -0.765625q0.390625 -0.34375 0.96875 -0.546875q0.578125 -0.203125 1.359375 -0.203125q0.734375 0 1.296875 0.15625q0.5625 0.15625 0.9375 0.453125q0.390625 0.296875 0.578125 0.71875q0.1875 0.40625 0.1875 0.921875q0 0.828125 -0.46875 1.421875q-0.46875 0.578125 -1.3125 1.015625q0.421875 0.21875 0.78125 0.484375q0.375 0.25 0.640625 0.5625q0.265625 0.3125 0.421875 0.703125q0.15625 0.390625 0.15625 0.859375zm-1.53125 -4.953125q0 -0.640625 -0.453125 -0.953125q-0.453125 -0.328125 -1.28125 -0.328125q-0.828125 0 -1.28125 0.3125q-0.453125 0.3125 -0.453125 0.9375q0 0.296875 0.109375 0.546875q0.109375 0.234375 0.328125 0.453125q0.234375 0.203125 0.578125 0.40625q0.34375 0.203125 0.828125 0.421875q0.828125 -0.390625 1.21875 -0.8125q0.40625 -0.421875 0.40625 -0.984375zm0.140625 5.046875q0 -0.265625 -0.09375 -0.515625q-0.078125 -0.265625 -0.3125 -0.515625q-0.21875 -0.25 -0.609375 -0.5q-0.375 -0.25 -0.96875 -0.5q-0.5 0.234375 -0.84375 0.46875q-0.328125 0.234375 -0.546875 0.484375q-0.203125 0.25 -0.296875 0.515625q-0.078125 0.265625 -0.078125 0.546875q0 0.328125 0.140625 0.59375q0.140625 0.25 0.390625 0.421875q0.25 0.171875 0.59375 0.265625q0.34375 0.09375 0.75 0.09375q0.390625 0 0.734375 -0.078125q0.34375 -0.09375 0.59375 -0.25q0.25 -0.171875 0.390625 -0.421875q0.15625 -0.25 0.15625 -0.609375zm2.85495 -1.203125q0 -0.9375 0.25 -1.671875q0.265625 -0.734375 0.734375 -1.234375q0.46875 -0.5 1.125 -0.75q0.671875 -0.265625 1.484375 -0.265625q0.359375 0 0.6875 0.046875q0.34375 0.03125 0.671875 0.125l0 -3.078125l1.28125 0l0 10.34375l-1.140625 0l-0.03125 -1.390625q-0.546875 0.78125 -1.171875 1.15625q-0.609375 0.359375 -1.34375 0.359375q-0.625 0 -1.109375 -0.25q-0.46875 -0.265625 -0.796875 -0.75q-0.3125 -0.484375 -0.484375 -1.15625q-0.15625 -0.671875 -0.15625 -1.484375zm1.3125 -0.09375q0 1.34375 0.390625 2.0q0.390625 0.65625 1.109375 0.65625q0.484375 0 1.015625 -0.4375q0.53125 -0.4375 1.125 -1.28125l0 -3.421875q-0.3125 -0.140625 -0.6875 -0.21875q-0.375 -0.078125 -0.75 -0.078125q-1.046875 0 -1.625 0.671875q-0.578125 0.671875 -0.578125 2.109375zm13.401825 -0.203125q0 0.921875 -0.25 1.640625q-0.25 0.71875 -0.71875 1.21875q-0.46875 0.5 -1.140625 0.78125q-0.65625 0.265625 -1.484375 0.265625q-0.65625 0 -1.34375 -0.125q-0.671875 -0.125 -1.34375 -0.40625l0 -9.90625l1.28125 0l0 2.84375l-0.0625 1.359375q0.546875 -0.734375 1.171875 -1.03125q0.625 -0.3125 1.34375 -0.3125q0.625 0 1.09375 0.265625q0.484375 0.265625 0.8125 0.75q0.328125 0.46875 0.484375 1.15625q0.15625 0.671875 0.15625 1.5zm-1.296875 0.0625q0 -0.578125 -0.09375 -1.0625q-0.078125 -0.484375 -0.265625 -0.828125q-0.171875 -0.34375 -0.46875 -0.53125q-0.28125 -0.203125 -0.671875 -0.203125q-0.25 0 -0.5 0.078125q-0.25 0.078125 -0.515625 0.265625q-0.265625 0.171875 -0.5625 0.46875q-0.296875 0.296875 -0.625 0.734375l0 3.5625q0.359375 0.15625 0.75 0.25q0.390625 0.078125 0.75 0.078125q0.4375 0 0.828125 -0.140625q0.40625 -0.140625 0.703125 -0.46875q0.3125 -0.328125 0.484375 -0.859375q0.1875 -0.546875 0.1875 -1.34375zm9.7612 1.640625l-1.640625 0l0 2.109375l-1.296875 0l0 -2.109375l-4.609375 0l0 -1.125l4.078125 -6.34375l1.828125 0l0 6.34375l1.640625 0l0 1.125zm-2.9375 -6.234375l-3.328125 5.109375l3.328125 0l0 -5.109375zm10.026825 8.078125q-0.5 0.1875 -1.03125 0.28125q-0.515625 0.09375 -1.078125 0.09375q-1.734375 0 -2.6875 -0.9375q-0.9375 -0.953125 -0.9375 -2.78125q0 -0.859375 0.265625 -1.5625q0.28125 -0.71875 0.765625 -1.21875q0.5 -0.515625 1.171875 -0.78125q0.6875 -0.28125 1.5 -0.28125q0.578125 0 1.078125 0.078125q0.5 0.078125 0.953125 0.265625l0 1.21875q-0.484375 -0.25 -0.984375 -0.359375q-0.484375 -0.125 -1.015625 -0.125q-0.484375 0 -0.921875 0.1875q-0.4375 0.1875 -0.765625 0.546875q-0.328125 0.34375 -0.53125 0.859375q-0.1875 0.5 -0.1875 1.140625q0 1.328125 0.640625 2.0q0.65625 0.65625 1.8125 0.65625q0.515625 0 1.0 -0.109375q0.5 -0.125 0.953125 -0.359375l0 1.1875zm8.058075 0q-0.5 0.1875 -1.03125 0.28125q-0.515625 0.09375 -1.078125 0.09375q-1.734375 0 -2.6875 -0.9375q-0.9375 -0.953125 -0.9375 -2.78125q0 -0.859375 0.265625 -1.5625q0.28125 -0.71875 0.765625 -1.21875q0.5 -0.515625 1.171875 -0.78125q0.6875 -0.28125 1.5 -0.28125q0.578125 0 1.078125 0.078125q0.5 0.078125 0.953125 0.265625l0 1.21875q-0.484375 -0.25 -0.984375 -0.359375q-0.484375 -0.125 -1.015625 -0.125q-0.484375 0 -0.921875 0.1875q-0.4375 0.1875 -0.765625 0.546875q-0.328125 0.34375 -0.53125 0.859375q-0.1875 0.5 -0.1875 1.140625q0 1.328125 0.640625 2.0q0.65625 0.65625 1.8125 0.65625q0.515625 0 1.0 -0.109375q0.5 -0.125 0.953125 -0.359375l0 1.1875zm8.558075 0.265625l-6.3125 0l0 -1.140625l2.46875 -2.46875q0.609375 -0.59375 0.984375 -1.03125q0.390625 -0.4375 0.59375 -0.796875q0.21875 -0.375 0.296875 -0.6875q0.078125 -0.328125 0.078125 -0.703125q0 -0.34375 -0.109375 -0.65625q-0.09375 -0.328125 -0.296875 -0.5625q-0.1875 -0.25 -0.5 -0.390625q-0.3125 -0.140625 -0.75 -0.140625q-0.609375 0 -1.109375 0.28125q-0.5 0.265625 -0.921875 0.6875l-0.703125 -0.828125q0.546875 -0.578125 1.25 -0.921875q0.703125 -0.34375 1.640625 -0.34375q0.640625 0 1.15625 0.1875q0.53125 0.1875 0.90625 0.546875q0.390625 0.359375 0.59375 0.890625q0.21875 0.515625 0.21875 1.15625q0 0.5625 -0.15625 1.03125q-0.140625 0.46875 -0.4375 0.9375q-0.296875 0.453125 -0.75 0.953125q-0.453125 0.5 -1.0625 1.09375l-1.734375 1.6875l4.65625 0l0 1.21875zm8.370575 -9.15625q-1.015625 -0.21875 -1.734375 -0.21875q-1.71875 0 -1.71875 1.796875l0 1.296875l3.21875 0l0 1.0625l-3.21875 0l0 5.21875l-1.296875 0l0 -5.21875l-2.359375 0l0 -1.0625l2.359375 0l0 -1.21875q0 -2.9375 3.0625 -2.9375q0.765625 0 1.6875 0.171875l0 1.109375zm-7.703125 1.796875l0 0zm15.4487 4.953125q0 0.609375 -0.25 1.09375q-0.25 0.46875 -0.703125 0.796875q-0.453125 0.3125 -1.0625 0.484375q-0.59375 0.15625 -1.3125 0.15625q-0.78125 0 -1.375 -0.171875q-0.59375 -0.171875 -1.0 -0.484375q-0.40625 -0.3125 -0.609375 -0.75q-0.203125 -0.4375 -0.203125 -0.953125q0 -0.875 0.484375 -1.515625q0.5 -0.640625 1.515625 -1.15625q-0.9375 -0.484375 -1.375 -1.0625q-0.421875 -0.578125 -0.421875 -1.328125q0 -0.46875 0.1875 -0.890625q0.1875 -0.4375 0.578125 -0.765625q0.390625 -0.34375 0.96875 -0.546875q0.578125 -0.203125 1.359375 -0.203125q0.734375 0 1.296875 0.15625q0.5625 0.15625 0.9375 0.453125q0.390625 0.296875 0.578125 0.71875q0.1875 0.40625 0.1875 0.921875q0 0.828125 -0.46875 1.421875q-0.46875 0.578125 -1.3125 1.015625q0.421875 0.21875 0.78125 0.484375q0.375 0.25 0.640625 0.5625q0.265625 0.3125 0.421875 0.703125q0.15625 0.390625 0.15625 0.859375zm-1.53125 -4.953125q0 -0.640625 -0.453125 -0.953125q-0.453125 -0.328125 -1.28125 -0.328125q-0.828125 0 -1.28125 0.3125q-0.453125 0.3125 -0.453125 0.9375q0 0.296875 0.109375 0.546875q0.109375 0.234375 0.328125 0.453125q0.234375 0.203125 0.578125 0.40625q0.34375 0.203125 0.828125 0.421875q0.828125 -0.390625 1.21875 -0.8125q0.40625 -0.421875 0.40625 -0.984375zm0.140625 5.046875q0 -0.265625 -0.09375 -0.515625q-0.078125 -0.265625 -0.3125 -0.515625q-0.21875 -0.25 -0.609375 -0.5q-0.375 -0.25 -0.96875 -0.5q-0.5 0.234375 -0.84375 0.46875q-0.328125 0.234375 -0.546875 0.484375q-0.203125 0.25 -0.296875 0.515625q-0.078125 0.265625 -0.078125 0.546875q0 0.328125 0.140625 0.59375q0.140625 0.25 0.390625 0.421875q0.25 0.171875 0.59375 0.265625q0.34375 0.09375 0.75 0.09375q0.390625 0 0.734375 -0.078125q0.34375 -0.09375 0.59375 -0.25q0.25 -0.171875 0.390625 -0.421875q0.15625 -0.25 0.15625 -0.609375zm9.1987 -0.75q0 0.71875 -0.3125 1.3125q-0.296875 0.578125 -0.84375 1.0q-0.53125 0.40625 -1.265625 0.640625q-0.734375 0.234375 -1.578125 0.234375q-0.21875 0 -0.46875 -0.015625q-0.234375 0 -0.484375 -0.015625q-0.234375 -0.015625 -0.46875 -0.046875q-0.234375 -0.015625 -0.421875 -0.046875l0 -1.15625q0.40625 0.09375 0.90625 0.140625q0.515625 0.046875 1.03125 0.046875q0.59375 0 1.0625 -0.140625q0.46875 -0.140625 0.796875 -0.390625q0.328125 -0.265625 0.5 -0.640625q0.171875 -0.375 0.171875 -0.828125q0 -0.90625 -0.640625 -1.3125q-0.640625 -0.40625 -1.84375 -0.40625l-1.8125 0l0 -4.890625l5.15625 0l0 1.125l-3.953125 0l0 2.6875l0.84375 0q0.6875 0 1.328125 0.125q0.65625 0.125 1.15625 0.4375q0.515625 0.296875 0.828125 0.828125q0.3125 0.515625 0.3125 1.3125zm7.808075 2.796875q-0.5 0.1875 -1.03125 0.28125q-0.515625 0.09375 -1.078125 0.09375q-1.734375 0 -2.6875 -0.9375q-0.9375 -0.953125 -0.9375 -2.78125q0 -0.859375 0.265625 -1.5625q0.28125 -0.71875 0.765625 -1.21875q0.5 -0.515625 1.171875 -0.78125q0.6875 -0.28125 1.5 -0.28125q0.578125 0 1.078125 0.078125q0.5 0.078125 0.953125 0.265625l0 1.21875q-0.484375 -0.25 -0.984375 -0.359375q-0.484375 -0.125 -1.015625 -0.125q-0.484375 0 -0.921875 0.1875q-0.4375 0.1875 -0.765625 0.546875q-0.328125 0.34375 -0.53125 0.859375q-0.1875 0.5 -0.1875 1.140625q0 1.328125 0.640625 2.0q0.65625 0.65625 1.8125 0.65625q0.515625 0 1.0 -0.109375q0.5 -0.125 0.953125 -0.359375l0 1.1875zm8.558075 -3.796875q0 0.265625 -0.015625 0.453125q0 0.1875 -0.015625 0.34375l-5.171875 0q0 1.125 0.625 1.734375q0.640625 0.59375 1.828125 0.59375q0.3125 0 0.640625 -0.015625q0.328125 -0.03125 0.625 -0.0625q0.296875 -0.046875 0.5625 -0.09375q0.28125 -0.0625 0.515625 -0.140625l0 1.046875q-0.515625 0.15625 -1.171875 0.234375q-0.65625 0.09375 -1.359375 0.09375q-0.9375 0 -1.625 -0.25q-0.6875 -0.25 -1.125 -0.734375q-0.421875 -0.5 -0.640625 -1.203125q-0.203125 -0.703125 -0.203125 -1.59375q0 -0.78125 0.21875 -1.46875q0.21875 -0.703125 0.640625 -1.21875q0.4375 -0.53125 1.0625 -0.828125q0.625 -0.3125 1.421875 -0.3125q0.765625 0 1.359375 0.25q0.59375 0.234375 1.0 0.6875q0.40625 0.4375 0.609375 1.078125q0.21875 0.625 0.21875 1.40625zm-1.328125 -0.1875q0.015625 -0.484375 -0.109375 -0.890625q-0.109375 -0.40625 -0.359375 -0.703125q-0.234375 -0.296875 -0.609375 -0.453125q-0.359375 -0.171875 -0.84375 -0.171875q-0.421875 0 -0.765625 0.171875q-0.34375 0.15625 -0.59375 0.453125q-0.25 0.28125 -0.40625 0.703125q-0.140625 0.40625 -0.1875 0.890625l3.875 0zm2.79245 0.734375q0 -0.9375 0.25 -1.671875q0.265625 -0.734375 0.734375 -1.234375q0.46875 -0.5 1.125 -0.75q0.671875 -0.265625 1.484375 -0.265625q0.359375 0 0.6875 0.046875q0.34375 0.03125 0.671875 0.125l0 -3.078125l1.28125 0l0 10.34375l-1.140625 0l-0.03125 -1.390625q-0.546875 0.78125 -1.171875 1.15625q-0.609375 0.359375 -1.34375 0.359375q-0.625 0 -1.109375 -0.25q-0.46875 -0.265625 -0.796875 -0.75q-0.3125 -0.484375 -0.484375 -1.15625q-0.15625 -0.671875 -0.15625 -1.484375zm1.3125 -0.09375q0 1.34375 0.390625 2.0q0.390625 0.65625 1.109375 0.65625q0.484375 0 1.015625 -0.4375q0.53125 -0.4375 1.125 -1.28125l0 -3.421875q-0.3125 -0.140625 -0.6875 -0.21875q-0.375 -0.078125 -0.75 -0.078125q-1.046875 0 -1.625 0.671875q-0.578125 0.671875 -0.578125 2.109375zm13.339325 -0.453125q0 0.265625 -0.015625 0.453125q0 0.1875 -0.015625 0.34375l-5.171875 0q0 1.125 0.625 1.734375q0.640625 0.59375 1.828125 0.59375q0.3125 0 0.640625 -0.015625q0.328125 -0.03125 0.625 -0.0625q0.296875 -0.046875 0.5625 -0.09375q0.28125 -0.0625 0.515625 -0.140625l0 1.046875q-0.515625 0.15625 -1.171875 0.234375q-0.65625 0.09375 -1.359375 0.09375q-0.9375 0 -1.625 -0.25q-0.6875 -0.25 -1.125 -0.734375q-0.421875 -0.5 -0.640625 -1.203125q-0.203125 -0.703125 -0.203125 -1.59375q0 -0.78125 0.21875 -1.46875q0.21875 -0.703125 0.640625 -1.21875q0.4375 -0.53125 1.0625 -0.828125q0.625 -0.3125 1.421875 -0.3125q0.765625 0 1.359375 0.25q0.59375 0.234375 1.0 0.6875q0.40625 0.4375 0.609375 1.078125q0.21875 0.625 0.21875 1.40625zm-1.328125 -0.1875q0.015625 -0.484375 -0.109375 -0.890625q-0.109375 -0.40625 -0.359375 -0.703125q-0.234375 -0.296875 -0.609375 -0.453125q-0.359375 -0.171875 -0.84375 -0.171875q-0.421875 0 -0.765625 0.171875q-0.34375 0.15625 -0.59375 0.453125q-0.25 0.28125 -0.40625 0.703125q-0.140625 0.40625 -0.1875 0.890625l3.875 0zm9.6987 -4.90625q-1.015625 -0.21875 -1.734375 -0.21875q-1.71875 0 -1.71875 1.796875l0 1.296875l3.21875 0l0 1.0625l-3.21875 0l0 5.21875l-1.296875 0l0 -5.21875l-2.359375 0l0 -1.0625l2.359375 0l0 -1.21875q0 -2.9375 3.0625 -2.9375q0.765625 0 1.6875 0.171875l0 1.109375zm-7.703125 1.796875l0 0zm15.558105 4.328125q0 0.65625 -0.234375 1.234375q-0.234375 0.578125 -0.6875 1.015625q-0.4375 0.421875 -1.0625 0.671875q-0.609375 0.234375 -1.359375 0.234375q-0.796875 0 -1.40625 -0.25q-0.609375 -0.25 -1.015625 -0.765625q-0.40625 -0.53125 -0.6250305 -1.328125q-0.203125 -0.796875 -0.203125 -1.890625q0 -0.734375 0.09375 -1.421875q0.09375 -0.6875 0.31253052 -1.296875q0.21875 -0.609375 0.578125 -1.109375q0.375 -0.5 0.921875 -0.859375q0.546875 -0.375 1.28125 -0.578125q0.734375 -0.203125 1.71875 -0.203125l0.9375 0l0 1.125l-1.015625 0q-0.859375 0 -1.5 0.203125q-0.625 0.203125 -1.046875 0.578125q-0.421875 0.375 -0.65625 0.90625q-0.21875 0.515625 -0.28125 1.171875l-0.03125 0.296875q0.46875 -0.265625 1.0625 -0.421875q0.609375 -0.171875 1.3125 -0.171875q0.71875 0 1.265625 0.21875q0.546875 0.203125 0.90625 0.578125q0.375 0.375 0.546875 0.90625q0.1875 0.53125 0.1875 1.15625zm-1.328125 0.078125q0 -0.4375 -0.109375 -0.796875q-0.109375 -0.359375 -0.34375 -0.59375q-0.21875 -0.25 -0.5625 -0.375q-0.34375 -0.140625 -0.828125 -0.140625q-0.28125 0 -0.578125 0.046875q-0.28125 0.046875 -0.5625 0.140625q-0.265625 0.09375 -0.515625 0.21875q-0.25 0.109375 -0.453125 0.234375q0 0.953125 0.125 1.59375q0.140625 0.625 0.390625 1.015625q0.265625 0.375 0.640625 0.53125q0.390625 0.15625 0.890625 0.15625q0.421875 0 0.765625 -0.125q0.34375 -0.140625 0.59375 -0.40625q0.25 -0.265625 0.390625 -0.640625q0.15625 -0.375 0.15625 -0.859375zm9.026794 -0.109375q0 0.71875 -0.3125 1.3125q-0.296875 0.578125 -0.84375 1.0q-0.53125 0.40625 -1.265625 0.640625q-0.734375 0.234375 -1.578125 0.234375q-0.21875 0 -0.46875 -0.015625q-0.234375 0 -0.484375 -0.015625q-0.234375 -0.015625 -0.46875 -0.046875q-0.234375 -0.015625 -0.421875 -0.046875l0 -1.15625q0.40625 0.09375 0.90625 0.140625q0.515625 0.046875 1.03125 0.046875q0.59375 0 1.0625 -0.140625q0.46875 -0.140625 0.796875 -0.390625q0.328125 -0.265625 0.5 -0.640625q0.171875 -0.375 0.171875 -0.828125q0 -0.90625 -0.640625 -1.3125q-0.640625 -0.40625 -1.84375 -0.40625l-1.8125 0l0 -4.890625l5.15625 0l0 1.125l-3.953125 0l0 2.6875l0.84375 0q0.6875 0 1.328125 0.125q0.65625 0.125 1.15625 0.4375q0.515625 0.296875 0.828125 0.828125q0.3125 0.515625 0.3125 1.3125zm8.776855 0.953125l-1.640625 0l0 2.109375l-1.296875 0l0 -2.109375l-4.609375 0l0 -1.125l4.078125 -6.34375l1.828125 0l0 6.34375l1.640625 0l0 1.125zm-2.9375 -6.234375l-3.328125 5.109375l3.328125 0l0 -5.109375zm10.839294 -0.8125q-1.015625 -0.21875 -1.734375 -0.21875q-1.71875 0 -1.71875 1.796875l0 1.296875l3.21875 0l0 1.0625l-3.21875 0l0 5.21875l-1.296875 0l0 -5.21875l-2.359375 0l0 -1.0625l2.359375 0l0 -1.21875q0 -2.9375 3.0625 -2.9375q0.765625 0 1.6875 0.171875l0 1.109375zm-7.703125 1.796875l0 0zm14.94873 7.09375q-0.5 0.1875 -1.03125 0.28125q-0.515625 0.09375 -1.078125 0.09375q-1.734375 0 -2.6875 -0.9375q-0.9375 -0.953125 -0.9375 -2.78125q0 -0.859375 0.265625 -1.5625q0.28125 -0.71875 0.765625 -1.21875q0.5 -0.515625 1.171875 -0.78125q0.6875 -0.28125 1.5 -0.28125q0.578125 0 1.078125 0.078125q0.5 0.078125 0.953125 0.265625l0 1.21875q-0.484375 -0.25 -0.984375 -0.359375q-0.484375 -0.125 -1.015625 -0.125q-0.484375 0 -0.921875 0.1875q-0.4375 0.1875 -0.765625 0.546875q-0.328125 0.34375 -0.53125 0.859375q-0.1875 0.5 -0.1875 1.140625q0 1.328125 0.640625 2.0q0.65625 0.65625 1.8125 0.65625q0.515625 0 1.0 -0.109375q0.5 -0.125 0.953125 -0.359375l0 1.1875zm8.058044 0q-0.5 0.1875 -1.03125 0.28125q-0.515625 0.09375 -1.078125 0.09375q-1.734375 0 -2.6875 -0.9375q-0.9375 -0.953125 -0.9375 -2.78125q0 -0.859375 0.265625 -1.5625q0.28125 -0.71875 0.765625 -1.21875q0.5 -0.515625 1.171875 -0.78125q0.6875 -0.28125 1.5 -0.28125q0.578125 0 1.078125 0.078125q0.5 0.078125 0.953125 0.265625l0 1.21875q-0.484375 -0.25 -0.984375 -0.359375q-0.484375 -0.125 -1.015625 -0.125q-0.484375 0 -0.921875 0.1875q-0.4375 0.1875 -0.765625 0.546875q-0.328125 0.34375 -0.53125 0.859375q-0.1875 0.5 -0.1875 1.140625q0 1.328125 0.640625 2.0q0.65625 0.65625 1.8125 0.65625q0.515625 0 1.0 -0.109375q0.5 -0.125 0.953125 -0.359375l0 1.1875zm8.058105 0q-0.5 0.1875 -1.03125 0.28125q-0.515625 0.09375 -1.078125 0.09375q-1.734375 0 -2.6875 -0.9375q-0.9375 -0.953125 -0.9375 -2.78125q0 -0.859375 0.265625 -1.5625q0.28125 -0.71875 0.765625 -1.21875q0.5 -0.515625 1.171875 -0.78125q0.6875 -0.28125 1.5 -0.28125q0.578125 0 1.078125 0.078125q0.5 0.078125 0.953125 0.265625l0 1.21875q-0.484375 -0.25 -0.984375 -0.359375q-0.484375 -0.125 -1.015625 -0.125q-0.484375 0 -0.921875 0.1875q-0.4375 0.1875 -0.765625 0.546875q-0.328125 0.34375 -0.53125 0.859375q-0.1875 0.5 -0.1875 1.140625q0 1.328125 0.640625 2.0q0.65625 0.65625 1.8125 0.65625q0.515625 0 1.0 -0.109375q0.5 -0.125 0.953125 -0.359375l0 1.1875zm9.026794 -1.84375l-1.640625 0l0 2.109375l-1.296875 0l0 -2.109375l-4.609375 0l0 -1.125l4.078125 -6.34375l1.828125 0l0 6.34375l1.640625 0l0 1.125zm-2.9375 -6.234375l-3.328125 5.109375l3.328125 0l0 -5.109375zm9.026855 8.34375l-0.03125 -0.984375q-0.59375 0.59375 -1.21875 0.859375q-0.609375 0.25 -1.296875 0.25q-0.625 0 -1.078125 -0.15625q-0.4375 -0.15625 -0.734375 -0.4375q-0.28125 -0.28125 -0.421875 -0.65625q-0.140625 -0.390625 -0.140625 -0.84375q0 -1.09375 0.828125 -1.71875q0.828125 -0.640625 2.4375 -0.640625l1.515625 0l0 -0.640625q0 -0.65625 -0.421875 -1.046875q-0.40625 -0.390625 -1.265625 -0.390625q-0.625 0 -1.234375 0.140625q-0.59375 0.140625 -1.234375 0.40625l0 -1.15625q0.234375 -0.09375 0.53125 -0.171875q0.296875 -0.078125 0.625 -0.140625q0.328125 -0.078125 0.6875 -0.109375q0.359375 -0.046875 0.734375 -0.046875q0.65625 0 1.1875 0.15625q0.546875 0.140625 0.90625 0.4375q0.375 0.296875 0.5625 0.75q0.203125 0.453125 0.203125 1.078125l0 5.0625l-1.140625 0zm-0.140625 -3.34375l-1.609375 0q-0.484375 0 -0.828125 0.09375q-0.34375 0.09375 -0.5625 0.265625q-0.21875 0.171875 -0.328125 0.421875q-0.09375 0.25 -0.09375 0.5625q0 0.203125 0.0625 0.40625q0.0625 0.1875 0.203125 0.34375q0.15625 0.140625 0.390625 0.234375q0.234375 0.078125 0.5625 0.078125q0.4375 0 1.0 -0.265625q0.578125 -0.265625 1.203125 -0.84375l0 -1.296875zm10.167419 1.234375l-1.640625 0l0 2.109375l-1.296875 0l0 -2.109375l-4.609375 0l0 -1.125l4.078125 -6.34375l1.828125 0l0 6.34375l1.640625 0l0 1.125zm-2.9375 -6.234375l-3.328125 5.109375l3.328125 0l0 -5.109375zm3.9331055 4.828125q0 -0.9375 0.25 -1.671875q0.265625 -0.734375 0.734375 -1.234375q0.46875 -0.5 1.125 -0.75q0.671875 -0.265625 1.484375 -0.265625q0.359375 0 0.6875 0.046875q0.34375 0.03125 0.671875 0.125l0 -3.078125l1.28125 0l0 10.34375l-1.140625 0l-0.03125 -1.390625q-0.546875 0.78125 -1.171875 1.15625q-0.609375 0.359375 -1.34375 0.359375q-0.625 0 -1.109375 -0.25q-0.46875 -0.265625 -0.796875 -0.75q-0.3125 -0.484375 -0.484375 -1.15625q-0.15625 -0.671875 -0.15625 -1.484375zm1.3125 -0.09375q0 1.34375 0.390625 2.0q0.390625 0.65625 1.109375 0.65625q0.484375 0 1.015625 -0.4375q0.53125 -0.4375 1.125 -1.28125l0 -3.421875q-0.3125 -0.140625 -0.6875 -0.21875q-0.375 -0.078125 -0.75 -0.078125q-1.046875 0 -1.625 0.671875q-0.578125 0.671875 -0.578125 2.109375zm13.339294 1.203125q0 0.609375 -0.25 1.09375q-0.25 0.46875 -0.703125 0.796875q-0.453125 0.3125 -1.0625 0.484375q-0.59375 0.15625 -1.3125 0.15625q-0.78125 0 -1.375 -0.171875q-0.59375 -0.171875 -1.0 -0.484375q-0.40625 -0.3125 -0.609375 -0.75q-0.203125 -0.4375 -0.203125 -0.953125q0 -0.875 0.484375 -1.515625q0.5 -0.640625 1.515625 -1.15625q-0.9375 -0.484375 -1.375 -1.0625q-0.421875 -0.578125 -0.421875 -1.328125q0 -0.46875 0.1875 -0.890625q0.1875 -0.4375 0.578125 -0.765625q0.390625 -0.34375 0.96875 -0.546875q0.578125 -0.203125 1.359375 -0.203125q0.734375 0 1.296875 0.15625q0.5625 0.15625 0.9375 0.453125q0.390625 0.296875 0.578125 0.71875q0.1875 0.40625 0.1875 0.921875q0 0.828125 -0.46875 1.421875q-0.46875 0.578125 -1.3125 1.015625q0.421875 0.21875 0.78125 0.484375q0.375 0.25 0.640625 0.5625q0.265625 0.3125 0.421875 0.703125q0.15625 0.390625 0.15625 0.859375zm-1.53125 -4.953125q0 -0.640625 -0.453125 -0.953125q-0.453125 -0.328125 -1.28125 -0.328125q-0.828125 0 -1.28125 0.3125q-0.453125 0.3125 -0.453125 0.9375q0 0.296875 0.109375 0.546875q0.109375 0.234375 0.328125 0.453125q0.234375 0.203125 0.578125 0.40625q0.34375 0.203125 0.828125 0.421875q0.828125 -0.390625 1.21875 -0.8125q0.40625 -0.421875 0.40625 -0.984375zm0.140625 5.046875q0 -0.265625 -0.09375 -0.515625q-0.078125 -0.265625 -0.3125 -0.515625q-0.21875 -0.25 -0.609375 -0.5q-0.375 -0.25 -0.96875 -0.5q-0.5 0.234375 -0.84375 0.46875q-0.328125 0.234375 -0.546875 0.484375q-0.203125 0.25 -0.296875 0.515625q-0.078125 0.265625 -0.078125 0.546875q0 0.328125 0.140625 0.59375q0.140625 0.25 0.390625 0.421875q0.25 0.171875 0.59375 0.265625q0.34375 0.09375 0.75 0.09375q0.390625 0 0.734375 -0.078125q0.34375 -0.09375 0.59375 -0.25q0.25 -0.171875 0.390625 -0.421875q0.15625 -0.25 0.15625 -0.609375z" fill-rule="nonzero"></path><path fill="#000000" d="m652.77905 303.88052l-6.3125 0l0 -1.140625l2.46875 -2.46875q0.609375 -0.59375 0.984375 -1.03125q0.390625 -0.4375 0.59375 -0.796875q0.21875 -0.375 0.296875 -0.6875q0.078125 -0.328125 0.078125 -0.703125q0 -0.34375 -0.109375 -0.65625q-0.09375 -0.328125 -0.296875 -0.5625q-0.1875 -0.25 -0.5 -0.390625q-0.3125 -0.140625 -0.75 -0.140625q-0.609375 0 -1.109375 0.28125q-0.5 0.265625 -0.921875 0.6875l-0.703125 -0.828125q0.546875 -0.578125 1.25 -0.921875q0.703125 -0.34375 1.640625 -0.34375q0.640625 0 1.15625 0.1875q0.53125 0.1875 0.90625 0.546875q0.390625 0.359375 0.59375 0.890625q0.21875 0.515625 0.21875 1.15625q0 0.5625 -0.15625 1.03125q-0.140625 0.46875 -0.4375 0.9375q-0.296875 0.453125 -0.75 0.953125q-0.453125 0.5 -1.0625 1.09375l-1.734375 1.6875l4.65625 0l0 1.21875zm1.4643555 -3.515625q0 -0.9375 0.25 -1.671875q0.265625 -0.734375 0.734375 -1.234375q0.46875 -0.5 1.125 -0.75q0.671875 -0.265625 1.484375 -0.265625q0.359375 0 0.6875 0.046875q0.34375 0.03125 0.671875 0.125l0 -3.078125l1.28125 0l0 10.34375l-1.140625 0l-0.03125 -1.390625q-0.546875 0.78125 -1.171875 1.15625q-0.609375 0.359375 -1.34375 0.359375q-0.625 0 -1.109375 -0.25q-0.46875 -0.265625 -0.796875 -0.75q-0.3125 -0.484375 -0.484375 -1.15625q-0.15625 -0.671875 -0.15625 -1.484375zm1.3125 -0.09375q0 1.34375 0.390625 2.0q0.390625 0.65625 1.109375 0.65625q0.484375 0 1.015625 -0.4375q0.53125 -0.4375 1.125 -1.28125l0 -3.421875q-0.3125 -0.140625 -0.6875 -0.21875q-0.375 -0.078125 -0.75 -0.078125q-1.046875 0 -1.625 0.671875q-0.578125 0.671875 -0.578125 2.109375zm13.339294 1.203125q0 0.609375 -0.25 1.09375q-0.25 0.46875 -0.703125 0.796875q-0.453125 0.3125 -1.0625 0.484375q-0.59375 0.15625 -1.3125 0.15625q-0.78125 0 -1.375 -0.171875q-0.59375 -0.171875 -1.0 -0.484375q-0.40625 -0.3125 -0.609375 -0.75q-0.203125 -0.4375 -0.203125 -0.953125q0 -0.875 0.484375 -1.515625q0.5 -0.640625 1.515625 -1.15625q-0.9375 -0.484375 -1.375 -1.0625q-0.421875 -0.578125 -0.421875 -1.328125q0 -0.46875 0.1875 -0.890625q0.1875 -0.4375 0.578125 -0.765625q0.390625 -0.34375 0.96875 -0.546875q0.578125 -0.203125 1.359375 -0.203125q0.734375 0 1.296875 0.15625q0.5625 0.15625 0.9375 0.453125q0.390625 0.296875 0.578125 0.71875q0.1875 0.40625 0.1875 0.921875q0 0.828125 -0.46875 1.421875q-0.46875 0.578125 -1.3125 1.015625q0.421875 0.21875 0.78125 0.484375q0.375 0.25 0.640625 0.5625q0.265625 0.3125 0.421875 0.703125q0.15625 0.390625 0.15625 0.859375zm-1.53125 -4.953125q0 -0.640625 -0.453125 -0.953125q-0.453125 -0.328125 -1.28125 -0.328125q-0.828125 0 -1.28125 0.3125q-0.453125 0.3125 -0.453125 0.9375q0 0.296875 0.109375 0.546875q0.109375 0.234375 0.328125 0.453125q0.234375 0.203125 0.578125 0.40625q0.34375 0.203125 0.828125 0.421875q0.828125 -0.390625 1.21875 -0.8125q0.40625 -0.421875 0.40625 -0.984375zm0.140625 5.046875q0 -0.265625 -0.09375 -0.515625q-0.078125 -0.265625 -0.3125 -0.515625q-0.21875 -0.25 -0.609375 -0.5q-0.375 -0.25 -0.96875 -0.5q-0.5 0.234375 -0.84375 0.46875q-0.328125 0.234375 -0.546875 0.484375q-0.203125 0.25 -0.296875 0.515625q-0.078125 0.265625 -0.078125 0.546875q0 0.328125 0.140625 0.59375q0.140625 0.25 0.390625 0.421875q0.25 0.171875 0.59375 0.265625q0.34375 0.09375 0.75 0.09375q0.390625 0 0.734375 -0.078125q0.34375 -0.09375 0.59375 -0.25q0.25 -0.171875 0.390625 -0.421875q0.15625 -0.25 0.15625 -0.609375zm9.44873 -1.75q0 0.265625 -0.015625 0.453125q0 0.1875 -0.015625 0.34375l-5.171875 0q0 1.125 0.625 1.734375q0.640625 0.59375 1.828125 0.59375q0.3125 0 0.640625 -0.015625q0.328125 -0.03125 0.625 -0.0625q0.296875 -0.046875 0.5625 -0.09375q0.28125 -0.0625 0.515625 -0.140625l0 1.046875q-0.515625 0.15625 -1.171875 0.234375q-0.65625 0.09375 -1.359375 0.09375q-0.9375 0 -1.625 -0.25q-0.6875 -0.25 -1.125 -0.734375q-0.421875 -0.5 -0.640625 -1.203125q-0.203125 -0.703125 -0.203125 -1.59375q0 -0.78125 0.21875 -1.46875q0.21875 -0.703125 0.640625 -1.21875q0.4375 -0.53125 1.0625 -0.828125q0.625 -0.3125 1.421875 -0.3125q0.765625 0 1.359375 0.25q0.59375 0.234375 1.0 0.6875q0.40625 0.4375 0.609375 1.078125q0.21875 0.625 0.21875 1.40625zm-1.328125 -0.1875q0.015625 -0.484375 -0.109375 -0.890625q-0.109375 -0.40625 -0.359375 -0.703125q-0.234375 -0.296875 -0.609375 -0.453125q-0.359375 -0.171875 -0.84375 -0.171875q-0.421875 0 -0.765625 0.171875q-0.34375 0.15625 -0.59375 0.453125q-0.25 0.28125 -0.40625 0.703125q-0.140625 0.40625 -0.1875 0.890625l3.875 0zm9.323669 -4.140625l-4.015625 8.390625l-1.453125 0l4.171875 -8.390625l-5.171875 0l0 -1.1875l6.46875 0l0 1.1875zm6.6206055 8.390625l-0.03125 -0.984375q-0.59375 0.59375 -1.21875 0.859375q-0.609375 0.25 -1.296875 0.25q-0.625 0 -1.078125 -0.15625q-0.4375 -0.15625 -0.734375 -0.4375q-0.28125 -0.28125 -0.421875 -0.65625q-0.140625 -0.390625 -0.140625 -0.84375q0 -1.09375 0.828125 -1.71875q0.828125 -0.640625 2.4375 -0.640625l1.515625 0l0 -0.640625q0 -0.65625 -0.421875 -1.046875q-0.40625 -0.390625 -1.265625 -0.390625q-0.625 0 -1.234375 0.140625q-0.59375 0.140625 -1.234375 0.40625l0 -1.15625q0.234375 -0.09375 0.53125 -0.171875q0.296875 -0.078125 0.625 -0.140625q0.328125 -0.078125 0.6875 -0.109375q0.359375 -0.046875 0.734375 -0.046875q0.65625 0 1.1875 0.15625q0.546875 0.140625 0.90625 0.4375q0.375 0.296875 0.5625 0.75q0.203125 0.453125 0.203125 1.078125l0 5.0625l-1.140625 0zm-0.140625 -3.34375l-1.609375 0q-0.484375 0 -0.828125 0.09375q-0.34375 0.09375 -0.5625 0.265625q-0.21875 0.171875 -0.328125 0.421875q-0.09375 0.25 -0.09375 0.5625q0 0.203125 0.0625 0.40625q0.0625 0.1875 0.203125 0.34375q0.15625 0.140625 0.390625 0.234375q0.234375 0.078125 0.5625 0.078125q0.4375 0 1.0 -0.265625q0.578125 -0.265625 1.203125 -0.84375l0 -1.296875zm9.698669 0.9375q0 0.609375 -0.25 1.09375q-0.25 0.46875 -0.703125 0.796875q-0.453125 0.3125 -1.0625 0.484375q-0.59375 0.15625 -1.3125 0.15625q-0.78125 0 -1.375 -0.171875q-0.59375 -0.171875 -1.0 -0.484375q-0.40625 -0.3125 -0.609375 -0.75q-0.203125 -0.4375 -0.203125 -0.953125q0 -0.875 0.484375 -1.515625q0.5 -0.640625 1.515625 -1.15625q-0.9375 -0.484375 -1.375 -1.0625q-0.421875 -0.578125 -0.421875 -1.328125q0 -0.46875 0.1875 -0.890625q0.1875 -0.4375 0.578125 -0.765625q0.390625 -0.34375 0.96875 -0.546875q0.578125 -0.203125 1.359375 -0.203125q0.734375 0 1.296875 0.15625q0.5625 0.15625 0.9375 0.453125q0.390625 0.296875 0.578125 0.71875q0.1875 0.40625 0.1875 0.921875q0 0.828125 -0.46875 1.421875q-0.46875 0.578125 -1.3125 1.015625q0.421875 0.21875 0.78125 0.484375q0.375 0.25 0.640625 0.5625q0.265625 0.3125 0.421875 0.703125q0.15625 0.390625 0.15625 0.859375zm-1.53125 -4.953125q0 -0.640625 -0.453125 -0.953125q-0.453125 -0.328125 -1.28125 -0.328125q-0.828125 0 -1.28125 0.3125q-0.453125 0.3125 -0.453125 0.9375q0 0.296875 0.109375 0.546875q0.109375 0.234375 0.328125 0.453125q0.234375 0.203125 0.578125 0.40625q0.34375 0.203125 0.828125 0.421875q0.828125 -0.390625 1.21875 -0.8125q0.40625 -0.421875 0.40625 -0.984375zm0.140625 5.046875q0 -0.265625 -0.09375 -0.515625q-0.078125 -0.265625 -0.3125 -0.515625q-0.21875 -0.25 -0.609375 -0.5q-0.375 -0.25 -0.96875 -0.5q-0.5 0.234375 -0.84375 0.46875q-0.328125 0.234375 -0.546875 0.484375q-0.203125 0.25 -0.296875 0.515625q-0.078125 0.265625 -0.078125 0.546875q0 0.328125 0.140625 0.59375q0.140625 0.25 0.390625 0.421875q0.25 0.171875 0.59375 0.265625q0.34375 0.09375 0.75 0.09375q0.390625 0 0.734375 -0.078125q0.34375 -0.09375 0.59375 -0.25q0.25 -0.171875 0.390625 -0.421875q0.15625 -0.25 0.15625 -0.609375zm9.91748 0.203125l-1.640625 0l0 2.109375l-1.296875 0l0 -2.109375l-4.609375 0l0 -1.125l4.078125 -6.34375l1.828125 0l0 6.34375l1.640625 0l0 1.125zm-2.9375 -6.234375l-3.328125 5.109375l3.328125 0l0 -5.109375zm10.526794 4.28125q0 0.265625 -0.015625 0.453125q0 0.1875 -0.015625 0.34375l-5.171875 0q0 1.125 0.625 1.734375q0.640625 0.59375 1.828125 0.59375q0.3125 0 0.640625 -0.015625q0.328125 -0.03125 0.625 -0.0625q0.296875 -0.046875 0.5625 -0.09375q0.28125 -0.0625 0.515625 -0.140625l0 1.046875q-0.515625 0.15625 -1.171875 0.234375q-0.65625 0.09375 -1.359375 0.09375q-0.9375 0 -1.625 -0.25q-0.6875 -0.25 -1.125 -0.734375q-0.421875 -0.5 -0.640625 -1.203125q-0.203125 -0.703125 -0.203125 -1.59375q0 -0.78125 0.21875 -1.46875q0.21875 -0.703125 0.640625 -1.21875q0.4375 -0.53125 1.0625 -0.828125q0.625 -0.3125 1.421875 -0.3125q0.765625 0 1.359375 0.25q0.59375 0.234375 1.0 0.6875q0.40625 0.4375 0.609375 1.078125q0.21875 0.625 0.21875 1.40625zm-1.328125 -0.1875q0.015625 -0.484375 -0.109375 -0.890625q-0.109375 -0.40625 -0.359375 -0.703125q-0.234375 -0.296875 -0.609375 -0.453125q-0.359375 -0.171875 -0.84375 -0.171875q-0.421875 0 -0.765625 0.171875q-0.34375 0.15625 -0.59375 0.453125q-0.25 0.28125 -0.40625 0.703125q-0.140625 0.40625 -0.1875 0.890625l3.875 0zm9.85498 2.140625l-1.640625 0l0 2.109375l-1.296875 0l0 -2.109375l-4.609375 0l0 -1.125l4.078125 -6.34375l1.828125 0l0 6.34375l1.640625 0l0 1.125zm-2.9375 -6.234375l-3.328125 5.109375l3.328125 0l0 -5.109375zm10.636169 5.3125q0 0.65625 -0.234375 1.234375q-0.234375 0.578125 -0.6875 1.015625q-0.4375 0.421875 -1.0625 0.671875q-0.609375 0.234375 -1.359375 0.234375q-0.796875 0 -1.40625 -0.25q-0.609375 -0.25 -1.015625 -0.765625q-0.40625 -0.53125 -0.625 -1.328125q-0.203125 -0.796875 -0.203125 -1.890625q0 -0.734375 0.09375 -1.421875q0.09375 -0.6875 0.3125 -1.296875q0.21875 -0.609375 0.578125 -1.109375q0.375 -0.5 0.921875 -0.859375q0.546875 -0.375 1.28125 -0.578125q0.734375 -0.203125 1.71875 -0.203125l0.9375 0l0 1.125l-1.015625 0q-0.859375 0 -1.5 0.203125q-0.625 0.203125 -1.046875 0.578125q-0.421875 0.375 -0.65625 0.90625q-0.21875 0.515625 -0.28125 1.171875l-0.03125 0.296875q0.46875 -0.265625 1.0625 -0.421875q0.609375 -0.171875 1.3125 -0.171875q0.71875 0 1.265625 0.21875q0.546875 0.203125 0.90625 0.578125q0.375 0.375 0.546875 0.90625q0.1875 0.53125 0.1875 1.15625zm-1.328125 0.078125q0 -0.4375 -0.109375 -0.796875q-0.109375 -0.359375 -0.34375 -0.59375q-0.21875 -0.25 -0.5625 -0.375q-0.34375 -0.140625 -0.828125 -0.140625q-0.28125 0 -0.578125 0.046875q-0.28125 0.046875 -0.5625 0.140625q-0.265625 0.09375 -0.515625 0.21875q-0.25 0.109375 -0.453125 0.234375q0 0.953125 0.125 1.59375q0.140625 0.625 0.390625 1.015625q0.265625 0.375 0.640625 0.53125q0.390625 0.15625 0.890625 0.15625q0.421875 0 0.765625 -0.125q0.34375 -0.140625 0.59375 -0.40625q0.25 -0.265625 0.390625 -0.640625q0.15625 -0.375 0.15625 -0.859375zm2.6831055 -0.5625q0 -0.9375 0.25 -1.671875q0.265625 -0.734375 0.734375 -1.234375q0.46875 -0.5 1.125 -0.75q0.671875 -0.265625 1.484375 -0.265625q0.359375 0 0.6875 0.046875q0.34375 0.03125 0.671875 0.125l0 -3.078125l1.28125 0l0 10.34375l-1.140625 0l-0.03125 -1.390625q-0.546875 0.78125 -1.171875 1.15625q-0.609375 0.359375 -1.34375 0.359375q-0.625 0 -1.109375 -0.25q-0.46875 -0.265625 -0.796875 -0.75q-0.3125 -0.484375 -0.484375 -1.15625q-0.15625 -0.671875 -0.15625 -1.484375zm1.3125 -0.09375q0 1.34375 0.390625 2.0q0.390625 0.65625 1.109375 0.65625q0.484375 0 1.015625 -0.4375q0.53125 -0.4375 1.125 -1.28125l0 -3.421875q-0.3125 -0.140625 -0.6875 -0.21875q-0.375 -0.078125 -0.75 -0.078125q-1.046875 0 -1.625 0.671875q-0.578125 0.671875 -0.578125 2.109375zm13.339294 1.203125q0 0.609375 -0.25 1.09375q-0.25 0.46875 -0.703125 0.796875q-0.453125 0.3125 -1.0625 0.484375q-0.59375 0.15625 -1.3125 0.15625q-0.78125 0 -1.375 -0.171875q-0.59375 -0.171875 -1.0 -0.484375q-0.40625 -0.3125 -0.609375 -0.75q-0.203125 -0.4375 -0.203125 -0.953125q0 -0.875 0.484375 -1.515625q0.5 -0.640625 1.515625 -1.15625q-0.9375 -0.484375 -1.375 -1.0625q-0.421875 -0.578125 -0.421875 -1.328125q0 -0.46875 0.1875 -0.890625q0.1875 -0.4375 0.578125 -0.765625q0.390625 -0.34375 0.96875 -0.546875q0.578125 -0.203125 1.359375 -0.203125q0.734375 0 1.296875 0.15625q0.5625 0.15625 0.9375 0.453125q0.390625 0.296875 0.578125 0.71875q0.1875 0.40625 0.1875 0.921875q0 0.828125 -0.46875 1.421875q-0.46875 0.578125 -1.3125 1.015625q0.421875 0.21875 0.78125 0.484375q0.375 0.25 0.640625 0.5625q0.265625 0.3125 0.421875 0.703125q0.15625 0.390625 0.15625 0.859375zm-1.53125 -4.953125q0 -0.640625 -0.453125 -0.953125q-0.453125 -0.328125 -1.28125 -0.328125q-0.828125 0 -1.28125 0.3125q-0.453125 0.3125 -0.453125 0.9375q0 0.296875 0.109375 0.546875q0.109375 0.234375 0.328125 0.453125q0.234375 0.203125 0.578125 0.40625q0.34375 0.203125 0.828125 0.421875q0.828125 -0.390625 1.21875 -0.8125q0.40625 -0.421875 0.40625 -0.984375zm0.140625 5.046875q0 -0.265625 -0.09375 -0.515625q-0.078125 -0.265625 -0.3125 -0.515625q-0.21875 -0.25 -0.609375 -0.5q-0.375 -0.25 -0.96875 -0.5q-0.5 0.234375 -0.84375 0.46875q-0.328125 0.234375 -0.546875 0.484375q-0.203125 0.25 -0.296875 0.515625q-0.078125 0.265625 -0.078125 0.546875q0 0.328125 0.140625 0.59375q0.140625 0.25 0.390625 0.421875q0.25 0.171875 0.59375 0.265625q0.34375 0.09375 0.75 0.09375q0.390625 0 0.734375 -0.078125q0.34375 -0.09375 0.59375 -0.25q0.25 -0.171875 0.390625 -0.421875q0.15625 -0.25 0.15625 -0.609375zm9.44873 -1.75q0 0.265625 -0.015625 0.453125q0 0.1875 -0.015625 0.34375l-5.171875 0q0 1.125 0.625 1.734375q0.640625 0.59375 1.828125 0.59375q0.3125 0 0.640625 -0.015625q0.328125 -0.03125 0.625 -0.0625q0.296875 -0.046875 0.5625 -0.09375q0.28125 -0.0625 0.515625 -0.140625l0 1.046875q-0.515625 0.15625 -1.171875 0.234375q-0.65625 0.09375 -1.359375 0.09375q-0.9375 0 -1.625 -0.25q-0.6875 -0.25 -1.125 -0.734375q-0.421875 -0.5 -0.640625 -1.203125q-0.203125 -0.703125 -0.203125 -1.59375q0 -0.78125 0.21875 -1.46875q0.21875 -0.703125 0.640625 -1.21875q0.4375 -0.53125 1.0625 -0.828125q0.625 -0.3125 1.421875 -0.3125q0.765625 0 1.359375 0.25q0.59375 0.234375 1.0 0.6875q0.40625 0.4375 0.609375 1.078125q0.21875 0.625 0.21875 1.40625zm-1.328125 -0.1875q0.015625 -0.484375 -0.109375 -0.890625q-0.109375 -0.40625 -0.359375 -0.703125q-0.234375 -0.296875 -0.609375 -0.453125q-0.359375 -0.171875 -0.84375 -0.171875q-0.421875 0 -0.765625 0.171875q-0.34375 0.15625 -0.59375 0.453125q-0.25 0.28125 -0.40625 0.703125q-0.140625 0.40625 -0.1875 0.890625l3.875 0zm9.136169 1.1875q0 0.71875 -0.3125 1.3125q-0.296875 0.578125 -0.84375 1.0q-0.53125 0.40625 -1.265625 0.640625q-0.734375 0.234375 -1.578125 0.234375q-0.21875 0 -0.46875 -0.015625q-0.234375 0 -0.484375 -0.015625q-0.234375 -0.015625 -0.46875 -0.046875q-0.234375 -0.015625 -0.421875 -0.046875l0 -1.15625q0.40625 0.09375 0.90625 0.140625q0.515625 0.046875 1.03125 0.046875q0.59375 0 1.0625 -0.140625q0.46875 -0.140625 0.796875 -0.390625q0.328125 -0.265625 0.5 -0.640625q0.171875 -0.375 0.171875 -0.828125q0 -0.90625 -0.640625 -1.3125q-0.640625 -0.40625 -1.84375 -0.40625l-1.8125 0l0 -4.890625l5.15625 0l0 1.125l-3.953125 0l0 2.6875l0.84375 0q0.6875 0 1.328125 0.125q0.65625 0.125 1.15625 0.4375q0.515625 0.296875 0.828125 0.828125q0.3125 0.515625 0.3125 1.3125zm8.245605 -5.328125l-4.015625 8.390625l-1.453125 0l4.171875 -8.390625l-5.171875 0l0 -1.1875l6.46875 0l0 1.1875zm8.433044 -0.765625q-1.015625 -0.21875 -1.734375 -0.21875q-1.71875 0 -1.71875 1.796875l0 1.296875l3.21875 0l0 1.0625l-3.21875 0l0 5.21875l-1.296875 0l0 -5.21875l-2.359375 0l0 -1.0625l2.359375 0l0 -1.21875q0 -2.9375 3.0625 -2.9375q0.765625 0 1.6875 0.171875l0 1.109375zm-7.703125 1.796875l0 0zm15.558105 4.328125q0 0.65625 -0.234375 1.234375q-0.234375 0.578125 -0.6875 1.015625q-0.4375 0.421875 -1.0625 0.671875q-0.609375 0.234375 -1.359375 0.234375q-0.796875 0 -1.40625 -0.25q-0.609375 -0.25 -1.015625 -0.765625q-0.40625 -0.53125 -0.625 -1.328125q-0.203125 -0.796875 -0.203125 -1.890625q0 -0.734375 0.09375 -1.421875q0.09375 -0.6875 0.3125 -1.296875q0.21875 -0.609375 0.578125 -1.109375q0.375 -0.5 0.921875 -0.859375q0.546875 -0.375 1.28125 -0.578125q0.734375 -0.203125 1.71875 -0.203125l0.9375 0l0 1.125l-1.015625 0q-0.859375 0 -1.5 0.203125q-0.625 0.203125 -1.046875 0.578125q-0.421875 0.375 -0.65625 0.90625q-0.21875 0.515625 -0.28125 1.171875l-0.03125 0.296875q0.46875 -0.265625 1.0625 -0.421875q0.609375 -0.171875 1.3125 -0.171875q0.71875 0 1.265625 0.21875q0.546875 0.203125 0.90625 0.578125q0.375 0.375 0.546875 0.90625q0.1875 0.53125 0.1875 1.15625zm-1.328125 0.078125q0 -0.4375 -0.109375 -0.796875q-0.109375 -0.359375 -0.34375 -0.59375q-0.21875 -0.25 -0.5625 -0.375q-0.34375 -0.140625 -0.828125 -0.140625q-0.28125 0 -0.578125 0.046875q-0.28125 0.046875 -0.5625 0.140625q-0.265625 0.09375 -0.515625 0.21875q-0.25 0.109375 -0.453125 0.234375q0 0.953125 0.125 1.59375q0.140625 0.625 0.390625 1.015625q0.265625 0.375 0.640625 0.53125q0.390625 0.15625 0.890625 0.15625q0.421875 0 0.765625 -0.125q0.34375 -0.140625 0.59375 -0.40625q0.25 -0.265625 0.390625 -0.640625q0.15625 -0.375 0.15625 -0.859375zm2.6830444 -0.5625q0 -0.9375 0.25 -1.671875q0.265625 -0.734375 0.734375 -1.234375q0.46875 -0.5 1.125 -0.75q0.671875 -0.265625 1.484375 -0.265625q0.359375 0 0.6875 0.046875q0.34375 0.03125 0.671875 0.125l0 -3.078125l1.28125 0l0 10.34375l-1.140625 0l-0.03125 -1.390625q-0.546875 0.78125 -1.171875 1.15625q-0.609375 0.359375 -1.34375 0.359375q-0.625 0 -1.109375 -0.25q-0.46875 -0.265625 -0.796875 -0.75q-0.3125 -0.484375 -0.484375 -1.15625q-0.15625 -0.671875 -0.15625 -1.484375zm1.3125 -0.09375q0 1.34375 0.390625 2.0q0.390625 0.65625 1.109375 0.65625q0.484375 0 1.015625 -0.4375q0.53125 -0.4375 1.125 -1.28125l0 -3.421875q-0.3125 -0.140625 -0.6875 -0.21875q-0.375 -0.078125 -0.75 -0.078125q-1.046875 0 -1.625 0.671875q-0.578125 0.671875 -0.578125 2.109375zm11.839355 3.609375l-0.03125 -0.984375q-0.59375 0.59375 -1.21875 0.859375q-0.609375 0.25 -1.296875 0.25q-0.625 0 -1.078125 -0.15625q-0.4375 -0.15625 -0.734375 -0.4375q-0.28125 -0.28125 -0.421875 -0.65625q-0.140625 -0.390625 -0.140625 -0.84375q0 -1.09375 0.828125 -1.71875q0.828125 -0.640625 2.4375 -0.640625l1.515625 0l0 -0.640625q0 -0.65625 -0.421875 -1.046875q-0.40625 -0.390625 -1.265625 -0.390625q-0.625 0 -1.234375 0.140625q-0.59375 0.140625 -1.234375 0.40625l0 -1.15625q0.234375 -0.09375 0.53125 -0.171875q0.296875 -0.078125 0.625 -0.140625q0.328125 -0.078125 0.6875 -0.109375q0.359375 -0.046875 0.734375 -0.046875q0.65625 0 1.1875 0.15625q0.546875 0.140625 0.90625 0.4375q0.375 0.296875 0.5625 0.75q0.203125 0.453125 0.203125 1.078125l0 5.0625l-1.140625 0zm-0.140625 -3.34375l-1.609375 0q-0.484375 0 -0.828125 0.09375q-0.34375 0.09375 -0.5625 0.265625q-0.21875 0.171875 -0.328125 0.421875q-0.09375 0.25 -0.09375 0.5625q0 0.203125 0.0625 0.40625q0.0625 0.1875 0.203125 0.34375q0.15625 0.140625 0.390625 0.234375q0.234375 0.078125 0.5625 0.078125q0.4375 0 1.0 -0.265625q0.578125 -0.265625 1.203125 -0.84375l0 -1.296875zm8.198669 3.34375l-0.03125 -0.984375q-0.59375 0.59375 -1.21875 0.859375q-0.609375 0.25 -1.296875 0.25q-0.625 0 -1.078125 -0.15625q-0.4375 -0.15625 -0.734375 -0.4375q-0.28125 -0.28125 -0.421875 -0.65625q-0.140625 -0.390625 -0.140625 -0.84375q0 -1.09375 0.828125 -1.71875q0.828125 -0.640625 2.4375 -0.640625l1.515625 0l0 -0.640625q0 -0.65625 -0.421875 -1.046875q-0.40625 -0.390625 -1.265625 -0.390625q-0.625 0 -1.234375 0.140625q-0.59375 0.140625 -1.234375 0.40625l0 -1.15625q0.234375 -0.09375 0.53125 -0.171875q0.296875 -0.078125 0.625 -0.140625q0.328125 -0.078125 0.6875 -0.109375q0.359375 -0.046875 0.734375 -0.046875q0.65625 0 1.1875 0.15625q0.546875 0.140625 0.90625 0.4375q0.375 0.296875 0.5625 0.75q0.203125 0.453125 0.203125 1.078125l0 5.0625l-1.140625 0zm-0.140625 -3.34375l-1.609375 0q-0.484375 0 -0.828125 0.09375q-0.34375 0.09375 -0.5625 0.265625q-0.21875 0.171875 -0.328125 0.421875q-0.09375 0.25 -0.09375 0.5625q0 0.203125 0.0625 0.40625q0.0625 0.1875 0.203125 0.34375q0.15625 0.140625 0.390625 0.234375q0.234375 0.078125 0.5625 0.078125q0.4375 0 1.0 -0.265625q0.578125 -0.265625 1.203125 -0.84375l0 -1.296875zm9.19873 3.078125q-0.5 0.1875 -1.03125 0.28125q-0.515625 0.09375 -1.078125 0.09375q-1.734375 0 -2.6875 -0.9375q-0.9375 -0.953125 -0.9375 -2.78125q0 -0.859375 0.265625 -1.5625q0.28125 -0.71875 0.765625 -1.21875q0.5 -0.515625 1.171875 -0.78125q0.6875 -0.28125 1.5 -0.28125q0.578125 0 1.078125 0.078125q0.5 0.078125 0.953125 0.265625l0 1.21875q-0.484375 -0.25 -0.984375 -0.359375q-0.484375 -0.125 -1.015625 -0.125q-0.484375 0 -0.921875 0.1875q-0.4375 0.1875 -0.765625 0.546875q-0.328125 0.34375 -0.53125 0.859375q-0.1875 0.5 -0.1875 1.140625q0 1.328125 0.640625 2.0q0.65625 0.65625 1.8125 0.65625q0.515625 0 1.0 -0.109375q0.5 -0.125 0.953125 -0.359375l0 1.1875zm9.026794 -1.84375l-1.640625 0l0 2.109375l-1.296875 0l0 -2.109375l-4.609375 0l0 -1.125l4.078125 -6.34375l1.828125 0l0 6.34375l1.640625 0l0 1.125zm-2.9375 -6.234375l-3.328125 5.109375l3.328125 0l0 -5.109375zm10.026855 8.078125q-0.5 0.1875 -1.03125 0.28125q-0.515625 0.09375 -1.078125 0.09375q-1.734375 0 -2.6875 -0.9375q-0.9375 -0.953125 -0.9375 -2.78125q0 -0.859375 0.265625 -1.5625q0.28125 -0.71875 0.765625 -1.21875q0.5 -0.515625 1.171875 -0.78125q0.6875 -0.28125 1.5 -0.28125q0.578125 0 1.078125 0.078125q0.5 0.078125 0.953125 0.265625l0 1.21875q-0.484375 -0.25 -0.984375 -0.359375q-0.484375 -0.125 -1.015625 -0.125q-0.484375 0 -0.921875 0.1875q-0.4375 0.1875 -0.765625 0.546875q-0.328125 0.34375 -0.53125 0.859375q-0.1875 0.5 -0.1875 1.140625q0 1.328125 0.640625 2.0q0.65625 0.65625 1.8125 0.65625q0.515625 0 1.0 -0.109375q0.5 -0.125 0.953125 -0.359375l0 1.1875zm7.0580444 0.265625l-0.03125 -0.984375q-0.59375 0.59375 -1.21875 0.859375q-0.609375 0.25 -1.296875 0.25q-0.625 0 -1.078125 -0.15625q-0.4375 -0.15625 -0.734375 -0.4375q-0.28125 -0.28125 -0.421875 -0.65625q-0.140625 -0.390625 -0.140625 -0.84375q0 -1.09375 0.828125 -1.71875q0.828125 -0.640625 2.4375 -0.640625l1.515625 0l0 -0.640625q0 -0.65625 -0.421875 -1.046875q-0.40625 -0.390625 -1.265625 -0.390625q-0.625 0 -1.234375 0.140625q-0.59375 0.140625 -1.234375 0.40625l0 -1.15625q0.234375 -0.09375 0.53125 -0.171875q0.296875 -0.078125 0.625 -0.140625q0.328125 -0.078125 0.6875 -0.109375q0.359375 -0.046875 0.734375 -0.046875q0.65625 0 1.1875 0.15625q0.546875 0.140625 0.90625 0.4375q0.375 0.296875 0.5625 0.75q0.203125 0.453125 0.203125 1.078125l0 5.0625l-1.140625 0zm-0.140625 -3.34375l-1.609375 0q-0.484375 0 -0.828125 0.09375q-0.34375 0.09375 -0.5625 0.265625q-0.21875 0.171875 -0.328125 0.421875q-0.09375 0.25 -0.09375 0.5625q0 0.203125 0.0625 0.40625q0.0625 0.1875 0.203125 0.34375q0.15625 0.140625 0.390625 0.234375q0.234375 0.078125 0.5625 0.078125q0.4375 0 1.0 -0.265625q0.578125 -0.265625 1.203125 -0.84375l0 -1.296875zm9.63623 -5.046875l-4.015625 8.390625l-1.453125 0l4.171875 -8.390625l-5.171875 0l0 -1.1875l6.46875 0l0 1.1875zm8.058044 3.0625q0 1.40625 -0.34375 2.421875q-0.328125 1.0 -0.984375 1.65625q-0.65625 0.640625 -1.640625 0.953125q-0.96875 0.296875 -2.25 0.296875l-0.796875 0l0 -1.109375l0.890625 0q0.953125 0 1.640625 -0.1875q0.6875 -0.203125 1.140625 -0.5625q0.453125 -0.375 0.6875 -0.90625q0.25 -0.53125 0.3125 -1.21875l0.03125 -0.296875q-0.46875 0.28125 -1.078125 0.453125q-0.59375 0.15625 -1.296875 0.15625q-0.71875 0 -1.265625 -0.21875q-0.546875 -0.21875 -0.921875 -0.59375q-0.359375 -0.375 -0.546875 -0.890625q-0.171875 -0.53125 -0.171875 -1.15625q0 -0.65625 0.234375 -1.234375q0.25 -0.578125 0.6875 -1.0q0.4375 -0.4375 1.03125 -0.6875q0.609375 -0.25 1.34375 -0.25q0.71875 0 1.3125 0.234375q0.609375 0.234375 1.046875 0.765625q0.4375 0.515625 0.6875 1.359375q0.25 0.828125 0.25 2.015625zm-3.34375 -3.328125q-0.421875 0 -0.765625 0.140625q-0.34375 0.125 -0.609375 0.390625q-0.25 0.265625 -0.390625 0.640625q-0.140625 0.375 -0.140625 0.875q0 0.4375 0.09375 0.796875q0.109375 0.34375 0.328125 0.59375q0.234375 0.25 0.578125 0.390625q0.359375 0.125 0.84375 0.125q0.265625 0 0.546875 -0.046875q0.296875 -0.0625 0.5625 -0.140625q0.28125 -0.09375 0.53125 -0.203125q0.25 -0.125 0.453125 -0.265625q0 -0.9375 -0.140625 -1.5625q-0.140625 -0.640625 -0.40625 -1.015625q-0.265625 -0.390625 -0.640625 -0.546875q-0.375 -0.171875 -0.84375 -0.171875zm11.464355 4.59375q0 0.265625 -0.015625 0.453125q0 0.1875 -0.015625 0.34375l-5.171875 0q0 1.125 0.625 1.734375q0.640625 0.59375 1.828125 0.59375q0.3125 0 0.640625 -0.015625q0.328125 -0.03125 0.625 -0.0625q0.296875 -0.046875 0.5625 -0.09375q0.28125 -0.0625 0.515625 -0.140625l0 1.046875q-0.515625 0.15625 -1.171875 0.234375q-0.65625 0.09375 -1.359375 0.09375q-0.9375 0 -1.625 -0.25q-0.6875 -0.25 -1.125 -0.734375q-0.421875 -0.5 -0.640625 -1.203125q-0.203125 -0.703125 -0.203125 -1.59375q0 -0.78125 0.21875 -1.46875q0.21875 -0.703125 0.640625 -1.21875q0.4375 -0.53125 1.0625 -0.828125q0.625 -0.3125 1.421875 -0.3125q0.765625 0 1.359375 0.25q0.59375 0.234375 1.0 0.6875q0.40625 0.4375 0.609375 1.078125q0.21875 0.625 0.21875 1.40625zm-1.328125 -0.1875q0.015625 -0.484375 -0.109375 -0.890625q-0.109375 -0.40625 -0.359375 -0.703125q-0.234375 -0.296875 -0.609375 -0.453125q-0.359375 -0.171875 -0.84375 -0.171875q-0.421875 0 -0.765625 0.171875q-0.34375 0.15625 -0.59375 0.453125q-0.25 0.28125 -0.40625 0.703125q-0.140625 0.40625 -0.1875 0.890625l3.875 0zm8.886169 3.984375q-0.5 0.1875 -1.03125 0.28125q-0.515625 0.09375 -1.078125 0.09375q-1.734375 0 -2.6875 -0.9375q-0.9375 -0.953125 -0.9375 -2.78125q0 -0.859375 0.265625 -1.5625q0.28125 -0.71875 0.765625 -1.21875q0.5 -0.515625 1.171875 -0.78125q0.6875 -0.28125 1.5 -0.28125q0.578125 0 1.078125 0.078125q0.5 0.078125 0.953125 0.265625l0 1.21875q-0.484375 -0.25 -0.984375 -0.359375q-0.484375 -0.125 -1.015625 -0.125q-0.484375 0 -0.921875 0.1875q-0.4375 0.1875 -0.765625 0.546875q-0.328125 0.34375 -0.53125 0.859375q-0.1875 0.5 -0.1875 1.140625q0 1.328125 0.640625 2.0q0.65625 0.65625 1.8125 0.65625q0.515625 0 1.0 -0.109375q0.5 -0.125 0.953125 -0.359375l0 1.1875zm8.495605 -8.125l-4.015625 8.390625l-1.453125 0l4.171875 -8.390625l-5.171875 0l0 -1.1875l6.46875 0l0 1.1875zm8.339294 3.609375q0 1.09375 -0.234375 2.0q-0.21875 0.90625 -0.671875 1.5625q-0.4375 0.640625 -1.109375 1.0q-0.65625 0.34375 -1.546875 0.34375q-0.765625 0 -1.40625 -0.28125q-0.625 -0.296875 -1.078125 -0.890625q-0.4375 -0.59375 -0.6875 -1.53125q-0.234375 -0.9375 -0.234375 -2.203125q0 -1.09375 0.21875 -2.0q0.234375 -0.921875 0.671875 -1.5625q0.453125 -0.65625 1.109375 -1.0q0.671875 -0.359375 1.5625 -0.359375q0.765625 0 1.390625 0.296875q0.625 0.28125 1.078125 0.890625q0.453125 0.59375 0.6875 1.53125q0.25 0.921875 0.25 2.203125zm-1.296875 0.046875q0 -0.25 -0.015625 -0.5q-0.015625 -0.25 -0.046875 -0.484375l-4.046875 3.015625q0.109375 0.375 0.28125 0.703125q0.171875 0.328125 0.40625 0.5625q0.234375 0.21875 0.53125 0.359375q0.3125 0.125 0.703125 0.125q0.5 0 0.90625 -0.234375q0.40625 -0.25 0.6875 -0.71875q0.28125 -0.484375 0.4375 -1.1875q0.15625 -0.71875 0.15625 -1.640625zm-4.375 -0.09375q0 0.234375 0 0.46875q0 0.21875 0.03125 0.421875l4.046875 -2.984375q-0.109375 -0.375 -0.28125 -0.6875q-0.171875 -0.3125 -0.40625 -0.53125q-0.234375 -0.21875 -0.53125 -0.34375q-0.296875 -0.125 -0.671875 -0.125q-0.5 0 -0.90625 0.25q-0.40625 0.234375 -0.703125 0.71875q-0.28125 0.46875 -0.4375 1.1875q-0.140625 0.703125 -0.140625 1.625zm13.82373 -4.328125q-1.015625 -0.21875 -1.734375 -0.21875q-1.71875 0 -1.71875 1.796875l0 1.296875l3.21875 0l0 1.0625l-3.21875 0l0 5.21875l-1.296875 0l0 -5.21875l-2.359375 0l0 -1.0625l2.359375 0l0 -1.21875q0 -2.9375 3.0625 -2.9375q0.765625 0 1.6875 0.171875l0 1.109375zm-7.703125 1.796875l0 0z" fill-rule="nonzero"></path><path fill="#000000" d="m61.447918 343.88052l-1.75 0l-3.421875 -3.9375l0 3.9375l-1.28125 0l0 -10.34375l1.28125 0l0 6.359375l3.296875 -3.375l1.6875 0l-3.453125 3.390625l3.640625 3.96875zm7.667446 -4.0625q0 0.265625 -0.015625 0.453125q0 0.1875 -0.015625 0.34375l-5.171871 0q0 1.125 0.6249962 1.734375q0.640625 0.59375 1.828125 0.59375q0.3125 0 0.640625 -0.015625q0.328125 -0.03125 0.625 -0.0625q0.296875 -0.046875 0.5625 -0.09375q0.28125 -0.0625 0.515625 -0.140625l0 1.046875q-0.515625 0.15625 -1.171875 0.234375q-0.65625 0.09375 -1.359375 0.09375q-0.9375 0 -1.625 -0.25q-0.6874962 -0.25 -1.1249962 -0.734375q-0.421875 -0.5 -0.640625 -1.203125q-0.203125 -0.703125 -0.203125 -1.59375q0 -0.78125 0.21875 -1.46875q0.21875 -0.703125 0.640625 -1.21875q0.4375 -0.53125 1.0624962 -0.828125q0.625 -0.3125 1.421875 -0.3125q0.765625 0 1.359375 0.25q0.59375 0.234375 1.0 0.6875q0.40625 0.4375 0.609375 1.078125q0.21875 0.625 0.21875 1.40625zm-1.328125 -0.1875q0.015625 -0.484375 -0.109375 -0.890625q-0.109375 -0.40625 -0.359375 -0.703125q-0.234375 -0.296875 -0.609375 -0.453125q-0.359375 -0.171875 -0.84375 -0.171875q-0.421875 0 -0.765625 0.171875q-0.34375 0.15625 -0.59375 0.453125q-0.25 0.28125 -0.40625 0.703125q-0.14062119 0.40625 -0.18749619 0.890625l3.8749962 0zm9.3862 0.1875q0 0.265625 -0.015625 0.453125q0 0.1875 -0.015625 0.34375l-5.171875 0q0 1.125 0.625 1.734375q0.640625 0.59375 1.828125 0.59375q0.3125 0 0.640625 -0.015625q0.328125 -0.03125 0.625 -0.0625q0.296875 -0.046875 0.5625 -0.09375q0.28125 -0.0625 0.515625 -0.140625l0 1.046875q-0.515625 0.15625 -1.171875 0.234375q-0.65625 0.09375 -1.359375 0.09375q-0.9375 0 -1.625 -0.25q-0.6875 -0.25 -1.125 -0.734375q-0.421875 -0.5 -0.640625 -1.203125q-0.203125 -0.703125 -0.203125 -1.59375q0 -0.78125 0.21875 -1.46875q0.21875 -0.703125 0.640625 -1.21875q0.4375 -0.53125 1.0625 -0.828125q0.625 -0.3125 1.421875 -0.3125q0.765625 0 1.359375 0.25q0.59375 0.234375 1.0 0.6875q0.40625 0.4375 0.609375 1.078125q0.21875 0.625 0.21875 1.40625zm-1.328125 -0.1875q0.015625 -0.484375 -0.109375 -0.890625q-0.109375 -0.40625 -0.359375 -0.703125q-0.234375 -0.296875 -0.609375 -0.453125q-0.359375 -0.171875 -0.84375 -0.171875q-0.421875 0 -0.765625 0.171875q-0.34375 0.15625 -0.59375 0.453125q-0.25 0.28125 -0.40625 0.703125q-0.140625 0.40625 -0.1875 0.890625l3.875 0zm9.4487 0.4375q0 0.984375 -0.28125 1.71875q-0.265625 0.734375 -0.75 1.21875q-0.484375 0.484375 -1.140625 0.734375q-0.65625 0.234375 -1.421875 0.234375q-0.359375 0 -0.703125 -0.046875q-0.34375 -0.03125 -0.703125 -0.125l0 3.078125l-1.28125 0l0 -10.359375l1.140625 0l0.078125 1.234375q0.546875 -0.75 1.171875 -1.046875q0.625 -0.3125 1.34375 -0.3125q0.625 0 1.09375 0.265625q0.484375 0.265625 0.8125 0.75q0.328125 0.46875 0.484375 1.15625q0.15625 0.671875 0.15625 1.5zm-1.296875 0.0625q0 -0.578125 -0.09375 -1.0625q-0.078125 -0.484375 -0.265625 -0.828125q-0.171875 -0.34375 -0.46875 -0.53125q-0.28125 -0.203125 -0.671875 -0.203125q-0.25 0 -0.5 0.078125q-0.25 0.078125 -0.515625 0.265625q-0.265625 0.171875 -0.5625 0.46875q-0.296875 0.296875 -0.625 0.734375l0 3.5625q0.34375 0.15625 0.734375 0.25q0.390625 0.078125 0.765625 0.078125q1.03125 0 1.609375 -0.703125q0.59375 -0.703125 0.59375 -2.109375zm9.29245 3.75l-6.3125 0l0 -1.140625l2.46875 -2.46875q0.609375 -0.59375 0.984375 -1.03125q0.390625 -0.4375 0.59375 -0.796875q0.21875 -0.375 0.296875 -0.6875q0.078125 -0.328125 0.078125 -0.703125q0 -0.34375 -0.109375 -0.65625q-0.09375 -0.328125 -0.296875 -0.5625q-0.1875 -0.25 -0.5 -0.390625q-0.3125 -0.140625 -0.75 -0.140625q-0.609375 0 -1.109375 0.28125q-0.5 0.265625 -0.921875 0.6875l-0.703125 -0.828125q0.546875 -0.578125 1.25 -0.921875q0.703125 -0.34375 1.640625 -0.34375q0.640625 0 1.15625 0.1875q0.53125 0.1875 0.90625 0.546875q0.390625 0.359375 0.59375 0.890625q0.21875 0.515625 0.21875 1.15625q0 0.5625 -0.15625 1.03125q-0.140625 0.46875 -0.4375 0.9375q-0.296875 0.453125 -0.75 0.953125q-0.453125 0.5 -1.0625 1.09375l-1.734375 1.6875l4.65625 0l0 1.21875z" fill-rule="nonzero"></path><path fill="#000000" d="m355.41818 343.88052l-0.03125 -0.984375q-0.59375 0.59375 -1.21875 0.859375q-0.609375 0.25 -1.296875 0.25q-0.625 0 -1.078125 -0.15625q-0.4375 -0.15625 -0.734375 -0.4375q-0.28125 -0.28125 -0.421875 -0.65625q-0.140625 -0.390625 -0.140625 -0.84375q0 -1.09375 0.828125 -1.71875q0.828125 -0.640625 2.4375 -0.640625l1.515625 0l0 -0.640625q0 -0.65625 -0.421875 -1.046875q-0.40625 -0.390625 -1.265625 -0.390625q-0.625 0 -1.234375 0.140625q-0.59375 0.140625 -1.234375 0.40625l0 -1.15625q0.234375 -0.09375 0.53125 -0.171875q0.296875 -0.078125 0.625 -0.140625q0.328125 -0.078125 0.6875 -0.109375q0.359375 -0.046875 0.734375 -0.046875q0.65625 0 1.1875 0.15625q0.546875 0.140625 0.90625 0.4375q0.375 0.296875 0.5625 0.75q0.203125 0.453125 0.203125 1.078125l0 5.0625l-1.140625 0zm-0.140625 -3.34375l-1.609375 0q-0.484375 0 -0.828125 0.09375q-0.34375 0.09375 -0.5625 0.265625q-0.21875 0.171875 -0.328125 0.421875q-0.09375 0.25 -0.09375 0.5625q0 0.203125 0.0625 0.40625q0.0625 0.1875 0.203125 0.34375q0.15625 0.140625 0.390625 0.234375q0.234375 0.078125 0.5625 0.078125q0.4375 0 1.0 -0.265625q0.578125 -0.265625 1.203125 -0.84375l0 -1.296875zm9.1987 3.078125q-0.5 0.1875 -1.03125 0.28125q-0.515625 0.09375 -1.078125 0.09375q-1.734375 0 -2.6875 -0.9375q-0.9375 -0.953125 -0.9375 -2.78125q0 -0.859375 0.265625 -1.5625q0.28125 -0.71875 0.765625 -1.21875q0.5 -0.515625 1.171875 -0.78125q0.6875 -0.28125 1.5 -0.28125q0.578125 0 1.078125 0.078125q0.5 0.078125 0.953125 0.265625l0 1.21875q-0.484375 -0.25 -0.984375 -0.359375q-0.484375 -0.125 -1.015625 -0.125q-0.484375 0 -0.921875 0.1875q-0.4375 0.1875 -0.765625 0.546875q-0.328125 0.34375 -0.53125 0.859375q-0.1875 0.5 -0.1875 1.140625q0 1.328125 0.640625 2.0q0.65625 0.65625 1.8125 0.65625q0.515625 0 1.0 -0.109375q0.5 -0.125 0.953125 -0.359375l0 1.1875zm8.620575 -3.546875q0 0.921875 -0.25 1.640625q-0.25 0.71875 -0.71875 1.21875q-0.46875 0.5 -1.140625 0.78125q-0.65625 0.265625 -1.484375 0.265625q-0.65625 0 -1.34375 -0.125q-0.671875 -0.125 -1.34375 -0.40625l0 -9.90625l1.28125 0l0 2.84375l-0.0625 1.359375q0.546875 -0.734375 1.171875 -1.03125q0.625 -0.3125 1.34375 -0.3125q0.625 0 1.09375 0.265625q0.484375 0.265625 0.8125 0.75q0.328125 0.46875 0.484375 1.15625q0.15625 0.671875 0.15625 1.5zm-1.296875 0.0625q0 -0.578125 -0.09375 -1.0625q-0.078125 -0.484375 -0.265625 -0.828125q-0.171875 -0.34375 -0.46875 -0.53125q-0.28125 -0.203125 -0.671875 -0.203125q-0.25 0 -0.5 0.078125q-0.25 0.078125 -0.515625 0.265625q-0.265625 0.171875 -0.5625 0.46875q-0.296875 0.296875 -0.625 0.734375l0 3.5625q0.359375 0.15625 0.75 0.25q0.390625 0.078125 0.75 0.078125q0.4375 0 0.828125 -0.140625q0.40625 -0.140625 0.703125 -0.46875q0.3125 -0.328125 0.484375 -0.859375q0.1875 -0.546875 0.1875 -1.34375zm2.6987 0.234375q0 -0.9375 0.25 -1.671875q0.265625 -0.734375 0.734375 -1.234375q0.46875 -0.5 1.125 -0.75q0.671875 -0.265625 1.484375 -0.265625q0.359375 0 0.6875 0.046875q0.34375 0.03125 0.671875 0.125l0 -3.078125l1.28125 0l0 10.34375l-1.140625 0l-0.03125 -1.390625q-0.546875 0.78125 -1.171875 1.15625q-0.609375 0.359375 -1.34375 0.359375q-0.625 0 -1.109375 -0.25q-0.46875 -0.265625 -0.796875 -0.75q-0.3125 -0.484375 -0.484375 -1.15625q-0.15625 -0.671875 -0.15625 -1.484375zm1.3125 -0.09375q0 1.34375 0.390625 2.0q0.390625 0.65625 1.109375 0.65625q0.484375 0 1.015625 -0.4375q0.53125 -0.4375 1.125 -1.28125l0 -3.421875q-0.3125 -0.140625 -0.6875 -0.21875q-0.375 -0.078125 -0.75 -0.078125q-1.046875 0 -1.625 0.671875q-0.578125 0.671875 -0.578125 2.109375zm13.245575 3.609375l-6.0 0l0 -1.1875l2.453125 0l0 -6.984375l-2.296875 1.25l-0.46875 -1.09375l3.046875 -1.59375l1.125 0l0 8.421875l2.140625 0l0 1.1875zm8.151825 -2.40625q0 0.609375 -0.25 1.09375q-0.25 0.46875 -0.703125 0.796875q-0.453125 0.3125 -1.0625 0.484375q-0.59375 0.15625 -1.3125 0.15625q-0.78125 0 -1.375 -0.171875q-0.59375 -0.171875 -1.0 -0.484375q-0.40625 -0.3125 -0.609375 -0.75q-0.203125 -0.4375 -0.203125 -0.953125q0 -0.875 0.484375 -1.515625q0.5 -0.640625 1.515625 -1.15625q-0.9375 -0.484375 -1.375 -1.0625q-0.421875 -0.578125 -0.421875 -1.328125q0 -0.46875 0.1875 -0.890625q0.1875 -0.4375 0.578125 -0.765625q0.390625 -0.34375 0.96875 -0.546875q0.578125 -0.203125 1.359375 -0.203125q0.734375 0 1.296875 0.15625q0.5625 0.15625 0.9375 0.453125q0.390625 0.296875 0.578125 0.71875q0.1875 0.40625 0.1875 0.921875q0 0.828125 -0.46875 1.421875q-0.46875 0.578125 -1.3125 1.015625q0.421875 0.21875 0.78125 0.484375q0.375 0.25 0.640625 0.5625q0.265625 0.3125 0.421875 0.703125q0.15625 0.390625 0.15625 0.859375zm-1.53125 -4.953125q0 -0.640625 -0.453125 -0.953125q-0.453125 -0.328125 -1.28125 -0.328125q-0.828125 0 -1.28125 0.3125q-0.453125 0.3125 -0.453125 0.9375q0 0.296875 0.109375 0.546875q0.109375 0.234375 0.328125 0.453125q0.234375 0.203125 0.578125 0.40625q0.34375 0.203125 0.828125 0.421875q0.828125 -0.390625 1.21875 -0.8125q0.40625 -0.421875 0.40625 -0.984375zm0.140625 5.046875q0 -0.265625 -0.09375 -0.515625q-0.078125 -0.265625 -0.3125 -0.515625q-0.21875 -0.25 -0.609375 -0.5q-0.375 -0.25 -0.96875 -0.5q-0.5 0.234375 -0.84375 0.46875q-0.328125 0.234375 -0.546875 0.484375q-0.203125 0.25 -0.296875 0.515625q-0.078125 0.265625 -0.078125 0.546875q0 0.328125 0.140625 0.59375q0.140625 0.25 0.390625 0.421875q0.25 0.171875 0.59375 0.265625q0.34375 0.09375 0.75 0.09375q0.390625 0 0.734375 -0.078125q0.34375 -0.09375 0.59375 -0.25q0.25 -0.171875 0.390625 -0.421875q0.15625 -0.25 0.15625 -0.609375zm2.85495 -1.203125q0 -0.9375 0.25 -1.671875q0.265625 -0.734375 0.734375 -1.234375q0.46875 -0.5 1.125 -0.75q0.671875 -0.265625 1.484375 -0.265625q0.359375 0 0.6875 0.046875q0.34375 0.03125 0.671875 0.125l0 -3.078125l1.28125 0l0 10.34375l-1.140625 0l-0.03125 -1.390625q-0.546875 0.78125 -1.171875 1.15625q-0.609375 0.359375 -1.34375 0.359375q-0.625 0 -1.109375 -0.25q-0.46875 -0.265625 -0.796875 -0.75q-0.3125 -0.484375 -0.484375 -1.15625q-0.15625 -0.671875 -0.15625 -1.484375zm1.3125 -0.09375q0 1.34375 0.390625 2.0q0.390625 0.65625 1.109375 0.65625q0.484375 0 1.015625 -0.4375q0.53125 -0.4375 1.125 -1.28125l0 -3.421875q-0.3125 -0.140625 -0.6875 -0.21875q-0.375 -0.078125 -0.75 -0.078125q-1.046875 0 -1.625 0.671875q-0.578125 0.671875 -0.578125 2.109375zm13.401825 -0.203125q0 0.921875 -0.25 1.640625q-0.25 0.71875 -0.71875 1.21875q-0.46875 0.5 -1.140625 0.78125q-0.65625 0.265625 -1.484375 0.265625q-0.65625 0 -1.34375 -0.125q-0.671875 -0.125 -1.34375 -0.40625l0 -9.90625l1.28125 0l0 2.84375l-0.0625 1.359375q0.546875 -0.734375 1.171875 -1.03125q0.625 -0.3125 1.34375 -0.3125q0.625 0 1.09375 0.265625q0.484375 0.265625 0.8125 0.75q0.328125 0.46875 0.484375 1.15625q0.15625 0.671875 0.15625 1.5zm-1.296875 0.0625q0 -0.578125 -0.09375 -1.0625q-0.078125 -0.484375 -0.265625 -0.828125q-0.171875 -0.34375 -0.46875 -0.53125q-0.28125 -0.203125 -0.671875 -0.203125q-0.25 0 -0.5 0.078125q-0.25 0.078125 -0.515625 0.265625q-0.265625 0.171875 -0.5625 0.46875q-0.296875 0.296875 -0.625 0.734375l0 3.5625q0.359375 0.15625 0.75 0.25q0.390625 0.078125 0.75 0.078125q0.4375 0 0.828125 -0.140625q0.40625 -0.140625 0.703125 -0.46875q0.3125 -0.328125 0.484375 -0.859375q0.1875 -0.546875 0.1875 -1.34375zm9.7612 1.640625l-1.640625 0l0 2.109375l-1.296875 0l0 -2.109375l-4.609375 0l0 -1.125l4.078125 -6.34375l1.828125 0l0 6.34375l1.640625 0l0 1.125zm-2.9375 -6.234375l-3.328125 5.109375l3.328125 0l0 -5.109375zm10.026825 8.078125q-0.5 0.1875 -1.03125 0.28125q-0.515625 0.09375 -1.078125 0.09375q-1.734375 0 -2.6875 -0.9375q-0.9375 -0.953125 -0.9375 -2.78125q0 -0.859375 0.265625 -1.5625q0.28125 -0.71875 0.765625 -1.21875q0.5 -0.515625 1.171875 -0.78125q0.6875 -0.28125 1.5 -0.28125q0.578125 0 1.078125 0.078125q0.5 0.078125 0.953125 0.265625l0 1.21875q-0.484375 -0.25 -0.984375 -0.359375q-0.484375 -0.125 -1.015625 -0.125q-0.484375 0 -0.921875 0.1875q-0.4375 0.1875 -0.765625 0.546875q-0.328125 0.34375 -0.53125 0.859375q-0.1875 0.5 -0.1875 1.140625q0 1.328125 0.640625 2.0q0.65625 0.65625 1.8125 0.65625q0.515625 0 1.0 -0.109375q0.5 -0.125 0.953125 -0.359375l0 1.1875zm8.058075 0q-0.5 0.1875 -1.03125 0.28125q-0.515625 0.09375 -1.078125 0.09375q-1.734375 0 -2.6875 -0.9375q-0.9375 -0.953125 -0.9375 -2.78125q0 -0.859375 0.265625 -1.5625q0.28125 -0.71875 0.765625 -1.21875q0.5 -0.515625 1.171875 -0.78125q0.6875 -0.28125 1.5 -0.28125q0.578125 0 1.078125 0.078125q0.5 0.078125 0.953125 0.265625l0 1.21875q-0.484375 -0.25 -0.984375 -0.359375q-0.484375 -0.125 -1.015625 -0.125q-0.484375 0 -0.921875 0.1875q-0.4375 0.1875 -0.765625 0.546875q-0.328125 0.34375 -0.53125 0.859375q-0.1875 0.5 -0.1875 1.140625q0 1.328125 0.640625 2.0q0.65625 0.65625 1.8125 0.65625q0.515625 0 1.0 -0.109375q0.5 -0.125 0.953125 -0.359375l0 1.1875zm8.558075 0.265625l-6.3125 0l0 -1.140625l2.46875 -2.46875q0.609375 -0.59375 0.984375 -1.03125q0.390625 -0.4375 0.59375 -0.796875q0.21875 -0.375 0.296875 -0.6875q0.078125 -0.328125 0.078125 -0.703125q0 -0.34375 -0.109375 -0.65625q-0.09375 -0.328125 -0.296875 -0.5625q-0.1875 -0.25 -0.5 -0.390625q-0.3125 -0.140625 -0.75 -0.140625q-0.609375 0 -1.109375 0.28125q-0.5 0.265625 -0.921875 0.6875l-0.703125 -0.828125q0.546875 -0.578125 1.25 -0.921875q0.703125 -0.34375 1.640625 -0.34375q0.640625 0 1.15625 0.1875q0.53125 0.1875 0.90625 0.546875q0.390625 0.359375 0.59375 0.890625q0.21875 0.515625 0.21875 1.15625q0 0.5625 -0.15625 1.03125q-0.140625 0.46875 -0.4375 0.9375q-0.296875 0.453125 -0.75 0.953125q-0.453125 0.5 -1.0625 1.09375l-1.734375 1.6875l4.65625 0l0 1.21875zm8.370575 -9.15625q-1.015625 -0.21875 -1.734375 -0.21875q-1.71875 0 -1.71875 1.796875l0 1.296875l3.21875 0l0 1.0625l-3.21875 0l0 5.21875l-1.296875 0l0 -5.21875l-2.359375 0l0 -1.0625l2.359375 0l0 -1.21875q0 -2.9375 3.0625 -2.9375q0.765625 0 1.6875 0.171875l0 1.109375zm-7.703125 1.796875l0 0zm15.4487 4.953125q0 0.609375 -0.25 1.09375q-0.25 0.46875 -0.703125 0.796875q-0.453125 0.3125 -1.0625 0.484375q-0.59375 0.15625 -1.3125 0.15625q-0.78125 0 -1.375 -0.171875q-0.59375 -0.171875 -1.0 -0.484375q-0.40625 -0.3125 -0.609375 -0.75q-0.203125 -0.4375 -0.203125 -0.953125q0 -0.875 0.484375 -1.515625q0.5 -0.640625 1.515625 -1.15625q-0.9375 -0.484375 -1.375 -1.0625q-0.421875 -0.578125 -0.421875 -1.328125q0 -0.46875 0.1875 -0.890625q0.1875 -0.4375 0.578125 -0.765625q0.390625 -0.34375 0.96875 -0.546875q0.578125 -0.203125 1.359375 -0.203125q0.734375 0 1.296875 0.15625q0.5625 0.15625 0.9375 0.453125q0.390625 0.296875 0.578125 0.71875q0.1875 0.40625 0.1875 0.921875q0 0.828125 -0.46875 1.421875q-0.46875 0.578125 -1.3125 1.015625q0.421875 0.21875 0.78125 0.484375q0.375 0.25 0.640625 0.5625q0.265625 0.3125 0.421875 0.703125q0.15625 0.390625 0.15625 0.859375zm-1.53125 -4.953125q0 -0.640625 -0.453125 -0.953125q-0.453125 -0.328125 -1.28125 -0.328125q-0.828125 0 -1.28125 0.3125q-0.453125 0.3125 -0.453125 0.9375q0 0.296875 0.109375 0.546875q0.109375 0.234375 0.328125 0.453125q0.234375 0.203125 0.578125 0.40625q0.34375 0.203125 0.828125 0.421875q0.828125 -0.390625 1.21875 -0.8125q0.40625 -0.421875 0.40625 -0.984375zm0.140625 5.046875q0 -0.265625 -0.09375 -0.515625q-0.078125 -0.265625 -0.3125 -0.515625q-0.21875 -0.25 -0.609375 -0.5q-0.375 -0.25 -0.96875 -0.5q-0.5 0.234375 -0.84375 0.46875q-0.328125 0.234375 -0.546875 0.484375q-0.203125 0.25 -0.296875 0.515625q-0.078125 0.265625 -0.078125 0.546875q0 0.328125 0.140625 0.59375q0.140625 0.25 0.390625 0.421875q0.25 0.171875 0.59375 0.265625q0.34375 0.09375 0.75 0.09375q0.390625 0 0.734375 -0.078125q0.34375 -0.09375 0.59375 -0.25q0.25 -0.171875 0.390625 -0.421875q0.15625 -0.25 0.15625 -0.609375zm9.1987 -0.75q0 0.71875 -0.3125 1.3125q-0.296875 0.578125 -0.84375 1.0q-0.53125 0.40625 -1.265625 0.640625q-0.734375 0.234375 -1.578125 0.234375q-0.21875 0 -0.46875 -0.015625q-0.234375 0 -0.484375 -0.015625q-0.234375 -0.015625 -0.46875 -0.046875q-0.234375 -0.015625 -0.421875 -0.046875l0 -1.15625q0.40625 0.09375 0.90625 0.140625q0.515625 0.046875 1.03125 0.046875q0.59375 0 1.0625 -0.140625q0.46875 -0.140625 0.796875 -0.390625q0.328125 -0.265625 0.5 -0.640625q0.171875 -0.375 0.171875 -0.828125q0 -0.90625 -0.640625 -1.3125q-0.640625 -0.40625 -1.84375 -0.40625l-1.8125 0l0 -4.890625l5.15625 0l0 1.125l-3.953125 0l0 2.6875l0.84375 0q0.6875 0 1.328125 0.125q0.65625 0.125 1.15625 0.4375q0.515625 0.296875 0.828125 0.828125q0.3125 0.515625 0.3125 1.3125zm7.808075 2.796875q-0.5 0.1875 -1.03125 0.28125q-0.515625 0.09375 -1.078125 0.09375q-1.734375 0 -2.6875 -0.9375q-0.9375 -0.953125 -0.9375 -2.78125q0 -0.859375 0.265625 -1.5625q0.28125 -0.71875 0.765625 -1.21875q0.5 -0.515625 1.171875 -0.78125q0.6875 -0.28125 1.5 -0.28125q0.578125 0 1.078125 0.078125q0.5 0.078125 0.953125 0.265625l0 1.21875q-0.484375 -0.25 -0.984375 -0.359375q-0.484375 -0.125 -1.015625 -0.125q-0.484375 0 -0.921875 0.1875q-0.4375 0.1875 -0.765625 0.546875q-0.328125 0.34375 -0.53125 0.859375q-0.1875 0.5 -0.1875 1.140625q0 1.328125 0.640625 2.0q0.65625 0.65625 1.8125 0.65625q0.515625 0 1.0 -0.109375q0.5 -0.125 0.953125 -0.359375l0 1.1875zm8.558075 -3.796875q0 0.265625 -0.015625 0.453125q0 0.1875 -0.015625 0.34375l-5.171875 0q0 1.125 0.625 1.734375q0.640625 0.59375 1.828125 0.59375q0.3125 0 0.640625 -0.015625q0.328125 -0.03125 0.625 -0.0625q0.296875 -0.046875 0.5625 -0.09375q0.28125 -0.0625 0.515625 -0.140625l0 1.046875q-0.515625 0.15625 -1.171875 0.234375q-0.65625 0.09375 -1.359375 0.09375q-0.9375 0 -1.625 -0.25q-0.6875 -0.25 -1.125 -0.734375q-0.421875 -0.5 -0.640625 -1.203125q-0.203125 -0.703125 -0.203125 -1.59375q0 -0.78125 0.21875 -1.46875q0.21875 -0.703125 0.640625 -1.21875q0.4375 -0.53125 1.0625 -0.828125q0.625 -0.3125 1.421875 -0.3125q0.765625 0 1.359375 0.25q0.59375 0.234375 1.0 0.6875q0.40625 0.4375 0.609375 1.078125q0.21875 0.625 0.21875 1.40625zm-1.328125 -0.1875q0.015625 -0.484375 -0.109375 -0.890625q-0.109375 -0.40625 -0.359375 -0.703125q-0.234375 -0.296875 -0.609375 -0.453125q-0.359375 -0.171875 -0.84375 -0.171875q-0.421875 0 -0.765625 0.171875q-0.34375 0.15625 -0.59375 0.453125q-0.25 0.28125 -0.40625 0.703125q-0.140625 0.40625 -0.1875 0.890625l3.875 0zm2.79245 0.734375q0 -0.9375 0.25 -1.671875q0.265625 -0.734375 0.734375 -1.234375q0.46875 -0.5 1.125 -0.75q0.671875 -0.265625 1.484375 -0.265625q0.359375 0 0.6875 0.046875q0.34375 0.03125 0.671875 0.125l0 -3.078125l1.28125 0l0 10.34375l-1.140625 0l-0.03125 -1.390625q-0.546875 0.78125 -1.171875 1.15625q-0.609375 0.359375 -1.34375 0.359375q-0.625 0 -1.109375 -0.25q-0.46875 -0.265625 -0.796875 -0.75q-0.3125 -0.484375 -0.484375 -1.15625q-0.15625 -0.671875 -0.15625 -1.484375zm1.3125 -0.09375q0 1.34375 0.390625 2.0q0.390625 0.65625 1.109375 0.65625q0.484375 0 1.015625 -0.4375q0.53125 -0.4375 1.125 -1.28125l0 -3.421875q-0.3125 -0.140625 -0.6875 -0.21875q-0.375 -0.078125 -0.75 -0.078125q-1.046875 0 -1.625 0.671875q-0.578125 0.671875 -0.578125 2.109375zm13.339325 -0.453125q0 0.265625 -0.015625 0.453125q0 0.1875 -0.015625 0.34375l-5.171875 0q0 1.125 0.625 1.734375q0.640625 0.59375 1.828125 0.59375q0.3125 0 0.640625 -0.015625q0.328125 -0.03125 0.625 -0.0625q0.296875 -0.046875 0.5625 -0.09375q0.28125 -0.0625 0.515625 -0.140625l0 1.046875q-0.515625 0.15625 -1.171875 0.234375q-0.65625 0.09375 -1.359375 0.09375q-0.9375 0 -1.625 -0.25q-0.6875 -0.25 -1.125 -0.734375q-0.421875 -0.5 -0.640625 -1.203125q-0.203125 -0.703125 -0.203125 -1.59375q0 -0.78125 0.21875 -1.46875q0.21875 -0.703125 0.640625 -1.21875q0.4375 -0.53125 1.0625 -0.828125q0.625 -0.3125 1.421875 -0.3125q0.765625 0 1.359375 0.25q0.59375 0.234375 1.0 0.6875q0.40625 0.4375 0.609375 1.078125q0.21875 0.625 0.21875 1.40625zm-1.328125 -0.1875q0.015625 -0.484375 -0.109375 -0.890625q-0.109375 -0.40625 -0.359375 -0.703125q-0.234375 -0.296875 -0.609375 -0.453125q-0.359375 -0.171875 -0.84375 -0.171875q-0.421875 0 -0.765625 0.171875q-0.34375 0.15625 -0.59375 0.453125q-0.25 0.28125 -0.40625 0.703125q-0.140625 0.40625 -0.1875 0.890625l3.875 0zm9.6987 -4.90625q-1.015625 -0.21875 -1.734375 -0.21875q-1.71875 0 -1.71875 1.796875l0 1.296875l3.21875 0l0 1.0625l-3.21875 0l0 5.21875l-1.296875 0l0 -5.21875l-2.359375 0l0 -1.0625l2.359375 0l0 -1.21875q0 -2.9375 3.0625 -2.9375q0.765625 0 1.6875 0.171875l0 1.109375zm-7.703125 1.796875l0 0zm15.558105 4.328125q0 0.65625 -0.234375 1.234375q-0.234375 0.578125 -0.6875 1.015625q-0.4375 0.421875 -1.0625 0.671875q-0.609375 0.234375 -1.359375 0.234375q-0.796875 0 -1.40625 -0.25q-0.609375 -0.25 -1.015625 -0.765625q-0.40625 -0.53125 -0.6250305 -1.328125q-0.203125 -0.796875 -0.203125 -1.890625q0 -0.734375 0.09375 -1.421875q0.09375 -0.6875 0.31253052 -1.296875q0.21875 -0.609375 0.578125 -1.109375q0.375 -0.5 0.921875 -0.859375q0.546875 -0.375 1.28125 -0.578125q0.734375 -0.203125 1.71875 -0.203125l0.9375 0l0 1.125l-1.015625 0q-0.859375 0 -1.5 0.203125q-0.625 0.203125 -1.046875 0.578125q-0.421875 0.375 -0.65625 0.90625q-0.21875 0.515625 -0.28125 1.171875l-0.03125 0.296875q0.46875 -0.265625 1.0625 -0.421875q0.609375 -0.171875 1.3125 -0.171875q0.71875 0 1.265625 0.21875q0.546875 0.203125 0.90625 0.578125q0.375 0.375 0.546875 0.90625q0.1875 0.53125 0.1875 1.15625zm-1.328125 0.078125q0 -0.4375 -0.109375 -0.796875q-0.109375 -0.359375 -0.34375 -0.59375q-0.21875 -0.25 -0.5625 -0.375q-0.34375 -0.140625 -0.828125 -0.140625q-0.28125 0 -0.578125 0.046875q-0.28125 0.046875 -0.5625 0.140625q-0.265625 0.09375 -0.515625 0.21875q-0.25 0.109375 -0.453125 0.234375q0 0.953125 0.125 1.59375q0.140625 0.625 0.390625 1.015625q0.265625 0.375 0.640625 0.53125q0.390625 0.15625 0.890625 0.15625q0.421875 0 0.765625 -0.125q0.34375 -0.140625 0.59375 -0.40625q0.25 -0.265625 0.390625 -0.640625q0.15625 -0.375 0.15625 -0.859375zm9.026794 -0.109375q0 0.71875 -0.3125 1.3125q-0.296875 0.578125 -0.84375 1.0q-0.53125 0.40625 -1.265625 0.640625q-0.734375 0.234375 -1.578125 0.234375q-0.21875 0 -0.46875 -0.015625q-0.234375 0 -0.484375 -0.015625q-0.234375 -0.015625 -0.46875 -0.046875q-0.234375 -0.015625 -0.421875 -0.046875l0 -1.15625q0.40625 0.09375 0.90625 0.140625q0.515625 0.046875 1.03125 0.046875q0.59375 0 1.0625 -0.140625q0.46875 -0.140625 0.796875 -0.390625q0.328125 -0.265625 0.5 -0.640625q0.171875 -0.375 0.171875 -0.828125q0 -0.90625 -0.640625 -1.3125q-0.640625 -0.40625 -1.84375 -0.40625l-1.8125 0l0 -4.890625l5.15625 0l0 1.125l-3.953125 0l0 2.6875l0.84375 0q0.6875 0 1.328125 0.125q0.65625 0.125 1.15625 0.4375q0.515625 0.296875 0.828125 0.828125q0.3125 0.515625 0.3125 1.3125zm8.776855 0.953125l-1.640625 0l0 2.109375l-1.296875 0l0 -2.109375l-4.609375 0l0 -1.125l4.078125 -6.34375l1.828125 0l0 6.34375l1.640625 0l0 1.125zm-2.9375 -6.234375l-3.328125 5.109375l3.328125 0l0 -5.109375zm10.839294 -0.8125q-1.015625 -0.21875 -1.734375 -0.21875q-1.71875 0 -1.71875 1.796875l0 1.296875l3.21875 0l0 1.0625l-3.21875 0l0 5.21875l-1.296875 0l0 -5.21875l-2.359375 0l0 -1.0625l2.359375 0l0 -1.21875q0 -2.9375 3.0625 -2.9375q0.765625 0 1.6875 0.171875l0 1.109375zm-7.703125 1.796875l0 0zm14.94873 7.09375q-0.5 0.1875 -1.03125 0.28125q-0.515625 0.09375 -1.078125 0.09375q-1.734375 0 -2.6875 -0.9375q-0.9375 -0.953125 -0.9375 -2.78125q0 -0.859375 0.265625 -1.5625q0.28125 -0.71875 0.765625 -1.21875q0.5 -0.515625 1.171875 -0.78125q0.6875 -0.28125 1.5 -0.28125q0.578125 0 1.078125 0.078125q0.5 0.078125 0.953125 0.265625l0 1.21875q-0.484375 -0.25 -0.984375 -0.359375q-0.484375 -0.125 -1.015625 -0.125q-0.484375 0 -0.921875 0.1875q-0.4375 0.1875 -0.765625 0.546875q-0.328125 0.34375 -0.53125 0.859375q-0.1875 0.5 -0.1875 1.140625q0 1.328125 0.640625 2.0q0.65625 0.65625 1.8125 0.65625q0.515625 0 1.0 -0.109375q0.5 -0.125 0.953125 -0.359375l0 1.1875zm8.058044 0q-0.5 0.1875 -1.03125 0.28125q-0.515625 0.09375 -1.078125 0.09375q-1.734375 0 -2.6875 -0.9375q-0.9375 -0.953125 -0.9375 -2.78125q0 -0.859375 0.265625 -1.5625q0.28125 -0.71875 0.765625 -1.21875q0.5 -0.515625 1.171875 -0.78125q0.6875 -0.28125 1.5 -0.28125q0.578125 0 1.078125 0.078125q0.5 0.078125 0.953125 0.265625l0 1.21875q-0.484375 -0.25 -0.984375 -0.359375q-0.484375 -0.125 -1.015625 -0.125q-0.484375 0 -0.921875 0.1875q-0.4375 0.1875 -0.765625 0.546875q-0.328125 0.34375 -0.53125 0.859375q-0.1875 0.5 -0.1875 1.140625q0 1.328125 0.640625 2.0q0.65625 0.65625 1.8125 0.65625q0.515625 0 1.0 -0.109375q0.5 -0.125 0.953125 -0.359375l0 1.1875zm8.058105 0q-0.5 0.1875 -1.03125 0.28125q-0.515625 0.09375 -1.078125 0.09375q-1.734375 0 -2.6875 -0.9375q-0.9375 -0.953125 -0.9375 -2.78125q0 -0.859375 0.265625 -1.5625q0.28125 -0.71875 0.765625 -1.21875q0.5 -0.515625 1.171875 -0.78125q0.6875 -0.28125 1.5 -0.28125q0.578125 0 1.078125 0.078125q0.5 0.078125 0.953125 0.265625l0 1.21875q-0.484375 -0.25 -0.984375 -0.359375q-0.484375 -0.125 -1.015625 -0.125q-0.484375 0 -0.921875 0.1875q-0.4375 0.1875 -0.765625 0.546875q-0.328125 0.34375 -0.53125 0.859375q-0.1875 0.5 -0.1875 1.140625q0 1.328125 0.640625 2.0q0.65625 0.65625 1.8125 0.65625q0.515625 0 1.0 -0.109375q0.5 -0.125 0.953125 -0.359375l0 1.1875zm9.026794 -1.84375l-1.640625 0l0 2.109375l-1.296875 0l0 -2.109375l-4.609375 0l0 -1.125l4.078125 -6.34375l1.828125 0l0 6.34375l1.640625 0l0 1.125zm-2.9375 -6.234375l-3.328125 5.109375l3.328125 0l0 -5.109375zm9.026855 8.34375l-0.03125 -0.984375q-0.59375 0.59375 -1.21875 0.859375q-0.609375 0.25 -1.296875 0.25q-0.625 0 -1.078125 -0.15625q-0.4375 -0.15625 -0.734375 -0.4375q-0.28125 -0.28125 -0.421875 -0.65625q-0.140625 -0.390625 -0.140625 -0.84375q0 -1.09375 0.828125 -1.71875q0.828125 -0.640625 2.4375 -0.640625l1.515625 0l0 -0.640625q0 -0.65625 -0.421875 -1.046875q-0.40625 -0.390625 -1.265625 -0.390625q-0.625 0 -1.234375 0.140625q-0.59375 0.140625 -1.234375 0.40625l0 -1.15625q0.234375 -0.09375 0.53125 -0.171875q0.296875 -0.078125 0.625 -0.140625q0.328125 -0.078125 0.6875 -0.109375q0.359375 -0.046875 0.734375 -0.046875q0.65625 0 1.1875 0.15625q0.546875 0.140625 0.90625 0.4375q0.375 0.296875 0.5625 0.75q0.203125 0.453125 0.203125 1.078125l0 5.0625l-1.140625 0zm-0.140625 -3.34375l-1.609375 0q-0.484375 0 -0.828125 0.09375q-0.34375 0.09375 -0.5625 0.265625q-0.21875 0.171875 -0.328125 0.421875q-0.09375 0.25 -0.09375 0.5625q0 0.203125 0.0625 0.40625q0.0625 0.1875 0.203125 0.34375q0.15625 0.140625 0.390625 0.234375q0.234375 0.078125 0.5625 0.078125q0.4375 0 1.0 -0.265625q0.578125 -0.265625 1.203125 -0.84375l0 -1.296875zm10.167419 1.234375l-1.640625 0l0 2.109375l-1.296875 0l0 -2.109375l-4.609375 0l0 -1.125l4.078125 -6.34375l1.828125 0l0 6.34375l1.640625 0l0 1.125zm-2.9375 -6.234375l-3.328125 5.109375l3.328125 0l0 -5.109375zm3.9331055 4.828125q0 -0.9375 0.25 -1.671875q0.265625 -0.734375 0.734375 -1.234375q0.46875 -0.5 1.125 -0.75q0.671875 -0.265625 1.484375 -0.265625q0.359375 0 0.6875 0.046875q0.34375 0.03125 0.671875 0.125l0 -3.078125l1.28125 0l0 10.34375l-1.140625 0l-0.03125 -1.390625q-0.546875 0.78125 -1.171875 1.15625q-0.609375 0.359375 -1.34375 0.359375q-0.625 0 -1.109375 -0.25q-0.46875 -0.265625 -0.796875 -0.75q-0.3125 -0.484375 -0.484375 -1.15625q-0.15625 -0.671875 -0.15625 -1.484375zm1.3125 -0.09375q0 1.34375 0.390625 2.0q0.390625 0.65625 1.109375 0.65625q0.484375 0 1.015625 -0.4375q0.53125 -0.4375 1.125 -1.28125l0 -3.421875q-0.3125 -0.140625 -0.6875 -0.21875q-0.375 -0.078125 -0.75 -0.078125q-1.046875 0 -1.625 0.671875q-0.578125 0.671875 -0.578125 2.109375zm13.339294 1.203125q0 0.609375 -0.25 1.09375q-0.25 0.46875 -0.703125 0.796875q-0.453125 0.3125 -1.0625 0.484375q-0.59375 0.15625 -1.3125 0.15625q-0.78125 0 -1.375 -0.171875q-0.59375 -0.171875 -1.0 -0.484375q-0.40625 -0.3125 -0.609375 -0.75q-0.203125 -0.4375 -0.203125 -0.953125q0 -0.875 0.484375 -1.515625q0.5 -0.640625 1.515625 -1.15625q-0.9375 -0.484375 -1.375 -1.0625q-0.421875 -0.578125 -0.421875 -1.328125q0 -0.46875 0.1875 -0.890625q0.1875 -0.4375 0.578125 -0.765625q0.390625 -0.34375 0.96875 -0.546875q0.578125 -0.203125 1.359375 -0.203125q0.734375 0 1.296875 0.15625q0.5625 0.15625 0.9375 0.453125q0.390625 0.296875 0.578125 0.71875q0.1875 0.40625 0.1875 0.921875q0 0.828125 -0.46875 1.421875q-0.46875 0.578125 -1.3125 1.015625q0.421875 0.21875 0.78125 0.484375q0.375 0.25 0.640625 0.5625q0.265625 0.3125 0.421875 0.703125q0.15625 0.390625 0.15625 0.859375zm-1.53125 -4.953125q0 -0.640625 -0.453125 -0.953125q-0.453125 -0.328125 -1.28125 -0.328125q-0.828125 0 -1.28125 0.3125q-0.453125 0.3125 -0.453125 0.9375q0 0.296875 0.109375 0.546875q0.109375 0.234375 0.328125 0.453125q0.234375 0.203125 0.578125 0.40625q0.34375 0.203125 0.828125 0.421875q0.828125 -0.390625 1.21875 -0.8125q0.40625 -0.421875 0.40625 -0.984375zm0.140625 5.046875q0 -0.265625 -0.09375 -0.515625q-0.078125 -0.265625 -0.3125 -0.515625q-0.21875 -0.25 -0.609375 -0.5q-0.375 -0.25 -0.96875 -0.5q-0.5 0.234375 -0.84375 0.46875q-0.328125 0.234375 -0.546875 0.484375q-0.203125 0.25 -0.296875 0.515625q-0.078125 0.265625 -0.078125 0.546875q0 0.328125 0.140625 0.59375q0.140625 0.25 0.390625 0.421875q0.25 0.171875 0.59375 0.265625q0.34375 0.09375 0.75 0.09375q0.390625 0 0.734375 -0.078125q0.34375 -0.09375 0.59375 -0.25q0.25 -0.171875 0.390625 -0.421875q0.15625 -0.25 0.15625 -0.609375z" fill-rule="nonzero"></path><path fill="#ffff00" d="m645.3884 332.9957l257.8584 0l0 17.16098l-257.8584 0l0 -17.16098z" fill-rule="nonzero"></path><path fill="#000000" d="m652.77905 341.47427q0 0.609375 -0.25 1.09375q-0.25 0.46875 -0.703125 0.796875q-0.453125 0.3125 -1.0625 0.484375q-0.59375 0.15625 -1.3125 0.15625q-0.78125 0 -1.375 -0.171875q-0.59375 -0.171875 -1.0 -0.484375q-0.40625 -0.3125 -0.609375 -0.75q-0.203125 -0.4375 -0.203125 -0.953125q0 -0.875 0.484375 -1.515625q0.5 -0.640625 1.515625 -1.15625q-0.9375 -0.484375 -1.375 -1.0625q-0.421875 -0.578125 -0.421875 -1.328125q0 -0.46875 0.1875 -0.890625q0.1875 -0.4375 0.578125 -0.765625q0.390625 -0.34375 0.96875 -0.546875q0.578125 -0.203125 1.359375 -0.203125q0.734375 0 1.296875 0.15625q0.5625 0.15625 0.9375 0.453125q0.390625 0.296875 0.578125 0.71875q0.1875 0.40625 0.1875 0.921875q0 0.828125 -0.46875 1.421875q-0.46875 0.578125 -1.3125 1.015625q0.421875 0.21875 0.78125 0.484375q0.375 0.25 0.640625 0.5625q0.265625 0.3125 0.421875 0.703125q0.15625 0.390625 0.15625 0.859375zm-1.53125 -4.953125q0 -0.640625 -0.453125 -0.953125q-0.453125 -0.328125 -1.28125 -0.328125q-0.828125 0 -1.28125 0.3125q-0.453125 0.3125 -0.453125 0.9375q0 0.296875 0.109375 0.546875q0.109375 0.234375 0.328125 0.453125q0.234375 0.203125 0.578125 0.40625q0.34375 0.203125 0.828125 0.421875q0.828125 -0.390625 1.21875 -0.8125q0.40625 -0.421875 0.40625 -0.984375zm0.140625 5.046875q0 -0.265625 -0.09375 -0.515625q-0.078125 -0.265625 -0.3125 -0.515625q-0.21875 -0.25 -0.609375 -0.5q-0.375 -0.25 -0.96875 -0.5q-0.5 0.234375 -0.84375 0.46875q-0.328125 0.234375 -0.546875 0.484375q-0.203125 0.25 -0.296875 0.515625q-0.078125 0.265625 -0.078125 0.546875q0 0.328125 0.140625 0.59375q0.140625 0.25 0.390625 0.421875q0.25 0.171875 0.59375 0.265625q0.34375 0.09375 0.75 0.09375q0.390625 0 0.734375 -0.078125q0.34375 -0.09375 0.59375 -0.25q0.25 -0.171875 0.390625 -0.421875q0.15625 -0.25 0.15625 -0.609375zm9.26123 -0.59375q0 0.625 -0.265625 1.1875q-0.25 0.546875 -0.765625 0.96875q-0.515625 0.40625 -1.296875 0.640625q-0.765625 0.234375 -1.796875 0.234375q-0.578125 0 -1.03125 -0.03125q-0.453125 -0.03125 -0.84375 -0.09375l0 -1.140625q0.453125 0.078125 0.953125 0.125q0.515625 0.046875 1.03125 0.046875q0.71875 0 1.21875 -0.125q0.515625 -0.140625 0.84375 -0.375q0.328125 -0.25 0.46875 -0.59375q0.140625 -0.34375 0.140625 -0.765625q0 -0.40625 -0.171875 -0.6875q-0.171875 -0.296875 -0.5 -0.5q-0.3125 -0.203125 -0.765625 -0.296875q-0.4375 -0.09375 -0.953125 -0.09375l-1.09375 0l0 -1.046875l1.109375 0q0.421875 0 0.78125 -0.109375q0.359375 -0.125 0.609375 -0.328125q0.25 -0.21875 0.375 -0.53125q0.140625 -0.3125 0.140625 -0.703125q0 -0.765625 -0.46875 -1.109375q-0.46875 -0.359375 -1.375 -0.359375q-0.484375 0 -1.0 0.09375q-0.5 0.09375 -1.09375 0.28125l0 -1.109375q0.25 -0.09375 0.53125 -0.15625q0.28125 -0.078125 0.5625 -0.125q0.28125 -0.046875 0.5625 -0.0625q0.28125 -0.03125 0.53125 -0.03125q0.765625 0 1.34375 0.171875q0.578125 0.15625 0.96875 0.46875q0.390625 0.296875 0.578125 0.75q0.203125 0.4375 0.203125 0.984375q0 0.8125 -0.421875 1.375q-0.421875 0.5625 -1.15625 0.890625q0.375 0.046875 0.734375 0.234375q0.375 0.171875 0.65625 0.453125q0.296875 0.265625 0.46875 0.640625q0.1875 0.375 0.1875 0.828125zm8.245544 0.5q0 0.609375 -0.25 1.09375q-0.25 0.46875 -0.703125 0.796875q-0.453125 0.3125 -1.0625 0.484375q-0.59375 0.15625 -1.3125 0.15625q-0.78125 0 -1.375 -0.171875q-0.59375 -0.171875 -1.0 -0.484375q-0.40625 -0.3125 -0.609375 -0.75q-0.203125 -0.4375 -0.203125 -0.953125q0 -0.875 0.484375 -1.515625q0.5 -0.640625 1.515625 -1.15625q-0.9375 -0.484375 -1.375 -1.0625q-0.421875 -0.578125 -0.421875 -1.328125q0 -0.46875 0.1875 -0.890625q0.1875 -0.4375 0.578125 -0.765625q0.390625 -0.34375 0.96875 -0.546875q0.578125 -0.203125 1.359375 -0.203125q0.734375 0 1.296875 0.15625q0.5625 0.15625 0.9375 0.453125q0.390625 0.296875 0.578125 0.71875q0.1875 0.40625 0.1875 0.921875q0 0.828125 -0.46875 1.421875q-0.46875 0.578125 -1.3125 1.015625q0.421875 0.21875 0.78125 0.484375q0.375 0.25 0.640625 0.5625q0.265625 0.3125 0.421875 0.703125q0.15625 0.390625 0.15625 0.859375zm-1.53125 -4.953125q0 -0.640625 -0.453125 -0.953125q-0.453125 -0.328125 -1.28125 -0.328125q-0.828125 0 -1.28125 0.3125q-0.453125 0.3125 -0.453125 0.9375q0 0.296875 0.109375 0.546875q0.109375 0.234375 0.328125 0.453125q0.234375 0.203125 0.578125 0.40625q0.34375 0.203125 0.828125 0.421875q0.828125 -0.390625 1.21875 -0.8125q0.40625 -0.421875 0.40625 -0.984375zm0.140625 5.046875q0 -0.265625 -0.09375 -0.515625q-0.078125 -0.265625 -0.3125 -0.515625q-0.21875 -0.25 -0.609375 -0.5q-0.375 -0.25 -0.96875 -0.5q-0.5 0.234375 -0.84375 0.46875q-0.328125 0.234375 -0.546875 0.484375q-0.203125 0.25 -0.296875 0.515625q-0.078125 0.265625 -0.078125 0.546875q0 0.328125 0.140625 0.59375q0.140625 0.25 0.390625 0.421875q0.25 0.171875 0.59375 0.265625q0.34375 0.09375 0.75 0.09375q0.390625 0 0.734375 -0.078125q0.34375 -0.09375 0.59375 -0.25q0.25 -0.171875 0.390625 -0.421875q0.15625 -0.25 0.15625 -0.609375zm9.38623 -3.015625q0 1.40625 -0.34375 2.421875q-0.328125 1.0 -0.984375 1.65625q-0.65625 0.640625 -1.640625 0.953125q-0.96875 0.296875 -2.25 0.296875l-0.796875 0l0 -1.109375l0.890625 0q0.953125 0 1.640625 -0.1875q0.6875 -0.203125 1.140625 -0.5625q0.453125 -0.375 0.6875 -0.90625q0.25 -0.53125 0.3125 -1.21875l0.03125 -0.296875q-0.46875 0.28125 -1.078125 0.453125q-0.59375 0.15625 -1.296875 0.15625q-0.71875 0 -1.265625 -0.21875q-0.546875 -0.21875 -0.921875 -0.59375q-0.359375 -0.375 -0.546875 -0.890625q-0.171875 -0.53125 -0.171875 -1.15625q0 -0.65625 0.234375 -1.234375q0.25 -0.578125 0.6875 -1.0q0.4375 -0.4375 1.03125 -0.6875q0.609375 -0.25 1.34375 -0.25q0.71875 0 1.3125 0.234375q0.609375 0.234375 1.046875 0.765625q0.4375 0.515625 0.6875 1.359375q0.25 0.828125 0.25 2.015625zm-3.34375 -3.328125q-0.421875 0 -0.765625 0.140625q-0.34375 0.125 -0.609375 0.390625q-0.25 0.265625 -0.390625 0.640625q-0.140625 0.375 -0.140625 0.875q0 0.4375 0.09375 0.796875q0.109375 0.34375 0.328125 0.59375q0.234375 0.25 0.578125 0.390625q0.359375 0.125 0.84375 0.125q0.265625 0 0.546875 -0.046875q0.296875 -0.0625 0.5625 -0.140625q0.28125 -0.09375 0.53125 -0.203125q0.25 -0.125 0.453125 -0.265625q0 -0.9375 -0.140625 -1.5625q-0.140625 -0.640625 -0.40625 -1.015625q-0.265625 -0.390625 -0.640625 -0.546875q-0.375 -0.171875 -0.84375 -0.171875zm11.401794 0.265625l-4.015625 8.390625l-1.453125 0l4.171875 -8.390625l-5.171875 0l0 -1.1875l6.46875 0l0 1.1875zm8.026855 8.390625l-6.0 0l0 -1.1875l2.453125 0l0 -6.984375l-2.296875 1.25l-0.46875 -1.09375l3.046875 -1.59375l1.125 0l0 8.421875l2.140625 0l0 1.1875zm7.9017944 -3.0625q0 0.71875 -0.3125 1.3125q-0.296875 0.578125 -0.84375 1.0q-0.53125 0.40625 -1.265625 0.640625q-0.734375 0.234375 -1.578125 0.234375q-0.21875 0 -0.46875 -0.015625q-0.234375 0 -0.484375 -0.015625q-0.234375 -0.015625 -0.46875 -0.046875q-0.234375 -0.015625 -0.421875 -0.046875l0 -1.15625q0.40625 0.09375 0.90625 0.140625q0.515625 0.046875 1.03125 0.046875q0.59375 0 1.0625 -0.140625q0.46875 -0.140625 0.796875 -0.390625q0.328125 -0.265625 0.5 -0.640625q0.171875 -0.375 0.171875 -0.828125q0 -0.90625 -0.640625 -1.3125q-0.640625 -0.40625 -1.84375 -0.40625l-1.8125 0l0 -4.890625l5.15625 0l0 1.125l-3.953125 0l0 2.6875l0.84375 0q0.6875 0 1.328125 0.125q0.65625 0.125 1.15625 0.4375q0.515625 0.296875 0.828125 0.828125q0.3125 0.515625 0.3125 1.3125zm7.8081055 2.796875q-0.5 0.1875 -1.03125 0.28125q-0.515625 0.09375 -1.078125 0.09375q-1.734375 0 -2.6875 -0.9375q-0.9375 -0.953125 -0.9375 -2.78125q0 -0.859375 0.265625 -1.5625q0.28125 -0.71875 0.765625 -1.21875q0.5 -0.515625 1.171875 -0.78125q0.6875 -0.28125 1.5 -0.28125q0.578125 0 1.078125 0.078125q0.5 0.078125 0.953125 0.265625l0 1.21875q-0.484375 -0.25 -0.984375 -0.359375q-0.484375 -0.125 -1.015625 -0.125q-0.484375 0 -0.921875 0.1875q-0.4375 0.1875 -0.765625 0.546875q-0.328125 0.34375 -0.53125 0.859375q-0.1875 0.5 -0.1875 1.140625q0 1.328125 0.640625 2.0q0.65625 0.65625 1.8125 0.65625q0.515625 0 1.0 -0.109375q0.5 -0.125 0.953125 -0.359375l0 1.1875zm8.620544 -3.546875q0 0.921875 -0.25 1.640625q-0.25 0.71875 -0.71875 1.21875q-0.46875 0.5 -1.140625 0.78125q-0.65625 0.265625 -1.484375 0.265625q-0.65625 0 -1.34375 -0.125q-0.671875 -0.125 -1.34375 -0.40625l0 -9.90625l1.28125 0l0 2.84375l-0.0625 1.359375q0.546875 -0.734375 1.171875 -1.03125q0.625 -0.3125 1.34375 -0.3125q0.625 0 1.09375 0.265625q0.484375 0.265625 0.8125 0.75q0.328125 0.46875 0.484375 1.15625q0.15625 0.671875 0.15625 1.5zm-1.296875 0.0625q0 -0.578125 -0.09375 -1.0625q-0.078125 -0.484375 -0.265625 -0.828125q-0.171875 -0.34375 -0.46875 -0.53125q-0.28125 -0.203125 -0.671875 -0.203125q-0.25 0 -0.5 0.078125q-0.25 0.078125 -0.515625 0.265625q-0.265625 0.171875 -0.5625 0.46875q-0.296875 0.296875 -0.625 0.734375l0 3.5625q0.359375 0.15625 0.75 0.25q0.390625 0.078125 0.75 0.078125q0.4375 0 0.828125 -0.140625q0.40625 -0.140625 0.703125 -0.46875q0.3125 -0.328125 0.484375 -0.859375q0.1875 -0.546875 0.1875 -1.34375zm7.7924805 3.75l-0.03125 -0.984375q-0.59375 0.59375 -1.21875 0.859375q-0.609375 0.25 -1.296875 0.25q-0.625 0 -1.078125 -0.15625q-0.4375 -0.15625 -0.734375 -0.4375q-0.28125 -0.28125 -0.421875 -0.65625q-0.140625 -0.390625 -0.140625 -0.84375q0 -1.09375 0.828125 -1.71875q0.828125 -0.640625 2.4375 -0.640625l1.515625 0l0 -0.640625q0 -0.65625 -0.421875 -1.046875q-0.40625 -0.390625 -1.265625 -0.390625q-0.625 0 -1.234375 0.140625q-0.59375 0.140625 -1.234375 0.40625l0 -1.15625q0.234375 -0.09375 0.53125 -0.171875q0.296875 -0.078125 0.625 -0.140625q0.328125 -0.078125 0.6875 -0.109375q0.359375 -0.046875 0.734375 -0.046875q0.65625 0 1.1875 0.15625q0.546875 0.140625 0.90625 0.4375q0.375 0.296875 0.5625 0.75q0.203125 0.453125 0.203125 1.078125l0 5.0625l-1.140625 0zm-0.140625 -3.34375l-1.609375 0q-0.484375 0 -0.828125 0.09375q-0.34375 0.09375 -0.5625 0.265625q-0.21875 0.171875 -0.328125 0.421875q-0.09375 0.25 -0.09375 0.5625q0 0.203125 0.0625 0.40625q0.0625 0.1875 0.203125 0.34375q0.15625 0.140625 0.390625 0.234375q0.234375 0.078125 0.5625 0.078125q0.4375 0 1.0 -0.265625q0.578125 -0.265625 1.203125 -0.84375l0 -1.296875zm9.604919 3.34375l-6.0 0l0 -1.1875l2.453125 0l0 -6.984375l-2.296875 1.25l-0.46875 -1.09375l3.046875 -1.59375l1.125 0l0 8.421875l2.140625 0l0 1.1875zm1.5581055 -3.515625q0 -0.9375 0.25 -1.671875q0.265625 -0.734375 0.734375 -1.234375q0.46875 -0.5 1.125 -0.75q0.671875 -0.265625 1.484375 -0.265625q0.359375 0 0.6875 0.046875q0.34375 0.03125 0.671875 0.125l0 -3.078125l1.28125 0l0 10.34375l-1.140625 0l-0.03125 -1.390625q-0.546875 0.78125 -1.171875 1.15625q-0.609375 0.359375 -1.34375 0.359375q-0.625 0 -1.109375 -0.25q-0.46875 -0.265625 -0.796875 -0.75q-0.3125 -0.484375 -0.484375 -1.15625q-0.15625 -0.671875 -0.15625 -1.484375zm1.3125 -0.09375q0 1.34375 0.390625 2.0q0.390625 0.65625 1.109375 0.65625q0.484375 0 1.015625 -0.4375q0.53125 -0.4375 1.125 -1.28125l0 -3.421875q-0.3125 -0.140625 -0.6875 -0.21875q-0.375 -0.078125 -0.75 -0.078125q-1.046875 0 -1.625 0.671875q-0.578125 0.671875 -0.578125 2.109375zm6.7455444 0.09375q0 -0.9375 0.25 -1.671875q0.265625 -0.734375 0.734375 -1.234375q0.46875 -0.5 1.125 -0.75q0.671875 -0.265625 1.484375 -0.265625q0.359375 0 0.6875 0.046875q0.34375 0.03125 0.671875 0.125l0 -3.078125l1.28125 0l0 10.34375l-1.140625 0l-0.03125 -1.390625q-0.546875 0.78125 -1.171875 1.15625q-0.609375 0.359375 -1.34375 0.359375q-0.625 0 -1.109375 -0.25q-0.46875 -0.265625 -0.796875 -0.75q-0.3125 -0.484375 -0.484375 -1.15625q-0.15625 -0.671875 -0.15625 -1.484375zm1.3125 -0.09375q0 1.34375 0.390625 2.0q0.390625 0.65625 1.109375 0.65625q0.484375 0 1.015625 -0.4375q0.53125 -0.4375 1.125 -1.28125l0 -3.421875q-0.3125 -0.140625 -0.6875 -0.21875q-0.375 -0.078125 -0.75 -0.078125q-1.046875 0 -1.625 0.671875q-0.578125 0.671875 -0.578125 2.109375zm13.808105 1.5l-1.640625 0l0 2.109375l-1.296875 0l0 -2.109375l-4.609375 0l0 -1.125l4.078125 -6.34375l1.828125 0l0 6.34375l1.640625 0l0 1.125zm-2.9375 -6.234375l-3.328125 5.109375l3.328125 0l0 -5.109375zm10.464294 -0.046875l-4.015625 8.390625l-1.453125 0l4.171875 -8.390625l-5.171875 0l0 -1.1875l6.46875 0l0 1.1875zm8.120605 5.984375q0 0.609375 -0.25 1.09375q-0.25 0.46875 -0.703125 0.796875q-0.453125 0.3125 -1.0625 0.484375q-0.59375 0.15625 -1.3125 0.15625q-0.78125 0 -1.375 -0.171875q-0.59375 -0.171875 -1.0 -0.484375q-0.40625 -0.3125 -0.609375 -0.75q-0.203125 -0.4375 -0.203125 -0.953125q0 -0.875 0.484375 -1.515625q0.5 -0.640625 1.515625 -1.15625q-0.9375 -0.484375 -1.375 -1.0625q-0.421875 -0.578125 -0.421875 -1.328125q0 -0.46875 0.1875 -0.890625q0.1875 -0.4375 0.578125 -0.765625q0.390625 -0.34375 0.96875 -0.546875q0.578125 -0.203125 1.359375 -0.203125q0.734375 0 1.296875 0.15625q0.5625 0.15625 0.9375 0.453125q0.390625 0.296875 0.578125 0.71875q0.1875 0.40625 0.1875 0.921875q0 0.828125 -0.46875 1.421875q-0.46875 0.578125 -1.3125 1.015625q0.421875 0.21875 0.78125 0.484375q0.375 0.25 0.640625 0.5625q0.265625 0.3125 0.421875 0.703125q0.15625 0.390625 0.15625 0.859375zm-1.53125 -4.953125q0 -0.640625 -0.453125 -0.953125q-0.453125 -0.328125 -1.28125 -0.328125q-0.828125 0 -1.28125 0.3125q-0.453125 0.3125 -0.453125 0.9375q0 0.296875 0.109375 0.546875q0.109375 0.234375 0.328125 0.453125q0.234375 0.203125 0.578125 0.40625q0.34375 0.203125 0.828125 0.421875q0.828125 -0.390625 1.21875 -0.8125q0.40625 -0.421875 0.40625 -0.984375zm0.140625 5.046875q0 -0.265625 -0.09375 -0.515625q-0.078125 -0.265625 -0.3125 -0.515625q-0.21875 -0.25 -0.609375 -0.5q-0.375 -0.25 -0.96875 -0.5q-0.5 0.234375 -0.84375 0.46875q-0.328125 0.234375 -0.546875 0.484375q-0.203125 0.25 -0.296875 0.515625q-0.078125 0.265625 -0.078125 0.546875q0 0.328125 0.140625 0.59375q0.140625 0.25 0.390625 0.421875q0.25 0.171875 0.59375 0.265625q0.34375 0.09375 0.75 0.09375q0.390625 0 0.734375 -0.078125q0.34375 -0.09375 0.59375 -0.25q0.25 -0.171875 0.390625 -0.421875q0.15625 -0.25 0.15625 -0.609375zm8.948669 2.046875q-0.5 0.1875 -1.03125 0.28125q-0.515625 0.09375 -1.078125 0.09375q-1.734375 0 -2.6875 -0.9375q-0.9375 -0.953125 -0.9375 -2.78125q0 -0.859375 0.265625 -1.5625q0.28125 -0.71875 0.765625 -1.21875q0.5 -0.515625 1.171875 -0.78125q0.6875 -0.28125 1.5 -0.28125q0.578125 0 1.078125 0.078125q0.5 0.078125 0.953125 0.265625l0 1.21875q-0.484375 -0.25 -0.984375 -0.359375q-0.484375 -0.125 -1.015625 -0.125q-0.484375 0 -0.921875 0.1875q-0.4375 0.1875 -0.765625 0.546875q-0.328125 0.34375 -0.53125 0.859375q-0.1875 0.5 -0.1875 1.140625q0 1.328125 0.640625 2.0q0.65625 0.65625 1.8125 0.65625q0.515625 0 1.0 -0.109375q0.5 -0.125 0.953125 -0.359375l0 1.1875zm1.9643555 -3.25q0 -0.9375 0.25 -1.671875q0.265625 -0.734375 0.734375 -1.234375q0.46875 -0.5 1.125 -0.75q0.671875 -0.265625 1.484375 -0.265625q0.359375 0 0.6875 0.046875q0.34375 0.03125 0.671875 0.125l0 -3.078125l1.28125 0l0 10.34375l-1.140625 0l-0.03125 -1.390625q-0.546875 0.78125 -1.171875 1.15625q-0.609375 0.359375 -1.34375 0.359375q-0.625 0 -1.109375 -0.25q-0.46875 -0.265625 -0.796875 -0.75q-0.3125 -0.484375 -0.484375 -1.15625q-0.15625 -0.671875 -0.15625 -1.484375zm1.3125 -0.09375q0 1.34375 0.390625 2.0q0.390625 0.65625 1.109375 0.65625q0.484375 0 1.015625 -0.4375q0.53125 -0.4375 1.125 -1.28125l0 -3.421875q-0.3125 -0.140625 -0.6875 -0.21875q-0.375 -0.078125 -0.75 -0.078125q-1.046875 0 -1.625 0.671875q-0.578125 0.671875 -0.578125 2.109375zm13.276794 -4.78125l-4.015625 8.390625l-1.453125 0l4.171875 -8.390625l-5.171875 0l0 -1.1875l6.46875 0l0 1.1875zm7.8706055 5.328125q0 0.71875 -0.3125 1.3125q-0.296875 0.578125 -0.84375 1.0q-0.53125 0.40625 -1.265625 0.640625q-0.734375 0.234375 -1.578125 0.234375q-0.21875 0 -0.46875 -0.015625q-0.234375 0 -0.484375 -0.015625q-0.234375 -0.015625 -0.46875 -0.046875q-0.234375 -0.015625 -0.421875 -0.046875l0 -1.15625q0.40625 0.09375 0.90625 0.140625q0.515625 0.046875 1.03125 0.046875q0.59375 0 1.0625 -0.140625q0.46875 -0.140625 0.796875 -0.390625q0.328125 -0.265625 0.5 -0.640625q0.171875 -0.375 0.171875 -0.828125q0 -0.90625 -0.640625 -1.3125q-0.640625 -0.40625 -1.84375 -0.40625l-1.8125 0l0 -4.890625l5.15625 0l0 1.125l-3.953125 0l0 2.6875l0.84375 0q0.6875 0 1.328125 0.125q0.65625 0.125 1.15625 0.4375q0.515625 0.296875 0.828125 0.828125q0.3125 0.515625 0.3125 1.3125zm8.308044 3.0625l-6.3125 0l0 -1.140625l2.46875 -2.46875q0.609375 -0.59375 0.984375 -1.03125q0.390625 -0.4375 0.59375 -0.796875q0.21875 -0.375 0.296875 -0.6875q0.078125 -0.328125 0.078125 -0.703125q0 -0.34375 -0.109375 -0.65625q-0.09375 -0.328125 -0.296875 -0.5625q-0.1875 -0.25 -0.5 -0.390625q-0.3125 -0.140625 -0.75 -0.140625q-0.609375 0 -1.109375 0.28125q-0.5 0.265625 -0.921875 0.6875l-0.703125 -0.828125q0.546875 -0.578125 1.25 -0.921875q0.703125 -0.34375 1.640625 -0.34375q0.640625 0 1.15625 0.1875q0.53125 0.1875 0.90625 0.546875q0.390625 0.359375 0.59375 0.890625q0.21875 0.515625 0.21875 1.15625q0 0.5625 -0.15625 1.03125q-0.140625 0.46875 -0.4375 0.9375q-0.296875 0.453125 -0.75 0.953125q-0.453125 0.5 -1.0625 1.09375l-1.734375 1.6875l4.65625 0l0 1.21875zm7.5581055 -0.265625q-0.5 0.1875 -1.03125 0.28125q-0.515625 0.09375 -1.078125 0.09375q-1.734375 0 -2.6875 -0.9375q-0.9375 -0.953125 -0.9375 -2.78125q0 -0.859375 0.265625 -1.5625q0.28125 -0.71875 0.765625 -1.21875q0.5 -0.515625 1.171875 -0.78125q0.6875 -0.28125 1.5 -0.28125q0.578125 0 1.078125 0.078125q0.5 0.078125 0.953125 0.265625l0 1.21875q-0.484375 -0.25 -0.984375 -0.359375q-0.484375 -0.125 -1.015625 -0.125q-0.484375 0 -0.921875 0.1875q-0.4375 0.1875 -0.765625 0.546875q-0.328125 0.34375 -0.53125 0.859375q-0.1875 0.5 -0.1875 1.140625q0 1.328125 0.640625 2.0q0.65625 0.65625 1.8125 0.65625q0.515625 0 1.0 -0.109375q0.5 -0.125 0.953125 -0.359375l0 1.1875zm9.026794 -1.84375l-1.640625 0l0 2.109375l-1.296875 0l0 -2.109375l-4.609375 0l0 -1.125l4.078125 -6.34375l1.828125 0l0 6.34375l1.640625 0l0 1.125zm-2.9375 -6.234375l-3.328125 5.109375l3.328125 0l0 -5.109375zm10.464355 -0.046875l-4.015625 8.390625l-1.453125 0l4.171875 -8.390625l-5.171875 0l0 -1.1875l6.46875 0l0 1.1875zm8.120544 5.984375q0 0.609375 -0.25 1.09375q-0.25 0.46875 -0.703125 0.796875q-0.453125 0.3125 -1.0625 0.484375q-0.59375 0.15625 -1.3125 0.15625q-0.78125 0 -1.375 -0.171875q-0.59375 -0.171875 -1.0 -0.484375q-0.40625 -0.3125 -0.609375 -0.75q-0.203125 -0.4375 -0.203125 -0.953125q0 -0.875 0.484375 -1.515625q0.5 -0.640625 1.515625 -1.15625q-0.9375 -0.484375 -1.375 -1.0625q-0.421875 -0.578125 -0.421875 -1.328125q0 -0.46875 0.1875 -0.890625q0.1875 -0.4375 0.578125 -0.765625q0.390625 -0.34375 0.96875 -0.546875q0.578125 -0.203125 1.359375 -0.203125q0.734375 0 1.296875 0.15625q0.5625 0.15625 0.9375 0.453125q0.390625 0.296875 0.578125 0.71875q0.1875 0.40625 0.1875 0.921875q0 0.828125 -0.46875 1.421875q-0.46875 0.578125 -1.3125 1.015625q0.421875 0.21875 0.78125 0.484375q0.375 0.25 0.640625 0.5625q0.265625 0.3125 0.421875 0.703125q0.15625 0.390625 0.15625 0.859375zm-1.53125 -4.953125q0 -0.640625 -0.453125 -0.953125q-0.453125 -0.328125 -1.28125 -0.328125q-0.828125 0 -1.28125 0.3125q-0.453125 0.3125 -0.453125 0.9375q0 0.296875 0.109375 0.546875q0.109375 0.234375 0.328125 0.453125q0.234375 0.203125 0.578125 0.40625q0.34375 0.203125 0.828125 0.421875q0.828125 -0.390625 1.21875 -0.8125q0.40625 -0.421875 0.40625 -0.984375zm0.140625 5.046875q0 -0.265625 -0.09375 -0.515625q-0.078125 -0.265625 -0.3125 -0.515625q-0.21875 -0.25 -0.609375 -0.5q-0.375 -0.25 -0.96875 -0.5q-0.5 0.234375 -0.84375 0.46875q-0.328125 0.234375 -0.546875 0.484375q-0.203125 0.25 -0.296875 0.515625q-0.078125 0.265625 -0.078125 0.546875q0 0.328125 0.140625 0.59375q0.140625 0.25 0.390625 0.421875q0.25 0.171875 0.59375 0.265625q0.34375 0.09375 0.75 0.09375q0.390625 0 0.734375 -0.078125q0.34375 -0.09375 0.59375 -0.25q0.25 -0.171875 0.390625 -0.421875q0.15625 -0.25 0.15625 -0.609375zm9.19873 -0.75q0 0.71875 -0.3125 1.3125q-0.296875 0.578125 -0.84375 1.0q-0.53125 0.40625 -1.265625 0.640625q-0.734375 0.234375 -1.578125 0.234375q-0.21875 0 -0.46875 -0.015625q-0.234375 0 -0.484375 -0.015625q-0.234375 -0.015625 -0.46875 -0.046875q-0.234375 -0.015625 -0.421875 -0.046875l0 -1.15625q0.40625 0.09375 0.90625 0.140625q0.515625 0.046875 1.03125 0.046875q0.59375 0 1.0625 -0.140625q0.46875 -0.140625 0.796875 -0.390625q0.328125 -0.265625 0.5 -0.640625q0.171875 -0.375 0.171875 -0.828125q0 -0.90625 -0.640625 -1.3125q-0.640625 -0.40625 -1.84375 -0.40625l-1.8125 0l0 -4.890625l5.15625 0l0 1.125l-3.953125 0l0 2.6875l0.84375 0q0.6875 0 1.328125 0.125q0.65625 0.125 1.15625 0.4375q0.515625 0.296875 0.828125 0.828125q0.3125 0.515625 0.3125 1.3125zm8.245544 -5.328125l-4.015625 8.390625l-1.453125 0l4.171875 -8.390625l-5.171875 0l0 -1.1875l6.46875 0l0 1.1875zm8.120605 5.984375q0 0.609375 -0.25 1.09375q-0.25 0.46875 -0.703125 0.796875q-0.453125 0.3125 -1.0625 0.484375q-0.59375 0.15625 -1.3125 0.15625q-0.78125 0 -1.375 -0.171875q-0.59375 -0.171875 -1.0 -0.484375q-0.40625 -0.3125 -0.609375 -0.75q-0.203125 -0.4375 -0.203125 -0.953125q0 -0.875 0.484375 -1.515625q0.5 -0.640625 1.515625 -1.15625q-0.9375 -0.484375 -1.375 -1.0625q-0.421875 -0.578125 -0.421875 -1.328125q0 -0.46875 0.1875 -0.890625q0.1875 -0.4375 0.578125 -0.765625q0.390625 -0.34375 0.96875 -0.546875q0.578125 -0.203125 1.359375 -0.203125q0.734375 0 1.296875 0.15625q0.5625 0.15625 0.9375 0.453125q0.390625 0.296875 0.578125 0.71875q0.1875 0.40625 0.1875 0.921875q0 0.828125 -0.46875 1.421875q-0.46875 0.578125 -1.3125 1.015625q0.421875 0.21875 0.78125 0.484375q0.375 0.25 0.640625 0.5625q0.265625 0.3125 0.421875 0.703125q0.15625 0.390625 0.15625 0.859375zm-1.53125 -4.953125q0 -0.640625 -0.453125 -0.953125q-0.453125 -0.328125 -1.28125 -0.328125q-0.828125 0 -1.28125 0.3125q-0.453125 0.3125 -0.453125 0.9375q0 0.296875 0.109375 0.546875q0.109375 0.234375 0.328125 0.453125q0.234375 0.203125 0.578125 0.40625q0.34375 0.203125 0.828125 0.421875q0.828125 -0.390625 1.21875 -0.8125q0.40625 -0.421875 0.40625 -0.984375zm0.140625 5.046875q0 -0.265625 -0.09375 -0.515625q-0.078125 -0.265625 -0.3125 -0.515625q-0.21875 -0.25 -0.609375 -0.5q-0.375 -0.25 -0.96875 -0.5q-0.5 0.234375 -0.84375 0.46875q-0.328125 0.234375 -0.546875 0.484375q-0.203125 0.25 -0.296875 0.515625q-0.078125 0.265625 -0.078125 0.546875q0 0.328125 0.140625 0.59375q0.140625 0.25 0.390625 0.421875q0.25 0.171875 0.59375 0.265625q0.34375 0.09375 0.75 0.09375q0.390625 0 0.734375 -0.078125q0.34375 -0.09375 0.59375 -0.25q0.25 -0.171875 0.390625 -0.421875q0.15625 -0.25 0.15625 -0.609375zm9.198669 -0.75q0 0.71875 -0.3125 1.3125q-0.296875 0.578125 -0.84375 1.0q-0.53125 0.40625 -1.265625 0.640625q-0.734375 0.234375 -1.578125 0.234375q-0.21875 0 -0.46875 -0.015625q-0.234375 0 -0.484375 -0.015625q-0.234375 -0.015625 -0.46875 -0.046875q-0.234375 -0.015625 -0.421875 -0.046875l0 -1.15625q0.40625 0.09375 0.90625 0.140625q0.515625 0.046875 1.03125 0.046875q0.59375 0 1.0625 -0.140625q0.46875 -0.140625 0.796875 -0.390625q0.328125 -0.265625 0.5 -0.640625q0.171875 -0.375 0.171875 -0.828125q0 -0.90625 -0.640625 -1.3125q-0.640625 -0.40625 -1.84375 -0.40625l-1.8125 0l0 -4.890625l5.15625 0l0 1.125l-3.953125 0l0 2.6875l0.84375 0q0.6875 0 1.328125 0.125q0.65625 0.125 1.15625 0.4375q0.515625 0.296875 0.828125 0.828125q0.3125 0.515625 0.3125 1.3125zm8.308105 3.0625l-6.3125 0l0 -1.140625l2.46875 -2.46875q0.609375 -0.59375 0.984375 -1.03125q0.390625 -0.4375 0.59375 -0.796875q0.21875 -0.375 0.296875 -0.6875q0.078125 -0.328125 0.078125 -0.703125q0 -0.34375 -0.109375 -0.65625q-0.09375 -0.328125 -0.296875 -0.5625q-0.1875 -0.25 -0.5 -0.390625q-0.3125 -0.140625 -0.75 -0.140625q-0.609375 0 -1.109375 0.28125q-0.5 0.265625 -0.921875 0.6875l-0.703125 -0.828125q0.546875 -0.578125 1.25 -0.921875q0.703125 -0.34375 1.640625 -0.34375q0.640625 0 1.15625 0.1875q0.53125 0.1875 0.90625 0.546875q0.390625 0.359375 0.59375 0.890625q0.21875 0.515625 0.21875 1.15625q0 0.5625 -0.15625 1.03125q-0.140625 0.46875 -0.4375 0.9375q-0.296875 0.453125 -0.75 0.953125q-0.453125 0.5 -1.0625 1.09375l-1.734375 1.6875l4.65625 0l0 1.21875zm7.8705444 -2.90625q0 0.625 -0.265625 1.1875q-0.25 0.546875 -0.765625 0.96875q-0.515625 0.40625 -1.296875 0.640625q-0.765625 0.234375 -1.796875 0.234375q-0.578125 0 -1.03125 -0.03125q-0.453125 -0.03125 -0.84375 -0.09375l0 -1.140625q0.453125 0.078125 0.953125 0.125q0.515625 0.046875 1.03125 0.046875q0.71875 0 1.21875 -0.125q0.515625 -0.140625 0.84375 -0.375q0.328125 -0.25 0.46875 -0.59375q0.140625 -0.34375 0.140625 -0.765625q0 -0.40625 -0.171875 -0.6875q-0.171875 -0.296875 -0.5 -0.5q-0.3125 -0.203125 -0.765625 -0.296875q-0.4375 -0.09375 -0.953125 -0.09375l-1.09375 0l0 -1.046875l1.109375 0q0.421875 0 0.78125 -0.109375q0.359375 -0.125 0.609375 -0.328125q0.25 -0.21875 0.375 -0.53125q0.140625 -0.3125 0.140625 -0.703125q0 -0.765625 -0.46875 -1.109375q-0.46875 -0.359375 -1.375 -0.359375q-0.484375 0 -1.0 0.09375q-0.5 0.09375 -1.09375 0.28125l0 -1.109375q0.25 -0.09375 0.53125 -0.15625q0.28125 -0.078125 0.5625 -0.125q0.28125 -0.046875 0.5625 -0.0625q0.28125 -0.03125 0.53125 -0.03125q0.765625 0 1.34375 0.171875q0.578125 0.15625 0.96875 0.46875q0.390625 0.296875 0.578125 0.75q0.203125 0.4375 0.203125 0.984375q0 0.8125 -0.421875 1.375q-0.421875 0.5625 -1.15625 0.890625q0.375 0.046875 0.734375 0.234375q0.375 0.171875 0.65625 0.453125q0.296875 0.265625 0.46875 0.640625q0.1875 0.375 0.1875 0.828125zm8.35498 -0.125q0 0.65625 -0.234375 1.234375q-0.234375 0.578125 -0.6875 1.015625q-0.4375 0.421875 -1.0625 0.671875q-0.609375 0.234375 -1.359375 0.234375q-0.796875 0 -1.40625 -0.25q-0.609375 -0.25 -1.015625 -0.765625q-0.40625 -0.53125 -0.625 -1.328125q-0.203125 -0.796875 -0.203125 -1.890625q0 -0.734375 0.09375 -1.421875q0.09375 -0.6875 0.3125 -1.296875q0.21875 -0.609375 0.578125 -1.109375q0.375 -0.5 0.921875 -0.859375q0.546875 -0.375 1.28125 -0.578125q0.734375 -0.203125 1.71875 -0.203125l0.9375 0l0 1.125l-1.015625 0q-0.859375 0 -1.5 0.203125q-0.625 0.203125 -1.046875 0.578125q-0.421875 0.375 -0.65625 0.90625q-0.21875 0.515625 -0.28125 1.171875l-0.03125 0.296875q0.46875 -0.265625 1.0625 -0.421875q0.609375 -0.171875 1.3125 -0.171875q0.71875 0 1.265625 0.21875q0.546875 0.203125 0.90625 0.578125q0.375 0.375 0.546875 0.90625q0.1875 0.53125 0.1875 1.15625zm-1.328125 0.078125q0 -0.4375 -0.109375 -0.796875q-0.109375 -0.359375 -0.34375 -0.59375q-0.21875 -0.25 -0.5625 -0.375q-0.34375 -0.140625 -0.828125 -0.140625q-0.28125 0 -0.578125 0.046875q-0.28125 0.046875 -0.5625 0.140625q-0.265625 0.09375 -0.515625 0.21875q-0.25 0.109375 -0.453125 0.234375q0 0.953125 0.125 1.59375q0.140625 0.625 0.390625 1.015625q0.265625 0.375 0.640625 0.53125q0.390625 0.15625 0.890625 0.15625q0.421875 0 0.765625 -0.125q0.34375 -0.140625 0.59375 -0.40625q0.25 -0.265625 0.390625 -0.640625q0.15625 -0.375 0.15625 -0.859375z" fill-rule="nonzero"></path><path fill="#000000" d="m61.447918 383.88052l-1.75 0l-3.421875 -3.9375l0 3.9375l-1.28125 0l0 -10.34375l1.28125 0l0 6.359375l3.296875 -3.375l1.6875 0l-3.453125 3.390625l3.640625 3.96875zm7.667446 -4.0625q0 0.265625 -0.015625 0.453125q0 0.1875 -0.015625 0.34375l-5.171871 0q0 1.125 0.6249962 1.734375q0.640625 0.59375 1.828125 0.59375q0.3125 0 0.640625 -0.015625q0.328125 -0.03125 0.625 -0.0625q0.296875 -0.046875 0.5625 -0.09375q0.28125 -0.0625 0.515625 -0.140625l0 1.046875q-0.515625 0.15625 -1.171875 0.234375q-0.65625 0.09375 -1.359375 0.09375q-0.9375 0 -1.625 -0.25q-0.6874962 -0.25 -1.1249962 -0.734375q-0.421875 -0.5 -0.640625 -1.203125q-0.203125 -0.703125 -0.203125 -1.59375q0 -0.78125 0.21875 -1.46875q0.21875 -0.703125 0.640625 -1.21875q0.4375 -0.53125 1.0624962 -0.828125q0.625 -0.3125 1.421875 -0.3125q0.765625 0 1.359375 0.25q0.59375 0.234375 1.0 0.6875q0.40625 0.4375 0.609375 1.078125q0.21875 0.625 0.21875 1.40625zm-1.328125 -0.1875q0.015625 -0.484375 -0.109375 -0.890625q-0.109375 -0.40625 -0.359375 -0.703125q-0.234375 -0.296875 -0.609375 -0.453125q-0.359375 -0.171875 -0.84375 -0.171875q-0.421875 0 -0.765625 0.171875q-0.34375 0.15625 -0.59375 0.453125q-0.25 0.28125 -0.40625 0.703125q-0.14062119 0.40625 -0.18749619 0.890625l3.8749962 0zm9.3862 0.1875q0 0.265625 -0.015625 0.453125q0 0.1875 -0.015625 0.34375l-5.171875 0q0 1.125 0.625 1.734375q0.640625 0.59375 1.828125 0.59375q0.3125 0 0.640625 -0.015625q0.328125 -0.03125 0.625 -0.0625q0.296875 -0.046875 0.5625 -0.09375q0.28125 -0.0625 0.515625 -0.140625l0 1.046875q-0.515625 0.15625 -1.171875 0.234375q-0.65625 0.09375 -1.359375 0.09375q-0.9375 0 -1.625 -0.25q-0.6875 -0.25 -1.125 -0.734375q-0.421875 -0.5 -0.640625 -1.203125q-0.203125 -0.703125 -0.203125 -1.59375q0 -0.78125 0.21875 -1.46875q0.21875 -0.703125 0.640625 -1.21875q0.4375 -0.53125 1.0625 -0.828125q0.625 -0.3125 1.421875 -0.3125q0.765625 0 1.359375 0.25q0.59375 0.234375 1.0 0.6875q0.40625 0.4375 0.609375 1.078125q0.21875 0.625 0.21875 1.40625zm-1.328125 -0.1875q0.015625 -0.484375 -0.109375 -0.890625q-0.109375 -0.40625 -0.359375 -0.703125q-0.234375 -0.296875 -0.609375 -0.453125q-0.359375 -0.171875 -0.84375 -0.171875q-0.421875 0 -0.765625 0.171875q-0.34375 0.15625 -0.59375 0.453125q-0.25 0.28125 -0.40625 0.703125q-0.140625 0.40625 -0.1875 0.890625l3.875 0zm9.4487 0.4375q0 0.984375 -0.28125 1.71875q-0.265625 0.734375 -0.75 1.21875q-0.484375 0.484375 -1.140625 0.734375q-0.65625 0.234375 -1.421875 0.234375q-0.359375 0 -0.703125 -0.046875q-0.34375 -0.03125 -0.703125 -0.125l0 3.078125l-1.28125 0l0 -10.359375l1.140625 0l0.078125 1.234375q0.546875 -0.75 1.171875 -1.046875q0.625 -0.3125 1.34375 -0.3125q0.625 0 1.09375 0.265625q0.484375 0.265625 0.8125 0.75q0.328125 0.46875 0.484375 1.15625q0.15625 0.671875 0.15625 1.5zm-1.296875 0.0625q0 -0.578125 -0.09375 -1.0625q-0.078125 -0.484375 -0.265625 -0.828125q-0.171875 -0.34375 -0.46875 -0.53125q-0.28125 -0.203125 -0.671875 -0.203125q-0.25 0 -0.5 0.078125q-0.25 0.078125 -0.515625 0.265625q-0.265625 0.171875 -0.5625 0.46875q-0.296875 0.296875 -0.625 0.734375l0 3.5625q0.34375 0.15625 0.734375 0.25q0.390625 0.078125 0.765625 0.078125q1.03125 0 1.609375 -0.703125q0.59375 -0.703125 0.59375 -2.109375zm9.10495 0.84375q0 0.625 -0.265625 1.1875q-0.25 0.546875 -0.765625 0.96875q-0.515625 0.40625 -1.296875 0.640625q-0.765625 0.234375 -1.796875 0.234375q-0.578125 0 -1.03125 -0.03125q-0.453125 -0.03125 -0.84375 -0.09375l0 -1.140625q0.453125 0.078125 0.953125 0.125q0.515625 0.046875 1.03125 0.046875q0.71875 0 1.21875 -0.125q0.515625 -0.140625 0.84375 -0.375q0.328125 -0.25 0.46875 -0.59375q0.140625 -0.34375 0.140625 -0.765625q0 -0.40625 -0.171875 -0.6875q-0.171875 -0.296875 -0.5 -0.5q-0.3125 -0.203125 -0.765625 -0.296875q-0.4375 -0.09375 -0.953125 -0.09375l-1.09375 0l0 -1.046875l1.109375 0q0.421875 0 0.78125 -0.109375q0.359375 -0.125 0.609375 -0.328125q0.25 -0.21875 0.375 -0.53125q0.140625 -0.3125 0.140625 -0.703125q0 -0.765625 -0.46875 -1.109375q-0.46875 -0.359375 -1.375 -0.359375q-0.484375 0 -1.0 0.09375q-0.5 0.09375 -1.09375 0.28125l0 -1.109375q0.25 -0.09375 0.53125 -0.15625q0.28125 -0.078125 0.5625 -0.125q0.28125 -0.046875 0.5625 -0.0625q0.28125 -0.03125 0.53125 -0.03125q0.765625 0 1.34375 0.171875q0.578125 0.15625 0.96875 0.46875q0.390625 0.296875 0.578125 0.75q0.203125 0.4375 0.203125 0.984375q0 0.8125 -0.421875 1.375q-0.421875 0.5625 -1.15625 0.890625q0.375 0.046875 0.734375 0.234375q0.375 0.171875 0.65625 0.453125q0.296875 0.265625 0.46875 0.640625q0.1875 0.375 0.1875 0.828125z" fill-rule="nonzero"></path><path fill="#000000" d="m355.41818 383.88052l-0.03125 -0.984375q-0.59375 0.59375 -1.21875 0.859375q-0.609375 0.25 -1.296875 0.25q-0.625 0 -1.078125 -0.15625q-0.4375 -0.15625 -0.734375 -0.4375q-0.28125 -0.28125 -0.421875 -0.65625q-0.140625 -0.390625 -0.140625 -0.84375q0 -1.09375 0.828125 -1.71875q0.828125 -0.640625 2.4375 -0.640625l1.515625 0l0 -0.640625q0 -0.65625 -0.421875 -1.046875q-0.40625 -0.390625 -1.265625 -0.390625q-0.625 0 -1.234375 0.140625q-0.59375 0.140625 -1.234375 0.40625l0 -1.15625q0.234375 -0.09375 0.53125 -0.171875q0.296875 -0.078125 0.625 -0.140625q0.328125 -0.078125 0.6875 -0.109375q0.359375 -0.046875 0.734375 -0.046875q0.65625 0 1.1875 0.15625q0.546875 0.140625 0.90625 0.4375q0.375 0.296875 0.5625 0.75q0.203125 0.453125 0.203125 1.078125l0 5.0625l-1.140625 0zm-0.140625 -3.34375l-1.609375 0q-0.484375 0 -0.828125 0.09375q-0.34375 0.09375 -0.5625 0.265625q-0.21875 0.171875 -0.328125 0.421875q-0.09375 0.25 -0.09375 0.5625q0 0.203125 0.0625 0.40625q0.0625 0.1875 0.203125 0.34375q0.15625 0.140625 0.390625 0.234375q0.234375 0.078125 0.5625 0.078125q0.4375 0 1.0 -0.265625q0.578125 -0.265625 1.203125 -0.84375l0 -1.296875zm9.1987 3.078125q-0.5 0.1875 -1.03125 0.28125q-0.515625 0.09375 -1.078125 0.09375q-1.734375 0 -2.6875 -0.9375q-0.9375 -0.953125 -0.9375 -2.78125q0 -0.859375 0.265625 -1.5625q0.28125 -0.71875 0.765625 -1.21875q0.5 -0.515625 1.171875 -0.78125q0.6875 -0.28125 1.5 -0.28125q0.578125 0 1.078125 0.078125q0.5 0.078125 0.953125 0.265625l0 1.21875q-0.484375 -0.25 -0.984375 -0.359375q-0.484375 -0.125 -1.015625 -0.125q-0.484375 0 -0.921875 0.1875q-0.4375 0.1875 -0.765625 0.546875q-0.328125 0.34375 -0.53125 0.859375q-0.1875 0.5 -0.1875 1.140625q0 1.328125 0.640625 2.0q0.65625 0.65625 1.8125 0.65625q0.515625 0 1.0 -0.109375q0.5 -0.125 0.953125 -0.359375l0 1.1875zm8.620575 -3.546875q0 0.921875 -0.25 1.640625q-0.25 0.71875 -0.71875 1.21875q-0.46875 0.5 -1.140625 0.78125q-0.65625 0.265625 -1.484375 0.265625q-0.65625 0 -1.34375 -0.125q-0.671875 -0.125 -1.34375 -0.40625l0 -9.90625l1.28125 0l0 2.84375l-0.0625 1.359375q0.546875 -0.734375 1.171875 -1.03125q0.625 -0.3125 1.34375 -0.3125q0.625 0 1.09375 0.265625q0.484375 0.265625 0.8125 0.75q0.328125 0.46875 0.484375 1.15625q0.15625 0.671875 0.15625 1.5zm-1.296875 0.0625q0 -0.578125 -0.09375 -1.0625q-0.078125 -0.484375 -0.265625 -0.828125q-0.171875 -0.34375 -0.46875 -0.53125q-0.28125 -0.203125 -0.671875 -0.203125q-0.25 0 -0.5 0.078125q-0.25 0.078125 -0.515625 0.265625q-0.265625 0.171875 -0.5625 0.46875q-0.296875 0.296875 -0.625 0.734375l0 3.5625q0.359375 0.15625 0.75 0.25q0.390625 0.078125 0.75 0.078125q0.4375 0 0.828125 -0.140625q0.40625 -0.140625 0.703125 -0.46875q0.3125 -0.328125 0.484375 -0.859375q0.1875 -0.546875 0.1875 -1.34375zm2.6987 0.234375q0 -0.9375 0.25 -1.671875q0.265625 -0.734375 0.734375 -1.234375q0.46875 -0.5 1.125 -0.75q0.671875 -0.265625 1.484375 -0.265625q0.359375 0 0.6875 0.046875q0.34375 0.03125 0.671875 0.125l0 -3.078125l1.28125 0l0 10.34375l-1.140625 0l-0.03125 -1.390625q-0.546875 0.78125 -1.171875 1.15625q-0.609375 0.359375 -1.34375 0.359375q-0.625 0 -1.109375 -0.25q-0.46875 -0.265625 -0.796875 -0.75q-0.3125 -0.484375 -0.484375 -1.15625q-0.15625 -0.671875 -0.15625 -1.484375zm1.3125 -0.09375q0 1.34375 0.390625 2.0q0.390625 0.65625 1.109375 0.65625q0.484375 0 1.015625 -0.4375q0.53125 -0.4375 1.125 -1.28125l0 -3.421875q-0.3125 -0.140625 -0.6875 -0.21875q-0.375 -0.078125 -0.75 -0.078125q-1.046875 0 -1.625 0.671875q-0.578125 0.671875 -0.578125 2.109375zm13.245575 3.609375l-6.0 0l0 -1.1875l2.453125 0l0 -6.984375l-2.296875 1.25l-0.46875 -1.09375l3.046875 -1.59375l1.125 0l0 8.421875l2.140625 0l0 1.1875zm8.151825 -2.40625q0 0.609375 -0.25 1.09375q-0.25 0.46875 -0.703125 0.796875q-0.453125 0.3125 -1.0625 0.484375q-0.59375 0.15625 -1.3125 0.15625q-0.78125 0 -1.375 -0.171875q-0.59375 -0.171875 -1.0 -0.484375q-0.40625 -0.3125 -0.609375 -0.75q-0.203125 -0.4375 -0.203125 -0.953125q0 -0.875 0.484375 -1.515625q0.5 -0.640625 1.515625 -1.15625q-0.9375 -0.484375 -1.375 -1.0625q-0.421875 -0.578125 -0.421875 -1.328125q0 -0.46875 0.1875 -0.890625q0.1875 -0.4375 0.578125 -0.765625q0.390625 -0.34375 0.96875 -0.546875q0.578125 -0.203125 1.359375 -0.203125q0.734375 0 1.296875 0.15625q0.5625 0.15625 0.9375 0.453125q0.390625 0.296875 0.578125 0.71875q0.1875 0.40625 0.1875 0.921875q0 0.828125 -0.46875 1.421875q-0.46875 0.578125 -1.3125 1.015625q0.421875 0.21875 0.78125 0.484375q0.375 0.25 0.640625 0.5625q0.265625 0.3125 0.421875 0.703125q0.15625 0.390625 0.15625 0.859375zm-1.53125 -4.953125q0 -0.640625 -0.453125 -0.953125q-0.453125 -0.328125 -1.28125 -0.328125q-0.828125 0 -1.28125 0.3125q-0.453125 0.3125 -0.453125 0.9375q0 0.296875 0.109375 0.546875q0.109375 0.234375 0.328125 0.453125q0.234375 0.203125 0.578125 0.40625q0.34375 0.203125 0.828125 0.421875q0.828125 -0.390625 1.21875 -0.8125q0.40625 -0.421875 0.40625 -0.984375zm0.140625 5.046875q0 -0.265625 -0.09375 -0.515625q-0.078125 -0.265625 -0.3125 -0.515625q-0.21875 -0.25 -0.609375 -0.5q-0.375 -0.25 -0.96875 -0.5q-0.5 0.234375 -0.84375 0.46875q-0.328125 0.234375 -0.546875 0.484375q-0.203125 0.25 -0.296875 0.515625q-0.078125 0.265625 -0.078125 0.546875q0 0.328125 0.140625 0.59375q0.140625 0.25 0.390625 0.421875q0.25 0.171875 0.59375 0.265625q0.34375 0.09375 0.75 0.09375q0.390625 0 0.734375 -0.078125q0.34375 -0.09375 0.59375 -0.25q0.25 -0.171875 0.390625 -0.421875q0.15625 -0.25 0.15625 -0.609375zm2.85495 -1.203125q0 -0.9375 0.25 -1.671875q0.265625 -0.734375 0.734375 -1.234375q0.46875 -0.5 1.125 -0.75q0.671875 -0.265625 1.484375 -0.265625q0.359375 0 0.6875 0.046875q0.34375 0.03125 0.671875 0.125l0 -3.078125l1.28125 0l0 10.34375l-1.140625 0l-0.03125 -1.390625q-0.546875 0.78125 -1.171875 1.15625q-0.609375 0.359375 -1.34375 0.359375q-0.625 0 -1.109375 -0.25q-0.46875 -0.265625 -0.796875 -0.75q-0.3125 -0.484375 -0.484375 -1.15625q-0.15625 -0.671875 -0.15625 -1.484375zm1.3125 -0.09375q0 1.34375 0.390625 2.0q0.390625 0.65625 1.109375 0.65625q0.484375 0 1.015625 -0.4375q0.53125 -0.4375 1.125 -1.28125l0 -3.421875q-0.3125 -0.140625 -0.6875 -0.21875q-0.375 -0.078125 -0.75 -0.078125q-1.046875 0 -1.625 0.671875q-0.578125 0.671875 -0.578125 2.109375zm13.401825 -0.203125q0 0.921875 -0.25 1.640625q-0.25 0.71875 -0.71875 1.21875q-0.46875 0.5 -1.140625 0.78125q-0.65625 0.265625 -1.484375 0.265625q-0.65625 0 -1.34375 -0.125q-0.671875 -0.125 -1.34375 -0.40625l0 -9.90625l1.28125 0l0 2.84375l-0.0625 1.359375q0.546875 -0.734375 1.171875 -1.03125q0.625 -0.3125 1.34375 -0.3125q0.625 0 1.09375 0.265625q0.484375 0.265625 0.8125 0.75q0.328125 0.46875 0.484375 1.15625q0.15625 0.671875 0.15625 1.5zm-1.296875 0.0625q0 -0.578125 -0.09375 -1.0625q-0.078125 -0.484375 -0.265625 -0.828125q-0.171875 -0.34375 -0.46875 -0.53125q-0.28125 -0.203125 -0.671875 -0.203125q-0.25 0 -0.5 0.078125q-0.25 0.078125 -0.515625 0.265625q-0.265625 0.171875 -0.5625 0.46875q-0.296875 0.296875 -0.625 0.734375l0 3.5625q0.359375 0.15625 0.75 0.25q0.390625 0.078125 0.75 0.078125q0.4375 0 0.828125 -0.140625q0.40625 -0.140625 0.703125 -0.46875q0.3125 -0.328125 0.484375 -0.859375q0.1875 -0.546875 0.1875 -1.34375zm9.7612 1.640625l-1.640625 0l0 2.109375l-1.296875 0l0 -2.109375l-4.609375 0l0 -1.125l4.078125 -6.34375l1.828125 0l0 6.34375l1.640625 0l0 1.125zm-2.9375 -6.234375l-3.328125 5.109375l3.328125 0l0 -5.109375zm10.026825 8.078125q-0.5 0.1875 -1.03125 0.28125q-0.515625 0.09375 -1.078125 0.09375q-1.734375 0 -2.6875 -0.9375q-0.9375 -0.953125 -0.9375 -2.78125q0 -0.859375 0.265625 -1.5625q0.28125 -0.71875 0.765625 -1.21875q0.5 -0.515625 1.171875 -0.78125q0.6875 -0.28125 1.5 -0.28125q0.578125 0 1.078125 0.078125q0.5 0.078125 0.953125 0.265625l0 1.21875q-0.484375 -0.25 -0.984375 -0.359375q-0.484375 -0.125 -1.015625 -0.125q-0.484375 0 -0.921875 0.1875q-0.4375 0.1875 -0.765625 0.546875q-0.328125 0.34375 -0.53125 0.859375q-0.1875 0.5 -0.1875 1.140625q0 1.328125 0.640625 2.0q0.65625 0.65625 1.8125 0.65625q0.515625 0 1.0 -0.109375q0.5 -0.125 0.953125 -0.359375l0 1.1875zm8.058075 0q-0.5 0.1875 -1.03125 0.28125q-0.515625 0.09375 -1.078125 0.09375q-1.734375 0 -2.6875 -0.9375q-0.9375 -0.953125 -0.9375 -2.78125q0 -0.859375 0.265625 -1.5625q0.28125 -0.71875 0.765625 -1.21875q0.5 -0.515625 1.171875 -0.78125q0.6875 -0.28125 1.5 -0.28125q0.578125 0 1.078125 0.078125q0.5 0.078125 0.953125 0.265625l0 1.21875q-0.484375 -0.25 -0.984375 -0.359375q-0.484375 -0.125 -1.015625 -0.125q-0.484375 0 -0.921875 0.1875q-0.4375 0.1875 -0.765625 0.546875q-0.328125 0.34375 -0.53125 0.859375q-0.1875 0.5 -0.1875 1.140625q0 1.328125 0.640625 2.0q0.65625 0.65625 1.8125 0.65625q0.515625 0 1.0 -0.109375q0.5 -0.125 0.953125 -0.359375l0 1.1875zm8.558075 0.265625l-6.3125 0l0 -1.140625l2.46875 -2.46875q0.609375 -0.59375 0.984375 -1.03125q0.390625 -0.4375 0.59375 -0.796875q0.21875 -0.375 0.296875 -0.6875q0.078125 -0.328125 0.078125 -0.703125q0 -0.34375 -0.109375 -0.65625q-0.09375 -0.328125 -0.296875 -0.5625q-0.1875 -0.25 -0.5 -0.390625q-0.3125 -0.140625 -0.75 -0.140625q-0.609375 0 -1.109375 0.28125q-0.5 0.265625 -0.921875 0.6875l-0.703125 -0.828125q0.546875 -0.578125 1.25 -0.921875q0.703125 -0.34375 1.640625 -0.34375q0.640625 0 1.15625 0.1875q0.53125 0.1875 0.90625 0.546875q0.390625 0.359375 0.59375 0.890625q0.21875 0.515625 0.21875 1.15625q0 0.5625 -0.15625 1.03125q-0.140625 0.46875 -0.4375 0.9375q-0.296875 0.453125 -0.75 0.953125q-0.453125 0.5 -1.0625 1.09375l-1.734375 1.6875l4.65625 0l0 1.21875zm8.370575 -9.15625q-1.015625 -0.21875 -1.734375 -0.21875q-1.71875 0 -1.71875 1.796875l0 1.296875l3.21875 0l0 1.0625l-3.21875 0l0 5.21875l-1.296875 0l0 -5.21875l-2.359375 0l0 -1.0625l2.359375 0l0 -1.21875q0 -2.9375 3.0625 -2.9375q0.765625 0 1.6875 0.171875l0 1.109375zm-7.703125 1.796875l0 0zm15.4487 4.953125q0 0.609375 -0.25 1.09375q-0.25 0.46875 -0.703125 0.796875q-0.453125 0.3125 -1.0625 0.484375q-0.59375 0.15625 -1.3125 0.15625q-0.78125 0 -1.375 -0.171875q-0.59375 -0.171875 -1.0 -0.484375q-0.40625 -0.3125 -0.609375 -0.75q-0.203125 -0.4375 -0.203125 -0.953125q0 -0.875 0.484375 -1.515625q0.5 -0.640625 1.515625 -1.15625q-0.9375 -0.484375 -1.375 -1.0625q-0.421875 -0.578125 -0.421875 -1.328125q0 -0.46875 0.1875 -0.890625q0.1875 -0.4375 0.578125 -0.765625q0.390625 -0.34375 0.96875 -0.546875q0.578125 -0.203125 1.359375 -0.203125q0.734375 0 1.296875 0.15625q0.5625 0.15625 0.9375 0.453125q0.390625 0.296875 0.578125 0.71875q0.1875 0.40625 0.1875 0.921875q0 0.828125 -0.46875 1.421875q-0.46875 0.578125 -1.3125 1.015625q0.421875 0.21875 0.78125 0.484375q0.375 0.25 0.640625 0.5625q0.265625 0.3125 0.421875 0.703125q0.15625 0.390625 0.15625 0.859375zm-1.53125 -4.953125q0 -0.640625 -0.453125 -0.953125q-0.453125 -0.328125 -1.28125 -0.328125q-0.828125 0 -1.28125 0.3125q-0.453125 0.3125 -0.453125 0.9375q0 0.296875 0.109375 0.546875q0.109375 0.234375 0.328125 0.453125q0.234375 0.203125 0.578125 0.40625q0.34375 0.203125 0.828125 0.421875q0.828125 -0.390625 1.21875 -0.8125q0.40625 -0.421875 0.40625 -0.984375zm0.140625 5.046875q0 -0.265625 -0.09375 -0.515625q-0.078125 -0.265625 -0.3125 -0.515625q-0.21875 -0.25 -0.609375 -0.5q-0.375 -0.25 -0.96875 -0.5q-0.5 0.234375 -0.84375 0.46875q-0.328125 0.234375 -0.546875 0.484375q-0.203125 0.25 -0.296875 0.515625q-0.078125 0.265625 -0.078125 0.546875q0 0.328125 0.140625 0.59375q0.140625 0.25 0.390625 0.421875q0.25 0.171875 0.59375 0.265625q0.34375 0.09375 0.75 0.09375q0.390625 0 0.734375 -0.078125q0.34375 -0.09375 0.59375 -0.25q0.25 -0.171875 0.390625 -0.421875q0.15625 -0.25 0.15625 -0.609375zm9.1987 -0.75q0 0.71875 -0.3125 1.3125q-0.296875 0.578125 -0.84375 1.0q-0.53125 0.40625 -1.265625 0.640625q-0.734375 0.234375 -1.578125 0.234375q-0.21875 0 -0.46875 -0.015625q-0.234375 0 -0.484375 -0.015625q-0.234375 -0.015625 -0.46875 -0.046875q-0.234375 -0.015625 -0.421875 -0.046875l0 -1.15625q0.40625 0.09375 0.90625 0.140625q0.515625 0.046875 1.03125 0.046875q0.59375 0 1.0625 -0.140625q0.46875 -0.140625 0.796875 -0.390625q0.328125 -0.265625 0.5 -0.640625q0.171875 -0.375 0.171875 -0.828125q0 -0.90625 -0.640625 -1.3125q-0.640625 -0.40625 -1.84375 -0.40625l-1.8125 0l0 -4.890625l5.15625 0l0 1.125l-3.953125 0l0 2.6875l0.84375 0q0.6875 0 1.328125 0.125q0.65625 0.125 1.15625 0.4375q0.515625 0.296875 0.828125 0.828125q0.3125 0.515625 0.3125 1.3125zm7.808075 2.796875q-0.5 0.1875 -1.03125 0.28125q-0.515625 0.09375 -1.078125 0.09375q-1.734375 0 -2.6875 -0.9375q-0.9375 -0.953125 -0.9375 -2.78125q0 -0.859375 0.265625 -1.5625q0.28125 -0.71875 0.765625 -1.21875q0.5 -0.515625 1.171875 -0.78125q0.6875 -0.28125 1.5 -0.28125q0.578125 0 1.078125 0.078125q0.5 0.078125 0.953125 0.265625l0 1.21875q-0.484375 -0.25 -0.984375 -0.359375q-0.484375 -0.125 -1.015625 -0.125q-0.484375 0 -0.921875 0.1875q-0.4375 0.1875 -0.765625 0.546875q-0.328125 0.34375 -0.53125 0.859375q-0.1875 0.5 -0.1875 1.140625q0 1.328125 0.640625 2.0q0.65625 0.65625 1.8125 0.65625q0.515625 0 1.0 -0.109375q0.5 -0.125 0.953125 -0.359375l0 1.1875zm8.558075 -3.796875q0 0.265625 -0.015625 0.453125q0 0.1875 -0.015625 0.34375l-5.171875 0q0 1.125 0.625 1.734375q0.640625 0.59375 1.828125 0.59375q0.3125 0 0.640625 -0.015625q0.328125 -0.03125 0.625 -0.0625q0.296875 -0.046875 0.5625 -0.09375q0.28125 -0.0625 0.515625 -0.140625l0 1.046875q-0.515625 0.15625 -1.171875 0.234375q-0.65625 0.09375 -1.359375 0.09375q-0.9375 0 -1.625 -0.25q-0.6875 -0.25 -1.125 -0.734375q-0.421875 -0.5 -0.640625 -1.203125q-0.203125 -0.703125 -0.203125 -1.59375q0 -0.78125 0.21875 -1.46875q0.21875 -0.703125 0.640625 -1.21875q0.4375 -0.53125 1.0625 -0.828125q0.625 -0.3125 1.421875 -0.3125q0.765625 0 1.359375 0.25q0.59375 0.234375 1.0 0.6875q0.40625 0.4375 0.609375 1.078125q0.21875 0.625 0.21875 1.40625zm-1.328125 -0.1875q0.015625 -0.484375 -0.109375 -0.890625q-0.109375 -0.40625 -0.359375 -0.703125q-0.234375 -0.296875 -0.609375 -0.453125q-0.359375 -0.171875 -0.84375 -0.171875q-0.421875 0 -0.765625 0.171875q-0.34375 0.15625 -0.59375 0.453125q-0.25 0.28125 -0.40625 0.703125q-0.140625 0.40625 -0.1875 0.890625l3.875 0zm2.79245 0.734375q0 -0.9375 0.25 -1.671875q0.265625 -0.734375 0.734375 -1.234375q0.46875 -0.5 1.125 -0.75q0.671875 -0.265625 1.484375 -0.265625q0.359375 0 0.6875 0.046875q0.34375 0.03125 0.671875 0.125l0 -3.078125l1.28125 0l0 10.34375l-1.140625 0l-0.03125 -1.390625q-0.546875 0.78125 -1.171875 1.15625q-0.609375 0.359375 -1.34375 0.359375q-0.625 0 -1.109375 -0.25q-0.46875 -0.265625 -0.796875 -0.75q-0.3125 -0.484375 -0.484375 -1.15625q-0.15625 -0.671875 -0.15625 -1.484375zm1.3125 -0.09375q0 1.34375 0.390625 2.0q0.390625 0.65625 1.109375 0.65625q0.484375 0 1.015625 -0.4375q0.53125 -0.4375 1.125 -1.28125l0 -3.421875q-0.3125 -0.140625 -0.6875 -0.21875q-0.375 -0.078125 -0.75 -0.078125q-1.046875 0 -1.625 0.671875q-0.578125 0.671875 -0.578125 2.109375zm13.339325 -0.453125q0 0.265625 -0.015625 0.453125q0 0.1875 -0.015625 0.34375l-5.171875 0q0 1.125 0.625 1.734375q0.640625 0.59375 1.828125 0.59375q0.3125 0 0.640625 -0.015625q0.328125 -0.03125 0.625 -0.0625q0.296875 -0.046875 0.5625 -0.09375q0.28125 -0.0625 0.515625 -0.140625l0 1.046875q-0.515625 0.15625 -1.171875 0.234375q-0.65625 0.09375 -1.359375 0.09375q-0.9375 0 -1.625 -0.25q-0.6875 -0.25 -1.125 -0.734375q-0.421875 -0.5 -0.640625 -1.203125q-0.203125 -0.703125 -0.203125 -1.59375q0 -0.78125 0.21875 -1.46875q0.21875 -0.703125 0.640625 -1.21875q0.4375 -0.53125 1.0625 -0.828125q0.625 -0.3125 1.421875 -0.3125q0.765625 0 1.359375 0.25q0.59375 0.234375 1.0 0.6875q0.40625 0.4375 0.609375 1.078125q0.21875 0.625 0.21875 1.40625zm-1.328125 -0.1875q0.015625 -0.484375 -0.109375 -0.890625q-0.109375 -0.40625 -0.359375 -0.703125q-0.234375 -0.296875 -0.609375 -0.453125q-0.359375 -0.171875 -0.84375 -0.171875q-0.421875 0 -0.765625 0.171875q-0.34375 0.15625 -0.59375 0.453125q-0.25 0.28125 -0.40625 0.703125q-0.140625 0.40625 -0.1875 0.890625l3.875 0zm9.6987 -4.90625q-1.015625 -0.21875 -1.734375 -0.21875q-1.71875 0 -1.71875 1.796875l0 1.296875l3.21875 0l0 1.0625l-3.21875 0l0 5.21875l-1.296875 0l0 -5.21875l-2.359375 0l0 -1.0625l2.359375 0l0 -1.21875q0 -2.9375 3.0625 -2.9375q0.765625 0 1.6875 0.171875l0 1.109375zm-7.703125 1.796875l0 0zm15.558105 4.328125q0 0.65625 -0.234375 1.234375q-0.234375 0.578125 -0.6875 1.015625q-0.4375 0.421875 -1.0625 0.671875q-0.609375 0.234375 -1.359375 0.234375q-0.796875 0 -1.40625 -0.25q-0.609375 -0.25 -1.015625 -0.765625q-0.40625 -0.53125 -0.6250305 -1.328125q-0.203125 -0.796875 -0.203125 -1.890625q0 -0.734375 0.09375 -1.421875q0.09375 -0.6875 0.31253052 -1.296875q0.21875 -0.609375 0.578125 -1.109375q0.375 -0.5 0.921875 -0.859375q0.546875 -0.375 1.28125 -0.578125q0.734375 -0.203125 1.71875 -0.203125l0.9375 0l0 1.125l-1.015625 0q-0.859375 0 -1.5 0.203125q-0.625 0.203125 -1.046875 0.578125q-0.421875 0.375 -0.65625 0.90625q-0.21875 0.515625 -0.28125 1.171875l-0.03125 0.296875q0.46875 -0.265625 1.0625 -0.421875q0.609375 -0.171875 1.3125 -0.171875q0.71875 0 1.265625 0.21875q0.546875 0.203125 0.90625 0.578125q0.375 0.375 0.546875 0.90625q0.1875 0.53125 0.1875 1.15625zm-1.328125 0.078125q0 -0.4375 -0.109375 -0.796875q-0.109375 -0.359375 -0.34375 -0.59375q-0.21875 -0.25 -0.5625 -0.375q-0.34375 -0.140625 -0.828125 -0.140625q-0.28125 0 -0.578125 0.046875q-0.28125 0.046875 -0.5625 0.140625q-0.265625 0.09375 -0.515625 0.21875q-0.25 0.109375 -0.453125 0.234375q0 0.953125 0.125 1.59375q0.140625 0.625 0.390625 1.015625q0.265625 0.375 0.640625 0.53125q0.390625 0.15625 0.890625 0.15625q0.421875 0 0.765625 -0.125q0.34375 -0.140625 0.59375 -0.40625q0.25 -0.265625 0.390625 -0.640625q0.15625 -0.375 0.15625 -0.859375zm9.026794 -0.109375q0 0.71875 -0.3125 1.3125q-0.296875 0.578125 -0.84375 1.0q-0.53125 0.40625 -1.265625 0.640625q-0.734375 0.234375 -1.578125 0.234375q-0.21875 0 -0.46875 -0.015625q-0.234375 0 -0.484375 -0.015625q-0.234375 -0.015625 -0.46875 -0.046875q-0.234375 -0.015625 -0.421875 -0.046875l0 -1.15625q0.40625 0.09375 0.90625 0.140625q0.515625 0.046875 1.03125 0.046875q0.59375 0 1.0625 -0.140625q0.46875 -0.140625 0.796875 -0.390625q0.328125 -0.265625 0.5 -0.640625q0.171875 -0.375 0.171875 -0.828125q0 -0.90625 -0.640625 -1.3125q-0.640625 -0.40625 -1.84375 -0.40625l-1.8125 0l0 -4.890625l5.15625 0l0 1.125l-3.953125 0l0 2.6875l0.84375 0q0.6875 0 1.328125 0.125q0.65625 0.125 1.15625 0.4375q0.515625 0.296875 0.828125 0.828125q0.3125 0.515625 0.3125 1.3125zm8.776855 0.953125l-1.640625 0l0 2.109375l-1.296875 0l0 -2.109375l-4.609375 0l0 -1.125l4.078125 -6.34375l1.828125 0l0 6.34375l1.640625 0l0 1.125zm-2.9375 -6.234375l-3.328125 5.109375l3.328125 0l0 -5.109375zm10.839294 -0.8125q-1.015625 -0.21875 -1.734375 -0.21875q-1.71875 0 -1.71875 1.796875l0 1.296875l3.21875 0l0 1.0625l-3.21875 0l0 5.21875l-1.296875 0l0 -5.21875l-2.359375 0l0 -1.0625l2.359375 0l0 -1.21875q0 -2.9375 3.0625 -2.9375q0.765625 0 1.6875 0.171875l0 1.109375zm-7.703125 1.796875l0 0zm14.94873 7.09375q-0.5 0.1875 -1.03125 0.28125q-0.515625 0.09375 -1.078125 0.09375q-1.734375 0 -2.6875 -0.9375q-0.9375 -0.953125 -0.9375 -2.78125q0 -0.859375 0.265625 -1.5625q0.28125 -0.71875 0.765625 -1.21875q0.5 -0.515625 1.171875 -0.78125q0.6875 -0.28125 1.5 -0.28125q0.578125 0 1.078125 0.078125q0.5 0.078125 0.953125 0.265625l0 1.21875q-0.484375 -0.25 -0.984375 -0.359375q-0.484375 -0.125 -1.015625 -0.125q-0.484375 0 -0.921875 0.1875q-0.4375 0.1875 -0.765625 0.546875q-0.328125 0.34375 -0.53125 0.859375q-0.1875 0.5 -0.1875 1.140625q0 1.328125 0.640625 2.0q0.65625 0.65625 1.8125 0.65625q0.515625 0 1.0 -0.109375q0.5 -0.125 0.953125 -0.359375l0 1.1875zm8.058044 0q-0.5 0.1875 -1.03125 0.28125q-0.515625 0.09375 -1.078125 0.09375q-1.734375 0 -2.6875 -0.9375q-0.9375 -0.953125 -0.9375 -2.78125q0 -0.859375 0.265625 -1.5625q0.28125 -0.71875 0.765625 -1.21875q0.5 -0.515625 1.171875 -0.78125q0.6875 -0.28125 1.5 -0.28125q0.578125 0 1.078125 0.078125q0.5 0.078125 0.953125 0.265625l0 1.21875q-0.484375 -0.25 -0.984375 -0.359375q-0.484375 -0.125 -1.015625 -0.125q-0.484375 0 -0.921875 0.1875q-0.4375 0.1875 -0.765625 0.546875q-0.328125 0.34375 -0.53125 0.859375q-0.1875 0.5 -0.1875 1.140625q0 1.328125 0.640625 2.0q0.65625 0.65625 1.8125 0.65625q0.515625 0 1.0 -0.109375q0.5 -0.125 0.953125 -0.359375l0 1.1875zm8.058105 0q-0.5 0.1875 -1.03125 0.28125q-0.515625 0.09375 -1.078125 0.09375q-1.734375 0 -2.6875 -0.9375q-0.9375 -0.953125 -0.9375 -2.78125q0 -0.859375 0.265625 -1.5625q0.28125 -0.71875 0.765625 -1.21875q0.5 -0.515625 1.171875 -0.78125q0.6875 -0.28125 1.5 -0.28125q0.578125 0 1.078125 0.078125q0.5 0.078125 0.953125 0.265625l0 1.21875q-0.484375 -0.25 -0.984375 -0.359375q-0.484375 -0.125 -1.015625 -0.125q-0.484375 0 -0.921875 0.1875q-0.4375 0.1875 -0.765625 0.546875q-0.328125 0.34375 -0.53125 0.859375q-0.1875 0.5 -0.1875 1.140625q0 1.328125 0.640625 2.0q0.65625 0.65625 1.8125 0.65625q0.515625 0 1.0 -0.109375q0.5 -0.125 0.953125 -0.359375l0 1.1875zm9.026794 -1.84375l-1.640625 0l0 2.109375l-1.296875 0l0 -2.109375l-4.609375 0l0 -1.125l4.078125 -6.34375l1.828125 0l0 6.34375l1.640625 0l0 1.125zm-2.9375 -6.234375l-3.328125 5.109375l3.328125 0l0 -5.109375zm9.026855 8.34375l-0.03125 -0.984375q-0.59375 0.59375 -1.21875 0.859375q-0.609375 0.25 -1.296875 0.25q-0.625 0 -1.078125 -0.15625q-0.4375 -0.15625 -0.734375 -0.4375q-0.28125 -0.28125 -0.421875 -0.65625q-0.140625 -0.390625 -0.140625 -0.84375q0 -1.09375 0.828125 -1.71875q0.828125 -0.640625 2.4375 -0.640625l1.515625 0l0 -0.640625q0 -0.65625 -0.421875 -1.046875q-0.40625 -0.390625 -1.265625 -0.390625q-0.625 0 -1.234375 0.140625q-0.59375 0.140625 -1.234375 0.40625l0 -1.15625q0.234375 -0.09375 0.53125 -0.171875q0.296875 -0.078125 0.625 -0.140625q0.328125 -0.078125 0.6875 -0.109375q0.359375 -0.046875 0.734375 -0.046875q0.65625 0 1.1875 0.15625q0.546875 0.140625 0.90625 0.4375q0.375 0.296875 0.5625 0.75q0.203125 0.453125 0.203125 1.078125l0 5.0625l-1.140625 0zm-0.140625 -3.34375l-1.609375 0q-0.484375 0 -0.828125 0.09375q-0.34375 0.09375 -0.5625 0.265625q-0.21875 0.171875 -0.328125 0.421875q-0.09375 0.25 -0.09375 0.5625q0 0.203125 0.0625 0.40625q0.0625 0.1875 0.203125 0.34375q0.15625 0.140625 0.390625 0.234375q0.234375 0.078125 0.5625 0.078125q0.4375 0 1.0 -0.265625q0.578125 -0.265625 1.203125 -0.84375l0 -1.296875zm10.167419 1.234375l-1.640625 0l0 2.109375l-1.296875 0l0 -2.109375l-4.609375 0l0 -1.125l4.078125 -6.34375l1.828125 0l0 6.34375l1.640625 0l0 1.125zm-2.9375 -6.234375l-3.328125 5.109375l3.328125 0l0 -5.109375zm3.9331055 4.828125q0 -0.9375 0.25 -1.671875q0.265625 -0.734375 0.734375 -1.234375q0.46875 -0.5 1.125 -0.75q0.671875 -0.265625 1.484375 -0.265625q0.359375 0 0.6875 0.046875q0.34375 0.03125 0.671875 0.125l0 -3.078125l1.28125 0l0 10.34375l-1.140625 0l-0.03125 -1.390625q-0.546875 0.78125 -1.171875 1.15625q-0.609375 0.359375 -1.34375 0.359375q-0.625 0 -1.109375 -0.25q-0.46875 -0.265625 -0.796875 -0.75q-0.3125 -0.484375 -0.484375 -1.15625q-0.15625 -0.671875 -0.15625 -1.484375zm1.3125 -0.09375q0 1.34375 0.390625 2.0q0.390625 0.65625 1.109375 0.65625q0.484375 0 1.015625 -0.4375q0.53125 -0.4375 1.125 -1.28125l0 -3.421875q-0.3125 -0.140625 -0.6875 -0.21875q-0.375 -0.078125 -0.75 -0.078125q-1.046875 0 -1.625 0.671875q-0.578125 0.671875 -0.578125 2.109375zm13.339294 1.203125q0 0.609375 -0.25 1.09375q-0.25 0.46875 -0.703125 0.796875q-0.453125 0.3125 -1.0625 0.484375q-0.59375 0.15625 -1.3125 0.15625q-0.78125 0 -1.375 -0.171875q-0.59375 -0.171875 -1.0 -0.484375q-0.40625 -0.3125 -0.609375 -0.75q-0.203125 -0.4375 -0.203125 -0.953125q0 -0.875 0.484375 -1.515625q0.5 -0.640625 1.515625 -1.15625q-0.9375 -0.484375 -1.375 -1.0625q-0.421875 -0.578125 -0.421875 -1.328125q0 -0.46875 0.1875 -0.890625q0.1875 -0.4375 0.578125 -0.765625q0.390625 -0.34375 0.96875 -0.546875q0.578125 -0.203125 1.359375 -0.203125q0.734375 0 1.296875 0.15625q0.5625 0.15625 0.9375 0.453125q0.390625 0.296875 0.578125 0.71875q0.1875 0.40625 0.1875 0.921875q0 0.828125 -0.46875 1.421875q-0.46875 0.578125 -1.3125 1.015625q0.421875 0.21875 0.78125 0.484375q0.375 0.25 0.640625 0.5625q0.265625 0.3125 0.421875 0.703125q0.15625 0.390625 0.15625 0.859375zm-1.53125 -4.953125q0 -0.640625 -0.453125 -0.953125q-0.453125 -0.328125 -1.28125 -0.328125q-0.828125 0 -1.28125 0.3125q-0.453125 0.3125 -0.453125 0.9375q0 0.296875 0.109375 0.546875q0.109375 0.234375 0.328125 0.453125q0.234375 0.203125 0.578125 0.40625q0.34375 0.203125 0.828125 0.421875q0.828125 -0.390625 1.21875 -0.8125q0.40625 -0.421875 0.40625 -0.984375zm0.140625 5.046875q0 -0.265625 -0.09375 -0.515625q-0.078125 -0.265625 -0.3125 -0.515625q-0.21875 -0.25 -0.609375 -0.5q-0.375 -0.25 -0.96875 -0.5q-0.5 0.234375 -0.84375 0.46875q-0.328125 0.234375 -0.546875 0.484375q-0.203125 0.25 -0.296875 0.515625q-0.078125 0.265625 -0.078125 0.546875q0 0.328125 0.140625 0.59375q0.140625 0.25 0.390625 0.421875q0.25 0.171875 0.59375 0.265625q0.34375 0.09375 0.75 0.09375q0.390625 0 0.734375 -0.078125q0.34375 -0.09375 0.59375 -0.25q0.25 -0.171875 0.390625 -0.421875q0.15625 -0.25 0.15625 -0.609375z" fill-rule="nonzero"></path><path fill="#000000" d="m652.6853 383.88052l-6.0 0l0 -1.1875l2.453125 0l0 -6.984375l-2.296875 1.25l-0.46875 -1.09375l3.046875 -1.59375l1.125 0l0 8.421875l2.140625 0l0 1.1875zm8.620605 -2.109375l-1.640625 0l0 2.109375l-1.296875 0l0 -2.109375l-4.609375 0l0 -1.125l4.078125 -6.34375l1.828125 0l0 6.34375l1.640625 0l0 1.125zm-2.9375 -6.234375l-3.328125 5.109375l3.328125 0l0 -5.109375zm10.276794 5.28125q0 0.71875 -0.3125 1.3125q-0.296875 0.578125 -0.84375 1.0q-0.53125 0.40625 -1.265625 0.640625q-0.734375 0.234375 -1.578125 0.234375q-0.21875 0 -0.46875 -0.015625q-0.234375 0 -0.484375 -0.015625q-0.234375 -0.015625 -0.46875 -0.046875q-0.234375 -0.015625 -0.421875 -0.046875l0 -1.15625q0.40625 0.09375 0.90625 0.140625q0.515625 0.046875 1.03125 0.046875q0.59375 0 1.0625 -0.140625q0.46875 -0.140625 0.796875 -0.390625q0.328125 -0.265625 0.5 -0.640625q0.171875 -0.375 0.171875 -0.828125q0 -0.90625 -0.640625 -1.3125q-0.640625 -0.40625 -1.84375 -0.40625l-1.8125 0l0 -4.890625l5.15625 0l0 1.125l-3.953125 0l0 2.6875l0.84375 0q0.6875 0 1.328125 0.125q0.65625 0.125 1.15625 0.4375q0.515625 0.296875 0.828125 0.828125q0.3125 0.515625 0.3125 1.3125zm8.41748 0.03125q0 0.65625 -0.234375 1.234375q-0.234375 0.578125 -0.6875 1.015625q-0.4375 0.421875 -1.0625 0.671875q-0.609375 0.234375 -1.359375 0.234375q-0.796875 0 -1.40625 -0.25q-0.609375 -0.25 -1.015625 -0.765625q-0.40625 -0.53125 -0.625 -1.328125q-0.203125 -0.796875 -0.203125 -1.890625q0 -0.734375 0.09375 -1.421875q0.09375 -0.6875 0.3125 -1.296875q0.21875 -0.609375 0.578125 -1.109375q0.375 -0.5 0.921875 -0.859375q0.546875 -0.375 1.28125 -0.578125q0.734375 -0.203125 1.71875 -0.203125l0.9375 0l0 1.125l-1.015625 0q-0.859375 0 -1.5 0.203125q-0.625 0.203125 -1.046875 0.578125q-0.421875 0.375 -0.65625 0.90625q-0.21875 0.515625 -0.28125 1.171875l-0.03125 0.296875q0.46875 -0.265625 1.0625 -0.421875q0.609375 -0.171875 1.3125 -0.171875q0.71875 0 1.265625 0.21875q0.546875 0.203125 0.90625 0.578125q0.375 0.375 0.546875 0.90625q0.1875 0.53125 0.1875 1.15625zm-1.328125 0.078125q0 -0.4375 -0.109375 -0.796875q-0.109375 -0.359375 -0.34375 -0.59375q-0.21875 -0.25 -0.5625 -0.375q-0.34375 -0.140625 -0.828125 -0.140625q-0.28125 0 -0.578125 0.046875q-0.28125 0.046875 -0.5625 0.140625q-0.265625 0.09375 -0.515625 0.21875q-0.25 0.109375 -0.453125 0.234375q0 0.953125 0.125 1.59375q0.140625 0.625 0.390625 1.015625q0.265625 0.375 0.640625 0.53125q0.390625 0.15625 0.890625 0.15625q0.421875 0 0.765625 -0.125q0.34375 -0.140625 0.59375 -0.40625q0.25 -0.265625 0.390625 -0.640625q0.15625 -0.375 0.15625 -0.859375zm9.589294 -6.203125q-1.015625 -0.21875 -1.734375 -0.21875q-1.71875 0 -1.71875 1.796875l0 1.296875l3.21875 0l0 1.0625l-3.21875 0l0 5.21875l-1.296875 0l0 -5.21875l-2.359375 0l0 -1.0625l2.359375 0l0 -1.21875q0 -2.9375 3.0625 -2.9375q0.765625 0 1.6875 0.171875l0 1.109375zm-7.703125 1.796875l0 0zm8.85498 3.84375q0 -0.9375 0.25 -1.671875q0.265625 -0.734375 0.734375 -1.234375q0.46875 -0.5 1.125 -0.75q0.671875 -0.265625 1.484375 -0.265625q0.359375 0 0.6875 0.046875q0.34375 0.03125 0.671875 0.125l0 -3.078125l1.28125 0l0 10.34375l-1.140625 0l-0.03125 -1.390625q-0.546875 0.78125 -1.171875 1.15625q-0.609375 0.359375 -1.34375 0.359375q-0.625 0 -1.109375 -0.25q-0.46875 -0.265625 -0.796875 -0.75q-0.3125 -0.484375 -0.484375 -1.15625q-0.15625 -0.671875 -0.15625 -1.484375zm1.3125 -0.09375q0 1.34375 0.390625 2.0q0.390625 0.65625 1.109375 0.65625q0.484375 0 1.015625 -0.4375q0.53125 -0.4375 1.125 -1.28125l0 -3.421875q-0.3125 -0.140625 -0.6875 -0.21875q-0.375 -0.078125 -0.75 -0.078125q-1.046875 0 -1.625 0.671875q-0.578125 0.671875 -0.578125 2.109375zm13.245544 3.609375l-6.0 0l0 -1.1875l2.453125 0l0 -6.984375l-2.296875 1.25l-0.46875 -1.09375l3.046875 -1.59375l1.125 0l0 8.421875l2.140625 0l0 1.1875zm8.370605 -4.78125q0 1.09375 -0.234375 2.0q-0.21875 0.90625 -0.671875 1.5625q-0.4375 0.640625 -1.109375 1.0q-0.65625 0.34375 -1.546875 0.34375q-0.765625 0 -1.40625 -0.28125q-0.625 -0.296875 -1.078125 -0.890625q-0.4375 -0.59375 -0.6875 -1.53125q-0.234375 -0.9375 -0.234375 -2.203125q0 -1.09375 0.21875 -2.0q0.234375 -0.921875 0.671875 -1.5625q0.453125 -0.65625 1.109375 -1.0q0.671875 -0.359375 1.5625 -0.359375q0.765625 0 1.390625 0.296875q0.625 0.28125 1.078125 0.890625q0.453125 0.59375 0.6875 1.53125q0.25 0.921875 0.25 2.203125zm-1.296875 0.046875q0 -0.25 -0.015625 -0.5q-0.015625 -0.25 -0.046875 -0.484375l-4.046875 3.015625q0.109375 0.375 0.28125 0.703125q0.171875 0.328125 0.40625 0.5625q0.234375 0.21875 0.53125 0.359375q0.3125 0.125 0.703125 0.125q0.5 0 0.90625 -0.234375q0.40625 -0.25 0.6875 -0.71875q0.28125 -0.484375 0.4375 -1.1875q0.15625 -0.71875 0.15625 -1.640625zm-4.375 -0.09375q0 0.234375 0 0.46875q0 0.21875 0.03125 0.421875l4.046875 -2.984375q-0.109375 -0.375 -0.28125 -0.6875q-0.171875 -0.3125 -0.40625 -0.53125q-0.234375 -0.21875 -0.53125 -0.34375q-0.296875 -0.125 -0.671875 -0.125q-0.5 0 -0.90625 0.25q-0.40625 0.234375 -0.703125 0.71875q-0.28125 0.46875 -0.4375 1.1875q-0.140625 0.703125 -0.140625 1.625zm13.261169 1.765625q0 0.71875 -0.3125 1.3125q-0.296875 0.578125 -0.84375 1.0q-0.53125 0.40625 -1.265625 0.640625q-0.734375 0.234375 -1.578125 0.234375q-0.21875 0 -0.46875 -0.015625q-0.234375 0 -0.484375 -0.015625q-0.234375 -0.015625 -0.46875 -0.046875q-0.234375 -0.015625 -0.421875 -0.046875l0 -1.15625q0.40625 0.09375 0.90625 0.140625q0.515625 0.046875 1.03125 0.046875q0.59375 0 1.0625 -0.140625q0.46875 -0.140625 0.796875 -0.390625q0.328125 -0.265625 0.5 -0.640625q0.171875 -0.375 0.171875 -0.828125q0 -0.90625 -0.640625 -1.3125q-0.640625 -0.40625 -1.84375 -0.40625l-1.8125 0l0 -4.890625l5.15625 0l0 1.125l-3.953125 0l0 2.6875l0.84375 0q0.6875 0 1.328125 0.125q0.65625 0.125 1.15625 0.4375q0.515625 0.296875 0.828125 0.828125q0.3125 0.515625 0.3125 1.3125zm8.245605 -2.265625q0 1.40625 -0.34375 2.421875q-0.328125 1.0 -0.984375 1.65625q-0.65625 0.640625 -1.640625 0.953125q-0.96875 0.296875 -2.25 0.296875l-0.796875 0l0 -1.109375l0.890625 0q0.953125 0 1.640625 -0.1875q0.6875 -0.203125 1.140625 -0.5625q0.453125 -0.375 0.6875 -0.90625q0.25 -0.53125 0.3125 -1.21875l0.03125 -0.296875q-0.46875 0.28125 -1.078125 0.453125q-0.59375 0.15625 -1.296875 0.15625q-0.71875 0 -1.265625 -0.21875q-0.546875 -0.21875 -0.921875 -0.59375q-0.359375 -0.375 -0.546875 -0.890625q-0.171875 -0.53125 -0.171875 -1.15625q0 -0.65625 0.234375 -1.234375q0.25 -0.578125 0.6875 -1.0q0.4375 -0.4375 1.03125 -0.6875q0.609375 -0.25 1.34375 -0.25q0.71875 0 1.3125 0.234375q0.609375 0.234375 1.046875 0.765625q0.4375 0.515625 0.6875 1.359375q0.25 0.828125 0.25 2.015625zm-3.34375 -3.328125q-0.421875 0 -0.765625 0.140625q-0.34375 0.125 -0.609375 0.390625q-0.25 0.265625 -0.390625 0.640625q-0.140625 0.375 -0.140625 0.875q0 0.4375 0.09375 0.796875q0.109375 0.34375 0.328125 0.59375q0.234375 0.25 0.578125 0.390625q0.359375 0.125 0.84375 0.125q0.265625 0 0.546875 -0.046875q0.296875 -0.0625 0.5625 -0.140625q0.28125 -0.09375 0.53125 -0.203125q0.25 -0.125 0.453125 -0.265625q0 -0.9375 -0.140625 -1.5625q-0.140625 -0.640625 -0.40625 -1.015625q-0.265625 -0.390625 -0.640625 -0.546875q-0.375 -0.171875 -0.84375 -0.171875zm11.526794 4.84375q0 0.921875 -0.25 1.640625q-0.25 0.71875 -0.71875 1.21875q-0.46875 0.5 -1.140625 0.78125q-0.65625 0.265625 -1.484375 0.265625q-0.65625 0 -1.34375 -0.125q-0.671875 -0.125 -1.34375 -0.40625l0 -9.90625l1.28125 0l0 2.84375l-0.0625 1.359375q0.546875 -0.734375 1.171875 -1.03125q0.625 -0.3125 1.34375 -0.3125q0.625 0 1.09375 0.265625q0.484375 0.265625 0.8125 0.75q0.328125 0.46875 0.484375 1.15625q0.15625 0.671875 0.15625 1.5zm-1.296875 0.0625q0 -0.578125 -0.09375 -1.0625q-0.078125 -0.484375 -0.265625 -0.828125q-0.171875 -0.34375 -0.46875 -0.53125q-0.28125 -0.203125 -0.671875 -0.203125q-0.25 0 -0.5 0.078125q-0.25 0.078125 -0.515625 0.265625q-0.265625 0.171875 -0.5625 0.46875q-0.296875 0.296875 -0.625 0.734375l0 3.5625q0.359375 0.15625 0.75 0.25q0.390625 0.078125 0.75 0.078125q0.4375 0 0.828125 -0.140625q0.40625 -0.140625 0.703125 -0.46875q0.3125 -0.328125 0.484375 -0.859375q0.1875 -0.546875 0.1875 -1.34375zm7.7924805 3.75l-0.03125 -0.984375q-0.59375 0.59375 -1.21875 0.859375q-0.609375 0.25 -1.296875 0.25q-0.625 0 -1.078125 -0.15625q-0.4375 -0.15625 -0.734375 -0.4375q-0.28125 -0.28125 -0.421875 -0.65625q-0.140625 -0.390625 -0.140625 -0.84375q0 -1.09375 0.828125 -1.71875q0.828125 -0.640625 2.4375 -0.640625l1.515625 0l0 -0.640625q0 -0.65625 -0.421875 -1.046875q-0.40625 -0.390625 -1.265625 -0.390625q-0.625 0 -1.234375 0.140625q-0.59375 0.140625 -1.234375 0.40625l0 -1.15625q0.234375 -0.09375 0.53125 -0.171875q0.296875 -0.078125 0.625 -0.140625q0.328125 -0.078125 0.6875 -0.109375q0.359375 -0.046875 0.734375 -0.046875q0.65625 0 1.1875 0.15625q0.546875 0.140625 0.90625 0.4375q0.375 0.296875 0.5625 0.75q0.203125 0.453125 0.203125 1.078125l0 5.0625l-1.140625 0zm-0.140625 -3.34375l-1.609375 0q-0.484375 0 -0.828125 0.09375q-0.34375 0.09375 -0.5625 0.265625q-0.21875 0.171875 -0.328125 0.421875q-0.09375 0.25 -0.09375 0.5625q0 0.203125 0.0625 0.40625q0.0625 0.1875 0.203125 0.34375q0.15625 0.140625 0.390625 0.234375q0.234375 0.078125 0.5625 0.078125q0.4375 0 1.0 -0.265625q0.578125 -0.265625 1.203125 -0.84375l0 -1.296875zm9.917419 -1.4375q0 1.09375 -0.234375 2.0q-0.21875 0.90625 -0.671875 1.5625q-0.4375 0.640625 -1.109375 1.0q-0.65625 0.34375 -1.546875 0.34375q-0.765625 0 -1.40625 -0.28125q-0.625 -0.296875 -1.078125 -0.890625q-0.4375 -0.59375 -0.6875 -1.53125q-0.234375 -0.9375 -0.234375 -2.203125q0 -1.09375 0.21875 -2.0q0.234375 -0.921875 0.671875 -1.5625q0.453125 -0.65625 1.109375 -1.0q0.671875 -0.359375 1.5625 -0.359375q0.765625 0 1.390625 0.296875q0.625 0.28125 1.078125 0.890625q0.453125 0.59375 0.6875 1.53125q0.25 0.921875 0.25 2.203125zm-1.296875 0.046875q0 -0.25 -0.015625 -0.5q-0.015625 -0.25 -0.046875 -0.484375l-4.046875 3.015625q0.109375 0.375 0.28125 0.703125q0.171875 0.328125 0.40625 0.5625q0.234375 0.21875 0.53125 0.359375q0.3125 0.125 0.703125 0.125q0.5 0 0.90625 -0.234375q0.40625 -0.25 0.6875 -0.71875q0.28125 -0.484375 0.4375 -1.1875q0.15625 -0.71875 0.15625 -1.640625zm-4.375 -0.09375q0 0.234375 0 0.46875q0 0.21875 0.03125 0.421875l4.046875 -2.984375q-0.109375 -0.375 -0.28125 -0.6875q-0.171875 -0.3125 -0.40625 -0.53125q-0.234375 -0.21875 -0.53125 -0.34375q-0.296875 -0.125 -0.671875 -0.125q-0.5 0 -0.90625 0.25q-0.40625 0.234375 -0.703125 0.71875q-0.28125 0.46875 -0.4375 1.1875q-0.140625 0.703125 -0.140625 1.625zm13.01123 4.5625q-0.5 0.1875 -1.03125 0.28125q-0.515625 0.09375 -1.078125 0.09375q-1.734375 0 -2.6875 -0.9375q-0.9375 -0.953125 -0.9375 -2.78125q0 -0.859375 0.265625 -1.5625q0.28125 -0.71875 0.765625 -1.21875q0.5 -0.515625 1.171875 -0.78125q0.6875 -0.28125 1.5 -0.28125q0.578125 0 1.078125 0.078125q0.5 0.078125 0.953125 0.265625l0 1.21875q-0.484375 -0.25 -0.984375 -0.359375q-0.484375 -0.125 -1.015625 -0.125q-0.484375 0 -0.921875 0.1875q-0.4375 0.1875 -0.765625 0.546875q-0.328125 0.34375 -0.53125 0.859375q-0.1875 0.5 -0.1875 1.140625q0 1.328125 0.640625 2.0q0.65625 0.65625 1.8125 0.65625q0.515625 0 1.0 -0.109375q0.5 -0.125 0.953125 -0.359375l0 1.1875zm8.558044 -2.140625q0 0.609375 -0.25 1.09375q-0.25 0.46875 -0.703125 0.796875q-0.453125 0.3125 -1.0625 0.484375q-0.59375 0.15625 -1.3125 0.15625q-0.78125 0 -1.375 -0.171875q-0.59375 -0.171875 -1.0 -0.484375q-0.40625 -0.3125 -0.609375 -0.75q-0.203125 -0.4375 -0.203125 -0.953125q0 -0.875 0.484375 -1.515625q0.5 -0.640625 1.515625 -1.15625q-0.9375 -0.484375 -1.375 -1.0625q-0.421875 -0.578125 -0.421875 -1.328125q0 -0.46875 0.1875 -0.890625q0.1875 -0.4375 0.578125 -0.765625q0.390625 -0.34375 0.96875 -0.546875q0.578125 -0.203125 1.359375 -0.203125q0.734375 0 1.296875 0.15625q0.5625 0.15625 0.9375 0.453125q0.390625 0.296875 0.578125 0.71875q0.1875 0.40625 0.1875 0.921875q0 0.828125 -0.46875 1.421875q-0.46875 0.578125 -1.3125 1.015625q0.421875 0.21875 0.78125 0.484375q0.375 0.25 0.640625 0.5625q0.265625 0.3125 0.421875 0.703125q0.15625 0.390625 0.15625 0.859375zm-1.53125 -4.953125q0 -0.640625 -0.453125 -0.953125q-0.453125 -0.328125 -1.28125 -0.328125q-0.828125 0 -1.28125 0.3125q-0.453125 0.3125 -0.453125 0.9375q0 0.296875 0.109375 0.546875q0.109375 0.234375 0.328125 0.453125q0.234375 0.203125 0.578125 0.40625q0.34375 0.203125 0.828125 0.421875q0.828125 -0.390625 1.21875 -0.8125q0.40625 -0.421875 0.40625 -0.984375zm0.140625 5.046875q0 -0.265625 -0.09375 -0.515625q-0.078125 -0.265625 -0.3125 -0.515625q-0.21875 -0.25 -0.609375 -0.5q-0.375 -0.25 -0.96875 -0.5q-0.5 0.234375 -0.84375 0.46875q-0.328125 0.234375 -0.546875 0.484375q-0.203125 0.25 -0.296875 0.515625q-0.078125 0.265625 -0.078125 0.546875q0 0.328125 0.140625 0.59375q0.140625 0.25 0.390625 0.421875q0.25 0.171875 0.59375 0.265625q0.34375 0.09375 0.75 0.09375q0.390625 0 0.734375 -0.078125q0.34375 -0.09375 0.59375 -0.25q0.25 -0.171875 0.390625 -0.421875q0.15625 -0.25 0.15625 -0.609375zm9.44873 2.3125l-6.3125 0l0 -1.140625l2.46875 -2.46875q0.609375 -0.59375 0.984375 -1.03125q0.390625 -0.4375 0.59375 -0.796875q0.21875 -0.375 0.296875 -0.6875q0.078125 -0.328125 0.078125 -0.703125q0 -0.34375 -0.109375 -0.65625q-0.09375 -0.328125 -0.296875 -0.5625q-0.1875 -0.25 -0.5 -0.390625q-0.3125 -0.140625 -0.75 -0.140625q-0.609375 0 -1.109375 0.28125q-0.5 0.265625 -0.921875 0.6875l-0.703125 -0.828125q0.546875 -0.578125 1.25 -0.921875q0.703125 -0.34375 1.640625 -0.34375q0.640625 0 1.15625 0.1875q0.53125 0.1875 0.90625 0.546875q0.390625 0.359375 0.59375 0.890625q0.21875 0.515625 0.21875 1.15625q0 0.5625 -0.15625 1.03125q-0.140625 0.46875 -0.4375 0.9375q-0.296875 0.453125 -0.75 0.953125q-0.453125 0.5 -1.0625 1.09375l-1.734375 1.6875l4.65625 0l0 1.21875zm7.8080444 -3.0625q0 0.71875 -0.3125 1.3125q-0.296875 0.578125 -0.84375 1.0q-0.53125 0.40625 -1.265625 0.640625q-0.734375 0.234375 -1.578125 0.234375q-0.21875 0 -0.46875 -0.015625q-0.234375 0 -0.484375 -0.015625q-0.234375 -0.015625 -0.46875 -0.046875q-0.234375 -0.015625 -0.421875 -0.046875l0 -1.15625q0.40625 0.09375 0.90625 0.140625q0.515625 0.046875 1.03125 0.046875q0.59375 0 1.0625 -0.140625q0.46875 -0.140625 0.796875 -0.390625q0.328125 -0.265625 0.5 -0.640625q0.171875 -0.375 0.171875 -0.828125q0 -0.90625 -0.640625 -1.3125q-0.640625 -0.40625 -1.84375 -0.40625l-1.8125 0l0 -4.890625l5.15625 0l0 1.125l-3.953125 0l0 2.6875l0.84375 0q0.6875 0 1.328125 0.125q0.65625 0.125 1.15625 0.4375q0.515625 0.296875 0.828125 0.828125q0.3125 0.515625 0.3125 1.3125zm8.214355 3.0625l-6.0 0l0 -1.1875l2.453125 0l0 -6.984375l-2.296875 1.25l-0.46875 -1.09375l3.046875 -1.59375l1.125 0l0 8.421875l2.140625 0l0 1.1875zm8.151794 -4.0625q0 0.265625 -0.015625 0.453125q0 0.1875 -0.015625 0.34375l-5.171875 0q0 1.125 0.625 1.734375q0.640625 0.59375 1.828125 0.59375q0.3125 0 0.640625 -0.015625q0.328125 -0.03125 0.625 -0.0625q0.296875 -0.046875 0.5625 -0.09375q0.28125 -0.0625 0.515625 -0.140625l0 1.046875q-0.515625 0.15625 -1.171875 0.234375q-0.65625 0.09375 -1.359375 0.09375q-0.9375 0 -1.625 -0.25q-0.6875 -0.25 -1.125 -0.734375q-0.421875 -0.5 -0.640625 -1.203125q-0.203125 -0.703125 -0.203125 -1.59375q0 -0.78125 0.21875 -1.46875q0.21875 -0.703125 0.640625 -1.21875q0.4375 -0.53125 1.0625 -0.828125q0.625 -0.3125 1.421875 -0.3125q0.765625 0 1.359375 0.25q0.59375 0.234375 1.0 0.6875q0.40625 0.4375 0.609375 1.078125q0.21875 0.625 0.21875 1.40625zm-1.328125 -0.1875q0.015625 -0.484375 -0.109375 -0.890625q-0.109375 -0.40625 -0.359375 -0.703125q-0.234375 -0.296875 -0.609375 -0.453125q-0.359375 -0.171875 -0.84375 -0.171875q-0.421875 0 -0.765625 0.171875q-0.34375 0.15625 -0.59375 0.453125q-0.25 0.28125 -0.40625 0.703125q-0.140625 0.40625 -0.1875 0.890625l3.875 0zm7.8862305 4.25l-0.03125 -0.984375q-0.59375 0.59375 -1.21875 0.859375q-0.609375 0.25 -1.296875 0.25q-0.625 0 -1.078125 -0.15625q-0.4375 -0.15625 -0.734375 -0.4375q-0.28125 -0.28125 -0.421875 -0.65625q-0.140625 -0.390625 -0.140625 -0.84375q0 -1.09375 0.828125 -1.71875q0.828125 -0.640625 2.4375 -0.640625l1.515625 0l0 -0.640625q0 -0.65625 -0.421875 -1.046875q-0.40625 -0.390625 -1.265625 -0.390625q-0.625 0 -1.234375 0.140625q-0.59375 0.140625 -1.234375 0.40625l0 -1.15625q0.234375 -0.09375 0.53125 -0.171875q0.296875 -0.078125 0.625 -0.140625q0.328125 -0.078125 0.6875 -0.109375q0.359375 -0.046875 0.734375 -0.046875q0.65625 0 1.1875 0.15625q0.546875 0.140625 0.90625 0.4375q0.375 0.296875 0.5625 0.75q0.203125 0.453125 0.203125 1.078125l0 5.0625l-1.140625 0zm-0.140625 -3.34375l-1.609375 0q-0.484375 0 -0.828125 0.09375q-0.34375 0.09375 -0.5625 0.265625q-0.21875 0.171875 -0.328125 0.421875q-0.09375 0.25 -0.09375 0.5625q0 0.203125 0.0625 0.40625q0.0625 0.1875 0.203125 0.34375q0.15625 0.140625 0.390625 0.234375q0.234375 0.078125 0.5625 0.078125q0.4375 0 1.0 -0.265625q0.578125 -0.265625 1.203125 -0.84375l0 -1.296875zm10.167419 1.234375l-1.640625 0l0 2.109375l-1.296875 0l0 -2.109375l-4.609375 0l0 -1.125l4.078125 -6.34375l1.828125 0l0 6.34375l1.640625 0l0 1.125zm-2.9375 -6.234375l-3.328125 5.109375l3.328125 0l0 -5.109375zm10.339355 5.4375q0 0.625 -0.265625 1.1875q-0.25 0.546875 -0.765625 0.96875q-0.515625 0.40625 -1.296875 0.640625q-0.765625 0.234375 -1.796875 0.234375q-0.578125 0 -1.03125 -0.03125q-0.453125 -0.03125 -0.84375 -0.09375l0 -1.140625q0.453125 0.078125 0.953125 0.125q0.515625 0.046875 1.03125 0.046875q0.71875 0 1.21875 -0.125q0.515625 -0.140625 0.84375 -0.375q0.328125 -0.25 0.46875 -0.59375q0.140625 -0.34375 0.140625 -0.765625q0 -0.40625 -0.171875 -0.6875q-0.171875 -0.296875 -0.5 -0.5q-0.3125 -0.203125 -0.765625 -0.296875q-0.4375 -0.09375 -0.953125 -0.09375l-1.09375 0l0 -1.046875l1.109375 0q0.421875 0 0.78125 -0.109375q0.359375 -0.125 0.609375 -0.328125q0.25 -0.21875 0.375 -0.53125q0.140625 -0.3125 0.140625 -0.703125q0 -0.765625 -0.46875 -1.109375q-0.46875 -0.359375 -1.375 -0.359375q-0.484375 0 -1.0 0.09375q-0.5 0.09375 -1.09375 0.28125l0 -1.109375q0.25 -0.09375 0.53125 -0.15625q0.28125 -0.078125 0.5625 -0.125q0.28125 -0.046875 0.5625 -0.0625q0.28125 -0.03125 0.53125 -0.03125q0.765625 0 1.34375 0.171875q0.578125 0.15625 0.96875 0.46875q0.390625 0.296875 0.578125 0.75q0.203125 0.4375 0.203125 0.984375q0 0.8125 -0.421875 1.375q-0.421875 0.5625 -1.15625 0.890625q0.375 0.046875 0.734375 0.234375q0.375 0.171875 0.65625 0.453125q0.296875 0.265625 0.46875 0.640625q0.1875 0.375 0.1875 0.828125zm1.6517944 -0.609375q0 -0.9375 0.25 -1.671875q0.265625 -0.734375 0.734375 -1.234375q0.46875 -0.5 1.125 -0.75q0.671875 -0.265625 1.484375 -0.265625q0.359375 0 0.6875 0.046875q0.34375 0.03125 0.671875 0.125l0 -3.078125l1.28125 0l0 10.34375l-1.140625 0l-0.03125 -1.390625q-0.546875 0.78125 -1.171875 1.15625q-0.609375 0.359375 -1.34375 0.359375q-0.625 0 -1.109375 -0.25q-0.46875 -0.265625 -0.796875 -0.75q-0.3125 -0.484375 -0.484375 -1.15625q-0.15625 -0.671875 -0.15625 -1.484375zm1.3125 -0.09375q0 1.34375 0.390625 2.0q0.390625 0.65625 1.109375 0.65625q0.484375 0 1.015625 -0.4375q0.53125 -0.4375 1.125 -1.28125l0 -3.421875q-0.3125 -0.140625 -0.6875 -0.21875q-0.375 -0.078125 -0.75 -0.078125q-1.046875 0 -1.625 0.671875q-0.578125 0.671875 -0.578125 2.109375zm13.808105 1.5l-1.640625 0l0 2.109375l-1.296875 0l0 -2.109375l-4.609375 0l0 -1.125l4.078125 -6.34375l1.828125 0l0 6.34375l1.640625 0l0 1.125zm-2.9375 -6.234375l-3.328125 5.109375l3.328125 0l0 -5.109375zm3.9330444 4.828125q0 -0.9375 0.25 -1.671875q0.265625 -0.734375 0.734375 -1.234375q0.46875 -0.5 1.125 -0.75q0.671875 -0.265625 1.484375 -0.265625q0.359375 0 0.6875 0.046875q0.34375 0.03125 0.671875 0.125l0 -3.078125l1.28125 0l0 10.34375l-1.140625 0l-0.03125 -1.390625q-0.546875 0.78125 -1.171875 1.15625q-0.609375 0.359375 -1.34375 0.359375q-0.625 0 -1.109375 -0.25q-0.46875 -0.265625 -0.796875 -0.75q-0.3125 -0.484375 -0.484375 -1.15625q-0.15625 -0.671875 -0.15625 -1.484375zm1.3125 -0.09375q0 1.34375 0.390625 2.0q0.390625 0.65625 1.109375 0.65625q0.484375 0 1.015625 -0.4375q0.53125 -0.4375 1.125 -1.28125l0 -3.421875q-0.3125 -0.140625 -0.6875 -0.21875q-0.375 -0.078125 -0.75 -0.078125q-1.046875 0 -1.625 0.671875q-0.578125 0.671875 -0.578125 2.109375zm13.401855 -0.203125q0 0.921875 -0.25 1.640625q-0.25 0.71875 -0.71875 1.21875q-0.46875 0.5 -1.140625 0.78125q-0.65625 0.265625 -1.484375 0.265625q-0.65625 0 -1.34375 -0.125q-0.671875 -0.125 -1.34375 -0.40625l0 -9.90625l1.28125 0l0 2.84375l-0.0625 1.359375q0.546875 -0.734375 1.171875 -1.03125q0.625 -0.3125 1.34375 -0.3125q0.625 0 1.09375 0.265625q0.484375 0.265625 0.8125 0.75q0.328125 0.46875 0.484375 1.15625q0.15625 0.671875 0.15625 1.5zm-1.296875 0.0625q0 -0.578125 -0.09375 -1.0625q-0.078125 -0.484375 -0.265625 -0.828125q-0.171875 -0.34375 -0.46875 -0.53125q-0.28125 -0.203125 -0.671875 -0.203125q-0.25 0 -0.5 0.078125q-0.25 0.078125 -0.515625 0.265625q-0.265625 0.171875 -0.5625 0.46875q-0.296875 0.296875 -0.625 0.734375l0 3.5625q0.359375 0.15625 0.75 0.25q0.390625 0.078125 0.75 0.078125q0.4375 0 0.828125 -0.140625q0.40625 -0.140625 0.703125 -0.46875q0.3125 -0.328125 0.484375 -0.859375q0.1875 -0.546875 0.1875 -1.34375zm9.604919 -5.40625q-1.015625 -0.21875 -1.734375 -0.21875q-1.71875 0 -1.71875 1.796875l0 1.296875l3.21875 0l0 1.0625l-3.21875 0l0 5.21875l-1.296875 0l0 -5.21875l-2.359375 0l0 -1.0625l2.359375 0l0 -1.21875q0 -2.9375 3.0625 -2.9375q0.765625 0 1.6875 0.171875l0 1.109375zm-7.703125 1.796875l0 0zm15.51123 3.546875q0 0.921875 -0.25 1.640625q-0.25 0.71875 -0.71875 1.21875q-0.46875 0.5 -1.140625 0.78125q-0.65625 0.265625 -1.484375 0.265625q-0.65625 0 -1.34375 -0.125q-0.671875 -0.125 -1.34375 -0.40625l0 -9.90625l1.28125 0l0 2.84375l-0.0625 1.359375q0.546875 -0.734375 1.171875 -1.03125q0.625 -0.3125 1.34375 -0.3125q0.625 0 1.09375 0.265625q0.484375 0.265625 0.8125 0.75q0.328125 0.46875 0.484375 1.15625q0.15625 0.671875 0.15625 1.5zm-1.296875 0.0625q0 -0.578125 -0.09375 -1.0625q-0.078125 -0.484375 -0.265625 -0.828125q-0.171875 -0.34375 -0.46875 -0.53125q-0.28125 -0.203125 -0.671875 -0.203125q-0.25 0 -0.5 0.078125q-0.25 0.078125 -0.515625 0.265625q-0.265625 0.171875 -0.5625 0.46875q-0.296875 0.296875 -0.625 0.734375l0 3.5625q0.359375 0.15625 0.75 0.25q0.390625 0.078125 0.75 0.078125q0.4375 0 0.828125 -0.140625q0.40625 -0.140625 0.703125 -0.46875q0.3125 -0.328125 0.484375 -0.859375q0.1875 -0.546875 0.1875 -1.34375zm9.401794 0.71875q0 0.65625 -0.234375 1.234375q-0.234375 0.578125 -0.6875 1.015625q-0.4375 0.421875 -1.0625 0.671875q-0.609375 0.234375 -1.359375 0.234375q-0.796875 0 -1.40625 -0.25q-0.609375 -0.25 -1.015625 -0.765625q-0.40625 -0.53125 -0.625 -1.328125q-0.203125 -0.796875 -0.203125 -1.890625q0 -0.734375 0.09375 -1.421875q0.09375 -0.6875 0.3125 -1.296875q0.21875 -0.609375 0.578125 -1.109375q0.375 -0.5 0.921875 -0.859375q0.546875 -0.375 1.28125 -0.578125q0.734375 -0.203125 1.71875 -0.203125l0.9375 0l0 1.125l-1.015625 0q-0.859375 0 -1.5 0.203125q-0.625 0.203125 -1.046875 0.578125q-0.421875 0.375 -0.65625 0.90625q-0.21875 0.515625 -0.28125 1.171875l-0.03125 0.296875q0.46875 -0.265625 1.0625 -0.421875q0.609375 -0.171875 1.3125 -0.171875q0.71875 0 1.265625 0.21875q0.546875 0.203125 0.90625 0.578125q0.375 0.375 0.546875 0.90625q0.1875 0.53125 0.1875 1.15625zm-1.328125 0.078125q0 -0.4375 -0.109375 -0.796875q-0.109375 -0.359375 -0.34375 -0.59375q-0.21875 -0.25 -0.5625 -0.375q-0.34375 -0.140625 -0.828125 -0.140625q-0.28125 0 -0.578125 0.046875q-0.28125 0.046875 -0.5625 0.140625q-0.265625 0.09375 -0.515625 0.21875q-0.25 0.109375 -0.453125 0.234375q0 0.953125 0.125 1.59375q0.140625 0.625 0.390625 1.015625q0.265625 0.375 0.640625 0.53125q0.390625 0.15625 0.890625 0.15625q0.421875 0 0.765625 -0.125q0.34375 -0.140625 0.59375 -0.40625q0.25 -0.265625 0.390625 -0.640625q0.15625 -0.375 0.15625 -0.859375zm9.276855 -1.109375q0 0.265625 -0.015625 0.453125q0 0.1875 -0.015625 0.34375l-5.171875 0q0 1.125 0.625 1.734375q0.640625 0.59375 1.828125 0.59375q0.3125 0 0.640625 -0.015625q0.328125 -0.03125 0.625 -0.0625q0.296875 -0.046875 0.5625 -0.09375q0.28125 -0.0625 0.515625 -0.140625l0 1.046875q-0.515625 0.15625 -1.171875 0.234375q-0.65625 0.09375 -1.359375 0.09375q-0.9375 0 -1.625 -0.25q-0.6875 -0.25 -1.125 -0.734375q-0.421875 -0.5 -0.640625 -1.203125q-0.203125 -0.703125 -0.203125 -1.59375q0 -0.78125 0.21875 -1.46875q0.21875 -0.703125 0.640625 -1.21875q0.4375 -0.53125 1.0625 -0.828125q0.625 -0.3125 1.421875 -0.3125q0.765625 0 1.359375 0.25q0.59375 0.234375 1.0 0.6875q0.40625 0.4375 0.609375 1.078125q0.21875 0.625 0.21875 1.40625zm-1.328125 -0.1875q0.015625 -0.484375 -0.109375 -0.890625q-0.109375 -0.40625 -0.359375 -0.703125q-0.234375 -0.296875 -0.609375 -0.453125q-0.359375 -0.171875 -0.84375 -0.171875q-0.421875 0 -0.765625 0.171875q-0.34375 0.15625 -0.59375 0.453125q-0.25 0.28125 -0.40625 0.703125q-0.140625 0.40625 -0.1875 0.890625l3.875 0zm9.386169 0.1875q0 0.265625 -0.015625 0.453125q0 0.1875 -0.015625 0.34375l-5.171875 0q0 1.125 0.625 1.734375q0.640625 0.59375 1.828125 0.59375q0.3125 0 0.640625 -0.015625q0.328125 -0.03125 0.625 -0.0625q0.296875 -0.046875 0.5625 -0.09375q0.28125 -0.0625 0.515625 -0.140625l0 1.046875q-0.515625 0.15625 -1.171875 0.234375q-0.65625 0.09375 -1.359375 0.09375q-0.9375 0 -1.625 -0.25q-0.6875 -0.25 -1.125 -0.734375q-0.421875 -0.5 -0.640625 -1.203125q-0.203125 -0.703125 -0.203125 -1.59375q0 -0.78125 0.21875 -1.46875q0.21875 -0.703125 0.640625 -1.21875q0.4375 -0.53125 1.0625 -0.828125q0.625 -0.3125 1.421875 -0.3125q0.765625 0 1.359375 0.25q0.59375 0.234375 1.0 0.6875q0.40625 0.4375 0.609375 1.078125q0.21875 0.625 0.21875 1.40625zm-1.328125 -0.1875q0.015625 -0.484375 -0.109375 -0.890625q-0.109375 -0.40625 -0.359375 -0.703125q-0.234375 -0.296875 -0.609375 -0.453125q-0.359375 -0.171875 -0.84375 -0.171875q-0.421875 0 -0.765625 0.171875q-0.34375 0.15625 -0.59375 0.453125q-0.25 0.28125 -0.40625 0.703125q-0.140625 0.40625 -0.1875 0.890625l3.875 0zm9.38623 0.1875q0 0.265625 -0.015625 0.453125q0 0.1875 -0.015625 0.34375l-5.171875 0q0 1.125 0.625 1.734375q0.640625 0.59375 1.828125 0.59375q0.3125 0 0.640625 -0.015625q0.328125 -0.03125 0.625 -0.0625q0.296875 -0.046875 0.5625 -0.09375q0.28125 -0.0625 0.515625 -0.140625l0 1.046875q-0.515625 0.15625 -1.171875 0.234375q-0.65625 0.09375 -1.359375 0.09375q-0.9375 0 -1.625 -0.25q-0.6875 -0.25 -1.125 -0.734375q-0.421875 -0.5 -0.640625 -1.203125q-0.203125 -0.703125 -0.203125 -1.59375q0 -0.78125 0.21875 -1.46875q0.21875 -0.703125 0.640625 -1.21875q0.4375 -0.53125 1.0625 -0.828125q0.625 -0.3125 1.421875 -0.3125q0.765625 0 1.359375 0.25q0.59375 0.234375 1.0 0.6875q0.40625 0.4375 0.609375 1.078125q0.21875 0.625 0.21875 1.40625zm-1.328125 -0.1875q0.015625 -0.484375 -0.109375 -0.890625q-0.109375 -0.40625 -0.359375 -0.703125q-0.234375 -0.296875 -0.609375 -0.453125q-0.359375 -0.171875 -0.84375 -0.171875q-0.421875 0 -0.765625 0.171875q-0.34375 0.15625 -0.59375 0.453125q-0.25 0.28125 -0.40625 0.703125q-0.140625 0.40625 -0.1875 0.890625l3.875 0z" fill-rule="nonzero"></path><path fill="#000000" d="m61.447918 425.47632l-1.75 0l-3.421875 -3.9375l0 3.9375l-1.28125 0l0 -10.34375l1.28125 0l0 6.359375l3.296875 -3.375l1.6875 0l-3.453125 3.390625l3.640625 3.96875zm7.667446 -4.0625q0 0.265625 -0.015625 0.453125q0 0.1875 -0.015625 0.34375l-5.171871 0q0 1.125 0.6249962 1.734375q0.640625 0.59375 1.828125 0.59375q0.3125 0 0.640625 -0.015625q0.328125 -0.03125 0.625 -0.0625q0.296875 -0.046875 0.5625 -0.09375q0.28125 -0.0625 0.515625 -0.140625l0 1.046875q-0.515625 0.15625 -1.171875 0.234375q-0.65625 0.09375 -1.359375 0.09375q-0.9375 0 -1.625 -0.25q-0.6874962 -0.25 -1.1249962 -0.734375q-0.421875 -0.5 -0.640625 -1.203125q-0.203125 -0.703125 -0.203125 -1.59375q0 -0.78125 0.21875 -1.46875q0.21875 -0.703125 0.640625 -1.21875q0.4375 -0.53125 1.0624962 -0.828125q0.625 -0.3125 1.421875 -0.3125q0.765625 0 1.359375 0.25q0.59375 0.234375 1.0 0.6875q0.40625 0.4375 0.609375 1.078125q0.21875 0.625 0.21875 1.40625zm-1.328125 -0.1875q0.015625 -0.484375 -0.109375 -0.890625q-0.109375 -0.40625 -0.359375 -0.703125q-0.234375 -0.296875 -0.609375 -0.453125q-0.359375 -0.171875 -0.84375 -0.171875q-0.421875 0 -0.765625 0.171875q-0.34375 0.15625 -0.59375 0.453125q-0.25 0.28125 -0.40625 0.703125q-0.14062119 0.40625 -0.18749619 0.890625l3.8749962 0zm9.3862 0.1875q0 0.265625 -0.015625 0.453125q0 0.1875 -0.015625 0.34375l-5.171875 0q0 1.125 0.625 1.734375q0.640625 0.59375 1.828125 0.59375q0.3125 0 0.640625 -0.015625q0.328125 -0.03125 0.625 -0.0625q0.296875 -0.046875 0.5625 -0.09375q0.28125 -0.0625 0.515625 -0.140625l0 1.046875q-0.515625 0.15625 -1.171875 0.234375q-0.65625 0.09375 -1.359375 0.09375q-0.9375 0 -1.625 -0.25q-0.6875 -0.25 -1.125 -0.734375q-0.421875 -0.5 -0.640625 -1.203125q-0.203125 -0.703125 -0.203125 -1.59375q0 -0.78125 0.21875 -1.46875q0.21875 -0.703125 0.640625 -1.21875q0.4375 -0.53125 1.0625 -0.828125q0.625 -0.3125 1.421875 -0.3125q0.765625 0 1.359375 0.25q0.59375 0.234375 1.0 0.6875q0.40625 0.4375 0.609375 1.078125q0.21875 0.625 0.21875 1.40625zm-1.328125 -0.1875q0.015625 -0.484375 -0.109375 -0.890625q-0.109375 -0.40625 -0.359375 -0.703125q-0.234375 -0.296875 -0.609375 -0.453125q-0.359375 -0.171875 -0.84375 -0.171875q-0.421875 0 -0.765625 0.171875q-0.34375 0.15625 -0.59375 0.453125q-0.25 0.28125 -0.40625 0.703125q-0.140625 0.40625 -0.1875 0.890625l3.875 0zm9.4487 0.4375q0 0.984375 -0.28125 1.71875q-0.265625 0.734375 -0.75 1.21875q-0.484375 0.484375 -1.140625 0.734375q-0.65625 0.234375 -1.421875 0.234375q-0.359375 0 -0.703125 -0.046875q-0.34375 -0.03125 -0.703125 -0.125l0 3.078125l-1.28125 0l0 -10.359375l1.140625 0l0.078125 1.234375q0.546875 -0.75 1.171875 -1.046875q0.625 -0.3125 1.34375 -0.3125q0.625 0 1.09375 0.265625q0.484375 0.265625 0.8125 0.75q0.328125 0.46875 0.484375 1.15625q0.15625 0.671875 0.15625 1.5zm-1.296875 0.0625q0 -0.578125 -0.09375 -1.0625q-0.078125 -0.484375 -0.265625 -0.828125q-0.171875 -0.34375 -0.46875 -0.53125q-0.28125 -0.203125 -0.671875 -0.203125q-0.25 0 -0.5 0.078125q-0.25 0.078125 -0.515625 0.265625q-0.265625 0.171875 -0.5625 0.46875q-0.296875 0.296875 -0.625 0.734375l0 3.5625q0.34375 0.15625 0.734375 0.25q0.390625 0.078125 0.765625 0.078125q1.03125 0 1.609375 -0.703125q0.59375 -0.703125 0.59375 -2.109375zm9.7612 1.640625l-1.640625 0l0 2.109375l-1.296875 0l0 -2.109375l-4.609375 0l0 -1.125l4.078125 -6.34375l1.828125 0l0 6.34375l1.640625 0l0 1.125zm-2.9375 -6.234375l-3.328125 5.109375l3.328125 0l0 -5.109375z" fill-rule="nonzero"></path><path fill="#000000" d="m355.41818 425.47632l-0.03125 -0.984375q-0.59375 0.59375 -1.21875 0.859375q-0.609375 0.25 -1.296875 0.25q-0.625 0 -1.078125 -0.15625q-0.4375 -0.15625 -0.734375 -0.4375q-0.28125 -0.28125 -0.421875 -0.65625q-0.140625 -0.390625 -0.140625 -0.84375q0 -1.09375 0.828125 -1.71875q0.828125 -0.640625 2.4375 -0.640625l1.515625 0l0 -0.640625q0 -0.65625 -0.421875 -1.046875q-0.40625 -0.390625 -1.265625 -0.390625q-0.625 0 -1.234375 0.140625q-0.59375 0.140625 -1.234375 0.40625l0 -1.15625q0.234375 -0.09375 0.53125 -0.171875q0.296875 -0.078125 0.625 -0.140625q0.328125 -0.078125 0.6875 -0.109375q0.359375 -0.046875 0.734375 -0.046875q0.65625 0 1.1875 0.15625q0.546875 0.140625 0.90625 0.4375q0.375 0.296875 0.5625 0.75q0.203125 0.453125 0.203125 1.078125l0 5.0625l-1.140625 0zm-0.140625 -3.34375l-1.609375 0q-0.484375 0 -0.828125 0.09375q-0.34375 0.09375 -0.5625 0.265625q-0.21875 0.171875 -0.328125 0.421875q-0.09375 0.25 -0.09375 0.5625q0 0.203125 0.0625 0.40625q0.0625 0.1875 0.203125 0.34375q0.15625 0.140625 0.390625 0.234375q0.234375 0.078125 0.5625 0.078125q0.4375 0 1.0 -0.265625q0.578125 -0.265625 1.203125 -0.84375l0 -1.296875zm9.1987 3.078125q-0.5 0.1875 -1.03125 0.28125q-0.515625 0.09375 -1.078125 0.09375q-1.734375 0 -2.6875 -0.9375q-0.9375 -0.953125 -0.9375 -2.78125q0 -0.859375 0.265625 -1.5625q0.28125 -0.71875 0.765625 -1.21875q0.5 -0.515625 1.171875 -0.78125q0.6875 -0.28125 1.5 -0.28125q0.578125 0 1.078125 0.078125q0.5 0.078125 0.953125 0.265625l0 1.21875q-0.484375 -0.25 -0.984375 -0.359375q-0.484375 -0.125 -1.015625 -0.125q-0.484375 0 -0.921875 0.1875q-0.4375 0.1875 -0.765625 0.546875q-0.328125 0.34375 -0.53125 0.859375q-0.1875 0.5 -0.1875 1.140625q0 1.328125 0.640625 2.0q0.65625 0.65625 1.8125 0.65625q0.515625 0 1.0 -0.109375q0.5 -0.125 0.953125 -0.359375l0 1.1875zm8.620575 -3.546875q0 0.921875 -0.25 1.640625q-0.25 0.71875 -0.71875 1.21875q-0.46875 0.5 -1.140625 0.78125q-0.65625 0.265625 -1.484375 0.265625q-0.65625 0 -1.34375 -0.125q-0.671875 -0.125 -1.34375 -0.40625l0 -9.90625l1.28125 0l0 2.84375l-0.0625 1.359375q0.546875 -0.734375 1.171875 -1.03125q0.625 -0.3125 1.34375 -0.3125q0.625 0 1.09375 0.265625q0.484375 0.265625 0.8125 0.75q0.328125 0.46875 0.484375 1.15625q0.15625 0.671875 0.15625 1.5zm-1.296875 0.0625q0 -0.578125 -0.09375 -1.0625q-0.078125 -0.484375 -0.265625 -0.828125q-0.171875 -0.34375 -0.46875 -0.53125q-0.28125 -0.203125 -0.671875 -0.203125q-0.25 0 -0.5 0.078125q-0.25 0.078125 -0.515625 0.265625q-0.265625 0.171875 -0.5625 0.46875q-0.296875 0.296875 -0.625 0.734375l0 3.5625q0.359375 0.15625 0.75 0.25q0.390625 0.078125 0.75 0.078125q0.4375 0 0.828125 -0.140625q0.40625 -0.140625 0.703125 -0.46875q0.3125 -0.328125 0.484375 -0.859375q0.1875 -0.546875 0.1875 -1.34375zm2.6987 0.234375q0 -0.9375 0.25 -1.671875q0.265625 -0.734375 0.734375 -1.234375q0.46875 -0.5 1.125 -0.75q0.671875 -0.265625 1.484375 -0.265625q0.359375 0 0.6875 0.046875q0.34375 0.03125 0.671875 0.125l0 -3.078125l1.28125 0l0 10.34375l-1.140625 0l-0.03125 -1.390625q-0.546875 0.78125 -1.171875 1.15625q-0.609375 0.359375 -1.34375 0.359375q-0.625 0 -1.109375 -0.25q-0.46875 -0.265625 -0.796875 -0.75q-0.3125 -0.484375 -0.484375 -1.15625q-0.15625 -0.671875 -0.15625 -1.484375zm1.3125 -0.09375q0 1.34375 0.390625 2.0q0.390625 0.65625 1.109375 0.65625q0.484375 0 1.015625 -0.4375q0.53125 -0.4375 1.125 -1.28125l0 -3.421875q-0.3125 -0.140625 -0.6875 -0.21875q-0.375 -0.078125 -0.75 -0.078125q-1.046875 0 -1.625 0.671875q-0.578125 0.671875 -0.578125 2.109375zm13.245575 3.609375l-6.0 0l0 -1.1875l2.453125 0l0 -6.984375l-2.296875 1.25l-0.46875 -1.09375l3.046875 -1.59375l1.125 0l0 8.421875l2.140625 0l0 1.1875zm8.151825 -2.40625q0 0.609375 -0.25 1.09375q-0.25 0.46875 -0.703125 0.796875q-0.453125 0.3125 -1.0625 0.484375q-0.59375 0.15625 -1.3125 0.15625q-0.78125 0 -1.375 -0.171875q-0.59375 -0.171875 -1.0 -0.484375q-0.40625 -0.3125 -0.609375 -0.75q-0.203125 -0.4375 -0.203125 -0.953125q0 -0.875 0.484375 -1.515625q0.5 -0.640625 1.515625 -1.15625q-0.9375 -0.484375 -1.375 -1.0625q-0.421875 -0.578125 -0.421875 -1.328125q0 -0.46875 0.1875 -0.890625q0.1875 -0.4375 0.578125 -0.765625q0.390625 -0.34375 0.96875 -0.546875q0.578125 -0.203125 1.359375 -0.203125q0.734375 0 1.296875 0.15625q0.5625 0.15625 0.9375 0.453125q0.390625 0.296875 0.578125 0.71875q0.1875 0.40625 0.1875 0.921875q0 0.828125 -0.46875 1.421875q-0.46875 0.578125 -1.3125 1.015625q0.421875 0.21875 0.78125 0.484375q0.375 0.25 0.640625 0.5625q0.265625 0.3125 0.421875 0.703125q0.15625 0.390625 0.15625 0.859375zm-1.53125 -4.953125q0 -0.640625 -0.453125 -0.953125q-0.453125 -0.328125 -1.28125 -0.328125q-0.828125 0 -1.28125 0.3125q-0.453125 0.3125 -0.453125 0.9375q0 0.296875 0.109375 0.546875q0.109375 0.234375 0.328125 0.453125q0.234375 0.203125 0.578125 0.40625q0.34375 0.203125 0.828125 0.421875q0.828125 -0.390625 1.21875 -0.8125q0.40625 -0.421875 0.40625 -0.984375zm0.140625 5.046875q0 -0.265625 -0.09375 -0.515625q-0.078125 -0.265625 -0.3125 -0.515625q-0.21875 -0.25 -0.609375 -0.5q-0.375 -0.25 -0.96875 -0.5q-0.5 0.234375 -0.84375 0.46875q-0.328125 0.234375 -0.546875 0.484375q-0.203125 0.25 -0.296875 0.515625q-0.078125 0.265625 -0.078125 0.546875q0 0.328125 0.140625 0.59375q0.140625 0.25 0.390625 0.421875q0.25 0.171875 0.59375 0.265625q0.34375 0.09375 0.75 0.09375q0.390625 0 0.734375 -0.078125q0.34375 -0.09375 0.59375 -0.25q0.25 -0.171875 0.390625 -0.421875q0.15625 -0.25 0.15625 -0.609375zm2.85495 -1.203125q0 -0.9375 0.25 -1.671875q0.265625 -0.734375 0.734375 -1.234375q0.46875 -0.5 1.125 -0.75q0.671875 -0.265625 1.484375 -0.265625q0.359375 0 0.6875 0.046875q0.34375 0.03125 0.671875 0.125l0 -3.078125l1.28125 0l0 10.34375l-1.140625 0l-0.03125 -1.390625q-0.546875 0.78125 -1.171875 1.15625q-0.609375 0.359375 -1.34375 0.359375q-0.625 0 -1.109375 -0.25q-0.46875 -0.265625 -0.796875 -0.75q-0.3125 -0.484375 -0.484375 -1.15625q-0.15625 -0.671875 -0.15625 -1.484375zm1.3125 -0.09375q0 1.34375 0.390625 2.0q0.390625 0.65625 1.109375 0.65625q0.484375 0 1.015625 -0.4375q0.53125 -0.4375 1.125 -1.28125l0 -3.421875q-0.3125 -0.140625 -0.6875 -0.21875q-0.375 -0.078125 -0.75 -0.078125q-1.046875 0 -1.625 0.671875q-0.578125 0.671875 -0.578125 2.109375zm13.401825 -0.203125q0 0.921875 -0.25 1.640625q-0.25 0.71875 -0.71875 1.21875q-0.46875 0.5 -1.140625 0.78125q-0.65625 0.265625 -1.484375 0.265625q-0.65625 0 -1.34375 -0.125q-0.671875 -0.125 -1.34375 -0.40625l0 -9.90625l1.28125 0l0 2.84375l-0.0625 1.359375q0.546875 -0.734375 1.171875 -1.03125q0.625 -0.3125 1.34375 -0.3125q0.625 0 1.09375 0.265625q0.484375 0.265625 0.8125 0.75q0.328125 0.46875 0.484375 1.15625q0.15625 0.671875 0.15625 1.5zm-1.296875 0.0625q0 -0.578125 -0.09375 -1.0625q-0.078125 -0.484375 -0.265625 -0.828125q-0.171875 -0.34375 -0.46875 -0.53125q-0.28125 -0.203125 -0.671875 -0.203125q-0.25 0 -0.5 0.078125q-0.25 0.078125 -0.515625 0.265625q-0.265625 0.171875 -0.5625 0.46875q-0.296875 0.296875 -0.625 0.734375l0 3.5625q0.359375 0.15625 0.75 0.25q0.390625 0.078125 0.75 0.078125q0.4375 0 0.828125 -0.140625q0.40625 -0.140625 0.703125 -0.46875q0.3125 -0.328125 0.484375 -0.859375q0.1875 -0.546875 0.1875 -1.34375zm9.7612 1.640625l-1.640625 0l0 2.109375l-1.296875 0l0 -2.109375l-4.609375 0l0 -1.125l4.078125 -6.34375l1.828125 0l0 6.34375l1.640625 0l0 1.125zm-2.9375 -6.234375l-3.328125 5.109375l3.328125 0l0 -5.109375zm10.026825 8.078125q-0.5 0.1875 -1.03125 0.28125q-0.515625 0.09375 -1.078125 0.09375q-1.734375 0 -2.6875 -0.9375q-0.9375 -0.953125 -0.9375 -2.78125q0 -0.859375 0.265625 -1.5625q0.28125 -0.71875 0.765625 -1.21875q0.5 -0.515625 1.171875 -0.78125q0.6875 -0.28125 1.5 -0.28125q0.578125 0 1.078125 0.078125q0.5 0.078125 0.953125 0.265625l0 1.21875q-0.484375 -0.25 -0.984375 -0.359375q-0.484375 -0.125 -1.015625 -0.125q-0.484375 0 -0.921875 0.1875q-0.4375 0.1875 -0.765625 0.546875q-0.328125 0.34375 -0.53125 0.859375q-0.1875 0.5 -0.1875 1.140625q0 1.328125 0.640625 2.0q0.65625 0.65625 1.8125 0.65625q0.515625 0 1.0 -0.109375q0.5 -0.125 0.953125 -0.359375l0 1.1875zm8.058075 0q-0.5 0.1875 -1.03125 0.28125q-0.515625 0.09375 -1.078125 0.09375q-1.734375 0 -2.6875 -0.9375q-0.9375 -0.953125 -0.9375 -2.78125q0 -0.859375 0.265625 -1.5625q0.28125 -0.71875 0.765625 -1.21875q0.5 -0.515625 1.171875 -0.78125q0.6875 -0.28125 1.5 -0.28125q0.578125 0 1.078125 0.078125q0.5 0.078125 0.953125 0.265625l0 1.21875q-0.484375 -0.25 -0.984375 -0.359375q-0.484375 -0.125 -1.015625 -0.125q-0.484375 0 -0.921875 0.1875q-0.4375 0.1875 -0.765625 0.546875q-0.328125 0.34375 -0.53125 0.859375q-0.1875 0.5 -0.1875 1.140625q0 1.328125 0.640625 2.0q0.65625 0.65625 1.8125 0.65625q0.515625 0 1.0 -0.109375q0.5 -0.125 0.953125 -0.359375l0 1.1875zm8.558075 0.265625l-6.3125 0l0 -1.140625l2.46875 -2.46875q0.609375 -0.59375 0.984375 -1.03125q0.390625 -0.4375 0.59375 -0.796875q0.21875 -0.375 0.296875 -0.6875q0.078125 -0.328125 0.078125 -0.703125q0 -0.34375 -0.109375 -0.65625q-0.09375 -0.328125 -0.296875 -0.5625q-0.1875 -0.25 -0.5 -0.390625q-0.3125 -0.140625 -0.75 -0.140625q-0.609375 0 -1.109375 0.28125q-0.5 0.265625 -0.921875 0.6875l-0.703125 -0.828125q0.546875 -0.578125 1.25 -0.921875q0.703125 -0.34375 1.640625 -0.34375q0.640625 0 1.15625 0.1875q0.53125 0.1875 0.90625 0.546875q0.390625 0.359375 0.59375 0.890625q0.21875 0.515625 0.21875 1.15625q0 0.5625 -0.15625 1.03125q-0.140625 0.46875 -0.4375 0.9375q-0.296875 0.453125 -0.75 0.953125q-0.453125 0.5 -1.0625 1.09375l-1.734375 1.6875l4.65625 0l0 1.21875zm8.370575 -9.15625q-1.015625 -0.21875 -1.734375 -0.21875q-1.71875 0 -1.71875 1.796875l0 1.296875l3.21875 0l0 1.0625l-3.21875 0l0 5.21875l-1.296875 0l0 -5.21875l-2.359375 0l0 -1.0625l2.359375 0l0 -1.21875q0 -2.9375 3.0625 -2.9375q0.765625 0 1.6875 0.171875l0 1.109375zm-7.703125 1.796875l0 0zm15.4487 4.953125q0 0.609375 -0.25 1.09375q-0.25 0.46875 -0.703125 0.796875q-0.453125 0.3125 -1.0625 0.484375q-0.59375 0.15625 -1.3125 0.15625q-0.78125 0 -1.375 -0.171875q-0.59375 -0.171875 -1.0 -0.484375q-0.40625 -0.3125 -0.609375 -0.75q-0.203125 -0.4375 -0.203125 -0.953125q0 -0.875 0.484375 -1.515625q0.5 -0.640625 1.515625 -1.15625q-0.9375 -0.484375 -1.375 -1.0625q-0.421875 -0.578125 -0.421875 -1.328125q0 -0.46875 0.1875 -0.890625q0.1875 -0.4375 0.578125 -0.765625q0.390625 -0.34375 0.96875 -0.546875q0.578125 -0.203125 1.359375 -0.203125q0.734375 0 1.296875 0.15625q0.5625 0.15625 0.9375 0.453125q0.390625 0.296875 0.578125 0.71875q0.1875 0.40625 0.1875 0.921875q0 0.828125 -0.46875 1.421875q-0.46875 0.578125 -1.3125 1.015625q0.421875 0.21875 0.78125 0.484375q0.375 0.25 0.640625 0.5625q0.265625 0.3125 0.421875 0.703125q0.15625 0.390625 0.15625 0.859375zm-1.53125 -4.953125q0 -0.640625 -0.453125 -0.953125q-0.453125 -0.328125 -1.28125 -0.328125q-0.828125 0 -1.28125 0.3125q-0.453125 0.3125 -0.453125 0.9375q0 0.296875 0.109375 0.546875q0.109375 0.234375 0.328125 0.453125q0.234375 0.203125 0.578125 0.40625q0.34375 0.203125 0.828125 0.421875q0.828125 -0.390625 1.21875 -0.8125q0.40625 -0.421875 0.40625 -0.984375zm0.140625 5.046875q0 -0.265625 -0.09375 -0.515625q-0.078125 -0.265625 -0.3125 -0.515625q-0.21875 -0.25 -0.609375 -0.5q-0.375 -0.25 -0.96875 -0.5q-0.5 0.234375 -0.84375 0.46875q-0.328125 0.234375 -0.546875 0.484375q-0.203125 0.25 -0.296875 0.515625q-0.078125 0.265625 -0.078125 0.546875q0 0.328125 0.140625 0.59375q0.140625 0.25 0.390625 0.421875q0.25 0.171875 0.59375 0.265625q0.34375 0.09375 0.75 0.09375q0.390625 0 0.734375 -0.078125q0.34375 -0.09375 0.59375 -0.25q0.25 -0.171875 0.390625 -0.421875q0.15625 -0.25 0.15625 -0.609375zm9.1987 -0.75q0 0.71875 -0.3125 1.3125q-0.296875 0.578125 -0.84375 1.0q-0.53125 0.40625 -1.265625 0.640625q-0.734375 0.234375 -1.578125 0.234375q-0.21875 0 -0.46875 -0.015625q-0.234375 0 -0.484375 -0.015625q-0.234375 -0.015625 -0.46875 -0.046875q-0.234375 -0.015625 -0.421875 -0.046875l0 -1.15625q0.40625 0.09375 0.90625 0.140625q0.515625 0.046875 1.03125 0.046875q0.59375 0 1.0625 -0.140625q0.46875 -0.140625 0.796875 -0.390625q0.328125 -0.265625 0.5 -0.640625q0.171875 -0.375 0.171875 -0.828125q0 -0.90625 -0.640625 -1.3125q-0.640625 -0.40625 -1.84375 -0.40625l-1.8125 0l0 -4.890625l5.15625 0l0 1.125l-3.953125 0l0 2.6875l0.84375 0q0.6875 0 1.328125 0.125q0.65625 0.125 1.15625 0.4375q0.515625 0.296875 0.828125 0.828125q0.3125 0.515625 0.3125 1.3125zm7.808075 2.796875q-0.5 0.1875 -1.03125 0.28125q-0.515625 0.09375 -1.078125 0.09375q-1.734375 0 -2.6875 -0.9375q-0.9375 -0.953125 -0.9375 -2.78125q0 -0.859375 0.265625 -1.5625q0.28125 -0.71875 0.765625 -1.21875q0.5 -0.515625 1.171875 -0.78125q0.6875 -0.28125 1.5 -0.28125q0.578125 0 1.078125 0.078125q0.5 0.078125 0.953125 0.265625l0 1.21875q-0.484375 -0.25 -0.984375 -0.359375q-0.484375 -0.125 -1.015625 -0.125q-0.484375 0 -0.921875 0.1875q-0.4375 0.1875 -0.765625 0.546875q-0.328125 0.34375 -0.53125 0.859375q-0.1875 0.5 -0.1875 1.140625q0 1.328125 0.640625 2.0q0.65625 0.65625 1.8125 0.65625q0.515625 0 1.0 -0.109375q0.5 -0.125 0.953125 -0.359375l0 1.1875zm8.558075 -3.796875q0 0.265625 -0.015625 0.453125q0 0.1875 -0.015625 0.34375l-5.171875 0q0 1.125 0.625 1.734375q0.640625 0.59375 1.828125 0.59375q0.3125 0 0.640625 -0.015625q0.328125 -0.03125 0.625 -0.0625q0.296875 -0.046875 0.5625 -0.09375q0.28125 -0.0625 0.515625 -0.140625l0 1.046875q-0.515625 0.15625 -1.171875 0.234375q-0.65625 0.09375 -1.359375 0.09375q-0.9375 0 -1.625 -0.25q-0.6875 -0.25 -1.125 -0.734375q-0.421875 -0.5 -0.640625 -1.203125q-0.203125 -0.703125 -0.203125 -1.59375q0 -0.78125 0.21875 -1.46875q0.21875 -0.703125 0.640625 -1.21875q0.4375 -0.53125 1.0625 -0.828125q0.625 -0.3125 1.421875 -0.3125q0.765625 0 1.359375 0.25q0.59375 0.234375 1.0 0.6875q0.40625 0.4375 0.609375 1.078125q0.21875 0.625 0.21875 1.40625zm-1.328125 -0.1875q0.015625 -0.484375 -0.109375 -0.890625q-0.109375 -0.40625 -0.359375 -0.703125q-0.234375 -0.296875 -0.609375 -0.453125q-0.359375 -0.171875 -0.84375 -0.171875q-0.421875 0 -0.765625 0.171875q-0.34375 0.15625 -0.59375 0.453125q-0.25 0.28125 -0.40625 0.703125q-0.140625 0.40625 -0.1875 0.890625l3.875 0zm2.79245 0.734375q0 -0.9375 0.25 -1.671875q0.265625 -0.734375 0.734375 -1.234375q0.46875 -0.5 1.125 -0.75q0.671875 -0.265625 1.484375 -0.265625q0.359375 0 0.6875 0.046875q0.34375 0.03125 0.671875 0.125l0 -3.078125l1.28125 0l0 10.34375l-1.140625 0l-0.03125 -1.390625q-0.546875 0.78125 -1.171875 1.15625q-0.609375 0.359375 -1.34375 0.359375q-0.625 0 -1.109375 -0.25q-0.46875 -0.265625 -0.796875 -0.75q-0.3125 -0.484375 -0.484375 -1.15625q-0.15625 -0.671875 -0.15625 -1.484375zm1.3125 -0.09375q0 1.34375 0.390625 2.0q0.390625 0.65625 1.109375 0.65625q0.484375 0 1.015625 -0.4375q0.53125 -0.4375 1.125 -1.28125l0 -3.421875q-0.3125 -0.140625 -0.6875 -0.21875q-0.375 -0.078125 -0.75 -0.078125q-1.046875 0 -1.625 0.671875q-0.578125 0.671875 -0.578125 2.109375zm13.339325 -0.453125q0 0.265625 -0.015625 0.453125q0 0.1875 -0.015625 0.34375l-5.171875 0q0 1.125 0.625 1.734375q0.640625 0.59375 1.828125 0.59375q0.3125 0 0.640625 -0.015625q0.328125 -0.03125 0.625 -0.0625q0.296875 -0.046875 0.5625 -0.09375q0.28125 -0.0625 0.515625 -0.140625l0 1.046875q-0.515625 0.15625 -1.171875 0.234375q-0.65625 0.09375 -1.359375 0.09375q-0.9375 0 -1.625 -0.25q-0.6875 -0.25 -1.125 -0.734375q-0.421875 -0.5 -0.640625 -1.203125q-0.203125 -0.703125 -0.203125 -1.59375q0 -0.78125 0.21875 -1.46875q0.21875 -0.703125 0.640625 -1.21875q0.4375 -0.53125 1.0625 -0.828125q0.625 -0.3125 1.421875 -0.3125q0.765625 0 1.359375 0.25q0.59375 0.234375 1.0 0.6875q0.40625 0.4375 0.609375 1.078125q0.21875 0.625 0.21875 1.40625zm-1.328125 -0.1875q0.015625 -0.484375 -0.109375 -0.890625q-0.109375 -0.40625 -0.359375 -0.703125q-0.234375 -0.296875 -0.609375 -0.453125q-0.359375 -0.171875 -0.84375 -0.171875q-0.421875 0 -0.765625 0.171875q-0.34375 0.15625 -0.59375 0.453125q-0.25 0.28125 -0.40625 0.703125q-0.140625 0.40625 -0.1875 0.890625l3.875 0zm9.6987 -4.90625q-1.015625 -0.21875 -1.734375 -0.21875q-1.71875 0 -1.71875 1.796875l0 1.296875l3.21875 0l0 1.0625l-3.21875 0l0 5.21875l-1.296875 0l0 -5.21875l-2.359375 0l0 -1.0625l2.359375 0l0 -1.21875q0 -2.9375 3.0625 -2.9375q0.765625 0 1.6875 0.171875l0 1.109375zm-7.703125 1.796875l0 0zm15.558105 4.328125q0 0.65625 -0.234375 1.234375q-0.234375 0.578125 -0.6875 1.015625q-0.4375 0.421875 -1.0625 0.671875q-0.609375 0.234375 -1.359375 0.234375q-0.796875 0 -1.40625 -0.25q-0.609375 -0.25 -1.015625 -0.765625q-0.40625 -0.53125 -0.6250305 -1.328125q-0.203125 -0.796875 -0.203125 -1.890625q0 -0.734375 0.09375 -1.421875q0.09375 -0.6875 0.31253052 -1.296875q0.21875 -0.609375 0.578125 -1.109375q0.375 -0.5 0.921875 -0.859375q0.546875 -0.375 1.28125 -0.578125q0.734375 -0.203125 1.71875 -0.203125l0.9375 0l0 1.125l-1.015625 0q-0.859375 0 -1.5 0.203125q-0.625 0.203125 -1.046875 0.578125q-0.421875 0.375 -0.65625 0.90625q-0.21875 0.515625 -0.28125 1.171875l-0.03125 0.296875q0.46875 -0.265625 1.0625 -0.421875q0.609375 -0.171875 1.3125 -0.171875q0.71875 0 1.265625 0.21875q0.546875 0.203125 0.90625 0.578125q0.375 0.375 0.546875 0.90625q0.1875 0.53125 0.1875 1.15625zm-1.328125 0.078125q0 -0.4375 -0.109375 -0.796875q-0.109375 -0.359375 -0.34375 -0.59375q-0.21875 -0.25 -0.5625 -0.375q-0.34375 -0.140625 -0.828125 -0.140625q-0.28125 0 -0.578125 0.046875q-0.28125 0.046875 -0.5625 0.140625q-0.265625 0.09375 -0.515625 0.21875q-0.25 0.109375 -0.453125 0.234375q0 0.953125 0.125 1.59375q0.140625 0.625 0.390625 1.015625q0.265625 0.375 0.640625 0.53125q0.390625 0.15625 0.890625 0.15625q0.421875 0 0.765625 -0.125q0.34375 -0.140625 0.59375 -0.40625q0.25 -0.265625 0.390625 -0.640625q0.15625 -0.375 0.15625 -0.859375zm9.026794 -0.109375q0 0.71875 -0.3125 1.3125q-0.296875 0.578125 -0.84375 1.0q-0.53125 0.40625 -1.265625 0.640625q-0.734375 0.234375 -1.578125 0.234375q-0.21875 0 -0.46875 -0.015625q-0.234375 0 -0.484375 -0.015625q-0.234375 -0.015625 -0.46875 -0.046875q-0.234375 -0.015625 -0.421875 -0.046875l0 -1.15625q0.40625 0.09375 0.90625 0.140625q0.515625 0.046875 1.03125 0.046875q0.59375 0 1.0625 -0.140625q0.46875 -0.140625 0.796875 -0.390625q0.328125 -0.265625 0.5 -0.640625q0.171875 -0.375 0.171875 -0.828125q0 -0.90625 -0.640625 -1.3125q-0.640625 -0.40625 -1.84375 -0.40625l-1.8125 0l0 -4.890625l5.15625 0l0 1.125l-3.953125 0l0 2.6875l0.84375 0q0.6875 0 1.328125 0.125q0.65625 0.125 1.15625 0.4375q0.515625 0.296875 0.828125 0.828125q0.3125 0.515625 0.3125 1.3125zm8.776855 0.953125l-1.640625 0l0 2.109375l-1.296875 0l0 -2.109375l-4.609375 0l0 -1.125l4.078125 -6.34375l1.828125 0l0 6.34375l1.640625 0l0 1.125zm-2.9375 -6.234375l-3.328125 5.109375l3.328125 0l0 -5.109375zm10.839294 -0.8125q-1.015625 -0.21875 -1.734375 -0.21875q-1.71875 0 -1.71875 1.796875l0 1.296875l3.21875 0l0 1.0625l-3.21875 0l0 5.21875l-1.296875 0l0 -5.21875l-2.359375 0l0 -1.0625l2.359375 0l0 -1.21875q0 -2.9375 3.0625 -2.9375q0.765625 0 1.6875 0.171875l0 1.109375zm-7.703125 1.796875l0 0zm14.94873 7.09375q-0.5 0.1875 -1.03125 0.28125q-0.515625 0.09375 -1.078125 0.09375q-1.734375 0 -2.6875 -0.9375q-0.9375 -0.953125 -0.9375 -2.78125q0 -0.859375 0.265625 -1.5625q0.28125 -0.71875 0.765625 -1.21875q0.5 -0.515625 1.171875 -0.78125q0.6875 -0.28125 1.5 -0.28125q0.578125 0 1.078125 0.078125q0.5 0.078125 0.953125 0.265625l0 1.21875q-0.484375 -0.25 -0.984375 -0.359375q-0.484375 -0.125 -1.015625 -0.125q-0.484375 0 -0.921875 0.1875q-0.4375 0.1875 -0.765625 0.546875q-0.328125 0.34375 -0.53125 0.859375q-0.1875 0.5 -0.1875 1.140625q0 1.328125 0.640625 2.0q0.65625 0.65625 1.8125 0.65625q0.515625 0 1.0 -0.109375q0.5 -0.125 0.953125 -0.359375l0 1.1875zm8.058044 0q-0.5 0.1875 -1.03125 0.28125q-0.515625 0.09375 -1.078125 0.09375q-1.734375 0 -2.6875 -0.9375q-0.9375 -0.953125 -0.9375 -2.78125q0 -0.859375 0.265625 -1.5625q0.28125 -0.71875 0.765625 -1.21875q0.5 -0.515625 1.171875 -0.78125q0.6875 -0.28125 1.5 -0.28125q0.578125 0 1.078125 0.078125q0.5 0.078125 0.953125 0.265625l0 1.21875q-0.484375 -0.25 -0.984375 -0.359375q-0.484375 -0.125 -1.015625 -0.125q-0.484375 0 -0.921875 0.1875q-0.4375 0.1875 -0.765625 0.546875q-0.328125 0.34375 -0.53125 0.859375q-0.1875 0.5 -0.1875 1.140625q0 1.328125 0.640625 2.0q0.65625 0.65625 1.8125 0.65625q0.515625 0 1.0 -0.109375q0.5 -0.125 0.953125 -0.359375l0 1.1875zm8.058105 0q-0.5 0.1875 -1.03125 0.28125q-0.515625 0.09375 -1.078125 0.09375q-1.734375 0 -2.6875 -0.9375q-0.9375 -0.953125 -0.9375 -2.78125q0 -0.859375 0.265625 -1.5625q0.28125 -0.71875 0.765625 -1.21875q0.5 -0.515625 1.171875 -0.78125q0.6875 -0.28125 1.5 -0.28125q0.578125 0 1.078125 0.078125q0.5 0.078125 0.953125 0.265625l0 1.21875q-0.484375 -0.25 -0.984375 -0.359375q-0.484375 -0.125 -1.015625 -0.125q-0.484375 0 -0.921875 0.1875q-0.4375 0.1875 -0.765625 0.546875q-0.328125 0.34375 -0.53125 0.859375q-0.1875 0.5 -0.1875 1.140625q0 1.328125 0.640625 2.0q0.65625 0.65625 1.8125 0.65625q0.515625 0 1.0 -0.109375q0.5 -0.125 0.953125 -0.359375l0 1.1875zm9.026794 -1.84375l-1.640625 0l0 2.109375l-1.296875 0l0 -2.109375l-4.609375 0l0 -1.125l4.078125 -6.34375l1.828125 0l0 6.34375l1.640625 0l0 1.125zm-2.9375 -6.234375l-3.328125 5.109375l3.328125 0l0 -5.109375zm9.026855 8.34375l-0.03125 -0.984375q-0.59375 0.59375 -1.21875 0.859375q-0.609375 0.25 -1.296875 0.25q-0.625 0 -1.078125 -0.15625q-0.4375 -0.15625 -0.734375 -0.4375q-0.28125 -0.28125 -0.421875 -0.65625q-0.140625 -0.390625 -0.140625 -0.84375q0 -1.09375 0.828125 -1.71875q0.828125 -0.640625 2.4375 -0.640625l1.515625 0l0 -0.640625q0 -0.65625 -0.421875 -1.046875q-0.40625 -0.390625 -1.265625 -0.390625q-0.625 0 -1.234375 0.140625q-0.59375 0.140625 -1.234375 0.40625l0 -1.15625q0.234375 -0.09375 0.53125 -0.171875q0.296875 -0.078125 0.625 -0.140625q0.328125 -0.078125 0.6875 -0.109375q0.359375 -0.046875 0.734375 -0.046875q0.65625 0 1.1875 0.15625q0.546875 0.140625 0.90625 0.4375q0.375 0.296875 0.5625 0.75q0.203125 0.453125 0.203125 1.078125l0 5.0625l-1.140625 0zm-0.140625 -3.34375l-1.609375 0q-0.484375 0 -0.828125 0.09375q-0.34375 0.09375 -0.5625 0.265625q-0.21875 0.171875 -0.328125 0.421875q-0.09375 0.25 -0.09375 0.5625q0 0.203125 0.0625 0.40625q0.0625 0.1875 0.203125 0.34375q0.15625 0.140625 0.390625 0.234375q0.234375 0.078125 0.5625 0.078125q0.4375 0 1.0 -0.265625q0.578125 -0.265625 1.203125 -0.84375l0 -1.296875zm10.167419 1.234375l-1.640625 0l0 2.109375l-1.296875 0l0 -2.109375l-4.609375 0l0 -1.125l4.078125 -6.34375l1.828125 0l0 6.34375l1.640625 0l0 1.125zm-2.9375 -6.234375l-3.328125 5.109375l3.328125 0l0 -5.109375zm3.9331055 4.828125q0 -0.9375 0.25 -1.671875q0.265625 -0.734375 0.734375 -1.234375q0.46875 -0.5 1.125 -0.75q0.671875 -0.265625 1.484375 -0.265625q0.359375 0 0.6875 0.046875q0.34375 0.03125 0.671875 0.125l0 -3.078125l1.28125 0l0 10.34375l-1.140625 0l-0.03125 -1.390625q-0.546875 0.78125 -1.171875 1.15625q-0.609375 0.359375 -1.34375 0.359375q-0.625 0 -1.109375 -0.25q-0.46875 -0.265625 -0.796875 -0.75q-0.3125 -0.484375 -0.484375 -1.15625q-0.15625 -0.671875 -0.15625 -1.484375zm1.3125 -0.09375q0 1.34375 0.390625 2.0q0.390625 0.65625 1.109375 0.65625q0.484375 0 1.015625 -0.4375q0.53125 -0.4375 1.125 -1.28125l0 -3.421875q-0.3125 -0.140625 -0.6875 -0.21875q-0.375 -0.078125 -0.75 -0.078125q-1.046875 0 -1.625 0.671875q-0.578125 0.671875 -0.578125 2.109375zm13.339294 1.203125q0 0.609375 -0.25 1.09375q-0.25 0.46875 -0.703125 0.796875q-0.453125 0.3125 -1.0625 0.484375q-0.59375 0.15625 -1.3125 0.15625q-0.78125 0 -1.375 -0.171875q-0.59375 -0.171875 -1.0 -0.484375q-0.40625 -0.3125 -0.609375 -0.75q-0.203125 -0.4375 -0.203125 -0.953125q0 -0.875 0.484375 -1.515625q0.5 -0.640625 1.515625 -1.15625q-0.9375 -0.484375 -1.375 -1.0625q-0.421875 -0.578125 -0.421875 -1.328125q0 -0.46875 0.1875 -0.890625q0.1875 -0.4375 0.578125 -0.765625q0.390625 -0.34375 0.96875 -0.546875q0.578125 -0.203125 1.359375 -0.203125q0.734375 0 1.296875 0.15625q0.5625 0.15625 0.9375 0.453125q0.390625 0.296875 0.578125 0.71875q0.1875 0.40625 0.1875 0.921875q0 0.828125 -0.46875 1.421875q-0.46875 0.578125 -1.3125 1.015625q0.421875 0.21875 0.78125 0.484375q0.375 0.25 0.640625 0.5625q0.265625 0.3125 0.421875 0.703125q0.15625 0.390625 0.15625 0.859375zm-1.53125 -4.953125q0 -0.640625 -0.453125 -0.953125q-0.453125 -0.328125 -1.28125 -0.328125q-0.828125 0 -1.28125 0.3125q-0.453125 0.3125 -0.453125 0.9375q0 0.296875 0.109375 0.546875q0.109375 0.234375 0.328125 0.453125q0.234375 0.203125 0.578125 0.40625q0.34375 0.203125 0.828125 0.421875q0.828125 -0.390625 1.21875 -0.8125q0.40625 -0.421875 0.40625 -0.984375zm0.140625 5.046875q0 -0.265625 -0.09375 -0.515625q-0.078125 -0.265625 -0.3125 -0.515625q-0.21875 -0.25 -0.609375 -0.5q-0.375 -0.25 -0.96875 -0.5q-0.5 0.234375 -0.84375 0.46875q-0.328125 0.234375 -0.546875 0.484375q-0.203125 0.25 -0.296875 0.515625q-0.078125 0.265625 -0.078125 0.546875q0 0.328125 0.140625 0.59375q0.140625 0.25 0.390625 0.421875q0.25 0.171875 0.59375 0.265625q0.34375 0.09375 0.75 0.09375q0.390625 0 0.734375 -0.078125q0.34375 -0.09375 0.59375 -0.25q0.25 -0.171875 0.390625 -0.421875q0.15625 -0.25 0.15625 -0.609375z" fill-rule="nonzero"></path><path fill="#ffff00" d="m645.3884 414.5915l257.8584 0l0 17.16098l-257.8584 0l0 -17.16098z" fill-rule="nonzero"></path><path fill="#000000" d="m652.59155 422.57007q0 0.625 -0.265625 1.1875q-0.25 0.546875 -0.765625 0.96875q-0.515625 0.40625 -1.296875 0.640625q-0.765625 0.234375 -1.796875 0.234375q-0.578125 0 -1.03125 -0.03125q-0.453125 -0.03125 -0.84375 -0.09375l0 -1.140625q0.453125 0.078125 0.953125 0.125q0.515625 0.046875 1.03125 0.046875q0.71875 0 1.21875 -0.125q0.515625 -0.140625 0.84375 -0.375q0.328125 -0.25 0.46875 -0.59375q0.140625 -0.34375 0.140625 -0.765625q0 -0.40625 -0.171875 -0.6875q-0.171875 -0.296875 -0.5 -0.5q-0.3125 -0.203125 -0.765625 -0.296875q-0.4375 -0.09375 -0.953125 -0.09375l-1.09375 0l0 -1.046875l1.109375 0q0.421875 0 0.78125 -0.109375q0.359375 -0.125 0.609375 -0.328125q0.25 -0.21875 0.375 -0.53125q0.140625 -0.3125 0.140625 -0.703125q0 -0.765625 -0.46875 -1.109375q-0.46875 -0.359375 -1.375 -0.359375q-0.484375 0 -1.0 0.09375q-0.5 0.09375 -1.09375 0.28125l0 -1.109375q0.25 -0.09375 0.53125 -0.15625q0.28125 -0.078125 0.5625 -0.125q0.28125 -0.046875 0.5625 -0.0625q0.28125 -0.03125 0.53125 -0.03125q0.765625 0 1.34375 0.171875q0.578125 0.15625 0.96875 0.46875q0.390625 0.296875 0.578125 0.75q0.203125 0.4375 0.203125 0.984375q0 0.8125 -0.421875 1.375q-0.421875 0.5625 -1.15625 0.890625q0.375 0.046875 0.734375 0.234375q0.375 0.171875 0.65625 0.453125q0.296875 0.265625 0.46875 0.640625q0.1875 0.375 0.1875 0.828125zm8.245605 2.90625l-6.3125 0l0 -1.140625l2.46875 -2.46875q0.609375 -0.59375 0.984375 -1.03125q0.390625 -0.4375 0.59375 -0.796875q0.21875 -0.375 0.296875 -0.6875q0.078125 -0.328125 0.078125 -0.703125q0 -0.34375 -0.109375 -0.65625q-0.09375 -0.328125 -0.296875 -0.5625q-0.1875 -0.25 -0.5 -0.390625q-0.3125 -0.140625 -0.75 -0.140625q-0.609375 0 -1.109375 0.28125q-0.5 0.265625 -0.921875 0.6875l-0.703125 -0.828125q0.546875 -0.578125 1.25 -0.921875q0.703125 -0.34375 1.640625 -0.34375q0.640625 0 1.15625 0.1875q0.53125 0.1875 0.90625 0.546875q0.390625 0.359375 0.59375 0.890625q0.21875 0.515625 0.21875 1.15625q0 0.5625 -0.15625 1.03125q-0.140625 0.46875 -0.4375 0.9375q-0.296875 0.453125 -0.75 0.953125q-0.453125 0.5 -1.0625 1.09375l-1.734375 1.6875l4.65625 0l0 1.21875zm7.8080444 -3.0625q0 0.71875 -0.3125 1.3125q-0.296875 0.578125 -0.84375 1.0q-0.53125 0.40625 -1.265625 0.640625q-0.734375 0.234375 -1.578125 0.234375q-0.21875 0 -0.46875 -0.015625q-0.234375 0 -0.484375 -0.015625q-0.234375 -0.015625 -0.46875 -0.046875q-0.234375 -0.015625 -0.421875 -0.046875l0 -1.15625q0.40625 0.09375 0.90625 0.140625q0.515625 0.046875 1.03125 0.046875q0.59375 0 1.0625 -0.140625q0.46875 -0.140625 0.796875 -0.390625q0.328125 -0.265625 0.5 -0.640625q0.171875 -0.375 0.171875 -0.828125q0 -0.90625 -0.640625 -1.3125q-0.640625 -0.40625 -1.84375 -0.40625l-1.8125 0l0 -4.890625l5.15625 0l0 1.125l-3.953125 0l0 2.6875l0.84375 0q0.6875 0 1.328125 0.125q0.65625 0.125 1.15625 0.4375q0.515625 0.296875 0.828125 0.828125q0.3125 0.515625 0.3125 1.3125zm8.776855 0.953125l-1.640625 0l0 2.109375l-1.296875 0l0 -2.109375l-4.609375 0l0 -1.125l4.078125 -6.34375l1.828125 0l0 6.34375l1.640625 0l0 1.125zm-2.9375 -6.234375l-3.328125 5.109375l3.328125 0l0 -5.109375zm3.9330444 4.828125q0 -0.9375 0.25 -1.671875q0.265625 -0.734375 0.734375 -1.234375q0.46875 -0.5 1.125 -0.75q0.671875 -0.265625 1.484375 -0.265625q0.359375 0 0.6875 0.046875q0.34375 0.03125 0.671875 0.125l0 -3.078125l1.28125 0l0 10.34375l-1.140625 0l-0.03125 -1.390625q-0.546875 0.78125 -1.171875 1.15625q-0.609375 0.359375 -1.34375 0.359375q-0.625 0 -1.109375 -0.25q-0.46875 -0.265625 -0.796875 -0.75q-0.3125 -0.484375 -0.484375 -1.15625q-0.15625 -0.671875 -0.15625 -1.484375zm1.3125 -0.09375q0 1.34375 0.390625 2.0q0.390625 0.65625 1.109375 0.65625q0.484375 0 1.015625 -0.4375q0.53125 -0.4375 1.125 -1.28125l0 -3.421875q-0.3125 -0.140625 -0.6875 -0.21875q-0.375 -0.078125 -0.75 -0.078125q-1.046875 0 -1.625 0.671875q-0.578125 0.671875 -0.578125 2.109375zm13.276855 -4.78125l-4.015625 8.390625l-1.453125 0l4.171875 -8.390625l-5.171875 0l0 -1.1875l6.46875 0l0 1.1875zm7.6205444 8.125q-0.5 0.1875 -1.03125 0.28125q-0.515625 0.09375 -1.078125 0.09375q-1.734375 0 -2.6875 -0.9375q-0.9375 -0.953125 -0.9375 -2.78125q0 -0.859375 0.265625 -1.5625q0.28125 -0.71875 0.765625 -1.21875q0.5 -0.515625 1.171875 -0.78125q0.6875 -0.28125 1.5 -0.28125q0.578125 0 1.078125 0.078125q0.5 0.078125 0.953125 0.265625l0 1.21875q-0.484375 -0.25 -0.984375 -0.359375q-0.484375 -0.125 -1.015625 -0.125q-0.484375 0 -0.921875 0.1875q-0.4375 0.1875 -0.765625 0.546875q-0.328125 0.34375 -0.53125 0.859375q-0.1875 0.5 -0.1875 1.140625q0 1.328125 0.640625 2.0q0.65625 0.65625 1.8125 0.65625q0.515625 0 1.0 -0.109375q0.5 -0.125 0.953125 -0.359375l0 1.1875zm8.370605 -2.640625q0 0.625 -0.265625 1.1875q-0.25 0.546875 -0.765625 0.96875q-0.515625 0.40625 -1.296875 0.640625q-0.765625 0.234375 -1.796875 0.234375q-0.578125 0 -1.03125 -0.03125q-0.453125 -0.03125 -0.84375 -0.09375l0 -1.140625q0.453125 0.078125 0.953125 0.125q0.515625 0.046875 1.03125 0.046875q0.71875 0 1.21875 -0.125q0.515625 -0.140625 0.84375 -0.375q0.328125 -0.25 0.46875 -0.59375q0.140625 -0.34375 0.140625 -0.765625q0 -0.40625 -0.171875 -0.6875q-0.171875 -0.296875 -0.5 -0.5q-0.3125 -0.203125 -0.765625 -0.296875q-0.4375 -0.09375 -0.953125 -0.09375l-1.09375 0l0 -1.046875l1.109375 0q0.421875 0 0.78125 -0.109375q0.359375 -0.125 0.609375 -0.328125q0.25 -0.21875 0.375 -0.53125q0.140625 -0.3125 0.140625 -0.703125q0 -0.765625 -0.46875 -1.109375q-0.46875 -0.359375 -1.375 -0.359375q-0.484375 0 -1.0 0.09375q-0.5 0.09375 -1.09375 0.28125l0 -1.109375q0.25 -0.09375 0.53125 -0.15625q0.28125 -0.078125 0.5625 -0.125q0.28125 -0.046875 0.5625 -0.0625q0.28125 -0.03125 0.53125 -0.03125q0.765625 0 1.34375 0.171875q0.578125 0.15625 0.96875 0.46875q0.390625 0.296875 0.578125 0.75q0.203125 0.4375 0.203125 0.984375q0 0.8125 -0.421875 1.375q-0.421875 0.5625 -1.15625 0.890625q0.375 0.046875 0.734375 0.234375q0.375 0.171875 0.65625 0.453125q0.296875 0.265625 0.46875 0.640625q0.1875 0.375 0.1875 0.828125zm7.7455444 2.640625q-0.5 0.1875 -1.03125 0.28125q-0.515625 0.09375 -1.078125 0.09375q-1.734375 0 -2.6875 -0.9375q-0.9375 -0.953125 -0.9375 -2.78125q0 -0.859375 0.265625 -1.5625q0.28125 -0.71875 0.765625 -1.21875q0.5 -0.515625 1.171875 -0.78125q0.6875 -0.28125 1.5 -0.28125q0.578125 0 1.078125 0.078125q0.5 0.078125 0.953125 0.265625l0 1.21875q-0.484375 -0.25 -0.984375 -0.359375q-0.484375 -0.125 -1.015625 -0.125q-0.484375 0 -0.921875 0.1875q-0.4375 0.1875 -0.765625 0.546875q-0.328125 0.34375 -0.53125 0.859375q-0.1875 0.5 -0.1875 1.140625q0 1.328125 0.640625 2.0q0.65625 0.65625 1.8125 0.65625q0.515625 0 1.0 -0.109375q0.5 -0.125 0.953125 -0.359375l0 1.1875zm8.058105 0q-0.5 0.1875 -1.03125 0.28125q-0.515625 0.09375 -1.078125 0.09375q-1.734375 0 -2.6875 -0.9375q-0.9375 -0.953125 -0.9375 -2.78125q0 -0.859375 0.265625 -1.5625q0.28125 -0.71875 0.765625 -1.21875q0.5 -0.515625 1.171875 -0.78125q0.6875 -0.28125 1.5 -0.28125q0.578125 0 1.078125 0.078125q0.5 0.078125 0.953125 0.265625l0 1.21875q-0.484375 -0.25 -0.984375 -0.359375q-0.484375 -0.125 -1.015625 -0.125q-0.484375 0 -0.921875 0.1875q-0.4375 0.1875 -0.765625 0.546875q-0.328125 0.34375 -0.53125 0.859375q-0.1875 0.5 -0.1875 1.140625q0 1.328125 0.640625 2.0q0.65625 0.65625 1.8125 0.65625q0.515625 0 1.0 -0.109375q0.5 -0.125 0.953125 -0.359375l0 1.1875zm8.558044 0.265625l-6.3125 0l0 -1.140625l2.46875 -2.46875q0.609375 -0.59375 0.984375 -1.03125q0.390625 -0.4375 0.59375 -0.796875q0.21875 -0.375 0.296875 -0.6875q0.078125 -0.328125 0.078125 -0.703125q0 -0.34375 -0.109375 -0.65625q-0.09375 -0.328125 -0.296875 -0.5625q-0.1875 -0.25 -0.5 -0.390625q-0.3125 -0.140625 -0.75 -0.140625q-0.609375 0 -1.109375 0.28125q-0.5 0.265625 -0.921875 0.6875l-0.703125 -0.828125q0.546875 -0.578125 1.25 -0.921875q0.703125 -0.34375 1.640625 -0.34375q0.640625 0 1.15625 0.1875q0.53125 0.1875 0.90625 0.546875q0.390625 0.359375 0.59375 0.890625q0.21875 0.515625 0.21875 1.15625q0 0.5625 -0.15625 1.03125q-0.140625 0.46875 -0.4375 0.9375q-0.296875 0.453125 -0.75 0.953125q-0.453125 0.5 -1.0625 1.09375l-1.734375 1.6875l4.65625 0l0 1.21875zm8.058105 -4.0625q0 0.265625 -0.015625 0.453125q0 0.1875 -0.015625 0.34375l-5.171875 0q0 1.125 0.625 1.734375q0.640625 0.59375 1.828125 0.59375q0.3125 0 0.640625 -0.015625q0.328125 -0.03125 0.625 -0.0625q0.296875 -0.046875 0.5625 -0.09375q0.28125 -0.0625 0.515625 -0.140625l0 1.046875q-0.515625 0.15625 -1.171875 0.234375q-0.65625 0.09375 -1.359375 0.09375q-0.9375 0 -1.625 -0.25q-0.6875 -0.25 -1.125 -0.734375q-0.421875 -0.5 -0.640625 -1.203125q-0.203125 -0.703125 -0.203125 -1.59375q0 -0.78125 0.21875 -1.46875q0.21875 -0.703125 0.640625 -1.21875q0.4375 -0.53125 1.0625 -0.828125q0.625 -0.3125 1.421875 -0.3125q0.765625 0 1.359375 0.25q0.59375 0.234375 1.0 0.6875q0.40625 0.4375 0.609375 1.078125q0.21875 0.625 0.21875 1.40625zm-1.328125 -0.1875q0.015625 -0.484375 -0.109375 -0.890625q-0.109375 -0.40625 -0.359375 -0.703125q-0.234375 -0.296875 -0.609375 -0.453125q-0.359375 -0.171875 -0.84375 -0.171875q-0.421875 0 -0.765625 0.171875q-0.34375 0.15625 -0.59375 0.453125q-0.25 0.28125 -0.40625 0.703125q-0.140625 0.40625 -0.1875 0.890625l3.875 0zm9.604919 -0.53125q0 1.09375 -0.234375 2.0q-0.21875 0.90625 -0.671875 1.5625q-0.4375 0.640625 -1.109375 1.0q-0.65625 0.34375 -1.546875 0.34375q-0.765625 0 -1.40625 -0.28125q-0.625 -0.296875 -1.078125 -0.890625q-0.4375 -0.59375 -0.6875 -1.53125q-0.234375 -0.9375 -0.234375 -2.203125q0 -1.09375 0.21875 -2.0q0.234375 -0.921875 0.671875 -1.5625q0.453125 -0.65625 1.109375 -1.0q0.671875 -0.359375 1.5625 -0.359375q0.765625 0 1.390625 0.296875q0.625 0.28125 1.078125 0.890625q0.453125 0.59375 0.6875 1.53125q0.25 0.921875 0.25 2.203125zm-1.296875 0.046875q0 -0.25 -0.015625 -0.5q-0.015625 -0.25 -0.046875 -0.484375l-4.046875 3.015625q0.109375 0.375 0.28125 0.703125q0.171875 0.328125 0.40625 0.5625q0.234375 0.21875 0.53125 0.359375q0.3125 0.125 0.703125 0.125q0.5 0 0.90625 -0.234375q0.40625 -0.25 0.6875 -0.71875q0.28125 -0.484375 0.4375 -1.1875q0.15625 -0.71875 0.15625 -1.640625zm-4.375 -0.09375q0 0.234375 0 0.46875q0 0.21875 0.03125 0.421875l4.046875 -2.984375q-0.109375 -0.375 -0.28125 -0.6875q-0.171875 -0.3125 -0.40625 -0.53125q-0.234375 -0.21875 -0.53125 -0.34375q-0.296875 -0.125 -0.671875 -0.125q-0.5 0 -0.90625 0.25q-0.40625 0.234375 -0.703125 0.71875q-0.28125 0.46875 -0.4375 1.1875q-0.140625 0.703125 -0.140625 1.625zm13.620605 1.796875q0 0.65625 -0.234375 1.234375q-0.234375 0.578125 -0.6875 1.015625q-0.4375 0.421875 -1.0625 0.671875q-0.609375 0.234375 -1.359375 0.234375q-0.796875 0 -1.40625 -0.25q-0.609375 -0.25 -1.015625 -0.765625q-0.40625 -0.53125 -0.625 -1.328125q-0.203125 -0.796875 -0.203125 -1.890625q0 -0.734375 0.09375 -1.421875q0.09375 -0.6875 0.3125 -1.296875q0.21875 -0.609375 0.578125 -1.109375q0.375 -0.5 0.921875 -0.859375q0.546875 -0.375 1.28125 -0.578125q0.734375 -0.203125 1.71875 -0.203125l0.9375 0l0 1.125l-1.015625 0q-0.859375 0 -1.5 0.203125q-0.625 0.203125 -1.046875 0.578125q-0.421875 0.375 -0.65625 0.90625q-0.21875 0.515625 -0.28125 1.171875l-0.03125 0.296875q0.46875 -0.265625 1.0625 -0.421875q0.609375 -0.171875 1.3125 -0.171875q0.71875 0 1.265625 0.21875q0.546875 0.203125 0.90625 0.578125q0.375 0.375 0.546875 0.90625q0.1875 0.53125 0.1875 1.15625zm-1.328125 0.078125q0 -0.4375 -0.109375 -0.796875q-0.109375 -0.359375 -0.34375 -0.59375q-0.21875 -0.25 -0.5625 -0.375q-0.34375 -0.140625 -0.828125 -0.140625q-0.28125 0 -0.578125 0.046875q-0.28125 0.046875 -0.5625 0.140625q-0.265625 0.09375 -0.515625 0.21875q-0.25 0.109375 -0.453125 0.234375q0 0.953125 0.125 1.59375q0.140625 0.625 0.390625 1.015625q0.265625 0.375 0.640625 0.53125q0.390625 0.15625 0.890625 0.15625q0.421875 0 0.765625 -0.125q0.34375 -0.140625 0.59375 -0.40625q0.25 -0.265625 0.390625 -0.640625q0.15625 -0.375 0.15625 -0.859375zm9.026794 -0.109375q0 0.71875 -0.3125 1.3125q-0.296875 0.578125 -0.84375 1.0q-0.53125 0.40625 -1.265625 0.640625q-0.734375 0.234375 -1.578125 0.234375q-0.21875 0 -0.46875 -0.015625q-0.234375 0 -0.484375 -0.015625q-0.234375 -0.015625 -0.46875 -0.046875q-0.234375 -0.015625 -0.421875 -0.046875l0 -1.15625q0.40625 0.09375 0.90625 0.140625q0.515625 0.046875 1.03125 0.046875q0.59375 0 1.0625 -0.140625q0.46875 -0.140625 0.796875 -0.390625q0.328125 -0.265625 0.5 -0.640625q0.171875 -0.375 0.171875 -0.828125q0 -0.90625 -0.640625 -1.3125q-0.640625 -0.40625 -1.84375 -0.40625l-1.8125 0l0 -4.890625l5.15625 0l0 1.125l-3.953125 0l0 2.6875l0.84375 0q0.6875 0 1.328125 0.125q0.65625 0.125 1.15625 0.4375q0.515625 0.296875 0.828125 0.828125q0.3125 0.515625 0.3125 1.3125zm8.526855 -1.71875q0 1.09375 -0.234375 2.0q-0.21875 0.90625 -0.671875 1.5625q-0.4375 0.640625 -1.109375 1.0q-0.65625 0.34375 -1.546875 0.34375q-0.765625 0 -1.40625 -0.28125q-0.625 -0.296875 -1.078125 -0.890625q-0.4375 -0.59375 -0.6875 -1.53125q-0.234375 -0.9375 -0.234375 -2.203125q0 -1.09375 0.21875 -2.0q0.234375 -0.921875 0.671875 -1.5625q0.453125 -0.65625 1.109375 -1.0q0.671875 -0.359375 1.5625 -0.359375q0.765625 0 1.390625 0.296875q0.625 0.28125 1.078125 0.890625q0.453125 0.59375 0.6875 1.53125q0.25 0.921875 0.25 2.203125zm-1.296875 0.046875q0 -0.25 -0.015625 -0.5q-0.015625 -0.25 -0.046875 -0.484375l-4.046875 3.015625q0.109375 0.375 0.28125 0.703125q0.171875 0.328125 0.40625 0.5625q0.234375 0.21875 0.53125 0.359375q0.3125 0.125 0.703125 0.125q0.5 0 0.90625 -0.234375q0.40625 -0.25 0.6875 -0.71875q0.28125 -0.484375 0.4375 -1.1875q0.15625 -0.71875 0.15625 -1.640625zm-4.375 -0.09375q0 0.234375 0 0.46875q0 0.21875 0.03125 0.421875l4.046875 -2.984375q-0.109375 -0.375 -0.28125 -0.6875q-0.171875 -0.3125 -0.40625 -0.53125q-0.234375 -0.21875 -0.53125 -0.34375q-0.296875 -0.125 -0.671875 -0.125q-0.5 0 -0.90625 0.25q-0.40625 0.234375 -0.703125 0.71875q-0.28125 0.46875 -0.4375 1.1875q-0.140625 0.703125 -0.140625 1.625zm13.511169 4.828125l-6.3125 0l0 -1.140625l2.46875 -2.46875q0.609375 -0.59375 0.984375 -1.03125q0.390625 -0.4375 0.59375 -0.796875q0.21875 -0.375 0.296875 -0.6875q0.078125 -0.328125 0.078125 -0.703125q0 -0.34375 -0.109375 -0.65625q-0.09375 -0.328125 -0.296875 -0.5625q-0.1875 -0.25 -0.5 -0.390625q-0.3125 -0.140625 -0.75 -0.140625q-0.609375 0 -1.109375 0.28125q-0.5 0.265625 -0.921875 0.6875l-0.703125 -0.828125q0.546875 -0.578125 1.25 -0.921875q0.703125 -0.34375 1.640625 -0.34375q0.640625 0 1.15625 0.1875q0.53125 0.1875 0.90625 0.546875q0.390625 0.359375 0.59375 0.890625q0.21875 0.515625 0.21875 1.15625q0 0.5625 -0.15625 1.03125q-0.140625 0.46875 -0.4375 0.9375q-0.296875 0.453125 -0.75 0.953125q-0.453125 0.5 -1.0625 1.09375l-1.734375 1.6875l4.65625 0l0 1.21875zm8.526855 -2.109375l-1.640625 0l0 2.109375l-1.296875 0l0 -2.109375l-4.609375 0l0 -1.125l4.078125 -6.34375l1.828125 0l0 6.34375l1.640625 0l0 1.125zm-2.9375 -6.234375l-3.328125 5.109375l3.328125 0l0 -5.109375zm10.636169 5.3125q0 0.65625 -0.234375 1.234375q-0.234375 0.578125 -0.6875 1.015625q-0.4375 0.421875 -1.0625 0.671875q-0.609375 0.234375 -1.359375 0.234375q-0.796875 0 -1.40625 -0.25q-0.609375 -0.25 -1.015625 -0.765625q-0.40625 -0.53125 -0.625 -1.328125q-0.203125 -0.796875 -0.203125 -1.890625q0 -0.734375 0.09375 -1.421875q0.09375 -0.6875 0.3125 -1.296875q0.21875 -0.609375 0.578125 -1.109375q0.375 -0.5 0.921875 -0.859375q0.546875 -0.375 1.28125 -0.578125q0.734375 -0.203125 1.71875 -0.203125l0.9375 0l0 1.125l-1.015625 0q-0.859375 0 -1.5 0.203125q-0.625 0.203125 -1.046875 0.578125q-0.421875 0.375 -0.65625 0.90625q-0.21875 0.515625 -0.28125 1.171875l-0.03125 0.296875q0.46875 -0.265625 1.0625 -0.421875q0.609375 -0.171875 1.3125 -0.171875q0.71875 0 1.265625 0.21875q0.546875 0.203125 0.90625 0.578125q0.375 0.375 0.546875 0.90625q0.1875 0.53125 0.1875 1.15625zm-1.328125 0.078125q0 -0.4375 -0.109375 -0.796875q-0.109375 -0.359375 -0.34375 -0.59375q-0.21875 -0.25 -0.5625 -0.375q-0.34375 -0.140625 -0.828125 -0.140625q-0.28125 0 -0.578125 0.046875q-0.28125 0.046875 -0.5625 0.140625q-0.265625 0.09375 -0.515625 0.21875q-0.25 0.109375 -0.453125 0.234375q0 0.953125 0.125 1.59375q0.140625 0.625 0.390625 1.015625q0.265625 0.375 0.640625 0.53125q0.390625 0.15625 0.890625 0.15625q0.421875 0 0.765625 -0.125q0.34375 -0.140625 0.59375 -0.40625q0.25 -0.265625 0.390625 -0.640625q0.15625 -0.375 0.15625 -0.859375zm9.183105 2.953125l-6.0 0l0 -1.1875l2.453125 0l0 -6.984375l-2.296875 1.25l-0.46875 -1.09375l3.046875 -1.59375l1.125 0l0 8.421875l2.140625 0l0 1.1875zm8.620544 -2.109375l-1.640625 0l0 2.109375l-1.296875 0l0 -2.109375l-4.609375 0l0 -1.125l4.078125 -6.34375l1.828125 0l0 6.34375l1.640625 0l0 1.125zm-2.9375 -6.234375l-3.328125 5.109375l3.328125 0l0 -5.109375zm10.433105 8.34375l-6.0 0l0 -1.1875l2.453125 0l0 -6.984375l-2.296875 1.25l-0.46875 -1.09375l3.046875 -1.59375l1.125 0l0 8.421875l2.140625 0l0 1.1875zm8.089294 -5.328125q0 1.40625 -0.34375 2.421875q-0.328125 1.0 -0.984375 1.65625q-0.65625 0.640625 -1.640625 0.953125q-0.96875 0.296875 -2.25 0.296875l-0.796875 0l0 -1.109375l0.890625 0q0.953125 0 1.640625 -0.1875q0.6875 -0.203125 1.140625 -0.5625q0.453125 -0.375 0.6875 -0.90625q0.25 -0.53125 0.3125 -1.21875l0.03125 -0.296875q-0.46875 0.28125 -1.078125 0.453125q-0.59375 0.15625 -1.296875 0.15625q-0.71875 0 -1.265625 -0.21875q-0.546875 -0.21875 -0.921875 -0.59375q-0.359375 -0.375 -0.546875 -0.890625q-0.171875 -0.53125 -0.171875 -1.15625q0 -0.65625 0.234375 -1.234375q0.25 -0.578125 0.6875 -1.0q0.4375 -0.4375 1.03125 -0.6875q0.609375 -0.25 1.34375 -0.25q0.71875 0 1.3125 0.234375q0.609375 0.234375 1.046875 0.765625q0.4375 0.515625 0.6875 1.359375q0.25 0.828125 0.25 2.015625zm-3.34375 -3.328125q-0.421875 0 -0.765625 0.140625q-0.34375 0.125 -0.609375 0.390625q-0.25 0.265625 -0.390625 0.640625q-0.140625 0.375 -0.140625 0.875q0 0.4375 0.09375 0.796875q0.109375 0.34375 0.328125 0.59375q0.234375 0.25 0.578125 0.390625q0.359375 0.125 0.84375 0.125q0.265625 0 0.546875 -0.046875q0.296875 -0.0625 0.5625 -0.140625q0.28125 -0.09375 0.53125 -0.203125q0.25 -0.125 0.453125 -0.265625q0 -0.9375 -0.140625 -1.5625q-0.140625 -0.640625 -0.40625 -1.015625q-0.265625 -0.390625 -0.640625 -0.546875q-0.375 -0.171875 -0.84375 -0.171875zm11.464355 8.65625l-6.3125 0l0 -1.140625l2.46875 -2.46875q0.609375 -0.59375 0.984375 -1.03125q0.390625 -0.4375 0.59375 -0.796875q0.21875 -0.375 0.296875 -0.6875q0.078125 -0.328125 0.078125 -0.703125q0 -0.34375 -0.109375 -0.65625q-0.09375 -0.328125 -0.296875 -0.5625q-0.1875 -0.25 -0.5 -0.390625q-0.3125 -0.140625 -0.75 -0.140625q-0.609375 0 -1.109375 0.28125q-0.5 0.265625 -0.921875 0.6875l-0.703125 -0.828125q0.546875 -0.578125 1.25 -0.921875q0.703125 -0.34375 1.640625 -0.34375q0.640625 0 1.15625 0.1875q0.53125 0.1875 0.90625 0.546875q0.390625 0.359375 0.59375 0.890625q0.21875 0.515625 0.21875 1.15625q0 0.5625 -0.15625 1.03125q-0.140625 0.46875 -0.4375 0.9375q-0.296875 0.453125 -0.75 0.953125q-0.453125 0.5 -1.0625 1.09375l-1.734375 1.6875l4.65625 0l0 1.21875zm7.9642944 0l-6.0 0l0 -1.1875l2.453125 0l0 -6.984375l-2.296875 1.25l-0.46875 -1.09375l3.046875 -1.59375l1.125 0l0 8.421875l2.140625 0l0 1.1875zm8.089355 -8.390625l-4.015625 8.390625l-1.453125 0l4.171875 -8.390625l-5.171875 0l0 -1.1875l6.46875 0l0 1.1875zm6.6205444 8.390625l-0.03125 -0.984375q-0.59375 0.59375 -1.21875 0.859375q-0.609375 0.25 -1.296875 0.25q-0.625 0 -1.078125 -0.15625q-0.4375 -0.15625 -0.734375 -0.4375q-0.28125 -0.28125 -0.421875 -0.65625q-0.140625 -0.390625 -0.140625 -0.84375q0 -1.09375 0.828125 -1.71875q0.828125 -0.640625 2.4375 -0.640625l1.515625 0l0 -0.640625q0 -0.65625 -0.421875 -1.046875q-0.40625 -0.390625 -1.265625 -0.390625q-0.625 0 -1.234375 0.140625q-0.59375 0.140625 -1.234375 0.40625l0 -1.15625q0.234375 -0.09375 0.53125 -0.171875q0.296875 -0.078125 0.625 -0.140625q0.328125 -0.078125 0.6875 -0.109375q0.359375 -0.046875 0.734375 -0.046875q0.65625 0 1.1875 0.15625q0.546875 0.140625 0.90625 0.4375q0.375 0.296875 0.5625 0.75q0.203125 0.453125 0.203125 1.078125l0 5.0625l-1.140625 0zm-0.140625 -3.34375l-1.609375 0q-0.484375 0 -0.828125 0.09375q-0.34375 0.09375 -0.5625 0.265625q-0.21875 0.171875 -0.328125 0.421875q-0.09375 0.25 -0.09375 0.5625q0 0.203125 0.0625 0.40625q0.0625 0.1875 0.203125 0.34375q0.15625 0.140625 0.390625 0.234375q0.234375 0.078125 0.5625 0.078125q0.4375 0 1.0 -0.265625q0.578125 -0.265625 1.203125 -0.84375l0 -1.296875zm9.69873 0.9375q0 0.609375 -0.25 1.09375q-0.25 0.46875 -0.703125 0.796875q-0.453125 0.3125 -1.0625 0.484375q-0.59375 0.15625 -1.3125 0.15625q-0.78125 0 -1.375 -0.171875q-0.59375 -0.171875 -1.0 -0.484375q-0.40625 -0.3125 -0.609375 -0.75q-0.203125 -0.4375 -0.203125 -0.953125q0 -0.875 0.484375 -1.515625q0.5 -0.640625 1.515625 -1.15625q-0.9375 -0.484375 -1.375 -1.0625q-0.421875 -0.578125 -0.421875 -1.328125q0 -0.46875 0.1875 -0.890625q0.1875 -0.4375 0.578125 -0.765625q0.390625 -0.34375 0.96875 -0.546875q0.578125 -0.203125 1.359375 -0.203125q0.734375 0 1.296875 0.15625q0.5625 0.15625 0.9375 0.453125q0.390625 0.296875 0.578125 0.71875q0.1875 0.40625 0.1875 0.921875q0 0.828125 -0.46875 1.421875q-0.46875 0.578125 -1.3125 1.015625q0.421875 0.21875 0.78125 0.484375q0.375 0.25 0.640625 0.5625q0.265625 0.3125 0.421875 0.703125q0.15625 0.390625 0.15625 0.859375zm-1.53125 -4.953125q0 -0.640625 -0.453125 -0.953125q-0.453125 -0.328125 -1.28125 -0.328125q-0.828125 0 -1.28125 0.3125q-0.453125 0.3125 -0.453125 0.9375q0 0.296875 0.109375 0.546875q0.109375 0.234375 0.328125 0.453125q0.234375 0.203125 0.578125 0.40625q0.34375 0.203125 0.828125 0.421875q0.828125 -0.390625 1.21875 -0.8125q0.40625 -0.421875 0.40625 -0.984375zm0.140625 5.046875q0 -0.265625 -0.09375 -0.515625q-0.078125 -0.265625 -0.3125 -0.515625q-0.21875 -0.25 -0.609375 -0.5q-0.375 -0.25 -0.96875 -0.5q-0.5 0.234375 -0.84375 0.46875q-0.328125 0.234375 -0.546875 0.484375q-0.203125 0.25 -0.296875 0.515625q-0.078125 0.265625 -0.078125 0.546875q0 0.328125 0.140625 0.59375q0.140625 0.25 0.390625 0.421875q0.25 0.171875 0.59375 0.265625q0.34375 0.09375 0.75 0.09375q0.390625 0 0.734375 -0.078125q0.34375 -0.09375 0.59375 -0.25q0.25 -0.171875 0.390625 -0.421875q0.15625 -0.25 0.15625 -0.609375zm9.448669 -1.75q0 0.265625 -0.015625 0.453125q0 0.1875 -0.015625 0.34375l-5.171875 0q0 1.125 0.625 1.734375q0.640625 0.59375 1.828125 0.59375q0.3125 0 0.640625 -0.015625q0.328125 -0.03125 0.625 -0.0625q0.296875 -0.046875 0.5625 -0.09375q0.28125 -0.0625 0.515625 -0.140625l0 1.046875q-0.515625 0.15625 -1.171875 0.234375q-0.65625 0.09375 -1.359375 0.09375q-0.9375 0 -1.625 -0.25q-0.6875 -0.25 -1.125 -0.734375q-0.421875 -0.5 -0.640625 -1.203125q-0.203125 -0.703125 -0.203125 -1.59375q0 -0.78125 0.21875 -1.46875q0.21875 -0.703125 0.640625 -1.21875q0.4375 -0.53125 1.0625 -0.828125q0.625 -0.3125 1.421875 -0.3125q0.765625 0 1.359375 0.25q0.59375 0.234375 1.0 0.6875q0.40625 0.4375 0.609375 1.078125q0.21875 0.625 0.21875 1.40625zm-1.328125 -0.1875q0.015625 -0.484375 -0.109375 -0.890625q-0.109375 -0.40625 -0.359375 -0.703125q-0.234375 -0.296875 -0.609375 -0.453125q-0.359375 -0.171875 -0.84375 -0.171875q-0.421875 0 -0.765625 0.171875q-0.34375 0.15625 -0.59375 0.453125q-0.25 0.28125 -0.40625 0.703125q-0.140625 0.40625 -0.1875 0.890625l3.875 0zm9.495605 1.21875q0 0.65625 -0.234375 1.234375q-0.234375 0.578125 -0.6875 1.015625q-0.4375 0.421875 -1.0625 0.671875q-0.609375 0.234375 -1.359375 0.234375q-0.796875 0 -1.40625 -0.25q-0.609375 -0.25 -1.015625 -0.765625q-0.40625 -0.53125 -0.625 -1.328125q-0.203125 -0.796875 -0.203125 -1.890625q0 -0.734375 0.09375 -1.421875q0.09375 -0.6875 0.3125 -1.296875q0.21875 -0.609375 0.578125 -1.109375q0.375 -0.5 0.921875 -0.859375q0.546875 -0.375 1.28125 -0.578125q0.734375 -0.203125 1.71875 -0.203125l0.9375 0l0 1.125l-1.015625 0q-0.859375 0 -1.5 0.203125q-0.625 0.203125 -1.046875 0.578125q-0.421875 0.375 -0.65625 0.90625q-0.21875 0.515625 -0.28125 1.171875l-0.03125 0.296875q0.46875 -0.265625 1.0625 -0.421875q0.609375 -0.171875 1.3125 -0.171875q0.71875 0 1.265625 0.21875q0.546875 0.203125 0.90625 0.578125q0.375 0.375 0.546875 0.90625q0.1875 0.53125 0.1875 1.15625zm-1.328125 0.078125q0 -0.4375 -0.109375 -0.796875q-0.109375 -0.359375 -0.34375 -0.59375q-0.21875 -0.25 -0.5625 -0.375q-0.34375 -0.140625 -0.828125 -0.140625q-0.28125 0 -0.578125 0.046875q-0.28125 0.046875 -0.5625 0.140625q-0.265625 0.09375 -0.515625 0.21875q-0.25 0.109375 -0.453125 0.234375q0 0.953125 0.125 1.59375q0.140625 0.625 0.390625 1.015625q0.265625 0.375 0.640625 0.53125q0.390625 0.15625 0.890625 0.15625q0.421875 0 0.765625 -0.125q0.34375 -0.140625 0.59375 -0.40625q0.25 -0.265625 0.390625 -0.640625q0.15625 -0.375 0.15625 -0.859375zm9.026794 -0.109375q0 0.71875 -0.3125 1.3125q-0.296875 0.578125 -0.84375 1.0q-0.53125 0.40625 -1.265625 0.640625q-0.734375 0.234375 -1.578125 0.234375q-0.21875 0 -0.46875 -0.015625q-0.234375 0 -0.484375 -0.015625q-0.234375 -0.015625 -0.46875 -0.046875q-0.234375 -0.015625 -0.421875 -0.046875l0 -1.15625q0.40625 0.09375 0.90625 0.140625q0.515625 0.046875 1.03125 0.046875q0.59375 0 1.0625 -0.140625q0.46875 -0.140625 0.796875 -0.390625q0.328125 -0.265625 0.5 -0.640625q0.171875 -0.375 0.171875 -0.828125q0 -0.90625 -0.640625 -1.3125q-0.640625 -0.40625 -1.84375 -0.40625l-1.8125 0l0 -4.890625l5.15625 0l0 1.125l-3.953125 0l0 2.6875l0.84375 0q0.6875 0 1.328125 0.125q0.65625 0.125 1.15625 0.4375q0.515625 0.296875 0.828125 0.828125q0.3125 0.515625 0.3125 1.3125zm8.120605 0.15625q0 0.625 -0.265625 1.1875q-0.25 0.546875 -0.765625 0.96875q-0.515625 0.40625 -1.296875 0.640625q-0.765625 0.234375 -1.796875 0.234375q-0.578125 0 -1.03125 -0.03125q-0.453125 -0.03125 -0.84375 -0.09375l0 -1.140625q0.453125 0.078125 0.953125 0.125q0.515625 0.046875 1.03125 0.046875q0.71875 0 1.21875 -0.125q0.515625 -0.140625 0.84375 -0.375q0.328125 -0.25 0.46875 -0.59375q0.140625 -0.34375 0.140625 -0.765625q0 -0.40625 -0.171875 -0.6875q-0.171875 -0.296875 -0.5 -0.5q-0.3125 -0.203125 -0.765625 -0.296875q-0.4375 -0.09375 -0.953125 -0.09375l-1.09375 0l0 -1.046875l1.109375 0q0.421875 0 0.78125 -0.109375q0.359375 -0.125 0.609375 -0.328125q0.25 -0.21875 0.375 -0.53125q0.140625 -0.3125 0.140625 -0.703125q0 -0.765625 -0.46875 -1.109375q-0.46875 -0.359375 -1.375 -0.359375q-0.484375 0 -1.0 0.09375q-0.5 0.09375 -1.09375 0.28125l0 -1.109375q0.25 -0.09375 0.53125 -0.15625q0.28125 -0.078125 0.5625 -0.125q0.28125 -0.046875 0.5625 -0.0625q0.28125 -0.03125 0.53125 -0.03125q0.765625 0 1.34375 0.171875q0.578125 0.15625 0.96875 0.46875q0.390625 0.296875 0.578125 0.75q0.203125 0.4375 0.203125 0.984375q0 0.8125 -0.421875 1.375q-0.421875 0.5625 -1.15625 0.890625q0.375 0.046875 0.734375 0.234375q0.375 0.171875 0.65625 0.453125q0.296875 0.265625 0.46875 0.640625q0.1875 0.375 0.1875 0.828125z" fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0" d="m284.04724 136.9462l391.90552 0l0 51.181107l-391.90552 0z" fill-rule="nonzero"></path><path fill="#000000" d="m319.1797 158.57675l-3.796875 -12.453125l2.171875 0l1.984375 7.1875l0.734375 2.671875q0.046875 -0.203125 0.640625 -2.5625l1.984375 -7.296875l2.171875 0l1.859375 7.21875l0.625 2.390625l0.71875 -2.40625l2.125 -7.203125l2.046875 0l-3.890625 12.453125l-2.1875 0l-1.984375 -7.453125l-0.46875 -2.125l-2.53125 9.578125l-2.203125 0zm23.566406 -4.015625l2.171875 0.28125q-0.515625 1.90625 -1.90625 2.96875q-1.390625 1.046875 -3.5625 1.046875q-2.734375 0 -4.34375 -1.671875q-1.59375 -1.6875 -1.59375 -4.734375q0 -3.140625 1.609375 -4.875q1.625 -1.734375 4.203125 -1.734375q2.5 0 4.078125 1.703125q1.59375 1.703125 1.59375 4.78125q0 0.1875 -0.015625 0.5625l-9.28125 0q0.109375 2.046875 1.15625 3.140625q1.046875 1.09375 2.609375 1.09375q1.15625 0 1.96875 -0.609375q0.828125 -0.609375 1.3125 -1.953125zm-6.9375 -3.40625l6.953125 0q-0.140625 -1.5625 -0.796875 -2.359375q-1.0 -1.21875 -2.609375 -1.21875q-1.453125 0 -2.453125 0.984375q-0.984375 0.96875 -1.09375 2.59375zm11.769531 -7.328125l0 -2.4375l2.109375 0l0 2.4375l-2.109375 0zm0 14.75l0 -12.453125l2.109375 0l0 12.453125l-2.109375 0zm4.9414062 1.03125l2.046875 0.3125q0.125 0.9375 0.71875 1.375q0.78125 0.59375 2.140625 0.59375q1.46875 0 2.265625 -0.59375q0.796875 -0.578125 1.078125 -1.640625q0.15625 -0.640625 0.140625 -2.703125q-1.375 1.625 -3.4375 1.625q-2.5625 0 -3.96875 -1.84375q-1.40625 -1.859375 -1.40625 -4.453125q0 -1.78125 0.640625 -3.28125q0.640625 -1.515625 1.859375 -2.328125q1.234375 -0.828125 2.890625 -0.828125q2.203125 0 3.625 1.78125l0 -1.5l1.953125 0l0 10.765625q0 2.90625 -0.59375 4.109375q-0.59375 1.21875 -1.875 1.921875q-1.28125 0.703125 -3.15625 0.703125q-2.234375 0 -3.609375 -1.0q-1.359375 -1.0 -1.3125 -3.015625zm1.734375 -7.484375q0 2.453125 0.96875 3.578125q0.984375 1.125 2.453125 1.125q1.453125 0 2.4375 -1.109375q0.984375 -1.125 0.984375 -3.515625q0 -2.28125 -1.015625 -3.4375q-1.015625 -1.171875 -2.453125 -1.171875q-1.40625 0 -2.390625 1.140625q-0.984375 1.140625 -0.984375 3.390625zm11.988281 6.453125l0 -17.1875l2.109375 0l0 6.171875q1.484375 -1.71875 3.734375 -1.71875q1.375 0 2.390625 0.546875q1.03125 0.546875 1.46875 1.515625q0.4375 0.953125 0.4375 2.78125l0 7.890625l-2.109375 0l0 -7.890625q0 -1.578125 -0.6875 -2.296875q-0.6875 -0.71875 -1.9375 -0.71875q-0.9375 0 -1.765625 0.484375q-0.828125 0.484375 -1.1875 1.3125q-0.34375 0.828125 -0.34375 2.296875l0 6.8125l-2.109375 0zm17.957031 -1.890625l0.3125 1.859375q-0.890625 0.203125 -1.59375 0.203125q-1.15625 0 -1.796875 -0.359375q-0.625 -0.375 -0.890625 -0.96875q-0.25 -0.59375 -0.25 -2.484375l0 -7.171875l-1.546875 0l0 -1.640625l1.546875 0l0 -3.078125l2.09375 -1.265625l0 4.34375l2.125 0l0 1.640625l-2.125 0l0 7.28125q0 0.90625 0.109375 1.171875q0.125 0.25 0.375 0.40625q0.25 0.140625 0.71875 0.140625q0.34375 0 0.921875 -0.078125zm19.835938 -8.21875l-11.34375 0l0 -1.96875l11.34375 0l0 1.96875zm0 5.21875l-11.34375 0l0 -1.96875l11.34375 0l0 1.96875zm9.777344 4.890625l0 -17.1875l3.421875 0l4.0625 12.171875q0.5625 1.703125 0.828125 2.546875q0.296875 -0.9375 0.90625 -2.765625l4.125 -11.953125l3.046875 0l0 17.1875l-2.1875 0l0 -14.375l-4.984375 14.375l-2.0625 0l-4.96875 -14.625l0 14.625l-2.1875 0zm20.070312 0l0 -17.1875l5.90625 0q2.015625 0 3.0625 0.25q1.484375 0.34375 2.515625 1.234375q1.359375 1.140625 2.03125 2.9375q0.6875 1.78125 0.6875 4.078125q0 1.953125 -0.46875 3.46875q-0.453125 1.515625 -1.171875 2.515625q-0.703125 0.984375 -1.5625 1.546875q-0.84375 0.5625 -2.046875 0.859375q-1.203125 0.296875 -2.765625 0.296875l-6.1875 0zm2.265625 -2.03125l3.671875 0q1.703125 0 2.65625 -0.3125q0.96875 -0.3125 1.546875 -0.890625q0.8125 -0.8125 1.265625 -2.171875q0.453125 -1.375 0.453125 -3.3125q0 -2.703125 -0.890625 -4.140625q-0.890625 -1.453125 -2.15625 -1.9375q-0.90625 -0.359375 -2.9375 -0.359375l-3.609375 0l0 13.125zm14.207031 -2.46875l2.21875 -0.1875q0.234375 1.609375 1.125 2.4375q0.90625 0.8125 2.171875 0.8125q1.53125 0 2.578125 -1.140625q1.0625 -1.15625 1.0625 -3.0625q0 -1.796875 -1.015625 -2.84375q-1.015625 -1.046875 -2.65625 -1.046875q-1.015625 0 -1.84375 0.46875q-0.8125 0.453125 -1.28125 1.203125l-1.984375 -0.265625l1.65625 -8.828125l8.546875 0l0 2.015625l-6.859375 0l-0.921875 4.625q1.546875 -1.078125 3.25 -1.078125q2.25 0 3.796875 1.5625q1.546875 1.546875 1.546875 4.0q0 2.328125 -1.359375 4.03125q-1.65625 2.09375 -4.515625 2.09375q-2.34375 0 -3.828125 -1.3125q-1.484375 -1.3125 -1.6875 -3.484375zm17.957031 9.546875q-1.734375 -2.203125 -2.953125 -5.15625q-1.203125 -2.953125 -1.203125 -6.109375q0 -2.796875 0.90625 -5.34375q1.046875 -2.96875 3.25 -5.90625l1.515625 0q-1.421875 2.4375 -1.875 3.46875q-0.71875 1.625 -1.125 3.375q-0.5 2.203125 -0.5 4.40625q0 5.640625 3.5 11.265625l-1.515625 0zm4.6171875 -5.046875l0 -17.1875l2.28125 0l0 17.1875l-2.28125 0zm6.2929688 0l0 -17.1875l5.90625 0q2.015625 0 3.0625 0.25q1.484375 0.34375 2.515625 1.234375q1.359375 1.140625 2.03125 2.9375q0.6875 1.78125 0.6875 4.078125q0 1.953125 -0.46875 3.46875q-0.453125 1.515625 -1.171875 2.515625q-0.703125 0.984375 -1.5625 1.546875q-0.84375 0.5625 -2.046875 0.859375q-1.203125 0.296875 -2.765625 0.296875l-6.1875 0zm2.265625 -2.03125l3.671875 0q1.703125 0 2.65625 -0.3125q0.96875 -0.3125 1.546875 -0.890625q0.8125 -0.8125 1.265625 -2.171875q0.453125 -1.375 0.453125 -3.3125q0 -2.703125 -0.890625 -4.140625q-0.890625 -1.453125 -2.15625 -1.9375q-0.90625 -0.359375 -2.9375 -0.359375l-3.609375 0l0 13.125zm18.816406 7.078125q-1.734375 -2.203125 -2.953125 -5.15625q-1.203125 -2.953125 -1.203125 -6.109375q0 -2.796875 0.90625 -5.34375q1.046875 -2.96875 3.25 -5.90625l1.515625 0q-1.421875 2.4375 -1.875 3.46875q-0.71875 1.625 -1.125 3.375q-0.5 2.203125 -0.5 4.40625q0 5.640625 3.5 11.265625l-1.515625 0zm3.4609375 -10.5625l2.140625 -0.1875q0.15625 1.28125 0.703125 2.109375q0.5625 0.828125 1.734375 1.34375q1.171875 0.5 2.640625 0.5q1.296875 0 2.296875 -0.375q1.0 -0.390625 1.484375 -1.0625q0.484375 -0.6875 0.484375 -1.484375q0 -0.796875 -0.46875 -1.40625q-0.46875 -0.609375 -1.546875 -1.015625q-0.6875 -0.265625 -3.0625 -0.828125q-2.359375 -0.578125 -3.3125 -1.078125q-1.234375 -0.640625 -1.84375 -1.59375q-0.59375 -0.96875 -0.59375 -2.140625q0 -1.3125 0.734375 -2.4375q0.75 -1.125 2.15625 -1.703125q1.421875 -0.59375 3.15625 -0.59375q1.90625 0 3.359375 0.609375q1.46875 0.609375 2.25 1.8125q0.796875 1.1875 0.84375 2.703125l-2.171875 0.171875q-0.171875 -1.640625 -1.1875 -2.46875q-1.015625 -0.828125 -3.0 -0.828125q-2.0625 0 -3.015625 0.765625q-0.9375 0.75 -0.9375 1.8125q0 0.921875 0.671875 1.515625q0.65625 0.609375 3.421875 1.234375q2.78125 0.625 3.8125 1.09375q1.5 0.6875 2.203125 1.75q0.71875 1.0625 0.71875 2.4375q0 1.375 -0.78125 2.59375q-0.78125 1.203125 -2.25 1.890625q-1.46875 0.671875 -3.3125 0.671875q-2.328125 0 -3.90625 -0.671875q-1.578125 -0.6875 -2.484375 -2.046875q-0.890625 -1.375 -0.9375 -3.09375zm17.898438 10.5625l-1.515625 0q3.5 -5.625 3.5 -11.265625q0 -2.203125 -0.5 -4.359375q-0.390625 -1.765625 -1.109375 -3.375q-0.453125 -1.0625 -1.890625 -3.515625l1.515625 0q2.203125 2.9375 3.25 5.90625q0.90625 2.546875 0.90625 5.34375q0 3.15625 -1.21875 6.109375q-1.203125 2.953125 -2.9375 5.15625zm17.707031 -7.828125l0 -4.703125l-4.671875 0l0 -1.96875l4.671875 0l0 -4.6875l1.984375 0l0 4.6875l4.6875 0l0 1.96875l-4.6875 0l0 4.703125l-1.984375 0zm16.449219 2.78125l0 -17.1875l3.421875 0l4.0625 12.171875q0.5625 1.703125 0.828125 2.546875q0.296875 -0.9375 0.90625 -2.765625l4.125 -11.953125l3.046875 0l0 17.1875l-2.1875 0l0 -14.375l-4.984375 14.375l-2.0625 0l-4.96875 -14.625l0 14.625l-2.1875 0zm20.070312 0l0 -17.1875l5.90625 0q2.015625 0 3.0625 0.25q1.484375 0.34375 2.515625 1.234375q1.359375 1.140625 2.03125 2.9375q0.6875 1.78125 0.6875 4.078125q0 1.953125 -0.46875 3.46875q-0.453125 1.515625 -1.171875 2.515625q-0.703125 0.984375 -1.5625 1.546875q-0.84375 0.5625 -2.046875 0.859375q-1.203125 0.296875 -2.765625 0.296875l-6.1875 0zm2.265625 -2.03125l3.671875 0q1.703125 0 2.65625 -0.3125q0.96875 -0.3125 1.546875 -0.890625q0.8125 -0.8125 1.265625 -2.171875q0.453125 -1.375 0.453125 -3.3125q0 -2.703125 -0.890625 -4.140625q-0.890625 -1.453125 -2.15625 -1.9375q-0.90625 -0.359375 -2.9375 -0.359375l-3.609375 0l0 13.125zm14.207031 -2.46875l2.21875 -0.1875q0.234375 1.609375 1.125 2.4375q0.90625 0.8125 2.171875 0.8125q1.53125 0 2.578125 -1.140625q1.0625 -1.15625 1.0625 -3.0625q0 -1.796875 -1.015625 -2.84375q-1.015625 -1.046875 -2.65625 -1.046875q-1.015625 0 -1.84375 0.46875q-0.8125 0.453125 -1.28125 1.203125l-1.984375 -0.265625l1.65625 -8.828125l8.546875 0l0 2.015625l-6.859375 0l-0.921875 4.625q1.546875 -1.078125 3.25 -1.078125q2.25 0 3.796875 1.5625q1.546875 1.546875 1.546875 4.0q0 2.328125 -1.359375 4.03125q-1.65625 2.09375 -4.515625 2.09375q-2.34375 0 -3.828125 -1.3125q-1.484375 -1.3125 -1.6875 -3.484375zm17.957031 9.546875q-1.734375 -2.203125 -2.953125 -5.15625q-1.203125 -2.953125 -1.203125 -6.109375q0 -2.796875 0.90625 -5.34375q1.046875 -2.96875 3.25 -5.90625l1.515625 0q-1.421875 2.4375 -1.875 3.46875q-0.71875 1.625 -1.125 3.375q-0.5 2.203125 -0.5 4.40625q0 5.640625 3.5 11.265625l-1.515625 0zm4.1484375 -5.046875l0 -17.1875l6.4375 0q1.96875 0 3.15625 0.53125q1.1875 0.515625 1.859375 1.609375q0.6875 1.078125 0.6875 2.265625q0 1.09375 -0.609375 2.078125q-0.59375 0.96875 -1.796875 1.5625q1.5625 0.453125 2.390625 1.5625q0.84375 1.09375 0.84375 2.59375q0 1.203125 -0.515625 2.25q-0.5 1.03125 -1.25 1.59375q-0.75 0.5625 -1.890625 0.859375q-1.125 0.28125 -2.765625 0.28125l-6.546875 0zm2.265625 -9.96875l3.71875 0q1.515625 0 2.171875 -0.1875q0.859375 -0.265625 1.296875 -0.859375q0.4375 -0.59375 0.4375 -1.5q0 -0.859375 -0.40625 -1.5q-0.40625 -0.65625 -1.171875 -0.890625q-0.765625 -0.25 -2.609375 -0.25l-3.4375 0l0 5.1875zm0 7.9375l4.28125 0q1.09375 0 1.546875 -0.078125q0.78125 -0.140625 1.3125 -0.46875q0.53125 -0.328125 0.859375 -0.953125q0.34375 -0.625 0.34375 -1.453125q0 -0.953125 -0.5 -1.65625q-0.484375 -0.71875 -1.359375 -1.0q-0.875 -0.296875 -2.515625 -0.296875l-3.96875 0l0 5.90625zm14.9453125 7.078125l-1.515625 0q3.5 -5.625 3.5 -11.265625q0 -2.203125 -0.5 -4.359375q-0.390625 -1.765625 -1.109375 -3.375q-0.453125 -1.0625 -1.890625 -3.515625l1.515625 0q2.203125 2.9375 3.25 5.90625q0.90625 2.546875 0.90625 5.34375q0 3.15625 -1.21875 6.109375q-1.203125 2.953125 -2.9375 5.15625zm7.9921875 0l-1.515625 0q3.5 -5.625 3.5 -11.265625q0 -2.203125 -0.5 -4.359375q-0.390625 -1.765625 -1.109375 -3.375q-0.453125 -1.0625 -1.890625 -3.515625l1.515625 0q2.203125 2.9375 3.25 5.90625q0.90625 2.546875 0.90625 5.34375q0 3.15625 -1.21875 6.109375q-1.203125 2.953125 -2.9375 5.15625z" fill-rule="nonzero"></path></g></svg>
+
diff --git a/doc/images/Session_Establishment.svg b/doc/images/Session_Establishment.svg
new file mode 100644 (file)
index 0000000..e60cb9b
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" standalone="yes"?>
+
+<svg version="1.1" viewBox="0.0 0.0 1338.0 1283.0" fill="none" stroke="none" stroke-linecap="square" stroke-miterlimit="10" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><clipPath id="p.0"><path d="m0 0l1338.0 0l0 1283.0l-1338.0 0l0 -1283.0z" clip-rule="nonzero"></path></clipPath><g clip-path="url(#p.0)"><path fill="#000000" fill-opacity="0.0" d="m0 0l1338.0 0l0 1283.0l-1338.0 0z" fill-rule="nonzero"></path><path fill="#d9ead3" d="m529.084 59.792652l179.27557 0l0 94.645676l-179.27557 0z" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m529.084 59.792652l179.27557 0l0 94.645676l-179.27557 0z" fill-rule="nonzero"></path><path fill="#000000" d="m573.0276 114.035484l-3.609375 -13.59375l1.84375 0l2.0625 8.90625q0.34375 1.40625 0.578125 2.78125q0.515625 -2.171875 0.609375 -2.515625l2.59375 -9.171875l2.171875 0l1.953125 6.875q0.734375 2.5625 1.046875 4.8125q0.265625 -1.28125 0.6875 -2.953125l2.125 -8.734375l1.8125 0l-3.734375 13.59375l-1.734375 0l-2.859375 -10.359375q-0.359375 -1.296875 -0.421875 -1.59375q-0.21875 0.9375 -0.40625 1.59375l-2.890625 10.359375l-1.828125 0zm14.389893 -4.921875q0 -2.734375 1.53125 -4.0625q1.265625 -1.09375 3.09375 -1.09375q2.03125 0 3.3125 1.34375q1.296875 1.328125 1.296875 3.671875q0 1.90625 -0.578125 3.0q-0.5625 1.078125 -1.65625 1.6875q-1.078125 0.59375 -2.375 0.59375q-2.0625 0 -3.34375 -1.328125q-1.28125 -1.328125 -1.28125 -3.8125zm1.71875 0q0 1.890625 0.828125 2.828125q0.828125 0.9375 2.078125 0.9375q1.25 0 2.0625 -0.9375q0.828125 -0.953125 0.828125 -2.890625q0 -1.828125 -0.828125 -2.765625q-0.828125 -0.9375 -2.0625 -0.9375q-1.25 0 -2.078125 0.9375q-0.828125 0.9375 -0.828125 2.828125zm9.266357 4.921875l0 -9.859375l1.5 0l0 1.5q0.578125 -1.046875 1.0625 -1.375q0.484375 -0.34375 1.078125 -0.34375q0.84375 0 1.71875 0.546875l-0.578125 1.546875q-0.609375 -0.359375 -1.234375 -0.359375q-0.546875 0 -0.984375 0.328125q-0.421875 0.328125 -0.609375 0.90625q-0.28125 0.890625 -0.28125 1.953125l0 5.15625l-1.671875 0zm6.2438965 0l0 -13.59375l1.671875 0l0 7.75l3.953125 -4.015625l2.15625 0l-3.765625 3.65625l4.140625 6.203125l-2.0625 0l-3.25 -5.03125l-1.171875 1.125l0 3.90625l-1.671875 0zm10.859375 0l-1.546875 0l0 -13.59375l1.65625 0l0 4.84375q1.0625 -1.328125 2.703125 -1.328125q0.90625 0 1.71875 0.375q0.8125 0.359375 1.328125 1.03125q0.53125 0.65625 0.828125 1.59375q0.296875 0.9375 0.296875 2.0q0 2.53125 -1.25 3.921875q-1.25 1.375 -3.0 1.375q-1.75 0 -2.734375 -1.453125l0 1.234375zm-0.015625 -5.0q0 1.765625 0.46875 2.5625q0.796875 1.28125 2.140625 1.28125q1.09375 0 1.890625 -0.9375q0.796875 -0.953125 0.796875 -2.84375q0 -1.921875 -0.765625 -2.84375q-0.765625 -0.921875 -1.84375 -0.921875q-1.09375 0 -1.890625 0.953125q-0.796875 0.953125 -0.796875 2.75zm15.594482 1.828125l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm9.110107 5.875l0 -9.859375l1.5 0l0 1.40625q1.09375 -1.625 3.140625 -1.625q0.890625 0 1.640625 0.328125q0.75 0.3125 1.109375 0.84375q0.375 0.515625 0.53125 1.21875q0.09375 0.46875 0.09375 1.625l0 6.0625l-1.671875 0l0 -6.0q0 -1.015625 -0.203125 -1.515625q-0.1875 -0.515625 -0.6875 -0.8125q-0.5 -0.296875 -1.171875 -0.296875q-1.0625 0 -1.84375 0.671875q-0.765625 0.671875 -0.765625 2.578125l0 5.375l-1.671875 0zm16.813171 -3.609375l1.640625 0.21875q-0.265625 1.6875 -1.375 2.65625q-1.109375 0.953125 -2.734375 0.953125q-2.015625 0 -3.25 -1.3125q-1.21875 -1.328125 -1.21875 -3.796875q0 -1.59375 0.515625 -2.78125q0.53125 -1.203125 1.609375 -1.796875q1.09375 -0.609375 2.359375 -0.609375q1.609375 0 2.625 0.8125q1.015625 0.8125 1.3125 2.3125l-1.625 0.25q-0.234375 -1.0 -0.828125 -1.5q-0.59375 -0.5 -1.421875 -0.5q-1.265625 0 -2.0625 0.90625q-0.78125 0.90625 -0.78125 2.859375q0 1.984375 0.765625 2.890625q0.765625 0.890625 1.984375 0.890625q0.984375 0 1.640625 -0.59375q0.65625 -0.609375 0.84375 -1.859375zm2.890625 3.609375l0 -13.59375l1.671875 0l0 4.875q1.171875 -1.359375 2.953125 -1.359375q1.09375 0 1.890625 0.4375q0.8125 0.421875 1.15625 1.1875q0.359375 0.765625 0.359375 2.203125l0 6.25l-1.671875 0l0 -6.25q0 -1.25 -0.546875 -1.8125q-0.546875 -0.578125 -1.53125 -0.578125q-0.75 0 -1.40625 0.390625q-0.640625 0.375 -0.921875 1.046875q-0.28125 0.65625 -0.28125 1.8125l0 5.390625l-1.671875 0z" fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0" d="m186.2126 85.77165l342.2677 2.708664" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m186.2126 85.77165l336.26794 2.6611862" fill-rule="evenodd"></path><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m522.4674 90.08451l4.5510254 -1.6157684l-4.5248413 -1.6875916z" fill-rule="evenodd"></path><path fill="#d9ead3" d="m464.64304 281.8714l154.07877 -82.47244l154.07874 82.47244l-154.07874 82.47244z" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m464.64304 281.8714l154.07877 -82.47244l154.07874 82.47244l-154.07874 82.47244z" fill-rule="nonzero"></path><path fill="#000000" d="m550.6512 266.79138l5.234375 -13.593735l1.9375 0l5.5625 13.593735l-2.046875 0l-1.59375 -4.125l-5.6875 0l-1.484375 4.125l-1.921875 0zm3.921875 -5.578125l4.609375 0l-1.40625 -3.78125q-0.65625 -1.7031097 -0.96875 -2.8124847q-0.265625 1.3125 -0.734375 2.5937347l-1.5 4.0zm9.8029175 5.578125l0 -9.859375l1.5 0l0 1.40625q1.09375 -1.625 3.140625 -1.625q0.890625 0 1.640625 0.328125q0.75 0.3125 1.109375 0.84375q0.375 0.515625 0.53125 1.21875q0.09375 0.46875 0.09375 1.625l0 6.0625l-1.671875 0l0 -6.0q0 -1.015625 -0.203125 -1.515625q-0.1875 -0.515625 -0.6875 -0.8125q-0.5 -0.296875 -1.171875 -0.296875q-1.0625 0 -1.84375 0.671875q-0.765625 0.671875 -0.765625 2.578125l0 5.375l-1.671875 0zm9.750732 -4.921875q0 -2.734375 1.53125 -4.0625q1.265625 -1.09375 3.09375 -1.09375q2.03125 0 3.3125 1.34375q1.296875 1.328125 1.296875 3.671875q0 1.90625 -0.578125 3.0q-0.5625 1.078125 -1.65625 1.6875q-1.078125 0.59375 -2.375 0.59375q-2.0625 0 -3.34375 -1.328125q-1.28125 -1.328125 -1.28125 -3.8125zm1.71875 0q0 1.890625 0.828125 2.828125q0.828125 0.9375 2.078125 0.9375q1.25 0 2.0625 -0.9375q0.828125 -0.953125 0.828125 -2.890625q0 -1.828125 -0.828125 -2.765625q-0.828125 -0.9375 -2.0625 -0.9375q-1.25 0 -2.078125 0.9375q-0.828125 0.9375 -0.828125 2.828125zm9.281982 4.921875l0 -9.859375l1.5 0l0 1.40625q1.09375 -1.625 3.140625 -1.625q0.890625 0 1.640625 0.328125q0.75 0.3125 1.109375 0.84375q0.375 0.515625 0.53125 1.21875q0.09375 0.46875 0.09375 1.625l0 6.0625l-1.671875 0l0 -6.0q0 -1.015625 -0.203125 -1.515625q-0.1875 -0.515625 -0.6875 -0.8125q-0.5 -0.296875 -1.171875 -0.296875q-1.0625 0 -1.84375 0.671875q-0.765625 0.671875 -0.765625 2.578125l0 5.375l-1.671875 0zm10.297546 3.796875l-0.171875 -1.5625q0.546875 0.140625 0.953125 0.140625q0.546875 0 0.875 -0.1875q0.34375 -0.1875 0.5625 -0.515625q0.15625 -0.25 0.5 -1.25q0.046875 -0.140625 0.15625 -0.40625l-3.734375 -9.875l1.796875 0l2.046875 5.71875q0.40625 1.078125 0.71875 2.28125q0.28125 -1.15625 0.6875 -2.25l2.09375 -5.75l1.671875 0l-3.75 10.03125q-0.59375 1.625 -0.9375 2.234375q-0.4375 0.828125 -1.015625 1.203125q-0.578125 0.390625 -1.375 0.390625q-0.484375 0 -1.078125 -0.203125zm9.40625 -3.796875l0 -9.859375l1.5 0l0 1.390625q0.453125 -0.71875 1.21875 -1.15625q0.78125 -0.453125 1.765625 -0.453125q1.09375 0 1.796875 0.453125q0.703125 0.453125 0.984375 1.28125q1.171875 -1.734375 3.046875 -1.734375q1.46875 0 2.25 0.8125q0.796875 0.8125 0.796875 2.5l0 6.765625l-1.671875 0l0 -6.203125q0 -1.0 -0.15625 -1.4375q-0.15625 -0.453125 -0.59375 -0.71875q-0.421875 -0.265625 -1.0 -0.265625q-1.03125 0 -1.71875 0.6875q-0.6875 0.6875 -0.6875 2.21875l0 5.71875l-1.671875 0l0 -6.40625q0 -1.109375 -0.40625 -1.65625q-0.40625 -0.5625 -1.34375 -0.5625q-0.703125 0 -1.3125 0.375q-0.59375 0.359375 -0.859375 1.078125q-0.265625 0.71875 -0.265625 2.0625l0 5.109375l-1.671875 0zm14.9158325 -4.921875q0 -2.734375 1.53125 -4.0625q1.265625 -1.09375 3.09375 -1.09375q2.03125 0 3.3125 1.34375q1.296875 1.328125 1.296875 3.671875q0 1.90625 -0.578125 3.0q-0.5625 1.078125 -1.65625 1.6875q-1.078125 0.59375 -2.375 0.59375q-2.0625 0 -3.34375 -1.328125q-1.28125 -1.328125 -1.28125 -3.8125zm1.71875 0q0 1.890625 0.828125 2.828125q0.828125 0.9375 2.078125 0.9375q1.25 0 2.0625 -0.9375q0.828125 -0.953125 0.828125 -2.890625q0 -1.828125 -0.828125 -2.765625q-0.828125 -0.9375 -2.0625 -0.9375q-1.25 0 -2.078125 0.9375q-0.828125 0.9375 -0.828125 2.828125zm15.735107 4.921875l0 -1.453125q-1.140625 1.671875 -3.125 1.671875q-0.859375 0 -1.625 -0.328125q-0.75 -0.34375 -1.125 -0.84375q-0.359375 -0.5 -0.515625 -1.234375q-0.09375 -0.5 -0.09375 -1.5625l0 -6.109375l1.671875 0l0 5.46875q0 1.3125 0.09375 1.765625q0.15625 0.65625 0.671875 1.03125q0.515625 0.375 1.265625 0.375q0.75 0 1.40625 -0.375q0.65625 -0.390625 0.921875 -1.046875q0.28125 -0.671875 0.28125 -1.9375l0 -5.28125l1.671875 0l0 9.859375l-1.5 0zm3.2506714 -2.9375l1.65625 -0.265625q0.140625 1.0 0.765625 1.53125q0.640625 0.515625 1.78125 0.515625q1.15625 0 1.703125 -0.46875q0.5625 -0.46875 0.5625 -1.09375q0 -0.5625 -0.484375 -0.890625q-0.34375 -0.21875 -1.703125 -0.5625q-1.84375 -0.46875 -2.5625 -0.796875q-0.703125 -0.34375 -1.078125 -0.9375q-0.359375 -0.609375 -0.359375 -1.328125q0 -0.65625 0.296875 -1.21875q0.3125 -0.5625 0.828125 -0.9375q0.390625 -0.28125 1.0625 -0.484375q0.671875 -0.203125 1.4375 -0.203125q1.171875 0 2.046875 0.34375q0.875 0.328125 1.28125 0.90625q0.421875 0.5625 0.578125 1.515625l-1.625 0.21875q-0.109375 -0.75 -0.65625 -1.171875q-0.53125 -0.4375 -1.5 -0.4375q-1.15625 0 -1.640625 0.390625q-0.484375 0.375 -0.484375 0.875q0 0.328125 0.203125 0.59375q0.203125 0.265625 0.640625 0.4375q0.25 0.09375 1.46875 0.4375q1.765625 0.46875 2.46875 0.765625q0.703125 0.296875 1.09375 0.875q0.40625 0.578125 0.40625 1.4375q0 0.828125 -0.484375 1.578125q-0.484375 0.734375 -1.40625 1.140625q-0.921875 0.390625 -2.078125 0.390625q-1.921875 0 -2.9375 -0.796875q-1.0 -0.796875 -1.28125 -2.359375z" fill-rule="nonzero"></path><path fill="#000000" d="m558.36993 287.57263q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm10.516296 1.328125l1.640625 0.21875q-0.265625 1.6875 -1.375 2.65625q-1.109375 0.953125 -2.734375 0.953125q-2.015625 0 -3.25 -1.3125q-1.21875 -1.328125 -1.21875 -3.796875q0 -1.59375 0.515625 -2.78125q0.53125 -1.203125 1.609375 -1.796875q1.09375 -0.609375 2.359375 -0.609375q1.609375 0 2.625 0.8125q1.015625 0.8125 1.3125 2.3125l-1.625 0.25q-0.234375 -1.0 -0.828125 -1.5q-0.59375 -0.5 -1.421875 -0.5q-1.265625 0 -2.0625 0.90625q-0.78125 0.90625 -0.78125 2.859375q0 1.984375 0.765625 2.890625q0.765625 0.890625 1.984375 0.890625q0.984375 0 1.640625 -0.59375q0.65625 -0.609375 0.84375 -1.859375zm9.328125 0l1.640625 0.21875q-0.265625 1.6875 -1.375 2.65625q-1.109375 0.953125 -2.734375 0.953125q-2.015625 0 -3.25 -1.3125q-1.21875 -1.328125 -1.21875 -3.796875q0 -1.59375 0.515625 -2.78125q0.53125 -1.203125 1.609375 -1.796875q1.09375 -0.609375 2.359375 -0.609375q1.609375 0 2.625 0.8125q1.015625 0.8125 1.3125 2.3125l-1.625 0.25q-0.234375 -1.0 -0.828125 -1.5q-0.59375 -0.5 -1.421875 -0.5q-1.265625 0 -2.0625 0.90625q-0.78125 0.90625 -0.78125 2.859375q0 1.984375 0.765625 2.890625q0.765625 0.890625 1.984375 0.890625q0.984375 0 1.640625 -0.59375q0.65625 -0.609375 0.84375 -1.859375zm9.640625 0.4375l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm8.438232 2.9375l1.65625 -0.265625q0.140625 1.0 0.765625 1.53125q0.640625 0.515625 1.78125 0.515625q1.15625 0 1.703125 -0.46875q0.5625 -0.46875 0.5625 -1.09375q0 -0.5625 -0.484375 -0.890625q-0.34375 -0.21875 -1.703125 -0.5625q-1.84375 -0.46875 -2.5625 -0.796875q-0.703125 -0.34375 -1.078125 -0.9375q-0.359375 -0.609375 -0.359375 -1.328125q0 -0.65625 0.296875 -1.21875q0.3125 -0.5625 0.828125 -0.9375q0.390625 -0.28125 1.0625 -0.484375q0.671875 -0.203125 1.4375 -0.203125q1.171875 0 2.046875 0.34375q0.875 0.328125 1.28125 0.90625q0.421875 0.5625 0.578125 1.515625l-1.625 0.21875q-0.109375 -0.75 -0.65625 -1.171875q-0.53125 -0.4375 -1.5 -0.4375q-1.15625 0 -1.640625 0.390625q-0.484375 0.375 -0.484375 0.875q0 0.328125 0.203125 0.59375q0.203125 0.265625 0.640625 0.4375q0.25 0.09375 1.46875 0.4375q1.765625 0.46875 2.46875 0.765625q0.703125 0.296875 1.09375 0.875q0.40625 0.578125 0.40625 1.4375q0 0.828125 -0.484375 1.578125q-0.484375 0.734375 -1.40625 1.140625q-0.921875 0.390625 -2.078125 0.390625q-1.921875 0 -2.9375 -0.796875q-1.0 -0.796875 -1.28125 -2.359375zm9.328125 0l1.65625 -0.265625q0.140625 1.0 0.765625 1.53125q0.640625 0.515625 1.78125 0.515625q1.15625 0 1.703125 -0.46875q0.5625 -0.46875 0.5625 -1.09375q0 -0.5625 -0.484375 -0.890625q-0.34375 -0.21875 -1.703125 -0.5625q-1.84375 -0.46875 -2.5625 -0.796875q-0.703125 -0.34375 -1.078125 -0.9375q-0.359375 -0.609375 -0.359375 -1.328125q0 -0.65625 0.296875 -1.21875q0.3125 -0.5625 0.828125 -0.9375q0.390625 -0.28125 1.0625 -0.484375q0.671875 -0.203125 1.4375 -0.203125q1.171875 0 2.046875 0.34375q0.875 0.328125 1.28125 0.90625q0.421875 0.5625 0.578125 1.515625l-1.625 0.21875q-0.109375 -0.75 -0.65625 -1.171875q-0.53125 -0.4375 -1.5 -0.4375q-1.15625 0 -1.640625 0.390625q-0.484375 0.375 -0.484375 0.875q0 0.328125 0.203125 0.59375q0.203125 0.265625 0.640625 0.4375q0.25 0.09375 1.46875 0.4375q1.765625 0.46875 2.46875 0.765625q0.703125 0.296875 1.09375 0.875q0.40625 0.578125 0.40625 1.4375q0 0.828125 -0.484375 1.578125q-0.484375 0.734375 -1.40625 1.140625q-0.921875 0.390625 -2.078125 0.390625q-1.921875 0 -2.9375 -0.796875q-1.0 -0.796875 -1.28125 -2.359375zm21.933289 -0.234375l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm9.110107 5.875l0 -9.859375l1.5 0l0 1.40625q1.09375 -1.625 3.140625 -1.625q0.890625 0 1.640625 0.328125q0.75 0.3125 1.109375 0.84375q0.375 0.515625 0.53125 1.21875q0.09375 0.46875 0.09375 1.625l0 6.0625l-1.671875 0l0 -6.0q0 -1.015625 -0.203125 -1.515625q-0.1875 -0.515625 -0.6875 -0.8125q-0.5 -0.296875 -1.171875 -0.296875q-1.0625 0 -1.84375 0.671875q-0.765625 0.671875 -0.765625 2.578125l0 5.375l-1.671875 0zm16.813232 -1.21875q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm5.6257324 4.9375l-1.546875 0l0 -13.59375l1.65625 0l0 4.84375q1.0625 -1.328125 2.703125 -1.328125q0.90625 0 1.71875 0.375q0.8125 0.359375 1.328125 1.03125q0.53125 0.65625 0.828125 1.59375q0.296875 0.9375 0.296875 2.0q0 2.53125 -1.25 3.921875q-1.25 1.375 -3.0 1.375q-1.75 0 -2.734375 -1.453125l0 1.234375zm-0.015625 -5.0q0 1.765625 0.46875 2.5625q0.796875 1.28125 2.140625 1.28125q1.09375 0 1.890625 -0.9375q0.796875 -0.953125 0.796875 -2.84375q0 -1.921875 -0.765625 -2.84375q-0.765625 -0.921875 -1.84375 -0.921875q-1.09375 0 -1.890625 0.953125q-0.796875 0.953125 -0.796875 2.75zm8.813171 5.0l0 -13.59375l1.671875 0l0 13.59375l-1.671875 0zm10.926086 -3.171875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm15.500732 5.875l0 -1.25q-0.9375 1.46875 -2.75 1.46875q-1.171875 0 -2.171875 -0.640625q-0.984375 -0.65625 -1.53125 -1.8125q-0.53125 -1.171875 -0.53125 -2.6875q0 -1.46875 0.484375 -2.671875q0.5 -1.203125 1.46875 -1.84375q0.984375 -0.640625 2.203125 -0.640625q0.890625 0 1.578125 0.375q0.703125 0.375 1.140625 0.984375l0 -4.875l1.65625 0l0 13.59375l-1.546875 0zm-5.28125 -4.921875q0 1.890625 0.796875 2.828125q0.8125 0.9375 1.890625 0.9375q1.09375 0 1.859375 -0.890625q0.765625 -0.890625 0.765625 -2.734375q0 -2.015625 -0.78125 -2.953125q-0.78125 -0.953125 -1.921875 -0.953125q-1.109375 0 -1.859375 0.90625q-0.75 0.90625 -0.75 2.859375z" fill-rule="nonzero"></path><path fill="#000000" d="m559.7137 309.182q-0.828125 0.921875 -1.8125 1.390625q-0.96875 0.453125 -2.09375 0.453125q-2.09375 0 -3.3125 -1.40625q-1.0 -1.15625 -1.0 -2.578125q0 -1.265625 0.8125 -2.28125q0.8125 -1.015625 2.421875 -1.78125q-0.90625 -1.0625 -1.21875 -1.71875q-0.296875 -0.65625 -0.296875 -1.265625q0 -1.234375 0.953125 -2.125q0.953125 -0.90625 2.421875 -0.90625q1.390625 0 2.265625 0.859375q0.890625 0.84375 0.890625 2.046875q0 1.9375 -2.5625 3.3125l2.4375 3.09375q0.421875 -0.8125 0.640625 -1.890625l1.734375 0.375q-0.4375 1.78125 -1.203125 2.9375q0.9375 1.234375 2.125 2.078125l-1.125 1.328125q-1.0 -0.640625 -2.078125 -1.921875zm-3.40625 -7.078125q1.09375 -0.640625 1.40625 -1.125q0.328125 -0.484375 0.328125 -1.0625q0 -0.703125 -0.453125 -1.140625q-0.4375 -0.4375 -1.09375 -0.4375q-0.671875 0 -1.125 0.4375q-0.453125 0.421875 -0.453125 1.0625q0 0.3125 0.15625 0.65625q0.171875 0.34375 0.5 0.734375l0.734375 0.875zm2.359375 5.765625l-3.0625 -3.796875q-1.359375 0.8125 -1.84375 1.5q-0.46875 0.6875 -0.46875 1.375q0 0.8125 0.65625 1.703125q0.671875 0.890625 1.875 0.890625q0.75 0 1.546875 -0.46875q0.8125 -0.46875 1.296875 -1.203125zm17.329956 1.703125q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm10.516357 1.328125l1.640625 0.21875q-0.265625 1.6875 -1.375 2.65625q-1.109375 0.953125 -2.734375 0.953125q-2.015625 0 -3.25 -1.3125q-1.21875 -1.328125 -1.21875 -3.796875q0 -1.59375 0.515625 -2.78125q0.53125 -1.203125 1.609375 -1.796875q1.09375 -0.609375 2.359375 -0.609375q1.609375 0 2.625 0.8125q1.015625 0.8125 1.3125 2.3125l-1.625 0.25q-0.234375 -1.0 -0.828125 -1.5q-0.59375 -0.5 -1.421875 -0.5q-1.265625 0 -2.0625 0.90625q-0.78125 0.90625 -0.78125 2.859375q0 1.984375 0.765625 2.890625q0.765625 0.890625 1.984375 0.890625q0.984375 0 1.640625 -0.59375q0.65625 -0.609375 0.84375 -1.859375zm9.328125 0l1.640625 0.21875q-0.265625 1.6875 -1.375 2.65625q-1.109375 0.953125 -2.734375 0.953125q-2.015625 0 -3.25 -1.3125q-1.21875 -1.328125 -1.21875 -3.796875q0 -1.59375 0.515625 -2.78125q0.53125 -1.203125 1.609375 -1.796875q1.09375 -0.609375 2.359375 -0.609375q1.609375 0 2.625 0.8125q1.015625 0.8125 1.3125 2.3125l-1.625 0.25q-0.234375 -1.0 -0.828125 -1.5q-0.59375 -0.5 -1.421875 -0.5q-1.265625 0 -2.0625 0.90625q-0.78125 0.90625 -0.78125 2.859375q0 1.984375 0.765625 2.890625q0.765625 0.890625 1.984375 0.890625q0.984375 0 1.640625 -0.59375q0.65625 -0.609375 0.84375 -1.859375zm9.640625 0.4375l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm8.438232 2.9375l1.65625 -0.265625q0.140625 1.0 0.765625 1.53125q0.640625 0.515625 1.78125 0.515625q1.15625 0 1.703125 -0.46875q0.5625 -0.46875 0.5625 -1.09375q0 -0.5625 -0.484375 -0.890625q-0.34375 -0.21875 -1.703125 -0.5625q-1.84375 -0.46875 -2.5625 -0.796875q-0.703125 -0.34375 -1.078125 -0.9375q-0.359375 -0.609375 -0.359375 -1.328125q0 -0.65625 0.296875 -1.21875q0.3125 -0.5625 0.828125 -0.9375q0.390625 -0.28125 1.0625 -0.484375q0.671875 -0.203125 1.4375 -0.203125q1.171875 0 2.046875 0.34375q0.875 0.328125 1.28125 0.90625q0.421875 0.5625 0.578125 1.515625l-1.625 0.21875q-0.109375 -0.75 -0.65625 -1.171875q-0.53125 -0.4375 -1.5 -0.4375q-1.15625 0 -1.640625 0.390625q-0.484375 0.375 -0.484375 0.875q0 0.328125 0.203125 0.59375q0.203125 0.265625 0.640625 0.4375q0.25 0.09375 1.46875 0.4375q1.765625 0.46875 2.46875 0.765625q0.703125 0.296875 1.09375 0.875q0.40625 0.578125 0.40625 1.4375q0 0.828125 -0.484375 1.578125q-0.484375 0.734375 -1.40625 1.140625q-0.921875 0.390625 -2.078125 0.390625q-1.921875 0 -2.9375 -0.796875q-1.0 -0.796875 -1.28125 -2.359375zm9.328125 0l1.65625 -0.265625q0.140625 1.0 0.765625 1.53125q0.640625 0.515625 1.78125 0.515625q1.15625 0 1.703125 -0.46875q0.5625 -0.46875 0.5625 -1.09375q0 -0.5625 -0.484375 -0.890625q-0.34375 -0.21875 -1.703125 -0.5625q-1.84375 -0.46875 -2.5625 -0.796875q-0.703125 -0.34375 -1.078125 -0.9375q-0.359375 -0.609375 -0.359375 -1.328125q0 -0.65625 0.296875 -1.21875q0.3125 -0.5625 0.828125 -0.9375q0.390625 -0.28125 1.0625 -0.484375q0.671875 -0.203125 1.4375 -0.203125q1.171875 0 2.046875 0.34375q0.875 0.328125 1.28125 0.90625q0.421875 0.5625 0.578125 1.515625l-1.625 0.21875q-0.109375 -0.75 -0.65625 -1.171875q-0.53125 -0.4375 -1.5 -0.4375q-1.15625 0 -1.640625 0.390625q-0.484375 0.375 -0.484375 0.875q0 0.328125 0.203125 0.59375q0.203125 0.265625 0.640625 0.4375q0.25 0.09375 1.46875 0.4375q1.765625 0.46875 2.46875 0.765625q0.703125 0.296875 1.09375 0.875q0.40625 0.578125 0.40625 1.4375q0 0.828125 -0.484375 1.578125q-0.484375 0.734375 -1.40625 1.140625q-0.921875 0.390625 -2.078125 0.390625q-1.921875 0 -2.9375 -0.796875q-1.0 -0.796875 -1.28125 -2.359375zm10.015625 -8.75l0 -1.90625l1.671875 0l0 1.90625l-1.671875 0zm0 11.6875l0 -9.859375l1.671875 0l0 9.859375l-1.671875 0zm5.6760864 0l-1.546875 0l0 -13.59375l1.65625 0l0 4.84375q1.0625 -1.328125 2.703125 -1.328125q0.90625 0 1.71875 0.375q0.8125 0.359375 1.328125 1.03125q0.53125 0.65625 0.828125 1.59375q0.296875 0.9375 0.296875 2.0q0 2.53125 -1.25 3.921875q-1.25 1.375 -3.0 1.375q-1.75 0 -2.734375 -1.453125l0 1.234375zm-0.015625 -5.0q0 1.765625 0.46875 2.5625q0.796875 1.28125 2.140625 1.28125q1.09375 0 1.890625 -0.9375q0.796875 -0.953125 0.796875 -2.84375q0 -1.921875 -0.765625 -2.84375q-0.765625 -0.921875 -1.84375 -0.921875q-1.09375 0 -1.890625 0.953125q-0.796875 0.953125 -0.796875 2.75zm8.813171 5.0l0 -13.59375l1.671875 0l0 13.59375l-1.671875 0zm10.926086 -3.171875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm12.235107 2.53125q0 -0.34375 0 -0.5q0 -0.984375 0.265625 -1.703125q0.21875 -0.546875 0.671875 -1.09375q0.328125 -0.390625 1.1875 -1.15625q0.875 -0.765625 1.125 -1.21875q0.265625 -0.453125 0.265625 -1.0q0 -0.96875 -0.765625 -1.703125q-0.75 -0.734375 -1.859375 -0.734375q-1.0625 0 -1.78125 0.671875q-0.703125 0.65625 -0.9375 2.078125l-1.71875 -0.203125q0.234375 -1.90625 1.375 -2.90625q1.15625 -1.015625 3.03125 -1.015625q2.0 0 3.1875 1.09375q1.1875 1.078125 1.1875 2.609375q0 0.890625 -0.421875 1.640625q-0.40625 0.75 -1.625 1.828125q-0.8125 0.734375 -1.0625 1.078125q-0.25 0.34375 -0.375 0.796875q-0.125 0.4375 -0.140625 1.4375l-1.609375 0zm-0.09375 3.34375l0 -1.90625l1.890625 0l0 1.90625l-1.890625 0z" fill-rule="nonzero"></path><path fill="#d9ead3" d="m848.9265 239.90552l156.34644 0l0 88.59842l-156.34644 0z" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m848.9265 239.90552l156.34644 0l0 88.59842l-156.34644 0z" fill-rule="nonzero"></path><path fill="#000000" d="m865.75464 274.7966l0 -1.609375l5.765625 0l0 5.046875q-1.328125 1.0625 -2.75 1.59375q-1.40625 0.53125 -2.890625 0.53125q-2.0 0 -3.640625 -0.859375q-1.625 -0.859375 -2.46875 -2.484375q-0.828125 -1.625 -0.828125 -3.625q0 -1.984375 0.828125 -3.703125q0.828125 -1.71875 2.390625 -2.546875q1.5625 -0.84375 3.59375 -0.84375q1.46875 0 2.65625 0.484375q1.203125 0.46875 1.875 1.328125q0.671875 0.84375 1.03125 2.21875l-1.625 0.4375q-0.3125 -1.03125 -0.765625 -1.625q-0.453125 -0.59375 -1.296875 -0.953125q-0.84375 -0.359375 -1.875 -0.359375q-1.234375 0 -2.140625 0.375q-0.890625 0.375 -1.453125 1.0q-0.546875 0.609375 -0.84375 1.34375q-0.53125 1.25 -0.53125 2.734375q0 1.8125 0.625 3.046875q0.640625 1.21875 1.828125 1.8125q1.203125 0.59375 2.546875 0.59375q1.171875 0 2.28125 -0.453125q1.109375 -0.453125 1.6875 -0.953125l0 -2.53125l-4.0 0zm14.683289 2.15625l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm12.766357 4.375l0.234375 1.484375q-0.703125 0.140625 -1.265625 0.140625q-0.90625 0 -1.40625 -0.28125q-0.5 -0.296875 -0.703125 -0.75q-0.203125 -0.46875 -0.203125 -1.984375l0 -5.65625l-1.234375 0l0 -1.3125l1.234375 0l0 -2.4375l1.65625 -1.0l0 3.4375l1.6875 0l0 1.3125l-1.6875 0l0 5.75q0 0.71875 0.078125 0.921875q0.09375 0.203125 0.296875 0.328125q0.203125 0.125 0.578125 0.125q0.265625 0 0.734375 -0.078125zm6.694702 1.5l0 -9.859375l1.5 0l0 1.5q0.578125 -1.046875 1.0625 -1.375q0.484375 -0.34375 1.078125 -0.34375q0.84375 0 1.71875 0.546875l-0.578125 1.546875q-0.609375 -0.359375 -1.234375 -0.359375q-0.546875 0 -0.984375 0.328125q-0.421875 0.328125 -0.609375 0.90625q-0.28125 0.890625 -0.28125 1.953125l0 5.15625l-1.671875 0zm12.9783325 -3.171875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm8.438232 2.9375l1.65625 -0.265625q0.140625 1.0 0.765625 1.53125q0.640625 0.515625 1.78125 0.515625q1.15625 0 1.703125 -0.46875q0.5625 -0.46875 0.5625 -1.09375q0 -0.5625 -0.484375 -0.890625q-0.34375 -0.21875 -1.703125 -0.5625q-1.84375 -0.46875 -2.5625 -0.796875q-0.703125 -0.34375 -1.078125 -0.9375q-0.359375 -0.609375 -0.359375 -1.328125q0 -0.65625 0.296875 -1.21875q0.3125 -0.5625 0.828125 -0.9375q0.390625 -0.28125 1.0625 -0.484375q0.671875 -0.203125 1.4375 -0.203125q1.171875 0 2.046875 0.34375q0.875 0.328125 1.28125 0.90625q0.421875 0.5625 0.578125 1.515625l-1.625 0.21875q-0.109375 -0.75 -0.65625 -1.171875q-0.53125 -0.4375 -1.5 -0.4375q-1.15625 0 -1.640625 0.390625q-0.484375 0.375 -0.484375 0.875q0 0.328125 0.203125 0.59375q0.203125 0.265625 0.640625 0.4375q0.25 0.09375 1.46875 0.4375q1.765625 0.46875 2.46875 0.765625q0.703125 0.296875 1.09375 0.875q0.40625 0.578125 0.40625 1.4375q0 0.828125 -0.484375 1.578125q-0.484375 0.734375 -1.40625 1.140625q-0.921875 0.390625 -2.078125 0.390625q-1.921875 0 -2.9375 -0.796875q-1.0 -0.796875 -1.28125 -2.359375zm9.375 -1.984375q0 -2.734375 1.53125 -4.0625q1.265625 -1.09375 3.09375 -1.09375q2.03125 0 3.3125 1.34375q1.296875 1.328125 1.296875 3.671875q0 1.90625 -0.578125 3.0q-0.5625 1.078125 -1.65625 1.6875q-1.078125 0.59375 -2.375 0.59375q-2.0625 0 -3.34375 -1.328125q-1.28125 -1.328125 -1.28125 -3.8125zm1.71875 0q0 1.890625 0.828125 2.828125q0.828125 0.9375 2.078125 0.9375q1.25 0 2.0625 -0.9375q0.828125 -0.953125 0.828125 -2.890625q0 -1.828125 -0.828125 -2.765625q-0.828125 -0.9375 -2.0625 -0.9375q-1.25 0 -2.078125 0.9375q-0.828125 0.9375 -0.828125 2.828125zm15.735046 4.921875l0 -1.453125q-1.140625 1.671875 -3.125 1.671875q-0.859375 0 -1.625 -0.328125q-0.75 -0.34375 -1.125 -0.84375q-0.359375 -0.5 -0.515625 -1.234375q-0.09375 -0.5 -0.09375 -1.5625l0 -6.109375l1.671875 0l0 5.46875q0 1.3125 0.09375 1.765625q0.15625 0.65625 0.671875 1.03125q0.515625 0.375 1.265625 0.375q0.75 0 1.40625 -0.375q0.65625 -0.390625 0.921875 -1.046875q0.28125 -0.671875 0.28125 -1.9375l0 -5.28125l1.671875 0l0 9.859375l-1.5 0zm3.9069824 0l0 -9.859375l1.5 0l0 1.5q0.578125 -1.046875 1.0625 -1.375q0.484375 -0.34375 1.078125 -0.34375q0.84375 0 1.71875 0.546875l-0.578125 1.546875q-0.609375 -0.359375 -1.234375 -0.359375q-0.546875 0 -0.984375 0.328125q-0.421875 0.328125 -0.609375 0.90625q-0.28125 0.890625 -0.28125 1.953125l0 5.15625l-1.671875 0zm12.6658325 -3.609375l1.640625 0.21875q-0.265625 1.6875 -1.375 2.65625q-1.109375 0.953125 -2.734375 0.953125q-2.015625 0 -3.25 -1.3125q-1.21875 -1.328125 -1.21875 -3.796875q0 -1.59375 0.515625 -2.78125q0.53125 -1.203125 1.609375 -1.796875q1.09375 -0.609375 2.359375 -0.609375q1.609375 0 2.625 0.8125q1.015625 0.8125 1.3125 2.3125l-1.625 0.25q-0.234375 -1.0 -0.828125 -1.5q-0.59375 -0.5 -1.421875 -0.5q-1.265625 0 -2.0625 0.90625q-0.78125 0.90625 -0.78125 2.859375q0 1.984375 0.765625 2.890625q0.765625 0.890625 1.984375 0.890625q0.984375 0 1.640625 -0.59375q0.65625 -0.609375 0.84375 -1.859375zm9.640625 0.4375l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875z" fill-rule="nonzero"></path><path fill="#000000" d="m859.58276 302.12473l0 -8.546875l-1.484375 0l0 -1.3125l1.484375 0l0 -1.046875q0 -0.984375 0.171875 -1.46875q0.234375 -0.65625 0.84375 -1.046875q0.609375 -0.40625 1.703125 -0.40625q0.703125 0 1.5625 0.15625l-0.25 1.46875q-0.515625 -0.09375 -0.984375 -0.09375q-0.765625 0 -1.078125 0.328125q-0.3125 0.3125 -0.3125 1.203125l0 0.90625l1.921875 0l0 1.3125l-1.921875 0l0 8.546875l-1.65625 0zm4.7614136 0l0 -9.859375l1.5 0l0 1.5q0.578125 -1.046875 1.0625 -1.375q0.484375 -0.34375 1.078125 -0.34375q0.84375 0 1.71875 0.546875l-0.578125 1.546875q-0.609375 -0.359375 -1.234375 -0.359375q-0.546875 0 -0.984375 0.328125q-0.421875 0.328125 -0.609375 0.90625q-0.28125 0.890625 -0.28125 1.953125l0 5.15625l-1.671875 0zm5.6033325 -4.921875q0 -2.734375 1.53125 -4.0625q1.265625 -1.09375 3.09375 -1.09375q2.03125 0 3.3125 1.34375q1.296875 1.328125 1.296875 3.671875q0 1.90625 -0.578125 3.0q-0.5625 1.078125 -1.65625 1.6875q-1.078125 0.59375 -2.375 0.59375q-2.0625 0 -3.34375 -1.328125q-1.28125 -1.328125 -1.28125 -3.8125zm1.71875 0q0 1.890625 0.828125 2.828125q0.828125 0.9375 2.078125 0.9375q1.25 0 2.0625 -0.9375q0.828125 -0.953125 0.828125 -2.890625q0 -1.828125 -0.828125 -2.765625q-0.828125 -0.9375 -2.0625 -0.9375q-1.25 0 -2.078125 0.9375q-0.828125 0.9375 -0.828125 2.828125zm9.281921 4.921875l0 -9.859375l1.5 0l0 1.390625q0.453125 -0.71875 1.21875 -1.15625q0.78125 -0.453125 1.765625 -0.453125q1.09375 0 1.796875 0.453125q0.703125 0.453125 0.984375 1.28125q1.171875 -1.734375 3.046875 -1.734375q1.46875 0 2.25 0.8125q0.796875 0.8125 0.796875 2.5l0 6.765625l-1.671875 0l0 -6.203125q0 -1.0 -0.15625 -1.4375q-0.15625 -0.453125 -0.59375 -0.71875q-0.421875 -0.265625 -1.0 -0.265625q-1.03125 0 -1.71875 0.6875q-0.6875 0.6875 -0.6875 2.21875l0 5.71875l-1.671875 0l0 -6.40625q0 -1.109375 -0.40625 -1.65625q-0.40625 -0.5625 -1.34375 -0.5625q-0.703125 0 -1.3125 0.375q-0.59375 0.359375 -0.859375 1.078125q-0.265625 0.71875 -0.265625 2.0625l0 5.109375l-1.671875 0zm19.442871 0l5.234375 -13.59375l1.9375 0l5.5625 13.59375l-2.046875 0l-1.59375 -4.125l-5.6875 0l-1.484375 4.125l-1.921875 0zm3.921875 -5.578125l4.609375 0l-1.40625 -3.78125q-0.65625 -1.703125 -0.96875 -2.8125q-0.265625 1.3125 -0.734375 2.59375l-1.5 4.0zm10.0217285 5.578125l0 -13.59375l5.125 0q1.359375 0 2.078125 0.125q1.0 0.171875 1.671875 0.640625q0.671875 0.46875 1.078125 1.3125q0.421875 0.84375 0.421875 1.84375q0 1.734375 -1.109375 2.9375q-1.09375 1.203125 -3.984375 1.203125l-3.484375 0l0 5.53125l-1.796875 0zm1.796875 -7.140625l3.515625 0q1.75 0 2.46875 -0.640625q0.734375 -0.65625 0.734375 -1.828125q0 -0.859375 -0.4375 -1.46875q-0.421875 -0.609375 -1.125 -0.796875q-0.453125 -0.125 -1.671875 -0.125l-3.484375 0l0 4.859375zm10.9435425 7.140625l0 -13.59375l1.8125 0l0 13.59375l-1.8125 0zm9.460388 -4.375l1.6875 -0.140625q0.125 1.015625 0.5625 1.671875q0.4375 0.65625 1.359375 1.0625q0.9375 0.40625 2.09375 0.40625q1.03125 0 1.8125 -0.3125q0.796875 -0.3125 1.1875 -0.84375q0.390625 -0.53125 0.390625 -1.15625q0 -0.640625 -0.375 -1.109375q-0.375 -0.484375 -1.234375 -0.8125q-0.546875 -0.21875 -2.421875 -0.65625q-1.875 -0.453125 -2.625 -0.859375q-0.96875 -0.515625 -1.453125 -1.265625q-0.46875 -0.75 -0.46875 -1.6875q0 -1.03125 0.578125 -1.921875q0.59375 -0.90625 1.703125 -1.359375q1.125 -0.46875 2.5 -0.46875q1.515625 0 2.671875 0.484375q1.15625 0.484375 1.765625 1.4375q0.625 0.9375 0.671875 2.140625l-1.71875 0.125q-0.140625 -1.28125 -0.953125 -1.9375q-0.796875 -0.671875 -2.359375 -0.671875q-1.625 0 -2.375 0.609375q-0.75 0.59375 -0.75 1.4375q0 0.734375 0.53125 1.203125q0.515625 0.46875 2.703125 0.96875q2.203125 0.5 3.015625 0.875q1.1875 0.546875 1.75 1.390625q0.578125 0.828125 0.578125 1.921875q0 1.09375 -0.625 2.0625q-0.625 0.953125 -1.796875 1.484375q-1.15625 0.53125 -2.609375 0.53125q-1.84375 0 -3.09375 -0.53125q-1.25 -0.546875 -1.96875 -1.625q-0.703125 -1.078125 -0.734375 -2.453125zm19.584167 1.203125l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm9.094482 5.875l0 -9.859375l1.5 0l0 1.5q0.578125 -1.046875 1.0625 -1.375q0.484375 -0.34375 1.078125 -0.34375q0.84375 0 1.71875 0.546875l-0.578125 1.546875q-0.609375 -0.359375 -1.234375 -0.359375q-0.546875 0 -0.984375 0.328125q-0.421875 0.328125 -0.609375 0.90625q-0.28125 0.890625 -0.28125 1.953125l0 5.15625l-1.671875 0zm8.9626465 0l-3.75 -9.859375l1.765625 0l2.125 5.90625q0.34375 0.953125 0.625 1.984375q0.21875 -0.78125 0.625 -1.875l2.1875 -6.015625l1.71875 0l-3.734375 9.859375l-1.5625 0zm13.34375 -3.171875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm9.094482 5.875l0 -9.859375l1.5 0l0 1.5q0.578125 -1.046875 1.0625 -1.375q0.484375 -0.34375 1.078125 -0.34375q0.84375 0 1.71875 0.546875l-0.578125 1.546875q-0.609375 -0.359375 -1.234375 -0.359375q-0.546875 0 -0.984375 0.328125q-0.421875 0.328125 -0.609375 0.90625q-0.28125 0.890625 -0.28125 1.953125l0 5.15625l-1.671875 0z" fill-rule="nonzero"></path><path fill="#d9ead3" d="m467.042 484.1076l154.07874 -74.80313l154.07874 74.80313l-154.07874 74.80316z" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m467.042 484.1076l154.07874 -74.80313l154.07874 74.80313l-154.07874 74.80316z" fill-rule="nonzero"></path><path fill="#000000" d="m553.94073 486.65262l1.6875 -0.140625q0.125 1.015625 0.5625 1.671875q0.4375 0.65625 1.359375 1.0625q0.9375 0.40625 2.09375 0.40625q1.03125 0 1.8125 -0.3125q0.796875 -0.3125 1.1875 -0.84375q0.390625 -0.53125 0.390625 -1.15625q0 -0.640625 -0.375 -1.109375q-0.375 -0.484375 -1.234375 -0.8125q-0.546875 -0.21875 -2.421875 -0.65625q-1.875 -0.453125 -2.625 -0.859375q-0.96875 -0.515625 -1.453125 -1.265625q-0.46875 -0.75 -0.46875 -1.6875q0 -1.03125 0.578125 -1.921875q0.59375 -0.90625 1.703125 -1.359375q1.125 -0.46875 2.5 -0.46875q1.515625 0 2.671875 0.484375q1.15625 0.484375 1.765625 1.4375q0.625 0.9375 0.671875 2.140625l-1.71875 0.125q-0.140625 -1.28125 -0.953125 -1.9375q-0.796875 -0.671875 -2.359375 -0.671875q-1.625 0 -2.375 0.609375q-0.75 0.59375 -0.75 1.4375q0 0.734375 0.53125 1.203125q0.515625 0.46875 2.703125 0.96875q2.203125 0.5 3.015625 0.875q1.1875 0.546875 1.75 1.390625q0.578125 0.828125 0.578125 1.921875q0 1.09375 -0.625 2.0625q-0.625 0.953125 -1.796875 1.484375q-1.15625 0.53125 -2.609375 0.53125q-1.84375 0 -3.09375 -0.53125q-1.25 -0.546875 -1.96875 -1.625q-0.703125 -1.078125 -0.734375 -2.453125zm19.584229 1.203125l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm8.438171 2.9375l1.65625 -0.265625q0.140625 1.0 0.765625 1.53125q0.640625 0.515625 1.78125 0.515625q1.15625 0 1.703125 -0.46875q0.5625 -0.46875 0.5625 -1.09375q0 -0.5625 -0.484375 -0.890625q-0.34375 -0.21875 -1.703125 -0.5625q-1.84375 -0.46875 -2.5625 -0.796875q-0.703125 -0.34375 -1.078125 -0.9375q-0.359375 -0.609375 -0.359375 -1.328125q0 -0.65625 0.296875 -1.21875q0.3125 -0.5625 0.828125 -0.9375q0.390625 -0.28125 1.0625 -0.484375q0.671875 -0.203125 1.4375 -0.203125q1.171875 0 2.046875 0.34375q0.875 0.328125 1.28125 0.90625q0.421875 0.5625 0.578125 1.515625l-1.625 0.21875q-0.109375 -0.75 -0.65625 -1.171875q-0.53125 -0.4375 -1.5 -0.4375q-1.15625 0 -1.640625 0.390625q-0.484375 0.375 -0.484375 0.875q0 0.328125 0.203125 0.59375q0.203125 0.265625 0.640625 0.4375q0.25 0.09375 1.46875 0.4375q1.765625 0.46875 2.46875 0.765625q0.703125 0.296875 1.09375 0.875q0.40625 0.578125 0.40625 1.4375q0 0.828125 -0.484375 1.578125q-0.484375 0.734375 -1.40625 1.140625q-0.921875 0.390625 -2.078125 0.390625q-1.921875 0 -2.9375 -0.796875q-1.0 -0.796875 -1.28125 -2.359375zm9.328125 0l1.65625 -0.265625q0.140625 1.0 0.765625 1.53125q0.640625 0.515625 1.78125 0.515625q1.15625 0 1.703125 -0.46875q0.5625 -0.46875 0.5625 -1.09375q0 -0.5625 -0.484375 -0.890625q-0.34375 -0.21875 -1.703125 -0.5625q-1.84375 -0.46875 -2.5625 -0.796875q-0.703125 -0.34375 -1.078125 -0.9375q-0.359375 -0.609375 -0.359375 -1.328125q0 -0.65625 0.296875 -1.21875q0.3125 -0.5625 0.828125 -0.9375q0.390625 -0.28125 1.0625 -0.484375q0.671875 -0.203125 1.4375 -0.203125q1.171875 0 2.046875 0.34375q0.875 0.328125 1.28125 0.90625q0.421875 0.5625 0.578125 1.515625l-1.625 0.21875q-0.109375 -0.75 -0.65625 -1.171875q-0.53125 -0.4375 -1.5 -0.4375q-1.15625 0 -1.640625 0.390625q-0.484375 0.375 -0.484375 0.875q0 0.328125 0.203125 0.59375q0.203125 0.265625 0.640625 0.4375q0.25 0.09375 1.46875 0.4375q1.765625 0.46875 2.46875 0.765625q0.703125 0.296875 1.09375 0.875q0.40625 0.578125 0.40625 1.4375q0 0.828125 -0.484375 1.578125q-0.484375 0.734375 -1.40625 1.140625q-0.921875 0.390625 -2.078125 0.390625q-1.921875 0 -2.9375 -0.796875q-1.0 -0.796875 -1.28125 -2.359375zm10.015625 -8.75l0 -1.90625l1.671875 0l0 1.90625l-1.671875 0zm0 11.6875l0 -9.859375l1.671875 0l0 9.859375l-1.671875 0zm3.5042114 -4.921875q0 -2.734375 1.53125 -4.0625q1.265625 -1.09375 3.09375 -1.09375q2.03125 0 3.3125 1.34375q1.296875 1.328125 1.296875 3.671875q0 1.90625 -0.578125 3.0q-0.5625 1.078125 -1.65625 1.6875q-1.078125 0.59375 -2.375 0.59375q-2.0625 0 -3.34375 -1.328125q-1.28125 -1.328125 -1.28125 -3.8125zm1.71875 0q0 1.890625 0.828125 2.828125q0.828125 0.9375 2.078125 0.9375q1.25 0 2.0625 -0.9375q0.828125 -0.953125 0.828125 -2.890625q0 -1.828125 -0.828125 -2.765625q-0.828125 -0.9375 -2.0625 -0.9375q-1.25 0 -2.078125 0.9375q-0.828125 0.9375 -0.828125 2.828125zm9.281982 4.921875l0 -9.859375l1.5 0l0 1.40625q1.09375 -1.625 3.140625 -1.625q0.890625 0 1.640625 0.328125q0.75 0.3125 1.109375 0.84375q0.375 0.515625 0.53125 1.21875q0.09375 0.46875 0.09375 1.625l0 6.0625l-1.671875 0l0 -6.0q0 -1.015625 -0.203125 -1.515625q-0.1875 -0.515625 -0.6875 -0.8125q-0.5 -0.296875 -1.171875 -0.296875q-1.0625 0 -1.84375 0.671875q-0.765625 0.671875 -0.765625 2.578125l0 5.375l-1.671875 0zm22.309021 -3.171875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm8.000732 5.875l3.59375 -5.125l-3.328125 -4.734375l2.09375 0l1.515625 2.3125q0.421875 0.65625 0.671875 1.109375q0.421875 -0.609375 0.765625 -1.09375l1.65625 -2.328125l1.984375 0l-3.390625 4.640625l3.65625 5.21875l-2.046875 0l-2.03125 -3.0625l-0.53125 -0.828125l-2.59375 3.890625l-2.015625 0zm10.453125 -11.6875l0 -1.90625l1.671875 0l0 1.90625l-1.671875 0zm0 11.6875l0 -9.859375l1.671875 0l0 9.859375l-1.671875 0zm3.4572754 -2.9375l1.65625 -0.265625q0.140625 1.0 0.765625 1.53125q0.640625 0.515625 1.78125 0.515625q1.15625 0 1.703125 -0.46875q0.5625 -0.46875 0.5625 -1.09375q0 -0.5625 -0.484375 -0.890625q-0.34375 -0.21875 -1.703125 -0.5625q-1.84375 -0.46875 -2.5625 -0.796875q-0.703125 -0.34375 -1.078125 -0.9375q-0.359375 -0.609375 -0.359375 -1.328125q0 -0.65625 0.296875 -1.21875q0.3125 -0.5625 0.828125 -0.9375q0.390625 -0.28125 1.0625 -0.484375q0.671875 -0.203125 1.4375 -0.203125q1.171875 0 2.046875 0.34375q0.875 0.328125 1.28125 0.90625q0.421875 0.5625 0.578125 1.515625l-1.625 0.21875q-0.109375 -0.75 -0.65625 -1.171875q-0.53125 -0.4375 -1.5 -0.4375q-1.15625 0 -1.640625 0.390625q-0.484375 0.375 -0.484375 0.875q0 0.328125 0.203125 0.59375q0.203125 0.265625 0.640625 0.4375q0.25 0.09375 1.46875 0.4375q1.765625 0.46875 2.46875 0.765625q0.703125 0.296875 1.09375 0.875q0.40625 0.578125 0.40625 1.4375q0 0.828125 -0.484375 1.578125q-0.484375 0.734375 -1.40625 1.140625q-0.921875 0.390625 -2.078125 0.390625q-1.921875 0 -2.9375 -0.796875q-1.0 -0.796875 -1.28125 -2.359375zm13.65625 1.4375l0.234375 1.484375q-0.703125 0.140625 -1.265625 0.140625q-0.90625 0 -1.40625 -0.28125q-0.5 -0.296875 -0.703125 -0.75q-0.203125 -0.46875 -0.203125 -1.984375l0 -5.65625l-1.234375 0l0 -1.3125l1.234375 0l0 -2.4375l1.65625 -1.0l0 3.4375l1.6875 0l0 1.3125l-1.6875 0l0 5.75q0 0.71875 0.078125 0.921875q0.09375 0.203125 0.296875 0.328125q0.203125 0.125 0.578125 0.125q0.265625 0 0.734375 -0.078125zm0.8552246 -1.4375l1.65625 -0.265625q0.140625 1.0 0.765625 1.53125q0.640625 0.515625 1.78125 0.515625q1.15625 0 1.703125 -0.46875q0.5625 -0.46875 0.5625 -1.09375q0 -0.5625 -0.484375 -0.890625q-0.34375 -0.21875 -1.703125 -0.5625q-1.84375 -0.46875 -2.5625 -0.796875q-0.703125 -0.34375 -1.078125 -0.9375q-0.359375 -0.609375 -0.359375 -1.328125q0 -0.65625 0.296875 -1.21875q0.3125 -0.5625 0.828125 -0.9375q0.390625 -0.28125 1.0625 -0.484375q0.671875 -0.203125 1.4375 -0.203125q1.171875 0 2.046875 0.34375q0.875 0.328125 1.28125 0.90625q0.421875 0.5625 0.578125 1.515625l-1.625 0.21875q-0.109375 -0.75 -0.65625 -1.171875q-0.53125 -0.4375 -1.5 -0.4375q-1.15625 0 -1.640625 0.390625q-0.484375 0.375 -0.484375 0.875q0 0.328125 0.203125 0.59375q0.203125 0.265625 0.640625 0.4375q0.25 0.09375 1.46875 0.4375q1.765625 0.46875 2.46875 0.765625q0.703125 0.296875 1.09375 0.875q0.40625 0.578125 0.40625 1.4375q0 0.828125 -0.484375 1.578125q-0.484375 0.734375 -1.40625 1.140625q-0.921875 0.390625 -2.078125 0.390625q-1.921875 0 -2.9375 -0.796875q-1.0 -0.796875 -1.28125 -2.359375zm13.125 -0.40625q0 -0.34375 0 -0.5q0 -0.984375 0.265625 -1.703125q0.21875 -0.546875 0.671875 -1.09375q0.328125 -0.390625 1.1875 -1.15625q0.875 -0.765625 1.125 -1.21875q0.265625 -0.453125 0.265625 -1.0q0 -0.96875 -0.765625 -1.703125q-0.75 -0.734375 -1.859375 -0.734375q-1.0625 0 -1.78125 0.671875q-0.703125 0.65625 -0.9375 2.078125l-1.71875 -0.203125q0.234375 -1.90625 1.375 -2.90625q1.15625 -1.015625 3.03125 -1.015625q2.0 0 3.1875 1.09375q1.1875 1.078125 1.1875 2.609375q0 0.890625 -0.421875 1.640625q-0.40625 0.75 -1.625 1.828125q-0.8125 0.734375 -1.0625 1.078125q-0.25 0.34375 -0.375 0.796875q-0.125 0.4375 -0.140625 1.4375l-1.609375 0zm-0.09375 3.34375l0 -1.90625l1.890625 0l0 1.90625l-1.890625 0z" fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0" d="m618.7218 154.43832l1.1968384 48.0" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m618.7218 154.43832l1.0472412 42.00186" fill-rule="evenodd"></path><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m618.11786 196.48135l1.7643433 4.495514l1.5380859 -4.5778503z" fill-rule="evenodd"></path><path fill="#000000" fill-opacity="0.0" d="m896.65094 455.34122l2.3936768 43.653534" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m896.65094 455.34122l2.0651855 37.662506" fill-rule="evenodd"></path><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m897.06683 493.09418l1.8977661 4.440857l1.4007568 -4.621704z" fill-rule="evenodd"></path><path fill="#000000" fill-opacity="0.0" d="m772.80054 281.8714l76.12598 1.669281" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m772.80054 281.8714l70.12744 1.5377502" fill-rule="evenodd"></path><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m842.8917 285.0605l4.573242 -1.5518494l-4.5007935 -1.750824z" fill-rule="evenodd"></path><path fill="#000000" fill-opacity="0.0" d="m620.52234 360.3176l1.1968384 48.0" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m620.52234 360.3176l1.0472412 42.00183" fill-rule="evenodd"></path><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m619.9184 402.36063l1.7643433 4.495514l1.5380859 -4.5778503z" fill-rule="evenodd"></path><path fill="#000000" fill-opacity="0.0" d="m585.021 367.1076l58.80316 0l0 34.4252l-58.80316 0z" fill-rule="nonzero"></path><path fill="#000000" d="m595.4741 394.02762l0 -13.59375l1.84375 0l7.140625 10.671875l0 -10.671875l1.71875 0l0 13.59375l-1.84375 0l-7.140625 -10.6875l0 10.6875l-1.71875 0zm12.644836 -4.921875q0 -2.734375 1.53125 -4.0625q1.265625 -1.09375 3.09375 -1.09375q2.03125 0 3.3125 1.34375q1.296875 1.328125 1.296875 3.671875q0 1.90625 -0.578125 3.0q-0.5625 1.078125 -1.65625 1.6875q-1.078125 0.59375 -2.375 0.59375q-2.0625 0 -3.34375 -1.328125q-1.28125 -1.328125 -1.28125 -3.8125zm1.71875 0q0 1.890625 0.828125 2.828125q0.828125 0.9375 2.078125 0.9375q1.25 0 2.0625 -0.9375q0.828125 -0.953125 0.828125 -2.890625q0 -1.828125 -0.828125 -2.765625q-0.828125 -0.9375 -2.0625 -0.9375q-1.25 0 -2.078125 0.9375q-0.828125 0.9375 -0.828125 2.828125z" fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0" d="m788.84515 248.8924l58.80316 0l0 34.4252l-58.80316 0z" fill-rule="nonzero"></path><path fill="#000000" d="m803.142 275.81238l0 -5.765625l-5.234375 -7.828125l2.1875 0l2.671875 4.09375q0.75 1.15625 1.390625 2.296875q0.609375 -1.0625 1.484375 -2.40625l2.625 -3.984375l2.109375 0l-5.4375 7.828125l0 5.765625l-1.796875 0zm15.1466675 -3.171875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm8.438232 2.9375l1.65625 -0.265625q0.140625 1.0 0.765625 1.53125q0.640625 0.515625 1.78125 0.515625q1.15625 0 1.703125 -0.46875q0.5625 -0.46875 0.5625 -1.09375q0 -0.5625 -0.484375 -0.890625q-0.34375 -0.21875 -1.703125 -0.5625q-1.84375 -0.46875 -2.5625 -0.796875q-0.703125 -0.34375 -1.078125 -0.9375q-0.359375 -0.609375 -0.359375 -1.328125q0 -0.65625 0.296875 -1.21875q0.3125 -0.5625 0.828125 -0.9375q0.390625 -0.28125 1.0625 -0.484375q0.671875 -0.203125 1.4375 -0.203125q1.171875 0 2.046875 0.34375q0.875 0.328125 1.28125 0.90625q0.421875 0.5625 0.578125 1.515625l-1.625 0.21875q-0.109375 -0.75 -0.65625 -1.171875q-0.53125 -0.4375 -1.5 -0.4375q-1.15625 0 -1.640625 0.390625q-0.484375 0.375 -0.484375 0.875q0 0.328125 0.203125 0.59375q0.203125 0.265625 0.640625 0.4375q0.25 0.09375 1.46875 0.4375q1.765625 0.46875 2.46875 0.765625q0.703125 0.296875 1.09375 0.875q0.40625 0.578125 0.40625 1.4375q0 0.828125 -0.484375 1.578125q-0.484375 0.734375 -1.40625 1.140625q-0.921875 0.390625 -2.078125 0.390625q-1.921875 0 -2.9375 -0.796875q-1.0 -0.796875 -1.28125 -2.359375z" fill-rule="nonzero"></path><path fill="#d9ead3" d="m845.084 442.14172l156.34644 0l0 88.59845l-156.34644 0z" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m845.084 442.14172l156.34644 0l0 88.59845l-156.34644 0z" fill-rule="nonzero"></path><path fill="#000000" d="m861.9121 477.0328l0 -1.609375l5.765625 0l0 5.046875q-1.328125 1.0625 -2.75 1.59375q-1.40625 0.53125 -2.890625 0.53125q-2.0 0 -3.640625 -0.859375q-1.625 -0.859375 -2.46875 -2.484375q-0.828125 -1.625 -0.828125 -3.625q0 -1.984375 0.828125 -3.703125q0.828125 -1.71875 2.390625 -2.546875q1.5625 -0.84375 3.59375 -0.84375q1.46875 0 2.65625 0.484375q1.203125 0.46875 1.875 1.328125q0.671875 0.84375 1.03125 2.21875l-1.625 0.4375q-0.3125 -1.03125 -0.765625 -1.625q-0.453125 -0.59375 -1.296875 -0.953125q-0.84375 -0.359375 -1.875 -0.359375q-1.234375 0 -2.140625 0.375q-0.890625 0.375 -1.453125 1.0q-0.546875 0.609375 -0.84375 1.34375q-0.53125 1.25 -0.53125 2.734375q0 1.8125 0.625 3.046875q0.640625 1.21875 1.828125 1.8125q1.203125 0.59375 2.546875 0.59375q1.171875 0 2.28125 -0.453125q1.109375 -0.453125 1.6875 -0.953125l0 -2.53125l-4.0 0zm14.683289 2.15625l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm12.766357 4.375l0.234375 1.484375q-0.703125 0.140625 -1.265625 0.140625q-0.90625 0 -1.40625 -0.28125q-0.5 -0.296875 -0.703125 -0.75q-0.203125 -0.46875 -0.203125 -1.984375l0 -5.65625l-1.234375 0l0 -1.3125l1.234375 0l0 -2.4375l1.65625 -1.0l0 3.4375l1.6875 0l0 1.3125l-1.6875 0l0 5.75q0 0.71875 0.078125 0.921875q0.09375 0.203125 0.296875 0.328125q0.203125 0.125 0.578125 0.125q0.265625 0 0.734375 -0.078125zm6.694763 1.5l0 -9.859375l1.5 0l0 1.5q0.578125 -1.046875 1.0625 -1.375q0.484375 -0.34375 1.078125 -0.34375q0.84375 0 1.71875 0.546875l-0.578125 1.546875q-0.609375 -0.359375 -1.234375 -0.359375q-0.546875 0 -0.984375 0.328125q-0.421875 0.328125 -0.609375 0.90625q-0.28125 0.890625 -0.28125 1.953125l0 5.15625l-1.671875 0zm12.9782715 -3.171875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm8.438232 2.9375l1.65625 -0.265625q0.140625 1.0 0.765625 1.53125q0.640625 0.515625 1.78125 0.515625q1.15625 0 1.703125 -0.46875q0.5625 -0.46875 0.5625 -1.09375q0 -0.5625 -0.484375 -0.890625q-0.34375 -0.21875 -1.703125 -0.5625q-1.84375 -0.46875 -2.5625 -0.796875q-0.703125 -0.34375 -1.078125 -0.9375q-0.359375 -0.609375 -0.359375 -1.328125q0 -0.65625 0.296875 -1.21875q0.3125 -0.5625 0.828125 -0.9375q0.390625 -0.28125 1.0625 -0.484375q0.671875 -0.203125 1.4375 -0.203125q1.171875 0 2.046875 0.34375q0.875 0.328125 1.28125 0.90625q0.421875 0.5625 0.578125 1.515625l-1.625 0.21875q-0.109375 -0.75 -0.65625 -1.171875q-0.53125 -0.4375 -1.5 -0.4375q-1.15625 0 -1.640625 0.390625q-0.484375 0.375 -0.484375 0.875q0 0.328125 0.203125 0.59375q0.203125 0.265625 0.640625 0.4375q0.25 0.09375 1.46875 0.4375q1.765625 0.46875 2.46875 0.765625q0.703125 0.296875 1.09375 0.875q0.40625 0.578125 0.40625 1.4375q0 0.828125 -0.484375 1.578125q-0.484375 0.734375 -1.40625 1.140625q-0.921875 0.390625 -2.078125 0.390625q-1.921875 0 -2.9375 -0.796875q-1.0 -0.796875 -1.28125 -2.359375zm9.375 -1.984375q0 -2.734375 1.53125 -4.0625q1.265625 -1.09375 3.09375 -1.09375q2.03125 0 3.3125 1.34375q1.296875 1.328125 1.296875 3.671875q0 1.90625 -0.578125 3.0q-0.5625 1.078125 -1.65625 1.6875q-1.078125 0.59375 -2.375 0.59375q-2.0625 0 -3.34375 -1.328125q-1.28125 -1.328125 -1.28125 -3.8125zm1.71875 0q0 1.890625 0.828125 2.828125q0.828125 0.9375 2.078125 0.9375q1.25 0 2.0625 -0.9375q0.828125 -0.953125 0.828125 -2.890625q0 -1.828125 -0.828125 -2.765625q-0.828125 -0.9375 -2.0625 -0.9375q-1.25 0 -2.078125 0.9375q-0.828125 0.9375 -0.828125 2.828125zm15.735107 4.921875l0 -1.453125q-1.140625 1.671875 -3.125 1.671875q-0.859375 0 -1.625 -0.328125q-0.75 -0.34375 -1.125 -0.84375q-0.359375 -0.5 -0.515625 -1.234375q-0.09375 -0.5 -0.09375 -1.5625l0 -6.109375l1.671875 0l0 5.46875q0 1.3125 0.09375 1.765625q0.15625 0.65625 0.671875 1.03125q0.515625 0.375 1.265625 0.375q0.75 0 1.40625 -0.375q0.65625 -0.390625 0.921875 -1.046875q0.28125 -0.671875 0.28125 -1.9375l0 -5.28125l1.671875 0l0 9.859375l-1.5 0zm3.9069214 0l0 -9.859375l1.5 0l0 1.5q0.578125 -1.046875 1.0625 -1.375q0.484375 -0.34375 1.078125 -0.34375q0.84375 0 1.71875 0.546875l-0.578125 1.546875q-0.609375 -0.359375 -1.234375 -0.359375q-0.546875 0 -0.984375 0.328125q-0.421875 0.328125 -0.609375 0.90625q-0.28125 0.890625 -0.28125 1.953125l0 5.15625l-1.671875 0zm12.6658325 -3.609375l1.640625 0.21875q-0.265625 1.6875 -1.375 2.65625q-1.109375 0.953125 -2.734375 0.953125q-2.015625 0 -3.25 -1.3125q-1.21875 -1.328125 -1.21875 -3.796875q0 -1.59375 0.515625 -2.78125q0.53125 -1.203125 1.609375 -1.796875q1.09375 -0.609375 2.359375 -0.609375q1.609375 0 2.625 0.8125q1.015625 0.8125 1.3125 2.3125l-1.625 0.25q-0.234375 -1.0 -0.828125 -1.5q-0.59375 -0.5 -1.421875 -0.5q-1.265625 0 -2.0625 0.90625q-0.78125 0.90625 -0.78125 2.859375q0 1.984375 0.765625 2.890625q0.765625 0.890625 1.984375 0.890625q0.984375 0 1.640625 -0.59375q0.65625 -0.609375 0.84375 -1.859375zm9.640625 0.4375l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875z" fill-rule="nonzero"></path><path fill="#000000" d="m855.74023 504.36093l0 -8.546875l-1.484375 0l0 -1.3125l1.484375 0l0 -1.046875q0 -0.984375 0.171875 -1.46875q0.234375 -0.65625 0.84375 -1.046875q0.609375 -0.40625 1.703125 -0.40625q0.703125 0 1.5625 0.15625l-0.25 1.46875q-0.515625 -0.09375 -0.984375 -0.09375q-0.765625 0 -1.078125 0.328125q-0.3125 0.3125 -0.3125 1.203125l0 0.90625l1.921875 0l0 1.3125l-1.921875 0l0 8.546875l-1.65625 0zm4.7614136 0l0 -9.859375l1.5 0l0 1.5q0.578125 -1.046875 1.0625 -1.375q0.484375 -0.34375 1.078125 -0.34375q0.84375 0 1.71875 0.546875l-0.578125 1.546875q-0.609375 -0.359375 -1.234375 -0.359375q-0.546875 0 -0.984375 0.328125q-0.421875 0.328125 -0.609375 0.90625q-0.28125 0.890625 -0.28125 1.953125l0 5.15625l-1.671875 0zm5.6033325 -4.921875q0 -2.734375 1.53125 -4.0625q1.265625 -1.09375 3.09375 -1.09375q2.03125 0 3.3125 1.34375q1.296875 1.328125 1.296875 3.671875q0 1.90625 -0.578125 3.0q-0.5625 1.078125 -1.65625 1.6875q-1.078125 0.59375 -2.375 0.59375q-2.0625 0 -3.34375 -1.328125q-1.28125 -1.328125 -1.28125 -3.8125zm1.71875 0q0 1.890625 0.828125 2.828125q0.828125 0.9375 2.078125 0.9375q1.25 0 2.0625 -0.9375q0.828125 -0.953125 0.828125 -2.890625q0 -1.828125 -0.828125 -2.765625q-0.828125 -0.9375 -2.0625 -0.9375q-1.25 0 -2.078125 0.9375q-0.828125 0.9375 -0.828125 2.828125zm9.281982 4.921875l0 -9.859375l1.5 0l0 1.390625q0.453125 -0.71875 1.21875 -1.15625q0.78125 -0.453125 1.765625 -0.453125q1.09375 0 1.796875 0.453125q0.703125 0.453125 0.984375 1.28125q1.171875 -1.734375 3.046875 -1.734375q1.46875 0 2.25 0.8125q0.796875 0.8125 0.796875 2.5l0 6.765625l-1.671875 0l0 -6.203125q0 -1.0 -0.15625 -1.4375q-0.15625 -0.453125 -0.59375 -0.71875q-0.421875 -0.265625 -1.0 -0.265625q-1.03125 0 -1.71875 0.6875q-0.6875 0.6875 -0.6875 2.21875l0 5.71875l-1.671875 0l0 -6.40625q0 -1.109375 -0.40625 -1.65625q-0.40625 -0.5625 -1.34375 -0.5625q-0.703125 0 -1.3125 0.375q-0.59375 0.359375 -0.859375 1.078125q-0.265625 0.71875 -0.265625 2.0625l0 5.109375l-1.671875 0zm19.44281 0l5.234375 -13.59375l1.9375 0l5.5625 13.59375l-2.046875 0l-1.59375 -4.125l-5.6875 0l-1.484375 4.125l-1.921875 0zm3.921875 -5.578125l4.609375 0l-1.40625 -3.78125q-0.65625 -1.703125 -0.96875 -2.8125q-0.265625 1.3125 -0.734375 2.59375l-1.5 4.0zm10.0217285 5.578125l0 -13.59375l5.125 0q1.359375 0 2.078125 0.125q1.0 0.171875 1.671875 0.640625q0.671875 0.46875 1.078125 1.3125q0.421875 0.84375 0.421875 1.84375q0 1.734375 -1.109375 2.9375q-1.09375 1.203125 -3.984375 1.203125l-3.484375 0l0 5.53125l-1.796875 0zm1.796875 -7.140625l3.515625 0q1.75 0 2.46875 -0.640625q0.734375 -0.65625 0.734375 -1.828125q0 -0.859375 -0.4375 -1.46875q-0.421875 -0.609375 -1.125 -0.796875q-0.453125 -0.125 -1.671875 -0.125l-3.484375 0l0 4.859375zm10.9435425 7.140625l0 -13.59375l1.8125 0l0 13.59375l-1.8125 0zm9.460388 -4.375l1.6875 -0.140625q0.125 1.015625 0.5625 1.671875q0.4375 0.65625 1.359375 1.0625q0.9375 0.40625 2.09375 0.40625q1.03125 0 1.8125 -0.3125q0.796875 -0.3125 1.1875 -0.84375q0.390625 -0.53125 0.390625 -1.15625q0 -0.640625 -0.375 -1.109375q-0.375 -0.484375 -1.234375 -0.8125q-0.546875 -0.21875 -2.421875 -0.65625q-1.875 -0.453125 -2.625 -0.859375q-0.96875 -0.515625 -1.453125 -1.265625q-0.46875 -0.75 -0.46875 -1.6875q0 -1.03125 0.578125 -1.921875q0.59375 -0.90625 1.703125 -1.359375q1.125 -0.46875 2.5 -0.46875q1.515625 0 2.671875 0.484375q1.15625 0.484375 1.765625 1.4375q0.625 0.9375 0.671875 2.140625l-1.71875 0.125q-0.140625 -1.28125 -0.953125 -1.9375q-0.796875 -0.671875 -2.359375 -0.671875q-1.625 0 -2.375 0.609375q-0.75 0.59375 -0.75 1.4375q0 0.734375 0.53125 1.203125q0.515625 0.46875 2.703125 0.96875q2.203125 0.5 3.015625 0.875q1.1875 0.546875 1.75 1.390625q0.578125 0.828125 0.578125 1.921875q0 1.09375 -0.625 2.0625q-0.625 0.953125 -1.796875 1.484375q-1.15625 0.53125 -2.609375 0.53125q-1.84375 0 -3.09375 -0.53125q-1.25 -0.546875 -1.96875 -1.625q-0.703125 -1.078125 -0.734375 -2.453125zm19.584167 1.203125l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm9.094482 5.875l0 -9.859375l1.5 0l0 1.5q0.578125 -1.046875 1.0625 -1.375q0.484375 -0.34375 1.078125 -0.34375q0.84375 0 1.71875 0.546875l-0.578125 1.546875q-0.609375 -0.359375 -1.234375 -0.359375q-0.546875 0 -0.984375 0.328125q-0.421875 0.328125 -0.609375 0.90625q-0.28125 0.890625 -0.28125 1.953125l0 5.15625l-1.671875 0zm8.9627075 0l-3.75 -9.859375l1.765625 0l2.125 5.90625q0.34375 0.953125 0.625 1.984375q0.21875 -0.78125 0.625 -1.875l2.1875 -6.015625l1.71875 0l-3.734375 9.859375l-1.5625 0zm13.34375 -3.171875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm9.094421 5.875l0 -9.859375l1.5 0l0 1.5q0.578125 -1.046875 1.0625 -1.375q0.484375 -0.34375 1.078125 -0.34375q0.84375 0 1.71875 0.546875l-0.578125 1.546875q-0.609375 -0.359375 -1.234375 -0.359375q-0.546875 0 -0.984375 0.328125q-0.421875 0.328125 -0.609375 0.90625q-0.28125 0.890625 -0.28125 1.953125l0 5.15625l-1.671875 0z" fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0" d="m768.958 484.1076l76.12598 1.6693115" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m768.958 484.1076l70.12744 1.5377808" fill-rule="evenodd"></path><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m839.0492 487.2967l4.573242 -1.5518494l-4.5007935 -1.750824z" fill-rule="evenodd"></path><path fill="#000000" fill-opacity="0.0" d="m785.0026 451.1286l58.80316 0l0 34.4252l-58.80316 0z" fill-rule="nonzero"></path><path fill="#000000" d="m799.2995 478.0486l0 -5.765625l-5.234375 -7.828125l2.1875 0l2.671875 4.09375q0.75 1.15625 1.390625 2.296875q0.609375 -1.0625 1.484375 -2.40625l2.625 -3.984375l2.109375 0l-5.4375 7.828125l0 5.765625l-1.796875 0zm15.1467285 -3.171875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm8.438171 2.9375l1.65625 -0.265625q0.140625 1.0 0.765625 1.53125q0.640625 0.515625 1.78125 0.515625q1.15625 0 1.703125 -0.46875q0.5625 -0.46875 0.5625 -1.09375q0 -0.5625 -0.484375 -0.890625q-0.34375 -0.21875 -1.703125 -0.5625q-1.84375 -0.46875 -2.5625 -0.796875q-0.703125 -0.34375 -1.078125 -0.9375q-0.359375 -0.609375 -0.359375 -1.328125q0 -0.65625 0.296875 -1.21875q0.3125 -0.5625 0.828125 -0.9375q0.390625 -0.28125 1.0625 -0.484375q0.671875 -0.203125 1.4375 -0.203125q1.171875 0 2.046875 0.34375q0.875 0.328125 1.28125 0.90625q0.421875 0.5625 0.578125 1.515625l-1.625 0.21875q-0.109375 -0.75 -0.65625 -1.171875q-0.53125 -0.4375 -1.5 -0.4375q-1.15625 0 -1.640625 0.390625q-0.484375 0.375 -0.484375 0.875q0 0.328125 0.203125 0.59375q0.203125 0.265625 0.640625 0.4375q0.25 0.09375 1.46875 0.4375q1.765625 0.46875 2.46875 0.765625q0.703125 0.296875 1.09375 0.875q0.40625 0.578125 0.40625 1.4375q0 0.828125 -0.484375 1.578125q-0.484375 0.734375 -1.40625 1.140625q-0.921875 0.390625 -2.078125 0.390625q-1.921875 0 -2.9375 -0.796875q-1.0 -0.796875 -1.28125 -2.359375z" fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0" d="m1093.5826 486.44095l3.4645996 -377.88977" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m1093.5826 486.44095l3.4645996 -377.88977" fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0" d="m1005.27295 284.2047l89.60632 1.6378174" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m1005.27295 284.2047l83.6073 1.5281677" fill-rule="evenodd"></path><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m1088.8501 287.38434l4.567505 -1.5685425l-4.507202 -1.734375z" fill-rule="evenodd"></path><path fill="#000000" fill-opacity="0.0" d="m1099.9213 111.42519l-391.55908 -2.8661423" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m1099.9213 111.42519l-385.5592 -2.8222198" fill-rule="evenodd"></path><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m714.37415 106.95129l-4.550049 1.6184692l4.525879 1.684906z" fill-rule="evenodd"></path><path fill="#000000" fill-opacity="0.0" d="m1001.4304 485.62204l89.60632 1.6378174" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m1001.4304 485.62204l83.6073 1.5281372" fill-rule="evenodd"></path><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m1085.0076 488.80167l4.567505 -1.5685425l-4.50708 -1.734375z" fill-rule="evenodd"></path><path fill="#000000" fill-opacity="0.0" d="m621.1207 558.91077l0.12597656 76.81891" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m621.1207 558.91077l0.1161499 70.81891" fill-rule="evenodd"></path><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m619.58514 629.73236l1.6591797 4.5354004l1.6442871 -4.5408325z" fill-rule="evenodd"></path><path fill="#000000" fill-opacity="0.0" d="m579.0289 573.6352l47.338562 0l0 34.42517l-47.338562 0z" fill-rule="nonzero"></path><path fill="#000000" d="m589.482 600.5552l0 -13.59375l1.84375 0l7.140625 10.671875l0 -10.671875l1.71875 0l0 13.59375l-1.84375 0l-7.140625 -10.6875l0 10.6875l-1.71875 0zm12.644836 -4.921875q0 -2.734375 1.53125 -4.0625q1.265625 -1.09375 3.09375 -1.09375q2.03125 0 3.3125 1.34375q1.296875 1.328125 1.296875 3.671875q0 1.90625 -0.578125 3.0q-0.5625 1.078125 -1.65625 1.6875q-1.078125 0.59375 -2.375 0.59375q-2.0625 0 -3.34375 -1.328125q-1.28125 -1.328125 -1.28125 -3.8125zm1.71875 0q0 1.890625 0.828125 2.828125q0.828125 0.9375 2.078125 0.9375q1.25 0 2.0625 -0.9375q0.828125 -0.953125 0.828125 -2.890625q0 -1.828125 -0.828125 -2.765625q-0.828125 -0.9375 -2.0625 -0.9375q-1.25 0 -2.078125 0.9375q-0.828125 0.9375 -0.828125 2.828125z" fill-rule="nonzero"></path><path fill="#ead1dc" d="m545.084 634.39105l156.34644 0l0 70.26776l-156.34644 0z" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m545.084 634.39105l156.34644 0l0 70.26776l-156.34644 0z" fill-rule="nonzero"></path><path fill="#000000" d="m557.92773 654.44495l-3.609375 -13.59375l1.84375 0l2.0625 8.90625q0.34375 1.40625 0.578125 2.78125q0.515625 -2.171875 0.609375 -2.515625l2.59375 -9.171875l2.171875 0l1.953125 6.875q0.734375 2.5625 1.046875 4.8125q0.265625 -1.28125 0.6875 -2.953125l2.125 -8.734375l1.8125 0l-3.734375 13.59375l-1.734375 0l-2.859375 -10.359375q-0.359375 -1.296875 -0.421875 -1.59375q-0.21875 0.9375 -0.40625 1.59375l-2.890625 10.359375l-1.828125 0zm21.764893 -3.171875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm9.078857 5.875l0 -13.59375l1.671875 0l0 13.59375l-1.671875 0zm10.613586 -3.609375l1.640625 0.21875q-0.265625 1.6875 -1.375 2.65625q-1.109375 0.953125 -2.734375 0.953125q-2.015625 0 -3.25 -1.3125q-1.21875 -1.328125 -1.21875 -3.796875q0 -1.59375 0.515625 -2.78125q0.53125 -1.203125 1.609375 -1.796875q1.09375 -0.609375 2.359375 -0.609375q1.609375 0 2.625 0.8125q1.015625 0.8125 1.3125 2.3125l-1.625 0.25q-0.234375 -1.0 -0.828125 -1.5q-0.59375 -0.5 -1.421875 -0.5q-1.265625 0 -2.0625 0.90625q-0.78125 0.90625 -0.78125 2.859375q0 1.984375 0.765625 2.890625q0.765625 0.890625 1.984375 0.890625q0.984375 0 1.640625 -0.59375q0.65625 -0.609375 0.84375 -1.859375zm2.265625 -1.3125q0 -2.734375 1.53125 -4.0625q1.265625 -1.09375 3.09375 -1.09375q2.03125 0 3.3125 1.34375q1.296875 1.328125 1.296875 3.671875q0 1.90625 -0.578125 3.0q-0.5625 1.078125 -1.65625 1.6875q-1.078125 0.59375 -2.375 0.59375q-2.0625 0 -3.34375 -1.328125q-1.28125 -1.328125 -1.28125 -3.8125zm1.71875 0q0 1.890625 0.828125 2.828125q0.828125 0.9375 2.078125 0.9375q1.25 0 2.0625 -0.9375q0.828125 -0.953125 0.828125 -2.890625q0 -1.828125 -0.828125 -2.765625q-0.828125 -0.9375 -2.0625 -0.9375q-1.25 0 -2.078125 0.9375q-0.828125 0.9375 -0.828125 2.828125zm9.281982 4.921875l0 -9.859375l1.5 0l0 1.390625q0.453125 -0.71875 1.21875 -1.15625q0.78125 -0.453125 1.765625 -0.453125q1.09375 0 1.796875 0.453125q0.703125 0.453125 0.984375 1.28125q1.171875 -1.734375 3.046875 -1.734375q1.46875 0 2.25 0.8125q0.796875 0.8125 0.796875 2.5l0 6.765625l-1.671875 0l0 -6.203125q0 -1.0 -0.15625 -1.4375q-0.15625 -0.453125 -0.59375 -0.71875q-0.421875 -0.265625 -1.0 -0.265625q-1.03125 0 -1.71875 0.6875q-0.6875 0.6875 -0.6875 2.21875l0 5.71875l-1.671875 0l0 -6.40625q0 -1.109375 -0.40625 -1.65625q-0.40625 -0.5625 -1.34375 -0.5625q-0.703125 0 -1.3125 0.375q-0.59375 0.359375 -0.859375 1.078125q-0.265625 0.71875 -0.265625 2.0625l0 5.109375l-1.671875 0zm22.290771 -3.171875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm14.293396 9.65625l0 -13.640625l1.53125 0l0 1.28125q0.53125 -0.75 1.203125 -1.125q0.6875 -0.375 1.640625 -0.375q1.265625 0 2.234375 0.65625q0.96875 0.640625 1.453125 1.828125q0.5 1.1875 0.5 2.59375q0 1.515625 -0.546875 2.734375q-0.546875 1.203125 -1.578125 1.84375q-1.03125 0.640625 -2.171875 0.640625q-0.84375 0 -1.515625 -0.34375q-0.65625 -0.359375 -1.078125 -0.890625l0 4.796875l-1.671875 0zm1.515625 -8.65625q0 1.90625 0.765625 2.8125q0.78125 0.90625 1.875 0.90625q1.109375 0 1.890625 -0.9375q0.796875 -0.9375 0.796875 -2.921875q0 -1.875 -0.78125 -2.8125q-0.765625 -0.9375 -1.84375 -0.9375q-1.0625 0 -1.890625 1.0q-0.8125 1.0 -0.8125 2.890625zm15.297607 3.65625q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm3.7819824 5.75l1.609375 0.25q0.109375 0.75 0.578125 1.09375q0.609375 0.453125 1.6875 0.453125q1.171875 0 1.796875 -0.46875q0.625 -0.453125 0.859375 -1.28125q0.125 -0.515625 0.109375 -2.15625q-1.09375 1.296875 -2.71875 1.296875q-2.03125 0 -3.15625 -1.46875q-1.109375 -1.46875 -1.109375 -3.515625q0 -1.40625 0.515625 -2.59375q0.515625 -1.203125 1.484375 -1.84375q0.96875 -0.65625 2.265625 -0.65625q1.75 0 2.875 1.40625l0 -1.1875l1.546875 0l0 8.515625q0 2.3125 -0.46875 3.265625q-0.46875 0.96875 -1.484375 1.515625q-1.015625 0.5625 -2.5 0.5625q-1.765625 0 -2.859375 -0.796875q-1.078125 -0.796875 -1.03125 -2.390625zm1.375 -5.921875q0 1.953125 0.765625 2.84375q0.78125 0.890625 1.9375 0.890625q1.140625 0 1.921875 -0.890625q0.78125 -0.890625 0.78125 -2.78125q0 -1.8125 -0.8125 -2.71875q-0.796875 -0.921875 -1.921875 -0.921875q-1.109375 0 -1.890625 0.90625q-0.78125 0.890625 -0.78125 2.671875zm16.047546 1.9375l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875z" fill-rule="nonzero"></path><path fill="#000000" d="m557.1621 676.44495l-3.015625 -9.859375l1.71875 0l1.5625 5.6875l0.59375 2.125q0.03125 -0.15625 0.5 -2.03125l1.578125 -5.78125l1.71875 0l1.46875 5.71875l0.484375 1.890625l0.578125 -1.90625l1.6875 -5.703125l1.625 0l-3.078125 9.859375l-1.734375 0l-1.578125 -5.90625l-0.375 -1.671875l-2.0 7.578125l-1.734375 0zm11.660461 -11.6875l0 -1.90625l1.671875 0l0 1.90625l-1.671875 0zm0 11.6875l0 -9.859375l1.671875 0l0 9.859375l-1.671875 0zm7.7854614 -1.5l0.234375 1.484375q-0.703125 0.140625 -1.265625 0.140625q-0.90625 0 -1.40625 -0.28125q-0.5 -0.296875 -0.703125 -0.75q-0.203125 -0.46875 -0.203125 -1.984375l0 -5.65625l-1.234375 0l0 -1.3125l1.234375 0l0 -2.4375l1.65625 -1.0l0 3.4375l1.6875 0l0 1.3125l-1.6875 0l0 5.75q0 0.71875 0.078125 0.921875q0.09375 0.203125 0.296875 0.328125q0.203125 0.125 0.578125 0.125q0.265625 0 0.734375 -0.078125zm1.5270386 1.5l0 -13.59375l1.671875 0l0 4.875q1.171875 -1.359375 2.953125 -1.359375q1.09375 0 1.890625 0.4375q0.8125 0.421875 1.15625 1.1875q0.359375 0.765625 0.359375 2.203125l0 6.25l-1.671875 0l0 -6.25q0 -1.25 -0.546875 -1.8125q-0.546875 -0.578125 -1.53125 -0.578125q-0.75 0 -1.40625 0.390625q-0.640625 0.375 -0.921875 1.046875q-0.28125 0.65625 -0.28125 1.8125l0 5.390625l-1.671875 0zm19.215271 -1.5l0.234375 1.484375q-0.703125 0.140625 -1.265625 0.140625q-0.90625 0 -1.40625 -0.28125q-0.5 -0.296875 -0.703125 -0.75q-0.203125 -0.46875 -0.203125 -1.984375l0 -5.65625l-1.234375 0l0 -1.3125l1.234375 0l0 -2.4375l1.65625 -1.0l0 3.4375l1.6875 0l0 1.3125l-1.6875 0l0 5.75q0 0.71875 0.078125 0.921875q0.09375 0.203125 0.296875 0.328125q0.203125 0.125 0.578125 0.125q0.265625 0 0.734375 -0.078125zm7.9645386 0.28125q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm4.0632324 4.9375l0 -9.859375l1.5 0l0 1.5q0.578125 -1.046875 1.0625 -1.375q0.484375 -0.34375 1.078125 -0.34375q0.84375 0 1.71875 0.546875l-0.578125 1.546875q-0.609375 -0.359375 -1.234375 -0.359375q-0.546875 0 -0.984375 0.328125q-0.421875 0.328125 -0.609375 0.90625q-0.28125 0.890625 -0.28125 1.953125l0 5.15625l-1.671875 0zm5.9313965 0.8125l1.609375 0.25q0.109375 0.75 0.578125 1.09375q0.609375 0.453125 1.6875 0.453125q1.171875 0 1.796875 -0.46875q0.625 -0.453125 0.859375 -1.28125q0.125 -0.515625 0.109375 -2.15625q-1.09375 1.296875 -2.71875 1.296875q-2.03125 0 -3.15625 -1.46875q-1.109375 -1.46875 -1.109375 -3.515625q0 -1.40625 0.515625 -2.59375q0.515625 -1.203125 1.484375 -1.84375q0.96875 -0.65625 2.265625 -0.65625q1.75 0 2.875 1.40625l0 -1.1875l1.546875 0l0 8.515625q0 2.3125 -0.46875 3.265625q-0.46875 0.96875 -1.484375 1.515625q-1.015625 0.5625 -2.5 0.5625q-1.765625 0 -2.859375 -0.796875q-1.078125 -0.796875 -1.03125 -2.390625zm1.375 -5.921875q0 1.953125 0.765625 2.84375q0.78125 0.890625 1.9375 0.890625q1.140625 0 1.921875 -0.890625q0.78125 -0.890625 0.78125 -2.78125q0 -1.8125 -0.8125 -2.71875q-0.796875 -0.921875 -1.921875 -0.921875q-1.109375 0 -1.890625 0.90625q-0.78125 0.890625 -0.78125 2.671875zm16.047607 1.9375l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm12.766357 4.375l0.234375 1.484375q-0.703125 0.140625 -1.265625 0.140625q-0.90625 0 -1.40625 -0.28125q-0.5 -0.296875 -0.703125 -0.75q-0.203125 -0.46875 -0.203125 -1.984375l0 -5.65625l-1.234375 0l0 -1.3125l1.234375 0l0 -2.4375l1.65625 -1.0l0 3.4375l1.6875 0l0 1.3125l-1.6875 0l0 5.75q0 0.71875 0.078125 0.921875q0.09375 0.203125 0.296875 0.328125q0.203125 0.125 0.578125 0.125q0.265625 0 0.734375 -0.078125z" fill-rule="nonzero"></path><path fill="#000000" d="m554.05273 698.44495l5.234375 -13.59375l1.9375 0l5.5625 13.59375l-2.046875 0l-1.59375 -4.125l-5.6875 0l-1.484375 4.125l-1.921875 0zm3.921875 -5.578125l4.609375 0l-1.40625 -3.78125q-0.65625 -1.703125 -0.96875 -2.8125q-0.265625 1.3125 -0.734375 2.59375l-1.5 4.0zm10.0217285 5.578125l0 -13.59375l5.125 0q1.359375 0 2.078125 0.125q1.0 0.171875 1.671875 0.640625q0.671875 0.46875 1.078125 1.3125q0.421875 0.84375 0.421875 1.84375q0 1.734375 -1.109375 2.9375q-1.09375 1.203125 -3.984375 1.203125l-3.484375 0l0 5.53125l-1.796875 0zm1.796875 -7.140625l3.515625 0q1.75 0 2.46875 -0.640625q0.734375 -0.65625 0.734375 -1.828125q0 -0.859375 -0.4375 -1.46875q-0.421875 -0.609375 -1.125 -0.796875q-0.453125 -0.125 -1.671875 -0.125l-3.484375 0l0 4.859375zm10.9435425 7.140625l0 -13.59375l1.8125 0l0 13.59375l-1.8125 0zm8.601013 0.234375l3.9375 -14.0625l1.34375 0l-3.9375 14.0625l-1.34375 0zm11.585327 -0.234375l0 -13.59375l1.671875 0l0 13.59375l-1.671875 0zm3.5510864 -4.921875q0 -2.734375 1.53125 -4.0625q1.265625 -1.09375 3.09375 -1.09375q2.03125 0 3.3125 1.34375q1.296875 1.328125 1.296875 3.671875q0 1.90625 -0.578125 3.0q-0.5625 1.078125 -1.65625 1.6875q-1.078125 0.59375 -2.375 0.59375q-2.0625 0 -3.34375 -1.328125q-1.28125 -1.328125 -1.28125 -3.8125zm1.71875 0q0 1.890625 0.828125 2.828125q0.828125 0.9375 2.078125 0.9375q1.25 0 2.0625 -0.9375q0.828125 -0.953125 0.828125 -2.890625q0 -1.828125 -0.828125 -2.765625q-0.828125 -0.9375 -2.0625 -0.9375q-1.25 0 -2.078125 0.9375q-0.828125 0.9375 -0.828125 2.828125zm8.985107 5.734375l1.609375 0.25q0.109375 0.75 0.578125 1.09375q0.609375 0.453125 1.6875 0.453125q1.171875 0 1.796875 -0.46875q0.625 -0.453125 0.859375 -1.28125q0.125 -0.515625 0.109375 -2.15625q-1.09375 1.296875 -2.71875 1.296875q-2.03125 0 -3.15625 -1.46875q-1.109375 -1.46875 -1.109375 -3.515625q0 -1.40625 0.515625 -2.59375q0.515625 -1.203125 1.484375 -1.84375q0.96875 -0.65625 2.265625 -0.65625q1.75 0 2.875 1.40625l0 -1.1875l1.546875 0l0 8.515625q0 2.3125 -0.46875 3.265625q-0.46875 0.96875 -1.484375 1.515625q-1.015625 0.5625 -2.5 0.5625q-1.765625 0 -2.859375 -0.796875q-1.078125 -0.796875 -1.03125 -2.390625zm1.375 -5.921875q0 1.953125 0.765625 2.84375q0.78125 0.890625 1.9375 0.890625q1.140625 0 1.921875 -0.890625q0.78125 -0.890625 0.78125 -2.78125q0 -1.8125 -0.8125 -2.71875q-0.796875 -0.921875 -1.921875 -0.921875q-1.109375 0 -1.890625 0.90625q-0.78125 0.890625 -0.78125 2.671875zm9.313171 -6.578125l0 -1.90625l1.671875 0l0 1.90625l-1.671875 0zm0 11.6875l0 -9.859375l1.671875 0l0 9.859375l-1.671875 0zm4.1292114 0l0 -9.859375l1.5 0l0 1.40625q1.09375 -1.625 3.140625 -1.625q0.890625 0 1.640625 0.328125q0.75 0.3125 1.109375 0.84375q0.375 0.515625 0.53125 1.21875q0.09375 0.46875 0.09375 1.625l0 6.0625l-1.671875 0l0 -6.0q0 -1.015625 -0.203125 -1.515625q-0.1875 -0.515625 -0.6875 -0.8125q-0.5 -0.296875 -1.171875 -0.296875q-1.0625 0 -1.84375 0.671875q-0.765625 0.671875 -0.765625 2.578125l0 5.375l-1.671875 0z" fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0" d="m603.2966 782.2992l2.3937378 43.653564" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m603.2966 782.2992l2.0652466 37.662598" fill-rule="evenodd"></path><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m603.7125 820.0522l1.8977051 4.440857l1.4008179 -4.621704z" fill-rule="evenodd"></path><path fill="#bf9000" d="m512.5171 813.52496l114.74011 -60.960632l114.74017 60.960632l-114.74017 60.96057z" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m512.5171 813.52496l114.74011 -60.960632l114.74017 60.960632l-114.74017 60.96057z" fill-rule="nonzero"></path><path fill="#000000" d="m605.663 816.06995l1.6875 -0.140625q0.125 1.015625 0.5625 1.671875q0.4375 0.65625 1.359375 1.0625q0.9375 0.40625 2.09375 0.40625q1.03125 0 1.8125 -0.3125q0.796875 -0.3125 1.1875 -0.84375q0.390625 -0.53125 0.390625 -1.15625q0 -0.640625 -0.375 -1.109375q-0.375 -0.484375 -1.234375 -0.8125q-0.546875 -0.21875 -2.421875 -0.65625q-1.875 -0.453125 -2.625 -0.859375q-0.96875 -0.515625 -1.453125 -1.265625q-0.46875 -0.75 -0.46875 -1.6875q0 -1.03125 0.578125 -1.921875q0.59375 -0.90625 1.703125 -1.359375q1.125 -0.46875 2.5 -0.46875q1.515625 0 2.671875 0.484375q1.15625 0.484375 1.765625 1.4375q0.625 0.9375 0.671875 2.140625l-1.71875 0.125q-0.140625 -1.28125 -0.953125 -1.9375q-0.796875 -0.671875 -2.359375 -0.671875q-1.625 0 -2.375 0.609375q-0.75 0.59375 -0.75 1.4375q0 0.734375 0.53125 1.203125q0.515625 0.46875 2.703125 0.96875q2.203125 0.5 3.015625 0.875q1.1875 0.546875 1.75 1.390625q0.578125 0.828125 0.578125 1.921875q0 1.09375 -0.625 2.0625q-0.625 0.953125 -1.796875 1.484375q-1.15625 0.53125 -2.609375 0.53125q-1.84375 0 -3.09375 -0.53125q-1.25 -0.546875 -1.96875 -1.625q-0.703125 -1.078125 -0.734375 -2.453125zm12.4436035 0l1.6875 -0.140625q0.125 1.015625 0.5625 1.671875q0.4375 0.65625 1.359375 1.0625q0.9375 0.40625 2.09375 0.40625q1.03125 0 1.8125 -0.3125q0.796875 -0.3125 1.1875 -0.84375q0.390625 -0.53125 0.390625 -1.15625q0 -0.640625 -0.375 -1.109375q-0.375 -0.484375 -1.234375 -0.8125q-0.546875 -0.21875 -2.421875 -0.65625q-1.875 -0.453125 -2.625 -0.859375q-0.96875 -0.515625 -1.453125 -1.265625q-0.46875 -0.75 -0.46875 -1.6875q0 -1.03125 0.578125 -1.921875q0.59375 -0.90625 1.703125 -1.359375q1.125 -0.46875 2.5 -0.46875q1.515625 0 2.671875 0.484375q1.15625 0.484375 1.765625 1.4375q0.625 0.9375 0.671875 2.140625l-1.71875 0.125q-0.140625 -1.28125 -0.953125 -1.9375q-0.796875 -0.671875 -2.359375 -0.671875q-1.625 0 -2.375 0.609375q-0.75 0.59375 -0.75 1.4375q0 0.734375 0.53125 1.203125q0.515625 0.46875 2.703125 0.96875q2.203125 0.5 3.015625 0.875q1.1875 0.546875 1.75 1.390625q0.578125 0.828125 0.578125 1.921875q0 1.09375 -0.625 2.0625q-0.625 0.953125 -1.796875 1.484375q-1.15625 0.53125 -2.609375 0.53125q-1.84375 0 -3.09375 -0.53125q-1.25 -0.546875 -1.96875 -1.625q-0.703125 -1.078125 -0.734375 -2.453125zm12.5060425 -2.25q0 -3.390625 1.8125 -5.296875q1.828125 -1.921875 4.703125 -1.921875q1.875 0 3.390625 0.90625q1.515625 0.890625 2.296875 2.5q0.796875 1.609375 0.796875 3.65625q0 2.0625 -0.84375 3.703125q-0.828125 1.625 -2.359375 2.46875q-1.53125 0.84375 -3.296875 0.84375q-1.921875 0 -3.4375 -0.921875q-1.5 -0.9375 -2.28125 -2.53125q-0.78125 -1.609375 -0.78125 -3.40625zm1.859375 0.03125q0 2.453125 1.3125 3.875q1.328125 1.40625 3.3125 1.40625q2.03125 0 3.34375 -1.421875q1.3125 -1.4375 1.3125 -4.0625q0 -1.65625 -0.5625 -2.890625q-0.546875 -1.234375 -1.640625 -1.921875q-1.078125 -0.6875 -2.421875 -0.6875q-1.90625 0 -3.28125 1.3125q-1.375 1.3125 -1.375 4.390625z" fill-rule="nonzero"></path><path fill="#f1c232" d="m677.6772 941.51184l179.27557 0l0 94.64563l-179.27557 0z" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m677.6772 941.51184l179.27557 0l0 94.64563l-179.27557 0z" fill-rule="nonzero"></path><path fill="#000000" d="m725.6051 990.4265l0 -1.609375l5.765625 0l0 5.046875q-1.328125 1.0625 -2.75 1.59375q-1.40625 0.53125 -2.890625 0.53125q-2.0 0 -3.640625 -0.859375q-1.625 -0.859375 -2.46875 -2.484375q-0.828125 -1.625 -0.828125 -3.625q0 -1.984375 0.828125 -3.703125q0.828125 -1.71875 2.390625 -2.546875q1.5625 -0.84375 3.59375 -0.84375q1.46875 0 2.65625 0.484375q1.203125 0.46875 1.875 1.328125q0.671875 0.84375 1.03125 2.21875l-1.625 0.4375q-0.3125 -1.03125 -0.765625 -1.625q-0.453125 -0.59375 -1.296875 -0.953125q-0.84375 -0.359375 -1.875 -0.359375q-1.234375 0 -2.140625 0.375q-0.890625 0.375 -1.453125 1.0q-0.546875 0.609375 -0.84375 1.34375q-0.53125 1.25 -0.53125 2.734375q0 1.8125 0.625 3.046875q0.640625 1.21875 1.828125 1.8125q1.203125 0.59375 2.546875 0.59375q1.171875 0 2.28125 -0.453125q1.109375 -0.453125 1.6875 -0.953125l0 -2.53125l-4.0 0zm7.9332886 5.328125l0 -9.859375l1.5 0l0 1.390625q0.453125 -0.71875 1.21875 -1.15625q0.78125 -0.453125 1.765625 -0.453125q1.09375 0 1.796875 0.453125q0.703125 0.453125 0.984375 1.28125q1.171875 -1.734375 3.046875 -1.734375q1.46875 0 2.25 0.8125q0.796875 0.8125 0.796875 2.5l0 6.765625l-1.671875 0l0 -6.203125q0 -1.0 -0.15625 -1.4375q-0.15625 -0.453125 -0.59375 -0.71875q-0.421875 -0.265625 -1.0 -0.265625q-1.03125 0 -1.71875 0.6875q-0.6875 0.6875 -0.6875 2.21875l0 5.71875l-1.671875 0l0 -6.40625q0 -1.109375 -0.40625 -1.65625q-0.40625 -0.5625 -1.34375 -0.5625q-0.703125 0 -1.3125 0.375q-0.59375 0.359375 -0.859375 1.078125q-0.265625 0.71875 -0.265625 2.0625l0 5.109375l-1.671875 0zm21.978333 -1.21875q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm4.0944824 -6.75l0 -1.90625l1.671875 0l0 1.90625l-1.671875 0zm0 11.6875l0 -9.859375l1.671875 0l0 9.859375l-1.671875 0zm4.0979004 0l0 -13.59375l1.671875 0l0 13.59375l-1.671875 0zm15.796875 -1.21875q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm10.531982 4.9375l0 -1.453125q-1.140625 1.671875 -3.125 1.671875q-0.859375 0 -1.625 -0.328125q-0.75 -0.34375 -1.125 -0.84375q-0.359375 -0.5 -0.515625 -1.234375q-0.09375 -0.5 -0.09375 -1.5625l0 -6.109375l1.671875 0l0 5.46875q0 1.3125 0.09375 1.765625q0.15625 0.65625 0.671875 1.03125q0.515625 0.375 1.265625 0.375q0.75 0 1.40625 -0.375q0.65625 -0.390625 0.921875 -1.046875q0.28125 -0.671875 0.28125 -1.9375l0 -5.28125l1.671875 0l0 9.859375l-1.5 0zm7.5788574 -1.5l0.234375 1.484375q-0.703125 0.140625 -1.265625 0.140625q-0.90625 0 -1.40625 -0.28125q-0.5 -0.296875 -0.703125 -0.75q-0.203125 -0.46875 -0.203125 -1.984375l0 -5.65625l-1.234375 0l0 -1.3125l1.234375 0l0 -2.4375l1.65625 -1.0l0 3.4375l1.6875 0l0 1.3125l-1.6875 0l0 5.75q0 0.71875 0.078125 0.921875q0.09375 0.203125 0.296875 0.328125q0.203125 0.125 0.578125 0.125q0.265625 0 0.734375 -0.078125zm1.5270386 1.5l0 -13.59375l1.671875 0l0 4.875q1.171875 -1.359375 2.953125 -1.359375q1.09375 0 1.890625 0.4375q0.8125 0.421875 1.15625 1.1875q0.359375 0.765625 0.359375 2.203125l0 6.25l-1.671875 0l0 -6.25q0 -1.25 -0.546875 -1.8125q-0.546875 -0.578125 -1.53125 -0.578125q-0.75 0 -1.40625 0.390625q-0.640625 0.375 -0.921875 1.046875q-0.28125 0.65625 -0.28125 1.8125l0 5.390625l-1.671875 0z" fill-rule="nonzero"></path><path fill="#ffd966" d="m400.60892 941.51184l179.2756 0l0 94.64563l-179.2756 0z" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m400.60892 941.51184l179.2756 0l0 94.64563l-179.2756 0z" fill-rule="nonzero"></path><path fill="#000000" d="m422.49536 995.75464l0 -1.453125q-1.140625 1.671875 -3.125 1.671875q-0.859375 0 -1.625 -0.328125q-0.75 -0.34375 -1.125 -0.84375q-0.359375 -0.5 -0.515625 -1.234375q-0.09375 -0.5 -0.09375 -1.5625l0 -6.109375l1.671875 0l0 5.46875q0 1.3125 0.09375 1.765625q0.15625 0.65625 0.671875 1.03125q0.515625 0.375 1.265625 0.375q0.75 0 1.40625 -0.375q0.65625 -0.390625 0.921875 -1.046875q0.28125 -0.671875 0.28125 -1.9375l0 -5.28125l1.671875 0l0 9.859375l-1.5 0zm3.250702 -2.9375l1.65625 -0.265625q0.140625 1.0 0.765625 1.53125q0.640625 0.515625 1.78125 0.515625q1.15625 0 1.703125 -0.46875q0.5625 -0.46875 0.5625 -1.09375q0 -0.5625 -0.484375 -0.890625q-0.34375 -0.21875 -1.703125 -0.5625q-1.84375 -0.46875 -2.5625 -0.796875q-0.703125 -0.34375 -1.078125 -0.9375q-0.359375 -0.609375 -0.359375 -1.328125q0 -0.65625 0.296875 -1.21875q0.3125 -0.5625 0.828125 -0.9375q0.390625 -0.28125 1.0625 -0.484375q0.671875 -0.203125 1.4375 -0.203125q1.171875 0 2.046875 0.34375q0.875 0.328125 1.28125 0.90625q0.421875 0.5625 0.578125 1.515625l-1.625 0.21875q-0.109375 -0.75 -0.65625 -1.171875q-0.53125 -0.4375 -1.5 -0.4375q-1.15625 0 -1.640625 0.390625q-0.484375 0.375 -0.484375 0.875q0 0.328125 0.203125 0.59375q0.203125 0.265625 0.640625 0.4375q0.25 0.09375 1.46875 0.4375q1.765625 0.46875 2.46875 0.765625q0.703125 0.296875 1.09375 0.875q0.40625 0.578125 0.40625 1.4375q0 0.828125 -0.484375 1.578125q-0.484375 0.734375 -1.40625 1.140625q-0.921875 0.390625 -2.078125 0.390625q-1.921875 0 -2.9375 -0.796875q-1.0 -0.796875 -1.28125 -2.359375zm16.75 -0.234375l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm9.094482 5.875l0 -9.859375l1.5 0l0 1.5q0.578125 -1.046875 1.0625 -1.375q0.484375 -0.34375 1.078125 -0.34375q0.84375 0 1.71875 0.546875l-0.578125 1.546875q-0.609375 -0.359375 -1.234375 -0.359375q-0.546875 0 -0.984375 0.328125q-0.421875 0.328125 -0.609375 0.90625q-0.28125 0.890625 -0.28125 1.953125l0 5.15625l-1.671875 0zm6.228302 0l0 -9.859375l1.5 0l0 1.40625q1.09375 -1.625 3.140625 -1.625q0.890625 0 1.640625 0.328125q0.75 0.3125 1.109375 0.84375q0.375 0.515625 0.53125 1.21875q0.09375 0.46875 0.09375 1.625l0 6.0625l-1.671875 0l0 -6.0q0 -1.015625 -0.203125 -1.515625q-0.1875 -0.515625 -0.6875 -0.8125q-0.5 -0.296875 -1.171875 -0.296875q-1.0625 0 -1.84375 0.671875q-0.765625 0.671875 -0.765625 2.578125l0 5.375l-1.671875 0zm16.813202 -1.21875q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm4.0788574 4.9375l0 -9.859375l1.5 0l0 1.390625q0.453125 -0.71875 1.21875 -1.15625q0.78125 -0.453125 1.765625 -0.453125q1.09375 0 1.796875 0.453125q0.703125 0.453125 0.984375 1.28125q1.171875 -1.734375 3.046875 -1.734375q1.46875 0 2.25 0.8125q0.796875 0.8125 0.796875 2.5l0 6.765625l-1.671875 0l0 -6.203125q0 -1.0 -0.15625 -1.4375q-0.15625 -0.453125 -0.59375 -0.71875q-0.421875 -0.265625 -1.0 -0.265625q-1.03125 0 -1.71875 0.6875q-0.6875 0.6875 -0.6875 2.21875l0 5.71875l-1.671875 0l0 -6.40625q0 -1.109375 -0.40625 -1.65625q-0.40625 -0.5625 -1.34375 -0.5625q-0.703125 0 -1.3125 0.375q-0.59375 0.359375 -0.859375 1.078125q-0.265625 0.71875 -0.265625 2.0625l0 5.109375l-1.671875 0zm22.290802 -3.171875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm13.043396 6.109375l3.9375 -14.0625l1.34375 0l-3.9375 14.0625l-1.34375 0zm11.616577 3.546875l0 -13.640625l1.53125 0l0 1.28125q0.53125 -0.75 1.203125 -1.125q0.6875 -0.375 1.640625 -0.375q1.265625 0 2.234375 0.65625q0.96875 0.640625 1.453125 1.828125q0.5 1.1875 0.5 2.59375q0 1.515625 -0.546875 2.734375q-0.546875 1.203125 -1.578125 1.84375q-1.03125 0.640625 -2.171875 0.640625q-0.84375 0 -1.515625 -0.34375q-0.65625 -0.359375 -1.078125 -0.890625l0 4.796875l-1.671875 0zm1.515625 -8.65625q0 1.90625 0.765625 2.8125q0.78125 0.90625 1.875 0.90625q1.109375 0 1.890625 -0.9375q0.796875 -0.9375 0.796875 -2.921875q0 -1.875 -0.78125 -2.8125q-0.765625 -0.9375 -1.84375 -0.9375q-1.0625 0 -1.890625 1.0q-0.8125 1.0 -0.8125 2.890625zm8.188232 1.9375l1.65625 -0.265625q0.140625 1.0 0.765625 1.53125q0.640625 0.515625 1.78125 0.515625q1.15625 0 1.703125 -0.46875q0.5625 -0.46875 0.5625 -1.09375q0 -0.5625 -0.484375 -0.890625q-0.34375 -0.21875 -1.703125 -0.5625q-1.84375 -0.46875 -2.5625 -0.796875q-0.703125 -0.34375 -1.078125 -0.9375q-0.359375 -0.609375 -0.359375 -1.328125q0 -0.65625 0.296875 -1.21875q0.3125 -0.5625 0.828125 -0.9375q0.390625 -0.28125 1.0625 -0.484375q0.671875 -0.203125 1.4375 -0.203125q1.171875 0 2.046875 0.34375q0.875 0.328125 1.28125 0.90625q0.421875 0.5625 0.578125 1.515625l-1.625 0.21875q-0.109375 -0.75 -0.65625 -1.171875q-0.53125 -0.4375 -1.5 -0.4375q-1.15625 0 -1.640625 0.390625q-0.484375 0.375 -0.484375 0.875q0 0.328125 0.203125 0.59375q0.203125 0.265625 0.640625 0.4375q0.25 0.09375 1.46875 0.4375q1.765625 0.46875 2.46875 0.765625q0.703125 0.296875 1.09375 0.875q0.40625 0.578125 0.40625 1.4375q0 0.828125 -0.484375 1.578125q-0.484375 0.734375 -1.40625 1.140625q-0.921875 0.390625 -2.078125 0.390625q-1.921875 0 -2.9375 -0.796875q-1.0 -0.796875 -1.28125 -2.359375zm11.828125 2.9375l-3.015625 -9.859375l1.71875 0l1.5625 5.6875l0.59375 2.125q0.03125 -0.15625 0.5 -2.03125l1.578125 -5.78125l1.71875 0l1.46875 5.71875l0.484375 1.890625l0.578125 -1.90625l1.6875 -5.703125l1.625 0l-3.078125 9.859375l-1.734375 0l-1.578125 -5.90625l-0.375 -1.671875l-2.0 7.578125l-1.734375 0zm18.035461 0l0 -1.25q-0.9375 1.46875 -2.75 1.46875q-1.171875 0 -2.171875 -0.640625q-0.984375 -0.65625 -1.53125 -1.8125q-0.53125 -1.171875 -0.53125 -2.6875q0 -1.46875 0.484375 -2.671875q0.5 -1.203125 1.46875 -1.84375q0.984375 -0.640625 2.203125 -0.640625q0.890625 0 1.578125 0.375q0.703125 0.375 1.140625 0.984375l0 -4.875l1.65625 0l0 13.59375l-1.546875 0zm-5.28125 -4.921875q0 1.890625 0.796875 2.828125q0.8125 0.9375 1.890625 0.9375q1.09375 0 1.859375 -0.890625q0.765625 -0.890625 0.765625 -2.734375q0 -2.015625 -0.78125 -2.953125q-0.78125 -0.953125 -1.921875 -0.953125q-1.109375 0 -1.859375 0.90625q-0.75 0.90625 -0.75 2.859375z" fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0" d="m627.2572 874.48553l0 33.513184l-137.00787 0l0 33.510498" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m627.2572 874.48553l0 33.513123l-137.00787 0l0 30.083435" fill-rule="evenodd"></path><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m490.24933 938.0821l-1.1245728 -1.1245728l1.1245728 3.0897827l1.1246033 -3.0897827z" fill-rule="evenodd"></path><path fill="#000000" fill-opacity="0.0" d="m627.2572 874.48553l0 33.513184l140.06299 0l0 33.510498" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m627.2572 874.48553l0 33.513123l140.06299 0l0 30.083435" fill-rule="evenodd"></path><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m767.3202 938.0821l-1.1245728 -1.1245728l1.1245728 3.0897827l1.1245728 -3.0897827z" fill-rule="evenodd"></path><path fill="#000000" fill-opacity="0.0" d="m733.7454 1068.1392l137.00787 0l0 48.0l-137.00787 0z" fill-rule="nonzero"></path><path fill="#000000" d="m742.7142 1095.0591l5.234375 -13.59375l1.9375 0l5.5625 13.59375l-2.046875 0l-1.59375 -4.125l-5.6875 0l-1.484375 4.125l-1.921875 0zm3.921875 -5.578125l4.609375 0l-1.40625 -3.78125q-0.65625 -1.703125 -0.96875 -2.8125q-0.265625 1.3125 -0.734375 2.59375l-1.5 4.0zm16.256042 5.578125l0 -1.453125q-1.140625 1.671875 -3.125 1.671875q-0.859375 0 -1.625 -0.328125q-0.75 -0.34375 -1.125 -0.84375q-0.359375 -0.5 -0.515625 -1.234375q-0.09375 -0.5 -0.09375 -1.5625l0 -6.109375l1.671875 0l0 5.46875q0 1.3125 0.09375 1.765625q0.15625 0.65625 0.671875 1.03125q0.515625 0.375 1.265625 0.375q0.75 0 1.40625 -0.375q0.65625 -0.390625 0.921875 -1.046875q0.28125 -0.671875 0.28125 -1.9375l0 -5.28125l1.671875 0l0 9.859375l-1.5 0zm7.5788574 -1.5l0.234375 1.484375q-0.703125 0.140625 -1.265625 0.140625q-0.90625 0 -1.40625 -0.28125q-0.5 -0.296875 -0.703125 -0.75q-0.203125 -0.46875 -0.203125 -1.984375l0 -5.65625l-1.234375 0l0 -1.3125l1.234375 0l0 -2.4375l1.65625 -1.0l0 3.4375l1.6875 0l0 1.3125l-1.6875 0l0 5.75q0 0.71875 0.078125 0.921875q0.09375 0.203125 0.296875 0.328125q0.203125 0.125 0.578125 0.125q0.265625 0 0.734375 -0.078125zm1.5270386 1.5l0 -13.59375l1.671875 0l0 4.875q1.171875 -1.359375 2.953125 -1.359375q1.09375 0 1.890625 0.4375q0.8125 0.421875 1.15625 1.1875q0.359375 0.765625 0.359375 2.203125l0 6.25l-1.671875 0l0 -6.25q0 -1.25 -0.546875 -1.8125q-0.546875 -0.578125 -1.53125 -0.578125q-0.75 0 -1.40625 0.390625q-0.640625 0.375 -0.921875 1.046875q-0.28125 0.65625 -0.28125 1.8125l0 5.390625l-1.671875 0zm19.215271 -1.5l0.234375 1.484375q-0.703125 0.140625 -1.265625 0.140625q-0.90625 0 -1.40625 -0.28125q-0.5 -0.296875 -0.703125 -0.75q-0.203125 -0.46875 -0.203125 -1.984375l0 -5.65625l-1.234375 0l0 -1.3125l1.234375 0l0 -2.4375l1.65625 -1.0l0 3.4375l1.6875 0l0 1.3125l-1.6875 0l0 5.75q0 0.71875 0.078125 0.921875q0.09375 0.203125 0.296875 0.328125q0.203125 0.125 0.578125 0.125q0.265625 0 0.734375 -0.078125zm0.9020386 -3.421875q0 -2.734375 1.53125 -4.0625q1.265625 -1.09375 3.09375 -1.09375q2.03125 0 3.3125 1.34375q1.296875 1.328125 1.296875 3.671875q0 1.90625 -0.578125 3.0q-0.5625 1.078125 -1.65625 1.6875q-1.078125 0.59375 -2.375 0.59375q-2.0625 0 -3.34375 -1.328125q-1.28125 -1.328125 -1.28125 -3.8125zm1.71875 0q0 1.890625 0.828125 2.828125q0.828125 0.9375 2.078125 0.9375q1.25 0 2.0625 -0.9375q0.828125 -0.953125 0.828125 -2.890625q0 -1.828125 -0.828125 -2.765625q-0.828125 -0.9375 -2.0625 -0.9375q-1.25 0 -2.078125 0.9375q-0.828125 0.9375 -0.828125 2.828125zm9.297607 4.921875l0 -13.59375l1.671875 0l0 7.75l3.953125 -4.015625l2.15625 0l-3.765625 3.65625l4.140625 6.203125l-2.0625 0l-3.25 -5.03125l-1.171875 1.125l0 3.90625l-1.671875 0zm16.0625 -3.171875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm9.110107 5.875l0 -9.859375l1.5 0l0 1.40625q1.09375 -1.625 3.140625 -1.625q0.890625 0 1.640625 0.328125q0.75 0.3125 1.109375 0.84375q0.375 0.515625 0.53125 1.21875q0.09375 0.46875 0.09375 1.625l0 6.0625l-1.671875 0l0 -6.0q0 -1.015625 -0.203125 -1.515625q-0.1875 -0.515625 -0.6875 -0.8125q-0.5 -0.296875 -1.171875 -0.296875q-1.0625 0 -1.84375 0.671875q-0.765625 0.671875 -0.765625 2.578125l0 5.375l-1.671875 0z" fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0" d="m1027.8976 907.0079l229.48035 0l0 94.64569l-229.48035 0z" fill-rule="nonzero"></path><path fill="#000000" d="m1038.3976 933.92786l0 -13.59375l6.03125 0q1.8125 0 2.75 0.359375q0.953125 0.359375 1.515625 1.296875q0.5625 0.921875 0.5625 2.046875q0 1.453125 -0.9375 2.453125q-0.921875 0.984375 -2.890625 1.25q0.71875 0.34375 1.09375 0.671875q0.78125 0.734375 1.484375 1.8125l2.375 3.703125l-2.265625 0l-1.796875 -2.828125q-0.796875 -1.21875 -1.3125 -1.875q-0.5 -0.65625 -0.90625 -0.90625q-0.40625 -0.265625 -0.8125 -0.359375q-0.3125 -0.078125 -1.015625 -0.078125l-2.078125 0l0 6.046875l-1.796875 0zm1.796875 -7.59375l3.859375 0q1.234375 0 1.921875 -0.25q0.703125 -0.265625 1.0625 -0.828125q0.375 -0.5625 0.375 -1.21875q0 -0.96875 -0.703125 -1.578125q-0.703125 -0.625 -2.21875 -0.625l-4.296875 0l0 4.5zm18.176147 4.421875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm15.500732 5.875l0 -1.25q-0.9375 1.46875 -2.75 1.46875q-1.171875 0 -2.171875 -0.640625q-0.984375 -0.65625 -1.53125 -1.8125q-0.53125 -1.171875 -0.53125 -2.6875q0 -1.46875 0.484375 -2.671875q0.5 -1.203125 1.46875 -1.84375q0.984375 -0.640625 2.203125 -0.640625q0.890625 0 1.578125 0.375q0.703125 0.375 1.140625 0.984375l0 -4.875l1.65625 0l0 13.59375l-1.546875 0zm-5.28125 -4.921875q0 1.890625 0.796875 2.828125q0.8125 0.9375 1.890625 0.9375q1.09375 0 1.859375 -0.890625q0.765625 -0.890625 0.765625 -2.734375q0 -2.015625 -0.78125 -2.953125q-0.78125 -0.953125 -1.921875 -0.953125q-1.109375 0 -1.859375 0.90625q-0.75 0.90625 -0.75 2.859375zm9.281982 -6.765625l0 -1.90625l1.671875 0l0 1.90625l-1.671875 0zm0 11.6875l0 -9.859375l1.671875 0l0 9.859375l-1.671875 0zm4.1135254 0l0 -9.859375l1.5 0l0 1.5q0.578125 -1.046875 1.0625 -1.375q0.484375 -0.34375 1.078125 -0.34375q0.84375 0 1.71875 0.546875l-0.578125 1.546875q-0.609375 -0.359375 -1.234375 -0.359375q-0.546875 0 -0.984375 0.328125q-0.421875 0.328125 -0.609375 0.90625q-0.28125 0.890625 -0.28125 1.953125l0 5.15625l-1.671875 0zm12.9782715 -3.171875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm15.547607 2.265625l1.640625 0.21875q-0.265625 1.6875 -1.375 2.65625q-1.109375 0.953125 -2.734375 0.953125q-2.015625 0 -3.25 -1.3125q-1.21875 -1.328125 -1.21875 -3.796875q0 -1.59375 0.515625 -2.78125q0.53125 -1.203125 1.609375 -1.796875q1.09375 -0.609375 2.359375 -0.609375q1.609375 0 2.625 0.8125q1.015625 0.8125 1.3125 2.3125l-1.625 0.25q-0.234375 -1.0 -0.828125 -1.5q-0.59375 -0.5 -1.421875 -0.5q-1.265625 0 -2.0625 0.90625q-0.78125 0.90625 -0.78125 2.859375q0 1.984375 0.765625 2.890625q0.765625 0.890625 1.984375 0.890625q0.984375 0 1.640625 -0.59375q0.65625 -0.609375 0.84375 -1.859375zm6.546875 2.109375l0.234375 1.484375q-0.703125 0.140625 -1.265625 0.140625q-0.90625 0 -1.40625 -0.28125q-0.5 -0.296875 -0.703125 -0.75q-0.203125 -0.46875 -0.203125 -1.984375l0 -5.65625l-1.234375 0l0 -1.3125l1.234375 0l0 -2.4375l1.65625 -1.0l0 3.4375l1.6875 0l0 1.3125l-1.6875 0l0 5.75q0 0.71875 0.078125 0.921875q0.09375 0.203125 0.296875 0.328125q0.203125 0.125 0.578125 0.125q0.265625 0 0.734375 -0.078125zm10.366577 0l0.234375 1.484375q-0.703125 0.140625 -1.265625 0.140625q-0.90625 0 -1.40625 -0.28125q-0.5 -0.296875 -0.703125 -0.75q-0.203125 -0.46875 -0.203125 -1.984375l0 -5.65625l-1.234375 0l0 -1.3125l1.234375 0l0 -2.4375l1.65625 -1.0l0 3.4375l1.6875 0l0 1.3125l-1.6875 0l0 5.75q0 0.71875 0.078125 0.921875q0.09375 0.203125 0.296875 0.328125q0.203125 0.125 0.578125 0.125q0.265625 0 0.734375 -0.078125zm0.9020996 -3.421875q0 -2.734375 1.53125 -4.0625q1.265625 -1.09375 3.09375 -1.09375q2.03125 0 3.3125 1.34375q1.296875 1.328125 1.296875 3.671875q0 1.90625 -0.578125 3.0q-0.5625 1.078125 -1.65625 1.6875q-1.078125 0.59375 -2.375 0.59375q-2.0625 0 -3.34375 -1.328125q-1.28125 -1.328125 -1.28125 -3.8125zm1.71875 0q0 1.890625 0.828125 2.828125q0.828125 0.9375 2.078125 0.9375q1.25 0 2.0625 -0.9375q0.828125 -0.953125 0.828125 -2.890625q0 -1.828125 -0.828125 -2.765625q-0.828125 -0.9375 -2.0625 -0.9375q-1.25 0 -2.078125 0.9375q-0.828125 0.9375 -0.828125 2.828125zm13.18396 4.921875l5.234375 -13.59375l1.9375 0l5.5625 13.59375l-2.046875 0l-1.59375 -4.125l-5.6875 0l-1.484375 4.125l-1.921875 0zm3.921875 -5.578125l4.609375 0l-1.40625 -3.78125q-0.65625 -1.703125 -0.96875 -2.8125q-0.265625 1.3125 -0.734375 2.59375l-1.5 4.0zm10.0217285 5.578125l0 -13.59375l5.125 0q1.359375 0 2.078125 0.125q1.0 0.171875 1.671875 0.640625q0.671875 0.46875 1.078125 1.3125q0.421875 0.84375 0.421875 1.84375q0 1.734375 -1.109375 2.9375q-1.09375 1.203125 -3.984375 1.203125l-3.484375 0l0 5.53125l-1.796875 0zm1.796875 -7.140625l3.515625 0q1.75 0 2.46875 -0.640625q0.734375 -0.65625 0.734375 -1.828125q0 -0.859375 -0.4375 -1.46875q-0.421875 -0.609375 -1.125 -0.796875q-0.453125 -0.125 -1.671875 -0.125l-3.484375 0l0 4.859375zm10.9436035 7.140625l0 -13.59375l1.8125 0l0 13.59375l-1.8125 0z" fill-rule="nonzero"></path><path fill="#000000" d="m1037.757 951.55286l1.6875 -0.140625q0.125 1.015625 0.5625 1.671875q0.4375 0.65625 1.359375 1.0625q0.9375 0.40625 2.09375 0.40625q1.03125 0 1.8125 -0.3125q0.796875 -0.3125 1.1875 -0.84375q0.390625 -0.53125 0.390625 -1.15625q0 -0.640625 -0.375 -1.109375q-0.375 -0.484375 -1.234375 -0.8125q-0.546875 -0.21875 -2.421875 -0.65625q-1.875 -0.453125 -2.625 -0.859375q-0.96875 -0.515625 -1.453125 -1.265625q-0.46875 -0.75 -0.46875 -1.6875q0 -1.03125 0.578125 -1.921875q0.59375 -0.90625 1.703125 -1.359375q1.125 -0.46875 2.5 -0.46875q1.515625 0 2.671875 0.484375q1.15625 0.484375 1.765625 1.4375q0.625 0.9375 0.671875 2.140625l-1.71875 0.125q-0.140625 -1.28125 -0.953125 -1.9375q-0.796875 -0.671875 -2.359375 -0.671875q-1.625 0 -2.375 0.609375q-0.75 0.59375 -0.75 1.4375q0 0.734375 0.53125 1.203125q0.515625 0.46875 2.703125 0.96875q2.203125 0.5 3.015625 0.875q1.1875 0.546875 1.75 1.390625q0.578125 0.828125 0.578125 1.921875q0 1.09375 -0.625 2.0625q-0.625 0.953125 -1.796875 1.484375q-1.15625 0.53125 -2.609375 0.53125q-1.84375 0 -3.09375 -0.53125q-1.25 -0.546875 -1.96875 -1.625q-0.703125 -1.078125 -0.734375 -2.453125zm19.584229 1.203125l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm9.094482 5.875l0 -9.859375l1.5 0l0 1.5q0.578125 -1.046875 1.0625 -1.375q0.484375 -0.34375 1.078125 -0.34375q0.84375 0 1.71875 0.546875l-0.578125 1.546875q-0.609375 -0.359375 -1.234375 -0.359375q-0.546875 0 -0.984375 0.328125q-0.421875 0.328125 -0.609375 0.90625q-0.28125 0.890625 -0.28125 1.953125l0 5.15625l-1.671875 0zm8.9626465 0l-3.75 -9.859375l1.765625 0l2.125 5.90625q0.34375 0.953125 0.625 1.984375q0.21875 -0.78125 0.625 -1.875l2.1875 -6.015625l1.71875 0l-3.734375 9.859375l-1.5625 0zm13.34375 -3.171875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm9.094482 5.875l0 -9.859375l1.5 0l0 1.5q0.578125 -1.046875 1.0625 -1.375q0.484375 -0.34375 1.078125 -0.34375q0.84375 0 1.71875 0.546875l-0.578125 1.546875q-0.609375 -0.359375 -1.234375 -0.359375q-0.546875 0 -0.984375 0.328125q-0.421875 0.328125 -0.609375 0.90625q-0.28125 0.890625 -0.28125 1.953125l0 5.15625l-1.671875 0zm18.423096 0l-3.015625 -9.859375l1.71875 0l1.5625 5.6875l0.59375 2.125q0.03125 -0.15625 0.5 -2.03125l1.578125 -5.78125l1.71875 0l1.46875 5.71875l0.484375 1.890625l0.578125 -1.90625l1.6875 -5.703125l1.625 0l-3.078125 9.859375l-1.734375 0l-1.578125 -5.90625l-0.375 -1.671875l-2.0 7.578125l-1.734375 0zm11.6604 -11.6875l0 -1.90625l1.671875 0l0 1.90625l-1.671875 0zm0 11.6875l0 -9.859375l1.671875 0l0 9.859375l-1.671875 0zm7.7854004 -1.5l0.234375 1.484375q-0.703125 0.140625 -1.265625 0.140625q-0.90625 0 -1.40625 -0.28125q-0.5 -0.296875 -0.703125 -0.75q-0.203125 -0.46875 -0.203125 -1.984375l0 -5.65625l-1.234375 0l0 -1.3125l1.234375 0l0 -2.4375l1.65625 -1.0l0 3.4375l1.6875 0l0 1.3125l-1.6875 0l0 5.75q0 0.71875 0.078125 0.921875q0.09375 0.203125 0.296875 0.328125q0.203125 0.125 0.578125 0.125q0.265625 0 0.734375 -0.078125zm1.5270996 1.5l0 -13.59375l1.671875 0l0 4.875q1.171875 -1.359375 2.953125 -1.359375q1.09375 0 1.890625 0.4375q0.8125 0.421875 1.15625 1.1875q0.359375 0.765625 0.359375 2.203125l0 6.25l-1.671875 0l0 -6.25q0 -1.25 -0.546875 -1.8125q-0.546875 -0.578125 -1.53125 -0.578125q-0.75 0 -1.40625 0.390625q-0.640625 0.375 -0.921875 1.046875q-0.28125 0.65625 -0.28125 1.8125l0 5.390625l-1.671875 0z" fill-rule="nonzero"></path><path fill="#000000" d="m1037.757 973.55286l1.6875 -0.140625q0.125 1.015625 0.5625 1.671875q0.4375 0.65625 1.359375 1.0625q0.9375 0.40625 2.09375 0.40625q1.03125 0 1.8125 -0.3125q0.796875 -0.3125 1.1875 -0.84375q0.390625 -0.53125 0.390625 -1.15625q0 -0.640625 -0.375 -1.109375q-0.375 -0.484375 -1.234375 -0.8125q-0.546875 -0.21875 -2.421875 -0.65625q-1.875 -0.453125 -2.625 -0.859375q-0.96875 -0.515625 -1.453125 -1.265625q-0.46875 -0.75 -0.46875 -1.6875q0 -1.03125 0.578125 -1.921875q0.59375 -0.90625 1.703125 -1.359375q1.125 -0.46875 2.5 -0.46875q1.515625 0 2.671875 0.484375q1.15625 0.484375 1.765625 1.4375q0.625 0.9375 0.671875 2.140625l-1.71875 0.125q-0.140625 -1.28125 -0.953125 -1.9375q-0.796875 -0.671875 -2.359375 -0.671875q-1.625 0 -2.375 0.609375q-0.75 0.59375 -0.75 1.4375q0 0.734375 0.53125 1.203125q0.515625 0.46875 2.703125 0.96875q2.203125 0.5 3.015625 0.875q1.1875 0.546875 1.75 1.390625q0.578125 0.828125 0.578125 1.921875q0 1.09375 -0.625 2.0625q-0.625 0.953125 -1.796875 1.484375q-1.15625 0.53125 -2.609375 0.53125q-1.84375 0 -3.09375 -0.53125q-1.25 -0.546875 -1.96875 -1.625q-0.703125 -1.078125 -0.734375 -2.453125zm12.4436035 0l1.6875 -0.140625q0.125 1.015625 0.5625 1.671875q0.4375 0.65625 1.359375 1.0625q0.9375 0.40625 2.09375 0.40625q1.03125 0 1.8125 -0.3125q0.796875 -0.3125 1.1875 -0.84375q0.390625 -0.53125 0.390625 -1.15625q0 -0.640625 -0.375 -1.109375q-0.375 -0.484375 -1.234375 -0.8125q-0.546875 -0.21875 -2.421875 -0.65625q-1.875 -0.453125 -2.625 -0.859375q-0.96875 -0.515625 -1.453125 -1.265625q-0.46875 -0.75 -0.46875 -1.6875q0 -1.03125 0.578125 -1.921875q0.59375 -0.90625 1.703125 -1.359375q1.125 -0.46875 2.5 -0.46875q1.515625 0 2.671875 0.484375q1.15625 0.484375 1.765625 1.4375q0.625 0.9375 0.671875 2.140625l-1.71875 0.125q-0.140625 -1.28125 -0.953125 -1.9375q-0.796875 -0.671875 -2.359375 -0.671875q-1.625 0 -2.375 0.609375q-0.75 0.59375 -0.75 1.4375q0 0.734375 0.53125 1.203125q0.515625 0.46875 2.703125 0.96875q2.203125 0.5 3.015625 0.875q1.1875 0.546875 1.75 1.390625q0.578125 0.828125 0.578125 1.921875q0 1.09375 -0.625 2.0625q-0.625 0.953125 -1.796875 1.484375q-1.15625 0.53125 -2.609375 0.53125q-1.84375 0 -3.09375 -0.53125q-1.25 -0.546875 -1.96875 -1.625q-0.703125 -1.078125 -0.734375 -2.453125zm12.5061035 -2.25q0 -3.390625 1.8125 -5.296875q1.828125 -1.921875 4.703125 -1.921875q1.875 0 3.390625 0.90625q1.515625 0.890625 2.296875 2.5q0.796875 1.609375 0.796875 3.65625q0 2.0625 -0.84375 3.703125q-0.828125 1.625 -2.359375 2.46875q-1.53125 0.84375 -3.296875 0.84375q-1.921875 0 -3.4375 -0.921875q-1.5 -0.9375 -2.28125 -2.53125q-0.78125 -1.609375 -0.78125 -3.40625zm1.859375 0.03125q0 2.453125 1.3125 3.875q1.328125 1.40625 3.3125 1.40625q2.03125 0 3.34375 -1.421875q1.3125 -1.4375 1.3125 -4.0625q0 -1.65625 -0.5625 -2.890625q-0.546875 -1.234375 -1.640625 -1.921875q-1.078125 -0.6875 -2.421875 -0.6875q-1.90625 0 -3.28125 1.3125q-1.375 1.3125 -1.375 4.390625zm21.819702 5.09375l0.234375 1.484375q-0.703125 0.140625 -1.265625 0.140625q-0.90625 0 -1.40625 -0.28125q-0.5 -0.296875 -0.703125 -0.75q-0.203125 -0.46875 -0.203125 -1.984375l0 -5.65625l-1.234375 0l0 -1.3125l1.234375 0l0 -2.4375l1.65625 -1.0l0 3.4375l1.6875 0l0 1.3125l-1.6875 0l0 5.75q0 0.71875 0.078125 0.921875q0.09375 0.203125 0.296875 0.328125q0.203125 0.125 0.578125 0.125q0.265625 0 0.734375 -0.078125zm0.9020996 -3.421875q0 -2.734375 1.53125 -4.0625q1.265625 -1.09375 3.09375 -1.09375q2.03125 0 3.3125 1.34375q1.296875 1.328125 1.296875 3.671875q0 1.90625 -0.578125 3.0q-0.5625 1.078125 -1.65625 1.6875q-1.078125 0.59375 -2.375 0.59375q-2.0625 0 -3.34375 -1.328125q-1.28125 -1.328125 -1.28125 -3.8125zm1.71875 0q0 1.890625 0.828125 2.828125q0.828125 0.9375 2.078125 0.9375q1.25 0 2.0625 -0.9375q0.828125 -0.953125 0.828125 -2.890625q0 -1.828125 -0.828125 -2.765625q-0.828125 -0.9375 -2.0625 -0.9375q-1.25 0 -2.078125 0.9375q-0.828125 0.9375 -0.828125 2.828125zm9.297607 4.921875l0 -13.59375l1.671875 0l0 7.75l3.953125 -4.015625l2.15625 0l-3.765625 3.65625l4.140625 6.203125l-2.0625 0l-3.25 -5.03125l-1.171875 1.125l0 3.90625l-1.671875 0zm16.0625 -3.171875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm9.110107 5.875l0 -9.859375l1.5 0l0 1.40625q1.09375 -1.625 3.140625 -1.625q0.890625 0 1.640625 0.328125q0.75 0.3125 1.109375 0.84375q0.375 0.515625 0.53125 1.21875q0.09375 0.46875 0.09375 1.625l0 6.0625l-1.671875 0l0 -6.0q0 -1.015625 -0.203125 -1.515625q-0.1875 -0.515625 -0.6875 -0.8125q-0.5 -0.296875 -1.171875 -0.296875q-1.0625 0 -1.84375 0.671875q-0.765625 0.671875 -0.765625 2.578125l0 5.375l-1.671875 0z" fill-rule="nonzero"></path><path fill="#bf9000" d="m550.4829 1121.1864l156.3465 0l0 76.81885l-156.3465 0z" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m550.4829 1121.1864l156.3465 0l0 76.81885l-156.3465 0z" fill-rule="nonzero"></path><path fill="#000000" d="m571.6152 1166.5157l0 -13.59375l1.8125 0l0 13.59375l-1.8125 0zm11.058289 0l0 -1.25q-0.9375 1.46875 -2.75 1.46875q-1.171875 0 -2.171875 -0.640625q-0.984375 -0.65625 -1.53125 -1.8125q-0.53125 -1.171875 -0.53125 -2.6875q0 -1.46875 0.484375 -2.671875q0.5 -1.203125 1.46875 -1.84375q0.984375 -0.640625 2.203125 -0.640625q0.890625 0 1.578125 0.375q0.703125 0.375 1.140625 0.984375l0 -4.875l1.65625 0l0 13.59375l-1.546875 0zm-5.28125 -4.921875q0 1.890625 0.796875 2.828125q0.8125 0.9375 1.890625 0.9375q1.09375 0 1.859375 -0.890625q0.765625 -0.890625 0.765625 -2.734375q0 -2.015625 -0.78125 -2.953125q-0.78125 -0.953125 -1.921875 -0.953125q-1.109375 0 -1.859375 0.90625q-0.75 0.90625 -0.75 2.859375zm16.016357 1.75l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm9.110107 5.875l0 -9.859375l1.5 0l0 1.40625q1.09375 -1.625 3.140625 -1.625q0.890625 0 1.640625 0.328125q0.75 0.3125 1.109375 0.84375q0.375 0.515625 0.53125 1.21875q0.09375 0.46875 0.09375 1.625l0 6.0625l-1.671875 0l0 -6.0q0 -1.015625 -0.203125 -1.515625q-0.1875 -0.515625 -0.6875 -0.8125q-0.5 -0.296875 -1.171875 -0.296875q-1.0625 0 -1.84375 0.671875q-0.765625 0.671875 -0.765625 2.578125l0 5.375l-1.671875 0zm14.031921 -1.5l0.234375 1.484375q-0.703125 0.140625 -1.265625 0.140625q-0.90625 0 -1.40625 -0.28125q-0.5 -0.296875 -0.703125 -0.75q-0.203125 -0.46875 -0.203125 -1.984375l0 -5.65625l-1.234375 0l0 -1.3125l1.234375 0l0 -2.4375l1.65625 -1.0l0 3.4375l1.6875 0l0 1.3125l-1.6875 0l0 5.75q0 0.71875 0.078125 0.921875q0.09375 0.203125 0.296875 0.328125q0.203125 0.125 0.578125 0.125q0.265625 0 0.734375 -0.078125zm1.5427246 -10.1875l0 -1.90625l1.671875 0l0 1.90625l-1.671875 0zm0 11.6875l0 -9.859375l1.671875 0l0 9.859375l-1.671875 0zm4.5354004 0l0 -8.546875l-1.484375 0l0 -1.3125l1.484375 0l0 -1.046875q0 -0.984375 0.171875 -1.46875q0.234375 -0.65625 0.84375 -1.046875q0.609375 -0.40625 1.703125 -0.40625q0.703125 0 1.5625 0.15625l-0.25 1.46875q-0.515625 -0.09375 -0.984375 -0.09375q-0.765625 0 -1.078125 0.328125q-0.3125 0.3125 -0.3125 1.203125l0 0.90625l1.921875 0l0 1.3125l-1.921875 0l0 8.546875l-1.65625 0zm4.6989746 3.796875l-0.171875 -1.5625q0.546875 0.140625 0.953125 0.140625q0.546875 0 0.875 -0.1875q0.34375 -0.1875 0.5625 -0.515625q0.15625 -0.25 0.5 -1.25q0.046875 -0.140625 0.15625 -0.40625l-3.734375 -9.875l1.796875 0l2.046875 5.71875q0.40625 1.078125 0.71875 2.28125q0.28125 -1.15625 0.6875 -2.25l2.09375 -5.75l1.671875 0l-3.75 10.03125q-0.59375 1.625 -0.9375 2.234375q-0.4375 0.828125 -1.015625 1.203125q-0.578125 0.390625 -1.375 0.390625q-0.484375 0 -1.078125 -0.203125zm21.042664 -3.796875l0 -1.453125q-1.140625 1.671875 -3.125 1.671875q-0.859375 0 -1.625 -0.328125q-0.75 -0.34375 -1.125 -0.84375q-0.359375 -0.5 -0.515625 -1.234375q-0.09375 -0.5 -0.09375 -1.5625l0 -6.109375l1.671875 0l0 5.46875q0 1.3125 0.09375 1.765625q0.15625 0.65625 0.671875 1.03125q0.515625 0.375 1.265625 0.375q0.75 0 1.40625 -0.375q0.65625 -0.390625 0.921875 -1.046875q0.28125 -0.671875 0.28125 -1.9375l0 -5.28125l1.671875 0l0 9.859375l-1.5 0zm3.2507324 -2.9375l1.65625 -0.265625q0.140625 1.0 0.765625 1.53125q0.640625 0.515625 1.78125 0.515625q1.15625 0 1.703125 -0.46875q0.5625 -0.46875 0.5625 -1.09375q0 -0.5625 -0.484375 -0.890625q-0.34375 -0.21875 -1.703125 -0.5625q-1.84375 -0.46875 -2.5625 -0.796875q-0.703125 -0.34375 -1.078125 -0.9375q-0.359375 -0.609375 -0.359375 -1.328125q0 -0.65625 0.296875 -1.21875q0.3125 -0.5625 0.828125 -0.9375q0.390625 -0.28125 1.0625 -0.484375q0.671875 -0.203125 1.4375 -0.203125q1.171875 0 2.046875 0.34375q0.875 0.328125 1.28125 0.90625q0.421875 0.5625 0.578125 1.515625l-1.625 0.21875q-0.109375 -0.75 -0.65625 -1.171875q-0.53125 -0.4375 -1.5 -0.4375q-1.15625 0 -1.640625 0.390625q-0.484375 0.375 -0.484375 0.875q0 0.328125 0.203125 0.59375q0.203125 0.265625 0.640625 0.4375q0.25 0.09375 1.46875 0.4375q1.765625 0.46875 2.46875 0.765625q0.703125 0.296875 1.09375 0.875q0.40625 0.578125 0.40625 1.4375q0 0.828125 -0.484375 1.578125q-0.484375 0.734375 -1.40625 1.140625q-0.921875 0.390625 -2.078125 0.390625q-1.921875 0 -2.9375 -0.796875q-1.0 -0.796875 -1.28125 -2.359375zm16.75 -0.234375l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm9.094421 5.875l0 -9.859375l1.5 0l0 1.5q0.578125 -1.046875 1.0625 -1.375q0.484375 -0.34375 1.078125 -0.34375q0.84375 0 1.71875 0.546875l-0.578125 1.546875q-0.609375 -0.359375 -1.234375 -0.359375q-0.546875 0 -0.984375 0.328125q-0.421875 0.328125 -0.609375 0.90625q-0.28125 0.890625 -0.28125 1.953125l0 5.15625l-1.671875 0z" fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0" d="m490.2467 1036.1575l0 42.51465l138.42523 0l0 42.52478" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m490.2467 1036.1575l0 42.51465l138.42523 0l0 39.097656" fill-rule="evenodd"></path><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m628.67194 1117.7698l-1.1246338 -1.1246338l1.1246338 3.0898438l1.1245728 -3.0898438z" fill-rule="evenodd"></path><path fill="#000000" fill-opacity="0.0" d="m767.31494 1036.1575l0 42.51465l-138.64563 0l0 42.52478" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m767.31494 1036.1575l0 42.51465l-138.64563 0l0 39.097656" fill-rule="evenodd"></path><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m628.6693 1117.7698l-1.1246338 -1.1246338l1.1246338 3.0898438l1.1245728 -3.0898438z" fill-rule="evenodd"></path><path fill="#000000" fill-opacity="0.0" d="m623.2572 704.6588l4.0 47.905518" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m623.2572 704.6588l3.5007324 41.92633" fill-rule="evenodd"></path><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m625.11194 746.72253l2.0236206 4.3849487l1.2684326 -4.65979z" fill-rule="evenodd"></path><path fill="#000000" fill-opacity="0.0" d="m628.6562 1198.0052l0 25.002075l385.45148 0l0 -553.4745l-312.66412 0" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m628.6562 1198.0052l0 25.002075l385.45148 0l0 -553.4745l-309.237 0" fill-rule="evenodd"></path><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m704.87067 669.53284l1.1245728 -1.1246338l-3.0897827 1.1246338l3.0897827 1.1245728z" fill-rule="evenodd"></path><path fill="#000000" fill-opacity="0.0" d="m701.4305 651.92975l522.5573 3.0775146l0 -581.44293l-519.1407 0" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m701.4304 651.92975l522.5575 3.0775146l0 -581.44293l-515.71375 0" fill-rule="evenodd"></path><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m708.2742 73.56431l1.1246338 -1.124588l-3.0897827 1.124588l3.0897827 1.1245804z" fill-rule="evenodd"></path><path fill="#000000" fill-opacity="0.0" d="m808.0315 611.3517l466.8661 0l0 43.653564l-466.8661 0z" fill-rule="nonzero"></path><path fill="#000000" d="m818.5315 638.2717l0 -13.59375l6.03125 0q1.8125 0 2.75 0.359375q0.953125 0.359375 1.515625 1.296875q0.5625 0.921875 0.5625 2.046875q0 1.453125 -0.9375 2.453125q-0.921875 0.984375 -2.890625 1.25q0.71875 0.34375 1.09375 0.671875q0.78125 0.734375 1.484375 1.8125l2.375 3.703125l-2.265625 0l-1.796875 -2.828125q-0.796875 -1.21875 -1.3125 -1.875q-0.5 -0.65625 -0.90625 -0.90625q-0.40625 -0.265625 -0.8125 -0.359375q-0.3125 -0.078125 -1.015625 -0.078125l-2.078125 0l0 6.046875l-1.796875 0zm1.796875 -7.59375l3.859375 0q1.234375 0 1.921875 -0.25q0.703125 -0.265625 1.0625 -0.828125q0.375 -0.5625 0.375 -1.21875q0 -0.96875 -0.703125 -1.578125q-0.703125 -0.625 -2.21875 -0.625l-4.296875 0l0 4.5zm18.176086 4.421875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm15.500732 5.875l0 -1.25q-0.9375 1.46875 -2.75 1.46875q-1.171875 0 -2.171875 -0.640625q-0.984375 -0.65625 -1.53125 -1.8125q-0.53125 -1.171875 -0.53125 -2.6875q0 -1.46875 0.484375 -2.671875q0.5 -1.203125 1.46875 -1.84375q0.984375 -0.640625 2.203125 -0.640625q0.890625 0 1.578125 0.375q0.703125 0.375 1.140625 0.984375l0 -4.875l1.65625 0l0 13.59375l-1.546875 0zm-5.28125 -4.921875q0 1.890625 0.796875 2.828125q0.8125 0.9375 1.890625 0.9375q1.09375 0 1.859375 -0.890625q0.765625 -0.890625 0.765625 -2.734375q0 -2.015625 -0.78125 -2.953125q-0.78125 -0.953125 -1.921875 -0.953125q-1.109375 0 -1.859375 0.90625q-0.75 0.90625 -0.75 2.859375zm9.281921 -6.765625l0 -1.90625l1.671875 0l0 1.90625l-1.671875 0zm0 11.6875l0 -9.859375l1.671875 0l0 9.859375l-1.671875 0zm4.1135864 0l0 -9.859375l1.5 0l0 1.5q0.578125 -1.046875 1.0625 -1.375q0.484375 -0.34375 1.078125 -0.34375q0.84375 0 1.71875 0.546875l-0.578125 1.546875q-0.609375 -0.359375 -1.234375 -0.359375q-0.546875 0 -0.984375 0.328125q-0.421875 0.328125 -0.609375 0.90625q-0.28125 0.890625 -0.28125 1.953125l0 5.15625l-1.671875 0zm12.9783325 -3.171875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm15.547546 2.265625l1.640625 0.21875q-0.265625 1.6875 -1.375 2.65625q-1.109375 0.953125 -2.734375 0.953125q-2.015625 0 -3.25 -1.3125q-1.21875 -1.328125 -1.21875 -3.796875q0 -1.59375 0.515625 -2.78125q0.53125 -1.203125 1.609375 -1.796875q1.09375 -0.609375 2.359375 -0.609375q1.609375 0 2.625 0.8125q1.015625 0.8125 1.3125 2.3125l-1.625 0.25q-0.234375 -1.0 -0.828125 -1.5q-0.59375 -0.5 -1.421875 -0.5q-1.265625 0 -2.0625 0.90625q-0.78125 0.90625 -0.78125 2.859375q0 1.984375 0.765625 2.890625q0.765625 0.890625 1.984375 0.890625q0.984375 0 1.640625 -0.59375q0.65625 -0.609375 0.84375 -1.859375zm6.546875 2.109375l0.234375 1.484375q-0.703125 0.140625 -1.265625 0.140625q-0.90625 0 -1.40625 -0.28125q-0.5 -0.296875 -0.703125 -0.75q-0.203125 -0.46875 -0.203125 -1.984375l0 -5.65625l-1.234375 0l0 -1.3125l1.234375 0l0 -2.4375l1.65625 -1.0l0 3.4375l1.6875 0l0 1.3125l-1.6875 0l0 5.75q0 0.71875 0.078125 0.921875q0.09375 0.203125 0.296875 0.328125q0.203125 0.125 0.578125 0.125q0.265625 0 0.734375 -0.078125zm10.366638 0l0.234375 1.484375q-0.703125 0.140625 -1.265625 0.140625q-0.90625 0 -1.40625 -0.28125q-0.5 -0.296875 -0.703125 -0.75q-0.203125 -0.46875 -0.203125 -1.984375l0 -5.65625l-1.234375 0l0 -1.3125l1.234375 0l0 -2.4375l1.65625 -1.0l0 3.4375l1.6875 0l0 1.3125l-1.6875 0l0 5.75q0 0.71875 0.078125 0.921875q0.09375 0.203125 0.296875 0.328125q0.203125 0.125 0.578125 0.125q0.265625 0 0.734375 -0.078125zm0.9020386 -3.421875q0 -2.734375 1.53125 -4.0625q1.265625 -1.09375 3.09375 -1.09375q2.03125 0 3.3125 1.34375q1.296875 1.328125 1.296875 3.671875q0 1.90625 -0.578125 3.0q-0.5625 1.078125 -1.65625 1.6875q-1.078125 0.59375 -2.375 0.59375q-2.0625 0 -3.34375 -1.328125q-1.28125 -1.328125 -1.28125 -3.8125zm1.71875 0q0 1.890625 0.828125 2.828125q0.828125 0.9375 2.078125 0.9375q1.25 0 2.0625 -0.9375q0.828125 -0.953125 0.828125 -2.890625q0 -1.828125 -0.828125 -2.765625q-0.828125 -0.9375 -2.0625 -0.9375q-1.25 0 -2.078125 0.9375q-0.828125 0.9375 -0.828125 2.828125zm13.215271 5.15625l3.9375 -14.0625l1.34375 0l-3.9375 14.0625l-1.34375 0zm8.261414 -0.234375l-3.015625 -9.859375l1.71875 0l1.5625 5.6875l0.59375 2.125q0.03125 -0.15625 0.5 -2.03125l1.578125 -5.78125l1.71875 0l1.46875 5.71875l0.484375 1.890625l0.578125 -1.90625l1.6875 -5.703125l1.625 0l-3.078125 9.859375l-1.734375 0l-1.578125 -5.90625l-0.375 -1.671875l-2.0 7.578125l-1.734375 0zm18.394836 -3.171875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm9.078857 5.875l0 -13.59375l1.671875 0l0 13.59375l-1.671875 0zm10.613586 -3.609375l1.640625 0.21875q-0.265625 1.6875 -1.375 2.65625q-1.109375 0.953125 -2.734375 0.953125q-2.015625 0 -3.25 -1.3125q-1.21875 -1.328125 -1.21875 -3.796875q0 -1.59375 0.515625 -2.78125q0.53125 -1.203125 1.609375 -1.796875q1.09375 -0.609375 2.359375 -0.609375q1.609375 0 2.625 0.8125q1.015625 0.8125 1.3125 2.3125l-1.625 0.25q-0.234375 -1.0 -0.828125 -1.5q-0.59375 -0.5 -1.421875 -0.5q-1.265625 0 -2.0625 0.90625q-0.78125 0.90625 -0.78125 2.859375q0 1.984375 0.765625 2.890625q0.765625 0.890625 1.984375 0.890625q0.984375 0 1.640625 -0.59375q0.65625 -0.609375 0.84375 -1.859375zm2.265625 -1.3125q0 -2.734375 1.53125 -4.0625q1.265625 -1.09375 3.09375 -1.09375q2.03125 0 3.3125 1.34375q1.296875 1.328125 1.296875 3.671875q0 1.90625 -0.578125 3.0q-0.5625 1.078125 -1.65625 1.6875q-1.078125 0.59375 -2.375 0.59375q-2.0625 0 -3.34375 -1.328125q-1.28125 -1.328125 -1.28125 -3.8125zm1.71875 0q0 1.890625 0.828125 2.828125q0.828125 0.9375 2.078125 0.9375q1.25 0 2.0625 -0.9375q0.828125 -0.953125 0.828125 -2.890625q0 -1.828125 -0.828125 -2.765625q-0.828125 -0.9375 -2.0625 -0.9375q-1.25 0 -2.078125 0.9375q-0.828125 0.9375 -0.828125 2.828125zm9.281921 4.921875l0 -9.859375l1.5 0l0 1.390625q0.453125 -0.71875 1.21875 -1.15625q0.78125 -0.453125 1.765625 -0.453125q1.09375 0 1.796875 0.453125q0.703125 0.453125 0.984375 1.28125q1.171875 -1.734375 3.046875 -1.734375q1.46875 0 2.25 0.8125q0.796875 0.8125 0.796875 2.5l0 6.765625l-1.671875 0l0 -6.203125q0 -1.0 -0.15625 -1.4375q-0.15625 -0.453125 -0.59375 -0.71875q-0.421875 -0.265625 -1.0 -0.265625q-1.03125 0 -1.71875 0.6875q-0.6875 0.6875 -0.6875 2.21875l0 5.71875l-1.671875 0l0 -6.40625q0 -1.109375 -0.40625 -1.65625q-0.40625 -0.5625 -1.34375 -0.5625q-0.703125 0 -1.3125 0.375q-0.59375 0.359375 -0.859375 1.078125q-0.265625 0.71875 -0.265625 2.0625l0 5.109375l-1.671875 0zm22.290833 -3.171875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm14.293396 9.65625l0 -13.640625l1.53125 0l0 1.28125q0.53125 -0.75 1.203125 -1.125q0.6875 -0.375 1.640625 -0.375q1.265625 0 2.234375 0.65625q0.96875 0.640625 1.453125 1.828125q0.5 1.1875 0.5 2.59375q0 1.515625 -0.546875 2.734375q-0.546875 1.203125 -1.578125 1.84375q-1.03125 0.640625 -2.171875 0.640625q-0.84375 0 -1.515625 -0.34375q-0.65625 -0.359375 -1.078125 -0.890625l0 4.796875l-1.671875 0zm1.515625 -8.65625q0 1.90625 0.765625 2.8125q0.78125 0.90625 1.875 0.90625q1.109375 0 1.890625 -0.9375q0.796875 -0.9375 0.796875 -2.921875q0 -1.875 -0.78125 -2.8125q-0.765625 -0.9375 -1.84375 -0.9375q-1.0625 0 -1.890625 1.0q-0.8125 1.0 -0.8125 2.890625zm15.297607 3.65625q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm3.7819214 5.75l1.609375 0.25q0.109375 0.75 0.578125 1.09375q0.609375 0.453125 1.6875 0.453125q1.171875 0 1.796875 -0.46875q0.62506104 -0.453125 0.85943604 -1.28125q0.125 -0.515625 0.109375 -2.15625q-1.093811 1.296875 -2.718811 1.296875q-2.03125 0 -3.15625 -1.46875q-1.109375 -1.46875 -1.109375 -3.515625q0 -1.40625 0.515625 -2.59375q0.515625 -1.203125 1.484375 -1.84375q0.96875 -0.65625 2.265625 -0.65625q1.75 0 2.875061 1.40625l0 -1.1875l1.546875 0l0 8.515625q0 2.3125 -0.46875 3.265625q-0.46875 0.96875 -1.484375 1.515625q-1.015686 0.5625 -2.500061 0.5625q-1.765625 0 -2.859375 -0.796875q-1.078125 -0.796875 -1.03125 -2.390625zm1.375 -5.921875q0 1.953125 0.765625 2.84375q0.78125 0.890625 1.9375 0.890625q1.140625 0 1.921936 -0.890625q0.78125 -0.890625 0.78125 -2.78125q0 -1.8125 -0.8125 -2.71875q-0.79693604 -0.921875 -1.921936 -0.921875q-1.109375 0 -1.890625 0.90625q-0.78125 0.890625 -0.78125 2.671875zm16.047668 1.9375l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm16.12146 5.875l-3.015625 -9.859375l1.71875 0l1.5625 5.6875l0.59375 2.125q0.03125 -0.15625 0.5 -2.03125l1.578125 -5.78125l1.71875 0l1.46875 5.71875l0.484375 1.890625l0.578125 -1.90625l1.6875 -5.703125l1.625 0l-3.078125 9.859375l-1.734375 0l-1.578125 -5.90625l-0.375 -1.671875l-2.0 7.578125l-1.734375 0zm11.6604 -11.6875l0 -1.90625l1.671875 0l0 1.90625l-1.671875 0zm0 11.6875l0 -9.859375l1.671875 0l0 9.859375l-1.671875 0zm7.7855225 -1.5l0.234375 1.484375q-0.703125 0.140625 -1.265625 0.140625q-0.90625 0 -1.40625 -0.28125q-0.5 -0.296875 -0.703125 -0.75q-0.203125 -0.46875 -0.203125 -1.984375l0 -5.65625l-1.234375 0l0 -1.3125l1.234375 0l0 -2.4375l1.65625 -1.0l0 3.4375l1.6875 0l0 1.3125l-1.6875 0l0 5.75q0 0.71875 0.078125 0.921875q0.09375 0.203125 0.296875 0.328125q0.203125 0.125 0.578125 0.125q0.265625 0 0.734375 -0.078125zm1.5270996 1.5l0 -13.59375l1.671875 0l0 4.875q1.171875 -1.359375 2.953125 -1.359375q1.09375 0 1.890625 0.4375q0.8125 0.421875 1.15625 1.1875q0.359375 0.765625 0.359375 2.203125l0 6.25l-1.671875 0l0 -6.25q0 -1.25 -0.546875 -1.8125q-0.546875 -0.578125 -1.53125 -0.578125q-0.75 0 -1.40625 0.390625q-0.640625 0.375 -0.921875 1.046875q-0.28125 0.65625 -0.28125 1.8125l0 5.390625l-1.671875 0zm14.887085 -2.9375l1.65625 -0.265625q0.140625 1.0 0.765625 1.53125q0.640625 0.515625 1.78125 0.515625q1.15625 0 1.703125 -0.46875q0.5625 -0.46875 0.5625 -1.09375q0 -0.5625 -0.484375 -0.890625q-0.34375 -0.21875 -1.703125 -0.5625q-1.84375 -0.46875 -2.5625 -0.796875q-0.703125 -0.34375 -1.078125 -0.9375q-0.359375 -0.609375 -0.359375 -1.328125q0 -0.65625 0.296875 -1.21875q0.3125 -0.5625 0.828125 -0.9375q0.390625 -0.28125 1.0625 -0.484375q0.671875 -0.203125 1.4375 -0.203125q1.171875 0 2.046875 0.34375q0.875 0.328125 1.28125 0.90625q0.421875 0.5625 0.578125 1.515625l-1.625 0.21875q-0.109375 -0.75 -0.65625 -1.171875q-0.53125 -0.4375 -1.5 -0.4375q-1.15625 0 -1.640625 0.390625q-0.484375 0.375 -0.484375 0.875q0 0.328125 0.203125 0.59375q0.203125 0.265625 0.640625 0.4375q0.25 0.09375 1.46875 0.4375q1.765625 0.46875 2.46875 0.765625q0.703125 0.296875 1.09375 0.875q0.40625 0.578125 0.40625 1.4375q0 0.828125 -0.484375 1.578125q-0.484375 0.734375 -1.40625 1.140625q-0.921875 0.390625 -2.078125 0.390625q-1.921875 0 -2.9375 -0.796875q-1.0 -0.796875 -1.28125 -2.359375zm16.75 -0.234375l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm8.438232 2.9375l1.65625 -0.265625q0.140625 1.0 0.765625 1.53125q0.640625 0.515625 1.78125 0.515625q1.15625 0 1.703125 -0.46875q0.5625 -0.46875 0.5625 -1.09375q0 -0.5625 -0.484375 -0.890625q-0.34375 -0.21875 -1.703125 -0.5625q-1.84375 -0.46875 -2.5625 -0.796875q-0.703125 -0.34375 -1.078125 -0.9375q-0.359375 -0.609375 -0.359375 -1.328125q0 -0.65625 0.296875 -1.21875q0.3125 -0.5625 0.828125 -0.9375q0.390625 -0.28125 1.0625 -0.484375q0.671875 -0.203125 1.4375 -0.203125q1.171875 0 2.046875 0.34375q0.875 0.328125 1.28125 0.90625q0.421875 0.5625 0.578125 1.515625l-1.625 0.21875q-0.109375 -0.75 -0.65625 -1.171875q-0.53125 -0.4375 -1.5 -0.4375q-1.15625 0 -1.640625 0.390625q-0.484375 0.375 -0.484375 0.875q0 0.328125 0.203125 0.59375q0.203125 0.265625 0.640625 0.4375q0.25 0.09375 1.46875 0.4375q1.765625 0.46875 2.46875 0.765625q0.703125 0.296875 1.09375 0.875q0.40625 0.578125 0.40625 1.4375q0 0.828125 -0.484375 1.578125q-0.484375 0.734375 -1.40625 1.140625q-0.921875 0.390625 -2.078125 0.390625q-1.921875 0 -2.9375 -0.796875q-1.0 -0.796875 -1.28125 -2.359375zm9.328125 0l1.65625 -0.265625q0.140625 1.0 0.765625 1.53125q0.640625 0.515625 1.78125 0.515625q1.15625 0 1.703125 -0.46875q0.5625 -0.46875 0.5625 -1.09375q0 -0.5625 -0.484375 -0.890625q-0.34375 -0.21875 -1.703125 -0.5625q-1.84375 -0.46875 -2.5625 -0.796875q-0.703125 -0.34375 -1.078125 -0.9375q-0.359375 -0.609375 -0.359375 -1.328125q0 -0.65625 0.296875 -1.21875q0.3125 -0.5625 0.828125 -0.9375q0.390625 -0.28125 1.0625 -0.484375q0.671875 -0.203125 1.4375 -0.203125q1.171875 0 2.046875 0.34375q0.875 0.328125 1.28125 0.90625q0.421875 0.5625 0.578125 1.515625l-1.625 0.21875q-0.109375 -0.75 -0.65625 -1.171875q-0.53125 -0.4375 -1.5 -0.4375q-1.15625 0 -1.640625 0.390625q-0.484375 0.375 -0.484375 0.875q0 0.328125 0.203125 0.59375q0.203125 0.265625 0.640625 0.4375q0.25 0.09375 1.46875 0.4375q1.765625 0.46875 2.46875 0.765625q0.703125 0.296875 1.09375 0.875q0.40625 0.578125 0.40625 1.4375q0 0.828125 -0.484375 1.578125q-0.484375 0.734375 -1.40625 1.140625q-0.921875 0.390625 -2.078125 0.390625q-1.921875 0 -2.9375 -0.796875q-1.0 -0.796875 -1.28125 -2.359375zm10.015625 -8.75l0 -1.90625l1.671875 0l0 1.90625l-1.671875 0zm0 11.6875l0 -9.859375l1.671875 0l0 9.859375l-1.671875 0zm3.5042725 -4.921875q0 -2.734375 1.53125 -4.0625q1.265625 -1.09375 3.09375 -1.09375q2.03125 0 3.3125 1.34375q1.296875 1.328125 1.296875 3.671875q0 1.90625 -0.578125 3.0q-0.5625 1.078125 -1.65625 1.6875q-1.078125 0.59375 -2.375 0.59375q-2.0625 0 -3.34375 -1.328125q-1.28125 -1.328125 -1.28125 -3.8125zm1.71875 0q0 1.890625 0.828125 2.828125q0.828125 0.9375 2.078125 0.9375q1.25 0 2.0625 -0.9375q0.828125 -0.953125 0.828125 -2.890625q0 -1.828125 -0.828125 -2.765625q-0.828125 -0.9375 -2.0625 -0.9375q-1.25 0 -2.078125 0.9375q-0.828125 0.9375 -0.828125 2.828125zm9.281982 4.921875l0 -9.859375l1.5 0l0 1.40625q1.09375 -1.625 3.140625 -1.625q0.890625 0 1.640625 0.328125q0.75 0.3125 1.109375 0.84375q0.375 0.515625 0.53125 1.21875q0.09375 0.46875 0.09375 1.625l0 6.0625l-1.671875 0l0 -6.0q0 -1.015625 -0.203125 -1.515625q-0.1875 -0.515625 -0.6875 -0.8125q-0.5 -0.296875 -1.171875 -0.296875q-1.0625 0 -1.84375 0.671875q-0.765625 0.671875 -0.765625 2.578125l0 5.375l-1.671875 0zm19.21521 -1.5l0.234375 1.484375q-0.703125 0.140625 -1.265625 0.140625q-0.90625 0 -1.40625 -0.28125q-0.5 -0.296875 -0.703125 -0.75q-0.203125 -0.46875 -0.203125 -1.984375l0 -5.65625l-1.234375 0l0 -1.3125l1.234375 0l0 -2.4375l1.65625 -1.0l0 3.4375l1.6875 0l0 1.3125l-1.6875 0l0 5.75q0 0.71875 0.078125 0.921875q0.09375 0.203125 0.296875 0.328125q0.203125 0.125 0.578125 0.125q0.265625 0 0.734375 -0.078125zm0.9020996 -3.421875q0 -2.734375 1.53125 -4.0625q1.265625 -1.09375 3.09375 -1.09375q2.03125 0 3.3125 1.34375q1.296875 1.328125 1.296875 3.671875q0 1.90625 -0.578125 3.0q-0.5625 1.078125 -1.65625 1.6875q-1.078125 0.59375 -2.375 0.59375q-2.0625 0 -3.34375 -1.328125q-1.28125 -1.328125 -1.28125 -3.8125zm1.71875 0q0 1.890625 0.828125 2.828125q0.828125 0.9375 2.078125 0.9375q1.25 0 2.0625 -0.9375q0.828125 -0.953125 0.828125 -2.890625q0 -1.828125 -0.828125 -2.765625q-0.828125 -0.9375 -2.0625 -0.9375q-1.25 0 -2.078125 0.9375q-0.828125 0.9375 -0.828125 2.828125zm9.297607 4.921875l0 -13.59375l1.671875 0l0 7.75l3.953125 -4.015625l2.15625 0l-3.765625 3.65625l4.140625 6.203125l-2.0625 0l-3.25 -5.03125l-1.171875 1.125l0 3.90625l-1.671875 0zm16.0625 -3.171875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm9.110107 5.875l0 -9.859375l1.5 0l0 1.40625q1.09375 -1.625 3.140625 -1.625q0.890625 0 1.640625 0.328125q0.75 0.3125 1.109375 0.84375q0.375 0.515625 0.53125 1.21875q0.09375 0.46875 0.09375 1.625l0 6.0625l-1.671875 0l0 -6.0q0 -1.015625 -0.203125 -1.515625q-0.1875 -0.515625 -0.6875 -0.8125q-0.5 -0.296875 -1.171875 -0.296875q-1.0625 0 -1.84375 0.671875q-0.765625 0.671875 -0.765625 2.578125l0 5.375l-1.671875 0z" fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0" d="m767.6221 103.74803l179.27557 0l0 43.65355l-179.27557 0z" fill-rule="nonzero"></path><path fill="#000000" d="m778.1221 130.66803l0 -13.59375l6.03125 0q1.8125 0 2.75 0.359375q0.953125 0.359375 1.515625 1.296875q0.5625 0.921875 0.5625 2.046875q0 1.453125 -0.9375 2.453125q-0.921875 0.984375 -2.890625 1.25q0.71875 0.34375 1.09375 0.671875q0.78125 0.734375 1.484375 1.8125l2.375 3.703125l-2.265625 0l-1.796875 -2.828125q-0.796875 -1.21875 -1.3125 -1.875q-0.5 -0.65625 -0.90625 -0.90625q-0.40625 -0.265625 -0.8125 -0.359375q-0.3125 -0.078125 -1.015625 -0.078125l-2.078125 0l0 6.046875l-1.796875 0zm1.796875 -7.59375l3.859375 0q1.234375 0 1.921875 -0.25q0.703125 -0.265625 1.0625 -0.828125q0.375 -0.5625 0.375 -1.21875q0 -0.96875 -0.703125 -1.578125q-0.703125 -0.625 -2.21875 -0.625l-4.296875 0l0 4.5zm18.176025 4.421875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm8.438232 2.9375l1.65625 -0.265625q0.140625 1.0 0.765625 1.53125q0.640625 0.515625 1.78125 0.515625q1.15625 0 1.703125 -0.46875q0.5625 -0.46875 0.5625 -1.09375q0 -0.5625 -0.484375 -0.890625q-0.34375 -0.21875 -1.703125 -0.5625q-1.84375 -0.46875 -2.5625 -0.796875q-0.703125 -0.34375 -1.078125 -0.9375q-0.359375 -0.609375 -0.359375 -1.328125q0 -0.65625 0.296875 -1.21875q0.3125 -0.5625 0.828125 -0.9375q0.390625 -0.28125 1.0625 -0.484375q0.671875 -0.203125 1.4375 -0.203125q1.171875 0 2.046875 0.34375q0.875 0.328125 1.28125 0.90625q0.421875 0.5625 0.578125 1.515625l-1.625 0.21875q-0.109375 -0.75 -0.65625 -1.171875q-0.53125 -0.4375 -1.5 -0.4375q-1.15625 0 -1.640625 0.390625q-0.484375 0.375 -0.484375 0.875q0 0.328125 0.203125 0.59375q0.203125 0.265625 0.640625 0.4375q0.25 0.09375 1.46875 0.4375q1.765625 0.46875 2.46875 0.765625q0.703125 0.296875 1.09375 0.875q0.40625 0.578125 0.40625 1.4375q0 0.828125 -0.484375 1.578125q-0.484375 0.734375 -1.40625 1.140625q-0.921875 0.390625 -2.078125 0.390625q-1.921875 0 -2.9375 -0.796875q-1.0 -0.796875 -1.28125 -2.359375zm9.375 -1.984375q0 -2.734375 1.53125 -4.0625q1.265625 -1.09375 3.09375 -1.09375q2.03125 0 3.3125 1.34375q1.296875 1.328125 1.296875 3.671875q0 1.90625 -0.578125 3.0q-0.5625 1.078125 -1.65625 1.6875q-1.078125 0.59375 -2.375 0.59375q-2.0625 0 -3.34375 -1.328125q-1.28125 -1.328125 -1.28125 -3.8125zm1.71875 0q0 1.890625 0.828125 2.828125q0.828125 0.9375 2.078125 0.9375q1.25 0 2.0625 -0.9375q0.828125 -0.953125 0.828125 -2.890625q0 -1.828125 -0.828125 -2.765625q-0.828125 -0.9375 -2.0625 -0.9375q-1.25 0 -2.078125 0.9375q-0.828125 0.9375 -0.828125 2.828125zm15.735107 4.921875l0 -1.453125q-1.140625 1.671875 -3.125 1.671875q-0.859375 0 -1.625 -0.328125q-0.75 -0.34375 -1.125 -0.84375q-0.359375 -0.5 -0.515625 -1.234375q-0.09375 -0.5 -0.09375 -1.5625l0 -6.109375l1.671875 0l0 5.46875q0 1.3125 0.09375 1.765625q0.15625 0.65625 0.671875 1.03125q0.515625 0.375 1.265625 0.375q0.75 0 1.40625 -0.375q0.65625 -0.390625 0.921875 -1.046875q0.28125 -0.671875 0.28125 -1.9375l0 -5.28125l1.671875 0l0 9.859375l-1.5 0zm3.9069824 0l0 -9.859375l1.5 0l0 1.5q0.578125 -1.046875 1.0625 -1.375q0.484375 -0.34375 1.078125 -0.34375q0.84375 0 1.71875 0.546875l-0.578125 1.546875q-0.609375 -0.359375 -1.234375 -0.359375q-0.546875 0 -0.984375 0.328125q-0.421875 0.328125 -0.609375 0.90625q-0.28125 0.890625 -0.28125 1.953125l0 5.15625l-1.671875 0zm12.6657715 -3.609375l1.640625 0.21875q-0.265625 1.6875 -1.375 2.65625q-1.109375 0.953125 -2.734375 0.953125q-2.015625 0 -3.25 -1.3125q-1.21875 -1.328125 -1.21875 -3.796875q0 -1.59375 0.515625 -2.78125q0.53125 -1.203125 1.609375 -1.796875q1.09375 -0.609375 2.359375 -0.609375q1.609375 0 2.625 0.8125q1.015625 0.8125 1.3125 2.3125l-1.625 0.25q-0.234375 -1.0 -0.828125 -1.5q-0.59375 -0.5 -1.421875 -0.5q-1.265625 0 -2.0625 0.90625q-0.78125 0.90625 -0.78125 2.859375q0 1.984375 0.765625 2.890625q0.765625 0.890625 1.984375 0.890625q0.984375 0 1.640625 -0.59375q0.65625 -0.609375 0.84375 -1.859375zm9.640625 0.4375l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm13.590271 2.015625l1.625 -0.21875q0.0625 1.546875 0.578125 2.125q0.53125 0.578125 1.4375 0.578125q0.6875 0 1.171875 -0.3125q0.5 -0.3125 0.671875 -0.84375q0.1875 -0.53125 0.1875 -1.703125l0 -9.359375l1.8125 0l0 9.265625q0 1.703125 -0.421875 2.640625q-0.40625 0.9375 -1.3125 1.4375q-0.890625 0.484375 -2.09375 0.484375q-1.796875 0 -2.75 -1.03125q-0.9375 -1.03125 -0.90625 -3.0625zm9.640625 -0.515625l1.6875 -0.140625q0.125 1.015625 0.5625 1.671875q0.4375 0.65625 1.359375 1.0625q0.9375 0.40625 2.09375 0.40625q1.03125 0 1.8125 -0.3125q0.796875 -0.3125 1.1875 -0.84375q0.390625 -0.53125 0.390625 -1.15625q0 -0.640625 -0.375 -1.109375q-0.375 -0.484375 -1.234375 -0.8125q-0.546875 -0.21875 -2.421875 -0.65625q-1.875 -0.453125 -2.625 -0.859375q-0.96875 -0.515625 -1.453125 -1.265625q-0.46875 -0.75 -0.46875 -1.6875q0 -1.03125 0.578125 -1.921875q0.59375 -0.90625 1.703125 -1.359375q1.125 -0.46875 2.5 -0.46875q1.515625 0 2.671875 0.484375q1.15625 0.484375 1.765625 1.4375q0.625 0.9375 0.671875 2.140625l-1.71875 0.125q-0.140625 -1.28125 -0.953125 -1.9375q-0.796875 -0.671875 -2.359375 -0.671875q-1.625 0 -2.375 0.609375q-0.75 0.59375 -0.75 1.4375q0 0.734375 0.53125 1.203125q0.515625 0.46875 2.703125 0.96875q2.203125 0.5 3.015625 0.875q1.1875 0.546875 1.75 1.390625q0.578125 0.828125 0.578125 1.921875q0 1.09375 -0.625 2.0625q-0.625 0.953125 -1.796875 1.484375q-1.15625 0.53125 -2.609375 0.53125q-1.84375 0 -3.09375 -0.53125q-1.25 -0.546875 -1.96875 -1.625q-0.703125 -1.078125 -0.734375 -2.453125zm12.5061035 -2.25q0 -3.390625 1.8125 -5.296875q1.828125 -1.921875 4.703125 -1.921875q1.875 0 3.390625 0.90625q1.515625 0.890625 2.296875 2.5q0.796875 1.609375 0.796875 3.65625q0 2.0625 -0.84375 3.703125q-0.828125 1.625 -2.359375 2.46875q-1.53125 0.84375 -3.296875 0.84375q-1.921875 0 -3.4375 -0.921875q-1.5 -0.9375 -2.28125 -2.53125q-0.78125 -1.609375 -0.78125 -3.40625zm1.859375 0.03125q0 2.453125 1.3125 3.875q1.328125 1.40625 3.3125 1.40625q2.03125 0 3.34375 -1.421875q1.3125 -1.4375 1.3125 -4.0625q0 -1.65625 -0.5625 -2.890625q-0.546875 -1.234375 -1.640625 -1.921875q-1.078125 -0.6875 -2.421875 -0.6875q-1.90625 0 -3.28125 1.3125q-1.375 1.3125 -1.375 4.390625zm13.183289 6.59375l0 -13.59375l1.84375 0l7.140625 10.671875l0 -10.671875l1.71875 0l0 13.59375l-1.84375 0l-7.140625 -10.6875l0 10.6875l-1.71875 0z" fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0" d="m529.084 131.11548l-343.0866 -1.102356" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m529.084 131.11548l-337.08667 -1.0830841" fill-rule="evenodd"></path><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m192.00266 128.38068l-4.5433807 1.637146l4.5327606 1.6663055z" fill-rule="evenodd"></path><path fill="#000000" fill-opacity="0.0" d="m258.7034 136.56955l156.34647 0l0 70.267715l-156.34647 0z" fill-rule="nonzero"></path><path fill="#000000" d="m269.17215 163.48955l0 -13.59375l5.125 0q1.359375 0 2.078125 0.125q1.0 0.171875 1.671875 0.640625q0.671875 0.46875 1.078125 1.3125q0.421875 0.84375 0.421875 1.84375q0 1.734375 -1.109375 2.9375q-1.09375 1.203125 -3.984375 1.203125l-3.484375 0l0 5.53125l-1.796875 0zm1.796875 -7.140625l3.515625 0q1.75 0 2.46875 -0.640625q0.734375 -0.65625 0.734375 -1.828125q0 -0.859375 -0.4375 -1.46875q-0.421875 -0.609375 -1.125 -0.796875q-0.453125 -0.125 -1.671875 -0.125l-3.484375 0l0 4.859375zm16.865448 5.921875q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm4.0632324 4.9375l0 -9.859375l1.5 0l0 1.5q0.578125 -1.046875 1.0625 -1.375q0.484375 -0.34375 1.078125 -0.34375q0.84375 0 1.71875 0.546875l-0.578125 1.546875q-0.609375 -0.359375 -1.234375 -0.359375q-0.546875 0 -0.984375 0.328125q-0.421875 0.328125 -0.609375 0.90625q-0.28125 0.890625 -0.28125 1.953125l0 5.15625l-1.671875 0zm5.556427 -2.9375l1.65625 -0.265625q0.140625 1.0 0.765625 1.53125q0.640625 0.515625 1.78125 0.515625q1.15625 0 1.703125 -0.46875q0.5625 -0.46875 0.5625 -1.09375q0 -0.5625 -0.484375 -0.890625q-0.34375 -0.21875 -1.703125 -0.5625q-1.84375 -0.46875 -2.5625 -0.796875q-0.703125 -0.34375 -1.078125 -0.9375q-0.359375 -0.609375 -0.359375 -1.328125q0 -0.65625 0.296875 -1.21875q0.3125 -0.5625 0.828125 -0.9375q0.390625 -0.28125 1.0625 -0.484375q0.671875 -0.203125 1.4375 -0.203125q1.171875 0 2.046875 0.34375q0.875 0.328125 1.28125 0.90625q0.421875 0.5625 0.578125 1.515625l-1.625 0.21875q-0.109375 -0.75 -0.65625 -1.171875q-0.53125 -0.4375 -1.5 -0.4375q-1.15625 0 -1.640625 0.390625q-0.484375 0.375 -0.484375 0.875q0 0.328125 0.203125 0.59375q0.203125 0.265625 0.640625 0.4375q0.25 0.09375 1.46875 0.4375q1.765625 0.46875 2.46875 0.765625q0.703125 0.296875 1.09375 0.875q0.40625 0.578125 0.40625 1.4375q0 0.828125 -0.484375 1.578125q-0.484375 0.734375 -1.40625 1.140625q-0.921875 0.390625 -2.078125 0.390625q-1.921875 0 -2.9375 -0.796875q-1.0 -0.796875 -1.28125 -2.359375zm16.75 -0.234375l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm13.012146 5.875l5.234375 -13.59375l1.9375 0l5.5625 13.59375l-2.046875 0l-1.59375 -4.125l-5.6875 0l-1.484375 4.125l-1.921875 0zm3.921875 -5.578125l4.609375 0l-1.40625 -3.78125q-0.65625 -1.703125 -0.96875 -2.8125q-0.265625 1.3125 -0.734375 2.59375l-1.5 4.0zm10.021698 5.578125l0 -13.59375l5.125 0q1.359375 0 2.078125 0.125q1.0 0.171875 1.671875 0.640625q0.671875 0.46875 1.078125 1.3125q0.421875 0.84375 0.421875 1.84375q0 1.734375 -1.109375 2.9375q-1.09375 1.203125 -3.984375 1.203125l-3.484375 0l0 5.53125l-1.796875 0zm1.796875 -7.140625l3.515625 0q1.75 0 2.46875 -0.640625q0.734375 -0.65625 0.734375 -1.828125q0 -0.859375 -0.4375 -1.46875q-0.421875 -0.609375 -1.125 -0.796875q-0.453125 -0.125 -1.671875 -0.125l-3.484375 0l0 4.859375zm10.943573 7.140625l0 -13.59375l1.8125 0l0 13.59375l-1.8125 0zm9.835358 0l0 -9.859375l1.5 0l0 1.5q0.578125 -1.046875 1.0625 -1.375q0.484375 -0.34375 1.078125 -0.34375q0.84375 0 1.71875 0.546875l-0.578125 1.546875q-0.609375 -0.359375 -1.234375 -0.359375q-0.546875 0 -0.984375 0.328125q-0.421875 0.328125 -0.609375 0.90625q-0.28125 0.890625 -0.28125 1.953125l0 5.15625l-1.671875 0zm12.978302 -3.171875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm8.438202 2.9375l1.65625 -0.265625q0.140625 1.0 0.765625 1.53125q0.640625 0.515625 1.78125 0.515625q1.15625 0 1.703125 -0.46875q0.5625 -0.46875 0.5625 -1.09375q0 -0.5625 -0.484375 -0.890625q-0.34375 -0.21875 -1.703125 -0.5625q-1.84375 -0.46875 -2.5625 -0.796875q-0.703125 -0.34375 -1.078125 -0.9375q-0.359375 -0.609375 -0.359375 -1.328125q0 -0.65625 0.296875 -1.21875q0.3125 -0.5625 0.828125 -0.9375q0.390625 -0.28125 1.0625 -0.484375q0.671875 -0.203125 1.4375 -0.203125q1.171875 0 2.046875 0.34375q0.875 0.328125 1.28125 0.90625q0.421875 0.5625 0.578125 1.515625l-1.625 0.21875q-0.109375 -0.75 -0.65625 -1.171875q-0.53125 -0.4375 -1.5 -0.4375q-1.15625 0 -1.640625 0.390625q-0.484375 0.375 -0.484375 0.875q0 0.328125 0.203125 0.59375q0.203125 0.265625 0.640625 0.4375q0.25 0.09375 1.46875 0.4375q1.765625 0.46875 2.46875 0.765625q0.703125 0.296875 1.09375 0.875q0.40625 0.578125 0.40625 1.4375q0 0.828125 -0.484375 1.578125q-0.484375 0.734375 -1.40625 1.140625q-0.921875 0.390625 -2.078125 0.390625q-1.921875 0 -2.9375 -0.796875q-1.0 -0.796875 -1.28125 -2.359375zm10.0 6.71875l0 -13.640625l1.53125 0l0 1.28125q0.53125 -0.75 1.203125 -1.125q0.6875 -0.375 1.640625 -0.375q1.265625 0 2.234375 0.65625q0.96875 0.640625 1.453125 1.828125q0.5 1.1875 0.5 2.59375q0 1.515625 -0.546875 2.734375q-0.546875 1.203125 -1.578125 1.84375q-1.03125 0.640625 -2.171875 0.640625q-0.84375 0 -1.515625 -0.34375q-0.65625 -0.359375 -1.078125 -0.890625l0 4.796875l-1.671875 0zm1.515625 -8.65625q0 1.90625 0.765625 2.8125q0.78125 0.90625 1.875 0.90625q1.109375 0 1.890625 -0.9375q0.796875 -0.9375 0.796875 -2.921875q0 -1.875 -0.78125 -2.8125q-0.765625 -0.9375 -1.84375 -0.9375q-1.0625 0 -1.890625 1.0q-0.8125 1.0 -0.8125 2.890625z" fill-rule="nonzero"></path><path fill="#000000" d="m276.73465 183.88017q-0.828125 0.921875 -1.8125 1.390625q-0.96875 0.453125 -2.09375 0.453125q-2.09375 0 -3.3125 -1.40625q-1.0 -1.15625 -1.0 -2.578125q0 -1.265625 0.8125 -2.28125q0.8125 -1.015625 2.421875 -1.78125q-0.90625 -1.0625 -1.21875 -1.71875q-0.296875 -0.65625 -0.296875 -1.265625q0 -1.234375 0.953125 -2.125q0.953125 -0.90625 2.421875 -0.90625q1.390625 0 2.265625 0.859375q0.890625 0.84375 0.890625 2.046875q0 1.9375 -2.5625 3.3125l2.4375 3.09375q0.421875 -0.8125 0.640625 -1.890625l1.734375 0.375q-0.4375 1.78125 -1.203125 2.9375q0.9375 1.234375 2.125 2.078125l-1.125 1.328125q-1.0 -0.640625 -2.078125 -1.921875zm-3.40625 -7.078125q1.09375 -0.640625 1.40625 -1.125q0.328125 -0.484375 0.328125 -1.0625q0 -0.703125 -0.453125 -1.140625q-0.4375 -0.4375 -1.09375 -0.4375q-0.671875 0 -1.125 0.4375q-0.453125 0.421875 -0.453125 1.0625q0 0.3125 0.15625 0.65625q0.171875 0.34375 0.5 0.734375l0.734375 0.875zm2.359375 5.765625l-3.0625 -3.796875q-1.359375 0.8125 -1.84375 1.5q-0.46875 0.6875 -0.46875 1.375q0 0.8125 0.65625 1.703125q0.671875 0.890625 1.875 0.890625q0.75 0 1.546875 -0.46875q0.8125 -0.46875 1.296875 -1.203125zm17.283142 2.921875l0 -1.25q-0.9375 1.46875 -2.75 1.46875q-1.171875 0 -2.171875 -0.640625q-0.984375 -0.65625 -1.53125 -1.8125q-0.53125 -1.171875 -0.53125 -2.6875q0 -1.46875 0.484375 -2.671875q0.5 -1.203125 1.46875 -1.84375q0.984375 -0.640625 2.203125 -0.640625q0.890625 0 1.578125 0.375q0.703125 0.375 1.140625 0.984375l0 -4.875l1.65625 0l0 13.59375l-1.546875 0zm-5.28125 -4.921875q0 1.890625 0.796875 2.828125q0.8125 0.9375 1.890625 0.9375q1.09375 0 1.859375 -0.890625q0.765625 -0.890625 0.765625 -2.734375q0 -2.015625 -0.78125 -2.953125q-0.78125 -0.953125 -1.921875 -0.953125q-1.109375 0 -1.859375 0.90625q-0.75 0.90625 -0.75 2.859375zm9.281952 -6.765625l0 -1.90625l1.671875 0l0 1.90625l-1.671875 0zm0 11.6875l0 -9.859375l1.671875 0l0 9.859375l-1.671875 0zm3.4573364 -2.9375l1.65625 -0.265625q0.140625 1.0 0.765625 1.53125q0.640625 0.515625 1.78125 0.515625q1.15625 0 1.703125 -0.46875q0.5625 -0.46875 0.5625 -1.09375q0 -0.5625 -0.484375 -0.890625q-0.34375 -0.21875 -1.703125 -0.5625q-1.84375 -0.46875 -2.5625 -0.796875q-0.703125 -0.34375 -1.078125 -0.9375q-0.359375 -0.609375 -0.359375 -1.328125q0 -0.65625 0.296875 -1.21875q0.3125 -0.5625 0.828125 -0.9375q0.390625 -0.28125 1.0625 -0.484375q0.671875 -0.203125 1.4375 -0.203125q1.171875 0 2.046875 0.34375q0.875 0.328125 1.28125 0.90625q0.421875 0.5625 0.578125 1.515625l-1.625 0.21875q-0.109375 -0.75 -0.65625 -1.171875q-0.53125 -0.4375 -1.5 -0.4375q-1.15625 0 -1.640625 0.390625q-0.484375 0.375 -0.484375 0.875q0 0.328125 0.203125 0.59375q0.203125 0.265625 0.640625 0.4375q0.25 0.09375 1.46875 0.4375q1.765625 0.46875 2.46875 0.765625q0.703125 0.296875 1.09375 0.875q0.40625 0.578125 0.40625 1.4375q0 0.828125 -0.484375 1.578125q-0.484375 0.734375 -1.40625 1.140625q-0.921875 0.390625 -2.078125 0.390625q-1.921875 0 -2.9375 -0.796875q-1.0 -0.796875 -1.28125 -2.359375zm10.0 6.71875l0 -13.640625l1.53125 0l0 1.28125q0.53125 -0.75 1.203125 -1.125q0.6875 -0.375 1.640625 -0.375q1.265625 0 2.234375 0.65625q0.96875 0.640625 1.453125 1.828125q0.5 1.1875 0.5 2.59375q0 1.515625 -0.546875 2.734375q-0.546875 1.203125 -1.578125 1.84375q-1.03125 0.640625 -2.171875 0.640625q-0.84375 0 -1.515625 -0.34375q-0.65625 -0.359375 -1.078125 -0.890625l0 4.796875l-1.671875 0zm1.515625 -8.65625q0 1.90625 0.765625 2.8125q0.78125 0.90625 1.875 0.90625q1.109375 0 1.890625 -0.9375q0.796875 -0.9375 0.796875 -2.921875q0 -1.875 -0.78125 -2.8125q-0.765625 -0.9375 -1.84375 -0.9375q-1.0625 0 -1.890625 1.0q-0.8125 1.0 -0.8125 2.890625zm8.828827 4.875l0 -13.59375l1.671875 0l0 13.59375l-1.671875 0zm10.613586 -1.21875q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm4.000702 8.734375l-0.171875 -1.5625q0.546875 0.140625 0.953125 0.140625q0.546875 0 0.875 -0.1875q0.34375 -0.1875 0.5625 -0.515625q0.15625 -0.25 0.5 -1.25q0.046875 -0.140625 0.15625 -0.40625l-3.734375 -9.875l1.796875 0l2.046875 5.71875q0.40625 1.078125 0.71875 2.28125q0.28125 -1.15625 0.6875 -2.25l2.09375 -5.75l1.671875 0l-3.75 10.03125q-0.59375 1.625 -0.9375 2.234375q-0.4375 0.828125 -1.015625 1.203125q-0.578125 0.390625 -1.375 0.390625q-0.484375 0 -1.078125 -0.203125zm14.589569 -0.015625l0 -13.640625l1.53125 0l0 1.28125q0.53125 -0.75 1.203125 -1.125q0.6875 -0.375 1.640625 -0.375q1.265625 0 2.234375 0.65625q0.96875 0.640625 1.453125 1.828125q0.5 1.1875 0.5 2.59375q0 1.515625 -0.546875 2.734375q-0.546875 1.203125 -1.578125 1.84375q-1.03125 0.640625 -2.171875 0.640625q-0.84375 0 -1.515625 -0.34375q-0.65625 -0.359375 -1.078125 -0.890625l0 4.796875l-1.671875 0zm1.515625 -8.65625q0 1.90625 0.765625 2.8125q0.78125 0.90625 1.875 0.90625q1.109375 0 1.890625 -0.9375q0.796875 -0.9375 0.796875 -2.921875q0 -1.875 -0.78125 -2.8125q-0.765625 -0.9375 -1.84375 -0.9375q-1.0625 0 -1.890625 1.0q-0.8125 1.0 -0.8125 2.890625zm15.297577 3.65625q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm3.7819824 5.75l1.609375 0.25q0.109375 0.75 0.578125 1.09375q0.609375 0.453125 1.6875 0.453125q1.171875 0 1.796875 -0.46875q0.625 -0.453125 0.859375 -1.28125q0.125 -0.515625 0.109375 -2.15625q-1.09375 1.296875 -2.71875 1.296875q-2.03125 0 -3.15625 -1.46875q-1.109375 -1.46875 -1.109375 -3.515625q0 -1.40625 0.515625 -2.59375q0.515625 -1.203125 1.484375 -1.84375q0.96875 -0.65625 2.265625 -0.65625q1.75 0 2.875 1.40625l0 -1.1875l1.546875 0l0 8.515625q0 2.3125 -0.46875 3.265625q-0.46875 0.96875 -1.484375 1.515625q-1.015625 0.5625 -2.5 0.5625q-1.765625 0 -2.859375 -0.796875q-1.078125 -0.796875 -1.03125 -2.390625zm1.375 -5.921875q0 1.953125 0.765625 2.84375q0.78125 0.890625 1.9375 0.890625q1.140625 0 1.921875 -0.890625q0.78125 -0.890625 0.78125 -2.78125q0 -1.8125 -0.8125 -2.71875q-0.796875 -0.921875 -1.921875 -0.921875q-1.109375 0 -1.890625 0.90625q-0.78125 0.890625 -0.78125 2.671875zm16.047577 1.9375l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875z" fill-rule="nonzero"></path><path fill="#ffffff" d="m94.25984 75.59843l0 0c0 -12.054596 10.597107 -21.826775 23.669289 -21.826775l0 0c13.072197 0 23.669289 9.772179 23.669289 21.826775l0 0c0 12.054588 -10.597092 21.826767 -23.669289 21.826767l0 0c-13.072182 0 -23.669289 -9.772179 -23.669289 -21.826767z" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m94.25984 75.59843l0 0c0 -12.054596 10.597107 -21.826775 23.669289 -21.826775l0 0c13.072197 0 23.669289 9.772179 23.669289 21.826775l0 0c0 12.054588 -10.597092 21.826767 -23.669289 21.826767l0 0c-13.072182 0 -23.669289 -9.772179 -23.669289 -21.826767z" fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0" d="m117.92913 97.42519l1.1653595 119.55906" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m117.92913 97.42519l1.1653595 119.55906" fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0" d="m117.92913 128.50131l29.574806 42.48819" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m117.92913 128.50131l29.574806 42.48819" fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0" d="m91.50131 170.50131l26.425194 -41.07086" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m91.50131 170.50131l26.425194 -41.07086" fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0" d="m235.77428 40.0l179.27559 0l0 48.0l-179.27559 0z" fill-rule="nonzero"></path><path fill="#000000" d="m273.33563 65.59187l0 -1.609375l5.765625 0l0 5.046875q-1.328125 1.0625 -2.75 1.59375q-1.40625 0.53125 -2.890625 0.53125q-2.0 0 -3.640625 -0.859375q-1.625 -0.859375 -2.46875 -2.484375q-0.828125 -1.625 -0.828125 -3.625q0 -1.984375 0.828125 -3.703125q0.828125 -1.71875 2.390625 -2.546875q1.5625 -0.84375 3.59375 -0.84375q1.46875 0 2.65625 0.484375q1.203125 0.46875 1.875 1.328125q0.671875 0.84375 1.03125 2.21875l-1.625 0.4375q-0.3125 -1.03125 -0.765625 -1.625q-0.453125 -0.59375 -1.296875 -0.953125q-0.84375 -0.359375 -1.875 -0.359375q-1.234375 0 -2.140625 0.375q-0.890625 0.375 -1.453125 1.0q-0.546875 0.609375 -0.84375 1.34375q-0.53125 1.25 -0.53125 2.734375q0 1.8125 0.625 3.046875q0.640625 1.21875 1.828125 1.8125q1.203125 0.59375 2.546875 0.59375q1.171875 0 2.28125 -0.453125q1.109375 -0.453125 1.6875 -0.953125l0 -2.53125l-4.0 0zm8.183289 5.328125l0 -13.59375l9.84375 0l0 1.59375l-8.046875 0l0 4.171875l7.53125 0l0 1.59375l-7.53125 0l0 4.625l8.359375 0l0 1.609375l-10.15625 0zm15.865448 0l0 -12.0l-4.46875 0l0 -1.59375l10.765625 0l0 1.59375l-4.5 0l0 12.0l-1.796875 0zm11.65741 0.234375l3.9375 -14.0625l1.34375 0l-3.9375 14.0625l-1.34375 0zm6.417694 -0.234375l0 -9.859375l1.5 0l0 1.5q0.578125 -1.046875 1.0625 -1.375q0.484375 -0.34375 1.078125 -0.34375q0.84375 0 1.71875 0.546875l-0.578125 1.546875q-0.609375 -0.359375 -1.234375 -0.359375q-0.546875 0 -0.984375 0.328125q-0.421875 0.328125 -0.609375 0.90625q-0.28125 0.890625 -0.28125 1.953125l0 5.15625l-1.671875 0zm12.978302 -3.171875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm8.438202 2.9375l1.65625 -0.265625q0.140625 1.0 0.765625 1.53125q0.640625 0.515625 1.78125 0.515625q1.15625 0 1.703125 -0.46875q0.5625 -0.46875 0.5625 -1.09375q0 -0.5625 -0.484375 -0.890625q-0.34375 -0.21875 -1.703125 -0.5625q-1.84375 -0.46875 -2.5625 -0.796875q-0.703125 -0.34375 -1.078125 -0.9375q-0.359375 -0.609375 -0.359375 -1.328125q0 -0.65625 0.296875 -1.21875q0.3125 -0.5625 0.828125 -0.9375q0.390625 -0.28125 1.0625 -0.484375q0.671875 -0.203125 1.4375 -0.203125q1.171875 0 2.046875 0.34375q0.875 0.328125 1.28125 0.90625q0.421875 0.5625 0.578125 1.515625l-1.625 0.21875q-0.109375 -0.75 -0.65625 -1.171875q-0.53125 -0.4375 -1.5 -0.4375q-1.15625 0 -1.640625 0.390625q-0.484375 0.375 -0.484375 0.875q0 0.328125 0.203125 0.59375q0.203125 0.265625 0.640625 0.4375q0.25 0.09375 1.46875 0.4375q1.765625 0.46875 2.46875 0.765625q0.703125 0.296875 1.09375 0.875q0.40625 0.578125 0.40625 1.4375q0 0.828125 -0.484375 1.578125q-0.484375 0.734375 -1.40625 1.140625q-0.921875 0.390625 -2.078125 0.390625q-1.921875 0 -2.9375 -0.796875q-1.0 -0.796875 -1.28125 -2.359375zm9.375 -1.984375q0 -2.734375 1.53125 -4.0625q1.265625 -1.09375 3.09375 -1.09375q2.03125 0 3.3125 1.34375q1.296875 1.328125 1.296875 3.671875q0 1.90625 -0.578125 3.0q-0.5625 1.078125 -1.65625 1.6875q-1.078125 0.59375 -2.375 0.59375q-2.0625 0 -3.34375 -1.328125q-1.28125 -1.328125 -1.28125 -3.8125zm1.71875 0q0 1.890625 0.828125 2.828125q0.828125 0.9375 2.078125 0.9375q1.25 0 2.0625 -0.9375q0.828125 -0.953125 0.828125 -2.890625q0 -1.828125 -0.828125 -2.765625q-0.828125 -0.9375 -2.0625 -0.9375q-1.25 0 -2.078125 0.9375q-0.828125 0.9375 -0.828125 2.828125zm15.735107 4.921875l0 -1.453125q-1.140625 1.671875 -3.125 1.671875q-0.859375 0 -1.625 -0.328125q-0.75 -0.34375 -1.125 -0.84375q-0.359375 -0.5 -0.515625 -1.234375q-0.09375 -0.5 -0.09375 -1.5625l0 -6.109375l1.671875 0l0 5.46875q0 1.3125 0.09375 1.765625q0.15625 0.65625 0.671875 1.03125q0.515625 0.375 1.265625 0.375q0.75 0 1.40625 -0.375q0.65625 -0.390625 0.921875 -1.046875q0.28125 -0.671875 0.28125 -1.9375l0 -5.28125l1.671875 0l0 9.859375l-1.5 0zm3.906952 0l0 -9.859375l1.5 0l0 1.5q0.578125 -1.046875 1.0625 -1.375q0.484375 -0.34375 1.078125 -0.34375q0.84375 0 1.71875 0.546875l-0.578125 1.546875q-0.609375 -0.359375 -1.234375 -0.359375q-0.546875 0 -0.984375 0.328125q-0.421875 0.328125 -0.609375 0.90625q-0.28125 0.890625 -0.28125 1.953125l0 5.15625l-1.671875 0zm12.665802 -3.609375l1.640625 0.21875q-0.265625 1.6875 -1.375 2.65625q-1.109375 0.953125 -2.734375 0.953125q-2.015625 0 -3.25 -1.3125q-1.21875 -1.328125 -1.21875 -3.796875q0 -1.59375 0.515625 -2.78125q0.53125 -1.203125 1.609375 -1.796875q1.09375 -0.609375 2.359375 -0.609375q1.609375 0 2.625 0.8125q1.015625 0.8125 1.3125 2.3125l-1.625 0.25q-0.234375 -1.0 -0.828125 -1.5q-0.59375 -0.5 -1.421875 -0.5q-1.265625 0 -2.0625 0.90625q-0.78125 0.90625 -0.78125 2.859375q0 1.984375 0.765625 2.890625q0.765625 0.890625 1.984375 0.890625q0.984375 0 1.640625 -0.59375q0.65625 -0.609375 0.84375 -1.859375zm9.640625 0.4375l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875z" fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0" d="m119.125984 215.50131l-38.58268 53.07086" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m119.125984 215.50131l-38.58268 53.07086" fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0" d="m119.62467 215.50131l42.99212 58.992126" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m119.62467 215.50131l42.99212 58.992126" fill-rule="nonzero"></path></g></svg>
+
index de8dc9e477f53e1369da23f5705d57cfdc901f0a..3d67577e68412a7e11b8f3307ff162caaee30520 100644 (file)
Binary files a/doc/images/upload-using-workbench.png and b/doc/images/upload-using-workbench.png differ
index 76df32c9e2b27aefdb96e6119b4e1bb216d18174..3cdf1e4a66cfd552fbcf41ce46c1e13687d673a2 100644 (file)
Binary files a/doc/images/workbench-dashboard.png and b/doc/images/workbench-dashboard.png differ
index 5ed1ef53e1b4a29a18cb5cb1394f1750e3963df7..bba1a1c60176748f51fb653bfdb3918a7e485e7b 100644 (file)
Binary files a/doc/images/workbench-move-selected.png and b/doc/images/workbench-move-selected.png differ
index c4dc92940ba3e09d448f5bf19217ee501b8f9413..26c7dde361482923a0ee9dc146cce90cd6871ba3 100644 (file)
@@ -1,9 +1,9 @@
 ---
 layout: default
 navsection: installguide
-title: Crunch v2 SLURM prerequisites
+title: Containers API SLURM prerequisites
 ...
 
-Crunch v2 containers can be dispatched to a SLURM cluster.  The dispatcher sends work to the cluster using SLURM's @sbatch@ command, so it works in a variety of SLURM configurations.
+Containers can be dispatched to a SLURM cluster.  The dispatcher sends work to the cluster using SLURM's @sbatch@ command, so it works in a variety of SLURM configurations.
 
 In order to run containers, you must run the dispatcher as a user that has permission to set up FUSE mounts and run Docker containers on each compute node.  This install guide refers to this user as the @crunch@ user.  We recommend you create this user on each compute node with the same UID and GID, and add it to the @fuse@ and @docker@ system groups to grant it the necessary permissions.  However, you can run the dispatcher under any account with sufficient permissions across the cluster.
index 16d23e6df56d2fb58cf38974a1e31dbda82d8915..f1c38ea4826394c3b77474c1787a23be6514b476 100644 (file)
@@ -51,7 +51,7 @@ Usage of keep-web:
 </code></pre>
 </notextile>
 
-{% assign railscmd = "bundle exec ./script/get_anonymous_user_token.rb" %}
+{% assign railscmd = "bundle exec ./script/get_anonymous_user_token.rb --get" %}
 {% assign railsout = "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz" %}
 If you intend to use Keep-web to serve public data to anonymous clients, configure it with an anonymous token. You can use the same one you used when you set up your Keepproxy server, or use the following command on the <strong>API server</strong> to create another. {% include 'install_rails_command' %}
 
index a6bb5d4bd9aeb3a6d2b276d20e1fc1e0505bf2c9..f1a2688014b54fc3da1db8309b231e766bbccfa8 100644 (file)
@@ -51,7 +51,7 @@ Usage of keepproxy:
 
 h3. Create an API token for the Keepproxy server
 
-{% assign railscmd = "bundle exec ./script/get_anonymous_user_token.rb" %}
+{% assign railscmd = "bundle exec ./script/get_anonymous_user_token.rb --get" %}
 {% assign railsout = "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz" %}
 The Keepproxy server needs a token to talk to the API server.  On the <strong>API server</strong>, use the following command to create the token.  {% include 'install_rails_command' %}
 
diff --git a/doc/install/install-ws.html.textile.liquid b/doc/install/install-ws.html.textile.liquid
new file mode 100644 (file)
index 0000000..a36a59a
--- /dev/null
@@ -0,0 +1,204 @@
+---
+layout: default
+navsection: installguide
+title: Install the websocket server
+...
+
+{% include 'notebox_begin_warning' %}
+
+This websocket server is an alternative to the puma server that comes with the API server. It is available as an *experimental pre-release* and is not recommended for production sites.
+
+{% include 'notebox_end' %}
+
+The arvados-ws server provides event notifications to websocket clients. It can be installed anywhere with access to Postgres database and the Arvados API server, typically behind a web proxy that provides SSL support. See the "godoc page":http://godoc.org/github.com/curoverse/arvados/services/keep-web for additional information.
+
+By convention, we use the following hostname for the websocket service.
+
+<notextile>
+<pre><code>ws.<span class="userinput">uuid_prefix.your.domain</span></code></pre>
+</notextile>
+
+The above hostname should resolve from anywhere on the internet.
+
+h2. Install arvados-ws
+
+Typically arvados-ws runs on the same host as the API server.
+
+On Debian-based systems:
+
+<notextile>
+<pre><code>~$ <span class="userinput">sudo apt-get install arvados-ws</span>
+</code></pre>
+</notextile>
+
+On Red Hat-based systems:
+
+<notextile>
+<pre><code>~$ <span class="userinput">sudo yum install arvados-ws</span>
+</code></pre>
+</notextile>
+
+Verify that @arvados-ws@ is functional:
+
+<notextile>
+<pre><code>~$ <span class="userinput">arvados-ws -h</span>
+Usage of arvados-ws:
+  -config path
+        path to config file (default "/etc/arvados/ws/ws.yml")
+  -dump-config
+        show current configuration and exit
+</code></pre>
+</notextile>
+
+h3. Create a configuration file
+
+Create @/etc/arvados/ws/ws.yml@ using the following template. Replace @xxxxxxxx@ with the "password you generated during database setup":install-postgresql.html#api.
+
+<notextile>
+<pre><code>Client:
+  APIHost: <span class="userinput">uuid_prefix.your.domain</span>:443
+Listen: ":<span class="userinput">9003</span>"
+Postgres:
+  dbname: arvados_production
+  host: localhost
+  password: <span class="userinput">xxxxxxxx</span>
+  user: arvados
+</code></pre>
+</notextile>
+
+h3. Start the service (option 1: systemd)
+
+If your system does not use systemd, skip this section and follow the "runit instructions":#runit instead.
+
+If your system uses systemd, the arvados-ws service should already be set up. Start it and check its status:
+
+<notextile>
+<pre><code>~$ <span class="userinput">sudo systemctl restart arvados-ws</span>
+~$ <span class="userinput">sudo systemctl status arvados-ws</span>
+&#x25cf; arvados-ws.service - Arvados websocket server
+   Loaded: loaded (/lib/systemd/system/arvados-ws.service; enabled)
+   Active: active (running) since Tue 2016-12-06 11:20:48 EST; 10s ago
+     Docs: https://doc.arvados.org/
+ Main PID: 9421 (arvados-ws)
+   CGroup: /system.slice/arvados-ws.service
+           â””─9421 /usr/bin/arvados-ws
+
+Dec 06 11:20:48 zzzzz arvados-ws[9421]: {"level":"info","msg":"started","time":"2016-12-06T11:20:48.207617188-05:00"}
+Dec 06 11:20:48 zzzzz arvados-ws[9421]: {"Listen":":9003","level":"info","msg":"listening","time":"2016-12-06T11:20:48.244956506-05:00"}
+Dec 06 11:20:48 zzzzz systemd[1]: Started Arvados websocket server.
+</code></pre>
+</notextile>
+
+If it is not running, use @journalctl@ to check logs for errors:
+
+<notextile>
+<pre><code>~$ <span class="userinput">sudo journalctl -n10 -u arvados-ws</span>
+...
+Dec 06 11:12:48 zzzzz systemd[1]: Starting Arvados websocket server...
+Dec 06 11:12:48 zzzzz arvados-ws[8918]: {"level":"info","msg":"started","time":"2016-12-06T11:12:48.030496636-05:00"}
+Dec 06 11:12:48 zzzzz arvados-ws[8918]: {"error":"pq: password authentication failed for user \"arvados\"","level":"fatal","msg":"db.Ping failed","time":"2016-12-06T11:12:48.058206400-05:00"}
+</code></pre>
+</notextile>
+
+Skip ahead to "confirm the service is working":#confirm.
+
+h3(#runit). Start the service (option 2: runit)
+
+Install runit to supervise the arvados-ws daemon.  {% include 'install_runit' %}
+
+Create a supervised service.
+
+<notextile>
+<pre><code>~$ <span class="userinput">sudo mkdir /etc/service/arvados-ws</span>
+~$ <span class="userinput">cd /etc/service/arvados-ws</span>
+~$ <span class="userinput">sudo mkdir log log/main</span>
+~$ <span class="userinput">printf '#!/bin/sh\nexec arvados-ws 2>&1\n' | sudo tee run</span>
+~$ <span class="userinput">printf '#!/bin/sh\nexec svlogd main\n' | sudo tee log/run</span>
+~$ <span class="userinput">sudo chmod +x run log/run</span>
+~$ <span class="userinput">sudo sv exit .</span>
+~$ <span class="userinput">cd -</span>
+</code></pre>
+</notextile>
+
+Use @sv stat@ and check the log file to verify the service is running.
+
+<notextile>
+<pre><code>~$ <span class="userinput">sudo sv stat /etc/service/arvados-ws</span>
+run: /etc/service/arvados-ws: (pid 12520) 2s; run: log: (pid 12519) 2s
+~$ <span class="userinput">tail /etc/service/arvados-ws/log/main/current</span>
+{"level":"info","msg":"started","time":"2016-12-06T11:56:20.669171449-05:00"}
+{"Listen":":9003","level":"info","msg":"listening","time":"2016-12-06T11:56:20.708847627-05:00"}
+</code></pre>
+</notextile>
+
+h3(#confirm). Confirm the service is working
+
+Confirm the service is listening on its assigned port and responding to requests.
+
+<notextile>
+<pre><code>~$ <span class="userinput">curl http://0.0.0.0:<b>9003</b>/status.json</span>
+{"Clients":1}
+</code></pre>
+</notextile>
+
+h3. Set up a reverse proxy with SSL support
+
+The arvados-ws service will be accessible from anywhere on the internet, so we recommend using SSL for transport encryption.
+
+This is best achieved by putting a reverse proxy with SSL support in front of arvados-ws, running on port 443 and passing requests to arvados-ws on port 9003 (or whatever port you chose in your configuration file).
+
+For example, using Nginx:
+
+<notextile><pre>
+upstream arvados-ws {
+  server                127.0.0.1:<span class="userinput">9003</span>;
+}
+
+server {
+  listen                <span class="userinput">[your public IP address]</span>:443 ssl;
+  server_name           ws.<span class="userinput">uuid_prefix.your.domain</span>;
+
+  proxy_connect_timeout 90s;
+  proxy_read_timeout    300s;
+
+  ssl                   on;
+  ssl_certificate       <span class="userinput"/>YOUR/PATH/TO/cert.pem</span>;
+  ssl_certificate_key   <span class="userinput"/>YOUR/PATH/TO/cert.key</span>;
+
+  location / {
+    proxy_pass          http://arvados-ws;
+    proxy_set_header    Upgrade         $http_upgrade;
+    proxy_set_header    Connection      "upgrade";
+    proxy_set_header    Host            $host;
+    proxy_set_header    X-Forwarded-For $proxy_add_x_forwarded_for;
+  }
+}
+</pre></notextile>
+
+If Nginx is already configured to proxy @ws@ requests to puma, move that configuration out of the way or change its @server_name@ so it doesn't conflict.
+
+h3. Update API server configuration
+
+Ensure the websocket server address is correct in the API server configuration file @/etc/arvados/api/application.yml@.
+
+<notextile>
+<pre><code>websocket_address: wss://ws.<span class="userinput">uuid_prefix.your.domain</span>/websocket
+</code></pre>
+</notextile>
+
+Restart Nginx to reload the API server configuration.
+
+<notextile>
+<pre><code>$ sudo nginx -s reload</span>
+</code></pre>
+</notextile>
+
+h3. Verify DNS and proxy setup
+
+Use a host elsewhere on the Internet to confirm that your DNS, proxy, and SSL are configured correctly.
+
+<notextile>
+<pre><code>$ <span class="userinput">curl https://ws.<b>uuid_prefix.your.domain</b>/status.json</span>
+{"Clients":1}
+</code></pre>
+</notextile>
index 8cde514f68f769a7ab336bb606f0e1ff8743b0c4..4776a97895553efcde7b71f38197d56f6f56db7c 100644 (file)
@@ -3,7 +3,6 @@ layout: default
 navsection: sdk
 navmenu: CLI
 title: "Installation"
-
 ...
 
 Arvados CLI tools are written in Ruby and Python.  To use the @arv@ command, you can either install the @arvados-cli@ gem via RubyGems or build and install the package from source.  The @arv@ command also relies on other Arvados tools.  To get those, install the @arvados-python-client@ and @arvados-cwl-runner@ packages, either from PyPI or source.
diff --git a/doc/sdk/go/example.html.textile.liquid b/doc/sdk/go/example.html.textile.liquid
new file mode 100644 (file)
index 0000000..5fe202d
--- /dev/null
@@ -0,0 +1,76 @@
+---
+layout: default
+navsection: sdk
+navmenu: Python
+title: Examples
+...
+
+See "Arvados GoDoc":https://godoc.org/git.curoverse.com/arvados.git/sdk/go for detailed documentation.
+
+In these examples, the site prefix is @aaaaa@.
+
+h2.  Initialize SDK
+
+<pre>
+import (
+  "git.curoverse.com/arvados.git/sdk/go/arvados"
+  "git.curoverse.com/arvados.git/sdk/go/arvadosclient"
+}
+
+func main() {
+  arv, err := arvadosclient.MakeArvadosClient()
+  if err != nil {
+    log.Fatalf("Error setting up arvados client %s", err.Error())
+  }
+}
+</pre>
+
+h2. create
+
+<pre>
+  var collection arvados.Collection
+  err := api.Create("collections", Dict{"collection": Dict{"name": "create example"}}, &collection)
+</pre>
+
+h2. delete
+
+<pre>
+  var collection arvados.Collection
+  err := api.Delete("collections", "aaaaa-4zz18-ccccccccccccccc", Dict{}, &collection)
+</pre>
+
+h2. get
+
+<pre>
+  var collection arvados.Collection
+  err := api.Get("collections", "aaaaa-4zz18-ccccccccccccccc", Dict{}, &collection)
+</pre>
+
+h2. list
+
+<pre>
+  var collection arvados.Collection
+  err := api.List("collections", Dict{}, &collection)
+</pre>
+
+h2. update
+
+<pre>
+  var collection arvados.Collection
+  err := api.Update("collections", "aaaaa-4zz18-ccccccccccccccc", Dict{"collection": Dict{"name": "update example"}}, &collection)
+</pre>
+
+h2. Get current user
+
+<pre>
+  var user arvados.User
+  err := api.Get("users", "current", Dict{}, &user)
+</pre>
+
+h2. Example program
+
+You can save this source as a .go file and run it:
+
+<notextile>{% code 'example_sdk_go' as go %}</notextile>
+
+A few more usage examples can be found in the "services/keepproxy":https://dev.arvados.org/projects/arvados/repository/revisions/master/show/services/keepproxy and "sdk/go/keepclient":https://dev.arvados.org/projects/arvados/repository/revisions/master/show/sdk/go/keepclient directories in the arvados source tree.
index 24873318da20dea0eaa2582706c7fb03c37ad6e7..81f4f9914bf69290760a60e2db847f3ec0a40a95 100644 (file)
@@ -2,24 +2,17 @@
 layout: default
 navsection: sdk
 navmenu: Go
-title: "Go SDK"
-
+title: "Installation"
 ...
 
 The Go ("Golang":http://golang.org) SDK provides a generic set of wrappers so you can make API calls easily.
 
+See "Arvados GoDoc":https://godoc.org/git.curoverse.com/arvados.git/sdk/go for detailed documentation.
+
 h3. Installation
 
-You don't need to install anything. Just import the client like this. The go tools will fetch the relevant code and dependencies for you.
+Use @go get git.curoverse.com/arvados.git/sdk/go/arvadosclient@.  The go tools will fetch the relevant code and dependencies for you.
 
 <notextile>{% code 'example_sdk_go_imports' as go %}</notextile>
 
 If you need pre-release client code, you can use the latest version from the repo by following "these instructions.":https://dev.arvados.org/projects/arvados/wiki/Go#Using-Go-with-Arvados
-
-h3. Example
-
-You can save this source as a .go file and run it:
-
-<notextile>{% code 'example_sdk_go' as go %}</notextile>
-
-A few more usage examples can be found in the "services/keepproxy":https://dev.arvados.org/projects/arvados/repository/revisions/master/show/services/keepproxy and "sdk/go/keepclient":https://dev.arvados.org/projects/arvados/repository/revisions/master/show/sdk/go/keepclient directories in the arvados source tree.
index db5d6f13b0cbdc08c62f1f156ca70bafdd23e88b..7633228995ba29a199716ec2b983b66e1892583c 100644 (file)
@@ -1,19 +1,16 @@
 ---
 layout: default
 navsection: sdk
-title: "Arvados SDK Reference"
+title: "SDK Reference"
 ...
 
-This section documents how to access the Arvados API and Keep using various programming languages.
+This section documents language bindings for the "Arvados API":{{site.baseurl}}/api and Keep that are available for various programming languages.  Not all features are available in every SDK.  The most complete SDK is the Python SDK.  Note that this section only gives a high level overview of each SDK.  Consult the "Arvados API":{{site.baseurl}}/api section for detailed documentation about Arvados API calls available on each resource.
 
 * "Python SDK":{{site.baseurl}}/sdk/python/sdk-python.html
+* "Command line SDK":{{site.baseurl}}/sdk/cli/install.html ("arv")
+* "Go SDK":{{site.baseurl}}/sdk/go/index.html
 * "Perl SDK":{{site.baseurl}}/sdk/perl/index.html
 * "Ruby SDK":{{site.baseurl}}/sdk/ruby/index.html
 * "Java SDK":{{site.baseurl}}/sdk/java/index.html
-* "Go SDK":{{site.baseurl}}/sdk/go/index.html
-* "Command line SDK":{{site.baseurl}}/sdk/cli/index.html ("arv")
-
-SDKs not yet implemented:
 
-* Rails SDK: Workbench uses an ActiveRecord-like interface to Arvados. This hasn't yet been extracted from Workbench and packaged as a gem.
-* R: We plan to support this, but it has not been implemented yet.
+Many Arvados Workbench pages, under the the *Advanced* tab, provide examples of API and SDK use for accessing the current resource .
diff --git a/doc/sdk/java/example.html.textile.liquid b/doc/sdk/java/example.html.textile.liquid
new file mode 100644 (file)
index 0000000..9ff848b
--- /dev/null
@@ -0,0 +1,78 @@
+---
+layout: default
+navsection: sdk
+navmenu: Java
+title: "Examples"
+...
+
+h2. Initialize SDK
+
+<pre>
+import org.arvados.sdk.Arvados;
+</pre>
+
+<pre>
+    String apiName = "arvados";
+    String apiVersion = "v1";
+
+    Arvados arv = new Arvados(apiName, apiVersion);
+</pre>
+
+h2. create
+
+<pre>
+    Map<String, String> collection = new HashMap<String, String>();
+    collection.put("name", "create example");
+
+    Map<String, Object> params = new HashMap<String, Object>();
+    params.put("collection", collection);
+    Map response = arv.call("collections", "create", params);
+</pre>
+
+h2. delete
+
+<pre>
+    Map<String, Object> params = new HashMap<String, Object>();
+    params.put("uuid", uuid);
+    Map response = arv.call("collections", "delete", params);
+</pre>
+
+h2. get
+
+<pre>
+    params = new HashMap<String, Object>();
+    params.put("uuid", userUuid);
+    Map response = arv.call("users", "get", params);
+</pre>
+
+h2. list
+
+<pre>
+    Map<String, Object> params = new HashMap<String, Object>();
+    Map response = arv.call("users", "list", params);
+
+    // get uuid of the first user from the response
+    List items = (List)response.get("items");
+
+    Map firstUser = (Map)items.get(0);
+    String userUuid = (String)firstUser.get("uuid");
+</pre>
+
+h2. update
+
+<pre>
+    Map<String, String> collection = new HashMap<String, String>();
+    collection.put("name", "update example");
+
+    Map<String, Object> params = new HashMap<String, Object>();
+    params.put("uuid", uuid);
+    params.put("collection", collection);
+    Map response = arv.call("collections", "update", params);
+</pre>
+
+h2. Get current user
+
+<pre>
+    Map<String, Object> params = new HashMap<String, Object>();
+    Map response = arv.call("users", "current", params);
+</pre>
index 48f72d3a7dedf35c48ff219ba29a1a59b17fc4f1..27984b2fe427404e872ac394813d6e1ee218eef2 100644 (file)
@@ -2,8 +2,7 @@
 layout: default
 navsection: sdk
 navmenu: Java
-title: "Java SDK"
-
+title: "Installation"
 ...
 
 The Java SDK provides a generic set of wrappers so you can make API calls in java.
@@ -11,10 +10,10 @@ The Java SDK provides a generic set of wrappers so you can make API calls in jav
 h3. Introdution
 
 * The Java SDK requires Java 6 or later
-  
+
 * The Java SDK is implemented as a maven project. Hence, you would need a working
 maven environment to be able to build the source code. If you do not have maven setup,
-you may find the "Maven in 5 Minutes":http://maven.apache.org/guides/getting-started/maven-in-five-minutes.html link useful. 
+you may find the "Maven in 5 Minutes":http://maven.apache.org/guides/getting-started/maven-in-five-minutes.html link useful.
 
 * In this document $ARVADOS_HOME is used to refer to the directory where
 arvados code is cloned in your system. For ex: $ARVADOS_HOME = $HOME/arvados
@@ -38,7 +37,7 @@ ARVADOS_API_HOST_INSECURE: Set this to true if you are using self-signed
 </notextile>
 
 * Please see "api-tokens":{{site.baseurl}}/user/reference/api-tokens.html for full details.
-         
+
 
 h3. Building the Arvados SDK
 
@@ -124,7 +123,7 @@ The local repository is usually located in your home directory at <code class="u
 <pre>
 In Eclipse IDE:
 Window -> Preferences -> Java -> Build Path -> Classpath Variables
-    Click on the "New..." button and add a new 
+    Click on the "New..." button and add a new
     M2_REPO variable and set it to your local Maven repository
 </pre>
 </notextile>
diff --git a/doc/sdk/perl/example.html.textile.liquid b/doc/sdk/perl/example.html.textile.liquid
new file mode 100644 (file)
index 0000000..980129c
--- /dev/null
@@ -0,0 +1,81 @@
+---
+layout: default
+navsection: sdk
+navmenu: Perl
+title: "Examples"
+...
+
+h2. Initialize SDK
+
+Set up an API client user agent:
+
+<notextile>
+<pre><code class="userinput">
+use Arvados;
+my $arv = Arvados->new('apiVersion' => 'v1');
+</code></pre>
+</notextile>
+
+The SDK retrieves the list of API methods from the server at run time. Therefore, the set of available methods is determined by the server version rather than the SDK version.
+
+h2. create
+
+Create an object:
+
+<notextile>
+<pre><code class="userinput">my $test_link = $arv->{'links'}->{'create'}->execute('link' => { 'link_class' => 'test', 'name' => 'test' });
+</code></pre>
+</notextile>
+
+h2. delete
+
+<notextile>
+<pre><code class="userinput">my $some_user = $arv->{'collections'}->{'get'}->execute('uuid' => $collection_uuid);
+</code></pre>
+</notextile>
+
+h2. get
+
+Retrieve an object by ID:
+
+<notextile>
+<pre><code class="userinput">my $some_user = $arv->{'users'}->{'get'}->execute('uuid' => $current_user_uuid);
+</code></pre>
+</notextile>
+
+Get the UUID of an object that was retrieved using the SDK:
+
+<notextile>
+<pre><code class="userinput">my $current_user_uuid = $current_user->{'uuid'}
+</code></pre>
+</notextile>
+
+h2. list
+
+Get a list of objects:
+
+<notextile>
+<pre><code class="userinput">my $repos = $arv->{'repositories'}->{'list'}->execute;
+print ("UUID of first repo returned is ", $repos->{'items'}->[0], "\n");
+</code></pre>
+</notextile>
+
+h2. update
+
+Update an object:
+
+<notextile>
+<pre><code class="userinput">my $test_link = $arv->{'links'}->{'update'}->execute(
+        'uuid' => $test_link->{'uuid'},
+        'link' => { 'properties' => { 'foo' => 'bar' } });
+</code></pre>
+</notextile>
+
+h2. Get current user
+
+Get the User object for the current user:
+
+<notextile>
+<pre><code class="userinput">my $current_user = $arv->{'users'}->{'current'}->execute;
+</code></pre>
+</notextile>
index e28d02011b8d559e7954bc7c5326a812c42b9adb..407008d1f76eebd6b12a6045551759004efa8b44 100644 (file)
@@ -2,8 +2,7 @@
 layout: default
 navsection: sdk
 navmenu: Perl
-title: "Perl SDK"
-
+title: "Installation"
 ...
 
 The Perl SDK provides a generic set of wrappers so you can make API calls easily.
@@ -63,59 +62,3 @@ EOF</code>
 arvados.v1.users.current.full_name = 'Your Name'
 </pre>
 </notextile>
-
-h3. Examples
-
-Set up an API client user agent:
-
-<notextile>
-<pre><code class="userinput">my $arv = Arvados->new('apiVersion' => 'v1');
-</code></pre>
-</notextile>
-
-Get the User object for the current user:
-
-<notextile>
-<pre><code class="userinput">my $current_user = $arv->{'users'}->{'current'}->execute;
-</code></pre>
-</notextile>
-
-Get the UUID of an object that was retrieved using the SDK:
-
-<notextile>
-<pre><code class="userinput">my $current_user_uuid = $current_user->{'uuid'}
-</code></pre>
-</notextile>
-
-Retrieve an object by ID:
-
-<notextile>
-<pre><code class="userinput">my $some_user = $arv->{'users'}->{'get'}->execute('uuid' => $current_user_uuid);
-</code></pre>
-</notextile>
-
-Create an object:
-
-<notextile>
-<pre><code class="userinput">my $test_link = $arv->{'links'}->{'create'}->execute('link' => { 'link_class' => 'test', 'name' => 'test' });
-</code></pre>
-</notextile>
-
-Update an object:
-
-<notextile>
-<pre><code class="userinput">my $test_link = $arv->{'links'}->{'update'}->execute(
-        'uuid' => $test_link->{'uuid'},
-        'link' => { 'properties' => { 'foo' => 'bar' } });
-</code></pre>
-</notextile>
-
-Get a list of objects:
-
-<notextile>
-<pre><code class="userinput">my $repos = $arv->{'repositories'}->{'list'}->execute;
-print ("UUID of first repo returned is ", $repos->{'items'}->[0], "\n");
-</code></pre>
-</notextile>
-
-The SDK retrieves the list of API methods from the server at run time. Therefore, the set of available methods is determined by the server version rather than the SDK version.
index 6afb9b51a3658efeffb984cb155efca7d37d3b82..0988a7ce9c4c6c05f990a36fd2d07d58662f6647 100644 (file)
@@ -5,7 +5,7 @@ navmenu: Python
 title: Subscribing to events
 ...
 
-Arvados applications can subscribe to a live event stream from the database.  Events are described in the "Log record schema.":{{site.baseurl}}/api/schema/Log.html
+Arvados applications can subscribe to a live event stream from the database.  Events are described in the "Log resource.":{{site.baseurl}}/api/methods/logs.html
 
 <notextile>
 {% code 'events_py' as python %}
diff --git a/doc/sdk/python/example.html.textile.liquid b/doc/sdk/python/example.html.textile.liquid
new file mode 100644 (file)
index 0000000..e91055e
--- /dev/null
@@ -0,0 +1,51 @@
+---
+layout: default
+navsection: sdk
+navmenu: Python
+title: Examples
+...
+
+In these examples, the site prefix is @aaaaa@.
+
+h2.  Initialize SDK
+
+<pre>
+import arvados
+api = arvados.api("v1")
+</pre>
+
+h2. create
+
+<pre>
+result = api.collection().create(body={"collection": {"name": "create example"}}).execute()
+</pre>
+
+h2. delete
+
+<pre>
+result = api.collections().delete(uuid="aaaaa-4zz18-ccccccccccccccc").execute()
+</pre>
+
+h2. get
+
+<pre>
+result = api.collections().get(uuid="aaaaa-4zz18-ccccccccccccccc").execute()
+</pre>
+
+h2. list
+
+<pre>
+result = api.collections().list(filters=[["uuid", "=", "aaaaa-bbbbb-ccccccccccccccc"]]).execute()
+</pre>
+
+h2. update
+
+<pre>
+result = api.collections().update(uuid="aaaaa-4zz18-ccccccccccccccc", body={"collection": {"name": "update example"}}).execute()
+</pre>
+
+h2. Get current user
+
+<pre>
+result = api.users().current().execute()
+</pre>
index 0b0f77d377e209afc7c0f2a7717f9a0360293a6d..e9b560a965d89e5cd2614efd25949ac405bc8620 100644 (file)
@@ -2,8 +2,7 @@
 layout: default
 navsection: sdk
 navmenu: Python
-title: "Python SDK"
-
+title: "Installation"
 ...
 
 The Python SDK provides access from Python to the Arvados API and Keep.  It also includes a number of command line tools for using and administering Arvados and Keep, and some conveniences for use in Crunch scripts; see "Crunch utility libraries":crunch-utility-libraries.html for details.
diff --git a/doc/sdk/ruby/example.html.textile.liquid b/doc/sdk/ruby/example.html.textile.liquid
new file mode 100644 (file)
index 0000000..409a70a
--- /dev/null
@@ -0,0 +1,75 @@
+---
+layout: default
+navsection: sdk
+navmenu: Python
+title: Examples
+...
+
+h2.  Initialize SDK
+
+Import the module and set up an API client user agent:
+
+<pre>
+require 'arvados'
+arv = Arvados.new(apiVersion: 'v1')
+</pre>
+
+The SDK retrieves the list of API methods from the server at run time. Therefore, the set of available methods is determined by the server version rather than the SDK version.
+
+h2. create
+
+Create an object:
+
+<pre>
+new_link = arv.link.create(link: {link_class: 'test', name: 'test'})
+</pre>
+
+h2. delete
+
+Delete an object:
+
+<pre>
+arv.link.delete(uuid: new_link[:uuid])
+</pre>
+
+h2. get
+
+Retrieve an object by ID:
+
+<pre>
+some_user = arv.user.get(uuid: current_user_uuid)
+</pre>
+
+h2. list
+
+Get a list of objects:
+
+<pre>
+repos = arv.repository.list
+first_repo = repos[:items][0]
+puts "UUID of first repo returned is #{first_repo[:uuid]}"</code>
+UUID of first repo returned is qr1hi-s0uqq-b1bnybpx3u5temz
+</pre>
+
+h2. update
+
+Update an object:
+
+<pre>
+updated_link = arv.link.update(uuid: new_link[:uuid],
+                               link: {properties: {foo: 'bar'}})
+</pre>
+
+h2. Get current user
+
+Get the User object for the current user:
+
+<pre>
+current_user = arv.user.current
+</pre>
+
+Get the UUID of an object that was retrieved using the SDK:
+
+<pre>
+current_user_uuid = current_user[:uuid]
+</pre>
index b78a37d03a8ff49c7bae98c303f9fa9b897b7e58..5ad02543ef95cce5f1e960e604f623bbd33a3503 100644 (file)
@@ -2,8 +2,7 @@
 layout: default
 navsection: sdk
 navmenu: Ruby
-title: "Ruby SDK"
-
+title: "Installation"
 ...
 
 The Ruby SDK provides a generic set of wrappers so you can make API calls easily.
@@ -52,74 +51,3 @@ EOF</code>
 arvados.v1.users.current.full_name = 'Your Name'
 </pre>
 </notextile>
-
-h3. Examples
-
-Import the module (we skipped this step above by using "ruby -r arvados"):
-
-<notextile>
-<pre><code class="userinput">require 'arvados'
-</code></pre>
-</notextile>
-
-Set up an API client user agent:
-
-<notextile>
-<pre><code class="userinput">arv = Arvados.new(apiVersion: 'v1')
-</code></pre>
-</notextile>
-
-Get the User object for the current user:
-
-<notextile>
-<pre><code class="userinput">current_user = arv.user.current
-</code></pre>
-</notextile>
-
-Get the UUID of an object that was retrieved using the SDK:
-
-<notextile>
-<pre><code class="userinput">current_user_uuid = current_user[:uuid]
-</code></pre>
-</notextile>
-
-Retrieve an object by ID:
-
-<notextile>
-<pre><code class="userinput">some_user = arv.user.get(uuid: current_user_uuid)
-</code></pre>
-</notextile>
-
-Create an object:
-
-<notextile>
-<pre><code class="userinput">new_link = arv.link.create(link: {link_class: 'test', name: 'test'})
-</code></pre>
-</notextile>
-
-Update an object:
-
-<notextile>
-<pre><code class="userinput">updated_link = arv.link.update(uuid: new_link[:uuid],
-                               link: {properties: {foo: 'bar'}})
-</code></pre>
-</notextile>
-
-Delete an object:
-
-<notextile>
-<pre><code class="userinput">arv.link.delete(uuid: new_link[:uuid])
-</code></pre>
-</notextile>
-
-Get a list of objects:
-
-<notextile>
-<pre><code class="userinput">repos = arv.repository.list
-first_repo = repos[:items][0]
-puts "UUID of first repo returned is #{first_repo[:uuid]}"</code>
-UUID of first repo returned is qr1hi-s0uqq-b1bnybpx3u5temz
-</pre>
-</notextile>
-
-The SDK retrieves the list of API methods from the server at run time. Therefore, the set of available methods is determined by the server version rather than the SDK version.
diff --git a/doc/user/cwl/cwl-extensions.html.textile.liquid b/doc/user/cwl/cwl-extensions.html.textile.liquid
new file mode 100644 (file)
index 0000000..8a7e64b
--- /dev/null
@@ -0,0 +1,56 @@
+---
+layout: default
+navsection: userguide
+title: Arvados CWL Extensions
+...
+
+Arvados provides several extensions to CWL for workflow optimization, site-specific configuration, and to enable access the Arvados API.
+
+To use Arvados CWL extensions, add the following @$namespaces@ section at the top of your CWL file:
+
+<pre>
+$namespaces:
+  arv: "http://arvados.org/cwl#"
+</pre>
+
+Arvados extensions must go into the @hints@ section, for example:
+
+<pre>
+hints:
+  arv:RunInSingleContainer: {}
+  arv:RuntimeConstraints:
+    keep_cache: 123456
+    keep_output_dir: local_output_dir
+  arv:PartitionRequirement:
+    partition: dev_partition
+  arv:APIRequirement: {}
+</pre>
+
+h2. arv:RunInSingleContainer
+
+Indicates that a subworkflow should run in a single container and not be scheduled as separate steps.
+
+h2. arv:RuntimeConstraints
+
+Set Arvados-specific runtime hints.
+
+table(table table-bordered table-condensed).
+|_. Field |_. Type |_. Description |
+|keep_cache|int|Size of file data buffer for Keep mount in MiB. Default is 256 MiB. Increase this to reduce cache thrashing in situations such as accessing multiple large (64+ MiB) files at the same time, or performing random access on a large file.|
+|outputDirType|enum|Preferred backing store for output staging.  If not specified, the system may choose which one to use.  One of *local_output_dir* or *keep_output_dir*|
+
+*local_output_dir*: Use regular file system local to the compute node. There must be sufficient local scratch space to store entire output; specify this with @outdirMin@ of @ResourceRequirement@.  Files are batch uploaded to Keep when the process completes.  Most compatible, but upload step can be time consuming for very large files.
+
+*keep_output_dir*: Use writable Keep mount.  Files are streamed to Keep as they are written.  Does not consume local scratch space, but does consume RAM for output buffers (up to 192 MiB per file simultaneously open for writing.)  Best suited to processes which produce sequential output of large files (non-sequential writes may produced fragmented file manifests).  Supports regular files and directories, does not support special files such as symlinks, hard links, named pipes, named sockets, or device nodes.|
+
+h2. arv:PartitionRequirement
+
+Select preferred compute partitions on which to run jobs.
+
+table(table table-bordered table-condensed).
+|_. Field |_. Type |_. Description |
+|partition|string or array of strings||
+
+h2. arv:APIRequirement
+
+Indicates that process wants to access to the Arvados API.  Will be granted limited network access and have @ARVADOS_API_HOST@ and @ARVADOS_API_TOKEN@ set in the environment.
diff --git a/doc/user/cwl/cwl-run-options.html.textile.liquid b/doc/user/cwl/cwl-run-options.html.textile.liquid
new file mode 100644 (file)
index 0000000..c9b18e6
--- /dev/null
@@ -0,0 +1,94 @@
+---
+layout: default
+navsection: userguide
+title: "Using arvados-cwl-runner"
+...
+
+The following command line options are available for @arvados-cwl-runner@:
+
+table(table table-bordered table-condensed).
+|_. Option |_. Description |
+|==--basedir== BASEDIR|     Base directory used to resolve relative references in the input, default to directory of input object file or current directory (if inputs piped/provided on command line).|
+|==--version==|             Print version and exit|
+|==--verbose==|             Default logging|
+|==--quiet==|               Only print warnings and errors.|
+|==--debug==|               Print even more logging|
+|==--tool-help==|           Print command line help for tool|
+|==--enable-reuse==|Enable job reuse (default)|
+|==--disable-reuse==|Disable job reuse (always run new jobs).|
+|==--project-uuid UUID==|   Project that will own the workflow jobs, if not provided, will go to home project.|
+|==--output-name OUTPUT_NAME==|Name to use for collection that stores the final output.|
+|==--output-tags OUTPUT_TAGS==|Tags for the final output collection separated by commas, e.g., =='--output-tags tag0,tag1,tag2'==.|
+|==--ignore-docker-for-reuse==|Ignore Docker image version when deciding whether to reuse past jobs.|
+|==--submit==|              Submit workflow to run on Arvados.|
+|==--local==|               Control workflow from local host (submits jobs to Arvados).|
+|==--create-template==|     (Deprecated) synonym for ==--create-workflow.==|
+|==--create-workflow==|     Create an Arvados workflow (if using the 'containers' API) or pipeline template (if using the 'jobs' API). See ==--api==.|
+|==--update-workflow== UUID|Update an existing Arvados workflow or pipeline template with the given UUID.|
+|==--wait==|                After submitting workflow runner job, wait for completion.|
+|==--no-wait==|             Submit workflow runner job and exit.|
+|==--api== WORK_API|        Select work submission API, one of 'jobs' or 'containers'. Default is 'jobs' if that API is available, otherwise 'containers'.|
+|==--compute-checksum==|    Compute checksum of contents while collecting outputs|
+|==--submit-runner-ram== SUBMIT_RUNNER_RAM|RAM (in MiB) required for the workflow runner job (default 1024)|
+|==--name== NAME|           Name to use for workflow execution instance.|
+
+h3. Specify workflow and output names
+
+Use the @--name@ and @--output-name@ options to specify the name of the workflow and name of the output collection.
+
+<notextile>
+<pre><code>~/arvados/doc/user/cwl/bwa-mem$ <span class="userinput">arvados-cwl-runner --name "Example bwa run" --output-name "Example bwa output" bwa-mem.cwl bwa-mem-input.yml</span>
+arvados-cwl-runner 1.0.20160628195002, arvados-python-client 0.1.20160616015107, cwltool 1.0.20160629140624
+2016-06-30 14:56:36 arvados.arv-run[27002] INFO: Upload local files: "bwa-mem.cwl"
+2016-06-30 14:56:36 arvados.arv-run[27002] INFO: Uploaded to qr1hi-4zz18-h7ljh5u76760ww2
+2016-06-30 14:56:40 arvados.cwl-runner[27002] INFO: Submitted job qr1hi-8i9sb-fm2n3b1w0l6bskg
+2016-06-30 14:56:41 arvados.cwl-runner[27002] INFO: Job bwa-mem.cwl (qr1hi-8i9sb-fm2n3b1w0l6bskg) is Running
+2016-06-30 14:57:12 arvados.cwl-runner[27002] INFO: Job bwa-mem.cwl (qr1hi-8i9sb-fm2n3b1w0l6bskg) is Complete
+2016-06-30 14:57:12 arvados.cwl-runner[27002] INFO: Overall process status is success
+{
+    "aligned_sam": {
+        "path": "keep:54325254b226664960de07b3b9482349+154/HWI-ST1027_129_D0THKACXX.1_1.sam",
+        "checksum": "sha1$0dc46a3126d0b5d4ce213b5f0e86e2d05a54755a",
+        "class": "File",
+        "size": 30738986
+    }
+}
+</code></pre>
+</notextile>
+
+h3. Submit a workflow with no waiting
+
+To submit a workflow and exit immediately, use the @--no-wait@ option.  This will submit the workflow to Arvados, print out the UUID of the job that was submitted to standard output, and exit.
+
+<notextile>
+<pre><code>~/arvados/doc/user/cwl/bwa-mem$ <span class="userinput">arvados-cwl-runner --no-wait bwa-mem.cwl bwa-mem-input.yml</span>
+arvados-cwl-runner 1.0.20160628195002, arvados-python-client 0.1.20160616015107, cwltool 1.0.20160629140624
+2016-06-30 15:07:52 arvados.arv-run[12480] INFO: Upload local files: "bwa-mem.cwl"
+2016-06-30 15:07:52 arvados.arv-run[12480] INFO: Uploaded to qr1hi-4zz18-eqnfwrow8aysa9q
+2016-06-30 15:07:52 arvados.cwl-runner[12480] INFO: Submitted job qr1hi-8i9sb-fm2n3b1w0l6bskg
+qr1hi-8i9sb-fm2n3b1w0l6bskg
+</code></pre>
+</notextile>
+
+h3. Control a workflow locally
+
+To run a workflow with local control, use @--local@.  This means that the host where you run @arvados-cwl-runner@ will be responsible for submitting jobs, however, the jobs themselves will still run on the Arvados cluster.  With @--local@, if you interrupt @arvados-cwl-runner@ or log out, the workflow will be terminated.
+
+<notextile>
+<pre><code>~/arvados/doc/user/cwl/bwa-mem$ <span class="userinput">arvados-cwl-runner --local bwa-mem.cwl bwa-mem-input.yml</span>
+arvados-cwl-runner 1.0.20160628195002, arvados-python-client 0.1.20160616015107, cwltool 1.0.20160629140624
+2016-07-01 10:05:19 arvados.cwl-runner[16290] INFO: Pipeline instance qr1hi-d1hrv-92wcu6ldtio74r4
+2016-07-01 10:05:28 arvados.cwl-runner[16290] INFO: Job bwa-mem.cwl (qr1hi-8i9sb-2nzzfbuf9zjrj4g) is Queued
+2016-07-01 10:05:29 arvados.cwl-runner[16290] INFO: Job bwa-mem.cwl (qr1hi-8i9sb-2nzzfbuf9zjrj4g) is Running
+2016-07-01 10:05:45 arvados.cwl-runner[16290] INFO: Job bwa-mem.cwl (qr1hi-8i9sb-2nzzfbuf9zjrj4g) is Complete
+2016-07-01 10:05:46 arvados.cwl-runner[16290] INFO: Overall process status is success
+{
+    "aligned_sam": {
+        "size": 30738986,
+        "path": "keep:15f56bad0aaa7364819bf14ca2a27c63+88/HWI-ST1027_129_D0THKACXX.1_1.sam",
+        "checksum": "sha1$0dc46a3126d0b5d4ce213b5f0e86e2d05a54755a",
+        "class": "File"
+    }
+}
+</code></pre>
+</notextile>
index c00f475ad9d0c43135b5b0f04db6569b7bb91aee..7c75c1c453ad8bd4e70107d9c5866f6e10137b10 100644 (file)
@@ -1,53 +1,26 @@
 ---
 layout: default
 navsection: userguide
-title: Using Common Workflow Language
+title: "Running an Arvados workflow"
 ...
 
-The "Common Workflow Language (CWL)":http://commonwl.org is a multi-vendor open standard for describing analysis tools and workflows that are portable across a variety of platforms.  CWL is the recommended way to develop and run workflows for Arvados.  Arvados supports the "CWL v1.0":http://commonwl.org/v1.0 specification.
+{% include 'what_is_cwl' %}
 
 {% include 'tutorial_expectations' %}
 
-h2. Setting up
+{% include 'notebox_begin' %}
 
-The @arvados-cwl-runner@ client is installed by default on Arvados shell nodes.  However, if you do not have @arvados-cwl-runner@, you may install it using @pip@:
+By default, the @arvados-cwl-runner@ is installed on Arvados shell nodes.  If you want to submit jobs from somewhere else, such as your workstation, you may install "arvados-cwl-runner.":#setup
 
-<notextile>
-<pre><code>~$ <span class="userinput">virtualenv ~/venv</span>
-~$ <span class="userinput">. ~/venv/bin/activate</span>
-~$ <span class="userinput">pip install arvados-cwl-runner</span>
-</code></pre>
-</notextile>
+{% include 'notebox_end' %}
 
-h3. Docker
+This tutorial will demonstrate how to submit a workflow at the command line using @arvados-cwl-runner@.
 
-Certain features of @arvados-cwl-runner@ require access to Docker.  You can determine if you have access to Docker by running @docker version@:
+h2. Running arvados-cwl-runner
 
-<notextile>
-<pre><code>~$ <span class="userinput">docker version</span>
-Client:
- Version:      1.9.1
- API version:  1.21
- Go version:   go1.4.2
- Git commit:   a34a1d5
- Built:        Fri Nov 20 12:59:02 UTC 2015
- OS/Arch:      linux/amd64
+h3. Get the example files
 
-Server:
- Version:      1.9.1
- API version:  1.21
- Go version:   go1.4.2
- Git commit:   a34a1d5
- Built:        Fri Nov 20 12:59:02 UTC 2015
- OS/Arch:      linux/amd64
-</code></pre>
-</notextile>
-
-If this returns an error, contact the sysadmin of your cluster for assistance.  Alternatively, if you have Docker installed on your local workstation, you may follow the instructions above to install @arvados-cwl-runner@.
-
-h3. Getting the example files
-
-The tutorial files are located in the documentation section of the Arvados source repository:
+The tutorial files are located in the "documentation section of the Arvados source repository:":https://github.com/curoverse/arvados/tree/master/doc/user/cwl/bwa-mem
 
 <notextile>
 <pre><code>~$ <span class="userinput">git clone https://github.com/curoverse/arvados</span>
@@ -60,6 +33,7 @@ The tutorial data is hosted on "https://cloud.curoverse.com":https://cloud.curov
 <notextile>
 <pre><code>~$ <span class="userinput">arv-copy --src qr1hi --dst settings 2463fa9efeb75e099685528b3b9071e0+438</span>
 ~$ <span class="userinput">arv-copy --src qr1hi --dst settings ae480c5099b81e17267b7445e35b4bc7+180</span>
+~$ <span class="userinput">arv-copy --src qr1hi --dst settings 655c6cd07550151b210961ed1d3852cf+57</span>
 </code></pre>
 </notextile>
 
@@ -69,9 +43,15 @@ If you do not wish to create an account on "https://cloud.curoverse.com":https:/
 
 "https://cloud.curoverse.com/collections/ae480c5099b81e17267b7445e35b4bc7+180":https://cloud.curoverse.com/collections/ae480c5099b81e17267b7445e35b4bc7+180
 
+"https://cloud.curoverse.com/collections/655c6cd07550151b210961ed1d3852cf+57":https://cloud.curoverse.com/collections/655c6cd07550151b210961ed1d3852cf+57
+
 h2. Submitting a workflow to an Arvados cluster
 
-Use @arvados-cwl-runner@ to submit CWL workflows to Arvados.  After submitting the job, it will wait for the workflow to complete and print out the final result to standard output.  Note that once submitted, the workflow runs entirely on Arvados, so even if you interrupt @arvados-cwl-runner@ or log out, the workflow will continue to run.
+h3. Submit a workflow and wait for results
+
+Use @arvados-cwl-runner@ to submit CWL workflows to Arvados.  After submitting the job, it will wait for the workflow to complete and print out the final result to standard output.
+
+*Note:* Once submitted, the workflow runs entirely on Arvados, so even if you log out, the workflow will continue to run.  However, if you interrupt @arvados-cwl-runner@ with control-C it will cancel the workflow.
 
 <notextile>
 <pre><code>~/arvados/doc/user/cwl/bwa-mem$ <span class="userinput">arvados-cwl-runner bwa-mem.cwl bwa-mem-input.yml</span>
@@ -93,44 +73,7 @@ arvados-cwl-runner 1.0.20160628195002, arvados-python-client 0.1.20160616015107,
 </code></pre>
 </notextile>
 
-To submit a workflow and exit immediately, use the @--no-wait@ option.  This will print out the uuid of the job that was submitted to standard output.
-
-<notextile>
-<pre><code>~/arvados/doc/user/cwl/bwa-mem$ <span class="userinput">arvados-cwl-runner --no-wait bwa-mem.cwl bwa-mem-input.yml</span>
-arvados-cwl-runner 1.0.20160628195002, arvados-python-client 0.1.20160616015107, cwltool 1.0.20160629140624
-2016-06-30 15:07:52 arvados.arv-run[12480] INFO: Upload local files: "bwa-mem.cwl"
-2016-06-30 15:07:52 arvados.arv-run[12480] INFO: Uploaded to qr1hi-4zz18-eqnfwrow8aysa9q
-2016-06-30 15:07:52 arvados.cwl-runner[12480] INFO: Submitted job qr1hi-8i9sb-fm2n3b1w0l6bskg
-qr1hi-8i9sb-fm2n3b1w0l6bskg
-</code></pre>
-</notextile>
-
-To run a workflow with local control, use @--local@.  This means that the host where you run @arvados-cwl-runner@ will be responsible for submitting jobs. With @--local@, if you interrupt @arvados-cwl-runner@ or log out, the workflow will be terminated.
-
-<notextile>
-<pre><code>~/arvados/doc/user/cwl/bwa-mem$ <span class="userinput">arvados-cwl-runner --local bwa-mem.cwl bwa-mem-input.yml</span>
-arvados-cwl-runner 1.0.20160628195002, arvados-python-client 0.1.20160616015107, cwltool 1.0.20160629140624
-2016-07-01 10:05:19 arvados.cwl-runner[16290] INFO: Pipeline instance qr1hi-d1hrv-92wcu6ldtio74r4
-2016-07-01 10:05:28 arvados.cwl-runner[16290] INFO: Job bwa-mem.cwl (qr1hi-8i9sb-2nzzfbuf9zjrj4g) is Queued
-2016-07-01 10:05:29 arvados.cwl-runner[16290] INFO: Job bwa-mem.cwl (qr1hi-8i9sb-2nzzfbuf9zjrj4g) is Running
-2016-07-01 10:05:45 arvados.cwl-runner[16290] INFO: Job bwa-mem.cwl (qr1hi-8i9sb-2nzzfbuf9zjrj4g) is Complete
-2016-07-01 10:05:46 arvados.cwl-runner[16290] INFO: Overall process status is success
-{
-    "aligned_sam": {
-        "size": 30738986,
-        "path": "keep:15f56bad0aaa7364819bf14ca2a27c63+88/HWI-ST1027_129_D0THKACXX.1_1.sam",
-        "checksum": "sha1$0dc46a3126d0b5d4ce213b5f0e86e2d05a54755a",
-        "class": "File"
-    }
-}
-</code></pre>
-</notextile>
-
-h2. Work reuse
-
-Workflows submitted with @arvados-cwl-runner@ will take advantage of Arvados job reuse.  If you submit a workflow which is identical to one that has run before, it will short cut the execution and return the result of the previous run.  This also applies to individual workflow steps.  For example, a two step workflow where the first step has run before will reuse results for first step and only execute the new second step.  You can disable this behavior with @--disable-reuse@.
-
-h2. Referencing files
+h3. Referencing files
 
 When running a workflow on an Arvados cluster, the input files must be stored in Keep.  There are several ways this can happen.
 
@@ -140,45 +83,11 @@ If you reference a file in "arv-mount":{{site.baseurl}}/user/tutorials/tutorial-
 
 If you reference a local file which is not in @arv-mount@, then @arvados-cwl-runner@ will upload the file to Keep and use the Keep URI reference from the upload.
 
-h2. Registering a workflow with Workbench
-
-Use @--create-template@ to register a CWL workflow with Arvados Workbench.  This enables you to run workflows by clicking on the <span class="btn btn-sm btn-primary"><i class="fa fa-fw fa-gear"></i> Run a pipeline...</span> on the Workbench Dashboard.
-
-<notextile>
-<pre><code>~/arvados/doc/user/cwl/bwa-mem$ <span class="userinput">arvados-cwl-runner --create-template bwa-mem.cwl</span>
-arvados-cwl-runner 1.0.20160628195002, arvados-python-client 0.1.20160616015107, cwltool 1.0.20160629140624
-2016-07-01 12:21:01 arvados.arv-run[15796] INFO: Upload local files: "bwa-mem.cwl"
-2016-07-01 12:21:01 arvados.arv-run[15796] INFO: Uploaded to qr1hi-4zz18-7e0hedrmkuyoei3
-2016-07-01 12:21:01 arvados.cwl-runner[15796] INFO: Created template qr1hi-p5p6p-rjleou1dwr167v5
-qr1hi-p5p6p-rjleou1dwr167v5
-</code></pre>
-</notextile>
-
-You can provide a partial input file to set default values for the workflow input parameters:
-
-<notextile>
-<pre><code>~/arvados/doc/user/cwl/bwa-mem$ <span class="userinput">arvados-cwl-runner --create-template bwa-mem.cwl bwa-mem-template.yml</span>
-arvados-cwl-runner 1.0.20160628195002, arvados-python-client 0.1.20160616015107, cwltool 1.0.20160629140624
-2016-07-01 14:09:50 arvados.arv-run[3730] INFO: Upload local files: "bwa-mem.cwl"
-2016-07-01 14:09:50 arvados.arv-run[3730] INFO: Uploaded to qr1hi-4zz18-0f91qkovk4ml18o
-2016-07-01 14:09:50 arvados.cwl-runner[3730] INFO: Created template qr1hi-p5p6p-0deqe6nuuyqns2i
-qr1hi-p5p6p-0deqe6nuuyqns2i
-</code></pre>
-</notextile>
-
-h2. Making workflows directly executable
-
-You can make a workflow file directly executable (@cwl-runner@ should be an alias to @arvados-cwl-runner@) by adding the following line to the top of the file:
+You can also execute CWL files directly from Keep:
 
 <notextile>
-<pre><code>#!/usr/bin/env cwl-runner
-</code></pre>
-</notextile>
-
-<notextile>
-<pre><code>~/arvados/doc/user/cwl/bwa-mem$ <span class="userinput">./bwa-mem.cwl bwa-mem-input.yml</span>
+<pre><code>~/arvados/doc/user/cwl/bwa-mem$ <span class="userinput">arvados-cwl-runner keep:655c6cd07550151b210961ed1d3852cf+57/bwa-mem.cwl bwa-mem-input.yml</span>
 arvados-cwl-runner 1.0.20160628195002, arvados-python-client 0.1.20160616015107, cwltool 1.0.20160629140624
-2016-06-30 14:56:36 arvados.arv-run[27002] INFO: Upload local files: "bwa-mem.cwl"
 2016-06-30 14:56:36 arvados.arv-run[27002] INFO: Uploaded to qr1hi-4zz18-h7ljh5u76760ww2
 2016-06-30 14:56:40 arvados.cwl-runner[27002] INFO: Submitted job qr1hi-8i9sb-fm2n3b1w0l6bskg
 2016-06-30 14:56:41 arvados.cwl-runner[27002] INFO: Job bwa-mem.cwl (qr1hi-8i9sb-fm2n3b1w0l6bskg) is Running
@@ -195,103 +104,50 @@ arvados-cwl-runner 1.0.20160628195002, arvados-python-client 0.1.20160616015107,
 </code></pre>
 </notextile>
 
-You can even make an input file directly executable the same way with the following two lines at the top:
+h3. Work reuse
 
-<notextile>
-<pre><code>#!/usr/bin/env cwl-runner
-cwl:tool: <span class="userinput">bwa-mem.cwl</span>
-</code></pre>
-</notextile>
+Workflows submitted with @arvados-cwl-runner@ will take advantage of Arvados job reuse.  If you submit a workflow which is identical to one that has run before, it will short cut the execution and return the result of the previous run.  This also applies to individual workflow steps.  For example, a two step workflow where the first step has run before will reuse results for first step and only execute the new second step.  You can disable this behavior with @--disable-reuse@.
+
+h3. Command line options
+
+See "Using arvados-cwl-runner":{{site.baseurl}}/user/cwl/cwl-run-options.html
+
+h2(#setup). Setting up arvados-cwl-runner
+
+By default, the @arvados-cwl-runner@ is installed on Arvados shell nodes.  If you want to submit jobs from somewhere else, such as your workstation, you may install @arvados-cwl-runner@ using @pip@:
 
 <notextile>
-<pre><code>~/arvados/doc/user/cwl/bwa-mem$ <span class="userinput">./bwa-mem-input.yml</span>
-arvados-cwl-runner 1.0.20160628195002, arvados-python-client 0.1.20160616015107, cwltool 1.0.20160629140624
-2016-06-30 14:56:36 arvados.arv-run[27002] INFO: Upload local files: "bwa-mem.cwl"
-2016-06-30 14:56:36 arvados.arv-run[27002] INFO: Uploaded to qr1hi-4zz18-h7ljh5u76760ww2
-2016-06-30 14:56:40 arvados.cwl-runner[27002] INFO: Submitted job qr1hi-8i9sb-fm2n3b1w0l6bskg
-2016-06-30 14:56:41 arvados.cwl-runner[27002] INFO: Job bwa-mem.cwl (qr1hi-8i9sb-fm2n3b1w0l6bskg) is Running
-2016-06-30 14:57:12 arvados.cwl-runner[27002] INFO: Job bwa-mem.cwl (qr1hi-8i9sb-fm2n3b1w0l6bskg) is Complete
-2016-06-30 14:57:12 arvados.cwl-runner[27002] INFO: Overall process status is success
-{
-    "aligned_sam": {
-        "path": "keep:54325254b226664960de07b3b9482349+154/HWI-ST1027_129_D0THKACXX.1_1.sam",
-        "checksum": "sha1$0dc46a3126d0b5d4ce213b5f0e86e2d05a54755a",
-        "class": "File",
-        "size": 30738986
-    }
-}
+<pre><code>~$ <span class="userinput">virtualenv ~/venv</span>
+~$ <span class="userinput">. ~/venv/bin/activate</span>
+~$ <span class="userinput">pip install -U setuptools</span>
+~$ <span class="userinput">pip install arvados-cwl-runner</span>
 </code></pre>
 </notextile>
 
-h2. Developing workflows
+h3. Check Docker access
 
-For an introduction and and detailed documentation about writing CWL, see the "User Guide":http://commonwl.org/v1.0/UserGuide.html and the "Specification":http://commonwl.org/v1.0 .
+In order to pull and upload Docker images, @arvados-cwl-runner@ requires access to Docker.  You do not need Docker if the Docker images you intend to use are already available in Aravdos.
 
-To run on Arvados, a workflow should provide a @DockerRequirement@ in the @hints@ section.
-
-When developing a workflow, it is often helpful to run it on the local host to avoid the overhead of submitting to the cluster.  To execute a workflow only on the local host (without submitting jobs to an Arvados cluster) you can use the @cwltool@ command.  Note that you must also have the input data accessible on the local host.  You can use @arv-get@ to fetch the data from Keep.
+You can determine if you have access to Docker by running @docker version@:
 
 <notextile>
-<pre><code>~/arvados/doc/user/cwl/bwa-mem$ <span class="userinput">arv-get 2463fa9efeb75e099685528b3b9071e0+438/ .</span>
-156 MiB / 156 MiB 100.0%
-~/arvados/doc/user/cwl/bwa-mem$ <span class="userinput">arv-get ae480c5099b81e17267b7445e35b4bc7+180/ .</span>
-23 MiB / 23 MiB 100.0%
-~/arvados/doc/user/cwl/bwa-mem$ <span class="userinput">cwltool bwa-mem-input.yml bwa-mem-input-local.yml</span>
-cwltool 1.0.20160629140624
-[job bwa-mem.cwl] /home/example/arvados/doc/user/cwl/bwa-mem$ docker \
-    run \
-    -i \
-    --volume=/home/example/arvados/doc/user/cwl/bwa-mem/19.fasta.ann:/var/lib/cwl/job979368791_bwa-mem/19.fasta.ann:ro \
-    --volume=/home/example/arvados/doc/user/cwl/bwa-mem/HWI-ST1027_129_D0THKACXX.1_1.fastq:/var/lib/cwl/job979368791_bwa-mem/HWI-ST1027_129_D0THKACXX.1_1.fastq:ro \
-    --volume=/home/example/arvados/doc/user/cwl/bwa-mem/19.fasta.sa:/var/lib/cwl/job979368791_bwa-mem/19.fasta.sa:ro \
-    --volume=/home/example/arvados/doc/user/cwl/bwa-mem/19.fasta.amb:/var/lib/cwl/job979368791_bwa-mem/19.fasta.amb:ro \
-    --volume=/home/example/arvados/doc/user/cwl/bwa-mem/19.fasta.pac:/var/lib/cwl/job979368791_bwa-mem/19.fasta.pac:ro \
-    --volume=/home/example/arvados/doc/user/cwl/bwa-mem/HWI-ST1027_129_D0THKACXX.1_2.fastq:/var/lib/cwl/job979368791_bwa-mem/HWI-ST1027_129_D0THKACXX.1_2.fastq:ro \
-    --volume=/home/example/arvados/doc/user/cwl/bwa-mem/19.fasta.bwt:/var/lib/cwl/job979368791_bwa-mem/19.fasta.bwt:ro \
-    --volume=/home/example/arvados/doc/user/cwl/bwa-mem:/var/spool/cwl:rw \
-    --volume=/tmp/tmpgzyou9:/tmp:rw \
-    --workdir=/var/spool/cwl \
-    --read-only=true \
-    --log-driver=none \
-    --user=1001 \
-    --rm \
-    --env=TMPDIR=/tmp \
-    --env=HOME=/var/spool/cwl \
-    biodckr/bwa \
-    bwa \
-    mem \
-    -t \
-    1 \
-    -R \
-    '@RG       ID:arvados_tutorial     PL:illumina     SM:HWI-ST1027_129' \
-    /var/lib/cwl/job979368791_bwa-mem/19.fasta \
-    /var/lib/cwl/job979368791_bwa-mem/HWI-ST1027_129_D0THKACXX.1_1.fastq \
-    /var/lib/cwl/job979368791_bwa-mem/HWI-ST1027_129_D0THKACXX.1_2.fastq > /home/example/arvados/doc/user/cwl/bwa-mem/HWI-ST1027_129_D0THKACXX.1_1.sam
-[M::bwa_idx_load_from_disk] read 0 ALT contigs
-[M::process] read 100000 sequences (10000000 bp)...
-[M::mem_pestat] # candidate unique pairs for (FF, FR, RF, RR): (0, 4745, 1, 0)
-[M::mem_pestat] skip orientation FF as there are not enough pairs
-[M::mem_pestat] analyzing insert size distribution for orientation FR...
-[M::mem_pestat] (25, 50, 75) percentile: (154, 181, 214)
-[M::mem_pestat] low and high boundaries for computing mean and std.dev: (34, 334)
-[M::mem_pestat] mean and std.dev: (185.63, 44.88)
-[M::mem_pestat] low and high boundaries for proper pairs: (1, 394)
-[M::mem_pestat] skip orientation RF as there are not enough pairs
-[M::mem_pestat] skip orientation RR as there are not enough pairs
-[M::mem_process_seqs] Processed 100000 reads in 9.848 CPU sec, 9.864 real sec
-[main] Version: 0.7.12-r1039
-[main] CMD: bwa mem -t 1 -R @RG        ID:arvados_tutorial     PL:illumina     SM:HWI-ST1027_129 /var/lib/cwl/job979368791_bwa-mem/19.fasta /var/lib/cwl/job979368791_bwa-mem/HWI-ST1027_129_D0THKACXX.1_1.fastq /var/lib/cwl/job979368791_bwa-mem/HWI-ST1027_129_D0THKACXX.1_2.fastq
-[main] Real time: 10.061 sec; CPU: 10.032 sec
-Final process status is success
-{
-    "aligned_sam": {
-        "size": 30738959,
-        "path": "/home/example/arvados/doc/user/cwl/bwa-mem/HWI-ST1027_129_D0THKACXX.1_1.sam",
-        "checksum": "sha1$0c668cca45fef02397bb5302880526d300ee4dac",
-        "class": "File"
-    }
-}
+<pre><code>~$ <span class="userinput">docker version</span>
+Client:
+ Version:      1.9.1
+ API version:  1.21
+ Go version:   go1.4.2
+ Git commit:   a34a1d5
+ Built:        Fri Nov 20 12:59:02 UTC 2015
+ OS/Arch:      linux/amd64
+
+Server:
+ Version:      1.9.1
+ API version:  1.21
+ Go version:   go1.4.2
+ Git commit:   a34a1d5
+ Built:        Fri Nov 20 12:59:02 UTC 2015
+ OS/Arch:      linux/amd64
 </code></pre>
 </notextile>
 
-If you get the error @JavascriptException: Long-running script killed after 20 seconds.@ this may be due to the Dockerized Node.js engine taking too long to start.  You may address this by installing Node.js locally (run @apt-get install nodejs@ on Debian or Ubuntu) or by specifying a longer timeout with the @--eval-timeout@ option.  For example, run the workflow with @cwltool --eval-timeout=40@ for a 40-second timeout.
+If this returns an error, contact the sysadmin of your cluster for assistance.
index 5c6d04940f6b1928120494e8134681d956efac50..0debd16a15cfae5a5bad1aa7279494837cdc19de 100644 (file)
@@ -4,6 +4,8 @@ navsection: userguide
 title: Best Practices for writing CWL
 ...
 
+* To run on Arvados, a workflow should provide a @DockerRequirement@ in the @hints@ section.
+
 * Build a reusable library of components.  Share tool wrappers and subworkflows between projects.  Make use of and contribute to "community maintained workflows and tools":https://github.com/common-workflow-language/workflows and tool registries such as "Dockstore":http://dockstore.org .
 
 * When combining a parameter value with a string, such as adding a filename extension, write @$(inputs.file.basename).ext@ instead of @$(inputs.file.basename + 'ext')@.  The first form is evaluated as a simple text substitution, the second form (using the @+@ operator) is evaluated as an arbitrary Javascript expression and requires that you declare @InlineJavascriptRequirement@.
index 21f30cc61d3b6d1d6572f97c7d67068c342b0e66..d1c7f12c8848663ba6d900fe600a924d022e6e53 100644 (file)
@@ -4,6 +4,8 @@ navsection: userguide
 title: "Scripts provided by Arvados"
 ...
 
+{% include 'pipeline_deprecation_notice' %}
+
 Several crunch scripts are included with Arvados in the "/crunch_scripts directory":https://dev.arvados.org/projects/arvados/repository/revisions/master/show/crunch_scripts. They are intended to provide examples and starting points for writing your own scripts.
 
 h4. bwa-aln
index 6e334ba0dd7583ddae43b48149a270d3d92ad458..b397bf4f2ecbed9125b49f2b286d2fa7a6816d40 100644 (file)
@@ -12,6 +12,6 @@ If you are using the default Arvados instance for this guide, you can Access Arv
 
 You may be asked to log in using a Google account.  Arvados uses only your name and email address from Google services for identification, and will never access any personal information.  If you are accessing Arvados for the first time, the Workbench may indicate your account status is *New / inactive*.  If this is the case, contact the administrator of the Arvados instance to request activation of your account.
 
-Once your account is active, logging in to the Workbench will present you with the Dashboard. This gives a summary of your projects and recent activity in the Arvados instance.  "You are now ready to run your first pipeline.":{{ site.baseurl }}/user/tutorials/tutorial-pipeline-workbench.html
+Once your account is active, logging in to the Workbench will present you with the Dashboard. This gives a summary of your projects and recent activity in the Arvados instance.  "You are now ready to run your first pipeline.":{{ site.baseurl }}/user/tutorials/tutorial-workflow-workbench.html
 
-!{{ site.baseurl }}/images/workbench-dashboard.png!
+!{display: block;margin-left: 25px;margin-right: auto;border:1px solid lightgray;}{{ site.baseurl }}/images/workbench-dashboard.png!
index f8f749c7cbdbfa97a5977d3a79dbe992abf3d373..9e8b2cf34b22621f0c3185f7cdcd7a66a182fe54 100644 (file)
@@ -4,4 +4,6 @@ navsection: userguide
 title: "Pipeline template reference"
 ...
 
-Pipeline template options are described on the "pipeline template schema page.":{{site.baseurl}}/api/schema/PipelineTemplate.html
+{% include 'pipeline_deprecation_notice' %}
+
+Pipeline template options are described on the "pipeline template schema page.":{{site.baseurl}}/api/methods/pipeline_templates.html
index ed0a126a41cf6d07f018ff7fe00cad49d5b32fd8..abb1a6bcfc73b56cad9a7bab630ebb9497313b84 100644 (file)
@@ -4,6 +4,9 @@ navsection: userguide
 title: "Using arv-copy"
 ...
 
+{% include 'crunch1only_begin' %}
+On those sites, the "copy a pipeline template" feature described below is not available. However, "copy a workflow" feature is not yet implemented.
+{% include 'crunch1only_end' %}
 
 This tutorial describes how to copy Arvados objects from one cluster to another by using @arv-copy@.
 
@@ -78,3 +81,26 @@ For example, we can copy the same object using this tag.
 <pre><code>~$ <span class="userinput">arv-copy --src qr1hi --dst dst_cluster --dst-git-repo $USER/tutorial --no-recursive qr1hi-p5p6p-9pkaxt6qjnkxhhu</span>
 </code></pre>
 </notextile>
+
+h3. How to copy a workflow
+
+We will use the uuid @zzzzz-7fd4e-sampleworkflow1@ as an example workflow.
+
+<notextile>
+<pre><code>~$ <span class="userinput">arv-copy --src zzzzz --dst dst_cluster --dst-git-repo $USER/tutorial zzzzz-7fd4e-sampleworkflow1</span>
+zzzzz-4zz18-jidprdejysravcr: 1143M / 1143M 100.0%
+2017-01-04 04:11:58 arvados.arv-copy[5906] INFO:
+2017-01-04 04:11:58 arvados.arv-copy[5906] INFO: Success: created copy with uuid dst_cluster-7fd4e-ojtgpne594ubkt7
+</code></pre>
+</notextile>
+
+The name, description, and workflow definition from the original workflow will be used for the destination copy. In addition, any *locations* and *docker images* found in the src workflow definition will also be copied to the destination recursively.
+
+If you would like to copy the object without dependencies, you can use the @--no-recursive@ flag.
+
+For example, we can copy the same object non-recursively using the following:
+
+<notextile>
+<pre><code>~$ <span class="userinput">arv-copy --src zzzzz --dst dst_cluster --dst-git-repo $USER/tutorial --no-recursive zzzzz-7fd4e-sampleworkflow1</span>
+</code></pre>
+</notextile>
index 1a31d126da698495eca853b72e1dcca9424c94a1..5ccd5ac476bdc8e3cda0cb4891869ee68e6cf2d8 100644 (file)
@@ -184,13 +184,13 @@ arvados/jobs-with-r             latest      33ea6b877923  qr1hi-4zz18-3fk2px2ji2
 </code></pre>
 </notextile>
 
-You are now able to specify the runtime environment for your program using the @docker_image@ field of the @runtime_constaints@ section of your pipeline components:
+You are now able to specify the runtime environment for your program using @DockerRequirement@ in your workflow:
 
-<notextile>
-{% code 'example_docker' as javascript %}
-</notextile>
-
-* The @docker_image@ field can be one of: the Docker repository name (as shown above), the Docker image hash, or the Arvados collection portable data hash.
+<pre>
+hints:
+  DockerRequirement:
+    dockerPull: arvados/jobs-with-r
+</pre>
 
 h2. Share Docker images
 
index 8d1aca63057f44f22abb5811c2ef73c8478e4129..93fc2c0f34e4a3da4388560b265d7c189da30fa3 100644 (file)
@@ -4,6 +4,10 @@ navsection: userguide
 title: "Using arv-run"
 ...
 
+{% include 'crunch1only_begin' %}
+On those sites, the features described here are not yet implemented.
+{% include 'crunch1only_end' %}
+
 The @arv-run@ command enables you create Arvados pipelines at the command line that fan out to multiple concurrent tasks across Arvados compute nodes.
 
 {% include 'tutorial_expectations' %}
index 78839196967260dace26bc7791c72656ab85d0c1..ac40458e3f73d43a7978013faa5597d5c8f7a667 100644 (file)
@@ -4,6 +4,8 @@ navsection: userguide
 title: "run-command reference"
 ...
 
+{% include 'pipeline_deprecation_notice' %}
+
 The @run-command@ crunch script enables you run command line programs.
 
 {% include 'tutorial_expectations_workstation' %}
index 9a2e12c09677beb59495b95404caea2c8622be5a..c4d3d296af09dcd79df53b7f7dfa536d42a1cee7 100644 (file)
@@ -4,7 +4,11 @@ navsection: userguide
 title: "Running an Arvados pipeline"
 ...
 
-This tutorial demonstrates how to use the command line to run the same pipeline as described in "running a pipeline using Workbench.":{{site.baseurl}}/user/tutorials/tutorial-pipeline-workbench.html
+{% include 'crunch1only_begin' %}
+If the Jobs API is not available, use the "Common Workflow Language":{{site.baseurl}}/user/cwl/cwl-runner.html instead.
+{% include 'crunch1only_end' %}
+
+This tutorial demonstrates how to use the command line to run the same pipeline as described in "running a pipeline using Workbench.":{{site.baseurl}}/user/tutorials/tutorial-workflow-workbench.html
 
 {% include 'tutorial_expectations' %}
 {% include 'tutorial_cluster_name' %}
index ef4634ee742dd26a35ea3f39c5414c2bc383127f..bdd943d393a8ded8e30a542dd00f02fe418ca445 100644 (file)
@@ -22,7 +22,7 @@ This will open the template record in an interactive text editor (as specified b
 
 * @"name"@ is a human-readable name for the pipeline.
 * @"components"@ is a set of scripts or commands that make up the pipeline.  Each component is given an identifier (@"bwa-mem"@ and @"SortSam"@) in this example).
-** Each entry in components @"components"@ is an Arvados job submission.  For more information about individual jobs, see the "job object reference":{{site.baseurl}}/api/schema/Job.html and "job create method.":{{site.baseurl}}/api/methods/jobs.html#create
+** Each entry in components @"components"@ is an Arvados job submission.  For more information about individual jobs, see the "job resource reference.":{{site.baseurl}}/api/methods/jobs.html
 * @"repository"@, @"script_version"@, and @"script"@ indicate that we intend to use the external @"run-command"@ tool wrapper that is part of the Arvados.  These parameters are described in more detail in "Writing a script":tutorial-firstscript.html.
 * @"runtime_constraints"@ describes runtime resource requirements for the component.
 ** @"docker_image"@ specifies the "Docker":https://www.docker.com/ runtime environment in which to run the job.  The Docker image @"bcosc/arv-base-java"@ supplied here has the Java runtime environment, bwa, and samtools installed.
@@ -55,14 +55,14 @@ See the "run-command reference":{{site.baseurl}}/user/topics/run-command.html fo
 
 h2. Running your pipeline
 
-Your new pipeline template should appear at the top of the Workbench "pipeline&nbsp;templates":{{site.arvados_workbench_host}}/pipeline_templates page.  You can run your pipeline "using Workbench":tutorial-pipeline-workbench.html or the "command line.":{{site.baseurl}}/user/topics/running-pipeline-command-line.html
+Your new pipeline template should appear at the top of the Workbench "pipeline&nbsp;templates":{{site.arvados_workbench_host}}/pipeline_templates page.  You can run your pipeline "using Workbench":tutorial-workflow-workbench.html or the "command line.":{{site.baseurl}}/user/topics/running-pipeline-command-line.html
 
 Test data is available in the "Arvados Tutorial":{{site.arvados_workbench_host}}/projects/qr1hi-j7d0g-u7zg1qdaowykd8d project:
 
 * Choose <i class="fa fa-fw fa-archive"></i> "Tutorial chromosome 19 reference (2463fa9efeb75e099685528b3b9071e0+438)":{{site.arvados_workbench_host}}/collections/2463fa9efeb75e099685528b3b9071e0+438 for the "reference_collection" parameter
 * Choose <i class="fa fa-fw fa-archive"></i> "Tutorial sample exome (3229739b505d2b878b62aed09895a55a+142)":{{site.arvados_workbench_host}}/collections/3229739b505d2b878b62aed09895a55a+142 for the "sample" parameter
 
-For more information and examples for writing pipelines, see the "pipeline template reference":{{site.baseurl}}/api/schema/PipelineTemplate.html
+For more information and examples for writing pipelines, see the "pipeline template reference":{{site.baseurl}}/api/methods/pipeline_templates.html
 
 h2. Re-using your pipeline run
 
diff --git a/doc/user/tutorials/tutorial-pipeline-workbench.html.textile.liquid b/doc/user/tutorials/tutorial-pipeline-workbench.html.textile.liquid
deleted file mode 100644 (file)
index fac573a..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
----
-layout: default
-navsection: userguide
-title: "Running a pipeline using Workbench"
-...
-
-A "pipeline" (sometimes called a "workflow" in other systems) is a sequence of steps that apply various programs or tools to transform input data to output data.  Pipelines are the principal means of performing computation with Arvados.  This tutorial demonstrates how to run a single-stage pipeline to take a small data set of paired-end reads from a sample "exome":https://en.wikipedia.org/wiki/Exome in "FASTQ":https://en.wikipedia.org/wiki/FASTQ_format format and align them to "Chromosome 19":https://en.wikipedia.org/wiki/Chromosome_19_%28human%29 using the "bwa mem":http://bio-bwa.sourceforge.net/ tool, producing a "Sequence Alignment/Map (SAM)":https://samtools.github.io/ file.  This tutorial will introduce the following Arvados features:
-
-<div>
-* How to create a new pipeline from an existing template.
-* How to browse and select input data for the pipeline and submit the pipeline to run on the Arvados cluster.
-* How to access your pipeline results.
-</div>
-
-notextile. <div class="spaced-out">
-
-h3. Steps
-
-# Start from the *Workbench Dashboard*.  You can access the Dashboard by clicking on *<i class="fa fa-lg fa-fw fa-dashboard"></i> Dashboard* in the upper left corner of any Workbench page.
-# Click on the <span class="btn btn-sm btn-primary"><i class="fa fa-fw fa-gear"></i> Run a pipeline...</span> button.  This will open a dialog box titled *Choose a pipeline to run*.
-# In the search box, type in *Tutorial align using bwa mem*.
-# Select *<i class="fa fa-fw fa-gear"></i> Tutorial align using bwa mem* and click the <span class="btn btn-sm btn-primary" >Next: choose inputs <i class="fa fa-fw fa-arrow-circle-right"></i></span> button.  This will create a new pipeline in your *Home* project and will open it. You can now supply the inputs for the pipeline.
-# The first input parameter to the pipeline is *"reference_collection" parameter for run-command script in bwa-mem component*.  Click the <span class="btn btn-sm btn-primary">Choose</span> button beneath that header.  This will open a dialog box titled *Choose a dataset for "reference_collection" parameter for run-command script in bwa-mem component*.
-# Open the *Home <span class="caret"></span>* menu and select *All Projects*. Search for and select *<i class="fa fa-fw fa-archive"></i> Tutorial chromosome 19 reference* and click the <span class="btn btn-sm btn-primary" >OK</span> button.
-# Repeat the previous two steps to set the *"sample" parameter for run-command script in bwa-mem component* parameter to *<i class="fa fa-fw fa-archive"></i> Tutorial sample exome*.
-# Click on the <span class="btn btn-sm btn-primary" >Run <i class="fa fa-fw fa-play"></i></span> button.  The page updates to show you that the pipeline has been submitted to run on the Arvados cluster.
-# After the pipeline starts running, you can track the progress by watching log messages from jobs.  This page refreshes automatically.  You will see a <span class="label label-success">complete</span> label when the pipeline completes successfully.
-# Click on the *Output* link to see the results of the job.  This will load a new page listing the output files from this pipeline.  You'll see the output SAM file from the alignment tool under the *Files* tab.
-# Click on the <span class="btn btn-sm btn-info"><i class="fa fa-download"></i></span> download button to the right of the SAM file to download your results.
-
-notextile. </div>
index 47e8dc750cf0691e8f859468cbc433e50ea358de..fe80dcb48ebeae24ff20e47cfc2500382d6741b5 100644 (file)
@@ -81,10 +81,10 @@ In the editor, enter the following template:
 * @"repository"@ is the name of a git repository to search for the script version.  You can access a list of available git repositories on the Arvados Workbench in the *Repositories* page using the <span class="fa fa-lg fa-user"></span> <span class="caret"></span> top navigation menu icon.
 * @"script_version"@ specifies the version of the script that you wish to run.  This can be in the form of an explicit Git revision hash, a tag, or a branch (in which case it will use the HEAD of the specified branch).  Arvados logs the script version that was used in the run, enabling you to go back and re-run any past job with the guarantee that the exact same code will be used as was used in the previous run.
 * @"script"@ specifies the filename of the script to run.  Crunch expects to find this in the @crunch_scripts/@ subdirectory of the Git repository.
-* @"runtime_constraints"@ describes the runtime environment required to run the job.  These are described in the "job record schema":{{site.baseurl}}/api/schema/Job.html
+* @"runtime_constraints"@ describes the runtime environment required to run the job.  These are described in the "job record schema":{{site.baseurl}}/api/methods/jobs.html
 
 h2. Running your pipeline
 
-Your new pipeline template should appear at the top of the Workbench "pipeline&nbsp;templates":{{site.arvados_workbench_host}}/pipeline_templates page.  You can run your pipeline "using Workbench":tutorial-pipeline-workbench.html or the "command line.":{{site.baseurl}}/user/topics/running-pipeline-command-line.html
+Your new pipeline template should appear at the top of the Workbench "pipeline&nbsp;templates":{{site.arvados_workbench_host}}/pipeline_templates page.  You can run your pipeline "using Workbench":tutorial-workflow-workbench.html or the "command line.":{{site.baseurl}}/user/topics/running-pipeline-command-line.html
 
-For more information and examples for writing pipelines, see the "pipeline template reference":{{site.baseurl}}/api/schema/PipelineTemplate.html
+For more information and examples for writing pipelines, see the "pipeline template reference":{{site.baseurl}}/api/methods/pipeline_templates.html
diff --git a/doc/user/tutorials/tutorial-workflow-workbench.html.textile.liquid b/doc/user/tutorials/tutorial-workflow-workbench.html.textile.liquid
new file mode 100644 (file)
index 0000000..445ce75
--- /dev/null
@@ -0,0 +1,27 @@
+---
+layout: default
+navsection: userguide
+title: "Running a workflow using Workbench"
+...
+
+A "workflow" (sometimes called a "pipeline" in other systems) is a sequence of steps that apply various programs or tools to transform input data to output data.  Workflows are the principal means of performing computation with Arvados.  This tutorial demonstrates how to run a single-stage workflow to take a small data set of paired-end reads from a sample "exome":https://en.wikipedia.org/wiki/Exome in "FASTQ":https://en.wikipedia.org/wiki/FASTQ_format format and align them to "Chromosome 19":https://en.wikipedia.org/wiki/Chromosome_19_%28human%29 using the "bwa mem":http://bio-bwa.sourceforge.net/ tool, producing a "Sequence Alignment/Map (SAM)":https://samtools.github.io/ file.  This tutorial will introduce the following Arvados features:
+
+<div>
+* How to create a new process from an existing workflow.
+* How to browse and select input data for the workflow and submit the process to run on the Arvados cluster.
+* How to access your process results.
+</div>
+
+h3. Steps
+
+# Start from the *Workbench Dashboard*.  You can access the Dashboard by clicking on *<i class="fa fa-lg fa-fw fa-dashboard"></i> Dashboard* in the upper left corner of any Workbench page.
+# Click on the <span class="btn btn-sm btn-primary"><i class="fa fa-fw fa-gear"></i> Run a process...</span> button.  This will open a dialog box titled *Choose a pipeline or workflow to run*.
+# In the search box, type in *Tutorial bwa mem cwl*.
+# Select *<i class="fa fa-fw fa-gear"></i> Tutorial bwa mem cwl* and click the <span class="btn btn-sm btn-primary" >Next: choose inputs <i class="fa fa-fw fa-arrow-circle-right"></i></span> button.  This will create a new process in your *Home* project and will open it. You can now supply the inputs for the process. Please note that all required inputs are populated with default values and you can change them if you prefer.
+# For example, let's see how to change *"reference" parameter* for this workflow. Click the <span class="btn btn-sm btn-primary">Choose</span> button beneath the *"reference" parameter* header.  This will open a dialog box titled *Choose a dataset for "reference" parameter for cwl-runner in bwa-mem.cwl component*.
+# Open the *Home <span class="caret"></span>* menu and select *All Projects*. Search for and select *<i class="fa fa-fw fa-archive"></i> Tutorial chromosome 19 reference*. You will then see a list of files. Select *<i class="fa fa-fw fa-file"></i> 19-fasta.bwt* and click the <span class="btn btn-sm btn-primary" >OK</span> button.
+# Repeat the previous two steps to set the *"read_p1" parameter for cwl-runner script in bwa-mem.cwl component* and *"read_p2" parameter for cwl-runner script in bwa-mem.cwl component* parameters.
+# Click on the <span class="btn btn-sm btn-primary" >Run <i class="fa fa-fw fa-play"></i></span> button.  The page updates to show you that the process has been submitted to run on the Arvados cluster.
+# After the process starts running, you can track the progress by watching log messages from the component(s).  This page refreshes automatically.  You will see a <span class="label label-success">complete</span> label when the process completes successfully.
+# Click on the *Output* link to see the results of the process.  This will load a new page listing the output files from this process.  You'll see the output SAM file from the alignment tool under the *Files* tab.
+# Click on the <span class="btn btn-sm btn-info"><i class="fa fa-download"></i></span> download button to the right of the SAM file to download your results.
diff --git a/doc/user/tutorials/writing-cwl-workflow.html.textile.liquid b/doc/user/tutorials/writing-cwl-workflow.html.textile.liquid
new file mode 100644 (file)
index 0000000..ab80c97
--- /dev/null
@@ -0,0 +1,196 @@
+---
+layout: default
+navsection: userguide
+title: "Writing a CWL workflow"
+...
+
+{% include 'what_is_cwl' %}
+
+{% include 'tutorial_expectations' %}
+
+h2. Developing workflows
+
+For an introduction and and detailed documentation about writing CWL, see the "CWL User Guide":http://commonwl.org/v1.0/UserGuide.html and the "CWL Specification":http://commonwl.org/v1.0 .
+
+See "Best Practices for writing CWL":{{site.baseurl}}/user/cwl/cwl-style.html and "Arvados CWL Extensions":{{site.baseurl}}/user/cwl/cwl-extensions.html for additional information about using CWL on Arvados.
+
+h2. Registering a workflow to use in Workbench
+
+Use @--create-workflow@ to register a CWL workflow with Arvados.  This enables you to share workflows with other Arvados users, and run them by clicking the <span class="btn btn-sm btn-primary"><i class="fa fa-fw fa-gear"></i> Run a process...</span> button on the Workbench Dashboard and on the command line by UUID.
+
+<notextile>
+<pre><code>~/arvados/doc/user/cwl/bwa-mem$ <span class="userinput">arvados-cwl-runner --create-workflow bwa-mem.cwl</span>
+arvados-cwl-runner 1.0.20160628195002, arvados-python-client 0.1.20160616015107, cwltool 1.0.20160629140624
+2016-07-01 12:21:01 arvados.arv-run[15796] INFO: Upload local files: "bwa-mem.cwl"
+2016-07-01 12:21:01 arvados.arv-run[15796] INFO: Uploaded to qr1hi-4zz18-7e0hedrmkuyoei3
+2016-07-01 12:21:01 arvados.cwl-runner[15796] INFO: Created template qr1hi-p5p6p-rjleou1dwr167v5
+qr1hi-p5p6p-rjleou1dwr167v5
+</code></pre>
+</notextile>
+
+You can provide a partial input file to set default values for the workflow input parameters.  You can also use the @--name@ option to set the name of the workflow:
+
+<notextile>
+<pre><code>~/arvados/doc/user/cwl/bwa-mem$ <span class="userinput">arvados-cwl-runner --name "My workflow with defaults" --create-workflow bwa-mem.cwl bwa-mem-template.yml</span>
+arvados-cwl-runner 1.0.20160628195002, arvados-python-client 0.1.20160616015107, cwltool 1.0.20160629140624
+2016-07-01 14:09:50 arvados.arv-run[3730] INFO: Upload local files: "bwa-mem.cwl"
+2016-07-01 14:09:50 arvados.arv-run[3730] INFO: Uploaded to qr1hi-4zz18-0f91qkovk4ml18o
+2016-07-01 14:09:50 arvados.cwl-runner[3730] INFO: Created template qr1hi-p5p6p-0deqe6nuuyqns2i
+qr1hi-p5p6p-zuniv58hn8d0qd8
+</code></pre>
+</notextile>
+
+h3. Running registered workflows at the command line
+
+You can run a registered workflow at the command line by its UUID:
+
+<notextile>
+<pre><code>~/arvados/doc/user/cwl/bwa-mem$ <span class="userinput">arvados-cwl-runner qr1hi-p5p6p-zuniv58hn8d0qd8 --help</span>
+/home/peter/work/scripts/venv/bin/arvados-cwl-runner 0d62edcb9d25bf4dcdb20d8872ea7b438e12fc59 1.0.20161209192028, arvados-python-client 0.1.20161212125425, cwltool 1.0.20161207161158
+Resolved 'qr1hi-p5p6p-zuniv58hn8d0qd8' to 'keep:655c6cd07550151b210961ed1d3852cf+57/bwa-mem.cwl'
+usage: qr1hi-p5p6p-zuniv58hn8d0qd8 [-h] [--PL PL] --group_id GROUP_ID
+                                   --read_p1 READ_P1 [--read_p2 READ_P2]
+                                   [--reference REFERENCE] --sample_id
+                                   SAMPLE_ID
+                                   [job_order]
+
+positional arguments:
+  job_order             Job input json file
+
+optional arguments:
+  -h, --help            show this help message and exit
+  --PL PL
+  --group_id GROUP_ID
+  --read_p1 READ_P1     The reads, in fastq format.
+  --read_p2 READ_P2     For mate paired reads, the second file (optional).
+  --reference REFERENCE
+                        The index files produced by `bwa index`
+  --sample_id SAMPLE_ID
+</code></pre>
+</notextile>
+
+h2. Using cwltool
+
+When developing a workflow, it is often helpful to run it on the local host to avoid the overhead of submitting to the cluster.  To execute a workflow only on the local host (without submitting jobs to an Arvados cluster) you can use the @cwltool@ command.  Note that when using @cwltool@ you must have the input data accessible on the local file system using either @arv-mount@ or @arv-get@ to fetch the data from Keep.
+
+<notextile>
+<pre><code>~/arvados/doc/user/cwl/bwa-mem$ <span class="userinput">arv-get 2463fa9efeb75e099685528b3b9071e0+438/ .</span>
+156 MiB / 156 MiB 100.0%
+~/arvados/doc/user/cwl/bwa-mem$ <span class="userinput">arv-get ae480c5099b81e17267b7445e35b4bc7+180/ .</span>
+23 MiB / 23 MiB 100.0%
+~/arvados/doc/user/cwl/bwa-mem$ <span class="userinput">cwltool bwa-mem-input.yml bwa-mem-input-local.yml</span>
+cwltool 1.0.20160629140624
+[job bwa-mem.cwl] /home/example/arvados/doc/user/cwl/bwa-mem$ docker \
+    run \
+    -i \
+    --volume=/home/example/arvados/doc/user/cwl/bwa-mem/19.fasta.ann:/var/lib/cwl/job979368791_bwa-mem/19.fasta.ann:ro \
+    --volume=/home/example/arvados/doc/user/cwl/bwa-mem/HWI-ST1027_129_D0THKACXX.1_1.fastq:/var/lib/cwl/job979368791_bwa-mem/HWI-ST1027_129_D0THKACXX.1_1.fastq:ro \
+    --volume=/home/example/arvados/doc/user/cwl/bwa-mem/19.fasta.sa:/var/lib/cwl/job979368791_bwa-mem/19.fasta.sa:ro \
+    --volume=/home/example/arvados/doc/user/cwl/bwa-mem/19.fasta.amb:/var/lib/cwl/job979368791_bwa-mem/19.fasta.amb:ro \
+    --volume=/home/example/arvados/doc/user/cwl/bwa-mem/19.fasta.pac:/var/lib/cwl/job979368791_bwa-mem/19.fasta.pac:ro \
+    --volume=/home/example/arvados/doc/user/cwl/bwa-mem/HWI-ST1027_129_D0THKACXX.1_2.fastq:/var/lib/cwl/job979368791_bwa-mem/HWI-ST1027_129_D0THKACXX.1_2.fastq:ro \
+    --volume=/home/example/arvados/doc/user/cwl/bwa-mem/19.fasta.bwt:/var/lib/cwl/job979368791_bwa-mem/19.fasta.bwt:ro \
+    --volume=/home/example/arvados/doc/user/cwl/bwa-mem:/var/spool/cwl:rw \
+    --volume=/tmp/tmpgzyou9:/tmp:rw \
+    --workdir=/var/spool/cwl \
+    --read-only=true \
+    --log-driver=none \
+    --user=1001 \
+    --rm \
+    --env=TMPDIR=/tmp \
+    --env=HOME=/var/spool/cwl \
+    biodckr/bwa \
+    bwa \
+    mem \
+    -t \
+    1 \
+    -R \
+    '@RG       ID:arvados_tutorial     PL:illumina     SM:HWI-ST1027_129' \
+    /var/lib/cwl/job979368791_bwa-mem/19.fasta \
+    /var/lib/cwl/job979368791_bwa-mem/HWI-ST1027_129_D0THKACXX.1_1.fastq \
+    /var/lib/cwl/job979368791_bwa-mem/HWI-ST1027_129_D0THKACXX.1_2.fastq > /home/example/arvados/doc/user/cwl/bwa-mem/HWI-ST1027_129_D0THKACXX.1_1.sam
+[M::bwa_idx_load_from_disk] read 0 ALT contigs
+[M::process] read 100000 sequences (10000000 bp)...
+[M::mem_pestat] # candidate unique pairs for (FF, FR, RF, RR): (0, 4745, 1, 0)
+[M::mem_pestat] skip orientation FF as there are not enough pairs
+[M::mem_pestat] analyzing insert size distribution for orientation FR...
+[M::mem_pestat] (25, 50, 75) percentile: (154, 181, 214)
+[M::mem_pestat] low and high boundaries for computing mean and std.dev: (34, 334)
+[M::mem_pestat] mean and std.dev: (185.63, 44.88)
+[M::mem_pestat] low and high boundaries for proper pairs: (1, 394)
+[M::mem_pestat] skip orientation RF as there are not enough pairs
+[M::mem_pestat] skip orientation RR as there are not enough pairs
+[M::mem_process_seqs] Processed 100000 reads in 9.848 CPU sec, 9.864 real sec
+[main] Version: 0.7.12-r1039
+[main] CMD: bwa mem -t 1 -R @RG        ID:arvados_tutorial     PL:illumina     SM:HWI-ST1027_129 /var/lib/cwl/job979368791_bwa-mem/19.fasta /var/lib/cwl/job979368791_bwa-mem/HWI-ST1027_129_D0THKACXX.1_1.fastq /var/lib/cwl/job979368791_bwa-mem/HWI-ST1027_129_D0THKACXX.1_2.fastq
+[main] Real time: 10.061 sec; CPU: 10.032 sec
+Final process status is success
+{
+    "aligned_sam": {
+        "size": 30738959,
+        "path": "/home/example/arvados/doc/user/cwl/bwa-mem/HWI-ST1027_129_D0THKACXX.1_1.sam",
+        "checksum": "sha1$0c668cca45fef02397bb5302880526d300ee4dac",
+        "class": "File"
+    }
+}
+</code></pre>
+</notextile>
+
+If you get the error @JavascriptException: Long-running script killed after 20 seconds.@ this may be due to the Dockerized Node.js engine taking too long to start.  You may address this by installing Node.js locally (run @apt-get install nodejs@ on Debian or Ubuntu) or by specifying a longer timeout with the @--eval-timeout@ option.  For example, run the workflow with @cwltool --eval-timeout=40@ for a 40-second timeout.
+
+h2. Making workflows directly executable
+
+You can make a workflow file directly executable (@cwl-runner@ should be an alias to @arvados-cwl-runner@) by adding the following line to the top of the file:
+
+<notextile>
+<pre><code>#!/usr/bin/env cwl-runner
+</code></pre>
+</notextile>
+
+<notextile>
+<pre><code>~/arvados/doc/user/cwl/bwa-mem$ <span class="userinput">./bwa-mem.cwl bwa-mem-input.yml</span>
+arvados-cwl-runner 1.0.20160628195002, arvados-python-client 0.1.20160616015107, cwltool 1.0.20160629140624
+2016-06-30 14:56:36 arvados.arv-run[27002] INFO: Upload local files: "bwa-mem.cwl"
+2016-06-30 14:56:36 arvados.arv-run[27002] INFO: Uploaded to qr1hi-4zz18-h7ljh5u76760ww2
+2016-06-30 14:56:40 arvados.cwl-runner[27002] INFO: Submitted job qr1hi-8i9sb-fm2n3b1w0l6bskg
+2016-06-30 14:56:41 arvados.cwl-runner[27002] INFO: Job bwa-mem.cwl (qr1hi-8i9sb-fm2n3b1w0l6bskg) is Running
+2016-06-30 14:57:12 arvados.cwl-runner[27002] INFO: Job bwa-mem.cwl (qr1hi-8i9sb-fm2n3b1w0l6bskg) is Complete
+2016-06-30 14:57:12 arvados.cwl-runner[27002] INFO: Overall process status is success
+{
+    "aligned_sam": {
+        "path": "keep:54325254b226664960de07b3b9482349+154/HWI-ST1027_129_D0THKACXX.1_1.sam",
+        "checksum": "sha1$0dc46a3126d0b5d4ce213b5f0e86e2d05a54755a",
+        "class": "File",
+        "size": 30738986
+    }
+}
+</code></pre>
+</notextile>
+
+You can even make an input file directly executable the same way with the following two lines at the top:
+
+<notextile>
+<pre><code>#!/usr/bin/env cwl-runner
+cwl:tool: <span class="userinput">bwa-mem.cwl</span>
+</code></pre>
+</notextile>
+
+<notextile>
+<pre><code>~/arvados/doc/user/cwl/bwa-mem$ <span class="userinput">./bwa-mem-input.yml</span>
+arvados-cwl-runner 1.0.20160628195002, arvados-python-client 0.1.20160616015107, cwltool 1.0.20160629140624
+2016-06-30 14:56:36 arvados.arv-run[27002] INFO: Upload local files: "bwa-mem.cwl"
+2016-06-30 14:56:36 arvados.arv-run[27002] INFO: Uploaded to qr1hi-4zz18-h7ljh5u76760ww2
+2016-06-30 14:56:40 arvados.cwl-runner[27002] INFO: Submitted job qr1hi-8i9sb-fm2n3b1w0l6bskg
+2016-06-30 14:56:41 arvados.cwl-runner[27002] INFO: Job bwa-mem.cwl (qr1hi-8i9sb-fm2n3b1w0l6bskg) is Running
+2016-06-30 14:57:12 arvados.cwl-runner[27002] INFO: Job bwa-mem.cwl (qr1hi-8i9sb-fm2n3b1w0l6bskg) is Complete
+2016-06-30 14:57:12 arvados.cwl-runner[27002] INFO: Overall process status is success
+{
+    "aligned_sam": {
+        "path": "keep:54325254b226664960de07b3b9482349+154/HWI-ST1027_129_D0THKACXX.1_1.sam",
+        "checksum": "sha1$0dc46a3126d0b5d4ce213b5f0e86e2d05a54755a",
+        "class": "File",
+        "size": 30738986
+    }
+}
+</code></pre>
+</notextile>
index be14be9d4adb6b2f76406912cf92057d055aea25..a2e42d6fac498b1b30437ad71def8bad7e946f3d 100755 (executable)
@@ -864,9 +864,9 @@ for (my $todo_ptr = 0; $todo_ptr <= $#jobstep_todo; $todo_ptr ++)
         ."&& MEMLIMIT=\$(( (\$MEM * 95) / ($ENV{CRUNCH_NODE_SLOTS} * 100) )) "
         ."&& let SWAPLIMIT=\$MEMLIMIT+\$SWAP "
         .q{&& declare -a VOLUMES=() }
-        .q{&& if which crunchrunner >/dev/null ; then VOLUMES+=("--volume=$(which crunchrunner):/usr/local/bin/crunchrunner") ; fi }
-        .q{&& if test -f /etc/ssl/certs/ca-certificates.crt ; then VOLUMES+=("--volume=/etc/ssl/certs/ca-certificates.crt:/etc/arvados/ca-certificates.crt") ; }
-        .q{elif test -f /etc/pki/tls/certs/ca-bundle.crt ; then VOLUMES+=("--volume=/etc/pki/tls/certs/ca-bundle.crt:/etc/arvados/ca-certificates.crt") ; fi };
+        .q{&& if which crunchrunner >/dev/null ; then VOLUMES+=("--volume=$(which crunchrunner):/usr/local/bin/crunchrunner:ro") ; fi }
+        .q{&& if test -f /etc/ssl/certs/ca-certificates.crt ; then VOLUMES+=("--volume=/etc/ssl/certs/ca-certificates.crt:/etc/arvados/ca-certificates.crt:ro") ; }
+        .q{elif test -f /etc/pki/tls/certs/ca-bundle.crt ; then VOLUMES+=("--volume=/etc/pki/tls/certs/ca-bundle.crt:/etc/arvados/ca-certificates.crt:ro") ; fi };
 
     $command .= "&& exec arv-mount --read-write --mount-by-pdh=by_pdh --mount-tmp=tmp --crunchstat-interval=10 --allow-other $arv_file_cache \Q$keep_mnt\E --exec ";
     $ENV{TASK_KEEPMOUNT} = "$keep_mnt/by_pdh";
@@ -1509,7 +1509,7 @@ sub preprocess_stderr
     my $line = $1;
     substr $jobstep[$jobstepidx]->{stderr}, 0, 1+length($line), "";
     Log ($jobstepidx, "stderr $line");
-    if ($line =~ /srun: error: (SLURM job $ENV{SLURM_JOB_ID} has expired|Unable to confirm allocation for job $ENV{SLURM_JOB_ID})/) {
+    if ($line =~ /srun: error: (SLURM job $ENV{SLURM_JOB_ID} has expired|Unable to confirm allocation for job $ENV{SLURM_JOB_ID})/i) {
       # If the allocation is revoked, we can't possibly continue, so mark all
       # nodes as failed.  This will cause the overall exit code to be
       # EX_RETRY_UNLOCKED instead of failure so that crunch_dispatch can re-run
@@ -1519,14 +1519,14 @@ sub preprocess_stderr
         $st->{node}->{fail_count}++;
       }
     }
-    elsif ($line =~ /srun: error: (Node failure on|Aborting, .*\bio error\b)/) {
+    elsif ($line =~ /srun: error: .*?\b(Node failure on|Aborting, .*?\bio error\b)/i) {
       $jobstep[$jobstepidx]->{tempfail} = 1;
       if (defined($job_slot_index)) {
         $slot[$job_slot_index]->{node}->{fail_count}++;
         ban_node_by_slot($job_slot_index);
       }
     }
-    elsif ($line =~ /srun: error: (Unable to create job step|.*: Communication connection failure)/) {
+    elsif ($line =~ /srun: error: (Unable to create job step|.*?: Communication connection failure)/i) {
       $jobstep[$jobstepidx]->{tempfail} = 1;
       ban_node_by_slot($job_slot_index) if (defined($job_slot_index));
     }
@@ -1780,7 +1780,7 @@ sub log_writer_finish()
   close($log_pipe_in);
 
   my $logger_failed = 0;
-  my $read_result = log_writer_read_output(120);
+  my $read_result = log_writer_read_output(600);
   if ($read_result == -1) {
     $logger_failed = -1;
     Log (undef, "timed out reading from 'arv-put'");
index d0224aedb01e354408f5a710eb88dff344314a90..04f454369cc6541be477fe3f585c637f45c7aee9 100644 (file)
@@ -20,6 +20,14 @@ class TestArvKeepGet < Minitest::Test
     assert_match /^usage:/, err
   end
 
+  def test_get_version
+    out, err = capture_subprocess_io do
+      assert_arv_get '--version'
+    end
+    assert_empty(out, "STDOUT not expected: '#{out}'")
+    assert_match(/[0-9]+\.[0-9]+\.[0-9]+/, err, "Version information incorrect: '#{err}'")
+  end
+
   def test_help
     out, err = capture_subprocess_io do
       assert_arv_get '-h'
index fefbc2729875e70cb890f69d56fe1d7f1c614b8d..e6ead25b807e70eb03d293bc9d3a92aad2a78c7b 100644 (file)
@@ -40,7 +40,7 @@ class TestArvKeepPut < Minitest::Test
 
   def test_raw_file
     out, err = capture_subprocess_io do
-      assert arv_put('--raw', './tmp/foo')
+      assert arv_put('--no-cache', '--raw', './tmp/foo')
     end
     $stderr.write err
     assert_match '', err
@@ -87,7 +87,7 @@ class TestArvKeepPut < Minitest::Test
 
   def test_as_stream
     out, err = capture_subprocess_io do
-      assert arv_put('--as-stream', './tmp/foo')
+      assert arv_put('--no-cache', '--as-stream', './tmp/foo')
     end
     $stderr.write err
     assert_match '', err
@@ -96,7 +96,7 @@ class TestArvKeepPut < Minitest::Test
 
   def test_progress
     out, err = capture_subprocess_io do
-      assert arv_put('--manifest', '--progress', './tmp/foo')
+      assert arv_put('--no-cache', '--manifest', '--progress', './tmp/foo')
     end
     assert_match /%/, err
     assert match_collection_uuid(out)
@@ -104,7 +104,7 @@ class TestArvKeepPut < Minitest::Test
 
   def test_batch_progress
     out, err = capture_subprocess_io do
-      assert arv_put('--manifest', '--batch-progress', './tmp/foo')
+      assert arv_put('--no-cache', '--manifest', '--batch-progress', './tmp/foo')
     end
     assert_match /: 0 written 3 total/, err
     assert_match /: 3 written 3 total/, err
index 92be92d6e0469fba63a1504a5f0c834fb4a9b2b7..3ffc4c7254a0b8a850716e82aebe6bbd1afb3c26 100644 (file)
@@ -17,17 +17,21 @@ import pkg_resources  # part of setuptools
 from cwltool.errors import WorkflowException
 import cwltool.main
 import cwltool.workflow
+import cwltool.process
 import schema_salad
+from schema_salad.sourceline import SourceLine
 
 import arvados
 import arvados.config
+from arvados.keep import KeepClient
+from arvados.errors import ApiError
 
 from .arvcontainer import ArvadosContainer, RunnerContainer
 from .arvjob import ArvadosJob, RunnerJob, RunnerTemplate
 from. runner import Runner, upload_instance
 from .arvtool import ArvadosCommandTool
 from .arvworkflow import ArvadosWorkflow, upload_workflow
-from .fsaccess import CollectionFsAccess
+from .fsaccess import CollectionFsAccess, CollectionFetcher, collectionResolver
 from .perf import Perf
 from .pathmapper import FinalOutputPathMapper
 from ._version import __version__
@@ -42,6 +46,9 @@ logger = logging.getLogger('arvados.cwl-runner')
 metrics = logging.getLogger('arvados.cwl-runner.metrics')
 logger.setLevel(logging.INFO)
 
+arvados.log_handler.setFormatter(logging.Formatter(
+        '%(asctime)s %(name)s %(levelname)s: %(message)s',
+        '%Y-%m-%d %H:%M:%S'))
 
 class ArvCwlRunner(object):
     """Execute a CWL tool or workflow, submit work (using either jobs or
@@ -49,7 +56,7 @@ class ArvCwlRunner(object):
 
     """
 
-    def __init__(self, api_client, work_api=None, keep_client=None, output_name=None):
+    def __init__(self, api_client, work_api=None, keep_client=None, output_name=None, output_tags=None, num_retries=4):
         self.api = api_client
         self.processes = {}
         self.lock = threading.Lock()
@@ -57,13 +64,14 @@ class ArvCwlRunner(object):
         self.final_output = None
         self.final_status = None
         self.uploaded = {}
-        self.num_retries = 4
+        self.num_retries = num_retries
         self.uuid = None
         self.stop_polling = threading.Event()
         self.poll_api = None
         self.pipeline = None
         self.final_output_collection = None
         self.output_name = output_name
+        self.output_tags = output_tags
         self.project_uuid = None
 
         if keep_client is not None:
@@ -71,7 +79,9 @@ class ArvCwlRunner(object):
         else:
             self.keep_client = arvados.keep.KeepClient(api_client=self.api, num_retries=self.num_retries)
 
-        for api in ["jobs", "containers"]:
+        self.work_api = None
+        expected_api = ["jobs", "containers"]
+        for api in expected_api:
             try:
                 methods = self.api._rootDesc.get('resources')[api]['methods']
                 if ('httpMethod' in methods['create'] and
@@ -80,14 +90,18 @@ class ArvCwlRunner(object):
                     break
             except KeyError:
                 pass
+
         if not self.work_api:
             if work_api is None:
                 raise Exception("No supported APIs")
             else:
-                raise Exception("Unsupported API '%s'" % work_api)
+                raise Exception("Unsupported API '%s', expected one of %s" % (work_api, expected_api))
 
     def arv_make_tool(self, toolpath_object, **kwargs):
         kwargs["work_api"] = self.work_api
+        kwargs["fetcher_constructor"] = partial(CollectionFetcher,
+                                                api_client=self.api,
+                                                keep_client=self.keep_client)
         if "class" in toolpath_object and toolpath_object["class"] == "CommandLineTool":
             return ArvadosCommandTool(self, toolpath_object, **kwargs)
         elif "class" in toolpath_object and toolpath_object["class"] == "Workflow":
@@ -116,56 +130,67 @@ class ArvCwlRunner(object):
                     uuid = event["object_uuid"]
                     with self.lock:
                         j = self.processes[uuid]
-                        logger.info("Job %s (%s) is Running", j.name, uuid)
+                        logger.info("%s %s is Running", self.label(j), uuid)
                         j.running = True
                         j.update_pipeline_component(event["properties"]["new_attributes"])
-                elif event["properties"]["new_attributes"]["state"] in ("Complete", "Failed", "Cancelled"):
+                elif event["properties"]["new_attributes"]["state"] in ("Complete", "Failed", "Cancelled", "Final"):
                     uuid = event["object_uuid"]
                     try:
                         self.cond.acquire()
                         j = self.processes[uuid]
-                        txt = self.work_api[0].upper() + self.work_api[1:-1]
-                        logger.info("%s %s (%s) is %s", txt, j.name, uuid, event["properties"]["new_attributes"]["state"])
+                        logger.info("%s %s is %s", self.label(j), uuid, event["properties"]["new_attributes"]["state"])
                         with Perf(metrics, "done %s" % j.name):
                             j.done(event["properties"]["new_attributes"])
                         self.cond.notify()
                     finally:
                         self.cond.release()
 
+    def label(self, obj):
+        return "[%s %s]" % (self.work_api[0:-1], obj.name)
+
     def poll_states(self):
         """Poll status of jobs or containers listed in the processes dict.
 
         Runs in a separate thread.
         """
 
-        while True:
-            self.stop_polling.wait(15)
-            if self.stop_polling.is_set():
-                break
-            with self.lock:
-                keys = self.processes.keys()
-            if not keys:
-                continue
+        try:
+            while True:
+                self.stop_polling.wait(15)
+                if self.stop_polling.is_set():
+                    break
+                with self.lock:
+                    keys = self.processes.keys()
+                if not keys:
+                    continue
 
-            if self.work_api == "containers":
-                table = self.poll_api.containers()
-            elif self.work_api == "jobs":
-                table = self.poll_api.jobs()
+                if self.work_api == "containers":
+                    table = self.poll_api.container_requests()
+                elif self.work_api == "jobs":
+                    table = self.poll_api.jobs()
 
-            try:
-                proc_states = table.list(filters=[["uuid", "in", keys]]).execute(num_retries=self.num_retries)
-            except Exception as e:
-                logger.warn("Error checking states on API server: %s", e)
-                continue
-
-            for p in proc_states["items"]:
-                self.on_message({
-                    "object_uuid": p["uuid"],
-                    "event_type": "update",
-                    "properties": {
-                        "new_attributes": p
-                    }
-                })
+                try:
+                    proc_states = table.list(filters=[["uuid", "in", keys]]).execute(num_retries=self.num_retries)
+                except Exception as e:
+                    logger.warn("Error checking states on API server: %s", e)
+                    continue
+
+                for p in proc_states["items"]:
+                    self.on_message({
+                        "object_uuid": p["uuid"],
+                        "event_type": "update",
+                        "properties": {
+                            "new_attributes": p
+                        }
+                    })
+        except:
+            logger.error("Fatal error in state polling thread.", exc_info=(sys.exc_info()[1] if self.debug else False))
+            self.cond.acquire()
+            self.processes.clear()
+            self.cond.notify()
+            self.cond.release()
+        finally:
+            self.stop_polling.set()
 
     def get_uploaded(self):
         return self.uploaded.copy()
@@ -173,17 +198,27 @@ class ArvCwlRunner(object):
     def add_uploaded(self, src, pair):
         self.uploaded[src] = pair
 
-    def check_writable(self, obj):
+    def check_features(self, obj):
         if isinstance(obj, dict):
+            if obj.get("class") == "InitialWorkDirRequirement":
+                if self.work_api == "containers":
+                    raise UnsupportedRequirement("InitialWorkDirRequirement not supported with --api=containers")
             if obj.get("writable"):
-                raise UnsupportedRequirement("InitialWorkDir feature 'writable: true' not supported")
+                raise SourceLine(obj, "writable", UnsupportedRequirement).makeError("InitialWorkDir feature 'writable: true' not supported")
+            if obj.get("class") == "CommandLineTool":
+                if self.work_api == "containers":
+                    if obj.get("stdin"):
+                        raise SourceLine(obj, "stdin", UnsupportedRequirement).makeError("Stdin redirection currently not suppported with --api=containers")
+                    if obj.get("stderr"):
+                        raise SourceLine(obj, "stderr", UnsupportedRequirement).makeError("Stderr redirection currently not suppported with --api=containers")
             for v in obj.itervalues():
-                self.check_writable(v)
-        if isinstance(obj, list):
-            for v in obj:
-                self.check_writable(v)
+                self.check_features(v)
+        elif isinstance(obj, list):
+            for i,v in enumerate(obj):
+                with SourceLine(obj, i, UnsupportedRequirement):
+                    self.check_features(v)
 
-    def make_output_collection(self, name, outputObj):
+    def make_output_collection(self, name, tagsString, outputObj):
         outputObj = copy.deepcopy(outputObj)
 
         files = []
@@ -248,6 +283,13 @@ class ArvCwlRunner(object):
                     final.api_response()["name"],
                     final.manifest_locator())
 
+        final_uuid = final.manifest_locator()
+        tags = tagsString.split(',')
+        for tag in tags:
+             self.api.links().create(body={
+                "head_uuid": final_uuid, "link_class": "tag", "name": tag
+                }).execute(num_retries=self.num_retries)
+
         def finalcollection(fileobj):
             fileobj["location"] = "keep:%s/%s" % (final.portable_data_hash(), fileobj["location"])
 
@@ -260,6 +302,12 @@ class ArvCwlRunner(object):
         if self.work_api == "containers":
             try:
                 current = self.api.containers().current().execute(num_retries=self.num_retries)
+            except ApiError as e:
+                # Status code 404 just means we're not running in a container.
+                if e.resp.status != 404:
+                    logger.info("Getting current container: %s", e)
+                return
+            try:
                 self.api.containers().update(uuid=current['uuid'],
                                              body={
                                                  'output': self.final_output_collection.portable_data_hash(),
@@ -277,24 +325,32 @@ class ArvCwlRunner(object):
     def arv_executor(self, tool, job_order, **kwargs):
         self.debug = kwargs.get("debug")
 
-        tool.visit(self.check_writable)
+        tool.visit(self.check_features)
 
-        useruuid = self.api.users().current().execute()["uuid"]
-        self.project_uuid = kwargs.get("project_uuid") if kwargs.get("project_uuid") else useruuid
+        self.project_uuid = kwargs.get("project_uuid")
         self.pipeline = None
         make_fs_access = kwargs.get("make_fs_access") or partial(CollectionFsAccess,
                                                                  api_client=self.api,
                                                                  keep_client=self.keep_client)
         self.fs_access = make_fs_access(kwargs["basedir"])
 
-        if kwargs.get("create_template"):
-            tmpl = RunnerTemplate(self, tool, job_order, kwargs.get("enable_reuse"))
-            tmpl.save()
-            # cwltool.main will write our return value to stdout.
-            return tmpl.uuid
-
-        if kwargs.get("create_workflow") or kwargs.get("update_workflow"):
-            return upload_workflow(self, tool, job_order, self.project_uuid, kwargs.get("update_workflow"))
+        existing_uuid = kwargs.get("update_workflow")
+        if existing_uuid or kwargs.get("create_workflow"):
+            if self.work_api == "jobs":
+                tmpl = RunnerTemplate(self, tool, job_order,
+                                      kwargs.get("enable_reuse"),
+                                      uuid=existing_uuid,
+                                      submit_runner_ram=kwargs.get("submit_runner_ram"),
+                                      name=kwargs.get("name"))
+                tmpl.save()
+                # cwltool.main will write our return value to stdout.
+                return (tmpl.uuid, "success")
+            else:
+                return (upload_workflow(self, tool, job_order,
+                                       self.project_uuid,
+                                       uuid=existing_uuid,
+                                       submit_runner_ram=kwargs.get("submit_runner_ram"),
+                                        name=kwargs.get("name")), "success")
 
         self.ignore_docker_for_reuse = kwargs.get("ignore_docker_for_reuse")
 
@@ -302,9 +358,11 @@ class ArvCwlRunner(object):
         kwargs["enable_reuse"] = kwargs.get("enable_reuse")
         kwargs["use_container"] = True
         kwargs["tmpdir_prefix"] = "tmp"
-        kwargs["on_error"] = "continue"
         kwargs["compute_checksum"] = kwargs.get("compute_checksum")
 
+        if not kwargs["name"]:
+            del kwargs["name"]
+
         if self.work_api == "containers":
             kwargs["outdir"] = "/var/spool/cwl"
             kwargs["docker_outdir"] = "/var/spool/cwl"
@@ -321,27 +379,32 @@ class ArvCwlRunner(object):
         if kwargs.get("submit"):
             if self.work_api == "containers":
                 if tool.tool["class"] == "CommandLineTool":
+                    kwargs["runnerjob"] = tool.tool["id"]
                     runnerjob = tool.job(job_order,
                                          self.output_callback,
                                          **kwargs).next()
                 else:
-                    runnerjob = RunnerContainer(self, tool, job_order, kwargs.get("enable_reuse"), self.output_name)
+                    runnerjob = RunnerContainer(self, tool, job_order, kwargs.get("enable_reuse"), self.output_name,
+                                                self.output_tags, submit_runner_ram=kwargs.get("submit_runner_ram"),
+                                                name=kwargs.get("name"), on_error=kwargs.get("on_error"))
             else:
-                runnerjob = RunnerJob(self, tool, job_order, kwargs.get("enable_reuse"), self.output_name)
+                runnerjob = RunnerJob(self, tool, job_order, kwargs.get("enable_reuse"), self.output_name,
+                                      self.output_tags, submit_runner_ram=kwargs.get("submit_runner_ram"),
+                                      name=kwargs.get("name"), on_error=kwargs.get("on_error"))
 
         if not kwargs.get("submit") and "cwl_runner_job" not in kwargs and not self.work_api == "containers":
             # Create pipeline for local run
             self.pipeline = self.api.pipeline_instances().create(
                 body={
                     "owner_uuid": self.project_uuid,
-                    "name": shortname(tool.tool["id"]),
+                    "name": kwargs["name"] if kwargs.get("name") else shortname(tool.tool["id"]),
                     "components": {},
                     "state": "RunningOnClient"}).execute(num_retries=self.num_retries)
             logger.info("Pipeline instance %s", self.pipeline["uuid"])
 
         if runnerjob and not kwargs.get("wait"):
             runnerjob.run(wait=kwargs.get("wait"))
-            return runnerjob.uuid
+            return (runnerjob.uuid, "success")
 
         self.poll_api = arvados.api('v1')
         self.polling_thread = threading.Thread(target=self.poll_states)
@@ -366,6 +429,10 @@ class ArvCwlRunner(object):
             loopperf.__enter__()
             for runnable in jobiter:
                 loopperf.__exit__()
+
+                if self.stop_polling.is_set():
+                    break
+
                 if runnable:
                     with Perf(metrics, "run"):
                         runnable.run(**kwargs)
@@ -387,7 +454,7 @@ class ArvCwlRunner(object):
             if sys.exc_info()[0] is KeyboardInterrupt:
                 logger.error("Interrupted, marking pipeline as failed")
             else:
-                logger.error("Caught unhandled exception, marking pipeline as failed.  Error was: %s", sys.exc_info()[1], exc_info=(sys.exc_info()[1] if self.debug else False))
+                logger.error("Execution failed: %s", sys.exc_info()[1], exc_info=(sys.exc_info()[1] if self.debug else False))
             if self.pipeline:
                 self.api.pipeline_instances().update(uuid=self.pipeline["uuid"],
                                                      body={"state": "Failed"}).execute(num_retries=self.num_retries)
@@ -410,17 +477,16 @@ class ArvCwlRunner(object):
         else:
             if self.output_name is None:
                 self.output_name = "Output of %s" % (shortname(tool.tool["id"]))
-            self.final_output, self.final_output_collection = self.make_output_collection(self.output_name, self.final_output)
+            if self.output_tags is None:
+                self.output_tags = ""
+            self.final_output, self.final_output_collection = self.make_output_collection(self.output_name, self.output_tags, self.final_output)
             self.set_crunch_output()
 
-        if self.final_status != "success":
-            raise WorkflowException("Workflow failed.")
-
         if kwargs.get("compute_checksum"):
             adjustDirObjs(self.final_output, partial(getListing, self.fs_access))
             adjustFileObjs(self.final_output, partial(compute_checksums, self.fs_access))
 
-        return self.final_output
+        return (self.final_output, self.final_status)
 
 
 def versionstring():
@@ -468,6 +534,7 @@ def arg_parser():  # type: () -> argparse.ArgumentParser
 
     parser.add_argument("--project-uuid", type=str, metavar="UUID", help="Project that will own the workflow jobs, if not provided, will go to home project.")
     parser.add_argument("--output-name", type=str, help="Name to use for collection that stores the final output.", default=None)
+    parser.add_argument("--output-tags", type=str, help="Tags for the final output collection separated by commas, e.g., '--output-tags tag0,tag1,tag2'.", default=None)
     parser.add_argument("--ignore-docker-for-reuse", action="store_true",
                         help="Ignore Docker image version when deciding whether to reuse past jobs.",
                         default=False)
@@ -477,9 +544,10 @@ def arg_parser():  # type: () -> argparse.ArgumentParser
                         default=True, dest="submit")
     exgroup.add_argument("--local", action="store_false", help="Run workflow on local host (submits jobs to Arvados).",
                         default=True, dest="submit")
-    exgroup.add_argument("--create-template", action="store_true", help="Create an Arvados pipeline template.")
-    exgroup.add_argument("--create-workflow", action="store_true", help="Create an Arvados workflow.")
-    exgroup.add_argument("--update-workflow", type=str, metavar="UUID", help="Update existing Arvados workflow with uuid.")
+    exgroup.add_argument("--create-template", action="store_true", help="(Deprecated) synonym for --create-workflow.",
+                         dest="create_workflow")
+    exgroup.add_argument("--create-workflow", action="store_true", help="Create an Arvados workflow (if using the 'containers' API) or pipeline template (if using the 'jobs' API). See --api.")
+    exgroup.add_argument("--update-workflow", type=str, metavar="UUID", help="Update an existing Arvados workflow or pipeline template with the given UUID.")
 
     exgroup = parser.add_mutually_exclusive_group()
     exgroup.add_argument("--wait", action="store_true", help="After submitting workflow runner job, wait for completion.",
@@ -487,6 +555,12 @@ def arg_parser():  # type: () -> argparse.ArgumentParser
     exgroup.add_argument("--no-wait", action="store_false", help="Submit workflow runner job and exit.",
                         default=True, dest="wait")
 
+    exgroup = parser.add_mutually_exclusive_group()
+    exgroup.add_argument("--log-timestamps", action="store_true", help="Prefix logging lines with timestamp",
+                        default=True, dest="log_timestamps")
+    exgroup.add_argument("--no-log-timestamps", action="store_false", help="No timestamp on logging lines",
+                        default=True, dest="log_timestamps")
+
     parser.add_argument("--api", type=str,
                         default=None, dest="work_api",
                         help="Select work submission API, one of 'jobs' or 'containers'. Default is 'jobs' if that API is available, otherwise 'containers'.")
@@ -495,6 +569,18 @@ def arg_parser():  # type: () -> argparse.ArgumentParser
                         help="Compute checksum of contents while collecting outputs",
                         dest="compute_checksum")
 
+    parser.add_argument("--submit-runner-ram", type=int,
+                        help="RAM (in MiB) required for the workflow runner job (default 1024)",
+                        default=1024)
+
+    parser.add_argument("--name", type=str,
+                        help="Name to use for workflow execution instance.",
+                        default=None)
+
+    parser.add_argument("--on-error", type=str,
+                        help="Desired workflow behavior when a step fails.  One of 'stop' or 'continue'. "
+                        "Default is 'continue'.", default="continue", choices=("stop", "continue"))
+
     parser.add_argument("workflow", type=str, nargs="?", default=None, help="The workflow to execute")
     parser.add_argument("job_order", nargs=argparse.REMAINDER, help="The input object to the workflow.")
 
@@ -517,7 +603,25 @@ def main(args, stdout, stderr, api_client=None, keep_client=None):
 
     job_order_object = None
     arvargs = parser.parse_args(args)
-    if (arvargs.create_template or arvargs.create_workflow or arvargs.update_workflow) and not arvargs.job_order:
+
+    if arvargs.version:
+        print versionstring()
+        return
+
+    if arvargs.update_workflow:
+        if arvargs.update_workflow.find('-7fd4e-') == 5:
+            want_api = 'containers'
+        elif arvargs.update_workflow.find('-p5p6p-') == 5:
+            want_api = 'jobs'
+        else:
+            want_api = None
+        if want_api and arvargs.work_api and want_api != arvargs.work_api:
+            logger.error('--update-workflow arg {!r} uses {!r} API, but --api={!r} specified'.format(
+                arvargs.update_workflow, want_api, arvargs.work_api))
+            return 1
+        arvargs.work_api = want_api
+
+    if (arvargs.create_workflow or arvargs.update_workflow) and not arvargs.job_order:
         job_order_object = ({}, "")
 
     add_arv_hints()
@@ -525,7 +629,11 @@ def main(args, stdout, stderr, api_client=None, keep_client=None):
     try:
         if api_client is None:
             api_client=arvados.api('v1', model=OrderedJsonModel())
-        runner = ArvCwlRunner(api_client, work_api=arvargs.work_api, keep_client=keep_client, output_name=arvargs.output_name)
+        if keep_client is None:
+            keep_client = arvados.keep.KeepClient(api_client=api_client, num_retries=4)
+        runner = ArvCwlRunner(api_client, work_api=arvargs.work_api, keep_client=keep_client,
+                              num_retries=4, output_name=arvargs.output_name,
+                              output_tags=arvargs.output_tags)
     except Exception as e:
         logger.error(e)
         return 1
@@ -541,8 +649,17 @@ def main(args, stdout, stderr, api_client=None, keep_client=None):
         metrics.setLevel(logging.DEBUG)
         logging.getLogger("cwltool.metrics").setLevel(logging.DEBUG)
 
+    if arvargs.log_timestamps:
+        arvados.log_handler.setFormatter(logging.Formatter(
+            '%(asctime)s %(name)s %(levelname)s: %(message)s',
+            '%Y-%m-%d %H:%M:%S'))
+    else:
+        arvados.log_handler.setFormatter(logging.Formatter('%(name)s %(levelname)s: %(message)s'))
+
     arvargs.conformance_test = None
     arvargs.use_container = True
+    arvargs.relax_path_checks = True
+    arvargs.validate = None
 
     return cwltool.main.main(args=arvargs,
                              stdout=stdout,
@@ -551,4 +668,11 @@ def main(args, stdout, stderr, api_client=None, keep_client=None):
                              makeTool=runner.arv_make_tool,
                              versionfunc=versionstring,
                              job_order_object=job_order_object,
-                             make_fs_access=partial(CollectionFsAccess, api_client=api_client))
+                             make_fs_access=partial(CollectionFsAccess,
+                                                    api_client=api_client,
+                                                    keep_client=keep_client),
+                             fetcher_constructor=partial(CollectionFetcher,
+                                                         api_client=api_client,
+                                                         keep_client=keep_client),
+                             resolver=partial(collectionResolver, api_client),
+                             logger_handler=arvados.log_handler)
index c0d82d9585a70488c450f085d9e91704718a5b46..987b0d64bbc821bc199456b4ab317ab252841ee6 100644 (file)
@@ -2,6 +2,8 @@ import logging
 import json
 import os
 
+import ruamel.yaml as yaml
+
 from cwltool.errors import WorkflowException
 from cwltool.process import get_feature, UnsupportedRequirement, shortname
 from cwltool.pathmapper import adjustFiles
@@ -12,6 +14,7 @@ import arvados.collection
 from .arvdocker import arv_docker_get_image
 from . import done
 from .runner import Runner, arvados_jobs_image
+from .fsaccess import CollectionFetcher
 
 logger = logging.getLogger('arvados.cwl-runner')
 
@@ -34,7 +37,8 @@ class ArvadosContainer(object):
             "output_path": self.outdir,
             "cwd": self.outdir,
             "priority": 1,
-            "state": "Committed"
+            "state": "Committed",
+            "properties": {}
         }
         runtime_constraints = {}
         mounts = {
@@ -42,6 +46,7 @@ class ArvadosContainer(object):
                 "kind": "tmp"
             }
         }
+        scheduling_parameters = {}
 
         dirs = set()
         for f in self.pathmapper.files():
@@ -61,7 +66,7 @@ class ArvadosContainer(object):
                 }
 
         if self.generatefiles["listing"]:
-            raise UnsupportedRequirement("Generate files not supported")
+            raise UnsupportedRequirement("InitialWorkDirRequirement not supported with --api=containers")
 
         container_request["environment"] = {"TMPDIR": self.tmpdir, "HOME": self.outdir}
         if self.environment:
@@ -102,35 +107,43 @@ class ArvadosContainer(object):
 
         partition_req, _ = get_feature(self, "http://arvados.org/cwl#PartitionRequirement")
         if partition_req:
-            runtime_constraints["partition"] = aslist(partition_req["partition"])
+            scheduling_parameters["partitions"] = aslist(partition_req["partition"])
 
         container_request["mounts"] = mounts
         container_request["runtime_constraints"] = runtime_constraints
         container_request["use_existing"] = kwargs.get("enable_reuse", True)
+        container_request["scheduling_parameters"] = scheduling_parameters
+
+        if kwargs.get("runnerjob", "").startswith("arvwf:"):
+            wfuuid = kwargs["runnerjob"][6:kwargs["runnerjob"].index("#")]
+            wfrecord = self.arvrunner.api.workflows().get(uuid=wfuuid).execute(num_retries=self.arvrunner.num_retries)
+            if container_request["name"] == "main":
+                container_request["name"] = wfrecord["name"]
+            container_request["properties"]["template_uuid"] = wfuuid
 
         try:
             response = self.arvrunner.api.container_requests().create(
                 body=container_request
             ).execute(num_retries=self.arvrunner.num_retries)
 
-            self.arvrunner.processes[response["container_uuid"]] = self
-
-            container = self.arvrunner.api.containers().get(
-                uuid=response["container_uuid"]
-            ).execute(num_retries=self.arvrunner.num_retries)
+            self.uuid = response["uuid"]
+            self.arvrunner.processes[self.uuid] = self
 
-            logger.info("Container request %s (%s) state is %s with container %s %s", self.name, response["uuid"], response["state"], container["uuid"], container["state"])
+            logger.info("%s %s state is %s", self.arvrunner.label(self), response["uuid"], response["state"])
 
-            if container["state"] in ("Complete", "Cancelled"):
-                self.done(container)
+            if response["state"] == "Final":
+                self.done(response)
         except Exception as e:
-            logger.error("Got error %s" % str(e))
+            logger.error("%s got error %s" % (self.arvrunner.label(self), str(e)))
             self.output_callback({}, "permanentFail")
 
     def done(self, record):
         try:
-            if record["state"] == "Complete":
-                rcode = record["exit_code"]
+            container = self.arvrunner.api.containers().get(
+                uuid=record["container_uuid"]
+            ).execute(num_retries=self.arvrunner.num_retries)
+            if container["state"] == "Complete":
+                rcode = container["exit_code"]
                 if self.successCodes and rcode in self.successCodes:
                     processStatus = "success"
                 elif self.temporaryFailCodes and rcode in self.temporaryFailCodes:
@@ -144,20 +157,27 @@ class ArvadosContainer(object):
             else:
                 processStatus = "permanentFail"
 
-            try:
-                outputs = {}
-                if record["output"]:
-                    outputs = done.done(self, record, "/tmp", self.outdir, "/keep")
-            except WorkflowException as e:
-                logger.error("Error while collecting container outputs:\n%s", e, exc_info=(e if self.arvrunner.debug else False))
-                processStatus = "permanentFail"
-            except Exception as e:
-                logger.exception("Got unknown exception while collecting job outputs:")
-                processStatus = "permanentFail"
-
-            self.output_callback(outputs, processStatus)
+            if processStatus == "permanentFail":
+                logc = arvados.collection.CollectionReader(container["log"],
+                                                           api_client=self.arvrunner.api,
+                                                           keep_client=self.arvrunner.keep_client,
+                                                           num_retries=self.arvrunner.num_retries)
+                done.logtail(logc, logger, "%s error log:" % self.arvrunner.label(self))
+
+            outputs = {}
+            if container["output"]:
+                outputs = done.done_outputs(self, container, "/tmp", self.outdir, "/keep")
+        except WorkflowException as e:
+            logger.error("%s unable to collect output from %s:\n%s",
+                         self.arvrunner.label(self), container["output"], e, exc_info=(e if self.arvrunner.debug else False))
+            processStatus = "permanentFail"
+        except Exception as e:
+            logger.exception("%s while getting output object: %s", self.arvrunner.label(self), e)
+            processStatus = "permanentFail"
         finally:
-            del self.arvrunner.processes[record["uuid"]]
+            self.output_callback(outputs, processStatus)
+            if record["uuid"] in self.arvrunner.processes:
+                del self.arvrunner.processes[record["uuid"]]
 
 
 class RunnerContainer(Runner):
@@ -172,32 +192,7 @@ class RunnerContainer(Runner):
 
         workflowmapper = super(RunnerContainer, self).arvados_job_spec(dry_run=dry_run, pull_image=pull_image, **kwargs)
 
-        with arvados.collection.Collection(api_client=self.arvrunner.api,
-                                           keep_client=self.arvrunner.keep_client,
-                                           num_retries=self.arvrunner.num_retries) as jobobj:
-            with jobobj.open("cwl.input.json", "w") as f:
-                json.dump(self.job_order, f, sort_keys=True, indent=4)
-            jobobj.save_new(owner_uuid=self.arvrunner.project_uuid)
-
-        workflowname = os.path.basename(self.tool.tool["id"])
-        workflowpath = "/var/lib/cwl/workflow/%s" % workflowname
-        workflowcollection = workflowmapper.mapper(self.tool.tool["id"])[1]
-        workflowcollection = workflowcollection[5:workflowcollection.index('/')]
-        jobpath = "/var/lib/cwl/job/cwl.input.json"
-
-        command = ["arvados-cwl-runner", "--local", "--api=containers"]
-        if self.output_name:
-            command.append("--output-name=" + self.output_name)
-
-        if self.enable_reuse:
-            command.append("--enable-reuse")
-        else:
-            command.append("--disable-reuse")
-
-        command.extend([workflowpath, jobpath])
-
-        return {
-            "command": command,
+        container_req = {
             "owner_uuid": self.arvrunner.project_uuid,
             "name": self.name,
             "output_path": "/var/spool/cwl",
@@ -206,13 +201,9 @@ class RunnerContainer(Runner):
             "state": "Committed",
             "container_image": arvados_jobs_image(self.arvrunner),
             "mounts": {
-                "/var/lib/cwl/workflow": {
-                    "kind": "collection",
-                    "portable_data_hash": "%s" % workflowcollection
-                },
-                jobpath: {
-                    "kind": "collection",
-                    "portable_data_hash": "%s/cwl.input.json" % jobobj.portable_data_hash()
+                "/var/lib/cwl/cwl.input.json": {
+                    "kind": "json",
+                    "content": self.job_order
                 },
                 "stdout": {
                     "kind": "file",
@@ -225,11 +216,60 @@ class RunnerContainer(Runner):
             },
             "runtime_constraints": {
                 "vcpus": 1,
-                "ram": 1024*1024*256,
+                "ram": 1024*1024 * self.submit_runner_ram,
                 "API": True
-            }
+            },
+            "properties": {}
         }
 
+        workflowcollection = workflowmapper.mapper(self.tool.tool["id"])[1]
+        if workflowcollection.startswith("keep:"):
+            workflowcollection = workflowcollection[5:workflowcollection.index('/')]
+            workflowname = os.path.basename(self.tool.tool["id"])
+            workflowpath = "/var/lib/cwl/workflow/%s" % workflowname
+            container_req["mounts"]["/var/lib/cwl/workflow"] = {
+                "kind": "collection",
+                "portable_data_hash": "%s" % workflowcollection
+                }
+        elif workflowcollection.startswith("arvwf:"):
+            workflowpath = "/var/lib/cwl/workflow.json#main"
+            wfuuid = workflowcollection[6:workflowcollection.index("#")]
+            wfrecord = self.arvrunner.api.workflows().get(uuid=wfuuid).execute(num_retries=self.arvrunner.num_retries)
+            wfobj = yaml.safe_load(wfrecord["definition"])
+            if container_req["name"].startswith("arvwf:"):
+                container_req["name"] = wfrecord["name"]
+            container_req["mounts"]["/var/lib/cwl/workflow.json"] = {
+                "kind": "json",
+                "json": wfobj
+            }
+            container_req["properties"]["template_uuid"] = wfuuid
+
+        command = ["arvados-cwl-runner", "--local", "--api=containers", "--no-log-timestamps"]
+        if self.output_name:
+            command.append("--output-name=" + self.output_name)
+            container_req["output_name"] = self.output_name
+
+        if self.output_tags:
+            command.append("--output-tags=" + self.output_tags)
+
+        if kwargs.get("debug"):
+            command.append("--debug")
+
+        if self.enable_reuse:
+            command.append("--enable-reuse")
+        else:
+            command.append("--disable-reuse")
+
+        if self.on_error:
+            command.append("--on-error=" + self.on_error)
+
+        command.extend([workflowpath, "/var/lib/cwl/cwl.input.json"])
+
+        container_req["command"] = command
+
+        return container_req
+
+
     def run(self, *args, **kwargs):
         kwargs["keepprefix"] = "keep:"
         job_spec = self.arvados_job_spec(*args, **kwargs)
@@ -240,9 +280,23 @@ class RunnerContainer(Runner):
         ).execute(num_retries=self.arvrunner.num_retries)
 
         self.uuid = response["uuid"]
-        self.arvrunner.processes[response["container_uuid"]] = self
+        self.arvrunner.processes[self.uuid] = self
 
-        logger.info("Submitted container %s", response["uuid"])
+        logger.info("%s submitted container %s", self.arvrunner.label(self), response["uuid"])
 
-        if response["state"] in ("Complete", "Failed", "Cancelled"):
+        if response["state"] == "Final":
             self.done(response)
+
+    def done(self, record):
+        try:
+            container = self.arvrunner.api.containers().get(
+                uuid=record["container_uuid"]
+            ).execute(num_retries=self.arvrunner.num_retries)
+        except Exception as e:
+            logger.exception("%s while getting runner container: %s", self.arvrunner.label(self), e)
+            self.arvrunner.output_callback({}, "permanentFail")
+        else:
+            super(RunnerContainer, self).done(container)
+        finally:
+            if record["uuid"] in self.arvrunner.processes:
+                del self.arvrunner.processes[record["uuid"]]
index b9691d215c4d46e071d13740c2d3c90b9f7d1a81..88c5dd2d4f428e946602c4eaeb5c59c1e4e4a2e6 100644 (file)
@@ -2,6 +2,8 @@ import logging
 import sys
 import threading
 
+from schema_salad.sourceline import SourceLine
+
 import cwltool.docker
 from cwltool.errors import WorkflowException
 import arvados.commands.keepdocker
@@ -16,6 +18,8 @@ def arv_docker_get_image(api_client, dockerRequirement, pull_image, project_uuid
 
     if "dockerImageId" not in dockerRequirement and "dockerPull" in dockerRequirement:
         dockerRequirement["dockerImageId"] = dockerRequirement["dockerPull"]
+        if hasattr(dockerRequirement, 'lc'):
+            dockerRequirement.lc.data["dockerImageId"] = dockerRequirement.lc.data["dockerPull"]
 
     global cached_lookups
     global cached_lookups_lock
@@ -23,42 +27,46 @@ def arv_docker_get_image(api_client, dockerRequirement, pull_image, project_uuid
         if dockerRequirement["dockerImageId"] in cached_lookups:
             return cached_lookups[dockerRequirement["dockerImageId"]]
 
-    sp = dockerRequirement["dockerImageId"].split(":")
-    image_name = sp[0]
-    image_tag = sp[1] if len(sp) > 1 else None
-
-    images = arvados.commands.keepdocker.list_images_in_arv(api_client, 3,
-                                                            image_name=image_name,
-                                                            image_tag=image_tag)
-
-    if not images:
-        # Fetch Docker image if necessary.
-        cwltool.docker.get_image(dockerRequirement, pull_image)
-
-        # Upload image to Arvados
-        args = ["--project-uuid="+project_uuid, image_name]
-        if image_tag:
-            args.append(image_tag)
-        logger.info("Uploading Docker image %s", ":".join(args[1:]))
-        try:
-            arvados.commands.keepdocker.main(args, stdout=sys.stderr)
-        except SystemExit as e:
-            if e.code:
-                raise WorkflowException("keepdocker exited with code %s" % e.code)
+    with SourceLine(dockerRequirement, "dockerImageId", WorkflowException):
+        sp = dockerRequirement["dockerImageId"].split(":")
+        image_name = sp[0]
+        image_tag = sp[1] if len(sp) > 1 else None
 
         images = arvados.commands.keepdocker.list_images_in_arv(api_client, 3,
                                                                 image_name=image_name,
                                                                 image_tag=image_tag)
 
-    if not images:
-        raise WorkflowException("Could not find Docker image %s:%s" % (image_name, image_tag))
-
-    pdh = api_client.collections().get(uuid=images[0][0]).execute()["portable_data_hash"]
-
-    with cached_lookups_lock:
-        cached_lookups[dockerRequirement["dockerImageId"]] = pdh
-
-    return pdh
+        if not images:
+            # Fetch Docker image if necessary.
+            cwltool.docker.get_image(dockerRequirement, pull_image)
+
+            # Upload image to Arvados
+            args = []
+            if project_uuid:
+                args.append("--project-uuid="+project_uuid)
+            args.append(image_name)
+            if image_tag:
+                args.append(image_tag)
+            logger.info("Uploading Docker image %s", ":".join(args[1:]))
+            try:
+                arvados.commands.keepdocker.main(args, stdout=sys.stderr)
+            except SystemExit as e:
+                if e.code:
+                    raise WorkflowException("keepdocker exited with code %s" % e.code)
+
+            images = arvados.commands.keepdocker.list_images_in_arv(api_client, 3,
+                                                                    image_name=image_name,
+                                                                    image_tag=image_tag)
+
+        if not images:
+            raise WorkflowException("Could not find Docker image %s:%s" % (image_name, image_tag))
+
+        pdh = api_client.collections().get(uuid=images[0][0]).execute()["portable_data_hash"]
+
+        with cached_lookups_lock:
+            cached_lookups[dockerRequirement["dockerImageId"]] = pdh
+
+        return pdh
 
 def arv_docker_clear_cache():
     global cached_lookups
index 8a62204f8fb9ec22298abec15529411ace70ed9e..11ef653a3d76c5862148493dc1736c0d72fadd0d 100644 (file)
@@ -24,6 +24,8 @@ metrics = logging.getLogger('arvados.cwl-runner.metrics')
 
 crunchrunner_re = re.compile(r"^\S+ \S+ \d+ \d+ stderr \S+ \S+ crunchrunner: \$\(task\.(tmpdir|outdir|keep)\)=(.*)")
 
+crunchrunner_git_commit = 'a3f2cb186e437bfce0031b024b2157b73ed2717d'
+
 class ArvadosJob(object):
     """Submit and manage a Crunch job for executing a CWL CommandLineTool."""
 
@@ -86,7 +88,8 @@ class ArvadosJob(object):
             (docker_req, docker_is_req) = get_feature(self, "DockerRequirement")
             if docker_req and kwargs.get("use_container") is not False:
                 if docker_req.get("dockerOutputDirectory"):
-                    raise UnsupportedRequirement("Option 'dockerOutputDirectory' of DockerRequirement not supported.")
+                    raise SourceLine(docker_req, "dockerOutputDirectory", UnsupportedRequirement).makeError(
+                        "Option 'dockerOutputDirectory' of DockerRequirement not supported.")
                 runtime_constraints["docker_image"] = arv_docker_get_image(self.arvrunner.api, docker_req, pull_image, self.arvrunner.project_uuid)
             else:
                 runtime_constraints["docker_image"] = arvados_jobs_image(self.arvrunner)
@@ -109,7 +112,7 @@ class ArvadosJob(object):
 
         filters = [["repository", "=", "arvados"],
                    ["script", "=", "crunchrunner"],
-                   ["script_version", "in git", "9e5b98e8f5f4727856b53447191f9c06e3da2ba6"]]
+                   ["script_version", "in git", crunchrunner_git_commit]]
         if not self.arvrunner.ignore_docker_for_reuse:
             filters.append(["docker_image_locator", "in docker", runtime_constraints["docker_image"]])
 
@@ -121,7 +124,7 @@ class ArvadosJob(object):
                         "script": "crunchrunner",
                         "repository": "arvados",
                         "script_version": "master",
-                        "minimum_script_version": "9e5b98e8f5f4727856b53447191f9c06e3da2ba6",
+                        "minimum_script_version": crunchrunner_git_commit,
                         "script_parameters": {"tasks": [script_parameters]},
                         "runtime_constraints": runtime_constraints
                     },
@@ -133,13 +136,13 @@ class ArvadosJob(object):
 
             self.update_pipeline_component(response)
 
-            logger.info("Job %s (%s) is %s", self.name, response["uuid"], response["state"])
+            logger.info("%s %s is %s", self.arvrunner.label(self), response["uuid"], response["state"])
 
             if response["state"] in ("Complete", "Failed", "Cancelled"):
                 with Perf(metrics, "done %s" % self.name):
                     self.done(response)
         except Exception as e:
-            logger.error("Got error %s" % str(e))
+            logger.exception("%s error" % (self.arvrunner.label(self)))
             self.output_callback({}, "permanentFail")
 
     def update_pipeline_component(self, record):
@@ -200,22 +203,30 @@ class ArvadosJob(object):
                             if g:
                                 dirs[g.group(1)] = g.group(2)
 
+                    if processStatus == "permanentFail":
+                        done.logtail(logc, logger, "%s error log:" % self.arvrunner.label(self))
+
                     with Perf(metrics, "output collection %s" % self.name):
                         outputs = done.done(self, record, dirs["tmpdir"],
                                             dirs["outdir"], dirs["keep"])
             except WorkflowException as e:
-                logger.error("Error while collecting job outputs:\n%s", e, exc_info=(e if self.arvrunner.debug else False))
+                logger.error("%s unable to collect output from %s:\n%s",
+                             self.arvrunner.label(self), record["output"], e, exc_info=(e if self.arvrunner.debug else False))
                 processStatus = "permanentFail"
-                outputs = None
             except Exception as e:
-                logger.exception("Got unknown exception while collecting job outputs:")
+                logger.exception("Got unknown exception while collecting output for job %s:", self.name)
                 processStatus = "permanentFail"
-                outputs = None
 
-            self.output_callback(outputs, processStatus)
+            # Note: Currently, on error output_callback is expecting an empty dict,
+            # anything else will fail.
+            if not isinstance(outputs, dict):
+                logger.error("Unexpected output type %s '%s'", type(outputs), outputs)
+                outputs = {}
+                processStatus = "permanentFail"
         finally:
-            del self.arvrunner.processes[record["uuid"]]
-
+            self.output_callback(outputs, processStatus)
+            if record["uuid"] in self.arvrunner.processes:
+                del self.arvrunner.processes[record["uuid"]]
 
 class RunnerJob(Runner):
     """Submit and manage a Crunch job that runs crunch_scripts/cwl-runner."""
@@ -230,26 +241,28 @@ class RunnerJob(Runner):
 
         workflowmapper = super(RunnerJob, self).arvados_job_spec(dry_run=dry_run, pull_image=pull_image, **kwargs)
 
-        # Need to filter this out, gets added by cwltool when providing
-        # parameters on the command line, and arv-run-pipeline-instance doesn't
-        # like it.
-        if "job_order" in self.job_order:
-            del self.job_order["job_order"]
-
         self.job_order["cwl:tool"] = workflowmapper.mapper(self.tool.tool["id"]).target[5:]
 
         if self.output_name:
             self.job_order["arv:output_name"] = self.output_name
 
+        if self.output_tags:
+            self.job_order["arv:output_tags"] = self.output_tags
+
         self.job_order["arv:enable_reuse"] = self.enable_reuse
 
+        if self.on_error:
+            self.job_order["arv:on_error"] = self.on_error
+
         return {
             "script": "cwl-runner",
-            "script_version": __version__,
+            "script_version": "master",
+            "minimum_script_version": "570509ab4d2ef93d870fd2b1f2eab178afb1bad9",
             "repository": "arvados",
             "script_parameters": self.job_order,
             "runtime_constraints": {
-                "docker_image": arvados_jobs_image(self.arvrunner)
+                "docker_image": arvados_jobs_image(self.arvrunner),
+                "min_ram_mb_per_node": self.submit_runner_ram
             }
         }
 
@@ -272,7 +285,7 @@ class RunnerJob(Runner):
         self.arvrunner.pipeline = self.arvrunner.api.pipeline_instances().create(
             body={
                 "owner_uuid": self.arvrunner.project_uuid,
-                "name": shortname(self.tool.tool["id"]),
+                "name": self.name,
                 "components": {"cwl-runner": job_spec },
                 "state": "RunningOnServer"}).execute(num_retries=self.arvrunner.num_retries)
         logger.info("Created pipeline %s", self.arvrunner.pipeline["uuid"])
@@ -300,7 +313,8 @@ class RunnerTemplate(object):
         'string': 'text',
     }
 
-    def __init__(self, runner, tool, job_order, enable_reuse):
+    def __init__(self, runner, tool, job_order, enable_reuse, uuid,
+                 submit_runner_ram=0, name=None):
         self.runner = runner
         self.tool = tool
         self.job = RunnerJob(
@@ -308,7 +322,11 @@ class RunnerTemplate(object):
             tool=tool,
             job_order=job_order,
             enable_reuse=enable_reuse,
-            output_name=None)
+            output_name=None,
+            output_tags=None,
+            submit_runner_ram=submit_runner_ram,
+            name=name)
+        self.uuid = uuid
 
     def pipeline_component_spec(self):
         """Return a component that Workbench and a-r-p-i will understand.
@@ -371,13 +389,21 @@ class RunnerTemplate(object):
         return spec
 
     def save(self):
-        job_spec = self.pipeline_component_spec()
-        response = self.runner.api.pipeline_templates().create(body={
+        body = {
             "components": {
-                self.job.name: job_spec,
+                self.job.name: self.pipeline_component_spec(),
             },
             "name": self.job.name,
-            "owner_uuid": self.runner.project_uuid,
-        }, ensure_unique_name=True).execute(num_retries=self.runner.num_retries)
-        self.uuid = response["uuid"]
-        logger.info("Created template %s", self.uuid)
+        }
+        if self.runner.project_uuid:
+            body["owner_uuid"] = self.runner.project_uuid
+        if self.uuid:
+            self.runner.api.pipeline_templates().update(
+                uuid=self.uuid, body=body).execute(
+                    num_retries=self.runner.num_retries)
+            logger.info("Updated template %s", self.uuid)
+        else:
+            self.uuid = self.runner.api.pipeline_templates().create(
+                body=body, ensure_unique_name=True).execute(
+                    num_retries=self.runner.num_retries)['uuid']
+            logger.info("Created template %s", self.uuid)
index ce633d43285a537268f3bc96dc446696d17d06a6..9e70a6e66f2b36f5393cd2f32039a38ec2489dd4 100644 (file)
@@ -3,6 +3,8 @@ import json
 import copy
 import logging
 
+from schema_salad.sourceline import SourceLine, cmap
+
 from cwltool.pack import pack
 from cwltool.load_tool import fetch_document
 from cwltool.process import shortname
@@ -18,7 +20,8 @@ from .perf import Perf
 logger = logging.getLogger('arvados.cwl-runner')
 metrics = logging.getLogger('arvados.cwl-runner.metrics')
 
-def upload_workflow(arvRunner, tool, job_order, project_uuid, update_uuid):
+def upload_workflow(arvRunner, tool, job_order, project_uuid, uuid=None,
+                    submit_runner_ram=0, name=None):
     upload_docker(arvRunner, tool)
 
     document_loader, workflowobj, uri = (tool.doc_loader, tool.doc_loader.fetch(tool.tool["id"]), tool.tool["id"])
@@ -33,22 +36,28 @@ def upload_workflow(arvRunner, tool, job_order, project_uuid, update_uuid):
         if sn in job_order:
             inp["default"] = job_order[sn]
 
-    name = os.path.basename(tool.tool["id"])
+    if not name:
+        name = tool.tool.get("label", os.path.basename(tool.tool["id"]))
+
     upload_dependencies(arvRunner, name, document_loader,
                         packed, uri, False)
 
+    # TODO nowhere for submit_runner_ram to go.
+
     body = {
         "workflow": {
-            "owner_uuid": project_uuid,
-            "name": tool.tool.get("label", name),
+            "name": name,
             "description": tool.tool.get("doc", ""),
-            "definition":yaml.safe_dump(packed)
+            "definition":yaml.round_trip_dump(packed)
         }}
+    if project_uuid:
+        body["workflow"]["owner_uuid"] = project_uuid
 
-    if update_uuid:
-        return arvRunner.api.workflows().update(uuid=update_uuid, body=body).execute(num_retries=arvRunner.num_retries)["uuid"]
+    if uuid:
+        call = arvRunner.api.workflows().update(uuid=uuid, body=body)
     else:
-        return arvRunner.api.workflows().create(body=body).execute(num_retries=arvRunner.num_retries)["uuid"]
+        call = arvRunner.api.workflows().create(body=body)
+    return call.execute(num_retries=arvRunner.num_retries)["uuid"]
 
 class ArvadosWorkflow(Workflow):
     """Wrap cwltool Workflow to override selected methods."""
@@ -62,6 +71,9 @@ class ArvadosWorkflow(Workflow):
         kwargs["work_api"] = self.work_api
         req, _ = self.get_requirement("http://arvados.org/cwl#RunInSingleContainer")
         if req:
+            with SourceLine(self.tool, None, WorkflowException):
+                if "id" not in self.tool:
+                    raise WorkflowException("%s object must have 'id'" % (self.tool["class"]))
             document_loader, workflowobj, uri = (self.doc_loader, self.doc_loader.fetch(self.tool["id"]), self.tool["id"])
 
             with Perf(metrics, "subworkflow upload_deps"):
@@ -87,23 +99,25 @@ class ArvadosWorkflow(Workflow):
                 joborder_keepmount = copy.deepcopy(joborder)
 
                 def keepmount(obj):
-                    if "location" not in obj:
-                        raise WorkflowException("%s object is missing required 'location' field: %s" % (obj["class"], obj))
-                    if obj["location"].startswith("keep:"):
-                        obj["location"] = "/keep/" + obj["location"][5:]
-                        if "listing" in obj:
-                            del obj["listing"]
-                    elif obj["location"].startswith("_:"):
-                        del obj["location"]
-                    else:
-                        raise WorkflowException("Location is not a keep reference or a literal: '%s'" % obj["location"])
+                    with SourceLine(obj, None, WorkflowException):
+                        if "location" not in obj:
+                            raise WorkflowException("%s object is missing required 'location' field: %s" % (obj["class"], obj))
+                    with SourceLine(obj, "location", WorkflowException):
+                        if obj["location"].startswith("keep:"):
+                            obj["location"] = "/keep/" + obj["location"][5:]
+                            if "listing" in obj:
+                                del obj["listing"]
+                        elif obj["location"].startswith("_:"):
+                            del obj["location"]
+                        else:
+                            raise WorkflowException("Location is not a keep reference or a literal: '%s'" % obj["location"])
 
                 adjustFileObjs(joborder_keepmount, keepmount)
                 adjustDirObjs(joborder_keepmount, keepmount)
                 adjustFileObjs(packed, keepmount)
                 adjustDirObjs(packed, keepmount)
 
-            wf_runner = {
+            wf_runner = cmap({
                 "class": "CommandLineTool",
                 "baseCommand": "cwltool",
                 "inputs": self.tool["inputs"],
@@ -114,15 +128,15 @@ class ArvadosWorkflow(Workflow):
                     "class": "InitialWorkDirRequirement",
                     "listing": [{
                             "entryname": "workflow.cwl",
-                            "entry": yaml.safe_dump(packed).replace("\\", "\\\\").replace('$(', '\$(').replace('${', '\${')
+                            "entry": yaml.round_trip_dump(packed).replace("\\", "\\\\").replace('$(', '\$(').replace('${', '\${')
                         }, {
                             "entryname": "cwl.input.yml",
-                            "entry": yaml.safe_dump(joborder_keepmount).replace("\\", "\\\\").replace('$(', '\$(').replace('${', '\${')
+                            "entry": yaml.round_trip_dump(joborder_keepmount).replace("\\", "\\\\").replace('$(', '\$(').replace('${', '\${')
                         }]
                 }],
                 "hints": workflowobj["hints"],
                 "arguments": ["--no-container", "--move-outputs", "--preserve-entire-environment", "workflow.cwl#main", "cwl.input.yml"]
-            }
+            })
             kwargs["loader"] = self.doc_loader
             kwargs["avsc_names"] = self.doc_schema
             return ArvadosCommandTool(self.arvrunner, wf_runner, **kwargs).job(joborder, output_callback, **kwargs)
index 173eb93daf2c4070ba92f28fca2ac053952f1662..f33619391d89ef2583d406a6ed25a22cb87f5159 100644 (file)
@@ -23,9 +23,14 @@ from cwltool.process import shortname, adjustFileObjs, adjustDirObjs, getListing
 from cwltool.load_tool import load_tool
 from cwltool.errors import WorkflowException
 
+from .fsaccess import CollectionFetcher
+
 logger = logging.getLogger('arvados.cwl-runner')
 
 def run():
+    # Timestamps are added by crunch-job, so don't print redundant timestamps.
+    arvados.log_handler.setFormatter(logging.Formatter('%(name)s %(levelname)s: %(message)s'))
+
     # Print package versions
     logger.info(arvados_cwl.versionstring())
 
@@ -36,6 +41,7 @@ def run():
     runner = None
     try:
         job_order_object = arvados.current_job()['script_parameters']
+        toolpath = "file://%s/%s" % (os.environ['TASK_KEEPMOUNT'], job_order_object.pop("cwl:tool"))
 
         pdh_path = re.compile(r'^[0-9a-f]{32}\+\d+(/.+)?$')
 
@@ -48,8 +54,6 @@ def run():
         def keeppathObj(v):
             v["location"] = keeppath(v["location"])
 
-        job_order_object["cwl:tool"] = "file://%s/%s" % (os.environ['TASK_KEEPMOUNT'], job_order_object["cwl:tool"])
-
         for k,v in job_order_object.items():
             if isinstance(v, basestring) and arvados.util.keep_locator_pattern.match(v):
                 job_order_object[k] = {
@@ -63,30 +67,45 @@ def run():
         adjustDirObjs(job_order_object, functools.partial(getListing, arvados_cwl.fsaccess.CollectionFsAccess("", api_client=api)))
 
         output_name = None
+        output_tags = None
         enable_reuse = True
+        on_error = "continue"
         if "arv:output_name" in job_order_object:
             output_name = job_order_object["arv:output_name"]
             del job_order_object["arv:output_name"]
 
+        if "arv:output_tags" in job_order_object:
+            output_tags = job_order_object["arv:output_tags"]
+            del job_order_object["arv:output_tags"]
+
         if "arv:enable_reuse" in job_order_object:
             enable_reuse = job_order_object["arv:enable_reuse"]
             del job_order_object["arv:enable_reuse"]
 
+        if "arv:on_error" in job_order_object:
+            on_error = job_order_object["arv:on_error"]
+            del job_order_object["arv:on_error"]
+
         runner = arvados_cwl.ArvCwlRunner(api_client=arvados.api('v1', model=OrderedJsonModel()),
-                                          output_name=output_name)
+                                          output_name=output_name, output_tags=output_tags)
 
-        t = load_tool(job_order_object, runner.arv_make_tool)
+        t = load_tool(toolpath, runner.arv_make_tool,
+                      fetcher_constructor=functools.partial(CollectionFetcher,
+                                                            api_client=api,
+                                                            keep_client=arvados.keep.KeepClient(api_client=api, num_retries=4)))
 
         args = argparse.Namespace()
         args.project_uuid = arvados.current_job()["owner_uuid"]
         args.enable_reuse = enable_reuse
+        args.on_error = on_error
         args.submit = False
-        args.debug = True
+        args.debug = False
         args.quiet = False
         args.ignore_docker_for_reuse = False
         args.basedir = os.getcwd()
+        args.name = None
         args.cwl_runner_job={"uuid": arvados.current_job()["uuid"], "state": arvados.current_job()["state"]}
-        outputObj = runner.arv_executor(t, job_order_object, **vars(args))
+        runner.arv_executor(t, job_order_object, **vars(args))
     except Exception as e:
         if isinstance(e, WorkflowException):
             logging.info("Workflow error %s", e)
index 31f353e5d8e0395028d50b86973fd4bb6e197e8f..87908c28ce762b48094cad22e433796a43495266 100644 (file)
@@ -1,4 +1,6 @@
+import re
 from cwltool.errors import WorkflowException
+from collections import deque
 
 def done(self, record, tmpdir, outdir, keepdir):
     colname = "Output %s of %s" % (record["output"][0:7], self.name)
@@ -22,8 +24,8 @@ def done(self, record, tmpdir, outdir, keepdir):
 
         if not collections["items"]:
             raise WorkflowException(
-                "Job output '%s' cannot be found on API server" % (
-                    record["output"]))
+                "[job %s] output '%s' cannot be found on API server" % (
+                    self.name, record["output"]))
 
         # Create new collection in the parent project
         # with the output contents.
@@ -35,6 +37,32 @@ def done(self, record, tmpdir, outdir, keepdir):
         }, ensure_unique_name=True).execute(
             num_retries=self.arvrunner.num_retries)
 
+    return done_outputs(self, record, tmpdir, outdir, keepdir)
+
+def done_outputs(self, record, tmpdir, outdir, keepdir):
     self.builder.outdir = outdir
     self.builder.pathmapper.keepdir = keepdir
     return self.collect_outputs("keep:" + record["output"])
+
+crunchstat_re = re.compile(r"^\d{4}-\d\d-\d\d_\d\d:\d\d:\d\d [a-z0-9]{5}-8i9sb-[a-z0-9]{15} \d+ \d+ stderr crunchstat:")
+timestamp_re = re.compile(r"^(\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d\.\d+Z) (.*)")
+
+def logtail(logcollection, logger, header, maxlen=25):
+    logtail = deque([], maxlen*len(logcollection))
+    containersapi = ("crunch-run.txt" in logcollection)
+
+    for log in logcollection.keys():
+        if not containersapi or log in ("crunch-run.txt", "stdout.txt", "stderr.txt"):
+            logname = log[:-4]
+            with logcollection.open(log) as f:
+                for l in f:
+                    if containersapi:
+                        g = timestamp_re.match(l)
+                        logtail.append("%s %s %s" % (g.group(1), logname, g.group(2)))
+                    elif not crunchstat_re.match(l):
+                        logtail.append(l)
+    if len(logcollection) > 1:
+        logtail = sorted(logtail)[-maxlen:]
+    logtxt = "\n  ".join(l.strip() for l in logtail)
+    logger.info(header)
+    logger.info("\n  %s", logtxt)
index 89a4308bf14ef2d5f9f3275ddcc4201e749205d8..500ea0f4203793fac7fdf418f79b0ae562f64334 100644 (file)
@@ -1,14 +1,21 @@
 import fnmatch
 import os
 import errno
+import urlparse
+import re
+
+import ruamel.yaml as yaml
 
 import cwltool.stdfsaccess
 from cwltool.pathmapper import abspath
+import cwltool.resolver
 
 import arvados.util
 import arvados.collection
 import arvados.arvfile
 
+from schema_salad.ref_resolver import DefaultFetcher
+
 class CollectionFsAccess(cwltool.stdfsaccess.StdFsAccess):
     """Implement the cwltool FsAccess interface for Arvados Collections."""
 
@@ -120,3 +127,80 @@ class CollectionFsAccess(cwltool.stdfsaccess.StdFsAccess):
             return path
         else:
             return os.path.realpath(path)
+
+class CollectionFetcher(DefaultFetcher):
+    def __init__(self, cache, session, api_client=None, keep_client=None):
+        super(CollectionFetcher, self).__init__(cache, session)
+        self.api_client = api_client
+        self.fsaccess = CollectionFsAccess("", api_client=api_client, keep_client=keep_client)
+
+    def fetch_text(self, url):
+        if url.startswith("keep:"):
+            with self.fsaccess.open(url, "r") as f:
+                return f.read()
+        if url.startswith("arvwf:"):
+            return self.api_client.workflows().get(uuid=url[6:]).execute()["definition"]
+        return super(CollectionFetcher, self).fetch_text(url)
+
+    def check_exists(self, url):
+        if url.startswith("keep:"):
+            return self.fsaccess.exists(url)
+        if url.startswith("arvwf:"):
+            if self.fetch_text(url):
+                return True
+        return super(CollectionFetcher, self).check_exists(url)
+
+    def urljoin(self, base_url, url):
+        if not url:
+            return base_url
+
+        urlsp = urlparse.urlsplit(url)
+        if urlsp.scheme or not base_url:
+            return url
+
+        basesp = urlparse.urlsplit(base_url)
+        if basesp.scheme in ("keep", "arvwf"):
+            if not basesp.path:
+                raise IOError(errno.EINVAL, "Invalid Keep locator", base_url)
+
+            baseparts = basesp.path.split("/")
+            urlparts = urlsp.path.split("/") if urlsp.path else []
+
+            pdh = baseparts.pop(0)
+
+            if basesp.scheme == "keep" and not arvados.util.keep_locator_pattern.match(pdh):
+                raise IOError(errno.EINVAL, "Invalid Keep locator", base_url)
+
+            if urlsp.path.startswith("/"):
+                baseparts = []
+                urlparts.pop(0)
+
+            if baseparts and urlsp.path:
+                baseparts.pop()
+
+            path = "/".join([pdh] + baseparts + urlparts)
+            return urlparse.urlunsplit((basesp.scheme, "", path, "", urlsp.fragment))
+
+        return super(CollectionFetcher, self).urljoin(base_url, url)
+
+workflow_uuid_pattern = re.compile(r'[a-z0-9]{5}-7fd4e-[a-z0-9]{15}')
+pipeline_template_uuid_pattern = re.compile(r'[a-z0-9]{5}-p5p6p-[a-z0-9]{15}')
+
+def collectionResolver(api_client, document_loader, uri):
+    if workflow_uuid_pattern.match(uri):
+        return "arvwf:%s#main" % (uri)
+
+    if pipeline_template_uuid_pattern.match(uri):
+        pt = api_client.pipeline_templates().get(uuid=uri).execute()
+        return "keep:" + pt["components"].values()[0]["script_parameters"]["cwl:tool"]
+
+    p = uri.split("/")
+    if arvados.util.keep_locator_pattern.match(p[0]):
+        return "keep:%s" % (uri)
+
+    if arvados.util.collection_uuid_pattern.match(p[0]):
+        return "keep:%s%s" % (api_client.collections().
+                              get(uuid=p[0]).execute()["portable_data_hash"],
+                              uri[len(p[0]):])
+
+    return cwltool.resolver.tool_resolver(document_loader, uri)
index 58500d3a993ddb74327c419925c2aed2b769a1b6..a6b3d15e2c503af7bab06eaf0bd7407f5975b9fe 100644 (file)
@@ -6,6 +6,8 @@ import os
 import arvados.commands.run
 import arvados.collection
 
+from schema_salad.sourceline import SourceLine
+
 from cwltool.pathmapper import PathMapper, MapperEnt, abspath, adjustFileObjs, adjustDirObjs
 from cwltool.workflow import WorkflowException
 
@@ -38,17 +40,20 @@ class ArvPathMapper(PathMapper):
                 # mount.
                 ab = abspath(src, self.input_basedir)
                 st = arvados.commands.run.statfile("", ab, fnPattern="keep:%s/%s")
-                if isinstance(st, arvados.commands.run.UploadFile):
-                    uploadfiles.add((src, ab, st))
-                elif isinstance(st, arvados.commands.run.ArvFile):
-                    self._pathmap[src] = MapperEnt(st.fn, self.collection_pattern % st.fn[5:], "File")
-                elif src.startswith("_:"):
-                    if "contents" in srcobj:
-                        pass
+                with SourceLine(srcobj, "location", WorkflowException):
+                    if isinstance(st, arvados.commands.run.UploadFile):
+                        uploadfiles.add((src, ab, st))
+                    elif isinstance(st, arvados.commands.run.ArvFile):
+                        self._pathmap[src] = MapperEnt(st.fn, self.collection_pattern % st.fn[5:], "File")
+                    elif src.startswith("_:"):
+                        if "contents" in srcobj:
+                            pass
+                        else:
+                            raise WorkflowException("File literal '%s' is missing contents" % src)
+                    elif src.startswith("arvwf:"):
+                        self._pathmap[src] = MapperEnt(src, src, "File")
                     else:
-                        raise WorkflowException("File literal '%s' is missing contents" % src)
-                else:
-                    raise WorkflowException("Input file path '%s' is invalid" % st)
+                        raise WorkflowException("Input file path '%s' is invalid" % st)
             if "secondaryFiles" in srcobj:
                 for l in srcobj["secondaryFiles"]:
                     self.visit(l, uploadfiles)
@@ -74,7 +79,7 @@ class ArvPathMapper(PathMapper):
             with c.open(path + "/" + obj["basename"], "w") as f:
                 f.write(obj["contents"].encode("utf-8"))
         else:
-            raise WorkflowException("Don't know what to do with '%s'" % obj["location"])
+            raise SourceLine(obj, "location", WorkflowException).makeError("Don't know what to do with '%s'" % obj["location"])
 
     def setup(self, referenced_files, basedir):
         # type: (List[Any], unicode) -> None
index a1142544f5bf2e16150d56dd4d0b707cfd4db984..1c3625e26bb1345673b31af5377d7b9d5282a10b 100644 (file)
@@ -6,6 +6,8 @@ import json
 import re
 from cStringIO import StringIO
 
+from schema_salad.sourceline import SourceLine
+
 import cwltool.draft2tool
 from cwltool.draft2tool import CommandLineTool
 import cwltool.workflow
@@ -21,6 +23,7 @@ import ruamel.yaml as yaml
 from .arvdocker import arv_docker_get_image
 from .pathmapper import ArvPathMapper
 from ._version import __version__
+from . import done
 
 logger = logging.getLogger('arvados.cwl-runner')
 
@@ -57,7 +60,7 @@ def upload_dependencies(arvrunner, name, document_loader,
 
     loaded = set()
     def loadref(b, u):
-        joined = urlparse.urljoin(b, u)
+        joined = document_loader.fetcher.urljoin(b, u)
         defrg, _ = urlparse.urldefrag(joined)
         if defrg not in loaded:
             loaded.add(defrg)
@@ -85,7 +88,7 @@ def upload_dependencies(arvrunner, name, document_loader,
     sc = scandeps(uri, scanobj,
                   loadref_fields,
                   set(("$include", "$schemas", "location")),
-                  loadref)
+                  loadref, urljoin=document_loader.fetcher.urljoin)
 
     normalizeFilesDirs(sc)
 
@@ -112,7 +115,8 @@ def upload_docker(arvrunner, tool):
         if docker_req:
             if docker_req.get("dockerOutputDirectory"):
                 # TODO: can be supported by containers API, but not jobs API.
-                raise UnsupportedRequirement("Option 'dockerOutputDirectory' of DockerRequirement not supported.")
+                raise SourceLine(docker_req, "dockerOutputDirectory", UnsupportedRequirement).makeError(
+                    "Option 'dockerOutputDirectory' of DockerRequirement not supported.")
             arv_docker_get_image(arvrunner.api, docker_req, True, arvrunner.project_uuid)
     elif isinstance(tool, cwltool.workflow.Workflow):
         for s in tool.steps:
@@ -161,7 +165,9 @@ def arvados_jobs_image(arvrunner):
     return img
 
 class Runner(object):
-    def __init__(self, runner, tool, job_order, enable_reuse, output_name):
+    def __init__(self, runner, tool, job_order, enable_reuse,
+                 output_name, output_tags, submit_runner_ram=0,
+                 name=None, on_error=None):
         self.arvrunner = runner
         self.tool = tool
         self.job_order = job_order
@@ -170,48 +176,78 @@ class Runner(object):
         self.uuid = None
         self.final_output = None
         self.output_name = output_name
+        self.output_tags = output_tags
+        self.name = name
+        self.on_error = on_error
+
+        if submit_runner_ram:
+            self.submit_runner_ram = submit_runner_ram
+        else:
+            self.submit_runner_ram = 1024
+
+        if self.submit_runner_ram <= 0:
+            raise Exception("Value of --submit-runner-ram must be greater than zero")
 
     def update_pipeline_component(self, record):
         pass
 
     def arvados_job_spec(self, *args, **kwargs):
-        self.name = os.path.basename(self.tool.tool["id"])
+        if self.name is None:
+            self.name = self.tool.tool.get("label") or os.path.basename(self.tool.tool["id"])
+
+        # Need to filter this out, gets added by cwltool when providing
+        # parameters on the command line.
+        if "job_order" in self.job_order:
+            del self.job_order["job_order"]
+
         workflowmapper = upload_instance(self.arvrunner, self.name, self.tool, self.job_order)
         adjustDirObjs(self.job_order, trim_listing)
         return workflowmapper
 
     def done(self, record):
-        if record["state"] == "Complete":
-            if record.get("exit_code") is not None:
-                if record["exit_code"] == 33:
-                    processStatus = "UnsupportedRequirement"
-                elif record["exit_code"] == 0:
-                    processStatus = "success"
+        try:
+            if record["state"] == "Complete":
+                if record.get("exit_code") is not None:
+                    if record["exit_code"] == 33:
+                        processStatus = "UnsupportedRequirement"
+                    elif record["exit_code"] == 0:
+                        processStatus = "success"
+                    else:
+                        processStatus = "permanentFail"
                 else:
-                    processStatus = "permanentFail"
+                    processStatus = "success"
             else:
-                processStatus = "success"
-        else:
-            processStatus = "permanentFail"
+                processStatus = "permanentFail"
 
-        outputs = None
-        try:
-            try:
-                self.final_output = record["output"]
-                outc = arvados.collection.CollectionReader(self.final_output,
+            outputs = {}
+
+            if processStatus == "permanentFail":
+                logc = arvados.collection.CollectionReader(record["log"],
                                                            api_client=self.arvrunner.api,
                                                            keep_client=self.arvrunner.keep_client,
                                                            num_retries=self.arvrunner.num_retries)
+                done.logtail(logc, logger, "%s error log:" % self.arvrunner.label(self), maxlen=40)
+
+            self.final_output = record["output"]
+            outc = arvados.collection.CollectionReader(self.final_output,
+                                                       api_client=self.arvrunner.api,
+                                                       keep_client=self.arvrunner.keep_client,
+                                                       num_retries=self.arvrunner.num_retries)
+            if "cwl.output.json" in outc:
                 with outc.open("cwl.output.json") as f:
-                    outputs = json.load(f)
-                def keepify(fileobj):
-                    path = fileobj["location"]
-                    if not path.startswith("keep:"):
-                        fileobj["location"] = "keep:%s/%s" % (record["output"], path)
-                adjustFileObjs(outputs, keepify)
-                adjustDirObjs(outputs, keepify)
-            except Exception as e:
-                logger.error("While getting final output object: %s", e)
+                    if f.size() > 0:
+                        outputs = json.load(f)
+            def keepify(fileobj):
+                path = fileobj["location"]
+                if not path.startswith("keep:"):
+                    fileobj["location"] = "keep:%s/%s" % (record["output"], path)
+            adjustFileObjs(outputs, keepify)
+            adjustDirObjs(outputs, keepify)
+        except Exception as e:
+            logger.exception("[%s] While getting final output object: %s", self.name, e)
+            self.arvrunner.output_callback({}, "permanentFail")
+        else:
             self.arvrunner.output_callback(outputs, processStatus)
         finally:
-            del self.arvrunner.processes[record["uuid"]]
+            if record["uuid"] in self.arvrunner.processes:
+                del self.arvrunner.processes[record["uuid"]]
index 9d9a1e1a7acf99f46d61d96de384681da114925a..534eb74bf5a9b2ec47bdc2bf7865c52475c1bb88 100644 (file)
@@ -45,11 +45,14 @@ setup(name='arvados-cwl-runner',
           'bin/cwl-runner',
           'bin/arvados-cwl-runner'
       ],
-      # Make sure to update arvados/build/run-build-packages.sh as well
-      # when updating the cwltool version pin.
+      # Note that arvados/build/run-build-packages.sh looks at this
+      # file to determine what version of cwltool and schema-salad to build.
       install_requires=[
-          'cwltool==1.0.20161107145355',
-          'arvados-python-client>=0.1.20160826210445'
+          'cwltool==1.0.20170112185927',
+          'schema-salad==2.2.20170111180227',
+          'ruamel.yaml==0.13.7',
+          'arvados-python-client>=0.1.20170112173420',
+          'setuptools'
       ],
       data_files=[
           ('share/doc/arvados-cwl-runner', ['LICENSE-2.0.txt', 'README.rst']),
index 93100ae9f76026c745fd5fdc3b011af550ac0079..45e2c7c45f20915fbca39e8e553564f23fc18df9 100644 (file)
@@ -7,8 +7,9 @@ import os
 import functools
 import cwltool.process
 from schema_salad.ref_resolver import Loader
+from schema_salad.sourceline import cmap
 
-from schema_salad.ref_resolver import Loader
+from .matcher import JsonDiffMatcher
 
 if not os.getenv('ARVADOS_DEBUG'):
     logging.getLogger('arvados.cwl-runner').setLevel(logging.WARN)
@@ -34,12 +35,12 @@ class TestContainer(unittest.TestCase):
 
             document_loader, avsc_names, schema_metadata, metaschema_loader = cwltool.process.get_schema("v1.0")
 
-            tool = {
+            tool = cmap({
                 "inputs": [],
                 "outputs": [],
                 "baseCommand": "ls",
                 "arguments": [{"valueFrom": "$(runtime.outdir)"}]
-            }
+            })
             make_fs_access=functools.partial(arvados_cwl.CollectionFsAccess, api_client=runner.api)
             arvtool = arvados_cwl.ArvadosCommandTool(runner, tool, work_api="containers", avsc_names=avsc_names,
                                                      basedir="", make_fs_access=make_fs_access, loader=Loader({}))
@@ -48,7 +49,7 @@ class TestContainer(unittest.TestCase):
                                  make_fs_access=make_fs_access, tmpdir="/tmp"):
                 j.run(enable_reuse=enable_reuse)
                 runner.api.container_requests().create.assert_called_with(
-                    body={
+                    body=JsonDiffMatcher({
                         'environment': {
                             'HOME': '/var/spool/cwl',
                             'TMPDIR': '/tmp'
@@ -68,8 +69,10 @@ class TestContainer(unittest.TestCase):
                         'output_path': '/var/spool/cwl',
                         'container_image': '99999999999999999999999999999993+99',
                         'command': ['ls', '/var/spool/cwl'],
-                        'cwd': '/var/spool/cwl'
-                    })
+                        'cwd': '/var/spool/cwl',
+                        'scheduling_parameters': {},
+                        'properties': {},
+                    }))
 
     # The test passes some fields in builder.resources
     # For the remaining fields, the defaults will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
@@ -85,7 +88,7 @@ class TestContainer(unittest.TestCase):
         runner.api.collections().get().execute.return_value = {
             "portable_data_hash": "99999999999999999999999999999993+99"}
 
-        tool = {
+        tool = cmap({
             "inputs": [],
             "outputs": [],
             "hints": [{
@@ -103,7 +106,7 @@ class TestContainer(unittest.TestCase):
                 "partition": "blurb"
             }],
             "baseCommand": "ls"
-        }
+        })
         make_fs_access=functools.partial(arvados_cwl.CollectionFsAccess, api_client=runner.api)
         arvtool = arvados_cwl.ArvadosCommandTool(runner, tool, work_api="containers",
                                                  avsc_names=avsc_names, make_fs_access=make_fs_access,
@@ -113,8 +116,9 @@ class TestContainer(unittest.TestCase):
                              make_fs_access=make_fs_access, tmpdir="/tmp"):
             j.run()
 
-        runner.api.container_requests().create.assert_called_with(
-            body={
+        call_args, call_kwargs = runner.api.container_requests().create.call_args
+
+        call_body_expected = {
                 'environment': {
                     'HOME': '/var/spool/cwl',
                     'TMPDIR': '/tmp'
@@ -124,8 +128,7 @@ class TestContainer(unittest.TestCase):
                     'vcpus': 3,
                     'ram': 3145728000,
                     'keep_cache_ram': 512,
-                    'API': True,
-                    'partition': ['blurb']
+                    'API': True
                 },
                 'use_existing': True,
                 'priority': 1,
@@ -137,8 +140,17 @@ class TestContainer(unittest.TestCase):
                 'output_path': '/var/spool/cwl',
                 'container_image': '99999999999999999999999999999993+99',
                 'command': ['ls'],
-                'cwd': '/var/spool/cwl'
-            })
+                'cwd': '/var/spool/cwl',
+                'scheduling_parameters': {
+                    'partitions': ['blurb']
+                },
+                'properties': {}
+        }
+
+        call_body = call_kwargs.get('body', None)
+        self.assertNotEqual(None, call_body)
+        for key in call_body:
+            self.assertEqual(call_body_expected.get(key), call_body.get(key))
 
     @mock.patch("arvados.collection.Collection")
     def test_done(self, col):
@@ -150,54 +162,11 @@ class TestContainer(unittest.TestCase):
         runner.num_retries = 0
         runner.ignore_docker_for_reuse = False
 
-        col().open.return_value = []
-        api.collections().list().execute.side_effect = ({"items": []},
-                                                        {"items": [{"manifest_text": "XYZ"}]})
-
-        arvjob = arvados_cwl.ArvadosContainer(runner)
-        arvjob.name = "testjob"
-        arvjob.builder = mock.MagicMock()
-        arvjob.output_callback = mock.MagicMock()
-        arvjob.collect_outputs = mock.MagicMock()
-        arvjob.successCodes = [0]
-        arvjob.outdir = "/var/spool/cwl"
-
-        arvjob.done({
-            "state": "Complete",
-            "output": "99999999999999999999999999999993+99",
-            "log": "99999999999999999999999999999994+99",
-            "uuid": "zzzzz-8i9sb-zzzzzzzzzzzzzzz",
-            "exit_code": 0
-        })
-
-        api.collections().list.assert_has_calls([
-            mock.call(),
-            mock.call(filters=[['owner_uuid', '=', 'zzzzz-8i9sb-zzzzzzzzzzzzzzz'],
-                          ['portable_data_hash', '=', '99999999999999999999999999999993+99'],
-                          ['name', '=', 'Output 9999999 of testjob']]),
-            mock.call().execute(num_retries=0),
-            mock.call(limit=1, filters=[['portable_data_hash', '=', '99999999999999999999999999999993+99']],
-                 select=['manifest_text']),
-            mock.call().execute(num_retries=0)])
-
-        api.collections().create.assert_called_with(
-            ensure_unique_name=True,
-            body={'portable_data_hash': '99999999999999999999999999999993+99',
-                  'manifest_text': 'XYZ',
-                  'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
-                  'name': 'Output 9999999 of testjob'})
-
-    @mock.patch("arvados.collection.Collection")
-    def test_done_use_existing_collection(self, col):
-        api = mock.MagicMock()
-
-        runner = mock.MagicMock()
-        runner.api = api
-        runner.project_uuid = "zzzzz-8i9sb-zzzzzzzzzzzzzzz"
-        runner.num_retries = 0
+        runner.api.containers().get().execute.return_value = {"state":"Complete",
+                                                              "output": "abc+123",
+                                                              "exit_code": 0}
 
         col().open.return_value = []
-        api.collections().list().execute.side_effect = ({"items": [{"uuid": "zzzzz-4zz18-zzzzzzzzzzzzzz2"}]},)
 
         arvjob = arvados_cwl.ArvadosContainer(runner)
         arvjob.name = "testjob"
@@ -207,19 +176,17 @@ class TestContainer(unittest.TestCase):
         arvjob.successCodes = [0]
         arvjob.outdir = "/var/spool/cwl"
 
+        arvjob.collect_outputs.return_value = {"out": "stuff"}
+
         arvjob.done({
-            "state": "Complete",
-            "output": "99999999999999999999999999999993+99",
-            "log": "99999999999999999999999999999994+99",
-            "uuid": "zzzzz-8i9sb-zzzzzzzzzzzzzzz",
-            "exit_code": 0
+            "state": "Final",
+            "log_uuid": "zzzzz-4zz18-zzzzzzzzzzzzzz1",
+            "output_uuid": "zzzzz-4zz18-zzzzzzzzzzzzzz2",
+            "uuid": "zzzzz-xvhdp-zzzzzzzzzzzzzzz",
+            "container_uuid": "zzzzz-8i9sb-zzzzzzzzzzzzzzz"
         })
 
-        api.collections().list.assert_has_calls([
-            mock.call(),
-            mock.call(filters=[['owner_uuid', '=', 'zzzzz-8i9sb-zzzzzzzzzzzzzzz'],
-                               ['portable_data_hash', '=', '99999999999999999999999999999993+99'],
-                               ['name', '=', 'Output 9999999 of testjob']]),
-            mock.call().execute(num_retries=0)])
-
         self.assertFalse(api.collections().create.called)
+
+        arvjob.collect_outputs.assert_called_with("keep:abc+123")
+        arvjob.output_callback.assert_called_with({"out": "stuff"}, "success")
index c8813adf7ea48d71f5b5b8cb9d7c091bfafa09c0..7675e3d4bc45bd64b55a690c405597fa6b23a615 100644 (file)
@@ -11,7 +11,9 @@ import arvados
 import arvados_cwl
 import cwltool.process
 from schema_salad.ref_resolver import Loader
+from schema_salad.sourceline import cmap
 from .mock_discovery import get_rootDesc
+from .matcher import JsonDiffMatcher
 
 if not os.getenv('ARVADOS_DEBUG'):
     logging.getLogger('arvados.cwl-runner').setLevel(logging.WARN)
@@ -33,12 +35,12 @@ class TestJob(unittest.TestCase):
             list_images_in_arv.return_value = [["zzzzz-4zz18-zzzzzzzzzzzzzzz"]]
             runner.api.collections().get().execute.return_vaulue = {"portable_data_hash": "99999999999999999999999999999993+99"}
 
-            tool = {
+            tool = cmap({
                 "inputs": [],
                 "outputs": [],
                 "baseCommand": "ls",
                 "arguments": [{"valueFrom": "$(runtime.outdir)"}]
-            }
+            })
             make_fs_access=functools.partial(arvados_cwl.CollectionFsAccess, api_client=runner.api)
             arvtool = arvados_cwl.ArvadosCommandTool(runner, tool, work_api="jobs", avsc_names=avsc_names,
                                                      basedir="", make_fs_access=make_fs_access, loader=Loader({}))
@@ -46,7 +48,7 @@ class TestJob(unittest.TestCase):
             for j in arvtool.job({}, mock.MagicMock(), basedir="", make_fs_access=make_fs_access):
                 j.run(enable_reuse=enable_reuse)
                 runner.api.jobs().create.assert_called_with(
-                    body={
+                    body=JsonDiffMatcher({
                         'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
                         'runtime_constraints': {},
                         'script_parameters': {
@@ -56,7 +58,7 @@ class TestJob(unittest.TestCase):
                             }],
                         },
                         'script_version': 'master',
-                        'minimum_script_version': '9e5b98e8f5f4727856b53447191f9c06e3da2ba6',
+                        'minimum_script_version': 'a3f2cb186e437bfce0031b024b2157b73ed2717d',
                         'repository': 'arvados',
                         'script': 'crunchrunner',
                         'runtime_constraints': {
@@ -65,11 +67,11 @@ class TestJob(unittest.TestCase):
                             'min_ram_mb_per_node': 1024,
                             'min_scratch_mb_per_node': 2048 # tmpdirSize + outdirSize
                         }
-                    },
+                    }),
                     find_or_create=enable_reuse,
                     filters=[['repository', '=', 'arvados'],
                              ['script', '=', 'crunchrunner'],
-                             ['script_version', 'in git', '9e5b98e8f5f4727856b53447191f9c06e3da2ba6'],
+                             ['script_version', 'in git', 'a3f2cb186e437bfce0031b024b2157b73ed2717d'],
                              ['docker_image_locator', 'in docker', 'arvados/jobs:'+arvados_cwl.__version__]]
                 )
 
@@ -113,7 +115,7 @@ class TestJob(unittest.TestCase):
         for j in arvtool.job({}, mock.MagicMock(), basedir="", make_fs_access=make_fs_access):
             j.run()
         runner.api.jobs().create.assert_called_with(
-            body={
+            body=JsonDiffMatcher({
                 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
                 'runtime_constraints': {},
                 'script_parameters': {
@@ -124,7 +126,7 @@ class TestJob(unittest.TestCase):
                     }]
             },
             'script_version': 'master',
-                'minimum_script_version': '9e5b98e8f5f4727856b53447191f9c06e3da2ba6',
+                'minimum_script_version': 'a3f2cb186e437bfce0031b024b2157b73ed2717d',
                 'repository': 'arvados',
                 'script': 'crunchrunner',
                 'runtime_constraints': {
@@ -134,11 +136,11 @@ class TestJob(unittest.TestCase):
                     'min_scratch_mb_per_node': 5024, # tmpdirSize + outdirSize
                     'keep_cache_mb_per_task': 512
                 }
-            },
+            }),
             find_or_create=True,
             filters=[['repository', '=', 'arvados'],
                      ['script', '=', 'crunchrunner'],
-                     ['script_version', 'in git', '9e5b98e8f5f4727856b53447191f9c06e3da2ba6'],
+                     ['script_version', 'in git', 'a3f2cb186e437bfce0031b024b2157b73ed2717d'],
                      ['docker_image_locator', 'in docker', 'arvados/jobs:'+arvados_cwl.__version__]])
 
     @mock.patch("arvados.collection.CollectionReader")
@@ -164,6 +166,7 @@ class TestJob(unittest.TestCase):
         arvjob.builder = mock.MagicMock()
         arvjob.output_callback = mock.MagicMock()
         arvjob.collect_outputs = mock.MagicMock()
+        arvjob.collect_outputs.return_value = {"out": "stuff"}
 
         arvjob.done({
             "state": "Complete",
@@ -189,6 +192,8 @@ class TestJob(unittest.TestCase):
                   'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
                   'name': 'Output 9999999 of testjob'})
 
+        arvjob.output_callback.assert_called_with({"out": "stuff"}, "success")
+
     @mock.patch("arvados.collection.CollectionReader")
     def test_done_use_existing_collection(self, reader):
         api = mock.MagicMock()
@@ -211,6 +216,7 @@ class TestJob(unittest.TestCase):
         arvjob.builder = mock.MagicMock()
         arvjob.output_callback = mock.MagicMock()
         arvjob.collect_outputs = mock.MagicMock()
+        arvjob.collect_outputs.return_value = {"out": "stuff"}
 
         arvjob.done({
             "state": "Complete",
@@ -228,6 +234,8 @@ class TestJob(unittest.TestCase):
 
         self.assertFalse(api.collections().create.called)
 
+        arvjob.output_callback.assert_called_with({"out": "stuff"}, "success")
+
 
 class TestWorkflow(unittest.TestCase):
     # The test passes no builder.resources
@@ -269,8 +277,8 @@ class TestWorkflow(unittest.TestCase):
             subwf = f.read()
 
         runner.api.jobs().create.assert_called_with(
-            body={
-                'minimum_script_version': '9e5b98e8f5f4727856b53447191f9c06e3da2ba6',
+            body=JsonDiffMatcher({
+                'minimum_script_version': 'a3f2cb186e437bfce0031b024b2157b73ed2717d',
                 'repository': 'arvados',
                 'script_version': 'master',
                 'script': 'crunchrunner',
@@ -290,15 +298,15 @@ class TestWorkflow(unittest.TestCase):
                     'docker_image': 'arvados/jobs:'+arvados_cwl.__version__,
                     'min_ram_mb_per_node': 1024
                 },
-                'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz'},
+                'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz'}),
             filters=[['repository', '=', 'arvados'],
                      ['script', '=', 'crunchrunner'],
-                     ['script_version', 'in git', '9e5b98e8f5f4727856b53447191f9c06e3da2ba6'],
+                     ['script_version', 'in git', 'a3f2cb186e437bfce0031b024b2157b73ed2717d'],
                      ['docker_image_locator', 'in docker', 'arvados/jobs:'+arvados_cwl.__version__]],
             find_or_create=True)
 
         mockcollection().open().__enter__().write.assert_has_calls([mock.call(subwf)])
-        mockcollection().open().__enter__().write.assert_has_calls([mock.call('{sleeptime: 5}')])
+        mockcollection().open().__enter__().write.assert_has_calls([mock.call('sleeptime: 5')])
 
     def test_default_work_api(self):
         arvados_cwl.add_arv_hints()
index 3228ad77b3ca9343c0d6ff736b0714c23acd060b..53f379f1a5ac0cc488af5157080e78933d543367 100644 (file)
@@ -27,12 +27,15 @@ class TestMakeOutput(unittest.TestCase):
         readermock = mock.MagicMock()
         reader.return_value = readermock
 
+        final_uuid = final.manifest_locator()
+        num_retries = runner.num_retries
+
         cwlout = StringIO.StringIO()
         openmock = mock.MagicMock()
         final.open.return_value = openmock
         openmock.__enter__.return_value = cwlout
 
-        _, runner.final_output_collection = runner.make_output_collection("Test output", {
+        _, runner.final_output_collection = runner.make_output_collection("Test output", "tag0,tag1,tag2", {
             "foo": {
                 "class": "File",
                 "location": "keep:99999999999999999999999999999991+99/foo.txt",
@@ -64,3 +67,7 @@ class TestMakeOutput(unittest.TestCase):
 }""", cwlout.getvalue())
 
         self.assertIs(final, runner.final_output_collection)
+        self.assertIs(final_uuid, runner.final_output_collection.manifest_locator())
+        self.api.links().create.assert_has_calls([mock.call(body={"head_uuid": final_uuid, "link_class": "tag", "name": "tag0"}), mock.call().execute(num_retries=num_retries)])
+        self.api.links().create.assert_has_calls([mock.call(body={"head_uuid": final_uuid, "link_class": "tag", "name": "tag1"}), mock.call().execute(num_retries=num_retries)])
+        self.api.links().create.assert_has_calls([mock.call(body={"head_uuid": final_uuid, "link_class": "tag", "name": "tag2"}), mock.call().execute(num_retries=num_retries)])
index c195b03916992561f5e52b1d970c3cabd778df30..0a01fb4cc37dd99809ae306d24fd087a3d09195d 100644 (file)
@@ -98,7 +98,8 @@ def stubs(func):
         }
         stubs.expect_job_spec = {
             'runtime_constraints': {
-                'docker_image': 'arvados/jobs:'+arvados_cwl.__version__
+                'docker_image': 'arvados/jobs:'+arvados_cwl.__version__,
+                'min_ram_mb_per_node': 1024
             },
             'script_parameters': {
                 'x': {
@@ -124,16 +125,18 @@ def stubs(func):
                 '99999999999999999999999999999991+99/wf/submit_wf.cwl'
             },
             'repository': 'arvados',
-            'script_version': arvados_cwl.__version__,
+            'script_version': 'master',
+            'minimum_script_version': '570509ab4d2ef93d870fd2b1f2eab178afb1bad9',
             'script': 'cwl-runner'
         }
         stubs.pipeline_component = stubs.expect_job_spec.copy()
         stubs.expect_pipeline_instance = {
             'name': 'submit_wf.cwl',
             'state': 'RunningOnServer',
+            'owner_uuid': None,
             "components": {
                 "cwl-runner": {
-                    'runtime_constraints': {'docker_image': 'arvados/jobs:'+arvados_cwl.__version__},
+                    'runtime_constraints': {'docker_image': 'arvados/jobs:'+arvados_cwl.__version__, 'min_ram_mb_per_node': 1024},
                     'script_parameters': {
                         'y': {"value": {'basename': '99999999999999999999999999999998+99', 'location': 'keep:99999999999999999999999999999998+99', 'class': 'Directory'}},
                         'x': {"value": {'basename': 'blorp.txt', 'class': 'File', 'location': 'keep:99999999999999999999999999999994+99/blorp.txt'}},
@@ -142,10 +145,12 @@ def stubs(func):
                                   {'basename': 'renamed.txt', 'class': 'File', 'location': 'keep:99999999999999999999999999999998+99/file1.txt'}
                               ]}},
                         'cwl:tool': '99999999999999999999999999999991+99/wf/submit_wf.cwl',
-                        'arv:enable_reuse': True
+                        'arv:enable_reuse': True,
+                        'arv:on_error': 'continue'
                     },
                     'repository': 'arvados',
-                    'script_version': arvados_cwl.__version__,
+                    'script_version': 'master',
+                    'minimum_script_version': '570509ab4d2ef93d870fd2b1f2eab178afb1bad9',
                     'script': 'cwl-runner',
                     'job': {'state': 'Queued', 'uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz'}
                 }
@@ -177,14 +182,23 @@ def stubs(func):
                     'path': '/var/spool/cwl/cwl.output.json',
                     'kind': 'file'
                 },
-                '/var/lib/cwl/job/cwl.input.json': {
-                    'portable_data_hash': 'd20d7cddd1984f105dd3702c7f125afb+60/cwl.input.json',
-                    'kind': 'collection'
+                '/var/lib/cwl/cwl.input.json': {
+                    'kind': 'json',
+                    'content': {
+                        'y': {'basename': '99999999999999999999999999999998+99', 'location': 'keep:99999999999999999999999999999998+99', 'class': 'Directory'},
+                        'x': {'basename': u'blorp.txt', 'class': 'File', 'location': u'keep:99999999999999999999999999999994+99/blorp.txt'},
+                        'z': {'basename': 'anonymous', 'class': 'Directory', 'listing': [
+                            {'basename': 'renamed.txt', 'class': 'File', 'location': 'keep:99999999999999999999999999999998+99/file1.txt'}
+                        ]}
+                    },
+                    'kind': 'json'
                 }
             },
             'state': 'Committed',
-            'owner_uuid': 'zzzzz-tpzed-zzzzzzzzzzzzzzz',
-            'command': ['arvados-cwl-runner', '--local', '--api=containers', '--enable-reuse', '/var/lib/cwl/workflow/submit_wf.cwl', '/var/lib/cwl/job/cwl.input.json'],
+            'owner_uuid': None,
+            'command': ['arvados-cwl-runner', '--local', '--api=containers', '--no-log-timestamps',
+                        '--enable-reuse', '--on-error=continue',
+                        '/var/lib/cwl/workflow/submit_wf.cwl', '/var/lib/cwl/cwl.input.json'],
             'name': 'submit_wf.cwl',
             'container_image': 'arvados/jobs:'+arvados_cwl.__version__,
             'output_path': '/var/spool/cwl',
@@ -192,14 +206,22 @@ def stubs(func):
             'runtime_constraints': {
                 'API': True,
                 'vcpus': 1,
-                'ram': 268435456
-            }
+                'ram': 1024*1024*1024
+            },
+            "properties": {}
         }
 
         stubs.expect_workflow_uuid = "zzzzz-7fd4e-zzzzzzzzzzzzzzz"
         stubs.api.workflows().create().execute.return_value = {
             "uuid": stubs.expect_workflow_uuid,
         }
+        def update_mock(**kwargs):
+            stubs.updated_uuid = kwargs.get('uuid')
+            return mock.DEFAULT
+        stubs.api.workflows().update.side_effect = update_mock
+        stubs.api.workflows().update().execute.side_effect = lambda **kwargs: {
+            "uuid": stubs.updated_uuid,
+        }
 
         return func(self, stubs, *args, **kwargs)
     return wrapped
@@ -223,13 +245,12 @@ class TestSubmit(unittest.TestCase):
                 './tool d51232d96b6116d964a69bfb7e0c73bf+450 '
                 '0:16:blub.txt 16:434:submit_tool.cwl\n./wf '
                 'cc2ffb940e60adf1b2b282c67587e43d+413 0:413:submit_wf.cwl\n',
-                'owner_uuid': 'zzzzz-tpzed-zzzzzzzzzzzzzzz',
+                'owner_uuid': None,
                 'name': 'submit_wf.cwl',
             }, ensure_unique_name=True),
             mock.call().execute(),
             mock.call(body={'manifest_text': '. d41d8cd98f00b204e9800998ecf8427e+0 '
                             '0:0:blub.txt 0:0:submit_tool.cwl\n',
-                            'owner_uuid': 'zzzzz-tpzed-zzzzzzzzzzzzzzz',
                             'replication_desired': None,
                             'name': 'New collection'
             }, ensure_unique_name=True),
@@ -237,13 +258,12 @@ class TestSubmit(unittest.TestCase):
             mock.call(body={
                 'manifest_text':
                 '. 979af1245a12a1fed634d4222473bfdc+16 0:16:blorp.txt\n',
-                'owner_uuid': 'zzzzz-tpzed-zzzzzzzzzzzzzzz',
+                'owner_uuid': None,
                 'name': '#',
             }, ensure_unique_name=True),
             mock.call().execute()])
 
         expect_pipeline = copy.deepcopy(stubs.expect_pipeline_instance)
-        expect_pipeline["owner_uuid"] = stubs.fake_user_uuid
         stubs.api.pipeline_instances().create.assert_called_with(
             body=expect_pipeline)
         self.assertEqual(capture_stdout.getvalue(),
@@ -263,12 +283,118 @@ class TestSubmit(unittest.TestCase):
         stubs.expect_pipeline_instance["components"]["cwl-runner"]["script_parameters"]["arv:enable_reuse"] = {"value": False}
 
         expect_pipeline = copy.deepcopy(stubs.expect_pipeline_instance)
-        expect_pipeline["owner_uuid"] = stubs.fake_user_uuid
+        stubs.api.pipeline_instances().create.assert_called_with(
+            body=JsonDiffMatcher(expect_pipeline))
+        self.assertEqual(capture_stdout.getvalue(),
+                         stubs.expect_pipeline_uuid + '\n')
+
+    @mock.patch("time.sleep")
+    @stubs
+    def test_submit_on_error(self, stubs, tm):
+        capture_stdout = cStringIO.StringIO()
+        exited = arvados_cwl.main(
+            ["--submit", "--no-wait", "--debug", "--on-error=stop",
+             "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
+            capture_stdout, sys.stderr, api_client=stubs.api)
+        self.assertEqual(exited, 0)
+
+        stubs.expect_pipeline_instance["components"]["cwl-runner"]["script_parameters"]["arv:on_error"] = "stop"
+
+        expect_pipeline = copy.deepcopy(stubs.expect_pipeline_instance)
+        stubs.api.pipeline_instances().create.assert_called_with(
+            body=JsonDiffMatcher(expect_pipeline))
+        self.assertEqual(capture_stdout.getvalue(),
+                         stubs.expect_pipeline_uuid + '\n')
+
+
+    @mock.patch("time.sleep")
+    @stubs
+    def test_submit_runner_ram(self, stubs, tm):
+        capture_stdout = cStringIO.StringIO()
+        exited = arvados_cwl.main(
+            ["--submit", "--no-wait", "--debug", "--submit-runner-ram=2048",
+             "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
+            capture_stdout, sys.stderr, api_client=stubs.api)
+        self.assertEqual(exited, 0)
+
+        stubs.expect_pipeline_instance["components"]["cwl-runner"]["runtime_constraints"]["min_ram_mb_per_node"] = 2048
+
+        expect_pipeline = copy.deepcopy(stubs.expect_pipeline_instance)
+        stubs.api.pipeline_instances().create.assert_called_with(
+            body=JsonDiffMatcher(expect_pipeline))
+        self.assertEqual(capture_stdout.getvalue(),
+                         stubs.expect_pipeline_uuid + '\n')
+
+
+    @mock.patch("time.sleep")
+    @stubs
+    def test_submit_invalid_runner_ram(self, stubs, tm):
+        capture_stdout = cStringIO.StringIO()
+        exited = arvados_cwl.main(
+            ["--submit", "--no-wait", "--debug", "--submit-runner-ram=-2048",
+             "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
+            capture_stdout, sys.stderr, api_client=stubs.api)
+        self.assertEqual(exited, 1)
+
+    @mock.patch("time.sleep")
+    @stubs
+    def test_submit_output_name(self, stubs, tm):
+        output_name = "test_output_name"
+
+        capture_stdout = cStringIO.StringIO()
+        exited = arvados_cwl.main(
+            ["--submit", "--no-wait", "--debug", "--output-name", output_name,
+             "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
+            capture_stdout, sys.stderr, api_client=stubs.api)
+        self.assertEqual(exited, 0)
+
+        stubs.expect_pipeline_instance["components"]["cwl-runner"]["script_parameters"]["arv:output_name"] = output_name
+
+        expect_pipeline = copy.deepcopy(stubs.expect_pipeline_instance)
+        stubs.api.pipeline_instances().create.assert_called_with(
+            body=JsonDiffMatcher(expect_pipeline))
+        self.assertEqual(capture_stdout.getvalue(),
+                         stubs.expect_pipeline_uuid + '\n')
+
+
+    @mock.patch("time.sleep")
+    @stubs
+    def test_submit_pipeline_name(self, stubs, tm):
+        capture_stdout = cStringIO.StringIO()
+        exited = arvados_cwl.main(
+            ["--submit", "--no-wait", "--debug", "--name=hello job 123",
+             "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
+            capture_stdout, sys.stderr, api_client=stubs.api)
+        self.assertEqual(exited, 0)
+
+        stubs.expect_pipeline_instance["name"] = "hello job 123"
+
+        expect_pipeline = copy.deepcopy(stubs.expect_pipeline_instance)
         stubs.api.pipeline_instances().create.assert_called_with(
             body=expect_pipeline)
         self.assertEqual(capture_stdout.getvalue(),
                          stubs.expect_pipeline_uuid + '\n')
 
+    @mock.patch("time.sleep")
+    @stubs
+    def test_submit_output_tags(self, stubs, tm):
+        output_tags = "tag0,tag1,tag2"
+
+        capture_stdout = cStringIO.StringIO()
+        exited = arvados_cwl.main(
+            ["--submit", "--no-wait", "--debug", "--output-tags", output_tags,
+             "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
+            capture_stdout, sys.stderr, api_client=stubs.api)
+        self.assertEqual(exited, 0)
+
+        stubs.expect_pipeline_instance["components"]["cwl-runner"]["script_parameters"]["arv:output_tags"] = output_tags
+
+        expect_pipeline = copy.deepcopy(stubs.expect_pipeline_instance)
+        stubs.api.pipeline_instances().create.assert_called_with(
+            body=JsonDiffMatcher(expect_pipeline))
+        self.assertEqual(capture_stdout.getvalue(),
+                         stubs.expect_pipeline_uuid + '\n')
+
     @mock.patch("time.sleep")
     @stubs
     def test_submit_with_project_uuid(self, stubs, tm):
@@ -284,7 +410,7 @@ class TestSubmit(unittest.TestCase):
         expect_pipeline = copy.deepcopy(stubs.expect_pipeline_instance)
         expect_pipeline["owner_uuid"] = project_uuid
         stubs.api.pipeline_instances().create.assert_called_with(
-            body=expect_pipeline)
+            body=JsonDiffMatcher(expect_pipeline))
 
     @stubs
     def test_submit_container(self, stubs):
@@ -305,13 +431,12 @@ class TestSubmit(unittest.TestCase):
                 './tool d51232d96b6116d964a69bfb7e0c73bf+450 '
                 '0:16:blub.txt 16:434:submit_tool.cwl\n./wf '
                 'cc2ffb940e60adf1b2b282c67587e43d+413 0:413:submit_wf.cwl\n',
-                'owner_uuid': 'zzzzz-tpzed-zzzzzzzzzzzzzzz',
+                'owner_uuid': None,
                 'name': 'submit_wf.cwl',
             }, ensure_unique_name=True),
             mock.call().execute(),
             mock.call(body={'manifest_text': '. d41d8cd98f00b204e9800998ecf8427e+0 '
                             '0:0:blub.txt 0:0:submit_tool.cwl\n',
-                            'owner_uuid': 'zzzzz-tpzed-zzzzzzzzzzzzzzz',
                             'name': 'New collection',
                             'replication_desired': None,
             }, ensure_unique_name=True),
@@ -319,15 +444,14 @@ class TestSubmit(unittest.TestCase):
             mock.call(body={
                 'manifest_text':
                 '. 979af1245a12a1fed634d4222473bfdc+16 0:16:blorp.txt\n',
-                'owner_uuid': 'zzzzz-tpzed-zzzzzzzzzzzzzzz',
+                'owner_uuid': None,
                 'name': '#',
             }, ensure_unique_name=True),
             mock.call().execute()])
 
         expect_container = copy.deepcopy(stubs.expect_container_spec)
-        expect_container["owner_uuid"] = stubs.fake_user_uuid
         stubs.api.container_requests().create.assert_called_with(
-            body=expect_container)
+            body=JsonDiffMatcher(expect_container))
         self.assertEqual(capture_stdout.getvalue(),
                          stubs.expect_container_request_uuid + '\n')
 
@@ -343,15 +467,291 @@ class TestSubmit(unittest.TestCase):
         except:
             logging.exception("")
 
-        stubs.expect_container_spec["command"] = ['arvados-cwl-runner', '--local', '--api=containers', '--disable-reuse', '/var/lib/cwl/workflow/submit_wf.cwl', '/var/lib/cwl/job/cwl.input.json']
+        stubs.expect_container_spec["command"] = ['arvados-cwl-runner', '--local', '--api=containers', '--no-log-timestamps',
+                                                  '--disable-reuse', '--on-error=continue',
+                                                  '/var/lib/cwl/workflow/submit_wf.cwl', '/var/lib/cwl/cwl.input.json']
+
+        expect_container = copy.deepcopy(stubs.expect_container_spec)
+        stubs.api.container_requests().create.assert_called_with(
+            body=JsonDiffMatcher(expect_container))
+        self.assertEqual(capture_stdout.getvalue(),
+                         stubs.expect_container_request_uuid + '\n')
+
+
+    @stubs
+    def test_submit_container_on_error(self, stubs):
+        capture_stdout = cStringIO.StringIO()
+        try:
+            exited = arvados_cwl.main(
+                ["--submit", "--no-wait", "--api=containers", "--debug", "--on-error=stop",
+                 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
+                capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
+            self.assertEqual(exited, 0)
+        except:
+            logging.exception("")
+
+        stubs.expect_container_spec["command"] = ['arvados-cwl-runner', '--local', '--api=containers', '--no-log-timestamps',
+                                                  '--enable-reuse', '--on-error=stop',
+                                                  '/var/lib/cwl/workflow/submit_wf.cwl', '/var/lib/cwl/cwl.input.json']
+
+        expect_container = copy.deepcopy(stubs.expect_container_spec)
+        stubs.api.container_requests().create.assert_called_with(
+            body=JsonDiffMatcher(expect_container))
+        self.assertEqual(capture_stdout.getvalue(),
+                         stubs.expect_container_request_uuid + '\n')
+
+    @stubs
+    def test_submit_container_output_name(self, stubs):
+        output_name = "test_output_name"
+
+        capture_stdout = cStringIO.StringIO()
+        try:
+            exited = arvados_cwl.main(
+                ["--submit", "--no-wait", "--api=containers", "--debug", "--output-name", output_name,
+                 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
+                capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
+            self.assertEqual(exited, 0)
+        except:
+            logging.exception("")
+
+        stubs.expect_container_spec["command"] = ['arvados-cwl-runner', '--local', '--api=containers', '--no-log-timestamps',
+                                                  "--output-name="+output_name, '--enable-reuse', '--on-error=continue',
+                                                  '/var/lib/cwl/workflow/submit_wf.cwl', '/var/lib/cwl/cwl.input.json']
+        stubs.expect_container_spec["output_name"] = output_name
+
+        expect_container = copy.deepcopy(stubs.expect_container_spec)
+        stubs.api.container_requests().create.assert_called_with(
+            body=JsonDiffMatcher(expect_container))
+        self.assertEqual(capture_stdout.getvalue(),
+                         stubs.expect_container_request_uuid + '\n')
+
+    @stubs
+    def test_submit_container_output_tags(self, stubs):
+        output_tags = "tag0,tag1,tag2"
+
+        capture_stdout = cStringIO.StringIO()
+        try:
+            exited = arvados_cwl.main(
+                ["--submit", "--no-wait", "--api=containers", "--debug", "--output-tags", output_tags,
+                 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
+                capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
+            self.assertEqual(exited, 0)
+        except:
+            logging.exception("")
+
+        stubs.expect_container_spec["command"] = ['arvados-cwl-runner', '--local', '--api=containers', '--no-log-timestamps',
+                                                  "--output-tags="+output_tags, '--enable-reuse', '--on-error=continue',
+                                                  '/var/lib/cwl/workflow/submit_wf.cwl', '/var/lib/cwl/cwl.input.json']
+
+        expect_container = copy.deepcopy(stubs.expect_container_spec)
+        stubs.api.container_requests().create.assert_called_with(
+            body=JsonDiffMatcher(expect_container))
+        self.assertEqual(capture_stdout.getvalue(),
+                         stubs.expect_container_request_uuid + '\n')
+
+    @stubs
+    def test_submit_container_runner_ram(self, stubs):
+        capture_stdout = cStringIO.StringIO()
+        try:
+            exited = arvados_cwl.main(
+                ["--submit", "--no-wait", "--api=containers", "--debug", "--submit-runner-ram=2048",
+                 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
+                capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
+            self.assertEqual(exited, 0)
+        except:
+            logging.exception("")
+
+        stubs.expect_container_spec["runtime_constraints"]["ram"] = 2048*1024*1024
+
+        expect_container = copy.deepcopy(stubs.expect_container_spec)
+        stubs.api.container_requests().create.assert_called_with(
+            body=JsonDiffMatcher(expect_container))
+        self.assertEqual(capture_stdout.getvalue(),
+                         stubs.expect_container_request_uuid + '\n')
+
+    @mock.patch("arvados.collection.CollectionReader")
+    @mock.patch("time.sleep")
+    @stubs
+    def test_submit_file_keepref(self, stubs, tm, collectionReader):
+        capture_stdout = cStringIO.StringIO()
+        exited = arvados_cwl.main(
+            ["--submit", "--no-wait", "--api=containers", "--debug",
+             "tests/wf/submit_keepref_wf.cwl"],
+            capture_stdout, sys.stderr, api_client=stubs.api)
+        self.assertEqual(exited, 0)
+
+
+    @mock.patch("arvados.collection.CollectionReader")
+    @mock.patch("time.sleep")
+    @stubs
+    def test_submit_keepref(self, stubs, tm, reader):
+        capture_stdout = cStringIO.StringIO()
+
+        with open("tests/wf/expect_arvworkflow.cwl") as f:
+            reader().open().__enter__().read.return_value = f.read()
+
+        exited = arvados_cwl.main(
+            ["--submit", "--no-wait", "--api=containers", "--debug",
+             "keep:99999999999999999999999999999994+99/expect_arvworkflow.cwl#main", "-x", "XxX"],
+            capture_stdout, sys.stderr, api_client=stubs.api)
+        self.assertEqual(exited, 0)
+
+        expect_container = {
+            'priority': 1,
+            'mounts': {
+                '/var/spool/cwl': {
+                    'writable': True,
+                    'kind': 'collection'
+                },
+                'stdout': {
+                    'path': '/var/spool/cwl/cwl.output.json',
+                    'kind': 'file'
+                },
+                '/var/lib/cwl/workflow': {
+                    'portable_data_hash': '99999999999999999999999999999994+99',
+                    'kind': 'collection'
+                },
+                '/var/lib/cwl/cwl.input.json': {
+                    'content': {
+                        'x': 'XxX'
+                    },
+                    'kind': 'json'
+                }
+            }, 'state': 'Committed',
+            'owner_uuid': None,
+            'output_path': '/var/spool/cwl',
+            'name': 'expect_arvworkflow.cwl#main',
+            'container_image': 'arvados/jobs:'+arvados_cwl.__version__,
+            'command': ['arvados-cwl-runner', '--local', '--api=containers', '--no-log-timestamps',
+                        '--enable-reuse', '--on-error=continue',
+                        '/var/lib/cwl/workflow/expect_arvworkflow.cwl#main', '/var/lib/cwl/cwl.input.json'],
+            'cwd': '/var/spool/cwl',
+            'runtime_constraints': {
+                'API': True,
+                'vcpus': 1,
+                'ram': 1073741824
+            },
+            "properties": {}
+        }
+
+        stubs.api.container_requests().create.assert_called_with(
+            body=JsonDiffMatcher(expect_container))
+        self.assertEqual(capture_stdout.getvalue(),
+                         stubs.expect_container_request_uuid + '\n')
+
+    @mock.patch("time.sleep")
+    @stubs
+    def test_submit_arvworkflow(self, stubs, tm):
+        capture_stdout = cStringIO.StringIO()
+
+        with open("tests/wf/expect_arvworkflow.cwl") as f:
+            stubs.api.workflows().get().execute.return_value = {"definition": f.read(), "name": "a test workflow"}
+
+        exited = arvados_cwl.main(
+            ["--submit", "--no-wait", "--api=containers", "--debug",
+             "962eh-7fd4e-gkbzl62qqtfig37", "-x", "XxX"],
+            capture_stdout, sys.stderr, api_client=stubs.api)
+        self.assertEqual(exited, 0)
+
+        expect_container = {
+            'priority': 1,
+            'mounts': {
+                '/var/spool/cwl': {
+                    'writable': True,
+                    'kind': 'collection'
+                },
+                'stdout': {
+                    'path': '/var/spool/cwl/cwl.output.json',
+                    'kind': 'file'
+                },
+                '/var/lib/cwl/workflow.json': {
+                    'kind': 'json',
+                    'json': {
+                        'cwlVersion': 'v1.0',
+                        '$graph': [
+                            {
+                                'inputs': [
+                                    {
+                                        'inputBinding': {'position': 1},
+                                        'type': 'string',
+                                        'id': '#submit_tool.cwl/x'}
+                                ],
+                                'requirements': [
+                                    {'dockerPull': 'debian:8', 'class': 'DockerRequirement'}
+                                ],
+                                'id': '#submit_tool.cwl',
+                                'outputs': [],
+                                'baseCommand': 'cat',
+                                'class': 'CommandLineTool'
+                            }, {
+                                'id': '#main',
+                                'inputs': [
+                                    {'type': 'string', 'id': '#main/x'}
+                                ],
+                                'steps': [
+                                    {'in': [{'source': '#main/x', 'id': '#main/step1/x'}],
+                                     'run': '#submit_tool.cwl',
+                                     'id': '#main/step1',
+                                     'out': []}
+                                ],
+                                'class': 'Workflow',
+                                'outputs': []
+                            }
+                        ]
+                    }
+                },
+                '/var/lib/cwl/cwl.input.json': {
+                    'content': {
+                        'x': 'XxX'
+                    },
+                    'kind': 'json'
+                }
+            }, 'state': 'Committed',
+            'owner_uuid': None,
+            'output_path': '/var/spool/cwl',
+            'name': 'a test workflow',
+            'container_image': 'arvados/jobs:'+arvados_cwl.__version__,
+            'command': ['arvados-cwl-runner', '--local', '--api=containers', '--no-log-timestamps',
+                        '--enable-reuse', '--on-error=continue',
+                        '/var/lib/cwl/workflow.json#main', '/var/lib/cwl/cwl.input.json'],
+            'cwd': '/var/spool/cwl',
+            'runtime_constraints': {
+                'API': True,
+                'vcpus': 1,
+                'ram': 1073741824
+            },
+            "properties": {
+                "template_uuid": "962eh-7fd4e-gkbzl62qqtfig37"
+            }
+        }
+
+        stubs.api.container_requests().create.assert_called_with(
+            body=JsonDiffMatcher(expect_container))
+        self.assertEqual(capture_stdout.getvalue(),
+                         stubs.expect_container_request_uuid + '\n')
+
+
+    @stubs
+    def test_submit_container_name(self, stubs):
+        capture_stdout = cStringIO.StringIO()
+        try:
+            exited = arvados_cwl.main(
+                ["--submit", "--no-wait", "--api=containers", "--debug", "--name=hello container 123",
+                 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
+                capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
+            self.assertEqual(exited, 0)
+        except:
+            logging.exception("")
+
+        stubs.expect_container_spec["name"] = "hello container 123"
 
         expect_container = copy.deepcopy(stubs.expect_container_spec)
-        expect_container["owner_uuid"] = stubs.fake_user_uuid
         stubs.api.container_requests().create.assert_called_with(
             body=expect_container)
         self.assertEqual(capture_stdout.getvalue(),
                          stubs.expect_container_request_uuid + '\n')
 
+
     @mock.patch("arvados.commands.keepdocker.find_one_image_hash")
     @mock.patch("cwltool.docker.get_image")
     @mock.patch("arvados.api")
@@ -396,23 +796,9 @@ class TestSubmit(unittest.TestCase):
         self.assertEqual("arvados/jobs:"+arvados_cwl.__version__, arvados_cwl.runner.arvados_jobs_image(arvrunner))
 
 class TestCreateTemplate(unittest.TestCase):
-    @stubs
-    def test_create(self, stubs):
-        project_uuid = 'zzzzz-j7d0g-zzzzzzzzzzzzzzz'
-
-        capture_stdout = cStringIO.StringIO()
+    existing_template_uuid = "zzzzz-d1hrv-validworkfloyml"
 
-        exited = arvados_cwl.main(
-            ["--create-template", "--debug",
-             "--project-uuid", project_uuid,
-             "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
-            capture_stdout, sys.stderr, api_client=stubs.api)
-        self.assertEqual(exited, 0)
-
-        stubs.api.pipeline_instances().create.refute_called()
-        stubs.api.jobs().create.refute_called()
-
-        expect_component = copy.deepcopy(stubs.expect_job_spec)
+    def _adjust_script_params(self, expect_component):
         expect_component['script_parameters']['x'] = {
             'dataclass': 'File',
             'required': True,
@@ -430,6 +816,26 @@ class TestCreateTemplate(unittest.TestCase):
             'required': True,
             'type': 'Directory',
         }
+
+    @stubs
+    def test_create(self, stubs):
+        project_uuid = 'zzzzz-j7d0g-zzzzzzzzzzzzzzz'
+
+        capture_stdout = cStringIO.StringIO()
+
+        exited = arvados_cwl.main(
+            ["--create-workflow", "--debug",
+             "--api=jobs",
+             "--project-uuid", project_uuid,
+             "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
+            capture_stdout, sys.stderr, api_client=stubs.api)
+        self.assertEqual(exited, 0)
+
+        stubs.api.pipeline_instances().create.refute_called()
+        stubs.api.jobs().create.refute_called()
+
+        expect_component = copy.deepcopy(stubs.expect_job_spec)
+        self._adjust_script_params(expect_component)
         expect_template = {
             "components": {
                 "submit_wf.cwl": expect_component,
@@ -444,7 +850,80 @@ class TestCreateTemplate(unittest.TestCase):
                          stubs.expect_pipeline_template_uuid + '\n')
 
 
+    @stubs
+    def test_create_name(self, stubs):
+        project_uuid = 'zzzzz-j7d0g-zzzzzzzzzzzzzzz'
+
+        capture_stdout = cStringIO.StringIO()
+
+        exited = arvados_cwl.main(
+            ["--create-workflow", "--debug",
+             "--project-uuid", project_uuid,
+             "--api=jobs",
+             "--name", "testing 123",
+             "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
+            capture_stdout, sys.stderr, api_client=stubs.api)
+        self.assertEqual(exited, 0)
+
+        stubs.api.pipeline_instances().create.refute_called()
+        stubs.api.jobs().create.refute_called()
+
+        expect_component = copy.deepcopy(stubs.expect_job_spec)
+        self._adjust_script_params(expect_component)
+        expect_template = {
+            "components": {
+                "testing 123": expect_component,
+            },
+            "name": "testing 123",
+            "owner_uuid": project_uuid,
+        }
+        stubs.api.pipeline_templates().create.assert_called_with(
+            body=JsonDiffMatcher(expect_template), ensure_unique_name=True)
+
+        self.assertEqual(capture_stdout.getvalue(),
+                         stubs.expect_pipeline_template_uuid + '\n')
+
+
+    @stubs
+    def test_update_name(self, stubs):
+        project_uuid = 'zzzzz-j7d0g-zzzzzzzzzzzzzzz'
+
+        capture_stdout = cStringIO.StringIO()
+
+        exited = arvados_cwl.main(
+            ["--update-workflow", self.existing_template_uuid,
+             "--debug",
+             "--project-uuid", project_uuid,
+             "--api=jobs",
+             "--name", "testing 123",
+             "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
+            capture_stdout, sys.stderr, api_client=stubs.api)
+        self.assertEqual(exited, 0)
+
+        stubs.api.pipeline_instances().create.refute_called()
+        stubs.api.jobs().create.refute_called()
+
+        expect_component = copy.deepcopy(stubs.expect_job_spec)
+        self._adjust_script_params(expect_component)
+        expect_template = {
+            "components": {
+                "testing 123": expect_component,
+            },
+            "name": "testing 123",
+            "owner_uuid": project_uuid,
+        }
+        stubs.api.pipeline_templates().create.refute_called()
+        stubs.api.pipeline_templates().update.assert_called_with(
+            body=JsonDiffMatcher(expect_template), uuid=self.existing_template_uuid)
+
+        self.assertEqual(capture_stdout.getvalue(),
+                         self.existing_template_uuid + '\n')
+
+
 class TestCreateWorkflow(unittest.TestCase):
+    existing_workflow_uuid = "zzzzz-7fd4e-validworkfloyml"
+    expect_workflow = open("tests/wf/expect_packed.cwl").read()
+
     @stubs
     def test_create(self, stubs):
         project_uuid = 'zzzzz-j7d0g-zzzzzzzzzzzzzzz'
@@ -453,6 +932,7 @@ class TestCreateWorkflow(unittest.TestCase):
 
         exited = arvados_cwl.main(
             ["--create-workflow", "--debug",
+             "--api=containers",
              "--project-uuid", project_uuid,
              "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
             capture_stdout, sys.stderr, api_client=stubs.api)
@@ -461,16 +941,46 @@ class TestCreateWorkflow(unittest.TestCase):
         stubs.api.pipeline_templates().create.refute_called()
         stubs.api.container_requests().create.refute_called()
 
-        with open("tests/wf/expect_packed.cwl") as f:
-            expect_workflow = f.read()
-
         body = {
             "workflow": {
                 "owner_uuid": project_uuid,
                 "name": "submit_wf.cwl",
                 "description": "",
-                "definition": expect_workflow
-                }
+                "definition": self.expect_workflow,
+            }
+        }
+        stubs.api.workflows().create.assert_called_with(
+            body=JsonDiffMatcher(body))
+
+        self.assertEqual(capture_stdout.getvalue(),
+                         stubs.expect_workflow_uuid + '\n')
+
+
+    @stubs
+    def test_create_name(self, stubs):
+        project_uuid = 'zzzzz-j7d0g-zzzzzzzzzzzzzzz'
+
+        capture_stdout = cStringIO.StringIO()
+
+        exited = arvados_cwl.main(
+            ["--create-workflow", "--debug",
+             "--api=containers",
+             "--project-uuid", project_uuid,
+             "--name", "testing 123",
+             "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
+            capture_stdout, sys.stderr, api_client=stubs.api)
+        self.assertEqual(exited, 0)
+
+        stubs.api.pipeline_templates().create.refute_called()
+        stubs.api.container_requests().create.refute_called()
+
+        body = {
+            "workflow": {
+                "owner_uuid": project_uuid,
+                "name": "testing 123",
+                "description": "",
+                "definition": self.expect_workflow,
+            }
         }
         stubs.api.workflows().create.assert_called_with(
             body=JsonDiffMatcher(body))
@@ -478,6 +988,72 @@ class TestCreateWorkflow(unittest.TestCase):
         self.assertEqual(capture_stdout.getvalue(),
                          stubs.expect_workflow_uuid + '\n')
 
+    @stubs
+    def test_incompatible_api(self, stubs):
+        capture_stderr = cStringIO.StringIO()
+        logging.getLogger('arvados.cwl-runner').addHandler(
+            logging.StreamHandler(capture_stderr))
+
+        exited = arvados_cwl.main(
+            ["--update-workflow", self.existing_workflow_uuid,
+             "--api=jobs",
+             "--debug",
+             "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
+            sys.stderr, sys.stderr, api_client=stubs.api)
+        self.assertEqual(exited, 1)
+        self.assertRegexpMatches(
+            capture_stderr.getvalue(),
+            "--update-workflow arg '{}' uses 'containers' API, but --api='jobs' specified".format(self.existing_workflow_uuid))
+
+    @stubs
+    def test_update(self, stubs):
+        capture_stdout = cStringIO.StringIO()
+
+        exited = arvados_cwl.main(
+            ["--update-workflow", self.existing_workflow_uuid,
+             "--debug",
+             "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
+            capture_stdout, sys.stderr, api_client=stubs.api)
+        self.assertEqual(exited, 0)
+
+        body = {
+            "workflow": {
+                "name": "submit_wf.cwl",
+                "description": "",
+                "definition": self.expect_workflow,
+            }
+        }
+        stubs.api.workflows().update.assert_called_with(
+            uuid=self.existing_workflow_uuid,
+            body=JsonDiffMatcher(body))
+        self.assertEqual(capture_stdout.getvalue(),
+                         self.existing_workflow_uuid + '\n')
+
+
+    @stubs
+    def test_update_name(self, stubs):
+        capture_stdout = cStringIO.StringIO()
+
+        exited = arvados_cwl.main(
+            ["--update-workflow", self.existing_workflow_uuid,
+             "--debug", "--name", "testing 123",
+             "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
+            capture_stdout, sys.stderr, api_client=stubs.api)
+        self.assertEqual(exited, 0)
+
+        body = {
+            "workflow": {
+                "name": "testing 123",
+                "description": "",
+                "definition": self.expect_workflow,
+            }
+        }
+        stubs.api.workflows().update.assert_called_with(
+            uuid=self.existing_workflow_uuid,
+            body=JsonDiffMatcher(body))
+        self.assertEqual(capture_stdout.getvalue(),
+                         self.existing_workflow_uuid + '\n')
+
 
 class TestTemplateInputs(unittest.TestCase):
     expect_template = {
@@ -485,6 +1061,7 @@ class TestTemplateInputs(unittest.TestCase):
             "inputs_test.cwl": {
                 'runtime_constraints': {
                     'docker_image': 'arvados/jobs:'+arvados_cwl.__version__,
+                    'min_ram_mb_per_node': 1024
                 },
                 'script_parameters': {
                     'cwl:tool':
@@ -519,7 +1096,8 @@ class TestTemplateInputs(unittest.TestCase):
                     },
                 },
                 'repository': 'arvados',
-                'script_version': arvados_cwl.__version__,
+                'script_version': 'master',
+                'minimum_script_version': '570509ab4d2ef93d870fd2b1f2eab178afb1bad9',
                 'script': 'cwl-runner',
             },
         },
@@ -529,29 +1107,23 @@ class TestTemplateInputs(unittest.TestCase):
     @stubs
     def test_inputs_empty(self, stubs):
         exited = arvados_cwl.main(
-            ["--create-template", "--no-wait",
+            ["--create-template",
              "tests/wf/inputs_test.cwl", "tests/order/empty_order.json"],
             cStringIO.StringIO(), sys.stderr, api_client=stubs.api)
         self.assertEqual(exited, 0)
 
-        expect_template = copy.deepcopy(self.expect_template)
-        expect_template["owner_uuid"] = stubs.fake_user_uuid
-
         stubs.api.pipeline_templates().create.assert_called_with(
-            body=JsonDiffMatcher(expect_template), ensure_unique_name=True)
+            body=JsonDiffMatcher(self.expect_template), ensure_unique_name=True)
 
     @stubs
     def test_inputs(self, stubs):
         exited = arvados_cwl.main(
-            ["--create-template", "--no-wait",
+            ["--create-template",
              "tests/wf/inputs_test.cwl", "tests/order/inputs_test_order.json"],
             cStringIO.StringIO(), sys.stderr, api_client=stubs.api)
         self.assertEqual(exited, 0)
 
-        self.expect_template["owner_uuid"] = stubs.fake_user_uuid
-
         expect_template = copy.deepcopy(self.expect_template)
-        expect_template["owner_uuid"] = stubs.fake_user_uuid
         params = expect_template[
             "components"]["inputs_test.cwl"]["script_parameters"]
         params["fileInput"]["value"] = '99999999999999999999999999999994+99/blorp.txt'
diff --git a/sdk/cwl/tests/test_urljoin.py b/sdk/cwl/tests/test_urljoin.py
new file mode 100644 (file)
index 0000000..b9c8cea
--- /dev/null
@@ -0,0 +1,56 @@
+import functools
+import mock
+import sys
+import unittest
+import json
+import logging
+import os
+
+import arvados
+import arvados.keep
+import arvados.collection
+import arvados_cwl
+
+from arvados_cwl.fsaccess import CollectionFetcher
+
+class TestUrljoin(unittest.TestCase):
+    def test_urljoin(self):
+        """Test path joining for keep references."""
+
+        cf = CollectionFetcher({}, None)
+
+        self.assertEquals("keep:99999999999999999999999999999991+99/hw.py",
+                          cf.urljoin("keep:99999999999999999999999999999991+99", "hw.py"))
+
+        self.assertEquals("keep:99999999999999999999999999999991+99/hw.py",
+                          cf.urljoin("keep:99999999999999999999999999999991+99/", "hw.py"))
+
+        self.assertEquals("keep:99999999999999999999999999999991+99/hw.py#main",
+                          cf.urljoin("keep:99999999999999999999999999999991+99", "hw.py#main"))
+
+        self.assertEquals("keep:99999999999999999999999999999991+99/hw.py#main",
+                          cf.urljoin("keep:99999999999999999999999999999991+99/hw.py", "#main"))
+
+        self.assertEquals("keep:99999999999999999999999999999991+99/dir/hw.py#main",
+                          cf.urljoin("keep:99999999999999999999999999999991+99/dir/hw.py", "#main"))
+
+        self.assertEquals("keep:99999999999999999999999999999991+99/dir/wh.py",
+                          cf.urljoin("keep:99999999999999999999999999999991+99/dir/hw.py", "wh.py"))
+
+        self.assertEquals("keep:99999999999999999999999999999991+99/wh.py",
+                          cf.urljoin("keep:99999999999999999999999999999991+99/dir/hw.py", "/wh.py"))
+
+        self.assertEquals("keep:99999999999999999999999999999991+99/wh.py#main",
+                          cf.urljoin("keep:99999999999999999999999999999991+99/dir/hw.py", "/wh.py#main"))
+
+        self.assertEquals("keep:99999999999999999999999999999991+99/wh.py",
+                          cf.urljoin("keep:99999999999999999999999999999991+99/hw.py#main", "wh.py"))
+
+        self.assertEquals("keep:99999999999999999999999999999992+99",
+                          cf.urljoin("keep:99999999999999999999999999999991+99", "keep:99999999999999999999999999999992+99"))
+
+        self.assertEquals("keep:99999999999999999999999999999991+99/dir/wh.py",
+                          cf.urljoin("keep:99999999999999999999999999999991+99/dir/", "wh.py"))
+
+    def test_resolver(self):
+        pass
diff --git a/sdk/cwl/tests/wf/expect_arvworkflow.cwl b/sdk/cwl/tests/wf/expect_arvworkflow.cwl
new file mode 100644 (file)
index 0000000..56ce0d5
--- /dev/null
@@ -0,0 +1,24 @@
+$graph:
+- baseCommand: cat
+  class: CommandLineTool
+  id: '#submit_tool.cwl'
+  inputs:
+  - id: '#submit_tool.cwl/x'
+    inputBinding: {position: 1}
+    type: string
+  outputs: []
+  requirements:
+  - {class: DockerRequirement, dockerPull: 'debian:8'}
+- class: Workflow
+  id: '#main'
+  inputs:
+  - id: '#main/x'
+    type: string
+  outputs: []
+  steps:
+  - id: '#main/step1'
+    in:
+    - {id: '#main/step1/x', source: '#main/x'}
+    out: []
+    run: '#submit_tool.cwl'
+cwlVersion: v1.0
index 25d02b2b809dcb7ad0272485eadb357b9e6f2eb6..f4d60dbfe8cad258da5e6eb81a7ec3315d629979 100644 (file)
@@ -1,31 +1,34 @@
+cwlVersion: v1.0
 $graph:
-- baseCommand: cat
-  class: CommandLineTool
-  id: '#submit_tool.cwl'
+- class: CommandLineTool
+  requirements:
+  - class: DockerRequirement
+    dockerPull: debian:8
   inputs:
-  - default: {class: File, location: 'keep:99999999999999999999999999999991+99/tool/blub.txt'}
-    id: '#submit_tool.cwl/x'
-    inputBinding: {position: 1}
+  - id: '#submit_tool.cwl/x'
     type: File
+    default:
+      class: File
+      location: keep:99999999999999999999999999999991+99/tool/blub.txt
+    inputBinding:
+      position: 1
   outputs: []
-  requirements:
-  - {class: DockerRequirement, dockerImageId: 'debian:8', dockerPull: 'debian:8'}
+  baseCommand: cat
+  id: '#submit_tool.cwl'
 - class: Workflow
-  id: '#main'
   inputs:
-  - default: {basename: blorp.txt, class: File, location: 'keep:99999999999999999999999999999991+99/input/blorp.txt'}
-    id: '#main/x'
+  - id: '#main/x'
     type: File
-  - default: {basename: 99999999999999999999999999999998+99, class: Directory, location: 'keep:99999999999999999999999999999998+99'}
-    id: '#main/y'
+    default: {class: File, location: 'keep:99999999999999999999999999999991+99/input/blorp.txt',
+      basename: blorp.txt}
+  - id: '#main/y'
     type: Directory
-  - default:
-      basename: anonymous
-      class: Directory
-      listing:
-      - {basename: renamed.txt, class: File, location: 'keep:99999999999999999999999999999998+99/file1.txt'}
-    id: '#main/z'
+    default: {class: Directory, location: 'keep:99999999999999999999999999999998+99',
+      basename: 99999999999999999999999999999998+99}
+  - id: '#main/z'
     type: Directory
+    default: {class: Directory, basename: anonymous, listing: [{basename: renamed.txt,
+          class: File, location: 'keep:99999999999999999999999999999998+99/file1.txt'}]}
   outputs: []
   steps:
   - id: '#main/step1'
@@ -33,4 +36,4 @@ $graph:
     - {id: '#main/step1/x', source: '#main/x'}
     out: []
     run: '#submit_tool.cwl'
-cwlVersion: v1.0
+  id: '#main'
index 0ae1cf04f05ed57fd57b4832cac207d8cdf629c1..daf18b11ca79cf6ddbb6892d331270df0bc2e9a0 100644 (file)
@@ -1,33 +1,41 @@
+cwlVersion: v1.0
 $graph:
 - class: Workflow
-  hints:
-  - {class: 'http://arvados.org/cwl#RunInSingleContainer'}
   id: '#main'
   inputs:
-  - {id: '#main/sleeptime', type: int}
+  - type: int
+    id: '#main/sleeptime'
   outputs:
-  - {id: '#main/out', outputSource: '#main/sleep1/out', type: string}
-  requirements:
-  - {class: InlineJavascriptRequirement}
-  - {class: ScatterFeatureRequirement}
-  - {class: StepInputExpressionRequirement}
-  - {class: SubworkflowFeatureRequirement}
+  - type: string
+    outputSource: '#main/sleep1/out'
+    id: '#main/out'
   steps:
-  - id: '#main/sleep1'
-    in:
-    - {id: '#main/sleep1/blurb', valueFrom: "${\n  return String(inputs.sleeptime)\
-        \ + \"b\";\n}\n"}
-    - {id: '#main/sleep1/sleeptime', source: '#main/sleeptime'}
+  - in:
+    - valueFrom: |
+        ${
+          return String(inputs.sleeptime) + "b";
+        }
+      id: '#main/sleep1/blurb'
+    - source: '#main/sleeptime'
+      id: '#main/sleep1/sleeptime'
     out: ['#main/sleep1/out']
     run:
-      baseCommand: sleep
       class: CommandLineTool
       inputs:
-      - id: '#main/sleep1/sleeptime'
+      - type: int
         inputBinding: {position: 1}
-        type: int
+        id: '#main/sleep1/sleeptime'
       outputs:
-      - id: '#main/sleep1/out'
-        outputBinding: {outputEval: out}
-        type: string
-cwlVersion: v1.0
\ No newline at end of file
+      - type: string
+        outputBinding:
+          outputEval: out
+        id: '#main/sleep1/out'
+      baseCommand: sleep
+    id: '#main/sleep1'
+  requirements:
+  - {class: InlineJavascriptRequirement}
+  - {class: ScatterFeatureRequirement}
+  - {class: StepInputExpressionRequirement}
+  - {class: SubworkflowFeatureRequirement}
+  hints:
+  - class: http://arvados.org/cwl#RunInSingleContainer
\ No newline at end of file
diff --git a/sdk/cwl/tests/wf/submit_keepref_wf.cwl b/sdk/cwl/tests/wf/submit_keepref_wf.cwl
new file mode 100644 (file)
index 0000000..f07714e
--- /dev/null
@@ -0,0 +1,20 @@
+# Test case for arvados-cwl-runner
+#
+# Used to test whether scanning a workflow file for dependencies
+# (e.g. submit_tool.cwl) and uploading to Keep works as intended.
+
+class: Workflow
+cwlVersion: v1.0
+inputs:
+  x:
+    type: File
+    default:
+      class: File
+      location: keep:99999999999999999999999999999994+99/blorp.txt
+outputs: []
+steps:
+  step1:
+    in:
+      x: x
+    out: []
+    run: ../tool/submit_tool.cwl
diff --git a/sdk/dev-jobs.dockerfile b/sdk/dev-jobs.dockerfile
new file mode 100644 (file)
index 0000000..38fefd0
--- /dev/null
@@ -0,0 +1,38 @@
+# Dockerfile for building an arvados/jobs Docker image from local git tree.
+#
+# Intended for use by developers working on arvados-python-client or
+# arvados-cwl-runner and need to run a crunch job with a custom package
+# version.
+#
+# Use arvados/build/build-dev-docker-jobs-image.sh to build.
+#
+# (This dockerfile file must be located in the arvados/sdk/ directory because
+#  of the docker build root.)
+
+FROM debian:jessie
+MAINTAINER Ward Vandewege <ward@curoverse.com>
+
+ENV DEBIAN_FRONTEND noninteractive
+
+RUN apt-get update -q && apt-get install -qy git python-pip python-virtualenv python-dev libcurl4-gnutls-dev libgnutls28-dev nodejs python-pyasn1-modules
+
+RUN pip install -U setuptools
+
+ARG sdk
+ARG runner
+ARG cwltool
+
+ADD python/dist/$sdk /tmp/
+ADD cwl/cwltool_dist/$cwltool /tmp/
+ADD cwl/dist/$runner /tmp/
+
+RUN cd /tmp/arvados-python-client-* && python setup.py install
+RUN if test -d /tmp/cwltool-* ; then cd /tmp/cwltool-* && python setup.py install ; fi
+RUN cd /tmp/arvados-cwl-runner-* && python setup.py install
+
+# Install dependencies and set up system.
+RUN /usr/sbin/adduser --disabled-password \
+      --gecos 'Crunch execution user' crunch && \
+    /usr/bin/install --directory --owner=crunch --group=crunch --mode=0700 /keep /tmp/crunch-src /tmp/crunch-job
+
+USER crunch
index 36f4eb52ae298982dfa09ddf82b0cea08c2604f7..fc937494e5a679728aea021dbe80a95231582d72 100644 (file)
@@ -41,6 +41,8 @@ type Client struct {
        // callers who use a Client to initialize an
        // arvadosclient.ArvadosClient.)
        KeepServiceURIs []string `json:",omitempty"`
+
+       dd *DiscoveryDocument
 }
 
 // The default http.Client used by a Client with Insecure==true and
@@ -198,14 +200,103 @@ func (c *Client) apiURL(path string) string {
 
 // DiscoveryDocument is the Arvados server's description of itself.
 type DiscoveryDocument struct {
-       DefaultCollectionReplication int   `json:"defaultCollectionReplication"`
-       BlobSignatureTTL             int64 `json:"blobSignatureTtl"`
+       BasePath                     string              `json:"basePath"`
+       DefaultCollectionReplication int                 `json:"defaultCollectionReplication"`
+       BlobSignatureTTL             int64               `json:"blobSignatureTtl"`
+       Schemas                      map[string]Schema   `json:"schemas"`
+       Resources                    map[string]Resource `json:"resources"`
+}
+
+type Resource struct {
+       Methods map[string]ResourceMethod `json:"methods"`
+}
+
+type ResourceMethod struct {
+       HTTPMethod string         `json:"httpMethod"`
+       Path       string         `json:"path"`
+       Response   MethodResponse `json:"response"`
+}
+
+type MethodResponse struct {
+       Ref string `json:"$ref"`
+}
+
+type Schema struct {
+       UUIDPrefix string `json:"uuidPrefix"`
 }
 
 // DiscoveryDocument returns a *DiscoveryDocument. The returned object
 // should not be modified: the same object may be returned by
 // subsequent calls.
 func (c *Client) DiscoveryDocument() (*DiscoveryDocument, error) {
+       if c.dd != nil {
+               return c.dd, nil
+       }
        var dd DiscoveryDocument
-       return &dd, c.RequestAndDecode(&dd, "GET", "discovery/v1/apis/arvados/v1/rest", nil, nil)
+       err := c.RequestAndDecode(&dd, "GET", "discovery/v1/apis/arvados/v1/rest", nil, nil)
+       if err != nil {
+               return nil, err
+       }
+       c.dd = &dd
+       return c.dd, nil
+}
+
+func (c *Client) modelForUUID(dd *DiscoveryDocument, uuid string) (string, error) {
+       if len(uuid) != 27 {
+               return "", fmt.Errorf("invalid UUID: %q", uuid)
+       }
+       infix := uuid[6:11]
+       var model string
+       for m, s := range dd.Schemas {
+               if s.UUIDPrefix == infix {
+                       model = m
+                       break
+               }
+       }
+       if model == "" {
+               return "", fmt.Errorf("unrecognized type portion %q in UUID %q", infix, uuid)
+       }
+       return model, nil
+}
+
+func (c *Client) KindForUUID(uuid string) (string, error) {
+       dd, err := c.DiscoveryDocument()
+       if err != nil {
+               return "", err
+       }
+       model, err := c.modelForUUID(dd, uuid)
+       if err != nil {
+               return "", err
+       }
+       return "arvados#" + strings.ToLower(model[:1]) + model[1:], nil
+}
+
+func (c *Client) PathForUUID(method, uuid string) (string, error) {
+       dd, err := c.DiscoveryDocument()
+       if err != nil {
+               return "", err
+       }
+       model, err := c.modelForUUID(dd, uuid)
+       if err != nil {
+               return "", err
+       }
+       var resource string
+       for r, rsc := range dd.Resources {
+               if rsc.Methods["get"].Response.Ref == model {
+                       resource = r
+                       break
+               }
+       }
+       if resource == "" {
+               return "", fmt.Errorf("no resource for model: %q", model)
+       }
+       m, ok := dd.Resources[resource].Methods[method]
+       if !ok {
+               return "", fmt.Errorf("no method %q for resource %q", method, resource)
+       }
+       path := dd.BasePath + strings.Replace(m.Path, "{uuid}", uuid, -1)
+       if path[0] == '/' {
+               path = path[1:]
+       }
+       return path, nil
 }
index 71f52476153bc4ecea1fbf26e3830dde6f2cdf2b..157ce1678873af7f709c16752e590fb79b2ea822 100644 (file)
@@ -12,26 +12,33 @@ import (
 // Collection is an arvados#collection resource.
 type Collection struct {
        UUID                   string     `json:"uuid,omitempty"`
-       ExpiresAt              *time.Time `json:"expires_at,omitempty"`
+       TrashAt                *time.Time `json:"trash_at,omitempty"`
        ManifestText           string     `json:"manifest_text,omitempty"`
+       UnsignedManifestText   string     `json:"unsigned_manifest_text,omitempty"`
        CreatedAt              *time.Time `json:"created_at,omitempty"`
        ModifiedAt             *time.Time `json:"modified_at,omitempty"`
        PortableDataHash       string     `json:"portable_data_hash,omitempty"`
        ReplicationConfirmed   *int       `json:"replication_confirmed,omitempty"`
        ReplicationConfirmedAt *time.Time `json:"replication_confirmed_at,omitempty"`
        ReplicationDesired     *int       `json:"replication_desired,omitempty"`
+       DeleteAt               *time.Time `json:"delete_at,omitempty"`
+       IsTrashed              bool       `json:"is_trashed,omitempty"`
 }
 
 // SizedDigests returns the hash+size part of each data block
 // referenced by the collection.
 func (c *Collection) SizedDigests() ([]SizedDigest, error) {
-       if c.ManifestText == "" && c.PortableDataHash != "d41d8cd98f00b204e9800998ecf8427e+0" {
+       manifestText := c.ManifestText
+       if manifestText == "" {
+               manifestText = c.UnsignedManifestText
+       }
+       if manifestText == "" && c.PortableDataHash != "d41d8cd98f00b204e9800998ecf8427e+0" {
                // TODO: Check more subtle forms of corruption, too
                return nil, fmt.Errorf("manifest is missing")
        }
        var sds []SizedDigest
-       scanner := bufio.NewScanner(strings.NewReader(c.ManifestText))
-       scanner.Buffer(make([]byte, 1048576), len(c.ManifestText))
+       scanner := bufio.NewScanner(strings.NewReader(manifestText))
+       scanner.Buffer(make([]byte, 1048576), len(manifestText))
        for scanner.Scan() {
                line := scanner.Text()
                tokens := strings.Split(line, " ")
index 6a76f1f396a32c89544f55030cb586ae413d0c0b..61c14ea0b6c1d445bb2a26fb83a57614e0b240f9 100644 (file)
@@ -2,18 +2,19 @@ package arvados
 
 // Container is an arvados#container resource.
 type Container struct {
-       UUID               string             `json:"uuid"`
-       Command            []string           `json:"command"`
-       ContainerImage     string             `json:"container_image"`
-       Cwd                string             `json:"cwd"`
-       Environment        map[string]string  `json:"environment"`
-       LockedByUUID       string             `json:"locked_by_uuid"`
-       Mounts             map[string]Mount   `json:"mounts"`
-       Output             string             `json:"output"`
-       OutputPath         string             `json:"output_path"`
-       Priority           int                `json:"priority"`
-       RuntimeConstraints RuntimeConstraints `json:"runtime_constraints"`
-       State              ContainerState     `json:"state"`
+       UUID                 string               `json:"uuid"`
+       Command              []string             `json:"command"`
+       ContainerImage       string               `json:"container_image"`
+       Cwd                  string               `json:"cwd"`
+       Environment          map[string]string    `json:"environment"`
+       LockedByUUID         string               `json:"locked_by_uuid"`
+       Mounts               map[string]Mount     `json:"mounts"`
+       Output               string               `json:"output"`
+       OutputPath           string               `json:"output_path"`
+       Priority             int                  `json:"priority"`
+       RuntimeConstraints   RuntimeConstraints   `json:"runtime_constraints"`
+       State                ContainerState       `json:"state"`
+       SchedulingParameters SchedulingParameters `json:"scheduling_parameters"`
 }
 
 // Mount is special behavior to attach to a filesystem path or device.
@@ -31,10 +32,15 @@ type Mount struct {
 // CPU) and network connectivity.
 type RuntimeConstraints struct {
        API          *bool
-       RAM          int      `json:"ram"`
-       VCPUs        int      `json:"vcpus"`
-       KeepCacheRAM int      `json:"keep_cache_ram"`
-       Partition    []string `json:"partition"`
+       RAM          int `json:"ram"`
+       VCPUs        int `json:"vcpus"`
+       KeepCacheRAM int `json:"keep_cache_ram"`
+}
+
+// SchedulingParameters specify a container's scheduling parameters
+// such as Partitions
+type SchedulingParameters struct {
+       Partitions []string `json:"partitions"`
 }
 
 // ContainerList is an arvados#containerList resource.
diff --git a/sdk/go/arvados/log.go b/sdk/go/arvados/log.go
new file mode 100644 (file)
index 0000000..a48f1c6
--- /dev/null
@@ -0,0 +1,25 @@
+package arvados
+
+import (
+       "time"
+)
+
+// Log is an arvados#log record
+type Log struct {
+       ID              uint64                 `json:"id"`
+       UUID            string                 `json:"uuid"`
+       ObjectUUID      string                 `json:"object_uuid"`
+       ObjectOwnerUUID string                 `json:"object_owner_uuid"`
+       EventType       string                 `json:"event_type"`
+       EventAt         *time.Time             `json:"event,omitempty"`
+       Properties      map[string]interface{} `json:"properties"`
+       CreatedAt       *time.Time             `json:"created_at,omitempty"`
+}
+
+// LogList is an arvados#logList resource.
+type LogList struct {
+       Items          []Log `json:"items"`
+       ItemsAvailable int   `json:"items_available"`
+       Offset         int   `json:"offset"`
+       Limit          int   `json:"limit"`
+}
index 2864307ebfa029024894b6fe4bdac3667f98fc73..0cc665807b17e4d5a9735c6bf1b7f4242ec08998 100644 (file)
@@ -5,13 +5,14 @@ import "encoding/json"
 // ResourceListParams expresses which results are requested in a
 // list/index API.
 type ResourceListParams struct {
-       Select   []string `json:"select,omitempty"`
-       Filters  []Filter `json:"filters,omitempty"`
-       Limit    *int     `json:"limit,omitempty"`
-       Offset   int      `json:"offset,omitempty"`
-       Order    string   `json:"order,omitempty"`
-       Distinct bool     `json:"distinct,omitempty"`
-       Count    bool     `json:"count,omitempty"`
+       Select       []string `json:"select,omitempty"`
+       Filters      []Filter `json:"filters,omitempty"`
+       IncludeTrash bool     `json:"include_trash,omitempty"`
+       Limit        *int     `json:"limit,omitempty"`
+       Offset       int      `json:"offset,omitempty"`
+       Order        string   `json:"order,omitempty"`
+       Distinct     bool     `json:"distinct,omitempty"`
+       Count        bool     `json:"count,omitempty"`
 }
 
 // A Filter restricts the set of records returned by a list/index API.
index 5f24c7107d72798621b4a3110030981297489fc9..021b9471ff93814b81c933923e819f821efd8f1b 100644 (file)
@@ -5,10 +5,12 @@ package arvadosclient
 import (
        "bytes"
        "crypto/tls"
+       "crypto/x509"
        "encoding/json"
        "errors"
        "fmt"
        "io"
+       "io/ioutil"
        "net/http"
        "net/url"
        "os"
@@ -103,22 +105,55 @@ type ArvadosClient struct {
        Retries int
 }
 
+var CertFiles = []string{
+       "/etc/arvados/ca-certificates.crt",
+       "/etc/ssl/certs/ca-certificates.crt", // Debian/Ubuntu/Gentoo etc.
+       "/etc/pki/tls/certs/ca-bundle.crt",   // Fedora/RHEL
+}
+
+// MakeTLSConfig sets up TLS configuration for communicating with Arvados and Keep services.
+func MakeTLSConfig(insecure bool) *tls.Config {
+       tlsconfig := tls.Config{InsecureSkipVerify: insecure}
+
+       if !insecure {
+               // Look for /etc/arvados/ca-certificates.crt in addition to normal system certs.
+               certs := x509.NewCertPool()
+               for _, file := range CertFiles {
+                       data, err := ioutil.ReadFile(file)
+                       if err == nil {
+                               success := certs.AppendCertsFromPEM(data)
+                               if !success {
+                                       fmt.Printf("Unable to load any certificates from %v", file)
+                               } else {
+                                       tlsconfig.RootCAs = certs
+                                       break
+                               }
+                       }
+               }
+               // Will use system default CA roots instead.
+       }
+
+       return &tlsconfig
+}
+
 // New returns an ArvadosClient using the given arvados.Client
 // configuration. This is useful for callers who load arvados.Client
 // fields from configuration files but still need to use the
 // arvadosclient.ArvadosClient package.
 func New(c *arvados.Client) (*ArvadosClient, error) {
-       return &ArvadosClient{
+       ac := &ArvadosClient{
                Scheme:      "https",
                ApiServer:   c.APIHost,
                ApiToken:    c.AuthToken,
                ApiInsecure: c.Insecure,
                Client: &http.Client{Transport: &http.Transport{
-                       TLSClientConfig: &tls.Config{InsecureSkipVerify: c.Insecure}}},
+                       TLSClientConfig: MakeTLSConfig(c.Insecure)}},
                External:          false,
                Retries:           2,
                lastClosedIdlesAt: time.Now(),
-       }, nil
+       }
+
+       return ac, nil
 }
 
 // MakeArvadosClient creates a new ArvadosClient using the standard
@@ -136,7 +171,7 @@ func MakeArvadosClient() (ac *ArvadosClient, err error) {
                ApiToken:    os.Getenv("ARVADOS_API_TOKEN"),
                ApiInsecure: insecure,
                Client: &http.Client{Transport: &http.Transport{
-                       TLSClientConfig: &tls.Config{InsecureSkipVerify: insecure}}},
+                       TLSClientConfig: MakeTLSConfig(insecure)}},
                External: external,
                Retries:  2}
 
index 9c65d65e84a57d9120d69dd84912615ff3949e35..2bbb440fb31211241a78a6f15c788f7e4d706334 100644 (file)
@@ -22,3 +22,8 @@ func LoadFile(cfg interface{}, configPath string) error {
        }
        return nil
 }
+
+// Dump returns a YAML representation of cfg.
+func Dump(cfg interface{}) ([]byte, error) {
+       return yaml.Marshal(cfg)
+}
index 5e0e101e7726b25d3791d137a7196d2b219782d6..5d7e10be4beb34fef1892b2d2d7c150fd9906176 100644 (file)
@@ -1,7 +1,6 @@
 package main
 
 import (
-       "crypto/x509"
        "encoding/json"
        "fmt"
        "git.curoverse.com/arvados.git/sdk/go/arvados"
@@ -10,7 +9,6 @@ import (
        "io"
        "io/ioutil"
        "log"
-       "net/http"
        "os"
        "os/exec"
        "os/signal"
@@ -396,24 +394,6 @@ func main() {
                log.Fatal(err)
        }
 
-       // Container may not have certificates installed, so need to look for
-       // /etc/arvados/ca-certificates.crt in addition to normal system certs.
-       var certFiles = []string{
-               "/etc/ssl/certs/ca-certificates.crt", // Debian
-               "/etc/pki/tls/certs/ca-bundle.crt",   // Red Hat
-               "/etc/arvados/ca-certificates.crt",
-       }
-
-       certs := x509.NewCertPool()
-       for _, file := range certFiles {
-               data, err := ioutil.ReadFile(file)
-               if err == nil {
-                       log.Printf("Using TLS certificates at %v", file)
-                       certs.AppendCertsFromPEM(data)
-               }
-       }
-       api.Client.Transport.(*http.Transport).TLSClientConfig.RootCAs = certs
-
        jobUuid := os.Getenv("JOB_UUID")
        taskUuid := os.Getenv("TASK_UUID")
        tmpdir := os.Getenv("TASK_WORK")
diff --git a/sdk/go/ctxlog/log.go b/sdk/go/ctxlog/log.go
new file mode 100644 (file)
index 0000000..6565c88
--- /dev/null
@@ -0,0 +1,59 @@
+package ctxlog
+
+import (
+       "context"
+
+       "github.com/Sirupsen/logrus"
+)
+
+var (
+       loggerCtxKey = new(int)
+       rootLogger   = logrus.New()
+)
+
+const rfc3339NanoFixed = "2006-01-02T15:04:05.000000000Z07:00"
+
+// Context returns a new child context such that FromContext(child)
+// returns the given logger.
+func Context(ctx context.Context, logger *logrus.Entry) context.Context {
+       return context.WithValue(ctx, loggerCtxKey, logger)
+}
+
+// FromContext returns the logger suitable for the given context -- the one
+// attached by contextWithLogger() if applicable, otherwise the
+// top-level logger with no fields/values.
+func FromContext(ctx context.Context) *logrus.Entry {
+       if ctx != nil {
+               if logger, ok := ctx.Value(loggerCtxKey).(*logrus.Entry); ok {
+                       return logger
+               }
+       }
+       return rootLogger.WithFields(nil)
+}
+
+// SetLevel sets the current logging level. See logrus for level
+// names.
+func SetLevel(level string) {
+       lvl, err := logrus.ParseLevel(level)
+       if err != nil {
+               logrus.Fatal(err)
+       }
+       rootLogger.Level = lvl
+}
+
+// SetFormat sets the current logging format to "json" or "text".
+func SetFormat(format string) {
+       switch format {
+       case "text":
+               rootLogger.Formatter = &logrus.TextFormatter{
+                       FullTimestamp:   true,
+                       TimestampFormat: rfc3339NanoFixed,
+               }
+       case "json":
+               rootLogger.Formatter = &logrus.JSONFormatter{
+                       TimestampFormat: rfc3339NanoFixed,
+               }
+       default:
+               logrus.WithField("LogFormat", format).Fatal("unknown log format")
+       }
+}
diff --git a/sdk/go/httpserver/id_generator.go b/sdk/go/httpserver/id_generator.go
new file mode 100644 (file)
index 0000000..c2830f7
--- /dev/null
@@ -0,0 +1,31 @@
+package httpserver
+
+import (
+       "strconv"
+       "sync"
+       "time"
+)
+
+// IDGenerator generates alphanumeric strings suitable for use as
+// unique IDs (a given IDGenerator will never return the same ID
+// twice).
+type IDGenerator struct {
+       // Prefix is prepended to each returned ID.
+       Prefix string
+
+       lastID int64
+       mtx    sync.Mutex
+}
+
+// Next returns a new ID string. It is safe to call Next from multiple
+// goroutines.
+func (g *IDGenerator) Next() string {
+       id := time.Now().UnixNano()
+       g.mtx.Lock()
+       if id <= g.lastID {
+               id = g.lastID + 1
+       }
+       g.lastID = id
+       g.mtx.Unlock()
+       return g.Prefix + strconv.FormatInt(id, 36)
+}
index 178ffb90f4facbebdfd6809bb1448e84904bc82f..ee35f4748b78ecfabac5c431ac5ad73340e4f300 100644 (file)
@@ -4,18 +4,42 @@ import (
        "net/http"
 )
 
+// RequestCounter is an http.Handler that tracks the number of
+// requests in progress.
+type RequestCounter interface {
+       http.Handler
+
+       // Current() returns the number of requests in progress.
+       Current() int
+
+       // Max() returns the maximum number of concurrent requests
+       // that will be accepted.
+       Max() int
+}
+
 type limiterHandler struct {
        requests chan struct{}
        handler  http.Handler
 }
 
-func NewRequestLimiter(maxRequests int, handler http.Handler) http.Handler {
+// NewRequestLimiter returns a RequestCounter that delegates up to
+// maxRequests at a time to the given handler, and responds 503 to all
+// incoming requests beyond that limit.
+func NewRequestLimiter(maxRequests int, handler http.Handler) RequestCounter {
        return &limiterHandler{
                requests: make(chan struct{}, maxRequests),
                handler:  handler,
        }
 }
 
+func (h *limiterHandler) Current() int {
+       return len(h.requests)
+}
+
+func (h *limiterHandler) Max() int {
+       return cap(h.requests)
+}
+
 func (h *limiterHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
        select {
        case h.requests <- struct{}{}:
index 58f3ffb8348ff7b5f9d9588e6455ae7c9e9ff18a..baf4bac02444170446c91a61c0b7469813bf308c 100644 (file)
@@ -4,7 +4,6 @@ package keepclient
 import (
        "bytes"
        "crypto/md5"
-       "crypto/tls"
        "errors"
        "fmt"
        "git.curoverse.com/arvados.git/sdk/go/arvadosclient"
@@ -103,7 +102,7 @@ func New(arv *arvadosclient.ArvadosClient) *KeepClient {
                Arvados:       arv,
                Want_replicas: defaultReplicationLevel,
                Client: &http.Client{Transport: &http.Transport{
-                       TLSClientConfig: &tls.Config{InsecureSkipVerify: arv.ApiInsecure}}},
+                       TLSClientConfig: arvadosclient.MakeTLSConfig(arv.ApiInsecure)}},
                Retries: 2,
        }
        return kc
@@ -168,6 +167,10 @@ func (kc *KeepClient) PutR(r io.Reader) (locator string, replicas int, err error
 }
 
 func (kc *KeepClient) getOrHead(method string, locator string) (io.ReadCloser, int64, string, error) {
+       if strings.HasPrefix(locator, "d41d8cd98f00b204e9800998ecf8427e+0") {
+               return ioutil.NopCloser(bytes.NewReader(nil)), 0, "", nil
+       }
+
        var errs []string
 
        tries_remaining := 1 + kc.Retries
index bd36d9d5e12f43b93567a5b30bd5a75c77d1e921..f0da600c24187f05cb3e8b2ab97512677c072794 100644 (file)
@@ -2,7 +2,6 @@ package keepclient
 
 import (
        "crypto/md5"
-       "flag"
        "fmt"
        "git.curoverse.com/arvados.git/sdk/go/arvadosclient"
        "git.curoverse.com/arvados.git/sdk/go/arvadostest"
@@ -28,8 +27,6 @@ func Test(t *testing.T) {
 var _ = Suite(&ServerRequiredSuite{})
 var _ = Suite(&StandaloneSuite{})
 
-var no_server = flag.Bool("no-server", false, "Skip 'ServerRequireSuite'")
-
 // Tests that require the Keep server running
 type ServerRequiredSuite struct{}
 
@@ -42,18 +39,11 @@ func pythonDir() string {
 }
 
 func (s *ServerRequiredSuite) SetUpSuite(c *C) {
-       if *no_server {
-               c.Skip("Skipping tests that require server")
-               return
-       }
        arvadostest.StartAPI()
        arvadostest.StartKeep(2, false)
 }
 
 func (s *ServerRequiredSuite) TearDownSuite(c *C) {
-       if *no_server {
-               return
-       }
        arvadostest.StopKeep(2)
        arvadostest.StopAPI()
 }
@@ -515,6 +505,27 @@ func (s *StandaloneSuite) TestGet404(c *C) {
        c.Check(r, Equals, nil)
 }
 
+func (s *StandaloneSuite) TestGetEmptyBlock(c *C) {
+       st := Error404Handler{make(chan string, 1)}
+
+       ks := RunFakeKeepServer(st)
+       defer ks.listener.Close()
+
+       arv, err := arvadosclient.MakeArvadosClient()
+       kc, _ := MakeKeepClient(arv)
+       arv.ApiToken = "abc123"
+       kc.SetServiceRoots(map[string]string{"x": ks.url}, nil, nil)
+
+       r, n, url2, err := kc.Get("d41d8cd98f00b204e9800998ecf8427e+0")
+       c.Check(err, IsNil)
+       c.Check(n, Equals, int64(0))
+       c.Check(url2, Equals, "")
+       c.Assert(r, NotNil)
+       buf, err := ioutil.ReadAll(r)
+       c.Check(err, IsNil)
+       c.Check(buf, DeepEquals, []byte{})
+}
+
 func (s *StandaloneSuite) TestGetFail(c *C) {
        hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
 
diff --git a/sdk/go/logger/logger.go b/sdk/go/logger/logger.go
deleted file mode 100644 (file)
index 6dd7fb3..0000000
+++ /dev/null
@@ -1,204 +0,0 @@
-// Logger periodically writes a log to the Arvados SDK.
-//
-// This package is useful for maintaining a log object that is updated
-// over time. This log object will be periodically written to the log,
-// as specified by WriteInterval in the Params.
-//
-// This package is safe for concurrent use as long as:
-// The maps passed to a LogMutator are not accessed outside of the
-// LogMutator
-//
-// Usage:
-// arvLogger := logger.NewLogger(params)
-// arvLogger.Update(func(properties map[string]interface{},
-//     entry map[string]interface{}) {
-//   // Modifiy properties and entry however you want
-//   // properties is a shortcut for entry["properties"].(map[string]interface{})
-//   // properties can take any (valid) values you want to give it,
-//   // entry will only take the fields listed at
-//   // http://doc.arvados.org/api/schema/Log.html
-//   // Valid values for properties are anything that can be json
-//   // encoded (i.e. will not error if you call json.Marshal() on it.
-// })
-package logger
-
-import (
-       "fmt"
-       "git.curoverse.com/arvados.git/sdk/go/arvadosclient"
-       "log"
-       "time"
-)
-
-const (
-       startSuffix              = "-start"
-       partialSuffix            = "-partial"
-       finalSuffix              = "-final"
-       numberNoMoreWorkMessages = 2 // To return from FinalUpdate() & Work().
-)
-
-type LoggerParams struct {
-       Client          *arvadosclient.ArvadosClient // The client we use to write log entries
-       EventTypePrefix string                       // The prefix we use for the event type in the log entry
-       WriteInterval   time.Duration                // Wait at least this long between log writes
-}
-
-// A LogMutator is a function which modifies the log entry.
-// It takes two maps as arguments, properties is the first and entry
-// is the second
-// properties is a shortcut for entry["properties"].(map[string]interface{})
-// properties can take any values you want to give it.
-// entry will only take the fields listed at http://doc.arvados.org/api/schema/Log.html
-// properties and entry are only safe to access inside the LogMutator,
-// they should not be stored anywhere, otherwise you'll risk
-// concurrent access.
-type LogMutator func(map[string]interface{}, map[string]interface{})
-
-// A Logger is used to build up a log entry over time and write every
-// version of it.
-type Logger struct {
-       // The data we write
-       data       map[string]interface{} // The entire map that we give to the api
-       entry      map[string]interface{} // Convenience shortcut into data
-       properties map[string]interface{} // Convenience shortcut into data
-
-       params LoggerParams // Parameters we were given
-
-       // Variables to coordinate updating and writing.
-       modified    bool            // Has this data been modified since the last write?
-       workToDo    chan LogMutator // Work to do in the worker thread.
-       writeTicker *time.Ticker    // On each tick we write the log data to arvados, if it has been modified.
-       hasWritten  bool            // Whether we've written at all yet.
-       noMoreWork  chan bool       // Signals that we're done writing.
-
-       writeHooks []LogMutator // Mutators we call before each write.
-}
-
-// Create a new logger based on the specified parameters.
-func NewLogger(params LoggerParams) (l *Logger, err error) {
-       // sanity check parameters
-       if &params.Client == nil {
-               err = fmt.Errorf("Nil arvados client in LoggerParams passed in to NewLogger()")
-               return
-       }
-       if params.EventTypePrefix == "" {
-               err = fmt.Errorf("Empty event type prefix in LoggerParams passed in to NewLogger()")
-               return
-       }
-
-       l = &Logger{
-               data:        make(map[string]interface{}),
-               entry:       make(map[string]interface{}),
-               properties:  make(map[string]interface{}),
-               params:      params,
-               workToDo:    make(chan LogMutator, 10),
-               writeTicker: time.NewTicker(params.WriteInterval),
-               noMoreWork:  make(chan bool, numberNoMoreWorkMessages)}
-
-       l.data["log"] = l.entry
-       l.entry["properties"] = l.properties
-
-       // Start the worker goroutine.
-       go l.work()
-
-       return l, nil
-}
-
-// Exported functions will be called from other goroutines, therefore
-// all they are allowed to do is enqueue work to be done in the worker
-// goroutine.
-
-// Enqueues an update. This will happen in another goroutine after
-// this method returns.
-func (l *Logger) Update(mutator LogMutator) {
-       l.workToDo <- mutator
-}
-
-// Similar to Update(), but writes the log entry as soon as possible
-// (ignoring MinimumWriteInterval) and blocks until the entry has been
-// written. This is useful if you know that you're about to quit
-// (e.g. if you discovered a fatal error, or you're finished), since
-// go will not wait for timers (including the pending write timer) to
-// go off before exiting.
-func (l *Logger) FinalUpdate(mutator LogMutator) {
-       // TODO(misha): Consider not accepting any future updates somehow,
-       // since they won't get written if they come in after this.
-
-       // Stop the periodic write ticker. We'll perform the final write
-       // before returning from this function.
-       l.workToDo <- func(p map[string]interface{}, e map[string]interface{}) {
-               l.writeTicker.Stop()
-       }
-
-       // Apply the final update
-       l.workToDo <- mutator
-
-       // Perform the final write and signal that we can return.
-       l.workToDo <- func(p map[string]interface{}, e map[string]interface{}) {
-               l.write(true)
-               for i := 0; i < numberNoMoreWorkMessages; {
-                       l.noMoreWork <- true
-               }
-       }
-
-       // Wait until we've performed the write.
-       <-l.noMoreWork
-}
-
-// Adds a hook which will be called every time this logger writes an entry.
-func (l *Logger) AddWriteHook(hook LogMutator) {
-       // We do the work in a LogMutator so that it happens in the worker
-       // goroutine.
-       l.workToDo <- func(p map[string]interface{}, e map[string]interface{}) {
-               l.writeHooks = append(l.writeHooks, hook)
-       }
-}
-
-// The worker loop
-func (l *Logger) work() {
-       for {
-               select {
-               case <-l.writeTicker.C:
-                       if l.modified {
-                               l.write(false)
-                               l.modified = false
-                       }
-               case mutator := <-l.workToDo:
-                       mutator(l.properties, l.entry)
-                       l.modified = true
-               case <-l.noMoreWork:
-                       return
-               }
-       }
-}
-
-// Actually writes the log entry.
-func (l *Logger) write(isFinal bool) {
-
-       // Run all our hooks
-       for _, hook := range l.writeHooks {
-               hook(l.properties, l.entry)
-       }
-
-       // Update the event type.
-       if isFinal {
-               l.entry["event_type"] = l.params.EventTypePrefix + finalSuffix
-       } else if l.hasWritten {
-               l.entry["event_type"] = l.params.EventTypePrefix + partialSuffix
-       } else {
-               l.entry["event_type"] = l.params.EventTypePrefix + startSuffix
-       }
-       l.hasWritten = true
-
-       // Write the log entry.
-       // This is a network write and will take a while, which is bad
-       // because we're blocking all the other work on this goroutine.
-       //
-       // TODO(misha): Consider rewriting this so that we can encode l.data
-       // into a string, and then perform the actual write in another
-       // routine. This will be tricky and will require support in the
-       // client.
-       err := l.params.Client.Create("logs", l.data, nil)
-       if err != nil {
-               log.Printf("Received error writing %v: %v", l.data, err)
-       }
-}
diff --git a/sdk/go/logger/util.go b/sdk/go/logger/util.go
deleted file mode 100644 (file)
index 6425aca..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-// Helper methods for interacting with Logger.
-package logger
-
-// Retrieves the map[string]interface{} stored at parent[key] if it
-// exists, otherwise it makes it and stores it there.
-// This is useful for logger because you may not know if a map you
-// need has already been created.
-func GetOrCreateMap(
-       parent map[string]interface{},
-       key string) (child map[string]interface{}) {
-       read, exists := parent[key]
-       if exists {
-               child = read.(map[string]interface{})
-
-       } else {
-               child = make(map[string]interface{})
-               parent[key] = child
-       }
-       return
-}
diff --git a/sdk/go/stats/duration.go b/sdk/go/stats/duration.go
new file mode 100644 (file)
index 0000000..103dea0
--- /dev/null
@@ -0,0 +1,35 @@
+package stats
+
+import (
+       "fmt"
+       "strconv"
+       "time"
+)
+
+// Duration is a duration that is displayed as a number of seconds in
+// fixed-point notation.
+type Duration time.Duration
+
+// MarshalJSON implements json.Marshaler.
+func (d Duration) MarshalJSON() ([]byte, error) {
+       return []byte(d.String()), nil
+}
+
+// String implements fmt.Stringer.
+func (d Duration) String() string {
+       return fmt.Sprintf("%.6f", time.Duration(d).Seconds())
+}
+
+// UnmarshalJSON implements json.Unmarshaler
+func (d *Duration) UnmarshalJSON(data []byte) error {
+       return d.Set(string(data))
+}
+
+// Value implements flag.Value
+func (d *Duration) Set(s string) error {
+       sec, err := strconv.ParseFloat(s, 64)
+       if err == nil {
+               *d = Duration(sec * float64(time.Second))
+       }
+       return err
+}
diff --git a/sdk/go/stats/duration_test.go b/sdk/go/stats/duration_test.go
new file mode 100644 (file)
index 0000000..730e646
--- /dev/null
@@ -0,0 +1,23 @@
+package stats
+
+import (
+       "testing"
+       "time"
+)
+
+func TestString(t *testing.T) {
+       d := Duration(123123123123 * time.Nanosecond)
+       if s, expect := d.String(), "123.123123"; s != expect {
+               t.Errorf("got %s, expect %s", s, expect)
+       }
+}
+
+func TestSet(t *testing.T) {
+       var d Duration
+       if err := d.Set("123.456"); err != nil {
+               t.Fatal(err)
+       }
+       if got, expect := time.Duration(d).Nanoseconds(), int64(123456000000); got != expect {
+               t.Errorf("got %d, expect %d", got, expect)
+       }
+}
diff --git a/sdk/go/util/util.go b/sdk/go/util/util.go
deleted file mode 100644 (file)
index ac510de..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-/* Helper methods for dealing with responses from API Server. */
-
-package util
-
-import (
-       "git.curoverse.com/arvados.git/sdk/go/arvadosclient"
-)
-
-func UserIsAdmin(arv *arvadosclient.ArvadosClient) (is_admin bool, err error) {
-       type user struct {
-               IsAdmin bool `json:"is_admin"`
-       }
-       var u user
-       err = arv.Call("GET", "users", "", "current", nil, &u)
-       return u.IsAdmin, err
-}
-
-// Returns the total count of a particular type of resource
-//
-//   resource - the arvados resource to count
-// return
-//   count - the number of items of type resource the api server reports, if no error
-//   err - error accessing the resource, or nil if no error
-func NumberItemsAvailable(client *arvadosclient.ArvadosClient, resource string) (count int, err error) {
-       var response struct {
-               ItemsAvailable int `json:"items_available"`
-       }
-       sdkParams := arvadosclient.Dict{"limit": 0}
-       err = client.List(resource, sdkParams, &response)
-       if err == nil {
-               count = response.ItemsAvailable
-       }
-       return
-}
diff --git a/sdk/python/arvados/_version.py b/sdk/python/arvados/_version.py
new file mode 100644 (file)
index 0000000..d823afc
--- /dev/null
@@ -0,0 +1,3 @@
+import pkg_resources
+
+__version__ = pkg_resources.require('arvados-python-client')[0].version
index 610fd7dc1317b6f0a6af7672d148766dbf9ce961..4cc2591ebb25034d0145de40c11f6638e3973864 100644 (file)
@@ -516,7 +516,7 @@ class _BlockManager(object):
                     return
                 self._keep.get(b)
             except Exception:
-                pass
+                _logger.exception("Exception doing block prefetch")
 
     @synchronized
     def start_get_threads(self):
@@ -759,6 +759,14 @@ class ArvadosFile(object):
     def writable(self):
         return self.parent.writable()
 
+    @synchronized
+    def permission_expired(self, as_of_dt=None):
+        """Returns True if any of the segment's locators is expired"""
+        for r in self._segments:
+            if KeepLocator(r.locator).permission_expired(as_of_dt):
+                return True
+        return False
+
     @synchronized
     def segments(self):
         return copy.copy(self._segments)
index 27aad033ae55523de4fd14ae7bf9cf8a7d2b654f..812438e2ccf493507d06ca9468e3c9418f9e0e69 100644 (file)
@@ -565,16 +565,23 @@ class RichCollectionBase(CollectionBase):
     def find(self, path):
         """Recursively search the specified file path.
 
-        May return either a Collection or ArvadosFile.  Return None if not
+        May return either a Collection or ArvadosFile. Return None if not
         found.
+        If path is invalid (ex: starts with '/'), an IOError exception will be
+        raised.
 
         """
         if not path:
             raise errors.ArgumentError("Parameter 'path' is empty.")
 
         pathcomponents = path.split("/", 1)
+        if pathcomponents[0] == '':
+            raise IOError(errno.ENOTDIR, "Not a directory", pathcomponents[0])
+
         item = self._items.get(pathcomponents[0])
-        if len(pathcomponents) == 1:
+        if item is None:
+            return None
+        elif len(pathcomponents) == 1:
             return item
         else:
             if isinstance(item, RichCollectionBase):
@@ -829,7 +836,7 @@ class RichCollectionBase(CollectionBase):
         if target_dir is None:
             raise IOError(errno.ENOENT, "Target directory not found", target_name)
 
-        if target_name in target_dir and isinstance(self[target_name], RichCollectionBase) and sourcecomponents:
+        if target_name in target_dir and isinstance(target_dir[target_name], RichCollectionBase) and sourcecomponents:
             target_dir = target_dir[target_name]
             target_name = sourcecomponents[-1]
 
index badbd668d951c46dd882b2468940463a17610728..5c5192860ccd0ed6b079c98d76b164c2ed3800a6 100755 (executable)
@@ -33,8 +33,10 @@ import arvados.keep
 import arvados.util
 import arvados.commands._util as arv_cmd
 import arvados.commands.keepdocker
+import ruamel.yaml as yaml
 
 from arvados.api import OrderedJsonModel
+from arvados._version import __version__
 
 COMMIT_HASH_RE = re.compile(r'^[0-9a-f]{1,40}$')
 
@@ -61,6 +63,9 @@ src_owner_uuid = None
 def main():
     copy_opts = argparse.ArgumentParser(add_help=False)
 
+    copy_opts.add_argument(
+        '--version', action='version', version="%s %s" % (sys.argv[0], __version__),
+        help='Print version and exit.')
     copy_opts.add_argument(
         '-v', '--verbose', dest='verbose', action='store_true',
         help='Verbose output.')
@@ -108,7 +113,7 @@ def main():
     copy_opts.set_defaults(recursive=True)
 
     parser = argparse.ArgumentParser(
-        description='Copy a pipeline instance, template or collection from one Arvados instance to another.',
+        description='Copy a pipeline instance, template, workflow, or collection from one Arvados instance to another.',
         parents=[copy_opts, arv_cmd.retry_opt])
     args = parser.parse_args()
 
@@ -140,6 +145,9 @@ def main():
         set_src_owner_uuid(src_arv.pipeline_templates(), args.object_uuid, args)
         result = copy_pipeline_template(args.object_uuid,
                                         src_arv, dst_arv, args)
+    elif t == 'Workflow':
+        set_src_owner_uuid(src_arv.workflows(), args.object_uuid, args)
+        result = copy_workflow(args.object_uuid, src_arv, dst_arv, args)
     else:
         abort("cannot copy object {} of type {}".format(args.object_uuid, t))
 
@@ -401,6 +409,64 @@ def copy_pipeline_template(pt_uuid, src, dst, args):
 
     return dst.pipeline_templates().create(body=pt, ensure_unique_name=True).execute(num_retries=args.retries)
 
+# copy_workflow(wf_uuid, src, dst, args)
+#
+#    Copies a workflow identified by wf_uuid from src to dst.
+#
+#    If args.recursive is True, also copy any collections
+#      referenced in the workflow definition yaml.
+#
+#    The owner_uuid of the new workflow is set to any given
+#      project_uuid or the user who copied the template.
+#
+#    Returns the copied workflow object.
+#
+def copy_workflow(wf_uuid, src, dst, args):
+    # fetch the workflow from the source instance
+    wf = src.workflows().get(uuid=wf_uuid).execute(num_retries=args.retries)
+
+    # copy collections and docker images
+    if args.recursive:
+        wf_def = yaml.safe_load(wf["definition"])
+        if wf_def is not None:
+            locations = []
+            docker_images = {}
+            graph = wf_def.get('$graph', None)
+            if graph is not None:
+                workflow_collections(graph, locations, docker_images)
+            else:
+                workflow_collections(wf_def, locations, docker_images)
+
+            if locations:
+                copy_collections(locations, src, dst, args)
+
+            for image in docker_images:
+                copy_docker_image(image, docker_images[image], src, dst, args)
+
+    # copy the workflow itself
+    del wf['uuid']
+    wf['owner_uuid'] = args.project_uuid
+    return dst.workflows().create(body=wf).execute(num_retries=args.retries)
+
+def workflow_collections(obj, locations, docker_images):
+    if isinstance(obj, dict):
+        loc = obj.get('location', None)
+        if loc is not None:
+            if loc.startswith("keep:"):
+                locations.append(loc[5:])
+
+        docker_image = obj.get('dockerImageId', None) or obj.get('dockerPull', None)
+        if docker_image is not None:
+            ds = docker_image.split(":", 1)
+            tag = ds[1] if len(ds)==2 else 'latest'
+            docker_images[ds[0]] = tag
+
+        for x in obj:
+            workflow_collections(obj[x], locations, docker_images)
+    elif isinstance(obj, list):
+        for x in obj:
+            workflow_collections(x, locations, docker_images)
+
 # copy_collections(obj, src, dst, args)
 #
 #    Recursively copies all collections referenced by 'obj' from src
index 9310f066219ae3063153e4a4393ecba771b7c6ff..3a0b64c38f4f543d5b52c8f96f4b38b03a4d741a 100644 (file)
@@ -21,6 +21,8 @@ import arvados.commands._util as arv_cmd
 import arvados.commands.put as arv_put
 import ciso8601
 
+from arvados._version import __version__
+
 EARLIEST_DATETIME = datetime.datetime(datetime.MINYEAR, 1, 1, 0, 0, 0)
 STAT_CACHE_ERRORS = (IOError, OSError, ValueError)
 
@@ -28,6 +30,9 @@ DockerImage = collections.namedtuple(
     'DockerImage', ['repo', 'tag', 'hash', 'created', 'vsize'])
 
 keepdocker_parser = argparse.ArgumentParser(add_help=False)
+keepdocker_parser.add_argument(
+    '--version', action='version', version="%s %s" % (sys.argv[0], __version__),
+    help='Print version and exit.')
 keepdocker_parser.add_argument(
     '-f', '--force', action='store_true', default=False,
     help="Re-upload the image even if it already exists on the server")
index e87244d7d12426d060bcb8a724445406448eb160..a2f2e542754f7e2e44edbd5673cf36d2c5d130af 100755 (executable)
@@ -3,10 +3,13 @@
 from __future__ import print_function
 
 import argparse
+import sys
 
 import arvados
 import arvados.commands._util as arv_cmd
 
+from arvados._version import __version__
+
 def parse_args(args):
     parser = argparse.ArgumentParser(
         description='List contents of a manifest',
@@ -16,6 +19,9 @@ def parse_args(args):
                         help="""Collection UUID or locator""")
     parser.add_argument('-s', action='store_true',
                         help="""List file sizes, in KiB.""")
+    parser.add_argument('--version', action='version',
+                        version="%s %s" % (sys.argv[0], __version__),
+                        help='Print version and exit.')
 
     return parser.parse_args(args)
 
index 34cef6725500e6cb5cd12ec317aa1be8eeb4bd80..714281cc95b0475831f1761470c9cf1b5e91cce5 100644 (file)
@@ -7,22 +7,24 @@ import argparse
 import arvados
 import arvados.collection
 import base64
+import copy
 import datetime
 import errno
 import fcntl
 import hashlib
 import json
+import logging
 import os
 import pwd
-import time
+import re
 import signal
 import socket
 import sys
 import tempfile
 import threading
-import copy
-import logging
+import time
 from apiclient import errors as apiclient_errors
+from arvados._version import __version__
 
 import arvados.commands._util as arv_cmd
 
@@ -31,6 +33,9 @@ api_client = None
 
 upload_opts = argparse.ArgumentParser(add_help=False)
 
+upload_opts.add_argument('--version', action='version',
+                         version="%s %s" % (sys.argv[0], __version__),
+                         help='Print version and exit.')
 upload_opts.add_argument('paths', metavar='path', type=str, nargs='*',
                          help="""
 Local file or directory. Default: read from standard input.
@@ -39,13 +44,7 @@ Local file or directory. Default: read from standard input.
 _group = upload_opts.add_mutually_exclusive_group()
 
 _group.add_argument('--max-manifest-depth', type=int, metavar='N',
-                    default=-1, help="""
-Maximum depth of directory tree to represent in the manifest
-structure. A directory structure deeper than this will be represented
-as a single stream in the manifest. If N=0, the manifest will contain
-a single stream. Default: -1 (unlimited), i.e., exactly one manifest
-stream per filesystem directory that contains files.
-""")
+                    default=-1, help=argparse.SUPPRESS)
 
 _group.add_argument('--normalize', action='store_true',
                     help="""
@@ -53,6 +52,12 @@ Normalize the manifest by re-ordering files and streams after writing
 data.
 """)
 
+_group.add_argument('--dry-run', action='store_true', default=False,
+                    help="""
+Don't actually upload files, but only check if any file should be
+uploaded. Exit with code=2 when files are pending for upload.
+""")
+
 _group = upload_opts.add_mutually_exclusive_group()
 
 _group.add_argument('--as-stream', action='store_true', dest='stream',
@@ -96,6 +101,12 @@ separated by commas, with a trailing newline. Do not store a
 manifest.
 """)
 
+upload_opts.add_argument('--update-collection', type=str, default=None,
+                         dest='update_collection', metavar="UUID", help="""
+Update an existing collection identified by the given Arvados collection
+UUID. All new local files will be uploaded.
+""")
+
 upload_opts.add_argument('--use-filename', type=str, default=None,
                          dest='filename', help="""
 Synonym for --filename.
@@ -163,6 +174,16 @@ _group.add_argument('--no-resume', action='store_false', dest='resume',
 Do not continue interrupted uploads from cached state.
 """)
 
+_group = run_opts.add_mutually_exclusive_group()
+_group.add_argument('--cache', action='store_true', dest='use_cache', default=True,
+                    help="""
+Save upload state in a cache file for resuming (default).
+""")
+_group.add_argument('--no-cache', action='store_false', dest='use_cache',
+                    help="""
+Do not save upload state in a cache file for resuming.
+""")
+
 arg_parser = argparse.ArgumentParser(
     description='Copy data from the local filesystem to Keep.',
     parents=[upload_opts, run_opts, arv_cmd.retry_opt])
@@ -187,17 +208,54 @@ def parse_arguments(arguments):
         and os.isatty(sys.stderr.fileno())):
         args.progress = True
 
+    # Turn off --resume (default) if --no-cache is used.
+    if not args.use_cache:
+        args.resume = False
+
     if args.paths == ['-']:
+        if args.update_collection:
+            arg_parser.error("""
+    --update-collection cannot be used when reading from stdin.
+    """)
         args.resume = False
+        args.use_cache = False
         if not args.filename:
             args.filename = 'stdin'
 
     return args
 
+
+class CollectionUpdateError(Exception):
+    pass
+
+
 class ResumeCacheConflict(Exception):
     pass
 
 
+class ArvPutArgumentConflict(Exception):
+    pass
+
+
+class ArvPutUploadIsPending(Exception):
+    pass
+
+
+class ArvPutUploadNotPending(Exception):
+    pass
+
+
+class FileUploadList(list):
+    def __init__(self, dry_run=False):
+        list.__init__(self)
+        self.dry_run = dry_run
+
+    def append(self, other):
+        if self.dry_run:
+            raise ArvPutUploadIsPending()
+        super(FileUploadList, self).append(other)
+
+
 class ResumeCache(object):
     CACHE_DIR = '.cache/arvados/arv-put'
 
@@ -213,7 +271,7 @@ class ResumeCache(object):
         realpaths = sorted(os.path.realpath(path) for path in args.paths)
         md5.update('\0'.join(realpaths))
         if any(os.path.isdir(path) for path in realpaths):
-            md5.update(str(max(args.max_manifest_depth, -1)))
+            md5.update("-1")
         elif args.filename:
             md5.update(args.filename)
         return os.path.join(
@@ -287,12 +345,15 @@ class ArvPutUploadJob(object):
         'files' : {} # Previous run file list: {path : {size, mtime}}
     }
 
-    def __init__(self, paths, resume=True, reporter=None, bytes_expected=None,
-                 name=None, owner_uuid=None, ensure_unique_name=False,
-                 num_retries=None, replication_desired=None,
-                 filename=None, update_time=1.0):
+    def __init__(self, paths, resume=True, use_cache=True, reporter=None,
+                 bytes_expected=None, name=None, owner_uuid=None,
+                 ensure_unique_name=False, num_retries=None, replication_desired=None,
+                 filename=None, update_time=20.0, update_collection=None,
+                 logger=logging.getLogger('arvados.arv_put'), dry_run=False):
         self.paths = paths
         self.resume = resume
+        self.use_cache = use_cache
+        self.update = False
         self.reporter = reporter
         self.bytes_expected = bytes_expected
         self.bytes_written = 0
@@ -307,51 +368,108 @@ class ArvPutUploadJob(object):
         self._state = None # Previous run state (file list & manifest)
         self._current_files = [] # Current run file list
         self._cache_file = None
-        self._collection = None
         self._collection_lock = threading.Lock()
+        self._remote_collection = None # Collection being updated (if asked)
+        self._local_collection = None # Collection from previous run manifest
+        self._file_paths = [] # Files to be updated in remote collection
         self._stop_checkpointer = threading.Event()
         self._checkpointer = threading.Thread(target=self._update_task)
+        self._checkpointer.daemon = True
         self._update_task_time = update_time  # How many seconds wait between update runs
-        self.logger = logging.getLogger('arvados.arv_put')
+        self._files_to_upload = FileUploadList(dry_run=dry_run)
+        self.logger = logger
+        self.dry_run = dry_run
+
+        if not self.use_cache and self.resume:
+            raise ArvPutArgumentConflict('resume cannot be True when use_cache is False')
+
+        # Check for obvious dry-run responses
+        if self.dry_run and (not self.use_cache or not self.resume):
+            raise ArvPutUploadIsPending()
+
         # Load cached data if any and if needed
-        self._setup_state()
+        self._setup_state(update_collection)
 
-    def start(self):
+    def start(self, save_collection):
         """
         Start supporting thread & file uploading
         """
-        self._checkpointer.daemon = True
-        self._checkpointer.start()
+        if not self.dry_run:
+            self._checkpointer.start()
         try:
             for path in self.paths:
                 # Test for stdin first, in case some file named '-' exist
                 if path == '-':
+                    if self.dry_run:
+                        raise ArvPutUploadIsPending()
                     self._write_stdin(self.filename or 'stdin')
                 elif os.path.isdir(path):
-                    self._write_directory_tree(path)
+                    # Use absolute paths on cache index so CWD doesn't interfere
+                    # with the caching logic.
+                    prefixdir = path = os.path.abspath(path)
+                    if prefixdir != '/':
+                        prefixdir += '/'
+                    for root, dirs, files in os.walk(path):
+                        # Make os.walk()'s dir traversing order deterministic
+                        dirs.sort()
+                        files.sort()
+                        for f in files:
+                            self._check_file(os.path.join(root, f),
+                                             os.path.join(root[len(prefixdir):], f))
                 else:
-                    self._write_file(path, self.filename or os.path.basename(path))
-        finally:
-            # Stop the thread before doing anything else
-            self._stop_checkpointer.set()
-            self._checkpointer.join()
-            # Commit all & one last _update()
-            self.manifest_text()
+                    self._check_file(os.path.abspath(path),
+                                     self.filename or os.path.basename(path))
+            # If dry-mode is on, and got up to this point, then we should notify that
+            # there aren't any file to upload.
+            if self.dry_run:
+                raise ArvPutUploadNotPending()
+            # Remove local_collection's files that don't exist locally anymore, so the
+            # bytes_written count is correct.
+            for f in self.collection_file_paths(self._local_collection,
+                                                path_prefix=""):
+                if f != 'stdin' and f != self.filename and not f in self._file_paths:
+                    self._local_collection.remove(f)
+            # Update bytes_written from current local collection and
+            # report initial progress.
             self._update()
-            if self.resume:
+            # Actual file upload
+            self._upload_files()
+        finally:
+            if not self.dry_run:
+                # Stop the thread before doing anything else
+                self._stop_checkpointer.set()
+                self._checkpointer.join()
+                # Commit all pending blocks & one last _update()
+                self._local_collection.manifest_text()
+                self._update(final=True)
+                if save_collection:
+                    self.save_collection()
+            if self.use_cache:
                 self._cache_file.close()
-                # Correct the final written bytes count
-                self.bytes_written -= self.bytes_skipped
 
     def save_collection(self):
-        with self._collection_lock:
-            self._my_collection().save_new(
+        if self.update:
+            # Check if files should be updated on the remote collection.
+            for fp in self._file_paths:
+                remote_file = self._remote_collection.find(fp)
+                if not remote_file:
+                    # File don't exist on remote collection, copy it.
+                    self._remote_collection.copy(fp, fp, self._local_collection)
+                elif remote_file != self._local_collection.find(fp):
+                    # A different file exist on remote collection, overwrite it.
+                    self._remote_collection.copy(fp, fp, self._local_collection, overwrite=True)
+                else:
+                    # The file already exist on remote collection, skip it.
+                    pass
+            self._remote_collection.save(num_retries=self.num_retries)
+        else:
+            self._local_collection.save_new(
                 name=self.name, owner_uuid=self.owner_uuid,
                 ensure_unique_name=self.ensure_unique_name,
                 num_retries=self.num_retries)
 
     def destroy_cache(self):
-        if self.resume:
+        if self.use_cache:
             try:
                 os.unlink(self._cache_filename)
             except OSError as error:
@@ -380,17 +498,20 @@ class ArvPutUploadJob(object):
         while not self._stop_checkpointer.wait(self._update_task_time):
             self._update()
 
-    def _update(self):
+    def _update(self, final=False):
         """
         Update cached manifest text and report progress.
         """
         with self._collection_lock:
-            self.bytes_written = self._collection_size(self._my_collection())
-            # Update cache, if resume enabled
-            if self.resume:
+            self.bytes_written = self._collection_size(self._local_collection)
+            if self.use_cache:
+                # Update cache
                 with self._state_lock:
-                    # Get the manifest text without comitting pending blocks
-                    self._state['manifest'] = self._my_collection()._get_manifest_text(".", strip=False, normalize=False, only_committed=True)
+                    if final:
+                        self._state['manifest'] = self._local_collection.manifest_text()
+                    else:
+                        # Get the manifest text without comitting pending blocks
+                        self._state['manifest'] = self._local_collection._get_manifest_text(".", strip=False, normalize=False, only_committed=True)
                 self._save_state()
         # Call the reporter, if any
         self.report_progress()
@@ -399,114 +520,116 @@ class ArvPutUploadJob(object):
         if self.reporter is not None:
             self.reporter(self.bytes_written, self.bytes_expected)
 
-    def _write_directory_tree(self, path, stream_name="."):
-        # TODO: Check what happens when multiple directories are passed as
-        # arguments.
-        # If the code below is uncommented, integration test
-        # test_ArvPutSignedManifest (tests.test_arv_put.ArvPutIntegrationTest)
-        # fails, I suppose it is because the manifest_uuid changes because
-        # of the dir addition to stream_name.
-
-        # if stream_name == '.':
-        #     stream_name = os.path.join('.', os.path.basename(path))
-        for item in os.listdir(path):
-            if os.path.isdir(os.path.join(path, item)):
-                self._write_directory_tree(os.path.join(path, item),
-                                os.path.join(stream_name, item))
-            else:
-                self._write_file(os.path.join(path, item),
-                                os.path.join(stream_name, item))
-
     def _write_stdin(self, filename):
-        with self._collection_lock:
-            output = self._my_collection().open(filename, 'w')
+        output = self._local_collection.open(filename, 'w')
         self._write(sys.stdin, output)
         output.close()
 
-    def _write_file(self, source, filename):
+    def _check_file(self, source, filename):
+        """Check if this file needs to be uploaded"""
         resume_offset = 0
-        if self.resume:
-            # Check if file was already uploaded (at least partially)
-            with self._collection_lock:
-                try:
-                    file_in_collection = self._my_collection().find(filename)
-                except IOError:
-                    # Not found
-                    file_in_collection = None
+        should_upload = False
+        new_file_in_cache = False
+        # Record file path for updating the remote collection before exiting
+        self._file_paths.append(filename)
+
+        with self._state_lock:
             # If no previous cached data on this file, store it for an eventual
             # repeated run.
             if source not in self._state['files']:
-                with self._state_lock:
-                    self._state['files'][source] = {
-                        'mtime': os.path.getmtime(source),
-                        'size' : os.path.getsize(source)
-                    }
-            with self._state_lock:
-                cached_file_data = self._state['files'][source]
-            # See if this file was already uploaded at least partially
-            if file_in_collection:
-                if cached_file_data['mtime'] == os.path.getmtime(source) and cached_file_data['size'] == os.path.getsize(source):
-                    if cached_file_data['size'] == file_in_collection.size():
-                        # File already there, skip it.
-                        self.bytes_skipped += cached_file_data['size']
-                        return
-                    elif cached_file_data['size'] > file_in_collection.size():
-                        # File partially uploaded, resume!
-                        resume_offset = file_in_collection.size()
-                    else:
-                        # Inconsistent cache, re-upload the file
-                        self.logger.warning("Uploaded version of file '{}' is bigger than local version, will re-upload it from scratch.".format(source))
-                else:
-                    # Local file differs from cached data, re-upload it
-                    pass
-        with open(source, 'r') as source_fd:
-            if resume_offset > 0:
-                # Start upload where we left off
-                with self._collection_lock:
-                    output = self._my_collection().open(filename, 'a')
-                source_fd.seek(resume_offset)
+                self._state['files'][source] = {
+                    'mtime': os.path.getmtime(source),
+                    'size' : os.path.getsize(source)
+                }
+                new_file_in_cache = True
+            cached_file_data = self._state['files'][source]
+
+        # Check if file was already uploaded (at least partially)
+        file_in_local_collection = self._local_collection.find(filename)
+
+        # If not resuming, upload the full file.
+        if not self.resume:
+            should_upload = True
+        # New file detected from last run, upload it.
+        elif new_file_in_cache:
+            should_upload = True
+        # Local file didn't change from last run.
+        elif cached_file_data['mtime'] == os.path.getmtime(source) and cached_file_data['size'] == os.path.getsize(source):
+            if not file_in_local_collection:
+                # File not uploaded yet, upload it completely
+                should_upload = True
+            elif file_in_local_collection.permission_expired():
+                # Permission token expired, re-upload file. This will change whenever
+                # we have a API for refreshing tokens.
+                should_upload = True
+                self._local_collection.remove(filename)
+            elif cached_file_data['size'] == file_in_local_collection.size():
+                # File already there, skip it.
+                self.bytes_skipped += cached_file_data['size']
+            elif cached_file_data['size'] > file_in_local_collection.size():
+                # File partially uploaded, resume!
+                resume_offset = file_in_local_collection.size()
                 self.bytes_skipped += resume_offset
+                should_upload = True
             else:
-                # Start from scratch
-                with self._collection_lock:
-                    output = self._my_collection().open(filename, 'w')
-            self._write(source_fd, output)
-            output.close(flush=False)
+                # Inconsistent cache, re-upload the file
+                should_upload = True
+                self._local_collection.remove(filename)
+                self.logger.warning("Uploaded version of file '{}' is bigger than local version, will re-upload it from scratch.".format(source))
+        # Local file differs from cached data, re-upload it.
+        else:
+            if file_in_local_collection:
+                self._local_collection.remove(filename)
+            should_upload = True
+
+        if should_upload:
+            self._files_to_upload.append((source, resume_offset, filename))
+
+    def _upload_files(self):
+        for source, resume_offset, filename in self._files_to_upload:
+            with open(source, 'r') as source_fd:
+                with self._state_lock:
+                    self._state['files'][source]['mtime'] = os.path.getmtime(source)
+                    self._state['files'][source]['size'] = os.path.getsize(source)
+                if resume_offset > 0:
+                    # Start upload where we left off
+                    output = self._local_collection.open(filename, 'a')
+                    source_fd.seek(resume_offset)
+                else:
+                    # Start from scratch
+                    output = self._local_collection.open(filename, 'w')
+                self._write(source_fd, output)
+                output.close(flush=False)
 
     def _write(self, source_fd, output):
-        first_read = True
         while True:
             data = source_fd.read(arvados.config.KEEP_BLOCK_SIZE)
-            # Allow an empty file to be written
-            if not data and not first_read:
+            if not data:
                 break
-            if first_read:
-                first_read = False
             output.write(data)
 
     def _my_collection(self):
-        """
-        Create a new collection if none cached. Load it from cache otherwise.
-        """
-        if self._collection is None:
-            with self._state_lock:
-                manifest = self._state['manifest']
-            if self.resume and manifest is not None:
-                # Create collection from saved state
-                self._collection = arvados.collection.Collection(
-                    manifest,
-                    replication_desired=self.replication_desired)
-            else:
-                # Create new collection
-                self._collection = arvados.collection.Collection(
-                    replication_desired=self.replication_desired)
-        return self._collection
+        return self._remote_collection if self.update else self._local_collection
 
-    def _setup_state(self):
+    def _setup_state(self, update_collection):
         """
         Create a new cache file or load a previously existing one.
         """
-        if self.resume:
+        # Load an already existing collection for update
+        if update_collection and re.match(arvados.util.collection_uuid_pattern,
+                                          update_collection):
+            try:
+                self._remote_collection = arvados.collection.Collection(update_collection)
+            except arvados.errors.ApiError as error:
+                raise CollectionUpdateError("Cannot read collection {} ({})".format(update_collection, error))
+            else:
+                self.update = True
+        elif update_collection:
+            # Collection locator provided, but unknown format
+            raise CollectionUpdateError("Collection locator unknown: '{}'".format(update_collection))
+
+        if self.use_cache:
+            # Set up cache file name from input paths.
             md5 = hashlib.md5()
             md5.update(arvados.config.get('ARVADOS_API_HOST', '!nohost'))
             realpaths = sorted(os.path.realpath(path) for path in self.paths)
@@ -514,13 +637,20 @@ class ArvPutUploadJob(object):
             if self.filename:
                 md5.update(self.filename)
             cache_filename = md5.hexdigest()
-            self._cache_file = open(os.path.join(
+            cache_filepath = os.path.join(
                 arv_cmd.make_home_conf_dir(self.CACHE_DIR, 0o700, 'raise'),
-                cache_filename), 'a+')
+                cache_filename)
+            if self.resume:
+                self._cache_file = open(cache_filepath, 'a+')
+            else:
+                # --no-resume means start with a empty cache file.
+                self._cache_file = open(cache_filepath, 'w+')
             self._cache_filename = self._cache_file.name
             self._lock_file(self._cache_file)
             self._cache_file.seek(0)
-            with self._state_lock:
+
+        with self._state_lock:
+            if self.use_cache:
                 try:
                     self._state = json.load(self._cache_file)
                     if not set(['manifest', 'files']).issubset(set(self._state.keys())):
@@ -529,13 +659,22 @@ class ArvPutUploadJob(object):
                 except ValueError:
                     # Cache file empty, set up new cache
                     self._state = copy.deepcopy(self.EMPTY_STATE)
-            # Load how many bytes were uploaded on previous run
-            with self._collection_lock:
-                self.bytes_written = self._collection_size(self._my_collection())
-        # No resume required
-        else:
-            with self._state_lock:
+            else:
+                # No cache file, set empty state
                 self._state = copy.deepcopy(self.EMPTY_STATE)
+            # Load the previous manifest so we can check if files were modified remotely.
+            self._local_collection = arvados.collection.Collection(self._state['manifest'], replication_desired=self.replication_desired)
+
+    def collection_file_paths(self, col, path_prefix='.'):
+        """Return a list of file paths by recursively go through the entire collection `col`"""
+        file_paths = []
+        for name, item in col.items():
+            if isinstance(item, arvados.arvfile.ArvadosFile):
+                file_paths.append(os.path.join(path_prefix, name))
+            elif isinstance(item, arvados.collection.Subcollection):
+                new_prefix = os.path.join(path_prefix, name)
+                file_paths += self.collection_file_paths(item, path_prefix=new_prefix)
+        return file_paths
 
     def _lock_file(self, fileobj):
         try:
@@ -549,7 +688,7 @@ class ArvPutUploadJob(object):
         """
         try:
             with self._state_lock:
-                state = self._state
+                state = copy.deepcopy(self._state)
             new_cache_fd, new_cache_name = tempfile.mkstemp(
                 dir=os.path.dirname(self._cache_filename))
             self._lock_file(new_cache_fd)
@@ -569,24 +708,16 @@ class ArvPutUploadJob(object):
             self._cache_file = new_cache
 
     def collection_name(self):
-        with self._collection_lock:
-            name = self._my_collection().api_response()['name'] if self._my_collection().api_response() else None
-        return name
+        return self._my_collection().api_response()['name'] if self._my_collection().api_response() else None
 
     def manifest_locator(self):
-        with self._collection_lock:
-            locator = self._my_collection().manifest_locator()
-        return locator
+        return self._my_collection().manifest_locator()
 
     def portable_data_hash(self):
-        with self._collection_lock:
-            datahash = self._my_collection().portable_data_hash()
-        return datahash
+        return self._my_collection().portable_data_hash()
 
     def manifest_text(self, stream_name=".", strip=False, normalize=False):
-        with self._collection_lock:
-            manifest = self._my_collection().manifest_text(stream_name, strip, normalize)
-        return manifest
+        return self._my_collection().manifest_text(stream_name, strip, normalize)
 
     def _datablocks_on_item(self, item):
         """
@@ -669,6 +800,7 @@ def desired_project_uuid(api_client, project_uuid, num_retries):
 def main(arguments=None, stdout=sys.stdout, stderr=sys.stderr):
     global api_client
 
+    logger = logging.getLogger('arvados.arv_put')
     args = parse_arguments(arguments)
     status = 0
     if api_client is None:
@@ -677,7 +809,10 @@ def main(arguments=None, stdout=sys.stdout, stderr=sys.stderr):
     # Determine the name to use
     if args.name:
         if args.stream or args.raw:
-            print >>stderr, "Cannot use --name with --stream or --raw"
+            logger.error("Cannot use --name with --stream or --raw")
+            sys.exit(1)
+        elif args.update_collection:
+            logger.error("Cannot use --name with --update-collection")
             sys.exit(1)
         collection_name = args.name
     else:
@@ -687,7 +822,7 @@ def main(arguments=None, stdout=sys.stdout, stderr=sys.stderr):
             socket.gethostname())
 
     if args.project_uuid and (args.stream or args.raw):
-        print >>stderr, "Cannot use --project-uuid with --stream or --raw"
+        logger.error("Cannot use --project-uuid with --stream or --raw")
         sys.exit(1)
 
     # Determine the parent project
@@ -695,7 +830,7 @@ def main(arguments=None, stdout=sys.stdout, stderr=sys.stderr):
         project_uuid = desired_project_uuid(api_client, args.project_uuid,
                                             args.retries)
     except (apiclient_errors.Error, ValueError) as error:
-        print >>stderr, error
+        logger.error(error)
         sys.exit(1)
 
     if args.progress:
@@ -706,9 +841,11 @@ def main(arguments=None, stdout=sys.stdout, stderr=sys.stderr):
         reporter = None
 
     bytes_expected = expected_bytes_for(args.paths)
+
     try:
         writer = ArvPutUploadJob(paths = args.paths,
                                  resume = args.resume,
+                                 use_cache = args.use_cache,
                                  filename = args.filename,
                                  reporter = reporter,
                                  bytes_expected = bytes_expected,
@@ -716,28 +853,54 @@ def main(arguments=None, stdout=sys.stdout, stderr=sys.stderr):
                                  replication_desired = args.replication,
                                  name = collection_name,
                                  owner_uuid = project_uuid,
-                                 ensure_unique_name = True)
+                                 ensure_unique_name = True,
+                                 update_collection = args.update_collection,
+                                 logger=logger,
+                                 dry_run=args.dry_run)
     except ResumeCacheConflict:
-        print >>stderr, "\n".join([
+        logger.error("\n".join([
             "arv-put: Another process is already uploading this data.",
-            "         Use --no-resume if this is really what you want."])
+            "         Use --no-cache if this is really what you want."]))
         sys.exit(1)
+    except CollectionUpdateError as error:
+        logger.error("\n".join([
+            "arv-put: %s" % str(error)]))
+        sys.exit(1)
+    except ArvPutUploadIsPending:
+        # Dry run check successful, return proper exit code.
+        sys.exit(2)
+    except ArvPutUploadNotPending:
+        # No files pending for upload
+        sys.exit(0)
 
     # Install our signal handler for each code in CAUGHT_SIGNALS, and save
     # the originals.
     orig_signal_handlers = {sigcode: signal.signal(sigcode, exit_signal_handler)
                             for sigcode in CAUGHT_SIGNALS}
 
-    if args.resume and writer.bytes_written > 0:
-        print >>stderr, "\n".join([
-                "arv-put: Resuming previous upload from last checkpoint.",
-                "         Use the --no-resume option to start over."])
+    if not args.dry_run and not args.update_collection and args.resume and writer.bytes_written > 0:
+        logger.warning("\n".join([
+            "arv-put: Resuming previous upload from last checkpoint.",
+            "         Use the --no-resume option to start over."]))
 
-    writer.report_progress()
+    if not args.dry_run:
+        writer.report_progress()
     output = None
-    writer.start()
+    try:
+        writer.start(save_collection=not(args.stream or args.raw))
+    except arvados.errors.ApiError as error:
+        logger.error("\n".join([
+            "arv-put: %s" % str(error)]))
+        sys.exit(1)
+    except ArvPutUploadIsPending:
+        # Dry run check successful, return proper exit code.
+        sys.exit(2)
+    except ArvPutUploadNotPending:
+        # No files pending for upload
+        sys.exit(0)
+
     if args.progress:  # Print newline to split stderr from stdout for humans.
-        print >>stderr
+        logger.info("\n")
 
     if args.stream:
         if args.normalize:
@@ -748,14 +911,16 @@ def main(arguments=None, stdout=sys.stdout, stderr=sys.stderr):
         output = ','.join(writer.data_locators())
     else:
         try:
-            writer.save_collection()
-            print >>stderr, "Collection saved as '%s'" % writer.collection_name()
+            if args.update_collection:
+                logger.info("Collection updated: '{}'".format(writer.collection_name()))
+            else:
+                logger.info("Collection saved as '{}'".format(writer.collection_name()))
             if args.portable_data_hash:
                 output = writer.portable_data_hash()
             else:
                 output = writer.manifest_locator()
         except apiclient_errors.Error as error:
-            print >>stderr, (
+            logger.error(
                 "arv-put: Error creating Collection on project: {}.".format(
                     error))
             status = 1
@@ -775,7 +940,6 @@ def main(arguments=None, stdout=sys.stdout, stderr=sys.stderr):
         sys.exit(status)
 
     # Success!
-    writer.destroy_cache()
     return output
 
 
index 54df452394e47bc7b44437bf580a3af2dc17b36e..860fc8f805718bb5330f1eb25f4499f8fd45fcf3 100644 (file)
@@ -11,22 +11,38 @@ import put
 import time
 import subprocess
 import logging
+import sys
 import arvados.commands._util as arv_cmd
 
+from arvados._version import __version__
+
 logger = logging.getLogger('arvados.arv-run')
 logger.setLevel(logging.INFO)
 
 arvrun_parser = argparse.ArgumentParser(parents=[arv_cmd.retry_opt])
-arvrun_parser.add_argument('--dry-run', action="store_true", help="Print out the pipeline that would be submitted and exit")
-arvrun_parser.add_argument('--local', action="store_true", help="Run locally using arv-run-pipeline-instance")
-arvrun_parser.add_argument('--docker-image', type=str, help="Docker image to use, otherwise use instance default.")
-arvrun_parser.add_argument('--ignore-rcode', action="store_true", help="Commands that return non-zero return codes should not be considered failed.")
-arvrun_parser.add_argument('--no-reuse', action="store_true", help="Do not reuse past jobs.")
-arvrun_parser.add_argument('--no-wait', action="store_true", help="Do not wait and display logs after submitting command, just exit.")
-arvrun_parser.add_argument('--project-uuid', type=str, help="Parent project of the pipeline")
-arvrun_parser.add_argument('--git-dir', type=str, default="", help="Git repository passed to arv-crunch-job when using --local")
-arvrun_parser.add_argument('--repository', type=str, default="arvados", help="repository field of component, default 'arvados'")
-arvrun_parser.add_argument('--script-version', type=str, default="master", help="script_version field of component, default 'master'")
+arvrun_parser.add_argument('--dry-run', action="store_true",
+                           help="Print out the pipeline that would be submitted and exit")
+arvrun_parser.add_argument('--local', action="store_true",
+                           help="Run locally using arv-run-pipeline-instance")
+arvrun_parser.add_argument('--docker-image', type=str,
+                           help="Docker image to use, otherwise use instance default.")
+arvrun_parser.add_argument('--ignore-rcode', action="store_true",
+                           help="Commands that return non-zero return codes should not be considered failed.")
+arvrun_parser.add_argument('--no-reuse', action="store_true",
+                           help="Do not reuse past jobs.")
+arvrun_parser.add_argument('--no-wait', action="store_true",
+                           help="Do not wait and display logs after submitting command, just exit.")
+arvrun_parser.add_argument('--project-uuid', type=str,
+                           help="Parent project of the pipeline")
+arvrun_parser.add_argument('--git-dir', type=str, default="",
+                           help="Git repository passed to arv-crunch-job when using --local")
+arvrun_parser.add_argument('--repository', type=str, default="arvados",
+                           help="repository field of component, default 'arvados'")
+arvrun_parser.add_argument('--script-version', type=str, default="master",
+                           help="script_version field of component, default 'master'")
+arvrun_parser.add_argument('--version', action='version',
+                           version="%s %s" % (sys.argv[0], __version__),
+                           help='Print version and exit.')
 arvrun_parser.add_argument('args', nargs=argparse.REMAINDER)
 
 class ArvFile(object):
@@ -155,9 +171,13 @@ def uploadfiles(files, api, dry_run=False, num_retries=0, project=None, fnPatter
                 collection.start_new_stream(stream)
             collection.write_file(f.fn, sp[1])
 
-        exists = api.collections().list(filters=[["owner_uuid", "=", project],
-                                                 ["portable_data_hash", "=", collection.portable_data_hash()],
-                                                 ["name", "=", name]]).execute(num_retries=num_retries)
+        filters=[["portable_data_hash", "=", collection.portable_data_hash()],
+                 ["name", "like", name+"%"]]
+        if project:
+            filters.append(["owner_uuid", "=", project])
+
+        exists = api.collections().list(filters=filters).execute(num_retries=num_retries)
+
         if exists["items"]:
             item = exists["items"][0]
             logger.info("Using collection %s", item["uuid"])
index f6dee177d9a6b1e5a69e44d1edefd396280f0ed7..72ef1befed85ffd4d8b883270ebefa0a3bcd3dac 100644 (file)
@@ -6,12 +6,16 @@ import argparse
 import arvados
 import json
 from arvados.events import subscribe
+from arvados._version import __version__
 import signal
 
 def main(arguments=None):
     logger = logging.getLogger('arvados.arv-ws')
 
     parser = argparse.ArgumentParser()
+    parser.add_argument('--version', action='version',
+                        version="%s %s" % (sys.argv[0], __version__),
+                        help='Print version and exit.')
     parser.add_argument('-u', '--uuid', type=str, default="", help="Filter events on object_uuid")
     parser.add_argument('-f', '--filters', type=str, default="", help="Arvados query filter to apply to log events (JSON encoded)")
     parser.add_argument('-s', '--start-time', type=str, default="", help="Arvados query filter to fetch log events created at or after this time. This will be server time in UTC. Allowed format: YYYY-MM-DD or YYYY-MM-DD hh:mm:ss")
index db7835be3746f8f67eddd61d2aac505356e601f4..38f332b38e2d51aae9b6a3fa5007a59aad6b006f 100644 (file)
@@ -296,14 +296,14 @@ class KeepClient(object):
 
         def _get_user_agent(self):
             try:
-                return self._user_agent_pool.get(False)
+                return self._user_agent_pool.get(block=False)
             except Queue.Empty:
                 return pycurl.Curl()
 
         def _put_user_agent(self, ua):
             try:
                 ua.reset()
-                self._user_agent_pool.put(ua, False)
+                self._user_agent_pool.put(ua, block=False)
             except:
                 ua.close()
 
@@ -511,8 +511,10 @@ class KeepClient(object):
             with self.successful_copies_lock:
                 self.successful_copies += replicas_nr
                 self.response = response
+            with self.pending_tries_notification:
+                self.pending_tries_notification.notify_all()
         
-        def write_fail(self, ks, status_code):
+        def write_fail(self, ks):
             with self.pending_tries_notification:
                 self.pending_tries += 1
                 self.pending_tries_notification.notify()
@@ -520,8 +522,36 @@ class KeepClient(object):
         def pending_copies(self):
             with self.successful_copies_lock:
                 return self.wanted_copies - self.successful_copies
-    
-    
+
+        def get_next_task(self):
+            with self.pending_tries_notification:
+                while True:
+                    if self.pending_copies() < 1:
+                        # This notify_all() is unnecessary --
+                        # write_success() already called notify_all()
+                        # when pending<1 became true, so it's not
+                        # possible for any other thread to be in
+                        # wait() now -- but it's cheap insurance
+                        # against deadlock so we do it anyway:
+                        self.pending_tries_notification.notify_all()
+                        # Drain the queue and then raise Queue.Empty
+                        while True:
+                            self.get_nowait()
+                            self.task_done()
+                    elif self.pending_tries > 0:
+                        service, service_root = self.get_nowait()
+                        if service.finished():
+                            self.task_done()
+                            continue
+                        self.pending_tries -= 1
+                        return service, service_root
+                    elif self.empty():
+                        self.pending_tries_notification.notify_all()
+                        raise Queue.Empty
+                    else:
+                        self.pending_tries_notification.wait()
+
+
     class KeepWriterThreadPool(object):
         def __init__(self, data, data_hash, copies, max_service_replicas, timeout=None):
             self.total_task_nr = 0
@@ -551,74 +581,64 @@ class KeepClient(object):
                 worker.start()
             # Wait for finished work
             self.queue.join()
-            with self.queue.pending_tries_notification:
-                self.queue.pending_tries_notification.notify_all()
-            for worker in self.workers:
-                worker.join()
         
         def response(self):
             return self.queue.response
     
     
     class KeepWriterThread(threading.Thread):
+        TaskFailed = RuntimeError()
+
         def __init__(self, queue, data, data_hash, timeout=None):
             super(KeepClient.KeepWriterThread, self).__init__()
             self.timeout = timeout
             self.queue = queue
             self.data = data
             self.data_hash = data_hash
-        
+            self.daemon = True
+
         def run(self):
-            while not self.queue.empty():
-                if self.queue.pending_copies() > 0:
-                    # Avoid overreplication, wait for some needed re-attempt
-                    with self.queue.pending_tries_notification:
-                        if self.queue.pending_tries <= 0:
-                            self.queue.pending_tries_notification.wait()
-                            continue # try again when awake
-                        self.queue.pending_tries -= 1
-
-                    # Get to work
-                    try:
-                        service, service_root = self.queue.get_nowait()
-                    except Queue.Empty:
-                        continue
-                    if service.finished():
-                        self.queue.task_done()
-                        continue
-                    success = bool(service.put(self.data_hash,
-                                                self.data,
-                                                timeout=self.timeout))
-                    result = service.last_result()
-                    if success:
-                        _logger.debug("KeepWriterThread %s succeeded %s+%i %s",
-                                      str(threading.current_thread()),
-                                      self.data_hash,
-                                      len(self.data),
-                                      service_root)
-                        try:
-                            replicas_stored = int(result['headers']['x-keep-replicas-stored'])
-                        except (KeyError, ValueError):
-                            replicas_stored = 1
-                        
-                        self.queue.write_success(result['body'].strip(), replicas_stored)
-                    else:
-                        if result.get('status_code', None):
-                            _logger.debug("Request fail: PUT %s => %s %s",
-                                          self.data_hash,
-                                          result['status_code'],
-                                          result['body'])
-                        self.queue.write_fail(service, result.get('status_code', None)) # Schedule a re-attempt with next service
-                    # Mark as done so the queue can be join()ed
-                    self.queue.task_done()
+            while True:
+                try:
+                    service, service_root = self.queue.get_next_task()
+                except Queue.Empty:
+                    return
+                try:
+                    locator, copies = self.do_task(service, service_root)
+                except Exception as e:
+                    if e is not self.TaskFailed:
+                        _logger.exception("Exception in KeepWriterThread")
+                    self.queue.write_fail(service)
                 else:
-                    # Remove the task from the queue anyways
-                    try:
-                        self.queue.get_nowait()
-                        # Mark as done so the queue can be join()ed
-                        self.queue.task_done()
-                    except Queue.Empty:
-                        continue
+                    self.queue.write_success(locator, copies)
+                finally:
+                    self.queue.task_done()
+
+        def do_task(self, service, service_root):
+            success = bool(service.put(self.data_hash,
+                                        self.data,
+                                        timeout=self.timeout))
+            result = service.last_result()
+
+            if not success:
+                if result.get('status_code', None):
+                    _logger.debug("Request fail: PUT %s => %s %s",
+                                  self.data_hash,
+                                  result['status_code'],
+                                  result['body'])
+                raise self.TaskFailed
+
+            _logger.debug("KeepWriterThread %s succeeded %s+%i %s",
+                          str(threading.current_thread()),
+                          self.data_hash,
+                          len(self.data),
+                          service_root)
+            try:
+                replicas_stored = int(result['headers']['x-keep-replicas-stored'])
+            except (KeyError, ValueError):
+                replicas_stored = 1
+
+            return result['body'].strip(), replicas_stored
 
 
     def __init__(self, api_client=None, proxy=None,
index 2ac6ab9129af16edfac4670452c7194761332274..e2692b738aa16a945d2d3935d39bae3030f5c687 100644 (file)
@@ -383,6 +383,8 @@ def ca_certs_path(fallback=httplib2.CA_CERTS):
     it returns the value of `fallback` (httplib2's CA certs by default).
     """
     for ca_certs_path in [
+        # Arvados specific:
+        '/etc/arvados/ca-certificates.crt',
         # Debian:
         '/etc/ssl/certs/ca-certificates.crt',
         # Red Hat:
index 60d4bec3b95c429643d7df4a600f72754954809a..f91b3977090da7c6f8b30844635174d122e67ba2 100755 (executable)
@@ -11,6 +11,8 @@ import logging
 import arvados
 import arvados.commands._util as arv_cmd
 
+from arvados._version import __version__
+
 logger = logging.getLogger('arvados.arv-get')
 
 def abort(msg, code=1):
@@ -20,6 +22,9 @@ def abort(msg, code=1):
 parser = argparse.ArgumentParser(
     description='Copy data from Keep to a local file or pipe.',
     parents=[arv_cmd.retry_opt])
+parser.add_argument('--version', action='version',
+                    version="%s %s" % (sys.argv[0], __version__),
+                    help='Print version and exit.')
 parser.add_argument('locator', type=str,
                     help="""
 Collection locator, optionally with a file path or prefix.
index b059d79459278e4859cdd7221183f2b863a3e73b..05a055e10855066588a707d539b2f250ea527be6 100755 (executable)
@@ -7,16 +7,22 @@ import re
 import string
 import sys
 
+import arvados
+from arvados._version import __version__
+
 parser = argparse.ArgumentParser(
     description='Read manifest on standard input and put normalized manifest on standard output.')
 
-parser.add_argument('--extract', type=str, help="The file to extract from the input manifest")
-parser.add_argument('--strip', action='store_true', help="Strip authorization tokens")
+parser.add_argument('--extract', type=str,
+                    help="The file to extract from the input manifest")
+parser.add_argument('--strip', action='store_true',
+                    help="Strip authorization tokens")
+parser.add_argument('--version', action='version',
+                    version="%s %s" % (sys.argv[0], __version__),
+                    help='Print version and exit.')
 
 args = parser.parse_args()
 
-import arvados
-
 r = sys.stdin.read()
 
 cr = arvados.CollectionReader(r)
index e0aae9625eb54d82eb4ee983696487079fa0d441..0dc59e543bdac20edf02046e09943a1421eda783 100644 (file)
@@ -46,12 +46,13 @@ setup(name='arvados-python-client',
       install_requires=[
           'google-api-python-client==1.4.2',
           'oauth2client >=1.4.6, <2',
-          'pyasn1-modules==0.0.5',
           'ciso8601',
           'httplib2',
           'pycurl >=7.19.5.1, <7.21.5',
           'python-gflags<3.0',
-          'ws4py'
+          'setuptools',
+          'ws4py',
+          'ruamel.yaml==0.13.7'
       ],
       test_suite='tests',
       tests_require=['pbr<1.7.0', 'mock>=1.0', 'PyYAML'],
index 71c9b178e7525808508babf86a383a37b4ab4ba6..dae3dd3b7b19c923ff53381e9f3ebef8c5abae49 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 
 import arvados
+import contextlib
 import errno
 import hashlib
 import httplib
@@ -11,6 +12,7 @@ import os
 import pycurl
 import Queue
 import shutil
+import sys
 import tempfile
 import unittest
 
@@ -50,6 +52,17 @@ def mock_api_responses(api_client, body, codes, headers={}):
 def str_keep_locator(s):
     return '{}+{}'.format(hashlib.md5(s).hexdigest(), len(s))
 
+@contextlib.contextmanager
+def redirected_streams(stdout=None, stderr=None):
+    orig_stdout, sys.stdout = sys.stdout, stdout or sys.stdout
+    orig_stderr, sys.stderr = sys.stderr, stderr or sys.stderr
+    try:
+        yield
+    finally:
+        sys.stdout = orig_stdout
+        sys.stderr = orig_stderr
+
+
 class FakeCurl:
     @classmethod
     def make(cls, code, body='', headers={}):
index 2b8b6ca1c4ad531c29bfd1c9a149da7c9bdf3599..006604077d457d286cdb9148332e087a0204313c 100644 (file)
@@ -54,4 +54,20 @@ http {
       proxy_redirect //download:{{KEEPWEBPORT}}/ https://$host:{{KEEPWEBDLSSLPORT}}/;
     }
   }
+  upstream ws {
+    server localhost:{{WSPORT}};
+  }
+  server {
+    listen *:{{WSSPORT}} ssl default_server;
+    server_name ~^(?<request_host>.*)$;
+    ssl_certificate {{SSLCERT}};
+    ssl_certificate_key {{SSLKEY}};
+    location  / {
+      proxy_pass http://ws;
+      proxy_set_header Upgrade $http_upgrade;
+      proxy_set_header Connection "upgrade";
+      proxy_set_header Host $request_host:{{WSPORT}};
+      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+    }
+  }
 }
index 642b7ccbad51846a9f1c25acc86f6b0505897c62..b969b12a7abc6374ae0993d0d95c2f5cf85f784b 100644 (file)
@@ -44,6 +44,7 @@ if not os.path.exists(TEST_TMPDIR):
 
 my_api_host = None
 _cached_config = {}
+_cached_db_config = {}
 
 def find_server_pid(PID_PATH, wait=10):
     now = time.time()
@@ -284,10 +285,19 @@ def run(leave_running_atexit=False):
         os.makedirs(gitdir)
     subprocess.check_output(['tar', '-xC', gitdir, '-f', gittarball])
 
+    # The nginx proxy isn't listening here yet, but we need to choose
+    # the wss:// port now so we can write the API server config file.
+    wss_port = find_available_port()
+    _setport('wss', wss_port)
+
     port = find_available_port()
     env = os.environ.copy()
     env['RAILS_ENV'] = 'test'
-    env['ARVADOS_WEBSOCKETS'] = 'yes'
+    env['ARVADOS_TEST_WSS_PORT'] = str(wss_port)
+    if env.get('ARVADOS_TEST_EXPERIMENTAL_WS'):
+        env.pop('ARVADOS_WEBSOCKETS', None)
+    else:
+        env['ARVADOS_WEBSOCKETS'] = 'yes'
     env.pop('ARVADOS_TEST_API_HOST', None)
     env.pop('ARVADOS_API_HOST', None)
     env.pop('ARVADOS_API_HOST_INSECURE', None)
@@ -360,6 +370,47 @@ def stop(force=False):
         kill_server_pid(_pidfile('api'))
         my_api_host = None
 
+def run_ws():
+    if 'ARVADOS_TEST_PROXY_SERVICES' in os.environ:
+        return
+    stop_ws()
+    port = find_available_port()
+    conf = os.path.join(TEST_TMPDIR, 'ws.yml')
+    with open(conf, 'w') as f:
+        f.write("""
+Client:
+  APIHost: {}
+  Insecure: true
+Listen: :{}
+LogLevel: {}
+Postgres:
+  host: {}
+  dbname: {}
+  user: {}
+  password: {}
+  sslmode: require
+        """.format(os.environ['ARVADOS_API_HOST'],
+                   port,
+                   ('info' if os.environ.get('ARVADOS_DEBUG', '') in ['','0'] else 'debug'),
+                   _dbconfig('host'),
+                   _dbconfig('database'),
+                   _dbconfig('username'),
+                   _dbconfig('password')))
+    logf = open(_fifo2stderr('ws'), 'w')
+    ws = subprocess.Popen(
+        ["ws", "-config", conf],
+        stdin=open('/dev/null'), stdout=logf, stderr=logf, close_fds=True)
+    with open(_pidfile('ws'), 'w') as f:
+        f.write(str(ws.pid))
+    _wait_until_port_listens(port)
+    _setport('ws', port)
+    return port
+
+def stop_ws():
+    if 'ARVADOS_TEST_PROXY_SERVICES' in os.environ:
+        return
+    kill_server_pid(_pidfile('ws'))
+
 def _start_keep(n, keep_args):
     keep0 = tempfile.mkdtemp()
     port = find_available_port()
@@ -537,6 +588,7 @@ def stop_keep_web():
 def run_nginx():
     if 'ARVADOS_TEST_PROXY_SERVICES' in os.environ:
         return
+    stop_nginx()
     nginxconf = {}
     nginxconf['KEEPWEBPORT'] = _getport('keep-web')
     nginxconf['KEEPWEBDLSSLPORT'] = find_available_port()
@@ -545,6 +597,8 @@ def run_nginx():
     nginxconf['KEEPPROXYSSLPORT'] = find_available_port()
     nginxconf['GITPORT'] = _getport('arv-git-httpd')
     nginxconf['GITSSLPORT'] = find_available_port()
+    nginxconf['WSPORT'] = _getport('ws')
+    nginxconf['WSSPORT'] = _getport('wss')
     nginxconf['SSLCERT'] = os.path.join(SERVICES_SRC_DIR, 'api', 'tmp', 'self-signed.pem')
     nginxconf['SSLKEY'] = os.path.join(SERVICES_SRC_DIR, 'api', 'tmp', 'self-signed.key')
     nginxconf['ACCESSLOG'] = _fifo2stderr('nginx_access_log')
@@ -593,7 +647,15 @@ def _getport(program):
     except IOError:
         return 9
 
+def _dbconfig(key):
+    global _cached_db_config
+    if not _cached_db_config:
+        _cached_db_config = yaml.load(open(os.path.join(
+            SERVICES_SRC_DIR, 'api', 'config', 'database.yml')))
+    return _cached_db_config['test'][key]
+
 def _apiconfig(key):
+    global _cached_config
     if _cached_config:
         return _cached_config[key]
     def _load(f, required=True):
@@ -647,6 +709,7 @@ class TestCaseWithServers(unittest.TestCase):
     original environment.
     """
     MAIN_SERVER = None
+    WS_SERVER = None
     KEEP_SERVER = None
     KEEP_PROXY_SERVER = None
     KEEP_WEB_SERVER = None
@@ -667,6 +730,7 @@ class TestCaseWithServers(unittest.TestCase):
         os.environ.pop('ARVADOS_EXTERNAL_CLIENT', None)
         for server_kwargs, start_func, stop_func in (
                 (cls.MAIN_SERVER, run, reset),
+                (cls.WS_SERVER, run_ws, stop_ws),
                 (cls.KEEP_SERVER, run_keep, stop_keep),
                 (cls.KEEP_PROXY_SERVER, run_keep_proxy, stop_keep_proxy),
                 (cls.KEEP_WEB_SERVER, run_keep_web, stop_keep_web)):
@@ -693,6 +757,7 @@ class TestCaseWithServers(unittest.TestCase):
 if __name__ == "__main__":
     actions = [
         'start', 'stop',
+        'start_ws', 'stop_ws',
         'start_keep', 'stop_keep',
         'start_keep_proxy', 'stop_keep_proxy',
         'start_keep-web', 'stop_keep-web',
@@ -725,6 +790,10 @@ if __name__ == "__main__":
             print(host)
     elif args.action == 'stop':
         stop(force=('ARVADOS_TEST_API_HOST' not in os.environ))
+    elif args.action == 'start_ws':
+        run_ws()
+    elif args.action == 'stop_ws':
+        stop_ws()
     elif args.action == 'start_keep':
         run_keep(enforce_permissions=args.keep_enforce_permissions, num_servers=args.num_keep_servers)
     elif args.action == 'stop_keep':
diff --git a/sdk/python/tests/test_arv_copy.py b/sdk/python/tests/test_arv_copy.py
new file mode 100644 (file)
index 0000000..e291ee0
--- /dev/null
@@ -0,0 +1,29 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import io
+import os
+import sys
+import tempfile
+import unittest
+
+import arvados.commands.arv_copy as arv_copy
+import arvados_testutil as tutil
+
+class ArvCopyTestCase(unittest.TestCase):
+    def run_copy(self, args):
+        sys.argv = ['arv-copy'] + args
+        return arv_copy.main()
+
+    def test_unsupported_arg(self):
+        with self.assertRaises(SystemExit):
+            self.run_copy(['-x=unknown'])
+
+    def test_version_argument(self):
+        err = io.BytesIO()
+        out = io.BytesIO()
+        with tutil.redirected_streams(stdout=out, stderr=err):
+            with self.assertRaises(SystemExit):
+                self.run_copy(['--version'])
+        self.assertEqual(out.getvalue(), '')
+        self.assertRegexpMatches(err.getvalue(), "[0-9]+\.[0-9]+\.[0-9]+")
diff --git a/sdk/python/tests/test_arv_keepdocker.py b/sdk/python/tests/test_arv_keepdocker.py
new file mode 100644 (file)
index 0000000..bb94db5
--- /dev/null
@@ -0,0 +1,30 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import io
+import os
+import sys
+import tempfile
+import unittest
+
+import arvados.commands.keepdocker as arv_keepdocker
+import arvados_testutil as tutil
+
+
+class ArvKeepdockerTestCase(unittest.TestCase):
+    def run_arv_keepdocker(self, args):
+        sys.argv = ['arv-keepdocker'] + args
+        return arv_keepdocker.main()
+
+    def test_unsupported_arg(self):
+        with self.assertRaises(SystemExit):
+            self.run_arv_keepdocker(['-x=unknown'])
+
+    def test_version_argument(self):
+        err = io.BytesIO()
+        out = io.BytesIO()
+        with tutil.redirected_streams(stdout=out, stderr=err):
+            with self.assertRaises(SystemExit):
+                self.run_arv_keepdocker(['--version'])
+        self.assertEqual(out.getvalue(), '')
+        self.assertRegexpMatches(err.getvalue(), "[0-9]+\.[0-9]+\.[0-9]+")
index 664b57fc00a57cef068e352232d55d0dfa548a58..5064f07d722ee77efc0c8a4f733eaf86d02b8b39 100644 (file)
@@ -2,15 +2,17 @@
 # -*- coding: utf-8 -*-
 
 import io
+import os
 import random
-
+import sys
 import mock
+import tempfile
 
 import arvados.errors as arv_error
 import arvados.commands.ls as arv_ls
 import run_test_server
 
-from arvados_testutil import str_keep_locator
+from arvados_testutil import str_keep_locator, redirected_streams
 
 class ArvLsTestCase(run_test_server.TestCaseWithServers):
     FAKE_UUID = 'zzzzz-4zz18-12345abcde12345'
@@ -78,3 +80,12 @@ class ArvLsTestCase(run_test_server.TestCaseWithServers):
             arv_error.NotFoundError)
         self.assertNotEqual(0, self.run_ls([self.FAKE_UUID], api_client))
         self.assertNotEqual('', self.stderr.getvalue())
+
+    def test_version_argument(self):
+        err = io.BytesIO()
+        out = io.BytesIO()
+        with redirected_streams(stdout=out, stderr=err):
+            with self.assertRaises(SystemExit):
+                self.run_ls(['--version'], None)
+        self.assertEqual(out.getvalue(), '')
+        self.assertRegexpMatches(err.getvalue(), "[0-9]+\.[0-9]+\.[0-9]+")
diff --git a/sdk/python/tests/test_arv_normalize.py b/sdk/python/tests/test_arv_normalize.py
new file mode 100644 (file)
index 0000000..8bce7e3
--- /dev/null
@@ -0,0 +1,27 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import subprocess
+import sys
+import tempfile
+import unittest
+
+
+class ArvNormalizeTestCase(unittest.TestCase):
+    def run_arv_normalize(self, args=[]):
+        p = subprocess.Popen([sys.executable, 'bin/arv-normalize'] + args,
+                             stdout=subprocess.PIPE,
+                             stderr=subprocess.PIPE)
+        (stdout, stderr) = p.communicate()
+        return p.returncode, stdout, stderr
+
+    def test_unsupported_arg(self):
+        returncode, out, err = self.run_arv_normalize(['-x=unknown'])
+        self.assertNotEqual(0, returncode)
+
+    def test_version_argument(self):
+        returncode, out, err = self.run_arv_normalize(['--version'])
+        self.assertEqual(0, returncode)
+        self.assertEqual('', out)
+        self.assertNotEqual('', err)
+        self.assertRegexpMatches(err, "[0-9]+\.[0-9]+\.[0-9]+")
index 7a0120c02814d00b27e81dd41fbb50e51ef2855c..f1dfd03def33d09c1ede560f50c5a059020f9c0c 100644 (file)
@@ -2,6 +2,7 @@
 # -*- coding: utf-8 -*-
 
 import apiclient
+import io
 import mock
 import os
 import pwd
@@ -31,9 +32,7 @@ class ArvadosPutResumeCacheTest(ArvadosBaseTestCase):
         [],
         ['/dev/null'],
         ['/dev/null', '--filename', 'empty'],
-        ['/tmp'],
-        ['/tmp', '--max-manifest-depth', '0'],
-        ['/tmp', '--max-manifest-depth', '1']
+        ['/tmp']
         ]
 
     def tearDown(self):
@@ -240,6 +239,7 @@ class ArvadosPutResumeCacheTest(ArvadosBaseTestCase):
 
 class ArvPutUploadJobTest(run_test_server.TestCaseWithServers,
                           ArvadosBaseTestCase):
+
     def setUp(self):
         super(ArvPutUploadJobTest, self).setUp()
         run_test_server.authorize_with('active')
@@ -270,7 +270,7 @@ class ArvPutUploadJobTest(run_test_server.TestCaseWithServers,
 
     def test_writer_works_without_cache(self):
         cwriter = arv_put.ArvPutUploadJob(['/dev/null'], resume=False)
-        cwriter.start()
+        cwriter.start(save_collection=False)
         self.assertEqual(". d41d8cd98f00b204e9800998ecf8427e+0 0:0:null\n", cwriter.manifest_text())
 
     def test_writer_works_with_cache(self):
@@ -278,13 +278,13 @@ class ArvPutUploadJobTest(run_test_server.TestCaseWithServers,
             f.write('foo')
             f.flush()
             cwriter = arv_put.ArvPutUploadJob([f.name])
-            cwriter.start()
-            self.assertEqual(3, cwriter.bytes_written)
+            cwriter.start(save_collection=False)
+            self.assertEqual(3, cwriter.bytes_written - cwriter.bytes_skipped)
             # Don't destroy the cache, and start another upload
             cwriter_new = arv_put.ArvPutUploadJob([f.name])
-            cwriter_new.start()
+            cwriter_new.start(save_collection=False)
             cwriter_new.destroy_cache()
-            self.assertEqual(0, cwriter_new.bytes_written)
+            self.assertEqual(0, cwriter_new.bytes_written - cwriter_new.bytes_skipped)
 
     def make_progress_tester(self):
         progression = []
@@ -300,13 +300,13 @@ class ArvPutUploadJobTest(run_test_server.TestCaseWithServers,
                 progression, reporter = self.make_progress_tester()
                 cwriter = arv_put.ArvPutUploadJob([f.name],
                     reporter=reporter, bytes_expected=expect_count)
-                cwriter.start()
+                cwriter.start(save_collection=False)
                 cwriter.destroy_cache()
                 self.assertIn((3, expect_count), progression)
 
     def test_writer_upload_directory(self):
         cwriter = arv_put.ArvPutUploadJob([self.tempdir])
-        cwriter.start()
+        cwriter.start(save_collection=False)
         cwriter.destroy_cache()
         self.assertEqual(1024*(1+2+3+4+5), cwriter.bytes_written)
 
@@ -324,17 +324,128 @@ class ArvPutUploadJobTest(run_test_server.TestCaseWithServers,
             writer = arv_put.ArvPutUploadJob([self.large_file_name],
                                              replication_desired=1)
             with self.assertRaises(SystemExit):
-                writer.start()
-                self.assertLess(writer.bytes_written,
-                                os.path.getsize(self.large_file_name))
+                writer.start(save_collection=False)
+            # Confirm that the file was partially uploaded
+            self.assertGreater(writer.bytes_written, 0)
+            self.assertLess(writer.bytes_written,
+                            os.path.getsize(self.large_file_name))
         # Retry the upload
         writer2 = arv_put.ArvPutUploadJob([self.large_file_name],
                                           replication_desired=1)
-        writer2.start()
-        self.assertEqual(writer.bytes_written + writer2.bytes_written,
+        writer2.start(save_collection=False)
+        self.assertEqual(writer.bytes_written + writer2.bytes_written - writer2.bytes_skipped,
+                         os.path.getsize(self.large_file_name))
+        writer2.destroy_cache()
+
+    def test_no_resume_when_asked(self):
+        def wrapped_write(*args, **kwargs):
+            data = args[1]
+            # Exit only on last block
+            if len(data) < arvados.config.KEEP_BLOCK_SIZE:
+                raise SystemExit("Simulated error")
+            return self.arvfile_write(*args, **kwargs)
+
+        with mock.patch('arvados.arvfile.ArvadosFileWriter.write',
+                        autospec=True) as mocked_write:
+            mocked_write.side_effect = wrapped_write
+            writer = arv_put.ArvPutUploadJob([self.large_file_name],
+                                             replication_desired=1)
+            with self.assertRaises(SystemExit):
+                writer.start(save_collection=False)
+            # Confirm that the file was partially uploaded
+            self.assertGreater(writer.bytes_written, 0)
+            self.assertLess(writer.bytes_written,
+                            os.path.getsize(self.large_file_name))
+        # Retry the upload, this time without resume
+        writer2 = arv_put.ArvPutUploadJob([self.large_file_name],
+                                          replication_desired=1,
+                                          resume=False)
+        writer2.start(save_collection=False)
+        self.assertEqual(writer2.bytes_skipped, 0)
+        self.assertEqual(writer2.bytes_written,
                          os.path.getsize(self.large_file_name))
         writer2.destroy_cache()
 
+    def test_no_resume_when_no_cache(self):
+        def wrapped_write(*args, **kwargs):
+            data = args[1]
+            # Exit only on last block
+            if len(data) < arvados.config.KEEP_BLOCK_SIZE:
+                raise SystemExit("Simulated error")
+            return self.arvfile_write(*args, **kwargs)
+
+        with mock.patch('arvados.arvfile.ArvadosFileWriter.write',
+                        autospec=True) as mocked_write:
+            mocked_write.side_effect = wrapped_write
+            writer = arv_put.ArvPutUploadJob([self.large_file_name],
+                                             replication_desired=1)
+            with self.assertRaises(SystemExit):
+                writer.start(save_collection=False)
+            # Confirm that the file was partially uploaded
+            self.assertGreater(writer.bytes_written, 0)
+            self.assertLess(writer.bytes_written,
+                            os.path.getsize(self.large_file_name))
+        # Retry the upload, this time without cache usage
+        writer2 = arv_put.ArvPutUploadJob([self.large_file_name],
+                                          replication_desired=1,
+                                          resume=False,
+                                          use_cache=False)
+        writer2.start(save_collection=False)
+        self.assertEqual(writer2.bytes_skipped, 0)
+        self.assertEqual(writer2.bytes_written,
+                         os.path.getsize(self.large_file_name))
+        writer2.destroy_cache()
+
+
+    def test_dry_run_feature(self):
+        def wrapped_write(*args, **kwargs):
+            data = args[1]
+            # Exit only on last block
+            if len(data) < arvados.config.KEEP_BLOCK_SIZE:
+                raise SystemExit("Simulated error")
+            return self.arvfile_write(*args, **kwargs)
+
+        with mock.patch('arvados.arvfile.ArvadosFileWriter.write',
+                        autospec=True) as mocked_write:
+            mocked_write.side_effect = wrapped_write
+            writer = arv_put.ArvPutUploadJob([self.large_file_name],
+                                             replication_desired=1)
+            with self.assertRaises(SystemExit):
+                writer.start(save_collection=False)
+            # Confirm that the file was partially uploaded
+            self.assertGreater(writer.bytes_written, 0)
+            self.assertLess(writer.bytes_written,
+                            os.path.getsize(self.large_file_name))
+        # Retry the upload using dry_run to check if there is a pending upload
+        writer2 = arv_put.ArvPutUploadJob([self.large_file_name],
+                                          replication_desired=1,
+                                          dry_run=True)
+        with self.assertRaises(arv_put.ArvPutUploadIsPending):
+            writer2.start(save_collection=False)
+        # Complete the pending upload
+        writer3 = arv_put.ArvPutUploadJob([self.large_file_name],
+                                          replication_desired=1)
+        writer3.start(save_collection=False)
+        # Confirm there's no pending upload with dry_run=True
+        writer4 = arv_put.ArvPutUploadJob([self.large_file_name],
+                                          replication_desired=1,
+                                          dry_run=True)
+        with self.assertRaises(arv_put.ArvPutUploadNotPending):
+            writer4.start(save_collection=False)
+        writer4.destroy_cache()
+        # Test obvious cases
+        with self.assertRaises(arv_put.ArvPutUploadIsPending):
+            arv_put.ArvPutUploadJob([self.large_file_name],
+                                    replication_desired=1,
+                                    dry_run=True,
+                                    resume=False,
+                                    use_cache=False)
+        with self.assertRaises(arv_put.ArvPutUploadIsPending):
+            arv_put.ArvPutUploadJob([self.large_file_name],
+                                    replication_desired=1,
+                                    dry_run=True,
+                                    resume=False)
+
 
 class ArvadosExpectedBytesTest(ArvadosBaseTestCase):
     TEST_SIZE = os.path.getsize(__file__)
@@ -408,6 +519,15 @@ class ArvadosPutTest(run_test_server.TestCaseWithServers, ArvadosBaseTestCase):
                 delattr(self, outbuf)
         super(ArvadosPutTest, self).tearDown()
 
+    def test_version_argument(self):
+        err = io.BytesIO()
+        out = io.BytesIO()
+        with tutil.redirected_streams(stdout=out, stderr=err):
+            with self.assertRaises(SystemExit):
+                self.call_main_with_args(['--version'])
+        self.assertEqual(out.getvalue(), '')
+        self.assertRegexpMatches(err.getvalue(), "[0-9]+\.[0-9]+\.[0-9]+")
+
     def test_simple_file_put(self):
         self.call_main_on_test_file()
 
@@ -624,6 +744,21 @@ class ArvPutIntegrationTest(run_test_server.TestCaseWithServers,
         self.assertEqual(1, len(collection_list))
         return collection_list[0]
 
+    def test_put_collection_with_later_update(self):
+        tmpdir = self.make_tmpdir()
+        with open(os.path.join(tmpdir, 'file1'), 'w') as f:
+            f.write('Relaxing in basins at the end of inlets terminates the endless tests from the box')
+        col = self.run_and_find_collection("", ['--no-progress', tmpdir])
+        self.assertNotEqual(None, col['uuid'])
+        # Add a new file to the directory
+        with open(os.path.join(tmpdir, 'file2'), 'w') as f:
+            f.write('The quick brown fox jumped over the lazy dog')
+        updated_col = self.run_and_find_collection("", ['--no-progress', '--update-collection', col['uuid'], tmpdir])
+        self.assertEqual(col['uuid'], updated_col['uuid'])
+        # Get the manifest and check that the new file is being included
+        c = arv_put.api_client.collections().get(uuid=updated_col['uuid']).execute()
+        self.assertRegexpMatches(c['manifest_text'], r'^\. .*:44:file2\n')
+
     def test_put_collection_with_high_redundancy(self):
         # Write empty data: we're not testing CollectionWriter, just
         # making sure collections.create tells the API server what our
diff --git a/sdk/python/tests/test_arv_run.py b/sdk/python/tests/test_arv_run.py
new file mode 100644 (file)
index 0000000..3d04d27
--- /dev/null
@@ -0,0 +1,29 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import io
+import os
+import sys
+import tempfile
+import unittest
+
+import arvados.commands.run as arv_run
+import arvados_testutil as tutil
+
+class ArvRunTestCase(unittest.TestCase):
+    def run_arv_run(self, args):
+        sys.argv = ['arv-run'] + args
+        return arv_run.main()
+
+    def test_unsupported_arg(self):
+        with self.assertRaises(SystemExit):
+            self.run_arv_run(['-x=unknown'])
+
+    def test_version_argument(self):
+        err = io.BytesIO()
+        out = io.BytesIO()
+        with tutil.redirected_streams(stdout=out, stderr=err):
+            with self.assertRaises(SystemExit):
+                self.run_arv_run(['--version'])
+        self.assertEqual(out.getvalue(), '')
+        self.assertRegexpMatches(err.getvalue(), "[0-9]+\.[0-9]+\.[0-9]+")
index 5a018273a4d0c8aa6b35970cbb151227083e0a47..2a85e04e87c06067bd7d83773295cf049f747852 100644 (file)
@@ -1,8 +1,14 @@
 #!/usr/bin/env python
 
+import io
+import os
+import sys
+import tempfile
 import unittest
+
 import arvados.errors as arv_error
 import arvados.commands.ws as arv_ws
+import arvados_testutil as tutil
 
 class ArvWsTestCase(unittest.TestCase):
     def run_ws(self, args):
@@ -11,3 +17,12 @@ class ArvWsTestCase(unittest.TestCase):
     def test_unsupported_arg(self):
         with self.assertRaises(SystemExit):
             self.run_ws(['-x=unknown'])
+
+    def test_version_argument(self):
+        err = io.BytesIO()
+        out = io.BytesIO()
+        with tutil.redirected_streams(stdout=out, stderr=err):
+            with self.assertRaises(SystemExit):
+                self.run_ws(['--version'])
+        self.assertEqual(out.getvalue(), '')
+        self.assertRegexpMatches(err.getvalue(), "[0-9]+\.[0-9]+\.[0-9]+")
index 6b3562602aa69601021b04d93a116c04972abab5..8f02d517fc54ff531755dbacdefdb48378ab13ea 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 
 import bz2
+import datetime
 import gzip
 import io
 import mock
@@ -570,6 +571,26 @@ class ArvadosFileReadlinesTestCase(ArvadosFileReadTestCase):
     def read_for_test(self, reader, byte_count, **kwargs):
         return ''.join(reader.readlines(**kwargs))
 
+
+class ArvadosFileTestCase(unittest.TestCase):
+    def datetime_to_hex(self, dt):
+        return hex(int(time.mktime(dt.timetuple())))[2:]
+
+    def test_permission_expired(self):
+        base_manifest = ". 781e5e245d69b566979b86e28d23f2c7+10+A715fd31f8111894f717eb1003c1b0216799dd9ec@{} 0:10:count.txt\n"
+        now = datetime.datetime.now()
+        a_week_ago = now - datetime.timedelta(days=7)
+        a_month_ago = now - datetime.timedelta(days=30)
+        a_week_from_now = now + datetime.timedelta(days=7)
+        with Collection(base_manifest.format(self.datetime_to_hex(a_week_from_now))) as c:
+            self.assertFalse(c.find('count.txt').permission_expired())
+        with Collection(base_manifest.format(self.datetime_to_hex(a_week_ago))) as c:
+            f = c.find('count.txt')
+            self.assertTrue(f.permission_expired())
+            self.assertTrue(f.permission_expired(a_week_from_now))
+            self.assertFalse(f.permission_expired(a_month_ago))
+
+
 class BlockManagerTest(unittest.TestCase):
     def test_bufferblock_append(self):
         keep = ArvadosFileWriterTestCase.MockKeep({})
index fc30a242eba1bfc665a05747de66f999869ef8a4..0e3d5e13f135c84f2fde2f741bd554b0ccdf3a85 100644 (file)
@@ -861,6 +861,8 @@ class NewCollectionTestCase(unittest.TestCase, CollectionTestMixin):
             c.find("/.")
         with self.assertRaises(arvados.errors.ArgumentError):
             c.find("")
+        self.assertIs(c.find("./nonexistant.txt"), None)
+        self.assertIs(c.find("./nonexistantsubdir/nonexistant.txt"), None)
 
     def test_remove_in_subdir(self):
         c = Collection('. 781e5e245d69b566979b86e28d23f2c7+10 0:10:count1.txt\n./foo 781e5e245d69b566979b86e28d23f2c7+10 0:10:count2.txt\n')
index f2cdba28c775a523bc178052644cb4a76dac2771..88e528362143b04b1477aef0b4af3c2af1d76b0a 100644 (file)
@@ -17,6 +17,8 @@ class WebsocketTest(run_test_server.TestCaseWithServers):
     TIME_FUTURE = time.time()+3600
     MOCK_WS_URL = 'wss://[{}]/'.format(arvados_testutil.TEST_HOST)
 
+    TEST_TIMEOUT = 10.0
+
     def setUp(self):
         self.ws = None
 
@@ -51,21 +53,22 @@ class WebsocketTest(run_test_server.TestCaseWithServers):
         self.assertEqual(200, events.get(True, 5)['status'])
         human = arvados.api('v1').humans().create(body={}).execute()
 
-        log_object_uuids = []
-        for i in range(0, expected):
-            log_object_uuids.append(events.get(True, 5)['object_uuid'])
-
+        want_uuids = []
         if expected > 0:
-            self.assertIn(human['uuid'], log_object_uuids)
-
+            want_uuids.append(human['uuid'])
         if expected > 1:
-            self.assertIn(ancestor['uuid'], log_object_uuids)
+            want_uuids.append(ancestor['uuid'])
+        log_object_uuids = []
+        while set(want_uuids) - set(log_object_uuids):
+            log_object_uuids.append(events.get(True, 5)['object_uuid'])
 
-        with self.assertRaises(Queue.Empty):
-            # assertEqual just serves to show us what unexpected thing
-            # comes out of the queue when the assertRaises fails; when
-            # the test passes, this assertEqual doesn't get called.
-            self.assertEqual(events.get(True, 2), None)
+        if expected < 2:
+            with self.assertRaises(Queue.Empty):
+                # assertEqual just serves to show us what unexpected
+                # thing comes out of the queue when the assertRaises
+                # fails; when the test passes, this assertEqual
+                # doesn't get called.
+                self.assertEqual(events.get(True, 2), None)
 
     def test_subscribe_websocket(self):
         self._test_subscribe(
@@ -143,8 +146,8 @@ class WebsocketTest(run_test_server.TestCaseWithServers):
         return time.strftime('%Y-%m-%dT%H:%M:%S', time.localtime(t)) + self.isotz(-time.timezone/60)
 
     def isotz(self, offset):
-        """Convert minutes-east-of-UTC to ISO8601 time zone designator"""
-        return '{:+03d}{:02d}'.format(offset/60, offset%60)
+        """Convert minutes-east-of-UTC to RFC3339- and ISO-compatible time zone designator"""
+        return '{:+03d}:{:02d}'.format(offset/60, offset%60)
 
     # Test websocket reconnection on (un)execpted close
     def _test_websocket_reconnect(self, close_unexpected):
@@ -262,20 +265,16 @@ class WebsocketTest(run_test_server.TestCaseWithServers):
 
     @mock.patch('arvados.events._EventClient')
     def test_run_forever_survives_reconnects(self, websocket_client):
-        connection_cond = threading.Condition()
-        def ws_connect():
-            with connection_cond:
-                connection_cond.notify_all()
-        websocket_client().connect.side_effect = ws_connect
+        connected = threading.Event()
+        websocket_client().connect.side_effect = connected.set
         client = arvados.events.EventClient(
             self.MOCK_WS_URL, [], lambda event: None, None)
-        with connection_cond:
-            forever_thread = threading.Thread(target=client.run_forever)
-            forever_thread.start()
-            # Simulate an unexpected disconnect, and wait for reconnect.
-            close_thread = threading.Thread(target=client.on_closed)
-            close_thread.start()
-            connection_cond.wait()
+        forever_thread = threading.Thread(target=client.run_forever)
+        forever_thread.start()
+        # Simulate an unexpected disconnect, and wait for reconnect.
+        close_thread = threading.Thread(target=client.on_closed)
+        close_thread.start()
+        self.assertTrue(connected.wait(timeout=self.TEST_TIMEOUT))
         close_thread.join()
         run_forever_alive = forever_thread.is_alive()
         client.close()
@@ -285,27 +284,42 @@ class WebsocketTest(run_test_server.TestCaseWithServers):
 
 
 class PollClientTestCase(unittest.TestCase):
+    TEST_TIMEOUT = 10.0
+
     class MockLogs(object):
+
         def __init__(self):
             self.logs = []
             self.lock = threading.Lock()
+            self.api_called = threading.Event()
 
         def add(self, log):
             with self.lock:
                 self.logs.append(log)
 
         def return_list(self, num_retries=None):
+            self.api_called.set()
+            args, kwargs = self.list_func.call_args_list[-1]
+            filters = kwargs.get('filters', [])
+            if not any(True for f in filters if f[0] == 'id' and f[1] == '>'):
+                # No 'id' filter was given -- this must be the probe
+                # to determine the most recent id.
+                return {'items': [{'id': 1}], 'items_available': 1}
             with self.lock:
                 retval = self.logs
                 self.logs = []
             return {'items': retval, 'items_available': len(retval)}
 
-
     def setUp(self):
         self.logs = self.MockLogs()
         self.arv = mock.MagicMock(name='arvados.api()')
         self.arv.logs().list().execute.side_effect = self.logs.return_list
-        self.callback_cond = threading.Condition()
+        # our MockLogs object's "execute" stub will need to inspect
+        # the call history to determine X in
+        # ....logs().list(filters=X).execute():
+        self.logs.list_func = self.arv.logs().list
+        self.status_ok = threading.Event()
+        self.event_received = threading.Event()
         self.recv_events = []
 
     def tearDown(self):
@@ -313,9 +327,11 @@ class PollClientTestCase(unittest.TestCase):
             self.client.close(timeout=None)
 
     def callback(self, event):
-        with self.callback_cond:
+        if event.get('status') == 200:
+            self.status_ok.set()
+        else:
             self.recv_events.append(event)
-            self.callback_cond.notify_all()
+            self.event_received.set()
 
     def build_client(self, filters=None, callback=None, last_log_id=None, poll_time=99):
         if filters is None:
@@ -333,40 +349,46 @@ class PollClientTestCase(unittest.TestCase):
         test_log = {'id': 12345, 'testkey': 'testtext'}
         self.logs.add({'id': 123})
         self.build_client(poll_time=.01)
-        with self.callback_cond:
-            self.client.start()
-            self.callback_cond.wait()
-            self.logs.add(test_log.copy())
-            self.callback_cond.wait()
-        self.client.close(timeout=None)
+        self.client.start()
+        self.assertTrue(self.status_ok.wait(self.TEST_TIMEOUT))
+        self.assertTrue(self.event_received.wait(self.TEST_TIMEOUT))
+        self.event_received.clear()
+        self.logs.add(test_log.copy())
+        self.assertTrue(self.event_received.wait(self.TEST_TIMEOUT))
         self.assertIn(test_log, self.recv_events)
 
     def test_subscribe(self):
         client_filter = ['kind', '=', 'arvados#test']
         self.build_client()
+        self.client.unsubscribe([])
         self.client.subscribe([client_filter[:]])
-        with self.callback_cond:
-            self.client.start()
-            self.callback_cond.wait()
-        self.client.close(timeout=None)
+        self.client.start()
+        self.assertTrue(self.status_ok.wait(self.TEST_TIMEOUT))
+        self.assertTrue(self.logs.api_called.wait(self.TEST_TIMEOUT))
         self.assertTrue(self.was_filter_used(client_filter))
 
     def test_unsubscribe(self):
-        client_filter = ['kind', '=', 'arvados#test']
-        self.build_client()
-        self.client.subscribe([client_filter[:]])
-        self.client.unsubscribe([client_filter[:]])
+        should_filter = ['foo', '=', 'foo']
+        should_not_filter = ['foo', '=', 'bar']
+        self.build_client(poll_time=0.01)
+        self.client.unsubscribe([])
+        self.client.subscribe([should_not_filter[:]])
+        self.client.subscribe([should_filter[:]])
+        self.client.unsubscribe([should_not_filter[:]])
         self.client.start()
-        self.client.close(timeout=None)
-        self.assertFalse(self.was_filter_used(client_filter))
+        self.logs.add({'id': 123})
+        self.assertTrue(self.status_ok.wait(self.TEST_TIMEOUT))
+        self.assertTrue(self.event_received.wait(self.TEST_TIMEOUT))
+        self.assertTrue(self.was_filter_used(should_filter))
+        self.assertFalse(self.was_filter_used(should_not_filter))
 
     def test_run_forever(self):
         self.build_client()
-        with self.callback_cond:
-            self.client.start()
-            forever_thread = threading.Thread(target=self.client.run_forever)
-            forever_thread.start()
-            self.callback_cond.wait()
+        self.client.start()
+        forever_thread = threading.Thread(target=self.client.run_forever)
+        forever_thread.start()
+        self.assertTrue(self.status_ok.wait(self.TEST_TIMEOUT))
         self.assertTrue(forever_thread.is_alive())
         self.client.close()
         forever_thread.join()
+        del self.client
index 908539b8cae010f1cf0f23046bdcaf1f15f136b0..85b5bc81f00902a2a816d606bbc2cecff06de289 100644 (file)
@@ -1081,58 +1081,74 @@ class KeepClientRetryPutTestCase(KeepClientRetryTestMixin, unittest.TestCase):
             self.check_exception(copies=2, num_retries=3)
 
 
-class KeepClientAvoidClientOverreplicationTestCase(unittest.TestCase, tutil.ApiClientMock):
-    
-    
+class AvoidOverreplication(unittest.TestCase, tutil.ApiClientMock):
+
     class FakeKeepService(object):
-        def __init__(self, delay, will_succeed, replicas=1):
+        def __init__(self, delay, will_succeed=False, will_raise=None, replicas=1):
             self.delay = delay
-            self.success = will_succeed
+            self.will_succeed = will_succeed
+            self.will_raise = will_raise
             self._result = {}
             self._result['headers'] = {}
             self._result['headers']['x-keep-replicas-stored'] = str(replicas)
             self._result['body'] = 'foobar'
-        
+
         def put(self, data_hash, data, timeout):
             time.sleep(self.delay)
-            return self.success
-        
+            if self.will_raise is not None:
+                raise self.will_raise
+            return self.will_succeed
+
         def last_result(self):
-            return self._result
-        
+            if self.will_succeed:
+                return self._result
+
         def finished(self):
             return False
     
-    
-    def test_only_write_enough_on_success(self):
-        copies = 3
-        pool = arvados.KeepClient.KeepWriterThreadPool(
+    def setUp(self):
+        self.copies = 3
+        self.pool = arvados.KeepClient.KeepWriterThreadPool(
             data = 'foo',
             data_hash = 'acbd18db4cc2f85cedef654fccc4a4d8+3',
-            max_service_replicas = copies,
-            copies = copies
+            max_service_replicas = self.copies,
+            copies = self.copies
         )
+
+    def test_only_write_enough_on_success(self):
         for i in range(10):
             ks = self.FakeKeepService(delay=i/10.0, will_succeed=True)
-            pool.add_task(ks, None)
-        pool.join()
-        self.assertEqual(pool.done(), copies)
+            self.pool.add_task(ks, None)
+        self.pool.join()
+        self.assertEqual(self.pool.done(), self.copies)
 
     def test_only_write_enough_on_partial_success(self):
-        copies = 3
-        pool = arvados.KeepClient.KeepWriterThreadPool(
-            data = 'foo',
-            data_hash = 'acbd18db4cc2f85cedef654fccc4a4d8+3',
-            max_service_replicas = copies,
-            copies = copies
-        )
         for i in range(5):
             ks = self.FakeKeepService(delay=i/10.0, will_succeed=False)
-            pool.add_task(ks, None)
+            self.pool.add_task(ks, None)
+            ks = self.FakeKeepService(delay=i/10.0, will_succeed=True)
+            self.pool.add_task(ks, None)
+        self.pool.join()
+        self.assertEqual(self.pool.done(), self.copies)
+
+    def test_only_write_enough_when_some_crash(self):
+        for i in range(5):
+            ks = self.FakeKeepService(delay=i/10.0, will_raise=Exception())
+            self.pool.add_task(ks, None)
+            ks = self.FakeKeepService(delay=i/10.0, will_succeed=True)
+            self.pool.add_task(ks, None)
+        self.pool.join()
+        self.assertEqual(self.pool.done(), self.copies)
+
+    def test_fail_when_too_many_crash(self):
+        for i in range(self.copies+1):
+            ks = self.FakeKeepService(delay=i/10.0, will_raise=Exception())
+            self.pool.add_task(ks, None)
+        for i in range(self.copies-1):
             ks = self.FakeKeepService(delay=i/10.0, will_succeed=True)
-            pool.add_task(ks, None)
-        pool.join()
-        self.assertEqual(pool.done(), copies)
+            self.pool.add_task(ks, None)
+        self.pool.join()
+        self.assertEqual(self.pool.done(), self.copies-1)
     
 
 @tutil.skip_sleep
index 5d9b031e0295a62b86a6f0b4d6e9c13cc784da70..39f217f100781fed106339a6b37d536b069b9bda 100644 (file)
@@ -1,6 +1,6 @@
 source 'https://rubygems.org'
 
-gem 'rails', '~> 3.2.0'
+gem 'rails', '~> 3.2'
 
 # Bundle edge Rails instead:
 # gem 'rails',     :git => 'git://github.com/rails/rails.git'
@@ -12,14 +12,13 @@ group :test, :development do
   # Note: "require: false" here tells bunder not to automatically
   # 'require' the packages during application startup. Installation is
   # still mandatory.
+  gem 'test-unit', '~> 3.0', require: false
   gem 'simplecov', '~> 0.7.1', require: false
   gem 'simplecov-rcov', require: false
   gem 'mocha', require: false
 end
 
-# This might not be needed in :test and :development, but we load it
-# anyway to make sure it always gets in Gemfile.lock and to help
-# reveal install problems sooner rather than later.
+# pg is the only supported database driver.
 gem 'pg'
 
 # Start using multi_json once we are on Rails 3.2;
@@ -31,13 +30,13 @@ gem 'oj'
 # Gems used only for assets and not required
 # in production environments by default.
 group :assets do
-  gem 'sass-rails',   '>= 3.2.0'
-  gem 'coffee-rails', '~> 3.2.0'
+  gem 'sass-rails',   '~> 3.2'
+  gem 'coffee-rails', '~> 3.2'
 
   # See https://github.com/sstephenson/execjs#readme for more supported runtimes
   gem 'therubyracer'
 
-  gem 'uglifier', '>= 1.0.3'
+  gem 'uglifier', '~> 2.0'
 end
 
 gem 'jquery-rails'
@@ -60,8 +59,8 @@ gem 'acts_as_api'
 
 gem 'passenger'
 
-gem 'omniauth', '1.1.1'
-gem 'omniauth-oauth2', '1.1.1'
+gem 'omniauth', '~> 1.1'
+gem 'omniauth-oauth2', '~> 1.1'
 
 gem 'andand'
 
@@ -78,8 +77,13 @@ gem 'arvados-cli', '>= 0.1.20161017193526'
 # pg_power lets us use partial indexes in schema.rb in Rails 3
 gem 'pg_power'
 
-gem 'puma'
+gem 'puma', '~> 2.0'
 gem 'sshkey'
 gem 'safe_yaml'
 gem 'lograge'
 gem 'logstash-event'
+
+# Install any plugin gems
+Dir.glob(File.join(File.dirname(__FILE__), 'lib', '**', "Gemfile")) do |gemfile|
+    eval(IO.read(gemfile), binding)
+end
index 6f7875163b63fa6af8462f2999e42bad4902f37d..9c9c4ae9e58b3105c47aa5eb330e052b3747a19f 100644 (file)
@@ -1,12 +1,12 @@
 GEM
   remote: https://rubygems.org/
   specs:
-    actionmailer (3.2.17)
-      actionpack (= 3.2.17)
+    actionmailer (3.2.22.5)
+      actionpack (= 3.2.22.5)
       mail (~> 2.5.4)
-    actionpack (3.2.17)
-      activemodel (= 3.2.17)
-      activesupport (= 3.2.17)
+    actionpack (3.2.22.5)
+      activemodel (= 3.2.22.5)
+      activesupport (= 3.2.22.5)
       builder (~> 3.0.0)
       erubis (~> 2.7.0)
       journey (~> 1.0.4)
@@ -14,31 +14,31 @@ GEM
       rack-cache (~> 1.2)
       rack-test (~> 0.6.1)
       sprockets (~> 2.2.1)
-    activemodel (3.2.17)
-      activesupport (= 3.2.17)
+    activemodel (3.2.22.5)
+      activesupport (= 3.2.22.5)
       builder (~> 3.0.0)
-    activerecord (3.2.17)
-      activemodel (= 3.2.17)
-      activesupport (= 3.2.17)
+    activerecord (3.2.22.5)
+      activemodel (= 3.2.22.5)
+      activesupport (= 3.2.22.5)
       arel (~> 3.0.2)
       tzinfo (~> 0.3.29)
-    activeresource (3.2.17)
-      activemodel (= 3.2.17)
-      activesupport (= 3.2.17)
-    activesupport (3.2.17)
+    activeresource (3.2.22.5)
+      activemodel (= 3.2.22.5)
+      activesupport (= 3.2.22.5)
+    activesupport (3.2.22.5)
       i18n (~> 0.6, >= 0.6.4)
       multi_json (~> 1.0)
-    acts_as_api (0.4.2)
+    acts_as_api (0.4.3)
       activemodel (>= 3.0.0)
       activesupport (>= 3.0.0)
       rack (>= 1.1.0)
     addressable (2.4.0)
     andand (1.3.3)
     arel (3.0.3)
-    arvados (0.1.20160420143004)
+    arvados (0.1.20160513152536)
       activesupport (>= 3, < 4.2.6)
       andand (~> 1.3, >= 1.3.3)
-      google-api-client (>= 0.7, < 0.9)
+      google-api-client (>= 0.7, < 0.8.9)
       i18n (~> 0)
       json (~> 1.7, >= 1.7.7)
       jwt (>= 0.1.5, < 2)
@@ -56,62 +56,71 @@ GEM
       extlib (>= 0.9.15)
       multi_json (>= 1.0.0)
     builder (3.0.4)
-    capistrano (2.15.5)
+    capistrano (2.15.9)
       highline
       net-scp (>= 1.0.0)
       net-sftp (>= 2.0.0)
       net-ssh (>= 2.0.14)
       net-ssh-gateway (>= 1.1.0)
-    coffee-rails (3.2.1)
+    coffee-rails (3.2.2)
       coffee-script (>= 2.2.0)
-      railties (~> 3.2.0.beta)
-    coffee-script (2.2.0)
+      railties (~> 3.2.0)
+    coffee-script (2.4.1)
       coffee-script-source
       execjs
-    coffee-script-source (1.7.0)
+    coffee-script-source (1.10.0)
     curb (0.9.3)
-    daemon_controller (1.2.0)
-    database_cleaner (1.2.0)
+    database_cleaner (1.5.3)
     erubis (2.7.0)
-    eventmachine (1.0.3)
-    execjs (2.0.2)
+    eventmachine (1.2.0.1)
+    execjs (2.7.0)
     extlib (0.9.16)
-    factory_girl (4.4.0)
+    factory_girl (4.7.0)
       activesupport (>= 3.0.0)
-    factory_girl_rails (4.4.1)
-      factory_girl (~> 4.4.0)
+    factory_girl_rails (4.7.0)
+      factory_girl (~> 4.7.0)
       railties (>= 3.0.0)
     faraday (0.9.2)
       multipart-post (>= 1.2, < 3)
-    faye-websocket (0.7.2)
+    faye-websocket (0.10.4)
       eventmachine (>= 0.12.0)
-      websocket-driver (>= 0.3.1)
-    google-api-client (0.7.1)
-      addressable (>= 2.3.2)
-      autoparse (>= 0.3.3)
-      extlib (>= 0.9.15)
-      faraday (>= 0.9.0)
-      jwt (>= 0.1.5)
-      launchy (>= 2.1.1)
-      multi_json (>= 1.0.0)
-      retriable (>= 1.4)
-      signet (>= 0.5.0)
-      uuidtools (>= 2.1.0)
-    hashie (1.2.0)
-    highline (1.6.21)
+      websocket-driver (>= 0.5.1)
+    google-api-client (0.8.7)
+      activesupport (>= 3.2, < 5.0)
+      addressable (~> 2.3)
+      autoparse (~> 0.3)
+      extlib (~> 0.9)
+      faraday (~> 0.9)
+      googleauth (~> 0.3)
+      launchy (~> 2.4)
+      multi_json (~> 1.10)
+      retriable (~> 1.4)
+      signet (~> 0.6)
+    googleauth (0.5.1)
+      faraday (~> 0.9)
+      jwt (~> 1.4)
+      logging (~> 2.0)
+      memoist (~> 0.12)
+      multi_json (~> 1.11)
+      os (~> 0.9)
+      signet (~> 0.7)
+    hashie (3.4.6)
+    highline (1.7.8)
     hike (1.2.3)
-    httpauth (0.2.1)
     i18n (0.7.0)
     journey (1.0.4)
-    jquery-rails (3.1.0)
+    jquery-rails (3.1.4)
       railties (>= 3.0, < 5.0)
       thor (>= 0.14, < 2.0)
     json (1.8.3)
-    jwt (0.1.13)
-      multi_json (>= 1.5)
+    jwt (1.5.6)
     launchy (2.4.3)
       addressable (~> 2.3)
-    libv8 (3.16.14.3)
+    libv8 (3.16.14.15)
+    little-plugger (1.1.4)
+    logging (2.1.0)
+      little-plugger (~> 1.1)
+      multi_json (~> 1.10)
     lograge (0.3.6)
       actionpack (>= 3)
       activesupport (>= 3)
@@ -120,100 +129,105 @@ GEM
     mail (2.5.4)
       mime-types (~> 1.16)
       treetop (~> 1.4.8)
+    memoist (0.15.0)
     metaclass (0.0.4)
     mime-types (1.25.1)
-    mocha (1.1.0)
+    mocha (1.2.0)
       metaclass (~> 0.0.1)
-    multi_json (1.12.0)
+    multi_json (1.12.1)
+    multi_xml (0.5.5)
     multipart-post (2.0.0)
-    net-scp (1.2.0)
+    net-scp (1.2.1)
       net-ssh (>= 2.6.5)
     net-sftp (2.1.2)
       net-ssh (>= 2.6.5)
-    net-ssh (2.8.0)
+    net-ssh (3.2.0)
     net-ssh-gateway (1.2.0)
       net-ssh (>= 2.6.5)
-    oauth2 (0.8.1)
-      faraday (~> 0.8)
-      httpauth (~> 0.1)
-      jwt (~> 0.1.4)
-      multi_json (~> 1.0)
-      rack (~> 1.2)
+    oauth2 (1.2.0)
+      faraday (>= 0.8, < 0.10)
+      jwt (~> 1.0)
+      multi_json (~> 1.3)
+      multi_xml (~> 0.5)
+      rack (>= 1.2, < 3)
     oj (2.15.0)
-    omniauth (1.1.1)
-      hashie (~> 1.2)
-      rack
-    omniauth-oauth2 (1.1.1)
-      oauth2 (~> 0.8.0)
-      omniauth (~> 1.0)
-    passenger (4.0.41)
-      daemon_controller (>= 1.2.0)
+    omniauth (1.3.1)
+      hashie (>= 1.2, < 4)
+      rack (>= 1.0, < 3)
+    omniauth-oauth2 (1.4.0)
+      oauth2 (~> 1.0)
+      omniauth (~> 1.2)
+    os (0.9.6)
+    passenger (5.0.30)
       rack
       rake (>= 0.8.1)
-    pg (0.17.1)
+    pg (0.19.0)
     pg_power (1.6.4)
       pg
       rails (~> 3.1)
-    polyglot (0.3.4)
-    puma (2.8.2)
-      rack (>= 1.1, < 2.0)
-    rack (1.4.5)
-    rack-cache (1.2)
+    polyglot (0.3.5)
+    power_assert (0.3.1)
+    puma (2.16.0)
+    rack (1.4.7)
+    rack-cache (1.6.1)
       rack (>= 0.4)
     rack-ssl (1.3.4)
       rack
-    rack-test (0.6.2)
+    rack-test (0.6.3)
       rack (>= 1.0)
-    rails (3.2.17)
-      actionmailer (= 3.2.17)
-      actionpack (= 3.2.17)
-      activerecord (= 3.2.17)
-      activeresource (= 3.2.17)
-      activesupport (= 3.2.17)
+    rails (3.2.22.5)
+      actionmailer (= 3.2.22.5)
+      actionpack (= 3.2.22.5)
+      activerecord (= 3.2.22.5)
+      activeresource (= 3.2.22.5)
+      activesupport (= 3.2.22.5)
       bundler (~> 1.0)
-      railties (= 3.2.17)
-    railties (3.2.17)
-      actionpack (= 3.2.17)
-      activesupport (= 3.2.17)
+      railties (= 3.2.22.5)
+    railties (3.2.22.5)
+      actionpack (= 3.2.22.5)
+      activesupport (= 3.2.22.5)
       rack-ssl (~> 1.3.2)
       rake (>= 0.8.7)
       rdoc (~> 3.4)
       thor (>= 0.14.6, < 2.0)
-    rake (10.2.2)
+    rake (11.3.0)
     rdoc (3.12.2)
       json (~> 1.4)
-    ref (1.0.5)
-    retriable (2.1.0)
-    ruby-prof (0.15.2)
-    rvm-capistrano (1.5.1)
+    ref (2.0.0)
+    retriable (1.4.1)
+    ruby-prof (0.16.2)
+    rvm-capistrano (1.5.6)
       capistrano (~> 2.15.4)
     safe_yaml (1.0.4)
-    sass (3.3.4)
+    sass (3.4.22)
     sass-rails (3.2.6)
       railties (~> 3.2.0)
       sass (>= 3.1.10)
       tilt (~> 1.3)
-    signet (0.5.1)
-      addressable (>= 2.2.3)
-      faraday (>= 0.9.0.rc5)
-      jwt (>= 0.1.5)
-      multi_json (>= 1.0.0)
+    signet (0.7.3)
+      addressable (~> 2.3)
+      faraday (~> 0.9)
+      jwt (~> 1.5)
+      multi_json (~> 1.10)
     simplecov (0.7.1)
       multi_json (~> 1.0)
       simplecov-html (~> 0.7.1)
     simplecov-html (0.7.1)
     simplecov-rcov (0.2.3)
       simplecov (>= 0.4.1)
-    sprockets (2.2.2)
+    sprockets (2.2.3)
       hike (~> 1.2)
       multi_json (~> 1.0)
       rack (~> 1.0)
       tilt (~> 1.1, != 1.3.0)
-    sshkey (1.6.1)
-    test_after_commit (0.2.3)
+    sshkey (1.8.0)
+    test-unit (3.2.1)
+      power_assert
+    test_after_commit (1.1.0)
+      activerecord (>= 3.2)
     themes_for_rails (0.5.1)
       rails (>= 3.0.0)
-    therubyracer (0.12.1)
+    therubyracer (0.12.2)
       libv8 (~> 3.16.14.0)
       ref
     thor (0.19.1)
@@ -222,12 +236,13 @@ GEM
       polyglot
       polyglot (>= 0.3.1)
     trollop (2.1.2)
-    tzinfo (0.3.39)
-    uglifier (2.5.0)
+    tzinfo (0.3.51)
+    uglifier (2.7.2)
       execjs (>= 0.3.0)
       json (>= 1.8.0)
-    uuidtools (2.1.5)
-    websocket-driver (0.3.2)
+    websocket-driver (0.6.4)
+      websocket-extensions (>= 0.1.0)
+    websocket-extensions (0.1.2)
 
 PLATFORMS
   ruby
@@ -237,7 +252,7 @@ DEPENDENCIES
   andand
   arvados (>= 0.1.20150615153458)
   arvados-cli (>= 0.1.20161017193526)
-  coffee-rails (~> 3.2.0)
+  coffee-rails (~> 3.2)
   database_cleaner
   factory_girl_rails
   faye-websocket
@@ -247,22 +262,26 @@ DEPENDENCIES
   mocha
   multi_json
   oj
-  omniauth (= 1.1.1)
-  omniauth-oauth2 (= 1.1.1)
+  omniauth (~> 1.1)
+  omniauth-oauth2 (~> 1.1)
   passenger
   pg
   pg_power
-  puma
-  rails (~> 3.2.0)
+  puma (~> 2.0)
+  rails (~> 3.2)
   ruby-prof
   rvm-capistrano
   safe_yaml
-  sass-rails (>= 3.2.0)
+  sass-rails (~> 3.2)
   simplecov (~> 0.7.1)
   simplecov-rcov
   sshkey
+  test-unit (~> 3.0)
   test_after_commit
   themes_for_rails
   therubyracer
   trollop
-  uglifier (>= 1.0.3)
+  uglifier (~> 2.0)
+
+BUNDLED WITH
+   1.13.6
index 1114ae14dfb5b765d162755261386fe2ed332d92..3876e673fc80c96d19671248e4b14fbfa247351c 100644 (file)
@@ -19,6 +19,7 @@ class ApplicationController < ActionController::Base
   include CurrentApiClient
   include ThemesForRails::ActionController
   include LoadParam
+  include DbCurrentTime
 
   respond_to :json
   protect_from_forgery
@@ -46,7 +47,9 @@ class ApplicationController < ActionController::Base
 
   theme :select_theme
 
-  attr_accessor :resource_attrs
+  attr_writer :resource_attrs
+
+  MAX_UNIQUE_NAME_ATTEMPTS = 10
 
   begin
     rescue_from(Exception,
@@ -59,6 +62,18 @@ class ApplicationController < ActionController::Base
                 :with => :render_not_found)
   end
 
+  def initialize *args
+    super
+    @object = nil
+    @objects = nil
+    @offset = nil
+    @limit = nil
+    @select = nil
+    @distinct = nil
+    @response_resource_name = nil
+    @attrs = nil
+  end
+
   def default_url_options
     if Rails.configuration.host
       {:host => Rails.configuration.host}
@@ -85,13 +100,16 @@ class ApplicationController < ActionController::Base
     if @object.respond_to? :name and params[:ensure_unique_name]
       # Record the original name.  See below.
       name_stem = @object.name
-      counter = 1
+      retries = MAX_UNIQUE_NAME_ATTEMPTS
+    else
+      retries = 0
     end
 
     begin
       @object.save!
     rescue ActiveRecord::RecordNotUnique => rn
-      raise unless params[:ensure_unique_name]
+      raise unless retries > 0
+      retries -= 1
 
       # Dig into the error to determine if it is specifically calling out a
       # (owner_uuid, name) uniqueness violation.  In this specific case, and
@@ -110,13 +128,19 @@ class ApplicationController < ActionController::Base
       detail = err.result.error_field(PG::Result::PG_DIAG_MESSAGE_DETAIL)
       raise unless /^Key \(owner_uuid, name\)=\([a-z0-9]{5}-[a-z0-9]{5}-[a-z0-9]{15}, .*?\) already exists\./.match detail
 
-      # OK, this exception really is just a unique name constraint
-      # violation, and we've been asked to ensure_unique_name.
-      counter += 1
       @object.uuid = nil
-      @object.name = "#{name_stem} (#{counter})"
-      redo
-    end while false
+
+      new_name = "#{name_stem} (#{db_current_time.utc.iso8601(3)})"
+      if new_name == @object.name
+        # If the database is fast enough to do two attempts in the
+        # same millisecond, we need to wait to ensure we try a
+        # different timestamp on each attempt.
+        sleep 0.002
+        new_name = "#{name_stem} (#{db_current_time.utc.iso8601(3)})"
+      end
+      @object.name = new_name
+      retry
+    end
     show
   end
 
@@ -420,7 +444,7 @@ class ApplicationController < ActionController::Base
   end
 
   def find_object_by_uuid
-    if params[:id] and params[:id].match /\D/
+    if params[:id] and params[:id].match(/\D/)
       params[:uuid] = params.delete :id
     end
     @where = { uuid: params[:uuid] }
@@ -570,7 +594,7 @@ class ApplicationController < ActionController::Base
         }
       end
     end
-    super *opts
+    super(*opts)
   end
 
   def select_theme
index 44733cdfb82ff1c21c4ca379a723110ebcaf5721..2beb1e714d7c3fd1af64731c9023b989caa9ed14 100644 (file)
@@ -1,6 +1,8 @@
 require "arvados/keep"
 
 class Arvados::V1::CollectionsController < ApplicationController
+  include DbCurrentTime
+
   def self.limit_index_columns_read
     ["manifest_text"]
   end
@@ -13,6 +15,13 @@ class Arvados::V1::CollectionsController < ApplicationController
     super
   end
 
+  def find_objects_for_index
+    if params[:include_trash] || ['destroy', 'trash'].include?(action_name)
+      @objects = Collection.unscoped.readable_by(*@read_users)
+    end
+    super
+  end
+
   def find_object_by_uuid
     if loc = Keep::Locator.parse(params[:id])
       loc.strip_hints!
@@ -23,20 +32,41 @@ class Arvados::V1::CollectionsController < ApplicationController
           manifest_text: c.signed_manifest_text,
         }
       end
+      true
     else
       super
     end
-    true
   end
 
   def show
     if @object.is_a? Collection
+      # Omit unsigned_manifest_text
+      @select ||= model_class.selectable_attributes - ["unsigned_manifest_text"]
       super
     else
       send_json @object
     end
   end
 
+  def destroy
+    if !@object.is_trashed
+      @object.update_attributes!(trash_at: db_current_time)
+    end
+    earliest_delete = (@object.trash_at +
+                       Rails.configuration.blob_signature_ttl.seconds)
+    if @object.delete_at > earliest_delete
+      @object.update_attributes!(delete_at: earliest_delete)
+    end
+    show
+  end
+
+  def trash
+    if !@object.is_trashed
+      @object.update_attributes!(trash_at: db_current_time)
+    end
+    show
+  end
+
   def find_collections(visited, sp, &b)
     case sp
     when ArvadosModel
@@ -125,9 +155,9 @@ class Arvados::V1::CollectionsController < ApplicationController
           visited[uuid] = job.as_api_response
           if direction == :search_up
             # Follow upstream collections referenced in the script parameters
-            find_collections(visited, job) do |hash, uuid|
+            find_collections(visited, job) do |hash, col_uuid|
               search_edges(visited, hash, :search_up) if hash
-              search_edges(visited, uuid, :search_up) if uuid
+              search_edges(visited, col_uuid, :search_up) if col_uuid
             end
           elsif direction == :search_down
             # Follow downstream job output
@@ -182,10 +212,10 @@ class Arvados::V1::CollectionsController < ApplicationController
   protected
 
   def load_limit_offset_order_params *args
+    super
     if action_name == 'index'
-      # Omit manifest_text from index results unless expressly selected.
-      @select ||= model_class.selectable_attributes - ["manifest_text"]
+      # Omit manifest_text and unsigned_manifest_text from index results unless expressly selected.
+      @select ||= model_class.selectable_attributes - ["manifest_text", "unsigned_manifest_text"]
     end
-    super
   end
 end
index 6e2848ceb53f34165379e3f1afa539d67dbb5651..ed04a4ba8f71c148d424db545c5d9bd7ddc69a7c 100644 (file)
@@ -4,4 +4,5 @@ class Arvados::V1::ContainerRequestsController < ApplicationController
   accept_attribute_as_json :runtime_constraints, Hash
   accept_attribute_as_json :command, Array
   accept_attribute_as_json :filters, Array
+  accept_attribute_as_json :scheduling_parameters, Hash
 end
index 7728ce6d536e90080be8a92d2b51753827e5342e..51f15ad84fd94c7c7834e952a14f5d75d6deaf04 100644 (file)
@@ -3,6 +3,7 @@ class Arvados::V1::ContainersController < ApplicationController
   accept_attribute_as_json :mounts, Hash
   accept_attribute_as_json :runtime_constraints, Hash
   accept_attribute_as_json :command, Array
+  accept_attribute_as_json :scheduling_parameters, Hash
 
   skip_before_filter :find_object_by_uuid, only: [:current]
   skip_before_filter :render_404_if_no_object, only: [:current]
index d6adbf08516a7c2c199cb240017b5e64ede195be..5d91a81074cdfe9e75df182132af4b17f1ff85e3 100644 (file)
@@ -68,9 +68,14 @@ class Arvados::V1::GroupsController < ApplicationController
      Collection,
      Human, Specimen, Trait]
 
-    table_names = klasses.map(&:table_name)
+    table_names = Hash[klasses.collect { |k| [k, k.table_name] }]
+
+    disabled_methods = Rails.configuration.disable_api_methods
+    avail_klasses = table_names.select{|k, t| !disabled_methods.include?(t+'.index')}
+    klasses = avail_klasses.keys
+
     request_filters.each do |col, op, val|
-      if col.index('.') && !table_names.include?(col.split('.', 2)[0])
+      if col.index('.') && !table_names.values.include?(col.split('.', 2)[0])
         raise ArgumentError.new("Invalid attribute '#{col}' in filter")
       end
     end
index 3952db61b08d3c458580893f2c6a49ef85df43aa..2eaeb909cf62e44d6ea9822aefda902a2bd0cd09 100644 (file)
@@ -85,7 +85,7 @@ class Arvados::V1::SchemaController < ApplicationController
       if Rails.application.config.websocket_address
         discovery[:websocketUrl] = Rails.application.config.websocket_address
       elsif ENV['ARVADOS_WEBSOCKETS']
-        discovery[:websocketUrl] = (root_url.sub /^http/, 'ws') + "websocket"
+        discovery[:websocketUrl] = root_url.sub(/^http/, 'ws') + "websocket"
       end
 
       ActiveRecord::Base.descendants.reject(&:abstract_class?).each do |k|
@@ -383,21 +383,21 @@ class Arvados::V1::SchemaController < ApplicationController
               method = d_methods[action.to_sym]
             end
             if ctl_class.respond_to? "_#{action}_requires_parameters".to_sym
-              ctl_class.send("_#{action}_requires_parameters".to_sym).each do |k, v|
+              ctl_class.send("_#{action}_requires_parameters".to_sym).each do |l, v|
                 if v.is_a? Hash
-                  method[:parameters][k] = v
+                  method[:parameters][l] = v
                 else
-                  method[:parameters][k] = {}
+                  method[:parameters][l] = {}
                 end
-                if !method[:parameters][k][:default].nil?
+                if !method[:parameters][l][:default].nil?
                   # The JAVA SDK is sensitive to all values being strings
-                  method[:parameters][k][:default] = method[:parameters][k][:default].to_s
+                  method[:parameters][l][:default] = method[:parameters][l][:default].to_s
                 end
-                method[:parameters][k][:type] ||= 'string'
-                method[:parameters][k][:description] ||= ''
-                method[:parameters][k][:location] = (route.segment_keys.include?(k) ? 'path' : 'query')
-                if method[:parameters][k][:required].nil?
-                  method[:parameters][k][:required] = v != false
+                method[:parameters][l][:type] ||= 'string'
+                method[:parameters][l][:description] ||= ''
+                method[:parameters][l][:location] = (route.segment_keys.include?(l) ? 'path' : 'query')
+                if method[:parameters][l][:required].nil?
+                  method[:parameters][l][:required] = v != false
                 end
               end
             end
index 32adde9507554ee9195bbc812b51cc1d86d753ba..f23cd98c354824b4998373183b2cbaa17b08a715 100644 (file)
@@ -17,7 +17,6 @@ class Arvados::V1::UserAgreementsController < ApplicationController
       # use this installation.
       @objects = []
     else
-      current_user_uuid = current_user.uuid
       act_as_system_user do
         uuids = Link.where("owner_uuid = ? and link_class = ? and name = ? and tail_uuid = ? and head_uuid like ?",
                            system_user_uuid,
@@ -25,7 +24,7 @@ class Arvados::V1::UserAgreementsController < ApplicationController
                            'require',
                            system_user_uuid,
                            Collection.uuid_like_pattern).
-          collect &:head_uuid
+          collect(&:head_uuid)
         @objects = Collection.where('uuid in (?)', uuids)
       end
     end
index 03efed999fcb9791df63d4c6bc8475003f55b4c7..db5e7bd952323f661bbcd11312a937956f4d5044 100644 (file)
@@ -159,7 +159,7 @@ class Arvados::V1::UsersController < ApplicationController
   end
 
   def apply_filters(model_class=nil)
-    return super if @read_users.any? &:is_admin
+    return super if @read_users.any?(&:is_admin)
     if params[:uuid] != current_user.andand.uuid
       # Non-admin index/show returns very basic information about readable users.
       safe_attrs = ["uuid", "is_active", "email", "first_name", "last_name"]
index e6474aa4e0328a6759039921b9962d627b0b374d..99b663da43b8d05fde6db0966fe5da515fdc0d84 100644 (file)
@@ -23,7 +23,7 @@ class Arvados::V1::VirtualMachinesController < ApplicationController
     @users = {}
     User.eager_load(:authorized_keys).
       where('users.uuid in (?)',
-            @vms.map { |vm| vm.login_permissions.map &:tail_uuid }.flatten.uniq).
+            @vms.map { |vm| vm.login_permissions.map(&:tail_uuid) }.flatten.uniq).
       each do |u|
       @users[u.uuid] = u
     end
index 21c8e4710cb5e62dbe224a6034c68ea1bd40b05e..6699f7363b35e9a8b49bb6badc1aba48bc3bd1a0 100644 (file)
@@ -11,7 +11,7 @@ class DatabaseController < ApplicationController
     # we can tell they're not valuable.
     user_uuids = User.
       where('email is null or email not like ?', '%@example.com').
-      collect &:uuid
+      collect(&:uuid)
     fixture_uuids =
       YAML::load_file(File.expand_path('../../../test/fixtures/users.yml',
                                        __FILE__)).
index d8c04a1adbfcd0512bdbf38a4225081709ca2de8..2487f2ecb7db7820dd35d1c5a393fa22dbc0f7cf 100644 (file)
@@ -7,7 +7,7 @@ class ArvadosApiToken
   # Create a new ArvadosApiToken handler
   # +app+  The next layer of the Rack stack.
   def initialize(app = nil, options = nil)
-    @app = app if app.respond_to?(:call)
+    @app = app.respond_to?(:call) ? app : nil
   end
 
   def call env
index 8f82e585df07f431650270997325be9d6c7cf448..08d163e6e24d547f966513dc73f288c92f9ef679 100644 (file)
@@ -44,18 +44,26 @@ class RackSocket
         if forked && EM.reactor_running?
           EM.stop
         end
-        Thread.new {
-          EM.run
-        }
+        Thread.new do
+          begin
+            EM.run
+          ensure
+            ActiveRecord::Base.connection.close
+          end
+        end
         die_gracefully_on_signal
       end
     else
       # faciliates debugging
       Thread.abort_on_exception = true
       # just spawn a thread and start it up
-      Thread.new {
-        EM.run
-      }
+      Thread.new do
+        begin
+          EM.run
+        ensure
+          ActiveRecord::Base.connection.close
+        end
+      end
     end
 
     # Create actual handler instance object from handler class.
index 18d5647cc929e760a72ed48ed709a9d18b8da8a3..fd542ca909e296c1284b06a2843b9859c6acc8d6 100644 (file)
@@ -239,7 +239,7 @@ class ArvadosModel < ActiveRecord::Base
   end
 
   def logged_attributes
-    attributes.except *Rails.configuration.unlogged_attributes
+    attributes.except(*Rails.configuration.unlogged_attributes)
   end
 
   def self.full_text_searchable_columns
@@ -252,12 +252,7 @@ class ArvadosModel < ActiveRecord::Base
     parts = full_text_searchable_columns.collect do |column|
       "coalesce(#{column},'')"
     end
-    # We prepend a space to the tsvector() argument here. Otherwise,
-    # it might start with a column that has its own (non-full-text)
-    # index, which causes Postgres to use the column index instead of
-    # the tsvector index, which causes full text queries to be just as
-    # slow as if we had no index at all.
-    "to_tsvector('english', ' ' || #{parts.join(" || ' ' || ")})"
+    "to_tsvector('english', #{parts.join(" || ' ' || ")})"
   end
 
   def self.apply_filters query, filters
@@ -490,7 +485,7 @@ class ArvadosModel < ActiveRecord::Base
   end
 
   def foreign_key_attributes
-    attributes.keys.select { |a| a.match /_uuid$/ }
+    attributes.keys.select { |a| a.match(/_uuid$/) }
   end
 
   def skip_uuid_read_permission_check
@@ -505,7 +500,7 @@ class ArvadosModel < ActiveRecord::Base
     foreign_key_attributes.each do |attr|
       attr_value = send attr
       if attr_value.is_a? String and
-          attr_value.match /^[0-9a-f]{32,}(\+[@\w]+)*$/
+          attr_value.match(/^[0-9a-f]{32,}(\+[@\w]+)*$/)
         begin
           send "#{attr}=", Collection.normalize_uuid(attr_value)
         rescue
@@ -584,13 +579,12 @@ class ArvadosModel < ActiveRecord::Base
     unless uuid.is_a? String
       return nil
     end
-    resource_class = nil
 
     uuid.match HasUuid::UUID_REGEX do |re|
       return uuid_prefixes[re[1]] if uuid_prefixes[re[1]]
     end
 
-    if uuid.match /.+@.+/
+    if uuid.match(/.+@.+/)
       return Email
     end
 
@@ -603,7 +597,7 @@ class ArvadosModel < ActiveRecord::Base
     if self == ArvadosModel
       # If called directly as ArvadosModel.find_by_uuid rather than via subclass,
       # delegate to the appropriate subclass based on the given uuid.
-      self.resource_class_for_uuid(uuid).find_by_uuid(uuid)
+      self.resource_class_for_uuid(uuid).unscoped.find_by_uuid(uuid)
     else
       super
     end
index 41d5b27093c3ab55c296f7a592b9defb7e25d6dc..00c2501865fa6098244b09487376f83514620e34 100644 (file)
@@ -64,9 +64,9 @@ class Blob
   #   Return value: true if the locator has a valid signature, false otherwise
   #   Arguments: signed_blob_locator, opts
   #
-  def self.verify_signature *args
+  def self.verify_signature(*args)
     begin
-      self.verify_signature! *args
+      self.verify_signature!(*args)
       true
     rescue Blob::InvalidSignatureError
       false
index 8579509de70e9eff1c46d25563ca239fcf9dff8d..f212e3358a4c8729d46f5edd09a2f2226a9b6a1b 100644 (file)
@@ -1,4 +1,5 @@
 require 'arvados/keep'
+require 'sweep_trashed_collections'
 
 class Collection < ArvadosModel
   extend DbCurrentTime
@@ -8,17 +9,21 @@ class Collection < ArvadosModel
 
   serialize :properties, Hash
 
+  before_validation :set_validation_timestamp
   before_validation :default_empty_manifest
   before_validation :check_encoding
   before_validation :check_manifest_validity
   before_validation :check_signatures
   before_validation :strip_signatures_and_update_replication_confirmed
+  before_validation :ensure_trash_at_not_in_past
+  before_validation :sync_trash_state
+  before_validation :default_trash_interval
   validate :ensure_pdh_matches_manifest_text
+  validate :validate_trash_and_delete_timing
   before_save :set_file_names
-  before_save :expires_at_not_in_past
 
-  # Query only undeleted collections by default.
-  default_scope where("expires_at IS NULL or expires_at > statement_timestamp()")
+  # Query only untrashed collections by default.
+  default_scope where("is_trashed = false")
 
   api_accessible :user, extend: :common do |t|
     t.add :name
@@ -26,10 +31,18 @@ class Collection < ArvadosModel
     t.add :properties
     t.add :portable_data_hash
     t.add :signed_manifest_text, as: :manifest_text
+    t.add :manifest_text, as: :unsigned_manifest_text
     t.add :replication_desired
     t.add :replication_confirmed
     t.add :replication_confirmed_at
-    t.add :expires_at
+    t.add :delete_at
+    t.add :trash_at
+    t.add :is_trashed
+  end
+
+  after_initialize do
+    @signatures_checked = false
+    @computed_pdh_for_manifest_text = false
   end
 
   def self.attributes_required_columns
@@ -40,9 +53,10 @@ class Collection < ArvadosModel
                 # API response, and never let clients select the
                 # manifest_text column.
                 #
-                # We need expires_at to determine the correct
-                # timestamp in signed_manifest_text.
-                'manifest_text' => ['manifest_text', 'expires_at'],
+                # We need trash_at and is_trashed to determine the
+                # correct timestamp in signed_manifest_text.
+                'manifest_text' => ['manifest_text', 'trash_at', 'is_trashed'],
+                'unsigned_manifest_text' => ['manifest_text'],
                 )
   end
 
@@ -61,7 +75,9 @@ class Collection < ArvadosModel
     # subsequent passes without checking any signatures. This is
     # important because the signatures have probably been stripped off
     # by the time we get to a second validation pass!
-    return true if @signatures_checked and @signatures_checked == computed_pdh
+    if @signatures_checked && @signatures_checked == computed_pdh
+      return true
+    end
 
     if self.manifest_text_changed?
       # Check permissions on the collection manifest.
@@ -70,7 +86,7 @@ class Collection < ArvadosModel
       api_token = current_api_client_authorization.andand.api_token
       signing_opts = {
         api_token: api_token,
-        now: db_current_time.to_i,
+        now: @validation_timestamp.to_i,
       }
       self.manifest_text.each_line do |entry|
         entry.split.each do |tok|
@@ -197,7 +213,7 @@ class Collection < ArvadosModel
         utf8 = manifest_text
         utf8.force_encoding Encoding::UTF_8
         if utf8.valid_encoding? and utf8 == manifest_text.encode(Encoding::UTF_8)
-          manifest_text = utf8
+          self.manifest_text = utf8
           return true
         end
       rescue
@@ -218,11 +234,15 @@ class Collection < ArvadosModel
   end
 
   def signed_manifest_text
-    if has_attribute? :manifest_text
+    if !has_attribute? :manifest_text
+      return nil
+    elsif is_trashed
+      return manifest_text
+    else
       token = current_api_client_authorization.andand.api_token
       exp = [db_current_time.to_i + Rails.configuration.blob_signature_ttl,
-             expires_at].compact.map(&:to_i).min
-      @signed_manifest_text = self.class.sign_manifest manifest_text, token, exp
+             trash_at].compact.map(&:to_i).min
+      self.class.sign_manifest manifest_text, token, exp
     end
   end
 
@@ -283,10 +303,10 @@ class Collection < ArvadosModel
     hash_part = nil
     size_part = nil
     uuid.split('+').each do |token|
-      if token.match /^[0-9a-f]{32,}$/
+      if token.match(/^[0-9a-f]{32,}$/)
         raise "uuid #{uuid} has multiple hash parts" if hash_part
         hash_part = token
-      elsif token.match /^\d+$/
+      elsif token.match(/^\d+$/)
         raise "uuid #{uuid} has multiple size parts" if size_part
         size_part = token
       end
@@ -360,6 +380,11 @@ class Collection < ArvadosModel
     super - ["manifest_text"]
   end
 
+  def self.where *args
+    SweepTrashedCollections.sweep_if_stale
+    super
+  end
+
   protected
   def portable_manifest_text
     self.class.munge_manifest_locators(manifest_text) do |match|
@@ -396,13 +421,65 @@ class Collection < ArvadosModel
     super
   end
 
-  # If expires_at is being changed to a time in the past, change it to
+  # Use a single timestamp for all validations, even though each
+  # validation runs at a different time.
+  def set_validation_timestamp
+    @validation_timestamp = db_current_time
+  end
+
+  # If trash_at is being changed to a time in the past, change it to
   # now. This allows clients to say "expires {client-current-time}"
   # without failing due to clock skew, while avoiding odd log entries
   # like "expiry date changed to {1 year ago}".
-  def expires_at_not_in_past
-    if expires_at_changed? and expires_at
-      self.expires_at = [db_current_time, expires_at].max
+  def ensure_trash_at_not_in_past
+    if trash_at_changed? && trash_at
+      self.trash_at = [@validation_timestamp, trash_at].max
     end
   end
+
+  # Caller can move into/out of trash by setting/clearing is_trashed
+  # -- however, if the caller also changes trash_at, then any changes
+  # to is_trashed are ignored.
+  def sync_trash_state
+    if is_trashed_changed? && !trash_at_changed?
+      if is_trashed
+        self.trash_at = @validation_timestamp
+      else
+        self.trash_at = nil
+        self.delete_at = nil
+      end
+    end
+    self.is_trashed = trash_at && trash_at <= @validation_timestamp || false
+    true
+  end
+
+  # If trash_at is updated without touching delete_at, automatically
+  # update delete_at to a sensible value.
+  def default_trash_interval
+    if trash_at_changed? && !delete_at_changed?
+      if trash_at.nil?
+        self.delete_at = nil
+      else
+        self.delete_at = trash_at + Rails.configuration.default_trash_lifetime.seconds
+      end
+    end
+  end
+
+  def validate_trash_and_delete_timing
+    if trash_at.nil? != delete_at.nil?
+      errors.add :delete_at, "must be set if trash_at is set, and must be nil otherwise"
+    end
+
+    earliest_delete = ([@validation_timestamp, trash_at_was].compact.min +
+                       Rails.configuration.blob_signature_ttl.seconds)
+    if delete_at && delete_at < earliest_delete
+      errors.add :delete_at, "#{delete_at} is too soon: earliest allowed is #{earliest_delete}"
+    end
+
+    if delete_at && delete_at < trash_at
+      errors.add :delete_at, "must not be earlier than trash_at"
+    end
+
+    true
+  end
 end
index 71ea57fb95ce15f1d3c9d95479a0c3ddf145b446..419eca2e01fd3002762124045707b5b8dc38bf8f 100644 (file)
@@ -16,13 +16,13 @@ class CommitAncestor < ActiveRecord::Base
     @gitdirbase = Rails.configuration.git_repositories_dir
     self.is = nil
     Dir.foreach @gitdirbase do |repo|
-      next if repo.match /^\./
+      next if repo.match(/^\./)
       git_dir = repo.match(/\.git$/) ? repo : File.join(repo, '.git')
       repo_name = repo.sub(/\.git$/, '')
       ENV['GIT_DIR'] = File.join(@gitdirbase, git_dir)
-      IO.foreach("|git rev-list --format=oneline '#{self.descendant.gsub /[^0-9a-f]/,""}'") do |line|
+      IO.foreach("|git rev-list --format=oneline '#{self.descendant.gsub(/[^0-9a-f]/,"")}'") do |line|
         self.is = false
-        sha1, message = line.strip.split(" ", 2)
+        sha1, _ = line.strip.split(" ", 2)
         if sha1 == self.ancestor
           self.is = true
           break
index b1ea9bd230a47e2382dbb12ac0c0d6bee6929588..7731a8d79ddc86da1c315cf4b2a554eab7ab6ce2 100644 (file)
@@ -11,6 +11,7 @@ class Container < ArvadosModel
   serialize :mounts, Hash
   serialize :runtime_constraints, Hash
   serialize :command, Array
+  serialize :scheduling_parameters, Hash
 
   before_validation :fill_field_defaults, :if => :new_record?
   before_validation :set_timestamps
@@ -44,6 +45,7 @@ class Container < ArvadosModel
     t.add :started_at
     t.add :state
     t.add :auth_uuid
+    t.add :scheduling_parameters
   end
 
   # Supported states for a container
@@ -180,6 +182,7 @@ class Container < ArvadosModel
     self.mounts ||= {}
     self.cwd ||= "."
     self.priority ||= 1
+    self.scheduling_parameters ||= {}
   end
 
   def permission_to_create
@@ -222,7 +225,7 @@ class Container < ArvadosModel
     if self.new_record?
       permitted.push(:owner_uuid, :command, :container_image, :cwd,
                      :environment, :mounts, :output_path, :priority,
-                     :runtime_constraints)
+                     :runtime_constraints, :scheduling_parameters)
     end
 
     case self.state
@@ -326,6 +329,9 @@ class Container < ArvadosModel
     if self.runtime_constraints_changed?
       self.runtime_constraints = self.class.deep_sort_hash(self.runtime_constraints)
     end
+    if self.scheduling_parameters_changed?
+      self.scheduling_parameters = self.class.deep_sort_hash(self.scheduling_parameters)
+    end
   end
 
   def handle_completed
@@ -335,7 +341,7 @@ class Container < ArvadosModel
       act_as_system_user do
 
         if self.state == Cancelled
-          retryable_requests = ContainerRequest.where("priority > 0 and state = 'Committed' and container_count < container_count_max")
+          retryable_requests = ContainerRequest.where("container_uuid = ? and priority > 0 and state = 'Committed' and container_count < container_count_max", uuid)
         else
           retryable_requests = []
         end
@@ -348,7 +354,8 @@ class Container < ArvadosModel
             output_path: self.output_path,
             container_image: self.container_image,
             mounts: self.mounts,
-            runtime_constraints: self.runtime_constraints
+            runtime_constraints: self.runtime_constraints,
+            scheduling_parameters: self.scheduling_parameters
           }
           c = Container.create! c_attrs
           retryable_requests.each do |cr|
index 05738de81e50627654e62e3f3a34d6cc46754460..a264bbfe813e73bd0e22526ac416deb3cc8ca007 100644 (file)
@@ -11,9 +11,11 @@ class ContainerRequest < ArvadosModel
   serialize :mounts, Hash
   serialize :runtime_constraints, Hash
   serialize :command, Array
+  serialize :scheduling_parameters, Hash
 
   before_validation :fill_field_defaults, :if => :new_record?
   before_validation :validate_runtime_constraints
+  before_validation :validate_scheduling_parameters
   before_validation :set_container
   validates :command, :container_image, :output_path, :cwd, :presence => true
   validate :validate_state_change
@@ -33,13 +35,17 @@ class ContainerRequest < ArvadosModel
     t.add :environment
     t.add :expires_at
     t.add :filters
+    t.add :log_uuid
     t.add :mounts
     t.add :name
+    t.add :output_name
     t.add :output_path
+    t.add :output_uuid
     t.add :priority
     t.add :properties
     t.add :requesting_container_uuid
     t.add :runtime_constraints
+    t.add :scheduling_parameters
     t.add :state
     t.add :use_existing
   end
@@ -79,21 +85,52 @@ class ContainerRequest < ArvadosModel
   # Finalize the container request after the container has
   # finished/cancelled.
   def finalize!
-    update_attributes!(state: Final)
+    out_coll = nil
+    log_coll = nil
     c = Container.find_by_uuid(container_uuid)
     ['output', 'log'].each do |out_type|
       pdh = c.send(out_type)
       next if pdh.nil?
+      if self.output_name and out_type == 'output'
+        coll_name = self.output_name
+      else
+        coll_name = "Container #{out_type} for request #{uuid}"
+      end
       manifest = Collection.where(portable_data_hash: pdh).first.manifest_text
-      Collection.create!(owner_uuid: owner_uuid,
-                         manifest_text: manifest,
-                         portable_data_hash: pdh,
-                         name: "Container #{out_type} for request #{uuid}",
-                         properties: {
-                           'type' => out_type,
-                           'container_request' => uuid,
-                         })
+      begin
+        coll = Collection.create!(owner_uuid: owner_uuid,
+                                  manifest_text: manifest,
+                                  portable_data_hash: pdh,
+                                  name: coll_name,
+                                  properties: {
+                                    'type' => out_type,
+                                    'container_request' => uuid,
+                                  })
+      rescue ActiveRecord::RecordNotUnique => rn
+        # In case this is executed as part of a transaction: When a Postgres exception happens,
+        # the following statements on the same transaction become invalid, so a rollback is
+        # needed. One example are Unit Tests, every test is enclosed inside a transaction so
+        # that the database can be reverted before every new test starts.
+        # See: http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html#module-ActiveRecord::Transactions::ClassMethods-label-Exception+handling+and+rolling+back
+        ActiveRecord::Base.connection.execute 'ROLLBACK'
+        raise unless out_type == 'output' and self.output_name
+        # Postgres specific unique name check. See ApplicationController#create for
+        # a detailed explanation.
+        raise unless rn.original_exception.is_a? PG::UniqueViolation
+        err = rn.original_exception
+        detail = err.result.error_field(PG::Result::PG_DIAG_MESSAGE_DETAIL)
+        raise unless /^Key \(owner_uuid, name\)=\([a-z0-9]{5}-[a-z0-9]{5}-[a-z0-9]{15}, .*?\) already exists\./.match detail
+        # Output collection name collision detected: append a timestamp.
+        coll_name = "#{self.output_name} #{Time.now.getgm.strftime('%FT%TZ')}"
+        retry
+      end
+      if out_type == 'output'
+        out_coll = coll.uuid
+      else
+        log_coll = coll.uuid
+      end
     end
+    update_attributes!(state: Final, output_uuid: out_coll, log_uuid: log_coll)
   end
 
   protected
@@ -105,6 +142,7 @@ class ContainerRequest < ArvadosModel
     self.mounts ||= {}
     self.cwd ||= "."
     self.container_count_max ||= Rails.configuration.container_count_max
+    self.scheduling_parameters ||= {}
   end
 
   # Create a new container (or find an existing one) to satisfy this
@@ -126,6 +164,7 @@ class ContainerRequest < ArvadosModel
       if not reusable.nil?
         reusable
       else
+        c_attrs[:scheduling_parameters] = self.scheduling_parameters
         Container.create!(c_attrs)
       end
     end
@@ -234,6 +273,17 @@ class ContainerRequest < ArvadosModel
     end
   end
 
+  def validate_scheduling_parameters
+    if self.state == Committed
+      if scheduling_parameters.include? 'partitions' and
+         (!scheduling_parameters['partitions'].is_a?(Array) ||
+          scheduling_parameters['partitions'].reject{|x| !x.is_a?(String)}.size !=
+            scheduling_parameters['partitions'].size)
+            errors.add :scheduling_parameters, "partitions must be an array of strings"
+      end
+    end
+  end
+
   def validate_change
     permitted = [:owner_uuid]
 
@@ -244,7 +294,8 @@ class ContainerRequest < ArvadosModel
                      :container_image, :cwd, :description, :environment,
                      :filters, :mounts, :name, :output_path, :priority,
                      :properties, :requesting_container_uuid, :runtime_constraints,
-                     :state, :container_uuid, :use_existing
+                     :state, :container_uuid, :use_existing, :scheduling_parameters,
+                     :output_name
 
     when Committed
       if container_uuid.nil?
@@ -256,14 +307,16 @@ class ContainerRequest < ArvadosModel
       end
 
       # Can update priority, container count, name and description
-      permitted.push :priority, :container_count, :container_count_max, :container_uuid, :name, :description
+      permitted.push :priority, :container_count, :container_count_max, :container_uuid,
+                     :name, :description
 
       if self.state_changed?
         # Allow create-and-commit in a single operation.
         permitted.push :command, :container_image, :cwd, :description, :environment,
                        :filters, :mounts, :name, :output_path, :properties,
                        :requesting_container_uuid, :runtime_constraints,
-                       :state, :container_uuid
+                       :state, :container_uuid, :use_existing, :scheduling_parameters,
+                       :output_name
       end
 
     when Final
@@ -271,8 +324,8 @@ class ContainerRequest < ArvadosModel
         errors.add :state, "of container request can only be set to Final by system."
       end
 
-      if self.state_changed? || self.name_changed? || self.description_changed?
-          permitted.push :state, :name, :description
+      if self.state_changed? || self.name_changed? || self.description_changed? || self.output_uuid_changed? || self.log_uuid_changed?
+          permitted.push :state, :name, :description, :output_uuid, :log_uuid
       else
         errors.add :state, "does not allow updates"
       end
index 30ca7f8cb29581b0c65b0a70dd49c02f3b59f753..2ae713928113af9817c0598ee9f25841d00cf4e9 100644 (file)
@@ -67,6 +67,10 @@ class Job < ArvadosModel
             (Complete = 'Complete'),
            ]
 
+  after_initialize do
+    @need_crunch_dispatch_trigger = false
+  end
+
   def assert_finished
     update_attributes(finished_at: finished_at || db_current_time,
                       success: success.nil? ? false : success,
@@ -115,6 +119,10 @@ class Job < ArvadosModel
     super - ["script_parameters_digest"]
   end
 
+  def self.full_text_searchable_columns
+    super - ["script_parameters_digest"]
+  end
+
   def self.load_job_specific_filters attrs, orig_filters, read_users
     # Convert Job-specific @filters entries into general SQL filters.
     script_info = {"repository" => nil, "script" => nil}
@@ -336,7 +344,7 @@ class Job < ArvadosModel
         assign_uuid
         Commit.tag_in_internal_repository repository, script_version, uuid
       rescue
-        uuid = uuid_was
+        self.uuid = uuid_was
         raise
       end
     end
@@ -415,7 +423,7 @@ class Job < ArvadosModel
           output_changed? or
           log_changed? or
           tasks_summary_changed? or
-          state_changed? or
+          (state_changed? && state != Cancelled) or
           components_changed?
         logger.warn "User #{current_user.uuid if current_user} tried to change protected job attributes on locked #{self.class.to_s} #{uuid_was}"
         return false
@@ -565,24 +573,6 @@ class Job < ArvadosModel
   end
 
   def ensure_no_collection_uuids_in_script_params
-    # recursive_hash_search searches recursively through hashes and
-    # arrays in 'thing' for string fields matching regular expression
-    # 'pattern'.  Returns true if pattern is found, false otherwise.
-    def recursive_hash_search thing, pattern
-      if thing.is_a? Hash
-        thing.each do |k, v|
-          return true if recursive_hash_search v, pattern
-        end
-      elsif thing.is_a? Array
-        thing.each do |k|
-          return true if recursive_hash_search k, pattern
-        end
-      elsif thing.is_a? String
-        return true if thing.match pattern
-      end
-      false
-    end
-
     # Fail validation if any script_parameters field includes a string containing a
     # collection uuid pattern.
     if self.script_parameters_changed?
@@ -593,4 +583,22 @@ class Job < ArvadosModel
     end
     true
   end
+
+  # recursive_hash_search searches recursively through hashes and
+  # arrays in 'thing' for string fields matching regular expression
+  # 'pattern'.  Returns true if pattern is found, false otherwise.
+  def recursive_hash_search thing, pattern
+    if thing.is_a? Hash
+      thing.each do |k, v|
+        return true if recursive_hash_search v, pattern
+      end
+    elsif thing.is_a? Array
+      thing.each do |k|
+        return true if recursive_hash_search k, pattern
+      end
+    elsif thing.is_a? String
+      return true if thing.match pattern
+    end
+    false
+  end
 end
index 24872b21ec7163852cf86d0c0ceb3d3b41f13608..649a6f80c281fc83f2d6eaf4b0fc80fe82c28ce6 100644 (file)
@@ -8,7 +8,6 @@ class Link < ArvadosModel
   after_update :maybe_invalidate_permissions_cache
   after_create :maybe_invalidate_permissions_cache
   after_destroy :maybe_invalidate_permissions_cache
-  attr_accessor :head_kind, :tail_kind
   validate :name_links_are_obsolete
 
   api_accessible :user, extend: :common do |t|
index 7eab402609b482a238f8a40313bf622ece86c3c0..3207d1f288f2f264c671d6709063d93140ce3fec 100644 (file)
@@ -4,7 +4,6 @@ class Log < ArvadosModel
   include CommonApiTemplate
   serialize :properties, Hash
   before_validation :set_default_event_at
-  attr_accessor :object, :object_kind
   after_save :send_notify
 
   api_accessible :user, extend: :common do |t|
index e470e4c2bd9c47a45b395a4c90f4814edf89a417..18550204669c7cc6353d87cfc863bcbf3c4d876a 100644 (file)
@@ -32,6 +32,10 @@ class Node < ArvadosModel
     t.add lambda { |x| Rails.configuration.compute_node_nameservers }, :as => :nameservers
   end
 
+  after_initialize do
+    @bypass_arvados_authorization = false
+  end
+
   def domain
     super || Rails.configuration.compute_node_domain
   end
@@ -226,7 +230,7 @@ class Node < ArvadosModel
     (0..Rails.configuration.max_compute_nodes-1).each do |slot_number|
       hostname = hostname_for_slot(slot_number)
       hostfile = File.join Rails.configuration.dns_server_conf_dir, "#{hostname}.conf"
-      if !File.exists? hostfile
+      if !File.exist? hostfile
         n = Node.where(:slot_number => slot_number).first
         if n.nil? or n.ip_address.nil?
           dns_server_update(hostname, UNUSED_NODE_IP)
index 77a0736b000d669d298abc93ad95d12417e8a3d1..f84c4a310fd19904f4f5f85cdbea23a4c0b83770 100644 (file)
@@ -10,6 +10,7 @@ class PipelineInstance < ArvadosModel
   before_validation :bootstrap_components
   before_validation :update_state
   before_validation :verify_status
+  before_validation :update_timestamps_when_state_changes
   before_create :set_state_before_save
   before_save :set_state_before_save
 
@@ -136,4 +137,17 @@ class PipelineInstance < ArvadosModel
     end
   end
 
+  def update_timestamps_when_state_changes
+    return if not (state_changed? or new_record?)
+
+    case state
+    when RunningOnServer, RunningOnClient
+      self.started_at ||= db_current_time
+    when Failed, Complete
+      current_time = db_current_time
+      self.started_at ||= current_time
+      self.finished_at ||= current_time
+    end
+  end
+
 end
index f361a49db5dcd49b649d7e7f79c255e214eae97a..13b00df544cf1b20b3378d08aba78d264d1f570a 100644 (file)
@@ -86,7 +86,7 @@ class Repository < ArvadosModel
       prefix_match = Regexp.escape(owner.username + "/")
       errmsg_start = "must be the owner's username, then '/', then"
     end
-    if not /^#{prefix_match}[A-Za-z][A-Za-z0-9]*$/.match(name)
+    if not (/^#{prefix_match}[A-Za-z][A-Za-z0-9]*$/.match(name))
       errors.add(:name,
                  "#{errmsg_start} a letter followed by alphanumerics")
       false
index 9363cc4f02aa04d08552b9e343bbda9f8dcda5c1..78ec7bea1ec1b9b1ac72cce590f1951dc87279ac 100644 (file)
@@ -57,6 +57,14 @@ class User < ArvadosModel
 
   ALL_PERMISSIONS = {read: true, write: true, manage: true}
 
+  # Map numeric permission levels (see lib/create_permission_view.sql)
+  # back to read/write/manage flags.
+  PERMS_FOR_VAL =
+    [{},
+     {read: true},
+     {read: true, write: true},
+     {read: true, write: true, manage: true}]
+
   def full_name
     "#{first_name} #{last_name}".strip
   end
@@ -64,7 +72,7 @@ class User < ArvadosModel
   def is_invited
     !!(self.is_active ||
        Rails.configuration.new_users_are_active ||
-       self.groups_i_can(:read).select { |x| x.match /-f+$/ }.first)
+       self.groups_i_can(:read).select { |x| x.match(/-f+$/) }.first)
   end
 
   def groups_i_can(verb)
@@ -135,60 +143,38 @@ class User < ArvadosModel
   # Return a hash of {group_uuid: perm_hash} where perm_hash[:read]
   # and perm_hash[:write] are true if this user can read and write
   # objects owned by group_uuid.
-  #
-  # The permission graph is built by repeatedly enumerating all
-  # permission links reachable from self.uuid, and then calling
-  # search_permissions
   def calculate_group_permissions
-      permissions_from = {}
-      todo = {self.uuid => true}
-      done = {}
-      # Build the equivalence class of permissions starting with
-      # self.uuid. On each iteration of this loop, todo contains
-      # the next set of uuids in the permission equivalence class
-      # to evaluate.
-      while !todo.empty?
-        lookup_uuids = todo.keys
-        lookup_uuids.each do |uuid| done[uuid] = true end
-        todo = {}
-        newgroups = []
-        # include all groups owned by the current set of uuids.
-        Group.where('owner_uuid in (?)', lookup_uuids).each do |group|
-          newgroups << [group.owner_uuid, group.uuid, 'can_manage']
-        end
-        # add any permission links from the current lookup_uuids to a Group.
-        Link.where('link_class = ? and tail_uuid in (?) and ' \
-                   '(head_uuid like ? or (name = ? and head_uuid like ?))',
-                   'permission',
-                   lookup_uuids,
-                   Group.uuid_like_pattern,
-                   'can_manage',
-                   User.uuid_like_pattern).each do |link|
-          newgroups << [link.tail_uuid, link.head_uuid, link.name]
-        end
-        newgroups.each do |tail_uuid, head_uuid, perm_name|
-          unless done.has_key? head_uuid
-            todo[head_uuid] = true
-          end
-          link_permissions = {}
-          case perm_name
-          when 'can_read'
-            link_permissions = {read:true}
-          when 'can_write'
-            link_permissions = {read:true,write:true}
-          when 'can_manage'
-            link_permissions = ALL_PERMISSIONS
-          end
-          permissions_from[tail_uuid] ||= {}
-          permissions_from[tail_uuid][head_uuid] ||= {}
-          link_permissions.each do |k,v|
-            permissions_from[tail_uuid][head_uuid][k] ||= v
-          end
-        end
+    conn = ActiveRecord::Base.connection
+    self.class.transaction do
+      # Check whether the temporary view has already been created
+      # during this connection. If not, create it.
+      conn.exec_query 'SAVEPOINT check_permission_view'
+      begin
+        conn.exec_query('SELECT 1 FROM permission_view LIMIT 0')
+      rescue
+        conn.exec_query 'ROLLBACK TO SAVEPOINT check_permission_view'
+        sql = File.read(Rails.root.join('lib', 'create_permission_view.sql'))
+        conn.exec_query(sql)
+      ensure
+        conn.exec_query 'RELEASE SAVEPOINT check_permission_view'
       end
-      perms = search_permissions(self.uuid, permissions_from)
-      Rails.cache.write "groups_for_user_#{self.uuid}", perms
-      perms
+    end
+
+    group_perms = {}
+    conn.exec_query('SELECT target_owner_uuid, max(perm_level)
+                    FROM permission_view
+                    WHERE user_uuid = $1
+                    AND target_owner_uuid IS NOT NULL
+                    GROUP BY target_owner_uuid',
+                    # "name" arg is a query label that appears in logs:
+                    "group_permissions for #{uuid}",
+                    # "binds" arg is an array of [col_id, value] for '$1' vars:
+                    [[nil, uuid]],
+                    ).rows.each do |group_uuid, max_p_val|
+      group_perms[group_uuid] = PERMS_FOR_VAL[max_p_val.to_i]
+    end
+    Rails.cache.write "groups_for_user_#{self.uuid}", group_perms
+    group_perms
   end
 
   # Return a hash of {group_uuid: perm_hash} where perm_hash[:read]
@@ -242,7 +228,7 @@ class User < ArvadosModel
 
     # delete "All users" group read permissions for this user
     group = Group.where(name: 'All users').select do |g|
-      g[:uuid].match /-f+$/
+      g[:uuid].match(/-f+$/)
     end.first
     Link.destroy_all(tail_uuid: self.uuid,
                      head_uuid: group[:uuid],
index a9aa953f9f36e948dc57d34336c7d3f1cc1df43c..bb1355d030a74fa4594ea1dee54ef1a91ef70ef2 100644 (file)
@@ -180,9 +180,15 @@ common:
   # The default is 2 weeks.
   blob_signature_ttl: 1209600
 
-  # Default lifetime for ephemeral collections: 2 weeks.
+  # Default lifetime for ephemeral collections: 2 weeks. This must not
+  # be less than blob_signature_ttl.
   default_trash_lifetime: 1209600
 
+  # Interval (seconds) between trash sweeps. During a trash sweep,
+  # collections are marked as trash if their trash_at time has
+  # arrived, and deleted if their delete_at time has arrived.
+  trash_sweep_interval: 60
+
   # Maximum characters of (JSON-encoded) query parameters to include
   # in each request log entry. When params exceed this size, they will
   # be JSON-encoded, truncated to this size, and logged as
@@ -444,3 +450,5 @@ test:
   workbench_address: https://localhost:3001/
   git_repositories_dir: <%= Rails.root.join 'tmp', 'git', 'test' %>
   git_internal_dir: <%= Rails.root.join 'tmp', 'internal.git' %>
+  websocket_address: <% if ENV['ARVADOS_TEST_EXPERIMENTAL_WS'] %>"wss://0.0.0.0:<%= ENV['ARVADOS_TEST_WSS_PORT'] %>/websocket"<% else %>false<% end %>
+  trash_sweep_interval: -1
index 4211df29d099cecaa33c8f54ef9dfcbff11dfac1..f3f6424b2dac260ac1973859296aa0dec5c42070 100644 (file)
@@ -35,5 +35,22 @@ module Server
     config.filter_parameters += [:password]
 
     I18n.enforce_available_locales = false
+
+    # Before using the filesystem backend for Rails.cache, check
+    # whether we own the relevant directory. If we don't, using it is
+    # likely to either fail or (if we're root) pollute it and cause
+    # other processes to fail later.
+    default_cache_path = Rails.root.join('tmp', 'cache')
+    if not File.owned?(default_cache_path)
+      if File.exist?(default_cache_path)
+        why = "owner (uid=#{File::Stat.new(default_cache_path).uid}) " +
+          "is not me (uid=#{Process.euid})"
+      else
+        why = "does not exist"
+      end
+      STDERR.puts("Defaulting to memory cache, " +
+                  "because #{default_cache_path} #{why}")
+      config.cache_store = :memory_store
+    end
   end
 end
index 4489e58688ca642d8e0e9489f6896f49f9b89da6..f2830ae3166dc7fc2849feff72258dccca1e5f97 100644 (file)
@@ -3,4 +3,4 @@ require 'rubygems'
 # Set up gems listed in the Gemfile.
 ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
 
-require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE'])
+require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
index 79bca3af389506f6bf63ee594b76399e6164514a..b6dadf7e2e386220fd8c1eaa2c9de0f7ea653a08 100644 (file)
@@ -10,8 +10,8 @@
 # end
 
 ActiveSupport::Inflector.inflections do |inflect|
-  inflect.plural /^([Ss]pecimen)$/i, '\1s'
-  inflect.singular /^([Ss]pecimen)s?/i, '\1'
-  inflect.plural /^([Hh]uman)$/i, '\1s'
-  inflect.singular /^([Hh]uman)s?/i, '\1'
+  inflect.plural(/^([Ss]pecimen)$/i, '\1s')
+  inflect.singular(/^([Ss]pecimen)s?/i, '\1')
+  inflect.plural(/^([Hh]uman)$/i, '\1s')
+  inflect.singular(/^([Hh]uman)s?/i, '\1')
 end
index 76234d3e4b0f6ab148f73cb7a1242af1eacefb6a..fd3c9773933703a58f06da0aaff3ad837cad41af 100644 (file)
@@ -6,7 +6,7 @@ rescue LoadError
   # configured by application.yml (i.e., here!) instead.
 end
 
-if (File.exists?(File.expand_path '../omniauth.rb', __FILE__) and
+if (File.exist?(File.expand_path '../omniauth.rb', __FILE__) and
     not defined? WARNED_OMNIAUTH_CONFIG)
   Rails.logger.warn <<-EOS
 DEPRECATED CONFIGURATION:
@@ -26,7 +26,7 @@ $application_config = {}
 
 %w(application.default application).each do |cfgfile|
   path = "#{::Rails.root.to_s}/config/#{cfgfile}.yml"
-  if File.exists? path
+  if File.exist? path
     yaml = ERB.new(IO.read path).result(binding)
     confs = YAML.load(yaml, deserialize_symbols: true)
     # Ignore empty YAML file:
index 7e2612377434b9e3bfc245a8b4dc6143d6ad00c6..1ae531c169af05428f390953a344b1d24cafa4b0 100644 (file)
@@ -7,6 +7,6 @@ require_relative 'load_config.rb'
 
 if Rails.env == 'development'
   Dir.foreach("#{Rails.root}/app/models") do |model_file|
-    require_dependency model_file if model_file.match /\.rb$/
+    require_dependency model_file if model_file.match(/\.rb$/)
   end
 end
index f28390489dca3f42e14b7274407881009cc80b2f..f89d2c16a8881f66444738ea6a7c6299759fd52f 100644 (file)
@@ -22,6 +22,7 @@ Server::Application.routes.draw do
       resources :collections do
         get 'provenance', on: :member
         get 'used_by', on: :member
+        post 'trash', on: :member
       end
       resources :groups do
         get 'contents', on: :collection
diff --git a/services/api/db/migrate/20161111143147_add_scheduling_parameters_to_container.rb b/services/api/db/migrate/20161111143147_add_scheduling_parameters_to_container.rb
new file mode 100644 (file)
index 0000000..1b317cf
--- /dev/null
@@ -0,0 +1,6 @@
+class AddSchedulingParametersToContainer < ActiveRecord::Migration
+  def change
+    add_column :containers, :scheduling_parameters, :text
+    add_column :container_requests, :scheduling_parameters, :text
+  end
+end
diff --git a/services/api/db/migrate/20161115171221_add_output_and_log_uuid_to_container_request.rb b/services/api/db/migrate/20161115171221_add_output_and_log_uuid_to_container_request.rb
new file mode 100644 (file)
index 0000000..e38bf7c
--- /dev/null
@@ -0,0 +1,22 @@
+require 'has_uuid'
+
+class AddOutputAndLogUuidToContainerRequest < ActiveRecord::Migration
+  extend HasUuid::ClassMethods
+
+  def up
+    add_column :container_requests, :output_uuid, :string
+    add_column :container_requests, :log_uuid, :string
+
+    no_such_out_coll = Server::Application.config.uuid_prefix + '-' + '4zz18' + '-xxxxxxxxxxxxxxx'
+    no_such_log_coll = Server::Application.config.uuid_prefix + '-' + '4zz18' + '-yyyyyyyyyyyyyyy'
+
+    update_sql <<-EOS
+update container_requests set output_uuid = ('#{no_such_out_coll}'), log_uuid = ('#{no_such_log_coll}');
+EOS
+  end
+
+  def down
+    remove_column :container_requests, :log_uuid
+    remove_column :container_requests, :output_uuid
+  end
+end
diff --git a/services/api/db/migrate/20161115174218_add_output_and_log_uuids_to_container_request_search_index.rb b/services/api/db/migrate/20161115174218_add_output_and_log_uuids_to_container_request_search_index.rb
new file mode 100644 (file)
index 0000000..b069d02
--- /dev/null
@@ -0,0 +1,21 @@
+class AddOutputAndLogUuidsToContainerRequestSearchIndex < ActiveRecord::Migration
+  def up
+    begin
+      remove_index :container_requests, :name => 'container_requests_search_index'
+    rescue
+    end
+    add_index :container_requests,
+              ["uuid", "owner_uuid", "modified_by_client_uuid", "modified_by_user_uuid", "name", "state", "requesting_container_uuid", "container_uuid", "container_image", "cwd", "output_path", "output_uuid", "log_uuid"],
+              name: "container_requests_search_index"
+  end
+
+  def down
+    begin
+      remove_index :container_requests, :name => 'container_requests_search_index'
+    rescue
+    end
+         add_index :container_requests,
+              ["uuid", "owner_uuid", "modified_by_client_uuid", "modified_by_user_uuid", "name", "state", "requesting_container_uuid", "container_uuid", "container_image", "cwd", "output_path"],
+              name: "container_requests_search_index"
+  end
+end
diff --git a/services/api/db/migrate/20161213172944_full_text_search_indexes.rb b/services/api/db/migrate/20161213172944_full_text_search_indexes.rb
new file mode 100644 (file)
index 0000000..aac3773
--- /dev/null
@@ -0,0 +1,33 @@
+class FullTextSearchIndexes < ActiveRecord::Migration
+  def fts_indexes
+    {
+      "collections" => "collections_full_text_search_idx",
+      "container_requests" => "container_requests_full_text_search_idx",
+      "groups" => "groups_full_text_search_idx",
+      "jobs" => "jobs_full_text_search_idx",
+      "pipeline_instances" => "pipeline_instances_full_text_search_idx",
+      "pipeline_templates" => "pipeline_templates_full_text_search_idx",
+      "workflows" => "workflows_full_text_search_idx",
+    }
+  end
+
+  def up
+    # remove existing fts indexes and create up to date ones with no leading space
+    fts_indexes.each do |t, i|
+      t.classify.constantize.reset_column_information
+      ActiveRecord::Base.connection.indexes(t).each do |idx|
+        if idx.name == i
+          remove_index t.to_sym, :name => i
+          break
+        end
+      end
+      execute "CREATE INDEX #{i} ON #{t} USING gin(#{t.classify.constantize.full_text_tsvector});"
+    end
+  end
+
+  def down
+    fts_indexes.each do |t, i|
+      remove_index t.to_sym, :name => i
+    end
+  end
+end
diff --git a/services/api/db/migrate/20161222153434_split_expiry_to_trash_and_delete.rb b/services/api/db/migrate/20161222153434_split_expiry_to_trash_and_delete.rb
new file mode 100644 (file)
index 0000000..13e4419
--- /dev/null
@@ -0,0 +1,42 @@
+class SplitExpiryToTrashAndDelete < ActiveRecord::Migration
+  def up
+    Collection.transaction do
+      add_column(:collections, :trash_at, :datetime)
+      add_index(:collections, :trash_at)
+      add_column(:collections, :is_trashed, :boolean, null: false, default: false)
+      add_index(:collections, :is_trashed)
+      rename_column(:collections, :expires_at, :delete_at)
+      add_index(:collections, :delete_at)
+
+      Collection.reset_column_information
+      Collection.
+        where('delete_at is not null and delete_at <= statement_timestamp()').
+        delete_all
+      Collection.
+        where('delete_at is not null').
+        update_all('is_trashed = true, trash_at = statement_timestamp()')
+      add_index(:collections, [:owner_uuid, :name],
+                unique: true,
+                where: 'is_trashed = false',
+                name: 'index_collections_on_owner_uuid_and_name')
+      remove_index(:collections,
+                   name: 'collection_owner_uuid_name_unique')
+    end
+  end
+
+  def down
+    Collection.transaction do
+      remove_index(:collections, :delete_at)
+      rename_column(:collections, :delete_at, :expires_at)
+      add_index(:collections, [:owner_uuid, :name],
+                unique: true,
+                where: 'expires_at is null',
+                name: 'collection_owner_uuid_name_unique')
+      remove_index(:collections,
+                   name: 'index_collections_on_owner_uuid_and_name')
+      remove_column(:collections, :is_trashed)
+      remove_index(:collections, :trash_at)
+      remove_column(:collections, :trash_at)
+    end
+  end
+end
diff --git a/services/api/db/migrate/20161223090712_add_output_name_to_container_requests.rb b/services/api/db/migrate/20161223090712_add_output_name_to_container_requests.rb
new file mode 100644 (file)
index 0000000..0e6adfb
--- /dev/null
@@ -0,0 +1,9 @@
+class AddOutputNameToContainerRequests < ActiveRecord::Migration
+  def up
+    add_column :container_requests, :output_name, :string, :default => nil
+  end
+
+  def down
+    remove_column :container_requests, :output_name
+  end
+end
diff --git a/services/api/db/migrate/20170102153111_add_output_name_to_container_request_search_index.rb b/services/api/db/migrate/20170102153111_add_output_name_to_container_request_search_index.rb
new file mode 100644 (file)
index 0000000..0bd7c47
--- /dev/null
@@ -0,0 +1,21 @@
+class AddOutputNameToContainerRequestSearchIndex < ActiveRecord::Migration
+  def up
+    begin
+      remove_index :container_requests, :name => 'container_requests_search_index'
+    rescue
+    end
+    add_index :container_requests,
+              ["uuid", "owner_uuid", "modified_by_client_uuid", "modified_by_user_uuid", "name", "state", "requesting_container_uuid", "container_uuid", "container_image", "cwd", "output_path", "output_uuid", "log_uuid", "output_name"],
+              name: "container_requests_search_index"
+  end
+
+  def down
+    begin
+      remove_index :container_requests, :name => 'container_requests_search_index'
+    rescue
+    end
+         add_index :container_requests,
+              ["uuid", "owner_uuid", "modified_by_client_uuid", "modified_by_user_uuid", "name", "state", "requesting_container_uuid", "container_uuid", "container_image", "cwd", "output_path", "output_uuid", "log_uuid"],
+              name: "container_requests_search_index"
+  end
+end
diff --git a/services/api/db/migrate/20170105160301_add_output_name_to_cr_fts_index.rb b/services/api/db/migrate/20170105160301_add_output_name_to_cr_fts_index.rb
new file mode 100644 (file)
index 0000000..9721ead
--- /dev/null
@@ -0,0 +1,22 @@
+class AddOutputNameToCrFtsIndex < ActiveRecord::Migration
+  def up
+    t = "container_requests"
+    i = "container_requests_full_text_search_idx"
+    t.classify.constantize.reset_column_information
+    ActiveRecord::Base.connection.indexes(t).each do |idx|
+      if idx.name == i
+        remove_index t.to_sym, :name => i
+        break
+      end
+    end
+    # By now, container_request should have the new column "output_name" so full_text_tsvector
+    # would include it on its results
+    execute "CREATE INDEX #{i} ON #{t} USING gin(#{t.classify.constantize.full_text_tsvector});"
+  end
+
+  def down
+    t = "container_requests"
+    i = "container_requests_full_text_search_idx"
+    remove_index t.to_sym, :name => i
+  end
+end
diff --git a/services/api/db/migrate/20170105160302_set_finished_at_on_finished_pipeline_instances.rb b/services/api/db/migrate/20170105160302_set_finished_at_on_finished_pipeline_instances.rb
new file mode 100644 (file)
index 0000000..758b86f
--- /dev/null
@@ -0,0 +1,5 @@
+class SetFinishedAtOnFinishedPipelineInstances < ActiveRecord::Migration
+  def change
+    ActiveRecord::Base.connection.execute("update pipeline_instances set finished_at=updated_at where finished_at is null and (state='Failed' or state='Complete')")
+  end
+end
index 0db782af69484e6a8e0c476620891702055f36c7..9ff935e2b2890aecc2da2d2b3c3c5f5273535640 100644 (file)
@@ -169,8 +169,10 @@ CREATE TABLE collections (
     name character varying(255),
     description character varying(524288),
     properties text,
-    expires_at timestamp without time zone,
-    file_names character varying(8192)
+    delete_at timestamp without time zone,
+    file_names character varying(8192),
+    trash_at timestamp without time zone,
+    is_trashed boolean DEFAULT false NOT NULL
 );
 
 
@@ -291,7 +293,11 @@ CREATE TABLE container_requests (
     filters text,
     updated_at timestamp without time zone NOT NULL,
     container_count integer DEFAULT 0,
-    use_existing boolean DEFAULT true
+    use_existing boolean DEFAULT true,
+    scheduling_parameters text,
+    output_uuid character varying(255),
+    log_uuid character varying(255),
+    output_name character varying(255) DEFAULT NULL::character varying
 );
 
 
@@ -343,7 +349,8 @@ CREATE TABLE containers (
     updated_at timestamp without time zone NOT NULL,
     exit_code integer,
     auth_uuid character varying(255),
-    locked_by_uuid character varying(255)
+    locked_by_uuid character varying(255),
+    scheduling_parameters text
 );
 
 
@@ -1491,18 +1498,11 @@ CREATE INDEX api_clients_search_index ON api_clients USING btree (uuid, owner_uu
 CREATE INDEX authorized_keys_search_index ON authorized_keys USING btree (uuid, owner_uuid, modified_by_client_uuid, modified_by_user_uuid, name, key_type, authorized_user_uuid);
 
 
---
--- Name: collection_owner_uuid_name_unique; Type: INDEX; Schema: public; Owner: -; Tablespace: 
---
-
-CREATE UNIQUE INDEX collection_owner_uuid_name_unique ON collections USING btree (owner_uuid, name) WHERE (expires_at IS NULL);
-
-
 --
 -- Name: collections_full_text_search_idx; Type: INDEX; Schema: public; Owner: -; Tablespace: 
 --
 
-CREATE INDEX collections_full_text_search_idx ON collections USING gin (to_tsvector('english'::regconfig, (((((((((((((((((' '::text || (COALESCE(owner_uuid, ''::character varying))::text) || ' '::text) || (COALESCE(modified_by_client_uuid, ''::character varying))::text) || ' '::text) || (COALESCE(modified_by_user_uuid, ''::character varying))::text) || ' '::text) || (COALESCE(portable_data_hash, ''::character varying))::text) || ' '::text) || (COALESCE(uuid, ''::character varying))::text) || ' '::text) || (COALESCE(name, ''::character varying))::text) || ' '::text) || (COALESCE(description, ''::character varying))::text) || ' '::text) || COALESCE(properties, ''::text)) || ' '::text) || (COALESCE(file_names, ''::character varying))::text)));
+CREATE INDEX collections_full_text_search_idx ON collections USING gin (to_tsvector('english'::regconfig, (((((((((((((((((COALESCE(owner_uuid, ''::character varying))::text || ' '::text) || (COALESCE(modified_by_client_uuid, ''::character varying))::text) || ' '::text) || (COALESCE(modified_by_user_uuid, ''::character varying))::text) || ' '::text) || (COALESCE(portable_data_hash, ''::character varying))::text) || ' '::text) || (COALESCE(uuid, ''::character varying))::text) || ' '::text) || (COALESCE(name, ''::character varying))::text) || ' '::text) || (COALESCE(description, ''::character varying))::text) || ' '::text) || COALESCE(properties, ''::text)) || ' '::text) || (COALESCE(file_names, ''::character varying))::text)));
 
 
 --
@@ -1516,14 +1516,14 @@ CREATE INDEX collections_search_index ON collections USING btree (owner_uuid, mo
 -- Name: container_requests_full_text_search_idx; Type: INDEX; Schema: public; Owner: -; Tablespace: 
 --
 
-CREATE INDEX container_requests_full_text_search_idx ON container_requests USING gin (to_tsvector('english'::regconfig, (((((((((((((((((((((((((((((((((((' '::text || (COALESCE(uuid, ''::character varying))::text) || ' '::text) || (COALESCE(owner_uuid, ''::character varying))::text) || ' '::text) || (COALESCE(modified_by_client_uuid, ''::character varying))::text) || ' '::text) || (COALESCE(modified_by_user_uuid, ''::character varying))::text) || ' '::text) || (COALESCE(name, ''::character varying))::text) || ' '::text) || COALESCE(description, ''::text)) || ' '::text) || COALESCE(properties, ''::text)) || ' '::text) || (COALESCE(state, ''::character varying))::text) || ' '::text) || (COALESCE(requesting_container_uuid, ''::character varying))::text) || ' '::text) || (COALESCE(container_uuid, ''::character varying))::text) || ' '::text) || COALESCE(mounts, ''::text)) || ' '::text) || COALESCE(runtime_constraints, ''::text)) || ' '::text) || (COALESCE(container_image, ''::character varying))::text) || ' '::text) || COALESCE(environment, ''::text)) || ' '::text) || (COALESCE(cwd, ''::character varying))::text) || ' '::text) || COALESCE(command, ''::text)) || ' '::text) || (COALESCE(output_path, ''::character varying))::text) || ' '::text) || COALESCE(filters, ''::text))));
+CREATE INDEX container_requests_full_text_search_idx ON container_requests USING gin (to_tsvector('english'::regconfig, (((((((((((((((((((((((((((((((((((((((((((COALESCE(uuid, ''::character varying))::text || ' '::text) || (COALESCE(owner_uuid, ''::character varying))::text) || ' '::text) || (COALESCE(modified_by_client_uuid, ''::character varying))::text) || ' '::text) || (COALESCE(modified_by_user_uuid, ''::character varying))::text) || ' '::text) || (COALESCE(name, ''::character varying))::text) || ' '::text) || COALESCE(description, ''::text)) || ' '::text) || COALESCE(properties, ''::text)) || ' '::text) || (COALESCE(state, ''::character varying))::text) || ' '::text) || (COALESCE(requesting_container_uuid, ''::character varying))::text) || ' '::text) || (COALESCE(container_uuid, ''::character varying))::text) || ' '::text) || COALESCE(mounts, ''::text)) || ' '::text) || COALESCE(runtime_constraints, ''::text)) || ' '::text) || (COALESCE(container_image, ''::character varying))::text) || ' '::text) || COALESCE(environment, ''::text)) || ' '::text) || (COALESCE(cwd, ''::character varying))::text) || ' '::text) || COALESCE(command, ''::text)) || ' '::text) || (COALESCE(output_path, ''::character varying))::text) || ' '::text) || COALESCE(filters, ''::text)) || ' '::text) || COALESCE(scheduling_parameters, ''::text)) || ' '::text) || (COALESCE(output_uuid, ''::character varying))::text) || ' '::text) || (COALESCE(log_uuid, ''::character varying))::text) || ' '::text) || (COALESCE(output_name, ''::character varying))::text)));
 
 
 --
 -- Name: container_requests_search_index; Type: INDEX; Schema: public; Owner: -; Tablespace: 
 --
 
-CREATE INDEX container_requests_search_index ON container_requests USING btree (uuid, owner_uuid, modified_by_client_uuid, modified_by_user_uuid, name, state, requesting_container_uuid, container_uuid, container_image, cwd, output_path);
+CREATE INDEX container_requests_search_index ON container_requests USING btree (uuid, owner_uuid, modified_by_client_uuid, modified_by_user_uuid, name, state, requesting_container_uuid, container_uuid, container_image, cwd, output_path, output_uuid, log_uuid, output_name);
 
 
 --
@@ -1537,7 +1537,7 @@ CREATE INDEX containers_search_index ON containers USING btree (uuid, owner_uuid
 -- Name: groups_full_text_search_idx; Type: INDEX; Schema: public; Owner: -; Tablespace: 
 --
 
-CREATE INDEX groups_full_text_search_idx ON groups USING gin (to_tsvector('english'::regconfig, (((((((((((((' '::text || (COALESCE(uuid, ''::character varying))::text) || ' '::text) || (COALESCE(owner_uuid, ''::character varying))::text) || ' '::text) || (COALESCE(modified_by_client_uuid, ''::character varying))::text) || ' '::text) || (COALESCE(modified_by_user_uuid, ''::character varying))::text) || ' '::text) || (COALESCE(name, ''::character varying))::text) || ' '::text) || (COALESCE(description, ''::character varying))::text) || ' '::text) || (COALESCE(group_class, ''::character varying))::text)));
+CREATE INDEX groups_full_text_search_idx ON groups USING gin (to_tsvector('english'::regconfig, (((((((((((((COALESCE(uuid, ''::character varying))::text || ' '::text) || (COALESCE(owner_uuid, ''::character varying))::text) || ' '::text) || (COALESCE(modified_by_client_uuid, ''::character varying))::text) || ' '::text) || (COALESCE(modified_by_user_uuid, ''::character varying))::text) || ' '::text) || (COALESCE(name, ''::character varying))::text) || ' '::text) || (COALESCE(description, ''::character varying))::text) || ' '::text) || (COALESCE(group_class, ''::character varying))::text)));
 
 
 --
@@ -1652,6 +1652,20 @@ CREATE UNIQUE INDEX index_authorized_keys_on_uuid ON authorized_keys USING btree
 CREATE INDEX index_collections_on_created_at ON collections USING btree (created_at);
 
 
+--
+-- Name: index_collections_on_delete_at; Type: INDEX; Schema: public; Owner: -; Tablespace: 
+--
+
+CREATE INDEX index_collections_on_delete_at ON collections USING btree (delete_at);
+
+
+--
+-- Name: index_collections_on_is_trashed; Type: INDEX; Schema: public; Owner: -; Tablespace: 
+--
+
+CREATE INDEX index_collections_on_is_trashed ON collections USING btree (is_trashed);
+
+
 --
 -- Name: index_collections_on_modified_at; Type: INDEX; Schema: public; Owner: -; Tablespace: 
 --
@@ -1666,6 +1680,20 @@ CREATE INDEX index_collections_on_modified_at ON collections USING btree (modifi
 CREATE INDEX index_collections_on_owner_uuid ON collections USING btree (owner_uuid);
 
 
+--
+-- Name: index_collections_on_owner_uuid_and_name; Type: INDEX; Schema: public; Owner: -; Tablespace: 
+--
+
+CREATE UNIQUE INDEX index_collections_on_owner_uuid_and_name ON collections USING btree (owner_uuid, name) WHERE (is_trashed = false);
+
+
+--
+-- Name: index_collections_on_trash_at; Type: INDEX; Schema: public; Owner: -; Tablespace: 
+--
+
+CREATE INDEX index_collections_on_trash_at ON collections USING btree (trash_at);
+
+
 --
 -- Name: index_collections_on_uuid; Type: INDEX; Schema: public; Owner: -; Tablespace: 
 --
@@ -2279,7 +2307,7 @@ CREATE INDEX job_tasks_search_index ON job_tasks USING btree (uuid, owner_uuid,
 -- Name: jobs_full_text_search_idx; Type: INDEX; Schema: public; Owner: -; Tablespace: 
 --
 
-CREATE INDEX jobs_full_text_search_idx ON jobs USING gin (to_tsvector('english'::regconfig, (((((((((((((((((((((((((((((((((((((((((' '::text || (COALESCE(uuid, ''::character varying))::text) || ' '::text) || (COALESCE(owner_uuid, ''::character varying))::text) || ' '::text) || (COALESCE(modified_by_client_uuid, ''::character varying))::text) || ' '::text) || (COALESCE(modified_by_user_uuid, ''::character varying))::text) || ' '::text) || (COALESCE(submit_id, ''::character varying))::text) || ' '::text) || (COALESCE(script, ''::character varying))::text) || ' '::text) || (COALESCE(script_version, ''::character varying))::text) || ' '::text) || COALESCE(script_parameters, ''::text)) || ' '::text) || (COALESCE(cancelled_by_client_uuid, ''::character varying))::text) || ' '::text) || (COALESCE(cancelled_by_user_uuid, ''::character varying))::text) || ' '::text) || (COALESCE(output, ''::character varying))::text) || ' '::text) || (COALESCE(is_locked_by_uuid, ''::character varying))::text) || ' '::text) || (COALESCE(log, ''::character varying))::text) || ' '::text) || COALESCE(tasks_summary, ''::text)) || ' '::text) || COALESCE(runtime_constraints, ''::text)) || ' '::text) || (COALESCE(repository, ''::character varying))::text) || ' '::text) || (COALESCE(supplied_script_version, ''::character varying))::text) || ' '::text) || (COALESCE(docker_image_locator, ''::character varying))::text) || ' '::text) || (COALESCE(description, ''::character varying))::text) || ' '::text) || (COALESCE(state, ''::character varying))::text) || ' '::text) || (COALESCE(arvados_sdk_version, ''::character varying))::text)));
+CREATE INDEX jobs_full_text_search_idx ON jobs USING gin (to_tsvector('english'::regconfig, (((((((((((((((((((((((((((((((((((((((((((COALESCE(uuid, ''::character varying))::text || ' '::text) || (COALESCE(owner_uuid, ''::character varying))::text) || ' '::text) || (COALESCE(modified_by_client_uuid, ''::character varying))::text) || ' '::text) || (COALESCE(modified_by_user_uuid, ''::character varying))::text) || ' '::text) || (COALESCE(submit_id, ''::character varying))::text) || ' '::text) || (COALESCE(script, ''::character varying))::text) || ' '::text) || (COALESCE(script_version, ''::character varying))::text) || ' '::text) || COALESCE(script_parameters, ''::text)) || ' '::text) || (COALESCE(cancelled_by_client_uuid, ''::character varying))::text) || ' '::text) || (COALESCE(cancelled_by_user_uuid, ''::character varying))::text) || ' '::text) || (COALESCE(output, ''::character varying))::text) || ' '::text) || (COALESCE(is_locked_by_uuid, ''::character varying))::text) || ' '::text) || (COALESCE(log, ''::character varying))::text) || ' '::text) || COALESCE(tasks_summary, ''::text)) || ' '::text) || COALESCE(runtime_constraints, ''::text)) || ' '::text) || (COALESCE(repository, ''::character varying))::text) || ' '::text) || (COALESCE(supplied_script_version, ''::character varying))::text) || ' '::text) || (COALESCE(docker_image_locator, ''::character varying))::text) || ' '::text) || (COALESCE(description, ''::character varying))::text) || ' '::text) || (COALESCE(state, ''::character varying))::text) || ' '::text) || (COALESCE(arvados_sdk_version, ''::character varying))::text) || ' '::text) || COALESCE(components, ''::text))));
 
 
 --
@@ -2335,7 +2363,7 @@ CREATE INDEX nodes_search_index ON nodes USING btree (uuid, owner_uuid, modified
 -- Name: pipeline_instances_full_text_search_idx; Type: INDEX; Schema: public; Owner: -; Tablespace: 
 --
 
-CREATE INDEX pipeline_instances_full_text_search_idx ON pipeline_instances USING gin (to_tsvector('english'::regconfig, (((((((((((((((((((((' '::text || (COALESCE(uuid, ''::character varying))::text) || ' '::text) || (COALESCE(owner_uuid, ''::character varying))::text) || ' '::text) || (COALESCE(modified_by_client_uuid, ''::character varying))::text) || ' '::text) || (COALESCE(modified_by_user_uuid, ''::character varying))::text) || ' '::text) || (COALESCE(pipeline_template_uuid, ''::character varying))::text) || ' '::text) || (COALESCE(name, ''::character varying))::text) || ' '::text) || COALESCE(components, ''::text)) || ' '::text) || COALESCE(properties, ''::text)) || ' '::text) || (COALESCE(state, ''::character varying))::text) || ' '::text) || COALESCE(components_summary, ''::text)) || ' '::text) || (COALESCE(description, ''::character varying))::text)));
+CREATE INDEX pipeline_instances_full_text_search_idx ON pipeline_instances USING gin (to_tsvector('english'::regconfig, (((((((((((((((((((((COALESCE(uuid, ''::character varying))::text || ' '::text) || (COALESCE(owner_uuid, ''::character varying))::text) || ' '::text) || (COALESCE(modified_by_client_uuid, ''::character varying))::text) || ' '::text) || (COALESCE(modified_by_user_uuid, ''::character varying))::text) || ' '::text) || (COALESCE(pipeline_template_uuid, ''::character varying))::text) || ' '::text) || (COALESCE(name, ''::character varying))::text) || ' '::text) || COALESCE(components, ''::text)) || ' '::text) || COALESCE(properties, ''::text)) || ' '::text) || (COALESCE(state, ''::character varying))::text) || ' '::text) || COALESCE(components_summary, ''::text)) || ' '::text) || (COALESCE(description, ''::character varying))::text)));
 
 
 --
@@ -2356,7 +2384,7 @@ CREATE UNIQUE INDEX pipeline_template_owner_uuid_name_unique ON pipeline_templat
 -- Name: pipeline_templates_full_text_search_idx; Type: INDEX; Schema: public; Owner: -; Tablespace: 
 --
 
-CREATE INDEX pipeline_templates_full_text_search_idx ON pipeline_templates USING gin (to_tsvector('english'::regconfig, (((((((((((((' '::text || (COALESCE(uuid, ''::character varying))::text) || ' '::text) || (COALESCE(owner_uuid, ''::character varying))::text) || ' '::text) || (COALESCE(modified_by_client_uuid, ''::character varying))::text) || ' '::text) || (COALESCE(modified_by_user_uuid, ''::character varying))::text) || ' '::text) || (COALESCE(name, ''::character varying))::text) || ' '::text) || COALESCE(components, ''::text)) || ' '::text) || (COALESCE(description, ''::character varying))::text)));
+CREATE INDEX pipeline_templates_full_text_search_idx ON pipeline_templates USING gin (to_tsvector('english'::regconfig, (((((((((((((COALESCE(uuid, ''::character varying))::text || ' '::text) || (COALESCE(owner_uuid, ''::character varying))::text) || ' '::text) || (COALESCE(modified_by_client_uuid, ''::character varying))::text) || ' '::text) || (COALESCE(modified_by_user_uuid, ''::character varying))::text) || ' '::text) || (COALESCE(name, ''::character varying))::text) || ' '::text) || COALESCE(components, ''::text)) || ' '::text) || (COALESCE(description, ''::character varying))::text)));
 
 
 --
@@ -2412,7 +2440,7 @@ CREATE INDEX virtual_machines_search_index ON virtual_machines USING btree (uuid
 -- Name: workflows_full_text_search_idx; Type: INDEX; Schema: public; Owner: -; Tablespace: 
 --
 
-CREATE INDEX workflows_full_text_search_idx ON workflows USING gin (to_tsvector('english'::regconfig, (((((((((((((' '::text || (COALESCE(uuid, ''::character varying))::text) || ' '::text) || (COALESCE(owner_uuid, ''::character varying))::text) || ' '::text) || (COALESCE(modified_by_client_uuid, ''::character varying))::text) || ' '::text) || (COALESCE(modified_by_user_uuid, ''::character varying))::text) || ' '::text) || (COALESCE(name, ''::character varying))::text) || ' '::text) || COALESCE(description, ''::text)) || ' '::text) || COALESCE(definition, ''::text))));
+CREATE INDEX workflows_full_text_search_idx ON workflows USING gin (to_tsvector('english'::regconfig, (((((((((((((COALESCE(uuid, ''::character varying))::text || ' '::text) || (COALESCE(owner_uuid, ''::character varying))::text) || ' '::text) || (COALESCE(modified_by_client_uuid, ''::character varying))::text) || ' '::text) || (COALESCE(modified_by_user_uuid, ''::character varying))::text) || ' '::text) || (COALESCE(name, ''::character varying))::text) || ' '::text) || COALESCE(description, ''::text)) || ' '::text) || COALESCE(definition, ''::text))));
 
 
 --
@@ -2694,4 +2722,22 @@ INSERT INTO schema_migrations (version) VALUES ('20160909181442');
 
 INSERT INTO schema_migrations (version) VALUES ('20160926194129');
 
-INSERT INTO schema_migrations (version) VALUES ('20161019171346');
\ No newline at end of file
+INSERT INTO schema_migrations (version) VALUES ('20161019171346');
+
+INSERT INTO schema_migrations (version) VALUES ('20161111143147');
+
+INSERT INTO schema_migrations (version) VALUES ('20161115171221');
+
+INSERT INTO schema_migrations (version) VALUES ('20161115174218');
+
+INSERT INTO schema_migrations (version) VALUES ('20161213172944');
+
+INSERT INTO schema_migrations (version) VALUES ('20161222153434');
+
+INSERT INTO schema_migrations (version) VALUES ('20161223090712');
+
+INSERT INTO schema_migrations (version) VALUES ('20170102153111');
+
+INSERT INTO schema_migrations (version) VALUES ('20170105160301');
+
+INSERT INTO schema_migrations (version) VALUES ('20170105160302');
\ No newline at end of file
diff --git a/services/api/lib/create_permission_view.sql b/services/api/lib/create_permission_view.sql
new file mode 100644 (file)
index 0000000..2a9e55b
--- /dev/null
@@ -0,0 +1,41 @@
+CREATE TEMPORARY VIEW permission_view AS
+WITH RECURSIVE
+perm_value (name, val) AS (
+     VALUES
+     ('can_read',   1::smallint),
+     ('can_login',  1),
+     ('can_write',  2),
+     ('can_manage', 3)
+     ),
+perm_edges (tail_uuid, head_uuid, val, follow) AS (
+       SELECT links.tail_uuid,
+              links.head_uuid,
+              pv.val,
+              (pv.val = 3 OR groups.uuid IS NOT NULL) AS follow
+              FROM links
+              LEFT JOIN perm_value pv ON pv.name = links.name
+              LEFT JOIN groups ON pv.val<3 AND groups.uuid = links.head_uuid
+              WHERE links.link_class = 'permission'
+       UNION ALL
+       SELECT owner_uuid, uuid, 3, true FROM groups
+       ),
+perm (val, follow, user_uuid, target_uuid) AS (
+     SELECT 3::smallint             AS val,
+            true                    AS follow,
+            users.uuid::varchar(32) AS user_uuid,
+            users.uuid::varchar(32) AS target_uuid
+            FROM users
+     UNION
+     SELECT LEAST(perm.val, edges.val)::smallint AS val,
+            edges.follow                         AS follow,
+            perm.user_uuid::varchar(32)          AS user_uuid,
+            edges.head_uuid::varchar(32)         AS target_uuid
+            FROM perm
+            INNER JOIN perm_edges edges
+            ON perm.follow AND edges.tail_uuid = perm.target_uuid
+)
+SELECT user_uuid,
+       target_uuid,
+       val AS perm_level,
+       CASE follow WHEN true THEN target_uuid ELSE NULL END AS target_owner_uuid
+       FROM perm;
index 54faa9a0afe8682de8b54096f1374be6c5f0d500..72b1ae7bc97c708368a92844fa60c020f67d7783 100755 (executable)
@@ -14,8 +14,12 @@ module CreateSuperUserToken
         api_client_auth = ApiClientAuthorization.
           where(api_token: supplied_token).
           first
-        if api_client_auth && !api_client_auth.user.uuid.match(/-000000000000000$/)
-          raise "Token already exists but is not a superuser token."
+        if !api_client_auth
+          # fall through to create a token
+        elsif !api_client_auth.user.uuid.match(/-000000000000000$/)
+          raise "Token exists but is not a superuser token."
+        elsif api_client_auth.scopes != ['all']
+          raise "Token exists but has limited scope #{api_client_auth.scopes.inspect}."
         end
       end
 
@@ -26,10 +30,11 @@ module CreateSuperUserToken
 
         # Check if there is an unexpired superuser token corresponding to this api client
         api_client_auth = ApiClientAuthorization.where(
-                'user_id = (?) AND
-                 api_client_id = (?) AND
+                'user_id = ? AND
+                 api_client_id = ? AND
+                 scopes = ? AND
                  (expires_at IS NULL OR expires_at > CURRENT_TIMESTAMP)',
-               system_user.id, apiClient.id).first
+               system_user.id, apiClient.id, ['all'].to_yaml).first
 
         # none exist; create one with the supplied token
         if !api_client_auth
index ce94f737a2467f855a7156ba76873db57cd183ee..48b0eb5983a750aad99e195cd2f08a7b4c01c92e 100644 (file)
@@ -27,7 +27,7 @@ class CrunchDispatch
     @cgroup_root = ENV['CRUNCH_CGROUP_ROOT']
 
     @arvados_internal = Rails.configuration.git_internal_dir
-    if not File.exists? @arvados_internal
+    if not File.exist? @arvados_internal
       $stderr.puts `mkdir -p #{@arvados_internal.shellescape} && git init --bare #{@arvados_internal.shellescape}`
       raise "No internal git repository available" unless ($? == 0)
     end
@@ -73,7 +73,7 @@ class CrunchDispatch
       # into multiple rows with one hostname each.
       `#{cmd} --noheader -o '%N:#{outfmt}'`.each_line do |line|
         tokens = line.chomp.split(":", max_fields)
-        if (re = tokens[0].match /^(.*?)\[([-,\d]+)\]$/)
+        if (re = tokens[0].match(/^(.*?)\[([-,\d]+)\]$/))
           tokens.shift
           re[2].split(",").each do |range|
             range = range.split("-").collect(&:to_i)
@@ -105,7 +105,7 @@ class CrunchDispatch
   end
 
   def update_node_status
-    return unless Server::Application.config.crunch_job_wrapper.to_s.match /^slurm/
+    return unless Server::Application.config.crunch_job_wrapper.to_s.match(/^slurm/)
     slurm_status.each_pair do |hostname, slurmdata|
       next if @node_state[hostname] == slurmdata
       begin
@@ -169,7 +169,7 @@ class CrunchDispatch
       end
       usable_nodes << node
       if usable_nodes.count >= min_node_count
-        return usable_nodes.map { |node| node.hostname }
+        return usable_nodes.map { |n| n.hostname }
       end
     end
     nil
@@ -512,8 +512,6 @@ class CrunchDispatch
 
   def read_pipes
     @running.each do |job_uuid, j|
-      job = j[:job]
-
       now = Time.now
       if now > j[:log_throttle_reset_time]
         # It has been more than throttle_period seconds since the last
index fbd4ef5f0c67933a7cc703d9f532c94fd601fc3d..97348d5fb4b7a9458089016dc280778559365185 100644 (file)
@@ -1,3 +1,11 @@
+$system_user = nil
+$system_group = nil
+$all_users_group = nil
+$anonymous_user = nil
+$anonymous_group = nil
+$anonymous_group_read_permission = nil
+$empty_collection = nil
+
 module CurrentApiClient
   def current_user
     Thread.current[:user]
@@ -83,9 +91,7 @@ module CurrentApiClient
             User.all.collect(&:uuid).each do |user_uuid|
               Link.create!(link_class: 'permission',
                            name: 'can_manage',
-                           tail_kind: 'arvados#group',
                            tail_uuid: system_group_uuid,
-                           head_kind: 'arvados#user',
                            head_uuid: user_uuid)
             end
           end
index 16bb030941c3033ebf32cb972a645eb821a063d3..5e413d5cabf580688693754af4201497f2d79e0a 100644 (file)
@@ -78,6 +78,10 @@ class EventBus
     @connection_count = 0
   end
 
+  def send_message(ws, obj)
+    ws.send(Oj.dump(obj, mode: :compat))
+  end
+
   # Push out any pending events to the connection +ws+
   # +notify_id+  the id of the most recent row in the log table, may be nil
   #
@@ -146,7 +150,7 @@ class EventBus
         logs.select('logs.id').find_each do |l|
           if not ws.sent_ids.include?(l.id)
             # only send if not a duplicate
-            ws.send(Log.find(l.id).as_api_response.to_json)
+            send_message(ws, Log.find(l.id).as_api_response)
           end
           if not ws.last_log_id.nil?
             # record ids only when sending "catchup" messages, not notifies
@@ -158,12 +162,12 @@ class EventBus
     rescue ArgumentError => e
       # There was some kind of user error.
       Rails.logger.warn "Error publishing event: #{$!}"
-      ws.send ({status: 500, message: $!}.to_json)
+      send_message(ws, {status: 500, message: $!})
       ws.close
     rescue => e
       Rails.logger.warn "Error publishing event: #{$!}"
       Rails.logger.warn "Backtrace:\n\t#{e.backtrace.join("\n\t")}"
-      ws.send ({status: 500, message: $!}.to_json)
+      send_message(ws, {status: 500, message: $!})
       ws.close
       # These exceptions typically indicate serious server trouble:
       # out of memory issues, database connection problems, etc.  Go ahead and
@@ -180,7 +184,7 @@ class EventBus
         p = (Oj.strict_load event.data).symbolize_keys
         filter = Filter.new(p)
       rescue Oj::Error => e
-        ws.send ({status: 400, message: "malformed request"}.to_json)
+        send_message(ws, {status: 400, message: "malformed request"})
         return
       end
 
@@ -200,12 +204,12 @@ class EventBus
           # Add a filter.  This gets the :filters field which is the same
           # format as used for regular index queries.
           ws.filters << filter
-          ws.send ({status: 200, message: 'subscribe ok', filter: p}.to_json)
+          send_message(ws, {status: 200, message: 'subscribe ok', filter: p})
 
           # Send any pending events
           push_events ws, nil
         else
-          ws.send ({status: 403, message: "maximum of #{Rails.configuration.websocket_max_filters} filters allowed per connection"}.to_json)
+          send_message(ws, {status: 403, message: "maximum of #{Rails.configuration.websocket_max_filters} filters allowed per connection"})
         end
 
       elsif p[:method] == 'unsubscribe'
@@ -214,18 +218,18 @@ class EventBus
         len = ws.filters.length
         ws.filters.select! { |f| not ((f.filters == p[:filters]) or (f.filters.empty? and p[:filters].nil?)) }
         if ws.filters.length < len
-          ws.send ({status: 200, message: 'unsubscribe ok'}.to_json)
+          send_message(ws, {status: 200, message: 'unsubscribe ok'})
         else
-          ws.send ({status: 404, message: 'filter not found'}.to_json)
+          send_message(ws, {status: 404, message: 'filter not found'})
         end
 
       else
-        ws.send ({status: 400, message: "missing or unrecognized method"}.to_json)
+        send_message(ws, {status: 400, message: "missing or unrecognized method"})
       end
     rescue => e
       Rails.logger.warn "Error handling message: #{$!}"
       Rails.logger.warn "Backtrace:\n\t#{e.backtrace.join("\n\t")}"
-      ws.send ({status: 500, message: 'error'}.to_json)
+      send_message(ws, {status: 500, message: 'error'})
       ws.close
     end
   end
@@ -241,8 +245,13 @@ class EventBus
     # Disconnect if no valid API token.
     # current_user is included from CurrentApiClient
     if not current_user
-      ws.send ({status: 401, message: "Valid API token required"}.to_json)
-      ws.close
+      send_message(ws, {status: 401, message: "Valid API token required"})
+      # Wait for the handshake to complete before closing the
+      # socket. Otherwise, nginx responds with HTTP 502 Bad gateway,
+      # and the client never sees our real error message.
+      ws.on :open do |event|
+        ws.close
+      end
       return
     end
 
@@ -262,7 +271,7 @@ class EventBus
     # forward them to the thread associated with the connection.
     sub = @channel.subscribe do |msg|
       if ws.queue.length > Rails.configuration.websocket_max_notify_backlog
-        ws.send ({status: 500, message: 'Notify backlog too long'}.to_json)
+        send_message(ws, {status: 500, message: 'Notify backlog too long'})
         ws.close
         @channel.unsubscribe sub
         ws.queue.clear
@@ -307,6 +316,7 @@ class EventBus
         @mtx.synchronize do
           @connection_count -= 1
         end
+        ActiveRecord::Base.connection.close
       end
     end
 
index 5b22274d07781325276b5df152037a5b2a13dc61..dee0f23b1d87118ac5aaff6652579be87b1d5229 100644 (file)
@@ -92,11 +92,11 @@ module LoadParam
         # has used set_table_name to use an alternate table name from the Rails standard.
         # I could not find a perfect way to handle this well, but ActiveRecord::Base.send(:descendants)
         # would be a place to start if this ever becomes necessary.
-        if attr.match /^[a-z][_a-z0-9]+$/ and
+        if attr.match(/^[a-z][_a-z0-9]+$/) and
             model_class.columns.collect(&:name).index(attr) and
             ['asc','desc'].index direction.downcase
           @orders << "#{table_name}.#{attr} #{direction.downcase}"
-        elsif attr.match /^([a-z][_a-z0-9]+)\.([a-z][_a-z0-9]+)$/ and
+        elsif attr.match(/^([a-z][_a-z0-9]+)\.([a-z][_a-z0-9]+)$/) and
             ['asc','desc'].index(direction.downcase) and
             ActiveRecord::Base.connection.tables.include?($1) and
             $1.classify.constantize.columns.collect(&:name).index($2)
@@ -153,8 +153,9 @@ module LoadParam
       # Any ordering columns must be selected when doing select,
       # otherwise it is an SQL error, so filter out invaliding orderings.
       @orders.select! { |o|
+        col, dir = o.split
         # match select column against order array entry
-        @select.select { |s| /^#{table_name}.#{s}( (asc|desc))?$/.match o }.any?
+        @select.select { |s| col == "#{table_name}.#{s}" }.any?
       }
     end
 
index 2011f812d5ccf8e3836394acafd50d14ef58f7ec..c6664b1ec916413fc3d2e431bb12551a303bf1ed 100755 (executable)
@@ -29,7 +29,7 @@ module SalvageCollection
   def salvage_collection_locator_data manifest
     locators = []
     size = 0
-    manifest.scan /(^|[^[:xdigit:]])([[:xdigit:]]{32})((\+\d+)(\+|\b))?/ do |_, hash, _, sizehint, _|
+    manifest.scan(/(^|[^[:xdigit:]])([[:xdigit:]]{32})((\+\d+)(\+|\b))?/) do |_, hash, _, sizehint, _|
       if sizehint
         locators << hash.downcase + sizehint
         size += sizehint.to_i
diff --git a/services/api/lib/sweep_trashed_collections.rb b/services/api/lib/sweep_trashed_collections.rb
new file mode 100644 (file)
index 0000000..ab2d27a
--- /dev/null
@@ -0,0 +1,34 @@
+require 'current_api_client'
+
+module SweepTrashedCollections
+  extend CurrentApiClient
+
+  def self.sweep_now
+    act_as_system_user do
+      Collection.unscoped.
+        where('delete_at is not null and delete_at < statement_timestamp()').
+        destroy_all
+      Collection.unscoped.
+        where('is_trashed = false and trash_at < statement_timestamp()').
+        update_all('is_trashed = true')
+    end
+  end
+
+  def self.sweep_if_stale
+    return if Rails.configuration.trash_sweep_interval <= 0
+    exp = Rails.configuration.trash_sweep_interval.seconds
+    need = false
+    Rails.cache.fetch('SweepTrashedCollections', expires_in: exp) do
+      need = true
+    end
+    if need
+      Thread.new do
+        begin
+          sweep_now
+        ensure
+          ActiveRecord::Base.connection.close
+        end
+      end
+    end
+  end
+end
diff --git a/services/api/lib/tasks/config_dump.rake b/services/api/lib/tasks/config_dump.rake
new file mode 100644 (file)
index 0000000..c7e0214
--- /dev/null
@@ -0,0 +1,6 @@
+namespace :config do
+  desc 'Show site configuration'
+  task dump: :environment do
+    puts $application_config.to_yaml
+  end
+end
index 3a8ed2724f236b9966f0f9e64d625e11db36486f..b78553491715bf6aa85ea5615bbbdb39392e0a95 100755 (executable)
@@ -22,7 +22,7 @@ DEBUG = 1
 # load and merge in the environment-specific application config info
 # if present, overriding base config parameters as specified
 path = File.absolute_path('../../config/arvados-clients.yml', __FILE__)
-if File.exists?(path) then
+if File.exist?(path) then
   cp_config = YAML.load_file(path)[ENV['RAILS_ENV']]
 else
   puts "Please create a\n #{path}\n file"
@@ -214,7 +214,7 @@ end
 
 begin
   # Get our local gitolite-admin repo up to snuff
-  if not File.exists?(gitolite_admin) then
+  if not File.exist?(gitolite_admin) then
     ensure_directory(gitolite_tmpdir, 0700)
     Dir.chdir(gitolite_tmpdir)
     `git clone #{gitolite_url}`
index 8db1a0edadf4f8707d630d5dc02021501e0724a3..169509f63b96337f9b129f1b1ffd50305112f3aa 100755 (executable)
@@ -35,7 +35,7 @@ DEBUG = 1
 # load and merge in the environment-specific application config info
 # if present, overriding base config parameters as specified
 path = File.dirname(__FILE__) + '/config/arvados-clients.yml'
-if File.exists?(path) then
+if File.exist?(path) then
   cp_config = YAML.load_file(path)[ENV['RAILS_ENV']]
 else
   puts "Please create a\n " + File.dirname(__FILE__) + "/config/arvados-clients.yml\n file"
@@ -186,7 +186,7 @@ end
 
 begin
   # Get our local gitolite-admin repo up to snuff
-  if not File.exists?(gitolite_admin) then
+  if not File.exist?(gitolite_admin) then
     ensure_directory(gitolite_tmpdir, 0700)
     Dir.chdir(gitolite_tmpdir)
     `git clone #{gitolite_url}`
index 6ec9e9f05d5ad7cdeff29cda76c20abbe7a4eae1..a0e5ad95dc32a0210d28577c2b899d05c53fd3f1 100644 (file)
@@ -4,7 +4,7 @@ end
 
 FactoryGirl.define do
   factory :user do
-    ignore do
+    transient do
       join_groups []
     end
     after :create do |user, evaluator|
index 2272b0f4a041094455c6a06a979a0ed4947f531a..2eb873b5d99ad088b97e3dd1f2dbbe4a6692a744 100644 (file)
@@ -221,6 +221,8 @@ collection_to_move_around_in_aproject:
   manifest_text: ". 73feffa4b7f6bb68e44cf984c85f6e88+3 0:3:baz\n"
   name: collection_to_move_around
 
+# Note: collections(:expired_collection) fixture finder won't work
+# because it is not in default scope
 expired_collection:
   uuid: zzzzz-4zz18-mto52zx1s7sn3ih
   portable_data_hash: 0b21a217243bfce5617fb9224b95bcb9+49
@@ -230,10 +232,44 @@ expired_collection:
   modified_by_user_uuid: zzzzz-tpzed-d9tiejq69daie8f
   modified_at: 2014-02-03T17:22:54Z
   updated_at: 2014-02-03T17:22:54Z
-  expires_at: 2001-01-01T00:00:00Z
+  is_trashed: true
+  trash_at: 2001-01-01T00:00:00Z
+  delete_at: 2038-01-01T00:00:00Z
   manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:expired\n"
   name: expired_collection
 
+trashed_on_next_sweep:
+  uuid: zzzzz-4zz18-4guozfh77ewd2f0
+  portable_data_hash: 0b21a217243bfce5617fb9224b95bcb9+49
+  owner_uuid: zzzzz-tpzed-xurymjxw79nv3jz
+  created_at: 2016-12-07T22:01:00.123456Z
+  modified_by_client_uuid: zzzzz-ozdt8-brczlopd8u8d0jr
+  modified_by_user_uuid: zzzzz-tpzed-d9tiejq69daie8f
+  modified_at: 2016-12-27T22:01:30.123456Z
+  updated_at: 2016-12-27T22:01:30.123456Z
+  is_trashed: false
+  trash_at: 2016-12-07T22:01:30.123456Z
+  delete_at: 2112-01-01T00:00:00Z
+  manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:expired\n"
+  name: trashed_on_next_sweep
+
+# Note: collections(:deleted_on_next_sweep) fixture finder won't work
+# because it is not in default scope
+deleted_on_next_sweep:
+  uuid: zzzzz-4zz18-3u1p5umicfpqszp
+  portable_data_hash: 0b21a217243bfce5617fb9224b95bcb9+49
+  owner_uuid: zzzzz-tpzed-xurymjxw79nv3jz
+  created_at: 2016-12-07T22:01:00.234567Z
+  modified_by_client_uuid: zzzzz-ozdt8-brczlopd8u8d0jr
+  modified_by_user_uuid: zzzzz-tpzed-d9tiejq69daie8f
+  modified_at: 2016-12-27T22:01:30.234567Z
+  updated_at: 2016-12-27T22:01:30.234567Z
+  is_trashed: true
+  trash_at: 2016-12-07T22:01:30.234567Z
+  delete_at: 2016-12-27T22:01:30.234567Z
+  manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:expired\n"
+  name: deleted_on_next_sweep
+
 collection_expires_in_future:
   uuid: zzzzz-4zz18-padkqo7yb8d9i3j
   portable_data_hash: 0b21a217243bfce5617fb9224b95bcb9+49
@@ -243,7 +279,8 @@ collection_expires_in_future:
   modified_by_user_uuid: zzzzz-tpzed-d9tiejq69daie8f
   modified_at: 2014-02-03T17:22:54Z
   updated_at: 2014-02-03T17:22:54Z
-  expires_at: 2038-01-01T00:00:00Z
+  trash_at: 2038-01-01T00:00:00Z
+  delete_at: 2038-03-01T00:00:00Z
   manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:expired\n"
   name: collection_expires_in_future
 
index acacf4023829756085caf1feb269845887043d4d..76f59c29f87a3ebbad2672a42a5f1502883c2b13 100644 (file)
@@ -90,6 +90,8 @@ completed:
   output_path: test
   command: ["echo", "hello"]
   container_uuid: zzzzz-dz642-compltcontainer
+  log_uuid: zzzzz-4zz18-y9vne9npefyxh8g
+  output_uuid: zzzzz-4zz18-znfnqtbbv4spc3w
   runtime_constraints:
     vcpus: 1
     ram: 123
index 1a06d573d914462bdcabd8a1a0bcb25008bc36ff..eb0da8b083e5b2ed20173a3c1a244cbde7881b6a 100644 (file)
@@ -510,6 +510,8 @@ completed_job_in_publicly_accessible_project:
   log: zzzzz-4zz18-4en62shvi99lxd4
   output: b519d9cb706a29fc7ea24dbea2f05851+93
   script_parameters_digest: 02a085407e751d00b5dc88f1bd5e8247
+  started_at: <%= 10.minute.ago.to_s(:db) %>
+  finished_at: <%= 5.minute.ago.to_s(:db) %>
 
 job_in_publicly_accessible_project_but_other_objects_elsewhere:
   uuid: zzzzz-8i9sb-jyq01muyhgr4ofj
index cbd82de9241101a72cc1c263903b9a403a8234fa..49503c971236af12a936085d92084a79e2dfc0ba 100644 (file)
@@ -243,3 +243,25 @@ template_in_asubproject_with_same_name_as_one_in_active_user_home:
         dataclass: Collection
         title: "Foo/bar pair"
         description: "Provide a collection containing at least two files."
+
+workflow_with_input_defaults:
+  uuid: zzzzz-p5p6p-aox0k0ofxrystg2
+  owner_uuid: zzzzz-j7d0g-v955i6s2oi1cbso
+  created_at: 2014-04-14 12:35:04 -0400
+  updated_at: 2014-04-14 12:35:04 -0400
+  modified_at: 2014-04-14 12:35:04 -0400
+  modified_by_client_uuid: zzzzz-ozdt8-brczlopd8u8d0jr
+  modified_by_user_uuid: zzzzz-tpzed-xurymjxw79nv3jz
+  name: Pipeline with default input specifications
+  components:
+    part-one:
+      script: foo
+      script_version: master
+      script_parameters:
+        ex_string:
+          required: true
+          dataclass: string
+        ex_string_def:
+          required: true
+          dataclass: string
+          default: hello-testing-123
\ No newline at end of file
index 4badf9e175f21c1fb521befa6d6c6bbe8024af5b..f79320e907801cae499cdcbf809a4602194656f4 100644 (file)
@@ -44,3 +44,22 @@ workflow_with_input_specifications:
       inputBinding:
         position: 1
     outputs: []
+
+workflow_with_input_defaults:
+  uuid: zzzzz-7fd4e-validwithinput2
+  owner_uuid: zzzzz-j7d0g-zhxawtyetzwc5f0
+  name: Workflow with default input specifications
+  description: this workflow has inputs specified
+  created_at: <%= 1.minute.ago.to_s(:db) %>
+  definition: |
+    cwlVersion: v1.0
+    class: CommandLineTool
+    baseCommand:
+    - echo
+    inputs:
+    - type: string
+      id: ex_string
+    - type: string
+      id: ex_string_def
+      default: hello-testing-123
+    outputs: []
index 37e690e0b21ccb221454d6a46ac04b4e732c2ce7..6c09d8e9f593a8e7c847c83ceb9bb46dcb913397 100644 (file)
@@ -46,7 +46,7 @@ class Arvados::V1::ApiClientAuthorizationsControllerTest < ActionController::Tes
     get :index, search_params
     assert_response :success
     got_tokens = JSON.parse(@response.body)['items']
-      .map { |auth| auth['api_token'] }
+      .map { |a| a['api_token'] }
     assert_equal(expected_tokens.sort, got_tokens.sort,
                  "wrong results for #{search_params.inspect}")
   end
index a8583be12bb70d915585c8c48aba0bc06aa32d3e..2391fc19b7a539f38397bd85b95e428550583645 100644 (file)
@@ -1,6 +1,9 @@
 require 'test_helper'
 
 class Arvados::V1::CollectionsControllerTest < ActionController::TestCase
+  include DbCurrentTime
+
+  PERM_TOKEN_RE = /\+A[[:xdigit:]]+@[[:xdigit:]]{8}\b/
 
   def permit_unsigned_manifests isok=true
     # Set security model for the life of a test.
@@ -10,11 +13,23 @@ class Arvados::V1::CollectionsControllerTest < ActionController::TestCase
   def assert_signed_manifest manifest_text, label=''
     assert_not_nil manifest_text, "#{label} manifest_text was nil"
     manifest_text.scan(/ [[:xdigit:]]{32}\S*/) do |tok|
-      assert_match(/\+A[[:xdigit:]]+@[[:xdigit:]]{8}\b/, tok,
+      assert_match(PERM_TOKEN_RE, tok,
                    "Locator in #{label} manifest_text was not signed")
     end
   end
 
+  def assert_unsigned_manifest resp, label=''
+    txt = resp['unsigned_manifest_text']
+    assert_not_nil(txt, "#{label} unsigned_manifest_text was nil")
+    locs = 0
+    txt.scan(/ [[:xdigit:]]{32}\S*/) do |tok|
+      locs += 1
+      refute_match(PERM_TOKEN_RE, tok,
+                   "Locator in #{label} unsigned_manifest_text was signed: #{tok}")
+    end
+    return locs
+  end
+
   test "should get index" do
     authorize_with :active
     get :index
@@ -24,12 +39,13 @@ class Arvados::V1::CollectionsControllerTest < ActionController::TestCase
            "basic Collections index included manifest_text")
   end
 
-  test "collections.get returns signed locators" do
+  test "collections.get returns signed locators, and no unsigned_manifest_text" do
     permit_unsigned_manifests
     authorize_with :active
     get :show, {id: collections(:foo_file).uuid}
     assert_response :success
     assert_signed_manifest json_response['manifest_text'], 'foo_file'
+    refute_includes json_response, 'unsigned_manifest_text'
   end
 
   test "index with manifest_text selected returns signed locators" do
@@ -40,12 +56,69 @@ class Arvados::V1::CollectionsControllerTest < ActionController::TestCase
     assert(assigns(:objects).andand.any?,
            "no Collections returned for index with columns selected")
     json_response["items"].each do |coll|
-      assert_equal(columns, columns & coll.keys,
+      assert_equal(coll.keys - ['kind'], columns,
                    "Collections index did not respect selected columns")
       assert_signed_manifest coll['manifest_text'], coll['uuid']
     end
   end
 
+  test "index with unsigned_manifest_text selected returns only unsigned locators" do
+    authorize_with :active
+    get :index, select: ['unsigned_manifest_text']
+    assert_response :success
+    assert_operator json_response["items"].count, :>, 0
+    locs = 0
+    json_response["items"].each do |coll|
+      assert_equal(coll.keys - ['kind'], ['unsigned_manifest_text'],
+                   "Collections index did not respect selected columns")
+      locs += assert_unsigned_manifest coll, coll['uuid']
+    end
+    assert_operator locs, :>, 0, "no locators found in any manifests"
+  end
+
+  test 'index without select returns everything except manifest' do
+    authorize_with :active
+    get :index
+    assert_response :success
+    assert json_response['items'].any?
+    json_response['items'].each do |coll|
+      assert_includes(coll.keys, 'uuid')
+      assert_includes(coll.keys, 'name')
+      assert_includes(coll.keys, 'created_at')
+      refute_includes(coll.keys, 'manifest_text')
+    end
+  end
+
+  ['', nil, false, 'null'].each do |select|
+    test "index with select=#{select.inspect} returns everything except manifest" do
+      authorize_with :active
+      get :index, select: select
+      assert_response :success
+      assert json_response['items'].any?
+      json_response['items'].each do |coll|
+        assert_includes(coll.keys, 'uuid')
+        assert_includes(coll.keys, 'name')
+        assert_includes(coll.keys, 'created_at')
+        refute_includes(coll.keys, 'manifest_text')
+      end
+    end
+  end
+
+  [["uuid"],
+   ["uuid", "manifest_text"],
+   '["uuid"]',
+   '["uuid", "manifest_text"]'].each do |select|
+    test "index with select=#{select.inspect} returns no name" do
+      authorize_with :active
+      get :index, select: select
+      assert_response :success
+      assert json_response['items'].any?
+      json_response['items'].each do |coll|
+        refute_includes(coll.keys, 'name')
+      end
+    end
+  end
+
   [0,1,2].each do |limit|
     test "get index with limit=#{limit}" do
       authorize_with :active
@@ -272,7 +345,7 @@ EOS
         ensure_unique_name: true
       }
       assert_response :success
-      assert_equal 'owned_by_active (2)', json_response['name']
+      assert_match /^owned_by_active \(\d{4}-\d\d-\d\d.*?Z\)$/, json_response['name']
     end
   end
 
@@ -751,11 +824,11 @@ EOS
     [2**8, :success],
     [2**18, 422],
   ].each do |description_size, expected_response|
-    test "create collection with description size #{description_size}
+    # Descriptions are not part of search indexes. Skip until
+    # full-text search is implemented, at which point replace with a
+    # search in description.
+    skip "create collection with description size #{description_size}
           and expect response #{expected_response}" do
-      skip "(Descriptions are not part of search indexes. Skip until full-text search
-            is implemented, at which point replace with a search in description.)"
-
       authorize_with :active
 
       description = 'here is a collection with a very large description'
@@ -887,4 +960,68 @@ EOS
       assert_response 200
     end
   end
+
+  test 'get trashed collection with include_trash' do
+    uuid = 'zzzzz-4zz18-mto52zx1s7sn3ih' # expired_collection
+    authorize_with :active
+    get :show, {
+      id: uuid,
+      include_trash: true,
+    }
+    assert_response 200
+  end
+
+  test 'get trashed collection without include_trash' do
+    uuid = 'zzzzz-4zz18-mto52zx1s7sn3ih' # expired_collection
+    authorize_with :active
+    get :show, {
+      id: uuid,
+    }
+    assert_response 404
+  end
+
+  test 'trash collection using http DELETE verb' do
+    uuid = collections(:collection_owned_by_active).uuid
+    authorize_with :active
+    delete :destroy, {
+      id: uuid,
+    }
+    assert_response 200
+    c = Collection.unscoped.find_by_uuid(uuid)
+    assert_operator c.trash_at, :<, db_current_time
+    assert_equal c.delete_at, c.trash_at + Rails.configuration.blob_signature_ttl
+  end
+
+  test 'delete long-trashed collection immediately using http DELETE verb' do
+    uuid = 'zzzzz-4zz18-mto52zx1s7sn3ih' # expired_collection
+    authorize_with :active
+    delete :destroy, {
+      id: uuid,
+    }
+    assert_response 200
+    c = Collection.unscoped.find_by_uuid(uuid)
+    assert_operator c.trash_at, :<, db_current_time
+    assert_operator c.delete_at, :<, db_current_time
+  end
+
+  ['zzzzz-4zz18-mto52zx1s7sn3ih', # expired_collection
+   :empty_collection_name_in_active_user_home_project,
+  ].each do |fixture|
+    test "trash collection #{fixture} via trash action with grace period" do
+      if fixture.is_a? String
+        uuid = fixture
+      else
+        uuid = collections(fixture).uuid
+      end
+      authorize_with :active
+      time_before_trashing = db_current_time
+      post :trash, {
+        id: uuid,
+      }
+      assert_response 200
+      c = Collection.unscoped.find_by_uuid(uuid)
+      assert_operator c.trash_at, :<, db_current_time
+      assert_operator c.delete_at, :>=, time_before_trashing + Rails.configuration.default_trash_lifetime
+    end
+  end
 end
diff --git a/services/api/test/functional/arvados/v1/container_requests_controller_test.rb b/services/api/test/functional/arvados/v1/container_requests_controller_test.rb
new file mode 100644 (file)
index 0000000..e54e15d
--- /dev/null
@@ -0,0 +1,22 @@
+require 'test_helper'
+
+class Arvados::V1::ContainerRequestsControllerTest < ActionController::TestCase
+  test 'create with scheduling parameters' do
+    authorize_with :system_user
+
+    sp = {'partitions' => ['test1', 'test2']}
+    post :create, {
+      container_request: {
+        command: ['echo', 'hello'],
+        container_image: 'test',
+        output_path: 'test',
+        scheduling_parameters: sp,
+      },
+    }
+    assert_response :success
+
+    cr = JSON.parse(@response.body)
+    assert_not_nil cr, 'Expected container request'
+    assert_equal sp, cr['scheduling_parameters']
+  end
+end
index 9344b0bc75c3be0c9bc878207df52275a1190e88..2b1b675323fa8a05cdd9682a176e3ec561a099ad 100644 (file)
@@ -32,7 +32,7 @@ class Arvados::V1::FiltersTest < ActionController::TestCase
       filters: [['uuid', '@@', 'abcdef']],
     }
     assert_response 422
-    assert_match /not supported/, json_response['errors'].join(' ')
+    assert_match(/not supported/, json_response['errors'].join(' '))
   end
 
   test 'difficult characters in full text search' do
@@ -52,7 +52,7 @@ class Arvados::V1::FiltersTest < ActionController::TestCase
       filters: [['any', '@@', ['abc', 'def']]],
     }
     assert_response 422
-    assert_match /not supported/, json_response['errors'].join(' ')
+    assert_match(/not supported/, json_response['errors'].join(' '))
   end
 
   test 'api responses provide timestamps with nanoseconds' do
@@ -65,7 +65,7 @@ class Arvados::V1::FiltersTest < ActionController::TestCase
       %w(created_at modified_at).each do |attr|
         # Pass fixtures with null timestamps.
         next if item[attr].nil?
-        assert_match /^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d.\d{9}Z$/, item[attr]
+        assert_match(/^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d.\d{9}Z$/, item[attr])
       end
     end
   end
index 10534a70610a8188d35863992f2810ac29195937..646089d5ddbcc31a16d0d749b0e828f4c58234be 100644 (file)
@@ -55,12 +55,12 @@ class Arvados::V1::GroupsControllerTest < ActionController::TestCase
     assert_equal 0, json_response['items_available']
   end
 
-  def check_project_contents_response
+  def check_project_contents_response disabled_kinds=[]
     assert_response :success
     assert_operator 2, :<=, json_response['items_available']
     assert_operator 2, :<=, json_response['items'].count
     kinds = json_response['items'].collect { |i| i['kind'] }.uniq
-    expect_kinds = %w'arvados#group arvados#specimen arvados#pipelineTemplate arvados#job'
+    expect_kinds = %w'arvados#group arvados#specimen arvados#pipelineTemplate arvados#job' - disabled_kinds
     assert_equal expect_kinds, (expect_kinds & kinds)
 
     json_response['items'].each do |i|
@@ -69,6 +69,10 @@ class Arvados::V1::GroupsControllerTest < ActionController::TestCase
                "group#contents returned a non-project group")
       end
     end
+
+    disabled_kinds.each do |d|
+      assert_equal true, !kinds.include?(d)
+    end
   end
 
   test 'get group-owned objects' do
@@ -376,9 +380,8 @@ class Arvados::V1::GroupsControllerTest < ActionController::TestCase
     assert_not_equal(new_project['uuid'],
                      groups(:aproject).uuid,
                      "create returned same uuid as existing project")
-    assert_equal(new_project['name'],
-                 'A Project (2)',
-                 "new project name '#{new_project['name']}' was expected to be 'A Project (2)'")
+    assert_match(/^A Project \(\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d\.\d{3}Z\)$/,
+                 new_project['name'])
   end
 
   test "unsharing a project results in hiding it from previously shared user" do
@@ -448,4 +451,15 @@ class Arvados::V1::GroupsControllerTest < ActionController::TestCase
       end
     end
   end
+
+  test 'get contents with jobs and pipeline instances disabled' do
+    Rails.configuration.disable_api_methods = ['jobs.index', 'pipeline_instances.index']
+
+    authorize_with :active
+    get :contents, {
+      id: groups(:aproject).uuid,
+      format: :json,
+    }
+    check_project_contents_response %w'arvados#pipelineInstance arvados#job'
+  end
 end
index 3c11b3e00940fefb644b2ea4b3e64f81ffdfed2b..8808a82c45c92d398df02e497438f137a7bf1e1a 100644 (file)
@@ -97,7 +97,7 @@ class Arvados::V1::JobsControllerTest < ActionController::TestCase
                  'server should correct bogus cancelled_at ' +
                  job['cancelled_at'])
     assert_equal(true,
-                 File.exists?(Rails.configuration.crunch_refresh_trigger),
+                 File.exist?(Rails.configuration.crunch_refresh_trigger),
                  'trigger file should be created when job is cancelled')
   end
 
index 1345701b43e8a7666a7c634d8f73c7762ad59467..6a19bdf4ad0baf8946d54be21c965f9b2d783ebd 100644 (file)
@@ -305,8 +305,8 @@ class Arvados::V1::LinksControllerTest < ActionController::TestCase
     assert_response 404
   end
 
-  test "retrieve all permissions using generic links index api" do
-    skip "(not implemented)"
+  # not implemented
+  skip "retrieve all permissions using generic links index api" do
     # Links.readable_by() does not return the full set of permission
     # links that are visible to a user (i.e., all permission links
     # whose head_uuid references an object for which the user has
index 71b528e72afd9467539f2136d3163481e582b956..56dd57ce7c64dd4697279ce3332ccd86546c99ef 100644 (file)
@@ -43,9 +43,8 @@ class Arvados::V1::RepositoriesControllerTest < ActionController::TestCase
   end
 
   test "get_all_permissions takes into account is_active flag" do
-    r = nil
     act_as_user users(:active) do
-      r = Repository.create! name: 'active/testrepo'
+      Repository.create! name: 'active/testrepo'
     end
     act_as_system_user do
       u = users(:active)
@@ -170,19 +169,19 @@ class Arvados::V1::RepositoriesControllerTest < ActionController::TestCase
         u = User.find_by_uuid(user_uuid)
         if perms['can_read']
           assert u.can? read: repo['uuid']
-          assert_match /R/, perms['gitolite_permissions']
+          assert_match(/R/, perms['gitolite_permissions'])
         else
-          refute_match /R/, perms['gitolite_permissions']
+          refute_match(/R/, perms['gitolite_permissions'])
         end
         if perms['can_write']
           assert u.can? write: repo['uuid']
-          assert_match /RW\+/, perms['gitolite_permissions']
+          assert_match(/RW\+/, perms['gitolite_permissions'])
         else
-          refute_match /W/, perms['gitolite_permissions']
+          refute_match(/W/, perms['gitolite_permissions'])
         end
         if perms['can_manage']
           assert u.can? manage: repo['uuid']
-          assert_match /RW\+/, perms['gitolite_permissions']
+          assert_match(/RW\+/, perms['gitolite_permissions'])
         end
       end
     end
index 2e370ec9cd63db9b61f6e93ab15111d028f469e0..710182174621b0bcf1eaddc8432b4ca824182949 100644 (file)
@@ -32,7 +32,7 @@ class Arvados::V1::SchemaControllerTest < ActionController::TestCase
     get :index
     assert_response :success
     discovery_doc = JSON.parse(@response.body)
-    assert_match /^[0-9a-f]+(-modified)?$/, discovery_doc['source_version']
+    assert_match(/^[0-9a-f]+(-modified)?$/, discovery_doc['source_version'])
   end
 
   test "discovery document overrides source_version with config" do
index 157e487859c927a978baad10c810592e36be9e77..579b8cc6d05256a88086cd0a50592e3d8a1afaa7 100644 (file)
@@ -603,7 +603,7 @@ class Arvados::V1::UsersControllerTest < ActionController::TestCase
 
     active_user = User.find_by_uuid(users(:active).uuid)
     readable_groups = active_user.groups_i_can(:read)
-    all_users_group = Group.all.collect(&:uuid).select { |g| g.match /-f+$/ }
+    all_users_group = Group.all.collect(&:uuid).select { |g| g.match(/-f+$/) }
     refute_includes(readable_groups, all_users_group,
                     "active user can read All Users group after being deactivated")
     assert_equal(false, active_user.is_invited,
@@ -842,14 +842,12 @@ class Arvados::V1::UsersControllerTest < ActionController::TestCase
   end
 
   def verify_num_links (original_links, expected_additional_links)
-    links_now = Link.all
     assert_equal expected_additional_links, Link.all.size-original_links.size,
         "Expected #{expected_additional_links.inspect} more links"
   end
 
   def find_obj_in_resp (response_items, object_type, head_kind=nil)
     return_obj = nil
-    response_items
     response_items.each { |x|
       if !x
         next
index 329bc1589afc6a2298472d739524fc094e7f0723..9b805af8e38b7095cdddafd67a5f18c63a7e5237 100644 (file)
@@ -33,7 +33,6 @@ class Arvados::V1::VirtualMachinesControllerTest < ActionController::TestCase
   test "groups is an empty list by default" do
     get_logins_for(:testvm2)
     active_login = find_login(:active)
-    perm = links(:active_can_login_to_testvm2)
     assert_equal([], active_login["groups"])
   end
 
index 2a618204c649bace8e6d271059ccee0413807e7d..4cf70cfbc61360aa9661bee1666d921a90137e8e 100644 (file)
@@ -74,7 +74,7 @@ module UsersTestHelper
     end
 
     group = Group.where(name: 'All users').select do |g|
-      g[:uuid].match /-f+$/
+      g[:uuid].match(/-f+$/)
     end.first
     group_read_perms = Link.where(tail_uuid: uuid,
                                   head_uuid: group[:uuid],
index 0bedc0726a08549711f3455f509778b1f9901de3..5f55f5eaecf8a56fa71c59a1f78cb9d6d065ec0c 100644 (file)
@@ -21,7 +21,7 @@ class CollectionsApiTest < ActionDispatch::IntegrationTest
       :filters => ['uuid', '=', 'ad02e37b6a7f45bbe2ead3c29a109b8a+54'].to_json
     }, auth(:active)
     assert_response 422
-    assert_match /nvalid element.*not an array/, json_response['errors'].join(' ')
+    assert_match(/nvalid element.*not an array/, json_response['errors'].join(' '))
   end
 
   test "get index with invalid filters (unsearchable column) responds 422" do
@@ -30,7 +30,7 @@ class CollectionsApiTest < ActionDispatch::IntegrationTest
       :filters => [['this_column_does_not_exist', '=', 'bogus']].to_json
     }, auth(:active)
     assert_response 422
-    assert_match /nvalid attribute/, json_response['errors'].join(' ')
+    assert_match(/nvalid attribute/, json_response['errors'].join(' '))
   end
 
   test "get index with invalid filters (invalid operator) responds 422" do
@@ -39,7 +39,7 @@ class CollectionsApiTest < ActionDispatch::IntegrationTest
       :filters => [['uuid', ':-(', 'displeased']].to_json
     }, auth(:active)
     assert_response 422
-    assert_match /nvalid operator/, json_response['errors'].join(' ')
+    assert_match(/nvalid operator/, json_response['errors'].join(' '))
   end
 
   test "get index with invalid filters (invalid operand type) responds 422" do
@@ -48,7 +48,7 @@ class CollectionsApiTest < ActionDispatch::IntegrationTest
       :filters => [['uuid', '=', {foo: 'bar'}]].to_json
     }, auth(:active)
     assert_response 422
-    assert_match /nvalid operand type/, json_response['errors'].join(' ')
+    assert_match(/nvalid operand type/, json_response['errors'].join(' '))
   end
 
   test "get index with where= (empty string)" do
@@ -73,7 +73,7 @@ class CollectionsApiTest < ActionDispatch::IntegrationTest
           :select => ['bogus'].to_json
         }, auth(:active)
     assert_response 422
-    assert_match /Invalid attribute.*bogus/, json_response['errors'].join(' ')
+    assert_match(/Invalid attribute.*bogus/, json_response['errors'].join(' '))
   end
 
   test "get index with select= (invalid attribute type) responds 422" do
@@ -82,7 +82,7 @@ class CollectionsApiTest < ActionDispatch::IntegrationTest
           :select => [['bogus']].to_json
         }, auth(:active)
     assert_response 422
-    assert_match /Invalid attribute.*bogus/, json_response['errors'].join(' ')
+    assert_match(/Invalid attribute.*bogus/, json_response['errors'].join(' '))
   end
 
   test "controller 404 response is json" do
@@ -243,8 +243,6 @@ class CollectionsApiTest < ActionDispatch::IntegrationTest
     assert_response :success
     assert_equal true, json_response['manifest_text'].include?('file4_in_subdir4.txt')
 
-    created = json_response
-
     # search using the filename
     search_using_full_text_search 'subdir2', 0
     search_using_full_text_search 'subdir2:*', 1
index a952c202cb7dbadf73fae734ca0141d000ac5cde..f6f39fe526edff84cc1679887bf400ecd2c3bc00 100644 (file)
@@ -5,8 +5,7 @@ require 'helpers/time_block'
 class CollectionsApiPerformanceTest < ActionDispatch::IntegrationTest
   include ManifestExamples
 
-  test "crud cycle for a collection with a big manifest" do
-    slow_test
+  slow_test "crud cycle for a collection with a big manifest" do
     bigmanifest = time_block 'make example' do
       make_manifest(streams: 100,
                     files_per_stream: 100,
@@ -39,8 +38,7 @@ class CollectionsApiPerformanceTest < ActionDispatch::IntegrationTest
     end
   end
 
-  test "memory usage" do
-    slow_test
+  slow_test "memory usage" do
     hugemanifest = make_manifest(streams: 1,
                                  files_per_stream: 2000,
                                  blocks_per_file: 200,
index ebe7ce7a6705b0d99d2e4b439b9339c7061a6531..28c1b81dabcc8621707044e1f7d225e6e1a624b7 100644 (file)
@@ -70,7 +70,7 @@ class CrossOriginTest < ActionDispatch::IntegrationTest
 
   def assert_no_cors_headers
     response.headers.keys.each do |h|
-      assert_no_match /^Access-Control-/i, h
+      assert_no_match(/^Access-Control-/i, h)
     end
   end
 end
index ecb2f2a05831a44a7798fd98d048a821878fd11a..029e37cbbfed18075a73785ce1b565d5907202cd 100644 (file)
@@ -3,8 +3,7 @@ require 'test_helper'
 class DatabaseResetTest < ActionDispatch::IntegrationTest
   self.use_transactional_fixtures = false
 
-  test "reset fails when Rails.env != 'test'" do
-    slow_test
+  slow_test "reset fails when Rails.env != 'test'" do
     rails_env_was = Rails.env
     begin
       Rails.env = 'production'
@@ -22,8 +21,7 @@ class DatabaseResetTest < ActionDispatch::IntegrationTest
     assert_response 403
   end
 
-  test "database reset doesn't break basic CRUD operations" do
-    slow_test
+  slow_test "database reset doesn't break basic CRUD operations" do
     active_auth = auth(:active)
     admin_auth = auth(:admin)
 
@@ -49,8 +47,7 @@ class DatabaseResetTest < ActionDispatch::IntegrationTest
     assert_response 404
   end
 
-  test "roll back database change" do
-    slow_test
+  slow_test "roll back database change" do
     active_auth = auth(:active)
     admin_auth = auth(:admin)
 
index a7bd545179813dcad92485b09f3e1a39c20c9927..982e172e186f3c9be84f2dddde653018377c6219 100644 (file)
@@ -36,6 +36,13 @@ class SelectTest < ActionDispatch::IntegrationTest
     end
   end
 
+  test "select with default order" do
+    get "/arvados/v1/links", {format: :json, select: ['uuid']}, auth(:admin)
+    assert_response :success
+    uuids = json_response['items'].collect { |i| i['uuid'] }
+    assert_equal uuids, uuids.sort
+  end
+
   def assert_link_classes_ascend(current_class, prev_class)
     # Databases and Ruby don't always agree about string ordering with
     # punctuation.  If the strings aren't ascending normally, check
index 7a9f9176d335c02c1a7dc425cf4212dfb85e0ea5..a46a4d1bc29c0e25992f6424bb4f50b919020229 100644 (file)
@@ -110,7 +110,7 @@ class UserSessionsApiTest < ActionDispatch::IntegrationTest
         (repos.collect(&:name) +
          vm_links.collect { |link| link.properties['username'] }
          ).each do |name|
-          r = name.match /^(.{#{prefix.length}})(\d+)$/
+          r = name.match(/^(.{#{prefix.length}})(\d+)$/)
           assert_not_nil r, "#{name.inspect} does not match {prefix}\\d+"
           assert_equal(prefix, r[1],
                        "#{name.inspect} was not {#{prefix.inspect} plus digits}")
index 99ca7ac960b3dac2fc4e0f9b82d89949afd6e76c..a9993b2fc318b4abe6ca0669f64a614250608733 100644 (file)
@@ -1,5 +1,4 @@
 require 'test_helper'
-require 'websocket_runner'
 require 'oj'
 require 'database_cleaner'
 
@@ -16,35 +15,92 @@ class WebsocketTest < ActionDispatch::IntegrationTest
     DatabaseCleaner.clean
   end
 
-  def ws_helper (token = nil, timeout = true)
+  def self.startup
+    s = TCPServer.new('0.0.0.0', 0)
+    @@port = s.addr[1]
+    s.close
+    @@pidfile = "tmp/pids/passenger.#{@@port}.pid"
+    DatabaseCleaner.start
+    Dir.chdir(Rails.root) do |apidir|
+      # Only passenger seems to be able to run the websockets server
+      # successfully.
+      _system('passenger', 'start', '-d',
+              "-p#{@@port}",
+              "--log-file", "/dev/stderr",
+              "--pid-file", @@pidfile)
+      timeout = Time.now.tv_sec + 10
+      begin
+        sleep 0.2
+        begin
+          server_pid = IO.read(@@pidfile).to_i
+          good_pid = (server_pid > 0) and (Process.kill(0, pid) rescue false)
+        rescue Errno::ENOENT
+          good_pid = false
+        end
+      end while (not good_pid) and (Time.now.tv_sec < timeout)
+      if not good_pid
+        raise RuntimeError, "could not find API server Rails pid"
+      end
+      STDERR.puts "Started websocket server on port #{@@port} with pid #{server_pid}"
+    end
+  end
+
+  def self.shutdown
+    Dir.chdir(Rails.root) do
+      _system('passenger', 'stop', "-p#{@@port}",
+              "--pid-file", @@pidfile)
+    end
+    # DatabaseCleaner leaves the database empty. Prefer to leave it full.
+    dc = DatabaseController.new
+    dc.define_singleton_method :render do |*args| end
+    dc.reset
+  end
+
+  def self._system(*cmd)
+    Bundler.with_clean_env do
+      env = {
+        'ARVADOS_WEBSOCKETS' => 'ws-only',
+        'RAILS_ENV' => 'test',
+      }
+      if not system(env, *cmd)
+        raise RuntimeError, "Command exited #{$?}: #{cmd.inspect}"
+      end
+    end
+  end
+
+  def ws_helper(token: nil, timeout: 8)
     opened = false
     close_status = nil
     too_long = false
 
-    EM.run {
+    EM.run do
       if token
-        ws = Faye::WebSocket::Client.new("ws://localhost:#{WEBSOCKET_PORT}/websocket?api_token=#{api_client_authorizations(token).api_token}")
+        ws = Faye::WebSocket::Client.new("ws://localhost:#{@@port}/websocket?api_token=#{api_client_authorizations(token).api_token}")
       else
-        ws = Faye::WebSocket::Client.new("ws://localhost:#{WEBSOCKET_PORT}/websocket")
+        ws = Faye::WebSocket::Client.new("ws://localhost:#{@@port}/websocket")
       end
 
       ws.on :open do |event|
         opened = true
         if timeout
-          EM::Timer.new 8 do
+          EM::Timer.new(timeout) do
             too_long = true if close_status.nil?
             EM.stop_event_loop
           end
         end
       end
 
+      ws.on :error do |event|
+        STDERR.puts "websocket client error: #{event.inspect}"
+      end
+
       ws.on :close do |event|
         close_status = [:close, event.code, event.reason]
         EM.stop_event_loop
       end
 
       yield ws
-    }
+    end
 
     assert opened, "Should have opened web socket"
     assert (not too_long), "Test took too long"
@@ -65,11 +121,10 @@ class WebsocketTest < ActionDispatch::IntegrationTest
     assert_equal 401, status
   end
 
-
   test "connect, subscribe and get response" do
     status = nil
 
-    ws_helper :active do |ws|
+    ws_helper(token: :active) do |ws|
       ws.on :open do |event|
         ws.send ({method: 'subscribe'}.to_json)
       end
@@ -91,7 +146,7 @@ class WebsocketTest < ActionDispatch::IntegrationTest
 
     authorize_with :active
 
-    ws_helper :active do |ws|
+    ws_helper(token: :active) do |ws|
       ws.on :open do |event|
         ws.send ({method: 'subscribe'}.to_json)
       end
@@ -128,7 +183,7 @@ class WebsocketTest < ActionDispatch::IntegrationTest
 
     authorize_with :active
 
-    ws_helper :active do |ws|
+    ws_helper(token: :active) do |ws|
       ws.on :open do |event|
         ws.send ({method: 'subscribe'}.to_json)
       end
@@ -168,7 +223,7 @@ class WebsocketTest < ActionDispatch::IntegrationTest
 
     authorize_with :active
 
-    ws_helper :active do |ws|
+    ws_helper(token: :active) do |ws|
       ws.on :open do |event|
         ws.send ({method: 'subscribe', filters: [['object_uuid', 'is_a', 'arvados#human']]}.to_json)
       end
@@ -206,7 +261,7 @@ class WebsocketTest < ActionDispatch::IntegrationTest
 
     authorize_with :active
 
-    ws_helper :active do |ws|
+    ws_helper(token: :active) do |ws|
       ws.on :open do |event|
         ws.send ({method: 'subscribe', filters: [['object_uuid', 'is_a', 'arvados#human']]}.to_json)
         ws.send ({method: 'subscribe', filters: [['object_uuid', 'is_a', 'arvados#specimen']]}.to_json)
@@ -251,7 +306,7 @@ class WebsocketTest < ActionDispatch::IntegrationTest
 
     authorize_with :active
 
-    ws_helper :active do |ws|
+    ws_helper(token: :active) do |ws|
       ws.on :open do |event|
         ws.send ({method: 'subscribe', filters: [['object_uuid', 'is_a', 'arvados#trait'], ['event_type', '=', 'update']]}.to_json)
       end
@@ -282,8 +337,6 @@ class WebsocketTest < ActionDispatch::IntegrationTest
 
   test "connect, subscribe, ask events starting at seq num" do
     state = 1
-    human = nil
-    human_ev_uuid = nil
 
     authorize_with :active
 
@@ -291,7 +344,7 @@ class WebsocketTest < ActionDispatch::IntegrationTest
     l1 = nil
     l2 = nil
 
-    ws_helper :active do |ws|
+    ws_helper(token: :active) do |ws|
       ws.on :open do |event|
         ws.send ({method: 'subscribe', last_log_id: lastid}.to_json)
       end
@@ -322,16 +375,14 @@ class WebsocketTest < ActionDispatch::IntegrationTest
     assert_equal expect_next_logs[1].object_uuid, l2
   end
 
-  test "connect, subscribe, get event, unsubscribe" do
-    slow_test
+  slow_test "connect, subscribe, get event, unsubscribe" do
     state = 1
     spec = nil
     spec_ev_uuid = nil
-    filter_id = nil
 
     authorize_with :active
 
-    ws_helper :active, false do |ws|
+    ws_helper(token: :active, timeout: false) do |ws|
       ws.on :open do |event|
         ws.send ({method: 'subscribe'}.to_json)
         EM::Timer.new 3 do
@@ -372,15 +423,14 @@ class WebsocketTest < ActionDispatch::IntegrationTest
     assert_equal spec.uuid, spec_ev_uuid
   end
 
-  test "connect, subscribe, get event, unsubscribe with filter" do
-    slow_test
+  slow_test "connect, subscribe, get event, unsubscribe with filter" do
     state = 1
     spec = nil
     spec_ev_uuid = nil
 
     authorize_with :active
 
-    ws_helper :active, false do |ws|
+    ws_helper(token: :active, timeout: false) do |ws|
       ws.on :open do |event|
         ws.send ({method: 'subscribe', filters: [['object_uuid', 'is_a', 'arvados#human']]}.to_json)
         EM::Timer.new 6 do
@@ -422,8 +472,7 @@ class WebsocketTest < ActionDispatch::IntegrationTest
   end
 
 
-  test "connect, subscribe, get event, try to unsubscribe with bogus filter" do
-    slow_test
+  slow_test "connect, subscribe, get event, try to unsubscribe with bogus filter" do
     state = 1
     spec = nil
     spec_ev_uuid = nil
@@ -432,7 +481,7 @@ class WebsocketTest < ActionDispatch::IntegrationTest
 
     authorize_with :active
 
-    ws_helper :active do |ws|
+    ws_helper(token: :active) do |ws|
       ws.on :open do |event|
         ws.send ({method: 'subscribe'}.to_json)
       end
@@ -473,13 +522,10 @@ class WebsocketTest < ActionDispatch::IntegrationTest
     assert_equal human.uuid, human_ev_uuid
   end
 
-
-
-  test "connected, not subscribed, no event" do
-    slow_test
+  slow_test "connected, not subscribed, no event" do
     authorize_with :active
 
-    ws_helper :active, false do |ws|
+    ws_helper(token: :active, timeout: false) do |ws|
       ws.on :open do |event|
         EM::Timer.new 1 do
           Specimen.create
@@ -496,13 +542,12 @@ class WebsocketTest < ActionDispatch::IntegrationTest
     end
   end
 
-  test "connected, not authorized to see event" do
-    slow_test
+  slow_test "connected, not authorized to see event" do
     state = 1
 
     authorize_with :admin
 
-    ws_helper :active, false do |ws|
+    ws_helper(token: :active, timeout: false) do |ws|
       ws.on :open do |event|
         ws.send ({method: 'subscribe'}.to_json)
 
@@ -530,7 +575,7 @@ class WebsocketTest < ActionDispatch::IntegrationTest
   test "connect, try bogus method" do
     status = nil
 
-    ws_helper :active do |ws|
+    ws_helper(token: :active) do |ws|
       ws.on :open do |event|
         ws.send ({method: 'frobnabble'}.to_json)
       end
@@ -548,7 +593,7 @@ class WebsocketTest < ActionDispatch::IntegrationTest
   test "connect, missing method" do
     status = nil
 
-    ws_helper :active do |ws|
+    ws_helper(token: :active) do |ws|
       ws.on :open do |event|
         ws.send ({fizzbuzz: 'frobnabble'}.to_json)
       end
@@ -566,7 +611,7 @@ class WebsocketTest < ActionDispatch::IntegrationTest
   test "connect, send malformed request" do
     status = nil
 
-    ws_helper :active do |ws|
+    ws_helper(token: :active) do |ws|
       ws.on :open do |event|
         ws.send '<XML4EVER></XML4EVER>'
       end
@@ -587,7 +632,7 @@ class WebsocketTest < ActionDispatch::IntegrationTest
 
     authorize_with :active
 
-    ws_helper :active do |ws|
+    ws_helper(token: :active) do |ws|
       ws.on :open do |event|
         (1..17).each do |i|
           ws.send ({method: 'subscribe', filters: [['object_uuid', '=', i]]}.to_json)
@@ -612,15 +657,14 @@ class WebsocketTest < ActionDispatch::IntegrationTest
 
   end
 
-  test "connect, subscribe, lots of events" do
-    slow_test
+  slow_test "connect, subscribe, lots of events" do
     state = 1
     event_count = 0
     log_start = Log.order(:id).last.id
 
     authorize_with :active
 
-    ws_helper :active, false do |ws|
+    ws_helper(token: :active, timeout: false) do |ws|
       EM::Timer.new 45 do
         # Needs a longer timeout than the default
         ws.close
@@ -637,7 +681,7 @@ class WebsocketTest < ActionDispatch::IntegrationTest
           assert_equal 200, d["status"]
           ActiveRecord::Base.transaction do
             (1..202).each do
-              spec = Specimen.create
+              Specimen.create
             end
           end
           state = 2
@@ -658,12 +702,10 @@ class WebsocketTest < ActionDispatch::IntegrationTest
 
   test "connect, subscribe with invalid filter" do
     state = 1
-    human = nil
-    human_ev_uuid = nil
 
     authorize_with :active
 
-    ws_helper :active do |ws|
+    ws_helper(token: :active) do |ws|
       ws.on :open do |event|
         # test that #6451 is fixed (invalid filter crashes websockets)
         ws.send ({method: 'subscribe', filters: [['object_blarg', 'is_a', 'arvados#human']]}.to_json)
@@ -675,7 +717,7 @@ class WebsocketTest < ActionDispatch::IntegrationTest
         when 1
           assert_equal 200, d["status"]
           Specimen.create
-          human = Human.create
+          Human.create
           state = 2
         when 2
           assert_equal 500, d["status"]
index 417ddf6bee8eeee96d8e960099ccc227cee4950a..86bc2397c5309e98310f659d4077d7d0f8a33184 100644 (file)
@@ -22,7 +22,7 @@ end
 
 require File.expand_path('../../config/environment', __FILE__)
 require 'rails/test_help'
-require 'mocha/mini_test'
+require 'mocha'
 
 module ArvadosTestSupport
   def json_response
@@ -84,7 +84,7 @@ class ActiveSupport::TestCase
   def restore_configuration
     # Restore configuration settings changed during tests
     $application_config.each do |k,v|
-      if k.match /^[^.]*$/
+      if k.match(/^[^.]*$/)
         Rails.configuration.send (k + '='), v
       end
     end
@@ -112,9 +112,18 @@ class ActiveSupport::TestCase
                              "HTTP_AUTHORIZATION" => "OAuth2 #{t}")
   end
 
-  def slow_test
-    skip "RAILS_TEST_SHORT is set" unless (ENV['RAILS_TEST_SHORT'] || '').empty?
+  def self.skip_slow_tests?
+    !(ENV['RAILS_TEST_SHORT'] || '').empty?
   end
+
+  def self.skip(*args, &block)
+  end
+
+  def self.slow_test(name, &block)
+    define_method(name, block) unless skip_slow_tests?
+  end
+
+  alias_method :skip, :omit
 end
 
 class ActionController::TestCase
@@ -135,6 +144,21 @@ class ActionController::TestCase
       super action, *args
     end
   end
+
+  def self.suite
+    s = super
+    def s.run(*args)
+      @test_case.startup()
+      begin
+        super
+      ensure
+        @test_case.shutdown()
+      end
+    end
+    s
+  end
+  def self.startup; end
+  def self.shutdown; end
 end
 
 class ActionDispatch::IntegrationTest
index 3e9b16757dbf8af4750ea21402b78a752202c54e..2e585051ad56ce0c2f6ecaf8fe6f53d5c3e9795d 100644 (file)
@@ -20,16 +20,19 @@ class AppVersionTest < ActiveSupport::TestCase
     end
   end
 
-  test 'override with configuration' do
+  test 'override with configuration "foobar"' do
     Rails.configuration.source_version = 'foobar'
     assert_equal 'foobar', AppVersion.hash
+  end
+
+  test 'override with configuration false' do
     Rails.configuration.source_version = false
     assert_not_equal 'foobar', AppVersion.hash
   end
 
   test 'override with file' do
     path = Rails.root.join 'git-commit.version'
-    assert(!File.exists?(path),
+    assert(!File.exist?(path),
            "Packaged version file found in source tree: #{path}")
     begin
       File.open(path, 'w') do |f|
index 6918aa0d00058b4d6183e92c89506aacb45a3f85..676581470cd402e761fb851cab64e3405445e396 100644 (file)
@@ -146,6 +146,28 @@ class ArvadosModelTest < ActiveSupport::TestCase
     end
   end
 
+  test "full text search index exists on models" do
+    fts_tables =  ["collections", "container_requests", "groups", "jobs",
+                   "pipeline_instances", "pipeline_templates", "workflows"]
+    fts_tables.each do |table|
+      table_class = table.classify.constantize
+      if table_class.respond_to?('full_text_searchable_columns')
+        fts_index_columns = table_class.full_text_searchable_columns
+        index_columns = nil
+        indexes = ActiveRecord::Base.connection.indexes(table)
+        fts_index_by_columns = indexes.select do |index|
+          if index.columns.first.match(/to_tsvector/)
+            index_columns = index.columns.first.scan(/\((?<columns>[A-Za-z_]+)\,/).flatten!
+            index_columns.sort == fts_index_columns.sort
+          else
+            false
+          end
+        end
+        assert !fts_index_by_columns.empty?, "#{table} has no FTS index with columns #{fts_index_columns}. Instead found FTS index with columns #{index_columns}"
+      end
+    end
+  end
+
   test "selectable_attributes includes database attributes" do
     assert_includes(Job.selectable_attributes, "success")
   end
index 5a661785bd7bef903747b5890bb135b8bacaebf1..25801bb9b6f6d38b65bd4947ba9b62a045c5c913 100644 (file)
@@ -25,7 +25,7 @@ class AuthorizedKeyTest < ActiveSupport::TestCase
       ak2 = AuthorizedKey.new(name: "bar", public_key: TEST_KEY, authorized_user_uuid: u2.uuid)
       refute ak2.valid?
       refute ak2.save
-      assert_match /already exists/, ak2.errors.full_messages.to_s
+      assert_match(/already exists/, ak2.errors.full_messages.to_s)
     end
   end
 
index 1c6e4f2db2c0dfafcde3d7fba519fe8e4431cf6b..57beddbe6d6247bf9f96d21103e6b2640c42732a 100644 (file)
@@ -17,8 +17,7 @@ class CollectionModelPerformanceTest < ActiveSupport::TestCase
   end
 
   # "crrud" == "create read render update delete", not a typo
-  test "crrud cycle for a collection with a big manifest)" do
-    slow_test
+  slow_test "crrud cycle for a collection with a big manifest)" do
     bigmanifest = time_block 'make example' do
       make_manifest(streams: 100,
                     files_per_stream: 100,
@@ -44,7 +43,7 @@ class CollectionModelPerformanceTest < ActiveSupport::TestCase
         c.signed_manifest_text
       end
       time_block 'sign + render' do
-        resp = c.as_api_response(nil)
+        c.as_api_response(nil)
       end
       loc = Blob.sign_locator(Digest::MD5.hexdigest('foo') + '+3',
                               api_token: api_token(:active))
index 91568927ae37654117da4dec7c811882818d0add..4984aad88b7a01ffbb4c226ebc01f91953be771a 100644 (file)
@@ -1,4 +1,5 @@
 require 'test_helper'
+require 'sweep_trashed_collections'
 
 class CollectionTest < ActiveSupport::TestCase
   include DbCurrentTime
@@ -28,7 +29,7 @@ class CollectionTest < ActiveSupport::TestCase
       c = create_collection "f\xc8o", Encoding::UTF_8
       assert !c.valid?
       assert_equal [:manifest_text], c.errors.messages.keys
-      assert_match /UTF-8/, c.errors.messages[:manifest_text].first
+      assert_match(/UTF-8/, c.errors.messages[:manifest_text].first)
     end
   end
 
@@ -37,7 +38,7 @@ class CollectionTest < ActiveSupport::TestCase
       c = create_collection "f\xc8o", Encoding::ASCII_8BIT
       assert !c.valid?
       assert_equal [:manifest_text], c.errors.messages.keys
-      assert_match /UTF-8/, c.errors.messages[:manifest_text].first
+      assert_match(/UTF-8/, c.errors.messages[:manifest_text].first)
     end
   end
 
@@ -107,11 +108,11 @@ class CollectionTest < ActiveSupport::TestCase
       assert c.valid?
       created_file_names = c.file_names
       assert created_file_names
-      assert_match /foo.txt/, c.file_names
+      assert_match(/foo.txt/, c.file_names)
 
       c.update_attribute 'manifest_text', ". d41d8cd98f00b204e9800998ecf8427e+0 0:0:foo2.txt\n"
       assert_not_equal created_file_names, c.file_names
-      assert_match /foo2.txt/, c.file_names
+      assert_match(/foo2.txt/, c.file_names)
     end
   end
 
@@ -134,11 +135,11 @@ class CollectionTest < ActiveSupport::TestCase
 
         assert c.valid?
         assert c.file_names
-        assert_match /veryverylongfilename0000000000001.txt/, c.file_names
-        assert_match /veryverylongfilename0000000000002.txt/, c.file_names
+        assert_match(/veryverylongfilename0000000000001.txt/, c.file_names)
+        assert_match(/veryverylongfilename0000000000002.txt/, c.file_names)
         if not allow_truncate
-          assert_match /veryverylastfilename/, c.file_names
-          assert_match /laststreamname/, c.file_names
+          assert_match(/veryverylastfilename/, c.file_names)
+          assert_match(/laststreamname/, c.file_names)
         end
       end
     end
@@ -307,11 +308,11 @@ class CollectionTest < ActiveSupport::TestCase
     end
   end
 
-  test 'signature expiry does not exceed expires_at' do
+  test 'signature expiry does not exceed trash_at' do
     act_as_user users(:active) do
       t0 = db_current_time
       c = Collection.create!(manifest_text: ". d41d8cd98f00b204e9800998ecf8427e+0 0:0:x\n", name: 'foo')
-      c.update_attributes! expires_at: (t0 + 1.hours)
+      c.update_attributes! trash_at: (t0 + 1.hours)
       c.reload
       sig_exp = /\+A[0-9a-f]{40}\@([0-9]+)/.match(c.signed_manifest_text)[1].to_i
       assert_operator sig_exp.to_i, :<=, (t0 + 1.hours).to_i
@@ -322,10 +323,10 @@ class CollectionTest < ActiveSupport::TestCase
     act_as_user users(:active) do
       c = Collection.create!(manifest_text: ". d41d8cd98f00b204e9800998ecf8427e+0 0:0:x\n",
                              name: 'foo',
-                             expires_at: db_current_time + 1.years)
+                             trash_at: db_current_time + 1.years)
       sig_exp = /\+A[0-9a-f]{40}\@([0-9]+)/.match(c.signed_manifest_text)[1].to_i
       expect_max_sig_exp = db_current_time.to_i + Rails.configuration.blob_signature_ttl
-      assert_operator c.expires_at.to_i, :>, expect_max_sig_exp
+      assert_operator c.trash_at.to_i, :>, expect_max_sig_exp
       assert_operator sig_exp.to_i, :<=, expect_max_sig_exp
     end
   end
@@ -348,7 +349,7 @@ class CollectionTest < ActiveSupport::TestCase
       uuid = c.uuid
 
       # mark collection as expired
-      c.update_attribute 'expires_at', Time.new.strftime("%Y-%m-%d")
+      c.update_attributes!(trash_at: Time.new.strftime("%Y-%m-%d"))
       c = Collection.where(uuid: uuid)
       assert_empty c, 'Should not be able to find expired collection'
 
@@ -359,6 +360,108 @@ class CollectionTest < ActiveSupport::TestCase
     end
   end
 
+  test 'trash_at cannot be set too far in the past' do
+    act_as_user users(:active) do
+      t0 = db_current_time
+      c = Collection.create!(manifest_text: '', name: 'foo')
+      c.update_attributes! trash_at: (t0 - 2.weeks)
+      c.reload
+      assert_operator c.trash_at, :>, t0
+    end
+  end
+
+  [['trash-to-delete interval negative',
+    :collection_owned_by_active,
+    {trash_at: Time.now+2.weeks, delete_at: Time.now},
+    {state: :invalid}],
+   ['trash-to-delete interval too short',
+    :collection_owned_by_active,
+    {trash_at: Time.now+3.days, delete_at: Time.now+7.days},
+    {state: :invalid}],
+   ['trash-to-delete interval ok',
+    :collection_owned_by_active,
+    {trash_at: Time.now, delete_at: Time.now+15.days},
+    {state: :trash_now}],
+   ['trash-to-delete interval short, but far enough in future',
+    :collection_owned_by_active,
+    {trash_at: Time.now+13.days, delete_at: Time.now+15.days},
+    {state: :trash_future}],
+   ['trash by setting is_trashed bool',
+    :collection_owned_by_active,
+    {is_trashed: true},
+    {state: :trash_now}],
+   ['trash in future by setting just trash_at',
+    :collection_owned_by_active,
+    {trash_at: Time.now+1.week},
+    {state: :trash_future}],
+   ['trash in future by setting trash_at and delete_at',
+    :collection_owned_by_active,
+    {trash_at: Time.now+1.week, delete_at: Time.now+4.weeks},
+    {state: :trash_future}],
+   ['untrash by clearing is_trashed bool',
+    :expired_collection,
+    {is_trashed: false},
+    {state: :not_trash}],
+  ].each do |test_name, fixture_name, updates, expect|
+    test test_name do
+      act_as_user users(:active) do
+        min_exp = (db_current_time +
+                   Rails.configuration.blob_signature_ttl.seconds)
+        if fixture_name == :expired_collection
+          # Fixture-finder shorthand doesn't find trashed collections
+          # because they're not in the default scope.
+          c = Collection.unscoped.find_by_uuid('zzzzz-4zz18-mto52zx1s7sn3ih')
+        else
+          c = collections(fixture_name)
+        end
+        updates_ok = c.update_attributes(updates)
+        expect_valid = expect[:state] != :invalid
+        assert_equal updates_ok, expect_valid, c.errors.full_messages.to_s
+        case expect[:state]
+        when :invalid
+          refute c.valid?
+        when :trash_now
+          assert c.is_trashed
+          assert_not_nil c.trash_at
+          assert_operator c.trash_at, :<=, db_current_time
+          assert_not_nil c.delete_at
+          assert_operator c.delete_at, :>=, min_exp
+        when :trash_future
+          refute c.is_trashed
+          assert_not_nil c.trash_at
+          assert_operator c.trash_at, :>, db_current_time
+          assert_not_nil c.delete_at
+          assert_operator c.delete_at, :>=, c.trash_at
+          # Currently this minimum interval is needed to prevent early
+          # garbage collection:
+          assert_operator c.delete_at, :>=, min_exp
+        when :not_trash
+          refute c.is_trashed
+          assert_nil c.trash_at
+          assert_nil c.delete_at
+        else
+          raise "bad expect[:state]==#{expect[:state].inspect} in test case"
+        end
+      end
+    end
+  end
+
+  test 'default trash interval > blob signature ttl' do
+    Rails.configuration.default_trash_lifetime = 86400 * 21 # 3 weeks
+    start = db_current_time
+    act_as_user users(:active) do
+      c = Collection.create!(manifest_text: '', name: 'foo')
+      c.update_attributes!(trash_at: start + 86400.seconds)
+      assert_operator c.delete_at, :>=, start + (86400*22).seconds
+      assert_operator c.delete_at, :<, start + (86400*22 + 30).seconds
+      c.destroy
+
+      c = Collection.create!(manifest_text: '', name: 'foo')
+      c.update_attributes!(is_trashed: true)
+      assert_operator c.delete_at, :>=, start + (86400*21).seconds
+    end
+  end
+
   test "find_all_for_docker_image resolves names that look like hashes" do
     coll_list = Collection.
       find_all_for_docker_image('a' * 64, nil, [users(:active)])
@@ -366,13 +469,28 @@ class CollectionTest < ActiveSupport::TestCase
     assert_includes(coll_uuids, collections(:docker_image).uuid)
   end
 
-  test 'expires_at cannot be set too far in the past' do
+  test "move to trash in SweepTrashedCollections" do
+    c = collections(:trashed_on_next_sweep)
+    refute_empty Collection.where('uuid=? and is_trashed=false', c.uuid)
+    assert_raises(ActiveRecord::RecordNotUnique) do
+      act_as_user users(:active) do
+        Collection.create!(owner_uuid: c.owner_uuid,
+                           name: c.name)
+      end
+    end
+    SweepTrashedCollections.sweep_now
+    c = Collection.unscoped.where('uuid=? and is_trashed=true', c.uuid).first
+    assert c
     act_as_user users(:active) do
-      t0 = db_current_time
-      c = Collection.create!(manifest_text: '', name: 'foo')
-      c.update_attributes! expires_at: (t0 - 2.weeks)
-      c.reload
-      assert_operator c.expires_at, :>, t0
+      assert Collection.create!(owner_uuid: c.owner_uuid,
+                                name: c.name)
     end
   end
+
+  test "delete in SweepTrashedCollections" do
+    uuid = 'zzzzz-4zz18-3u1p5umicfpqszp' # deleted_on_next_sweep
+    assert_not_empty Collection.unscoped.where(uuid: uuid)
+    SweepTrashedCollections.sweep_now
+    assert_empty Collection.unscoped.where(uuid: uuid)
+  end
 end
index b57c23b4538dee4339a0a27630a1ad36e7e575a6..a8594169fb3c7567aa95a00922be6350ac41ac52 100644 (file)
@@ -18,7 +18,7 @@ class CommitTest < ActiveSupport::TestCase
   test 'find_commit_range does not bypass permissions' do
     authorize_with :inactive
     assert_raises ArgumentError do
-      c = Commit.find_commit_range 'foo', nil, 'master', []
+      Commit.find_commit_range 'foo', nil, 'master', []
     end
   end
 
@@ -68,10 +68,10 @@ class CommitTest < ActiveSupport::TestCase
     authorize_with :active
     gitint = "git --git-dir #{Rails.configuration.git_internal_dir}"
     IO.read("|#{gitint} tag -d testtag 2>/dev/null") # "no such tag", fine
-    assert_match /^fatal: /, IO.read("|#{gitint} show testtag 2>&1")
+    assert_match(/^fatal: /, IO.read("|#{gitint} show testtag 2>&1"))
     refute $?.success?
     Commit.tag_in_internal_repository 'active/foo', '31ce37fe365b3dc204300a3e4c396ad333ed0556', 'testtag'
-    assert_match /^commit 31ce37f/, IO.read("|#{gitint} show testtag")
+    assert_match(/^commit 31ce37f/, IO.read("|#{gitint} show testtag"))
     assert $?.success?
   end
 
@@ -183,34 +183,34 @@ class CommitTest < ActiveSupport::TestCase
     Dir.mktmpdir do |touchdir|
       # invalid input to maximum
       a = Commit.find_commit_range('active/foo', nil, "31ce37fe365b3dc204300a3e4c396ad333ed0556 ; touch #{touchdir}/uh_oh", nil)
-      assert !File.exists?("#{touchdir}/uh_oh"), "#{touchdir}/uh_oh should not exist, 'maximum' parameter of find_commit_range is exploitable"
+      assert !File.exist?("#{touchdir}/uh_oh"), "#{touchdir}/uh_oh should not exist, 'maximum' parameter of find_commit_range is exploitable"
       assert_equal [], a
 
       # invalid input to maximum
       a = Commit.find_commit_range('active/foo', nil, "$(uname>#{touchdir}/uh_oh)", nil)
-      assert !File.exists?("#{touchdir}/uh_oh"), "#{touchdir}/uh_oh should not exist, 'maximum' parameter of find_commit_range is exploitable"
+      assert !File.exist?("#{touchdir}/uh_oh"), "#{touchdir}/uh_oh should not exist, 'maximum' parameter of find_commit_range is exploitable"
       assert_equal [], a
 
       # invalid input to minimum
       a = Commit.find_commit_range('active/foo', "31ce37fe365b3dc204300a3e4c396ad333ed0556 ; touch #{touchdir}/uh_oh", "31ce37fe365b3dc204300a3e4c396ad333ed0556", nil)
-      assert !File.exists?("#{touchdir}/uh_oh"), "#{touchdir}/uh_oh should not exist, 'minimum' parameter of find_commit_range is exploitable"
+      assert !File.exist?("#{touchdir}/uh_oh"), "#{touchdir}/uh_oh should not exist, 'minimum' parameter of find_commit_range is exploitable"
       assert_equal [], a
 
       # invalid input to minimum
       a = Commit.find_commit_range('active/foo', "$(uname>#{touchdir}/uh_oh)", "31ce37fe365b3dc204300a3e4c396ad333ed0556", nil)
-      assert !File.exists?("#{touchdir}/uh_oh"), "#{touchdir}/uh_oh should not exist, 'minimum' parameter of find_commit_range is exploitable"
+      assert !File.exist?("#{touchdir}/uh_oh"), "#{touchdir}/uh_oh should not exist, 'minimum' parameter of find_commit_range is exploitable"
       assert_equal [], a
 
       # invalid input to 'excludes'
       # complains "fatal: bad object 077ba2ad3ea24a929091a9e6ce545c93199b8e57"
       a = Commit.find_commit_range('active/foo', "31ce37fe365b3dc204300a3e4c396ad333ed0556", "077ba2ad3ea24a929091a9e6ce545c93199b8e57", ["4fe459abe02d9b365932b8f5dc419439ab4e2577 ; touch #{touchdir}/uh_oh"])
-      assert !File.exists?("#{touchdir}/uh_oh"), "#{touchdir}/uh_oh should not exist, 'excludes' parameter of find_commit_range is exploitable"
+      assert !File.exist?("#{touchdir}/uh_oh"), "#{touchdir}/uh_oh should not exist, 'excludes' parameter of find_commit_range is exploitable"
       assert_equal [], a
 
       # invalid input to 'excludes'
       # complains "fatal: bad object 077ba2ad3ea24a929091a9e6ce545c93199b8e57"
       a = Commit.find_commit_range('active/foo', "31ce37fe365b3dc204300a3e4c396ad333ed0556", "077ba2ad3ea24a929091a9e6ce545c93199b8e57", ["$(uname>#{touchdir}/uh_oh)"])
-      assert !File.exists?("#{touchdir}/uh_oh"), "#{touchdir}/uh_oh should not exist, 'excludes' parameter of find_commit_range is exploitable"
+      assert !File.exist?("#{touchdir}/uh_oh"), "#{touchdir}/uh_oh should not exist, 'excludes' parameter of find_commit_range is exploitable"
       assert_equal [], a
     end
   end
index 34aa442c0938381e5fb56ebb2c6379616ad93976..da4f0bb7a8db515e595656b4c8d0fa063ffac8ac 100644 (file)
@@ -230,10 +230,12 @@ class ContainerRequestTest < ActiveSupport::TestCase
     cr.reload
     assert_equal "Committed", cr.state
 
+    output_pdh = '1f4b0bc7583c2a7f9102c395f4ffc5e3+45'
+    log_pdh = 'fa7aeb5140e2848d39b416daeef4ffc5+45'
     act_as_system_user do
       c.update_attributes!(state: Container::Complete,
-                           output: '1f4b0bc7583c2a7f9102c395f4ffc5e3+45',
-                           log: 'fa7aeb5140e2848d39b416daeef4ffc5+45')
+                           output: output_pdh,
+                           log: log_pdh)
     end
 
     cr.reload
@@ -244,6 +246,12 @@ class ContainerRequestTest < ActiveSupport::TestCase
                                        owner_uuid: project.uuid).count,
                    "Container #{out_type} should be copied to #{project.uuid}")
     end
+    assert_not_nil cr.output_uuid
+    assert_not_nil cr.log_uuid
+    output = Collection.find_by_uuid cr.output_uuid
+    assert_equal output_pdh, output.portable_data_hash
+    log = Collection.find_by_uuid cr.log_uuid
+    assert_equal log_pdh, log.portable_data_hash
   end
 
   test "Container makes container request, then is cancelled" do
@@ -463,6 +471,7 @@ class ContainerRequestTest < ActiveSupport::TestCase
   test "Retry on container cancelled" do
     set_user_from_auth :active
     cr = create_minimal_req!(priority: 1, state: "Committed", container_count_max: 2)
+    cr2 = create_minimal_req!(priority: 1, state: "Committed", container_count_max: 2, command: ["echo", "baz"])
     prev_container_uuid = cr.container_uuid
 
     c = act_as_system_user do
@@ -473,8 +482,10 @@ class ContainerRequestTest < ActiveSupport::TestCase
     end
 
     cr.reload
+    cr2.reload
     assert_equal "Committed", cr.state
     assert_equal prev_container_uuid, cr.container_uuid
+    assert_not_equal cr2.container_uuid, cr.container_uuid
     prev_container_uuid = cr.container_uuid
 
     act_as_system_user do
@@ -482,8 +493,10 @@ class ContainerRequestTest < ActiveSupport::TestCase
     end
 
     cr.reload
+    cr2.reload
     assert_equal "Committed", cr.state
     assert_not_equal prev_container_uuid, cr.container_uuid
+    assert_not_equal cr2.container_uuid, cr.container_uuid
     prev_container_uuid = cr.container_uuid
 
     c = act_as_system_user do
@@ -493,8 +506,39 @@ class ContainerRequestTest < ActiveSupport::TestCase
     end
 
     cr.reload
+    cr2.reload
     assert_equal "Final", cr.state
     assert_equal prev_container_uuid, cr.container_uuid
+    assert_not_equal cr2.container_uuid, cr.container_uuid
+  end
+
+  test "Output collection name setting using output_name with name collision resolution" do
+    set_user_from_auth :active
+    output_name = collections(:foo_file).name
+
+    cr = create_minimal_req!(priority: 1,
+                             state: ContainerRequest::Committed,
+                             output_name: output_name)
+    act_as_system_user do
+      c = Container.find_by_uuid(cr.container_uuid)
+      c.update_attributes!(state: Container::Locked)
+      c.update_attributes!(state: Container::Running)
+      c.update_attributes!(state: Container::Complete,
+                           exit_code: 0,
+                           output: '1f4b0bc7583c2a7f9102c395f4ffc5e3+45',
+                           log: 'fa7aeb5140e2848d39b416daeef4ffc5+45')
+    end
+    cr.save
+    assert_equal ContainerRequest::Final, cr.state
+    output_coll = Collection.find_by_uuid(cr.output_uuid)
+    # Make sure the resulting output collection name include the original name
+    # plus the date
+    assert_not_equal output_name, output_coll.name,
+                     "It shouldn't exist more than one collection with the same owner and name '${output_name}'"
+    assert output_coll.name.include?(output_name),
+           "New name should include original name"
+    assert_match /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z/, output_coll.name,
+                 "New name should include ISO8601 date"
   end
 
   test "Finalize committed request when reusing a finished container" do
@@ -552,4 +596,36 @@ class ContainerRequestTest < ActiveSupport::TestCase
       end
     end
   end
+
+  [
+    [{"partitions" => ["fastcpu","vfastcpu", 100]}, ContainerRequest::Committed, ActiveRecord::RecordInvalid],
+    [{"partitions" => ["fastcpu","vfastcpu", 100]}, ContainerRequest::Uncommitted],
+    [{"partitions" => "fastcpu"}, ContainerRequest::Committed, ActiveRecord::RecordInvalid],
+    [{"partitions" => "fastcpu"}, ContainerRequest::Uncommitted],
+    [{"partitions" => ["fastcpu","vfastcpu"]}, ContainerRequest::Committed],
+  ].each do |sp, state, expected|
+    test "create container request with scheduling_parameters #{sp} in state #{state} and verify #{expected}" do
+      common_attrs = {cwd: "test",
+                      priority: 1,
+                      command: ["echo", "hello"],
+                      output_path: "test",
+                      scheduling_parameters: sp,
+                      mounts: {"test" => {"kind" => "json"}}}
+      set_user_from_auth :active
+
+      if expected == ActiveRecord::RecordInvalid
+        assert_raises(ActiveRecord::RecordInvalid) do
+          create_minimal_req!(common_attrs.merge({state: state}))
+        end
+      else
+        cr = create_minimal_req!(common_attrs.merge({state: state}))
+        assert_equal sp, cr.scheduling_parameters
+
+        if state == ContainerRequest::Committed
+          c = Container.find_by_uuid(cr.container_uuid)
+          assert_equal sp, c.scheduling_parameters
+        end
+      end
+    end
+  end
 end
index 4fd9f8e75931eff3d3f66e8fcddd4e65253cec7f..a1755332853e227acee600d990b1866a9d22b443 100644 (file)
@@ -102,8 +102,9 @@ class ContainerTest < ActiveSupport::TestCase
   test "find_reusable method should select higher priority queued container" do
     set_user_from_auth :active
     common_attrs = REUSABLE_COMMON_ATTRS.merge({environment:{"var" => "queued"}})
-    c_low_priority, _ = minimal_new(common_attrs.merge({priority:1}))
-    c_high_priority, _ = minimal_new(common_attrs.merge({priority:2}))
+    c_low_priority, _ = minimal_new(common_attrs.merge({use_existing:false, priority:1}))
+    c_high_priority, _ = minimal_new(common_attrs.merge({use_existing:false, priority:2}))
+    assert_not_equal c_low_priority.uuid, c_high_priority.uuid
     assert_equal Container::Queued, c_low_priority.state
     assert_equal Container::Queued, c_high_priority.state
     reused = Container.find_reusable(common_attrs)
@@ -121,8 +122,9 @@ class ContainerTest < ActiveSupport::TestCase
       output: '1f4b0bc7583c2a7f9102c395f4ffc5e3+45'
     }
 
-    c_older, _ = minimal_new(common_attrs)
-    c_recent, _ = minimal_new(common_attrs)
+    c_older, _ = minimal_new(common_attrs.merge({use_existing: false}))
+    c_recent, _ = minimal_new(common_attrs.merge({use_existing: false}))
+    assert_not_equal c_older.uuid, c_recent.uuid
 
     set_user_from_auth :dispatch1
     c_older.update_attributes!({state: Container::Locked})
@@ -151,6 +153,7 @@ class ContainerTest < ActiveSupport::TestCase
 
     c_output1 = Container.create common_attrs
     c_output2 = Container.create common_attrs
+    assert_not_equal c_output1.uuid, c_output2.uuid
 
     cr = ContainerRequest.new common_attrs
     cr.state = ContainerRequest::Committed
@@ -177,9 +180,11 @@ class ContainerTest < ActiveSupport::TestCase
   test "find_reusable method should select running container by start date" do
     set_user_from_auth :active
     common_attrs = REUSABLE_COMMON_ATTRS.merge({environment: {"var" => "running"}})
-    c_slower, _ = minimal_new(common_attrs)
-    c_faster_started_first, _ = minimal_new(common_attrs)
-    c_faster_started_second, _ = minimal_new(common_attrs)
+    c_slower, _ = minimal_new(common_attrs.merge({use_existing: false}))
+    c_faster_started_first, _ = minimal_new(common_attrs.merge({use_existing: false}))
+    c_faster_started_second, _ = minimal_new(common_attrs.merge({use_existing: false}))
+    # Confirm the 3 container UUIDs are different.
+    assert_equal 3, [c_slower.uuid, c_faster_started_first.uuid, c_faster_started_second.uuid].uniq.length
     set_user_from_auth :dispatch1
     c_slower.update_attributes!({state: Container::Locked})
     c_slower.update_attributes!({state: Container::Running,
@@ -199,9 +204,11 @@ class ContainerTest < ActiveSupport::TestCase
   test "find_reusable method should select running container by progress" do
     set_user_from_auth :active
     common_attrs = REUSABLE_COMMON_ATTRS.merge({environment: {"var" => "running2"}})
-    c_slower, _ = minimal_new(common_attrs)
-    c_faster_started_first, _ = minimal_new(common_attrs)
-    c_faster_started_second, _ = minimal_new(common_attrs)
+    c_slower, _ = minimal_new(common_attrs.merge({use_existing: false}))
+    c_faster_started_first, _ = minimal_new(common_attrs.merge({use_existing: false}))
+    c_faster_started_second, _ = minimal_new(common_attrs.merge({use_existing: false}))
+    # Confirm the 3 container UUIDs are different.
+    assert_equal 3, [c_slower.uuid, c_faster_started_first.uuid, c_faster_started_second.uuid].uniq.length
     set_user_from_auth :dispatch1
     c_slower.update_attributes!({state: Container::Locked})
     c_slower.update_attributes!({state: Container::Running,
@@ -221,9 +228,11 @@ class ContainerTest < ActiveSupport::TestCase
   test "find_reusable method should select locked container most likely to start sooner" do
     set_user_from_auth :active
     common_attrs = REUSABLE_COMMON_ATTRS.merge({environment: {"var" => "locked"}})
-    c_low_priority, _ = minimal_new(common_attrs)
-    c_high_priority_older, _ = minimal_new(common_attrs)
-    c_high_priority_newer, _ = minimal_new(common_attrs)
+    c_low_priority, _ = minimal_new(common_attrs.merge({use_existing: false}))
+    c_high_priority_older, _ = minimal_new(common_attrs.merge({use_existing: false}))
+    c_high_priority_newer, _ = minimal_new(common_attrs.merge({use_existing: false}))
+    # Confirm the 3 container UUIDs are different.
+    assert_equal 3, [c_low_priority.uuid, c_high_priority_older.uuid, c_high_priority_newer.uuid].uniq.length
     set_user_from_auth :dispatch1
     c_low_priority.update_attributes!({state: Container::Locked,
                                        priority: 1})
@@ -239,8 +248,9 @@ class ContainerTest < ActiveSupport::TestCase
   test "find_reusable method should select running over failed container" do
     set_user_from_auth :active
     common_attrs = REUSABLE_COMMON_ATTRS.merge({environment: {"var" => "failed_vs_running"}})
-    c_failed, _ = minimal_new(common_attrs)
-    c_running, _ = minimal_new(common_attrs)
+    c_failed, _ = minimal_new(common_attrs.merge({use_existing: false}))
+    c_running, _ = minimal_new(common_attrs.merge({use_existing: false}))
+    assert_not_equal c_failed.uuid, c_running.uuid
     set_user_from_auth :dispatch1
     c_failed.update_attributes!({state: Container::Locked})
     c_failed.update_attributes!({state: Container::Running})
@@ -259,8 +269,9 @@ class ContainerTest < ActiveSupport::TestCase
   test "find_reusable method should select complete over running container" do
     set_user_from_auth :active
     common_attrs = REUSABLE_COMMON_ATTRS.merge({environment: {"var" => "completed_vs_running"}})
-    c_completed, _ = minimal_new(common_attrs)
-    c_running, _ = minimal_new(common_attrs)
+    c_completed, _ = minimal_new(common_attrs.merge({use_existing: false}))
+    c_running, _ = minimal_new(common_attrs.merge({use_existing: false}))
+    assert_not_equal c_completed.uuid, c_running.uuid
     set_user_from_auth :dispatch1
     c_completed.update_attributes!({state: Container::Locked})
     c_completed.update_attributes!({state: Container::Running})
@@ -279,8 +290,9 @@ class ContainerTest < ActiveSupport::TestCase
   test "find_reusable method should select running over locked container" do
     set_user_from_auth :active
     common_attrs = REUSABLE_COMMON_ATTRS.merge({environment: {"var" => "running_vs_locked"}})
-    c_locked, _ = minimal_new(common_attrs)
-    c_running, _ = minimal_new(common_attrs)
+    c_locked, _ = minimal_new(common_attrs.merge({use_existing: false}))
+    c_running, _ = minimal_new(common_attrs.merge({use_existing: false}))
+    assert_not_equal c_running.uuid, c_locked.uuid
     set_user_from_auth :dispatch1
     c_locked.update_attributes!({state: Container::Locked})
     c_running.update_attributes!({state: Container::Locked})
@@ -294,8 +306,9 @@ class ContainerTest < ActiveSupport::TestCase
   test "find_reusable method should select locked over queued container" do
     set_user_from_auth :active
     common_attrs = REUSABLE_COMMON_ATTRS.merge({environment: {"var" => "running_vs_locked"}})
-    c_locked, _ = minimal_new(common_attrs)
-    c_queued, _ = minimal_new(common_attrs)
+    c_locked, _ = minimal_new(common_attrs.merge({use_existing: false}))
+    c_queued, _ = minimal_new(common_attrs.merge({use_existing: false}))
+    assert_not_equal c_queued.uuid, c_locked.uuid
     set_user_from_auth :dispatch1
     c_locked.update_attributes!({state: Container::Locked})
     reused = Container.find_reusable(common_attrs)
index d5ca3f965060018e5c0b56a9af5e5a50cf2ba15e..ba813602b32f4804de6a8d8f62459ab3f57b47bc 100644 (file)
@@ -73,6 +73,25 @@ class CreateSuperUserTokenTest < ActiveSupport::TestCase
       create_superuser_token active_user_token
     end
     assert_not_nil e
-    assert_equal "Token already exists but is not a superuser token.", e.message
+    assert_equal "Token exists but is not a superuser token.", e.message
+  end
+
+  test "specified token has limited scope" do
+    active_user_token = api_client_authorizations("data_manager").api_token
+    e = assert_raises RuntimeError do
+      create_superuser_token active_user_token
+    end
+    assert_not_nil e
+    assert_match /^Token exists but has limited scope/, e.message
+  end
+
+  test "existing token has limited scope" do
+    active_user_token = api_client_authorizations("admin_vm").api_token
+    ApiClientAuthorization.
+      where(user_id: system_user.id).
+      update_all(scopes: ["GET /"])
+    fixture_tokens = ApiClientAuthorization.all.collect(&:api_token)
+    new_token = create_superuser_token
+    refute_includes(fixture_tokens, new_token)
   end
 end
index c390b3213e94120eea370fdaaeec25500c7f5f3f..1f5847aea6e22b5bea9fe2e51d7f9354861145dd 100644 (file)
@@ -60,13 +60,13 @@ class FailJobsTest < ActiveSupport::TestCase
 
   test 'command line help' do
     cmd = Rails.root.join('script/fail-jobs.rb').to_s
-    assert_match /Options:.*--before=/m, File.popen([cmd, '--help']).read
+    assert_match(/Options:.*--before=/m, File.popen([cmd, '--help']).read)
   end
 
   protected
 
   def assert_end_states
-    @job.values.map &:reload
+    @job.values.map(&:reload)
     assert_equal 'Failed', @job[:before_reboot].state
     assert_equal false, @job[:before_reboot].running
     assert_equal false, @job[:before_reboot].success
index 3da2c836ed61579fe1d5058e4c367db2c9dd3eab..761953e8eb4e441f3e30cacf86a1f45ddf816e6c 100644 (file)
@@ -185,7 +185,7 @@ class JobTest < ActiveSupport::TestCase
       # Ensure valid_attrs doesn't produce errors -- otherwise we will
       # not know whether errors reported below are actually caused by
       # invalid_attrs.
-      dummy = Job.create! job_attrs
+      Job.create! job_attrs
 
       job = Job.create job_attrs(invalid_attrs)
       assert_raises(ActiveRecord::RecordInvalid, ArgumentError,
@@ -223,7 +223,7 @@ class JobTest < ActiveSupport::TestCase
 
       parameters.each do |parameter|
         expectations = parameter[2]
-        if parameter[1] == 'use_current_user_uuid'
+        if 'use_current_user_uuid' == parameter[1]
           parameter[1] = Thread.current[:user].uuid
         end
 
@@ -307,6 +307,24 @@ class JobTest < ActiveSupport::TestCase
     assert_equal "Failed", job.state
   end
 
+  test "admin user can cancel a running job despite lock" do
+    set_user_from_auth :active_trustedclient
+    job = Job.create! job_attrs
+    job.lock current_user.uuid
+    assert_equal Job::Running, job.state
+
+    set_user_from_auth :spectator
+    assert_raises do
+      job.update_attributes!(state: Job::Cancelled)
+    end
+
+    set_user_from_auth :admin
+    job.reload
+    assert_equal Job::Running, job.state
+    job.update_attributes!(state: Job::Cancelled)
+    assert_equal Job::Cancelled, job.state
+  end
+
   test "verify job queue position" do
     job1 = Job.create! job_attrs
     assert_equal 'Queued', job1.state, "Incorrect job state for newly created job1"
@@ -411,7 +429,7 @@ class JobTest < ActiveSupport::TestCase
     }
     assert_raises(ActiveRecord::RecordInvalid,
                   "created job with a collection uuid in script_parameters") do
-      job = Job.create!(job_attrs(bad_params))
+      Job.create!(job_attrs(bad_params))
     end
   end
 
index efbb189c9f8f0e50f1db262a8cd3bac7342a0b03..92976e0053580eafa45707febfe753fd08f43c9b 100644 (file)
@@ -263,7 +263,7 @@ class LogTest < ActiveSupport::TestCase
     # appear too, but only if they are _not_ listed in known_logs
     # (i.e., we do not make any assertions about logs not mentioned in
     # either "known" or "expected".)
-    result_ids = result.collect &:id
+    result_ids = result.collect(&:id)
     expected_logs.each do |want|
       assert_includes result_ids, logs(want).id
     end
index 6eb1df56d129f0279c2e86323b865d13fd09817c..df8c22baf4ad04a6a20b97c0f9c551a335a9fb96 100644 (file)
@@ -33,7 +33,7 @@ class NodeTest < ActiveSupport::TestCase
     conffile = Rails.root.join 'tmp', 'compute65535.conf'
     File.unlink conffile rescue nil
     assert Node.dns_server_update 'compute65535', '127.0.0.1'
-    assert_match /\"1\.0\.0\.127\.in-addr\.arpa\. IN PTR compute65535\.zzzzz\.arvadosapi\.com\"/, IO.read(conffile)
+    assert_match(/\"1\.0\.0\.127\.in-addr\.arpa\. IN PTR compute65535\.zzzzz\.arvadosapi\.com\"/, IO.read(conffile))
     File.unlink conffile
   end
 
index c7f9776ac6a98c36f8ab8a3e002773c03603ea6a..6fcc3165289acbccc2884b52887146fae71317b5 100644 (file)
@@ -27,7 +27,7 @@ class OwnerTest < ActiveSupport::TestCase
     test "create object with non-existent #{o_class} owner" do
       assert_raises(ActiveRecord::RecordInvalid,
                     "create should fail with random owner_uuid") do
-        i = Specimen.create!(owner_uuid: o_class.generate_uuid)
+        Specimen.create!(owner_uuid: o_class.generate_uuid)
       end
 
       i = Specimen.create(owner_uuid: o_class.generate_uuid)
@@ -89,7 +89,6 @@ class OwnerTest < ActiveSupport::TestCase
       o = eval ofixt
       assert_equal(true, Specimen.where(owner_uuid: o.uuid).any?,
                    "need something to be owned by #{o.uuid} for this test")
-      old_uuid = o.uuid
       new_uuid = o.uuid.sub(/..........$/, rand(2**256).to_s(36)[0..9])
       assert(!o.update_attributes(uuid: new_uuid),
              "should not change uuid of #{ofixt} that owns objects")
index 79fc1f29c7bf46a2f1efb3ae8f9dd298f0222015..df110549989c124f1f92f2aa46fd2a3baca5c459 100644 (file)
@@ -125,10 +125,10 @@ class PermissionTest < ActiveSupport::TestCase
     sp_grp = Group.create!
     sp = Specimen.create!(owner_uuid: sp_grp.uuid)
 
-    manage_perm = Link.create!(link_class: 'permission',
-                               name: 'can_manage',
-                               tail_uuid: owner_grp.uuid,
-                               head_uuid: sp_grp.uuid)
+    Link.create!(link_class: 'permission',
+                 name: 'can_manage',
+                 tail_uuid: owner_grp.uuid,
+                 head_uuid: sp_grp.uuid)
 
     # active user owns owner_grp, which has can_manage permission on sp_grp
     # user should be able to add permissions on sp.
@@ -137,14 +137,12 @@ class PermissionTest < ActiveSupport::TestCase
                             head_uuid: sp.uuid,
                             link_class: 'permission',
                             name: 'can_write')
-    test_uuid = test_perm.uuid
     assert test_perm.save, "could not save new permission on target object"
     assert test_perm.destroy, "could not delete new permission on target object"
   end
 
-  # TODO(twp): fix bug #3091, which should fix this test.
-  test "can_manage permission on a non-group object" do
-    skip
+  # bug #3091
+  skip "can_manage permission on a non-group object" do
     set_user_from_auth :admin
 
     ob = Specimen.create!
index 93354f8b1edd31d2b332a71cd6d7de28bcc490e6..05ba135700463312efed027d785b752393612435 100644 (file)
@@ -38,6 +38,8 @@ class PipelineInstanceTest < ActiveSupport::TestCase
     pi = PipelineInstance.find_by_uuid 'zzzzz-d1hrv-f4gneyn6br1xize'
     assert_equal PipelineInstance::New, pi.state, 'expected state to be New after adding component with input'
     assert_equal pi.components.size, 1, 'expected one component'
+    assert_nil pi.started_at, 'expected started_at to be nil on new pipeline instance'
+    assert_nil pi.finished_at, 'expected finished_at to be nil on new pipeline instance'
 
     # add a component with no input not required
     component = {'script_parameters' => {"input_not_provided" => {"required" => false}}}
@@ -61,6 +63,8 @@ class PipelineInstanceTest < ActiveSupport::TestCase
     pi.save
     pi = PipelineInstance.find_by_uuid 'zzzzz-d1hrv-f4gneyn6br1xize'
     assert_equal PipelineInstance::RunningOnServer, pi.state, 'expected state to be RunningOnServer after updating state to RunningOnServer'
+    assert_not_nil pi.started_at, 'expected started_at to have a value on a running pipeline instance'
+    assert_nil pi.finished_at, 'expected finished_at to be nil on a running pipeline instance'
 
     pi.state = PipelineInstance::Paused
     pi.save
@@ -71,6 +75,8 @@ class PipelineInstanceTest < ActiveSupport::TestCase
     pi.save
     pi = PipelineInstance.find_by_uuid 'zzzzz-d1hrv-f4gneyn6br1xize'
     assert_equal PipelineInstance::Complete, pi.state, 'expected state to be Complete after updating state to Complete'
+    assert_not_nil pi.started_at, 'expected started_at to have a value on a completed pipeline instance'
+    assert_not_nil pi.finished_at, 'expected finished_at to have a value on a completed pipeline instance'
 
     pi.state = 'bogus'
     pi.save
@@ -81,6 +87,8 @@ class PipelineInstanceTest < ActiveSupport::TestCase
     pi.save
     pi = PipelineInstance.find_by_uuid 'zzzzz-d1hrv-f4gneyn6br1xize'
     assert_equal PipelineInstance::Failed, pi.state, 'expected state to be Failed after updating state to Failed'
+    assert_not_nil pi.started_at, 'expected started_at to have a value on a failed pipeline instance'
+    assert_not_nil pi.finished_at, 'expected finished_at to have a value on a failed pipeline instance'
   end
 
   test "update attributes for pipeline with two components" do
@@ -91,7 +99,6 @@ class PipelineInstanceTest < ActiveSupport::TestCase
     component2 = {'script_parameters' => {"something_else" => "xxxad4b39ca5a924e481008009d94e32+210", "input_missing" => {"required" => true}}}
     pi.components['first'] = component1
     pi.components['second'] = component2
-    components = pi.components
 
     Thread.current[:user] = users(:admin)
     pi.update_attribute 'components', pi.components
index a269078b736b92fcebf8a3bb1fbca4c52516c166..1381c8f538ff3bbf9de5596c2dce784123318f1c 100644 (file)
@@ -60,7 +60,7 @@ class SalvageCollectionTest < ActiveSupport::TestCase
     updated_name = updated_src_collection.name
     assert_equal true, updated_name.include?(src_collection.name)
 
-    match = updated_name.match /^test collection.*salvaged data at (.*)\)$/
+    match = updated_name.match(/^test collection.*salvaged data at (.*)\)$/)
     assert_not_nil match
     assert_not_nil match[1]
     assert_empty updated_src_collection.manifest_text
@@ -68,7 +68,7 @@ class SalvageCollectionTest < ActiveSupport::TestCase
     # match[1] is the uuid of the new collection created from src_collection's salvaged data
     # use this to get the new collection and verify
     new_collection = Collection.find_by_uuid match[1]
-    match = new_collection.name.match /^salvaged from (.*),.*/
+    match = new_collection.name.match(/^salvaged from (.*),.*/)
     assert_not_nil match
     assert_equal src_collection.uuid, match[1]
 
@@ -80,7 +80,7 @@ class SalvageCollectionTest < ActiveSupport::TestCase
   end
 
   test "salvage collection with no uuid required argument" do
-    e = assert_raises RuntimeError do
+    assert_raises RuntimeError do
       salvage_collection nil
     end
   end
@@ -107,7 +107,7 @@ class SalvageCollectionTest < ActiveSupport::TestCase
     e = assert_raises RuntimeError do
       salvage_collection collections('user_agreement').uuid
     end
-    assert_match /Error during arv-put: pid \d+ exit \d+ \(cmd was \"arv-put .*\"\)/, e.message
+    assert_match(/Error during arv-put: pid \d+ exit \d+ \(cmd was \"arv-put .*\"\)/, e.message)
   end
 
   # This test uses BAD_MANIFEST, which has the following flaws:
@@ -146,7 +146,7 @@ class SalvageCollectionTest < ActiveSupport::TestCase
     updated_name = updated_src_collection.name
     assert_equal true, updated_name.include?(src_collection.name)
 
-    match = updated_name.match /^test collection.*salvaged data at (.*)\)$/
+    match = updated_name.match(/^test collection.*salvaged data at (.*)\)$/)
     assert_not_nil match
     assert_not_nil match[1]
     assert_empty updated_src_collection.manifest_text
@@ -154,7 +154,7 @@ class SalvageCollectionTest < ActiveSupport::TestCase
     # match[1] is the uuid of the new collection created from src_collection's salvaged data
     # use this to get the new collection and verify
     new_collection = Collection.find_by_uuid match[1]
-    match = new_collection.name.match /^salvaged from (.*),.*/
+    match = new_collection.name.match(/^salvaged from (.*),.*/)
     assert_not_nil match
     assert_equal src_collection.uuid, match[1]
     # verify the new collection's manifest includes the bad locators
index 4df6cc0b369a67cd99907cb128979a4cf1c21956..3bd6ed4003f865a43911cbda8d81e07c62e2e88e 100644 (file)
@@ -136,7 +136,6 @@ class UserTest < ActiveSupport::TestCase
   test "admin can't clear username when user owns repositories" do
     set_user_from_auth :admin
     user = users(:active)
-    start_username = user.username
     user.username = nil
     assert_not_allowed { user.save }
     refute_empty(user.errors[:username])
@@ -277,10 +276,12 @@ class UserTest < ActiveSupport::TestCase
     assert @uninvited_user.can? :write=>"#{@uninvited_user.uuid}"
     assert @uninvited_user.can? :manage=>"#{@uninvited_user.uuid}"
 
-    assert @uninvited_user.groups_i_can(:read).size == 1, "inactive and uninvited user can only read anonymous user group"
-    assert @uninvited_user.groups_i_can(:read).first.ends_with? 'anonymouspublic' , "inactive and uninvited user can only read anonymous user group"
-    assert @uninvited_user.groups_i_can(:write).size == 0, "inactive and uninvited user should not be able write to any groups"
-    assert @uninvited_user.groups_i_can(:manage).size == 0, "inactive and uninvited user should not be able manage any groups"
+    assert_equal(@uninvited_user.groups_i_can(:read).sort,
+                 [@uninvited_user.uuid, groups(:anonymous_group).uuid].sort)
+    assert_equal(@uninvited_user.groups_i_can(:write),
+                 [@uninvited_user.uuid])
+    assert_equal(@uninvited_user.groups_i_can(:manage),
+                 [@uninvited_user.uuid])
   end
 
   test "find user method checks" do
diff --git a/services/api/test/websocket_runner.rb b/services/api/test/websocket_runner.rb
deleted file mode 100644 (file)
index be32a0f..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-require 'bundler'
-require 'socket'
-
-$ARV_API_SERVER_DIR = File.expand_path('../..', __FILE__)
-
-s = TCPServer.new('0.0.0.0', 0)
-WEBSOCKET_PORT = s.addr[1]
-s.close
-SERVER_PID_PATH = "tmp/pids/passenger.#{WEBSOCKET_PORT}.pid"
-
-class WebsocketTestRunner < MiniTest::Unit
-  def _system(*cmd)
-    Bundler.with_clean_env do
-      if not system({'ARVADOS_WEBSOCKETS' => 'ws-only', 'RAILS_ENV' => 'test'}, *cmd)
-        raise RuntimeError, "Command failed with exit status #{$?}: #{cmd.inspect}"
-      end
-    end
-  end
-
-  def _run(args=[])
-    server_pid = Dir.chdir($ARV_API_SERVER_DIR) do |apidir|
-      # Only passenger seems to be able to run the websockets server successfully.
-      _system('passenger', 'start', '-d', "-p#{WEBSOCKET_PORT}")
-      timeout = Time.now.tv_sec + 10
-      begin
-        sleep 0.2
-        begin
-          server_pid = IO.read(SERVER_PID_PATH).to_i
-          good_pid = (server_pid > 0) and (Process.kill(0, pid) rescue false)
-        rescue Errno::ENOENT
-          good_pid = false
-        end
-      end while (not good_pid) and (Time.now.tv_sec < timeout)
-      if not good_pid
-        raise RuntimeError, "could not find API server Rails pid"
-      end
-      server_pid
-    end
-    begin
-      super(args)
-    ensure
-      Dir.chdir($ARV_API_SERVER_DIR) do
-        _system('passenger', 'stop', "-p#{WEBSOCKET_PORT}")
-      end
-      # DatabaseCleaner leaves the database empty. Prefer to leave it full.
-      dc = DatabaseController.new
-      dc.define_singleton_method :render do |*args| end
-      dc.reset
-    end
-  end
-end
-
-MiniTest::Unit.runner = WebsocketTestRunner.new
index 3bd7b3a8aa93c5871ff18773188c1336033d2599..75645ff47215c2b5f7f666057006ea2510f99811 100644 (file)
@@ -67,7 +67,7 @@ func main() {
        if err := srv.Start(); err != nil {
                log.Fatal(err)
        }
-       if _, err := daemon.SdNotify("READY=1"); err != nil {
+       if _, err := daemon.SdNotify(false, "READY=1"); err != nil {
                log.Printf("Error notifying init daemon: %v", err)
        }
        log.Println("Listening at", srv.Addr)
index 5a95e27b93b26789d10d93f4dadeac849fcfaa9b..f440aa608773de22eb221049993d91b63115edf4 100755 (executable)
@@ -72,7 +72,7 @@ class ArvWeb(object):
                         et = 'add'
                     else:
                         et = 'remove'
-                if ev['properties']['new_attributes']['expires_at'] is not None:
+                if ev['properties']['new_attributes']['trash_at'] is not None:
                     et = 'remove'
 
             self.evqueue.put((self.project, et, ev['object_uuid']))
index 0c1ce49592a6b08223271d440dca41f3a5d8fd46..e768b509cd6f2c69bb529d9e9a90e2d923e422ce 100644 (file)
@@ -105,7 +105,7 @@ func doMain() error {
                PollInterval:   time.Duration(theConfig.PollPeriod),
                DoneProcessing: make(chan struct{})}
 
-       if _, err := daemon.SdNotify("READY=1"); err != nil {
+       if _, err := daemon.SdNotify(false, "READY=1"); err != nil {
                log.Printf("Error notifying init daemon: %v", err)
        }
 
@@ -127,8 +127,8 @@ func sbatchFunc(container arvados.Container) *exec.Cmd {
        sbatchArgs = append(sbatchArgs, fmt.Sprintf("--job-name=%s", container.UUID))
        sbatchArgs = append(sbatchArgs, fmt.Sprintf("--mem-per-cpu=%d", int(memPerCPU)))
        sbatchArgs = append(sbatchArgs, fmt.Sprintf("--cpus-per-task=%d", container.RuntimeConstraints.VCPUs))
-       if container.RuntimeConstraints.Partition != nil {
-               sbatchArgs = append(sbatchArgs, fmt.Sprintf("--partition=%s", strings.Join(container.RuntimeConstraints.Partition, ",")))
+       if container.SchedulingParameters.Partitions != nil {
+               sbatchArgs = append(sbatchArgs, fmt.Sprintf("--partition=%s", strings.Join(container.SchedulingParameters.Partitions, ",")))
        }
 
        return exec.Command("sbatch", sbatchArgs...)
@@ -195,6 +195,7 @@ func submit(dispatcher *dispatch.Dispatcher,
                b, _ := ioutil.ReadAll(stdoutReader)
                stdoutReader.Close()
                stdoutChan <- b
+               close(stdoutChan)
        }()
 
        stderrChan := make(chan []byte)
@@ -202,6 +203,7 @@ func submit(dispatcher *dispatch.Dispatcher,
                b, _ := ioutil.ReadAll(stderrReader)
                stderrReader.Close()
                stderrChan <- b
+               close(stderrChan)
        }()
 
        // Send a tiny script on stdin to execute the crunch-run command
@@ -209,13 +211,10 @@ func submit(dispatcher *dispatch.Dispatcher,
        io.WriteString(stdinWriter, execScript(append(crunchRunCommand, container.UUID)))
        stdinWriter.Close()
 
-       err = cmd.Wait()
-
        stdoutMsg := <-stdoutChan
        stderrmsg := <-stderrChan
 
-       close(stdoutChan)
-       close(stderrChan)
+       err = cmd.Wait()
 
        if err != nil {
                submitErr = fmt.Errorf("Container submission failed: %v: %v (stderr: %q)", cmd.Args, err, stderrmsg)
@@ -302,12 +301,13 @@ func run(dispatcher *dispatch.Dispatcher,
 
                                // Mutex between squeue sync and running sbatch or scancel.
                                squeueUpdater.SlurmLock.Lock()
-                               err := scancelCmd(container).Run()
+                               cmd := scancelCmd(container)
+                               msg, err := cmd.CombinedOutput()
                                squeueUpdater.SlurmLock.Unlock()
 
                                if err != nil {
-                                       log.Printf("Error stopping container %s with scancel: %v",
-                                               container.UUID, err)
+                                       log.Printf("Error stopping container %s with %v %v: %v %v",
+                                               container.UUID, cmd.Path, cmd.Args, err, string(msg))
                                        if squeueUpdater.CheckSqueue(container.UUID) {
                                                log.Printf("Container %s is still in squeue after scancel.",
                                                        container.UUID)
index c9208a6943924a1604c7b15735536229cde68104..40461031e214486f1dbed9feeda6aae0d97fb76c 100644 (file)
@@ -81,7 +81,8 @@ func (s *TestSuite) TestIntegrationCancel(c *C) {
                return exec.Command("echo")
        }
 
-       container := s.integrationTest(c, func() *exec.Cmd { return exec.Command("echo", "zzzzz-dz642-queuedcontainer") },
+       container := s.integrationTest(c,
+               func() *exec.Cmd { return exec.Command("echo", "zzzzz-dz642-queuedcontainer") },
                []string(nil),
                func(dispatcher *dispatch.Dispatcher, container arvados.Container) {
                        dispatcher.UpdateState(container.UUID, dispatch.Running)
@@ -134,7 +135,7 @@ func (s *TestSuite) integrationTest(c *C,
        }(squeueCmd)
        squeueCmd = newSqueueCmd
 
-       // There should be no queued containers now
+       // There should be one queued container
        params := arvadosclient.Dict{
                "filters": [][]string{{"state", "=", "Queued"}},
        }
@@ -318,7 +319,7 @@ func testSbatchFuncWithArgs(c *C, args []string) {
 
 func (s *MockArvadosServerSuite) TestSbatchPartition(c *C) {
        theConfig.SbatchArguments = nil
-       container := arvados.Container{UUID: "123", RuntimeConstraints: arvados.RuntimeConstraints{RAM: 250000000, VCPUs: 1, Partition: []string{"blurb", "b2"}}}
+       container := arvados.Container{UUID: "123", RuntimeConstraints: arvados.RuntimeConstraints{RAM: 250000000, VCPUs: 1}, SchedulingParameters: arvados.SchedulingParameters{Partitions: []string{"blurb", "b2"}}}
        sbatchCmd := sbatchFunc(container)
 
        var expected []string
index 61decde61c4bd61d0a92e96bde20ff0c82780f57..45d06c8c1e27f12f2bc6e83ca262ab2ff7f08a53 100644 (file)
@@ -2,6 +2,8 @@ package main
 
 import (
        "bufio"
+       "io"
+       "io/ioutil"
        "log"
        "os/exec"
        "sync"
@@ -45,31 +47,49 @@ func (squeue *Squeue) RunSqueue() {
                log.Printf("Error creating stdout pipe for squeue: %v", err)
                return
        }
+
+       stderrReader, err := cmd.StderrPipe()
+       if err != nil {
+               log.Printf("Error creating stderr pipe for squeue: %v", err)
+               return
+       }
+
        err = cmd.Start()
        if err != nil {
                log.Printf("Error running squeue: %v", err)
                return
        }
+
+       stderrChan := make(chan []byte)
+       go func() {
+               b, _ := ioutil.ReadAll(stderrReader)
+               stderrChan <- b
+               close(stderrChan)
+       }()
+
        scanner := bufio.NewScanner(sq)
        for scanner.Scan() {
                newSqueueContents = append(newSqueueContents, scanner.Text())
        }
-       if err := scanner.Err(); err != nil {
-               cmd.Wait()
-               log.Printf("Error reading from squeue pipe: %v", err)
-               return
-       }
+       io.Copy(ioutil.Discard, sq)
+
+       stderrmsg := <-stderrChan
 
        err = cmd.Wait()
+
+       if scanner.Err() != nil {
+               log.Printf("Error reading from squeue pipe: %v", err)
+       }
        if err != nil {
-               log.Printf("Error running squeue: %v", err)
-               return
+               log.Printf("Error running %v %v: %v %q", cmd.Path, cmd.Args, err, string(stderrmsg))
        }
 
-       squeue.squeueCond.L.Lock()
-       squeue.squeueContents = newSqueueContents
-       squeue.squeueCond.Broadcast()
-       squeue.squeueCond.L.Unlock()
+       if scanner.Err() == nil && err == nil {
+               squeue.squeueCond.L.Lock()
+               squeue.squeueContents = newSqueueContents
+               squeue.squeueCond.Broadcast()
+               squeue.squeueCond.L.Unlock()
+       }
 }
 
 // CheckSqueue checks if a given container UUID is in the slurm queue.  This
index ade40c6b03a4d4a98812172aab31da5173453c4e..971cb3a27a246c9fbe1a325496a6da63e6e69ac4 100644 (file)
@@ -257,6 +257,7 @@ func (runner *ContainerRunner) SetupMounts() (err error) {
 
        collectionPaths := []string{}
        runner.Binds = nil
+       needCertMount := true
 
        for bind, mnt := range runner.Container.Mounts {
                if bind == "stdout" {
@@ -274,6 +275,9 @@ func (runner *ContainerRunner) SetupMounts() (err error) {
                                return fmt.Errorf("Stdout path does not start with OutputPath: %s, %s", mnt.Path, prefix)
                        }
                }
+               if bind == "/etc/arvados/ca-certificates.crt" {
+                       needCertMount = false
+               }
 
                switch {
                case mnt.Kind == "collection":
@@ -355,6 +359,16 @@ func (runner *ContainerRunner) SetupMounts() (err error) {
                return fmt.Errorf("Output path does not correspond to a writable mount point")
        }
 
+       if wantAPI := runner.Container.RuntimeConstraints.API; needCertMount && wantAPI != nil && *wantAPI {
+               for _, certfile := range arvadosclient.CertFiles {
+                       _, err := os.Stat(certfile)
+                       if err == nil {
+                               runner.Binds = append(runner.Binds, fmt.Sprintf("%s:/etc/arvados/ca-certificates.crt:ro", certfile))
+                               break
+                       }
+               }
+       }
+
        if pdhOnly {
                arvMountCmd = append(arvMountCmd, "--mount-by-pdh", "by_id")
        } else {
@@ -623,7 +637,7 @@ func (runner *ContainerRunner) CaptureOutput() error {
        err = runner.ArvClient.Create("collections",
                arvadosclient.Dict{
                        "collection": arvadosclient.Dict{
-                               "expires_at":    time.Now().Add(runner.trashLifetime).Format(time.RFC3339),
+                               "trash_at":      time.Now().Add(runner.trashLifetime).Format(time.RFC3339),
                                "name":          "output for " + runner.Container.UUID,
                                "manifest_text": manifestText}},
                &response)
@@ -694,7 +708,7 @@ func (runner *ContainerRunner) CommitLogs() error {
        err = runner.ArvClient.Create("collections",
                arvadosclient.Dict{
                        "collection": arvadosclient.Dict{
-                               "expires_at":    time.Now().Add(runner.trashLifetime).Format(time.RFC3339),
+                               "trash_at":      time.Now().Add(runner.trashLifetime).Format(time.RFC3339),
                                "name":          "logs for " + runner.Container.UUID,
                                "manifest_text": mt}},
                &response)
@@ -737,10 +751,10 @@ func (runner *ContainerRunner) ContainerToken() (string, error) {
 func (runner *ContainerRunner) UpdateContainerFinal() error {
        update := arvadosclient.Dict{}
        update["state"] = runner.finalState
+       if runner.LogsPDH != nil {
+               update["log"] = *runner.LogsPDH
+       }
        if runner.finalState == "Complete" {
-               if runner.LogsPDH != nil {
-                       update["log"] = *runner.LogsPDH
-               }
                if runner.ExitCode != nil {
                        update["exit_code"] = *runner.ExitCode
                }
@@ -800,6 +814,7 @@ func (runner *ContainerRunner) Run() (err error) {
                checkErr(err)
 
                if runner.finalState == "Queued" {
+                       runner.CrunchLog.Close()
                        runner.UpdateContainerFinal()
                        return
                }
@@ -832,6 +847,7 @@ func (runner *ContainerRunner) Run() (err error) {
        // check for and/or load image
        err = runner.LoadImage()
        if err != nil {
+               runner.finalState = "Cancelled"
                err = fmt.Errorf("While loading container image: %v", err)
                return
        }
@@ -839,6 +855,7 @@ func (runner *ContainerRunner) Run() (err error) {
        // set up FUSE mount and binds
        err = runner.SetupMounts()
        if err != nil {
+               runner.finalState = "Cancelled"
                err = fmt.Errorf("While setting up mounts: %v", err)
                return
        }
@@ -895,10 +912,15 @@ func main() {
        cgroupRoot := flag.String("cgroup-root", "/sys/fs/cgroup", "path to sysfs cgroup tree")
        cgroupParent := flag.String("cgroup-parent", "docker", "name of container's parent cgroup (ignored if -cgroup-parent-subsystem is used)")
        cgroupParentSubsystem := flag.String("cgroup-parent-subsystem", "", "use current cgroup for given subsystem as parent cgroup for container")
+       caCertsPath := flag.String("ca-certs", "", "Path to TLS root certificates")
        flag.Parse()
 
        containerId := flag.Arg(0)
 
+       if *caCertsPath != "" {
+               arvadosclient.CertFiles = []string{*caCertsPath}
+       }
+
        api, err := arvadosclient.MakeArvadosClient()
        if err != nil {
                log.Fatalf("%s: %v", containerId, err)
index 2c7145998ab402786e1f9bf47bfa5575afb1c086..b9856aca2964755a7fab0f9baf527084adff8532 100644 (file)
@@ -759,6 +759,14 @@ func (am *ArvMountCmdLine) ArvMountTest(c []string, token string) (*exec.Cmd, er
        return nil, nil
 }
 
+func stubCert(temp string) string {
+       path := temp + "/ca-certificates.crt"
+       crt, _ := os.Create(path)
+       crt.Close()
+       arvadosclient.CertFiles = []string{path}
+       return path
+}
+
 func (s *TestSuite) TestSetupMounts(c *C) {
        api := &ArvTestClient{}
        kc := &KeepTestClient{}
@@ -766,9 +774,14 @@ func (s *TestSuite) TestSetupMounts(c *C) {
        am := &ArvMountCmdLine{}
        cr.RunArvMount = am.ArvMountTest
 
-       realTemp, err := ioutil.TempDir("", "crunchrun_test-")
+       realTemp, err := ioutil.TempDir("", "crunchrun_test1-")
+       c.Assert(err, IsNil)
+       certTemp, err := ioutil.TempDir("", "crunchrun_test2-")
        c.Assert(err, IsNil)
+       stubCertPath := stubCert(certTemp)
+
        defer os.RemoveAll(realTemp)
+       defer os.RemoveAll(certTemp)
 
        i := 0
        cr.MkTempDir = func(_ string, prefix string) (string, error) {
@@ -804,6 +817,25 @@ func (s *TestSuite) TestSetupMounts(c *C) {
                checkEmpty()
        }
 
+       {
+               i = 0
+               cr.Container.Mounts = make(map[string]arvados.Mount)
+               cr.Container.Mounts["/tmp"] = arvados.Mount{Kind: "tmp"}
+               cr.OutputPath = "/tmp"
+
+               apiflag := true
+               cr.Container.RuntimeConstraints.API = &apiflag
+
+               err := cr.SetupMounts()
+               c.Check(err, IsNil)
+               c.Check(am.Cmd, DeepEquals, []string{"--foreground", "--allow-other", "--read-write", "--mount-by-pdh", "by_id", realTemp + "/keep1"})
+               c.Check(cr.Binds, DeepEquals, []string{realTemp + "/2:/tmp", stubCertPath + ":/etc/arvados/ca-certificates.crt:ro"})
+               cr.CleanupDirs()
+               checkEmpty()
+
+               apiflag = false
+       }
+
        {
                i = 0
                cr.Container.Mounts = map[string]arvados.Mount{
index cae95fdd9d6cfd30110764e4ea7c87188c0ed6aa..e71989afb82cc6df750d6a3627247f418c0e5db5 100644 (file)
@@ -16,6 +16,11 @@ import (
 
 const MaxLogLine = 1 << 14 // Child stderr lines >16KiB will be split
 
+var (
+       signalOnDeadPPID  int = 15
+       ppidCheckInterval     = time.Second
+)
+
 func main() {
        reporter := crunchstat.Reporter{
                Logger: log.New(os.Stderr, "crunchstat: ", 0),
@@ -24,12 +29,16 @@ func main() {
        flag.StringVar(&reporter.CgroupRoot, "cgroup-root", "", "Root of cgroup tree")
        flag.StringVar(&reporter.CgroupParent, "cgroup-parent", "", "Name of container parent under cgroup")
        flag.StringVar(&reporter.CIDFile, "cgroup-cid", "", "Path to container id file")
+       flag.IntVar(&signalOnDeadPPID, "signal-on-dead-ppid", signalOnDeadPPID, "Signal to send child if crunchstat's parent process disappears (0 to disable)")
+       flag.DurationVar(&ppidCheckInterval, "ppid-check-interval", ppidCheckInterval, "Time between checks for parent process disappearance")
        pollMsec := flag.Int64("poll", 1000, "Reporting interval, in milliseconds")
 
        flag.Parse()
 
        if reporter.CgroupRoot == "" {
                reporter.Logger.Fatal("error: must provide -cgroup-root")
+       } else if signalOnDeadPPID < 0 {
+               reporter.Logger.Fatalf("-signal-on-dead-ppid=%d is invalid (use a positive signal number, or 0 to disable)", signalOnDeadPPID)
        }
        reporter.PollPeriod = time.Duration(*pollMsec) * time.Millisecond
 
@@ -77,6 +86,11 @@ func runCommand(argv []string, logger *log.Logger) error {
        signal.Notify(sigChan, syscall.SIGTERM)
        signal.Notify(sigChan, syscall.SIGINT)
 
+       // Kill our child proc if our parent process disappears
+       if signalOnDeadPPID != 0 {
+               go sendSignalOnDeadPPID(ppidCheckInterval, signalOnDeadPPID, os.Getppid(), cmd, logger)
+       }
+
        // Funnel stderr through our channel
        stderr_pipe, err := cmd.StderrPipe()
        if err != nil {
@@ -97,6 +111,28 @@ func runCommand(argv []string, logger *log.Logger) error {
        return cmd.Wait()
 }
 
+func sendSignalOnDeadPPID(intvl time.Duration, signum, ppidOrig int, cmd *exec.Cmd, logger *log.Logger) {
+       ticker := time.NewTicker(intvl)
+       for _ = range ticker.C {
+               ppid := os.Getppid()
+               if ppid == ppidOrig {
+                       continue
+               }
+               if cmd.Process == nil {
+                       // Child process isn't running yet
+                       continue
+               }
+               logger.Printf("notice: crunchstat ppid changed from %d to %d -- killing child pid %d with signal %d", ppidOrig, ppid, cmd.Process.Pid, signum)
+               err := cmd.Process.Signal(syscall.Signal(signum))
+               if err != nil {
+                       logger.Printf("error: sending signal: %s", err)
+                       continue
+               }
+               ticker.Stop()
+               break
+       }
+}
+
 func copyPipeToChildLog(in io.ReadCloser, logger *log.Logger) {
        reader := bufio.NewReaderSize(in, MaxLogLine)
        var prefix string
index fe3b56d25876fd832d3596abe3db8e40852ebbf7..759b3aa073c11df927923b703141587b8dbbabd9 100644 (file)
@@ -3,9 +3,15 @@ package main
 import (
        "bufio"
        "bytes"
+       "fmt"
        "io"
+       "io/ioutil"
        "log"
        "math/rand"
+       "os"
+       "os/exec"
+       "sync"
+       "syscall"
        "testing"
        "time"
 )
@@ -82,3 +88,147 @@ func bufLogger() (*log.Logger, *bufio.Reader) {
        logger := log.New(w, "", 0)
        return logger, bufio.NewReader(r)
 }
+
+func TestSignalOnDeadPPID(t *testing.T) {
+       if !testDeadParent(t, 0) {
+               t.Fatal("child should still be alive after parent dies")
+       }
+       if testDeadParent(t, 15) {
+               t.Fatal("child should have been killed when parent died")
+       }
+}
+
+// testDeadParent returns true if crunchstat's child proc is still
+// alive after its parent dies.
+func testDeadParent(t *testing.T, signum int) bool {
+       var err error
+       var bin, childlockfile, parentlockfile *os.File
+       for _, f := range []**os.File{&bin, &childlockfile, &parentlockfile} {
+               *f, err = ioutil.TempFile("", "crunchstat_")
+               if err != nil {
+                       t.Fatal(err)
+               }
+               defer (*f).Close()
+               defer os.Remove((*f).Name())
+       }
+
+       bin.Close()
+       err = exec.Command("go", "build", "-o", bin.Name()).Run()
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       err = syscall.Flock(int(parentlockfile.Fd()), syscall.LOCK_EX)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       cmd := exec.Command("bash", "-c", `
+set -e
+"$BINFILE" -cgroup-root=/none -ppid-check-interval=10ms -signal-on-dead-ppid="$SIGNUM" bash -c '
+    set -e
+    unlock() {
+        flock --unlock "$CHILDLOCKFD"
+        kill %1
+    }
+    trap unlock TERM
+    flock --exclusive "$CHILDLOCKFD"
+    echo -n "$$" > "$CHILDLOCKFILE"
+    flock --unlock "$PARENTLOCKFD"
+    sleep 20 </dev/null >/dev/null 2>/dev/null &
+    wait %1
+    unlock
+' &
+
+# wait for inner bash to start, to ensure $BINFILE has seen this bash proc as its initial PPID
+flock --exclusive "$PARENTLOCKFILE" true
+`)
+       cmd.Env = append(os.Environ(),
+               "SIGNUM="+fmt.Sprintf("%d", signum),
+               "PARENTLOCKFD=3",
+               "PARENTLOCKFILE="+parentlockfile.Name(),
+               "CHILDLOCKFD=4",
+               "CHILDLOCKFILE="+childlockfile.Name(),
+               "BINFILE="+bin.Name())
+       cmd.ExtraFiles = []*os.File{parentlockfile, childlockfile}
+       stderr, err := cmd.StderrPipe()
+       if err != nil {
+               t.Fatal(err)
+       }
+       stdout, err := cmd.StdoutPipe()
+       if err != nil {
+               t.Fatal(err)
+       }
+       cmd.Start()
+       defer cmd.Wait()
+
+       var wg sync.WaitGroup
+       wg.Add(2)
+       defer wg.Wait()
+       for _, rdr := range []io.ReadCloser{stderr, stdout} {
+               go func(rdr io.ReadCloser) {
+                       defer wg.Done()
+                       buf := make([]byte, 1024)
+                       for {
+                               n, err := rdr.Read(buf)
+                               if n > 0 {
+                                       t.Logf("%s", buf[:n])
+                               }
+                               if err != nil {
+                                       return
+                               }
+                       }
+               }(rdr)
+       }
+
+       // Wait until inner bash process releases parentlockfile
+       // (which means it has locked childlockfile and written its
+       // PID)
+       err = exec.Command("flock", "--exclusive", parentlockfile.Name(), "true").Run()
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       childDone := make(chan bool)
+       go func() {
+               // Notify the main thread when the inner bash process
+               // releases its lock on childlockfile (which means
+               // either its sleep process ended or it received a
+               // TERM signal).
+               t0 := time.Now()
+               err = exec.Command("flock", "--exclusive", childlockfile.Name(), "true").Run()
+               if err != nil {
+                       t.Fatal(err)
+               }
+               t.Logf("child done after %s", time.Since(t0))
+               close(childDone)
+       }()
+
+       select {
+       case <-time.After(500 * time.Millisecond):
+               // Inner bash process is still alive after the timeout
+               // period. Kill it now, so our stdout and stderr pipes
+               // can finish and we don't leave a mess of child procs
+               // behind.
+               buf, err := ioutil.ReadFile(childlockfile.Name())
+               if err != nil {
+                       t.Fatal(err)
+               }
+               var childPID int
+               _, err = fmt.Sscanf(string(buf), "%d", &childPID)
+               if err != nil {
+                       t.Fatal(err)
+               }
+               child, err := os.FindProcess(childPID)
+               if err != nil {
+                       t.Fatal(err)
+               }
+               child.Signal(syscall.Signal(15))
+               return true
+
+       case <-childDone:
+               // Inner bash process ended soon after its grandparent
+               // ended.
+               return false
+       }
+}
diff --git a/services/datamanager/collection/collection.go b/services/datamanager/collection/collection.go
deleted file mode 100644 (file)
index 05e7a5f..0000000
+++ /dev/null
@@ -1,408 +0,0 @@
-// Deals with parsing Collection responses from API Server.
-
-package collection
-
-import (
-       "flag"
-       "fmt"
-       "git.curoverse.com/arvados.git/sdk/go/arvadosclient"
-       "git.curoverse.com/arvados.git/sdk/go/blockdigest"
-       "git.curoverse.com/arvados.git/sdk/go/logger"
-       "git.curoverse.com/arvados.git/sdk/go/manifest"
-       "git.curoverse.com/arvados.git/sdk/go/util"
-       "log"
-       "os"
-       "runtime/pprof"
-       "time"
-)
-
-var (
-       HeapProfileFilename string
-)
-
-// Collection representation
-type Collection struct {
-       UUID              string
-       OwnerUUID         string
-       ReplicationLevel  int
-       BlockDigestToSize map[blockdigest.BlockDigest]int
-       TotalSize         int
-}
-
-// ReadCollections holds information about collections from API server
-type ReadCollections struct {
-       ReadAllCollections        bool
-       UUIDToCollection          map[string]Collection
-       OwnerToCollectionSize     map[string]int
-       BlockToDesiredReplication map[blockdigest.DigestWithSize]int
-       CollectionUUIDToIndex     map[string]int
-       CollectionIndexToUUID     []string
-       BlockToCollectionIndices  map[blockdigest.DigestWithSize][]int
-}
-
-// GetCollectionsParams params
-type GetCollectionsParams struct {
-       Client    *arvadosclient.ArvadosClient
-       Logger    *logger.Logger
-       BatchSize int
-}
-
-// SdkCollectionInfo holds collection info from api
-type SdkCollectionInfo struct {
-       UUID               string    `json:"uuid"`
-       OwnerUUID          string    `json:"owner_uuid"`
-       ReplicationDesired int       `json:"replication_desired"`
-       ModifiedAt         time.Time `json:"modified_at"`
-       ManifestText       string    `json:"manifest_text"`
-}
-
-// SdkCollectionList lists collections from api
-type SdkCollectionList struct {
-       ItemsAvailable int                 `json:"items_available"`
-       Items          []SdkCollectionInfo `json:"items"`
-}
-
-func init() {
-       flag.StringVar(&HeapProfileFilename,
-               "heap-profile",
-               "",
-               "File to write the heap profiles to. Leave blank to skip profiling.")
-}
-
-// WriteHeapProfile writes the heap profile to a file for later review.
-// Since a file is expected to only contain a single heap profile this
-// function overwrites the previously written profile, so it is safe
-// to call multiple times in a single run.
-// Otherwise we would see cumulative numbers as explained here:
-// https://groups.google.com/d/msg/golang-nuts/ZyHciRglQYc/2nh4Ndu2fZcJ
-func WriteHeapProfile() error {
-       if HeapProfileFilename != "" {
-               heapProfile, err := os.Create(HeapProfileFilename)
-               if err != nil {
-                       return err
-               }
-
-               defer heapProfile.Close()
-
-               err = pprof.WriteHeapProfile(heapProfile)
-               return err
-       }
-
-       return nil
-}
-
-// GetCollectionsAndSummarize gets collections from api and summarizes
-func GetCollectionsAndSummarize(params GetCollectionsParams) (results ReadCollections, err error) {
-       results, err = GetCollections(params)
-       if err != nil {
-               return
-       }
-
-       results.Summarize(params.Logger)
-
-       log.Printf("Uuid to Size used: %v", results.OwnerToCollectionSize)
-       log.Printf("Read and processed %d collections",
-               len(results.UUIDToCollection))
-
-       // TODO(misha): Add a "readonly" flag. If we're in readonly mode,
-       // lots of behaviors can become warnings (and obviously we can't
-       // write anything).
-       // if !readCollections.ReadAllCollections {
-       //      log.Fatalf("Did not read all collections")
-       // }
-
-       return
-}
-
-// GetCollections gets collections from api
-func GetCollections(params GetCollectionsParams) (results ReadCollections, err error) {
-       if &params.Client == nil {
-               err = fmt.Errorf("params.Client passed to GetCollections() should " +
-                       "contain a valid ArvadosClient, but instead it is nil.")
-               return
-       }
-
-       fieldsWanted := []string{"manifest_text",
-               "owner_uuid",
-               "uuid",
-               "replication_desired",
-               "modified_at"}
-
-       sdkParams := arvadosclient.Dict{
-               "select":  fieldsWanted,
-               "order":   []string{"modified_at ASC", "uuid ASC"},
-               "filters": [][]string{{"modified_at", ">=", "1900-01-01T00:00:00Z"}},
-               "offset":  0}
-
-       if params.BatchSize > 0 {
-               sdkParams["limit"] = params.BatchSize
-       }
-
-       var defaultReplicationLevel int
-       {
-               var value interface{}
-               value, err = params.Client.Discovery("defaultCollectionReplication")
-               if err != nil {
-                       return
-               }
-
-               defaultReplicationLevel = int(value.(float64))
-               if defaultReplicationLevel <= 0 {
-                       err = fmt.Errorf("Default collection replication returned by arvados SDK "+
-                               "should be a positive integer but instead it was %d.",
-                               defaultReplicationLevel)
-                       return
-               }
-       }
-
-       initialNumberOfCollectionsAvailable, err :=
-               util.NumberItemsAvailable(params.Client, "collections")
-       if err != nil {
-               return
-       }
-       // Include a 1% margin for collections added while we're reading so
-       // that we don't have to grow the map in most cases.
-       maxExpectedCollections := int(
-               float64(initialNumberOfCollectionsAvailable) * 1.01)
-       results.UUIDToCollection = make(map[string]Collection, maxExpectedCollections)
-
-       if params.Logger != nil {
-               params.Logger.Update(func(p map[string]interface{}, e map[string]interface{}) {
-                       collectionInfo := logger.GetOrCreateMap(p, "collection_info")
-                       collectionInfo["num_collections_at_start"] = initialNumberOfCollectionsAvailable
-                       collectionInfo["batch_size"] = params.BatchSize
-                       collectionInfo["default_replication_level"] = defaultReplicationLevel
-               })
-       }
-
-       // These values are just for getting the loop to run the first time,
-       // afterwards they'll be set to real values.
-       remainingCollections := 1
-       var totalCollections int
-       var previousTotalCollections int
-       for remainingCollections > 0 {
-               // We're still finding new collections
-
-               // Write the heap profile for examining memory usage
-               err = WriteHeapProfile()
-               if err != nil {
-                       return
-               }
-
-               // Get next batch of collections.
-               var collections SdkCollectionList
-               err = params.Client.List("collections", sdkParams, &collections)
-               if err != nil {
-                       return
-               }
-               batchCollections := len(collections.Items)
-
-               // We must always have at least one collection in the batch
-               if batchCollections < 1 {
-                       err = fmt.Errorf("API query returned no collections for %+v", sdkParams)
-                       return
-               }
-
-               // Update count of remaining collections
-               remainingCollections = collections.ItemsAvailable - sdkParams["offset"].(int) - batchCollections
-
-               // Process collection and update our date filter.
-               latestModificationDate, maxManifestSize, totalManifestSize, err := ProcessCollections(params.Logger,
-                       collections.Items,
-                       defaultReplicationLevel,
-                       results.UUIDToCollection)
-               if err != nil {
-                       return results, err
-               }
-               if sdkParams["filters"].([][]string)[0][2] != latestModificationDate.Format(time.RFC3339) {
-                       sdkParams["filters"].([][]string)[0][2] = latestModificationDate.Format(time.RFC3339)
-                       sdkParams["offset"] = 0
-               } else {
-                       sdkParams["offset"] = sdkParams["offset"].(int) + batchCollections
-               }
-
-               // update counts
-               previousTotalCollections = totalCollections
-               totalCollections = len(results.UUIDToCollection)
-
-               log.Printf("%d collections read, %d (%d new) in last batch, "+
-                       "%d remaining, "+
-                       "%s latest modified date, %.0f %d %d avg,max,total manifest size",
-                       totalCollections,
-                       batchCollections,
-                       totalCollections-previousTotalCollections,
-                       remainingCollections,
-                       sdkParams["filters"].([][]string)[0][2],
-                       float32(totalManifestSize)/float32(totalCollections),
-                       maxManifestSize, totalManifestSize)
-
-               if params.Logger != nil {
-                       params.Logger.Update(func(p map[string]interface{}, e map[string]interface{}) {
-                               collectionInfo := logger.GetOrCreateMap(p, "collection_info")
-                               collectionInfo["collections_read"] = totalCollections
-                               collectionInfo["latest_modified_date_seen"] = sdkParams["filters"].([][]string)[0][2]
-                               collectionInfo["total_manifest_size"] = totalManifestSize
-                               collectionInfo["max_manifest_size"] = maxManifestSize
-                       })
-               }
-       }
-
-       // Make one final API request to verify that we have processed all collections available up to the latest modification date
-       var collections SdkCollectionList
-       sdkParams["filters"].([][]string)[0][1] = "<="
-       sdkParams["limit"] = 0
-       err = params.Client.List("collections", sdkParams, &collections)
-       if err != nil {
-               return
-       }
-       finalNumberOfCollectionsAvailable, err :=
-               util.NumberItemsAvailable(params.Client, "collections")
-       if err != nil {
-               return
-       }
-       if totalCollections < finalNumberOfCollectionsAvailable {
-               err = fmt.Errorf("API server indicates a total of %d collections "+
-                       "available up to %v, but we only retrieved %d. "+
-                       "Refusing to continue as this could indicate an "+
-                       "otherwise undetected failure.",
-                       finalNumberOfCollectionsAvailable,
-                       sdkParams["filters"].([][]string)[0][2],
-                       totalCollections)
-               return
-       }
-
-       // Write the heap profile for examining memory usage
-       err = WriteHeapProfile()
-
-       return
-}
-
-// StrCopy returns a newly allocated string.
-// It is useful to copy slices so that the garbage collector can reuse
-// the memory of the longer strings they came from.
-func StrCopy(s string) string {
-       return string([]byte(s))
-}
-
-// ProcessCollections read from api server
-func ProcessCollections(arvLogger *logger.Logger,
-       receivedCollections []SdkCollectionInfo,
-       defaultReplicationLevel int,
-       UUIDToCollection map[string]Collection,
-) (
-       latestModificationDate time.Time,
-       maxManifestSize, totalManifestSize uint64,
-       err error,
-) {
-       for _, sdkCollection := range receivedCollections {
-               collection := Collection{UUID: StrCopy(sdkCollection.UUID),
-                       OwnerUUID:         StrCopy(sdkCollection.OwnerUUID),
-                       ReplicationLevel:  sdkCollection.ReplicationDesired,
-                       BlockDigestToSize: make(map[blockdigest.BlockDigest]int)}
-
-               if sdkCollection.ModifiedAt.IsZero() {
-                       err = fmt.Errorf(
-                               "Arvados SDK collection returned with unexpected zero "+
-                                       "modification date. This probably means that either we failed to "+
-                                       "parse the modification date or the API server has changed how "+
-                                       "it returns modification dates: %+v",
-                               collection)
-                       return
-               }
-
-               if sdkCollection.ModifiedAt.After(latestModificationDate) {
-                       latestModificationDate = sdkCollection.ModifiedAt
-               }
-
-               if collection.ReplicationLevel == 0 {
-                       collection.ReplicationLevel = defaultReplicationLevel
-               }
-
-               manifest := manifest.Manifest{Text: sdkCollection.ManifestText}
-               manifestSize := uint64(len(sdkCollection.ManifestText))
-
-               if _, alreadySeen := UUIDToCollection[collection.UUID]; !alreadySeen {
-                       totalManifestSize += manifestSize
-               }
-               if manifestSize > maxManifestSize {
-                       maxManifestSize = manifestSize
-               }
-
-               blockChannel := manifest.BlockIterWithDuplicates()
-               for block := range blockChannel {
-                       if storedSize, stored := collection.BlockDigestToSize[block.Digest]; stored && storedSize != block.Size {
-                               log.Printf(
-                                       "Collection %s contains multiple sizes (%d and %d) for block %s",
-                                       collection.UUID,
-                                       storedSize,
-                                       block.Size,
-                                       block.Digest)
-                       }
-                       collection.BlockDigestToSize[block.Digest] = block.Size
-               }
-               if manifest.Err != nil {
-                       err = manifest.Err
-                       return
-               }
-
-               collection.TotalSize = 0
-               for _, size := range collection.BlockDigestToSize {
-                       collection.TotalSize += size
-               }
-               UUIDToCollection[collection.UUID] = collection
-
-               // Clear out all the manifest strings that we don't need anymore.
-               // These hopefully form the bulk of our memory usage.
-               manifest.Text = ""
-               sdkCollection.ManifestText = ""
-       }
-
-       return
-}
-
-// Summarize the collections read
-func (readCollections *ReadCollections) Summarize(arvLogger *logger.Logger) {
-       readCollections.OwnerToCollectionSize = make(map[string]int)
-       readCollections.BlockToDesiredReplication = make(map[blockdigest.DigestWithSize]int)
-       numCollections := len(readCollections.UUIDToCollection)
-       readCollections.CollectionUUIDToIndex = make(map[string]int, numCollections)
-       readCollections.CollectionIndexToUUID = make([]string, 0, numCollections)
-       readCollections.BlockToCollectionIndices = make(map[blockdigest.DigestWithSize][]int)
-
-       for _, coll := range readCollections.UUIDToCollection {
-               collectionIndex := len(readCollections.CollectionIndexToUUID)
-               readCollections.CollectionIndexToUUID =
-                       append(readCollections.CollectionIndexToUUID, coll.UUID)
-               readCollections.CollectionUUIDToIndex[coll.UUID] = collectionIndex
-
-               readCollections.OwnerToCollectionSize[coll.OwnerUUID] =
-                       readCollections.OwnerToCollectionSize[coll.OwnerUUID] + coll.TotalSize
-
-               for block, size := range coll.BlockDigestToSize {
-                       locator := blockdigest.DigestWithSize{Digest: block, Size: uint32(size)}
-                       readCollections.BlockToCollectionIndices[locator] =
-                               append(readCollections.BlockToCollectionIndices[locator],
-                                       collectionIndex)
-                       storedReplication := readCollections.BlockToDesiredReplication[locator]
-                       if coll.ReplicationLevel > storedReplication {
-                               readCollections.BlockToDesiredReplication[locator] =
-                                       coll.ReplicationLevel
-                       }
-               }
-       }
-
-       if arvLogger != nil {
-               arvLogger.Update(func(p map[string]interface{}, e map[string]interface{}) {
-                       collectionInfo := logger.GetOrCreateMap(p, "collection_info")
-                       // Since maps are shallow copied, we run a risk of concurrent
-                       // updates here. By copying results.OwnerToCollectionSize into
-                       // the log, we're assuming that it won't be updated.
-                       collectionInfo["owner_to_collection_size"] =
-                               readCollections.OwnerToCollectionSize
-                       collectionInfo["distinct_blocks_named"] =
-                               len(readCollections.BlockToDesiredReplication)
-               })
-       }
-
-       return
-}
diff --git a/services/datamanager/collection/collection_test.go b/services/datamanager/collection/collection_test.go
deleted file mode 100644 (file)
index 1bf6a89..0000000
+++ /dev/null
@@ -1,202 +0,0 @@
-package collection
-
-import (
-       "git.curoverse.com/arvados.git/sdk/go/arvadosclient"
-       "git.curoverse.com/arvados.git/sdk/go/arvadostest"
-       "git.curoverse.com/arvados.git/sdk/go/blockdigest"
-       . "gopkg.in/check.v1"
-       "net/http"
-       "net/http/httptest"
-       "testing"
-)
-
-// Gocheck boilerplate
-func Test(t *testing.T) {
-       TestingT(t)
-}
-
-type MySuite struct{}
-
-var _ = Suite(&MySuite{})
-
-// This captures the result we expect from
-// ReadCollections.Summarize().  Because CollectionUUIDToIndex is
-// indeterminate, we replace BlockToCollectionIndices with
-// BlockToCollectionUuids.
-type ExpectedSummary struct {
-       OwnerToCollectionSize     map[string]int
-       BlockToDesiredReplication map[blockdigest.DigestWithSize]int
-       BlockToCollectionUuids    map[blockdigest.DigestWithSize][]string
-}
-
-func CompareSummarizedReadCollections(c *C,
-       summarized ReadCollections,
-       expected ExpectedSummary) {
-
-       c.Assert(summarized.OwnerToCollectionSize, DeepEquals,
-               expected.OwnerToCollectionSize)
-
-       c.Assert(summarized.BlockToDesiredReplication, DeepEquals,
-               expected.BlockToDesiredReplication)
-
-       summarizedBlockToCollectionUuids :=
-               make(map[blockdigest.DigestWithSize]map[string]struct{})
-       for digest, indices := range summarized.BlockToCollectionIndices {
-               uuidSet := make(map[string]struct{})
-               summarizedBlockToCollectionUuids[digest] = uuidSet
-               for _, index := range indices {
-                       uuidSet[summarized.CollectionIndexToUUID[index]] = struct{}{}
-               }
-       }
-
-       expectedBlockToCollectionUuids :=
-               make(map[blockdigest.DigestWithSize]map[string]struct{})
-       for digest, uuidSlice := range expected.BlockToCollectionUuids {
-               uuidSet := make(map[string]struct{})
-               expectedBlockToCollectionUuids[digest] = uuidSet
-               for _, uuid := range uuidSlice {
-                       uuidSet[uuid] = struct{}{}
-               }
-       }
-
-       c.Assert(summarizedBlockToCollectionUuids, DeepEquals,
-               expectedBlockToCollectionUuids)
-}
-
-func (s *MySuite) TestSummarizeSimple(checker *C) {
-       rc := MakeTestReadCollections([]TestCollectionSpec{{
-               ReplicationLevel: 5,
-               Blocks:           []int{1, 2},
-       }})
-
-       rc.Summarize(nil)
-
-       c := rc.UUIDToCollection["col0"]
-
-       blockDigest1 := blockdigest.MakeTestDigestWithSize(1)
-       blockDigest2 := blockdigest.MakeTestDigestWithSize(2)
-
-       expected := ExpectedSummary{
-               OwnerToCollectionSize:     map[string]int{c.OwnerUUID: c.TotalSize},
-               BlockToDesiredReplication: map[blockdigest.DigestWithSize]int{blockDigest1: 5, blockDigest2: 5},
-               BlockToCollectionUuids:    map[blockdigest.DigestWithSize][]string{blockDigest1: {c.UUID}, blockDigest2: {c.UUID}},
-       }
-
-       CompareSummarizedReadCollections(checker, rc, expected)
-}
-
-func (s *MySuite) TestSummarizeOverlapping(checker *C) {
-       rc := MakeTestReadCollections([]TestCollectionSpec{
-               {
-                       ReplicationLevel: 5,
-                       Blocks:           []int{1, 2},
-               },
-               {
-                       ReplicationLevel: 8,
-                       Blocks:           []int{2, 3},
-               },
-       })
-
-       rc.Summarize(nil)
-
-       c0 := rc.UUIDToCollection["col0"]
-       c1 := rc.UUIDToCollection["col1"]
-
-       blockDigest1 := blockdigest.MakeTestDigestWithSize(1)
-       blockDigest2 := blockdigest.MakeTestDigestWithSize(2)
-       blockDigest3 := blockdigest.MakeTestDigestWithSize(3)
-
-       expected := ExpectedSummary{
-               OwnerToCollectionSize: map[string]int{
-                       c0.OwnerUUID: c0.TotalSize,
-                       c1.OwnerUUID: c1.TotalSize,
-               },
-               BlockToDesiredReplication: map[blockdigest.DigestWithSize]int{
-                       blockDigest1: 5,
-                       blockDigest2: 8,
-                       blockDigest3: 8,
-               },
-               BlockToCollectionUuids: map[blockdigest.DigestWithSize][]string{
-                       blockDigest1: {c0.UUID},
-                       blockDigest2: {c0.UUID, c1.UUID},
-                       blockDigest3: {c1.UUID},
-               },
-       }
-
-       CompareSummarizedReadCollections(checker, rc, expected)
-}
-
-type APITestData struct {
-       // path and response map
-       responses map[string]arvadostest.StubResponse
-
-       // expected error, if any
-       expectedError string
-}
-
-func (s *MySuite) TestGetCollectionsAndSummarize_DiscoveryError(c *C) {
-       testGetCollectionsAndSummarize(c,
-               APITestData{
-                       responses:     make(map[string]arvadostest.StubResponse),
-                       expectedError: "arvados API server error: 500.*",
-               })
-}
-
-func (s *MySuite) TestGetCollectionsAndSummarize_ApiErrorGetCollections(c *C) {
-       respMap := make(map[string]arvadostest.StubResponse)
-       respMap["/discovery/v1/apis/arvados/v1/rest"] = arvadostest.StubResponse{200, `{"defaultCollectionReplication":2}`}
-       respMap["/arvados/v1/collections"] = arvadostest.StubResponse{-1, ``}
-
-       testGetCollectionsAndSummarize(c,
-               APITestData{
-                       responses:     respMap,
-                       expectedError: "arvados API server error: 302.*",
-               })
-}
-
-func (s *MySuite) TestGetCollectionsAndSummarize_GetCollectionsBadStreamName(c *C) {
-       respMap := make(map[string]arvadostest.StubResponse)
-       respMap["/discovery/v1/apis/arvados/v1/rest"] = arvadostest.StubResponse{200, `{"defaultCollectionReplication":2}`}
-       respMap["/arvados/v1/collections"] = arvadostest.StubResponse{200, `{"items_available":1,"items":[{"modified_at":"2015-11-24T15:04:05Z","manifest_text":"badstreamname"}]}`}
-
-       testGetCollectionsAndSummarize(c,
-               APITestData{
-                       responses:     respMap,
-                       expectedError: "Invalid stream name: badstreamname",
-               })
-}
-
-func (s *MySuite) TestGetCollectionsAndSummarize_GetCollectionsBadFileToken(c *C) {
-       respMap := make(map[string]arvadostest.StubResponse)
-       respMap["/discovery/v1/apis/arvados/v1/rest"] = arvadostest.StubResponse{200, `{"defaultCollectionReplication":2}`}
-       respMap["/arvados/v1/collections"] = arvadostest.StubResponse{200, `{"items_available":1,"items":[{"modified_at":"2015-11-24T15:04:05Z","manifest_text":"./goodstream acbd18db4cc2f85cedef654fccc4a4d8+3 0:1:file1.txt file2.txt"}]}`}
-
-       testGetCollectionsAndSummarize(c,
-               APITestData{
-                       responses:     respMap,
-                       expectedError: "Invalid file token: file2.txt",
-               })
-}
-
-func testGetCollectionsAndSummarize(c *C, testData APITestData) {
-       apiStub := arvadostest.ServerStub{testData.responses}
-
-       api := httptest.NewServer(&apiStub)
-       defer api.Close()
-
-       arv := &arvadosclient.ArvadosClient{
-               Scheme:    "http",
-               ApiServer: api.URL[7:],
-               ApiToken:  "abc123",
-               Client:    &http.Client{Transport: &http.Transport{}},
-       }
-
-       // GetCollectionsAndSummarize
-       _, err := GetCollectionsAndSummarize(GetCollectionsParams{arv, nil, 10})
-
-       if testData.expectedError == "" {
-               c.Assert(err, IsNil)
-       } else {
-               c.Assert(err, ErrorMatches, testData.expectedError)
-       }
-}
diff --git a/services/datamanager/collection/testing.go b/services/datamanager/collection/testing.go
deleted file mode 100644 (file)
index 2238433..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-// Code used for testing only.
-
-package collection
-
-import (
-       "fmt"
-       "git.curoverse.com/arvados.git/sdk/go/blockdigest"
-)
-
-// TestCollectionSpec with test blocks and desired replication level
-type TestCollectionSpec struct {
-       // The desired replication level
-       ReplicationLevel int
-       // Blocks this contains, represented by ints. Ints repeated will
-       // still only represent one block
-       Blocks []int
-}
-
-// MakeTestReadCollections creates a ReadCollections object for testing
-// based on the give specs. Only the ReadAllCollections and UUIDToCollection
-// fields are populated. To populate other fields call rc.Summarize().
-func MakeTestReadCollections(specs []TestCollectionSpec) (rc ReadCollections) {
-       rc = ReadCollections{
-               ReadAllCollections: true,
-               UUIDToCollection:   map[string]Collection{},
-       }
-
-       for i, spec := range specs {
-               c := Collection{
-                       UUID:              fmt.Sprintf("col%d", i),
-                       OwnerUUID:         fmt.Sprintf("owner%d", i),
-                       ReplicationLevel:  spec.ReplicationLevel,
-                       BlockDigestToSize: map[blockdigest.BlockDigest]int{},
-               }
-               rc.UUIDToCollection[c.UUID] = c
-               for _, j := range spec.Blocks {
-                       c.BlockDigestToSize[blockdigest.MakeTestBlockDigest(j)] = j
-               }
-               // We compute the size in a separate loop because the value
-               // computed in the above loop would be invalid if c.Blocks
-               // contained duplicates.
-               for _, size := range c.BlockDigestToSize {
-                       c.TotalSize += size
-               }
-       }
-       return
-}
-
-// CollectionIndicesForTesting returns a slice giving the collection
-// index of each collection that was passed in to MakeTestReadCollections.
-// rc.Summarize() must be called before this method, since Summarize()
-// assigns an index to each collection.
-func (rc ReadCollections) CollectionIndicesForTesting() (indices []int) {
-       // TODO(misha): Assert that rc.Summarize() has been called.
-       numCollections := len(rc.CollectionIndexToUUID)
-       indices = make([]int, numCollections)
-       for i := 0; i < numCollections; i++ {
-               indices[i] = rc.CollectionUUIDToIndex[fmt.Sprintf("col%d", i)]
-       }
-       return
-}
diff --git a/services/datamanager/datamanager.go b/services/datamanager/datamanager.go
deleted file mode 100644 (file)
index 5250d17..0000000
+++ /dev/null
@@ -1,220 +0,0 @@
-/* Keep Datamanager. Responsible for checking on and reporting on Keep Storage */
-
-package main
-
-import (
-       "errors"
-       "flag"
-       "fmt"
-       "git.curoverse.com/arvados.git/sdk/go/arvadosclient"
-       "git.curoverse.com/arvados.git/sdk/go/keepclient"
-       "git.curoverse.com/arvados.git/sdk/go/logger"
-       "git.curoverse.com/arvados.git/sdk/go/util"
-       "git.curoverse.com/arvados.git/services/datamanager/collection"
-       "git.curoverse.com/arvados.git/services/datamanager/keep"
-       "git.curoverse.com/arvados.git/services/datamanager/loggerutil"
-       "git.curoverse.com/arvados.git/services/datamanager/summary"
-       "log"
-       "time"
-)
-
-var (
-       logEventTypePrefix  string
-       logFrequencySeconds int
-       minutesBetweenRuns  int
-       collectionBatchSize int
-       dryRun              bool
-)
-
-func init() {
-       flag.StringVar(&logEventTypePrefix,
-               "log-event-type-prefix",
-               "experimental-data-manager",
-               "Prefix to use in the event_type of our arvados log entries. Set to empty to turn off logging")
-       flag.IntVar(&logFrequencySeconds,
-               "log-frequency-seconds",
-               20,
-               "How frequently we'll write log entries in seconds.")
-       flag.IntVar(&minutesBetweenRuns,
-               "minutes-between-runs",
-               0,
-               "How many minutes we wait between data manager runs. 0 means run once and exit.")
-       flag.IntVar(&collectionBatchSize,
-               "collection-batch-size",
-               1000,
-               "How many collections to request in each batch.")
-       flag.BoolVar(&dryRun,
-               "dry-run",
-               false,
-               "Perform a dry run. Log how many blocks would be deleted/moved, but do not issue any changes to keepstore.")
-}
-
-func main() {
-       flag.Parse()
-
-       if minutesBetweenRuns == 0 {
-               arv, err := arvadosclient.MakeArvadosClient()
-               if err != nil {
-                       loggerutil.FatalWithMessage(arvLogger, fmt.Sprintf("Error making arvados client: %v", err))
-               }
-               err = singlerun(arv)
-               if err != nil {
-                       loggerutil.FatalWithMessage(arvLogger, fmt.Sprintf("singlerun: %v", err))
-               }
-       } else {
-               waitTime := time.Minute * time.Duration(minutesBetweenRuns)
-               for {
-                       log.Println("Beginning Run")
-                       arv, err := arvadosclient.MakeArvadosClient()
-                       if err != nil {
-                               loggerutil.FatalWithMessage(arvLogger, fmt.Sprintf("Error making arvados client: %v", err))
-                       }
-                       err = singlerun(arv)
-                       if err != nil {
-                               log.Printf("singlerun: %v", err)
-                       }
-                       log.Printf("Sleeping for %d minutes", minutesBetweenRuns)
-                       time.Sleep(waitTime)
-               }
-       }
-}
-
-var arvLogger *logger.Logger
-
-func singlerun(arv *arvadosclient.ArvadosClient) error {
-       var err error
-       if isAdmin, err := util.UserIsAdmin(arv); err != nil {
-               return errors.New("Error verifying admin token: " + err.Error())
-       } else if !isAdmin {
-               return errors.New("Current user is not an admin. Datamanager requires a privileged token.")
-       }
-
-       if logEventTypePrefix != "" {
-               arvLogger, err = logger.NewLogger(logger.LoggerParams{
-                       Client:          arv,
-                       EventTypePrefix: logEventTypePrefix,
-                       WriteInterval:   time.Second * time.Duration(logFrequencySeconds)})
-       }
-
-       loggerutil.LogRunInfo(arvLogger)
-       if arvLogger != nil {
-               arvLogger.AddWriteHook(loggerutil.LogMemoryAlloc)
-       }
-
-       var (
-               dataFetcher     summary.DataFetcher
-               readCollections collection.ReadCollections
-               keepServerInfo  keep.ReadServers
-       )
-
-       if summary.ShouldReadData() {
-               dataFetcher = summary.ReadData
-       } else {
-               dataFetcher = BuildDataFetcher(arv)
-       }
-
-       err = dataFetcher(arvLogger, &readCollections, &keepServerInfo)
-       if err != nil {
-               return err
-       }
-
-       err = summary.MaybeWriteData(arvLogger, readCollections, keepServerInfo)
-       if err != nil {
-               return err
-       }
-
-       buckets := summary.BucketReplication(readCollections, keepServerInfo)
-       bucketCounts := buckets.Counts()
-
-       replicationSummary := buckets.SummarizeBuckets(readCollections)
-       replicationCounts := replicationSummary.ComputeCounts()
-
-       log.Printf("Blocks In Collections: %d, "+
-               "\nBlocks In Keep: %d.",
-               len(readCollections.BlockToDesiredReplication),
-               len(keepServerInfo.BlockToServers))
-       log.Println(replicationCounts.PrettyPrint())
-
-       log.Printf("Blocks Histogram:")
-       for _, rlbss := range bucketCounts {
-               log.Printf("%+v: %10d",
-                       rlbss.Levels,
-                       rlbss.Count)
-       }
-
-       kc, err := keepclient.MakeKeepClient(arv)
-       if err != nil {
-               return fmt.Errorf("Error setting up keep client %v", err.Error())
-       }
-
-       // Log that we're finished. We force the recording, since go will
-       // not wait for the write timer before exiting.
-       if arvLogger != nil {
-               defer arvLogger.FinalUpdate(func(p map[string]interface{}, e map[string]interface{}) {
-                       summaryInfo := logger.GetOrCreateMap(p, "summary_info")
-                       summaryInfo["block_replication_counts"] = bucketCounts
-                       summaryInfo["replication_summary"] = replicationCounts
-                       p["summary_info"] = summaryInfo
-
-                       p["run_info"].(map[string]interface{})["finished_at"] = time.Now()
-               })
-       }
-
-       pullServers := summary.ComputePullServers(kc,
-               &keepServerInfo,
-               readCollections.BlockToDesiredReplication,
-               replicationSummary.UnderReplicatedBlocks)
-
-       pullLists := summary.BuildPullLists(pullServers)
-
-       trashLists, trashErr := summary.BuildTrashLists(kc,
-               &keepServerInfo,
-               replicationSummary.KeepBlocksNotInCollections)
-
-       err = summary.WritePullLists(arvLogger, pullLists, dryRun)
-       if err != nil {
-               return err
-       }
-
-       if trashErr != nil {
-               return err
-       }
-       keep.SendTrashLists(arvLogger, kc, trashLists, dryRun)
-
-       return nil
-}
-
-// BuildDataFetcher returns a data fetcher that fetches data from remote servers.
-func BuildDataFetcher(arv *arvadosclient.ArvadosClient) summary.DataFetcher {
-       return func(
-               arvLogger *logger.Logger,
-               readCollections *collection.ReadCollections,
-               keepServerInfo *keep.ReadServers,
-       ) error {
-               collDone := make(chan struct{})
-               var collErr error
-               go func() {
-                       *readCollections, collErr = collection.GetCollectionsAndSummarize(
-                               collection.GetCollectionsParams{
-                                       Client:    arv,
-                                       Logger:    arvLogger,
-                                       BatchSize: collectionBatchSize})
-                       collDone <- struct{}{}
-               }()
-
-               var keepErr error
-               *keepServerInfo, keepErr = keep.GetKeepServersAndSummarize(
-                       keep.GetKeepServersParams{
-                               Client: arv,
-                               Logger: arvLogger,
-                               Limit:  1000})
-
-               <-collDone
-
-               // Return a nil error only if both parts succeeded.
-               if collErr != nil {
-                       return collErr
-               }
-               return keepErr
-       }
-}
diff --git a/services/datamanager/datamanager_test.go b/services/datamanager/datamanager_test.go
deleted file mode 100644 (file)
index 7a8fff5..0000000
+++ /dev/null
@@ -1,732 +0,0 @@
-package main
-
-import (
-       "encoding/json"
-       "fmt"
-       "git.curoverse.com/arvados.git/sdk/go/arvadosclient"
-       "git.curoverse.com/arvados.git/sdk/go/arvadostest"
-       "git.curoverse.com/arvados.git/sdk/go/keepclient"
-       "git.curoverse.com/arvados.git/services/datamanager/collection"
-       "git.curoverse.com/arvados.git/services/datamanager/summary"
-       "io/ioutil"
-       "net/http"
-       "os"
-       "os/exec"
-       "path"
-       "regexp"
-       "strings"
-       "testing"
-       "time"
-)
-
-var arv *arvadosclient.ArvadosClient
-var keepClient *keepclient.KeepClient
-var keepServers []string
-
-func SetupDataManagerTest(t *testing.T) {
-       os.Setenv("ARVADOS_API_HOST_INSECURE", "true")
-
-       // start api and keep servers
-       arvadostest.ResetEnv()
-       arvadostest.StartAPI()
-       arvadostest.StartKeep(2, false)
-
-       var err error
-       arv, err = arvadosclient.MakeArvadosClient()
-       if err != nil {
-               t.Fatalf("Error making arvados client: %s", err)
-       }
-       arv.ApiToken = arvadostest.DataManagerToken
-
-       // keep client
-       keepClient = &keepclient.KeepClient{
-               Arvados:       arv,
-               Want_replicas: 2,
-               Client:        &http.Client{},
-       }
-
-       // discover keep services
-       if err = keepClient.DiscoverKeepServers(); err != nil {
-               t.Fatalf("Error discovering keep services: %s", err)
-       }
-       keepServers = []string{}
-       for _, host := range keepClient.LocalRoots() {
-               keepServers = append(keepServers, host)
-       }
-}
-
-func TearDownDataManagerTest(t *testing.T) {
-       arvadostest.StopKeep(2)
-       arvadostest.StopAPI()
-       summary.WriteDataTo = ""
-       collection.HeapProfileFilename = ""
-}
-
-func putBlock(t *testing.T, data string) string {
-       locator, _, err := keepClient.PutB([]byte(data))
-       if err != nil {
-               t.Fatalf("Error putting test data for %s %s %v", data, locator, err)
-       }
-       if locator == "" {
-               t.Fatalf("No locator found after putting test data")
-       }
-
-       splits := strings.Split(locator, "+")
-       return splits[0] + "+" + splits[1]
-}
-
-func getBlock(t *testing.T, locator string, data string) {
-       reader, blocklen, _, err := keepClient.Get(locator)
-       if err != nil {
-               t.Fatalf("Error getting test data in setup for %s %s %v", data, locator, err)
-       }
-       if reader == nil {
-               t.Fatalf("No reader found after putting test data")
-       }
-       if blocklen != int64(len(data)) {
-               t.Fatalf("blocklen %d did not match data len %d", blocklen, len(data))
-       }
-
-       all, err := ioutil.ReadAll(reader)
-       if string(all) != data {
-               t.Fatalf("Data read %s did not match expected data %s", string(all), data)
-       }
-}
-
-// Create a collection using arv-put
-func createCollection(t *testing.T, data string) string {
-       tempfile, err := ioutil.TempFile(os.TempDir(), "temp-test-file")
-       defer os.Remove(tempfile.Name())
-
-       _, err = tempfile.Write([]byte(data))
-       if err != nil {
-               t.Fatalf("Error writing to tempfile %v", err)
-       }
-
-       // arv-put
-       output, err := exec.Command("arv-put", "--use-filename", "test.txt", tempfile.Name()).Output()
-       if err != nil {
-               t.Fatalf("Error running arv-put %s", err)
-       }
-
-       uuid := string(output[0:27]) // trim terminating char
-       return uuid
-}
-
-// Get collection locator
-var locatorMatcher = regexp.MustCompile(`^([0-9a-f]{32})\+(\d*)(.*)$`)
-
-func getFirstLocatorFromCollection(t *testing.T, uuid string) string {
-       manifest := getCollection(t, uuid)["manifest_text"].(string)
-
-       locator := strings.Split(manifest, " ")[1]
-       match := locatorMatcher.FindStringSubmatch(locator)
-       if match == nil {
-               t.Fatalf("No locator found in collection manifest %s", manifest)
-       }
-
-       return match[1] + "+" + match[2]
-}
-
-func switchToken(t string) func() {
-       orig := arv.ApiToken
-       restore := func() {
-               arv.ApiToken = orig
-       }
-       arv.ApiToken = t
-       return restore
-}
-
-func getCollection(t *testing.T, uuid string) Dict {
-       defer switchToken(arvadostest.AdminToken)()
-
-       getback := make(Dict)
-       err := arv.Get("collections", uuid, nil, &getback)
-       if err != nil {
-               t.Fatalf("Error getting collection %s", err)
-       }
-       if getback["uuid"] != uuid {
-               t.Fatalf("Get collection uuid did not match original: $s, result: $s", uuid, getback["uuid"])
-       }
-
-       return getback
-}
-
-func updateCollection(t *testing.T, uuid string, paramName string, paramValue string) {
-       defer switchToken(arvadostest.AdminToken)()
-
-       err := arv.Update("collections", uuid, arvadosclient.Dict{
-               "collection": arvadosclient.Dict{
-                       paramName: paramValue,
-               },
-       }, &arvadosclient.Dict{})
-
-       if err != nil {
-               t.Fatalf("Error updating collection %s", err)
-       }
-}
-
-type Dict map[string]interface{}
-
-func deleteCollection(t *testing.T, uuid string) {
-       defer switchToken(arvadostest.AdminToken)()
-
-       getback := make(Dict)
-       err := arv.Delete("collections", uuid, nil, &getback)
-       if err != nil {
-               t.Fatalf("Error deleting collection %s", err)
-       }
-       if getback["uuid"] != uuid {
-               t.Fatalf("Delete collection uuid did not match original: $s, result: $s", uuid, getback["uuid"])
-       }
-}
-
-func dataManagerSingleRun(t *testing.T) {
-       err := singlerun(arv)
-       if err != nil {
-               t.Fatalf("Error during singlerun %s", err)
-       }
-}
-
-func getBlockIndexesForServer(t *testing.T, i int) []string {
-       var indexes []string
-
-       path := keepServers[i] + "/index"
-       client := http.Client{}
-       req, err := http.NewRequest("GET", path, nil)
-       req.Header.Add("Authorization", "OAuth2 "+arvadostest.DataManagerToken)
-       req.Header.Add("Content-Type", "application/octet-stream")
-       resp, err := client.Do(req)
-       defer resp.Body.Close()
-
-       if err != nil {
-               t.Fatalf("Error during %s %s", path, err)
-       }
-
-       body, err := ioutil.ReadAll(resp.Body)
-       if err != nil {
-               t.Fatalf("Error reading response from %s %s", path, err)
-       }
-
-       lines := strings.Split(string(body), "\n")
-       for _, line := range lines {
-               indexes = append(indexes, strings.Split(line, " ")...)
-       }
-
-       return indexes
-}
-
-func getBlockIndexes(t *testing.T) [][]string {
-       var indexes [][]string
-
-       for i := 0; i < len(keepServers); i++ {
-               indexes = append(indexes, getBlockIndexesForServer(t, i))
-       }
-       return indexes
-}
-
-func verifyBlocks(t *testing.T, notExpected []string, expected []string, minReplication int) {
-       blocks := getBlockIndexes(t)
-
-       for _, block := range notExpected {
-               for _, idx := range blocks {
-                       if valueInArray(block, idx) {
-                               t.Fatalf("Found unexpected block %s", block)
-                       }
-               }
-       }
-
-       for _, block := range expected {
-               nFound := 0
-               for _, idx := range blocks {
-                       if valueInArray(block, idx) {
-                               nFound++
-                       }
-               }
-               if nFound < minReplication {
-                       t.Fatalf("Found %d replicas of block %s, expected >= %d", nFound, block, minReplication)
-               }
-       }
-}
-
-func valueInArray(value string, list []string) bool {
-       for _, v := range list {
-               if value == v {
-                       return true
-               }
-       }
-       return false
-}
-
-// Test env uses two keep volumes. The volume names can be found by reading the files
-// ARVADOS_HOME/tmp/keep0.volume and ARVADOS_HOME/tmp/keep1.volume
-//
-// The keep volumes are of the dir structure: volumeN/subdir/locator
-func backdateBlocks(t *testing.T, oldUnusedBlockLocators []string) {
-       // First get rid of any size hints in the locators
-       var trimmedBlockLocators []string
-       for _, block := range oldUnusedBlockLocators {
-               trimmedBlockLocators = append(trimmedBlockLocators, strings.Split(block, "+")[0])
-       }
-
-       // Get the working dir so that we can read keep{n}.volume files
-       wd, err := os.Getwd()
-       if err != nil {
-               t.Fatalf("Error getting working dir %s", err)
-       }
-
-       // Now cycle through the two keep volumes
-       oldTime := time.Now().AddDate(0, -2, 0)
-       for i := 0; i < 2; i++ {
-               filename := fmt.Sprintf("%s/../../tmp/keep%d.volume", wd, i)
-               volumeDir, err := ioutil.ReadFile(filename)
-               if err != nil {
-                       t.Fatalf("Error reading keep volume file %s %s", filename, err)
-               }
-
-               // Read the keep volume dir structure
-               volumeContents, err := ioutil.ReadDir(string(volumeDir))
-               if err != nil {
-                       t.Fatalf("Error reading keep dir %s %s", string(volumeDir), err)
-               }
-
-               // Read each subdir for each of the keep volume dir
-               for _, subdir := range volumeContents {
-                       subdirName := fmt.Sprintf("%s/%s", volumeDir, subdir.Name())
-                       subdirContents, err := ioutil.ReadDir(string(subdirName))
-                       if err != nil {
-                               t.Fatalf("Error reading keep dir %s %s", string(subdirName), err)
-                       }
-
-                       // Now we got to the files. The files are names are the block locators
-                       for _, fileInfo := range subdirContents {
-                               blockName := fileInfo.Name()
-                               myname := fmt.Sprintf("%s/%s", subdirName, blockName)
-                               if valueInArray(blockName, trimmedBlockLocators) {
-                                       err = os.Chtimes(myname, oldTime, oldTime)
-                               }
-                       }
-               }
-       }
-}
-
-func getStatus(t *testing.T, path string) interface{} {
-       client := http.Client{}
-       req, err := http.NewRequest("GET", path, nil)
-       req.Header.Add("Authorization", "OAuth2 "+arvadostest.DataManagerToken)
-       req.Header.Add("Content-Type", "application/octet-stream")
-       resp, err := client.Do(req)
-       if err != nil {
-               t.Fatalf("Error during %s %s", path, err)
-       }
-       defer resp.Body.Close()
-
-       var s interface{}
-       json.NewDecoder(resp.Body).Decode(&s)
-
-       return s
-}
-
-// Wait until PullQueue and TrashQueue are empty on all keepServers.
-func waitUntilQueuesFinishWork(t *testing.T) {
-       for _, ks := range keepServers {
-               for done := false; !done; {
-                       time.Sleep(100 * time.Millisecond)
-                       s := getStatus(t, ks+"/status.json")
-                       for _, qName := range []string{"PullQueue", "TrashQueue"} {
-                               qStatus := s.(map[string]interface{})[qName].(map[string]interface{})
-                               if qStatus["Queued"].(float64)+qStatus["InProgress"].(float64) == 0 {
-                                       done = true
-                               }
-                       }
-               }
-       }
-}
-
-// Create some blocks and backdate some of them.
-// Also create some collections and delete some of them.
-// Verify block indexes.
-func TestPutAndGetBlocks(t *testing.T) {
-       defer TearDownDataManagerTest(t)
-       SetupDataManagerTest(t)
-
-       // Put some blocks which will be backdated later on
-       // The first one will also be used in a collection and hence should not be deleted when datamanager runs.
-       // The rest will be old and unreferenced and hence should be deleted when datamanager runs.
-       var oldUnusedBlockLocators []string
-       oldUnusedBlockData := "this block will have older mtime"
-       for i := 0; i < 5; i++ {
-               oldUnusedBlockLocators = append(oldUnusedBlockLocators, putBlock(t, fmt.Sprintf("%s%d", oldUnusedBlockData, i)))
-       }
-       for i := 0; i < 5; i++ {
-               getBlock(t, oldUnusedBlockLocators[i], fmt.Sprintf("%s%d", oldUnusedBlockData, i))
-       }
-
-       // The rest will be old and unreferenced and hence should be deleted when datamanager runs.
-       oldUsedBlockData := "this collection block will have older mtime"
-       oldUsedBlockLocator := putBlock(t, oldUsedBlockData)
-       getBlock(t, oldUsedBlockLocator, oldUsedBlockData)
-
-       // Put some more blocks which will not be backdated; hence they are still new, but not in any collection.
-       // Hence, even though unreferenced, these should not be deleted when datamanager runs.
-       var newBlockLocators []string
-       newBlockData := "this block is newer"
-       for i := 0; i < 5; i++ {
-               newBlockLocators = append(newBlockLocators, putBlock(t, fmt.Sprintf("%s%d", newBlockData, i)))
-       }
-       for i := 0; i < 5; i++ {
-               getBlock(t, newBlockLocators[i], fmt.Sprintf("%s%d", newBlockData, i))
-       }
-
-       // Create a collection that would be deleted later on
-       toBeDeletedCollectionUUID := createCollection(t, "some data for collection creation")
-       toBeDeletedCollectionLocator := getFirstLocatorFromCollection(t, toBeDeletedCollectionUUID)
-
-       // Create another collection that has the same data as the one of the old blocks
-       oldUsedBlockCollectionUUID := createCollection(t, oldUsedBlockData)
-       oldUsedBlockCollectionLocator := getFirstLocatorFromCollection(t, oldUsedBlockCollectionUUID)
-       if oldUsedBlockCollectionLocator != oldUsedBlockLocator {
-               t.Fatalf("Locator of the collection with the same data as old block is different %s", oldUsedBlockCollectionLocator)
-       }
-
-       // Create another collection whose replication level will be changed
-       replicationCollectionUUID := createCollection(t, "replication level on this collection will be reduced")
-       replicationCollectionLocator := getFirstLocatorFromCollection(t, replicationCollectionUUID)
-
-       // Create two collections with same data; one will be deleted later on
-       dataForTwoCollections := "one of these collections will be deleted"
-       oneOfTwoWithSameDataUUID := createCollection(t, dataForTwoCollections)
-       oneOfTwoWithSameDataLocator := getFirstLocatorFromCollection(t, oneOfTwoWithSameDataUUID)
-       secondOfTwoWithSameDataUUID := createCollection(t, dataForTwoCollections)
-       secondOfTwoWithSameDataLocator := getFirstLocatorFromCollection(t, secondOfTwoWithSameDataUUID)
-       if oneOfTwoWithSameDataLocator != secondOfTwoWithSameDataLocator {
-               t.Fatalf("Locators for both these collections expected to be same: %s %s", oneOfTwoWithSameDataLocator, secondOfTwoWithSameDataLocator)
-       }
-
-       // create collection with empty manifest text
-       emptyBlockLocator := putBlock(t, "")
-       emptyCollection := createCollection(t, "")
-
-       // Verify blocks before doing any backdating / deleting.
-       var expected []string
-       expected = append(expected, oldUnusedBlockLocators...)
-       expected = append(expected, newBlockLocators...)
-       expected = append(expected, toBeDeletedCollectionLocator)
-       expected = append(expected, replicationCollectionLocator)
-       expected = append(expected, oneOfTwoWithSameDataLocator)
-       expected = append(expected, secondOfTwoWithSameDataLocator)
-       expected = append(expected, emptyBlockLocator)
-
-       verifyBlocks(t, nil, expected, 2)
-
-       // Run datamanager in singlerun mode
-       dataManagerSingleRun(t)
-       waitUntilQueuesFinishWork(t)
-
-       verifyBlocks(t, nil, expected, 2)
-
-       // Backdate the to-be old blocks and delete the collections
-       backdateBlocks(t, oldUnusedBlockLocators)
-       deleteCollection(t, toBeDeletedCollectionUUID)
-       deleteCollection(t, secondOfTwoWithSameDataUUID)
-       backdateBlocks(t, []string{emptyBlockLocator})
-       deleteCollection(t, emptyCollection)
-
-       // Run data manager again
-       dataManagerSingleRun(t)
-       waitUntilQueuesFinishWork(t)
-
-       // Get block indexes and verify that all backdated blocks except the first one used in collection are not included.
-       expected = expected[:0]
-       expected = append(expected, oldUsedBlockLocator)
-       expected = append(expected, newBlockLocators...)
-       expected = append(expected, toBeDeletedCollectionLocator)
-       expected = append(expected, oneOfTwoWithSameDataLocator)
-       expected = append(expected, secondOfTwoWithSameDataLocator)
-       expected = append(expected, emptyBlockLocator) // even when unreferenced, this remains
-
-       verifyBlocks(t, oldUnusedBlockLocators, expected, 2)
-
-       // Reduce desired replication on replicationCollectionUUID
-       // collection, and verify that Data Manager does not reduce
-       // actual replication any further than that. (It might not
-       // reduce actual replication at all; that's OK for this test.)
-
-       // Reduce desired replication level.
-       updateCollection(t, replicationCollectionUUID, "replication_desired", "1")
-       collection := getCollection(t, replicationCollectionUUID)
-       if collection["replication_desired"].(interface{}) != float64(1) {
-               t.Fatalf("After update replication_desired is not 1; instead it is %v", collection["replication_desired"])
-       }
-
-       // Verify data is currently overreplicated.
-       verifyBlocks(t, nil, []string{replicationCollectionLocator}, 2)
-
-       // Run data manager again
-       dataManagerSingleRun(t)
-       waitUntilQueuesFinishWork(t)
-
-       // Verify data is not underreplicated.
-       verifyBlocks(t, nil, []string{replicationCollectionLocator}, 1)
-
-       // Verify *other* collections' data is not underreplicated.
-       verifyBlocks(t, oldUnusedBlockLocators, expected, 2)
-}
-
-func TestDatamanagerSingleRunRepeatedly(t *testing.T) {
-       defer TearDownDataManagerTest(t)
-       SetupDataManagerTest(t)
-
-       for i := 0; i < 10; i++ {
-               err := singlerun(arv)
-               if err != nil {
-                       t.Fatalf("Got an error during datamanager singlerun: %v", err)
-               }
-       }
-}
-
-func TestGetStatusRepeatedly(t *testing.T) {
-       defer TearDownDataManagerTest(t)
-       SetupDataManagerTest(t)
-
-       for i := 0; i < 10; i++ {
-               for j := 0; j < 2; j++ {
-                       s := getStatus(t, keepServers[j]+"/status.json")
-
-                       var pullQueueStatus interface{}
-                       pullQueueStatus = s.(map[string]interface{})["PullQueue"]
-                       var trashQueueStatus interface{}
-                       trashQueueStatus = s.(map[string]interface{})["TrashQueue"]
-
-                       if pullQueueStatus.(map[string]interface{})["Queued"] == nil ||
-                               pullQueueStatus.(map[string]interface{})["InProgress"] == nil ||
-                               trashQueueStatus.(map[string]interface{})["Queued"] == nil ||
-                               trashQueueStatus.(map[string]interface{})["InProgress"] == nil {
-                               t.Fatalf("PullQueue and TrashQueue status not found")
-                       }
-
-                       time.Sleep(100 * time.Millisecond)
-               }
-       }
-}
-
-func TestRunDatamanagerWithBogusServer(t *testing.T) {
-       defer TearDownDataManagerTest(t)
-       SetupDataManagerTest(t)
-
-       arv.ApiServer = "bogus-server"
-
-       err := singlerun(arv)
-       if err == nil {
-               t.Fatalf("Expected error during singlerun with bogus server")
-       }
-}
-
-func TestRunDatamanagerAsNonAdminUser(t *testing.T) {
-       defer TearDownDataManagerTest(t)
-       SetupDataManagerTest(t)
-
-       arv.ApiToken = arvadostest.ActiveToken
-
-       err := singlerun(arv)
-       if err == nil {
-               t.Fatalf("Expected error during singlerun as non-admin user")
-       }
-}
-
-func TestPutAndGetBlocks_NoErrorDuringSingleRun(t *testing.T) {
-       testOldBlocksNotDeletedOnDataManagerError(t, "", "", false, false)
-}
-
-func TestPutAndGetBlocks_ErrorDuringGetCollectionsBadWriteTo(t *testing.T) {
-       badpath, err := arvadostest.CreateBadPath()
-       if err != nil {
-               t.Fatalf(err.Error())
-       }
-       defer func() {
-               err = arvadostest.DestroyBadPath(badpath)
-               if err != nil {
-                       t.Fatalf(err.Error())
-               }
-       }()
-       testOldBlocksNotDeletedOnDataManagerError(t, path.Join(badpath, "writetofile"), "", true, true)
-}
-
-func TestPutAndGetBlocks_ErrorDuringGetCollectionsBadHeapProfileFilename(t *testing.T) {
-       badpath, err := arvadostest.CreateBadPath()
-       if err != nil {
-               t.Fatalf(err.Error())
-       }
-       defer func() {
-               err = arvadostest.DestroyBadPath(badpath)
-               if err != nil {
-                       t.Fatalf(err.Error())
-               }
-       }()
-       testOldBlocksNotDeletedOnDataManagerError(t, "", path.Join(badpath, "heapprofilefile"), true, true)
-}
-
-// Create some blocks and backdate some of them.
-// Run datamanager while producing an error condition.
-// Verify that the blocks are hence not deleted.
-func testOldBlocksNotDeletedOnDataManagerError(t *testing.T, writeDataTo string, heapProfileFile string, expectError bool, expectOldBlocks bool) {
-       defer TearDownDataManagerTest(t)
-       SetupDataManagerTest(t)
-
-       // Put some blocks and backdate them.
-       var oldUnusedBlockLocators []string
-       oldUnusedBlockData := "this block will have older mtime"
-       for i := 0; i < 5; i++ {
-               oldUnusedBlockLocators = append(oldUnusedBlockLocators, putBlock(t, fmt.Sprintf("%s%d", oldUnusedBlockData, i)))
-       }
-       backdateBlocks(t, oldUnusedBlockLocators)
-
-       // Run data manager
-       summary.WriteDataTo = writeDataTo
-       collection.HeapProfileFilename = heapProfileFile
-
-       err := singlerun(arv)
-       if !expectError {
-               if err != nil {
-                       t.Fatalf("Got an error during datamanager singlerun: %v", err)
-               }
-       } else {
-               if err == nil {
-                       t.Fatalf("Expected error during datamanager singlerun")
-               }
-       }
-       waitUntilQueuesFinishWork(t)
-
-       // Get block indexes and verify that all backdated blocks are not/deleted as expected
-       if expectOldBlocks {
-               verifyBlocks(t, nil, oldUnusedBlockLocators, 2)
-       } else {
-               verifyBlocks(t, oldUnusedBlockLocators, nil, 2)
-       }
-}
-
-// Create a collection with multiple streams and blocks
-func createMultiStreamBlockCollection(t *testing.T, data string, numStreams, numBlocks int) (string, []string) {
-       defer switchToken(arvadostest.AdminToken)()
-
-       manifest := ""
-       locators := make(map[string]bool)
-       for s := 0; s < numStreams; s++ {
-               manifest += fmt.Sprintf("./stream%d ", s)
-               for b := 0; b < numBlocks; b++ {
-                       locator, _, err := keepClient.PutB([]byte(fmt.Sprintf("%s in stream %d and block %d", data, s, b)))
-                       if err != nil {
-                               t.Fatalf("Error creating block %d in stream %d: %v", b, s, err)
-                       }
-                       locators[strings.Split(locator, "+A")[0]] = true
-                       manifest += locator + " "
-               }
-               manifest += "0:1:dummyfile.txt\n"
-       }
-
-       collection := make(Dict)
-       err := arv.Create("collections",
-               arvadosclient.Dict{"collection": arvadosclient.Dict{"manifest_text": manifest}},
-               &collection)
-
-       if err != nil {
-               t.Fatalf("Error creating collection %v", err)
-       }
-
-       var locs []string
-       for k := range locators {
-               locs = append(locs, k)
-       }
-
-       return collection["uuid"].(string), locs
-}
-
-// Create collection with multiple streams and blocks; backdate the blocks and but do not delete the collection.
-// Also, create stray block and backdate it.
-// After datamanager run: expect blocks from the collection, but not the stray block.
-func TestManifestWithMultipleStreamsAndBlocks(t *testing.T) {
-       testManifestWithMultipleStreamsAndBlocks(t, 100, 10, "", false)
-}
-
-// Same test as TestManifestWithMultipleStreamsAndBlocks with an additional
-// keepstore of a service type other than "disk". Only the "disk" type services
-// will be indexed by datamanager and hence should work the same way.
-func TestManifestWithMultipleStreamsAndBlocks_WithOneUnsupportedKeepServer(t *testing.T) {
-       testManifestWithMultipleStreamsAndBlocks(t, 2, 2, "testblobstore", false)
-}
-
-// Test datamanager with dry-run. Expect no block to be deleted.
-func TestManifestWithMultipleStreamsAndBlocks_DryRun(t *testing.T) {
-       testManifestWithMultipleStreamsAndBlocks(t, 2, 2, "", true)
-}
-
-func testManifestWithMultipleStreamsAndBlocks(t *testing.T, numStreams, numBlocks int, createExtraKeepServerWithType string, isDryRun bool) {
-       defer TearDownDataManagerTest(t)
-       SetupDataManagerTest(t)
-
-       // create collection whose blocks will be backdated
-       collectionWithOldBlocks, oldBlocks := createMultiStreamBlockCollection(t, "old block", numStreams, numBlocks)
-       if collectionWithOldBlocks == "" {
-               t.Fatalf("Failed to create collection with %d blocks", numStreams*numBlocks)
-       }
-       if len(oldBlocks) != numStreams*numBlocks {
-               t.Fatalf("Not all blocks are created: expected %v, found %v", 1000, len(oldBlocks))
-       }
-
-       // create a stray block that will be backdated
-       strayOldBlock := putBlock(t, "this stray block is old")
-
-       expected := []string{strayOldBlock}
-       expected = append(expected, oldBlocks...)
-       verifyBlocks(t, nil, expected, 2)
-
-       // Backdate old blocks; but the collection still references these blocks
-       backdateBlocks(t, oldBlocks)
-
-       // also backdate the stray old block
-       backdateBlocks(t, []string{strayOldBlock})
-
-       // If requested, create an extra keepserver with the given type
-       // This should be ignored during indexing and hence not change the datamanager outcome
-       var extraKeepServerUUID string
-       if createExtraKeepServerWithType != "" {
-               extraKeepServerUUID = addExtraKeepServer(t, createExtraKeepServerWithType)
-               defer deleteExtraKeepServer(extraKeepServerUUID)
-       }
-
-       // run datamanager
-       dryRun = isDryRun
-       dataManagerSingleRun(t)
-
-       if dryRun {
-               // verify that all blocks, including strayOldBlock, are still to be found
-               verifyBlocks(t, nil, expected, 2)
-       } else {
-               // verify that strayOldBlock is not to be found, but the collections blocks are still there
-               verifyBlocks(t, []string{strayOldBlock}, oldBlocks, 2)
-       }
-}
-
-// Add one more keepstore with the given service type
-func addExtraKeepServer(t *testing.T, serviceType string) string {
-       defer switchToken(arvadostest.AdminToken)()
-
-       extraKeepService := make(arvadosclient.Dict)
-       err := arv.Create("keep_services",
-               arvadosclient.Dict{"keep_service": arvadosclient.Dict{
-                       "service_host":     "localhost",
-                       "service_port":     "21321",
-                       "service_ssl_flag": false,
-                       "service_type":     serviceType}},
-               &extraKeepService)
-       if err != nil {
-               t.Fatal(err)
-       }
-
-       return extraKeepService["uuid"].(string)
-}
-
-func deleteExtraKeepServer(uuid string) {
-       defer switchToken(arvadostest.AdminToken)()
-       arv.Delete("keep_services", uuid, nil, nil)
-}
diff --git a/services/datamanager/experimental/datamanager.py b/services/datamanager/experimental/datamanager.py
deleted file mode 100755 (executable)
index 8207bdc..0000000
+++ /dev/null
@@ -1,887 +0,0 @@
-#! /usr/bin/env python
-
-import arvados
-
-import argparse
-import cgi
-import csv
-import json
-import logging
-import math
-import pprint
-import re
-import threading
-import urllib2
-
-from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
-from collections import defaultdict, Counter
-from functools import partial
-from operator import itemgetter
-from SocketServer import ThreadingMixIn
-
-arv = arvados.api('v1')
-
-# Adapted from http://stackoverflow.com/questions/4180980/formatting-data-quantity-capacity-as-string
-byteunits = ('B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB')
-def fileSizeFormat(value):
-  exponent = 0 if value == 0 else int(math.log(value, 1024))
-  return "%7.2f %-3s" % (float(value) / pow(1024, exponent),
-                         byteunits[exponent])
-
-def percentageFloor(x):
-  """ Returns a float which is the input rounded down to the neared 0.01.
-
-e.g. precentageFloor(0.941354) = 0.94
-"""
-  return math.floor(x*100) / 100.0
-
-
-def byteSizeFromValidUuid(valid_uuid):
-  return int(valid_uuid.split('+')[1])
-
-class maxdict(dict):
-  """A dictionary that holds the largest value entered for each key."""
-  def addValue(self, key, value):
-    dict.__setitem__(self, key, max(dict.get(self, key), value))
-  def addValues(self, kv_pairs):
-    for key,value in kv_pairs:
-      self.addValue(key, value)
-  def addDict(self, d):
-    self.addValues(d.items())
-
-class CollectionInfo:
-  DEFAULT_PERSISTER_REPLICATION_LEVEL=2
-  all_by_uuid = {}
-
-  def __init__(self, uuid):
-    if CollectionInfo.all_by_uuid.has_key(uuid):
-      raise ValueError('Collection for uuid "%s" already exists.' % uuid)
-    self.uuid = uuid
-    self.block_uuids = set()  # uuids of keep blocks in this collection
-    self.reader_uuids = set()  # uuids of users who can read this collection
-    self.persister_uuids = set()  # uuids of users who want this collection saved
-    # map from user uuid to replication level they desire
-    self.persister_replication = maxdict()
-
-    # The whole api response in case we need anything else later.
-    self.api_response = []
-    CollectionInfo.all_by_uuid[uuid] = self
-
-  def byteSize(self):
-    return sum(map(byteSizeFromValidUuid, self.block_uuids))
-
-  def __str__(self):
-    return ('CollectionInfo uuid: %s\n'
-            '               %d block(s) containing %s\n'
-            '               reader_uuids: %s\n'
-            '               persister_replication: %s' %
-            (self.uuid,
-             len(self.block_uuids),
-             fileSizeFormat(self.byteSize()),
-             pprint.pformat(self.reader_uuids, indent = 15),
-             pprint.pformat(self.persister_replication, indent = 15)))
-
-  @staticmethod
-  def get(uuid):
-    if not CollectionInfo.all_by_uuid.has_key(uuid):
-      CollectionInfo(uuid)
-    return CollectionInfo.all_by_uuid[uuid]
-
-
-def extractUuid(candidate):
-  """ Returns a canonical (hash+size) uuid from a valid uuid, or None if candidate is not a valid uuid."""
-  match = re.match('([0-9a-fA-F]{32}\+[0-9]+)(\+[^+]+)*$', candidate)
-  return match and match.group(1)
-
-def checkUserIsAdmin():
-  current_user = arv.users().current().execute()
-
-  if not current_user['is_admin']:
-    log.warning('Current user %s (%s - %s) does not have '
-                'admin access and will not see much of the data.',
-                current_user['full_name'],
-                current_user['email'],
-                current_user['uuid'])
-    if args.require_admin_user:
-      log.critical('Exiting, rerun with --no-require-admin-user '
-                   'if you wish to continue.')
-      exit(1)
-
-def buildCollectionsList():
-  if args.uuid:
-    return [args.uuid,]
-  else:
-    collections_list_response = arv.collections().list(limit=args.max_api_results).execute()
-
-    print ('Returned %d of %d collections.' %
-           (len(collections_list_response['items']),
-            collections_list_response['items_available']))
-
-    return [item['uuid'] for item in collections_list_response['items']]
-
-
-def readCollections(collection_uuids):
-  for collection_uuid in collection_uuids:
-    collection_block_uuids = set()
-    collection_response = arv.collections().get(uuid=collection_uuid).execute()
-    collection_info = CollectionInfo.get(collection_uuid)
-    collection_info.api_response = collection_response
-    manifest_lines = collection_response['manifest_text'].split('\n')
-
-    if args.verbose:
-      print 'Manifest text for %s:' % collection_uuid
-      pprint.pprint(manifest_lines)
-
-    for manifest_line in manifest_lines:
-      if manifest_line:
-        manifest_tokens = manifest_line.split(' ')
-        if args.verbose:
-          print 'manifest tokens: ' + pprint.pformat(manifest_tokens)
-        stream_name = manifest_tokens[0]
-
-        line_block_uuids = set(filter(None,
-                                      [extractUuid(candidate)
-                                       for candidate in manifest_tokens[1:]]))
-        collection_info.block_uuids.update(line_block_uuids)
-
-        # file_tokens = [token
-        #                for token in manifest_tokens[1:]
-        #                if extractUuid(token) is None]
-
-        # # Sort file tokens by start position in case they aren't already
-        # file_tokens.sort(key=lambda file_token: int(file_token.split(':')[0]))
-
-        # if args.verbose:
-        #   print 'line_block_uuids: ' + pprint.pformat(line_block_uuids)
-        #   print 'file_tokens: ' + pprint.pformat(file_tokens)
-
-
-def readLinks():
-  link_classes = set()
-
-  for collection_uuid,collection_info in CollectionInfo.all_by_uuid.items():
-    # TODO(misha): We may not be seing all the links, but since items
-    # available does not return an accurate number, I don't knos how
-    # to confirm that we saw all of them.
-    collection_links_response = arv.links().list(where={'head_uuid':collection_uuid}).execute()
-    link_classes.update([link['link_class'] for link in collection_links_response['items']])
-    for link in collection_links_response['items']:
-      if link['link_class'] == 'permission':
-        collection_info.reader_uuids.add(link['tail_uuid'])
-      elif link['link_class'] == 'resources':
-        replication_level = link['properties'].get(
-          'replication',
-          CollectionInfo.DEFAULT_PERSISTER_REPLICATION_LEVEL)
-        collection_info.persister_replication.addValue(
-          link['tail_uuid'],
-          replication_level)
-        collection_info.persister_uuids.add(link['tail_uuid'])
-
-  print 'Found the following link classes:'
-  pprint.pprint(link_classes)
-
-def reportMostPopularCollections():
-  most_popular_collections = sorted(
-    CollectionInfo.all_by_uuid.values(),
-    key=lambda info: len(info.reader_uuids) + 10 * len(info.persister_replication),
-    reverse=True)[:10]
-
-  print 'Most popular Collections:'
-  for collection_info in most_popular_collections:
-    print collection_info
-
-
-def buildMaps():
-  for collection_uuid,collection_info in CollectionInfo.all_by_uuid.items():
-    # Add the block holding the manifest itself for all calculations
-    block_uuids = collection_info.block_uuids.union([collection_uuid,])
-    for block_uuid in block_uuids:
-      block_to_collections[block_uuid].add(collection_uuid)
-      block_to_readers[block_uuid].update(collection_info.reader_uuids)
-      block_to_persisters[block_uuid].update(collection_info.persister_uuids)
-      block_to_persister_replication[block_uuid].addDict(
-        collection_info.persister_replication)
-    for reader_uuid in collection_info.reader_uuids:
-      reader_to_collections[reader_uuid].add(collection_uuid)
-      reader_to_blocks[reader_uuid].update(block_uuids)
-    for persister_uuid in collection_info.persister_uuids:
-      persister_to_collections[persister_uuid].add(collection_uuid)
-      persister_to_blocks[persister_uuid].update(block_uuids)
-
-
-def itemsByValueLength(original):
-  return sorted(original.items(),
-                key=lambda item:len(item[1]),
-                reverse=True)
-
-
-def reportBusiestUsers():
-  busiest_readers = itemsByValueLength(reader_to_collections)
-  print 'The busiest readers are:'
-  for reader,collections in busiest_readers:
-    print '%s reading %d collections.' % (reader, len(collections))
-  busiest_persisters = itemsByValueLength(persister_to_collections)
-  print 'The busiest persisters are:'
-  for persister,collections in busiest_persisters:
-    print '%s reading %d collections.' % (persister, len(collections))
-
-
-def blockDiskUsage(block_uuid):
-  """Returns the disk usage of a block given its uuid.
-
-  Will return 0 before reading the contents of the keep servers.
-  """
-  return byteSizeFromValidUuid(block_uuid) * block_to_replication[block_uuid]
-
-def blockPersistedUsage(user_uuid, block_uuid):
-  return (byteSizeFromValidUuid(block_uuid) *
-          block_to_persister_replication[block_uuid].get(user_uuid, 0))
-
-memo_computeWeightedReplicationCosts = {}
-def computeWeightedReplicationCosts(replication_levels):
-  """Computes the relative cost of varied replication levels.
-
-  replication_levels: a tuple of integers representing the desired
-  replication level. If n users want a replication level of x then x
-  should appear n times in replication_levels.
-
-  Returns a dictionary from replication level to cost.
-
-  The basic thinking is that the cost of replicating at level x should
-  be shared by everyone who wants replication of level x or higher.
-
-  For example, if we have two users who want 1 copy, one user who
-  wants 3 copies and two users who want 6 copies:
-  the input would be [1, 1, 3, 6, 6] (or any permutation)
-
-  The cost of the first copy is shared by all 5 users, so they each
-  pay 1 copy / 5 users = 0.2.
-  The cost of the second and third copies shared by 3 users, so they
-  each pay 2 copies / 3 users = 0.67 (plus the above costs)
-  The cost of the fourth, fifth and sixth copies is shared by two
-  users, so they each pay 3 copies / 2 users = 1.5 (plus the above costs)
-
-  Here are some other examples:
-  computeWeightedReplicationCosts([1,]) -> {1:1.0}
-  computeWeightedReplicationCosts([2,]) -> {2:2.0}
-  computeWeightedReplicationCosts([1,1]) -> {1:0.5}
-  computeWeightedReplicationCosts([2,2]) -> {1:1.0}
-  computeWeightedReplicationCosts([1,2]) -> {1:0.5,2:1.5}
-  computeWeightedReplicationCosts([1,3]) -> {1:0.5,2:2.5}
-  computeWeightedReplicationCosts([1,3,6,6,10]) -> {1:0.2,3:0.7,6:1.7,10:5.7}
-  """
-  replication_level_counts = sorted(Counter(replication_levels).items())
-
-  memo_key = str(replication_level_counts)
-
-  if not memo_key in memo_computeWeightedReplicationCosts:
-    last_level = 0
-    current_cost = 0
-    total_interested = float(sum(map(itemgetter(1), replication_level_counts)))
-    cost_for_level = {}
-    for replication_level, count in replication_level_counts:
-      copies_added = replication_level - last_level
-      # compute marginal cost from last level and add it to the last cost
-      current_cost += copies_added / total_interested
-      cost_for_level[replication_level] = current_cost
-      # update invariants
-      last_level = replication_level
-      total_interested -= count
-    memo_computeWeightedReplicationCosts[memo_key] = cost_for_level
-
-  return memo_computeWeightedReplicationCosts[memo_key]
-
-def blockPersistedWeightedUsage(user_uuid, block_uuid):
-  persister_replication_for_block = block_to_persister_replication[block_uuid]
-  user_replication = persister_replication_for_block[user_uuid]
-  return (
-    byteSizeFromValidUuid(block_uuid) *
-    computeWeightedReplicationCosts(
-      persister_replication_for_block.values())[user_replication])
-
-
-def computeUserStorageUsage():
-  for user, blocks in reader_to_blocks.items():
-    user_to_usage[user][UNWEIGHTED_READ_SIZE_COL] = sum(map(
-        byteSizeFromValidUuid,
-        blocks))
-    user_to_usage[user][WEIGHTED_READ_SIZE_COL] = sum(map(
-        lambda block_uuid:(float(byteSizeFromValidUuid(block_uuid))/
-                                 len(block_to_readers[block_uuid])),
-        blocks))
-  for user, blocks in persister_to_blocks.items():
-    user_to_usage[user][UNWEIGHTED_PERSIST_SIZE_COL] = sum(map(
-        partial(blockPersistedUsage, user),
-        blocks))
-    user_to_usage[user][WEIGHTED_PERSIST_SIZE_COL] = sum(map(
-        partial(blockPersistedWeightedUsage, user),
-        blocks))
-
-def printUserStorageUsage():
-  print ('user: unweighted readable block size, weighted readable block size, '
-         'unweighted persisted block size, weighted persisted block size:')
-  for user, usage in user_to_usage.items():
-    print ('%s: %s %s %s %s' %
-           (user,
-            fileSizeFormat(usage[UNWEIGHTED_READ_SIZE_COL]),
-            fileSizeFormat(usage[WEIGHTED_READ_SIZE_COL]),
-            fileSizeFormat(usage[UNWEIGHTED_PERSIST_SIZE_COL]),
-            fileSizeFormat(usage[WEIGHTED_PERSIST_SIZE_COL])))
-
-def logUserStorageUsage():
-  for user, usage in user_to_usage.items():
-    body = {}
-    # user could actually represent a user or a group. We don't set
-    # the object_type field since we don't know which we have.
-    body['object_uuid'] = user
-    body['event_type'] = args.user_storage_log_event_type
-    properties = {}
-    properties['read_collections_total_bytes'] = usage[UNWEIGHTED_READ_SIZE_COL]
-    properties['read_collections_weighted_bytes'] = (
-      usage[WEIGHTED_READ_SIZE_COL])
-    properties['persisted_collections_total_bytes'] = (
-      usage[UNWEIGHTED_PERSIST_SIZE_COL])
-    properties['persisted_collections_weighted_bytes'] = (
-      usage[WEIGHTED_PERSIST_SIZE_COL])
-    body['properties'] = properties
-    # TODO(misha): Confirm that this will throw an exception if it
-    # fails to create the log entry.
-    arv.logs().create(body=body).execute()
-
-def getKeepServers():
-  response = arv.keep_disks().list().execute()
-  return [[keep_server['service_host'], keep_server['service_port']]
-          for keep_server in response['items']]
-
-
-def getKeepBlocks(keep_servers):
-  blocks = []
-  for host,port in keep_servers:
-    response = urllib2.urlopen('http://%s:%d/index' % (host, port))
-    server_blocks = [line.split(' ')
-                     for line in response.read().split('\n')
-                     if line]
-    server_blocks = [(block_id, int(mtime))
-                     for block_id, mtime in server_blocks]
-    blocks.append(server_blocks)
-  return blocks
-
-def getKeepStats(keep_servers):
-  MOUNT_COLUMN = 5
-  TOTAL_COLUMN = 1
-  FREE_COLUMN = 3
-  DISK_BLOCK_SIZE = 1024
-  stats = []
-  for host,port in keep_servers:
-    response = urllib2.urlopen('http://%s:%d/status.json' % (host, port))
-
-    parsed_json = json.load(response)
-    df_entries = [line.split()
-                  for line in parsed_json['df'].split('\n')
-                  if line]
-    keep_volumes = [columns
-                    for columns in df_entries
-                    if 'keep' in columns[MOUNT_COLUMN]]
-    total_space = DISK_BLOCK_SIZE*sum(map(int,map(itemgetter(TOTAL_COLUMN),
-                                                  keep_volumes)))
-    free_space =  DISK_BLOCK_SIZE*sum(map(int,map(itemgetter(FREE_COLUMN),
-                                                  keep_volumes)))
-    stats.append([total_space, free_space])
-  return stats
-
-
-def computeReplication(keep_blocks):
-  for server_blocks in keep_blocks:
-    for block_uuid, _ in server_blocks:
-      block_to_replication[block_uuid] += 1
-  log.debug('Seeing the following replication levels among blocks: %s',
-            str(set(block_to_replication.values())))
-
-
-def computeGarbageCollectionCandidates():
-  for server_blocks in keep_blocks:
-    block_to_latest_mtime.addValues(server_blocks)
-  empty_set = set()
-  garbage_collection_priority = sorted(
-    [(block,mtime)
-     for block,mtime in block_to_latest_mtime.items()
-     if len(block_to_persisters.get(block,empty_set)) == 0],
-    key = itemgetter(1))
-  global garbage_collection_report
-  garbage_collection_report = []
-  cumulative_disk_size = 0
-  for block,mtime in garbage_collection_priority:
-    disk_size = blockDiskUsage(block)
-    cumulative_disk_size += disk_size
-    garbage_collection_report.append(
-      (block,
-       mtime,
-       disk_size,
-       cumulative_disk_size,
-       float(free_keep_space + cumulative_disk_size)/total_keep_space))
-
-  print 'The oldest Garbage Collection Candidates: '
-  pprint.pprint(garbage_collection_report[:20])
-
-
-def outputGarbageCollectionReport(filename):
-  with open(filename, 'wb') as csvfile:
-    gcwriter = csv.writer(csvfile)
-    gcwriter.writerow(['block uuid', 'latest mtime', 'disk size',
-                       'cumulative size', 'disk free'])
-    for line in garbage_collection_report:
-      gcwriter.writerow(line)
-
-def computeGarbageCollectionHistogram():
-  # TODO(misha): Modify this to allow users to specify the number of
-  # histogram buckets through a flag.
-  histogram = []
-  last_percentage = -1
-  for _,mtime,_,_,disk_free in garbage_collection_report:
-    curr_percentage = percentageFloor(disk_free)
-    if curr_percentage > last_percentage:
-      histogram.append( (mtime, curr_percentage) )
-    last_percentage = curr_percentage
-
-  log.info('Garbage collection histogram is: %s', histogram)
-
-  return histogram
-
-
-def logGarbageCollectionHistogram():
-  body = {}
-  # TODO(misha): Decide whether we should specify an object_uuid in
-  # the body and if so, which uuid to use.
-  body['event_type'] = args.block_age_free_space_histogram_log_event_type
-  properties = {}
-  properties['histogram'] = garbage_collection_histogram
-  body['properties'] = properties
-  # TODO(misha): Confirm that this will throw an exception if it
-  # fails to create the log entry.
-  arv.logs().create(body=body).execute()
-
-
-def detectReplicationProblems():
-  blocks_not_in_any_collections.update(
-    set(block_to_replication.keys()).difference(block_to_collections.keys()))
-  underreplicated_persisted_blocks.update(
-    [uuid
-     for uuid, persister_replication in block_to_persister_replication.items()
-     if len(persister_replication) > 0 and
-     block_to_replication[uuid] < max(persister_replication.values())])
-  overreplicated_persisted_blocks.update(
-    [uuid
-     for uuid, persister_replication in block_to_persister_replication.items()
-     if len(persister_replication) > 0 and
-     block_to_replication[uuid] > max(persister_replication.values())])
-
-  log.info('Found %d blocks not in any collections, e.g. %s...',
-           len(blocks_not_in_any_collections),
-           ','.join(list(blocks_not_in_any_collections)[:5]))
-  log.info('Found %d underreplicated blocks, e.g. %s...',
-           len(underreplicated_persisted_blocks),
-           ','.join(list(underreplicated_persisted_blocks)[:5]))
-  log.info('Found %d overreplicated blocks, e.g. %s...',
-           len(overreplicated_persisted_blocks),
-           ','.join(list(overreplicated_persisted_blocks)[:5]))
-
-  # TODO:
-  #  Read blocks sorted by mtime
-  #  Cache window vs % free space
-  #  Collections which candidates will appear in
-  #  Youngest underreplicated read blocks that appear in collections.
-  #  Report Collections that have blocks which are missing from (or
-  #   underreplicated in) keep.
-
-
-# This is the main flow here
-
-parser = argparse.ArgumentParser(description='Report on keep disks.')
-"""The command line argument parser we use.
-
-We only use it in the __main__ block, but leave it outside the block
-in case another package wants to use it or customize it by specifying
-it as a parent to their commandline parser.
-"""
-parser.add_argument('-m',
-                    '--max-api-results',
-                    type=int,
-                    default=5000,
-                    help=('The max results to get at once.'))
-parser.add_argument('-p',
-                    '--port',
-                    type=int,
-                    default=9090,
-                    help=('The port number to serve on. 0 means no server.'))
-parser.add_argument('-v',
-                    '--verbose',
-                    help='increase output verbosity',
-                    action='store_true')
-parser.add_argument('-u',
-                    '--uuid',
-                    help='uuid of specific collection to process')
-parser.add_argument('--require-admin-user',
-                    action='store_true',
-                    default=True,
-                    help='Fail if the user is not an admin [default]')
-parser.add_argument('--no-require-admin-user',
-                    dest='require_admin_user',
-                    action='store_false',
-                    help=('Allow users without admin permissions with '
-                          'only a warning.'))
-parser.add_argument('--log-to-workbench',
-                    action='store_true',
-                    default=False,
-                    help='Log findings to workbench')
-parser.add_argument('--no-log-to-workbench',
-                    dest='log_to_workbench',
-                    action='store_false',
-                    help='Don\'t log findings to workbench [default]')
-parser.add_argument('--user-storage-log-event-type',
-                    default='user-storage-report',
-                    help=('The event type to set when logging user '
-                          'storage usage to workbench.'))
-parser.add_argument('--block-age-free-space-histogram-log-event-type',
-                    default='block-age-free-space-histogram',
-                    help=('The event type to set when logging user '
-                          'storage usage to workbench.'))
-parser.add_argument('--garbage-collection-file',
-                    default='',
-                    help=('The file to write a garbage collection report, or '
-                          'leave empty for no report.'))
-
-args = None
-
-# TODO(misha): Think about moving some of this to the __main__ block.
-log = logging.getLogger('arvados.services.datamanager')
-stderr_handler = logging.StreamHandler()
-log.setLevel(logging.INFO)
-stderr_handler.setFormatter(
-  logging.Formatter('%(asctime)-15s %(levelname)-8s %(message)s'))
-log.addHandler(stderr_handler)
-
-# Global Data - don't try this at home
-collection_uuids = []
-
-# These maps all map from uuids to a set of uuids
-block_to_collections = defaultdict(set)  # keep blocks
-reader_to_collections = defaultdict(set)  # collection(s) for which the user has read access
-persister_to_collections = defaultdict(set)  # collection(s) which the user has persisted
-block_to_readers = defaultdict(set)
-block_to_persisters = defaultdict(set)
-block_to_persister_replication = defaultdict(maxdict)
-reader_to_blocks = defaultdict(set)
-persister_to_blocks = defaultdict(set)
-
-UNWEIGHTED_READ_SIZE_COL = 0
-WEIGHTED_READ_SIZE_COL = 1
-UNWEIGHTED_PERSIST_SIZE_COL = 2
-WEIGHTED_PERSIST_SIZE_COL = 3
-NUM_COLS = 4
-user_to_usage = defaultdict(lambda : [0,]*NUM_COLS)
-
-keep_servers = []
-keep_blocks = []
-keep_stats = []
-total_keep_space = 0
-free_keep_space =  0
-
-block_to_replication = defaultdict(lambda: 0)
-block_to_latest_mtime = maxdict()
-
-garbage_collection_report = []
-"""A list of non-persisted blocks, sorted by increasing mtime
-
-Each entry is of the form (block uuid, latest mtime, disk size,
-cumulative size)
-
-* block uuid: The id of the block we want to delete
-* latest mtime: The latest mtime of the block across all keep servers.
-* disk size: The total disk space used by this block (block size
-multiplied by current replication level)
-* cumulative disk size: The sum of this block's disk size and all the
-blocks listed above it
-* disk free: The proportion of our disk space that would be free if we
-deleted this block and all the above. So this is (free disk space +
-cumulative disk size) / total disk capacity
-"""
-
-garbage_collection_histogram = []
-""" Shows the tradeoff of keep block age vs keep disk free space.
-
-Each entry is of the form (mtime, Disk Proportion).
-
-An entry of the form (1388747781, 0.52) means that if we deleted the
-oldest non-presisted blocks until we had 52% of the disk free, then
-all blocks with an mtime greater than 1388747781 would be preserved.
-"""
-
-# Stuff to report on
-blocks_not_in_any_collections = set()
-underreplicated_persisted_blocks = set()
-overreplicated_persisted_blocks = set()
-
-all_data_loaded = False
-
-def loadAllData():
-  checkUserIsAdmin()
-
-  log.info('Building Collection List')
-  global collection_uuids
-  collection_uuids = filter(None, [extractUuid(candidate)
-                                   for candidate in buildCollectionsList()])
-
-  log.info('Reading Collections')
-  readCollections(collection_uuids)
-
-  if args.verbose:
-    pprint.pprint(CollectionInfo.all_by_uuid)
-
-  log.info('Reading Links')
-  readLinks()
-
-  reportMostPopularCollections()
-
-  log.info('Building Maps')
-  buildMaps()
-
-  reportBusiestUsers()
-
-  log.info('Getting Keep Servers')
-  global keep_servers
-  keep_servers = getKeepServers()
-
-  print keep_servers
-
-  log.info('Getting Blocks from each Keep Server.')
-  global keep_blocks
-  keep_blocks = getKeepBlocks(keep_servers)
-
-  log.info('Getting Stats from each Keep Server.')
-  global keep_stats, total_keep_space, free_keep_space
-  keep_stats = getKeepStats(keep_servers)
-
-  total_keep_space = sum(map(itemgetter(0), keep_stats))
-  free_keep_space = sum(map(itemgetter(1), keep_stats))
-
-  # TODO(misha): Delete this hack when the keep servers are fixed!
-  # This hack deals with the fact that keep servers report each other's disks.
-  total_keep_space /= len(keep_stats)
-  free_keep_space /= len(keep_stats)
-
-  log.info('Total disk space: %s, Free disk space: %s (%d%%).' %
-           (fileSizeFormat(total_keep_space),
-            fileSizeFormat(free_keep_space),
-            100*free_keep_space/total_keep_space))
-
-  computeReplication(keep_blocks)
-
-  log.info('average replication level is %f',
-           (float(sum(block_to_replication.values())) /
-            len(block_to_replication)))
-
-  computeGarbageCollectionCandidates()
-
-  if args.garbage_collection_file:
-    log.info('Writing garbage Collection report to %s',
-             args.garbage_collection_file)
-    outputGarbageCollectionReport(args.garbage_collection_file)
-
-  global garbage_collection_histogram
-  garbage_collection_histogram = computeGarbageCollectionHistogram()
-
-  if args.log_to_workbench:
-    logGarbageCollectionHistogram()
-
-  detectReplicationProblems()
-
-  computeUserStorageUsage()
-  printUserStorageUsage()
-  if args.log_to_workbench:
-    logUserStorageUsage()
-
-  global all_data_loaded
-  all_data_loaded = True
-
-
-class DataManagerHandler(BaseHTTPRequestHandler):
-  USER_PATH = 'user'
-  COLLECTION_PATH = 'collection'
-  BLOCK_PATH = 'block'
-
-  def userLink(self, uuid):
-    return ('<A HREF="/%(path)s/%(uuid)s">%(uuid)s</A>' %
-            {'uuid': uuid,
-             'path': DataManagerHandler.USER_PATH})
-
-  def collectionLink(self, uuid):
-    return ('<A HREF="/%(path)s/%(uuid)s">%(uuid)s</A>' %
-            {'uuid': uuid,
-             'path': DataManagerHandler.COLLECTION_PATH})
-
-  def blockLink(self, uuid):
-    return ('<A HREF="/%(path)s/%(uuid)s">%(uuid)s</A>' %
-            {'uuid': uuid,
-             'path': DataManagerHandler.BLOCK_PATH})
-
-  def writeTop(self, title):
-    self.wfile.write('<HTML><HEAD><TITLE>%s</TITLE></HEAD>\n<BODY>' % title)
-
-  def writeBottom(self):
-    self.wfile.write('</BODY></HTML>\n')
-
-  def writeHomePage(self):
-    self.send_response(200)
-    self.end_headers()
-    self.writeTop('Home')
-    self.wfile.write('<TABLE>')
-    self.wfile.write('<TR><TH>user'
-                     '<TH>unweighted readable block size'
-                     '<TH>weighted readable block size'
-                     '<TH>unweighted persisted block size'
-                     '<TH>weighted persisted block size</TR>\n')
-    for user, usage in user_to_usage.items():
-      self.wfile.write('<TR><TD>%s<TD>%s<TD>%s<TD>%s<TD>%s</TR>\n' %
-                       (self.userLink(user),
-                        fileSizeFormat(usage[UNWEIGHTED_READ_SIZE_COL]),
-                        fileSizeFormat(usage[WEIGHTED_READ_SIZE_COL]),
-                        fileSizeFormat(usage[UNWEIGHTED_PERSIST_SIZE_COL]),
-                        fileSizeFormat(usage[WEIGHTED_PERSIST_SIZE_COL])))
-    self.wfile.write('</TABLE>\n')
-    self.writeBottom()
-
-  def userExists(self, uuid):
-    # Currently this will return false for a user who exists but
-    # doesn't appear on any manifests.
-    # TODO(misha): Figure out if we need to fix this.
-    return user_to_usage.has_key(uuid)
-
-  def writeUserPage(self, uuid):
-    if not self.userExists(uuid):
-      self.send_error(404,
-                      'User (%s) Not Found.' % cgi.escape(uuid, quote=False))
-    else:
-      # Here we assume that since a user exists, they don't need to be
-      # html escaped.
-      self.send_response(200)
-      self.end_headers()
-      self.writeTop('User %s' % uuid)
-      self.wfile.write('<TABLE>')
-      self.wfile.write('<TR><TH>user'
-                       '<TH>unweighted readable block size'
-                       '<TH>weighted readable block size'
-                       '<TH>unweighted persisted block size'
-                       '<TH>weighted persisted block size</TR>\n')
-      usage = user_to_usage[uuid]
-      self.wfile.write('<TR><TD>%s<TD>%s<TD>%s<TD>%s<TD>%s</TR>\n' %
-                       (self.userLink(uuid),
-                        fileSizeFormat(usage[UNWEIGHTED_READ_SIZE_COL]),
-                        fileSizeFormat(usage[WEIGHTED_READ_SIZE_COL]),
-                        fileSizeFormat(usage[UNWEIGHTED_PERSIST_SIZE_COL]),
-                        fileSizeFormat(usage[WEIGHTED_PERSIST_SIZE_COL])))
-      self.wfile.write('</TABLE>\n')
-      self.wfile.write('<P>Persisting Collections: %s\n' %
-                       ', '.join(map(self.collectionLink,
-                                     persister_to_collections[uuid])))
-      self.wfile.write('<P>Reading Collections: %s\n' %
-                       ', '.join(map(self.collectionLink,
-                                     reader_to_collections[uuid])))
-      self.writeBottom()
-
-  def collectionExists(self, uuid):
-    return CollectionInfo.all_by_uuid.has_key(uuid)
-
-  def writeCollectionPage(self, uuid):
-    if not self.collectionExists(uuid):
-      self.send_error(404,
-                      'Collection (%s) Not Found.' % cgi.escape(uuid, quote=False))
-    else:
-      collection = CollectionInfo.get(uuid)
-      # Here we assume that since a collection exists, its id doesn't
-      # need to be html escaped.
-      self.send_response(200)
-      self.end_headers()
-      self.writeTop('Collection %s' % uuid)
-      self.wfile.write('<H1>Collection %s</H1>\n' % uuid)
-      self.wfile.write('<P>Total size %s (not factoring in replication).\n' %
-                       fileSizeFormat(collection.byteSize()))
-      self.wfile.write('<P>Readers: %s\n' %
-                       ', '.join(map(self.userLink, collection.reader_uuids)))
-
-      if len(collection.persister_replication) == 0:
-        self.wfile.write('<P>No persisters\n')
-      else:
-        replication_to_users = defaultdict(set)
-        for user,replication in collection.persister_replication.items():
-          replication_to_users[replication].add(user)
-        replication_levels = sorted(replication_to_users.keys())
-
-        self.wfile.write('<P>%d persisters in %d replication level(s) maxing '
-                         'out at %dx replication:\n' %
-                         (len(collection.persister_replication),
-                          len(replication_levels),
-                          replication_levels[-1]))
-
-        # TODO(misha): This code is used twice, let's move it to a method.
-        self.wfile.write('<TABLE><TR><TH>%s</TR>\n' %
-                         '<TH>'.join(['Replication Level ' + str(x)
-                                      for x in replication_levels]))
-        self.wfile.write('<TR>\n')
-        for replication_level in replication_levels:
-          users = replication_to_users[replication_level]
-          self.wfile.write('<TD valign="top">%s\n' % '<BR>\n'.join(
-              map(self.userLink, users)))
-        self.wfile.write('</TR></TABLE>\n')
-
-      replication_to_blocks = defaultdict(set)
-      for block in collection.block_uuids:
-        replication_to_blocks[block_to_replication[block]].add(block)
-      replication_levels = sorted(replication_to_blocks.keys())
-      self.wfile.write('<P>%d blocks in %d replication level(s):\n' %
-                       (len(collection.block_uuids), len(replication_levels)))
-      self.wfile.write('<TABLE><TR><TH>%s</TR>\n' %
-                       '<TH>'.join(['Replication Level ' + str(x)
-                                    for x in replication_levels]))
-      self.wfile.write('<TR>\n')
-      for replication_level in replication_levels:
-        blocks = replication_to_blocks[replication_level]
-        self.wfile.write('<TD valign="top">%s\n' % '<BR>\n'.join(blocks))
-      self.wfile.write('</TR></TABLE>\n')
-
-
-  def do_GET(self):
-    if not all_data_loaded:
-      self.send_error(503,
-                      'Sorry, but I am still loading all the data I need.')
-    else:
-      # Removing leading '/' and process request path
-      split_path = self.path[1:].split('/')
-      request_type = split_path[0]
-      log.debug('path (%s) split as %s with request_type %s' % (self.path,
-                                                                split_path,
-                                                                request_type))
-      if request_type == '':
-        self.writeHomePage()
-      elif request_type == DataManagerHandler.USER_PATH:
-        self.writeUserPage(split_path[1])
-      elif request_type == DataManagerHandler.COLLECTION_PATH:
-        self.writeCollectionPage(split_path[1])
-      else:
-        self.send_error(404, 'Unrecognized request path.')
-    return
-
-class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
-  """Handle requests in a separate thread."""
-
-
-if __name__ == '__main__':
-  args = parser.parse_args()
-
-  if args.port == 0:
-    loadAllData()
-  else:
-    loader = threading.Thread(target = loadAllData, name = 'loader')
-    loader.start()
-
-    server = ThreadedHTTPServer(('localhost', args.port), DataManagerHandler)
-    server.serve_forever()
diff --git a/services/datamanager/experimental/datamanager_test.py b/services/datamanager/experimental/datamanager_test.py
deleted file mode 100755 (executable)
index 0842c16..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-#! /usr/bin/env python
-
-import datamanager
-import unittest
-
-class TestComputeWeightedReplicationCosts(unittest.TestCase):
-  def test_obvious(self):
-    self.assertEqual(datamanager.computeWeightedReplicationCosts([1,]),
-                     {1:1.0})
-
-  def test_simple(self):
-    self.assertEqual(datamanager.computeWeightedReplicationCosts([2,]),
-                     {2:2.0})
-
-  def test_even_split(self):
-    self.assertEqual(datamanager.computeWeightedReplicationCosts([1,1]),
-                     {1:0.5})
-
-  def test_even_split_bigger(self):
-    self.assertEqual(datamanager.computeWeightedReplicationCosts([2,2]),
-                     {2:1.0})
-
-  def test_uneven_split(self):
-    self.assertEqual(datamanager.computeWeightedReplicationCosts([1,2]),
-                     {1:0.5, 2:1.5})
-
-  def test_uneven_split_bigger(self):
-    self.assertEqual(datamanager.computeWeightedReplicationCosts([1,3]),
-                     {1:0.5, 3:2.5})
-
-  def test_uneven_split_jumble(self):
-    self.assertEqual(datamanager.computeWeightedReplicationCosts([1,3,6,6,10]),
-                     {1:0.2, 3:0.7, 6:1.7, 10:5.7})
-
-  def test_documentation_example(self):
-    self.assertEqual(datamanager.computeWeightedReplicationCosts([1,1,3,6,6]),
-                     {1:0.2, 3: 0.2 + 2.0 / 3, 6: 0.2 + 2.0 / 3 + 1.5})
-
-
-if __name__ == '__main__':
-  unittest.main()
diff --git a/services/datamanager/keep/keep.go b/services/datamanager/keep/keep.go
deleted file mode 100644 (file)
index 39d2d5b..0000000
+++ /dev/null
@@ -1,551 +0,0 @@
-/* Deals with getting Keep Server blocks from API Server and Keep Servers. */
-
-package keep
-
-import (
-       "bufio"
-       "encoding/json"
-       "errors"
-       "flag"
-       "fmt"
-       "git.curoverse.com/arvados.git/sdk/go/arvadosclient"
-       "git.curoverse.com/arvados.git/sdk/go/blockdigest"
-       "git.curoverse.com/arvados.git/sdk/go/keepclient"
-       "git.curoverse.com/arvados.git/sdk/go/logger"
-       "io"
-       "io/ioutil"
-       "log"
-       "net/http"
-       "strconv"
-       "strings"
-       "time"
-)
-
-// ServerAddress struct
-type ServerAddress struct {
-       SSL         bool   `json:"service_ssl_flag"`
-       Host        string `json:"service_host"`
-       Port        int    `json:"service_port"`
-       UUID        string `json:"uuid"`
-       ServiceType string `json:"service_type"`
-}
-
-// BlockInfo is info about a particular block returned by the server
-type BlockInfo struct {
-       Digest blockdigest.DigestWithSize
-       Mtime  int64 // TODO(misha): Replace this with a timestamp.
-}
-
-// BlockServerInfo is info about a specified block given by a server
-type BlockServerInfo struct {
-       ServerIndex int
-       Mtime       int64 // TODO(misha): Replace this with a timestamp.
-}
-
-// ServerContents struct
-type ServerContents struct {
-       BlockDigestToInfo map[blockdigest.DigestWithSize]BlockInfo
-}
-
-// ServerResponse struct
-type ServerResponse struct {
-       Address  ServerAddress
-       Contents ServerContents
-       Err      error
-}
-
-// ReadServers struct
-type ReadServers struct {
-       ReadAllServers           bool
-       KeepServerIndexToAddress []ServerAddress
-       KeepServerAddressToIndex map[ServerAddress]int
-       ServerToContents         map[ServerAddress]ServerContents
-       BlockToServers           map[blockdigest.DigestWithSize][]BlockServerInfo
-       BlockReplicationCounts   map[int]int
-}
-
-// GetKeepServersParams struct
-type GetKeepServersParams struct {
-       Client *arvadosclient.ArvadosClient
-       Logger *logger.Logger
-       Limit  int
-}
-
-// ServiceList consists of the addresses of all the available kee servers
-type ServiceList struct {
-       ItemsAvailable int             `json:"items_available"`
-       KeepServers    []ServerAddress `json:"items"`
-}
-
-var serviceType string
-
-func init() {
-       flag.StringVar(&serviceType,
-               "service-type",
-               "disk",
-               "Operate only on keep_services with the specified service_type, ignoring all others.")
-}
-
-// String
-// TODO(misha): Change this to include the UUID as well.
-func (s ServerAddress) String() string {
-       return s.URL()
-}
-
-// URL of the keep server
-func (s ServerAddress) URL() string {
-       if s.SSL {
-               return fmt.Sprintf("https://%s:%d", s.Host, s.Port)
-       }
-       return fmt.Sprintf("http://%s:%d", s.Host, s.Port)
-}
-
-// GetKeepServersAndSummarize gets keep servers from api
-func GetKeepServersAndSummarize(params GetKeepServersParams) (results ReadServers, err error) {
-       results, err = GetKeepServers(params)
-       if err != nil {
-               return
-       }
-       log.Printf("Returned %d keep disks", len(results.ServerToContents))
-
-       results.Summarize(params.Logger)
-       log.Printf("Replication level distribution: %v",
-               results.BlockReplicationCounts)
-
-       return
-}
-
-// GetKeepServers from api server
-func GetKeepServers(params GetKeepServersParams) (results ReadServers, err error) {
-       sdkParams := arvadosclient.Dict{
-               "filters": [][]string{{"service_type", "!=", "proxy"}},
-       }
-       if params.Limit > 0 {
-               sdkParams["limit"] = params.Limit
-       }
-
-       var sdkResponse ServiceList
-       err = params.Client.List("keep_services", sdkParams, &sdkResponse)
-
-       if err != nil {
-               return
-       }
-
-       var keepServers []ServerAddress
-       for _, server := range sdkResponse.KeepServers {
-               if server.ServiceType == serviceType {
-                       keepServers = append(keepServers, server)
-               } else {
-                       log.Printf("Skipping keep_service %q because its service_type %q does not match -service-type=%q", server, server.ServiceType, serviceType)
-               }
-       }
-
-       if len(keepServers) == 0 {
-               return results, fmt.Errorf("Found no keepservices with the service type %v", serviceType)
-       }
-
-       if params.Logger != nil {
-               params.Logger.Update(func(p map[string]interface{}, e map[string]interface{}) {
-                       keepInfo := logger.GetOrCreateMap(p, "keep_info")
-                       keepInfo["num_keep_servers_available"] = sdkResponse.ItemsAvailable
-                       keepInfo["num_keep_servers_received"] = len(sdkResponse.KeepServers)
-                       keepInfo["keep_servers"] = sdkResponse.KeepServers
-                       keepInfo["indexable_keep_servers"] = keepServers
-               })
-       }
-
-       log.Printf("Received keep services list: %+v", sdkResponse)
-
-       if len(sdkResponse.KeepServers) < sdkResponse.ItemsAvailable {
-               return results, fmt.Errorf("Did not receive all available keep servers: %+v", sdkResponse)
-       }
-
-       results.KeepServerIndexToAddress = keepServers
-       results.KeepServerAddressToIndex = make(map[ServerAddress]int)
-       for i, address := range results.KeepServerIndexToAddress {
-               results.KeepServerAddressToIndex[address] = i
-       }
-
-       log.Printf("Got Server Addresses: %v", results)
-
-       // Send off all the index requests concurrently
-       responseChan := make(chan ServerResponse)
-       for _, keepServer := range results.KeepServerIndexToAddress {
-               // The above keepsServer variable is reused for each iteration, so
-               // it would be shared across all goroutines. This would result in
-               // us querying one server n times instead of n different servers
-               // as we intended. To avoid this we add it as an explicit
-               // parameter which gets copied. This bug and solution is described
-               // in https://golang.org/doc/effective_go.html#channels
-               go func(keepServer ServerAddress) {
-                       responseChan <- GetServerContents(params.Logger,
-                               keepServer,
-                               params.Client)
-               }(keepServer)
-       }
-
-       results.ServerToContents = make(map[ServerAddress]ServerContents)
-       results.BlockToServers = make(map[blockdigest.DigestWithSize][]BlockServerInfo)
-
-       // Read all the responses
-       for i := range results.KeepServerIndexToAddress {
-               _ = i // Here to prevent go from complaining.
-               response := <-responseChan
-
-               // Check if there were any errors during GetServerContents
-               if response.Err != nil {
-                       return results, response.Err
-               }
-
-               log.Printf("Received channel response from %v containing %d files",
-                       response.Address,
-                       len(response.Contents.BlockDigestToInfo))
-               results.ServerToContents[response.Address] = response.Contents
-               serverIndex := results.KeepServerAddressToIndex[response.Address]
-               for _, blockInfo := range response.Contents.BlockDigestToInfo {
-                       results.BlockToServers[blockInfo.Digest] = append(
-                               results.BlockToServers[blockInfo.Digest],
-                               BlockServerInfo{ServerIndex: serverIndex,
-                                       Mtime: blockInfo.Mtime})
-               }
-       }
-       return
-}
-
-// GetServerContents of the keep server
-func GetServerContents(arvLogger *logger.Logger,
-       keepServer ServerAddress,
-       arv *arvadosclient.ArvadosClient) (response ServerResponse) {
-
-       err := GetServerStatus(arvLogger, keepServer, arv)
-       if err != nil {
-               response.Err = err
-               return
-       }
-
-       req, err := CreateIndexRequest(arvLogger, keepServer, arv)
-       if err != nil {
-               response.Err = err
-               return
-       }
-
-       resp, err := arv.Client.Do(req)
-       if err != nil {
-               response.Err = err
-               return
-       }
-
-       response, err = ReadServerResponse(arvLogger, keepServer, resp)
-       if err != nil {
-               response.Err = err
-               return
-       }
-
-       return
-}
-
-// GetServerStatus get keep server status by invoking /status.json
-func GetServerStatus(arvLogger *logger.Logger,
-       keepServer ServerAddress,
-       arv *arvadosclient.ArvadosClient) error {
-       url := fmt.Sprintf("http://%s:%d/status.json",
-               keepServer.Host,
-               keepServer.Port)
-
-       if arvLogger != nil {
-               now := time.Now()
-               arvLogger.Update(func(p map[string]interface{}, e map[string]interface{}) {
-                       keepInfo := logger.GetOrCreateMap(p, "keep_info")
-                       serverInfo := make(map[string]interface{})
-                       serverInfo["status_request_sent_at"] = now
-                       serverInfo["host"] = keepServer.Host
-                       serverInfo["port"] = keepServer.Port
-
-                       keepInfo[keepServer.UUID] = serverInfo
-               })
-       }
-
-       resp, err := arv.Client.Get(url)
-       if err != nil {
-               return fmt.Errorf("Error getting keep status from %s: %v", url, err)
-       } else if resp.StatusCode != 200 {
-               return fmt.Errorf("Received error code %d in response to request "+
-                       "for %s status: %s",
-                       resp.StatusCode, url, resp.Status)
-       }
-
-       var keepStatus map[string]interface{}
-       decoder := json.NewDecoder(resp.Body)
-       decoder.UseNumber()
-       err = decoder.Decode(&keepStatus)
-       if err != nil {
-               return fmt.Errorf("Error decoding keep status from %s: %v", url, err)
-       }
-
-       if arvLogger != nil {
-               now := time.Now()
-               arvLogger.Update(func(p map[string]interface{}, e map[string]interface{}) {
-                       keepInfo := logger.GetOrCreateMap(p, "keep_info")
-                       serverInfo := keepInfo[keepServer.UUID].(map[string]interface{})
-                       serverInfo["status_response_processed_at"] = now
-                       serverInfo["status"] = keepStatus
-               })
-       }
-
-       return nil
-}
-
-// CreateIndexRequest to the keep server
-func CreateIndexRequest(arvLogger *logger.Logger,
-       keepServer ServerAddress,
-       arv *arvadosclient.ArvadosClient) (req *http.Request, err error) {
-       url := fmt.Sprintf("http://%s:%d/index", keepServer.Host, keepServer.Port)
-       log.Println("About to fetch keep server contents from " + url)
-
-       if arvLogger != nil {
-               now := time.Now()
-               arvLogger.Update(func(p map[string]interface{}, e map[string]interface{}) {
-                       keepInfo := logger.GetOrCreateMap(p, "keep_info")
-                       serverInfo := keepInfo[keepServer.UUID].(map[string]interface{})
-                       serverInfo["index_request_sent_at"] = now
-               })
-       }
-
-       req, err = http.NewRequest("GET", url, nil)
-       if err != nil {
-               return req, fmt.Errorf("Error building http request for %s: %v", url, err)
-       }
-
-       req.Header.Add("Authorization", "OAuth2 "+arv.ApiToken)
-       return req, err
-}
-
-// ReadServerResponse reads reasponse from keep server
-func ReadServerResponse(arvLogger *logger.Logger,
-       keepServer ServerAddress,
-       resp *http.Response) (response ServerResponse, err error) {
-
-       if resp.StatusCode != 200 {
-               return response, fmt.Errorf("Received error code %d in response to index request for %s: %s",
-                       resp.StatusCode, keepServer.String(), resp.Status)
-       }
-
-       if arvLogger != nil {
-               now := time.Now()
-               arvLogger.Update(func(p map[string]interface{}, e map[string]interface{}) {
-                       keepInfo := logger.GetOrCreateMap(p, "keep_info")
-                       serverInfo := keepInfo[keepServer.UUID].(map[string]interface{})
-                       serverInfo["index_response_received_at"] = now
-               })
-       }
-
-       response.Address = keepServer
-       response.Contents.BlockDigestToInfo =
-               make(map[blockdigest.DigestWithSize]BlockInfo)
-       reader := bufio.NewReader(resp.Body)
-       numLines, numDuplicates, numSizeDisagreements := 0, 0, 0
-       for {
-               numLines++
-               line, err := reader.ReadString('\n')
-               if err == io.EOF {
-                       return response, fmt.Errorf("Index from %s truncated at line %d",
-                               keepServer.String(), numLines)
-               } else if err != nil {
-                       return response, fmt.Errorf("Error reading index response from %s at line %d: %v",
-                               keepServer.String(), numLines, err)
-               }
-               if line == "\n" {
-                       if _, err := reader.Peek(1); err == nil {
-                               extra, _ := reader.ReadString('\n')
-                               return response, fmt.Errorf("Index from %s had trailing data at line %d after EOF marker: %s",
-                                       keepServer.String(), numLines+1, extra)
-                       } else if err != io.EOF {
-                               return response, fmt.Errorf("Index from %s had read error after EOF marker at line %d: %v",
-                                       keepServer.String(), numLines, err)
-                       }
-                       numLines--
-                       break
-               }
-               blockInfo, err := parseBlockInfoFromIndexLine(line)
-               if err != nil {
-                       return response, fmt.Errorf("Error parsing BlockInfo from index line "+
-                               "received from %s: %v",
-                               keepServer.String(),
-                               err)
-               }
-
-               if storedBlock, ok := response.Contents.BlockDigestToInfo[blockInfo.Digest]; ok {
-                       // This server returned multiple lines containing the same block digest.
-                       numDuplicates++
-                       // Keep the block that's newer.
-                       if storedBlock.Mtime < blockInfo.Mtime {
-                               response.Contents.BlockDigestToInfo[blockInfo.Digest] = blockInfo
-                       }
-               } else {
-                       response.Contents.BlockDigestToInfo[blockInfo.Digest] = blockInfo
-               }
-       }
-
-       log.Printf("%s index contained %d lines with %d duplicates with "+
-               "%d size disagreements",
-               keepServer.String(),
-               numLines,
-               numDuplicates,
-               numSizeDisagreements)
-
-       if arvLogger != nil {
-               now := time.Now()
-               arvLogger.Update(func(p map[string]interface{}, e map[string]interface{}) {
-                       keepInfo := logger.GetOrCreateMap(p, "keep_info")
-                       serverInfo := keepInfo[keepServer.UUID].(map[string]interface{})
-
-                       serverInfo["processing_finished_at"] = now
-                       serverInfo["lines_received"] = numLines
-                       serverInfo["duplicates_seen"] = numDuplicates
-                       serverInfo["size_disagreements_seen"] = numSizeDisagreements
-               })
-       }
-       resp.Body.Close()
-       return
-}
-
-func parseBlockInfoFromIndexLine(indexLine string) (blockInfo BlockInfo, err error) {
-       tokens := strings.Fields(indexLine)
-       if len(tokens) != 2 {
-               err = fmt.Errorf("Expected 2 tokens per line but received a "+
-                       "line containing %#q instead.",
-                       tokens)
-       }
-
-       var locator blockdigest.BlockLocator
-       if locator, err = blockdigest.ParseBlockLocator(tokens[0]); err != nil {
-               err = fmt.Errorf("%v Received error while parsing line \"%#q\"",
-                       err, indexLine)
-               return
-       }
-       if len(locator.Hints) > 0 {
-               err = fmt.Errorf("Block locator in index line should not contain hints "+
-                       "but it does: %#q",
-                       locator)
-               return
-       }
-
-       var ns int64
-       ns, err = strconv.ParseInt(tokens[1], 10, 64)
-       if err != nil {
-               return
-       }
-       if ns < 1e12 {
-               // An old version of keepstore is giving us timestamps
-               // in seconds instead of nanoseconds. (This threshold
-               // correctly handles all times between 1970-01-02 and
-               // 33658-09-27.)
-               ns = ns * 1e9
-       }
-       blockInfo.Mtime = ns
-       blockInfo.Digest = blockdigest.DigestWithSize{
-               Digest: locator.Digest,
-               Size:   uint32(locator.Size),
-       }
-       return
-}
-
-// Summarize results from keep server
-func (readServers *ReadServers) Summarize(arvLogger *logger.Logger) {
-       readServers.BlockReplicationCounts = make(map[int]int)
-       for _, infos := range readServers.BlockToServers {
-               replication := len(infos)
-               readServers.BlockReplicationCounts[replication]++
-       }
-
-       if arvLogger != nil {
-               arvLogger.Update(func(p map[string]interface{}, e map[string]interface{}) {
-                       keepInfo := logger.GetOrCreateMap(p, "keep_info")
-                       keepInfo["distinct_blocks_stored"] = len(readServers.BlockToServers)
-               })
-       }
-}
-
-// TrashRequest struct
-type TrashRequest struct {
-       Locator    string `json:"locator"`
-       BlockMtime int64  `json:"block_mtime"`
-}
-
-// TrashList is an array of TrashRequest objects
-type TrashList []TrashRequest
-
-// SendTrashLists to trash queue
-func SendTrashLists(arvLogger *logger.Logger, kc *keepclient.KeepClient, spl map[string]TrashList, dryRun bool) (errs []error) {
-       count := 0
-       barrier := make(chan error)
-
-       client := kc.Client
-
-       for url, v := range spl {
-               if arvLogger != nil {
-                       // We need a local variable because Update doesn't call our mutator func until later,
-                       // when our list variable might have been reused by the next loop iteration.
-                       url := url
-                       trashLen := len(v)
-                       arvLogger.Update(func(p map[string]interface{}, e map[string]interface{}) {
-                               trashListInfo := logger.GetOrCreateMap(p, "trash_list_len")
-                               trashListInfo[url] = trashLen
-                       })
-               }
-
-               if dryRun {
-                       log.Printf("dry run, not sending trash list to service %s with %d blocks", url, len(v))
-                       continue
-               }
-
-               count++
-               log.Printf("Sending trash list to %v", url)
-
-               go (func(url string, v TrashList) {
-                       pipeReader, pipeWriter := io.Pipe()
-                       go (func() {
-                               enc := json.NewEncoder(pipeWriter)
-                               enc.Encode(v)
-                               pipeWriter.Close()
-                       })()
-
-                       req, err := http.NewRequest("PUT", fmt.Sprintf("%s/trash", url), pipeReader)
-                       if err != nil {
-                               log.Printf("Error creating trash list request for %v error: %v", url, err.Error())
-                               barrier <- err
-                               return
-                       }
-
-                       req.Header.Add("Authorization", "OAuth2 "+kc.Arvados.ApiToken)
-
-                       // Make the request
-                       var resp *http.Response
-                       if resp, err = client.Do(req); err != nil {
-                               log.Printf("Error sending trash list to %v error: %v", url, err.Error())
-                               barrier <- err
-                               return
-                       }
-
-                       log.Printf("Sent trash list to %v: response was HTTP %v", url, resp.Status)
-
-                       io.Copy(ioutil.Discard, resp.Body)
-                       resp.Body.Close()
-
-                       if resp.StatusCode != 200 {
-                               barrier <- errors.New(fmt.Sprintf("Got HTTP code %v", resp.StatusCode))
-                       } else {
-                               barrier <- nil
-                       }
-               })(url, v)
-       }
-
-       for i := 0; i < count; i++ {
-               b := <-barrier
-               if b != nil {
-                       errs = append(errs, b)
-               }
-       }
-
-       return errs
-}
diff --git a/services/datamanager/keep/keep_test.go b/services/datamanager/keep/keep_test.go
deleted file mode 100644 (file)
index ca8797e..0000000
+++ /dev/null
@@ -1,278 +0,0 @@
-package keep
-
-import (
-       "encoding/json"
-       "fmt"
-       "net"
-       "net/http"
-       "net/http/httptest"
-       "net/url"
-       "strconv"
-       "strings"
-       "testing"
-
-       "git.curoverse.com/arvados.git/sdk/go/arvadosclient"
-       "git.curoverse.com/arvados.git/sdk/go/arvadostest"
-       "git.curoverse.com/arvados.git/sdk/go/blockdigest"
-       "git.curoverse.com/arvados.git/sdk/go/keepclient"
-
-       . "gopkg.in/check.v1"
-)
-
-// Gocheck boilerplate
-func Test(t *testing.T) {
-       TestingT(t)
-}
-
-type KeepSuite struct{}
-
-var _ = Suite(&KeepSuite{})
-
-type TestHandler struct {
-       request TrashList
-}
-
-func (ts *TestHandler) ServeHTTP(writer http.ResponseWriter, req *http.Request) {
-       r := json.NewDecoder(req.Body)
-       r.Decode(&ts.request)
-}
-
-func (s *KeepSuite) TestSendTrashLists(c *C) {
-       th := TestHandler{}
-       server := httptest.NewServer(&th)
-       defer server.Close()
-
-       tl := map[string]TrashList{
-               server.URL: {TrashRequest{"000000000000000000000000deadbeef", 99}}}
-
-       arv := &arvadosclient.ArvadosClient{ApiToken: "abc123"}
-       kc := keepclient.KeepClient{Arvados: arv, Client: &http.Client{}}
-       kc.SetServiceRoots(map[string]string{"xxxx": server.URL},
-               map[string]string{"xxxx": server.URL},
-               map[string]string{})
-
-       err := SendTrashLists(nil, &kc, tl, false)
-
-       c.Check(err, IsNil)
-
-       c.Check(th.request,
-               DeepEquals,
-               tl[server.URL])
-
-}
-
-type TestHandlerError struct {
-}
-
-func (tse *TestHandlerError) ServeHTTP(writer http.ResponseWriter, req *http.Request) {
-       http.Error(writer, "I'm a teapot", 418)
-}
-
-func sendTrashListError(c *C, server *httptest.Server) {
-       tl := map[string]TrashList{
-               server.URL: {TrashRequest{"000000000000000000000000deadbeef", 99}}}
-
-       arv := &arvadosclient.ArvadosClient{ApiToken: "abc123"}
-       kc := keepclient.KeepClient{Arvados: arv, Client: &http.Client{}}
-       kc.SetServiceRoots(map[string]string{"xxxx": server.URL},
-               map[string]string{"xxxx": server.URL},
-               map[string]string{})
-
-       err := SendTrashLists(nil, &kc, tl, false)
-
-       c.Check(err, NotNil)
-       c.Check(err[0], NotNil)
-}
-
-func (s *KeepSuite) TestSendTrashListErrorResponse(c *C) {
-       server := httptest.NewServer(&TestHandlerError{})
-       sendTrashListError(c, server)
-       defer server.Close()
-}
-
-func (s *KeepSuite) TestSendTrashListUnreachable(c *C) {
-       sendTrashListError(c, httptest.NewUnstartedServer(&TestHandler{}))
-}
-
-type APITestData struct {
-       numServers int
-       serverType string
-       statusCode int
-}
-
-func (s *KeepSuite) TestGetKeepServers_UnsupportedServiceType(c *C) {
-       testGetKeepServersFromAPI(c, APITestData{1, "notadisk", 200}, "Found no keepservices with the service type disk")
-}
-
-func (s *KeepSuite) TestGetKeepServers_ReceivedTooFewServers(c *C) {
-       testGetKeepServersFromAPI(c, APITestData{2, "disk", 200}, "Did not receive all available keep servers")
-}
-
-func (s *KeepSuite) TestGetKeepServers_ServerError(c *C) {
-       testGetKeepServersFromAPI(c, APITestData{-1, "disk", -1}, "arvados API server error")
-}
-
-func testGetKeepServersFromAPI(c *C, testData APITestData, expectedError string) {
-       keepServers := ServiceList{
-               ItemsAvailable: testData.numServers,
-               KeepServers: []ServerAddress{{
-                       SSL:         false,
-                       Host:        "example.com",
-                       Port:        12345,
-                       UUID:        "abcdefg",
-                       ServiceType: testData.serverType,
-               }},
-       }
-
-       ksJSON, _ := json.Marshal(keepServers)
-       apiStubResponses := make(map[string]arvadostest.StubResponse)
-       apiStubResponses["/arvados/v1/keep_services"] = arvadostest.StubResponse{testData.statusCode, string(ksJSON)}
-       apiStub := arvadostest.ServerStub{apiStubResponses}
-
-       api := httptest.NewServer(&apiStub)
-       defer api.Close()
-
-       arv := &arvadosclient.ArvadosClient{
-               Scheme:    "http",
-               ApiServer: api.URL[7:],
-               ApiToken:  "abc123",
-               Client:    &http.Client{Transport: &http.Transport{}},
-       }
-
-       kc := keepclient.KeepClient{Arvados: arv, Client: &http.Client{}}
-       kc.SetServiceRoots(map[string]string{"xxxx": "http://example.com:23456"},
-               map[string]string{"xxxx": "http://example.com:23456"},
-               map[string]string{})
-
-       params := GetKeepServersParams{
-               Client: arv,
-               Logger: nil,
-               Limit:  10,
-       }
-
-       _, err := GetKeepServersAndSummarize(params)
-       c.Assert(err, NotNil)
-       c.Assert(err, ErrorMatches, fmt.Sprintf(".*%s.*", expectedError))
-}
-
-type KeepServerTestData struct {
-       // handle /status.json
-       statusStatusCode int
-
-       // handle /index
-       indexStatusCode   int
-       indexResponseBody string
-
-       // expected error, if any
-       expectedError string
-}
-
-func (s *KeepSuite) TestGetKeepServers_ErrorGettingKeepServerStatus(c *C) {
-       testGetKeepServersAndSummarize(c, KeepServerTestData{500, 200, "ok",
-               ".*http://.* 500 Internal Server Error"})
-}
-
-func (s *KeepSuite) TestGetKeepServers_GettingIndex(c *C) {
-       testGetKeepServersAndSummarize(c, KeepServerTestData{200, -1, "notok",
-               ".*redirect-loop.*"})
-}
-
-func (s *KeepSuite) TestGetKeepServers_ErrorReadServerResponse(c *C) {
-       testGetKeepServersAndSummarize(c, KeepServerTestData{200, 500, "notok",
-               ".*http://.* 500 Internal Server Error"})
-}
-
-func (s *KeepSuite) TestGetKeepServers_ReadServerResponseTuncatedAtLineOne(c *C) {
-       testGetKeepServersAndSummarize(c, KeepServerTestData{200, 200,
-               "notterminatedwithnewline", "Index from http://.* truncated at line 1"})
-}
-
-func (s *KeepSuite) TestGetKeepServers_InvalidBlockLocatorPattern(c *C) {
-       testGetKeepServersAndSummarize(c, KeepServerTestData{200, 200, "testing\n",
-               "Error parsing BlockInfo from index line.*"})
-}
-
-func (s *KeepSuite) TestGetKeepServers_ReadServerResponseEmpty(c *C) {
-       testGetKeepServersAndSummarize(c, KeepServerTestData{200, 200, "\n", ""})
-}
-
-func (s *KeepSuite) TestGetKeepServers_ReadServerResponseWithTwoBlocks(c *C) {
-       testGetKeepServersAndSummarize(c, KeepServerTestData{200, 200,
-               "51752ba076e461ec9ec1d27400a08548+20 1447526361\na048cc05c02ba1ee43ad071274b9e547+52 1447526362\n\n", ""})
-}
-
-func testGetKeepServersAndSummarize(c *C, testData KeepServerTestData) {
-       ksStubResponses := make(map[string]arvadostest.StubResponse)
-       ksStubResponses["/status.json"] = arvadostest.StubResponse{testData.statusStatusCode, string(`{}`)}
-       ksStubResponses["/index"] = arvadostest.StubResponse{testData.indexStatusCode, testData.indexResponseBody}
-       ksStub := arvadostest.ServerStub{ksStubResponses}
-       ks := httptest.NewServer(&ksStub)
-       defer ks.Close()
-
-       ksURL, err := url.Parse(ks.URL)
-       c.Check(err, IsNil)
-       ksHost, port, err := net.SplitHostPort(ksURL.Host)
-       ksPort, err := strconv.Atoi(port)
-       c.Check(err, IsNil)
-
-       servers_list := ServiceList{
-               ItemsAvailable: 1,
-               KeepServers: []ServerAddress{{
-                       SSL:         false,
-                       Host:        ksHost,
-                       Port:        ksPort,
-                       UUID:        "abcdefg",
-                       ServiceType: "disk",
-               }},
-       }
-       ksJSON, _ := json.Marshal(servers_list)
-       apiStubResponses := make(map[string]arvadostest.StubResponse)
-       apiStubResponses["/arvados/v1/keep_services"] = arvadostest.StubResponse{200, string(ksJSON)}
-       apiStub := arvadostest.ServerStub{apiStubResponses}
-
-       api := httptest.NewServer(&apiStub)
-       defer api.Close()
-
-       arv := &arvadosclient.ArvadosClient{
-               Scheme:    "http",
-               ApiServer: api.URL[7:],
-               ApiToken:  "abc123",
-               Client:    &http.Client{Transport: &http.Transport{}},
-       }
-
-       kc := keepclient.KeepClient{Arvados: arv, Client: &http.Client{}}
-       kc.SetServiceRoots(map[string]string{"xxxx": ks.URL},
-               map[string]string{"xxxx": ks.URL},
-               map[string]string{})
-
-       params := GetKeepServersParams{
-               Client: arv,
-               Logger: nil,
-               Limit:  10,
-       }
-
-       // GetKeepServersAndSummarize
-       results, err := GetKeepServersAndSummarize(params)
-
-       if testData.expectedError == "" {
-               c.Assert(err, IsNil)
-               c.Assert(results, NotNil)
-
-               blockToServers := results.BlockToServers
-
-               blockLocators := strings.Split(testData.indexResponseBody, "\n")
-               for _, loc := range blockLocators {
-                       locator := strings.Split(loc, " ")[0]
-                       if locator != "" {
-                               blockLocator, err := blockdigest.ParseBlockLocator(locator)
-                               c.Assert(err, IsNil)
-
-                               blockDigestWithSize := blockdigest.DigestWithSize{blockLocator.Digest, uint32(blockLocator.Size)}
-                               blockServerInfo := blockToServers[blockDigestWithSize]
-                               c.Assert(blockServerInfo[0].Mtime, NotNil)
-                       }
-               }
-       } else {
-               c.Assert(err, ErrorMatches, testData.expectedError)
-       }
-}
diff --git a/services/datamanager/loggerutil/loggerutil.go b/services/datamanager/loggerutil/loggerutil.go
deleted file mode 100644 (file)
index 8111425..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-/* Datamanager-specific logging methods. */
-
-package loggerutil
-
-import (
-       "git.curoverse.com/arvados.git/sdk/go/logger"
-       "log"
-       "os"
-       "runtime"
-       "time"
-)
-
-// Useful to call at the beginning of execution to log info about the
-// current run.
-func LogRunInfo(arvLogger *logger.Logger) {
-       if arvLogger != nil {
-               now := time.Now()
-               arvLogger.Update(func(p map[string]interface{}, e map[string]interface{}) {
-                       runInfo := logger.GetOrCreateMap(p, "run_info")
-                       runInfo["started_at"] = now
-                       runInfo["args"] = os.Args
-                       hostname, err := os.Hostname()
-                       if err != nil {
-                               runInfo["hostname_error"] = err.Error()
-                       } else {
-                               runInfo["hostname"] = hostname
-                       }
-                       runInfo["pid"] = os.Getpid()
-               })
-       }
-}
-
-// A LogMutator that records the current memory usage. This is most useful as a logger write hook.
-func LogMemoryAlloc(p map[string]interface{}, e map[string]interface{}) {
-       runInfo := logger.GetOrCreateMap(p, "run_info")
-       var memStats runtime.MemStats
-       runtime.ReadMemStats(&memStats)
-       runInfo["memory_bytes_in_use"] = memStats.Alloc
-       runInfo["memory_bytes_reserved"] = memStats.Sys
-}
-
-func FatalWithMessage(arvLogger *logger.Logger, message string) {
-       if arvLogger != nil {
-               arvLogger.FinalUpdate(func(p map[string]interface{}, e map[string]interface{}) {
-                       p["FATAL"] = message
-                       runInfo := logger.GetOrCreateMap(p, "run_info")
-                       runInfo["finished_at"] = time.Now()
-               })
-       }
-
-       log.Fatalf(message)
-}
diff --git a/services/datamanager/summary/canonical_string.go b/services/datamanager/summary/canonical_string.go
deleted file mode 100644 (file)
index 152314c..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-/* Ensures that we only have one copy of each unique string. This is
-/* not designed for concurrent access. */
-
-package summary
-
-// This code should probably be moved somewhere more universal.
-
-// CanonicalString struct
-type CanonicalString struct {
-       m map[string]string
-}
-
-// Get a CanonicalString
-func (cs *CanonicalString) Get(s string) (r string) {
-       if cs.m == nil {
-               cs.m = make(map[string]string)
-       }
-       value, found := cs.m[s]
-       if found {
-               return value
-       }
-
-       // s may be a substring of a much larger string.
-       // If we store s, it will prevent that larger string from getting
-       // garbage collected.
-       // If this is something you worry about you should change this code
-       // to make an explict copy of s using a byte array.
-       cs.m[s] = s
-       return s
-}
diff --git a/services/datamanager/summary/file.go b/services/datamanager/summary/file.go
deleted file mode 100644 (file)
index 6e463d7..0000000
+++ /dev/null
@@ -1,115 +0,0 @@
-// Handles writing data to and reading data from disk to speed up development.
-
-package summary
-
-import (
-       "encoding/gob"
-       "flag"
-       "fmt"
-       "git.curoverse.com/arvados.git/sdk/go/logger"
-       "git.curoverse.com/arvados.git/services/datamanager/collection"
-       "git.curoverse.com/arvados.git/services/datamanager/keep"
-       "log"
-       "os"
-)
-
-// Used to locally cache data read from servers to reduce execution
-// time when developing. Not for use in production.
-type serializedData struct {
-       ReadCollections collection.ReadCollections
-       KeepServerInfo  keep.ReadServers
-}
-
-var (
-       WriteDataTo  string
-       readDataFrom string
-)
-
-// DataFetcher to fetch data from keep servers
-type DataFetcher func(arvLogger *logger.Logger,
-       readCollections *collection.ReadCollections,
-       keepServerInfo *keep.ReadServers) error
-
-func init() {
-       flag.StringVar(&WriteDataTo,
-               "write-data-to",
-               "",
-               "Write summary of data received to this file. Used for development only.")
-       flag.StringVar(&readDataFrom,
-               "read-data-from",
-               "",
-               "Avoid network i/o and read summary data from this file instead. Used for development only.")
-}
-
-// MaybeWriteData writes data we've read to a file.
-//
-// This is useful for development, so that we don't need to read all
-// our data from the network every time we tweak something.
-//
-// This should not be used outside of development, since you'll be
-// working with stale data.
-func MaybeWriteData(arvLogger *logger.Logger,
-       readCollections collection.ReadCollections,
-       keepServerInfo keep.ReadServers) error {
-       if WriteDataTo == "" {
-               return nil
-       }
-       summaryFile, err := os.Create(WriteDataTo)
-       if err != nil {
-               return err
-       }
-       defer summaryFile.Close()
-
-       enc := gob.NewEncoder(summaryFile)
-       data := serializedData{
-               ReadCollections: readCollections,
-               KeepServerInfo:  keepServerInfo}
-       err = enc.Encode(data)
-       if err != nil {
-               return err
-       }
-       log.Printf("Wrote summary data to: %s", WriteDataTo)
-       return nil
-}
-
-// ShouldReadData should not be used outside of development
-func ShouldReadData() bool {
-       return readDataFrom != ""
-}
-
-// ReadData reads data that we've written to a file.
-//
-// This is useful for development, so that we don't need to read all
-// our data from the network every time we tweak something.
-//
-// This should not be used outside of development, since you'll be
-// working with stale data.
-func ReadData(arvLogger *logger.Logger,
-       readCollections *collection.ReadCollections,
-       keepServerInfo *keep.ReadServers) error {
-       if readDataFrom == "" {
-               return fmt.Errorf("ReadData() called with empty filename.")
-       }
-       summaryFile, err := os.Open(readDataFrom)
-       if err != nil {
-               return err
-       }
-       defer summaryFile.Close()
-
-       dec := gob.NewDecoder(summaryFile)
-       data := serializedData{}
-       err = dec.Decode(&data)
-       if err != nil {
-               return err
-       }
-
-       // re-summarize data, so that we can update our summarizing
-       // functions without needing to do all our network i/o
-       data.ReadCollections.Summarize(arvLogger)
-       data.KeepServerInfo.Summarize(arvLogger)
-
-       *readCollections = data.ReadCollections
-       *keepServerInfo = data.KeepServerInfo
-       log.Printf("Read summary data from: %s", readDataFrom)
-       return nil
-}
diff --git a/services/datamanager/summary/pull_list.go b/services/datamanager/summary/pull_list.go
deleted file mode 100644 (file)
index d7fb3eb..0000000
+++ /dev/null
@@ -1,215 +0,0 @@
-// Code for generating pull lists as described in https://arvados.org/projects/arvados/wiki/Keep_Design_Doc#Pull-List
-
-package summary
-
-import (
-       "encoding/json"
-       "fmt"
-       "git.curoverse.com/arvados.git/sdk/go/blockdigest"
-       "git.curoverse.com/arvados.git/sdk/go/keepclient"
-       "git.curoverse.com/arvados.git/sdk/go/logger"
-       "git.curoverse.com/arvados.git/services/datamanager/keep"
-       "log"
-       "os"
-       "strings"
-)
-
-// Locator is a block digest
-type Locator blockdigest.DigestWithSize
-
-// MarshalJSON encoding
-func (l Locator) MarshalJSON() ([]byte, error) {
-       return []byte("\"" + blockdigest.DigestWithSize(l).String() + "\""), nil
-}
-
-// PullRequest represents one entry in the Pull List
-type PullRequest struct {
-       Locator Locator  `json:"locator"`
-       Servers []string `json:"servers"`
-}
-
-// PullList for a particular server
-type PullList []PullRequest
-
-// PullListByLocator implements sort.Interface for PullList based on
-// the Digest.
-type PullListByLocator PullList
-
-func (a PullListByLocator) Len() int      { return len(a) }
-func (a PullListByLocator) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
-func (a PullListByLocator) Less(i, j int) bool {
-       di, dj := a[i].Locator.Digest, a[j].Locator.Digest
-       if di.H < dj.H {
-               return true
-       } else if di.H == dj.H {
-               if di.L < dj.L {
-                       return true
-               } else if di.L == dj.L {
-                       return a[i].Locator.Size < a[j].Locator.Size
-               }
-       }
-       return false
-}
-
-// PullServers struct
-// For a given under-replicated block, this structure represents which
-// servers should pull the specified block and which servers they can
-// pull it from.
-type PullServers struct {
-       To   []string // Servers that should pull the specified block
-       From []string // Servers that already contain the specified block
-}
-
-// ComputePullServers creates a map from block locator to PullServers
-// with one entry for each under-replicated block.
-//
-// This method ignores zero-replica blocks since there are no servers
-// to pull them from, so callers should feel free to omit them, but
-// this function will ignore them if they are provided.
-func ComputePullServers(kc *keepclient.KeepClient,
-       keepServerInfo *keep.ReadServers,
-       blockToDesiredReplication map[blockdigest.DigestWithSize]int,
-       underReplicated BlockSet) (m map[Locator]PullServers) {
-       m = map[Locator]PullServers{}
-       // We use CanonicalString to avoid filling memory with duplicate
-       // copies of the same string.
-       var cs CanonicalString
-
-       // Servers that are writeable
-       writableServers := map[string]struct{}{}
-       for _, url := range kc.WritableLocalRoots() {
-               writableServers[cs.Get(url)] = struct{}{}
-       }
-
-       for block := range underReplicated {
-               serversStoringBlock := keepServerInfo.BlockToServers[block]
-               numCopies := len(serversStoringBlock)
-               numCopiesMissing := blockToDesiredReplication[block] - numCopies
-               if numCopiesMissing > 0 {
-                       // We expect this to always be true, since the block was listed
-                       // in underReplicated.
-
-                       if numCopies > 0 {
-                               // Not much we can do with blocks with no copies.
-
-                               // A server's host-port string appears as a key in this map
-                               // iff it contains the block.
-                               serverHasBlock := map[string]struct{}{}
-                               for _, info := range serversStoringBlock {
-                                       sa := keepServerInfo.KeepServerIndexToAddress[info.ServerIndex]
-                                       serverHasBlock[cs.Get(sa.URL())] = struct{}{}
-                               }
-
-                               roots := keepclient.NewRootSorter(kc.LocalRoots(),
-                                       block.String()).GetSortedRoots()
-
-                               l := Locator(block)
-                               m[l] = CreatePullServers(cs, serverHasBlock, writableServers,
-                                       roots, numCopiesMissing)
-                       }
-               }
-       }
-       return m
-}
-
-// CreatePullServers creates a pull list in which the To and From
-// fields preserve the ordering of sorted servers and the contents
-// are all canonical strings.
-func CreatePullServers(cs CanonicalString,
-       serverHasBlock map[string]struct{},
-       writableServers map[string]struct{},
-       sortedServers []string,
-       maxToFields int) (ps PullServers) {
-
-       ps = PullServers{
-               To:   make([]string, 0, maxToFields),
-               From: make([]string, 0, len(serverHasBlock)),
-       }
-
-       for _, host := range sortedServers {
-               // Strip the protocol portion of the url.
-               // Use the canonical copy of the string to avoid memory waste.
-               server := cs.Get(host)
-               _, hasBlock := serverHasBlock[server]
-               if hasBlock {
-                       // The from field should include the protocol.
-                       ps.From = append(ps.From, cs.Get(host))
-               } else if len(ps.To) < maxToFields {
-                       _, writable := writableServers[host]
-                       if writable {
-                               ps.To = append(ps.To, server)
-                       }
-               }
-       }
-
-       return
-}
-
-// RemoveProtocolPrefix strips the protocol prefix from a url.
-func RemoveProtocolPrefix(url string) string {
-       return url[(strings.LastIndex(url, "/") + 1):]
-}
-
-// BuildPullLists produces a PullList for each keep server.
-func BuildPullLists(lps map[Locator]PullServers) (spl map[string]PullList) {
-       spl = map[string]PullList{}
-       // We don't worry about canonicalizing our strings here, because we
-       // assume lps was created by ComputePullServers() which already
-       // canonicalized the strings for us.
-       for locator, pullServers := range lps {
-               for _, destination := range pullServers.To {
-                       pullList, pullListExists := spl[destination]
-                       if !pullListExists {
-                               pullList = PullList{}
-                       }
-                       spl[destination] = append(pullList,
-                               PullRequest{Locator: locator, Servers: pullServers.From})
-               }
-       }
-       return
-}
-
-// WritePullLists writes each pull list to a file.
-// The filename is based on the hostname.
-//
-// This is just a hack for prototyping, it is not expected to be used
-// in production.
-func WritePullLists(arvLogger *logger.Logger,
-       pullLists map[string]PullList,
-       dryRun bool) error {
-       r := strings.NewReplacer(":", ".")
-
-       for host, list := range pullLists {
-               if arvLogger != nil {
-                       // We need a local variable because Update doesn't call our mutator func until later,
-                       // when our list variable might have been reused by the next loop iteration.
-                       host := host
-                       listLen := len(list)
-                       arvLogger.Update(func(p map[string]interface{}, e map[string]interface{}) {
-                               pullListInfo := logger.GetOrCreateMap(p, "pull_list_len")
-                               pullListInfo[host] = listLen
-                       })
-               }
-
-               if dryRun {
-                       log.Print("dry run, not sending pull list to service %s with %d blocks", host, len(list))
-                       continue
-               }
-
-               filename := fmt.Sprintf("pull_list.%s", r.Replace(RemoveProtocolPrefix(host)))
-               pullListFile, err := os.Create(filename)
-               if err != nil {
-                       return err
-               }
-               defer pullListFile.Close()
-
-               enc := json.NewEncoder(pullListFile)
-               err = enc.Encode(list)
-               if err != nil {
-                       return err
-               }
-               log.Printf("Wrote pull list to %s.", filename)
-       }
-
-       return nil
-}
diff --git a/services/datamanager/summary/pull_list_test.go b/services/datamanager/summary/pull_list_test.go
deleted file mode 100644 (file)
index 60b495c..0000000
+++ /dev/null
@@ -1,272 +0,0 @@
-package summary
-
-import (
-       "encoding/json"
-       "git.curoverse.com/arvados.git/sdk/go/blockdigest"
-       . "gopkg.in/check.v1"
-       "sort"
-       "testing"
-)
-
-// Gocheck boilerplate
-func TestPullLists(t *testing.T) {
-       TestingT(t)
-}
-
-type PullSuite struct{}
-
-var _ = Suite(&PullSuite{})
-
-// Helper method to declare string sets more succinctly
-// Could be placed somewhere more general.
-func stringSet(slice ...string) (m map[string]struct{}) {
-       m = map[string]struct{}{}
-       for _, s := range slice {
-               m[s] = struct{}{}
-       }
-       return
-}
-
-func (s *PullSuite) TestPullListPrintsJSONCorrectly(c *C) {
-       pl := PullList{PullRequest{
-               Locator: Locator(blockdigest.MakeTestDigestSpecifySize(0xBadBeef, 56789)),
-               Servers: []string{"keep0.qr1hi.arvadosapi.com:25107",
-                       "keep1.qr1hi.arvadosapi.com:25108"}}}
-
-       b, err := json.Marshal(pl)
-       c.Assert(err, IsNil)
-       expectedOutput := `[{"locator":"0000000000000000000000000badbeef+56789",` +
-               `"servers":["keep0.qr1hi.arvadosapi.com:25107",` +
-               `"keep1.qr1hi.arvadosapi.com:25108"]}]`
-       c.Check(string(b), Equals, expectedOutput)
-}
-
-func (s *PullSuite) TestCreatePullServers(c *C) {
-       var cs CanonicalString
-       c.Check(
-               CreatePullServers(cs,
-                       stringSet(),
-                       stringSet(),
-                       []string{},
-                       5),
-               DeepEquals,
-               PullServers{To: []string{}, From: []string{}})
-
-       c.Check(
-               CreatePullServers(cs,
-                       stringSet("https://keep0:25107", "https://keep1:25108"),
-                       stringSet(),
-                       []string{},
-                       5),
-               DeepEquals,
-               PullServers{To: []string{}, From: []string{}})
-
-       c.Check(
-               CreatePullServers(cs,
-                       stringSet("https://keep0:25107", "https://keep1:25108"),
-                       stringSet("https://keep0:25107"),
-                       []string{"https://keep0:25107"},
-                       5),
-               DeepEquals,
-               PullServers{To: []string{}, From: []string{"https://keep0:25107"}})
-
-       c.Check(
-               CreatePullServers(cs,
-                       stringSet("https://keep0:25107", "https://keep1:25108"),
-                       stringSet("https://keep3:25110", "https://keep2:25109", "https://keep1:25108", "https://keep0:25107"),
-                       []string{"https://keep3:25110", "https://keep2:25109", "https://keep1:25108", "https://keep0:25107"},
-                       5),
-               DeepEquals,
-               PullServers{To: []string{"https://keep3:25110", "https://keep2:25109"},
-                       From: []string{"https://keep1:25108", "https://keep0:25107"}})
-
-       c.Check(
-               CreatePullServers(cs,
-                       stringSet("https://keep0:25107", "https://keep1:25108"),
-                       stringSet("https://keep3:25110", "https://keep1:25108", "https://keep0:25107"),
-                       []string{"https://keep3:25110", "https://keep2:25109", "https://keep1:25108", "https://keep0:25107"},
-                       5),
-               DeepEquals,
-               PullServers{To: []string{"https://keep3:25110"},
-                       From: []string{"https://keep1:25108", "https://keep0:25107"}})
-
-       c.Check(
-               CreatePullServers(cs,
-                       stringSet("https://keep0:25107", "https://keep1:25108"),
-                       stringSet("https://keep3:25110", "https://keep2:25109", "https://keep1:25108", "https://keep0:25107"),
-                       []string{"https://keep3:25110", "https://keep2:25109", "https://keep1:25108", "https://keep0:25107"},
-                       1),
-               DeepEquals,
-               PullServers{To: []string{"https://keep3:25110"},
-                       From: []string{"https://keep1:25108", "https://keep0:25107"}})
-
-       c.Check(
-               CreatePullServers(cs,
-                       stringSet("https://keep0:25107", "https://keep1:25108"),
-                       stringSet("https://keep3:25110", "https://keep2:25109",
-                               "https://keep1:25108", "https://keep0:25107"),
-                       []string{"https://keep3:25110", "https://keep2:25109",
-                               "https://keep1:25108", "https://keep0:25107"},
-                       1),
-               DeepEquals,
-               PullServers{To: []string{"https://keep3:25110"},
-                       From: []string{"https://keep1:25108", "https://keep0:25107"}})
-
-       c.Check(
-               CreatePullServers(cs,
-                       stringSet("https://keep0:25107", "https://keep1:25108"),
-                       stringSet("https://keep3:25110", "https://keep2:25109", "https://keep1:25108", "https://keep0:25107"),
-                       []string{"https://keep3:25110", "https://keep2:25109", "https://keep1:25108", "https://keep0:25107"},
-                       0),
-               DeepEquals,
-               PullServers{To: []string{},
-                       From: []string{"https://keep1:25108", "https://keep0:25107"}})
-}
-
-// Checks whether two pull list maps are equal. Since pull lists are
-// ordered arbitrarily, we need to sort them by digest before
-// comparing them for deep equality.
-type pullListMapEqualsChecker struct {
-       *CheckerInfo
-}
-
-func (c *pullListMapEqualsChecker) Check(params []interface{}, names []string) (result bool, error string) {
-       obtained, ok := params[0].(map[string]PullList)
-       if !ok {
-               return false, "First parameter is not a PullList map"
-       }
-       expected, ok := params[1].(map[string]PullList)
-       if !ok {
-               return false, "Second parameter is not a PullList map"
-       }
-
-       for _, v := range obtained {
-               sort.Sort(PullListByLocator(v))
-       }
-       for _, v := range expected {
-               sort.Sort(PullListByLocator(v))
-       }
-
-       return DeepEquals.Check(params, names)
-}
-
-var PullListMapEquals Checker = &pullListMapEqualsChecker{&CheckerInfo{
-       Name:   "PullListMapEquals",
-       Params: []string{"obtained", "expected"},
-}}
-
-func (s *PullSuite) TestBuildPullLists(c *C) {
-       c.Check(
-               BuildPullLists(map[Locator]PullServers{}),
-               PullListMapEquals,
-               map[string]PullList{})
-
-       locator1 := Locator{Digest: blockdigest.MakeTestBlockDigest(0xBadBeef)}
-       c.Check(
-               BuildPullLists(map[Locator]PullServers{
-                       locator1: {To: []string{}, From: []string{}}}),
-               PullListMapEquals,
-               map[string]PullList{})
-
-       c.Check(
-               BuildPullLists(map[Locator]PullServers{
-                       locator1: {To: []string{}, From: []string{"f1", "f2"}}}),
-               PullListMapEquals,
-               map[string]PullList{})
-
-       c.Check(
-               BuildPullLists(map[Locator]PullServers{
-                       locator1: {To: []string{"t1"}, From: []string{"f1", "f2"}}}),
-               PullListMapEquals,
-               map[string]PullList{
-                       "t1": {PullRequest{locator1, []string{"f1", "f2"}}}})
-
-       c.Check(
-               BuildPullLists(map[Locator]PullServers{
-                       locator1: {To: []string{"t1"}, From: []string{}}}),
-               PullListMapEquals,
-               map[string]PullList{"t1": {
-                       PullRequest{locator1, []string{}}}})
-
-       c.Check(
-               BuildPullLists(map[Locator]PullServers{
-                       locator1: {
-                               To:   []string{"t1", "t2"},
-                               From: []string{"f1", "f2"},
-                       }}),
-               PullListMapEquals,
-               map[string]PullList{
-                       "t1": {PullRequest{locator1, []string{"f1", "f2"}}},
-                       "t2": {PullRequest{locator1, []string{"f1", "f2"}}},
-               })
-
-       locator2 := Locator{Digest: blockdigest.MakeTestBlockDigest(0xCabbed)}
-       c.Check(
-               BuildPullLists(map[Locator]PullServers{
-                       locator1: {To: []string{"t1"}, From: []string{"f1", "f2"}},
-                       locator2: {To: []string{"t2"}, From: []string{"f3", "f4"}}}),
-               PullListMapEquals,
-               map[string]PullList{
-                       "t1": {PullRequest{locator1, []string{"f1", "f2"}}},
-                       "t2": {PullRequest{locator2, []string{"f3", "f4"}}},
-               })
-
-       c.Check(
-               BuildPullLists(map[Locator]PullServers{
-                       locator1: {
-                               To:   []string{"t1"},
-                               From: []string{"f1", "f2"}},
-                       locator2: {
-                               To:   []string{"t2", "t1"},
-                               From: []string{"f3", "f4"}},
-               }),
-               PullListMapEquals,
-               map[string]PullList{
-                       "t1": {
-                               PullRequest{locator1, []string{"f1", "f2"}},
-                               PullRequest{locator2, []string{"f3", "f4"}},
-                       },
-                       "t2": {
-                               PullRequest{locator2, []string{"f3", "f4"}},
-                       },
-               })
-
-       locator3 := Locator{Digest: blockdigest.MakeTestBlockDigest(0xDeadBeef)}
-       locator4 := Locator{Digest: blockdigest.MakeTestBlockDigest(0xFedBeef)}
-       c.Check(
-               BuildPullLists(map[Locator]PullServers{
-                       locator1: {
-                               To:   []string{"t1"},
-                               From: []string{"f1", "f2"}},
-                       locator2: {
-                               To:   []string{"t2", "t1"},
-                               From: []string{"f3", "f4"}},
-                       locator3: {
-                               To:   []string{"t3", "t2", "t1"},
-                               From: []string{"f4", "f5"}},
-                       locator4: {
-                               To:   []string{"t4", "t3", "t2", "t1"},
-                               From: []string{"f1", "f5"}},
-               }),
-               PullListMapEquals,
-               map[string]PullList{
-                       "t1": {
-                               PullRequest{locator1, []string{"f1", "f2"}},
-                               PullRequest{locator2, []string{"f3", "f4"}},
-                               PullRequest{locator3, []string{"f4", "f5"}},
-                               PullRequest{locator4, []string{"f1", "f5"}},
-                       },
-                       "t2": {
-                               PullRequest{locator2, []string{"f3", "f4"}},
-                               PullRequest{locator3, []string{"f4", "f5"}},
-                               PullRequest{locator4, []string{"f1", "f5"}},
-                       },
-                       "t3": {
-                               PullRequest{locator3, []string{"f4", "f5"}},
-                               PullRequest{locator4, []string{"f1", "f5"}},
-                       },
-                       "t4": {
-                               PullRequest{locator4, []string{"f1", "f5"}},
-                       },
-               })
-}
diff --git a/services/datamanager/summary/summary.go b/services/datamanager/summary/summary.go
deleted file mode 100644 (file)
index 9fb0316..0000000
+++ /dev/null
@@ -1,277 +0,0 @@
-// Summarizes Collection Data and Keep Server Contents.
-
-package summary
-
-// TODO(misha): Check size of blocks as well as their digest.
-
-import (
-       "fmt"
-       "git.curoverse.com/arvados.git/sdk/go/blockdigest"
-       "git.curoverse.com/arvados.git/services/datamanager/collection"
-       "git.curoverse.com/arvados.git/services/datamanager/keep"
-       "sort"
-)
-
-// BlockSet is a map of blocks
-type BlockSet map[blockdigest.DigestWithSize]struct{}
-
-// Insert adds a single block to the set.
-func (bs BlockSet) Insert(digest blockdigest.DigestWithSize) {
-       bs[digest] = struct{}{}
-}
-
-// Union adds a set of blocks to the set.
-func (bs BlockSet) Union(obs BlockSet) {
-       for k, v := range obs {
-               bs[k] = v
-       }
-}
-
-// CollectionIndexSet is used to save space. To convert to and from
-// the uuid, use collection.ReadCollections' fields
-// CollectionIndexToUUID and CollectionUUIDToIndex.
-type CollectionIndexSet map[int]struct{}
-
-// Insert adds a single collection to the set. The collection is specified by
-// its index.
-func (cis CollectionIndexSet) Insert(collectionIndex int) {
-       cis[collectionIndex] = struct{}{}
-}
-
-// ToCollectionIndexSet gets block to collection indices
-func (bs BlockSet) ToCollectionIndexSet(
-       readCollections collection.ReadCollections,
-       collectionIndexSet *CollectionIndexSet) {
-       for block := range bs {
-               for _, collectionIndex := range readCollections.BlockToCollectionIndices[block] {
-                       collectionIndexSet.Insert(collectionIndex)
-               }
-       }
-}
-
-// ReplicationLevels struct
-// Keeps track of the requested and actual replication levels.
-// Currently this is only used for blocks but could easily be used for
-// collections as well.
-type ReplicationLevels struct {
-       // The requested replication level.
-       // For Blocks this is the maximum replication level among all the
-       // collections this block belongs to.
-       Requested int
-
-       // The actual number of keep servers this is on.
-       Actual int
-}
-
-// ReplicationLevelBlockSetMap maps from replication levels to their blocks.
-type ReplicationLevelBlockSetMap map[ReplicationLevels]BlockSet
-
-// ReplicationLevelBlockCount is an individual entry from ReplicationLevelBlockSetMap
-// which only reports the number of blocks, not which blocks.
-type ReplicationLevelBlockCount struct {
-       Levels ReplicationLevels
-       Count  int
-}
-
-// ReplicationLevelBlockSetSlice is an ordered list of ReplicationLevelBlockCount useful for reporting.
-type ReplicationLevelBlockSetSlice []ReplicationLevelBlockCount
-
-// ReplicationSummary sturct
-type ReplicationSummary struct {
-       CollectionBlocksNotInKeep  BlockSet
-       UnderReplicatedBlocks      BlockSet
-       OverReplicatedBlocks       BlockSet
-       CorrectlyReplicatedBlocks  BlockSet
-       KeepBlocksNotInCollections BlockSet
-
-       CollectionsNotFullyInKeep      CollectionIndexSet
-       UnderReplicatedCollections     CollectionIndexSet
-       OverReplicatedCollections      CollectionIndexSet
-       CorrectlyReplicatedCollections CollectionIndexSet
-}
-
-// ReplicationSummaryCounts struct counts the elements in each set in ReplicationSummary.
-type ReplicationSummaryCounts struct {
-       CollectionBlocksNotInKeep      int
-       UnderReplicatedBlocks          int
-       OverReplicatedBlocks           int
-       CorrectlyReplicatedBlocks      int
-       KeepBlocksNotInCollections     int
-       CollectionsNotFullyInKeep      int
-       UnderReplicatedCollections     int
-       OverReplicatedCollections      int
-       CorrectlyReplicatedCollections int
-}
-
-// GetOrCreate gets the BlockSet for a given set of ReplicationLevels,
-// creating it if it doesn't already exist.
-func (rlbs ReplicationLevelBlockSetMap) GetOrCreate(
-       repLevels ReplicationLevels) (bs BlockSet) {
-       bs, exists := rlbs[repLevels]
-       if !exists {
-               bs = make(BlockSet)
-               rlbs[repLevels] = bs
-       }
-       return
-}
-
-// Insert adds a block to the set for a given replication level.
-func (rlbs ReplicationLevelBlockSetMap) Insert(
-       repLevels ReplicationLevels,
-       block blockdigest.DigestWithSize) {
-       rlbs.GetOrCreate(repLevels).Insert(block)
-}
-
-// Union adds a set of blocks to the set for a given replication level.
-func (rlbs ReplicationLevelBlockSetMap) Union(
-       repLevels ReplicationLevels,
-       bs BlockSet) {
-       rlbs.GetOrCreate(repLevels).Union(bs)
-}
-
-// Counts outputs a sorted list of ReplicationLevelBlockCounts.
-func (rlbs ReplicationLevelBlockSetMap) Counts() (
-       sorted ReplicationLevelBlockSetSlice) {
-       sorted = make(ReplicationLevelBlockSetSlice, len(rlbs))
-       i := 0
-       for levels, set := range rlbs {
-               sorted[i] = ReplicationLevelBlockCount{Levels: levels, Count: len(set)}
-               i++
-       }
-       sort.Sort(sorted)
-       return
-}
-
-// Implemented to meet sort.Interface
-func (rlbss ReplicationLevelBlockSetSlice) Len() int {
-       return len(rlbss)
-}
-
-// Implemented to meet sort.Interface
-func (rlbss ReplicationLevelBlockSetSlice) Less(i, j int) bool {
-       return rlbss[i].Levels.Requested < rlbss[j].Levels.Requested ||
-               (rlbss[i].Levels.Requested == rlbss[j].Levels.Requested &&
-                       rlbss[i].Levels.Actual < rlbss[j].Levels.Actual)
-}
-
-// Implemented to meet sort.Interface
-func (rlbss ReplicationLevelBlockSetSlice) Swap(i, j int) {
-       rlbss[i], rlbss[j] = rlbss[j], rlbss[i]
-}
-
-// ComputeCounts returns ReplicationSummaryCounts
-func (rs ReplicationSummary) ComputeCounts() (rsc ReplicationSummaryCounts) {
-       // TODO(misha): Consider rewriting this method to iterate through
-       // the fields using reflection, instead of explictily listing the
-       // fields as we do now.
-       rsc.CollectionBlocksNotInKeep = len(rs.CollectionBlocksNotInKeep)
-       rsc.UnderReplicatedBlocks = len(rs.UnderReplicatedBlocks)
-       rsc.OverReplicatedBlocks = len(rs.OverReplicatedBlocks)
-       rsc.CorrectlyReplicatedBlocks = len(rs.CorrectlyReplicatedBlocks)
-       rsc.KeepBlocksNotInCollections = len(rs.KeepBlocksNotInCollections)
-       rsc.CollectionsNotFullyInKeep = len(rs.CollectionsNotFullyInKeep)
-       rsc.UnderReplicatedCollections = len(rs.UnderReplicatedCollections)
-       rsc.OverReplicatedCollections = len(rs.OverReplicatedCollections)
-       rsc.CorrectlyReplicatedCollections = len(rs.CorrectlyReplicatedCollections)
-       return rsc
-}
-
-// PrettyPrint ReplicationSummaryCounts
-func (rsc ReplicationSummaryCounts) PrettyPrint() string {
-       return fmt.Sprintf("Replication Block Counts:"+
-               "\n Missing From Keep: %d, "+
-               "\n Under Replicated: %d, "+
-               "\n Over Replicated: %d, "+
-               "\n Replicated Just Right: %d, "+
-               "\n Not In Any Collection: %d. "+
-               "\nReplication Collection Counts:"+
-               "\n Missing From Keep: %d, "+
-               "\n Under Replicated: %d, "+
-               "\n Over Replicated: %d, "+
-               "\n Replicated Just Right: %d.",
-               rsc.CollectionBlocksNotInKeep,
-               rsc.UnderReplicatedBlocks,
-               rsc.OverReplicatedBlocks,
-               rsc.CorrectlyReplicatedBlocks,
-               rsc.KeepBlocksNotInCollections,
-               rsc.CollectionsNotFullyInKeep,
-               rsc.UnderReplicatedCollections,
-               rsc.OverReplicatedCollections,
-               rsc.CorrectlyReplicatedCollections)
-}
-
-// BucketReplication returns ReplicationLevelBlockSetMap
-func BucketReplication(readCollections collection.ReadCollections,
-       keepServerInfo keep.ReadServers) (rlbs ReplicationLevelBlockSetMap) {
-       rlbs = make(ReplicationLevelBlockSetMap)
-
-       for block, requestedReplication := range readCollections.BlockToDesiredReplication {
-               rlbs.Insert(
-                       ReplicationLevels{
-                               Requested: requestedReplication,
-                               Actual:    len(keepServerInfo.BlockToServers[block])},
-                       block)
-       }
-
-       for block, servers := range keepServerInfo.BlockToServers {
-               if 0 == readCollections.BlockToDesiredReplication[block] {
-                       rlbs.Insert(
-                               ReplicationLevels{Requested: 0, Actual: len(servers)},
-                               block)
-               }
-       }
-       return
-}
-
-// SummarizeBuckets reads collections and summarizes
-func (rlbs ReplicationLevelBlockSetMap) SummarizeBuckets(
-       readCollections collection.ReadCollections) (
-       rs ReplicationSummary) {
-       rs.CollectionBlocksNotInKeep = make(BlockSet)
-       rs.UnderReplicatedBlocks = make(BlockSet)
-       rs.OverReplicatedBlocks = make(BlockSet)
-       rs.CorrectlyReplicatedBlocks = make(BlockSet)
-       rs.KeepBlocksNotInCollections = make(BlockSet)
-
-       rs.CollectionsNotFullyInKeep = make(CollectionIndexSet)
-       rs.UnderReplicatedCollections = make(CollectionIndexSet)
-       rs.OverReplicatedCollections = make(CollectionIndexSet)
-       rs.CorrectlyReplicatedCollections = make(CollectionIndexSet)
-
-       for levels, bs := range rlbs {
-               if levels.Actual == 0 {
-                       rs.CollectionBlocksNotInKeep.Union(bs)
-               } else if levels.Requested == 0 {
-                       rs.KeepBlocksNotInCollections.Union(bs)
-               } else if levels.Actual < levels.Requested {
-                       rs.UnderReplicatedBlocks.Union(bs)
-               } else if levels.Actual > levels.Requested {
-                       rs.OverReplicatedBlocks.Union(bs)
-               } else {
-                       rs.CorrectlyReplicatedBlocks.Union(bs)
-               }
-       }
-
-       rs.CollectionBlocksNotInKeep.ToCollectionIndexSet(readCollections,
-               &rs.CollectionsNotFullyInKeep)
-       // Since different collections can specify different replication
-       // levels, the fact that a block is under-replicated does not imply
-       // that all collections that it belongs to are under-replicated, but
-       // we'll ignore that for now.
-       // TODO(misha): Fix this and report the correct set of collections.
-       rs.UnderReplicatedBlocks.ToCollectionIndexSet(readCollections,
-               &rs.UnderReplicatedCollections)
-       rs.OverReplicatedBlocks.ToCollectionIndexSet(readCollections,
-               &rs.OverReplicatedCollections)
-
-       for i := range readCollections.CollectionIndexToUUID {
-               if _, notInKeep := rs.CollectionsNotFullyInKeep[i]; notInKeep {
-               } else if _, underReplicated := rs.UnderReplicatedCollections[i]; underReplicated {
-               } else if _, overReplicated := rs.OverReplicatedCollections[i]; overReplicated {
-               } else {
-                       rs.CorrectlyReplicatedCollections.Insert(i)
-               }
-       }
-
-       return
-}
diff --git a/services/datamanager/summary/summary_test.go b/services/datamanager/summary/summary_test.go
deleted file mode 100644 (file)
index 8268404..0000000
+++ /dev/null
@@ -1,220 +0,0 @@
-package summary
-
-import (
-       "git.curoverse.com/arvados.git/sdk/go/blockdigest"
-       "git.curoverse.com/arvados.git/services/datamanager/collection"
-       "git.curoverse.com/arvados.git/services/datamanager/keep"
-       "reflect"
-       "sort"
-       "testing"
-)
-
-func BlockSetFromSlice(digests []int) (bs BlockSet) {
-       bs = make(BlockSet)
-       for _, digest := range digests {
-               bs.Insert(blockdigest.MakeTestDigestWithSize(digest))
-       }
-       return
-}
-
-func CollectionIndexSetFromSlice(indices []int) (cis CollectionIndexSet) {
-       cis = make(CollectionIndexSet)
-       for _, index := range indices {
-               cis.Insert(index)
-       }
-       return
-}
-
-func (cis CollectionIndexSet) ToSlice() (ints []int) {
-       ints = make([]int, len(cis))
-       i := 0
-       for collectionIndex := range cis {
-               ints[i] = collectionIndex
-               i++
-       }
-       sort.Ints(ints)
-       return
-}
-
-// Helper method to meet interface expected by older tests.
-func SummarizeReplication(readCollections collection.ReadCollections,
-       keepServerInfo keep.ReadServers) (rs ReplicationSummary) {
-       return BucketReplication(readCollections, keepServerInfo).
-               SummarizeBuckets(readCollections)
-}
-
-// Takes a map from block digest to replication level and represents
-// it in a keep.ReadServers structure.
-func SpecifyReplication(digestToReplication map[int]int) (rs keep.ReadServers) {
-       rs.BlockToServers = make(map[blockdigest.DigestWithSize][]keep.BlockServerInfo)
-       for digest, replication := range digestToReplication {
-               rs.BlockToServers[blockdigest.MakeTestDigestWithSize(digest)] =
-                       make([]keep.BlockServerInfo, replication)
-       }
-       return
-}
-
-// Verifies that
-// blocks.ToCollectionIndexSet(rc.BlockToCollectionIndices) returns
-// expectedCollections.
-func VerifyToCollectionIndexSet(
-       t *testing.T,
-       blocks []int,
-       blockToCollectionIndices map[int][]int,
-       expectedCollections []int) {
-
-       expected := CollectionIndexSetFromSlice(expectedCollections)
-
-       rc := collection.ReadCollections{
-               BlockToCollectionIndices: map[blockdigest.DigestWithSize][]int{},
-       }
-       for digest, indices := range blockToCollectionIndices {
-               rc.BlockToCollectionIndices[blockdigest.MakeTestDigestWithSize(digest)] = indices
-       }
-
-       returned := make(CollectionIndexSet)
-       BlockSetFromSlice(blocks).ToCollectionIndexSet(rc, &returned)
-
-       if !reflect.DeepEqual(returned, expected) {
-               t.Errorf("Expected %v.ToCollectionIndexSet(%v) to return \n %v \n but instead received \n %v",
-                       blocks,
-                       blockToCollectionIndices,
-                       expectedCollections,
-                       returned.ToSlice())
-       }
-}
-
-func TestToCollectionIndexSet(t *testing.T) {
-       VerifyToCollectionIndexSet(t, []int{6}, map[int][]int{6: {0}}, []int{0})
-       VerifyToCollectionIndexSet(t, []int{4}, map[int][]int{4: {1}}, []int{1})
-       VerifyToCollectionIndexSet(t, []int{4}, map[int][]int{4: {1, 9}}, []int{1, 9})
-       VerifyToCollectionIndexSet(t, []int{5, 6},
-               map[int][]int{5: {2, 3}, 6: {3, 4}},
-               []int{2, 3, 4})
-       VerifyToCollectionIndexSet(t, []int{5, 6},
-               map[int][]int{5: {8}, 6: {4}},
-               []int{4, 8})
-       VerifyToCollectionIndexSet(t, []int{6}, map[int][]int{5: {0}}, []int{})
-}
-
-func TestSimpleSummary(t *testing.T) {
-       rc := collection.MakeTestReadCollections([]collection.TestCollectionSpec{
-               {ReplicationLevel: 1, Blocks: []int{1, 2}},
-       })
-       rc.Summarize(nil)
-       cIndex := rc.CollectionIndicesForTesting()
-
-       keepInfo := SpecifyReplication(map[int]int{1: 1, 2: 1})
-
-       expectedSummary := ReplicationSummary{
-               CollectionBlocksNotInKeep:  BlockSet{},
-               UnderReplicatedBlocks:      BlockSet{},
-               OverReplicatedBlocks:       BlockSet{},
-               CorrectlyReplicatedBlocks:  BlockSetFromSlice([]int{1, 2}),
-               KeepBlocksNotInCollections: BlockSet{},
-
-               CollectionsNotFullyInKeep:      CollectionIndexSet{},
-               UnderReplicatedCollections:     CollectionIndexSet{},
-               OverReplicatedCollections:      CollectionIndexSet{},
-               CorrectlyReplicatedCollections: CollectionIndexSetFromSlice([]int{cIndex[0]}),
-       }
-
-       returnedSummary := SummarizeReplication(rc, keepInfo)
-
-       if !reflect.DeepEqual(returnedSummary, expectedSummary) {
-               t.Fatalf("Expected returnedSummary to look like %+v but instead it is %+v", expectedSummary, returnedSummary)
-       }
-}
-
-func TestMissingBlock(t *testing.T) {
-       rc := collection.MakeTestReadCollections([]collection.TestCollectionSpec{
-               {ReplicationLevel: 1, Blocks: []int{1, 2}},
-       })
-       rc.Summarize(nil)
-       cIndex := rc.CollectionIndicesForTesting()
-
-       keepInfo := SpecifyReplication(map[int]int{1: 1})
-
-       expectedSummary := ReplicationSummary{
-               CollectionBlocksNotInKeep:  BlockSetFromSlice([]int{2}),
-               UnderReplicatedBlocks:      BlockSet{},
-               OverReplicatedBlocks:       BlockSet{},
-               CorrectlyReplicatedBlocks:  BlockSetFromSlice([]int{1}),
-               KeepBlocksNotInCollections: BlockSet{},
-
-               CollectionsNotFullyInKeep:      CollectionIndexSetFromSlice([]int{cIndex[0]}),
-               UnderReplicatedCollections:     CollectionIndexSet{},
-               OverReplicatedCollections:      CollectionIndexSet{},
-               CorrectlyReplicatedCollections: CollectionIndexSet{},
-       }
-
-       returnedSummary := SummarizeReplication(rc, keepInfo)
-
-       if !reflect.DeepEqual(returnedSummary, expectedSummary) {
-               t.Fatalf("Expected returnedSummary to look like %+v but instead it is %+v",
-                       expectedSummary,
-                       returnedSummary)
-       }
-}
-
-func TestUnderAndOverReplicatedBlocks(t *testing.T) {
-       rc := collection.MakeTestReadCollections([]collection.TestCollectionSpec{
-               {ReplicationLevel: 2, Blocks: []int{1, 2}},
-       })
-       rc.Summarize(nil)
-       cIndex := rc.CollectionIndicesForTesting()
-
-       keepInfo := SpecifyReplication(map[int]int{1: 1, 2: 3})
-
-       expectedSummary := ReplicationSummary{
-               CollectionBlocksNotInKeep:  BlockSet{},
-               UnderReplicatedBlocks:      BlockSetFromSlice([]int{1}),
-               OverReplicatedBlocks:       BlockSetFromSlice([]int{2}),
-               CorrectlyReplicatedBlocks:  BlockSet{},
-               KeepBlocksNotInCollections: BlockSet{},
-
-               CollectionsNotFullyInKeep:      CollectionIndexSet{},
-               UnderReplicatedCollections:     CollectionIndexSetFromSlice([]int{cIndex[0]}),
-               OverReplicatedCollections:      CollectionIndexSetFromSlice([]int{cIndex[0]}),
-               CorrectlyReplicatedCollections: CollectionIndexSet{},
-       }
-
-       returnedSummary := SummarizeReplication(rc, keepInfo)
-
-       if !reflect.DeepEqual(returnedSummary, expectedSummary) {
-               t.Fatalf("Expected returnedSummary to look like %+v but instead it is %+v",
-                       expectedSummary,
-                       returnedSummary)
-       }
-}
-
-func TestMixedReplication(t *testing.T) {
-       rc := collection.MakeTestReadCollections([]collection.TestCollectionSpec{
-               {ReplicationLevel: 1, Blocks: []int{1, 2}},
-               {ReplicationLevel: 1, Blocks: []int{3, 4}},
-               {ReplicationLevel: 2, Blocks: []int{5, 6}},
-       })
-       rc.Summarize(nil)
-       cIndex := rc.CollectionIndicesForTesting()
-
-       keepInfo := SpecifyReplication(map[int]int{1: 1, 2: 1, 3: 1, 5: 1, 6: 3, 7: 2})
-
-       expectedSummary := ReplicationSummary{
-               CollectionBlocksNotInKeep:  BlockSetFromSlice([]int{4}),
-               UnderReplicatedBlocks:      BlockSetFromSlice([]int{5}),
-               OverReplicatedBlocks:       BlockSetFromSlice([]int{6}),
-               CorrectlyReplicatedBlocks:  BlockSetFromSlice([]int{1, 2, 3}),
-               KeepBlocksNotInCollections: BlockSetFromSlice([]int{7}),
-
-               CollectionsNotFullyInKeep:      CollectionIndexSetFromSlice([]int{cIndex[1]}),
-               UnderReplicatedCollections:     CollectionIndexSetFromSlice([]int{cIndex[2]}),
-               OverReplicatedCollections:      CollectionIndexSetFromSlice([]int{cIndex[2]}),
-               CorrectlyReplicatedCollections: CollectionIndexSetFromSlice([]int{cIndex[0]}),
-       }
-
-       returnedSummary := SummarizeReplication(rc, keepInfo)
-
-       if !reflect.DeepEqual(returnedSummary, expectedSummary) {
-               t.Fatalf("Expected returnedSummary to look like: \n%+v but instead it is: \n%+v. Index to UUID is %v. BlockToCollectionIndices is %v.", expectedSummary, returnedSummary, rc.CollectionIndexToUUID, rc.BlockToCollectionIndices)
-       }
-}
diff --git a/services/datamanager/summary/trash_list.go b/services/datamanager/summary/trash_list.go
deleted file mode 100644 (file)
index 3e4d387..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-// Code for generating trash lists
-
-package summary
-
-import (
-       "errors"
-       "fmt"
-       "git.curoverse.com/arvados.git/sdk/go/keepclient"
-       "git.curoverse.com/arvados.git/services/datamanager/keep"
-       "time"
-)
-
-// BuildTrashLists builds list of blocks to be sent to trash queue
-func BuildTrashLists(kc *keepclient.KeepClient,
-       keepServerInfo *keep.ReadServers,
-       keepBlocksNotInCollections BlockSet) (m map[string]keep.TrashList, err error) {
-
-       // Servers that are writeable
-       writableServers := map[string]struct{}{}
-       for _, url := range kc.WritableLocalRoots() {
-               writableServers[url] = struct{}{}
-       }
-
-       _ttl, err := kc.Arvados.Discovery("blobSignatureTtl")
-       if err != nil {
-               return nil, errors.New(fmt.Sprintf("Failed to get blobSignatureTtl, can't build trash lists: %v", err))
-       }
-
-       ttl := int64(_ttl.(float64))
-
-       // expire unreferenced blocks more than "ttl" seconds old.
-       expiry := time.Now().UTC().UnixNano() - ttl*1e9
-
-       return buildTrashListsInternal(writableServers, keepServerInfo, expiry, keepBlocksNotInCollections), nil
-}
-
-func buildTrashListsInternal(writableServers map[string]struct{},
-       keepServerInfo *keep.ReadServers,
-       expiry int64,
-       keepBlocksNotInCollections BlockSet) (m map[string]keep.TrashList) {
-
-       m = make(map[string]keep.TrashList)
-
-       for block := range keepBlocksNotInCollections {
-               for _, blockOnServer := range keepServerInfo.BlockToServers[block] {
-                       if blockOnServer.Mtime >= expiry {
-                               continue
-                       }
-
-                       // block is older than expire cutoff
-                       srv := keepServerInfo.KeepServerIndexToAddress[blockOnServer.ServerIndex].String()
-
-                       if _, writable := writableServers[srv]; !writable {
-                               continue
-                       }
-
-                       m[srv] = append(m[srv], keep.TrashRequest{Locator: block.Digest.String(), BlockMtime: blockOnServer.Mtime})
-               }
-       }
-       return
-
-}
diff --git a/services/datamanager/summary/trash_list_test.go b/services/datamanager/summary/trash_list_test.go
deleted file mode 100644 (file)
index 3626904..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-package summary
-
-import (
-       "git.curoverse.com/arvados.git/sdk/go/blockdigest"
-       "git.curoverse.com/arvados.git/services/datamanager/keep"
-       . "gopkg.in/check.v1"
-       "testing"
-)
-
-// Gocheck boilerplate
-func TestTrash(t *testing.T) {
-       TestingT(t)
-}
-
-type TrashSuite struct{}
-
-var _ = Suite(&TrashSuite{})
-
-func (s *TrashSuite) TestBuildTrashLists(c *C) {
-       var sv0 = keep.ServerAddress{Host: "keep0.example.com", Port: 80}
-       var sv1 = keep.ServerAddress{Host: "keep1.example.com", Port: 80}
-
-       var block0 = blockdigest.MakeTestDigestWithSize(0xdeadbeef)
-       var block1 = blockdigest.MakeTestDigestWithSize(0xfedbeef)
-
-       var keepServerInfo = keep.ReadServers{
-               KeepServerIndexToAddress: []keep.ServerAddress{sv0, sv1},
-               BlockToServers: map[blockdigest.DigestWithSize][]keep.BlockServerInfo{
-                       block0: {
-                               {0, 99},
-                               {1, 101}},
-                       block1: {
-                               {0, 99},
-                               {1, 101}}}}
-
-       // only block0 is in delete set
-       var bs = make(BlockSet)
-       bs[block0] = struct{}{}
-
-       // Test trash list where only sv0 is on writable list.
-       c.Check(buildTrashListsInternal(
-               map[string]struct{}{
-                       sv0.URL(): {}},
-               &keepServerInfo,
-               110,
-               bs),
-               DeepEquals,
-               map[string]keep.TrashList{
-                       "http://keep0.example.com:80": {keep.TrashRequest{"000000000000000000000000deadbeef", 99}}})
-
-       // Test trash list where both sv0 and sv1 are on writable list.
-       c.Check(buildTrashListsInternal(
-               map[string]struct{}{
-                       sv0.URL(): {},
-                       sv1.URL(): {}},
-               &keepServerInfo,
-               110,
-               bs),
-               DeepEquals,
-               map[string]keep.TrashList{
-                       "http://keep0.example.com:80": {keep.TrashRequest{"000000000000000000000000deadbeef", 99}},
-                       "http://keep1.example.com:80": {keep.TrashRequest{"000000000000000000000000deadbeef", 101}}})
-
-       // Test trash list where only block on sv0 is expired
-       c.Check(buildTrashListsInternal(
-               map[string]struct{}{
-                       sv0.URL(): {},
-                       sv1.URL(): {}},
-               &keepServerInfo,
-               100,
-               bs),
-               DeepEquals,
-               map[string]keep.TrashList{
-                       "http://keep0.example.com:80": {keep.TrashRequest{"000000000000000000000000deadbeef", 99}}})
-
-}
index 1828e150bb76bdf6185f7fcbb3fe3172fb68e616..1d25aa83cba34c2753579e2f56423db22f97a310 100644 (file)
@@ -74,7 +74,12 @@ import Queue
 # unlimited to avoid deadlocks, see https://arvados.org/issues/3198#note-43 for
 # details.
 
-llfuse.capi._notify_queue = Queue.Queue()
+if hasattr(llfuse, 'capi'):
+    # llfuse < 0.42
+    llfuse.capi._notify_queue = Queue.Queue()
+else:
+    # llfuse >= 0.42
+    llfuse._notify_queue = Queue.Queue()
 
 from fusedir import sanitize_filename, Directory, CollectionDirectory, TmpCollectionDirectory, MagicDirectory, TagsDirectory, ProjectDirectory, SharedDirectory, CollectionDirectoryBase
 from fusefile import StringFile, FuseArvadosFile
@@ -355,12 +360,17 @@ class Operations(llfuse.Operations):
 
     @catch_exceptions
     def destroy(self):
-        with llfuse.lock:
-            self._shutdown_started.set()
-            if self.events:
-                self.events.close()
-                self.events = None
+        self._shutdown_started.set()
+        if self.events:
+            self.events.close()
+            self.events = None
 
+        if llfuse.lock.acquire():
+            # llfuse < 0.42
+            self.inodes.clear()
+            llfuse.lock.release()
+        else:
+            # llfuse >= 0.42
             self.inodes.clear()
 
     def access(self, inode, mode, ctx):
@@ -377,38 +387,30 @@ class Operations(llfuse.Operations):
         if 'event_type' not in ev:
             return
         with llfuse.lock:
+            new_attrs = (ev.get("properties") or {}).get("new_attributes") or {}
+            pdh = new_attrs.get("portable_data_hash")
+            # new_attributes.modified_at currently lacks
+            # subsecond precision (see #6347) so use event_at
+            # which should always be the same.
+            stamp = ev.get("event_at")
+
             for item in self.inodes.inode_cache.find_by_uuid(ev["object_uuid"]):
                 item.invalidate()
-                if ev["object_kind"] == "arvados#collection":
-                    new_attr = (ev.get("properties") and
-                                ev["properties"].get("new_attributes") and
-                                ev["properties"]["new_attributes"])
-
-                    # new_attributes.modified_at currently lacks
-                    # subsecond precision (see #6347) so use event_at
-                    # which should always be the same.
-                    record_version = (
-                        (ev["event_at"], new_attr["portable_data_hash"])
-                        if new_attr else None)
-
-                    item.update(to_record_version=record_version)
+                if stamp and pdh and ev.get("object_kind") == "arvados#collection":
+                    item.update(to_record_version=(stamp, pdh))
                 else:
                     item.update()
 
-            oldowner = (
-                ev.get("properties") and
-                ev["properties"].get("old_attributes") and
-                ev["properties"]["old_attributes"].get("owner_uuid"))
-            newowner = ev["object_owner_uuid"]
+            oldowner = ((ev.get("properties") or {}).get("old_attributes") or {}).get("owner_uuid")
+            newowner = ev.get("object_owner_uuid")
             for parent in (
                     self.inodes.inode_cache.find_by_uuid(oldowner) +
                     self.inodes.inode_cache.find_by_uuid(newowner)):
                 parent.invalidate()
                 parent.update()
 
-
     @catch_exceptions
-    def getattr(self, inode):
+    def getattr(self, inode, ctx=None):
         if inode not in self.inodes:
             raise llfuse.FUSEError(errno.ENOENT)
 
@@ -440,19 +442,36 @@ class Operations(llfuse.Operations):
 
         entry.st_blksize = 512
         entry.st_blocks = (entry.st_size/512)+1
-        entry.st_atime = int(e.atime())
-        entry.st_mtime = int(e.mtime())
-        entry.st_ctime = int(e.mtime())
+        if hasattr(entry, 'st_atime_ns'):
+            # llfuse >= 0.42
+            entry.st_atime_ns = int(e.atime() * 1000000000)
+            entry.st_mtime_ns = int(e.mtime() * 1000000000)
+            entry.st_ctime_ns = int(e.mtime() * 1000000000)
+        else:
+            # llfuse < 0.42
+            entry.st_atime = int(e.atime)
+            entry.st_mtime = int(e.mtime)
+            entry.st_ctime = int(e.mtime)
 
         return entry
 
     @catch_exceptions
-    def setattr(self, inode, attr):
+    def setattr(self, inode, attr, fields=None, fh=None, ctx=None):
         entry = self.getattr(inode)
 
-        e = self.inodes[inode]
+        if fh is not None and fh in self._filehandles:
+            handle = self._filehandles[fh]
+            e = handle.obj
+        else:
+            e = self.inodes[inode]
 
-        if attr.st_size is not None and isinstance(e, FuseArvadosFile):
+        if fields is None:
+            # llfuse < 0.42
+            update_size = attr.st_size is not None
+        else:
+            # llfuse >= 0.42
+            update_size = fields.update_size
+        if update_size and isinstance(e, FuseArvadosFile):
             with llfuse.lock_released:
                 e.arvfile.truncate(attr.st_size)
                 entry.st_size = e.arvfile.size()
@@ -460,7 +479,7 @@ class Operations(llfuse.Operations):
         return entry
 
     @catch_exceptions
-    def lookup(self, parent_inode, name):
+    def lookup(self, parent_inode, name, ctx=None):
         name = unicode(name, self.inodes.encoding)
         inode = None
 
@@ -496,7 +515,7 @@ class Operations(llfuse.Operations):
                 self.inodes.del_entry(ent)
 
     @catch_exceptions
-    def open(self, inode, flags):
+    def open(self, inode, flags, ctx=None):
         if inode in self.inodes:
             p = self.inodes[inode]
         else:
@@ -583,7 +602,7 @@ class Operations(llfuse.Operations):
         self.release(fh)
 
     @catch_exceptions
-    def opendir(self, inode):
+    def opendir(self, inode, ctx=None):
         _logger.debug("arv-mount opendir: inode %i", inode)
 
         if inode in self.inodes:
@@ -622,7 +641,7 @@ class Operations(llfuse.Operations):
             e += 1
 
     @catch_exceptions
-    def statfs(self):
+    def statfs(self, ctx=None):
         st = llfuse.StatvfsData()
         st.f_bsize = 128 * 1024
         st.f_blocks = 0
@@ -655,7 +674,7 @@ class Operations(llfuse.Operations):
         return p
 
     @catch_exceptions
-    def create(self, inode_parent, name, mode, flags, ctx):
+    def create(self, inode_parent, name, mode, flags, ctx=None):
         _logger.debug("arv-mount create: parent_inode %i '%s' %o", inode_parent, name, mode)
 
         p = self._check_writable(inode_parent)
@@ -671,7 +690,7 @@ class Operations(llfuse.Operations):
         return (fh, self.getattr(f.inode))
 
     @catch_exceptions
-    def mkdir(self, inode_parent, name, mode, ctx):
+    def mkdir(self, inode_parent, name, mode, ctx=None):
         _logger.debug("arv-mount mkdir: parent_inode %i '%s' %o", inode_parent, name, mode)
 
         p = self._check_writable(inode_parent)
@@ -684,19 +703,19 @@ class Operations(llfuse.Operations):
         return self.getattr(d.inode)
 
     @catch_exceptions
-    def unlink(self, inode_parent, name):
+    def unlink(self, inode_parent, name, ctx=None):
         _logger.debug("arv-mount unlink: parent_inode %i '%s'", inode_parent, name)
         p = self._check_writable(inode_parent)
         p.unlink(name)
 
     @catch_exceptions
-    def rmdir(self, inode_parent, name):
+    def rmdir(self, inode_parent, name, ctx=None):
         _logger.debug("arv-mount rmdir: parent_inode %i '%s'", inode_parent, name)
         p = self._check_writable(inode_parent)
         p.rmdir(name)
 
     @catch_exceptions
-    def rename(self, inode_parent_old, name_old, inode_parent_new, name_new):
+    def rename(self, inode_parent_old, name_old, inode_parent_new, name_new, ctx=None):
         _logger.debug("arv-mount rename: old_parent_inode %i '%s' new_parent_inode %i '%s'", inode_parent_old, name_old, inode_parent_new, name_new)
         src = self._check_writable(inode_parent_old)
         dest = self._check_writable(inode_parent_new)
diff --git a/services/fuse/arvados_fuse/_version.py b/services/fuse/arvados_fuse/_version.py
new file mode 100644 (file)
index 0000000..837d4b9
--- /dev/null
@@ -0,0 +1,3 @@
+import pkg_resources
+
+__version__ = pkg_resources.require('arvados_fuse')[0].version
index 3f89732bea25dcd1ca546fbef126227e9e0a9256..ffcfc6500f5c5ac31289da3c404166b386f74374 100644 (file)
@@ -13,6 +13,7 @@ import time
 import arvados.commands._util as arv_cmd
 from arvados_fuse import crunchstat
 from arvados_fuse import *
+from arvados_fuse._version import __version__
 
 class ArgumentParser(argparse.ArgumentParser):
     def __init__(self):
@@ -24,6 +25,9 @@ class ArgumentParser(argparse.ArgumentParser):
     mountpoint before --exec, or mark the end of your --exec arguments
     with "--".
             """)
+        self.add_argument('--version', action='version',
+                          version="%s %s" % (sys.argv[0], __version__),
+                          help='Print version and exit.')
         self.add_argument('mountpoint', type=str, help="""Mount point.""")
         self.add_argument('--allow-other', action='store_true',
                             help="""Let other users read the mount""")
@@ -122,6 +126,8 @@ class Mount(object):
         return self
 
     def __exit__(self, exc_type, exc_value, traceback):
+        if self.operations.events:
+            self.operations.events.close(timeout=self.args.unmount_timeout)
         subprocess.call(["fusermount", "-u", "-z", self.args.mountpoint])
         self.llfuse_thread.join(timeout=self.args.unmount_timeout)
         if self.llfuse_thread.is_alive():
index d7e1a8afb302b26ae582bc5a3a5aaecc9514ae7c..9e282caf49919972b3fefe60001c603ae8176305 100644 (file)
@@ -40,7 +40,8 @@ setup(name='arvados_fuse',
         'arvados-python-client >= 0.1.20151118035730',
         'llfuse==0.41.1',
         'python-daemon',
-        'ciso8601'
+        'ciso8601',
+        'setuptools'
         ],
       test_suite='tests',
       tests_require=['pbr<1.7.0', 'mock>=1.0', 'PyYAML'],
index 20192f9d84302e1d9967136bcfa19e03ef7012dd..1319aebdccaa1e9dcc0f3e4323fa66340cc05a7f 100644 (file)
@@ -66,6 +66,8 @@ class MountTestBase(unittest.TestCase):
 
     def tearDown(self):
         if self.llfuse_thread:
+            if self.operations.events:
+                self.operations.events.close(timeout=10)
             subprocess.call(["fusermount", "-u", "-z", self.mounttmp])
             t0 = time.time()
             self.llfuse_thread.join(timeout=10)
index e8488d7ff967179423f3732c8e6e56b05194ed58..57b4a37826d6c4b4a73a22c6c51716021367f22b 100644 (file)
@@ -3,6 +3,7 @@ import arvados_fuse
 import arvados_fuse.command
 import contextlib
 import functools
+import io
 import json
 import llfuse
 import logging
@@ -48,6 +49,14 @@ class MountArgsTest(unittest.TestCase):
             ent = ent[p]
         return ent
 
+    @contextlib.contextmanager
+    def stderrMatches(self, stderr):
+        orig, sys.stderr = sys.stderr, stderr
+        try:
+            yield
+        finally:
+            sys.stderr = orig
+
     def check_ent_type(self, cls, *path):
         ent = self.lookup(self.mnt, *path)
         self.assertEqual(ent.__class__, cls)
@@ -170,6 +179,13 @@ class MountArgsTest(unittest.TestCase):
                          run_test_server.fixture('users')['active']['uuid'])
         self.assertEqual(True, self.mnt.listen_for_events)
 
+    def test_version_argument(self):
+        orig, sys.stderr = sys.stderr, io.BytesIO()
+        with self.assertRaises(SystemExit):
+            args = arvados_fuse.command.ArgumentParser().parse_args(['--version'])
+        self.assertRegexpMatches(sys.stderr.getvalue(), "[0-9]+\.[0-9]+\.[0-9]+")
+        sys.stderr = orig
+
     @noexit
     @mock.patch('arvados.events.subscribe')
     def test_disable_event_listening(self, mock_subscribe):
index 4c70ce98d3d36bb2d28f77b3ad30a5bbfda42238..f623ae538633a75d87fc9b520c740f6ae117f6c8 100644 (file)
@@ -267,14 +267,22 @@ class FuseSharedTest(MountTestBase):
 
         # check mtime on template
         st = os.stat(pipeline_template_path)
-        self.assertEqual(st.st_mtime, 1397493304)
+        try:
+            mtime = st.st_mtime_ns / 1000000000
+        except AttributeError:
+            mtime = st.st_mtime
+        self.assertEqual(mtime, 1397493304)
 
         # check mtime on collection
         st = os.stat(os.path.join(
                 self.mounttmp,
                 'FUSE User',
                 'collection #1 owned by FUSE'))
-        self.assertEqual(st.st_mtime, 1391448174)
+        try:
+            mtime = st.st_mtime_ns / 1000000000
+        except AttributeError:
+            mtime = st.st_mtime
+        self.assertEqual(mtime, 1391448174)
 
 
 class FuseHomeTest(MountTestBase):
index b46ba7839f72f2a578fb6fbcdcbe8fbe9e71d681..81d5c86072d8b0f6e7008235f128adfda2857550 100644 (file)
@@ -50,7 +50,7 @@ class RetryPUT(IntegrationTest):
         q.put(mockedCurl)
         q.put(pycurl.Curl())
         q.put(pycurl.Curl())
-        with mock.patch('arvados.keep.KeepClient.KeepService._get_user_agent', side_effect=lambda: q.get(block=None)):
+        with mock.patch('arvados.keep.KeepClient.KeepService._get_user_agent', side_effect=q.get_nowait):
             self.pool_test(os.path.join(self.mnt, 'zzz'))
             self.assertTrue(mockedCurl.perform.called)
     @staticmethod
index 8fc06c3534b76054cecbfdb1116007579952bcb1..9389f19ed801cf1ee840642d2078b70de8aa9e50 100644 (file)
@@ -246,7 +246,7 @@ func (bal *Balancer) GetCurrentState(c *arvados.Client, pageSize, bufs int) erro
                        }
                        if len(errs) > 0 {
                                // Some other goroutine encountered an
-                               // error -- any futher effort here
+                               // error -- any further effort here
                                // will be wasted.
                                return
                        }
index 23d74fe1198a4f9b91f840624b06af3ccbbe4ccb..30683b4228957386dcea27d709ded7d2cdbcfee7 100644 (file)
@@ -302,7 +302,7 @@ func (s *runSuite) TestDryRun(c *check.C) {
                Logger:      s.logger(c),
        }
        s.stub.serveCurrentUserAdmin()
-       s.stub.serveFooBarFileCollections()
+       collReqs := s.stub.serveFooBarFileCollections()
        s.stub.serveFourDiskKeepServices()
        s.stub.serveKeepstoreIndexFoo4Bar1()
        trashReqs := s.stub.serveKeepstoreTrash()
@@ -310,6 +310,9 @@ func (s *runSuite) TestDryRun(c *check.C) {
        var bal Balancer
        _, err := bal.Run(s.config, opts)
        c.Check(err, check.IsNil)
+       for _, req := range collReqs.reqs {
+               c.Check(req.Form.Get("include_trash"), check.Equals, "true")
+       }
        c.Check(trashReqs.Count(), check.Equals, 0)
        c.Check(pullReqs.Count(), check.Equals, 0)
        stats := bal.getStatistics()
index 060621b7715b55efd450d1753c57b296ef5d33fb..15556ed61282ad551fd171af4efce2813dc69f04 100644 (file)
@@ -31,7 +31,9 @@ func EachCollection(c *arvados.Client, pageSize int, f func(arvados.Collection)
                progress = func(_, _ int) {}
        }
 
-       expectCount, err := countCollections(c, arvados.ResourceListParams{})
+       expectCount, err := countCollections(c, arvados.ResourceListParams{
+               IncludeTrash: true,
+       })
        if err != nil {
                return err
        }
@@ -42,10 +44,11 @@ func EachCollection(c *arvados.Client, pageSize int, f func(arvados.Collection)
                limit = 1<<31 - 1
        }
        params := arvados.ResourceListParams{
-               Limit:  &limit,
-               Order:  "modified_at, uuid",
-               Count:  false,
-               Select: []string{"uuid", "manifest_text", "modified_at", "portable_data_hash", "replication_desired"},
+               Limit:        &limit,
+               Order:        "modified_at, uuid",
+               Count:        false,
+               Select:       []string{"uuid", "unsigned_manifest_text", "modified_at", "portable_data_hash", "replication_desired"},
+               IncludeTrash: true,
        }
        var last arvados.Collection
        var filterTime time.Time
@@ -91,10 +94,13 @@ func EachCollection(c *arvados.Client, pageSize int, f func(arvados.Collection)
        }
        progress(callCount, expectCount)
 
-       if checkCount, err := countCollections(c, arvados.ResourceListParams{Filters: []arvados.Filter{{
-               Attr:     "modified_at",
-               Operator: "<=",
-               Operand:  filterTime}}}); err != nil {
+       if checkCount, err := countCollections(c, arvados.ResourceListParams{
+               Filters: []arvados.Filter{{
+                       Attr:     "modified_at",
+                       Operator: "<=",
+                       Operand:  filterTime}},
+               IncludeTrash: true,
+       }); err != nil {
                return err
        } else if callCount < checkCount {
                return fmt.Errorf("Retrieved %d collections with modtime <= T=%q, but server now reports there are %d collections with modtime <= T", callCount, filterTime, checkCount)
index 157e42cc55122f7fc7aacd8ebfd6b5832a29de64..a6f5b6e349a69e2c12b297f048f5e4e7d3d2decf 100644 (file)
@@ -6,7 +6,7 @@ AssertPathExists=/etc/arvados/keep-balance/keep-balance.yml
 
 [Service]
 Type=simple
-ExecStart=/usr/bin/keep-balance -config /etc/arvados/keep-balance/keep-balance.yml -commit-pulls -commit-trash
+ExecStart=/usr/bin/keep-balance -commit-pulls -commit-trash
 Restart=always
 RestartSec=10s
 
index 60349e91d8b9064c9715db9c701cd9b11b9ef8d3..310c77a21c228c12779de5ac39c686cc08d7b624 100644 (file)
@@ -13,6 +13,8 @@ import (
        "git.curoverse.com/arvados.git/sdk/go/config"
 )
 
+const defaultConfigPath = "/etc/arvados/keep-balance/keep-balance.yml"
+
 // Config specifies site configuration, like API credentials and the
 // choice of which servers are to be balanced.
 //
@@ -65,7 +67,7 @@ func main() {
        var config Config
        var runOptions RunOptions
 
-       configPath := flag.String("config", "",
+       configPath := flag.String("config", defaultConfigPath,
                "`path` of JSON or YAML configuration file")
        serviceListPath := flag.String("config.KeepServiceList", "",
                "`path` of JSON or YAML file with list of keep services to balance, as given by \"arv keep_service list\" "+
@@ -81,9 +83,6 @@ func main() {
        flag.Usage = usage
        flag.Parse()
 
-       if *configPath == "" {
-               log.Fatal("You must specify a config file (see `keep-balance -help`)")
-       }
        mustReadConfig(&config, *configPath)
        if *serviceListPath != "" {
                mustReadConfig(&config.KeepServiceList, *serviceListPath)
index d11201047bff92752c89cfd3a3183b6f08dd3371..6f48af116f1091b9307aff10c62b21eca9beb15e 100644 (file)
@@ -26,7 +26,7 @@ overreplicated and unreferenced blocks, and moves blocks to better
 positions (according to the rendezvous hash algorithm) so clients find
 them faster.
 
-Usage: keep-balance -config path/to/keep-balance.yml [options]
+Usage: keep-balance [options]
 
 Options:
 `)
index 13d8c1b329ab6583fec2a3d4f281d61a55036ca2..df8a0b5c078b31d3a42b51786e3d4f6d0f15477f 100644 (file)
@@ -83,7 +83,7 @@ func main() {
        if err := srv.Start(); err != nil {
                log.Fatal(err)
        }
-       if _, err := daemon.SdNotify("READY=1"); err != nil {
+       if _, err := daemon.SdNotify(false, "READY=1"); err != nil {
                log.Printf("Error notifying init daemon: %v", err)
        }
        log.Println("Listening at", srv.Addr)
index 816de29da8abc4bad7ddcf22afcb572a7b16da69..24df531fa4ab434fea7652503db1d44f1ba04a9b 100644 (file)
@@ -131,7 +131,7 @@ func main() {
        if err != nil {
                log.Fatalf("listen(%s): %s", cfg.Listen, err)
        }
-       if _, err := daemon.SdNotify("READY=1"); err != nil {
+       if _, err := daemon.SdNotify(false, "READY=1"); err != nil {
                log.Printf("Error notifying init daemon: %v", err)
        }
        log.Println("Listening at", listener.Addr())
index 6ca31c38329ec7347631a03c802b4216bd05f167..16f1a0bd97c427446cd8325eec1e4ae130ef5f47 100644 (file)
@@ -8,7 +8,6 @@ import (
        "fmt"
        "io"
        "io/ioutil"
-       "log"
        "net/http"
        "os"
        "regexp"
@@ -18,6 +17,7 @@ import (
        "time"
 
        "git.curoverse.com/arvados.git/sdk/go/arvados"
+       log "github.com/Sirupsen/logrus"
        "github.com/curoverse/azure-sdk-for-go/storage"
 )
 
@@ -97,13 +97,14 @@ func init() {
 type AzureBlobVolume struct {
        StorageAccountName    string
        StorageAccountKeyFile string
+       StorageBaseURL        string // "" means default, "core.windows.net"
        ContainerName         string
        AzureReplication      int
        ReadOnly              bool
        RequestTimeout        arvados.Duration
 
        azClient storage.Client
-       bsClient storage.BlobStorageClient
+       bsClient *azureBlobClient
 }
 
 // Examples implements VolumeWithExamples.
@@ -116,6 +117,14 @@ func (*AzureBlobVolume) Examples() []Volume {
                        AzureReplication:      3,
                        RequestTimeout:        azureDefaultRequestTimeout,
                },
+               &AzureBlobVolume{
+                       StorageAccountName:    "cn-account-name",
+                       StorageAccountKeyFile: "/etc/azure_cn_storage_account_key.txt",
+                       StorageBaseURL:        "core.chinacloudapi.cn",
+                       ContainerName:         "cn-container-name",
+                       AzureReplication:      3,
+                       RequestTimeout:        azureDefaultRequestTimeout,
+               },
        }
 }
 
@@ -136,7 +145,10 @@ func (v *AzureBlobVolume) Start() error {
        if err != nil {
                return err
        }
-       v.azClient, err = storage.NewBasicClient(v.StorageAccountName, accountKey)
+       if v.StorageBaseURL == "" {
+               v.StorageBaseURL = storage.DefaultBaseURL
+       }
+       v.azClient, err = storage.NewClient(v.StorageAccountName, accountKey, v.StorageBaseURL, storage.DefaultAPIVersion, true)
        if err != nil {
                return fmt.Errorf("creating Azure storage client: %s", err)
        }
@@ -147,7 +159,10 @@ func (v *AzureBlobVolume) Start() error {
        v.azClient.HTTPClient = &http.Client{
                Timeout: time.Duration(v.RequestTimeout),
        }
-       v.bsClient = v.azClient.GetBlobService()
+       bs := v.azClient.GetBlobService()
+       v.bsClient = &azureBlobClient{
+               client: &bs,
+       }
 
        ok, err := v.bsClient.ContainerExists(v.ContainerName)
        if err != nil {
@@ -187,7 +202,7 @@ func (v *AzureBlobVolume) Get(ctx context.Context, loc string, buf []byte) (int,
        }
        var deadline time.Time
        haveDeadline := false
-       size, err := v.get(loc, buf)
+       size, err := v.get(ctx, loc, buf)
        for err == nil && size == 0 && loc != "d41d8cd98f00b204e9800998ecf8427e" {
                // Seeing a brand new empty block probably means we're
                // in a race with CreateBlob, which under the hood
@@ -208,8 +223,12 @@ func (v *AzureBlobVolume) Get(ctx context.Context, loc string, buf []byte) (int,
                } else if time.Now().After(deadline) {
                        break
                }
-               time.Sleep(azureWriteRacePollTime)
-               size, err = v.get(loc, buf)
+               select {
+               case <-ctx.Done():
+                       return 0, ctx.Err()
+               case <-time.After(azureWriteRacePollTime):
+               }
+               size, err = v.get(ctx, loc, buf)
        }
        if haveDeadline {
                log.Printf("Race ended with size==%d", size)
@@ -217,7 +236,9 @@ func (v *AzureBlobVolume) Get(ctx context.Context, loc string, buf []byte) (int,
        return size, err
 }
 
-func (v *AzureBlobVolume) get(loc string, buf []byte) (int, error) {
+func (v *AzureBlobVolume) get(ctx context.Context, loc string, buf []byte) (int, error) {
+       ctx, cancel := context.WithCancel(ctx)
+       defer cancel()
        expectSize := len(buf)
        if azureMaxGetBytes < BlockSize {
                // Unfortunately the handler doesn't tell us how long the blob
@@ -239,10 +260,18 @@ func (v *AzureBlobVolume) get(loc string, buf []byte) (int, error) {
        // We'll update this actualSize if/when we get the last piece.
        actualSize := -1
        pieces := (expectSize + azureMaxGetBytes - 1) / azureMaxGetBytes
-       errors := make([]error, pieces)
+       errors := make(chan error, pieces)
        var wg sync.WaitGroup
        wg.Add(pieces)
        for p := 0; p < pieces; p++ {
+               // Each goroutine retrieves one piece. If we hit an
+               // error, it is sent to the errors chan so get() can
+               // return it -- but only if the error happens before
+               // ctx is done. This way, if ctx is done before we hit
+               // any other error (e.g., requesting client has hung
+               // up), we return the original ctx.Err() instead of
+               // the secondary errors from the transfers that got
+               // interrupted as a result.
                go func(p int) {
                        defer wg.Done()
                        startPos := p * azureMaxGetBytes
@@ -252,23 +281,51 @@ func (v *AzureBlobVolume) get(loc string, buf []byte) (int, error) {
                        }
                        var rdr io.ReadCloser
                        var err error
-                       if startPos == 0 && endPos == expectSize {
-                               rdr, err = v.bsClient.GetBlob(v.ContainerName, loc)
-                       } else {
-                               rdr, err = v.bsClient.GetBlobRange(v.ContainerName, loc, fmt.Sprintf("%d-%d", startPos, endPos-1), nil)
+                       gotRdr := make(chan struct{})
+                       go func() {
+                               defer close(gotRdr)
+                               if startPos == 0 && endPos == expectSize {
+                                       rdr, err = v.bsClient.GetBlob(v.ContainerName, loc)
+                               } else {
+                                       rdr, err = v.bsClient.GetBlobRange(v.ContainerName, loc, fmt.Sprintf("%d-%d", startPos, endPos-1), nil)
+                               }
+                       }()
+                       select {
+                       case <-ctx.Done():
+                               go func() {
+                                       <-gotRdr
+                                       if err == nil {
+                                               rdr.Close()
+                                       }
+                               }()
+                               return
+                       case <-gotRdr:
                        }
                        if err != nil {
-                               errors[p] = err
+                               errors <- err
+                               cancel()
                                return
                        }
-                       defer rdr.Close()
+                       go func() {
+                               // Close the reader when the client
+                               // hangs up or another piece fails
+                               // (possibly interrupting ReadFull())
+                               // or when all pieces succeed and
+                               // get() returns.
+                               <-ctx.Done()
+                               rdr.Close()
+                       }()
                        n, err := io.ReadFull(rdr, buf[startPos:endPos])
                        if pieces == 1 && (err == io.ErrUnexpectedEOF || err == io.EOF) {
                                // If we don't know the actual size,
                                // and just tried reading 64 MiB, it's
                                // normal to encounter EOF.
                        } else if err != nil {
-                               errors[p] = err
+                               if ctx.Err() == nil {
+                                       errors <- err
+                               }
+                               cancel()
+                               return
                        }
                        if p == pieces-1 {
                                actualSize = startPos + n
@@ -276,10 +333,12 @@ func (v *AzureBlobVolume) get(loc string, buf []byte) (int, error) {
                }(p)
        }
        wg.Wait()
-       for _, err := range errors {
-               if err != nil {
-                       return 0, v.translateError(err)
-               }
+       close(errors)
+       if len(errors) > 0 {
+               return 0, v.translateError(<-errors)
+       }
+       if ctx.Err() != nil {
+               return 0, ctx.Err()
        }
        return actualSize, nil
 }
@@ -293,7 +352,23 @@ func (v *AzureBlobVolume) Compare(ctx context.Context, loc string, expect []byte
        if trashed {
                return os.ErrNotExist
        }
-       rdr, err := v.bsClient.GetBlob(v.ContainerName, loc)
+       var rdr io.ReadCloser
+       gotRdr := make(chan struct{})
+       go func() {
+               defer close(gotRdr)
+               rdr, err = v.bsClient.GetBlob(v.ContainerName, loc)
+       }()
+       select {
+       case <-ctx.Done():
+               go func() {
+                       <-gotRdr
+                       if err == nil {
+                               rdr.Close()
+                       }
+               }()
+               return ctx.Err()
+       case <-gotRdr:
+       }
        if err != nil {
                return v.translateError(err)
        }
@@ -306,7 +381,36 @@ func (v *AzureBlobVolume) Put(ctx context.Context, loc string, block []byte) err
        if v.ReadOnly {
                return MethodDisabledError
        }
-       return v.bsClient.CreateBlockBlobFromReader(v.ContainerName, loc, uint64(len(block)), bytes.NewReader(block), nil)
+       // Send the block data through a pipe, so that (if we need to)
+       // we can close the pipe early and abandon our
+       // CreateBlockBlobFromReader() goroutine, without worrying
+       // about CreateBlockBlobFromReader() accessing our block
+       // buffer after we release it.
+       bufr, bufw := io.Pipe()
+       go func() {
+               io.Copy(bufw, bytes.NewReader(block))
+               bufw.Close()
+       }()
+       errChan := make(chan error)
+       go func() {
+               errChan <- v.bsClient.CreateBlockBlobFromReader(v.ContainerName, loc, uint64(len(block)), bufr, nil)
+       }()
+       select {
+       case <-ctx.Done():
+               theConfig.debugLogf("%s: taking CreateBlockBlobFromReader's input away: %s", v, ctx.Err())
+               // Our pipe might be stuck in Write(), waiting for
+               // io.Copy() to read. If so, un-stick it. This means
+               // CreateBlockBlobFromReader will get corrupt data,
+               // but that's OK: the size won't match, so the write
+               // will fail.
+               go io.Copy(ioutil.Discard, bufr)
+               // CloseWithError() will return once pending I/O is done.
+               bufw.CloseWithError(ctx.Err())
+               theConfig.debugLogf("%s: abandoning CreateBlockBlobFromReader goroutine", v)
+               return ctx.Err()
+       case err := <-errChan:
+               return err
+       }
 }
 
 // Touch updates the last-modified property of a block blob.
@@ -534,3 +638,104 @@ func (v *AzureBlobVolume) EmptyTrash() {
 
        log.Printf("EmptyTrash stats for %v: Deleted %v bytes in %v blocks. Remaining in trash: %v bytes in %v blocks.", v.String(), bytesDeleted, blocksDeleted, bytesInTrash-bytesDeleted, blocksInTrash-blocksDeleted)
 }
+
+// InternalStats returns bucket I/O and API call counters.
+func (v *AzureBlobVolume) InternalStats() interface{} {
+       return &v.bsClient.stats
+}
+
+type azureBlobStats struct {
+       statsTicker
+       Ops              uint64
+       GetOps           uint64
+       GetRangeOps      uint64
+       GetMetadataOps   uint64
+       GetPropertiesOps uint64
+       CreateOps        uint64
+       SetMetadataOps   uint64
+       DelOps           uint64
+       ListOps          uint64
+}
+
+func (s *azureBlobStats) TickErr(err error) {
+       if err == nil {
+               return
+       }
+       errType := fmt.Sprintf("%T", err)
+       if err, ok := err.(storage.AzureStorageServiceError); ok {
+               errType = errType + fmt.Sprintf(" %d (%s)", err.StatusCode, err.Code)
+       }
+       log.Printf("errType %T, err %s", err, err)
+       s.statsTicker.TickErr(err, errType)
+}
+
+// azureBlobClient wraps storage.BlobStorageClient in order to count
+// I/O and API usage stats.
+type azureBlobClient struct {
+       client *storage.BlobStorageClient
+       stats  azureBlobStats
+}
+
+func (c *azureBlobClient) ContainerExists(cname string) (bool, error) {
+       c.stats.Tick(&c.stats.Ops)
+       ok, err := c.client.ContainerExists(cname)
+       c.stats.TickErr(err)
+       return ok, err
+}
+
+func (c *azureBlobClient) GetBlobMetadata(cname, bname string) (map[string]string, error) {
+       c.stats.Tick(&c.stats.Ops, &c.stats.GetMetadataOps)
+       m, err := c.client.GetBlobMetadata(cname, bname)
+       c.stats.TickErr(err)
+       return m, err
+}
+
+func (c *azureBlobClient) GetBlobProperties(cname, bname string) (*storage.BlobProperties, error) {
+       c.stats.Tick(&c.stats.Ops, &c.stats.GetPropertiesOps)
+       p, err := c.client.GetBlobProperties(cname, bname)
+       c.stats.TickErr(err)
+       return p, err
+}
+
+func (c *azureBlobClient) GetBlob(cname, bname string) (io.ReadCloser, error) {
+       c.stats.Tick(&c.stats.Ops, &c.stats.GetOps)
+       rdr, err := c.client.GetBlob(cname, bname)
+       c.stats.TickErr(err)
+       return NewCountingReader(rdr, c.stats.TickInBytes), err
+}
+
+func (c *azureBlobClient) GetBlobRange(cname, bname, byterange string, hdrs map[string]string) (io.ReadCloser, error) {
+       c.stats.Tick(&c.stats.Ops, &c.stats.GetRangeOps)
+       rdr, err := c.client.GetBlobRange(cname, bname, byterange, hdrs)
+       c.stats.TickErr(err)
+       return NewCountingReader(rdr, c.stats.TickInBytes), err
+}
+
+func (c *azureBlobClient) CreateBlockBlobFromReader(cname, bname string, size uint64, rdr io.Reader, hdrs map[string]string) error {
+       c.stats.Tick(&c.stats.Ops, &c.stats.CreateOps)
+       rdr = NewCountingReader(rdr, c.stats.TickOutBytes)
+       err := c.client.CreateBlockBlobFromReader(cname, bname, size, rdr, hdrs)
+       c.stats.TickErr(err)
+       return err
+}
+
+func (c *azureBlobClient) SetBlobMetadata(cname, bname string, m, hdrs map[string]string) error {
+       c.stats.Tick(&c.stats.Ops, &c.stats.SetMetadataOps)
+       err := c.client.SetBlobMetadata(cname, bname, m, hdrs)
+       c.stats.TickErr(err)
+       return err
+}
+
+func (c *azureBlobClient) ListBlobs(cname string, params storage.ListBlobsParameters) (storage.BlobListResponse, error) {
+       c.stats.Tick(&c.stats.Ops, &c.stats.ListOps)
+       resp, err := c.client.ListBlobs(cname, params)
+       c.stats.TickErr(err)
+       return resp, err
+}
+
+func (c *azureBlobClient) DeleteBlob(cname, bname string, hdrs map[string]string) error {
+       c.stats.Tick(&c.stats.Ops, &c.stats.DelOps)
+       err := c.client.DeleteBlob(cname, bname, hdrs)
+       c.stats.TickErr(err)
+       return err
+}
index d636a5ee86887806372a14e2f291e5c4f2c11b33..4b015a9962d170e47c9f8fdd401d6c63bdd569ea 100644 (file)
@@ -5,11 +5,11 @@ import (
        "context"
        "crypto/md5"
        "encoding/base64"
+       "encoding/json"
        "encoding/xml"
        "flag"
        "fmt"
        "io/ioutil"
-       "log"
        "math/rand"
        "net"
        "net/http"
@@ -22,13 +22,18 @@ import (
        "testing"
        "time"
 
+       log "github.com/Sirupsen/logrus"
        "github.com/curoverse/azure-sdk-for-go/storage"
+       check "gopkg.in/check.v1"
 )
 
 const (
-       // The same fake credentials used by Microsoft's Azure emulator
-       emulatorAccountName = "devstoreaccount1"
-       emulatorAccountKey  = "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw=="
+       // This cannot be the fake account name "devstoreaccount1"
+       // used by Microsoft's Azure emulator: the Azure SDK
+       // recognizes that magic string and changes its behavior to
+       // cater to the Azure SDK's own test suite.
+       fakeAccountName = "fakeAccountName"
+       fakeAccountKey  = "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw=="
 )
 
 var azureTestContainer string
@@ -350,7 +355,7 @@ func NewTestableAzureBlobVolume(t TB, readonly bool, replication int) *TestableA
                // Connect to stub instead of real Azure storage service
                stubURLBase := strings.Split(azStub.URL, "://")[1]
                var err error
-               if azClient, err = storage.NewClient(emulatorAccountName, emulatorAccountKey, stubURLBase, storage.DefaultAPIVersion, false); err != nil {
+               if azClient, err = storage.NewClient(fakeAccountName, fakeAccountKey, stubURLBase, storage.DefaultAPIVersion, false); err != nil {
                        t.Fatal(err)
                }
                container = "fakecontainername"
@@ -366,12 +371,13 @@ func NewTestableAzureBlobVolume(t TB, readonly bool, replication int) *TestableA
                }
        }
 
+       bs := azClient.GetBlobService()
        v := &AzureBlobVolume{
                ContainerName:    container,
                ReadOnly:         readonly,
                AzureReplication: replication,
                azClient:         azClient,
-               bsClient:         azClient.GetBlobService(),
+               bsClient:         &azureBlobClient{client: &bs},
        }
 
        return &TestableAzureBlobVolume{
@@ -382,6 +388,29 @@ func NewTestableAzureBlobVolume(t TB, readonly bool, replication int) *TestableA
        }
 }
 
+var _ = check.Suite(&StubbedAzureBlobSuite{})
+
+type StubbedAzureBlobSuite struct {
+       volume            *TestableAzureBlobVolume
+       origHTTPTransport http.RoundTripper
+}
+
+func (s *StubbedAzureBlobSuite) SetUpTest(c *check.C) {
+       s.origHTTPTransport = http.DefaultTransport
+       http.DefaultTransport = &http.Transport{
+               Dial: (&azStubDialer{}).Dial,
+       }
+       azureWriteRaceInterval = time.Millisecond
+       azureWriteRacePollTime = time.Nanosecond
+
+       s.volume = NewTestableAzureBlobVolume(c, false, 3)
+}
+
+func (s *StubbedAzureBlobSuite) TearDownTest(c *check.C) {
+       s.volume.Teardown()
+       http.DefaultTransport = s.origHTTPTransport
+}
+
 func TestAzureBlobVolumeWithGeneric(t *testing.T) {
        defer func(t http.RoundTripper) {
                http.DefaultTransport = t
@@ -466,10 +495,10 @@ func TestAzureBlobVolumeRangeFenceposts(t *testing.T) {
                }
                gotHash := fmt.Sprintf("%x", md5.Sum(gotData))
                if gotLen != size {
-                       t.Error("length mismatch: got %d != %d", gotLen, size)
+                       t.Errorf("length mismatch: got %d != %d", gotLen, size)
                }
                if gotHash != hash {
-                       t.Error("hash mismatch: got %s != %s", gotHash, hash)
+                       t.Errorf("hash mismatch: got %s != %s", gotHash, hash)
                }
        }
 }
@@ -576,6 +605,100 @@ func TestAzureBlobVolumeCreateBlobRaceDeadline(t *testing.T) {
        }
 }
 
+func TestAzureBlobVolumeContextCancelGet(t *testing.T) {
+       testAzureBlobVolumeContextCancel(t, func(ctx context.Context, v *TestableAzureBlobVolume) error {
+               v.PutRaw(TestHash, TestBlock)
+               _, err := v.Get(ctx, TestHash, make([]byte, BlockSize))
+               return err
+       })
+}
+
+func TestAzureBlobVolumeContextCancelPut(t *testing.T) {
+       testAzureBlobVolumeContextCancel(t, func(ctx context.Context, v *TestableAzureBlobVolume) error {
+               return v.Put(ctx, TestHash, make([]byte, BlockSize))
+       })
+}
+
+func TestAzureBlobVolumeContextCancelCompare(t *testing.T) {
+       testAzureBlobVolumeContextCancel(t, func(ctx context.Context, v *TestableAzureBlobVolume) error {
+               v.PutRaw(TestHash, TestBlock)
+               return v.Compare(ctx, TestHash, TestBlock2)
+       })
+}
+
+func testAzureBlobVolumeContextCancel(t *testing.T, testFunc func(context.Context, *TestableAzureBlobVolume) error) {
+       defer func(t http.RoundTripper) {
+               http.DefaultTransport = t
+       }(http.DefaultTransport)
+       http.DefaultTransport = &http.Transport{
+               Dial: (&azStubDialer{}).Dial,
+       }
+
+       v := NewTestableAzureBlobVolume(t, false, 3)
+       defer v.Teardown()
+       v.azHandler.race = make(chan chan struct{})
+
+       ctx, cancel := context.WithCancel(context.Background())
+       allDone := make(chan struct{})
+       go func() {
+               defer close(allDone)
+               err := testFunc(ctx, v)
+               if err != context.Canceled {
+                       t.Errorf("got %T %q, expected %q", err, err, context.Canceled)
+               }
+       }()
+       releaseHandler := make(chan struct{})
+       select {
+       case <-allDone:
+               t.Error("testFunc finished without waiting for v.azHandler.race")
+       case <-time.After(10 * time.Second):
+               t.Error("timed out waiting to enter handler")
+       case v.azHandler.race <- releaseHandler:
+       }
+
+       cancel()
+
+       select {
+       case <-time.After(10 * time.Second):
+               t.Error("timed out waiting to cancel")
+       case <-allDone:
+       }
+
+       go func() {
+               <-releaseHandler
+       }()
+}
+
+func (s *StubbedAzureBlobSuite) TestStats(c *check.C) {
+       stats := func() string {
+               buf, err := json.Marshal(s.volume.InternalStats())
+               c.Check(err, check.IsNil)
+               return string(buf)
+       }
+
+       c.Check(stats(), check.Matches, `.*"Ops":0,.*`)
+       c.Check(stats(), check.Matches, `.*"Errors":0,.*`)
+
+       loc := "acbd18db4cc2f85cedef654fccc4a4d8"
+       _, err := s.volume.Get(context.Background(), loc, make([]byte, 3))
+       c.Check(err, check.NotNil)
+       c.Check(stats(), check.Matches, `.*"Ops":[^0],.*`)
+       c.Check(stats(), check.Matches, `.*"Errors":[^0],.*`)
+       c.Check(stats(), check.Matches, `.*"storage\.AzureStorageServiceError 404 \(404 Not Found\)":[^0].*`)
+       c.Check(stats(), check.Matches, `.*"InBytes":0,.*`)
+
+       err = s.volume.Put(context.Background(), loc, []byte("foo"))
+       c.Check(err, check.IsNil)
+       c.Check(stats(), check.Matches, `.*"OutBytes":3,.*`)
+       c.Check(stats(), check.Matches, `.*"CreateOps":1,.*`)
+
+       _, err = s.volume.Get(context.Background(), loc, make([]byte, 3))
+       c.Check(err, check.IsNil)
+       _, err = s.volume.Get(context.Background(), loc, make([]byte, 3))
+       c.Check(err, check.IsNil)
+       c.Check(stats(), check.Matches, `.*"InBytes":6,.*`)
+}
+
 func (v *TestableAzureBlobVolume) PutRaw(locator string, data []byte) {
        v.azHandler.PutRaw(v.ContainerName, locator, data)
 }
index 9a3509424a3b10a1b8361c06be8475ddaa31f832..38f97aff1183d0da0fdaa05b3022765277c9a954 100644 (file)
@@ -1,10 +1,11 @@
 package main
 
 import (
-       "log"
        "sync"
        "sync/atomic"
        "time"
+
+       log "github.com/Sirupsen/logrus"
 )
 
 type bufferPool struct {
index dc06ef549877ba0316294e4a0e7767393ef4436d..83dd84ecc09d3ebf41c4407344b2387b3dc5fd4b 100644 (file)
@@ -5,17 +5,19 @@ import (
        "encoding/json"
        "fmt"
        "io/ioutil"
-       "log"
        "strings"
        "time"
 
        "git.curoverse.com/arvados.git/sdk/go/arvados"
+       log "github.com/Sirupsen/logrus"
 )
 
 type Config struct {
        Debug  bool
        Listen string
 
+       LogFormat string
+
        PIDFile string
 
        MaxBuffers  int
@@ -38,10 +40,13 @@ type Config struct {
 
 var theConfig = DefaultConfig()
 
+const rfc3339NanoFixed = "2006-01-02T15:04:05.000000000Z07:00"
+
 // DefaultConfig returns the default configuration.
 func DefaultConfig() *Config {
        return &Config{
                Listen:             ":25107",
+               LogFormat:          "json",
                MaxBuffers:         128,
                RequireSignatures:  true,
                BlobSignatureTTL:   arvados.Duration(14 * 24 * time.Hour),
@@ -55,12 +60,27 @@ func DefaultConfig() *Config {
 // fields, and before using the config.
 func (cfg *Config) Start() error {
        if cfg.Debug {
+               log.SetLevel(log.DebugLevel)
                cfg.debugLogf = log.Printf
                cfg.debugLogf("debugging enabled")
        } else {
                cfg.debugLogf = func(string, ...interface{}) {}
        }
 
+       switch strings.ToLower(cfg.LogFormat) {
+       case "text":
+               log.SetFormatter(&log.TextFormatter{
+                       FullTimestamp:   true,
+                       TimestampFormat: rfc3339NanoFixed,
+               })
+       case "json":
+               log.SetFormatter(&log.JSONFormatter{
+                       TimestampFormat: rfc3339NanoFixed,
+               })
+       default:
+               return fmt.Errorf(`unsupported log format %q (try "text" or "json")`, cfg.LogFormat)
+       }
+
        if cfg.MaxBuffers < 0 {
                return fmt.Errorf("MaxBuffers must be greater than zero")
        }
index eaa09042484388dd8185b2d8c79679034c6951f7..a6d46e5e4a1166967f5d3be9c22581d3c1401ef9 100644 (file)
@@ -1,7 +1,7 @@
 package main
 
 import (
-       "log"
+       log "github.com/Sirupsen/logrus"
 )
 
 func init() {
diff --git a/services/keepstore/count.go b/services/keepstore/count.go
new file mode 100644 (file)
index 0000000..a9f7436
--- /dev/null
@@ -0,0 +1,44 @@
+package main
+
+import (
+       "io"
+)
+
+func NewCountingWriter(w io.Writer, f func(uint64)) io.WriteCloser {
+       return &countingReadWriter{
+               writer:  w,
+               counter: f,
+       }
+}
+
+func NewCountingReader(r io.Reader, f func(uint64)) io.ReadCloser {
+       return &countingReadWriter{
+               reader:  r,
+               counter: f,
+       }
+}
+
+type countingReadWriter struct {
+       reader  io.Reader
+       writer  io.Writer
+       counter func(uint64)
+}
+
+func (crw *countingReadWriter) Read(buf []byte) (int, error) {
+       n, err := crw.reader.Read(buf)
+       crw.counter(uint64(n))
+       return n, err
+}
+
+func (crw *countingReadWriter) Write(buf []byte) (int, error) {
+       n, err := crw.writer.Write(buf)
+       crw.counter(uint64(n))
+       return n, err
+}
+
+func (crw *countingReadWriter) Close() error {
+       if c, ok := crw.writer.(io.Closer); ok {
+               return c.Close()
+       }
+       return nil
+}
index 9708b4e6be32f96645d500dfcd4319972f213d47..40b4839e06cc96fc05cd8eb5a2be4e73707ac03d 100644 (file)
@@ -958,7 +958,7 @@ func TestGetHandlerClientDisconnect(t *testing.T) {
        ok := make(chan struct{})
        go func() {
                req, _ := http.NewRequest("GET", fmt.Sprintf("/%s+%d", TestHash, len(TestBlock)), nil)
-               (&LoggingRESTRouter{MakeRESTRouter()}).ServeHTTP(resp, req)
+               (&LoggingRESTRouter{router: MakeRESTRouter()}).ServeHTTP(resp, req)
                ok <- struct{}{}
        }()
 
index 289dce15a06168572f5269d7fed82bdb31a75075..adaaa361e96177080a9df4e2b2f1d77aac98424d 100644 (file)
@@ -15,7 +15,6 @@ import (
        "fmt"
        "github.com/gorilla/mux"
        "io"
-       "log"
        "net/http"
        "os"
        "regexp"
@@ -24,13 +23,21 @@ import (
        "strings"
        "sync"
        "time"
+
+       "git.curoverse.com/arvados.git/sdk/go/httpserver"
+       log "github.com/Sirupsen/logrus"
 )
 
-// MakeRESTRouter returns a new mux.Router that forwards all Keep
-// requests to the appropriate handlers.
-//
-func MakeRESTRouter() *mux.Router {
+type router struct {
+       *mux.Router
+       limiter httpserver.RequestCounter
+}
+
+// MakeRESTRouter returns a new router that forwards all Keep requests
+// to the appropriate handlers.
+func MakeRESTRouter() *router {
        rest := mux.NewRouter()
+       rtr := &router{Router: rest}
 
        rest.HandleFunc(
                `/{hash:[0-9a-f]{32}}`, GetBlockHandler).Methods("GET", "HEAD")
@@ -46,8 +53,11 @@ func MakeRESTRouter() *mux.Router {
        // Privileged client only.
        rest.HandleFunc(`/index/{prefix:[0-9a-f]{0,32}}`, IndexHandler).Methods("GET", "HEAD")
 
+       // Internals/debugging info (runtime.MemStats)
+       rest.HandleFunc(`/debug.json`, rtr.DebugHandler).Methods("GET", "HEAD")
+
        // List volumes: path, device number, bytes used/avail.
-       rest.HandleFunc(`/status.json`, StatusHandler).Methods("GET", "HEAD")
+       rest.HandleFunc(`/status.json`, rtr.StatusHandler).Methods("GET", "HEAD")
 
        // Replace the current pull queue.
        rest.HandleFunc(`/pull`, PullHandler).Methods("PUT")
@@ -62,7 +72,7 @@ func MakeRESTRouter() *mux.Router {
        // 400 Bad Request.
        rest.NotFoundHandler = http.HandlerFunc(BadRequestHandler)
 
-       return rest
+       return rtr
 }
 
 // BadRequestHandler is a HandleFunc to address bad requests.
@@ -239,18 +249,6 @@ func IndexHandler(resp http.ResponseWriter, req *http.Request) {
        resp.Write([]byte{'\n'})
 }
 
-// StatusHandler
-//     Responds to /status.json requests with the current node status,
-//     described in a JSON structure.
-//
-//     The data given in a status.json response includes:
-//        volumes - a list of Keep volumes currently in use by this server
-//          each volume is an object with the following fields:
-//            * mount_point
-//            * device_num (an integer identifying the underlying filesystem)
-//            * bytes_free
-//            * bytes_used
-
 // PoolStatus struct
 type PoolStatus struct {
        Alloc uint64 `json:"BytesAllocated"`
@@ -258,22 +256,43 @@ type PoolStatus struct {
        Len   int    `json:"BuffersInUse"`
 }
 
+type volumeStatusEnt struct {
+       Label         string
+       Status        *VolumeStatus `json:",omitempty"`
+       VolumeStats   *ioStats      `json:",omitempty"`
+       InternalStats interface{}   `json:",omitempty"`
+}
+
 // NodeStatus struct
 type NodeStatus struct {
-       Volumes    []*VolumeStatus `json:"volumes"`
-       BufferPool PoolStatus
-       PullQueue  WorkQueueStatus
-       TrashQueue WorkQueueStatus
-       Memory     runtime.MemStats
+       Volumes         []*volumeStatusEnt
+       BufferPool      PoolStatus
+       PullQueue       WorkQueueStatus
+       TrashQueue      WorkQueueStatus
+       RequestsCurrent int
+       RequestsMax     int
 }
 
 var st NodeStatus
 var stLock sync.Mutex
 
+// DebugHandler addresses /debug.json requests.
+func (rtr *router) DebugHandler(resp http.ResponseWriter, req *http.Request) {
+       type debugStats struct {
+               MemStats runtime.MemStats
+       }
+       var ds debugStats
+       runtime.ReadMemStats(&ds.MemStats)
+       err := json.NewEncoder(resp).Encode(&ds)
+       if err != nil {
+               http.Error(resp, err.Error(), 500)
+       }
+}
+
 // StatusHandler addresses /status.json requests.
-func StatusHandler(resp http.ResponseWriter, req *http.Request) {
+func (rtr *router) StatusHandler(resp http.ResponseWriter, req *http.Request) {
        stLock.Lock()
-       readNodeStatus(&st)
+       rtr.readNodeStatus(&st)
        jstat, err := json.Marshal(&st)
        stLock.Unlock()
        if err == nil {
@@ -286,23 +305,33 @@ func StatusHandler(resp http.ResponseWriter, req *http.Request) {
 }
 
 // populate the given NodeStatus struct with current values.
-func readNodeStatus(st *NodeStatus) {
+func (rtr *router) readNodeStatus(st *NodeStatus) {
        vols := KeepVM.AllReadable()
        if cap(st.Volumes) < len(vols) {
-               st.Volumes = make([]*VolumeStatus, len(vols))
+               st.Volumes = make([]*volumeStatusEnt, len(vols))
        }
        st.Volumes = st.Volumes[:0]
        for _, vol := range vols {
-               if s := vol.Status(); s != nil {
-                       st.Volumes = append(st.Volumes, s)
+               var internalStats interface{}
+               if vol, ok := vol.(InternalStatser); ok {
+                       internalStats = vol.InternalStats()
                }
+               st.Volumes = append(st.Volumes, &volumeStatusEnt{
+                       Label:         vol.String(),
+                       Status:        vol.Status(),
+                       InternalStats: internalStats,
+                       //VolumeStats: KeepVM.VolumeStats(vol),
+               })
        }
        st.BufferPool.Alloc = bufs.Alloc()
        st.BufferPool.Cap = bufs.Cap()
        st.BufferPool.Len = bufs.Len()
        st.PullQueue = getWorkQueueStatus(pullq)
        st.TrashQueue = getWorkQueueStatus(trashq)
-       runtime.ReadMemStats(&st.Memory)
+       if rtr.limiter != nil {
+               st.RequestsCurrent = rtr.limiter.Current()
+               st.RequestsMax = rtr.limiter.Max()
+       }
 }
 
 // return a WorkQueueStatus for the given queue. If q is nil (which
index 3fb86bc0f147182087cb845ebb4b250891507a27..54147959719183141a8e3137d5d1363ec9667e6b 100644 (file)
@@ -3,7 +3,6 @@ package main
 import (
        "flag"
        "fmt"
-       "log"
        "net"
        "net/http"
        "os"
@@ -15,6 +14,7 @@ import (
        "git.curoverse.com/arvados.git/sdk/go/config"
        "git.curoverse.com/arvados.git/sdk/go/httpserver"
        "git.curoverse.com/arvados.git/sdk/go/keepclient"
+       log "github.com/Sirupsen/logrus"
        "github.com/coreos/go-systemd/daemon"
        "github.com/ghodss/yaml"
 )
@@ -114,6 +114,9 @@ func main() {
        }
 
        err = theConfig.Start()
+       if err != nil {
+               log.Fatal(err)
+       }
 
        if pidfile := theConfig.PIDFile; pidfile != "" {
                f, err := os.OpenFile(pidfile, os.O_RDWR|os.O_CREATE, 0777)
@@ -147,10 +150,10 @@ func main() {
        KeepVM = MakeRRVolumeManager(theConfig.Volumes)
 
        // Middleware stack: logger, MaxRequests limiter, method handlers
-       http.Handle("/", &LoggingRESTRouter{
-               httpserver.NewRequestLimiter(theConfig.MaxRequests,
-                       MakeRESTRouter()),
-       })
+       router := MakeRESTRouter()
+       limiter := httpserver.NewRequestLimiter(theConfig.MaxRequests, router)
+       router.limiter = limiter
+       http.Handle("/", &LoggingRESTRouter{router: limiter})
 
        // Set up a TCP listener.
        listener, err := net.Listen("tcp", theConfig.Listen)
@@ -189,7 +192,7 @@ func main() {
        signal.Notify(term, syscall.SIGTERM)
        signal.Notify(term, syscall.SIGINT)
 
-       if _, err := daemon.SdNotify("READY=1"); err != nil {
+       if _, err := daemon.SdNotify(false, "READY=1"); err != nil {
                log.Printf("Error notifying init daemon: %v", err)
        }
        log.Println("listening at", listener.Addr())
index 0f556b538ac7ae15b1939f61bad13be3ed0404e5..e34f8581fd5448d606a6305e52c0673f944a8898 100644 (file)
@@ -4,10 +4,14 @@ package main
 // LoggingResponseWriter
 
 import (
-       "log"
+       "context"
        "net/http"
        "strings"
        "time"
+
+       "git.curoverse.com/arvados.git/sdk/go/httpserver"
+       "git.curoverse.com/arvados.git/sdk/go/stats"
+       log "github.com/Sirupsen/logrus"
 )
 
 // LoggingResponseWriter has anonymous fields ResponseWriter and ResponseBody
@@ -57,21 +61,47 @@ func (resp *LoggingResponseWriter) Write(data []byte) (int, error) {
 
 // LoggingRESTRouter is used to add logging capabilities to mux.Router
 type LoggingRESTRouter struct {
-       router http.Handler
+       router      http.Handler
+       idGenerator httpserver.IDGenerator
 }
 
 func (loggingRouter *LoggingRESTRouter) ServeHTTP(wrappedResp http.ResponseWriter, req *http.Request) {
-       t0 := time.Now()
+       tStart := time.Now()
+
+       // Attach a requestID-aware logger to the request context.
+       lgr := log.WithField("RequestID", loggingRouter.idGenerator.Next())
+       ctx := context.WithValue(req.Context(), "logger", lgr)
+       req = req.WithContext(ctx)
+
+       lgr = lgr.WithFields(log.Fields{
+               "remoteAddr":      req.RemoteAddr,
+               "reqForwardedFor": req.Header.Get("X-Forwarded-For"),
+               "reqMethod":       req.Method,
+               "reqPath":         req.URL.Path[1:],
+               "reqBytes":        req.ContentLength,
+       })
+       lgr.Debug("request")
+
        resp := LoggingResponseWriter{http.StatusOK, 0, wrappedResp, "", zeroTime}
        loggingRouter.router.ServeHTTP(&resp, req)
+       tDone := time.Now()
+
        statusText := http.StatusText(resp.Status)
        if resp.Status >= 400 {
                statusText = strings.Replace(resp.ResponseBody, "\n", "", -1)
        }
-       now := time.Now()
-       tTotal := now.Sub(t0)
-       tLatency := resp.sentHdr.Sub(t0)
-       tResponse := now.Sub(resp.sentHdr)
-       log.Printf("[%s] %s %s %d %.6fs %.6fs %.6fs %d %d \"%s\"", req.RemoteAddr, req.Method, req.URL.Path[1:], req.ContentLength, tTotal.Seconds(), tLatency.Seconds(), tResponse.Seconds(), resp.Status, resp.Length, statusText)
+       if resp.sentHdr == zeroTime {
+               // Nobody changed status or wrote any data, i.e., we
+               // returned a 200 response with no body.
+               resp.sentHdr = tDone
+       }
 
+       lgr.WithFields(log.Fields{
+               "timeTotal":      stats.Duration(tDone.Sub(tStart)),
+               "timeToStatus":   stats.Duration(resp.sentHdr.Sub(tStart)),
+               "timeWriteBody":  stats.Duration(tDone.Sub(resp.sentHdr)),
+               "respStatusCode": resp.Status,
+               "respStatus":     statusText,
+               "respBytes":      resp.Length,
+       }).Info("response")
 }
diff --git a/services/keepstore/pipe_adapters.go b/services/keepstore/pipe_adapters.go
new file mode 100644 (file)
index 0000000..3cb01f1
--- /dev/null
@@ -0,0 +1,89 @@
+package main
+
+import (
+       "bytes"
+       "context"
+       "io"
+       "io/ioutil"
+)
+
+// getWithPipe invokes getter and copies the resulting data into
+// buf. If ctx is done before all data is copied, getWithPipe closes
+// the pipe with an error, and returns early with an error.
+func getWithPipe(ctx context.Context, loc string, buf []byte, br BlockReader) (int, error) {
+       piper, pipew := io.Pipe()
+       go func() {
+               pipew.CloseWithError(br.ReadBlock(ctx, loc, pipew))
+       }()
+       done := make(chan struct{})
+       var size int
+       var err error
+       go func() {
+               size, err = io.ReadFull(piper, buf)
+               if err == io.EOF || err == io.ErrUnexpectedEOF {
+                       err = nil
+               }
+               close(done)
+       }()
+       select {
+       case <-ctx.Done():
+               piper.CloseWithError(ctx.Err())
+               return 0, ctx.Err()
+       case <-done:
+               piper.Close()
+               return size, err
+       }
+}
+
+// putWithPipe invokes putter with a new pipe, and and copies data
+// from buf into the pipe. If ctx is done before all data is copied,
+// putWithPipe closes the pipe with an error, and returns early with
+// an error.
+func putWithPipe(ctx context.Context, loc string, buf []byte, bw BlockWriter) error {
+       piper, pipew := io.Pipe()
+       copyErr := make(chan error)
+       go func() {
+               _, err := io.Copy(pipew, bytes.NewReader(buf))
+               copyErr <- err
+               close(copyErr)
+       }()
+
+       putErr := make(chan error, 1)
+       go func() {
+               putErr <- bw.WriteBlock(ctx, loc, piper)
+               close(putErr)
+       }()
+
+       var err error
+       select {
+       case err = <-copyErr:
+       case err = <-putErr:
+       case <-ctx.Done():
+               err = ctx.Err()
+       }
+
+       // Ensure io.Copy goroutine isn't blocked writing to pipew
+       // (otherwise, io.Copy is still using buf so it isn't safe to
+       // return). This can cause pipew to receive corrupt data if
+       // err came from copyErr or ctx.Done() before the copy
+       // finished. That's OK, though: in that case err != nil, and
+       // CloseWithErr(err) ensures putter() will get an error from
+       // piper.Read() before seeing EOF.
+       go pipew.CloseWithError(err)
+       go io.Copy(ioutil.Discard, piper)
+       <-copyErr
+
+       // Note: io.Copy() is finished now, but putter() might still
+       // be running. If we encounter an error before putter()
+       // returns, we return right away without waiting for putter().
+
+       if err != nil {
+               return err
+       }
+       select {
+       case <-ctx.Done():
+               return ctx.Err()
+       case err = <-putErr:
+               return err
+       }
+}
index 12860bb662d91a1e31191fed2bccace97bc2ac30..3c6278d478d3d897982b2b6f8c9a166c505e6433 100644 (file)
@@ -7,8 +7,9 @@ import (
        "git.curoverse.com/arvados.git/sdk/go/keepclient"
        "io"
        "io/ioutil"
-       "log"
        "time"
+
+       log "github.com/Sirupsen/logrus"
 )
 
 // RunPullWorker is used by Keepstore to initiate pull worker channel goroutine.
index 17923f807dc8a8f11bc77ce8dc0732001a4a8ba8..d34b8772c5eb90d8a5ac3ad89d3a2cee6a1f5d9c 100644 (file)
@@ -9,7 +9,6 @@ import (
        "fmt"
        "io"
        "io/ioutil"
-       "log"
        "net/http"
        "os"
        "regexp"
@@ -20,6 +19,7 @@ import (
        "git.curoverse.com/arvados.git/sdk/go/arvados"
        "github.com/AdRoll/goamz/aws"
        "github.com/AdRoll/goamz/s3"
+       log "github.com/Sirupsen/logrus"
 )
 
 const (
@@ -148,7 +148,7 @@ type S3Volume struct {
        ReadOnly           bool
        UnsafeDelete       bool
 
-       bucket *s3.Bucket
+       bucket *s3bucket
 
        startOnce sync.Once
 }
@@ -230,9 +230,11 @@ func (v *S3Volume) Start() error {
        client := s3.New(auth, region)
        client.ConnectTimeout = time.Duration(v.ConnectTimeout)
        client.ReadTimeout = time.Duration(v.ReadTimeout)
-       v.bucket = &s3.Bucket{
-               S3:   client,
-               Name: v.Bucket,
+       v.bucket = &s3bucket{
+               Bucket: &s3.Bucket{
+                       S3:   client,
+                       Name: v.Bucket,
+               },
        }
        return nil
 }
@@ -269,6 +271,7 @@ func (v *S3Volume) getReader(loc string) (rdr io.ReadCloser, err error) {
        if err == nil || !os.IsNotExist(err) {
                return
        }
+
        _, err = v.bucket.Head("recent/"+loc, nil)
        err = v.translateError(err)
        if err != nil {
@@ -280,6 +283,7 @@ func (v *S3Volume) getReader(loc string) (rdr io.ReadCloser, err error) {
                err = os.ErrNotExist
                return
        }
+
        rdr, err = v.bucket.GetReader(loc)
        if err != nil {
                log.Printf("warning: reading %s after successful fixRace: %s", loc, err)
@@ -442,16 +446,19 @@ func (v *S3Volume) Mtime(loc string) (time.Time, error) {
 func (v *S3Volume) IndexTo(prefix string, writer io.Writer) error {
        // Use a merge sort to find matching sets of X and recent/X.
        dataL := s3Lister{
-               Bucket:   v.bucket,
+               Bucket:   v.bucket.Bucket,
                Prefix:   prefix,
                PageSize: v.IndexPageSize,
        }
        recentL := s3Lister{
-               Bucket:   v.bucket,
+               Bucket:   v.bucket.Bucket,
                Prefix:   "recent/" + prefix,
                PageSize: v.IndexPageSize,
        }
+       v.bucket.stats.Tick(&v.bucket.stats.Ops, &v.bucket.stats.ListOps)
+       v.bucket.stats.Tick(&v.bucket.stats.Ops, &v.bucket.stats.ListOps)
        for data, recent := dataL.First(), recentL.First(); data != nil; data = dataL.Next() {
+               v.bucket.stats.Tick(&v.bucket.stats.Ops, &v.bucket.stats.ListOps)
                if data.Key >= "g" {
                        // Conveniently, "recent/*" and "trash/*" are
                        // lexically greater than all hex-encoded data
@@ -473,10 +480,12 @@ func (v *S3Volume) IndexTo(prefix string, writer io.Writer) error {
                for recent != nil {
                        if cmp := strings.Compare(recent.Key[7:], data.Key); cmp < 0 {
                                recent = recentL.Next()
+                               v.bucket.stats.Tick(&v.bucket.stats.Ops, &v.bucket.stats.ListOps)
                                continue
                        } else if cmp == 0 {
                                stamp = recent
                                recent = recentL.Next()
+                               v.bucket.stats.Tick(&v.bucket.stats.Ops, &v.bucket.stats.ListOps)
                                break
                        } else {
                                // recent/X marker is missing: we'll
@@ -508,7 +517,7 @@ func (v *S3Volume) Trash(loc string) error {
                if !s3UnsafeDelete {
                        return ErrS3TrashDisabled
                }
-               return v.bucket.Del(loc)
+               return v.translateError(v.bucket.Del(loc))
        }
        err := v.checkRaceWindow(loc)
        if err != nil {
@@ -611,9 +620,14 @@ func (v *S3Volume) Status() *VolumeStatus {
        }
 }
 
+// InternalStats returns bucket I/O and API call counters.
+func (v *S3Volume) InternalStats() interface{} {
+       return &v.bucket.stats
+}
+
 // String implements fmt.Stringer.
 func (v *S3Volume) String() string {
-       return fmt.Sprintf("s3-bucket:%+q", v.bucket.Name)
+       return fmt.Sprintf("s3-bucket:%+q", v.Bucket)
 }
 
 // Writable returns false if all future Put, Mtime, and Delete calls
@@ -702,7 +716,7 @@ func (v *S3Volume) EmptyTrash() {
 
        // Use a merge sort to find matching sets of trash/X and recent/X.
        trashL := s3Lister{
-               Bucket:   v.bucket,
+               Bucket:   v.bucket.Bucket,
                Prefix:   "trash/",
                PageSize: v.IndexPageSize,
        }
@@ -752,7 +766,9 @@ func (v *S3Volume) EmptyTrash() {
                                v.fixRace(loc)
                                v.Touch(loc)
                                continue
-                       } else if _, err := v.bucket.Head(loc, nil); os.IsNotExist(err) {
+                       }
+                       _, err := v.bucket.Head(loc, nil)
+                       if os.IsNotExist(err) {
                                log.Printf("notice: %s: EmptyTrash: detected recent race for %q, calling fixRace", v, loc)
                                v.fixRace(loc)
                                continue
@@ -846,3 +862,65 @@ func (lister *s3Lister) pop() (k *s3.Key) {
        }
        return
 }
+
+// s3bucket wraps s3.bucket and counts I/O and API usage stats.
+type s3bucket struct {
+       *s3.Bucket
+       stats s3bucketStats
+}
+
+func (b *s3bucket) GetReader(path string) (io.ReadCloser, error) {
+       rdr, err := b.Bucket.GetReader(path)
+       b.stats.Tick(&b.stats.Ops, &b.stats.GetOps)
+       b.stats.TickErr(err)
+       return NewCountingReader(rdr, b.stats.TickInBytes), err
+}
+
+func (b *s3bucket) Head(path string, headers map[string][]string) (*http.Response, error) {
+       resp, err := b.Bucket.Head(path, headers)
+       b.stats.Tick(&b.stats.Ops, &b.stats.HeadOps)
+       b.stats.TickErr(err)
+       return resp, err
+}
+
+func (b *s3bucket) PutReader(path string, r io.Reader, length int64, contType string, perm s3.ACL, options s3.Options) error {
+       err := b.Bucket.PutReader(path, NewCountingReader(r, b.stats.TickOutBytes), length, contType, perm, options)
+       b.stats.Tick(&b.stats.Ops, &b.stats.PutOps)
+       b.stats.TickErr(err)
+       return err
+}
+
+func (b *s3bucket) Put(path string, data []byte, contType string, perm s3.ACL, options s3.Options) error {
+       err := b.Bucket.PutReader(path, NewCountingReader(bytes.NewBuffer(data), b.stats.TickOutBytes), int64(len(data)), contType, perm, options)
+       b.stats.Tick(&b.stats.Ops, &b.stats.PutOps)
+       b.stats.TickErr(err)
+       return err
+}
+
+func (b *s3bucket) Del(path string) error {
+       err := b.Bucket.Del(path)
+       b.stats.Tick(&b.stats.Ops, &b.stats.DelOps)
+       b.stats.TickErr(err)
+       return err
+}
+
+type s3bucketStats struct {
+       statsTicker
+       Ops     uint64
+       GetOps  uint64
+       PutOps  uint64
+       HeadOps uint64
+       DelOps  uint64
+       ListOps uint64
+}
+
+func (s *s3bucketStats) TickErr(err error) {
+       if err == nil {
+               return
+       }
+       errType := fmt.Sprintf("%T", err)
+       if err, ok := err.(*s3.Error); ok {
+               errType = errType + fmt.Sprintf(" %d %s", err.StatusCode, err.Code)
+       }
+       s.statsTicker.TickErr(err, errType)
+}
index 63b186220c30a562e900722d63d38be50bde05d6..c43b85b1c588700ca41378432363ea41545e5dd0 100644 (file)
@@ -4,15 +4,18 @@ import (
        "bytes"
        "context"
        "crypto/md5"
+       "encoding/json"
        "fmt"
        "io/ioutil"
-       "log"
+       "net/http"
+       "net/http/httptest"
        "os"
        "time"
 
        "git.curoverse.com/arvados.git/sdk/go/arvados"
        "github.com/AdRoll/goamz/s3"
        "github.com/AdRoll/goamz/s3/s3test"
+       log "github.com/Sirupsen/logrus"
        check "gopkg.in/check.v1"
 )
 
@@ -82,6 +85,123 @@ func (s *StubbedS3Suite) TestIndex(c *check.C) {
        }
 }
 
+func (s *StubbedS3Suite) TestStats(c *check.C) {
+       v := s.newTestableVolume(c, 5*time.Minute, false, 2)
+       stats := func() string {
+               buf, err := json.Marshal(v.InternalStats())
+               c.Check(err, check.IsNil)
+               return string(buf)
+       }
+
+       c.Check(stats(), check.Matches, `.*"Ops":0,.*`)
+
+       loc := "acbd18db4cc2f85cedef654fccc4a4d8"
+       _, err := v.Get(context.Background(), loc, make([]byte, 3))
+       c.Check(err, check.NotNil)
+       c.Check(stats(), check.Matches, `.*"Ops":[^0],.*`)
+       c.Check(stats(), check.Matches, `.*"\*s3.Error 404 [^"]*":[^0].*`)
+       c.Check(stats(), check.Matches, `.*"InBytes":0,.*`)
+
+       err = v.Put(context.Background(), loc, []byte("foo"))
+       c.Check(err, check.IsNil)
+       c.Check(stats(), check.Matches, `.*"OutBytes":3,.*`)
+       c.Check(stats(), check.Matches, `.*"PutOps":2,.*`)
+
+       _, err = v.Get(context.Background(), loc, make([]byte, 3))
+       c.Check(err, check.IsNil)
+       _, err = v.Get(context.Background(), loc, make([]byte, 3))
+       c.Check(err, check.IsNil)
+       c.Check(stats(), check.Matches, `.*"InBytes":6,.*`)
+}
+
+type blockingHandler struct {
+       requested chan *http.Request
+       unblock   chan struct{}
+}
+
+func (h *blockingHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+       if h.requested != nil {
+               h.requested <- r
+       }
+       if h.unblock != nil {
+               <-h.unblock
+       }
+       http.Error(w, "nothing here", http.StatusNotFound)
+}
+
+func (s *StubbedS3Suite) TestGetContextCancel(c *check.C) {
+       loc := "acbd18db4cc2f85cedef654fccc4a4d8"
+       buf := make([]byte, 3)
+
+       s.testContextCancel(c, func(ctx context.Context, v *TestableS3Volume) error {
+               _, err := v.Get(ctx, loc, buf)
+               return err
+       })
+}
+
+func (s *StubbedS3Suite) TestCompareContextCancel(c *check.C) {
+       loc := "acbd18db4cc2f85cedef654fccc4a4d8"
+       buf := []byte("bar")
+
+       s.testContextCancel(c, func(ctx context.Context, v *TestableS3Volume) error {
+               return v.Compare(ctx, loc, buf)
+       })
+}
+
+func (s *StubbedS3Suite) TestPutContextCancel(c *check.C) {
+       loc := "acbd18db4cc2f85cedef654fccc4a4d8"
+       buf := []byte("foo")
+
+       s.testContextCancel(c, func(ctx context.Context, v *TestableS3Volume) error {
+               return v.Put(ctx, loc, buf)
+       })
+}
+
+func (s *StubbedS3Suite) testContextCancel(c *check.C, testFunc func(context.Context, *TestableS3Volume) error) {
+       handler := &blockingHandler{}
+       srv := httptest.NewServer(handler)
+       defer srv.Close()
+
+       v := s.newTestableVolume(c, 5*time.Minute, false, 2)
+       vol := *v.S3Volume
+       vol.Endpoint = srv.URL
+       v = &TestableS3Volume{S3Volume: &vol}
+       v.Start()
+
+       ctx, cancel := context.WithCancel(context.Background())
+
+       handler.requested = make(chan *http.Request)
+       handler.unblock = make(chan struct{})
+       defer close(handler.unblock)
+
+       doneFunc := make(chan struct{})
+       go func() {
+               err := testFunc(ctx, v)
+               c.Check(err, check.Equals, context.Canceled)
+               close(doneFunc)
+       }()
+
+       timeout := time.After(10 * time.Second)
+
+       // Wait for the stub server to receive a request, meaning
+       // Get() is waiting for an s3 operation.
+       select {
+       case <-timeout:
+               c.Fatal("timed out waiting for test func to call our handler")
+       case <-doneFunc:
+               c.Fatal("test func finished without even calling our handler!")
+       case <-handler.requested:
+       }
+
+       cancel()
+
+       select {
+       case <-timeout:
+               c.Fatal("timed out")
+       case <-doneFunc:
+       }
+}
+
 func (s *StubbedS3Suite) TestBackendStates(c *check.C) {
        defer func(tl, bs arvados.Duration) {
                theConfig.TrashLifetime = tl
@@ -290,18 +410,9 @@ func (s *StubbedS3Suite) newTestableVolume(c *check.C, raceWindow time.Duration,
        srv, err := s3test.NewServer(&s3test.Config{Clock: clock})
        c.Assert(err, check.IsNil)
 
-       tmp, err := ioutil.TempFile("", "keepstore")
-       c.Assert(err, check.IsNil)
-       defer os.Remove(tmp.Name())
-       _, err = tmp.Write([]byte("xxx\n"))
-       c.Assert(err, check.IsNil)
-       c.Assert(tmp.Close(), check.IsNil)
-
        v := &TestableS3Volume{
                S3Volume: &S3Volume{
                        Bucket:             TestBucketName,
-                       AccessKeyFile:      tmp.Name(),
-                       SecretKeyFile:      tmp.Name(),
                        Endpoint:           srv.URL(),
                        Region:             "test-region-1",
                        LocationConstraint: true,
@@ -311,15 +422,31 @@ func (s *StubbedS3Suite) newTestableVolume(c *check.C, raceWindow time.Duration,
                        ReadOnly:           readonly,
                        IndexPageSize:      1000,
                },
+               c:           c,
                server:      srv,
                serverClock: clock,
        }
-       c.Assert(v.Start(), check.IsNil)
+       v.Start()
        err = v.bucket.PutBucket(s3.ACL("private"))
        c.Assert(err, check.IsNil)
        return v
 }
 
+func (v *TestableS3Volume) Start() error {
+       tmp, err := ioutil.TempFile("", "keepstore")
+       v.c.Assert(err, check.IsNil)
+       defer os.Remove(tmp.Name())
+       _, err = tmp.Write([]byte("xxx\n"))
+       v.c.Assert(err, check.IsNil)
+       v.c.Assert(tmp.Close(), check.IsNil)
+
+       v.S3Volume.AccessKeyFile = tmp.Name()
+       v.S3Volume.SecretKeyFile = tmp.Name()
+
+       v.c.Assert(v.S3Volume.Start(), check.IsNil)
+       return nil
+}
+
 // PutRaw skips the ContentMD5 test
 func (v *TestableS3Volume) PutRaw(loc string, block []byte) {
        err := v.bucket.Put(loc, block, "application/octet-stream", s3ACL, s3.Options{})
diff --git a/services/keepstore/stats_ticker.go b/services/keepstore/stats_ticker.go
new file mode 100644 (file)
index 0000000..f3a79c6
--- /dev/null
@@ -0,0 +1,50 @@
+package main
+
+import (
+       "sync"
+       "sync/atomic"
+)
+
+type statsTicker struct {
+       Errors   uint64
+       InBytes  uint64
+       OutBytes uint64
+
+       ErrorCodes map[string]uint64 `json:",omitempty"`
+       lock       sync.Mutex
+}
+
+// Tick increments each of the given counters by 1 using
+// atomic.AddUint64.
+func (s *statsTicker) Tick(counters ...*uint64) {
+       for _, counter := range counters {
+               atomic.AddUint64(counter, 1)
+       }
+}
+
+// TickErr increments the overall error counter, as well as the
+// ErrorCodes entry for the given errType. If err is nil, TickErr is a
+// no-op.
+func (s *statsTicker) TickErr(err error, errType string) {
+       if err == nil {
+               return
+       }
+       s.Tick(&s.Errors)
+
+       s.lock.Lock()
+       if s.ErrorCodes == nil {
+               s.ErrorCodes = make(map[string]uint64)
+       }
+       s.ErrorCodes[errType]++
+       s.lock.Unlock()
+}
+
+// TickInBytes increments the incoming byte counter by n.
+func (s *statsTicker) TickInBytes(n uint64) {
+       atomic.AddUint64(&s.InBytes, n)
+}
+
+// TickOutBytes increments the outgoing byte counter by n.
+func (s *statsTicker) TickOutBytes(n uint64) {
+       atomic.AddUint64(&s.OutBytes, n)
+}
index 27d6216d01633feca360de94f0a8febaabfb475a..696c3e53a60abbd352efc035f5a0bb1afaec737f 100644 (file)
@@ -2,10 +2,10 @@ package main
 
 import (
        "errors"
-       "log"
        "time"
 
        "git.curoverse.com/arvados.git/sdk/go/arvados"
+       log "github.com/Sirupsen/logrus"
 )
 
 // RunTrashWorker is used by Keepstore to initiate trash worker channel goroutine.
index 29f89f567d7b64729d10ecde3a4699186f233711..887cfd3a9edf80f5ef9a620efa880b9bc93856d8 100644 (file)
@@ -48,6 +48,10 @@ Listen:
     "address" is a host IP address or name and "port" is a port number
     or name.
 
+LogFormat:
+
+    Format of request/response and error logs: "json" or "text".
+
 PIDFile:
 
    Path to write PID file during startup. This file is kept open and
index 57e18aba9f691ceb43f32a928a0e3a95e9d505ec..778f27fcde87cbc324a246aec571b3fe7a5c2b8a 100644 (file)
@@ -7,6 +7,18 @@ import (
        "time"
 )
 
+type BlockWriter interface {
+       // WriteBlock reads all data from r, writes it to a backing
+       // store as "loc", and returns the number of bytes written.
+       WriteBlock(ctx context.Context, loc string, r io.Reader) error
+}
+
+type BlockReader interface {
+       // ReadBlock retrieves data previously stored as "loc" and
+       // writes it to w.
+       ReadBlock(ctx context.Context, loc string, w io.Writer) error
+}
+
 // A Volume is an interface representing a Keep back-end storage unit:
 // for example, a single mounted disk, a RAID array, an Amazon S3 volume,
 // etc.
@@ -243,6 +255,10 @@ type VolumeManager interface {
        // with more free space, etc.
        NextWritable() Volume
 
+       // VolumeStats returns the ioStats used for tracking stats for
+       // the given Volume.
+       VolumeStats(Volume) *ioStats
+
        // Close shuts down the volume manager cleanly.
        Close()
 }
@@ -254,12 +270,16 @@ type RRVolumeManager struct {
        readables []Volume
        writables []Volume
        counter   uint32
+       iostats   map[Volume]*ioStats
 }
 
 // MakeRRVolumeManager initializes RRVolumeManager
 func MakeRRVolumeManager(volumes []Volume) *RRVolumeManager {
-       vm := &RRVolumeManager{}
+       vm := &RRVolumeManager{
+               iostats: make(map[Volume]*ioStats),
+       }
        for _, v := range volumes {
+               vm.iostats[v] = &ioStats{}
                vm.readables = append(vm.readables, v)
                if v.Writable() {
                        vm.writables = append(vm.writables, v)
@@ -287,18 +307,35 @@ func (vm *RRVolumeManager) NextWritable() Volume {
        return vm.writables[i%uint32(len(vm.writables))]
 }
 
+// VolumeStats returns an ioStats for the given volume.
+func (vm *RRVolumeManager) VolumeStats(v Volume) *ioStats {
+       return vm.iostats[v]
+}
+
 // Close the RRVolumeManager
 func (vm *RRVolumeManager) Close() {
 }
 
-// VolumeStatus provides status information of the volume consisting of:
-//   * mount_point
-//   * device_num (an integer identifying the underlying storage system)
-//   * bytes_free
-//   * bytes_used
+// VolumeStatus describes the current condition of a volume
 type VolumeStatus struct {
-       MountPoint string `json:"mount_point"`
-       DeviceNum  uint64 `json:"device_num"`
-       BytesFree  uint64 `json:"bytes_free"`
-       BytesUsed  uint64 `json:"bytes_used"`
+       MountPoint string
+       DeviceNum  uint64
+       BytesFree  uint64
+       BytesUsed  uint64
+}
+
+// ioStats tracks I/O statistics for a volume or server
+type ioStats struct {
+       Errors     uint64
+       Ops        uint64
+       CompareOps uint64
+       GetOps     uint64
+       PutOps     uint64
+       TouchOps   uint64
+       InBytes    uint64
+       OutBytes   uint64
+}
+
+type InternalStatser interface {
+       InternalStats() interface{}
 }
index 5239ed37402c93f25af0d6c65c03f6a953597cda..fa28ee272ff7b00948557ca8177288f518d255bf 100644 (file)
@@ -7,7 +7,6 @@ import (
        "fmt"
        "io"
        "io/ioutil"
-       "log"
        "os"
        "path/filepath"
        "regexp"
@@ -16,6 +15,8 @@ import (
        "sync"
        "syscall"
        "time"
+
+       log "github.com/Sirupsen/logrus"
 )
 
 type unixVolumeAdder struct {
@@ -110,6 +111,8 @@ type UnixVolume struct {
        // something to lock during IO, typically a sync.Mutex (or nil
        // to skip locking)
        locker sync.Locker
+
+       os osWithStats
 }
 
 // Examples implements VolumeWithExamples.
@@ -144,7 +147,7 @@ func (v *UnixVolume) Start() error {
        if v.DirectoryReplication == 0 {
                v.DirectoryReplication = 1
        }
-       _, err := os.Stat(v.Root)
+       _, err := v.os.Stat(v.Root)
        return err
 }
 
@@ -154,27 +157,30 @@ func (v *UnixVolume) Touch(loc string) error {
                return MethodDisabledError
        }
        p := v.blockPath(loc)
-       f, err := os.OpenFile(p, os.O_RDWR|os.O_APPEND, 0644)
+       f, err := v.os.OpenFile(p, os.O_RDWR|os.O_APPEND, 0644)
        if err != nil {
                return err
        }
        defer f.Close()
-       if v.locker != nil {
-               v.locker.Lock()
-               defer v.locker.Unlock()
+       if err := v.lock(context.TODO()); err != nil {
+               return err
        }
-       if e := lockfile(f); e != nil {
+       defer v.unlock()
+       if e := v.lockfile(f); e != nil {
                return e
        }
-       defer unlockfile(f)
+       defer v.unlockfile(f)
        ts := syscall.NsecToTimespec(time.Now().UnixNano())
-       return syscall.UtimesNano(p, []syscall.Timespec{ts, ts})
+       v.os.stats.Tick(&v.os.stats.UtimesOps)
+       err = syscall.UtimesNano(p, []syscall.Timespec{ts, ts})
+       v.os.stats.TickErr(err)
+       return err
 }
 
 // Mtime returns the stored timestamp for the given locator.
 func (v *UnixVolume) Mtime(loc string) (time.Time, error) {
        p := v.blockPath(loc)
-       fi, err := os.Stat(p)
+       fi, err := v.os.Stat(p)
        if err != nil {
                return time.Time{}, err
        }
@@ -184,24 +190,21 @@ func (v *UnixVolume) Mtime(loc string) (time.Time, error) {
 // Lock the locker (if one is in use), open the file for reading, and
 // call the given function if and when the file is ready to read.
 func (v *UnixVolume) getFunc(ctx context.Context, path string, fn func(io.Reader) error) error {
-       if v.locker != nil {
-               v.locker.Lock()
-               defer v.locker.Unlock()
-       }
-       if ctx.Err() != nil {
-               return ctx.Err()
+       if err := v.lock(ctx); err != nil {
+               return err
        }
-       f, err := os.Open(path)
+       defer v.unlock()
+       f, err := v.os.Open(path)
        if err != nil {
                return err
        }
        defer f.Close()
-       return fn(f)
+       return fn(NewCountingReader(ioutil.NopCloser(f), v.os.stats.TickInBytes))
 }
 
 // stat is os.Stat() with some extra sanity checks.
 func (v *UnixVolume) stat(path string) (os.FileInfo, error) {
-       stat, err := os.Stat(path)
+       stat, err := v.os.Stat(path)
        if err == nil {
                if stat.Size() < 0 {
                        err = os.ErrInvalid
@@ -215,21 +218,23 @@ func (v *UnixVolume) stat(path string) (os.FileInfo, error) {
 // Get retrieves a block, copies it to the given slice, and returns
 // the number of bytes copied.
 func (v *UnixVolume) Get(ctx context.Context, loc string, buf []byte) (int, error) {
+       return getWithPipe(ctx, loc, buf, v)
+}
+
+// ReadBlock implements BlockReader.
+func (v *UnixVolume) ReadBlock(ctx context.Context, loc string, w io.Writer) error {
        path := v.blockPath(loc)
        stat, err := v.stat(path)
        if err != nil {
-               return 0, v.translateError(err)
-       }
-       if stat.Size() > int64(len(buf)) {
-               return 0, TooLongError
+               return v.translateError(err)
        }
-       var read int
-       size := int(stat.Size())
-       err = v.getFunc(ctx, path, func(rdr io.Reader) error {
-               read, err = io.ReadFull(rdr, buf[:size])
+       return v.getFunc(ctx, path, func(rdr io.Reader) error {
+               n, err := io.Copy(w, rdr)
+               if err == nil && n != stat.Size() {
+                       err = io.ErrUnexpectedEOF
+               }
                return err
        })
-       return read, err
 }
 
 // Compare returns nil if Get(loc) would return the same content as
@@ -250,6 +255,11 @@ func (v *UnixVolume) Compare(ctx context.Context, loc string, expect []byte) err
 // returns a FullError.  If the write fails due to some other error,
 // that error is returned.
 func (v *UnixVolume) Put(ctx context.Context, loc string, block []byte) error {
+       return putWithPipe(ctx, loc, block, v)
+}
+
+// ReadBlock implements BlockWriter.
+func (v *UnixVolume) WriteBlock(ctx context.Context, loc string, rdr io.Reader) error {
        if v.ReadOnly {
                return MethodDisabledError
        }
@@ -263,37 +273,34 @@ func (v *UnixVolume) Put(ctx context.Context, loc string, block []byte) error {
                return err
        }
 
-       tmpfile, tmperr := ioutil.TempFile(bdir, "tmp"+loc)
+       tmpfile, tmperr := v.os.TempFile(bdir, "tmp"+loc)
        if tmperr != nil {
                log.Printf("ioutil.TempFile(%s, tmp%s): %s", bdir, loc, tmperr)
                return tmperr
        }
+
        bpath := v.blockPath(loc)
 
-       if v.locker != nil {
-               v.locker.Lock()
-               defer v.locker.Unlock()
-       }
-       select {
-       case <-ctx.Done():
-               return ctx.Err()
-       default:
+       if err := v.lock(ctx); err != nil {
+               return err
        }
-       if _, err := tmpfile.Write(block); err != nil {
+       defer v.unlock()
+       n, err := io.Copy(tmpfile, rdr)
+       v.os.stats.TickOutBytes(uint64(n))
+       if err != nil {
                log.Printf("%s: writing to %s: %s\n", v, bpath, err)
                tmpfile.Close()
-               os.Remove(tmpfile.Name())
+               v.os.Remove(tmpfile.Name())
                return err
        }
        if err := tmpfile.Close(); err != nil {
                log.Printf("closing %s: %s\n", tmpfile.Name(), err)
-               os.Remove(tmpfile.Name())
+               v.os.Remove(tmpfile.Name())
                return err
        }
-       if err := os.Rename(tmpfile.Name(), bpath); err != nil {
+       if err := v.os.Rename(tmpfile.Name(), bpath); err != nil {
                log.Printf("rename %s %s: %s\n", tmpfile.Name(), bpath, err)
-               os.Remove(tmpfile.Name())
-               return err
+               return v.os.Remove(tmpfile.Name())
        }
        return nil
 }
@@ -302,18 +309,15 @@ func (v *UnixVolume) Put(ctx context.Context, loc string, block []byte) error {
 // current state, or nil if an error occurs.
 //
 func (v *UnixVolume) Status() *VolumeStatus {
-       var fs syscall.Statfs_t
-       var devnum uint64
-
-       if fi, err := os.Stat(v.Root); err == nil {
-               devnum = fi.Sys().(*syscall.Stat_t).Dev
-       } else {
+       fi, err := v.os.Stat(v.Root)
+       if err != nil {
                log.Printf("%s: os.Stat: %s\n", v, err)
                return nil
        }
+       devnum := fi.Sys().(*syscall.Stat_t).Dev
 
-       err := syscall.Statfs(v.Root, &fs)
-       if err != nil {
+       var fs syscall.Statfs_t
+       if err := syscall.Statfs(v.Root, &fs); err != nil {
                log.Printf("%s: statfs: %s\n", v, err)
                return nil
        }
@@ -322,7 +326,12 @@ func (v *UnixVolume) Status() *VolumeStatus {
        // uses fs.Blocks - fs.Bfree.
        free := fs.Bavail * uint64(fs.Bsize)
        used := (fs.Blocks - fs.Bfree) * uint64(fs.Bsize)
-       return &VolumeStatus{v.Root, devnum, free, used}
+       return &VolumeStatus{
+               MountPoint: v.Root,
+               DeviceNum:  devnum,
+               BytesFree:  free,
+               BytesUsed:  used,
+       }
 }
 
 var blockDirRe = regexp.MustCompile(`^[0-9a-f]+$`)
@@ -344,11 +353,12 @@ var blockFileRe = regexp.MustCompile(`^[0-9a-f]{32}$`)
 //
 func (v *UnixVolume) IndexTo(prefix string, w io.Writer) error {
        var lastErr error
-       rootdir, err := os.Open(v.Root)
+       rootdir, err := v.os.Open(v.Root)
        if err != nil {
                return err
        }
        defer rootdir.Close()
+       v.os.stats.Tick(&v.os.stats.ReaddirOps)
        for {
                names, err := rootdir.Readdirnames(1)
                if err == io.EOF {
@@ -364,12 +374,13 @@ func (v *UnixVolume) IndexTo(prefix string, w io.Writer) error {
                        continue
                }
                blockdirpath := filepath.Join(v.Root, names[0])
-               blockdir, err := os.Open(blockdirpath)
+               blockdir, err := v.os.Open(blockdirpath)
                if err != nil {
                        log.Print("Error reading ", blockdirpath, ": ", err)
                        lastErr = err
                        continue
                }
+               v.os.stats.Tick(&v.os.stats.ReaddirOps)
                for {
                        fileInfo, err := blockdir.Readdir(1)
                        if err == io.EOF {
@@ -412,36 +423,36 @@ func (v *UnixVolume) Trash(loc string) error {
        if v.ReadOnly {
                return MethodDisabledError
        }
-       if v.locker != nil {
-               v.locker.Lock()
-               defer v.locker.Unlock()
+       if err := v.lock(context.TODO()); err != nil {
+               return err
        }
+       defer v.unlock()
        p := v.blockPath(loc)
-       f, err := os.OpenFile(p, os.O_RDWR|os.O_APPEND, 0644)
+       f, err := v.os.OpenFile(p, os.O_RDWR|os.O_APPEND, 0644)
        if err != nil {
                return err
        }
        defer f.Close()
-       if e := lockfile(f); e != nil {
+       if e := v.lockfile(f); e != nil {
                return e
        }
-       defer unlockfile(f)
+       defer v.unlockfile(f)
 
        // If the block has been PUT in the last blobSignatureTTL
        // seconds, return success without removing the block. This
        // protects data from garbage collection until it is no longer
        // possible for clients to retrieve the unreferenced blocks
        // anyway (because the permission signatures have expired).
-       if fi, err := os.Stat(p); err != nil {
+       if fi, err := v.os.Stat(p); err != nil {
                return err
        } else if time.Since(fi.ModTime()) < time.Duration(theConfig.BlobSignatureTTL) {
                return nil
        }
 
        if theConfig.TrashLifetime == 0 {
-               return os.Remove(p)
+               return v.os.Remove(p)
        }
-       return os.Rename(p, fmt.Sprintf("%v.trash.%d", p, time.Now().Add(theConfig.TrashLifetime.Duration()).Unix()))
+       return v.os.Rename(p, fmt.Sprintf("%v.trash.%d", p, time.Now().Add(theConfig.TrashLifetime.Duration()).Unix()))
 }
 
 // Untrash moves block from trash back into store
@@ -452,6 +463,7 @@ func (v *UnixVolume) Untrash(loc string) (err error) {
                return MethodDisabledError
        }
 
+       v.os.stats.Tick(&v.os.stats.ReaddirOps)
        files, err := ioutil.ReadDir(v.blockDir(loc))
        if err != nil {
                return err
@@ -466,7 +478,7 @@ func (v *UnixVolume) Untrash(loc string) (err error) {
        for _, f := range files {
                if strings.HasPrefix(f.Name(), prefix) {
                        foundTrash = true
-                       err = os.Rename(v.blockPath(f.Name()), v.blockPath(loc))
+                       err = v.os.Rename(v.blockPath(f.Name()), v.blockPath(loc))
                        if err == nil {
                                break
                        }
@@ -553,13 +565,55 @@ func (v *UnixVolume) Replication() int {
        return v.DirectoryReplication
 }
 
+// InternalStats returns I/O and filesystem ops counters.
+func (v *UnixVolume) InternalStats() interface{} {
+       return &v.os.stats
+}
+
+// lock acquires the serialize lock, if one is in use. If ctx is done
+// before the lock is acquired, lock returns ctx.Err() instead of
+// acquiring the lock.
+func (v *UnixVolume) lock(ctx context.Context) error {
+       if v.locker == nil {
+               return nil
+       }
+       locked := make(chan struct{})
+       go func() {
+               v.locker.Lock()
+               close(locked)
+       }()
+       select {
+       case <-ctx.Done():
+               go func() {
+                       <-locked
+                       v.locker.Unlock()
+               }()
+               return ctx.Err()
+       case <-locked:
+               return nil
+       }
+}
+
+// unlock releases the serialize lock, if one is in use.
+func (v *UnixVolume) unlock() {
+       if v.locker == nil {
+               return
+       }
+       v.locker.Unlock()
+}
+
 // lockfile and unlockfile use flock(2) to manage kernel file locks.
-func lockfile(f *os.File) error {
-       return syscall.Flock(int(f.Fd()), syscall.LOCK_EX)
+func (v *UnixVolume) lockfile(f *os.File) error {
+       v.os.stats.Tick(&v.os.stats.FlockOps)
+       err := syscall.Flock(int(f.Fd()), syscall.LOCK_EX)
+       v.os.stats.TickErr(err)
+       return err
 }
 
-func unlockfile(f *os.File) error {
-       return syscall.Flock(int(f.Fd()), syscall.LOCK_UN)
+func (v *UnixVolume) unlockfile(f *os.File) error {
+       err := syscall.Flock(int(f.Fd()), syscall.LOCK_UN)
+       v.os.stats.TickErr(err)
+       return err
 }
 
 // Where appropriate, translate a more specific filesystem error to an
@@ -605,7 +659,7 @@ func (v *UnixVolume) EmptyTrash() {
                if deadline > time.Now().Unix() {
                        return nil
                }
-               err = os.Remove(path)
+               err = v.os.Remove(path)
                if err != nil {
                        log.Printf("EmptyTrash: Remove %v: %v", path, err)
                        return nil
@@ -621,3 +675,68 @@ func (v *UnixVolume) EmptyTrash() {
 
        log.Printf("EmptyTrash stats for %v: Deleted %v bytes in %v blocks. Remaining in trash: %v bytes in %v blocks.", v.String(), bytesDeleted, blocksDeleted, bytesInTrash-bytesDeleted, blocksInTrash-blocksDeleted)
 }
+
+type unixStats struct {
+       statsTicker
+       OpenOps    uint64
+       StatOps    uint64
+       FlockOps   uint64
+       UtimesOps  uint64
+       CreateOps  uint64
+       RenameOps  uint64
+       UnlinkOps  uint64
+       ReaddirOps uint64
+}
+
+func (s *unixStats) TickErr(err error) {
+       if err == nil {
+               return
+       }
+       s.statsTicker.TickErr(err, fmt.Sprintf("%T", err))
+}
+
+type osWithStats struct {
+       stats unixStats
+}
+
+func (o *osWithStats) Open(name string) (*os.File, error) {
+       o.stats.Tick(&o.stats.OpenOps)
+       f, err := os.Open(name)
+       o.stats.TickErr(err)
+       return f, err
+}
+
+func (o *osWithStats) OpenFile(name string, flag int, perm os.FileMode) (*os.File, error) {
+       o.stats.Tick(&o.stats.OpenOps)
+       f, err := os.OpenFile(name, flag, perm)
+       o.stats.TickErr(err)
+       return f, err
+}
+
+func (o *osWithStats) Remove(path string) error {
+       o.stats.Tick(&o.stats.UnlinkOps)
+       err := os.Remove(path)
+       o.stats.TickErr(err)
+       return err
+}
+
+func (o *osWithStats) Rename(a, b string) error {
+       o.stats.Tick(&o.stats.RenameOps)
+       err := os.Rename(a, b)
+       o.stats.TickErr(err)
+       return err
+}
+
+func (o *osWithStats) Stat(path string) (os.FileInfo, error) {
+       o.stats.Tick(&o.stats.StatOps)
+       fi, err := os.Stat(path)
+       o.stats.TickErr(err)
+       return fi, err
+}
+
+func (o *osWithStats) TempFile(dir, base string) (*os.File, error) {
+       o.stats.Tick(&o.stats.CreateOps)
+       f, err := ioutil.TempFile(dir, base)
+       o.stats.TickErr(err)
+       return f, err
+}
index 3021d6bd362724e7136d1054095e49bb53778199..0edf9b8538b39b9cc995cbcce57776bc2f45a28c 100644 (file)
@@ -3,6 +3,7 @@ package main
 import (
        "bytes"
        "context"
+       "encoding/json"
        "errors"
        "fmt"
        "io"
@@ -13,6 +14,8 @@ import (
        "syscall"
        "testing"
        "time"
+
+       check "gopkg.in/check.v1"
 )
 
 type TestableUnixVolume struct {
@@ -323,14 +326,100 @@ func TestUnixVolumeCompare(t *testing.T) {
        }
 }
 
-// TODO(twp): show that the underlying Read/Write operations executed
-// serially and not concurrently. The easiest way to do this is
-// probably to activate verbose or debug logging, capture log output
-// and examine it to confirm that Reads and Writes did not overlap.
-//
-// TODO(twp): a proper test of I/O serialization requires that a
-// second request start while the first one is still underway.
-// Guaranteeing that the test behaves this way requires some tricky
-// synchronization and mocking.  For now we'll just launch a bunch of
-// requests simultaenously in goroutines and demonstrate that they
-// return accurate results.
+func TestUnixVolumeContextCancelPut(t *testing.T) {
+       v := NewTestableUnixVolume(t, true, false)
+       defer v.Teardown()
+       v.locker.Lock()
+       ctx, cancel := context.WithCancel(context.Background())
+       go func() {
+               time.Sleep(50 * time.Millisecond)
+               cancel()
+               time.Sleep(50 * time.Millisecond)
+               v.locker.Unlock()
+       }()
+       err := v.Put(ctx, TestHash, TestBlock)
+       if err != context.Canceled {
+               t.Errorf("Put() returned %s -- expected short read / canceled", err)
+       }
+}
+
+func TestUnixVolumeContextCancelGet(t *testing.T) {
+       v := NewTestableUnixVolume(t, false, false)
+       defer v.Teardown()
+       bpath := v.blockPath(TestHash)
+       v.PutRaw(TestHash, TestBlock)
+       os.Remove(bpath)
+       err := syscall.Mkfifo(bpath, 0600)
+       if err != nil {
+               t.Fatalf("Mkfifo %s: %s", bpath, err)
+       }
+       defer os.Remove(bpath)
+       ctx, cancel := context.WithCancel(context.Background())
+       go func() {
+               time.Sleep(50 * time.Millisecond)
+               cancel()
+       }()
+       buf := make([]byte, len(TestBlock))
+       n, err := v.Get(ctx, TestHash, buf)
+       if n == len(TestBlock) || err != context.Canceled {
+               t.Errorf("Get() returned %d, %s -- expected short read / canceled", n, err)
+       }
+}
+
+var _ = check.Suite(&UnixVolumeSuite{})
+
+type UnixVolumeSuite struct {
+       volume *TestableUnixVolume
+}
+
+func (s *UnixVolumeSuite) TearDownTest(c *check.C) {
+       if s.volume != nil {
+               s.volume.Teardown()
+       }
+}
+
+func (s *UnixVolumeSuite) TestStats(c *check.C) {
+       s.volume = NewTestableUnixVolume(c, false, false)
+       stats := func() string {
+               buf, err := json.Marshal(s.volume.InternalStats())
+               c.Check(err, check.IsNil)
+               return string(buf)
+       }
+
+       c.Check(stats(), check.Matches, `.*"StatOps":0,.*`)
+       c.Check(stats(), check.Matches, `.*"Errors":0,.*`)
+
+       loc := "acbd18db4cc2f85cedef654fccc4a4d8"
+       _, err := s.volume.Get(context.Background(), loc, make([]byte, 3))
+       c.Check(err, check.NotNil)
+       c.Check(stats(), check.Matches, `.*"StatOps":[^0],.*`)
+       c.Check(stats(), check.Matches, `.*"Errors":[^0],.*`)
+       c.Check(stats(), check.Matches, `.*"\*os\.PathError":[^0].*`)
+       c.Check(stats(), check.Matches, `.*"InBytes":0,.*`)
+       c.Check(stats(), check.Matches, `.*"OpenOps":0,.*`)
+       c.Check(stats(), check.Matches, `.*"CreateOps":0,.*`)
+
+       err = s.volume.Put(context.Background(), loc, []byte("foo"))
+       c.Check(err, check.IsNil)
+       c.Check(stats(), check.Matches, `.*"OutBytes":3,.*`)
+       c.Check(stats(), check.Matches, `.*"CreateOps":1,.*`)
+       c.Check(stats(), check.Matches, `.*"OpenOps":0,.*`)
+       c.Check(stats(), check.Matches, `.*"UtimesOps":0,.*`)
+
+       err = s.volume.Touch(loc)
+       c.Check(err, check.IsNil)
+       c.Check(stats(), check.Matches, `.*"FlockOps":1,.*`)
+       c.Check(stats(), check.Matches, `.*"OpenOps":1,.*`)
+       c.Check(stats(), check.Matches, `.*"UtimesOps":1,.*`)
+
+       _, err = s.volume.Get(context.Background(), loc, make([]byte, 3))
+       c.Check(err, check.IsNil)
+       err = s.volume.Compare(context.Background(), loc, []byte("foo"))
+       c.Check(err, check.IsNil)
+       c.Check(stats(), check.Matches, `.*"InBytes":6,.*`)
+       c.Check(stats(), check.Matches, `.*"OpenOps":3,.*`)
+
+       err = s.volume.Trash(loc)
+       c.Check(err, check.IsNil)
+       c.Check(stats(), check.Matches, `.*"FlockOps":2,.*`)
+}
diff --git a/services/nodemanager/arvnodeman/_version.py b/services/nodemanager/arvnodeman/_version.py
new file mode 100644 (file)
index 0000000..9a29cc1
--- /dev/null
@@ -0,0 +1,3 @@
+import pkg_resources
+
+__version__ = pkg_resources.require('arvados-node-manager')[0].version
index b853f00a6728693cce4b855021e18bb35c869087..1c6d214fe8818e9dd49e94b413daa6609096a4c8 100644 (file)
@@ -31,6 +31,7 @@ class ComputeNodeDriver(BaseComputeNodeDriver):
         create_kwargs = create_kwargs.copy()
         create_kwargs.setdefault('external_ip', None)
         create_kwargs.setdefault('ex_metadata', {})
+        self._project = auth_kwargs.get("project")
         super(ComputeNodeDriver, self).__init__(
             auth_kwargs, list_kwargs, create_kwargs,
             driver_class)
@@ -44,7 +45,7 @@ class ComputeNodeDriver(BaseComputeNodeDriver):
 
     def _init_image(self, image_name):
         return 'image', self.search_for(
-            image_name, 'list_images', self._name_key)
+            image_name, 'list_images', self._name_key, ex_project=self._project)
 
     def _init_network(self, network_name):
         return 'ex_network', self.search_for(
index 1be7e46387ff6c5bfe38d4e4805694fb7986cfa7..4fe5f8aaed0258a9c161d49a9b8fcf7ea0166bce 100644 (file)
@@ -10,6 +10,7 @@ import time
 
 import daemon
 import pykka
+import libcloud
 
 from . import config as nmconfig
 from .baseactor import WatchdogActor
@@ -17,6 +18,7 @@ from .daemon import NodeManagerDaemonActor
 from .jobqueue import JobQueueMonitorActor, ServerCalculator
 from .nodelist import ArvadosNodeListMonitorActor, CloudNodeListMonitorActor
 from .timedcallback import TimedCallBackActor
+from ._version import __version__
 
 node_daemon = None
 
@@ -28,6 +30,10 @@ def parse_cli(args):
     parser = argparse.ArgumentParser(
         prog='arvados-node-manager',
         description="Dynamically allocate Arvados cloud compute nodes")
+    parser.add_argument(
+        '--version', action='version',
+        version="%s %s" % (sys.argv[0], __version__),
+        help='Print version and exit.')
     parser.add_argument(
         '--foreground', action='store_true', default=False,
         help="Run in the foreground.  Don't daemonize.")
@@ -57,6 +63,7 @@ def setup_logging(path, level, **sublevels):
     for logger_name, sublevel in sublevels.iteritems():
         sublogger = logging.getLogger(logger_name)
         sublogger.setLevel(sublevel)
+    return root_logger
 
 def build_server_calculator(config):
     cloud_size_list = config.node_sizes(config.new_cloud_client().list_sizes())
@@ -105,7 +112,8 @@ def main(args=None):
         signal.signal(sigcode, shutdown_signal)
 
     try:
-        setup_logging(config.get('Logging', 'file'), **config.log_levels())
+        root_logger = setup_logging(config.get('Logging', 'file'), **config.log_levels())
+        root_logger.info("%s %s, libcloud %s", sys.argv[0], __version__, libcloud.__version__)
         node_setup, node_shutdown, node_update, node_monitor = \
             config.dispatch_classes()
         server_calculator = build_server_calculator(config)
index 3d838e49b443750be9608eec67738bbdb9b679f2..c30108f44bb65a487945e665e7f2afae91528c00 100644 (file)
@@ -33,6 +33,7 @@ setup(name='arvados-node-manager',
         'arvados-python-client>=0.1.20150206225333',
         'pykka',
         'python-daemon',
+        'setuptools'
         ],
       dependency_links = [
           "https://github.com/curoverse/libcloud/archive/apache-libcloud-0.18.1.dev4.zip"
diff --git a/services/nodemanager/tests/test_arguments.py b/services/nodemanager/tests/test_arguments.py
new file mode 100644 (file)
index 0000000..f98309a
--- /dev/null
@@ -0,0 +1,27 @@
+#!/usr/bin/env python
+
+import io
+import os
+import sys
+import tempfile
+import unittest
+
+import arvnodeman.launcher as nodeman
+from . import testutil
+
+class ArvNodemArgumentsTestCase(unittest.TestCase):
+    def run_nodeman(self, args):
+        return nodeman.main(args)
+
+    def test_unsupported_arg(self):
+        with self.assertRaises(SystemExit):
+            self.run_nodeman(['-x=unknown'])
+
+    def test_version_argument(self):
+        err = io.BytesIO()
+        out = io.BytesIO()
+        with testutil.redirected_streams(stdout=out, stderr=err):
+            with self.assertRaises(SystemExit):
+                self.run_nodeman(['--version'])
+        self.assertEqual(out.getvalue(), '')
+        self.assertRegexpMatches(err.getvalue(), "[0-9]+\.[0-9]+\.[0-9]+")
index 1b6aab3cafed16cfc0960d1a39a32d669fe53ffb..15337c4120173b6a7f2ca6b21f0924bd571f2326 100644 (file)
@@ -2,13 +2,15 @@
 
 from __future__ import absolute_import, print_function
 
+import contextlib
 import datetime
+import mock
+import pykka
+import sys
 import threading
 import time
 
 import libcloud.common.types as cloud_types
-import mock
-import pykka
 
 from . import pykka_timeout
 
@@ -55,6 +57,17 @@ def cloud_node_fqdn(node):
 def ip_address_mock(last_octet):
     return '10.20.30.{}'.format(last_octet)
 
+@contextlib.contextmanager
+def redirected_streams(stdout=None, stderr=None):
+    orig_stdout, sys.stdout = sys.stdout, stdout or sys.stdout
+    orig_stderr, sys.stderr = sys.stderr, stderr or sys.stderr
+    try:
+        yield
+    finally:
+        sys.stdout = orig_stdout
+        sys.stderr = orig_stderr
+
+
 class MockShutdownTimer(object):
     def _set_state(self, is_open, next_opening):
         self.window_open = lambda: is_open
diff --git a/services/ws/arvados-ws.service b/services/ws/arvados-ws.service
new file mode 100644 (file)
index 0000000..ebccf0c
--- /dev/null
@@ -0,0 +1,13 @@
+[Unit]
+Description=Arvados websocket server
+Documentation=https://doc.arvados.org/
+After=network.target
+AssertPathExists=/etc/arvados/ws/ws.yml
+
+[Service]
+Type=notify
+ExecStart=/usr/bin/arvados-ws
+Restart=always
+
+[Install]
+WantedBy=multi-user.target
diff --git a/services/ws/config.go b/services/ws/config.go
new file mode 100644 (file)
index 0000000..0faa863
--- /dev/null
@@ -0,0 +1,40 @@
+package main
+
+import (
+       "time"
+
+       "git.curoverse.com/arvados.git/sdk/go/arvados"
+)
+
+type wsConfig struct {
+       Client    arvados.Client
+       Postgres  pgConfig
+       Listen    string
+       LogLevel  string
+       LogFormat string
+
+       PingTimeout      arvados.Duration
+       ClientEventQueue int
+       ServerEventQueue int
+}
+
+func defaultConfig() wsConfig {
+       return wsConfig{
+               Client: arvados.Client{
+                       APIHost: "localhost:443",
+               },
+               Postgres: pgConfig{
+                       "dbname":          "arvados_production",
+                       "user":            "arvados",
+                       "password":        "xyzzy",
+                       "host":            "localhost",
+                       "connect_timeout": "30",
+                       "sslmode":         "require",
+               },
+               LogLevel:         "info",
+               LogFormat:        "json",
+               PingTimeout:      arvados.Duration(time.Minute),
+               ClientEventQueue: 64,
+               ServerEventQueue: 4,
+       }
+}
diff --git a/services/ws/doc.go b/services/ws/doc.go
new file mode 100644 (file)
index 0000000..7ccb588
--- /dev/null
@@ -0,0 +1,55 @@
+// Arvados-ws exposes Arvados APIs (currently just one, the
+// cache-invalidation event feed at "ws://.../websocket") to
+// websocket clients.
+//
+// Installation
+//
+// See https://doc.arvados.org/install/install-ws.html.
+//
+// Developer info
+//
+// See https://dev.arvados.org/projects/arvados/wiki/Hacking_websocket_server.
+//
+// Usage
+//
+//     arvados-ws [-config /etc/arvados/ws/ws.yml] [-dump-config]
+//
+// Minimal configuration
+//
+//     Client:
+//       APIHost: localhost:443
+//     Listen: ":1234"
+//     Postgres:
+//       dbname: arvados_production
+//       host: localhost
+//       password: xyzzy
+//       user: arvados
+//
+// Options
+//
+// -config path
+//
+// Load configuration from the given file instead of the default
+// /etc/arvados/ws/ws.yml
+//
+// -dump-config
+//
+// Print the loaded configuration to stdout and exit.
+//
+// Logs
+//
+// Logs are printed to stderr, formatted as JSON.
+//
+// A log is printed each time a client connects or disconnects.
+//
+// Enable additional logs by configuring:
+//
+//     LogLevel: debug
+//
+// Runtime status
+//
+// GET /debug.json responds with debug stats.
+//
+// GET /status.json responds with health check results and
+// activity/usage metrics.
+package main
diff --git a/services/ws/event.go b/services/ws/event.go
new file mode 100644 (file)
index 0000000..304f86b
--- /dev/null
@@ -0,0 +1,65 @@
+package main
+
+import (
+       "database/sql"
+       "sync"
+       "time"
+
+       "git.curoverse.com/arvados.git/sdk/go/arvados"
+       "github.com/ghodss/yaml"
+)
+
+type eventSink interface {
+       Channel() <-chan *event
+       Stop()
+}
+
+type eventSource interface {
+       NewSink() eventSink
+       DB() *sql.DB
+}
+
+type event struct {
+       LogID    uint64
+       Received time.Time
+       Ready    time.Time
+       Serial   uint64
+
+       db     *sql.DB
+       logRow *arvados.Log
+       err    error
+       mtx    sync.Mutex
+}
+
+// Detail returns the database row corresponding to the event. It can
+// be called safely from multiple goroutines. Only one attempt will be
+// made. If the database row cannot be retrieved, Detail returns nil.
+func (e *event) Detail() *arvados.Log {
+       e.mtx.Lock()
+       defer e.mtx.Unlock()
+       if e.logRow != nil || e.err != nil {
+               return e.logRow
+       }
+       var logRow arvados.Log
+       var propYAML []byte
+       e.err = e.db.QueryRow(`SELECT id, uuid, object_uuid, COALESCE(object_owner_uuid,''), COALESCE(event_type,''), event_at, created_at, properties FROM logs WHERE id = $1`, e.LogID).Scan(
+               &logRow.ID,
+               &logRow.UUID,
+               &logRow.ObjectUUID,
+               &logRow.ObjectOwnerUUID,
+               &logRow.EventType,
+               &logRow.EventAt,
+               &logRow.CreatedAt,
+               &propYAML)
+       if e.err != nil {
+               logger(nil).WithField("LogID", e.LogID).WithError(e.err).Error("QueryRow failed")
+               return nil
+       }
+       e.err = yaml.Unmarshal(propYAML, &logRow.Properties)
+       if e.err != nil {
+               logger(nil).WithField("LogID", e.LogID).WithError(e.err).Error("yaml decode failed")
+               return nil
+       }
+       e.logRow = &logRow
+       return e.logRow
+}
diff --git a/services/ws/event_source.go b/services/ws/event_source.go
new file mode 100644 (file)
index 0000000..ea90ec7
--- /dev/null
@@ -0,0 +1,216 @@
+package main
+
+import (
+       "database/sql"
+       "strconv"
+       "strings"
+       "sync"
+       "sync/atomic"
+       "time"
+
+       "git.curoverse.com/arvados.git/sdk/go/stats"
+       "github.com/lib/pq"
+)
+
+type pgConfig map[string]string
+
+func (c pgConfig) ConnectionString() string {
+       s := ""
+       for k, v := range c {
+               s += k
+               s += "='"
+               s += strings.Replace(
+                       strings.Replace(v, `\`, `\\`, -1),
+                       `'`, `\'`, -1)
+               s += "' "
+       }
+       return s
+}
+
+type pgEventSource struct {
+       DataSource string
+       QueueSize  int
+
+       db         *sql.DB
+       pqListener *pq.Listener
+       queue      chan *event
+       sinks      map[*pgEventSink]bool
+       setupOnce  sync.Once
+       mtx        sync.Mutex
+       shutdown   chan error
+
+       lastQDelay time.Duration
+       eventsIn   uint64
+       eventsOut  uint64
+}
+
+var _ debugStatuser = (*pgEventSource)(nil)
+
+func (ps *pgEventSource) setup() {
+       ps.shutdown = make(chan error, 1)
+       ps.sinks = make(map[*pgEventSink]bool)
+
+       db, err := sql.Open("postgres", ps.DataSource)
+       if err != nil {
+               logger(nil).WithError(err).Fatal("sql.Open failed")
+       }
+       if err = db.Ping(); err != nil {
+               logger(nil).WithError(err).Fatal("db.Ping failed")
+       }
+       ps.db = db
+
+       ps.pqListener = pq.NewListener(ps.DataSource, time.Second, time.Minute, func(ev pq.ListenerEventType, err error) {
+               if err != nil {
+                       // Until we have a mechanism for catching up
+                       // on missed events, we cannot recover from a
+                       // dropped connection without breaking our
+                       // promises to clients.
+                       logger(nil).WithError(err).Error("listener problem")
+                       ps.shutdown <- err
+               }
+       })
+       err = ps.pqListener.Listen("logs")
+       if err != nil {
+               logger(nil).WithError(err).Fatal("pq Listen failed")
+       }
+       logger(nil).Debug("pgEventSource listening")
+
+       go ps.run()
+}
+
+func (ps *pgEventSource) run() {
+       ps.queue = make(chan *event, ps.QueueSize)
+
+       go func() {
+               for e := range ps.queue {
+                       // Wait for the "select ... from logs" call to
+                       // finish. This limits max concurrent queries
+                       // to ps.QueueSize. Without this, max
+                       // concurrent queries would be bounded by
+                       // client_count X client_queue_size.
+                       e.Detail()
+
+                       logger(nil).
+                               WithField("serial", e.Serial).
+                               WithField("detail", e.Detail()).
+                               Debug("event ready")
+                       e.Ready = time.Now()
+                       ps.lastQDelay = e.Ready.Sub(e.Received)
+
+                       ps.mtx.Lock()
+                       atomic.AddUint64(&ps.eventsOut, uint64(len(ps.sinks)))
+                       for sink := range ps.sinks {
+                               sink.channel <- e
+                       }
+                       ps.mtx.Unlock()
+               }
+       }()
+
+       var serial uint64
+       ticker := time.NewTicker(time.Minute)
+       defer ticker.Stop()
+       for {
+               select {
+               case err, ok := <-ps.shutdown:
+                       if ok {
+                               logger(nil).WithError(err).Info("shutdown")
+                       }
+                       close(ps.queue)
+                       return
+
+               case <-ticker.C:
+                       logger(nil).Debug("listener ping")
+                       ps.pqListener.Ping()
+
+               case pqEvent, ok := <-ps.pqListener.Notify:
+                       if !ok {
+                               close(ps.queue)
+                               return
+                       }
+                       if pqEvent.Channel != "logs" {
+                               continue
+                       }
+                       logID, err := strconv.ParseUint(pqEvent.Extra, 10, 64)
+                       if err != nil {
+                               logger(nil).WithField("pqEvent", pqEvent).Error("bad notify payload")
+                               continue
+                       }
+                       serial++
+                       e := &event{
+                               LogID:    logID,
+                               Received: time.Now(),
+                               Serial:   serial,
+                               db:       ps.db,
+                       }
+                       logger(nil).WithField("event", e).Debug("incoming")
+                       atomic.AddUint64(&ps.eventsIn, 1)
+                       ps.queue <- e
+                       go e.Detail()
+               }
+       }
+}
+
+// NewSink subscribes to the event source. NewSink returns an
+// eventSink, whose Channel() method returns a channel: a pointer to
+// each subsequent event will be sent to that channel.
+//
+// The caller must ensure events are received from the sink channel as
+// quickly as possible because when one sink stops being ready, all
+// other sinks block.
+func (ps *pgEventSource) NewSink() eventSink {
+       ps.setupOnce.Do(ps.setup)
+       sink := &pgEventSink{
+               channel: make(chan *event, 1),
+               source:  ps,
+       }
+       ps.mtx.Lock()
+       ps.sinks[sink] = true
+       ps.mtx.Unlock()
+       return sink
+}
+
+func (ps *pgEventSource) DB() *sql.DB {
+       ps.setupOnce.Do(ps.setup)
+       return ps.db
+}
+
+func (ps *pgEventSource) DebugStatus() interface{} {
+       ps.mtx.Lock()
+       defer ps.mtx.Unlock()
+       blocked := 0
+       for sink := range ps.sinks {
+               blocked += len(sink.channel)
+       }
+       return map[string]interface{}{
+               "EventsIn":     atomic.LoadUint64(&ps.eventsIn),
+               "EventsOut":    atomic.LoadUint64(&ps.eventsOut),
+               "Queue":        len(ps.queue),
+               "QueueLimit":   cap(ps.queue),
+               "QueueDelay":   stats.Duration(ps.lastQDelay),
+               "Sinks":        len(ps.sinks),
+               "SinksBlocked": blocked,
+       }
+}
+
+type pgEventSink struct {
+       channel chan *event
+       source  *pgEventSource
+}
+
+func (sink *pgEventSink) Channel() <-chan *event {
+       return sink.channel
+}
+
+func (sink *pgEventSink) Stop() {
+       go func() {
+               // Ensure this sink cannot fill up and block the
+               // server-side queue (which otherwise could in turn
+               // block our mtx.Lock() here)
+               for _ = range sink.channel {
+               }
+       }()
+       sink.source.mtx.Lock()
+       delete(sink.source.sinks, sink)
+       sink.source.mtx.Unlock()
+       close(sink.channel)
+}
diff --git a/services/ws/handler.go b/services/ws/handler.go
new file mode 100644 (file)
index 0000000..7229190
--- /dev/null
@@ -0,0 +1,235 @@
+package main
+
+import (
+       "context"
+       "io"
+       "sync"
+       "time"
+
+       "git.curoverse.com/arvados.git/sdk/go/arvados"
+       "git.curoverse.com/arvados.git/sdk/go/stats"
+)
+
+type handler struct {
+       Client      arvados.Client
+       PingTimeout time.Duration
+       QueueSize   int
+
+       mtx       sync.Mutex
+       lastDelay map[chan interface{}]stats.Duration
+       setupOnce sync.Once
+}
+
+type handlerStats struct {
+       QueueDelayNs time.Duration
+       WriteDelayNs time.Duration
+       EventBytes   uint64
+       EventCount   uint64
+}
+
+func (h *handler) Handle(ws wsConn, eventSource eventSource, newSession func(wsConn, chan<- interface{}) (session, error)) (hStats handlerStats) {
+       h.setupOnce.Do(h.setup)
+
+       ctx, cancel := context.WithCancel(ws.Request().Context())
+       defer cancel()
+       log := logger(ctx)
+
+       incoming := eventSource.NewSink()
+       defer incoming.Stop()
+
+       queue := make(chan interface{}, h.QueueSize)
+       h.mtx.Lock()
+       h.lastDelay[queue] = 0
+       h.mtx.Unlock()
+       defer func() {
+               h.mtx.Lock()
+               delete(h.lastDelay, queue)
+               h.mtx.Unlock()
+       }()
+
+       sess, err := newSession(ws, queue)
+       if err != nil {
+               log.WithError(err).Error("newSession failed")
+               return
+       }
+
+       // Receive websocket frames from the client and pass them to
+       // sess.Receive().
+       go func() {
+               buf := make([]byte, 2<<20)
+               for {
+                       select {
+                       case <-ctx.Done():
+                               return
+                       default:
+                       }
+                       ws.SetReadDeadline(time.Now().Add(24 * 365 * time.Hour))
+                       n, err := ws.Read(buf)
+                       buf := buf[:n]
+                       log.WithField("frame", string(buf[:n])).Debug("received frame")
+                       if err == nil && n == cap(buf) {
+                               err = errFrameTooBig
+                       }
+                       if err != nil {
+                               if err != io.EOF {
+                                       log.WithError(err).Info("read error")
+                               }
+                               cancel()
+                               return
+                       }
+                       err = sess.Receive(buf)
+                       if err != nil {
+                               log.WithError(err).Error("sess.Receive() failed")
+                               cancel()
+                               return
+                       }
+               }
+       }()
+
+       // Take items from the outgoing queue, serialize them using
+       // sess.EventMessage() as needed, and send them to the client
+       // as websocket frames.
+       go func() {
+               for {
+                       var ok bool
+                       var data interface{}
+                       select {
+                       case <-ctx.Done():
+                               return
+                       case data, ok = <-queue:
+                               if !ok {
+                                       return
+                               }
+                       }
+                       var e *event
+                       var buf []byte
+                       var err error
+                       log := log
+
+                       switch data := data.(type) {
+                       case []byte:
+                               buf = data
+                       case *event:
+                               e = data
+                               log = log.WithField("serial", e.Serial)
+                               buf, err = sess.EventMessage(e)
+                               if err != nil {
+                                       log.WithError(err).Error("EventMessage failed")
+                                       cancel()
+                                       break
+                               } else if len(buf) == 0 {
+                                       log.Debug("skip")
+                                       continue
+                               }
+                       default:
+                               log.WithField("data", data).Error("bad object in client queue")
+                               continue
+                       }
+
+                       log.WithField("frame", string(buf)).Debug("send event")
+                       ws.SetWriteDeadline(time.Now().Add(h.PingTimeout))
+                       t0 := time.Now()
+                       _, err = ws.Write(buf)
+                       if err != nil {
+                               log.WithError(err).Error("write failed")
+                               cancel()
+                               break
+                       }
+                       log.Debug("sent")
+
+                       if e != nil {
+                               hStats.QueueDelayNs += t0.Sub(e.Ready)
+                               h.mtx.Lock()
+                               h.lastDelay[queue] = stats.Duration(time.Since(e.Ready))
+                               h.mtx.Unlock()
+                       }
+                       hStats.WriteDelayNs += time.Since(t0)
+                       hStats.EventBytes += uint64(len(buf))
+                       hStats.EventCount++
+               }
+       }()
+
+       // Filter incoming events against the current subscription
+       // list, and forward matching events to the outgoing message
+       // queue. Close the queue and return when the request context
+       // is done/cancelled or the incoming event stream ends. Shut
+       // down the handler if the outgoing queue fills up.
+       go func() {
+               ticker := time.NewTicker(h.PingTimeout)
+               defer ticker.Stop()
+
+               for {
+                       select {
+                       case <-ctx.Done():
+                               return
+                       case <-ticker.C:
+                               // If the outgoing queue is empty,
+                               // send an empty message. This can
+                               // help detect a disconnected network
+                               // socket, and prevent an idle socket
+                               // from being closed.
+                               if len(queue) == 0 {
+                                       select {
+                                       case queue <- []byte(`{}`):
+                                       default:
+                                       }
+                               }
+                               continue
+                       case e, ok := <-incoming.Channel():
+                               if !ok {
+                                       cancel()
+                                       return
+                               }
+                               if !sess.Filter(e) {
+                                       continue
+                               }
+                               select {
+                               case queue <- e:
+                               default:
+                                       log.WithError(errQueueFull).Error("terminate")
+                                       cancel()
+                                       return
+                               }
+                       }
+               }
+       }()
+
+       <-ctx.Done()
+       return
+}
+
+func (h *handler) DebugStatus() interface{} {
+       h.mtx.Lock()
+       defer h.mtx.Unlock()
+
+       var s struct {
+               QueueCount    int
+               QueueMin      int
+               QueueMax      int
+               QueueTotal    uint64
+               QueueDelayMin stats.Duration
+               QueueDelayMax stats.Duration
+       }
+       for q, lastDelay := range h.lastDelay {
+               s.QueueCount++
+               n := len(q)
+               s.QueueTotal += uint64(n)
+               if s.QueueMax < n {
+                       s.QueueMax = n
+               }
+               if s.QueueMin > n || s.QueueCount == 1 {
+                       s.QueueMin = n
+               }
+               if (s.QueueDelayMin > lastDelay || s.QueueDelayMin == 0) && lastDelay > 0 {
+                       s.QueueDelayMin = lastDelay
+               }
+               if s.QueueDelayMax < lastDelay {
+                       s.QueueDelayMax = lastDelay
+               }
+       }
+       return &s
+}
+
+func (h *handler) setup() {
+       h.lastDelay = make(map[chan interface{}]stats.Duration)
+}
diff --git a/services/ws/main.go b/services/ws/main.go
new file mode 100644 (file)
index 0000000..7c3625b
--- /dev/null
@@ -0,0 +1,67 @@
+package main
+
+import (
+       "flag"
+       "fmt"
+       "net/http"
+       "time"
+
+       "git.curoverse.com/arvados.git/sdk/go/config"
+       "git.curoverse.com/arvados.git/sdk/go/ctxlog"
+       "github.com/coreos/go-systemd/daemon"
+)
+
+var logger = ctxlog.FromContext
+
+func main() {
+       log := logger(nil)
+
+       configPath := flag.String("config", "/etc/arvados/ws/ws.yml", "`path` to config file")
+       dumpConfig := flag.Bool("dump-config", false, "show current configuration and exit")
+       cfg := defaultConfig()
+       flag.Parse()
+
+       err := config.LoadFile(&cfg, *configPath)
+       if err != nil {
+               log.Fatal(err)
+       }
+
+       ctxlog.SetLevel(cfg.LogLevel)
+       ctxlog.SetFormat(cfg.LogFormat)
+
+       if *dumpConfig {
+               txt, err := config.Dump(&cfg)
+               if err != nil {
+                       log.Fatal(err)
+               }
+               fmt.Print(string(txt))
+               return
+       }
+
+       log.Info("started")
+       eventSource := &pgEventSource{
+               DataSource: cfg.Postgres.ConnectionString(),
+               QueueSize:  cfg.ServerEventQueue,
+       }
+       srv := &http.Server{
+               Addr:           cfg.Listen,
+               ReadTimeout:    time.Minute,
+               WriteTimeout:   time.Minute,
+               MaxHeaderBytes: 1 << 20,
+               Handler: &router{
+                       Config:         &cfg,
+                       eventSource:    eventSource,
+                       newPermChecker: func() permChecker { return newPermChecker(cfg.Client) },
+               },
+       }
+       // Bootstrap the eventSource by attaching a dummy subscriber
+       // and hanging up.
+       eventSource.NewSink().Stop()
+
+       if _, err := daemon.SdNotify(false, "READY=1"); err != nil {
+               log.WithError(err).Warn("error notifying init daemon")
+       }
+
+       log.WithField("Listen", srv.Addr).Info("listening")
+       log.Fatal(srv.ListenAndServe())
+}
diff --git a/services/ws/permission.go b/services/ws/permission.go
new file mode 100644 (file)
index 0000000..e467e06
--- /dev/null
@@ -0,0 +1,94 @@
+package main
+
+import (
+       "net/http"
+       "net/url"
+       "time"
+
+       "git.curoverse.com/arvados.git/sdk/go/arvados"
+)
+
+const (
+       maxPermCacheAge = time.Hour
+       minPermCacheAge = 5 * time.Minute
+)
+
+type permChecker interface {
+       SetToken(token string)
+       Check(uuid string) (bool, error)
+}
+
+func newPermChecker(ac arvados.Client) permChecker {
+       ac.AuthToken = ""
+       return &cachingPermChecker{
+               Client:     &ac,
+               cache:      make(map[string]cacheEnt),
+               maxCurrent: 16,
+       }
+}
+
+type cacheEnt struct {
+       time.Time
+       allowed bool
+}
+
+type cachingPermChecker struct {
+       *arvados.Client
+       cache      map[string]cacheEnt
+       maxCurrent int
+}
+
+func (pc *cachingPermChecker) SetToken(token string) {
+       pc.Client.AuthToken = token
+}
+
+func (pc *cachingPermChecker) Check(uuid string) (bool, error) {
+       logger := logger(nil).
+               WithField("token", pc.Client.AuthToken).
+               WithField("uuid", uuid)
+       pc.tidy()
+       now := time.Now()
+       if perm, ok := pc.cache[uuid]; ok && now.Sub(perm.Time) < maxPermCacheAge {
+               logger.WithField("allowed", perm.allowed).Debug("cache hit")
+               return perm.allowed, nil
+       }
+       var buf map[string]interface{}
+       path, err := pc.PathForUUID("get", uuid)
+       if err != nil {
+               return false, err
+       }
+       err = pc.RequestAndDecode(&buf, "GET", path, nil, url.Values{
+               "select": {`["uuid"]`},
+       })
+
+       var allowed bool
+       if err == nil {
+               allowed = true
+       } else if txErr, ok := err.(*arvados.TransactionError); ok && txErr.StatusCode == http.StatusNotFound {
+               allowed = false
+       } else if txErr.StatusCode == http.StatusForbidden {
+               // Some requests are expressly forbidden for reasons
+               // other than "you aren't allowed to know whether this
+               // UUID exists" (404).
+               allowed = false
+       } else {
+               logger.WithError(err).Error("lookup error")
+               return false, err
+       }
+       logger.WithField("allowed", allowed).Debug("cache miss")
+       pc.cache[uuid] = cacheEnt{Time: now, allowed: allowed}
+       return allowed, nil
+}
+
+func (pc *cachingPermChecker) tidy() {
+       if len(pc.cache) <= pc.maxCurrent*2 {
+               return
+       }
+       tooOld := time.Now().Add(-minPermCacheAge)
+       for uuid, t := range pc.cache {
+               if t.Before(tooOld) {
+                       delete(pc.cache, uuid)
+               }
+       }
+       pc.maxCurrent = len(pc.cache)
+}
diff --git a/services/ws/router.go b/services/ws/router.go
new file mode 100644 (file)
index 0000000..15b825f
--- /dev/null
@@ -0,0 +1,140 @@
+package main
+
+import (
+       "encoding/json"
+       "io"
+       "net/http"
+       "strconv"
+       "sync"
+       "sync/atomic"
+       "time"
+
+       "git.curoverse.com/arvados.git/sdk/go/ctxlog"
+       "github.com/Sirupsen/logrus"
+       "golang.org/x/net/websocket"
+)
+
+type wsConn interface {
+       io.ReadWriter
+       Request() *http.Request
+       SetReadDeadline(time.Time) error
+       SetWriteDeadline(time.Time) error
+}
+
+type router struct {
+       Config         *wsConfig
+       eventSource    eventSource
+       newPermChecker func() permChecker
+
+       handler   *handler
+       mux       *http.ServeMux
+       setupOnce sync.Once
+
+       lastReqID  int64
+       lastReqMtx sync.Mutex
+
+       status routerDebugStatus
+}
+
+type routerDebugStatus struct {
+       ReqsReceived int64
+       ReqsActive   int64
+}
+
+type debugStatuser interface {
+       DebugStatus() interface{}
+}
+
+func (rtr *router) setup() {
+       rtr.handler = &handler{
+               PingTimeout: rtr.Config.PingTimeout.Duration(),
+               QueueSize:   rtr.Config.ClientEventQueue,
+       }
+       rtr.mux = http.NewServeMux()
+       rtr.mux.Handle("/websocket", rtr.makeServer(newSessionV0))
+       rtr.mux.Handle("/arvados/v1/events.ws", rtr.makeServer(newSessionV1))
+       rtr.mux.HandleFunc("/debug.json", jsonHandler(rtr.DebugStatus))
+       rtr.mux.HandleFunc("/status.json", jsonHandler(rtr.Status))
+}
+
+func (rtr *router) makeServer(newSession sessionFactory) *websocket.Server {
+       return &websocket.Server{
+               Handshake: func(c *websocket.Config, r *http.Request) error {
+                       return nil
+               },
+               Handler: websocket.Handler(func(ws *websocket.Conn) {
+                       t0 := time.Now()
+                       log := logger(ws.Request().Context())
+                       log.Info("connected")
+
+                       stats := rtr.handler.Handle(ws, rtr.eventSource,
+                               func(ws wsConn, sendq chan<- interface{}) (session, error) {
+                                       return newSession(ws, sendq, rtr.eventSource.DB(), rtr.newPermChecker(), &rtr.Config.Client)
+                               })
+
+                       log.WithFields(logrus.Fields{
+                               "elapsed": time.Now().Sub(t0).Seconds(),
+                               "stats":   stats,
+                       }).Info("disconnect")
+                       ws.Close()
+               }),
+       }
+}
+
+func (rtr *router) newReqID() string {
+       rtr.lastReqMtx.Lock()
+       defer rtr.lastReqMtx.Unlock()
+       id := time.Now().UnixNano()
+       if id <= rtr.lastReqID {
+               id = rtr.lastReqID + 1
+       }
+       return strconv.FormatInt(id, 36)
+}
+
+func (rtr *router) DebugStatus() interface{} {
+       s := map[string]interface{}{
+               "HTTP":     rtr.status,
+               "Outgoing": rtr.handler.DebugStatus(),
+       }
+       if es, ok := rtr.eventSource.(debugStatuser); ok {
+               s["EventSource"] = es.DebugStatus()
+       }
+       return s
+}
+
+func (rtr *router) Status() interface{} {
+       return map[string]interface{}{
+               "Clients": atomic.LoadInt64(&rtr.status.ReqsActive),
+       }
+}
+
+func (rtr *router) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
+       rtr.setupOnce.Do(rtr.setup)
+       atomic.AddInt64(&rtr.status.ReqsReceived, 1)
+       atomic.AddInt64(&rtr.status.ReqsActive, 1)
+       defer atomic.AddInt64(&rtr.status.ReqsActive, -1)
+
+       logger := logger(req.Context()).
+               WithField("RequestID", rtr.newReqID())
+       ctx := ctxlog.Context(req.Context(), logger)
+       req = req.WithContext(ctx)
+       logger.WithFields(logrus.Fields{
+               "remoteAddr":      req.RemoteAddr,
+               "reqForwardedFor": req.Header.Get("X-Forwarded-For"),
+       }).Info("accept request")
+       rtr.mux.ServeHTTP(resp, req)
+}
+
+func jsonHandler(fn func() interface{}) http.HandlerFunc {
+       return func(resp http.ResponseWriter, req *http.Request) {
+               logger := logger(req.Context())
+               resp.Header().Set("Content-Type", "application/json")
+               enc := json.NewEncoder(resp)
+               err := enc.Encode(fn())
+               if err != nil {
+                       msg := "encode failed"
+                       logger.WithError(err).Error(msg)
+                       http.Error(resp, msg, http.StatusInternalServerError)
+               }
+       }
+}
diff --git a/services/ws/session.go b/services/ws/session.go
new file mode 100644 (file)
index 0000000..67f4608
--- /dev/null
@@ -0,0 +1,33 @@
+package main
+
+import (
+       "database/sql"
+
+       "git.curoverse.com/arvados.git/sdk/go/arvados"
+)
+
+type session interface {
+       // Receive processes a message received from the client. If a
+       // non-nil error is returned, the connection will be
+       // terminated.
+       Receive([]byte) error
+
+       // Filter returns true if the event should be queued for
+       // sending to the client. It should return as fast as
+       // possible, and must not block.
+       Filter(*event) bool
+
+       // EventMessage encodes the given event (from the front of the
+       // queue) into a form suitable to send to the client. If a
+       // non-nil error is returned, the connection is terminated. If
+       // the returned buffer is empty, nothing is sent to the client
+       // and the event is not counted in statistics.
+       //
+       // Unlike Filter, EventMessage can block without affecting
+       // other connections. If EventMessage is slow, additional
+       // incoming events will be queued. If the event queue fills
+       // up, the connection will be dropped.
+       EventMessage(*event) ([]byte, error)
+}
+
+type sessionFactory func(wsConn, chan<- interface{}, *sql.DB, permChecker, *arvados.Client) (session, error)
diff --git a/services/ws/session_v0.go b/services/ws/session_v0.go
new file mode 100644 (file)
index 0000000..44e2a1d
--- /dev/null
@@ -0,0 +1,295 @@
+package main
+
+import (
+       "database/sql"
+       "encoding/json"
+       "errors"
+       "sync"
+       "sync/atomic"
+       "time"
+
+       "git.curoverse.com/arvados.git/sdk/go/arvados"
+       "github.com/Sirupsen/logrus"
+)
+
+var (
+       errQueueFull   = errors.New("client queue full")
+       errFrameTooBig = errors.New("frame too big")
+
+       sendObjectAttributes = []string{"state", "name", "owner_uuid", "portable_data_hash"}
+
+       v0subscribeOK   = []byte(`{"status":200}`)
+       v0subscribeFail = []byte(`{"status":400}`)
+)
+
+type v0session struct {
+       ac            *arvados.Client
+       ws            wsConn
+       sendq         chan<- interface{}
+       db            *sql.DB
+       permChecker   permChecker
+       subscriptions []v0subscribe
+       lastMsgID     uint64
+       log           *logrus.Entry
+       mtx           sync.Mutex
+       setupOnce     sync.Once
+}
+
+// newSessionV0 returns a v0 session: a partial port of the Rails/puma
+// implementation, with just enough functionality to support Workbench
+// and arv-mount.
+func newSessionV0(ws wsConn, sendq chan<- interface{}, db *sql.DB, pc permChecker, ac *arvados.Client) (session, error) {
+       sess := &v0session{
+               sendq:       sendq,
+               ws:          ws,
+               db:          db,
+               ac:          ac,
+               permChecker: pc,
+               log:         logger(ws.Request().Context()),
+       }
+
+       err := ws.Request().ParseForm()
+       if err != nil {
+               sess.log.WithError(err).Error("ParseForm failed")
+               return nil, err
+       }
+       token := ws.Request().Form.Get("api_token")
+       sess.permChecker.SetToken(token)
+       sess.log.WithField("token", token).Debug("set token")
+
+       return sess, nil
+}
+
+func (sess *v0session) Receive(buf []byte) error {
+       var sub v0subscribe
+       if err := json.Unmarshal(buf, &sub); err != nil {
+               sess.log.WithError(err).Info("invalid message from client")
+       } else if sub.Method == "subscribe" {
+               sub.prepare(sess)
+               sess.log.WithField("sub", sub).Debug("sub prepared")
+               sess.sendq <- v0subscribeOK
+               sess.mtx.Lock()
+               sess.subscriptions = append(sess.subscriptions, sub)
+               sess.mtx.Unlock()
+               sub.sendOldEvents(sess)
+               return nil
+       } else {
+               sess.log.WithField("Method", sub.Method).Info("unknown method")
+       }
+       sess.sendq <- v0subscribeFail
+       return nil
+}
+
+func (sess *v0session) EventMessage(e *event) ([]byte, error) {
+       detail := e.Detail()
+       if detail == nil {
+               return nil, nil
+       }
+
+       ok, err := sess.permChecker.Check(detail.ObjectUUID)
+       if err != nil || !ok {
+               return nil, err
+       }
+
+       kind, _ := sess.ac.KindForUUID(detail.ObjectUUID)
+       msg := map[string]interface{}{
+               "msgID":             atomic.AddUint64(&sess.lastMsgID, 1),
+               "id":                detail.ID,
+               "uuid":              detail.UUID,
+               "object_uuid":       detail.ObjectUUID,
+               "object_owner_uuid": detail.ObjectOwnerUUID,
+               "object_kind":       kind,
+               "event_type":        detail.EventType,
+               "event_at":          detail.EventAt,
+       }
+       if detail.Properties != nil && detail.Properties["text"] != nil {
+               msg["properties"] = detail.Properties
+       } else {
+               msgProps := map[string]map[string]interface{}{}
+               for _, ak := range []string{"old_attributes", "new_attributes"} {
+                       eventAttrs, ok := detail.Properties[ak].(map[string]interface{})
+                       if !ok {
+                               continue
+                       }
+                       msgAttrs := map[string]interface{}{}
+                       for _, k := range sendObjectAttributes {
+                               if v, ok := eventAttrs[k]; ok {
+                                       msgAttrs[k] = v
+                               }
+                       }
+                       msgProps[ak] = msgAttrs
+               }
+               msg["properties"] = msgProps
+       }
+       return json.Marshal(msg)
+}
+
+func (sess *v0session) Filter(e *event) bool {
+       sess.mtx.Lock()
+       defer sess.mtx.Unlock()
+       for _, sub := range sess.subscriptions {
+               if sub.match(sess, e) {
+                       return true
+               }
+       }
+       return false
+}
+
+func (sub *v0subscribe) sendOldEvents(sess *v0session) {
+       if sub.LastLogID == 0 {
+               return
+       }
+       sess.log.WithField("LastLogID", sub.LastLogID).Debug("getOldEvents")
+       // Here we do a "select id" query and queue an event for every
+       // log since the given ID, then use (*event)Detail() to
+       // retrieve the whole row and decide whether to send it. This
+       // approach is very inefficient if the subscriber asks for
+       // last_log_id==1, even if the filters end up matching very
+       // few events.
+       //
+       // To mitigate this, filter on "created > 10 minutes ago" when
+       // retrieving the list of old event IDs to consider.
+       rows, err := sess.db.Query(
+               `SELECT id FROM logs WHERE id > $1 AND created_at > $2 ORDER BY id`,
+               sub.LastLogID,
+               time.Now().UTC().Add(-10*time.Minute).Format(time.RFC3339Nano))
+       if err != nil {
+               sess.log.WithError(err).Error("db.Query failed")
+               return
+       }
+       for rows.Next() {
+               var id uint64
+               err := rows.Scan(&id)
+               if err != nil {
+                       sess.log.WithError(err).Error("row Scan failed")
+                       continue
+               }
+               for len(sess.sendq)*2 > cap(sess.sendq) {
+                       // Ugly... but if we fill up the whole client
+                       // queue with a backlog of old events, a
+                       // single new event will overflow it and
+                       // terminate the connection, and then the
+                       // client will probably reconnect and do the
+                       // same thing all over again.
+                       time.Sleep(100 * time.Millisecond)
+               }
+               now := time.Now()
+               e := &event{
+                       LogID:    id,
+                       Received: now,
+                       Ready:    now,
+                       db:       sess.db,
+               }
+               if sub.match(sess, e) {
+                       select {
+                       case sess.sendq <- e:
+                       case <-sess.ws.Request().Context().Done():
+                               return
+                       }
+               }
+       }
+       if err := rows.Err(); err != nil {
+               sess.log.WithError(err).Error("db.Query failed")
+       }
+}
+
+type v0subscribe struct {
+       Method    string
+       Filters   []v0filter
+       LastLogID int64 `json:"last_log_id"`
+
+       funcs []func(*event) bool
+}
+
+type v0filter [3]interface{}
+
+func (sub *v0subscribe) match(sess *v0session, e *event) bool {
+       log := sess.log.WithField("LogID", e.LogID)
+       detail := e.Detail()
+       if detail == nil {
+               log.Error("match failed, no detail")
+               return false
+       }
+       log = log.WithField("funcs", len(sub.funcs))
+       for i, f := range sub.funcs {
+               if !f(e) {
+                       log.WithField("func", i).Debug("match failed")
+                       return false
+               }
+       }
+       log.Debug("match passed")
+       return true
+}
+
+func (sub *v0subscribe) prepare(sess *v0session) {
+       for _, f := range sub.Filters {
+               if len(f) != 3 {
+                       continue
+               }
+               if col, ok := f[0].(string); ok && col == "event_type" {
+                       op, ok := f[1].(string)
+                       if !ok || op != "in" {
+                               continue
+                       }
+                       arr, ok := f[2].([]interface{})
+                       if !ok {
+                               continue
+                       }
+                       var strs []string
+                       for _, s := range arr {
+                               if s, ok := s.(string); ok {
+                                       strs = append(strs, s)
+                               }
+                       }
+                       sub.funcs = append(sub.funcs, func(e *event) bool {
+                               for _, s := range strs {
+                                       if s == e.Detail().EventType {
+                                               return true
+                                       }
+                               }
+                               return false
+                       })
+               } else if ok && col == "created_at" {
+                       op, ok := f[1].(string)
+                       if !ok {
+                               continue
+                       }
+                       tstr, ok := f[2].(string)
+                       if !ok {
+                               continue
+                       }
+                       t, err := time.Parse(time.RFC3339Nano, tstr)
+                       if err != nil {
+                               sess.log.WithField("data", tstr).WithError(err).Info("time.Parse failed")
+                               continue
+                       }
+                       var fn func(*event) bool
+                       switch op {
+                       case ">=":
+                               fn = func(e *event) bool {
+                                       return !e.Detail().CreatedAt.Before(t)
+                               }
+                       case "<=":
+                               fn = func(e *event) bool {
+                                       return !e.Detail().CreatedAt.After(t)
+                               }
+                       case ">":
+                               fn = func(e *event) bool {
+                                       return e.Detail().CreatedAt.After(t)
+                               }
+                       case "<":
+                               fn = func(e *event) bool {
+                                       return e.Detail().CreatedAt.Before(t)
+                               }
+                       case "=":
+                               fn = func(e *event) bool {
+                                       return e.Detail().CreatedAt.Equal(t)
+                               }
+                       default:
+                               sess.log.WithField("operator", op).Info("bogus operator")
+                               continue
+                       }
+                       sub.funcs = append(sub.funcs, fn)
+               }
+       }
+}
diff --git a/services/ws/session_v1.go b/services/ws/session_v1.go
new file mode 100644 (file)
index 0000000..71a1303
--- /dev/null
@@ -0,0 +1,14 @@
+package main
+
+import (
+       "database/sql"
+       "errors"
+
+       "git.curoverse.com/arvados.git/sdk/go/arvados"
+)
+
+// newSessionV1 returns a v1 session -- see
+// https://dev.arvados.org/projects/arvados/wiki/Websocket_server
+func newSessionV1(ws wsConn, sendq chan<- interface{}, db *sql.DB, pc permChecker, ac *arvados.Client) (session, error) {
+       return nil, errors.New("Not implemented")
+}
diff --git a/tools/arvbash/arvbash.sh b/tools/arvbash/arvbash.sh
new file mode 100755 (executable)
index 0000000..c2785c4
--- /dev/null
@@ -0,0 +1,124 @@
+#!/bin/bash
+# bash functions for managing Arvados tokens and other conveniences.
+
+read -rd "\000" helpmessage <<EOF
+$(basename $0): bash functions for managing Arvados tokens and other shortcuts.
+
+Syntax:
+        . $0            # activate for current shell
+        $0 --install    # install into .bashrc
+
+arvswitch <name>
+  Set ARVADOS_API_HOST and ARVADOS_API_TOKEN in the current environment based on
+  $HOME/.config/arvados/<name>.conf
+  With no arguments, list available Arvados configurations.
+
+arvsave <name>
+  Save values of ARVADOS_API_HOST and ARVADOS_API_TOKEN in the current environment to
+  $HOME/.config/arvados/<name>.conf
+
+arvrm <name>
+  Delete $HOME/.config/arvados/<name>.conf
+
+arvboxswitch <name>
+  Set ARVBOX_CONTAINER to <name>
+  With no arguments, list available arvboxes.
+
+arvopen:
+  Open an Arvados uuid in web browser (http://curover.se)
+
+arvissue
+  Open an Arvados ticket in web browser (http://dev.arvados.org)
+
+EOF
+
+if [[ "$1" = "--install" ]] ; then
+    this=$(readlink -f $0)
+    if ! grep ". $this" ~/.bashrc >/dev/null ; then
+        echo ". $this" >> ~/.bashrc
+        echo "Installed into ~/.bashrc"
+    else
+        echo "Already installed in ~/.bashrc"
+    fi
+elif ! [[ $0 =~ bash$ ]] ; then
+    echo "$helpmessage"
+fi
+
+HISTIGNORE=$HISTIGNORE:'export ARVADOS_API_TOKEN=*'
+
+arvswitch() {
+    if [[ -n "$1" ]] ; then
+        if [[ -f $HOME/.config/arvados/$1.conf ]] ; then
+            unset ARVADOS_API_HOST_INSECURE
+            for a in $(cat $HOME/.config/arvados/$1.conf) ; do export $a ; done
+            echo "Switched to $1"
+        else
+            echo "$1 unknown"
+        fi
+    else
+        echo "Switch Arvados environment conf"
+        echo "Usage: arvswitch name"
+        echo "Available confs:" $((cd $HOME/.config/arvados && ls --indicator-style=none *.conf) | rev | cut -c6- | rev)
+    fi
+}
+
+arvsave() {
+    if [[ -n "$1" ]] ; then
+       touch $HOME/.config/arvados/$1.conf
+       chmod 0600 $HOME/.config/arvados/$1.conf
+        env | grep ARVADOS_ > $HOME/.config/arvados/$1.conf
+    else
+        echo "Save current Arvados environment variables to conf file"
+        echo "Usage: arvsave name"
+    fi
+}
+
+arvrm() {
+    if [[ -n "$1" ]] ; then
+        if [[ -f $HOME/.config/arvados/$1.conf ]] ; then
+            rm $HOME/.config/arvados/$1.conf
+        else
+            echo "$1 unknown"
+        fi
+    else
+        echo "Delete Arvados environment conf"
+        echo "Usage: arvrm name"
+    fi
+}
+
+arvboxswitch() {
+    if [[ -n "$1" ]] ; then
+        if [[ -d $HOME/.arvbox/$1 ]] ; then
+            export ARVBOX_CONTAINER=$1
+            echo "Arvbox switched to $1"
+        else
+            echo "$1 unknown"
+        fi
+    else
+        if test -z "$ARVBOX_CONTAINER" ; then
+            ARVBOX_CONTAINER=arvbox
+        fi
+        echo "Switch Arvbox environment conf"
+        echo "Usage: arvboxswitch name"
+        echo "Your current container is: $ARVBOX_CONTAINER"
+        echo "Available confs:" $(cd $HOME/.arvbox && ls --indicator-style=none)
+    fi
+}
+
+arvopen() {
+    if [[ -n "$1" ]] ; then
+        xdg-open https://curover.se/$1
+    else
+        echo "Open Arvados uuid in browser"
+        echo "Usage: arvopen uuid"
+    fi
+}
+
+arvissue() {
+    if [[ -n "$1" ]] ; then
+        xdg-open https://dev.arvados.org/issues/$1
+    else
+        echo "Open Arvados issue in browser"
+        echo "Usage: arvissue uuid"
+    fi
+}
index 0b926732b656a3b435cef6e1061887a622e77997..a894350970f115594300befe9eab2798946cdfe4 100644 (file)
@@ -1,32 +1,48 @@
 FROM debian:8
 
-RUN apt-get update
+ENV DEBIAN_FRONTEND noninteractive
 
-RUN DEBIAN_FRONTEND=noninteractive apt-get -yq --no-install-recommends install \
-    postgresql-9.4 git build-essential runit \
-    ruby rake bundler curl libpq-dev ruby-dev \
+RUN apt-get clean && \
+    apt-get update && \
+    apt-get -yq --no-install-recommends -o Acquire::Retries=6 install \
+    postgresql-9.4 git build-essential runit curl libpq-dev \
     libcurl4-openssl-dev libssl-dev zlib1g-dev libpcre3-dev \
     openssh-server python-setuptools netcat-traditional \
-    python-epydoc graphviz bzip2 less sudo virtualenv
-
-RUN DEBIAN_FRONTEND=noninteractive apt-get -yq --no-install-recommends install \
+    python-epydoc graphviz bzip2 less sudo virtualenv \
     libpython-dev fuse libfuse-dev python-pip python-yaml \
     pkg-config libattr1-dev python-llfuse python-pycurl \
     libwww-perl libio-socket-ssl-perl libcrypt-ssleay-perl \
-    libjson-perl nginx gitolite3 lsof \
-    apt-transport-https ca-certificates slurm-wlm
+    libjson-perl nginx gitolite3 lsof libreadline-dev \
+    apt-transport-https ca-certificates slurm-wlm \
+    linkchecker python3-virtualenv python-virtualenv xvfb iceweasel && \
+    apt-get clean
 
-RUN apt-get update && \
-    DEBIAN_FRONTEND=noninteractive apt-get -yq --no-install-recommends install \
-    linkchecker python3-virtualenv python-virtualenv xvfb iceweasel
+ENV GOVERSION 1.7.3
 
+# Install golang binary
 RUN cd /usr/local && \
-    GOVERSION=1.7.1 && \
     curl -O http://storage.googleapis.com/golang/go${GOVERSION}.linux-amd64.tar.gz && \
     tar -xzf go${GOVERSION}.linux-amd64.tar.gz && \
-    rm go${GOVERSION}.linux-amd64.tar.gz && \
-    cd bin && \
-    ln -s /usr/local/go/bin/* .
+    rm go${GOVERSION}.linux-amd64.tar.gz
+
+ENV PATH ${PATH}:/usr/local/go/bin
+
+ENV RUBYVERSION 2.1.8
+
+# Install Ruby from source
+RUN cd /usr/local/lib && \
+ curl -O http://cache.ruby-lang.org/pub/ruby/2.1/ruby-${RUBYVERSION}.tar.gz && \
+ tar xzf ruby-${RUBYVERSION}.tar.gz && \
+ cd ruby-${RUBYVERSION} && \
+ ./configure --disable-install-doc && \
+ make && \
+ make install && \
+ cd /usr/local/lib && \
+ rm -rf ruby-${RUBYVERSION}.tar.gz ruby-${RUBYVERSION}
+
+ENV GEM_HOME /var/lib/gems
+ENV GEM_PATH /var/lib/gems
+ENV PATH $PATH:/var/lib/gems/bin
 
 VOLUME /var/lib/docker
 VOLUME /var/log/nginx
@@ -52,6 +68,8 @@ RUN set -e && \
  tar -C /usr/local -xjf /tmp/$PJS.tar.bz2 && \
  ln -s ../$PJS/bin/phantomjs /usr/local/bin/
 
+RUN pip install -U setuptools
+
 ARG arvados_version
 RUN echo arvados_version is git commit $arvados_version
 
index 67c43b47c6bed69efe12f4d6fc31ca6ff3b58a6d..d4ff7c94dd3738c44a838e26eb06a3bc2ef6b5d7 100755 (executable)
@@ -82,5 +82,6 @@ if ! test -s /var/lib/arvados/superuser_token ; then
 fi
 
 rm -rf tmp
+mkdir -p tmp/cache
 
 bundle exec rake db:migrate
index 742658f5f1d797bdbaa068e3b029445b545b946f..8488ed8cab2a5340ec589ab63cc99de48c1263a8 100644 (file)
@@ -1,4 +1,8 @@
 
+export PATH=${PATH}:/usr/local/go/bin:/var/lib/gems/bin
+export GEM_HOME=/var/lib/gems
+export GEM_PATH=/var/lib/gems
+
 if test -s /var/run/localip_override ; then
     localip=$(cat /var/run/localip_override)
 else
@@ -6,9 +10,6 @@ else
     localip=$(ip addr show $defaultdev | grep 'inet ' | sed 's/ *inet \(.*\)\/.*/\1/')
 fi
 
-export GEM_HOME=/var/lib/gems
-export GEM_PATH=/var/lib/gems
-
 declare -A services
 services=(
   [workbench]=80
@@ -38,6 +39,14 @@ run_bundler() {
     else
         frozen=""
     fi
+    if ! test -x bundle ; then
+        bundlergem=$(ls -r $GEM_HOME/cache/bundler-*.gem 2>/dev/null | head -n1 || true)
+        if test -n "$bundlergem" ; then
+            flock /var/lib/gems/gems.lock gem install --local --no-document $bundlergem
+        else
+            flock /var/lib/gems/gems.lock gem install --no-document bundler
+        fi
+    fi
     if ! flock /var/lib/gems/gems.lock bundle install --path $GEM_HOME --local --no-deployment $frozen "$@" ; then
         flock /var/lib/gems/gems.lock bundle install --path $GEM_HOME --no-deployment $frozen "$@"
     fi
@@ -45,7 +54,7 @@ run_bundler() {
 
 pip_install() {
     pushd /var/lib/pip
-    for p in $(ls http*.tar.gz) $(ls http*.whl) $(ls http*.zip) ; do
+    for p in $(ls http*.tar.gz) $(ls http*.tar.bz2) $(ls http*.whl) $(ls http*.zip) ; do
         if test -f $p ; then
             ln -sf $p $(echo $p | sed 's/.*%2F\(.*\)/\1/')
         fi
index 9ef37921ec18f01b85f9b77bd91fc69f24b48415..dbdf9d0642653644f6f0d095e6481f4ef9da3fd5 100755 (executable)
@@ -26,8 +26,8 @@ if ! grep "^arvbox:" /etc/passwd >/dev/null 2>/dev/null ; then
           /var/lib/nginx /var/log/nginx /etc/ssl/private \
           /var/lib/gopath /var/lib/pip
 
-    mkdir -p /var/lib/gems/ruby/2.1.0
-    chown arvbox:arvbox -R /var/lib/gems/ruby/2.1.0
+    mkdir -p /var/lib/gems/ruby
+    chown arvbox:arvbox -R /var/lib/gems/ruby
 
     mkdir -p /tmp/crunch0 /tmp/crunch1
     chown crunch:crunch -R /tmp/crunch0 /tmp/crunch1
index a36205c9678d4e67063bbec141072df1a737cea9..5f72f1c613192e5b8aa806a0f490711b6055ff69 100755 (executable)
@@ -9,7 +9,8 @@ cd /usr/src/arvados/services/api
 export RAILS_ENV=development
 
 run_bundler --without=development
-bundle exec passenger start --runtime-check-only --runtime-dir=/var/lib/passenger
+bundle exec passenger-config build-native-support
+bundle exec passenger-config install-standalone-runtime
 
 if test "$1" = "--only-deps" ; then
     exit
@@ -23,6 +24,5 @@ if test "$1" = "--only-setup" ; then
 fi
 
 exec bundle exec passenger start --port=${services[api]} \
-                  --runtime-dir=/var/lib/passenger \
                   --ssl --ssl-certificate=/var/lib/arvados/self-signed.pem \
                   --ssl-certificate-key=/var/lib/arvados/self-signed.key
index 977f61298ff7c475f69c7097e6b15d1650096a1b..c2487735ef0e5cd127882ea7dc48a1f3ffc9ecd6 100755 (executable)
@@ -70,11 +70,11 @@ if ! [[ -z "$waiting" ]] ; then
                      /usr/src/arvados/apps/workbench/Gemfile.lock \
                      /usr/src/sso/Gemfile.lock ; do
             gc=$(cat $l \
-                        | grep -vE "(GEM|PLATFORMS|DEPENDENCIES|$^|remote:|specs:)" \
+                        | grep -vE "(GEM|PLATFORMS|DEPENDENCIES|BUNDLED|GIT|$^|remote:|specs:|revision:)" \
                         | sed 's/^ *//' | sed 's/(.*)//' | sed 's/ *$//' | sort | uniq | wc -l)
             gemlockcount=$(($gemlockcount + $gc))
         done
-        waiting="$waiting (installing ruby gems $gemcount/$gemlockcount)"
+        waiting="$waiting (installing ruby gems $gemcount of about $gemlockcount)"
     fi
 
     if ps x | grep -v grep | grep "c++.*/var/lib/passenger" > /dev/null ; then
index da413e09de3d189b57c2924a0dec7a189e53270f..4c2407a4e7e737f6414feafbede72b91a09e7ca1 100755 (executable)
@@ -73,6 +73,7 @@ EOF
 fi
 
 rm -rf tmp
+mkdir -p tmp/cache
 
 bundle exec rake db:migrate