From 6ce7ac33e3388873dd38c534d6c468bf160ef041 Mon Sep 17 00:00:00 2001 From: SungJin1212 Date: Wed, 19 Feb 2025 13:41:32 +0900 Subject: [PATCH 1/2] Upgrade alertmanager version to v0.28.0 Signed-off-by: SungJin1212 --- CHANGELOG.md | 3 +- docs/configuration/config-file-reference.md | 3 +- go.mod | 20 +- go.sum | 44 +- pkg/alertmanager/alertmanager.go | 99 ++- pkg/alertmanager/alertmanager_http_test.go | 4 +- pkg/alertmanager/api_test.go | 2 +- .../merger/v2_alert_groups_test.go | 12 +- pkg/alertmanager/merger/v2_alerts_test.go | 12 +- pkg/alertmanager/multitenant.go | 2 +- pkg/alertmanager/multitenant_test.go | 68 +- .../validation/notifications_limit_flag.go | 2 +- .../alecthomas/units/renovate.json5 | 4 + vendor/github.com/benbjohnson/clock/LICENSE | 21 - vendor/github.com/benbjohnson/clock/README.md | 105 --- vendor/github.com/benbjohnson/clock/clock.go | 422 ----------- .../github.com/benbjohnson/clock/context.go | 86 --- vendor/github.com/coder/quartz/.gitignore | 1 + vendor/github.com/coder/quartz/LICENSE | 121 ++++ vendor/github.com/coder/quartz/README.md | 632 +++++++++++++++++ vendor/github.com/coder/quartz/clock.go | 43 ++ vendor/github.com/coder/quartz/mock.go | 661 ++++++++++++++++++ vendor/github.com/coder/quartz/real.go | 80 +++ vendor/github.com/coder/quartz/ticker.go | 75 ++ vendor/github.com/coder/quartz/timer.go | 81 +++ .../github.com/jessevdk/go-flags/.travis.yml | 39 -- vendor/github.com/jessevdk/go-flags/README.md | 5 +- .../github.com/jessevdk/go-flags/command.go | 11 + .../github.com/jessevdk/go-flags/convert.go | 17 +- vendor/github.com/jessevdk/go-flags/flags.go | 274 ++++---- vendor/github.com/jessevdk/go-flags/help.go | 14 +- vendor/github.com/jessevdk/go-flags/ini.go | 14 +- vendor/github.com/jessevdk/go-flags/option.go | 4 +- .../jessevdk/go-flags/optstyle_other.go | 1 + .../jessevdk/go-flags/optstyle_windows.go | 1 + vendor/github.com/jessevdk/go-flags/parser.go | 15 +- .../github.com/jessevdk/go-flags/termsize.go | 3 +- .../jessevdk/go-flags/termsize_nosysioctl.go | 3 +- .../jessevdk/go-flags/termsize_windows.go | 1 + .../github.com/prometheus/alertmanager/NOTICE | 5 - .../prometheus/alertmanager/api/api.go | 35 +- .../alertmanager/api/metrics/metrics.go | 2 +- .../alertmanager/api/v1_deprecation_router.go | 14 +- .../prometheus/alertmanager/api/v2/api.go | 79 ++- .../alertgroup/get_alert_groups_parameters.go | 39 ++ .../prometheus/alertmanager/api/v2/compat.go | 13 +- .../api/v2/models/alert_status.go | 17 + .../alertmanager/api/v2/openapi.yaml | 10 + .../api/v2/restapi/embedded_spec.go | 32 +- .../alertgroup/get_alert_groups_parameters.go | 37 + .../alertgroup/get_alert_groups_urlbuilder.go | 9 + .../prometheus/alertmanager/api/v2/testing.go | 9 +- .../alertmanager/asset/assets_vfsdata.go | 12 +- .../alertmanager/cluster/channel.go | 21 +- .../alertmanager/cluster/cluster.go | 77 +- .../alertmanager/cluster/delegate.go | 38 +- .../alertmanager/cluster/tls_transport.go | 13 +- .../prometheus/alertmanager/config/config.go | 283 +++++--- .../alertmanager/config/coordinator.go | 23 +- .../alertmanager/config/notifiers.go | 270 +++++-- .../alertmanager/dispatch/dispatch.go | 115 +-- .../prometheus/alertmanager/dispatch/route.go | 22 +- .../featurecontrol/featurecontrol.go | 52 +- .../alertmanager/inhibit/inhibit.go | 15 +- .../{matchers => matcher}/compat/parse.go | 64 +- .../{matchers => matcher}/parse/lexer.go | 2 +- .../{matchers => matcher}/parse/parse.go | 8 +- .../{matchers => matcher}/parse/token.go | 0 .../prometheus/alertmanager/nflog/nflog.go | 42 +- .../alertmanager/notify/discord/discord.go | 44 +- .../alertmanager/notify/email/email.go | 33 +- .../alertmanager/notify/jira/jira.go | 351 ++++++++++ .../alertmanager/notify/jira/types.go | 110 +++ .../alertmanager/notify/msteams/msteams.go | 9 +- .../notify/msteamsv2/msteamsv2.go | 200 ++++++ .../prometheus/alertmanager/notify/notify.go | 153 ++-- .../alertmanager/notify/opsgenie/opsgenie.go | 11 +- .../notify/pagerduty/pagerduty.go | 15 +- .../alertmanager/notify/pushover/pushover.go | 18 +- .../notify/rocketchat/rocketchat.go | 269 +++++++ .../alertmanager/notify/slack/slack.go | 9 +- .../prometheus/alertmanager/notify/sns/sns.go | 47 +- .../alertmanager/notify/telegram/telegram.go | 13 +- .../prometheus/alertmanager/notify/util.go | 13 +- .../notify/victorops/victorops.go | 9 +- .../alertmanager/notify/webex/webex.go | 14 +- .../alertmanager/notify/webhook/webhook.go | 40 +- .../alertmanager/notify/wechat/wechat.go | 11 +- .../alertmanager/pkg/labels/matcher.go | 2 +- .../alertmanager/pkg/labels/parse.go | 2 +- .../alertmanager/provider/mem/mem.go | 95 ++- .../alertmanager/provider/provider.go | 2 +- .../alertmanager/silence/silence.go | 209 +++--- .../prometheus/alertmanager/store/store.go | 42 +- .../alertmanager/template/Dockerfile | 4 +- .../prometheus/alertmanager/template/Makefile | 2 +- .../alertmanager/template/default.tmpl | 59 ++ .../alertmanager/template/template.go | 15 + .../alertmanager/timeinterval/timeinterval.go | 14 +- .../prometheus/alertmanager/types/types.go | 142 ++-- .../prometheus/alertmanager/ui/web.go | 4 +- .../prometheus/common/promslog/slog.go | 201 ++++++ .../exporter-toolkit/web/handler.go | 6 +- .../exporter-toolkit/web/landing_page.go | 4 + .../exporter-toolkit/web/tls_config.go | 21 +- vendor/github.com/rs/cors/cors.go | 16 +- .../github.com/rs/cors/internal/sortedset.go | 152 +++- vendor/github.com/rs/cors/utils.go | 9 - vendor/github.com/shurcooL/vfsgen/.travis.yml | 16 - vendor/github.com/shurcooL/vfsgen/README.md | 18 +- .../github.com/shurcooL/vfsgen/generator.go | 13 +- vendor/github.com/trivago/tgo/LICENSE | 202 ++++++ .../trivago/tgo/tcontainer/arrays.go | 113 +++ .../trivago/tgo/tcontainer/bytepool.go | 157 +++++ .../trivago/tgo/tcontainer/marshalmap.go | 565 +++++++++++++++ .../github.com/trivago/tgo/tcontainer/trie.go | 227 ++++++ .../github.com/trivago/tgo/treflect/clone.go | 80 +++ .../trivago/tgo/treflect/reflection.go | 371 ++++++++++ .../trivago/tgo/treflect/typeregistry.go | 97 +++ .../x/tools/go/gcexportdata/gcexportdata.go | 119 +++- vendor/golang.org/x/tools/go/packages/doc.go | 15 +- .../x/tools/go/packages/external.go | 15 +- .../golang.org/x/tools/go/packages/golist.go | 43 +- .../x/tools/go/packages/loadmode_string.go | 70 +- .../x/tools/go/packages/packages.go | 391 ++++++----- .../x/tools/go/types/objectpath/objectpath.go | 111 +-- .../x/tools/go/types/typeutil/callee.go | 68 ++ .../x/tools/go/types/typeutil/imports.go | 30 + .../x/tools/go/types/typeutil/map.go | 517 ++++++++++++++ .../tools/go/types/typeutil/methodsetcache.go | 71 ++ .../x/tools/go/types/typeutil/ui.go | 53 ++ .../x/tools/internal/aliases/aliases.go | 10 +- .../x/tools/internal/aliases/aliases_go121.go | 35 - .../x/tools/internal/aliases/aliases_go122.go | 33 +- .../x/tools/internal/gcimporter/bimport.go | 61 -- .../x/tools/internal/gcimporter/exportdata.go | 71 +- .../x/tools/internal/gcimporter/gcimporter.go | 69 +- .../x/tools/internal/gcimporter/iexport.go | 284 +++++++- .../x/tools/internal/gcimporter/iimport.go | 43 +- .../internal/gcimporter/iimport_go122.go | 53 ++ .../internal/gcimporter/newInterface10.go | 22 - .../internal/gcimporter/newInterface11.go | 14 - .../tools/internal/gcimporter/predeclared.go | 91 +++ .../internal/gcimporter/support_go118.go | 34 - .../x/tools/internal/gcimporter/unified_no.go | 10 - .../tools/internal/gcimporter/unified_yes.go | 10 - .../tools/internal/gcimporter/ureader_yes.go | 44 +- .../x/tools/internal/gocommand/invoke.go | 18 +- .../internal/packagesinternal/packages.go | 2 - .../x/tools/internal/pkgbits/decoder.go | 34 +- .../x/tools/internal/pkgbits/encoder.go | 43 +- .../x/tools/internal/pkgbits/frames_go1.go | 21 - .../x/tools/internal/pkgbits/frames_go17.go | 28 - .../x/tools/internal/pkgbits/support.go | 2 +- .../x/tools/internal/pkgbits/sync.go | 23 + .../internal/pkgbits/syncmarker_string.go | 7 +- .../x/tools/internal/pkgbits/version.go | 85 +++ .../x/tools/internal/stdlib/manifest.go | 2 +- .../internal/tokeninternal/tokeninternal.go | 137 ---- .../x/tools/internal/typeparams/common.go | 140 ++++ .../x/tools/internal/typeparams/coretype.go | 150 ++++ .../x/tools/internal/typeparams/free.go | 131 ++++ .../x/tools/internal/typeparams/normalize.go | 218 ++++++ .../x/tools/internal/typeparams/termlist.go | 163 +++++ .../x/tools/internal/typeparams/typeterm.go | 169 +++++ .../x/tools/internal/typesinternal/element.go | 133 ++++ .../tools/internal/typesinternal/errorcode.go | 8 +- .../x/tools/internal/typesinternal/recv.go | 8 +- .../x/tools/internal/typesinternal/types.go | 56 ++ .../tools/internal/typesinternal/zerovalue.go | 282 ++++++++ .../x/tools/internal/versions/constraint.go | 13 - .../internal/versions/constraint_go121.go | 14 - .../x/tools/internal/versions/toolchain.go | 14 - .../internal/versions/toolchain_go119.go | 14 - .../internal/versions/toolchain_go120.go | 14 - .../internal/versions/toolchain_go121.go | 14 - .../x/tools/internal/versions/types.go | 28 +- .../x/tools/internal/versions/types_go121.go | 30 - .../x/tools/internal/versions/types_go122.go | 41 -- vendor/gopkg.in/telebot.v3/admin.go | 7 + vendor/gopkg.in/telebot.v3/api.go | 57 +- vendor/gopkg.in/telebot.v3/boost.go | 113 +++ vendor/gopkg.in/telebot.v3/bot.go | 211 ++++-- vendor/gopkg.in/telebot.v3/callback.go | 9 + vendor/gopkg.in/telebot.v3/chat.go | 58 +- vendor/gopkg.in/telebot.v3/context.go | 41 +- vendor/gopkg.in/telebot.v3/errors.go | 9 + vendor/gopkg.in/telebot.v3/giveaway.go | 103 +++ vendor/gopkg.in/telebot.v3/inline.go | 42 ++ vendor/gopkg.in/telebot.v3/inline_types.go | 48 +- vendor/gopkg.in/telebot.v3/input_types.go | 6 +- vendor/gopkg.in/telebot.v3/markup.go | 22 +- vendor/gopkg.in/telebot.v3/media.go | 37 +- vendor/gopkg.in/telebot.v3/message.go | 264 ++++++- vendor/gopkg.in/telebot.v3/options.go | 58 +- vendor/gopkg.in/telebot.v3/poll.go | 1 + vendor/gopkg.in/telebot.v3/poller.go | 23 +- vendor/gopkg.in/telebot.v3/react.go | 67 ++ vendor/gopkg.in/telebot.v3/sendable.go | 3 +- vendor/gopkg.in/telebot.v3/sticker.go | 306 ++++++++ vendor/gopkg.in/telebot.v3/stickers.go | 212 ------ vendor/gopkg.in/telebot.v3/telebot.go | 3 + vendor/gopkg.in/telebot.v3/topic.go | 28 +- vendor/gopkg.in/telebot.v3/update.go | 42 +- vendor/gopkg.in/telebot.v3/web_app.go | 4 +- vendor/modules.txt | 47 +- 206 files changed, 11647 insertions(+), 3195 deletions(-) delete mode 100644 vendor/github.com/benbjohnson/clock/LICENSE delete mode 100644 vendor/github.com/benbjohnson/clock/README.md delete mode 100644 vendor/github.com/benbjohnson/clock/clock.go delete mode 100644 vendor/github.com/benbjohnson/clock/context.go create mode 100644 vendor/github.com/coder/quartz/.gitignore create mode 100644 vendor/github.com/coder/quartz/LICENSE create mode 100644 vendor/github.com/coder/quartz/README.md create mode 100644 vendor/github.com/coder/quartz/clock.go create mode 100644 vendor/github.com/coder/quartz/mock.go create mode 100644 vendor/github.com/coder/quartz/real.go create mode 100644 vendor/github.com/coder/quartz/ticker.go create mode 100644 vendor/github.com/coder/quartz/timer.go delete mode 100644 vendor/github.com/jessevdk/go-flags/.travis.yml rename vendor/github.com/prometheus/alertmanager/{matchers => matcher}/compat/parse.go (65%) rename vendor/github.com/prometheus/alertmanager/{matchers => matcher}/parse/lexer.go (99%) rename vendor/github.com/prometheus/alertmanager/{matchers => matcher}/parse/parse.go (98%) rename vendor/github.com/prometheus/alertmanager/{matchers => matcher}/parse/token.go (100%) create mode 100644 vendor/github.com/prometheus/alertmanager/notify/jira/jira.go create mode 100644 vendor/github.com/prometheus/alertmanager/notify/jira/types.go create mode 100644 vendor/github.com/prometheus/alertmanager/notify/msteamsv2/msteamsv2.go create mode 100644 vendor/github.com/prometheus/alertmanager/notify/rocketchat/rocketchat.go create mode 100644 vendor/github.com/prometheus/common/promslog/slog.go delete mode 100644 vendor/github.com/shurcooL/vfsgen/.travis.yml create mode 100644 vendor/github.com/trivago/tgo/LICENSE create mode 100644 vendor/github.com/trivago/tgo/tcontainer/arrays.go create mode 100644 vendor/github.com/trivago/tgo/tcontainer/bytepool.go create mode 100644 vendor/github.com/trivago/tgo/tcontainer/marshalmap.go create mode 100644 vendor/github.com/trivago/tgo/tcontainer/trie.go create mode 100644 vendor/github.com/trivago/tgo/treflect/clone.go create mode 100644 vendor/github.com/trivago/tgo/treflect/reflection.go create mode 100644 vendor/github.com/trivago/tgo/treflect/typeregistry.go create mode 100644 vendor/golang.org/x/tools/go/types/typeutil/callee.go create mode 100644 vendor/golang.org/x/tools/go/types/typeutil/imports.go create mode 100644 vendor/golang.org/x/tools/go/types/typeutil/map.go create mode 100644 vendor/golang.org/x/tools/go/types/typeutil/methodsetcache.go create mode 100644 vendor/golang.org/x/tools/go/types/typeutil/ui.go delete mode 100644 vendor/golang.org/x/tools/internal/aliases/aliases_go121.go create mode 100644 vendor/golang.org/x/tools/internal/gcimporter/iimport_go122.go delete mode 100644 vendor/golang.org/x/tools/internal/gcimporter/newInterface10.go delete mode 100644 vendor/golang.org/x/tools/internal/gcimporter/newInterface11.go create mode 100644 vendor/golang.org/x/tools/internal/gcimporter/predeclared.go delete mode 100644 vendor/golang.org/x/tools/internal/gcimporter/support_go118.go delete mode 100644 vendor/golang.org/x/tools/internal/gcimporter/unified_no.go delete mode 100644 vendor/golang.org/x/tools/internal/gcimporter/unified_yes.go delete mode 100644 vendor/golang.org/x/tools/internal/pkgbits/frames_go1.go delete mode 100644 vendor/golang.org/x/tools/internal/pkgbits/frames_go17.go create mode 100644 vendor/golang.org/x/tools/internal/pkgbits/version.go delete mode 100644 vendor/golang.org/x/tools/internal/tokeninternal/tokeninternal.go create mode 100644 vendor/golang.org/x/tools/internal/typeparams/common.go create mode 100644 vendor/golang.org/x/tools/internal/typeparams/coretype.go create mode 100644 vendor/golang.org/x/tools/internal/typeparams/free.go create mode 100644 vendor/golang.org/x/tools/internal/typeparams/normalize.go create mode 100644 vendor/golang.org/x/tools/internal/typeparams/termlist.go create mode 100644 vendor/golang.org/x/tools/internal/typeparams/typeterm.go create mode 100644 vendor/golang.org/x/tools/internal/typesinternal/element.go create mode 100644 vendor/golang.org/x/tools/internal/typesinternal/zerovalue.go delete mode 100644 vendor/golang.org/x/tools/internal/versions/constraint.go delete mode 100644 vendor/golang.org/x/tools/internal/versions/constraint_go121.go delete mode 100644 vendor/golang.org/x/tools/internal/versions/toolchain.go delete mode 100644 vendor/golang.org/x/tools/internal/versions/toolchain_go119.go delete mode 100644 vendor/golang.org/x/tools/internal/versions/toolchain_go120.go delete mode 100644 vendor/golang.org/x/tools/internal/versions/toolchain_go121.go delete mode 100644 vendor/golang.org/x/tools/internal/versions/types_go121.go delete mode 100644 vendor/golang.org/x/tools/internal/versions/types_go122.go create mode 100644 vendor/gopkg.in/telebot.v3/boost.go create mode 100644 vendor/gopkg.in/telebot.v3/giveaway.go create mode 100644 vendor/gopkg.in/telebot.v3/react.go create mode 100644 vendor/gopkg.in/telebot.v3/sticker.go delete mode 100644 vendor/gopkg.in/telebot.v3/stickers.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 35487bfec8..4deb01dede 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,9 @@ ## master / unreleased * [FEATURE] Query Frontend: Add dynamic interval size for query splitting. This is enabled by configuring experimental flags `querier.max-shards-per-query` and/or `querier.max-fetched-data-duration-per-query`. The split interval size is dynamically increased to maintain a number of shards and total duration fetched below the configured values. #6458 -* [ENHANCEMENT] Add `compactor.auto-forget-delay` for compactor to auto forget compactors after X minutes without heartbeat. #6533 * [FEATURE] Querier/Ruler: Add `query_partial_data` and `rules_partial_data` limits to allow queries/rules to be evaluated with data from a single zone, if other zones are not available. #6526 +* [FEATURE] Update prometheus alertmanager version to v0.28.0 and add new integration msteamsv2, jira, and rocketchat. #6590 +* [ENHANCEMENT] Add `compactor.auto-forget-delay` for compactor to auto forget compactors after X minutes without heartbeat. #6533 * [ENHANCEMENT] StoreGateway: Emit more histogram buckets on the `cortex_querier_storegateway_refetches_per_query` metric. #6570 * [ENHANCEMENT] Querier: Apply bytes limiter to LabelNames and LabelValuesForLabelNames. #6568 * [ENHANCEMENT] Query Frontend: Add a `too_many_tenants` reason label value to `cortex_rejected_queries_total` metric to track the rejected query count due to the # of tenant limits. #6569 diff --git a/docs/configuration/config-file-reference.md b/docs/configuration/config-file-reference.md index 5fa95418d2..34f5cda05c 100644 --- a/docs/configuration/config-file-reference.md +++ b/docs/configuration/config-file-reference.md @@ -3707,7 +3707,8 @@ query_rejection: # is given in JSON format. Rate limit has the same meaning as # -alertmanager.notification-rate-limit, but only applies for specific # integration. Allowed integration names: webhook, email, pagerduty, opsgenie, -# wechat, slack, victorops, pushover, sns, telegram, discord, webex, msteams. +# wechat, slack, victorops, pushover, sns, telegram, discord, webex, msteams, +# msteamsv2, jira, rocketchat. # CLI flag: -alertmanager.notification-rate-limit-per-integration [alertmanager_notification_rate_limit_per_integration: | default = {}] diff --git a/go.mod b/go.mod index b15d4dc826..8b7d8bfc74 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.23.0 require ( github.com/Masterminds/squirrel v1.5.4 - github.com/alecthomas/units v0.0.0-20240626203959-61d1e3462e30 + github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b github.com/alicebob/miniredis/v2 v2.34.0 github.com/armon/go-metrics v0.4.1 github.com/aws/aws-sdk-go v1.55.5 @@ -40,7 +40,7 @@ require ( github.com/opentracing-contrib/go-stdlib v1.1.0 github.com/opentracing/opentracing-go v1.2.0 github.com/pkg/errors v0.9.1 - github.com/prometheus/alertmanager v0.27.0 + github.com/prometheus/alertmanager v0.28.0 github.com/prometheus/client_golang v1.20.5 github.com/prometheus/client_model v0.6.1 github.com/prometheus/common v0.61.0 @@ -120,6 +120,7 @@ require ( github.com/blang/semver/v4 v4.0.0 // indirect github.com/caio/go-tdigest v3.1.0+incompatible // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/coder/quartz v0.1.2 // indirect github.com/coreos/go-semver v0.3.0 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/cristalhq/hedgedhttp v0.9.1 // indirect @@ -166,7 +167,7 @@ require ( github.com/hashicorp/go-rootcerts v1.0.2 // indirect github.com/hashicorp/golang-lru v0.6.0 // indirect github.com/hashicorp/serf v0.10.1 // indirect - github.com/jessevdk/go-flags v1.5.0 // indirect + github.com/jessevdk/go-flags v1.6.1 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/jpillora/backoff v1.0.0 // indirect @@ -196,18 +197,19 @@ require ( github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus-community/prom-label-proxy v0.8.1-0.20240127162815-c1195f9aabc0 // indirect github.com/prometheus/common/sigv4 v0.1.0 // indirect - github.com/prometheus/exporter-toolkit v0.12.0 // indirect + github.com/prometheus/exporter-toolkit v0.13.2 // indirect github.com/prometheus/procfs v0.15.1 // indirect github.com/redis/rueidis v1.0.45-alpha.1 // indirect - github.com/rs/cors v1.11.0 // indirect + github.com/rs/cors v1.11.1 // indirect github.com/rs/xid v1.6.0 // indirect github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect github.com/seiflotfy/cuckoofilter v0.0.0-20240715131351-a2f2c23f1771 // indirect github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c // indirect - github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546 // indirect + github.com/shurcooL/vfsgen v0.0.0-20230704071429-0000e147ea92 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/soheilhy/cmux v0.1.5 // indirect github.com/stretchr/objx v0.5.2 // indirect + github.com/trivago/tgo v1.0.7 // indirect github.com/uber/jaeger-lib v2.4.1+incompatible // indirect github.com/vimeo/galaxycache v0.0.0-20210323154928-b7e5d71c067a // indirect github.com/weaveworks/promrus v1.2.0 // indirect @@ -231,18 +233,18 @@ require ( go4.org/intern v0.0.0-20230525184215-6c62f75575cb // indirect go4.org/unsafe/assume-no-moving-gc v0.0.0-20230525183740-e7c30c78aeb2 // indirect golang.org/x/crypto v0.31.0 // indirect - golang.org/x/mod v0.21.0 // indirect + golang.org/x/mod v0.22.0 // indirect golang.org/x/oauth2 v0.24.0 // indirect golang.org/x/sys v0.28.0 // indirect golang.org/x/text v0.21.0 // indirect - golang.org/x/tools v0.24.0 // indirect + golang.org/x/tools v0.28.0 // indirect gonum.org/v1/gonum v0.15.0 // indirect google.golang.org/api v0.195.0 // indirect google.golang.org/genproto v0.0.0-20240823204242-4ba0660f739c // indirect google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 // indirect gopkg.in/alecthomas/kingpin.v2 v2.2.6 // indirect - gopkg.in/telebot.v3 v3.2.1 // indirect + gopkg.in/telebot.v3 v3.3.8 // indirect k8s.io/apimachinery v0.31.1 // indirect k8s.io/client-go v0.31.0 // indirect k8s.io/klog/v2 v2.130.1 // indirect diff --git a/go.sum b/go.sum index b91e0501f9..e9a1570147 100644 --- a/go.sum +++ b/go.sum @@ -813,8 +813,8 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/alecthomas/units v0.0.0-20210208195552-ff826a37aa15/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= -github.com/alecthomas/units v0.0.0-20240626203959-61d1e3462e30 h1:t3eaIm0rUkzbrIewtiFmMK5RXHej2XnoXNhxVsAYUfg= -github.com/alecthomas/units v0.0.0-20240626203959-61d1e3462e30/go.mod h1:fvzegU4vN3H1qMT+8wDmzjAcDONcgo2/SZ/TyfdUOFs= +github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b h1:mimo19zliBX/vSQ6PWWSL9lK8qwHozUj03+zLoEB8O0= +github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b/go.mod h1:fvzegU4vN3H1qMT+8wDmzjAcDONcgo2/SZ/TyfdUOFs= github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 h1:uvdUDbHQHO85qeSydJtItA4T55Pw6BtAejd0APRJOCE= github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc= github.com/alicebob/miniredis/v2 v2.34.0 h1:mBFWMaJSNL9RwdGRyEDoAAv8OQc5UlEhLDQggTglU/0= @@ -929,6 +929,8 @@ github.com/cncf/xds/go v0.0.0-20230310173818-32f1caf87195/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b h1:ga8SEFjZ60pxLcmhnThWgvH2wg8376yUJmPhEH4H3kw= github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= +github.com/coder/quartz v0.1.2 h1:PVhc9sJimTdKd3VbygXtS4826EOCpB1fXoRlLnCrE+s= +github.com/coder/quartz v0.1.2/go.mod h1:vsiCc+AHViMKH2CQpGIpFgdHIEQsxwm8yCscqKmzbRA= github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= @@ -975,6 +977,10 @@ github.com/efficientgo/e2e v0.14.1-0.20230710114240-c316eb95ae5b h1:8VX23BNufsa4 github.com/efficientgo/e2e v0.14.1-0.20230710114240-c316eb95ae5b/go.mod h1:plsKU0YHE9uX+7utvr7SiDtVBSHJyEfHRO4UnUgDmts= github.com/efficientgo/tools/extkingpin v0.0.0-20220817170617-6c25e3b627dd h1:VaYzzXeUbC5fVheskcKVNOyJMEYD+HgrJNzIAg/mRIM= github.com/efficientgo/tools/extkingpin v0.0.0-20220817170617-6c25e3b627dd/go.mod h1:ZV0utlglOczUWv3ih2AbqPSoLoFzdplUYxwV62eZi6Q= +github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21 h1:OJyUGMJTzHTd1XQp98QTaHernxMYzRaOasRir9hUlFQ= +github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ= +github.com/emersion/go-smtp v0.21.3 h1:7uVwagE8iPYE48WhNsng3RRpCUpFvNl39JGNSIyGVMY= +github.com/emersion/go-smtp v0.21.3/go.mod h1:qm27SGYgoIPRot6ubfQ/GpiPy/g3PaZAVRxiO/sDUgQ= github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= @@ -1329,8 +1335,8 @@ github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/ionos-cloud/sdk-go/v6 v6.0.4 h1:4LoWeM7WtcDqYDjlntqQ3fD6XaENlCw2YqiVWkHQbNA= github.com/ionos-cloud/sdk-go/v6 v6.0.4/go.mod h1:UE3V/2DjnqD5doOqtjYqzJRMpI1RiwrvuuSEPX1pdnk= -github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LFvc= -github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= +github.com/jessevdk/go-flags v1.6.1 h1:Cvu5U8UGrLay1rZfv/zP7iLpSHGUZ/Ou68T0iX1bBK4= +github.com/jessevdk/go-flags v1.6.1/go.mod h1:Mk8T1hIAWpOiJiHa9rJASDK2UGWji0EuPGBnNLMooyc= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= @@ -1534,8 +1540,8 @@ github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndr github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= github.com/prometheus-community/prom-label-proxy v0.8.1-0.20240127162815-c1195f9aabc0 h1:owfYHh79h8Y5HvNMGyww+DaVwo10CKiRW1RQrrZzIwg= github.com/prometheus-community/prom-label-proxy v0.8.1-0.20240127162815-c1195f9aabc0/go.mod h1:rT989D4UtOcfd9tVqIZRVIM8rkg+9XbreBjFNEKXvVI= -github.com/prometheus/alertmanager v0.27.0 h1:V6nTa2J5V4s8TG4C4HtrBP/WNSebCCTYGGv4qecA/+I= -github.com/prometheus/alertmanager v0.27.0/go.mod h1:8Ia/R3urPmbzJ8OsdvmZvIprDwvwmYCmUbwBL+jlPOE= +github.com/prometheus/alertmanager v0.28.0 h1:sLN+6HhZet8hrbmGHLAHWsTXgZSVCvq9Ix3U3wvivqc= +github.com/prometheus/alertmanager v0.28.0/go.mod h1:/okSnb2LlodbMlRoOWQEKtqI/coOo2NKZDm2Hu9QHLQ= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= @@ -1562,8 +1568,8 @@ github.com/prometheus/common v0.61.0 h1:3gv/GThfX0cV2lpO7gkTUwZru38mxevy90Bj8YFS github.com/prometheus/common v0.61.0/go.mod h1:zr29OCN/2BsJRaFwG8QOBr41D6kkchKbpeNH7pAjb/s= github.com/prometheus/common/sigv4 v0.1.0 h1:qoVebwtwwEhS85Czm2dSROY5fTo2PAPEVdDeppTwGX4= github.com/prometheus/common/sigv4 v0.1.0/go.mod h1:2Jkxxk9yYvCkE5G1sQT7GuEXm57JrvHu9k5YwTjsNtI= -github.com/prometheus/exporter-toolkit v0.12.0 h1:DkE5RcEZR3lQA2QD5JLVQIf41dFKNsVMXFhgqcif7fo= -github.com/prometheus/exporter-toolkit v0.12.0/go.mod h1:fQH0KtTn0yrrS0S82kqppRjDDiwMfIQUwT+RBRRhwUc= +github.com/prometheus/exporter-toolkit v0.13.2 h1:Z02fYtbqTMy2i/f+xZ+UK5jy/bl1Ex3ndzh06T/Q9DQ= +github.com/prometheus/exporter-toolkit v0.13.2/go.mod h1:tCqnfx21q6qN1KA4U3Bfb8uWzXfijIrJz3/kTIqMV7g= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= @@ -1583,8 +1589,8 @@ github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTE github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= -github.com/rs/cors v1.11.0 h1:0B9GE/r9Bc2UxRMMtymBkHTenPkHDv0CW4Y98GBY+po= -github.com/rs/cors v1.11.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= +github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA= +github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU= github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w= @@ -1603,8 +1609,8 @@ github.com/sercand/kuberesolver/v5 v5.1.1 h1:CYH+d67G0sGBj7q5wLK61yzqJJ8gLLC8aep github.com/sercand/kuberesolver/v5 v5.1.1/go.mod h1:Fs1KbKhVRnB2aDWN12NjKCB+RgYMWZJ294T3BtmVCpQ= github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c h1:aqg5Vm5dwtvL+YgDpBcK1ITf3o96N/K7/wsRXQnUTEs= github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c/go.mod h1:owqhoLW1qZoYLZzLnBw+QkPP9WZnjlSWihhxAJC1+/M= -github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546 h1:pXY9qYc/MP5zdvqWEUH6SjNiu7VhSjuVFTFiTcphaLU= -github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= +github.com/shurcooL/vfsgen v0.0.0-20230704071429-0000e147ea92 h1:OfRzdxCzDhp+rsKWXuOO2I/quKMJ/+TQwVbIP/gltZg= +github.com/shurcooL/vfsgen v0.0.0-20230704071429-0000e147ea92/go.mod h1:7/OT02F6S6I7v6WXb+IjhMuZEYfH/RJ5RwEWnEo5BMg= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= @@ -1661,6 +1667,8 @@ github.com/thanos-io/thanos v0.37.3-0.20250203070737-57efc2aacd6f h1:Q8lYAyPHlQs github.com/thanos-io/thanos v0.37.3-0.20250203070737-57efc2aacd6f/go.mod h1:Y86Y7QT3nlYNC1bGCPYBHDn2ecDzblYBJRhs41u8qcY= github.com/tjhop/slog-gokit v0.1.2 h1:pmQI4SvU9h4gA0vIQsdhJQSqQg4mOmsPykG2/PM3j1I= github.com/tjhop/slog-gokit v0.1.2/go.mod h1:8fhlcp8C8ELbg3GCyKv06tgt4B5sDq2P1r2DQAu1HuM= +github.com/trivago/tgo v1.0.7 h1:uaWH/XIy9aWYWpjm2CU3RpcqZXmX2ysQ9/Go+d9gyrM= +github.com/trivago/tgo v1.0.7/go.mod h1:w4dpD+3tzNIIiIfkWWa85w5/B77tlvdZckQ+6PkFnhc= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaOOb6ThwMmTEbhRwtKR97o= github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= @@ -1851,8 +1859,8 @@ golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= -golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= +golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -2204,8 +2212,8 @@ golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= golang.org/x/tools v0.10.0/go.mod h1:UJwyiVBsOA2uwvK/e5OY3GTpDUJriEd+/YlqAwLPmyM= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= -golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= -golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= +golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8= +golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -2507,8 +2515,8 @@ gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/telebot.v3 v3.2.1 h1:3I4LohaAyJBiivGmkfB+CiVu7QFOWkuZ4+KHgO/G3rs= -gopkg.in/telebot.v3 v3.2.1/go.mod h1:GJKwwWqp9nSkIVN51eRKU78aB5f5OnQuWdwiIZfPbko= +gopkg.in/telebot.v3 v3.3.8 h1:uVDGjak9l824FN9YARWUHMsiNZnlohAVwUycw21k6t8= +gopkg.in/telebot.v3 v3.3.8/go.mod h1:1mlbqcLTVSfK9dx7fdp+Nb5HZsy4LLPtpZTKmwhwtzM= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/pkg/alertmanager/alertmanager.go b/pkg/alertmanager/alertmanager.go index ec134c07f4..4e31545ce6 100644 --- a/pkg/alertmanager/alertmanager.go +++ b/pkg/alertmanager/alertmanager.go @@ -28,10 +28,13 @@ import ( "github.com/prometheus/alertmanager/notify" "github.com/prometheus/alertmanager/notify/discord" "github.com/prometheus/alertmanager/notify/email" + "github.com/prometheus/alertmanager/notify/jira" "github.com/prometheus/alertmanager/notify/msteams" + "github.com/prometheus/alertmanager/notify/msteamsv2" "github.com/prometheus/alertmanager/notify/opsgenie" "github.com/prometheus/alertmanager/notify/pagerduty" "github.com/prometheus/alertmanager/notify/pushover" + "github.com/prometheus/alertmanager/notify/rocketchat" "github.com/prometheus/alertmanager/notify/slack" "github.com/prometheus/alertmanager/notify/sns" "github.com/prometheus/alertmanager/notify/telegram" @@ -54,6 +57,7 @@ import ( "github.com/cortexproject/cortex/pkg/alertmanager/alertstore" "github.com/cortexproject/cortex/pkg/util/flagext" + util_log "github.com/cortexproject/cortex/pkg/util/log" util_net "github.com/cortexproject/cortex/pkg/util/net" "github.com/cortexproject/cortex/pkg/util/services" ) @@ -99,7 +103,7 @@ type Alertmanager struct { persister *statePersister nflog *nflog.Log silences *silence.Silences - marker types.Marker + marker *types.MemMarker alerts *mem.Alerts dispatcher *dispatch.Dispatcher inhibitor *inhibit.Inhibitor @@ -204,7 +208,7 @@ func New(cfg *Config, reg *prometheus.Registry) (*Alertmanager, error) { am.nflog, err = nflog.New(nflog.Options{ SnapshotFile: notificationFile, Retention: cfg.Retention, - Logger: log.With(am.logger, "component", "nflog"), + Logger: util_log.GoKitLogToSlog(log.With(am.logger, "component", "nflog")), Metrics: am.registry, }) @@ -224,7 +228,7 @@ func New(cfg *Config, reg *prometheus.Registry) (*Alertmanager, error) { am.silences, err = silence.New(silence.Options{ SnapshotFile: silencesFile, Retention: cfg.Retention, - Logger: log.With(am.logger, "component", "silences"), + Logger: util_log.GoKitLogToSlog(log.With(am.logger, "component", "silences")), Metrics: am.registry, }) if err != nil { @@ -246,7 +250,7 @@ func New(cfg *Config, reg *prometheus.Registry) (*Alertmanager, error) { } // Lets not enable any AM experimental feature for now. - featureConfig, err := featurecontrol.NewFlags(am.logger, "") + featureConfig, err := featurecontrol.NewFlags(util_log.GoKitLogToSlog(am.logger), "") if err != nil { level.Error(am.logger).Log("msg", "error parsing the feature flag list", "err", err) return nil, errors.Wrap(err, "error parsing the feature flag list") @@ -264,19 +268,20 @@ func New(cfg *Config, reg *prometheus.Registry) (*Alertmanager, error) { if am.cfg.Limits != nil { callback = newAlertsLimiter(am.cfg.UserID, am.cfg.Limits, reg) } - am.alerts, err = mem.NewAlerts(context.Background(), am.marker, am.cfg.GCInterval, callback, am.logger, am.registry) + am.alerts, err = mem.NewAlerts(context.Background(), am.marker, am.cfg.GCInterval, callback, util_log.GoKitLogToSlog(am.logger), am.registry) if err != nil { return nil, fmt.Errorf("failed to create alerts: %v", err) } am.api, err = api.New(api.Options{ - Alerts: am.alerts, - Silences: am.silences, - StatusFunc: am.marker.Status, + Alerts: am.alerts, + Silences: am.silences, + AlertStatusFunc: am.marker.Status, + GroupMutedFunc: am.marker.Muted, // Cortex should not expose cluster information back to its tenants. Peer: &NilPeer{}, Registry: am.registry, - Logger: log.With(am.logger, "component", "api"), + Logger: util_log.GoKitLogToSlog(log.With(am.logger, "component", "api")), GroupFunc: func(f1 func(*dispatch.Route) bool, f2 func(*types.Alert, time.Time) bool) (dispatch.AlertGroups, map[model.Fingerprint][]string) { return am.dispatcher.Groups(f1, f2) }, @@ -288,7 +293,7 @@ func New(cfg *Config, reg *prometheus.Registry) (*Alertmanager, error) { router := route.New().WithPrefix(am.cfg.ExternalURL.Path) - ui.Register(router, webReload, log.With(am.logger, "component", "ui")) + ui.Register(router, webReload, util_log.GoKitLogToSlog(log.With(am.logger, "component", "ui"))) am.mux = am.api.Register(router, am.cfg.ExternalURL.Path) // Override some extra paths registered in the router (eg. /metrics which by default exposes prometheus.DefaultRegisterer). @@ -355,7 +360,7 @@ func (am *Alertmanager) ApplyConfig(userID string, conf *config.Config, rawCfg s am.dispatcher.Stop() } - am.inhibitor = inhibit.NewInhibitor(am.alerts, conf.InhibitRules, am.marker, log.With(am.logger, "component", "inhibitor")) + am.inhibitor = inhibit.NewInhibitor(am.alerts, conf.InhibitRules, am.marker, util_log.GoKitLogToSlog(log.With(am.logger, "component", "inhibitor"))) waitFunc := clusterWait(am.state.Position, am.cfg.PeerTimeout) @@ -398,8 +403,9 @@ func (am *Alertmanager) ApplyConfig(userID string, conf *config.Config, rawCfg s integrationsMap, waitFunc, am.inhibitor, - silence.NewSilencer(am.silences, am.marker, am.logger), + silence.NewSilencer(am.silences, am.marker, util_log.GoKitLogToSlog(am.logger)), timeinterval.NewIntervener(timeIntervals), + am.marker, am.nflog, am.state, ) @@ -411,7 +417,7 @@ func (am *Alertmanager) ApplyConfig(userID string, conf *config.Config, rawCfg s am.marker, timeoutFunc, &dispatcherLimits{tenant: am.cfg.UserID, limits: am.cfg.Limits}, - log.With(am.logger, "component", "dispatcher"), + util_log.GoKitLogToSlog(log.With(am.logger, "component", "dispatcher")), am.dispatcherMetrics, ) @@ -514,43 +520,84 @@ func buildReceiverIntegrations(nc config.Receiver, tmpl *template.Template, fire } for i, c := range nc.WebhookConfigs { - add("webhook", i, c, func(l log.Logger) (notify.Notifier, error) { return webhook.New(c, tmpl, l, httpOps...) }) + add("webhook", i, c, func(l log.Logger) (notify.Notifier, error) { + return webhook.New(c, tmpl, util_log.GoKitLogToSlog(l), httpOps...) + }) } for i, c := range nc.EmailConfigs { - add("email", i, c, func(l log.Logger) (notify.Notifier, error) { return email.New(c, tmpl, l), nil }) + add("email", i, c, func(l log.Logger) (notify.Notifier, error) { + return email.New(c, tmpl, util_log.GoKitLogToSlog(l)), nil + }) } for i, c := range nc.PagerdutyConfigs { - add("pagerduty", i, c, func(l log.Logger) (notify.Notifier, error) { return pagerduty.New(c, tmpl, l, httpOps...) }) + add("pagerduty", i, c, func(l log.Logger) (notify.Notifier, error) { + return pagerduty.New(c, tmpl, util_log.GoKitLogToSlog(l), httpOps...) + }) } for i, c := range nc.OpsGenieConfigs { - add("opsgenie", i, c, func(l log.Logger) (notify.Notifier, error) { return opsgenie.New(c, tmpl, l, httpOps...) }) + add("opsgenie", i, c, func(l log.Logger) (notify.Notifier, error) { + return opsgenie.New(c, tmpl, util_log.GoKitLogToSlog(l), httpOps...) + }) } for i, c := range nc.WechatConfigs { - add("wechat", i, c, func(l log.Logger) (notify.Notifier, error) { return wechat.New(c, tmpl, l, httpOps...) }) + add("wechat", i, c, func(l log.Logger) (notify.Notifier, error) { + return wechat.New(c, tmpl, util_log.GoKitLogToSlog(l), httpOps...) + }) } for i, c := range nc.SlackConfigs { - add("slack", i, c, func(l log.Logger) (notify.Notifier, error) { return slack.New(c, tmpl, l, httpOps...) }) + add("slack", i, c, func(l log.Logger) (notify.Notifier, error) { + return slack.New(c, tmpl, util_log.GoKitLogToSlog(l), httpOps...) + }) } for i, c := range nc.VictorOpsConfigs { - add("victorops", i, c, func(l log.Logger) (notify.Notifier, error) { return victorops.New(c, tmpl, l, httpOps...) }) + add("victorops", i, c, func(l log.Logger) (notify.Notifier, error) { + return victorops.New(c, tmpl, util_log.GoKitLogToSlog(l), httpOps...) + }) } for i, c := range nc.PushoverConfigs { - add("pushover", i, c, func(l log.Logger) (notify.Notifier, error) { return pushover.New(c, tmpl, l, httpOps...) }) + add("pushover", i, c, func(l log.Logger) (notify.Notifier, error) { + return pushover.New(c, tmpl, util_log.GoKitLogToSlog(l), httpOps...) + }) } for i, c := range nc.SNSConfigs { - add("sns", i, c, func(l log.Logger) (notify.Notifier, error) { return sns.New(c, tmpl, l, httpOps...) }) + add("sns", i, c, func(l log.Logger) (notify.Notifier, error) { + return sns.New(c, tmpl, util_log.GoKitLogToSlog(l), httpOps...) + }) } for i, c := range nc.TelegramConfigs { - add("telegram", i, c, func(l log.Logger) (notify.Notifier, error) { return telegram.New(c, tmpl, l, httpOps...) }) + add("telegram", i, c, func(l log.Logger) (notify.Notifier, error) { + return telegram.New(c, tmpl, util_log.GoKitLogToSlog(l), httpOps...) + }) } for i, c := range nc.DiscordConfigs { - add("discord", i, c, func(l log.Logger) (notify.Notifier, error) { return discord.New(c, tmpl, l, httpOps...) }) + add("discord", i, c, func(l log.Logger) (notify.Notifier, error) { + return discord.New(c, tmpl, util_log.GoKitLogToSlog(l), httpOps...) + }) } for i, c := range nc.WebexConfigs { - add("webex", i, c, func(l log.Logger) (notify.Notifier, error) { return webex.New(c, tmpl, l, httpOps...) }) + add("webex", i, c, func(l log.Logger) (notify.Notifier, error) { + return webex.New(c, tmpl, util_log.GoKitLogToSlog(l), httpOps...) + }) } for i, c := range nc.MSTeamsConfigs { - add("msteams", i, c, func(l log.Logger) (notify.Notifier, error) { return msteams.New(c, tmpl, l) }) + add("msteams", i, c, func(l log.Logger) (notify.Notifier, error) { + return msteams.New(c, tmpl, util_log.GoKitLogToSlog(l), httpOps...) + }) + } + for i, c := range nc.MSTeamsV2Configs { + add("msteamsv2", i, c, func(l log.Logger) (notify.Notifier, error) { + return msteamsv2.New(c, tmpl, util_log.GoKitLogToSlog(l), httpOps...) + }) + } + for i, c := range nc.JiraConfigs { + add("jira", i, c, func(l log.Logger) (notify.Notifier, error) { + return jira.New(c, tmpl, util_log.GoKitLogToSlog(l), httpOps...) + }) + } + for i, c := range nc.RocketchatConfigs { + add("rocketchat", i, c, func(l log.Logger) (notify.Notifier, error) { + return rocketchat.New(c, tmpl, util_log.GoKitLogToSlog(l), httpOps...) + }) } // If we add support for more integrations, we need to add them to validation as well. See validation.allowedIntegrationNames field. if errs.Len() > 0 { diff --git a/pkg/alertmanager/alertmanager_http_test.go b/pkg/alertmanager/alertmanager_http_test.go index fc554ffa0f..987221593a 100644 --- a/pkg/alertmanager/alertmanager_http_test.go +++ b/pkg/alertmanager/alertmanager_http_test.go @@ -7,9 +7,9 @@ import ( "testing" "time" - "github.com/go-kit/log" "github.com/prometheus/alertmanager/cluster" "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/common/promslog" "github.com/stretchr/testify/require" ) @@ -18,7 +18,7 @@ func TestMultitenantAlertmanager_GetStatusHandler(t *testing.T) { defer cancel() var peer *cluster.Peer { - logger := log.NewNopLogger() + logger := promslog.NewNopLogger() createPeer := func(peers []string) (*cluster.Peer, error) { return cluster.Create( logger, diff --git a/pkg/alertmanager/api_test.go b/pkg/alertmanager/api_test.go index 0b26abe963..bdd9c6504d 100644 --- a/pkg/alertmanager/api_test.go +++ b/pkg/alertmanager/api_test.go @@ -894,7 +894,7 @@ func TestValidateAlertmanagerConfig(t *testing.T) { "map containing TLSConfig as nested child": { input: map[string][]config.EmailConfig{ "test": {{ - TLSConfig: commoncfg.TLSConfig{ + TLSConfig: &commoncfg.TLSConfig{ CAFile: "/file", }, }}, diff --git a/pkg/alertmanager/merger/v2_alert_groups_test.go b/pkg/alertmanager/merger/v2_alert_groups_test.go index 3c667a784c..db6bfd1d79 100644 --- a/pkg/alertmanager/merger/v2_alert_groups_test.go +++ b/pkg/alertmanager/merger/v2_alert_groups_test.go @@ -17,19 +17,19 @@ func TestV2AlertGroups(t *testing.T) { []byte(`[` + `{"alerts":[{"annotations":{},"endsAt":"2021-04-21T10:47:32.161+02:00","fingerprint":"c4b6b79a607b6ba0",` + `"receivers":[{"name":"dummy"}],"startsAt":"2021-04-21T09:47:32.161+02:00",` + - `"status":{"inhibitedBy":[],"silencedBy":[],"state":"unprocessed"},` + + `"status":{"inhibitedBy":[],"mutedBy":[],"silencedBy":[],"state":"unprocessed"},` + `"updatedAt":"2021-04-21T07:47:32.163Z","labels":{"group":"group_1","name":"alert_1"}}],` + `"labels":{"group":"group_1"},"receiver":{"name":"dummy"}},` + `{"alerts":[{"annotations":{},"endsAt":"2021-04-21T10:47:32.165+02:00","fingerprint":"465de60f606461c3",` + `"receivers":[{"name":"dummy"}],"startsAt":"2021-04-21T09:47:32.165+02:00",` + - `"status":{"inhibitedBy":[],"silencedBy":[],"state":"unprocessed"},` + + `"status":{"inhibitedBy":[],"mutedBy":[],"silencedBy":[],"state":"unprocessed"},` + `"updatedAt":"2021-04-21T07:47:32.167Z","labels":{"group":"group_2","name":"alert_3"}}],` + `"labels":{"group":"group_2"},"receiver":{"name":"dummy"}}` + `]`), []byte(`[` + `{"alerts":[{"annotations":{},"endsAt":"2021-04-21T10:47:32.163+02:00","fingerprint":"c4b8b79a607bee77",` + `"receivers":[{"name":"dummy"}],"startsAt":"2021-04-21T09:47:32.163+02:00",` + - `"status":{"inhibitedBy":[],"silencedBy":[],"state":"unprocessed"},` + + `"status":{"inhibitedBy":[],"mutedBy":[],"silencedBy":[],"state":"unprocessed"},` + `"updatedAt":"2021-04-21T07:47:32.165Z","labels":{"group":"group_1","name":"alert_2"}}],` + `"labels":{"group":"group_1"},"receiver":{"name":"dummy"}}` + `]`), @@ -39,16 +39,16 @@ func TestV2AlertGroups(t *testing.T) { expected := []byte(`[` + `{"alerts":[{"annotations":{},"endsAt":"2021-04-21T10:47:32.161+02:00","fingerprint":"c4b6b79a607b6ba0",` + `"receivers":[{"name":"dummy"}],"startsAt":"2021-04-21T09:47:32.161+02:00",` + - `"status":{"inhibitedBy":[],"silencedBy":[],"state":"unprocessed"},` + + `"status":{"inhibitedBy":[],"mutedBy":[],"silencedBy":[],"state":"unprocessed"},` + `"updatedAt":"2021-04-21T07:47:32.163Z","labels":{"group":"group_1","name":"alert_1"}},` + `{"annotations":{},"endsAt":"2021-04-21T10:47:32.163+02:00","fingerprint":"c4b8b79a607bee77",` + `"receivers":[{"name":"dummy"}],"startsAt":"2021-04-21T09:47:32.163+02:00",` + - `"status":{"inhibitedBy":[],"silencedBy":[],"state":"unprocessed"},` + + `"status":{"inhibitedBy":[],"mutedBy":[],"silencedBy":[],"state":"unprocessed"},` + `"updatedAt":"2021-04-21T07:47:32.165Z","labels":{"group":"group_1","name":"alert_2"}}],` + `"labels":{"group":"group_1"},"receiver":{"name":"dummy"}},` + `{"alerts":[{"annotations":{},"endsAt":"2021-04-21T10:47:32.165+02:00","fingerprint":"465de60f606461c3",` + `"receivers":[{"name":"dummy"}],"startsAt":"2021-04-21T09:47:32.165+02:00",` + - `"status":{"inhibitedBy":[],"silencedBy":[],"state":"unprocessed"},` + + `"status":{"inhibitedBy":[],"mutedBy":[],"silencedBy":[],"state":"unprocessed"},` + `"updatedAt":"2021-04-21T07:47:32.167Z","labels":{"group":"group_2","name":"alert_3"}}],` + `"labels":{"group":"group_2"},"receiver":{"name":"dummy"}}]`) diff --git a/pkg/alertmanager/merger/v2_alerts_test.go b/pkg/alertmanager/merger/v2_alerts_test.go index 3067f846fd..e782ea4a93 100644 --- a/pkg/alertmanager/merger/v2_alerts_test.go +++ b/pkg/alertmanager/merger/v2_alerts_test.go @@ -18,16 +18,16 @@ func TestV2Alerts(t *testing.T) { []byte(`[` + `{"annotations":{},"endsAt":"2021-04-21T10:47:32.161+02:00","fingerprint":"c4b6b79a607b6ba0",` + `"receivers":[{"name":"dummy"}],"startsAt":"2021-04-21T09:47:32.161+02:00",` + - `"status":{"inhibitedBy":[],"silencedBy":[],"state":"unprocessed"},` + + `"status":{"inhibitedBy":[],"mutedBy":[],"silencedBy":[],"state":"unprocessed"},` + `"updatedAt":"2021-04-21T07:47:32.163Z","labels":{"group":"group_1","name":"alert_1"}},` + `{"annotations":{},"endsAt":"2021-04-21T10:47:32.163+02:00","fingerprint":"c4b8b79a607bee77",` + `"receivers":[{"name":"dummy"}],"startsAt":"2021-04-21T09:47:32.163+02:00",` + - `"status":{"inhibitedBy":[],"silencedBy":[],"state":"unprocessed"},` + + `"status":{"inhibitedBy":[],"mutedBy":[],"silencedBy":[],"state":"unprocessed"},` + `"updatedAt":"2021-04-21T07:47:32.165Z","labels":{"group":"group_1","name":"alert_2"}}` + `]`), []byte(`[{"annotations":{},"endsAt":"2021-04-21T10:47:32.165+02:00","fingerprint":"465de60f606461c3",` + `"receivers":[{"name":"dummy"}],"startsAt":"2021-04-21T09:47:32.165+02:00",` + - `"status":{"inhibitedBy":[],"silencedBy":[],"state":"unprocessed"},` + + `"status":{"inhibitedBy":[], "mutedBy":[], "silencedBy":[],"state":"unprocessed"},` + `"updatedAt":"2021-04-21T07:47:32.167Z","labels":{"group":"group_2","name":"alert_3"}}]`), []byte(`[]`), } @@ -35,15 +35,15 @@ func TestV2Alerts(t *testing.T) { expected := []byte(`[` + `{"annotations":{},"endsAt":"2021-04-21T10:47:32.165+02:00","fingerprint":"465de60f606461c3",` + `"receivers":[{"name":"dummy"}],"startsAt":"2021-04-21T09:47:32.165+02:00",` + - `"status":{"inhibitedBy":[],"silencedBy":[],"state":"unprocessed"},` + + `"status":{"inhibitedBy":[],"mutedBy":[],"silencedBy":[],"state":"unprocessed"},` + `"updatedAt":"2021-04-21T07:47:32.167Z","labels":{"group":"group_2","name":"alert_3"}},` + `{"annotations":{},"endsAt":"2021-04-21T10:47:32.161+02:00","fingerprint":"c4b6b79a607b6ba0",` + `"receivers":[{"name":"dummy"}],"startsAt":"2021-04-21T09:47:32.161+02:00",` + - `"status":{"inhibitedBy":[],"silencedBy":[],"state":"unprocessed"},` + + `"status":{"inhibitedBy":[],"mutedBy":[],"silencedBy":[],"state":"unprocessed"},` + `"updatedAt":"2021-04-21T07:47:32.163Z","labels":{"group":"group_1","name":"alert_1"}},` + `{"annotations":{},"endsAt":"2021-04-21T10:47:32.163+02:00","fingerprint":"c4b8b79a607bee77",` + `"receivers":[{"name":"dummy"}],"startsAt":"2021-04-21T09:47:32.163+02:00",` + - `"status":{"inhibitedBy":[],"silencedBy":[],"state":"unprocessed"},` + + `"status":{"inhibitedBy":[],"mutedBy":[],"silencedBy":[],"state":"unprocessed"},` + `"updatedAt":"2021-04-21T07:47:32.165Z","labels":{"group":"group_1","name":"alert_2"}}` + `]`) diff --git a/pkg/alertmanager/multitenant.go b/pkg/alertmanager/multitenant.go index 97f98707aa..abf421335e 100644 --- a/pkg/alertmanager/multitenant.go +++ b/pkg/alertmanager/multitenant.go @@ -315,7 +315,7 @@ func NewMultitenantAlertmanager(cfg *MultitenantAlertmanagerConfig, store alerts // We need to take this case into account to support our legacy upstream clustering. if cfg.Cluster.ListenAddr != "" && !cfg.ShardingEnabled { peer, err = cluster.Create( - log.With(logger, "component", "cluster"), + util_log.GoKitLogToSlog(log.With(logger, "component", "cluster")), registerer, cfg.Cluster.ListenAddr, cfg.Cluster.AdvertiseAddr, diff --git a/pkg/alertmanager/multitenant_test.go b/pkg/alertmanager/multitenant_test.go index 96223708db..6c9801ed61 100644 --- a/pkg/alertmanager/multitenant_test.go +++ b/pkg/alertmanager/multitenant_test.go @@ -28,6 +28,7 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/testutil" "github.com/prometheus/common/model" + "github.com/prometheus/common/promslog" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/thanos-io/objstore" @@ -450,6 +451,70 @@ receivers: - api_url: %s api_secret: secret corp_id: babycorp +`, backendURL) + }, + }, + "msteams": { + getAlertmanagerConfig: func(backendURL string) string { + return fmt.Sprintf(` +route: + receiver: msteams + group_wait: 0s + group_interval: 1s + +receivers: + - name: msteams + msteams_configs: + - webhook_url: %s +`, backendURL) + }, + }, + "msteamsv2": { + getAlertmanagerConfig: func(backendURL string) string { + return fmt.Sprintf(` +route: + receiver: msteamsv2 + group_wait: 0s + group_interval: 1s + +receivers: + - name: msteamsv2 + msteamsv2_configs: + - webhook_url: %s +`, backendURL) + }, + }, + "jira": { + getAlertmanagerConfig: func(backendURL string) string { + return fmt.Sprintf(` +route: + receiver: jira + group_wait: 0s + group_interval: 1s + +receivers: + - name: jira + jira_configs: + - api_url: %s + project: test-project + issue_type: Incident +`, backendURL) + }, + }, + "rocketchat": { + getAlertmanagerConfig: func(backendURL string) string { + return fmt.Sprintf(` +route: + receiver: rocketchat + group_wait: 0s + group_interval: 1s + +receivers: + - name: rocketchat + rocketchat_configs: + - api_url: %s + token: token + token_id: token-id `, backendURL) }, }, @@ -2154,9 +2219,10 @@ receivers: ctx = notify.WithReceiverName(ctx, "email") ctx = notify.WithGroupKey(ctx, "key") ctx = notify.WithRepeatInterval(ctx, time.Minute) + ctx = notify.WithRouteID(ctx, "routeId") // Verify that rate-limiter is in place for email notifier. - _, _, err = uam.lastPipeline.Exec(ctx, log.NewNopLogger(), &types.Alert{}) + _, _, err = uam.lastPipeline.Exec(ctx, promslog.NewNopLogger(), &types.Alert{}) require.NotNil(t, err) require.Contains(t, err.Error(), errRateLimited.Error()) } diff --git a/pkg/util/validation/notifications_limit_flag.go b/pkg/util/validation/notifications_limit_flag.go index 2d86cea298..403980cd04 100644 --- a/pkg/util/validation/notifications_limit_flag.go +++ b/pkg/util/validation/notifications_limit_flag.go @@ -11,7 +11,7 @@ import ( var allowedIntegrationNames = []string{ "webhook", "email", "pagerduty", "opsgenie", "wechat", "slack", "victorops", "pushover", "sns", "telegram", "discord", "webex", - "msteams", + "msteams", "msteamsv2", "jira", "rocketchat", } type NotificationRateLimitMap map[string]float64 diff --git a/vendor/github.com/alecthomas/units/renovate.json5 b/vendor/github.com/alecthomas/units/renovate.json5 index 897864b852..6bb4acde94 100644 --- a/vendor/github.com/alecthomas/units/renovate.json5 +++ b/vendor/github.com/alecthomas/units/renovate.json5 @@ -8,4 +8,8 @@ "group:allNonMajor", "schedule:earlyMondays", // Run once a week. ], + postUpdateOptions: [ + "gomodTidy", + "gomodUpdateImportPaths" + ] } diff --git a/vendor/github.com/benbjohnson/clock/LICENSE b/vendor/github.com/benbjohnson/clock/LICENSE deleted file mode 100644 index ce212cb1ce..0000000000 --- a/vendor/github.com/benbjohnson/clock/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2014 Ben Johnson - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/vendor/github.com/benbjohnson/clock/README.md b/vendor/github.com/benbjohnson/clock/README.md deleted file mode 100644 index 4f1f82fc6d..0000000000 --- a/vendor/github.com/benbjohnson/clock/README.md +++ /dev/null @@ -1,105 +0,0 @@ -clock -===== - -[![go.dev reference](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat-square)](https://pkg.go.dev/mod/github.com/benbjohnson/clock) - -Clock is a small library for mocking time in Go. It provides an interface -around the standard library's [`time`][time] package so that the application -can use the realtime clock while tests can use the mock clock. - -The module is currently maintained by @djmitche. - -[time]: https://pkg.go.dev/github.com/benbjohnson/clock - -## Usage - -### Realtime Clock - -Your application can maintain a `Clock` variable that will allow realtime and -mock clocks to be interchangeable. For example, if you had an `Application` type: - -```go -import "github.com/benbjohnson/clock" - -type Application struct { - Clock clock.Clock -} -``` - -You could initialize it to use the realtime clock like this: - -```go -var app Application -app.Clock = clock.New() -... -``` - -Then all timers and time-related functionality should be performed from the -`Clock` variable. - - -### Mocking time - -In your tests, you will want to use a `Mock` clock: - -```go -import ( - "testing" - - "github.com/benbjohnson/clock" -) - -func TestApplication_DoSomething(t *testing.T) { - mock := clock.NewMock() - app := Application{Clock: mock} - ... -} -``` - -Now that you've initialized your application to use the mock clock, you can -adjust the time programmatically. The mock clock always starts from the Unix -epoch (midnight UTC on Jan 1, 1970). - - -### Controlling time - -The mock clock provides the same functions that the standard library's `time` -package provides. For example, to find the current time, you use the `Now()` -function: - -```go -mock := clock.NewMock() - -// Find the current time. -mock.Now().UTC() // 1970-01-01 00:00:00 +0000 UTC - -// Move the clock forward. -mock.Add(2 * time.Hour) - -// Check the time again. It's 2 hours later! -mock.Now().UTC() // 1970-01-01 02:00:00 +0000 UTC -``` - -Timers and Tickers are also controlled by this same mock clock. They will only -execute when the clock is moved forward: - -```go -mock := clock.NewMock() -count := 0 - -// Kick off a timer to increment every 1 mock second. -go func() { - ticker := mock.Ticker(1 * time.Second) - for { - <-ticker.C - count++ - } -}() -runtime.Gosched() - -// Move the clock forward 10 seconds. -mock.Add(10 * time.Second) - -// This prints 10. -fmt.Println(count) -``` diff --git a/vendor/github.com/benbjohnson/clock/clock.go b/vendor/github.com/benbjohnson/clock/clock.go deleted file mode 100644 index 14ddc0795b..0000000000 --- a/vendor/github.com/benbjohnson/clock/clock.go +++ /dev/null @@ -1,422 +0,0 @@ -package clock - -import ( - "context" - "sort" - "sync" - "time" -) - -// Re-export of time.Duration -type Duration = time.Duration - -// Clock represents an interface to the functions in the standard library time -// package. Two implementations are available in the clock package. The first -// is a real-time clock which simply wraps the time package's functions. The -// second is a mock clock which will only change when -// programmatically adjusted. -type Clock interface { - After(d time.Duration) <-chan time.Time - AfterFunc(d time.Duration, f func()) *Timer - Now() time.Time - Since(t time.Time) time.Duration - Until(t time.Time) time.Duration - Sleep(d time.Duration) - Tick(d time.Duration) <-chan time.Time - Ticker(d time.Duration) *Ticker - Timer(d time.Duration) *Timer - WithDeadline(parent context.Context, d time.Time) (context.Context, context.CancelFunc) - WithTimeout(parent context.Context, t time.Duration) (context.Context, context.CancelFunc) -} - -// New returns an instance of a real-time clock. -func New() Clock { - return &clock{} -} - -// clock implements a real-time clock by simply wrapping the time package functions. -type clock struct{} - -func (c *clock) After(d time.Duration) <-chan time.Time { return time.After(d) } - -func (c *clock) AfterFunc(d time.Duration, f func()) *Timer { - return &Timer{timer: time.AfterFunc(d, f)} -} - -func (c *clock) Now() time.Time { return time.Now() } - -func (c *clock) Since(t time.Time) time.Duration { return time.Since(t) } - -func (c *clock) Until(t time.Time) time.Duration { return time.Until(t) } - -func (c *clock) Sleep(d time.Duration) { time.Sleep(d) } - -func (c *clock) Tick(d time.Duration) <-chan time.Time { return time.Tick(d) } - -func (c *clock) Ticker(d time.Duration) *Ticker { - t := time.NewTicker(d) - return &Ticker{C: t.C, ticker: t} -} - -func (c *clock) Timer(d time.Duration) *Timer { - t := time.NewTimer(d) - return &Timer{C: t.C, timer: t} -} - -func (c *clock) WithDeadline(parent context.Context, d time.Time) (context.Context, context.CancelFunc) { - return context.WithDeadline(parent, d) -} - -func (c *clock) WithTimeout(parent context.Context, t time.Duration) (context.Context, context.CancelFunc) { - return context.WithTimeout(parent, t) -} - -// Mock represents a mock clock that only moves forward programmically. -// It can be preferable to a real-time clock when testing time-based functionality. -type Mock struct { - // mu protects all other fields in this struct, and the data that they - // point to. - mu sync.Mutex - - now time.Time // current time - timers clockTimers // tickers & timers -} - -// NewMock returns an instance of a mock clock. -// The current time of the mock clock on initialization is the Unix epoch. -func NewMock() *Mock { - return &Mock{now: time.Unix(0, 0)} -} - -// Add moves the current time of the mock clock forward by the specified duration. -// This should only be called from a single goroutine at a time. -func (m *Mock) Add(d time.Duration) { - // Calculate the final current time. - m.mu.Lock() - t := m.now.Add(d) - m.mu.Unlock() - - // Continue to execute timers until there are no more before the new time. - for { - if !m.runNextTimer(t) { - break - } - } - - // Ensure that we end with the new time. - m.mu.Lock() - m.now = t - m.mu.Unlock() - - // Give a small buffer to make sure that other goroutines get handled. - gosched() -} - -// Set sets the current time of the mock clock to a specific one. -// This should only be called from a single goroutine at a time. -func (m *Mock) Set(t time.Time) { - // Continue to execute timers until there are no more before the new time. - for { - if !m.runNextTimer(t) { - break - } - } - - // Ensure that we end with the new time. - m.mu.Lock() - m.now = t - m.mu.Unlock() - - // Give a small buffer to make sure that other goroutines get handled. - gosched() -} - -// WaitForAllTimers sets the clock until all timers are expired -func (m *Mock) WaitForAllTimers() time.Time { - // Continue to execute timers until there are no more - for { - m.mu.Lock() - if len(m.timers) == 0 { - m.mu.Unlock() - return m.Now() - } - - sort.Sort(m.timers) - next := m.timers[len(m.timers)-1].Next() - m.mu.Unlock() - m.Set(next) - } -} - -// runNextTimer executes the next timer in chronological order and moves the -// current time to the timer's next tick time. The next time is not executed if -// its next time is after the max time. Returns true if a timer was executed. -func (m *Mock) runNextTimer(max time.Time) bool { - m.mu.Lock() - - // Sort timers by time. - sort.Sort(m.timers) - - // If we have no more timers then exit. - if len(m.timers) == 0 { - m.mu.Unlock() - return false - } - - // Retrieve next timer. Exit if next tick is after new time. - t := m.timers[0] - if t.Next().After(max) { - m.mu.Unlock() - return false - } - - // Move "now" forward and unlock clock. - m.now = t.Next() - now := m.now - m.mu.Unlock() - - // Execute timer. - t.Tick(now) - return true -} - -// After waits for the duration to elapse and then sends the current time on the returned channel. -func (m *Mock) After(d time.Duration) <-chan time.Time { - return m.Timer(d).C -} - -// AfterFunc waits for the duration to elapse and then executes a function in its own goroutine. -// A Timer is returned that can be stopped. -func (m *Mock) AfterFunc(d time.Duration, f func()) *Timer { - m.mu.Lock() - defer m.mu.Unlock() - ch := make(chan time.Time, 1) - t := &Timer{ - c: ch, - fn: f, - mock: m, - next: m.now.Add(d), - stopped: false, - } - m.timers = append(m.timers, (*internalTimer)(t)) - return t -} - -// Now returns the current wall time on the mock clock. -func (m *Mock) Now() time.Time { - m.mu.Lock() - defer m.mu.Unlock() - return m.now -} - -// Since returns time since `t` using the mock clock's wall time. -func (m *Mock) Since(t time.Time) time.Duration { - return m.Now().Sub(t) -} - -// Until returns time until `t` using the mock clock's wall time. -func (m *Mock) Until(t time.Time) time.Duration { - return t.Sub(m.Now()) -} - -// Sleep pauses the goroutine for the given duration on the mock clock. -// The clock must be moved forward in a separate goroutine. -func (m *Mock) Sleep(d time.Duration) { - <-m.After(d) -} - -// Tick is a convenience function for Ticker(). -// It will return a ticker channel that cannot be stopped. -func (m *Mock) Tick(d time.Duration) <-chan time.Time { - return m.Ticker(d).C -} - -// Ticker creates a new instance of Ticker. -func (m *Mock) Ticker(d time.Duration) *Ticker { - m.mu.Lock() - defer m.mu.Unlock() - ch := make(chan time.Time, 1) - t := &Ticker{ - C: ch, - c: ch, - mock: m, - d: d, - next: m.now.Add(d), - } - m.timers = append(m.timers, (*internalTicker)(t)) - return t -} - -// Timer creates a new instance of Timer. -func (m *Mock) Timer(d time.Duration) *Timer { - m.mu.Lock() - ch := make(chan time.Time, 1) - t := &Timer{ - C: ch, - c: ch, - mock: m, - next: m.now.Add(d), - stopped: false, - } - m.timers = append(m.timers, (*internalTimer)(t)) - now := m.now - m.mu.Unlock() - m.runNextTimer(now) - return t -} - -// removeClockTimer removes a timer from m.timers. m.mu MUST be held -// when this method is called. -func (m *Mock) removeClockTimer(t clockTimer) { - for i, timer := range m.timers { - if timer == t { - copy(m.timers[i:], m.timers[i+1:]) - m.timers[len(m.timers)-1] = nil - m.timers = m.timers[:len(m.timers)-1] - break - } - } - sort.Sort(m.timers) -} - -// clockTimer represents an object with an associated start time. -type clockTimer interface { - Next() time.Time - Tick(time.Time) -} - -// clockTimers represents a list of sortable timers. -type clockTimers []clockTimer - -func (a clockTimers) Len() int { return len(a) } -func (a clockTimers) Swap(i, j int) { a[i], a[j] = a[j], a[i] } -func (a clockTimers) Less(i, j int) bool { return a[i].Next().Before(a[j].Next()) } - -// Timer represents a single event. -// The current time will be sent on C, unless the timer was created by AfterFunc. -type Timer struct { - C <-chan time.Time - c chan time.Time - timer *time.Timer // realtime impl, if set - next time.Time // next tick time - mock *Mock // mock clock, if set - fn func() // AfterFunc function, if set - stopped bool // True if stopped, false if running -} - -// Stop turns off the ticker. -func (t *Timer) Stop() bool { - if t.timer != nil { - return t.timer.Stop() - } - - t.mock.mu.Lock() - registered := !t.stopped - t.mock.removeClockTimer((*internalTimer)(t)) - t.stopped = true - t.mock.mu.Unlock() - return registered -} - -// Reset changes the expiry time of the timer -func (t *Timer) Reset(d time.Duration) bool { - if t.timer != nil { - return t.timer.Reset(d) - } - - t.mock.mu.Lock() - t.next = t.mock.now.Add(d) - defer t.mock.mu.Unlock() - - registered := !t.stopped - if t.stopped { - t.mock.timers = append(t.mock.timers, (*internalTimer)(t)) - } - - t.stopped = false - return registered -} - -type internalTimer Timer - -func (t *internalTimer) Next() time.Time { return t.next } -func (t *internalTimer) Tick(now time.Time) { - // a gosched() after ticking, to allow any consequences of the - // tick to complete - defer gosched() - - t.mock.mu.Lock() - if t.fn != nil { - // defer function execution until the lock is released, and - defer func() { go t.fn() }() - } else { - t.c <- now - } - t.mock.removeClockTimer((*internalTimer)(t)) - t.stopped = true - t.mock.mu.Unlock() -} - -// Ticker holds a channel that receives "ticks" at regular intervals. -type Ticker struct { - C <-chan time.Time - c chan time.Time - ticker *time.Ticker // realtime impl, if set - next time.Time // next tick time - mock *Mock // mock clock, if set - d time.Duration // time between ticks - stopped bool // True if stopped, false if running -} - -// Stop turns off the ticker. -func (t *Ticker) Stop() { - if t.ticker != nil { - t.ticker.Stop() - } else { - t.mock.mu.Lock() - t.mock.removeClockTimer((*internalTicker)(t)) - t.stopped = true - t.mock.mu.Unlock() - } -} - -// Reset resets the ticker to a new duration. -func (t *Ticker) Reset(dur time.Duration) { - if t.ticker != nil { - t.ticker.Reset(dur) - return - } - - t.mock.mu.Lock() - defer t.mock.mu.Unlock() - - if t.stopped { - t.mock.timers = append(t.mock.timers, (*internalTicker)(t)) - t.stopped = false - } - - t.d = dur - t.next = t.mock.now.Add(dur) -} - -type internalTicker Ticker - -func (t *internalTicker) Next() time.Time { return t.next } -func (t *internalTicker) Tick(now time.Time) { - select { - case t.c <- now: - default: - } - t.mock.mu.Lock() - t.next = now.Add(t.d) - t.mock.mu.Unlock() - gosched() -} - -// Sleep momentarily so that other goroutines can process. -func gosched() { time.Sleep(1 * time.Millisecond) } - -var ( - // type checking - _ Clock = &Mock{} -) diff --git a/vendor/github.com/benbjohnson/clock/context.go b/vendor/github.com/benbjohnson/clock/context.go deleted file mode 100644 index eb67594f2c..0000000000 --- a/vendor/github.com/benbjohnson/clock/context.go +++ /dev/null @@ -1,86 +0,0 @@ -package clock - -import ( - "context" - "fmt" - "sync" - "time" -) - -func (m *Mock) WithTimeout(parent context.Context, timeout time.Duration) (context.Context, context.CancelFunc) { - return m.WithDeadline(parent, m.Now().Add(timeout)) -} - -func (m *Mock) WithDeadline(parent context.Context, deadline time.Time) (context.Context, context.CancelFunc) { - if cur, ok := parent.Deadline(); ok && cur.Before(deadline) { - // The current deadline is already sooner than the new one. - return context.WithCancel(parent) - } - ctx := &timerCtx{clock: m, parent: parent, deadline: deadline, done: make(chan struct{})} - propagateCancel(parent, ctx) - dur := m.Until(deadline) - if dur <= 0 { - ctx.cancel(context.DeadlineExceeded) // deadline has already passed - return ctx, func() {} - } - ctx.Lock() - defer ctx.Unlock() - if ctx.err == nil { - ctx.timer = m.AfterFunc(dur, func() { - ctx.cancel(context.DeadlineExceeded) - }) - } - return ctx, func() { ctx.cancel(context.Canceled) } -} - -// propagateCancel arranges for child to be canceled when parent is. -func propagateCancel(parent context.Context, child *timerCtx) { - if parent.Done() == nil { - return // parent is never canceled - } - go func() { - select { - case <-parent.Done(): - child.cancel(parent.Err()) - case <-child.Done(): - } - }() -} - -type timerCtx struct { - sync.Mutex - - clock Clock - parent context.Context - deadline time.Time - done chan struct{} - - err error - timer *Timer -} - -func (c *timerCtx) cancel(err error) { - c.Lock() - defer c.Unlock() - if c.err != nil { - return // already canceled - } - c.err = err - close(c.done) - if c.timer != nil { - c.timer.Stop() - c.timer = nil - } -} - -func (c *timerCtx) Deadline() (deadline time.Time, ok bool) { return c.deadline, true } - -func (c *timerCtx) Done() <-chan struct{} { return c.done } - -func (c *timerCtx) Err() error { return c.err } - -func (c *timerCtx) Value(key interface{}) interface{} { return c.parent.Value(key) } - -func (c *timerCtx) String() string { - return fmt.Sprintf("clock.WithDeadline(%s [%s])", c.deadline, c.deadline.Sub(c.clock.Now())) -} diff --git a/vendor/github.com/coder/quartz/.gitignore b/vendor/github.com/coder/quartz/.gitignore new file mode 100644 index 0000000000..62c893550a --- /dev/null +++ b/vendor/github.com/coder/quartz/.gitignore @@ -0,0 +1 @@ +.idea/ \ No newline at end of file diff --git a/vendor/github.com/coder/quartz/LICENSE b/vendor/github.com/coder/quartz/LICENSE new file mode 100644 index 0000000000..f7c5d7fee6 --- /dev/null +++ b/vendor/github.com/coder/quartz/LICENSE @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be + protected by copyright and related or neighboring rights ("Copyright and + Related Rights"). Copyright and Related Rights include, but are not + limited to, the following: + +i. the right to reproduce, adapt, distribute, perform, display, +communicate, and translate a Work; +ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or +likeness depicted in a Work; +iv. rights protecting against unfair competition in regards to a Work, +subject to the limitations in paragraph 4(a), below; +v. rights protecting the extraction, dissemination, use and reuse of data +in a Work; +vi. database rights (such as those arising under Directive 96/9/EC of the +European Parliament and of the Council of 11 March 1996 on the legal +protection of databases, and under any national implementation +thereof, including any amended or successor version of such +directive); and +vii. other similar, equivalent or corresponding rights throughout the +world based on applicable law or treaty, and any national +implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention + of, applicable law, Affirmer hereby overtly, fully, permanently, + irrevocably and unconditionally waives, abandons, and surrenders all of + Affirmer's Copyright and Related Rights and associated claims and causes + of action, whether now known or unknown (including existing as well as + future claims and causes of action), in the Work (i) in all territories + worldwide, (ii) for the maximum duration provided by applicable law or + treaty (including future time extensions), (iii) in any current or future + medium and for any number of copies, and (iv) for any purpose whatsoever, + including without limitation commercial, advertising or promotional + purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each + member of the public at large and to the detriment of Affirmer's heirs and + successors, fully intending that such Waiver shall not be subject to + revocation, rescission, cancellation, termination, or any other legal or + equitable action to disrupt the quiet enjoyment of the Work by the public + as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason + be judged legally invalid or ineffective under applicable law, then the + Waiver shall be preserved to the maximum extent permitted taking into + account Affirmer's express Statement of Purpose. In addition, to the + extent the Waiver is so judged Affirmer hereby grants to each affected + person a royalty-free, non transferable, non sublicensable, non exclusive, + irrevocable and unconditional license to exercise Affirmer's Copyright and + Related Rights in the Work (i) in all territories worldwide, (ii) for the + maximum duration provided by applicable law or treaty (including future + time extensions), (iii) in any current or future medium and for any number + of copies, and (iv) for any purpose whatsoever, including without + limitation commercial, advertising or promotional purposes (the + "License"). The License shall be deemed effective as of the date CC0 was + applied by Affirmer to the Work. Should any part of the License for any + reason be judged legally invalid or ineffective under applicable law, such + partial invalidity or ineffectiveness shall not invalidate the remainder + of the License, and in such case Affirmer hereby affirms that he or she + will not (i) exercise any of his or her remaining Copyright and Related + Rights in the Work or (ii) assert any associated claims and causes of + action with respect to the Work, in either case contrary to Affirmer's + express Statement of Purpose. + +4. Limitations and Disclaimers. + +a. No trademark or patent rights held by Affirmer are waived, abandoned, +surrendered, licensed or otherwise affected by this document. +b. Affirmer offers the Work as-is and makes no representations or +warranties of any kind concerning the Work, express, implied, +statutory or otherwise, including without limitation warranties of +title, merchantability, fitness for a particular purpose, non +infringement, or the absence of latent or other defects, accuracy, or +the present or absence of errors, whether or not discoverable, all to +the greatest extent permissible under applicable law. +c. Affirmer disclaims responsibility for clearing rights of other persons +that may apply to the Work or any use thereof, including without +limitation any person's Copyright and Related Rights in the Work. +Further, Affirmer disclaims responsibility for obtaining any necessary +consents, permissions or other rights required for any use of the +Work. +d. Affirmer understands and acknowledges that Creative Commons is not a +party to this document and has no duty or obligation with respect to +this CC0 or use of the Work. \ No newline at end of file diff --git a/vendor/github.com/coder/quartz/README.md b/vendor/github.com/coder/quartz/README.md new file mode 100644 index 0000000000..f0703c4d74 --- /dev/null +++ b/vendor/github.com/coder/quartz/README.md @@ -0,0 +1,632 @@ +# Quartz + +A Go time testing library for writing deterministic unit tests + +Our high level goal is to write unit tests that + +1. execute quickly +2. don't flake +3. are straightforward to write and understand + +For tests to execute quickly without flakes, we want to focus on _determinism_: the test should run +the same each time, and it should be easy to force the system into a known state (no races) before +executing test assertions. `time.Sleep`, `runtime.Gosched()`, and +polling/[Eventually](https://pkg.go.dev/github.com/stretchr/testify/assert#Eventually) are all +symptoms of an inability to do this easily. + +## Usage + +### `Clock` interface + +In your application code, maintain a reference to a `quartz.Clock` instance to start timers and +tickers, instead of the bare `time` standard library. + +```go +import "github.com/coder/quartz" + +type Component struct { + ... + + // for testing + clock quartz.Clock +} +``` + +Whenever you would call into `time` to start a timer or ticker, call `Component`'s `clock` instead. + +In production, set this clock to `quartz.NewReal()` to create a clock that just transparently passes +through to the standard `time` library. + +### Mocking + +In your tests, you can use a `*Mock` to control the tickers and timers your code under test gets. + +```go +import ( + "testing" + "github.com/coder/quartz" +) + +func TestComponent(t *testing.T) { + mClock := quartz.NewMock(t) + comp := &Component{ + ... + clock: mClock, + } +} +``` + +The `*Mock` clock starts at Jan 1, 2024, 00:00 UTC by default, but you can set any start time you'd like prior to your test. + +```go +mClock := quartz.NewMock(t) +mClock.Set(time.Date(2021, 6, 18, 12, 0, 0, 0, time.UTC)) // June 18, 2021 @ 12pm UTC +``` + +#### Advancing the clock + +Once you begin setting timers or tickers, you cannot change the time backward, only advance it +forward. You may continue to use `Set()`, but it is often easier and clearer to use `Advance()`. + +For example, with a timer: + +```go +fired := false + +tmr := mClock.AfterFunc(time.Second, func() { + fired = true +}) +mClock.Advance(time.Second) +``` + +When you call `Advance()` it immediately moves the clock forward the given amount, and triggers any +tickers or timers that are scheduled to happen at that time. Any triggered events happen on separate +goroutines, so _do not_ immediately assert the results: + +```go +fired := false + +tmr := mClock.AfterFunc(time.Second, func() { + fired = true +}) +mClock.Advance(time.Second) + +// RACE CONDITION, DO NOT DO THIS! +if !fired { + t.Fatal("didn't fire") +} +``` + +`Advance()` (and `Set()` for that matter) return an `AdvanceWaiter` object you can use to wait for +all triggered events to complete. + +```go +fired := false +// set a test timeout so we don't wait the default `go test` timeout for a failure +ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + +tmr := mClock.AfterFunc(time.Second, func() { + fired = true +}) + +w := mClock.Advance(time.Second) +err := w.Wait(ctx) +if err != nil { + t.Fatal("AfterFunc f never completed") +} +if !fired { + t.Fatal("didn't fire") +} +``` + +The construction of waiting for the triggered events and failing the test if they don't complete is +very common, so there is a shorthand: + +```go +w := mClock.Advance(time.Second) +err := w.Wait(ctx) +if err != nil { + t.Fatal("AfterFunc f never completed") +} +``` + +is equivalent to: + +```go +w := mClock.Advance(time.Second) +w.MustWait(ctx) +``` + +or even more briefly: + +```go +mClock.Advance(time.Second).MustWait(ctx) +``` + +### Advance only to the next event + +One important restriction on advancing the clock is that you may only advance forward to the next +timer or ticker event and no further. The following will result in a test failure: + +```go +func TestAdvanceTooFar(t *testing.T) { + ctx, cancel := context.WithTimeout(10*time.Second) + defer cancel() + mClock := quartz.NewMock(t) + var firedAt time.Time + mClock.AfterFunc(time.Second, func() { + firedAt := mClock.Now() + }) + mClock.Advance(2*time.Second).MustWait(ctx) +} +``` + +This is a deliberate design decision to allow `Advance()` to immediately and synchronously move the +clock forward (even without calling `Wait()` on returned waiter). This helps meet Quartz's design +goals of writing deterministic and easy to understand unit tests. It also allows the clock to be +advanced, deterministically _during_ the execution of a tick or timer function, as explained in the +next sections on Traps. + +Advancing multiple events can be accomplished via looping. E.g. if you have a 1-second ticker + +```go +for i := 0; i < 10; i++ { + mClock.Advance(time.Second).MustWait(ctx) +} +``` + +will advance 10 ticks. + +If you don't know or don't want to compute the time to the next event, you can use `AdvanceNext()`. + +```go +d, w := mClock.AdvanceNext() +w.MustWait(ctx) +// d contains the duration we advanced +``` + +`d, ok := Peek()` returns the duration until the next event, if any (`ok` is `true`). You can use +this to advance a specific time, regardless of the tickers and timer events: + +```go +desired := time.Minute // time to advance +for desired > 0 { + p, ok := mClock.Peek() + if !ok || p > desired { + mClock.Advance(desired).MustWait(ctx) + break + } + mClock.Advance(p).MustWait(ctx) + desired -= p +} +``` + +### Traps + +A trap allows you to match specific calls into the library while mocking, block their return, +inspect their arguments, then release them to allow them to return. They help you write +deterministic unit tests even when the code under test executes asynchronously from the test. + +You set your traps prior to executing code under test, and then wait for them to be triggered. + +```go +func TestTrap(t *testing.T) { + ctx, cancel := context.WithTimeout(10*time.Second) + defer cancel() + mClock := quartz.NewMock(t) + trap := mClock.Trap().AfterFunc() + defer trap.Close() // stop trapping AfterFunc calls + + count := 0 + go mClock.AfterFunc(time.Hour, func(){ + count++ + }) + call := trap.MustWait(ctx) + call.Release() + if call.Duration != time.Hour { + t.Fatal("wrong duration") + } + + // Now that the async call to AfterFunc has occurred, we can advance the clock to trigger it + mClock.Advance(call.Duration).MustWait(ctx) + if count != 1 { + t.Fatal("wrong count") + } +} +``` + +In this test, the trap serves 2 purposes. Firstly, it allows us to capture and assert the duration +passed to the `AfterFunc` call. Secondly, it prevents a race between setting the timer and advancing +it. Since these things happen on different goroutines, if `Advance()` completes before +`AfterFunc()` is called, then the timer never pops in this test. + +Any untrapped calls immediately complete using the current time, and calling `Close()` on a trap +causes the mock clock to stop trapping those calls. + +You may also `Advance()` the clock between trapping a call and releasing it. The call uses the +current (mocked) time at the moment it is released. + +```go +func TestTrap2(t *testing.T) { + ctx, cancel := context.WithTimeout(10*time.Second) + defer cancel() + mClock := quartz.NewMock(t) + trap := mClock.Trap().Now() + defer trap.Close() // stop trapping AfterFunc calls + + var logs []string + done := make(chan struct{}) + go func(clk quartz.Clock){ + defer close(done) + start := clk.Now() + phase1() + p1end := clk.Now() + logs = append(fmt.Sprintf("Phase 1 took %s", p1end.Sub(start).String())) + phase2() + p2end := clk.Now() + logs = append(fmt.Sprintf("Phase 2 took %s", p2end.Sub(p1end).String())) + }(mClock) + + // start + trap.MustWait(ctx).Release() + // phase 1 + call := trap.MustWait(ctx) + mClock.Advance(3*time.Second).MustWait(ctx) + call.Release() + // phase 2 + call = trap.MustWait(ctx) + mClock.Advance(5*time.Second).MustWait(ctx) + call.Release() + + <-done + // Now logs contains []string{"Phase 1 took 3s", "Phase 2 took 5s"} +} +``` + +### Tags + +When multiple goroutines in the code under test call into the Clock, you can use `tags` to +distinguish them in your traps. + +```go +trap := mClock.Trap.Now("foo") // traps any calls that contain "foo" +defer trap.Close() + +foo := make(chan time.Time) +go func(){ + foo <- mClock.Now("foo", "bar") +}() +baz := make(chan time.Time) +go func(){ + baz <- mClock.Now("baz") +}() +call := trap.MustWait(ctx) +mClock.Advance(time.Second).MustWait(ctx) +call.Release() +// call.Tags contains []string{"foo", "bar"} + +gotFoo := <-foo // 1s after start +gotBaz := <-baz // ?? never trapped, so races with Advance() +``` + +Tags appear as an optional suffix on all `Clock` methods (type `...string`) and are ignored entirely +by the real clock. They also appear on all methods on returned timers and tickers. + +## Recommended Patterns + +### Options + +We use the Option pattern to inject the mock clock for testing, keeping the call signature in +production clean. The option pattern is compatible with other optional fields as well. + +```go +type Option func(*Thing) + +// WithTestClock is used in tests to inject a mock Clock +func WithTestClock(clk quartz.Clock) Option { + return func(t *Thing) { + t.clock = clk + } +} + +func NewThing(, opts ...Option) *Thing { + t := &Thing{ + ... + clock: quartz.NewReal() + } + for _, o := range opts { + o(t) + } + return t +} +``` + +In tests, this becomes + +```go +func TestThing(t *testing.T) { + mClock := quartz.NewMock(t) + thing := NewThing(, WithTestClock(mClock)) + ... +} +``` + +### Tagging convention + +Tag your `Clock` method calls as: + +```go +func (c *Component) Method() { + now := c.clock.Now("Component", "Method") +} +``` + +or + +```go +func (c *Component) Method() { + start := c.clock.Now("Component", "Method", "start") + ... + end := c.clock.Now("Component", "Method", "end") +} +``` + +This makes it much less likely that code changes that introduce new components or methods will spoil +existing unit tests. + +## Why another time testing library? + +Writing good unit tests for components and functions that use the `time` package is difficult, even +though several open source libraries exist. In building Quartz, we took some inspiration from + +- [github.com/benbjohnson/clock](https://github.com/benbjohnson/clock) +- Tailscale's [tstest.Clock](https://github.com/coder/tailscale/blob/main/tstest/clock.go) +- [github.com/aspenmesh/tock](https://github.com/aspenmesh/tock) + +Quartz shares the high level design of a `Clock` interface that closely resembles the functions in +the `time` standard library, and a "real" clock passes thru to the standard library in production, +while a mock clock gives precise control in testing. + +As mentioned in our introduction, our high level goal is to write unit tests that + +1. execute quickly +2. don't flake +3. are straightforward to write and understand + +For several reasons, this is a tall order when it comes to code that depends on time, and we found +the existing libraries insufficient for our goals. + +### Preventing test flakes + +The following example comes from the README from benbjohnson/clock: + +```go +mock := clock.NewMock() +count := 0 + +// Kick off a timer to increment every 1 mock second. +go func() { + ticker := mock.Ticker(1 * time.Second) + for { + <-ticker.C + count++ + } +}() +runtime.Gosched() + +// Move the clock forward 10 seconds. +mock.Add(10 * time.Second) + +// This prints 10. +fmt.Println(count) +``` + +The first race condition is fairly obvious: moving the clock forward 10 seconds may generate 10 +ticks on the `ticker.C` channel, but there is no guarantee that `count++` executes before +`fmt.Println(count)`. + +The second race condition is more subtle, but `runtime.Gosched()` is the tell. Since the ticker +is started on a separate goroutine, there is no guarantee that `mock.Ticker()` executes before +`mock.Add()`. `runtime.Gosched()` is an attempt to get this to happen, but it makes no hard +promises. On a busy system, especially when running tests in parallel, this can flake, advance the +time 10 seconds first, then start the ticker and never generate a tick. + +Let's talk about how Quartz tackles these problems. + +In our experience, an extremely common use case is creating a ticker then doing a 2-arm `select` +with ticks in one and context expiring in another, i.e. + +```go +t := time.NewTicker(duration) +for { + select { + case <-ctx.Done(): + return ctx.Err() + case <-t.C: + err := do() + if err != nil { + return err + } + } +} +``` + +In Quartz, we refactor this to be more compact and testing friendly: + +```go +t := clock.TickerFunc(ctx, duration, do) +return t.Wait() +``` + +This affords the mock `Clock` the ability to explicitly know when processing of a tick is finished +because it's wrapped in the function passed to `TickerFunc` (`do()` in this example). + +In Quartz, when you advance the clock, you are returned an object you can `Wait()` on to ensure all +ticks and timers triggered are finished. This solves the first race condition in the example. + +(As an aside, we still support a traditional standard library-style `Ticker`. You may find it useful +if you want to keep your code as close as possible to the standard library, or if you need to use +the channel in a larger `select` block. In that case, you'll have to find some other mechanism to +sync tick processing to your test code.) + +To prevent race conditions related to the starting of the ticker, Quartz allows you to set "traps" +for calls that access the clock. + +```go +func TestTicker(t *testing.T) { + mClock := quartz.NewMock(t) + trap := mClock.Trap().TickerFunc() + defer trap.Close() // stop trapping at end + go runMyTicker(mClock) // async calls TickerFunc() + call := trap.Wait(context.Background()) // waits for a call and blocks its return + call.Release() // allow the TickerFunc() call to return + // optionally check the duration using call.Duration + // Move the clock forward 1 tick + mClock.Advance(time.Second).MustWait(context.Background()) + // assert results of the tick +} +``` + +Trapping and then releasing the call to `TickerFunc()` ensures the ticker is started at a +deterministic time, so our calls to `Advance()` will have a predictable effect. + +Take a look at `TestExampleTickerFunc` in `example_test.go` for a complete worked example. + +### Complex time dependence + +Another difficult issue to handle when unit testing is when some code under test makes multiple +calls that depend on the time, and you want to simulate some time passing between them. + +A very basic example is measuring how long something took: + +```go +var measurement time.Duration +go func(clock quartz.Clock) { + start := clock.Now() + doSomething() + measurement = clock.Since(start) +}(mClock) + +// how to get measurement to be, say, 5 seconds? +``` + +The two calls into the clock happen asynchronously, so we need to be able to advance the clock after +the first call to `Now()` but before the call to `Since()`. Doing this with the libraries we +mentioned above means that you have to be able to mock out or otherwise block the completion of +`doSomething()`. + +But, with the trap functionality we mentioned in the previous section, you can deterministically +control the time each call sees. + +```go +trap := mClock.Trap().Since() +var measurement time.Duration +go func(clock quartz.Clock) { + start := clock.Now() + doSomething() + measurement = clock.Since(start) +}(mClock) + +c := trap.Wait(ctx) +mClock.Advance(5*time.Second) +c.Release() +``` + +We wait until we trap the `clock.Since()` call, which implies that `clock.Now()` has completed, then +advance the mock clock 5 seconds. Finally, we release the `clock.Since()` call. Any changes to the +clock that happen _before_ we release the call will be included in the time used for the +`clock.Since()` call. + +As a more involved example, consider an inactivity timeout: we want something to happen if there is +no activity recorded for some period, say 10 minutes in the following example: + +```go +type InactivityTimer struct { + mu sync.Mutex + activity time.Time + clock quartz.Clock +} + +func (i *InactivityTimer) Start() { + i.mu.Lock() + defer i.mu.Unlock() + next := i.clock.Until(i.activity.Add(10*time.Minute)) + t := i.clock.AfterFunc(next, func() { + i.mu.Lock() + defer i.mu.Unlock() + next := i.clock.Until(i.activity.Add(10*time.Minute)) + if next == 0 { + i.timeoutLocked() + return + } + t.Reset(next) + }) +} +``` + +The actual contents of `timeoutLocked()` doesn't matter for this example, and assume there are other +functions that record the latest `activity`. + +We found that some time testing libraries hold a lock on the mock clock while calling the function +passed to `AfterFunc`, resulting in a deadlock if you made clock calls from within. + +Others allow this sort of thing, but don't have the flexibility to test edge cases. There is a +subtle bug in our `Start()` function. The timer may pop a little late, and/or some measurable real +time may elapse before `Until()` gets called inside the `AfterFunc`. If there hasn't been activity, +`next` might be negative. + +To test this in Quartz, we'll use a trap. We only want to trap the inner `Until()` call, not the +initial one, so to make testing easier we can "tag" the call we want. Like this: + +```go +func (i *InactivityTimer) Start() { + i.mu.Lock() + defer i.mu.Unlock() + next := i.clock.Until(i.activity.Add(10*time.Minute)) + t := i.clock.AfterFunc(next, func() { + i.mu.Lock() + defer i.mu.Unlock() + next := i.clock.Until(i.activity.Add(10*time.Minute), "inner") + if next == 0 { + i.timeoutLocked() + return + } + t.Reset(next) + }) +} +``` + +All Quartz `Clock` functions, and functions on returned timers and tickers support zero or more +string tags that allow traps to match on them. + +```go +func TestInactivityTimer_Late(t *testing.T) { + // set a timeout on the test itself, so that if Wait functions get blocked, we don't have to + // wait for the default test timeout of 10 minutes. + ctx, cancel := context.WithTimeout(10*time.Second) + defer cancel() + mClock := quartz.NewMock(t) + trap := mClock.Trap.Until("inner") + defer trap.Close() + + it := &InactivityTimer{ + activity: mClock.Now(), + clock: mClock, + } + it.Start() + + // Trigger the AfterFunc + w := mClock.Advance(10*time.Minute) + c := trap.Wait(ctx) + // Advance the clock a few ms to simulate a busy system + mClock.Advance(3*time.Millisecond) + c.Release() // Until() returns + w.MustWait(ctx) // Wait for the AfterFunc to wrap up + + // Assert that the timeoutLocked() function was called +} +``` + +This test case will fail with our bugged implementation, since the triggered AfterFunc won't call +`timeoutLocked()` and instead will reset the timer with a negative number. The fix is easy, use +`next <= 0` as the comparison. diff --git a/vendor/github.com/coder/quartz/clock.go b/vendor/github.com/coder/quartz/clock.go new file mode 100644 index 0000000000..729edfa562 --- /dev/null +++ b/vendor/github.com/coder/quartz/clock.go @@ -0,0 +1,43 @@ +// Package quartz is a library for testing time related code. It exports an interface Clock that +// mimics the standard library time package functions. In production, an implementation that calls +// thru to the standard library is used. In testing, a Mock clock is used to precisely control and +// intercept time functions. +package quartz + +import ( + "context" + "time" +) + +type Clock interface { + // NewTicker returns a new Ticker containing a channel that will send the current time on the + // channel after each tick. The period of the ticks is specified by the duration argument. The + // ticker will adjust the time interval or drop ticks to make up for slow receivers. The + // duration d must be greater than zero; if not, NewTicker will panic. Stop the ticker to + // release associated resources. + NewTicker(d time.Duration, tags ...string) *Ticker + // TickerFunc is a convenience function that calls f on the interval d until either the given + // context expires or f returns an error. Callers may call Wait() on the returned Waiter to + // wait until this happens and obtain the error. The duration d must be greater than zero; if + // not, TickerFunc will panic. + TickerFunc(ctx context.Context, d time.Duration, f func() error, tags ...string) Waiter + // NewTimer creates a new Timer that will send the current time on its channel after at least + // duration d. + NewTimer(d time.Duration, tags ...string) *Timer + // AfterFunc waits for the duration to elapse and then calls f in its own goroutine. It returns + // a Timer that can be used to cancel the call using its Stop method. The returned Timer's C + // field is not used and will be nil. + AfterFunc(d time.Duration, f func(), tags ...string) *Timer + + // Now returns the current local time. + Now(tags ...string) time.Time + // Since returns the time elapsed since t. It is shorthand for Clock.Now().Sub(t). + Since(t time.Time, tags ...string) time.Duration + // Until returns the duration until t. It is shorthand for t.Sub(Clock.Now()). + Until(t time.Time, tags ...string) time.Duration +} + +// Waiter can be waited on for an error. +type Waiter interface { + Wait(tags ...string) error +} diff --git a/vendor/github.com/coder/quartz/mock.go b/vendor/github.com/coder/quartz/mock.go new file mode 100644 index 0000000000..cc8e064c21 --- /dev/null +++ b/vendor/github.com/coder/quartz/mock.go @@ -0,0 +1,661 @@ +package quartz + +import ( + "context" + "errors" + "fmt" + "slices" + "sync" + "testing" + "time" +) + +// Mock is the testing implementation of Clock. It tracks a time that monotonically increases +// during a test, triggering any timers or tickers automatically. +type Mock struct { + tb testing.TB + mu sync.Mutex + + // cur is the current time + cur time.Time + + all []event + nextTime time.Time + nextEvents []event + traps []*Trap +} + +type event interface { + next() time.Time + fire(t time.Time) +} + +func (m *Mock) TickerFunc(ctx context.Context, d time.Duration, f func() error, tags ...string) Waiter { + if d <= 0 { + panic("TickerFunc called with negative or zero duration") + } + m.mu.Lock() + defer m.mu.Unlock() + c := newCall(clockFunctionTickerFunc, tags, withDuration(d)) + m.matchCallLocked(c) + defer close(c.complete) + t := &mockTickerFunc{ + ctx: ctx, + d: d, + f: f, + nxt: m.cur.Add(d), + mock: m, + cond: sync.NewCond(&m.mu), + } + m.all = append(m.all, t) + m.recomputeNextLocked() + go t.waitForCtx() + return t +} + +func (m *Mock) NewTicker(d time.Duration, tags ...string) *Ticker { + if d <= 0 { + panic("NewTicker called with negative or zero duration") + } + m.mu.Lock() + defer m.mu.Unlock() + c := newCall(clockFunctionNewTicker, tags, withDuration(d)) + m.matchCallLocked(c) + defer close(c.complete) + // 1 element buffer follows standard library implementation + ticks := make(chan time.Time, 1) + t := &Ticker{ + C: ticks, + c: ticks, + d: d, + nxt: m.cur.Add(d), + mock: m, + } + m.addEventLocked(t) + return t +} + +func (m *Mock) NewTimer(d time.Duration, tags ...string) *Timer { + m.mu.Lock() + defer m.mu.Unlock() + c := newCall(clockFunctionNewTimer, tags, withDuration(d)) + defer close(c.complete) + m.matchCallLocked(c) + ch := make(chan time.Time, 1) + t := &Timer{ + C: ch, + c: ch, + nxt: m.cur.Add(d), + mock: m, + } + if d <= 0 { + // zero or negative duration timer means we should immediately fire + // it, rather than add it. + go t.fire(t.mock.cur) + return t + } + m.addEventLocked(t) + return t +} + +func (m *Mock) AfterFunc(d time.Duration, f func(), tags ...string) *Timer { + m.mu.Lock() + defer m.mu.Unlock() + c := newCall(clockFunctionAfterFunc, tags, withDuration(d)) + defer close(c.complete) + m.matchCallLocked(c) + t := &Timer{ + nxt: m.cur.Add(d), + fn: f, + mock: m, + } + if d <= 0 { + // zero or negative duration timer means we should immediately fire + // it, rather than add it. + go t.fire(t.mock.cur) + return t + } + m.addEventLocked(t) + return t +} + +func (m *Mock) Now(tags ...string) time.Time { + m.mu.Lock() + defer m.mu.Unlock() + c := newCall(clockFunctionNow, tags) + defer close(c.complete) + m.matchCallLocked(c) + return m.cur +} + +func (m *Mock) Since(t time.Time, tags ...string) time.Duration { + m.mu.Lock() + defer m.mu.Unlock() + c := newCall(clockFunctionSince, tags, withTime(t)) + defer close(c.complete) + m.matchCallLocked(c) + return m.cur.Sub(t) +} + +func (m *Mock) Until(t time.Time, tags ...string) time.Duration { + m.mu.Lock() + defer m.mu.Unlock() + c := newCall(clockFunctionUntil, tags, withTime(t)) + defer close(c.complete) + m.matchCallLocked(c) + return t.Sub(m.cur) +} + +func (m *Mock) addEventLocked(e event) { + m.all = append(m.all, e) + m.recomputeNextLocked() +} + +func (m *Mock) recomputeNextLocked() { + var best time.Time + var events []event + for _, e := range m.all { + if best.IsZero() || e.next().Before(best) { + best = e.next() + events = []event{e} + continue + } + if e.next().Equal(best) { + events = append(events, e) + continue + } + } + m.nextTime = best + m.nextEvents = events +} + +func (m *Mock) removeTimer(t *Timer) { + m.mu.Lock() + defer m.mu.Unlock() + m.removeTimerLocked(t) +} + +func (m *Mock) removeTimerLocked(t *Timer) { + t.stopped = true + m.removeEventLocked(t) +} + +func (m *Mock) removeEventLocked(e event) { + defer m.recomputeNextLocked() + for i := range m.all { + if m.all[i] == e { + m.all = append(m.all[:i], m.all[i+1:]...) + return + } + } +} + +func (m *Mock) matchCallLocked(c *Call) { + var traps []*Trap + for _, t := range m.traps { + if t.matches(c) { + traps = append(traps, t) + } + } + if len(traps) == 0 { + return + } + c.releases.Add(len(traps)) + m.mu.Unlock() + for _, t := range traps { + go t.catch(c) + } + c.releases.Wait() + m.mu.Lock() +} + +// AdvanceWaiter is returned from Advance and Set calls and allows you to wait for ticks and timers +// to complete. In the case of functions passed to AfterFunc or TickerFunc, it waits for the +// functions to return. For other ticks & timers, it just waits for the tick to be delivered to +// the channel. +// +// If multiple timers or tickers trigger simultaneously, they are all run on separate +// go routines. +type AdvanceWaiter struct { + tb testing.TB + ch chan struct{} +} + +// Wait for all timers and ticks to complete, or until context expires. +func (w AdvanceWaiter) Wait(ctx context.Context) error { + select { + case <-w.ch: + return nil + case <-ctx.Done(): + return ctx.Err() + } +} + +// MustWait waits for all timers and ticks to complete, and fails the test immediately if the +// context completes first. MustWait must be called from the goroutine running the test or +// benchmark, similar to `t.FailNow()`. +func (w AdvanceWaiter) MustWait(ctx context.Context) { + w.tb.Helper() + select { + case <-w.ch: + return + case <-ctx.Done(): + w.tb.Fatalf("context expired while waiting for clock to advance: %s", ctx.Err()) + } +} + +// Done returns a channel that is closed when all timers and ticks complete. +func (w AdvanceWaiter) Done() <-chan struct{} { + return w.ch +} + +// Advance moves the clock forward by d, triggering any timers or tickers. The returned value can +// be used to wait for all timers and ticks to complete. Advance sets the clock forward before +// returning, and can only advance up to the next timer or tick event. It will fail the test if you +// attempt to advance beyond. +// +// If you need to advance exactly to the next event, and don't know or don't wish to calculate it, +// consider AdvanceNext(). +func (m *Mock) Advance(d time.Duration) AdvanceWaiter { + m.tb.Helper() + w := AdvanceWaiter{tb: m.tb, ch: make(chan struct{})} + m.mu.Lock() + fin := m.cur.Add(d) + // nextTime.IsZero implies no events scheduled. + if m.nextTime.IsZero() || fin.Before(m.nextTime) { + m.cur = fin + m.mu.Unlock() + close(w.ch) + return w + } + if fin.After(m.nextTime) { + m.tb.Errorf(fmt.Sprintf("cannot advance %s which is beyond next timer/ticker event in %s", + d.String(), m.nextTime.Sub(m.cur))) + m.mu.Unlock() + close(w.ch) + return w + } + + m.cur = m.nextTime + go m.advanceLocked(w) + return w +} + +func (m *Mock) advanceLocked(w AdvanceWaiter) { + defer close(w.ch) + wg := sync.WaitGroup{} + for i := range m.nextEvents { + e := m.nextEvents[i] + t := m.cur + wg.Add(1) + go func() { + e.fire(t) + wg.Done() + }() + } + // release the lock and let the events resolve. This allows them to call back into the + // Mock to query the time or set new timers. Each event should remove or reschedule + // itself from nextEvents. + m.mu.Unlock() + wg.Wait() +} + +// Set the time to t. If the time is after the current mocked time, then this is equivalent to +// Advance() with the difference. You may only Set the time earlier than the current time before +// starting tickers and timers (e.g. at the start of your test case). +func (m *Mock) Set(t time.Time) AdvanceWaiter { + m.tb.Helper() + w := AdvanceWaiter{tb: m.tb, ch: make(chan struct{})} + m.mu.Lock() + if t.Before(m.cur) { + defer close(w.ch) + defer m.mu.Unlock() + // past + if !m.nextTime.IsZero() { + m.tb.Error("Set mock clock to the past after timers/tickers started") + } + m.cur = t + return w + } + // future + // nextTime.IsZero implies no events scheduled. + if m.nextTime.IsZero() || t.Before(m.nextTime) { + defer close(w.ch) + defer m.mu.Unlock() + m.cur = t + return w + } + if t.After(m.nextTime) { + defer close(w.ch) + defer m.mu.Unlock() + m.tb.Errorf("cannot Set time to %s which is beyond next timer/ticker event at %s", + t.String(), m.nextTime) + return w + } + + m.cur = m.nextTime + go m.advanceLocked(w) + return w +} + +// AdvanceNext advances the clock to the next timer or tick event. It fails the test if there are +// none scheduled. It returns the duration the clock was advanced and a waiter that can be used to +// wait for the timer/tick event(s) to finish. +func (m *Mock) AdvanceNext() (time.Duration, AdvanceWaiter) { + m.mu.Lock() + m.tb.Helper() + w := AdvanceWaiter{tb: m.tb, ch: make(chan struct{})} + if m.nextTime.IsZero() { + defer close(w.ch) + defer m.mu.Unlock() + m.tb.Error("cannot AdvanceNext because there are no timers or tickers running") + } + d := m.nextTime.Sub(m.cur) + m.cur = m.nextTime + go m.advanceLocked(w) + return d, w +} + +// Peek returns the duration until the next ticker or timer event and the value +// true, or, if there are no running tickers or timers, it returns zero and +// false. +func (m *Mock) Peek() (d time.Duration, ok bool) { + m.mu.Lock() + defer m.mu.Unlock() + if m.nextTime.IsZero() { + return 0, false + } + return m.nextTime.Sub(m.cur), true +} + +// Trapper allows the creation of Traps +type Trapper struct { + // mock is the underlying Mock. This is a thin wrapper around Mock so that + // we can have our interface look like mClock.Trap().NewTimer("foo") + mock *Mock +} + +func (t Trapper) NewTimer(tags ...string) *Trap { + return t.mock.newTrap(clockFunctionNewTimer, tags) +} + +func (t Trapper) AfterFunc(tags ...string) *Trap { + return t.mock.newTrap(clockFunctionAfterFunc, tags) +} + +func (t Trapper) TimerStop(tags ...string) *Trap { + return t.mock.newTrap(clockFunctionTimerStop, tags) +} + +func (t Trapper) TimerReset(tags ...string) *Trap { + return t.mock.newTrap(clockFunctionTimerReset, tags) +} + +func (t Trapper) TickerFunc(tags ...string) *Trap { + return t.mock.newTrap(clockFunctionTickerFunc, tags) +} + +func (t Trapper) TickerFuncWait(tags ...string) *Trap { + return t.mock.newTrap(clockFunctionTickerFuncWait, tags) +} + +func (t Trapper) NewTicker(tags ...string) *Trap { + return t.mock.newTrap(clockFunctionNewTicker, tags) +} + +func (t Trapper) TickerStop(tags ...string) *Trap { + return t.mock.newTrap(clockFunctionTickerStop, tags) +} + +func (t Trapper) TickerReset(tags ...string) *Trap { + return t.mock.newTrap(clockFunctionTickerReset, tags) +} + +func (t Trapper) Now(tags ...string) *Trap { + return t.mock.newTrap(clockFunctionNow, tags) +} + +func (t Trapper) Since(tags ...string) *Trap { + return t.mock.newTrap(clockFunctionSince, tags) +} + +func (t Trapper) Until(tags ...string) *Trap { + return t.mock.newTrap(clockFunctionUntil, tags) +} + +func (m *Mock) Trap() Trapper { + return Trapper{m} +} + +func (m *Mock) newTrap(fn clockFunction, tags []string) *Trap { + m.mu.Lock() + defer m.mu.Unlock() + tr := &Trap{ + fn: fn, + tags: tags, + mock: m, + calls: make(chan *Call), + done: make(chan struct{}), + } + m.traps = append(m.traps, tr) + return tr +} + +// NewMock creates a new Mock with the time set to midnight UTC on Jan 1, 2024. +// You may re-set the time earlier than this, but only before timers or tickers +// are created. +func NewMock(tb testing.TB) *Mock { + cur, err := time.Parse(time.RFC3339, "2024-01-01T00:00:00Z") + if err != nil { + panic(err) + } + return &Mock{ + tb: tb, + cur: cur, + } +} + +var _ Clock = &Mock{} + +type mockTickerFunc struct { + ctx context.Context + d time.Duration + f func() error + nxt time.Time + mock *Mock + + // cond is a condition Locked on the main Mock.mu + cond *sync.Cond + // inProgress is true when we are actively calling f + inProgress bool + // done is true when the ticker exits + done bool + // err holds the error when the ticker exits + err error +} + +func (m *mockTickerFunc) next() time.Time { + return m.nxt +} + +func (m *mockTickerFunc) fire(_ time.Time) { + m.mock.mu.Lock() + if m.done { + m.mock.mu.Unlock() + return + } + m.nxt = m.nxt.Add(m.d) + m.mock.recomputeNextLocked() + // we need this check to happen after we've computed the next tick, + // otherwise it will be immediately rescheduled. + if m.inProgress { + m.mock.mu.Unlock() + return + } + + m.inProgress = true + m.mock.mu.Unlock() + err := m.f() + m.mock.mu.Lock() + defer m.mock.mu.Unlock() + m.inProgress = false + m.cond.Broadcast() // wake up anything waiting for f to finish + if err != nil { + m.exitLocked(err) + } +} + +func (m *mockTickerFunc) exitLocked(err error) { + if m.done { + return + } + m.done = true + m.err = err + m.mock.removeEventLocked(m) + m.cond.Broadcast() +} + +func (m *mockTickerFunc) waitForCtx() { + <-m.ctx.Done() + m.mock.mu.Lock() + defer m.mock.mu.Unlock() + for m.inProgress { + m.cond.Wait() + } + m.exitLocked(m.ctx.Err()) +} + +func (m *mockTickerFunc) Wait(tags ...string) error { + m.mock.mu.Lock() + defer m.mock.mu.Unlock() + c := newCall(clockFunctionTickerFuncWait, tags) + m.mock.matchCallLocked(c) + defer close(c.complete) + for !m.done { + m.cond.Wait() + } + return m.err +} + +var _ Waiter = &mockTickerFunc{} + +type clockFunction int + +const ( + clockFunctionNewTimer clockFunction = iota + clockFunctionAfterFunc + clockFunctionTimerStop + clockFunctionTimerReset + clockFunctionTickerFunc + clockFunctionTickerFuncWait + clockFunctionNewTicker + clockFunctionTickerReset + clockFunctionTickerStop + clockFunctionNow + clockFunctionSince + clockFunctionUntil +) + +type callArg func(c *Call) + +type Call struct { + Time time.Time + Duration time.Duration + Tags []string + + fn clockFunction + releases sync.WaitGroup + complete chan struct{} +} + +func (c *Call) Release() { + c.releases.Done() + <-c.complete +} + +func withTime(t time.Time) callArg { + return func(c *Call) { + c.Time = t + } +} + +func withDuration(d time.Duration) callArg { + return func(c *Call) { + c.Duration = d + } +} + +func newCall(fn clockFunction, tags []string, args ...callArg) *Call { + c := &Call{ + fn: fn, + Tags: tags, + complete: make(chan struct{}), + } + for _, a := range args { + a(c) + } + return c +} + +type Trap struct { + fn clockFunction + tags []string + mock *Mock + calls chan *Call + done chan struct{} +} + +func (t *Trap) catch(c *Call) { + select { + case t.calls <- c: + case <-t.done: + c.Release() + } +} + +func (t *Trap) matches(c *Call) bool { + if t.fn != c.fn { + return false + } + for _, tag := range t.tags { + if !slices.Contains(c.Tags, tag) { + return false + } + } + return true +} + +func (t *Trap) Close() { + t.mock.mu.Lock() + defer t.mock.mu.Unlock() + for i, tr := range t.mock.traps { + if t == tr { + t.mock.traps = append(t.mock.traps[:i], t.mock.traps[i+1:]...) + } + } + close(t.done) +} + +var ErrTrapClosed = errors.New("trap closed") + +func (t *Trap) Wait(ctx context.Context) (*Call, error) { + select { + case <-ctx.Done(): + return nil, ctx.Err() + case <-t.done: + return nil, ErrTrapClosed + case c := <-t.calls: + return c, nil + } +} + +// MustWait calls Wait() and then if there is an error, immediately fails the +// test via tb.Fatalf() +func (t *Trap) MustWait(ctx context.Context) *Call { + t.mock.tb.Helper() + c, err := t.Wait(ctx) + if err != nil { + t.mock.tb.Fatalf("context expired while waiting for trap: %s", err.Error()) + } + return c +} diff --git a/vendor/github.com/coder/quartz/real.go b/vendor/github.com/coder/quartz/real.go new file mode 100644 index 0000000000..f39fb1163e --- /dev/null +++ b/vendor/github.com/coder/quartz/real.go @@ -0,0 +1,80 @@ +package quartz + +import ( + "context" + "time" +) + +type realClock struct{} + +func NewReal() Clock { + return realClock{} +} + +func (realClock) NewTicker(d time.Duration, _ ...string) *Ticker { + tkr := time.NewTicker(d) + return &Ticker{ticker: tkr, C: tkr.C} +} + +func (realClock) TickerFunc(ctx context.Context, d time.Duration, f func() error, _ ...string) Waiter { + ct := &realContextTicker{ + ctx: ctx, + tkr: time.NewTicker(d), + f: f, + err: make(chan error, 1), + } + go ct.run() + return ct +} + +type realContextTicker struct { + ctx context.Context + tkr *time.Ticker + f func() error + err chan error +} + +func (t *realContextTicker) Wait(_ ...string) error { + return <-t.err +} + +func (t *realContextTicker) run() { + defer t.tkr.Stop() + for { + select { + case <-t.ctx.Done(): + t.err <- t.ctx.Err() + return + case <-t.tkr.C: + err := t.f() + if err != nil { + t.err <- err + return + } + } + } +} + +func (realClock) NewTimer(d time.Duration, _ ...string) *Timer { + rt := time.NewTimer(d) + return &Timer{C: rt.C, timer: rt} +} + +func (realClock) AfterFunc(d time.Duration, f func(), _ ...string) *Timer { + rt := time.AfterFunc(d, f) + return &Timer{C: rt.C, timer: rt} +} + +func (realClock) Now(_ ...string) time.Time { + return time.Now() +} + +func (realClock) Since(t time.Time, _ ...string) time.Duration { + return time.Since(t) +} + +func (realClock) Until(t time.Time, _ ...string) time.Duration { + return time.Until(t) +} + +var _ Clock = realClock{} diff --git a/vendor/github.com/coder/quartz/ticker.go b/vendor/github.com/coder/quartz/ticker.go new file mode 100644 index 0000000000..5506750d5d --- /dev/null +++ b/vendor/github.com/coder/quartz/ticker.go @@ -0,0 +1,75 @@ +package quartz + +import "time" + +// A Ticker holds a channel that delivers “ticks” of a clock at intervals. +type Ticker struct { + C <-chan time.Time + //nolint: revive + c chan time.Time + ticker *time.Ticker // realtime impl, if set + d time.Duration // period, if set + nxt time.Time // next tick time + mock *Mock // mock clock, if set + stopped bool // true if the ticker is not running +} + +func (t *Ticker) fire(tt time.Time) { + t.mock.mu.Lock() + defer t.mock.mu.Unlock() + if t.stopped { + return + } + for !t.nxt.After(t.mock.cur) { + t.nxt = t.nxt.Add(t.d) + } + t.mock.recomputeNextLocked() + select { + case t.c <- tt: + default: + } +} + +func (t *Ticker) next() time.Time { + return t.nxt +} + +// Stop turns off a ticker. After Stop, no more ticks will be sent. Stop does +// not close the channel, to prevent a concurrent goroutine reading from the +// channel from seeing an erroneous "tick". +func (t *Ticker) Stop(tags ...string) { + if t.ticker != nil { + t.ticker.Stop() + return + } + t.mock.mu.Lock() + defer t.mock.mu.Unlock() + c := newCall(clockFunctionTickerStop, tags) + t.mock.matchCallLocked(c) + defer close(c.complete) + t.mock.removeEventLocked(t) + t.stopped = true +} + +// Reset stops a ticker and resets its period to the specified duration. The +// next tick will arrive after the new period elapses. The duration d must be +// greater than zero; if not, Reset will panic. +func (t *Ticker) Reset(d time.Duration, tags ...string) { + if t.ticker != nil { + t.ticker.Reset(d) + return + } + t.mock.mu.Lock() + defer t.mock.mu.Unlock() + c := newCall(clockFunctionTickerReset, tags, withDuration(d)) + t.mock.matchCallLocked(c) + defer close(c.complete) + t.nxt = t.mock.cur.Add(d) + t.d = d + if t.stopped { + t.stopped = false + t.mock.addEventLocked(t) + } else { + t.mock.recomputeNextLocked() + } +} diff --git a/vendor/github.com/coder/quartz/timer.go b/vendor/github.com/coder/quartz/timer.go new file mode 100644 index 0000000000..8d928ac6f6 --- /dev/null +++ b/vendor/github.com/coder/quartz/timer.go @@ -0,0 +1,81 @@ +package quartz + +import "time" + +// The Timer type represents a single event. When the Timer expires, the current time will be sent +// on C, unless the Timer was created by AfterFunc. A Timer must be created with NewTimer or +// AfterFunc. +type Timer struct { + C <-chan time.Time + //nolint: revive + c chan time.Time + timer *time.Timer // realtime impl, if set + nxt time.Time // next tick time + mock *Mock // mock clock, if set + fn func() // AfterFunc function, if set + stopped bool // True if stopped, false if running +} + +func (t *Timer) fire(tt time.Time) { + t.mock.removeTimer(t) + if t.fn != nil { + t.fn() + } else { + t.c <- tt + } +} + +func (t *Timer) next() time.Time { + return t.nxt +} + +// Stop prevents the Timer from firing. It returns true if the call stops the timer, false if the +// timer has already expired or been stopped. Stop does not close the channel, to prevent a read +// from the channel succeeding incorrectly. +// +// See https://pkg.go.dev/time#Timer.Stop for more information. +func (t *Timer) Stop(tags ...string) bool { + if t.timer != nil { + return t.timer.Stop() + } + t.mock.mu.Lock() + defer t.mock.mu.Unlock() + c := newCall(clockFunctionTimerStop, tags) + t.mock.matchCallLocked(c) + defer close(c.complete) + result := !t.stopped + t.mock.removeTimerLocked(t) + return result +} + +// Reset changes the timer to expire after duration d. It returns true if the timer had been active, +// false if the timer had expired or been stopped. +// +// See https://pkg.go.dev/time#Timer.Reset for more information. +func (t *Timer) Reset(d time.Duration, tags ...string) bool { + if t.timer != nil { + return t.timer.Reset(d) + } + t.mock.mu.Lock() + defer t.mock.mu.Unlock() + c := newCall(clockFunctionTimerReset, tags, withDuration(d)) + t.mock.matchCallLocked(c) + defer close(c.complete) + result := !t.stopped + select { + case <-t.c: + default: + } + if d <= 0 { + // zero or negative duration timer means we should immediately re-fire + // it, rather than remove and re-add it. + t.stopped = false + go t.fire(t.mock.cur) + return result + } + t.mock.removeTimerLocked(t) + t.stopped = false + t.nxt = t.mock.cur.Add(d) + t.mock.addEventLocked(t) + return result +} diff --git a/vendor/github.com/jessevdk/go-flags/.travis.yml b/vendor/github.com/jessevdk/go-flags/.travis.yml deleted file mode 100644 index 2fc5e5f5b5..0000000000 --- a/vendor/github.com/jessevdk/go-flags/.travis.yml +++ /dev/null @@ -1,39 +0,0 @@ -language: go - -os: - - linux - - osx - -go: - - 1.16.x - -install: - # go-flags - - go build -v ./... - - # linting - - go get -v golang.org/x/lint/golint - - # code coverage - - go get golang.org/x/tools/cmd/cover - - go get github.com/onsi/ginkgo/ginkgo - - go get github.com/modocache/gover - - if [ "$TRAVIS_SECURE_ENV_VARS" = "true" ]; then go get github.com/mattn/goveralls; fi - -script: - # go-flags - - $(exit $(gofmt -l . | wc -l)) - - go test -v ./... - - # linting - - go tool vet -all=true -v=true . || true - - $(go env GOPATH | awk 'BEGIN{FS=":"} {print $1}')/bin/golint ./... - - # code coverage - - $(go env GOPATH | awk 'BEGIN{FS=":"} {print $1}')/bin/ginkgo -r -cover - - $(go env GOPATH | awk 'BEGIN{FS=":"} {print $1}')/bin/gover - - if [ "$TRAVIS_SECURE_ENV_VARS" = "true" ]; then $(go env GOPATH | awk 'BEGIN{FS=":"} {print $1}')/bin/goveralls -coverprofile=gover.coverprofile -service=travis-ci -repotoken $COVERALLS_TOKEN; fi - -env: - # coveralls.io - secure: "RCYbiB4P0RjQRIoUx/vG/AjP3mmYCbzOmr86DCww1Z88yNcy3hYr3Cq8rpPtYU5v0g7wTpu4adaKIcqRE9xknYGbqj3YWZiCoBP1/n4Z+9sHW3Dsd9D/GRGeHUus0laJUGARjWoCTvoEtOgTdGQDoX7mH+pUUY0FBltNYUdOiiU=" diff --git a/vendor/github.com/jessevdk/go-flags/README.md b/vendor/github.com/jessevdk/go-flags/README.md index f22650b20a..759eeb0d4e 100644 --- a/vendor/github.com/jessevdk/go-flags/README.md +++ b/vendor/github.com/jessevdk/go-flags/README.md @@ -1,7 +1,7 @@ go-flags: a go library for parsing command line arguments ========================================================= -[![GoDoc](https://godoc.org/github.com/jessevdk/go-flags?status.png)](https://godoc.org/github.com/jessevdk/go-flags) [![Build Status](https://travis-ci.org/jessevdk/go-flags.svg?branch=master)](https://travis-ci.org/jessevdk/go-flags) [![Coverage Status](https://img.shields.io/coveralls/jessevdk/go-flags.svg)](https://coveralls.io/r/jessevdk/go-flags?branch=master) +[![GoDoc](https://godoc.org/github.com/jessevdk/go-flags?status.png)](https://godoc.org/github.com/jessevdk/go-flags) This library provides similar functionality to the builtin flag library of go, but provides much more functionality and nicer formatting. From the @@ -78,6 +78,9 @@ var opts struct { // Example of a map IntMap map[string]int `long:"intmap" description:"A map from string to int"` + + // Example of env variable + Thresholds []int `long:"thresholds" default:"1" default:"2" env:"THRESHOLD_VALUES" env-delim:","` } // Callback which will invoke callto: to call a number. diff --git a/vendor/github.com/jessevdk/go-flags/command.go b/vendor/github.com/jessevdk/go-flags/command.go index 879465d7a5..ac4f1e392b 100644 --- a/vendor/github.com/jessevdk/go-flags/command.go +++ b/vendor/github.com/jessevdk/go-flags/command.go @@ -30,6 +30,12 @@ type Command struct { // Whether positional arguments are required ArgsRequired bool + // Whether to pass all arguments after the first non option as remaining + // command line arguments. This is equivalent to strict POSIX processing. + // This is command-local version of PassAfterNonOption Parser flag. It + // cannot be turned off when PassAfterNonOption Parser flag is set. + PassAfterNonOption bool + commands []*Command hasBuiltinHelpGroup bool args []*Arg @@ -244,6 +250,7 @@ func (c *Command) scanSubcommandHandler(parentg *Group) scanHandler { longDescription := mtag.Get("long-description") subcommandsOptional := mtag.Get("subcommands-optional") aliases := mtag.GetMany("alias") + passAfterNonOption := mtag.Get("pass-after-non-option") subc, err := c.AddCommand(subcommand, shortDescription, longDescription, ptrval.Interface()) @@ -261,6 +268,10 @@ func (c *Command) scanSubcommandHandler(parentg *Group) scanHandler { subc.Aliases = aliases } + if len(passAfterNonOption) > 0 { + subc.PassAfterNonOption = true + } + return true, nil } diff --git a/vendor/github.com/jessevdk/go-flags/convert.go b/vendor/github.com/jessevdk/go-flags/convert.go index cda29b2f04..b27f698dc4 100644 --- a/vendor/github.com/jessevdk/go-flags/convert.go +++ b/vendor/github.com/jessevdk/go-flags/convert.go @@ -53,7 +53,7 @@ func getBase(options multiTag, base int) (int, error) { func convertMarshal(val reflect.Value) (bool, string, error) { // Check first for the Marshaler interface - if val.Type().NumMethod() > 0 && val.CanInterface() { + if val.IsValid() && val.Type().NumMethod() > 0 && val.CanInterface() { if marshaler, ok := val.Interface().(Marshaler); ok { ret, err := marshaler.MarshalFlag() return true, ret, err @@ -68,6 +68,10 @@ func convertToString(val reflect.Value, options multiTag) (string, error) { return ret, err } + if !val.IsValid() { + return "", nil + } + tp := val.Type() // Support for time.Duration @@ -220,7 +224,7 @@ func convert(val string, retval reflect.Value, options multiTag) error { retval.SetBool(b) } case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - base, err := getBase(options, 10) + base, err := getBase(options, 0) if err != nil { return err @@ -234,7 +238,7 @@ func convert(val string, retval reflect.Value, options multiTag) error { retval.SetInt(parsed) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - base, err := getBase(options, 10) + base, err := getBase(options, 0) if err != nil { return err @@ -267,7 +271,12 @@ func convert(val string, retval reflect.Value, options multiTag) error { retval.Set(reflect.Append(retval, elemval)) case reflect.Map: - parts := strings.SplitN(val, ":", 2) + keyValueDelimiter := options.Get("key-value-delimiter") + if keyValueDelimiter == "" { + keyValueDelimiter = ":" + } + + parts := strings.SplitN(val, keyValueDelimiter, 2) key := parts[0] var value string diff --git a/vendor/github.com/jessevdk/go-flags/flags.go b/vendor/github.com/jessevdk/go-flags/flags.go index ac2157dd60..a6acf1be1f 100644 --- a/vendor/github.com/jessevdk/go-flags/flags.go +++ b/vendor/github.com/jessevdk/go-flags/flags.go @@ -8,46 +8,45 @@ The flags package is similar in functionality to the go built-in flag package but provides more options and uses reflection to provide a convenient and succinct way of specifying command line options. - -Supported features +# Supported features The following features are supported in go-flags: - Options with short names (-v) - Options with long names (--verbose) - Options with and without arguments (bool v.s. other type) - Options with optional arguments and default values - Option default values from ENVIRONMENT_VARIABLES, including slice and map values - Multiple option groups each containing a set of options - Generate and print well-formatted help message - Passing remaining command line arguments after -- (optional) - Ignoring unknown command line options (optional) - Supports -I/usr/include -I=/usr/include -I /usr/include option argument specification - Supports multiple short options -aux - Supports all primitive go types (string, int{8..64}, uint{8..64}, float) - Supports same option multiple times (can store in slice or last option counts) - Supports maps - Supports function callbacks - Supports namespaces for (nested) option groups + Options with short names (-v) + Options with long names (--verbose) + Options with and without arguments (bool v.s. other type) + Options with optional arguments and default values + Option default values from ENVIRONMENT_VARIABLES, including slice and map values + Multiple option groups each containing a set of options + Generate and print well-formatted help message + Passing remaining command line arguments after -- (optional) + Ignoring unknown command line options (optional) + Supports -I/usr/include -I=/usr/include -I /usr/include option argument specification + Supports multiple short options -aux + Supports all primitive go types (string, int{8..64}, uint{8..64}, float) + Supports same option multiple times (can store in slice or last option counts) + Supports maps + Supports function callbacks + Supports namespaces for (nested) option groups Additional features specific to Windows: - Options with short names (/v) - Options with long names (/verbose) - Windows-style options with arguments use a colon as the delimiter - Modify generated help message with Windows-style / options - Windows style options can be disabled at build time using the "forceposix" - build tag + Options with short names (/v) + Options with long names (/verbose) + Windows-style options with arguments use a colon as the delimiter + Modify generated help message with Windows-style / options + Windows style options can be disabled at build time using the "forceposix" + build tag -Basic usage +# Basic usage The flags package uses structs, reflection and struct field tags to allow users to specify command line options. This results in very simple and concise specification of your application options. For example: - type Options struct { - Verbose []bool `short:"v" long:"verbose" description:"Show verbose debug information"` - } + type Options struct { + Verbose []bool `short:"v" long:"verbose" description:"Show verbose debug information"` + } This specifies one option with a short name -v and a long name --verbose. When either -v or --verbose is found on the command line, a 'true' value @@ -60,9 +59,9 @@ whenever the option is encountered, a value is appended to the slice. Map options from string to primitive type are also supported. On the command line, you specify the value for such an option as key:value. For example - type Options struct { - AuthorInfo string[string] `short:"a"` - } + type Options struct { + AuthorInfo string[string] `short:"a"` + } Then, the AuthorInfo map can be filled with something like -a name:Jesse -a "surname:van den Kieboom". @@ -71,96 +70,94 @@ Finally, for full control over the conversion between command line argument values and options, user defined types can choose to implement the Marshaler and Unmarshaler interfaces. - -Available field tags +# Available field tags The following is a list of tags for struct fields supported by go-flags: - short: the short name of the option (single character) - long: the long name of the option - required: if non empty, makes the option required to appear on the command - line. If a required option is not present, the parser will - return ErrRequired (optional) - description: the description of the option (optional) - long-description: the long description of the option. Currently only - displayed in generated man pages (optional) - no-flag: if non-empty, this field is ignored as an option (optional) - - optional: if non-empty, makes the argument of the option optional. When an - argument is optional it can only be specified using - --option=argument (optional) - optional-value: the value of an optional option when the option occurs - without an argument. This tag can be specified multiple - times in the case of maps or slices (optional) - default: the default value of an option. This tag can be specified - multiple times in the case of slices or maps (optional) - default-mask: when specified, this value will be displayed in the help - instead of the actual default value. This is useful - mostly for hiding otherwise sensitive information from - showing up in the help. If default-mask takes the special - value "-", then no default value will be shown at all - (optional) - env: the default value of the option is overridden from the - specified environment variable, if one has been defined. - (optional) - env-delim: the 'env' default value from environment is split into - multiple values with the given delimiter string, use with - slices and maps (optional) - value-name: the name of the argument value (to be shown in the help) - (optional) - choice: limits the values for an option to a set of values. - Repeat this tag once for each allowable value. - e.g. `long:"animal" choice:"cat" choice:"dog"` - hidden: if non-empty, the option is not visible in the help or man page. - - base: a base (radix) used to convert strings to integer values, the - default base is 10 (i.e. decimal) (optional) - - ini-name: the explicit ini option name (optional) - no-ini: if non-empty this field is ignored as an ini option - (optional) - - group: when specified on a struct field, makes the struct - field a separate group with the given name (optional) - namespace: when specified on a group struct field, the namespace - gets prepended to every option's long name and - subgroup's namespace of this group, separated by - the parser's namespace delimiter (optional) - env-namespace: when specified on a group struct field, the env-namespace - gets prepended to every option's env key and - subgroup's env-namespace of this group, separated by - the parser's env-namespace delimiter (optional) - command: when specified on a struct field, makes the struct - field a (sub)command with the given name (optional) - subcommands-optional: when specified on a command struct field, makes - any subcommands of that command optional (optional) - alias: when specified on a command struct field, adds the - specified name as an alias for the command. Can be - be specified multiple times to add more than one - alias (optional) - positional-args: when specified on a field with a struct type, - uses the fields of that struct to parse remaining - positional command line arguments into (in order - of the fields). If a field has a slice type, - then all remaining arguments will be added to it. - Positional arguments are optional by default, - unless the "required" tag is specified together - with the "positional-args" tag. The "required" tag - can also be set on the individual rest argument - fields, to require only the first N positional - arguments. If the "required" tag is set on the - rest arguments slice, then its value determines - the minimum amount of rest arguments that needs to - be provided (e.g. `required:"2"`) (optional) - positional-arg-name: used on a field in a positional argument struct; name - of the positional argument placeholder to be shown in - the help (optional) + short: the short name of the option (single character) + long: the long name of the option + required: if non empty, makes the option required to appear on the command + line. If a required option is not present, the parser will + return ErrRequired (optional) + description: the description of the option (optional) + long-description: the long description of the option. Currently only + displayed in generated man pages (optional) + no-flag: if non-empty, this field is ignored as an option (optional) + + optional: if non-empty, makes the argument of the option optional. When an + argument is optional it can only be specified using + --option=argument (optional) + optional-value: the value of an optional option when the option occurs + without an argument. This tag can be specified multiple + times in the case of maps or slices (optional) + default: the default value of an option. This tag can be specified + multiple times in the case of slices or maps (optional) + default-mask: when specified, this value will be displayed in the help + instead of the actual default value. This is useful + mostly for hiding otherwise sensitive information from + showing up in the help. If default-mask takes the special + value "-", then no default value will be shown at all + (optional) + env: the default value of the option is overridden from the + specified environment variable, if one has been defined. + (optional) + env-delim: the 'env' default value from environment is split into + multiple values with the given delimiter string, use with + slices and maps (optional) + value-name: the name of the argument value (to be shown in the help) + (optional) + choice: limits the values for an option to a set of values. + Repeat this tag once for each allowable value. + e.g. `long:"animal" choice:"cat" choice:"dog"` + hidden: if non-empty, the option is not visible in the help or man page. + + base: a base (radix) used to convert strings to integer values, the + default base is 10 (i.e. decimal) (optional) + + ini-name: the explicit ini option name (optional) + no-ini: if non-empty this field is ignored as an ini option + (optional) + + group: when specified on a struct field, makes the struct + field a separate group with the given name (optional) + namespace: when specified on a group struct field, the namespace + gets prepended to every option's long name and + subgroup's namespace of this group, separated by + the parser's namespace delimiter (optional) + env-namespace: when specified on a group struct field, the env-namespace + gets prepended to every option's env key and + subgroup's env-namespace of this group, separated by + the parser's env-namespace delimiter (optional) + command: when specified on a struct field, makes the struct + field a (sub)command with the given name (optional) + subcommands-optional: when specified on a command struct field, makes + any subcommands of that command optional (optional) + alias: when specified on a command struct field, adds the + specified name as an alias for the command. Can be + be specified multiple times to add more than one + alias (optional) + positional-args: when specified on a field with a struct type, + uses the fields of that struct to parse remaining + positional command line arguments into (in order + of the fields). If a field has a slice type, + then all remaining arguments will be added to it. + Positional arguments are optional by default, + unless the "required" tag is specified together + with the "positional-args" tag. The "required" tag + can also be set on the individual rest argument + fields, to require only the first N positional + arguments. If the "required" tag is set on the + rest arguments slice, then its value determines + the minimum amount of rest arguments that needs to + be provided (e.g. `required:"2"`) (optional) + positional-arg-name: used on a field in a positional argument struct; name + of the positional argument placeholder to be shown in + the help (optional) Either the `short:` tag or the `long:` must be specified to make the field eligible as an option. - -Option groups +# Option groups Option groups are a simple way to semantically separate your options. All options in a particular group are shown together in the help under the name @@ -169,14 +166,12 @@ precisely and emphasize the options affiliation to their group. There are currently three ways to specify option groups. - 1. Use NewNamedParser specifying the various option groups. - 2. Use AddGroup to add a group to an existing parser. - 3. Add a struct field to the top-level options annotated with the - group:"group-name" tag. + 1. Use NewNamedParser specifying the various option groups. + 2. Use AddGroup to add a group to an existing parser. + 3. Add a struct field to the top-level options annotated with the + group:"group-name" tag. - - -Commands +# Commands The flags package also has basic support for commands. Commands are often used in monolithic applications that support various commands or actions. @@ -186,9 +181,9 @@ application. There are currently two ways to specify a command. - 1. Use AddCommand on an existing parser. - 2. Add a struct field to your options struct annotated with the - command:"command-name" tag. + 1. Use AddCommand on an existing parser. + 2. Add a struct field to your options struct annotated with the + command:"command-name" tag. The most common, idiomatic way to implement commands is to define a global parser instance and implement each command in a separate file. These @@ -204,15 +199,14 @@ command has been specified on the command line, in addition to the options of all the parent commands. I.e. considering a -v flag on the parser and an add command, the following are equivalent: - ./app -v add - ./app add -v + ./app -v add + ./app add -v However, if the -v flag is defined on the add command, then the first of the two examples above would fail since the -v flag is not defined before the add command. - -Completion +# Completion go-flags has builtin support to provide bash completion of flags, commands and argument values. To use completion, the binary which uses go-flags @@ -226,7 +220,7 @@ by replacing the argument parsing routine with the completion routine which outputs completions for the passed arguments. The basic invocation to complete a set of arguments is therefore: - GO_FLAGS_COMPLETION=1 ./completion-example arg1 arg2 arg3 + GO_FLAGS_COMPLETION=1 ./completion-example arg1 arg2 arg3 where `completion-example` is the binary, `arg1` and `arg2` are the current arguments, and `arg3` (the last argument) is the argument @@ -237,20 +231,20 @@ are more than 1 completion items. To use this with bash completion, a simple file can be written which calls the binary which supports go-flags completion: - _completion_example() { - # All arguments except the first one - args=("${COMP_WORDS[@]:1:$COMP_CWORD}") + _completion_example() { + # All arguments except the first one + args=("${COMP_WORDS[@]:1:$COMP_CWORD}") - # Only split on newlines - local IFS=$'\n' + # Only split on newlines + local IFS=$'\n' - # Call completion (note that the first element of COMP_WORDS is - # the executable itself) - COMPREPLY=($(GO_FLAGS_COMPLETION=1 ${COMP_WORDS[0]} "${args[@]}")) - return 0 - } + # Call completion (note that the first element of COMP_WORDS is + # the executable itself) + COMPREPLY=($(GO_FLAGS_COMPLETION=1 ${COMP_WORDS[0]} "${args[@]}")) + return 0 + } - complete -F _completion_example completion-example + complete -F _completion_example completion-example Completion requires the parser option PassDoubleDash and is therefore enforced if the environment variable GO_FLAGS_COMPLETION is set. diff --git a/vendor/github.com/jessevdk/go-flags/help.go b/vendor/github.com/jessevdk/go-flags/help.go index 068fce1525..8fd324443a 100644 --- a/vendor/github.com/jessevdk/go-flags/help.go +++ b/vendor/github.com/jessevdk/go-flags/help.go @@ -72,15 +72,15 @@ func (p *Parser) getAlignmentInfo() alignmentInfo { var prevcmd *Command p.eachActiveGroup(func(c *Command, grp *Group) { - if !grp.showInHelp() { - return - } if c != prevcmd { for _, arg := range c.args { ret.updateLen(arg.Name, c != p.Command) } + prevcmd = c + } + if !grp.showInHelp() { + return } - for _, info := range grp.options { if !info.showInHelp() { continue @@ -334,7 +334,11 @@ func (p *Parser) WriteHelp(writer io.Writer) { } if !allcmd.ArgsRequired { - fmt.Fprintf(wr, "[%s]", name) + if arg.Required > 0 { + fmt.Fprintf(wr, "%s", name) + } else { + fmt.Fprintf(wr, "[%s]", name) + } } else { fmt.Fprintf(wr, "%s", name) } diff --git a/vendor/github.com/jessevdk/go-flags/ini.go b/vendor/github.com/jessevdk/go-flags/ini.go index 60b36c79c4..565595e5f1 100644 --- a/vendor/github.com/jessevdk/go-flags/ini.go +++ b/vendor/github.com/jessevdk/go-flags/ini.go @@ -113,18 +113,18 @@ func (i *IniParser) ParseFile(filename string) error { // // The format of the ini file is as follows: // -// [Option group name] -// option = value +// [Option group name] +// option = value // // Each section in the ini file represents an option group or command in the // flags parser. The default flags parser option group (i.e. when using // flags.Parse) is named 'Application Options'. The ini option name is matched // in the following order: // -// 1. Compared to the ini-name tag on the option struct field (if present) -// 2. Compared to the struct field name -// 3. Compared to the option long name (if present) -// 4. Compared to the option short name (if present) +// 1. Compared to the ini-name tag on the option struct field (if present) +// 2. Compared to the struct field name +// 3. Compared to the option long name (if present) +// 4. Compared to the option short name (if present) // // Sections for nested groups and commands can be addressed using a dot `.' // namespacing notation (i.e [subcommand.Options]). Group section names are @@ -584,7 +584,7 @@ func (i *IniParser) parse(ini *ini) error { if i.ParseAsDefaults { err = opt.setDefault(pval) } else { - err = opt.set(pval) + err = opt.Set(pval) } if err != nil { diff --git a/vendor/github.com/jessevdk/go-flags/option.go b/vendor/github.com/jessevdk/go-flags/option.go index f6d6941813..257996a792 100644 --- a/vendor/github.com/jessevdk/go-flags/option.go +++ b/vendor/github.com/jessevdk/go-flags/option.go @@ -239,7 +239,7 @@ func (option *Option) IsSetDefault() bool { // Set the value of an option to the specified value. An error will be returned // if the specified value could not be converted to the corresponding option // value type. -func (option *Option) set(value *string) error { +func (option *Option) Set(value *string) error { kind := option.value.Type().Kind() if (kind == reflect.Map || kind == reflect.Slice) && option.clearReferenceBeforeSet { @@ -287,7 +287,7 @@ func (option *Option) setDefault(value *string) error { return nil } - if err := option.set(value); err != nil { + if err := option.Set(value); err != nil { return err } diff --git a/vendor/github.com/jessevdk/go-flags/optstyle_other.go b/vendor/github.com/jessevdk/go-flags/optstyle_other.go index 56dfdae128..f84b69746d 100644 --- a/vendor/github.com/jessevdk/go-flags/optstyle_other.go +++ b/vendor/github.com/jessevdk/go-flags/optstyle_other.go @@ -1,3 +1,4 @@ +//go:build !windows || forceposix // +build !windows forceposix package flags diff --git a/vendor/github.com/jessevdk/go-flags/optstyle_windows.go b/vendor/github.com/jessevdk/go-flags/optstyle_windows.go index f3f28aeeff..e80290420a 100644 --- a/vendor/github.com/jessevdk/go-flags/optstyle_windows.go +++ b/vendor/github.com/jessevdk/go-flags/optstyle_windows.go @@ -1,3 +1,4 @@ +//go:build !forceposix // +build !forceposix package flags diff --git a/vendor/github.com/jessevdk/go-flags/parser.go b/vendor/github.com/jessevdk/go-flags/parser.go index 3fc3f7ba1c..939dd7bf49 100644 --- a/vendor/github.com/jessevdk/go-flags/parser.go +++ b/vendor/github.com/jessevdk/go-flags/parser.go @@ -113,6 +113,10 @@ const ( // POSIX processing. PassAfterNonOption + // AllowBoolValues allows a user to assign true/false to a boolean value + // rather than raising an error stating it cannot have an argument. + AllowBoolValues + // Default is a convenient default set of options which should cover // most of the uses of the flags package. Default = HelpFlag | PrintErrors | PassDoubleDash @@ -252,7 +256,7 @@ func (p *Parser) ParseArgs(args []string) ([]string, error) { } if !argumentIsOption(arg) { - if (p.Options&PassAfterNonOption) != None && s.lookup.commands[arg] == nil { + if ((p.Options&PassAfterNonOption) != None || s.command.PassAfterNonOption) && s.lookup.commands[arg] == nil { // If PassAfterNonOption is set then all remaining arguments // are considered positional if err = s.addArgs(s.arg); err != nil { @@ -521,11 +525,10 @@ func (p *parseState) estimateCommand() error { func (p *Parser) parseOption(s *parseState, name string, option *Option, canarg bool, argument *string) (err error) { if !option.canArgument() { - if argument != nil { + if argument != nil && (p.Options&AllowBoolValues) == None { return newErrorf(ErrNoArgumentForBool, "bool flag `%s' cannot have an argument", option) } - - err = option.set(nil) + err = option.Set(argument) } else if argument != nil || (canarg && !s.eof()) { var arg string @@ -546,13 +549,13 @@ func (p *Parser) parseOption(s *parseState, name string, option *Option, canarg } if err == nil { - err = option.set(&arg) + err = option.Set(&arg) } } else if option.OptionalArgument { option.empty() for _, v := range option.OptionalValue { - err = option.set(&v) + err = option.Set(&v) if err != nil { break diff --git a/vendor/github.com/jessevdk/go-flags/termsize.go b/vendor/github.com/jessevdk/go-flags/termsize.go index 829e477add..7bcf66feeb 100644 --- a/vendor/github.com/jessevdk/go-flags/termsize.go +++ b/vendor/github.com/jessevdk/go-flags/termsize.go @@ -1,4 +1,5 @@ -// +build !windows,!plan9,!appengine,!wasm +//go:build !windows && !plan9 && !appengine && !wasm && !aix +// +build !windows,!plan9,!appengine,!wasm,!aix package flags diff --git a/vendor/github.com/jessevdk/go-flags/termsize_nosysioctl.go b/vendor/github.com/jessevdk/go-flags/termsize_nosysioctl.go index c1ff18673b..d839220aa5 100644 --- a/vendor/github.com/jessevdk/go-flags/termsize_nosysioctl.go +++ b/vendor/github.com/jessevdk/go-flags/termsize_nosysioctl.go @@ -1,4 +1,5 @@ -// +build plan9 appengine wasm +//go:build plan9 || appengine || wasm || aix +// +build plan9 appengine wasm aix package flags diff --git a/vendor/github.com/jessevdk/go-flags/termsize_windows.go b/vendor/github.com/jessevdk/go-flags/termsize_windows.go index 5c0fa6ba27..189a1b3faf 100644 --- a/vendor/github.com/jessevdk/go-flags/termsize_windows.go +++ b/vendor/github.com/jessevdk/go-flags/termsize_windows.go @@ -1,3 +1,4 @@ +//go:build windows // +build windows package flags diff --git a/vendor/github.com/prometheus/alertmanager/NOTICE b/vendor/github.com/prometheus/alertmanager/NOTICE index f5d0bbb421..e0dd624377 100644 --- a/vendor/github.com/prometheus/alertmanager/NOTICE +++ b/vendor/github.com/prometheus/alertmanager/NOTICE @@ -11,8 +11,3 @@ Bootstrap http://getbootstrap.com Copyright 2011-2014 Twitter, Inc. Licensed under the MIT License - -bootstrap-datetimepicker.js -http://www.eyecon.ro/bootstrap-datepicker -Copyright 2012 Stefan Petre -Licensed under the Apache License, Version 2.0 diff --git a/vendor/github.com/prometheus/alertmanager/api/api.go b/vendor/github.com/prometheus/alertmanager/api/api.go index 2e1e1ea425..6839d2d282 100644 --- a/vendor/github.com/prometheus/alertmanager/api/api.go +++ b/vendor/github.com/prometheus/alertmanager/api/api.go @@ -16,13 +16,14 @@ package api import ( "errors" "fmt" + "log/slog" "net/http" "runtime" "time" - "github.com/go-kit/log" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/common/model" + "github.com/prometheus/common/promslog" "github.com/prometheus/common/route" apiv2 "github.com/prometheus/alertmanager/api/v2" @@ -45,16 +46,20 @@ type API struct { inFlightSem chan struct{} } -// Options for the creation of an API object. Alerts, Silences, and StatusFunc -// are mandatory to set. The zero value for everything else is a safe default. +// Options for the creation of an API object. Alerts, Silences, AlertStatusFunc +// and GroupMutedFunc are mandatory. The zero value for everything else is a safe +// default. type Options struct { // Alerts to be used by the API. Mandatory. Alerts provider.Alerts // Silences to be used by the API. Mandatory. Silences *silence.Silences - // StatusFunc is used be the API to retrieve the AlertStatus of an + // AlertStatusFunc is used be the API to retrieve the AlertStatus of an // alert. Mandatory. - StatusFunc func(model.Fingerprint) types.AlertStatus + AlertStatusFunc func(model.Fingerprint) types.AlertStatus + // GroupMutedFunc is used be the API to know if an alert is muted. + // Mandatory. + GroupMutedFunc func(routeID, groupKey string) ([]string, bool) // Peer from the gossip cluster. If nil, no clustering will be used. Peer cluster.ClusterPeer // Timeout for all HTTP connections. The zero value (and negative @@ -66,7 +71,7 @@ type Options struct { // the concurrency limit. Concurrency int // Logger is used for logging, if nil, no logging will happen. - Logger log.Logger + Logger *slog.Logger // Registry is used to register Prometheus metrics. If nil, no metrics // registration will happen. Registry prometheus.Registerer @@ -83,8 +88,11 @@ func (o Options) validate() error { if o.Silences == nil { return errors.New("mandatory field Silences not set") } - if o.StatusFunc == nil { - return errors.New("mandatory field StatusFunc not set") + if o.AlertStatusFunc == nil { + return errors.New("mandatory field AlertStatusFunc not set") + } + if o.GroupMutedFunc == nil { + return errors.New("mandatory field GroupMutedFunc not set") } if o.GroupFunc == nil { return errors.New("mandatory field GroupFunc not set") @@ -100,7 +108,7 @@ func New(opts Options) (*API, error) { } l := opts.Logger if l == nil { - l = log.NewNopLogger() + l = promslog.NewNopLogger() } concurrency := opts.Concurrency if concurrency < 1 { @@ -113,10 +121,11 @@ func New(opts Options) (*API, error) { v2, err := apiv2.NewAPI( opts.Alerts, opts.GroupFunc, - opts.StatusFunc, + opts.AlertStatusFunc, + opts.GroupMutedFunc, opts.Silences, opts.Peer, - log.With(l, "version", "v2"), + l.With("version", "v2"), opts.Registry, ) if err != nil { @@ -145,7 +154,7 @@ func New(opts Options) (*API, error) { } return &API{ - deprecationRouter: NewV1DeprecationRouter(log.With(l, "version", "v1")), + deprecationRouter: NewV1DeprecationRouter(l.With("version", "v1")), v2: v2, requestsInFlight: requestsInFlight, concurrencyLimitExceeded: concurrencyLimitExceeded, @@ -162,7 +171,7 @@ func New(opts Options) (*API, error) { // true for the concurrency limit, with the exception that it is only applied to // GET requests. func (api *API) Register(r *route.Router, routePrefix string) *http.ServeMux { - // TODO(gotjosh) API V1 was removed as of version 0.28, when we reach 1.0.0 we should removed these deprecation warnings. + // TODO(gotjosh) API V1 was removed as of version 0.27, when we reach 1.0.0 we should removed these deprecation warnings. api.deprecationRouter.Register(r.WithPrefix("/api/v1")) mux := http.NewServeMux() diff --git a/vendor/github.com/prometheus/alertmanager/api/metrics/metrics.go b/vendor/github.com/prometheus/alertmanager/api/metrics/metrics.go index ea45acc2ee..439010166d 100644 --- a/vendor/github.com/prometheus/alertmanager/api/metrics/metrics.go +++ b/vendor/github.com/prometheus/alertmanager/api/metrics/metrics.go @@ -23,7 +23,7 @@ type Alerts struct { } // NewAlerts returns an *Alerts struct for the given API version. -// Since v1 was deprecated in 0.28, v2 is now hardcoded. +// Since v1 was deprecated in 0.27, v2 is now hardcoded. func NewAlerts(r prometheus.Registerer) *Alerts { numReceivedAlerts := prometheus.NewCounterVec(prometheus.CounterOpts{ Name: "alertmanager_alerts_received_total", diff --git a/vendor/github.com/prometheus/alertmanager/api/v1_deprecation_router.go b/vendor/github.com/prometheus/alertmanager/api/v1_deprecation_router.go index 3ebbbd076f..582e06ab41 100644 --- a/vendor/github.com/prometheus/alertmanager/api/v1_deprecation_router.go +++ b/vendor/github.com/prometheus/alertmanager/api/v1_deprecation_router.go @@ -8,26 +8,24 @@ // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific l package api import ( "encoding/json" + "log/slog" "net/http" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/prometheus/common/route" ) // V1DeprecationRouter is the router to signal v1 users that the API v1 is now removed. type V1DeprecationRouter struct { - logger log.Logger + logger *slog.Logger } // NewV1DeprecationRouter returns a new V1DeprecationRouter. -func NewV1DeprecationRouter(l log.Logger) *V1DeprecationRouter { +func NewV1DeprecationRouter(l *slog.Logger) *V1DeprecationRouter { return &V1DeprecationRouter{ logger: l, } @@ -48,20 +46,20 @@ func (dr *V1DeprecationRouter) Register(r *route.Router) { } func (dr *V1DeprecationRouter) deprecationHandler(w http.ResponseWriter, req *http.Request) { - level.Warn(dr.logger).Log("msg", "v1 API received a request on a removed endpoint", "path", req.URL.Path, "method", req.Method) + dr.logger.Warn("v1 API received a request on a removed endpoint", "path", req.URL.Path, "method", req.Method) resp := struct { Status string `json:"status"` Error string `json:"error"` }{ "deprecated", - "The Alertmanager v1 API was deprecated in version 0.16.0 and is removed as of version 0.28.0 - please use the equivalent route in the v2 API", + "The Alertmanager v1 API was deprecated in version 0.16.0 and is removed as of version 0.27.0 - please use the equivalent route in the v2 API", } w.Header().Set("Content-Type", "application/json") w.WriteHeader(410) if err := json.NewEncoder(w).Encode(resp); err != nil { - level.Error(dr.logger).Log("msg", "failed to write response", "err", err) + dr.logger.Error("failed to write response", "err", err) } } diff --git a/vendor/github.com/prometheus/alertmanager/api/v2/api.go b/vendor/github.com/prometheus/alertmanager/api/v2/api.go index b4f57e75e2..0e29375d40 100644 --- a/vendor/github.com/prometheus/alertmanager/api/v2/api.go +++ b/vendor/github.com/prometheus/alertmanager/api/v2/api.go @@ -16,14 +16,13 @@ package v2 import ( "errors" "fmt" + "log/slog" "net/http" "regexp" "sort" "sync" "time" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/go-openapi/analysis" "github.com/go-openapi/loads" "github.com/go-openapi/runtime/middleware" @@ -45,7 +44,7 @@ import ( "github.com/prometheus/alertmanager/cluster" "github.com/prometheus/alertmanager/config" "github.com/prometheus/alertmanager/dispatch" - "github.com/prometheus/alertmanager/matchers/compat" + "github.com/prometheus/alertmanager/matcher/compat" "github.com/prometheus/alertmanager/pkg/labels" "github.com/prometheus/alertmanager/provider" "github.com/prometheus/alertmanager/silence" @@ -53,13 +52,14 @@ import ( "github.com/prometheus/alertmanager/types" ) -// API represents an Alertmanager API v2 +// API represents an Alertmanager API v2. type API struct { peer cluster.ClusterPeer silences *silence.Silences alerts provider.Alerts alertGroups groupsFn getAlertStatus getAlertStatusFn + groupMutedFunc groupMutedFunc uptime time.Time // mtx protects alertmanagerConfig, setAlertStatus and route. @@ -70,7 +70,7 @@ type API struct { route *dispatch.Route setAlertStatus setAlertStatusFn - logger log.Logger + logger *slog.Logger m *metrics.Alerts Handler http.Handler @@ -78,24 +78,27 @@ type API struct { type ( groupsFn func(func(*dispatch.Route) bool, func(*types.Alert, time.Time) bool) (dispatch.AlertGroups, map[prometheus_model.Fingerprint][]string) + groupMutedFunc func(routeID, groupKey string) ([]string, bool) getAlertStatusFn func(prometheus_model.Fingerprint) types.AlertStatus setAlertStatusFn func(prometheus_model.LabelSet) ) -// NewAPI returns a new Alertmanager API v2 +// NewAPI returns a new Alertmanager API v2. func NewAPI( alerts provider.Alerts, gf groupsFn, - sf getAlertStatusFn, + asf getAlertStatusFn, + gmf groupMutedFunc, silences *silence.Silences, peer cluster.ClusterPeer, - l log.Logger, + l *slog.Logger, r prometheus.Registerer, ) (*API, error) { api := API{ alerts: alerts, - getAlertStatus: sf, + getAlertStatus: asf, alertGroups: gf, + groupMutedFunc: gmf, peer: peer, silences: silences, logger: l, @@ -150,8 +153,8 @@ func setResponseHeaders(h http.Handler) http.Handler { }) } -func (api *API) requestLogger(req *http.Request) log.Logger { - return log.With(api.logger, "path", req.URL.Path, "method", req.Method) +func (api *API) requestLogger(req *http.Request) *slog.Logger { + return api.logger.With("path", req.URL.Path, "method", req.Method) } // Update sets the API struct members that may change between reloads of alertmanager. @@ -245,14 +248,14 @@ func (api *API) getAlertsHandler(params alert_ops.GetAlertsParams) middleware.Re matchers, err := parseFilter(params.Filter) if err != nil { - level.Debug(logger).Log("msg", "Failed to parse matchers", "err", err) + logger.Debug("Failed to parse matchers", "err", err) return alertgroup_ops.NewGetAlertGroupsBadRequest().WithPayload(err.Error()) } if params.Receiver != nil { receiverFilter, err = regexp.Compile("^(?:" + *params.Receiver + ")$") if err != nil { - level.Debug(logger).Log("msg", "Failed to compile receiver regex", "err", err) + logger.Debug("Failed to compile receiver regex", "err", err) return alert_ops. NewGetAlertsBadRequest(). WithPayload( @@ -290,14 +293,14 @@ func (api *API) getAlertsHandler(params alert_ops.GetAlertsParams) middleware.Re continue } - alert := AlertToOpenAPIAlert(a, api.getAlertStatus(a.Fingerprint()), receivers) + alert := AlertToOpenAPIAlert(a, api.getAlertStatus(a.Fingerprint()), receivers, nil) res = append(res, alert) } api.mtx.RUnlock() if err != nil { - level.Error(logger).Log("msg", "Failed to get alerts", "err", err) + logger.Error("Failed to get alerts", "err", err) return alert_ops.NewGetAlertsInternalServerError().WithPayload(err.Error()) } sort.Slice(res, func(i, j int) bool { @@ -357,12 +360,12 @@ func (api *API) postAlertsHandler(params alert_ops.PostAlertsParams) middleware. validAlerts = append(validAlerts, a) } if err := api.alerts.Put(validAlerts...); err != nil { - level.Error(logger).Log("msg", "Failed to create alerts", "err", err) + logger.Error("Failed to create alerts", "err", err) return alert_ops.NewPostAlertsInternalServerError().WithPayload(err.Error()) } if validationErrs.Len() > 0 { - level.Error(logger).Log("msg", "Failed to validate alerts", "err", validationErrs.Error()) + logger.Error("Failed to validate alerts", "err", validationErrs.Error()) return alert_ops.NewPostAlertsBadRequest().WithPayload(validationErrs.Error()) } @@ -374,7 +377,7 @@ func (api *API) getAlertGroupsHandler(params alertgroup_ops.GetAlertGroupsParams matchers, err := parseFilter(params.Filter) if err != nil { - level.Debug(logger).Log("msg", "Failed to parse matchers", "err", err) + logger.Debug("Failed to parse matchers", "err", err) return alertgroup_ops.NewGetAlertGroupsBadRequest().WithPayload(err.Error()) } @@ -382,7 +385,7 @@ func (api *API) getAlertGroupsHandler(params alertgroup_ops.GetAlertGroupsParams if params.Receiver != nil { receiverFilter, err = regexp.Compile("^(?:" + *params.Receiver + ")$") if err != nil { - level.Error(logger).Log("msg", "Failed to compile receiver regex", "err", err) + logger.Error("Failed to compile receiver regex", "err", err) return alertgroup_ops. NewGetAlertGroupsBadRequest(). WithPayload( @@ -407,6 +410,11 @@ func (api *API) getAlertGroupsHandler(params alertgroup_ops.GetAlertGroupsParams res := make(open_api_models.AlertGroups, 0, len(alertGroups)) for _, alertGroup := range alertGroups { + mutedBy, isMuted := api.groupMutedFunc(alertGroup.RouteID, alertGroup.GroupKey) + if !*params.Muted && isMuted { + continue + } + ag := &open_api_models.AlertGroup{ Receiver: &open_api_models.Receiver{Name: &alertGroup.Receiver}, Labels: ModelLabelSetToAPILabelSet(alertGroup.Labels), @@ -417,7 +425,7 @@ func (api *API) getAlertGroupsHandler(params alertgroup_ops.GetAlertGroupsParams fp := alert.Fingerprint() receivers := allReceivers[fp] status := api.getAlertStatus(fp) - apiAlert := AlertToOpenAPIAlert(alert, status, receivers) + apiAlert := AlertToOpenAPIAlert(alert, status, receivers, mutedBy) ag.Alerts = append(ag.Alerts, apiAlert) } res = append(res, ag) @@ -509,13 +517,13 @@ func (api *API) getSilencesHandler(params silence_ops.GetSilencesParams) middlew matchers, err := parseFilter(params.Filter) if err != nil { - level.Debug(logger).Log("msg", "Failed to parse matchers", "err", err) + logger.Debug("Failed to parse matchers", "err", err) return silence_ops.NewGetSilencesBadRequest().WithPayload(err.Error()) } psils, _, err := api.silences.Query() if err != nil { - level.Error(logger).Log("msg", "Failed to get silences", "err", err) + logger.Error("Failed to get silences", "err", err) return silence_ops.NewGetSilencesInternalServerError().WithPayload(err.Error()) } @@ -526,7 +534,7 @@ func (api *API) getSilencesHandler(params silence_ops.GetSilencesParams) middlew } silence, err := GettableSilenceFromProto(ps) if err != nil { - level.Error(logger).Log("msg", "Failed to unmarshal silence from proto", "err", err) + logger.Error("Failed to unmarshal silence from proto", "err", err) return silence_ops.NewGetSilencesInternalServerError().WithPayload(err.Error()) } sils = append(sils, &silence) @@ -545,9 +553,9 @@ var silenceStateOrder = map[types.SilenceState]int{ // SortSilences sorts first according to the state "active, pending, expired" // then by end time or start time depending on the state. -// active silences should show the next to expire first +// Active silences should show the next to expire first // pending silences are ordered based on which one starts next -// expired are ordered based on which one expired most recently +// expired are ordered based on which one expired most recently. func SortSilences(sils open_api_models.GettableSilences) { sort.Slice(sils, func(i, j int) bool { state1 := types.SilenceState(*sils[i].Status.State) @@ -605,18 +613,18 @@ func (api *API) getSilenceHandler(params silence_ops.GetSilenceParams) middlewar sils, _, err := api.silences.Query(silence.QIDs(params.SilenceID.String())) if err != nil { - level.Error(logger).Log("msg", "Failed to get silence by id", "err", err, "id", params.SilenceID.String()) + logger.Error("Failed to get silence by id", "err", err, "id", params.SilenceID.String()) return silence_ops.NewGetSilenceInternalServerError().WithPayload(err.Error()) } if len(sils) == 0 { - level.Error(logger).Log("msg", "Failed to find silence", "err", err, "id", params.SilenceID.String()) + logger.Error("Failed to find silence", "err", err, "id", params.SilenceID.String()) return silence_ops.NewGetSilenceNotFound() } sil, err := GettableSilenceFromProto(sils[0]) if err != nil { - level.Error(logger).Log("msg", "Failed to convert unmarshal from proto", "err", err) + logger.Error("Failed to convert unmarshal from proto", "err", err) return silence_ops.NewGetSilenceInternalServerError().WithPayload(err.Error()) } @@ -628,7 +636,7 @@ func (api *API) deleteSilenceHandler(params silence_ops.DeleteSilenceParams) mid sid := params.SilenceID.String() if err := api.silences.Expire(sid); err != nil { - level.Error(logger).Log("msg", "Failed to expire silence", "err", err) + logger.Error("Failed to expire silence", "err", err) if errors.Is(err, silence.ErrNotFound) { return silence_ops.NewDeleteSilenceNotFound() } @@ -642,7 +650,7 @@ func (api *API) postSilencesHandler(params silence_ops.PostSilencesParams) middl sil, err := PostableSilenceToProto(params.Silence) if err != nil { - level.Error(logger).Log("msg", "Failed to marshal silence to proto", "err", err) + logger.Error("Failed to marshal silence to proto", "err", err) return silence_ops.NewPostSilencesBadRequest().WithPayload( fmt.Sprintf("failed to convert API silence to internal silence: %v", err.Error()), ) @@ -650,19 +658,18 @@ func (api *API) postSilencesHandler(params silence_ops.PostSilencesParams) middl if sil.StartsAt.After(sil.EndsAt) || sil.StartsAt.Equal(sil.EndsAt) { msg := "Failed to create silence: start time must be before end time" - level.Error(logger).Log("msg", msg, "starts_at", sil.StartsAt, "ends_at", sil.EndsAt) + logger.Error(msg, "starts_at", sil.StartsAt, "ends_at", sil.EndsAt) return silence_ops.NewPostSilencesBadRequest().WithPayload(msg) } if sil.EndsAt.Before(time.Now()) { msg := "Failed to create silence: end time can't be in the past" - level.Error(logger).Log("msg", msg, "ends_at", sil.EndsAt) + logger.Error(msg, "ends_at", sil.EndsAt) return silence_ops.NewPostSilencesBadRequest().WithPayload(msg) } - sid, err := api.silences.Set(sil) - if err != nil { - level.Error(logger).Log("msg", "Failed to create silence", "err", err) + if err = api.silences.Set(sil); err != nil { + logger.Error("Failed to create silence", "err", err) if errors.Is(err, silence.ErrNotFound) { return silence_ops.NewPostSilencesNotFound().WithPayload(err.Error()) } @@ -670,7 +677,7 @@ func (api *API) postSilencesHandler(params silence_ops.PostSilencesParams) middl } return silence_ops.NewPostSilencesOK().WithPayload(&silence_ops.PostSilencesOKBody{ - SilenceID: sid, + SilenceID: sil.Id, }) } diff --git a/vendor/github.com/prometheus/alertmanager/api/v2/client/alertgroup/get_alert_groups_parameters.go b/vendor/github.com/prometheus/alertmanager/api/v2/client/alertgroup/get_alert_groups_parameters.go index 1196ade8f1..81020fcf67 100644 --- a/vendor/github.com/prometheus/alertmanager/api/v2/client/alertgroup/get_alert_groups_parameters.go +++ b/vendor/github.com/prometheus/alertmanager/api/v2/client/alertgroup/get_alert_groups_parameters.go @@ -98,6 +98,14 @@ type GetAlertGroupsParams struct { */ Inhibited *bool + /* Muted. + + Show muted alerts + + Default: true + */ + Muted *bool + /* Receiver. A regex matching receivers to filter alerts by @@ -134,12 +142,15 @@ func (o *GetAlertGroupsParams) SetDefaults() { inhibitedDefault = bool(true) + mutedDefault = bool(true) + silencedDefault = bool(true) ) val := GetAlertGroupsParams{ Active: &activeDefault, Inhibited: &inhibitedDefault, + Muted: &mutedDefault, Silenced: &silencedDefault, } @@ -215,6 +226,17 @@ func (o *GetAlertGroupsParams) SetInhibited(inhibited *bool) { o.Inhibited = inhibited } +// WithMuted adds the muted to the get alert groups params +func (o *GetAlertGroupsParams) WithMuted(muted *bool) *GetAlertGroupsParams { + o.SetMuted(muted) + return o +} + +// SetMuted adds the muted to the get alert groups params +func (o *GetAlertGroupsParams) SetMuted(muted *bool) { + o.Muted = muted +} + // WithReceiver adds the receiver to the get alert groups params func (o *GetAlertGroupsParams) WithReceiver(receiver *string) *GetAlertGroupsParams { o.SetReceiver(receiver) @@ -290,6 +312,23 @@ func (o *GetAlertGroupsParams) WriteToRequest(r runtime.ClientRequest, reg strfm } } + if o.Muted != nil { + + // query param muted + var qrMuted bool + + if o.Muted != nil { + qrMuted = *o.Muted + } + qMuted := swag.FormatBool(qrMuted) + if qMuted != "" { + + if err := r.SetQueryParam("muted", qMuted); err != nil { + return err + } + } + } + if o.Receiver != nil { // query param receiver diff --git a/vendor/github.com/prometheus/alertmanager/api/v2/compat.go b/vendor/github.com/prometheus/alertmanager/api/v2/compat.go index 112bfc4f2f..8954f2f752 100644 --- a/vendor/github.com/prometheus/alertmanager/api/v2/compat.go +++ b/vendor/github.com/prometheus/alertmanager/api/v2/compat.go @@ -117,7 +117,7 @@ func PostableSilenceToProto(s *open_api_models.PostableSilence) (*silencepb.Sile } // AlertToOpenAPIAlert converts internal alerts, alert types, and receivers to *open_api_models.GettableAlert. -func AlertToOpenAPIAlert(alert *types.Alert, status types.AlertStatus, receivers []string) *open_api_models.GettableAlert { +func AlertToOpenAPIAlert(alert *types.Alert, status types.AlertStatus, receivers, mutedBy []string) *open_api_models.GettableAlert { startsAt := strfmt.DateTime(alert.StartsAt) updatedAt := strfmt.DateTime(alert.UpdatedAt) endsAt := strfmt.DateTime(alert.EndsAt) @@ -128,7 +128,13 @@ func AlertToOpenAPIAlert(alert *types.Alert, status types.AlertStatus, receivers } fp := alert.Fingerprint().String() + state := string(status.State) + if len(mutedBy) > 0 { + // If the alert is muted, change the state to suppressed. + state = open_api_models.AlertStatusStateSuppressed + } + aa := &open_api_models.GettableAlert{ Alert: open_api_models.Alert{ GeneratorURL: strfmt.URI(alert.GeneratorURL), @@ -144,6 +150,7 @@ func AlertToOpenAPIAlert(alert *types.Alert, status types.AlertStatus, receivers State: &state, SilencedBy: status.SilencedBy, InhibitedBy: status.InhibitedBy, + MutedBy: mutedBy, }, } @@ -155,6 +162,10 @@ func AlertToOpenAPIAlert(alert *types.Alert, status types.AlertStatus, receivers aa.Status.InhibitedBy = []string{} } + if aa.Status.MutedBy == nil { + aa.Status.MutedBy = []string{} + } + return aa } diff --git a/vendor/github.com/prometheus/alertmanager/api/v2/models/alert_status.go b/vendor/github.com/prometheus/alertmanager/api/v2/models/alert_status.go index ae1f248c99..2fa4f2314b 100644 --- a/vendor/github.com/prometheus/alertmanager/api/v2/models/alert_status.go +++ b/vendor/github.com/prometheus/alertmanager/api/v2/models/alert_status.go @@ -38,6 +38,10 @@ type AlertStatus struct { // Required: true InhibitedBy []string `json:"inhibitedBy"` + // muted by + // Required: true + MutedBy []string `json:"mutedBy"` + // silenced by // Required: true SilencedBy []string `json:"silencedBy"` @@ -56,6 +60,10 @@ func (m *AlertStatus) Validate(formats strfmt.Registry) error { res = append(res, err) } + if err := m.validateMutedBy(formats); err != nil { + res = append(res, err) + } + if err := m.validateSilencedBy(formats); err != nil { res = append(res, err) } @@ -79,6 +87,15 @@ func (m *AlertStatus) validateInhibitedBy(formats strfmt.Registry) error { return nil } +func (m *AlertStatus) validateMutedBy(formats strfmt.Registry) error { + + if err := validate.Required("mutedBy", "body", m.MutedBy); err != nil { + return err + } + + return nil +} + func (m *AlertStatus) validateSilencedBy(formats strfmt.Registry) error { if err := validate.Required("silencedBy", "body", m.SilencedBy); err != nil { diff --git a/vendor/github.com/prometheus/alertmanager/api/v2/openapi.yaml b/vendor/github.com/prometheus/alertmanager/api/v2/openapi.yaml index 549cc33e7f..bc525ff37a 100644 --- a/vendor/github.com/prometheus/alertmanager/api/v2/openapi.yaml +++ b/vendor/github.com/prometheus/alertmanager/api/v2/openapi.yaml @@ -223,6 +223,11 @@ paths: type: boolean description: Show inhibited alerts default: true + - in: query + name: muted + type: boolean + description: Show muted alerts + default: true - name: filter in: query description: A list of matchers to filter alerts by @@ -501,10 +506,15 @@ definitions: type: array items: type: string + mutedBy: + type: array + items: + type: string required: - state - silencedBy - inhibitedBy + - mutedBy receiver: type: object properties: diff --git a/vendor/github.com/prometheus/alertmanager/api/v2/restapi/embedded_spec.go b/vendor/github.com/prometheus/alertmanager/api/v2/restapi/embedded_spec.go index 0e23efd060..37938bdc14 100644 --- a/vendor/github.com/prometheus/alertmanager/api/v2/restapi/embedded_spec.go +++ b/vendor/github.com/prometheus/alertmanager/api/v2/restapi/embedded_spec.go @@ -177,6 +177,13 @@ func init() { "name": "inhibited", "in": "query" }, + { + "type": "boolean", + "default": true, + "description": "Show muted alerts", + "name": "muted", + "in": "query" + }, { "type": "array", "items": { @@ -433,7 +440,8 @@ func init() { "required": [ "state", "silencedBy", - "inhibitedBy" + "inhibitedBy", + "mutedBy" ], "properties": { "inhibitedBy": { @@ -442,6 +450,12 @@ func init() { "type": "string" } }, + "mutedBy": { + "type": "array", + "items": { + "type": "string" + } + }, "silencedBy": { "type": "array", "items": { @@ -979,6 +993,13 @@ func init() { "name": "inhibited", "in": "query" }, + { + "type": "boolean", + "default": true, + "description": "Show muted alerts", + "name": "muted", + "in": "query" + }, { "type": "array", "items": { @@ -1256,7 +1277,8 @@ func init() { "required": [ "state", "silencedBy", - "inhibitedBy" + "inhibitedBy", + "mutedBy" ], "properties": { "inhibitedBy": { @@ -1265,6 +1287,12 @@ func init() { "type": "string" } }, + "mutedBy": { + "type": "array", + "items": { + "type": "string" + } + }, "silencedBy": { "type": "array", "items": { diff --git a/vendor/github.com/prometheus/alertmanager/api/v2/restapi/operations/alertgroup/get_alert_groups_parameters.go b/vendor/github.com/prometheus/alertmanager/api/v2/restapi/operations/alertgroup/get_alert_groups_parameters.go index 0f5712dc42..e8b3e72eb3 100644 --- a/vendor/github.com/prometheus/alertmanager/api/v2/restapi/operations/alertgroup/get_alert_groups_parameters.go +++ b/vendor/github.com/prometheus/alertmanager/api/v2/restapi/operations/alertgroup/get_alert_groups_parameters.go @@ -39,6 +39,7 @@ func NewGetAlertGroupsParams() GetAlertGroupsParams { activeDefault = bool(true) inhibitedDefault = bool(true) + mutedDefault = bool(true) silencedDefault = bool(true) ) @@ -48,6 +49,8 @@ func NewGetAlertGroupsParams() GetAlertGroupsParams { Inhibited: &inhibitedDefault, + Muted: &mutedDefault, + Silenced: &silencedDefault, } } @@ -76,6 +79,11 @@ type GetAlertGroupsParams struct { Default: true */ Inhibited *bool + /*Show muted alerts + In: query + Default: true + */ + Muted *bool /*A regex matching receivers to filter alerts by In: query */ @@ -113,6 +121,11 @@ func (o *GetAlertGroupsParams) BindRequest(r *http.Request, route *middleware.Ma res = append(res, err) } + qMuted, qhkMuted, _ := qs.GetOK("muted") + if err := o.bindMuted(qMuted, qhkMuted, route.Formats); err != nil { + res = append(res, err) + } + qReceiver, qhkReceiver, _ := qs.GetOK("receiver") if err := o.bindReceiver(qReceiver, qhkReceiver, route.Formats); err != nil { res = append(res, err) @@ -198,6 +211,30 @@ func (o *GetAlertGroupsParams) bindInhibited(rawData []string, hasKey bool, form return nil } +// bindMuted binds and validates parameter Muted from query. +func (o *GetAlertGroupsParams) bindMuted(rawData []string, hasKey bool, formats strfmt.Registry) error { + var raw string + if len(rawData) > 0 { + raw = rawData[len(rawData)-1] + } + + // Required: false + // AllowEmptyValue: false + + if raw == "" { // empty values pass all other validations + // Default values have been previously initialized by NewGetAlertGroupsParams() + return nil + } + + value, err := swag.ConvertBool(raw) + if err != nil { + return errors.InvalidType("muted", "query", "bool", raw) + } + o.Muted = &value + + return nil +} + // bindReceiver binds and validates parameter Receiver from query. func (o *GetAlertGroupsParams) bindReceiver(rawData []string, hasKey bool, formats strfmt.Registry) error { var raw string diff --git a/vendor/github.com/prometheus/alertmanager/api/v2/restapi/operations/alertgroup/get_alert_groups_urlbuilder.go b/vendor/github.com/prometheus/alertmanager/api/v2/restapi/operations/alertgroup/get_alert_groups_urlbuilder.go index b89f54779e..d229a4a5b2 100644 --- a/vendor/github.com/prometheus/alertmanager/api/v2/restapi/operations/alertgroup/get_alert_groups_urlbuilder.go +++ b/vendor/github.com/prometheus/alertmanager/api/v2/restapi/operations/alertgroup/get_alert_groups_urlbuilder.go @@ -32,6 +32,7 @@ type GetAlertGroupsURL struct { Active *bool Filter []string Inhibited *bool + Muted *bool Receiver *string Silenced *bool @@ -99,6 +100,14 @@ func (o *GetAlertGroupsURL) Build() (*url.URL, error) { qs.Set("inhibited", inhibitedQ) } + var mutedQ string + if o.Muted != nil { + mutedQ = swag.FormatBool(*o.Muted) + } + if mutedQ != "" { + qs.Set("muted", mutedQ) + } + var receiverQ string if o.Receiver != nil { receiverQ = *o.Receiver diff --git a/vendor/github.com/prometheus/alertmanager/api/v2/testing.go b/vendor/github.com/prometheus/alertmanager/api/v2/testing.go index c813d350d3..7665a19f67 100644 --- a/vendor/github.com/prometheus/alertmanager/api/v2/testing.go +++ b/vendor/github.com/prometheus/alertmanager/api/v2/testing.go @@ -14,19 +14,17 @@ package v2 import ( - "encoding/json" "testing" "time" "github.com/go-openapi/strfmt" - "github.com/stretchr/testify/require" open_api_models "github.com/prometheus/alertmanager/api/v2/models" "github.com/prometheus/alertmanager/pkg/labels" "github.com/prometheus/alertmanager/silence/silencepb" ) -func createSilence(t *testing.T, ID, creator string, start, ends time.Time) (open_api_models.PostableSilence, []byte) { +func createSilence(t *testing.T, ID, creator string, start, ends time.Time) open_api_models.PostableSilence { t.Helper() comment := "test" @@ -46,10 +44,7 @@ func createSilence(t *testing.T, ID, creator string, start, ends time.Time) (ope Comment: &comment, }, } - b, err := json.Marshal(&sil) - require.NoError(t, err) - - return sil, b + return sil } func createSilenceMatcher(t *testing.T, name, pattern string, matcherType silencepb.Matcher_Type) *silencepb.Matcher { diff --git a/vendor/github.com/prometheus/alertmanager/asset/assets_vfsdata.go b/vendor/github.com/prometheus/alertmanager/asset/assets_vfsdata.go index f74112ad01..3dd06e052b 100644 --- a/vendor/github.com/prometheus/alertmanager/asset/assets_vfsdata.go +++ b/vendor/github.com/prometheus/alertmanager/asset/assets_vfsdata.go @@ -1,7 +1,6 @@ // Code generated by vfsgen; DO NOT EDIT. //go:build !dev -// +build !dev package asset @@ -10,7 +9,6 @@ import ( "compress/gzip" "fmt" "io" - "io/ioutil" "net/http" "os" pathpkg "path" @@ -152,9 +150,9 @@ var Assets = func() http.FileSystem { "/static/script.js": &vfsgen۰CompressedFileInfo{ name: "script.js", modTime: time.Date(1970, 1, 1, 0, 0, 1, 0, time.UTC), - uncompressedSize: 110210, + uncompressedSize: 110586, - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\xdc\xbd\x09\x7b\xdb\xba\x92\x28\xf8\x57\x64\xb6\x5b\x17\x38\x2a\x31\x94\xb7\x24\x94\x71\x35\xce\xe2\x2c\x27\xb1\x13\xc7\x59\x7d\xfc\xf2\x40\x0a\x92\x69\x4b\xa0\x02\x82\x92\x1d\x4b\xfd\x37\xe6\x07\xcd\x1f\x9b\x0f\x00\x17\x90\x5a\x92\xd3\x7d\xfb\xbd\x37\x73\xef\xf9\x1c\x8a\x2c\x14\x0a\x5b\xa1\x36\x14\xb6\x06\x29\x0f\x65\x14\x73\xc4\xf1\xbd\x93\x26\xac\x91\x48\x11\x85\xd2\xe9\xe6\x1f\x1a\x02\x71\x10\x20\xf1\xbd\x60\x32\x15\xbc\x21\x5d\x4a\x38\x48\x77\x40\x04\xc8\x45\x01\x36\x42\x25\x88\x40\x3b\x20\xa1\x40\x2d\x8a\x0f\x76\x6d\x39\x3a\x24\x80\xe3\xc5\x02\x97\xa8\xfa\x88\x59\xa8\x76\x81\x95\xa8\xe4\x32\xaa\x8d\xd8\x19\x92\x60\xf0\xdb\x15\x48\x94\x5a\x15\xec\x41\x5a\x56\xc0\x96\xb1\xfd\xdd\x3a\x53\xc4\xa0\xa8\xd5\xae\x36\x44\xd4\xaa\x76\x1f\x68\x59\x6d\xba\x8c\xf0\x5f\x40\x09\x45\x29\xd8\xb4\xd8\xc4\x30\x14\x5a\xc4\x1c\x40\x58\x12\x43\x97\x71\xfe\xf7\xd0\x17\x22\x0a\x35\x0a\x6d\x12\x53\x14\x5b\x24\x3e\x84\xb8\x24\x31\x5c\x46\xfb\xbf\x8c\xea\x18\x85\xb0\x4c\xb7\x4d\x38\x45\x91\x45\xf8\x23\x88\x4a\xc2\xe3\x65\xcc\xff\x3b\xdb\x12\xa1\x18\x56\xb6\xc6\x6e\x4e\x8c\x06\x56\x73\x1e\xc3\xa0\x6c\x4e\xb4\x8c\xfc\xff\xb0\x16\x0e\x50\x04\xeb\xda\x68\x37\x72\x56\x63\x73\x3b\x84\x10\xee\xd2\x1e\x77\x07\x48\xbd\xf7\x55\x3d\x48\x5a\x25\xee\x4c\x09\x28\x89\xdc\xad\x96\x01\x56\x94\x42\xcc\x2a\x78\x9b\x17\x84\xb2\xc9\x7b\xf5\xa2\x90\xda\x85\x51\x6a\x95\x9f\x96\xe5\xa1\xec\xc7\xfd\x65\x0c\x40\x6b\x38\x10\xb5\xd0\x0c\x6c\x34\x50\x8e\xd1\xc1\x2a\x44\x10\x2e\xa3\x42\xa1\x85\x2d\xa8\x62\x83\x72\x1a\x3c\x5c\x8d\x0f\xe2\x95\x18\x51\x8c\x17\x53\x2a\x1a\x11\xe9\xa3\x72\x38\xcd\xd0\x0c\x62\x81\xd4\x37\x46\x8e\x84\xa0\x77\x88\x63\x48\x89\xd7\x4d\x0f\x79\x37\x6d\xb5\x30\xbb\x48\x2f\x89\x44\xa2\x95\xe2\x6e\xce\xff\x17\x18\x12\x32\xaa\x60\x2a\xf1\xc8\x12\x0f\x23\x5e\x97\x1d\xf2\x66\x53\xb8\x41\x97\xb5\x5a\x58\x5e\xb0\x4b\x22\x5c\x0a\x82\xa8\x57\xc5\xe6\x37\x62\x7c\x28\xaf\x08\x83\x53\xb5\xb5\xe0\x05\x86\x6d\x82\xea\x15\xe4\x8b\xe5\x82\x5f\x2e\x30\x6c\x6a\x48\x8e\x10\xd2\x8c\x16\x86\x81\x12\xaf\x4b\x0f\x59\x97\xb6\x5a\x38\xbd\xa0\x97\x44\x5e\xd0\xcb\x9c\x82\xf4\x82\x5f\x12\x01\xe9\x02\xc3\xfa\x66\x89\x1c\x6b\xde\x53\xb2\xd5\xc9\xfb\x4a\x96\x7d\x25\x2e\xd2\x02\x2f\xbb\x90\x97\x84\x03\xfb\x7d\x7a\x15\x32\xa6\x91\x09\xa2\x56\x8f\xbc\x48\x2f\x41\x14\x5d\x2f\x7e\x13\x53\xbb\xd3\xf5\x0e\x09\xeb\xb2\x76\xbb\x40\xc4\x6a\x88\x70\xf7\xef\xb4\x75\x45\x4b\x39\x52\x6d\xad\x4c\x8b\xff\xf2\xa8\x68\x8e\xd1\xa2\xa0\x46\xa7\xc0\x9c\xae\xc4\x5c\xcc\x9e\x64\x14\x85\x4c\xb7\xe0\x17\x14\x88\x92\x02\xde\x66\x40\x73\x3a\x5a\x28\x2d\xa8\x3b\x4c\x7b\xf9\xa3\x9f\x62\x0c\x21\xf1\xba\xe1\x21\xeb\x86\xad\x16\xa6\x17\xa1\x1a\xdf\xf0\xb2\xab\x70\x9a\x2f\x69\xfe\xa5\xc5\xd4\x9c\x0a\x8b\xb1\xa7\x2b\x26\x93\x35\x8a\x6b\x3e\x85\x31\x4f\xe2\x11\x73\x47\xf1\x10\xf1\x96\xe3\x37\x9c\xd6\x18\x61\x0c\x62\x81\x4b\xe9\x71\x8c\x72\x78\xe7\x30\xe2\x92\x09\x4e\x47\xc9\x3f\x9d\x92\x6f\x5c\x29\x5e\x2d\xaf\x44\x3c\x6b\x3c\x17\x22\x16\xc8\xb9\x92\x72\x92\xf8\x0f\x1e\x0c\x23\x79\x95\x06\x6e\x18\x8f\x1f\xb0\xd1\xf8\x41\x18\x0b\xf6\x20\x18\xc5\xc1\x83\x8e\xeb\xb9\xde\x83\xab\x88\xcb\xe4\x81\xd3\xe2\x2d\xc7\x1d\xf7\x1d\x8b\x15\x0d\x6b\x53\x04\x18\xb9\xb8\x84\x94\x4c\x74\x37\x7b\xc0\x70\x37\x6d\x36\x91\x24\xcc\x9d\xc4\x13\x84\x71\x57\x7d\x93\x2e\x05\xe9\x06\xfa\xbb\x35\x9c\x05\xd6\x49\xc9\xee\xa3\x01\xea\x78\xde\xa1\xc4\xf9\x74\x72\x27\x69\x72\x85\x4e\x75\xbd\x18\xb6\xbc\x6e\x34\x40\x9c\x10\x22\x32\x08\xf3\xc6\x89\x83\x6b\x16\x4a\x67\x8b\xc8\xbb\x09\x8b\x07\x0d\x3e\x9f\xf3\x74\x34\x52\xfc\xb1\x78\xca\x8b\x38\x79\xc5\x0e\x29\xc0\x9b\xcd\x2b\xb4\x8f\x61\xab\xd3\xcd\xdb\x96\x36\x22\xde\xe0\xee\xf6\xa1\xd7\x6c\x22\x4e\x06\x52\xf1\x32\xa1\xfe\x55\x84\x70\x1c\x0d\xd0\xd6\x04\x71\xbd\x32\xd5\x1f\xd9\xea\xa8\xe6\x65\x54\x75\xba\x39\x79\x9a\xe5\xde\x90\x11\x1a\x62\x38\x22\xab\x07\x7c\x6b\x98\x4d\xdc\x72\x74\x3f\xe4\x13\x77\x65\xeb\xf2\xee\xd1\x3d\xd1\xf3\x7c\x7e\x28\x7a\xed\x8e\xdf\x51\x7d\xb1\xc5\xdd\xed\xec\x3b\x92\xe4\x03\xe2\x8a\xd3\xba\x14\xe3\xf9\x3c\xfb\x1d\x80\x70\x03\x8c\x7b\xd2\x57\xbf\x42\x10\x6e\x88\x75\xbb\xbb\xdc\x0d\x34\x9f\x6e\x36\xb7\xaa\x65\xbb\x9c\xe8\x72\x8a\x63\x17\x43\x28\xe7\x73\x85\xad\xd7\xf1\x85\x1b\xa8\xfa\x3d\xb3\xc1\x3c\x5f\xd3\x4c\xd3\x28\x7c\xe8\x2d\x30\x5c\xaf\x65\xed\x19\x50\x67\xfd\xf2\xf0\x0e\x3f\xe4\x0b\x7d\x2d\x04\xc9\x41\x30\x3c\x5d\x26\x27\xfb\x5a\xf0\xc1\x43\xaf\xd7\x97\xbe\xe8\x8d\xa4\x9f\xc8\x05\x86\x57\xc4\x2b\x87\xe2\xd4\x46\x7d\x4f\x7d\x0e\x81\x2f\x16\xe5\xf4\xfd\x5e\xe5\x45\x39\x04\x84\xbe\xb4\xa0\xce\x0d\x96\x7c\xd9\xdc\x2f\x74\x8f\x4b\x3d\xcb\xb0\xd9\x22\x2e\xe4\x65\xf9\x52\x98\x97\x42\xbd\x2c\x78\xab\x2a\xfd\x85\x8c\xd0\x99\x35\x53\xce\x0c\x62\x35\x4f\x94\x86\xc9\x87\xd6\xb4\x2e\xe6\x49\x4b\x64\x53\x23\xc8\x5f\x89\xae\x61\xf2\x27\x66\x90\xb3\x11\xee\x5a\x9c\x5a\xcd\x06\xf3\x16\x33\xc2\xdc\xa0\x04\xcd\xc7\x5f\xd3\x73\x4c\xee\xb7\x7d\x6f\x51\x12\x74\x52\xe9\xaf\x6d\xbf\x03\x45\x9f\x29\xf8\x37\x64\x84\x4e\x2c\xfa\x3f\x2b\x36\x95\x57\x2b\xc8\x31\x48\xc2\x33\xde\xdb\x95\xed\x76\x17\x0b\x55\xf1\x85\xac\x6e\x5c\x45\xf1\x67\xd5\xe2\x17\x97\x16\xd9\xc2\x70\x0f\xee\x52\xab\xa4\x82\x7b\xbd\x51\x04\xba\xb8\xec\xea\x35\x20\x95\x74\xa2\x66\x3c\x48\x22\x55\x2f\x18\x74\x7a\x77\xd2\x2c\x8d\x96\xec\xec\xb3\x92\x43\x31\x7c\x24\x48\xd6\x30\x2b\xb6\x56\x30\x15\x1b\x77\xb3\xc9\xaa\x15\x80\xee\x66\x9c\x9a\x6a\xee\xca\x6a\x80\x55\xab\x4a\x55\x55\xe1\x52\x3d\x4a\xe8\xcd\x6b\xa2\x4b\x35\x35\x9b\xe9\xaa\xea\x20\x25\xa9\x1b\x60\x6a\x2a\xbd\xad\x56\x0a\x69\xb5\x62\xaa\x2a\x66\x2b\x2a\x56\xd2\x72\x5e\x75\xb8\xba\xea\x66\x93\xae\xaf\x1f\x28\xa1\x6e\x80\x43\x43\xc5\x74\x99\x0a\xa0\x55\x4a\xc2\xda\xea\x97\x50\x2a\x26\x9f\x91\x9a\x14\x6e\x12\x0b\xb9\x8e\xc5\x68\x6e\xae\x59\xf9\x42\xff\x07\x3f\x2b\xfc\xe8\x37\xb1\x91\x99\x82\xb4\xe7\x25\x21\x24\x91\x3d\xcf\x57\x0f\x7d\xa9\x79\xb2\xae\x60\x1d\xa3\xe2\x2d\x25\x83\xc1\xcb\xb5\xdc\x90\xb7\x37\xc8\x09\xfc\x0f\x5d\xfa\xed\xfa\xd2\x0f\x36\x95\x7e\x20\xe6\x9e\xfe\xfc\x96\xca\x2b\x77\x12\xcf\xd6\xcb\x2a\xff\xce\x57\xe1\xf9\x77\xc2\xf3\x96\x7b\x6a\xab\xed\x5d\xa1\x4e\x07\xfb\xde\xa1\x68\x36\xf9\xa1\x37\x9f\x0b\xb5\x7b\x7a\x87\xbc\x27\x5a\xdc\xcf\xa4\x4d\x5d\x19\x95\x94\xef\x60\xcd\x82\x3e\x11\xfd\x26\x64\xd1\x08\xbe\x99\xe7\xc1\x28\x8e\x05\xfc\x30\x3f\x44\x9c\xf2\x3e\x3c\x31\x3f\x46\xf1\xb0\xbb\xae\x39\xcd\xe6\xa6\xc6\xce\xe7\x9b\xbe\x6e\x11\xa2\x84\x2b\x45\xcf\x7b\xb2\x69\xac\xba\x7f\xeb\xe3\xef\xc9\xd2\x66\x57\xa0\x44\xb8\xe1\x15\x15\x4f\xe3\x3e\x3b\x92\x28\xc5\x5d\x7a\xb8\xbf\xbf\xf3\xf8\x60\x3e\xdf\x3f\xd8\xed\x3c\x3e\xa4\x3d\x64\x4b\xdc\xa0\x44\x70\xdf\x7e\xd5\x12\x17\x69\xab\xa3\xbf\x90\x1d\xbc\x28\x44\xa8\xeb\x38\xe2\xc8\x71\xf0\x46\xc5\xe6\xe2\x12\x2a\xb2\xb1\xd1\x41\x0a\xd2\x94\x88\x13\x2e\x51\x98\xb6\x5a\x10\x56\xa9\x0c\xe7\x73\x44\x5b\xa6\x80\xa2\x10\x38\xa2\x58\xb1\x02\xbd\xb2\x69\x41\x96\xb4\xc8\xea\xfe\x2d\xbd\x28\xa7\x49\x1a\x9a\xe4\x6f\xd3\x24\x0b\x9a\x8c\x36\xa4\xf6\xb2\x45\x29\x8d\xeb\xd1\x7f\x47\x7e\x87\x16\xa5\x52\x65\x74\xa4\x44\x2b\x55\xb4\x4a\x07\xc3\x5d\x53\x47\x4a\xe8\x3f\xc9\xfe\xc1\xee\x8e\xd7\x6c\xee\x3f\xdc\xdd\xdb\xfd\x27\xa1\x3d\x79\xd1\x6e\xb3\xcb\x56\xea\xa7\x55\x0a\xe0\xcf\x75\x73\x4f\xb8\xc9\x64\x14\x29\xae\xb5\xc0\xf0\x62\x3d\x94\xee\x53\x0d\xf4\x95\xfc\xa6\xb2\xf4\x1b\x93\xd6\xec\xc3\xf7\x99\xfe\xa4\xb6\xe2\xb4\x3a\x15\x24\xd6\x72\x39\x62\x24\xad\xb7\x36\xed\x89\x8b\x76\x5b\x5e\xb6\x98\x5f\x8a\xc7\xde\x22\x97\x93\xb3\x6e\xe7\x9c\xfc\xcb\xc8\xd8\xfa\x7d\x3a\x3a\x8b\x82\x20\x0c\x82\xaf\x13\xcf\xb7\xfe\x43\xb8\x11\xef\xb3\xdb\xd3\x81\xe9\x5c\xb9\x0e\x54\xf1\xc1\x1a\x2c\x5b\x0b\x9b\x8b\x39\x87\x45\x0b\x95\xf0\x3d\xa2\x89\x7c\x55\x20\x20\xc5\xb7\x76\x0e\xbe\xc0\x90\x2e\xe3\x34\x5d\x55\x48\x4e\xd1\x00\xc9\xc3\x4e\x2e\xec\x1d\x5b\x72\x9d\x07\x5a\x0a\x69\x77\x0e\x11\xb3\x69\xd5\xca\x59\x2e\x7c\x30\x0c\xac\x45\x64\x55\xe4\xd0\x23\x45\xd7\x35\x47\x49\x7a\x1e\xf4\x73\x51\x6f\x1d\xa7\x54\x60\x1d\x60\x25\x58\x69\xc4\x5f\x82\xdb\x85\x81\xcf\x61\x98\x4b\x8e\xf1\xa6\xaa\xf7\xb4\xcc\x7d\xe5\xf3\x45\x46\xe8\x60\x6d\xbf\xeb\x8a\x2e\xc4\xa5\x1a\x9d\x80\xaf\x5f\x27\x19\x1c\x48\x0d\x99\xf0\xd5\xb2\x5d\x1d\x18\xd8\xe5\x7a\xf1\x6c\x19\x18\xd2\xcb\x4d\x42\xd5\xaa\x02\x40\x75\x91\x74\x65\x11\xcb\xdc\x58\x2b\x04\xa1\x2e\x46\xd7\x14\xb3\xec\x8a\x4b\x05\x21\xd6\x45\xe3\xb5\x45\x21\xda\x54\x18\xa2\xcb\x55\x9b\x8f\x14\x77\x79\xa1\xbe\x7a\xf5\xfa\xc3\xe9\x89\x3b\xa1\x22\x61\x5a\x30\x0b\xa9\x0c\xaf\x2c\x7b\xf3\xb6\x44\x33\x34\x96\xe0\x9c\x5f\x45\x49\x23\x4a\x1a\x3c\x96\x8d\x29\x1d\x45\xfd\x86\x2a\xb9\xd5\x70\x5a\xdc\x1d\xb3\x24\xa1\x43\x06\x0a\x81\x92\x8d\x46\x6a\x16\xf4\xb9\x35\xcd\xfa\x59\xed\xc9\x2c\xd2\xf8\xdd\x6d\x7c\x1f\xd2\x84\x35\x76\xfd\xcc\x3e\x10\xc4\xf1\x88\x51\xcb\x3c\x20\x7a\x13\x25\x2b\xfa\x63\x8e\x1c\xda\x78\x72\x7a\xfa\xc6\x51\x42\x9f\x2e\xb5\x93\x97\xe2\xe9\x38\x60\xa2\x54\xd2\x45\x4f\x83\xf3\xc6\xab\x93\x73\x05\xee\x23\x71\x48\xda\x3b\x9d\xbd\x87\x7b\x8f\x76\x0f\xf6\x1e\xce\xe7\xe5\xf3\x21\x11\xf3\x39\xf2\xe6\x02\x2b\x41\x04\x37\x9b\x68\x2b\x4a\x8e\x23\x1e\x49\xd5\x15\xf3\xb9\xf8\xf7\x0e\xae\xa3\xd3\x24\x19\x1a\xf6\x6a\x34\xac\x21\xfc\xf8\xcd\xe9\xd1\x79\x49\xf9\x41\x5e\xaa\xae\x36\xe6\xa5\x44\x23\xe2\x89\xa4\x3c\x54\x2f\x3f\x68\x20\xfd\xa5\xe5\x38\x39\xca\x0f\xe7\x67\xaf\x4e\x5e\x94\x38\x1f\xfb\x39\x6f\xcb\x6c\x2e\xaa\x00\x77\x43\x03\xaf\x5e\x96\xb0\xfb\x39\xac\xd5\x92\x87\xf9\x3b\x2d\x24\xb9\x51\x62\x84\x25\x81\x7b\x53\x6e\x2c\x17\xf0\x39\xaf\xfb\xcd\xab\x0f\x56\x6b\x1e\xfd\xba\xe4\x36\xcf\x8a\xf2\xc6\xd1\xd9\xd9\xd1\xd7\xb2\x70\xc7\xf3\x73\xfe\xd9\x5f\x69\x4f\x12\xa5\x15\x69\x3e\xdf\xca\x35\xf4\x9c\xbd\x66\x48\x4f\x9f\xbc\x7e\xfe\xf4\xbc\x31\x8b\xe4\x55\x83\x36\x06\x11\x1b\xf5\x1b\x9c\x8e\x59\xbf\xf1\x3f\x9d\x96\x6c\x39\xff\x53\x57\x68\xb8\x70\x3f\x23\xea\x42\x96\x16\xce\x98\x21\x86\x7b\xcc\xd7\x13\xfd\x4a\xaa\x15\xa4\x95\x1e\x43\x62\xc7\x57\xe4\x31\xbd\xc3\xd5\xdb\x58\x23\xa4\x6c\x5d\x34\x40\xa2\xd8\x65\x64\x05\xac\xf1\xe6\xf4\xe4\xc5\xf3\xb3\x06\xd5\xb8\x1a\x27\x8c\xf5\x1b\x7a\x33\x68\x68\x62\x1b\x41\x2a\x1b\x31\x1f\xdd\x35\x12\xc6\x1a\x4e\x2b\x47\xd3\x72\x1a\x8c\x4b\x11\xb1\x44\x57\xf0\x1b\x2d\x19\xd6\x5b\xb2\xe3\xff\xb2\x8b\x7f\xd1\x40\xd3\xd3\x45\x77\xa6\x40\x89\xd9\xe2\x52\x33\x30\xba\xd9\x57\x34\x39\x9d\xf1\x77\x22\x9e\x30\x21\xef\x50\x8a\xf1\xbd\x45\x6d\x7a\x69\x84\x05\x4d\x2a\xb6\x59\xcc\x95\x84\x34\xa3\x97\x92\x13\x74\x8a\xcc\x2f\x28\x65\xd7\x89\x44\x27\x12\x95\x0d\xda\xf5\x4b\xdd\x97\xbb\x03\x88\x09\x77\x87\x10\x11\xaf\x1b\x1d\xc6\xc5\x8e\xdc\x6a\x65\x04\xc4\x17\xd1\x65\x36\x38\xd5\xea\x59\x37\x24\x21\x52\x95\x59\x35\x85\x79\x2d\x7b\x7e\x49\x7e\xad\xa7\xf5\xeb\x2b\x5d\x52\xb1\x06\x96\x95\xd8\x2f\xe8\x1a\x90\x63\x08\x14\x55\xdd\xc0\x0d\xba\x01\x09\xdc\x20\x23\x26\x30\x26\x9d\x68\x80\x6a\xa4\x0c\xc8\x89\x42\x08\x83\x82\x18\xd5\x3b\xba\xe5\x03\x5c\x34\xdd\xaf\x31\x67\x63\x21\xca\xbe\x7a\xd6\x0a\xe7\xaa\x55\x96\xa7\x6d\xb3\x65\x7e\xa5\x6f\xe0\xde\xf4\xb0\x61\xe0\xda\x35\x90\xf5\x60\x58\x1d\xc0\xa1\x54\xdb\x8e\x1e\x40\xed\x4e\x08\x5d\x6a\xf5\xa7\x52\x12\x2c\x93\xf6\xb6\xed\xe5\x9c\xa1\x90\x41\x41\xc6\x0a\x9f\xa7\xf1\x3b\x95\xa5\xc7\x55\xc9\xa2\xd8\xa2\x9e\xdf\x4e\x58\x28\x23\x3e\x54\x9b\x92\xde\x8c\x4a\xb3\x3c\x2f\x0c\x76\xcb\x86\x6c\xee\x6e\xab\x1d\xa0\xb0\xe0\x6e\x75\xba\x4b\xfb\x94\xe7\x57\xbb\x9e\xbb\x54\xe1\x71\x69\x37\xdb\xc6\xb2\x7d\x29\xdb\x1a\x32\x5e\x5f\x61\xb9\x5b\x5e\x9d\x5d\xbb\xa1\xc6\x11\xe6\x6c\x38\xe3\xa9\xf9\x82\xcd\xc0\xae\x78\x61\x3e\x2e\x18\x67\x81\xa1\xaf\x31\xf4\x9b\xcd\x65\x28\x8b\x56\xa6\xa1\xd8\x2a\xa8\xdd\x12\x6a\xa0\xa1\x06\xcd\xe6\x50\x41\x0d\x41\xb8\xc3\x72\x19\x14\x50\x57\x1a\xea\x6a\x15\xae\x62\x73\xb1\x10\x58\xd3\x6f\xb8\x5e\x6a\xde\x2a\x45\xee\x72\x10\x2c\xf1\xb9\xcb\x0e\xa5\xf6\x63\xaa\xc9\xa7\x2a\xd6\x3e\xb5\x0b\x76\xb9\xce\xf8\x3f\x59\x2b\x88\x6a\x81\xc7\xec\xbe\xd1\xe0\x0e\x09\x50\x0c\x10\x38\x6e\x39\x8e\x2d\x18\xcf\xec\x19\xc8\x35\xce\xbb\x0d\x62\xab\x34\x7e\x4c\x99\xc9\xc1\xb7\x9c\x28\xb4\x25\xba\x1b\x0b\xdd\xfd\xb6\xef\x01\x55\x42\x73\xf1\xf9\xa8\xfa\xb9\x53\xfb\xfc\xa1\xfa\x79\x07\x02\x9f\x43\xe8\xab\x2a\x8c\x94\xfe\x7c\x83\x94\xbe\xab\xa1\xfb\x5a\xf0\x87\xeb\x0d\x80\x7b\x16\xa0\x6e\xc5\x53\x6e\x9b\xe4\x5f\x69\x22\xb8\xb6\x3c\x03\xf3\x9f\xf2\x56\x2b\x53\x15\x74\x0f\x5e\xf9\x17\x97\x8b\x9c\x43\x1e\x2b\x58\xe0\x65\x0b\x4e\xed\x15\xff\x81\x23\x7b\x99\x73\x74\xc3\xd1\x2b\x05\x80\xb1\xbd\xce\xbf\x67\x04\x72\xf7\xca\x28\x48\x02\x83\x46\xac\x9b\x7c\x5e\x69\x89\x3d\x16\x35\xf4\xdf\xcd\x57\x30\xd5\xe8\x2a\x4c\xf3\xbe\x70\xb2\xd5\x81\x33\xae\x34\xb3\xa2\x52\x5d\x81\xe2\x13\x67\x3c\x33\x64\x63\xd8\xfa\x92\x99\xbb\x55\x09\xaf\xcb\xc9\x19\x77\x93\xab\x68\x20\x11\xee\xe2\x2d\x3b\x70\x43\x3b\x74\x84\x3b\xc8\x34\x66\xae\xd6\x93\xbb\xad\xa6\xb8\x67\xdc\x62\x1d\xf5\x4f\x01\x37\x54\xba\xe7\x50\xb3\x1f\xde\xc5\xc2\x1d\x12\xf5\x33\xd2\x2c\x56\x2d\x1e\xd3\x20\x85\x50\x7f\x08\x90\x42\xa7\xb6\x9b\x1c\x72\xc1\x46\x09\x53\xd4\xea\xf8\x8e\xc2\xcd\xe0\x0e\xdc\x50\x57\x1d\x54\x7a\x42\xe1\xe1\xaa\x0b\xb5\x09\x22\x1a\xa0\x7d\x43\x4d\x46\x9e\x70\xaf\xaa\x6b\x30\xab\x78\xa0\x2b\xbe\xca\x9b\x8c\x75\xa5\x0d\x45\x83\x9a\x5d\xda\x06\xe9\xf9\x1d\xa5\x0a\x2a\x50\x88\x7c\xe1\x0e\x17\x90\x97\xed\x2f\x16\x0b\xc4\x71\x57\xf7\xf6\x62\xb1\x41\x9b\x3b\x51\x03\xc5\x80\xbb\xe1\x13\xf5\xe7\xa1\xfa\xb3\x53\x6e\x08\xcb\x61\x31\xf8\x7e\xb1\xa8\x38\xf0\x4e\x6a\x8a\x9c\xd9\xbb\x66\x68\xc4\x81\x83\xe8\x09\x77\x30\xa2\xc3\xc4\x9f\xc6\x51\xbf\xe1\xe1\xae\xde\xc5\xe6\xf3\x2b\x94\x59\x45\x63\x72\xbf\x80\x88\xa0\x90\x48\xa4\xb7\x32\xb5\x13\x13\x8a\x02\x88\xd4\xa6\xb8\x42\xf7\x07\xa6\xb9\x14\x53\x12\xd0\x1b\x9e\x1b\xa7\xde\x28\xf6\xd4\x4d\x5d\xda\x6c\x22\x24\x89\x9c\xcf\xef\x17\xf8\x82\x5d\x92\xd4\xa5\x48\xab\x49\xa0\x20\x56\x20\x64\xe4\x7e\xa8\x35\x6a\x43\xe2\x02\x52\xc2\xdd\x10\xa8\x92\x91\x41\xc9\x39\x4c\xcb\x39\x83\xc2\x3b\xe5\x5e\x91\x57\x1c\xcd\xd0\x73\x5e\x74\x54\xc3\x8e\x2a\xd2\x5f\x38\xdc\x6f\xfb\xfb\x10\xf8\xf6\x64\x30\xae\x1b\xee\xd2\x8a\x35\xd9\xdd\xee\xdd\x21\x0a\x4c\x33\x37\x3f\x6c\x36\xe3\xde\xad\x0e\xbb\x13\x6e\x04\xc2\xbd\x56\x6f\xef\xf4\x8b\xb0\x27\x5c\x35\xd4\xea\x95\x1a\x06\xe0\x6e\x80\xf1\x02\xd9\xe6\x35\xb9\x40\x31\x04\xd6\x00\x05\xa6\xa9\x6a\x4c\x18\x70\xd5\xad\x03\x14\x29\x59\x01\x04\x86\x9f\x1c\xc5\x10\xba\x01\xa4\x28\xc2\x05\x8e\xea\x5b\xa0\xbd\xfb\x49\x2c\x64\xe2\xd3\x85\x7f\x9f\x39\xb7\x38\xb9\x5f\xe8\x01\xfc\xfc\xbb\x3c\x41\xb8\x43\x54\x67\x09\x6b\xf6\x8b\x19\x3a\xe7\xc0\xdd\x2b\xc8\xd8\xb6\xa8\x4e\xb9\x67\x9b\x03\xb6\x34\x33\xbf\xf1\x05\x8c\x14\x43\x2f\x79\xdb\xeb\x3a\x47\x1f\xab\xef\xaa\x0d\x1f\x37\xb2\x72\xee\x73\x88\x6b\x36\x9c\x9f\xc5\x66\xa4\xa7\x10\xa4\xb9\xa3\x53\x4f\xcb\x97\x1c\x6d\x79\x20\x20\xd5\x1b\x1d\x06\xf5\xbb\x03\xb2\xf8\xcd\xf1\xf7\x6c\x3f\xbd\xdf\xf6\x9d\xc1\xad\x03\xd4\x4f\x2f\xd8\xe5\x7c\x7e\x1f\xf9\xc7\x70\xed\x1f\x57\x02\xcb\x5e\x5a\xeb\x36\x93\x92\x44\x21\x25\x75\x7c\xb3\x00\x84\x7b\x03\x94\x20\x4a\x52\x88\x09\x83\x19\xe2\xbd\x37\xfc\x82\x5e\xba\xcc\x37\xff\x0e\x2a\x72\x5e\xe9\x49\x8c\xbb\x42\x7b\xab\x7e\x60\xc5\x3a\x27\x8a\x6f\x14\x5b\xb0\x92\x11\x4b\x6f\x94\x5a\x20\x48\x5e\xa4\x97\xaa\x1a\x0a\x29\x41\xa9\xb6\x35\x63\x8b\x6e\xe0\xbd\xd4\x8d\xc8\x09\xa2\x90\xba\x11\xf6\x53\xf7\x3a\xfb\x71\x8d\x21\xc5\x85\x31\xa1\x54\x24\x84\x3b\xee\x86\x6e\xa0\x54\x02\x37\xc0\xba\xad\x6a\x72\xaa\xd6\x66\x15\x77\x2b\x66\x0b\x4d\x46\xd6\x27\x6e\x0c\x12\xee\x27\xbe\x70\x39\xfc\xf0\xd9\xc2\x6c\x53\x14\xe2\xb2\xf3\xde\xea\xe6\xbe\xe1\x17\xfc\xb2\xd9\xbc\x42\xbb\x56\xbf\x7e\xaa\xce\x3a\x0d\x09\x1a\x92\xdc\x33\xff\x1b\x07\xe1\x0b\xa0\xfe\x0f\xbe\x80\x67\xc5\x1e\xf8\x6d\xad\x94\x53\x89\x5a\xf9\x51\x2c\x78\x09\x21\xb9\xb8\x84\x98\x68\xcc\xae\x50\xdc\x4e\x12\x0f\x6a\xcb\xc3\x0c\x46\xc2\xe4\x79\x34\x66\x71\x6a\xf1\xec\x7c\xb7\xc6\x78\x01\xb2\x18\x0c\xeb\x73\x38\x62\x54\xe4\xc5\x84\xb6\x07\xe5\x50\xa6\xce\x80\x44\xa6\x5d\x6e\xb8\xc6\xec\xdf\x15\xb9\xcf\x12\x97\x22\x60\x08\x29\x89\x91\xd0\x3a\xa1\x51\x4f\x72\xf9\x91\xea\xa8\x28\x7a\x89\xca\x38\xb9\x68\x81\xe1\x3e\x49\x83\x24\x14\x51\xc0\x2a\x6c\x2f\xcc\x77\xf5\x05\xa4\x7c\x35\x08\xe2\x6a\x0b\x08\x33\x83\x3d\xc6\x96\x69\x19\x1f\x7a\xf3\x79\xa8\xfd\x02\xda\x96\xdf\xc1\x0b\xb3\x6a\x9f\xf0\xee\x1a\xce\xb3\x4a\xa1\xd1\xae\x50\x9c\x8b\x59\xef\x39\x71\x52\xde\x67\x83\x88\xb3\x7e\xa9\x9b\xf7\xe3\x30\x1d\x33\x2e\x7b\xf9\x83\x7f\x6f\x39\xfc\xdf\x15\xc2\x11\x9d\x4c\x18\xef\x3f\xbd\x8a\x46\x7d\xd5\xe1\xab\x36\x58\x46\x98\xcb\xe3\x3e\x2b\xb7\x8d\x09\x15\x8c\xcb\x93\xb8\xcf\x5c\xc1\x26\x23\x1a\x32\x83\x60\x2a\x10\xb7\xb7\xdc\x05\x06\x86\xe1\xbe\xc2\x6f\xfe\x5c\x29\xcb\xaa\x96\xbc\xa8\xcc\x47\xdb\x2c\xfa\x0b\x1f\x98\x67\x8d\xf9\x7d\xce\x43\x68\x97\xb5\x48\xea\x06\xf3\xb9\x07\x99\x2b\x2b\x2d\x3d\x6c\xad\xd2\x49\xa5\x99\x6c\xe8\x87\xd0\xf7\x47\x42\x87\x3f\xfa\x12\x06\x3e\x85\xc0\x67\x5a\x42\x40\xd9\x8e\x0f\x5f\xff\x5b\x08\xfc\x3d\x12\x77\x7e\x8b\x44\xe3\x99\x11\x9b\xa4\xf4\x6b\x9f\xeb\x2d\x25\xf0\x3b\x2d\x24\x74\xe5\xb8\x32\x40\x42\xd4\xca\xec\xab\xed\x07\xc6\xbe\x80\x9b\x5c\xb2\x58\xac\x63\x1c\x02\x5d\x70\x10\x97\x2b\xe4\x2e\x23\x37\x66\x93\x56\x8a\xf5\xca\x50\x86\x03\xe4\x2a\x2c\x79\x64\xf2\x42\x5b\xe0\x05\xd9\x20\x10\x16\x78\x80\xad\xc2\x54\x46\x2c\x6b\x5c\xa9\x20\x68\xa3\x03\xc0\x42\x07\xe9\x2a\x84\x76\x24\xf3\xe2\x37\x1c\x04\x15\x84\x40\x57\xa1\xac\x06\x37\x2f\x7e\xcb\x85\x50\x43\x0b\xe1\x2a\xc4\xf5\x70\xe7\xc5\x6f\xba\x19\x96\x90\x43\xbc\x0a\xfd\x72\xfc\xf3\xa2\xe6\x8c\x18\x40\x00\x09\x8c\xa0\x0f\x53\xd8\x86\x31\x5c\x55\xaa\x58\xfa\xba\xaa\x12\x41\x02\x90\x24\x01\x46\x46\x90\x92\x3e\x50\x32\x85\x90\x6c\x43\x4c\xc6\x10\x91\x2b\x78\x44\x08\x41\x9c\x0c\xf0\xaa\x70\x6b\x88\xd6\x05\x5c\xa3\x28\x5b\x45\x75\x77\xc9\x62\x7d\x50\x88\x92\x7d\xa8\xe7\x58\xf2\x15\x06\xba\x61\x15\x3a\xb4\x63\x03\x43\xb8\x11\x76\xa7\x02\x1b\x6f\x84\xdd\xb5\x61\xbb\xeb\xd6\x98\x06\xdd\x53\xa0\x02\x62\xff\x7e\xa0\x4b\xc8\x45\x85\x0d\x44\xa2\xe4\xd3\x8e\xda\xe7\x26\xd2\x51\xca\x9a\x33\x71\x7c\xbe\x66\xfd\xab\x4e\xd0\xaa\xe0\x76\x6f\x86\x52\x01\x4a\x98\x41\x92\x70\xe0\x24\x61\xda\x77\x19\x63\xc5\xcc\x98\xbb\xad\xd8\x7e\xef\x0e\x0d\x18\xf0\xc3\xdd\x5e\x20\xfc\x44\x40\xc0\x94\x58\xcd\x5c\x8a\xfd\x19\x8a\x58\x66\x83\x5e\x60\xec\x67\xa1\x6f\xc0\x72\x4f\xa1\x80\x60\x5d\x3f\x34\x4e\x11\x37\x5b\xbd\x62\xb7\x0b\x0c\xc9\xda\x1e\x0b\x5f\xab\x49\xe0\x86\xaf\x31\xd0\x23\x5f\xb8\xf4\x08\x68\xaa\xfe\x4d\x2b\x5d\xa1\x79\xae\x25\x67\xde\x2f\xac\x88\xb5\xc2\x20\x45\x81\x11\xe9\x6e\x43\x4a\xa4\xcb\x75\x74\x41\xdc\x55\x83\xb7\x45\x08\xeb\x21\x49\x84\x16\x8b\x91\xfa\x87\xa8\x9d\x51\x0d\x16\x21\x84\x35\x9b\x4e\x38\xa2\x49\xa2\x7e\xa4\xbd\xbe\x40\xd2\x1c\x56\xd0\xe2\x29\xc5\xbe\xf9\x7a\x42\xc7\xac\x80\x10\x06\x42\x68\x88\xc5\x72\x84\x5d\x5f\x54\x64\x7a\xc2\x2f\xc4\x65\x57\xfd\x21\xac\xc7\x5a\x4e\xc3\x69\x49\xdf\x3a\xae\x36\x15\x55\xd3\xda\x76\xae\xdd\x17\xee\x07\x05\xe1\xde\xe8\xe0\xd1\x1b\xc2\x5d\x13\xda\x8c\x73\xcb\x44\x01\xf6\x9e\xbb\xa1\x60\x54\xb2\x73\x76\xab\xc5\x03\x13\xc8\x17\x0d\xd0\x9e\x06\xb3\x2c\xc7\xdc\xbd\xd1\xea\xe9\x75\x57\x7d\x62\xee\x76\x17\x2f\xf9\x17\xd2\x5e\x4a\x2e\x52\x60\xee\xf5\xa5\x9f\x7b\xb9\x95\xf0\xad\x04\x92\x1b\xe3\xda\x26\xf7\xd7\x7e\x0a\x13\x5f\xe4\xc6\x23\x14\x92\xa9\x40\x0c\x94\xfe\xcd\x46\xe3\xef\x6c\xca\xb8\xfc\xae\xc4\x97\xef\x82\x0d\x08\x85\x70\x11\x0d\xd0\xae\x4d\xf5\xb6\x40\x4a\x39\xbe\x42\xdc\x1d\x62\x10\xc0\xdd\x3e\x86\xb0\x5b\x38\x07\x7a\x45\xb3\x9e\x8f\x98\x12\xa5\x4e\x3e\x20\xee\x0e\x40\x3b\xc9\xea\xdf\xb4\xeb\xac\xfb\x84\x37\x9b\x0e\x55\xeb\xc5\x0d\x9b\xcd\xd0\xa5\xfd\xfe\x73\x45\xc8\x9b\x28\x91\x8c\x33\x81\x9c\x70\x14\x85\x37\x0e\x3c\xe1\x28\xc4\x18\x14\x09\x59\xcd\x85\xe1\x32\xd6\x0a\xfb\x0a\x97\xc4\x3b\x8e\x42\x98\x0a\xd4\x51\x8d\xe8\xc5\x17\xd1\xa5\xaf\xfe\x68\x27\x43\x21\xc4\x86\x96\xbd\x5c\x2c\x19\xee\x95\x6a\x27\xed\x68\x98\xae\x62\x49\x6a\x20\x7a\x2b\xed\x14\x84\xbb\x89\xbc\x1b\xb1\x95\xc1\xae\x0b\xc4\x21\xc5\x7e\xb6\xf8\xab\x18\x6c\xbd\x92\xab\x01\x39\x4e\xf4\x2c\xd2\x4f\x6a\x19\x94\xaa\xa6\x2c\xa3\x84\xd8\x25\x84\x44\xa9\x91\x6a\xea\x50\x6d\x62\x0a\xcd\x5f\xf7\x87\xbb\x4d\x08\xa1\x5a\x69\x74\x7f\x10\xda\x0d\x63\x2e\x23\x9e\xb2\x05\x77\x05\x1b\xc7\x53\x56\xed\x68\xa6\x76\xb7\xb0\x34\x96\x44\xa0\x96\xb2\x75\xec\x27\xd7\x57\x06\xee\x0f\x90\xa4\xaf\x59\x07\xf0\xdc\xbd\x22\xb1\xd5\x6b\x90\x12\xed\xe8\x06\x41\xa4\x4b\x81\x92\xb4\x97\x1e\xee\xf6\x84\x4b\x7d\xc5\x44\x7c\x01\x92\x74\xd4\x12\x15\x6e\xe0\xef\x12\x92\x36\x9b\x9a\xa7\x84\x04\xc9\x66\x53\x75\x61\x3c\x79\x27\xe2\x09\x1d\x52\xb3\x95\x01\xda\x59\x02\x4f\xb1\x02\x9d\x08\x3d\x71\x9f\xb1\x01\x4d\x47\x12\x61\x88\x70\x97\x91\xd0\xbd\xee\x9a\xb8\xe1\xe5\x80\x78\x86\x29\x61\x88\xe2\xae\xb6\xaf\x95\x93\xa8\xd0\x74\xe2\x76\xbb\xab\x60\x2e\xe2\x4b\x05\xa6\x74\x94\xc9\x22\x44\x54\x5b\x60\x72\xb9\xc0\xfd\x41\x38\x0c\x16\x48\x00\xc5\xc0\x97\xe7\x2d\x83\x10\x06\xa2\xd9\xbc\x9f\xd0\x24\x89\xa6\xcc\x4f\x54\x9d\x87\x3b\x4a\x32\x51\x8c\x2d\x34\xe6\xbd\xf5\x63\x61\xc0\x72\x31\x52\x4f\x11\x3d\x77\x76\x57\xcd\xbe\x42\x8c\x36\x33\xce\x8a\x23\xea\xb2\x1e\x77\x13\x26\x8f\xa4\x14\x51\x90\x4a\x86\xcc\x09\xb3\xac\x5e\xeb\x35\x5e\x14\xf3\x73\xef\xef\xd5\x01\x29\x61\xee\x40\x73\x9b\x78\xa9\xbe\x93\x0f\x28\x85\xd5\x75\x9a\x4f\x65\xbd\x53\x3a\x4a\x59\xc1\xea\xaf\x58\x78\xc3\xfa\xd9\x4f\x6d\xc8\x23\x24\x55\x6b\x42\x9b\xf8\xf0\x62\x21\xc5\xdd\xfd\x2c\xe2\xfd\x78\xb6\x82\x6d\x48\xc7\x78\x1c\x4e\x35\xab\x74\x8d\xda\x57\x38\x4c\xef\x17\xe0\x64\x03\xe3\xc0\xfd\x90\x49\xdf\x12\x9b\x06\x82\x6c\x79\x4a\x34\x29\xc3\x34\x2c\xaf\x58\x65\x0b\xb8\x28\x02\xd8\x87\x19\xef\x00\x0f\xdb\x07\x9c\xaf\x84\xad\x1e\xde\x6f\xfb\x02\x84\x2f\x21\xf1\x19\xc8\x4c\x47\x80\x34\x57\x16\x0a\x23\x4c\x19\xa8\x64\xb9\x75\x44\xe5\x28\x89\x0e\xeb\xcc\x19\x13\x57\x62\x82\x92\x26\xd4\x62\x4c\xb7\x08\x31\xac\xa0\xb3\xa5\x7b\x6c\x47\xbf\xb0\x2d\x29\x57\x6a\xef\xf4\x80\x69\x67\x2b\x59\x6d\x23\x52\x3c\xf5\x37\xcf\x49\xa9\xed\xb5\x38\xed\x96\x69\x89\xdc\x55\x4a\x18\x77\xfb\xc0\x7c\x06\x03\x5f\xed\x03\x81\xcf\xdd\x60\xb1\x50\x8c\x81\x92\xce\x22\xb3\x6b\xd1\xcc\xaa\xb5\x5f\xf1\x34\x8f\x20\x56\x95\x43\x44\xc2\xc2\x67\x49\x22\x42\x48\xc1\xe1\x07\xcd\x66\xa4\x56\xea\x80\x84\x17\x91\x9a\x1c\x8a\xb7\xab\x0e\x18\xd8\x6d\x45\x42\x6f\xc4\x37\xb8\xab\x1e\x84\xda\x91\xf5\x86\x15\xd4\xc6\xce\xbd\x01\xe1\xde\x40\xa0\xc6\x4f\x97\xf3\x0e\x83\x22\x72\x4d\xf7\x57\x07\x18\x04\xb8\x08\x4b\xc9\x89\x4d\xd4\xde\x0c\x23\x22\xdc\x6b\xe8\x93\xad\x0e\x4c\x55\x75\x7a\xb3\x9e\xaa\xcd\xba\x4f\xb6\x3c\x58\xda\xb1\x93\x5e\x42\x2e\x12\x98\xaa\x1d\x3b\xc9\x22\xc3\xd5\x8e\x3d\x25\x53\xf7\xa6\xd8\xd9\xb6\x89\xc8\x50\x6d\xaf\x47\x35\xea\x8d\xc8\xc5\x08\xb6\x15\xaa\x91\x41\xb5\xad\x50\x6d\x93\x6d\xf7\x26\x6f\x62\xbf\xd9\x4c\xb2\xe6\x6c\x11\x32\xca\x1e\x7b\xf5\xd9\xe0\x23\xd4\x5f\xb7\xec\x89\xd7\x95\x87\xe5\xf1\x05\xe3\x25\xe4\x17\xf2\x52\xcd\xc4\x0b\x79\xb9\xc2\x45\x88\x12\x18\x61\x3f\x21\x84\x8c\xf0\x7c\xae\xeb\xd9\x01\x06\x23\xd3\xc5\xaa\xdf\x95\xda\x22\x81\xb5\x3a\x4b\x7e\x75\x3d\x08\xdc\xa5\xda\x67\x49\xb3\x31\xd8\xd5\x26\x72\xba\xe4\xa2\xd7\xe8\x26\xc5\x0a\x81\xbb\x7a\x08\xd3\x12\xc4\x6d\x0e\xb1\xeb\x6b\xdf\xf4\x95\xae\xe7\x6a\xed\x32\x09\xc8\x4c\xcd\x92\x3e\x08\x25\x7a\x04\x19\x3d\x7b\x7a\x4e\x74\x03\x22\xdc\xa8\x74\xe6\xda\x2d\xc8\x21\xf7\xcd\xec\xb1\x8d\xd3\x16\x35\x69\xb6\xa5\x77\x0b\x67\xb5\xda\xdb\x32\x4f\x71\x0f\x21\x6a\xd7\x8e\xad\xca\xa9\x52\x74\x73\xbe\x80\x7d\x8b\x62\xeb\x90\xb2\xa8\x09\x18\xb9\xa2\x56\x86\x95\x70\xac\xc4\x9a\x2d\xbd\xb5\x2a\xf1\x24\x7f\xda\x2d\x9e\xf6\xf4\x53\xcf\x04\xa1\xf4\x50\x4c\xf8\x45\x7a\x89\x95\xe6\x68\x22\xa8\x71\xb3\x99\xf1\xef\xac\x44\xce\xbf\x0d\x0f\xca\x64\x1e\xd9\x6c\x22\x14\x92\x18\x2b\xe1\x04\xc5\x84\x62\x77\x5b\xbb\xb8\x43\x97\x42\x9c\x9d\xe4\x42\x8c\x30\xe3\xcb\x31\x72\x7d\xe5\xb7\xec\x65\x02\x98\xec\x39\x4e\x2e\x4a\x49\x55\xc1\xae\x79\x6b\x78\xa9\xd6\xd5\x14\x5b\x1a\x40\x9c\xb3\x57\x7f\xf9\x10\xd1\x45\x7a\xa9\xd0\xa8\x9d\xc2\xcf\x3a\x39\x3f\xf5\xa6\x6a\x84\x54\x75\x76\x9d\x20\xdd\x6d\x51\x16\x8d\xa3\x7b\xaf\x42\x74\xa4\x18\x62\x64\x1f\x1b\x2d\x0f\x7f\x5b\x3c\x3c\xe7\xdc\x4c\x73\x6e\x06\x9c\xa4\x39\xa3\x13\x84\xe6\xcb\x4c\x1c\xf2\x9e\x1e\xd4\x03\x60\x70\x3f\xf5\x05\x44\xbe\x3e\x37\xe1\xf3\x43\x91\xcd\x83\x87\xe6\x13\x07\xe6\xd3\x45\x29\x16\x87\x84\x1f\x8a\x9e\xd6\x5c\x89\xd7\x8d\x0f\xc3\x6e\x9c\x07\x9a\x44\x24\xbd\x88\x2f\xbb\x43\x81\x22\xa0\x17\xf1\x25\x48\x68\xb5\x4c\x5c\x6c\xa4\x0d\x5d\xd6\x2c\xbd\x15\xab\xcf\xfa\x00\x25\xf7\x8b\xdc\xce\x6d\x04\x70\xd5\x8c\x41\xc1\xa0\x21\x20\x51\xfe\x98\x10\x0f\x46\xc4\x83\x3e\x61\xdd\xe4\x70\xd0\x6c\x8e\x0e\x83\xcc\x79\x3b\x85\x6d\x82\xa6\x24\xbe\x48\x2e\xb1\x4b\x61\x4c\xd0\x35\x89\x2e\x46\xfa\xc7\x15\x99\xba\x01\x0c\xc9\xb5\x1b\x28\xc6\xbe\xbd\x45\xc8\xd8\x94\x9a\xc0\x0c\xee\xe0\x16\x6e\xe0\x08\x3e\xa8\xc2\xad\xce\x25\x3c\x57\x05\x5b\x1d\xbd\x09\x7c\x68\x36\xd1\x8c\x7c\x70\x03\xb8\x23\x63\x35\x4d\x27\xe4\x83\x9a\x5f\xf0\xbc\xd9\x44\x37\xe4\xb9\x1b\xc0\x11\x51\x12\x32\xba\x25\xcf\xf5\x87\xa3\x66\xf3\x0e\x0f\x05\xba\x82\x1b\x48\xa1\xd5\xea\x63\x38\x12\x3a\xd7\xc4\x36\x0c\x61\xa4\x44\xb2\x7e\x8b\x5c\x19\x2b\xe4\x87\xfc\xcb\xcc\x40\xf6\x5b\x64\x66\xbe\x24\x2d\xb2\x03\xa3\x16\xd9\x31\xf2\x65\x34\x40\x47\xb8\xdf\x6a\xe5\xb8\xc6\x39\xae\xa2\xa6\xbe\x8d\x37\x69\x91\x4e\xb5\xf4\x1d\x2e\xea\xba\x2a\xea\xca\xa0\x87\x02\xcd\x60\x98\x53\xbb\x4c\x43\xa7\x9b\x3b\xae\xb7\x3e\xcc\xe7\x93\x2d\x42\x6e\x71\x20\x18\xbd\xe9\xd6\x71\xd6\xa9\xab\xd5\x71\xb3\xbe\x8e\x9d\x85\x91\x64\x75\x7b\x6c\x5a\x8a\x16\xb5\x60\xd4\x6a\x2d\xb4\xcb\x21\x39\x1c\x74\xf3\xf6\x58\x83\x6e\xc6\x79\xb9\xa0\x39\xb9\x59\xce\x95\x6b\x78\x4a\x9e\xce\xe7\x17\x97\xdd\x8c\x5e\x6b\xae\x5c\xbb\x01\x64\x02\xd5\x53\xac\x6b\x44\xde\x61\xbe\xa4\xe6\x73\xef\x30\x2c\x9e\x9f\xe6\x1c\xf4\x91\x5a\x39\x33\x3f\x85\x5b\x3f\x84\x3b\xff\x69\xe6\x4c\xba\x11\xc4\xf9\xce\x46\xe3\xcf\x07\x4f\xde\x58\x39\x6d\x8e\xc4\x2a\xb7\xb7\x3e\xda\xa8\x7a\x38\xcc\xf7\x8e\xec\xbc\xd9\xbd\xf0\x53\x38\xf2\x43\x72\x1f\xfa\x1e\xfc\xf4\x19\xa8\x17\x49\x61\x19\xce\xe4\x0c\x55\x9e\x84\x5a\x8d\x52\x7a\x6a\xe8\x86\xf8\xbe\x86\x61\x81\x21\x74\x43\xb2\x93\x79\xcf\x2b\x82\x4b\xe8\xfe\x04\x06\x31\x84\xae\x50\x50\x82\xa4\x06\x6d\xe8\x26\x6e\x42\xee\x67\x7e\x6c\x30\x2c\x72\xea\x5b\x37\x22\x37\x93\x96\x81\x2f\xcb\x3b\x51\xd1\x2e\x5a\x04\x2e\x50\x43\x5a\x4e\x48\x58\x25\x84\x01\x75\x7f\x42\x08\x69\xb6\xb9\x5f\x09\x24\xe0\x31\xa4\xaa\x83\x43\x38\x52\xac\x69\xf1\xa1\x4a\x83\x09\x71\xb8\x17\xa4\x80\xcd\xad\xf3\xba\x53\xee\x43\xbf\x63\xf5\x9b\xb0\xf7\xce\xe7\x16\x57\xda\xb2\x5c\xf2\x35\x6b\xac\x61\x76\x4a\x48\x35\xd6\x30\x12\xb9\xc2\x4c\x2a\xb5\xb7\xd2\x6c\x5a\x05\x90\x90\xc8\x08\xcf\x6a\x08\x92\xde\x73\x45\x8f\x74\x6f\x20\x72\x13\x88\xb1\xff\x48\xbf\x45\x91\x2b\x89\x80\xc8\x4d\x49\x0c\xde\x21\x52\xfc\x2d\x71\x67\xb8\x10\x13\x4d\xf5\x01\x78\x59\xf5\xd8\x7f\xbc\x5c\x10\xa9\xba\x12\xb5\xad\x24\xee\x91\x9b\x10\x61\x50\x6d\x46\x84\x7d\x1b\x07\x86\x2d\xa4\x5a\xd5\x6a\x69\x47\x2c\xd2\xcd\xc2\xff\x2c\x26\x60\xaa\x67\xf1\x88\x48\xd3\xa6\x3d\x2d\x7f\x15\xcc\xbb\x4f\x64\x26\x54\xf6\x8d\x50\xd9\x2f\xc5\x44\x55\x71\xdf\x74\x60\xab\x03\x21\x88\x15\xa6\x21\xb3\x46\xa6\x44\xba\xac\x9b\x0b\xa9\xe1\x55\x34\xea\x9f\xc4\x7d\x96\x14\xdb\xcf\x98\x78\xdd\xf1\xe1\x34\xdf\xc8\xc6\xf9\xde\x73\xa5\x34\x7f\x32\xea\x4d\x2f\xc6\x97\xbe\xfa\xa3\x39\x7c\xab\x45\x5b\xc8\x2c\x7c\xbd\x14\xe8\x21\x19\x34\x9b\x83\x43\x32\x6c\x36\x51\x4a\x38\xda\xbe\x18\x5f\xc2\x55\x36\xb6\x43\x28\xfa\xa0\xd6\x03\x45\x17\x74\x29\x19\x2e\x8a\xfe\xc8\x75\x33\xf0\x40\xb8\x01\xd8\x79\x55\xae\xc5\x92\x43\x44\x9b\xea\x72\x11\x99\xfb\xc8\x9a\x6c\xf0\x54\x3d\x4b\x3b\xe8\xf1\xa9\x58\x25\x28\x0b\x5b\x50\xae\xeb\xca\x12\x58\x2d\x2a\x65\xc9\xc7\x5f\xc8\xc1\x2b\x8d\x44\x84\x5b\xae\x45\x90\x64\xaa\x27\x2c\xee\xca\x15\xe3\x35\x9f\xa3\x55\xaf\x8d\x95\xa9\x3e\xb6\x5d\xd6\x6c\xca\x2d\x42\x78\xb3\x59\x73\x59\x4a\xe0\xd6\xe9\x68\xed\x89\x4f\x40\xb8\x69\x2d\x4e\x3f\x33\xa2\xb9\xa9\xfa\x8e\xa1\xee\xc8\xe7\x39\xd2\x67\x54\x52\xe4\x01\x2f\x64\x1e\x0b\xba\x10\xeb\x4d\xd7\xba\x49\x5d\x98\x5f\x45\x7a\x6f\xd5\x4b\xf7\x9a\x08\x37\xf1\x57\x7d\x22\xf7\xd7\xbe\x6a\xc2\xc4\x17\x6e\xba\xc8\xab\x3e\xf0\xed\xb3\x59\x49\x96\xe1\x45\xba\x91\x0e\x8b\xcc\x2d\x18\xa6\x47\xb8\x35\xed\x2f\xa4\x3b\x2d\x45\x3e\x9e\x87\x9a\x96\x02\x13\xd2\xf8\xb0\x16\xf7\x2a\x05\x19\x51\x45\xbb\xac\xd8\xab\xb2\x9a\x22\x9e\x30\x21\x9f\xb0\x41\x2c\x18\x9a\x0a\x94\xea\x58\x4c\x37\xc5\x40\xeb\xf5\x3c\x56\x2a\xcc\x56\x56\x03\x2e\x8d\x08\xb6\xf3\xd9\x22\x5b\xf5\xb3\x61\xe0\xd2\x3d\xb2\x95\x95\x86\xb7\xa5\x36\x20\xa1\xed\x6a\x6b\x0b\x87\x6e\x42\xcc\x2a\x70\x67\xc5\x90\x3d\x5a\x35\x5d\x73\x2b\x89\xe9\xc8\xea\x97\x68\x60\x9b\x1b\x24\x29\xcc\xc3\xcf\x32\x37\xfc\xb1\xa0\x43\x6d\x27\x2e\xd2\xec\xd8\xfd\x93\x8b\xcf\x17\xec\xd2\x3d\xea\xbe\xe3\x4a\xb3\x24\x84\xa4\x6e\xd8\x4b\xdd\xc4\x57\xfd\xe5\xfe\xd4\xdd\x65\x45\x49\x2d\x90\x74\xef\x4c\x3e\x80\xa2\x01\x65\x52\x0c\x22\xdd\xdb\x2c\xe8\x21\xb5\x83\x1e\xb2\x7d\x3e\xbd\xa0\x4a\xd2\x0d\xdd\x23\x88\xc9\x8e\x36\x44\x84\xbd\xd8\xd4\x15\x67\x75\x75\x6b\xc3\x16\x43\x65\xa8\x43\x57\x5c\xe2\x05\x6b\x36\x75\x54\x01\xb3\x82\x6e\x4c\xca\x86\xea\xd1\x11\xe1\x26\x88\xe3\x6e\xdf\x58\x36\xfd\x2b\xd4\xf1\xf0\x62\x81\x52\x9d\x89\x84\xe8\x25\x8a\x38\x61\x45\xfb\xac\x30\xd0\x57\x22\x8b\xae\x34\xb9\xa5\xd4\x94\x3f\xbf\x9b\xb0\x7c\x6a\xfc\xc9\x11\x77\x25\xbb\x95\x4f\x63\x2e\x19\x37\x47\x0b\x3b\x5b\x6b\x40\x1d\xa7\xec\xa4\x3c\x87\x01\xcd\x4d\x78\x09\xd4\x4f\x8e\x5a\x07\x47\x05\x39\x41\x33\x14\x0b\x48\x5d\x4e\xc7\x0c\x52\x57\x6b\x88\xda\x23\x52\x1e\xe6\xe7\xae\xa4\xc3\x13\x3a\x66\xae\x8c\xdf\xc4\x33\x26\x9e\xd2\x84\x21\x0c\x21\x39\xd6\x9a\x45\xd9\x81\xc0\x4a\xeb\x8f\xae\x2b\x24\x27\xe8\x95\x40\xf1\x05\xbb\xc4\x10\x16\xfd\x79\x87\x5e\xe8\x13\xb0\x10\x56\xe2\x34\x04\x70\x90\x96\x03\x58\x87\x31\xea\x14\x21\x4f\xd4\x9f\x87\xea\x8f\x15\x08\xa9\x0f\xca\xe7\xd1\xfb\xe1\x63\x48\x49\xa8\xbb\x07\x28\x79\x25\xac\x28\x98\xf3\x4a\x2c\x47\x61\x26\x67\x7a\xa1\x91\xb1\x12\x54\x85\x1a\xb1\x6b\x81\xd4\xde\xa5\x76\x0f\xa5\xf2\x2d\x4a\x27\xff\x69\xc5\x3d\x6f\xc8\x64\x55\x32\xd9\x5a\x32\x19\x70\x2b\x54\x62\xa6\x4d\xe3\x33\x73\xbc\x40\x93\x1d\xaa\xa5\x25\x23\x39\x62\x10\xab\xc7\x20\xee\xdf\x41\xa4\x9a\x10\xaf\x6f\xc2\x13\x4e\xd2\xae\x69\x07\xd5\xe9\x04\xc8\x0b\x8e\x1c\x55\xd4\xc1\xe8\x18\x23\xe1\x06\x8f\xb2\xd6\x45\x6a\xdf\x89\x55\xeb\x62\x88\x40\x4d\x6b\x88\x88\x84\x27\x9c\x78\x10\x6a\xa3\x4a\xb8\xd7\x6c\xa2\x9c\x08\xa2\x4f\x5a\xef\x61\xd3\x7c\xf8\x2e\x56\x06\xe5\x08\xf6\x23\x65\x89\x3c\xe2\xd1\x58\x7b\x00\x8e\x05\x1d\xb3\xde\xca\xb7\x95\x98\x22\x2b\x96\x8a\x43\x87\xed\x3e\x38\xf0\xb0\x15\xcd\x73\x2e\x90\xb1\xc4\x22\x99\x9d\xae\xb1\x23\xaf\x29\xc2\xf7\xa9\x96\x4e\xd2\x9e\xe7\xa3\xef\x02\x51\x0c\xda\xd3\xda\x29\x16\x59\xed\x94\x1f\xe1\x20\x7a\x48\xc3\x68\xf6\xa3\x85\x95\x8e\x12\xd7\x3c\xf3\xd3\x20\x49\xc9\x8e\x1d\xa1\xff\x45\x94\xce\xf9\xe7\x4c\x75\xcd\x28\x0e\x75\x8b\xdc\x2b\xb5\x09\xbb\x74\x3e\xbf\x42\x1d\xbc\x58\x1b\x2b\x49\x63\xb8\x66\x95\x70\x32\x7c\x2f\x9a\xcd\xab\x28\x91\xb1\xb8\x73\x87\x31\x12\x18\x38\x32\x19\x20\x74\x4b\xcf\xd6\x7a\x81\x57\x63\xcb\x51\x29\x45\xe4\x83\xa4\x92\x69\x9b\xb9\x03\x16\x5e\x38\x16\x6b\xb3\x33\x6c\x46\x9a\xc9\x00\xeb\xf0\xde\xd7\xcd\xf9\xb6\x75\x7e\x01\x2b\x3c\x26\x7e\x35\x58\x19\x4e\x56\x4f\x2b\xe3\x2d\xe8\x99\x7f\xfc\x63\x61\xfb\xfd\xab\x41\x2c\xa7\x1c\xd5\x82\xf5\xec\xd4\xa6\xf8\xfe\x15\x47\xa9\x0e\x2e\x2b\xf3\x9b\x2e\x7b\x80\x44\xd5\x03\xa4\x4f\x62\x5b\x84\xca\x35\xbe\x1f\x13\xce\xb7\x2a\x86\x22\x3b\x1f\x64\x9f\x90\x12\xb8\x77\x2b\xb5\x17\xdf\xbf\x91\xb6\x2f\xfe\x4d\x36\xd7\x57\x44\xe6\x0a\x7c\xff\x5d\xd8\xe3\x62\x82\xe8\xf3\x00\x38\x77\xc8\x64\xe6\xa4\x7d\x72\xf7\xaa\xaf\xd6\x8a\x40\xbc\x77\xc3\x91\xe2\x69\xd8\x3f\xe2\x68\xa4\x1d\x7e\x66\x11\xeb\x88\x60\x51\x8d\x08\x2e\x83\xf0\xde\xd4\x59\x4b\xbe\x5b\x5d\x88\x4b\x84\xe1\xd5\xa6\x98\x60\x49\x96\x83\x59\x4e\x84\x9b\x84\x22\x1e\x8d\x34\x24\xbc\x5a\xd4\x83\x2a\xab\x2d\xd3\x61\x94\x12\x61\xeb\x3c\x82\xdc\x10\xef\xb1\x9e\xdc\xac\xd6\x37\x6c\xa0\xd4\xb0\xfc\xe7\x79\x3c\x21\x32\x6b\x84\xc2\xfd\x4c\x90\x5f\xe5\xae\x29\x24\xde\x90\x88\x16\x3d\x2c\xad\x7e\x31\xf1\xba\x61\xb3\x19\x1f\x52\xb3\x89\x46\x4a\x9a\x29\x13\x00\x28\xf5\x9e\xf0\x8b\xb8\xd5\xd2\x8e\xb0\x0b\xd1\x6a\x5d\x36\x9b\xa8\xe3\x11\x12\xf5\x90\x6c\xb5\x80\x91\x0e\xf6\x11\x6b\xb5\x40\x67\x88\x20\x04\x1d\xec\xee\x3d\x7a\xd4\x8c\x70\xaf\x56\xce\xef\x94\xfe\xef\xef\x28\xec\x09\xbf\xdd\xc9\x22\xbc\xe0\xf5\xa6\x88\xb3\xc3\x42\x2d\xaa\x56\x21\xab\x94\xe2\x1e\x47\xd2\x4d\xd2\x20\x91\x4a\x31\xd9\xc1\xb8\x27\x5a\x3b\x7e\xbb\xe3\x73\x24\x2f\xc4\x25\xee\x39\x7f\x71\x6d\xae\xbd\x10\x97\xbd\xf6\x8e\x2f\x5a\x1d\xf5\xb5\xdd\x59\x60\xf8\x28\x36\xa5\x77\xa8\xd4\xa3\xa4\x9b\x05\x86\x9f\x62\x65\x86\x85\x2e\x2f\xb5\x30\x9e\x0b\x72\xb2\x9a\x56\xc1\xf8\xaf\xe5\xe1\xde\xa3\xf9\x7c\xff\x61\x99\x9c\x8d\x97\x52\x15\x86\x97\x62\x63\xe6\x0c\xaf\x5b\xf6\x4b\x57\x94\xc2\x69\x8d\xd8\xf6\xde\x23\xed\x9e\x3b\xf4\xe6\x73\x7e\x48\xd2\xcc\x12\xc7\x08\xff\x83\xb5\xd2\x45\x11\x93\x23\xcc\x38\xbc\x15\x1b\xd2\x46\x78\x2b\xdb\xc6\x56\xb5\x6d\xef\xd1\x3f\xd9\x7c\xce\xfe\xb9\xff\x10\x47\x03\x74\xb0\x6f\x7e\x3d\xf4\xb4\x7c\xc8\x0e\x1f\x3f\x9c\xcf\x3b\xde\xce\x21\xcb\xc8\x91\xa4\x73\xf0\x87\x6c\xb1\xf6\xa3\x87\xc6\xae\x57\xbc\xd8\xdf\xef\x56\x5f\xec\x3d\x2a\x89\xe6\x3a\xd4\xb0\xfb\xab\xc9\x9f\x5a\x39\x19\xf4\x84\xa6\x87\x5e\x2f\x5f\x01\x3e\x6d\xf1\xd2\xee\x1d\x66\xc6\x99\xb8\xb6\x0c\x5a\x2d\xdc\x55\x93\x3e\xee\x21\x46\x3a\x20\x4d\xa6\x98\xa5\x49\x1f\xe3\x66\x53\xc1\x2e\x8a\x69\x4e\xb3\x19\x6e\x32\xf3\x54\x7a\xd7\x8e\x36\xac\x31\x4a\x13\xd7\xc1\x09\x67\xb3\xc6\x97\xb7\x6f\x5e\x4a\x39\x39\x33\x62\x88\x1a\x39\x38\x1a\x20\x49\x28\x56\xda\xf2\xb2\x0f\x7a\x22\xe2\xa1\x60\x49\xe2\x54\x38\x4a\xde\xc6\xa7\xf1\x78\x92\x4a\x1a\x8c\x58\xb3\xf9\x4a\xad\x17\x8a\xee\x43\xea\x2b\x61\x80\xf6\x59\x1f\xc2\xc0\xe7\xae\x8c\x25\x1d\x99\xdd\x60\x45\x90\x81\xc3\x84\x88\x85\x53\x89\xf9\x43\x47\x1c\xdd\x0e\xd6\x96\x90\x46\x3c\x5a\x2e\x73\xb3\xbe\x8c\x22\xa8\x56\x60\x95\x9a\xb7\xe2\x58\x46\xf8\x48\x7b\xfd\x93\x49\xcc\x13\xf6\xf1\xec\x0d\x04\xef\xfd\xfb\x70\xe0\x73\x37\x91\x54\xa6\x09\x84\xaf\x8b\xe7\x73\x76\x2b\x17\x10\xde\xae\x38\x3e\x33\x8a\x4d\xf2\x93\x32\xc1\x5b\xb9\x14\x78\x96\x3e\xc6\xf9\x4b\xfc\xc5\x1d\x0c\xab\x33\xd9\x00\x85\xd0\x28\x25\x4a\x85\xcb\x27\xa1\xe3\x37\x1c\xdc\xf5\x0e\x63\x2d\xb7\x85\x19\xc7\x8a\xf8\x10\x79\x10\x2b\x09\xda\x7e\xb5\xd3\x8a\x31\x08\x72\x87\xce\x07\x76\xee\xef\x72\x8f\xb8\x95\xe8\x48\x69\xb5\x3d\xda\x72\x40\x67\x6c\xa0\x3e\xc5\x0b\x7d\x44\xb6\x08\x3d\x43\x5c\x6d\xaf\x47\xa3\xd1\x59\xd6\x2b\x2f\x19\xed\x33\x91\x20\x8c\x21\xb0\x7b\xcb\x1c\xe7\xd2\xbe\x49\xd3\x3f\x87\x3b\x9e\x37\x9f\xef\x7a\xde\x21\xc9\x5f\xe1\x82\x2d\x2a\xd1\x9c\x94\x85\x55\x5f\xc2\x11\x47\xb3\x81\xda\xa7\xbb\x82\x08\x24\x6b\x52\xc3\x8d\x89\xfd\xf3\xd1\xfa\xc2\x68\x32\x30\xb9\xca\xd4\xf6\x89\x38\xa4\x6e\x38\x71\xa9\x16\x29\xa5\xb8\xbb\xe7\x6e\x3c\x61\x1c\xa5\x6e\xf8\xa7\xfa\xf4\x08\xb6\xbc\xe5\xcc\x16\x7a\x6e\xdd\x0d\x14\xd4\x23\x85\x66\x6b\x7d\x26\x9c\xf0\xb6\x2b\xdd\xa0\x6b\x12\xd2\xe9\x20\x92\x6c\xa5\x99\x2e\x52\x2b\xc4\x24\x4e\x73\x03\xa5\x69\x17\xe4\xde\x4d\x34\xe3\x9b\xb8\x01\x70\x77\x16\xc9\xab\xa7\x82\xf5\x19\x97\x11\x1d\x25\x44\xb8\xfd\x40\xad\x52\xe1\x86\xbb\x58\x69\xcc\x6e\xb6\x02\x54\x91\x5d\x97\x9a\x86\xe5\xa9\x0b\x52\x37\x78\x54\x46\x61\x24\x8c\xf7\xd1\x87\x01\x62\xb8\x87\x56\xd0\xe3\x64\xea\x73\x5b\x51\xe0\x98\x83\xf4\xcc\x0d\xb0\xaf\x9f\x6c\x21\xc4\xa5\x41\x2c\x24\xc2\x8b\xba\xb4\x53\x0d\x35\xf5\x20\xf0\xa5\x1b\x00\xad\x2c\x01\x4e\x14\x6b\x28\x8d\x6f\x33\xf4\x7c\x50\xa4\xa7\xd7\x74\x7f\x13\x6b\x52\xe6\x38\x43\xa7\xcb\xdd\xf0\x6d\xb3\x89\x64\x8b\x38\x63\x47\xad\xef\x30\xcc\x7e\x46\x8e\x19\xc7\x72\xfa\x9e\xb1\xe1\xf3\xdb\x89\xa6\x6a\x79\x24\x6f\xa4\x92\xac\x7f\xac\x97\xf9\x79\x3a\x1a\x69\x85\x6f\x9c\x95\xdc\x9c\xad\x16\x32\x2f\xa2\xa7\x56\xa6\x09\x35\xc9\xd3\x03\x41\x4c\xda\x9d\x2e\x6d\xb5\x0e\x79\xb3\xa9\xc3\x61\xd9\x2d\x0b\x51\x88\x71\xb3\x19\x6f\xd9\x90\xdd\x12\x61\x54\x44\x70\xb5\x3b\x30\xc8\xc2\x55\x22\xb5\xbc\xa3\xdc\xce\x4f\xd8\x45\x74\xd9\x1d\x5c\xb4\xdb\xd1\x25\x09\x94\xe0\x1c\x68\xb1\x39\xcd\x33\x04\x5e\x07\xc0\x2e\xbc\x4b\x60\x86\x45\x00\x85\xcf\x3a\x23\x80\x09\x46\xc9\x2b\x2d\x56\x73\xf9\x8a\x48\xc8\x72\x17\x4a\x7b\x5b\x31\x89\xb8\x32\x0b\x92\x57\xa4\xd2\xcb\x15\xa0\x9a\xac\x19\x0d\x50\xd8\x6a\xfd\x93\xa4\x85\x14\x62\x19\x5e\xa8\x18\x6a\xe9\x3c\x0f\xe3\x68\xef\x42\x9e\x89\x5a\xa8\x46\x8a\x22\x7d\x54\x01\x79\x21\x2e\xbb\xf2\xa2\xdd\xd6\xe1\xac\xb7\x12\x31\xdd\xd8\x22\xcd\xbf\x6e\x2e\x87\x12\x7c\xa9\x8a\x9d\x4b\x08\xe1\x73\x21\xea\x63\x78\xb2\x59\x12\x92\xf9\x98\xda\x63\x59\x19\xd9\x2e\x2f\x78\x72\x98\x0f\x2b\xc3\x99\xff\xcc\x48\x20\x45\x00\xab\x39\x18\x44\x21\xe3\xd7\x18\x57\x11\x17\xd6\xfd\x1a\x3c\xc6\xb0\x72\x60\xde\xaf\xd5\x80\x79\x73\x53\xf6\xbb\x8d\xc9\xf5\xfe\xc7\x86\x92\xe2\xf0\x90\x2f\x70\xf7\x78\x6d\xbd\xe2\x9f\xff\xe4\x1b\x92\x28\xff\x53\x7f\xee\x56\x72\x37\xb2\x75\x47\x2d\x8b\xb3\x64\xaf\xb8\x64\x62\x4a\x47\xb6\x16\xf4\x8a\x23\xb6\xf1\x24\x59\x51\x48\xe0\xda\x59\xdf\x77\x56\x74\x7b\x63\x86\x9e\x4a\x70\xfe\xe2\x8d\x46\xa3\xe1\xc0\x0c\xbd\xd2\xbf\x1c\xe0\xb6\x3f\xe4\x4f\xbb\xc4\x1d\x3a\x95\x6b\x1b\xd8\x52\xe2\xbe\xa7\x38\x59\x51\xf8\x85\x5d\xf8\xf1\xc3\x43\x82\x38\x39\xd6\xc7\xb8\x9a\x4d\x7e\x48\x3a\x3b\x3b\x25\xec\x57\x0b\xb6\x00\x3b\x24\x8f\xbd\x66\xf3\x60\xff\x90\x58\xf6\x50\x2e\x57\x42\xee\x3f\x6c\x36\xf7\x1e\x55\x20\x85\x05\x69\x88\x99\xcf\xbf\x9a\x7f\x34\x12\xeb\x3a\x10\x59\xc9\x5c\x50\x5e\x91\x51\x79\xef\x52\xeb\x66\x8a\x35\x25\xa8\xf5\xde\x71\xb4\x4e\xa3\xd7\x87\x24\x6f\x20\x96\x64\x1b\x22\x49\x50\xc5\x52\x61\x1d\x64\x16\x6e\xa8\x53\x17\xf7\x21\x5d\x37\xcd\xee\x50\x2c\x81\xbb\xdb\x3d\xe9\xa7\x3a\x76\x9a\xaa\x11\xb6\x3f\xa6\xa0\xff\x31\x88\x8d\xfa\xb1\xe6\xcc\xa0\x89\xaa\x6d\xef\x68\x0f\xd9\x76\x35\xe1\xae\x92\xbc\x14\x19\x1c\x28\xb9\xd3\xe6\xf5\x00\xa4\x1b\xc2\x1d\x8a\x34\x66\x90\x2e\xd3\xe9\x96\x53\x10\x84\x82\x24\x4c\x9f\xff\x18\xac\x92\x1c\x1b\xba\xd0\x5a\x3d\x70\x86\x42\x09\x26\x79\xb1\x56\x3c\xe0\x58\xcd\x22\x08\x56\xa2\xe2\x84\xc3\x6f\xa0\xe3\x36\xa6\x44\x92\x0e\x8c\x24\xd9\x81\xbe\x24\x1e\x4c\xe5\x5a\xde\xb1\xc0\xb0\xbd\x52\xf6\x2d\xf2\x4b\xc0\x58\x6e\x3a\x6b\x5c\x64\x10\xc6\x70\xb5\x01\xd0\xb3\x01\x87\x1b\x00\x3b\x36\xe0\x64\x0d\x69\xd9\x69\x42\x98\xad\xf9\xbe\x93\x7d\xbf\x93\xe4\x27\xdc\xfe\x02\xc9\x8d\x24\xaa\xde\x05\x1c\x49\xc2\x39\x7c\x90\xe4\x0b\x3c\x97\x64\xc2\xe1\x7a\xf5\x80\xb4\x1c\x67\x01\x4f\xe5\x7a\x8b\xe4\x0b\xe0\xf0\xcc\xa4\xb0\x85\x57\x6b\xe1\x3e\xa3\x19\xfa\x13\x4c\x0e\x1a\x0c\xa7\x92\x6c\x9c\xb4\x5b\xb2\x9e\x21\x9a\xe9\x54\xbd\x66\xc2\xea\x2b\x04\xf2\x6c\xd1\xf6\x04\x85\xef\x92\xbc\x86\xf3\x5f\x20\xef\xe4\x59\xb9\x0b\x91\x3c\xab\x81\x43\x4a\x44\xbb\xa3\x6b\x08\xa5\x2e\xd7\xe5\x84\x81\x20\x29\x48\x42\x55\x05\x5f\xe4\xfa\x95\x7b\x6e\x56\xce\xb1\x6a\xe0\xd9\x06\xb8\xef\x0a\x6e\x86\xbe\x48\xf0\xe0\x4f\x81\x04\x6e\x77\xb0\x49\x23\x7e\x5c\x1d\x82\x3c\xb8\xd7\x52\xa9\x3d\x2b\x59\x78\x25\xb7\xa8\xe8\x09\xbf\xe3\xed\xec\xfd\x81\x44\x5b\x7f\xc0\xad\x4a\xc1\x0e\x6e\xeb\x44\x94\xad\x83\xfd\xfd\xdd\x83\x05\x9c\xac\x59\xc9\xa7\x12\x42\x99\x2d\xac\x37\x7f\x87\x1c\x6d\xda\xac\xd3\x74\x8a\xb8\x12\xd1\x78\xb6\xdf\x77\x30\xf6\xcd\xab\x16\xbf\xe8\x94\xef\x77\x30\xd6\xe2\x0e\x7c\x5e\xd7\x6d\xce\x5f\xfc\x2f\x8e\x9c\xd6\xb5\x44\xbc\xd5\xc1\x2d\xe4\xe0\x86\xd3\x7a\x27\xd0\x33\x99\x25\x49\x81\x67\x2b\x5b\x34\x43\xaf\x55\x77\x1f\xe3\x05\xbc\x5e\x46\xce\xfd\x6c\x5e\xac\xc8\x6e\x64\x1f\x81\xe2\x7a\xee\x2d\x59\x61\xdf\xe4\xb9\x3e\x3b\x26\x51\x44\x19\x56\x5b\xa4\x92\x00\x6e\xdd\x2e\x82\x5e\x08\xa4\x36\x00\x6a\x76\x2a\xa1\x76\xca\x19\x3a\x92\x20\xa4\xea\x70\xa4\xa4\x26\x25\x6b\xeb\xe9\x97\xf6\x1c\xd7\x69\x49\xdf\xb9\xf8\x87\xce\x84\xf6\x8f\x4b\xc7\xcc\x78\xaa\x26\x64\x71\x44\xa4\x51\x38\xe2\xf3\x68\x83\x00\x42\xe2\x5c\x98\xbe\x72\x29\x6e\x39\x97\x8e\x8d\x37\xdc\x84\x65\xc7\xcf\x4f\xe9\x50\x7d\x58\xc4\x0d\xf4\x92\x89\xdd\x20\x3f\x15\x16\x11\x24\xdc\xa0\xe7\x9c\x5f\xb1\xc6\xeb\x24\xe6\xee\x33\x16\xc6\x7d\xe6\xc6\x9c\x9d\x0e\x1a\x54\x36\xae\x93\x98\x3b\x2d\x23\x7e\x38\x70\xa2\xc7\xc7\x77\x96\x40\x1d\xdc\x72\x1a\x03\x1a\x8d\x74\xbe\xb7\x86\xbc\x62\x8d\x41\x3c\x1a\xc5\x33\x93\xad\xea\x5a\xa2\x3f\x05\x8a\xb1\x82\x9a\xd1\xbb\xc4\x77\xba\x35\xc1\x46\x09\x33\xba\x41\x11\xcc\xd0\x99\x84\xcf\x52\x47\xdf\x2c\x38\xa1\x84\x91\x58\x5f\xec\x92\x12\x61\x35\x31\x93\x42\x9d\x33\xca\x1b\x11\x97\x71\x83\xae\x68\x81\x4e\x9b\xc7\xe3\xc6\x24\x4e\x92\x28\x88\x46\x91\x8c\x58\xe2\xb4\x4c\xa3\xd7\xb7\x6f\xcb\x29\xdd\xbf\xa1\x1e\xf8\x58\x27\xb2\xcf\x06\x3e\x22\xba\xfc\x3b\x11\x07\x23\x36\x36\x95\xa8\x26\x6b\x27\xeb\x3a\xac\x2d\xc7\x57\xcd\xd4\x12\x9c\xbf\x5c\x76\x18\x4d\x19\x37\x18\x34\x9c\x83\x5b\xe8\x9d\x40\x33\xf4\x5c\xc2\x1e\xe8\xae\xcb\x5e\x87\x8a\x75\x7d\x94\x1b\x0e\x16\xdb\x9b\x16\x84\xbe\x84\xbe\x3e\x84\x0d\x3f\xf5\x61\xef\x97\x92\x7c\x82\xb7\x6b\x59\xda\x13\x24\xf0\x83\x27\x46\x89\xfc\x24\xc9\x4b\x89\x66\xe8\xad\x84\x1d\xd8\xdd\xc1\x18\xbe\x49\x72\x8b\x3e\x2a\x66\xf7\x49\xc2\x4f\xf5\x1f\x86\x1f\x92\x44\xf0\x64\xfd\x46\xad\x8f\x55\x43\xb4\x56\x18\x37\x75\xbd\x97\xe4\x06\xde\x49\xf2\x0d\xfe\x5c\xbd\x77\xe5\x99\x6a\xe1\xc5\xda\xaa\xf2\x4b\x1c\x7a\x3a\x31\x38\x7c\x95\xe4\x25\x70\x46\x12\x10\x6c\xb5\xa1\xba\x9b\x2b\xec\x33\xc4\x19\xec\xee\x00\x37\x76\xac\x00\xa4\x59\x5f\x59\x57\x4a\x97\x2e\xf2\xec\x7a\xac\xdc\xcd\x4e\x34\xcb\x30\xbb\x8a\x56\xd2\xe5\xaf\xea\x79\x29\x91\x78\xb0\xbb\x53\xf0\x99\xc2\xee\x5d\x12\xe0\xd2\x2e\x27\x33\x24\x98\xe6\x74\x39\x66\xb6\x8c\x59\x27\x24\x64\x39\xe6\xdd\x9d\x3f\x84\xcb\x80\x91\x77\xd9\x88\xed\xee\x80\x6c\x77\x30\x06\x4e\x78\x4f\xcd\x41\x77\x88\x7d\xe1\x0e\x41\x61\x97\x0a\xbb\x2a\xdd\x2d\xce\x86\x7f\x94\xf0\xa7\x82\x1a\xe0\x96\x84\x19\x7a\xa1\x43\xe7\xff\xf8\x24\xb1\x86\x2c\x33\xf6\xd9\x90\xd9\x2c\xd0\x9f\x31\xa4\x8c\x3c\x07\xca\x36\x78\x62\xb2\x4d\x5b\x1c\x7a\x65\xbb\x19\x83\xad\x0e\xdc\x0f\x7d\x06\xcc\x97\x0f\x76\x77\xe6\x1e\x0c\xfc\x34\x4f\x0d\x4c\x32\xc9\xea\x0e\xfd\xd0\x6d\xd2\x06\x98\xae\x12\x2d\x05\x11\x6d\xd5\x48\x22\x81\x99\xd1\xa2\xc0\x30\xa4\x24\xd5\x27\x96\x57\x76\x98\x77\xc8\x8b\x53\xa4\xff\xbe\xbb\x03\x8c\x68\xbc\x12\x78\x5b\x5a\xfe\xbd\x29\xa2\x4c\xd5\xd4\x96\xaa\x06\x0e\xc7\x50\x86\x7b\x7c\x93\x0b\x0c\x31\x5b\x31\x47\xb7\xb8\xbb\xbd\x80\x88\x91\x01\x87\x01\x23\x01\x87\x80\x6d\x16\xe0\x92\xea\xf7\x15\x1b\x57\x3e\xb3\x6b\x01\x5a\x9d\x5a\x60\xd6\x4e\xc1\xaf\xf2\xcb\xcd\x16\x0b\x18\x31\x92\x4a\xe8\x33\xb2\xf9\x9c\xff\x3d\xdd\xf7\x29\x04\x8a\x59\x50\xe9\x33\x08\x12\x5f\x42\x30\xf3\x39\x84\x67\xbe\xee\xcb\x29\x23\x82\xc3\x36\xdb\xbc\x2e\xc7\x8c\x7c\x85\x2b\xb6\x96\x0f\x1c\x76\x7a\xc2\xbf\x43\x63\x35\xf5\xb6\xcd\x29\x45\x35\x6b\x86\x8c\xa4\x1c\x26\x1b\xcb\x39\x8e\x29\xe8\x41\x76\x47\xcb\x8c\xad\x39\x3d\xe5\xe9\x30\x99\x8a\x54\x03\x8c\xec\xed\xea\xb3\x0a\x7b\xfb\x84\xc8\x5e\xc7\xf7\x20\x25\xac\x9b\x96\x91\x4d\xad\x56\x19\x7e\x5b\x4b\xfd\xae\x63\x22\x33\x2f\x55\x71\x98\xeb\x46\x76\x05\xe9\x78\x7f\x88\x16\xb5\xbc\x31\x29\x21\xac\x77\x23\xfd\x5b\x89\x4c\x4d\x6d\xe1\x0b\xbc\x80\xbb\x75\x4b\x42\xe1\x56\x95\xcc\xe7\x33\x34\x65\xe0\xfc\x5f\x0e\xa4\xd8\xaa\xc3\x50\x34\x43\x43\x06\x8e\xaf\xbe\x69\x6a\xb2\x6d\x9c\xea\x6d\xbc\x0a\x1c\x12\xaa\x8f\x7d\xce\x18\x9a\xa1\x2b\x06\x61\xab\xa3\x13\x01\x65\xdc\x86\x96\x9a\xe3\x8d\xec\x52\x52\xe4\xc6\xba\x95\x68\x80\xfa\x4c\x8b\xb2\x13\x66\x82\x89\x69\x7e\xb8\x65\xb1\x04\x94\xc2\x8d\x2c\xbe\x62\xb8\x65\x2b\x37\x23\xd3\x3a\x56\x6f\x50\x9a\x35\xe8\x81\x03\xc6\x00\x95\xaa\x06\xa5\x24\x2d\x53\x75\x4d\xd1\x9d\xa1\xe5\x4a\x4d\x54\x86\x35\x46\x4d\x59\x6a\x13\x94\x81\x29\x4c\x79\x9e\x0c\xb8\x61\x2b\xf4\x05\x43\x89\xac\x53\xc2\x32\x4a\x7a\x0e\x18\xc9\x8f\x99\x0c\xa6\xac\xa4\xe4\x16\xdd\xaa\x2a\x6e\xa5\xe9\x4f\xd6\xea\x80\xc4\x8a\x1e\x4d\x0d\xd3\xf6\xda\x2a\xa8\xe9\x19\x45\xca\xd1\x4a\x0e\x44\xa5\x95\x7a\x36\x23\x44\x66\x84\xfc\x5b\x9e\x5f\x57\x69\x4c\xf7\x92\xc8\x92\x90\x3b\x74\x63\x13\x22\x5b\x1d\x9d\x67\x4d\x93\x21\x6d\x4f\x47\x06\x78\x93\x5d\x77\xf7\x81\x11\xc9\xe1\x39\x5b\x2d\x4e\x7f\x60\xa0\x2f\xf0\xf2\x1f\x3c\x70\x80\xe3\xde\x0c\x1d\xa9\x45\xa6\xab\x78\x08\x1c\x63\xbf\x84\x49\x2a\x40\x1d\x03\xf4\x48\x03\x29\x69\xff\x7a\x79\x45\x76\xbb\xd8\x5b\xc0\x53\x46\x6e\x38\x7c\x25\x4f\x99\x5a\x8b\xaf\xd6\xce\x15\x56\xa6\x9e\x61\x7a\x12\x33\x73\xf8\x84\xe6\xef\xcd\xe4\x8e\x09\x35\xef\x63\x33\x58\xb1\x86\x8d\x2d\x58\x03\x61\xa5\x63\x49\x61\xa6\x03\xce\x66\x3a\x9e\x4b\xdf\xd2\xe0\x52\xd8\xf7\xbc\x43\xd9\xd3\x4a\x92\x22\xe3\x44\x2a\x01\xd5\xbf\x45\xaf\x98\xb1\x99\xb4\x3a\x5a\x04\x2d\x7b\x76\x19\x95\x58\xf3\x55\xd4\x5f\x57\x6f\x64\x38\x65\xeb\x1d\xed\x45\xfd\x9e\x99\x44\xdf\x2b\x93\xc8\xbe\x43\xe6\x0e\x9d\x32\x58\xab\xc8\x87\x12\xcc\xed\x62\x85\x69\x05\xc3\x39\x23\xcf\x39\x7c\x61\xeb\x42\x46\x66\xe8\x9c\xad\xf2\xaf\x3d\x65\x48\xe8\x98\x1b\x83\xe6\xac\x42\xbe\xd4\xa1\x67\xab\x51\x88\xdf\x41\x3d\xcb\x2e\xfd\xc5\x0b\xb5\x82\x4d\x15\xc7\x8c\x7c\xe6\x70\xb2\x76\x4f\x38\xe5\x48\x63\x3c\x66\xa6\x8d\xd8\xdc\x5f\xb0\xc1\xce\xf4\x65\x65\xf5\xde\x02\x90\x20\x33\xf4\x9d\xc1\x49\x86\x0a\x74\xbf\x9e\x31\x14\x4a\x0c\x4f\x19\x3a\xc6\x90\xe9\xa4\x72\x43\x05\x7a\x72\x2f\x30\x7c\xe6\xeb\x6d\x2b\x5f\x58\xb6\x7b\x75\xdf\x70\xf7\x9c\x26\x37\xe4\x3e\xf0\xbf\x42\xe8\xa7\x1c\xfa\xbe\xe4\xc0\xfc\xcf\x1c\x06\xc5\xa1\xe4\x32\xa2\x88\x59\xc2\x03\xfd\xd3\xdf\xea\x40\x10\xf9\x8e\x03\xc1\x75\x25\x8d\xea\xe7\x0a\x5c\xdf\x1f\xc7\x10\x7c\xa9\x40\x3c\xab\x40\x7c\xf0\x3f\x33\xe4\x38\x18\xe8\xc7\xfc\x69\xea\xdf\xd3\xc8\xbf\x91\x40\xaf\xd5\xdf\x70\xea\x73\xa0\xb1\x7e\xf1\x43\xff\xbd\xd3\x7f\x7f\xea\x25\x9f\x14\xe5\x3f\xe7\x4f\x41\xa8\xbe\x07\xa7\xf9\xef\xd7\xfe\x56\xc7\x4e\x0b\x68\xd5\x8f\x66\x68\x16\x6b\xe6\xad\x79\xca\x24\x86\x0e\x70\xec\x73\xdc\x72\x1e\xd0\x49\xf4\x60\xba\x63\xdd\x3b\xf8\x91\x6d\xcc\x2f\xfb\xb3\xfa\x79\xbf\xf6\xf9\x25\x5b\x65\x63\x2b\xd3\xd6\x55\x3f\xef\xd6\x3e\x7f\x62\x1b\x93\xd7\x7e\xab\x7e\xde\xab\x7d\xfe\xb1\xb9\xee\x27\x9b\xeb\x7e\xbf\x19\xf9\xbb\xcd\xed\xfe\x73\x33\xe5\x2f\x36\x77\xea\xd7\xcd\x94\xf3\x74\x63\x69\x91\x6e\xa4\x5c\xa6\x1b\x29\x67\xe9\xc6\x6e\x49\xab\x9f\x77\xea\x2d\xa3\x9b\x8b\x87\xa9\xed\x28\xd7\x1a\x86\x69\x1d\xac\xdc\x25\x5f\xc6\x4a\x43\x99\xa1\x77\x71\x66\xf3\x34\x16\x21\xeb\xca\xe5\x74\xf3\xad\xc6\x77\xe8\xad\x46\x61\x9c\xc7\x65\x02\xa3\xb4\x92\xc9\xf4\x5b\x5c\x77\x4a\x0e\xd1\xb6\xe6\x4a\x12\xe7\x06\xbb\x19\xb3\x1c\xd2\xc2\xdd\xee\x85\x29\xfa\xc7\x2b\x6e\x6e\xac\x88\xb8\x64\x43\x26\xfc\x86\xf3\x8f\x16\x6f\xfd\xc3\xf9\x07\xf6\xd3\x48\x07\x1b\xe4\xbb\x4f\x98\xa2\x2c\x8d\x38\xeb\x1b\xbb\x8c\xc4\x2d\xe4\x34\xfa\xd1\x30\x92\x09\xe8\xfc\xfc\xc3\x58\x9a\x4f\xba\x6e\xac\x33\x21\x46\x88\x47\x88\x6b\x77\x64\x99\x99\x24\xb5\x35\x1f\xeb\x6a\xe1\x14\xf1\xf2\x94\x2d\xd5\xc7\x2e\x02\x08\xc9\x96\x16\x75\xbb\x2b\x7a\xc9\x34\x6d\x8a\xc6\x11\xa4\xc0\x75\x34\x41\xf0\x1c\xb8\x4b\xcf\xb4\xcf\x44\x3b\x32\xa9\xbe\x49\xca\xdc\x16\x19\x16\xa7\xdf\x90\x84\x76\x47\x71\x8f\xca\x10\x51\x8c\x7d\xdd\xe5\x21\x78\x70\x4f\xcf\xb4\x15\xa4\xcc\x8b\x10\x28\xf5\xe6\xb9\xcf\xd4\x88\xbb\x74\x61\x8f\x48\x62\x4f\x8c\xab\x08\xcd\xd0\x76\x64\xd2\xcb\x3e\xd2\xf3\xc3\x6e\xff\xc8\x86\x55\x1d\x9b\x0f\x43\x9f\xde\xf9\xa6\x07\x2b\xee\xb9\xbe\xdd\x5f\x68\x86\x86\x11\xec\x01\xc7\xf3\xf9\x96\x7e\xee\x78\x1e\x70\x6d\x42\x54\x1f\xf4\x0f\xfb\x12\x69\xab\x30\x42\xbc\x4d\x3a\xf8\xc1\xde\xdc\xc3\x6d\xc4\x1f\x74\x3c\x6f\xee\xe1\x16\xe2\x0f\xf6\xf4\x93\x95\x57\xe7\x97\xf3\xf2\x4e\x35\xef\x7d\xac\x43\x5f\xcb\x54\x1b\x69\x05\xe6\x43\x54\x19\x90\xc0\xbe\x55\xfb\x2a\xad\x7a\x29\x23\x38\x8a\x2a\x7e\xc5\x61\xfa\x9b\x4e\xc9\x3b\xf4\x25\xd2\x99\xfe\xcd\x5d\x04\x0b\x0c\xa3\xb8\x82\x69\x62\x63\xe2\x3d\x47\x8a\x94\x39\xbe\x33\xa0\xa3\x84\x59\x3b\xc5\xac\x02\xe6\x6e\xcf\xe7\x8e\xb3\x95\x5d\x69\xad\x76\xad\xf2\x4c\xb8\x0d\xf8\x52\x8d\xf5\xab\x18\xde\x46\xf0\x41\x22\xde\x72\x88\x53\x99\xec\xb7\xe9\x5a\xf7\xa4\xf3\xdd\xc9\xae\x84\xad\xfa\x29\x6f\x2a\x84\x90\x72\x2a\x3d\x36\x53\xa9\xba\x30\x08\xaf\xc4\xae\xfc\x0b\x56\xc5\x7c\xee\x1d\x92\x3b\xf4\x22\x5e\x79\xe7\x80\xb4\x69\x5f\x80\xbe\x64\xe1\xbf\xbc\x90\xba\x45\xc8\x97\x95\xfa\xbd\x3a\x43\x9e\xa8\x4e\x70\xec\xfb\x7f\x3f\xa4\x1b\x37\xa9\xe7\x9b\xb7\x99\x6b\x7b\x61\x70\x77\xbb\xf7\x21\xf5\x9f\xa7\xd8\x5c\x0d\x51\x1e\x15\xdc\x5c\xc7\xab\xcd\x75\x9c\x56\x9b\xf0\x55\x35\xe1\x45\x64\x67\x72\xaf\x02\xe4\x03\x6d\xf0\x58\x70\xe7\x36\x5c\x90\xa2\xef\x69\x95\x45\x7c\xb1\xd7\xeb\x6d\x84\x3e\xa3\x8b\x19\x4a\x22\x48\x23\xf4\x34\x45\x1e\xc6\x70\x9e\xa2\xa3\x54\x3b\x49\x40\x7f\xc9\x3f\xbf\xd2\x2f\xe1\x34\x45\xcb\x83\xed\xfc\xf5\x97\x19\xe5\x75\x00\x5b\xde\x22\xc7\xf7\x1b\xa8\xcc\x21\xc5\xe2\x7a\x61\x7c\x69\xb7\xe0\xac\xb6\xfa\x32\x73\x48\x75\x34\x8e\xab\x40\x3a\x1e\x88\x67\x81\xf4\x65\xae\xf3\x1a\x50\xf9\xe5\x4d\x6a\x3b\xb0\x82\x2f\x65\xdc\x58\x78\xdc\x42\x33\x34\x1e\x80\xe3\xc0\x0c\xbd\x8c\x80\x49\x38\x4e\x15\x17\x1f\x80\x5e\xde\x27\x29\xbc\x57\x75\x9d\x62\x0c\x3f\x22\xb5\xb9\xe9\x6d\xb2\x25\xd4\x3f\x96\x20\x5d\xad\xe1\x56\xdb\x94\xce\xb5\xeb\xe8\x83\x4e\x13\x76\xa6\x17\xef\x4b\x9d\xb2\xfc\x3b\xf0\x65\x67\xd2\x74\x50\x10\x42\x71\x16\x4d\xb8\x5d\xde\x9c\xc6\xed\xc4\xe4\x5a\xfd\xd0\xe4\xbd\x49\xcd\xbf\xb7\x12\x3e\x45\xc8\x19\x44\x23\xc9\x84\x62\x44\x99\x43\x29\x24\x33\xf4\x33\x5a\x2d\xa4\xdc\xa5\x39\xf7\xc4\x0b\x38\x43\x1c\x3e\xa3\x8b\x53\xe4\x24\xd1\x88\xf1\x90\xf5\x1d\xb8\x95\x68\x92\x1a\xaa\xb6\xb4\x15\x43\x0d\x32\x72\x22\x7e\x15\x05\x91\x5c\x86\x60\x39\x04\x0d\xa5\x4e\xd7\x64\x7f\xf6\x20\xcd\x3f\x0b\x16\xb2\x68\xca\x84\x03\xb3\x6c\xe6\x20\x67\x28\xe2\x74\xe2\x40\xa8\x26\x47\xb1\xe5\x7b\x87\x7f\x0a\x14\xe2\x9e\xd3\xcb\x7d\x23\x4d\x05\xe2\x3b\x16\xf7\x7e\x96\x66\xa2\x4f\xdb\x1c\x8b\xdb\x9e\xcf\xb3\xa7\xbe\xf5\xcc\xca\xbe\xe4\xdd\x12\x98\x19\x20\xf3\x40\xcb\x01\xec\xeb\xf1\x63\x90\xbb\x9d\xa5\x4e\x39\x2f\x75\xca\x79\xe9\xb2\xd2\xea\xf4\x2a\x52\x3a\x88\x66\xb5\x21\xe8\x9f\xe6\xd8\xb2\x8e\xf5\xd0\x49\x60\x18\xce\xdf\xe7\x89\x49\x8b\x34\xd8\xba\xa2\x28\xab\x28\xd2\x15\x45\x6e\x08\x92\x20\x4a\x22\xb7\x8f\x55\x79\x42\x5d\x06\x21\x89\x6a\x95\x7a\x40\xdd\x00\x68\x5e\xe9\x12\x0d\xb1\x1b\x40\xec\x86\x10\xbb\x7d\x88\x15\x0d\x12\xe7\x90\x8a\x0c\xa1\xc9\x28\x95\xac\xff\x4a\x1f\xf6\xf3\x3e\xec\x2f\xf7\xa1\xea\x02\xa6\x1b\x98\xea\x5b\x76\x29\x61\x6e\x08\x21\x61\x8a\x2c\xc2\x96\x7a\x32\xd2\x1b\xdc\x80\xac\xe8\x4d\xa9\x37\xae\xb2\x37\xf3\xc4\x01\xa6\x37\xed\x82\x8c\x20\x7d\x85\x16\xd6\xd5\x22\xb5\x72\x18\xd6\x95\x73\x5d\x39\xd7\x95\xf3\xa5\x1e\xcd\x83\x60\x32\x5a\x90\x50\x03\x8e\x57\x0e\x66\x07\x22\x18\x00\x5b\xa2\xc5\xea\xd3\x8f\x4b\x62\xcc\xa7\x01\x4c\xab\x02\xca\xcf\x2a\xcc\x34\x28\x9c\xef\xa5\x22\x5a\x13\x97\x02\xb8\x0f\x43\xa5\xcf\x87\x6f\x95\xa2\x5c\x81\x7d\x5b\x85\x7d\xae\x05\x80\xb1\x6a\x88\xfe\x57\x49\x14\xe3\x01\xbc\x0a\xe0\x65\x8a\x9c\x8b\xf6\xc5\x5f\x7f\x5d\xde\x2f\x10\xfe\xa3\xd5\x73\xe1\xaf\xbf\xfe\xfa\xeb\x7f\x6c\xcf\xff\xed\xaf\xbf\x92\x4b\x07\x6b\x9b\x46\xa0\x0d\xaa\xaf\xe2\xd5\x87\x6c\xc2\x57\x0b\x25\xf8\x28\xfe\x8e\x8d\xc1\xa3\xae\xe3\x7c\xaa\x33\x6d\xe7\x3e\x5f\xc6\xa0\x63\xd2\xbe\x33\xc5\xbf\x38\xc6\x2d\x67\xe1\x54\x16\xf5\xb7\xcd\xda\xd8\x8f\xda\xe6\x5c\xdf\x9d\x9f\xd4\x94\xbd\xfa\xf6\xfc\xbe\xfa\xfd\x71\x5d\x47\xae\x7e\x7e\x54\xd7\x91\x7f\x5f\x40\xbd\xe3\x55\x01\xf5\xbe\x3a\x60\x2f\x6c\x4c\x7f\xa6\xc8\x30\x60\x4e\xc7\xcc\x81\xb3\x00\xa9\x51\x33\xdc\xd1\xa4\x6b\xca\xde\x7d\xc9\xd8\x70\x72\xc6\x86\xec\xd6\x81\xf7\x6a\xab\x08\x86\xf9\xdb\xe7\x3f\x52\x3a\x52\xdd\x3b\x1e\xc0\xeb\xc0\xec\x6a\xef\x07\x8a\x86\x01\xae\xed\xbd\x5f\xd3\xd5\x61\x6e\x9c\x96\xbb\xd9\x0c\x7d\x54\x13\xa1\xb3\xb7\xe7\xe1\xd6\xc3\xce\xe3\xbd\x83\x47\x8a\x43\x89\x43\xaf\x27\xda\x9d\xbd\x03\xef\xf1\x81\x2f\xf0\x03\xfd\xf4\x70\xee\xa9\x55\x6a\x5e\x3f\xfc\x43\xaa\xe5\xc7\xda\x88\xe9\xaf\x5a\xe3\x60\x0f\x76\x0f\xf6\x77\x8c\x1e\x62\x5e\x3f\x3e\x98\x7b\x18\xab\xd7\xf3\x3c\xea\xf6\x9e\x7e\xf2\x11\x27\xac\x8d\x76\x0f\xf6\xff\x48\x5b\x28\xcd\x34\x97\x34\xd3\x5c\x30\x6e\x23\xd4\xd9\xdf\xfd\x03\x09\x82\xf6\xff\xe0\xad\x1d\xfc\xa0\xb3\xbf\xab\x6a\xd8\xc1\x0f\xf6\xd5\xbf\x1d\xa0\xb1\xcf\x88\x68\x21\x71\xd8\xf1\x7a\xbb\x7e\xfb\x31\x86\xa0\xe3\xa7\xad\x3d\xcf\xfb\x43\xb6\xd0\xce\x21\xeb\x79\x7e\xc7\xd6\x62\x04\xad\xac\x9f\x17\x01\xec\xc1\x0c\xf1\x04\x44\xa2\x67\x29\x72\xda\x6a\x02\xbf\x08\x60\x07\x56\x39\xe1\xea\x2e\xb8\x4e\xcd\x05\xb7\x53\x73\xc1\xed\xd6\x32\x2e\xec\xd5\xf2\x33\xec\xd7\x8e\x99\x1f\x54\x6f\x4c\x6c\x3c\xac\xdd\x56\xf8\xa8\x76\xc0\xff\x71\xed\x7a\xad\x8e\x57\xbf\x2e\xab\xd3\xa9\x7b\x01\x3b\x3b\x8b\x05\x9a\xa1\x27\x41\xd6\xe8\x6a\xab\x67\xe8\x6d\x50\xf6\xc6\xb9\xf5\xfe\x93\xf5\xde\xb7\xde\xff\x58\xf3\xfe\xab\xf5\xde\xcd\xde\xef\xc2\x0c\x7d\x2b\x2a\xd6\xff\x6b\x39\xdf\x2c\xb6\x20\x69\x45\xca\x1a\x9a\xdc\xa4\x86\xdd\x07\x03\xac\xb6\xa1\xd2\x01\x72\x1f\x1e\xfb\x9a\xef\x9d\xfa\x5b\xa2\xd9\xd4\xf7\xa1\xcf\xe7\xb2\xa7\x9f\x77\xfc\x5d\xbf\xa3\x6d\x9d\x6e\xf0\xc5\xb6\x22\xd9\x53\x80\x1f\x7a\x99\x34\x1a\x24\x68\xe5\x42\xaf\x07\x09\x71\xad\x61\x09\x2b\xe6\xa7\x71\x8a\xc4\x03\x36\xf7\x7a\xbc\x85\xae\xa5\x79\xc6\x2d\x24\x5b\x4e\xc3\xc1\xd8\x57\x1c\x76\x18\x19\xc7\xc0\x42\x2f\x60\x07\x58\xa4\x64\x7c\x48\x12\xac\x4f\x70\x94\x26\x2c\x8b\xb6\xad\xc2\x1a\xd0\x6c\x6e\xd9\xe6\x80\xdc\x38\x60\xec\x01\x96\x7d\xcb\x6e\x98\x5a\x56\xc6\x24\xa0\xa4\xdf\x69\x02\x1c\xf6\x70\x3b\x7b\xea\x78\x1e\x6e\xe5\x6f\x3d\xcf\xa6\x20\xa4\xff\x7f\x9c\xee\xa5\x51\xce\x6e\x9e\x8e\x67\xe8\xa8\x99\x78\x5f\x6d\x96\x57\x6b\x56\xa7\xd6\xac\x9d\x5a\xb3\x76\x6b\xcd\xda\xab\x35\x6b\xbf\xd6\xac\x83\x5a\xb3\x1e\xd6\x9a\xf5\xa8\xde\xaa\xc7\xf5\xeb\xed\x3a\xde\x52\x33\x6d\x93\x7a\x64\xad\x22\xa4\x3d\x9a\x7a\xd8\x35\xcf\xd6\x37\x3c\x71\xb5\xa2\xf4\x6b\x25\xb3\x69\x86\xad\xdf\xcb\x02\x5c\x89\x4a\x9d\xbd\x83\x8e\x7e\xcd\x2a\xb0\x85\xf0\xae\xf8\xac\x68\x75\x3c\xef\x0f\xde\xda\xfb\x43\xb6\x98\x4b\x5b\x88\xb9\x41\xaf\xe3\xdb\x16\xa7\x81\x4d\x8d\x20\x8a\x38\x22\x08\xc7\x70\x4f\x13\x9f\xb7\x29\x45\x42\x33\x6e\x61\x07\x42\x5f\x25\x4a\x2e\xec\x68\xf1\x90\x26\x16\xb6\xc0\x9e\xe6\x8f\x0e\xf6\x98\x9e\xe8\x0f\x3b\x8f\x3b\x07\xf6\xdd\x32\x09\xad\x6e\xe1\x09\x74\x76\x0d\xdf\xb1\xac\x75\xb4\x1e\xeb\xdd\xdb\x96\xc8\xf9\x70\x15\xa7\xa3\xbe\xbe\xf5\x37\x60\x0d\x36\x9e\xc8\x3b\x07\xfb\x33\x74\x9c\xc0\x54\x22\xe7\xb3\x88\xf9\xb0\xf1\xea\xc3\xe9\xa3\x03\xaf\xd3\x18\xc4\x62\x4c\xa5\x83\xe1\xaa\xa6\xb7\xf7\x6d\x02\xce\xd1\x5d\x0c\xf7\x67\x8a\xd1\x6c\x79\x18\x3e\xe4\x0f\xe7\xf9\xc3\x4b\xf5\xf0\x49\x69\x52\xdf\x19\x48\xaa\xd9\xa3\x6d\x25\x98\xda\xd8\xa8\x66\x57\x1c\xe3\x0d\xe4\x4e\xaa\x86\xa7\xed\x0a\x5f\x0d\x4d\xe2\x90\x53\xc3\xd3\xbe\x54\x23\x1f\xb3\xd5\x21\xeb\x8b\xff\x14\x6d\x75\x60\xcb\xab\x67\xfb\x34\xaf\x3b\xf5\xac\x40\xa7\x68\xcb\xd3\xd0\xb5\x79\x6a\xde\xab\x4d\x19\x61\x9d\x66\xb1\x60\xe6\xc1\x40\xf5\x41\xea\x06\x18\x82\xa1\xcf\x21\x18\xfb\x42\xf1\x6f\x66\x5b\x21\xab\xdb\xc3\x75\x37\x1f\x3c\xad\xa7\x04\x91\xee\x90\x77\x23\xa6\x48\x09\xe3\xf1\x64\xc4\x24\x6b\xd0\x7e\x3f\xe2\x43\x1d\x51\xa7\x0f\x43\x29\xf5\xd9\x17\x6e\xd0\x9b\x48\xd3\xdf\xdb\xfa\x2a\x51\x5f\x95\x7d\x6b\x00\x92\x06\x15\x4c\x27\xbe\x88\x04\xeb\xdb\x96\xa9\xab\xea\xbc\x7a\x97\xc0\x8e\x9a\x56\xb7\xe8\x47\x02\x9d\x0e\x74\xd4\x0f\x3d\xdd\x76\xf2\x5d\xce\xb2\x7b\x2e\xcf\xc9\x4e\x7d\x4e\x4e\xe8\x46\x07\xcb\xac\xda\x01\xa1\x49\x00\xf3\xd1\x0c\xe4\xa9\xb6\x43\xd0\xcf\x5a\x0f\x4a\xb4\x1e\x34\xed\x96\x8e\xbd\x19\xda\x1e\xc1\x54\xc9\xaa\xf4\x83\xf6\xef\xe5\x2f\x94\xe6\xe8\x87\x90\x98\x17\xa3\x11\x50\xed\xbe\xd3\xbf\x5e\x26\x6a\x45\x7f\xc1\x90\x6a\x47\x9e\x80\xe0\x34\x03\xd3\x81\x55\x35\x5f\xde\x9d\xdd\x42\xa5\x3c\x22\x4e\xf4\x98\x61\x77\xbb\x37\x1d\xf9\xaa\x75\x15\xb3\xcf\x2d\xdd\xe8\x11\xba\xd9\xdc\x1b\x47\x76\x75\xdb\x03\x74\x3b\xaa\xae\xc1\x0f\x9b\x8b\x3f\xa7\x9b\xcd\x88\x9b\x3f\x3f\xad\x08\x12\xd5\x7c\x39\x96\x19\xb1\xca\xfc\xb4\xcc\x7e\x6c\x44\xf6\x19\x7a\x39\xd2\x31\x13\xa5\xca\xf6\xd7\x5f\x3d\x63\xda\x71\xe9\x3e\xc6\xd8\x0d\x7a\xa7\x3a\xb3\xbb\x70\x03\x63\x12\xcb\xe3\x47\x85\x1b\xe8\x18\x0b\xec\x9f\x22\xe7\x81\x03\x37\xc5\x71\x22\x5d\xc9\x9b\x11\xfc\x1c\xc1\x39\xe2\x70\x4f\xf7\xb5\x77\x56\xfa\x0a\x4f\x78\xa6\x26\xfe\x02\xeb\xf1\x78\x1e\xab\x77\x96\x49\xd3\x22\x55\x93\xdd\x5d\x4a\x84\x96\x6f\x79\xb9\x4a\xa6\xca\x57\x37\xbf\x4c\x9b\x2a\x3f\xec\xf8\x15\x2d\xac\xfc\xb0\xeb\xdb\xf7\xf9\xdb\x5f\xbc\xf2\xcb\x41\xe5\x43\xb1\xa3\x7e\x1b\xd5\xb6\xd0\x4f\xa3\xda\x1e\xfb\x63\x54\xe7\x3c\x6f\x47\xd6\xe0\x7d\xa7\x1b\x5d\x8d\xe7\xd5\xcf\x9d\xfa\xd8\x7f\xa9\x7e\x7f\x58\xfb\x7c\x56\xfd\x7c\x50\xfb\x7c\x6c\x75\xf4\x1b\x69\x39\xef\x3a\xd9\x35\x24\xdc\x57\x6a\x8f\x16\x35\xb1\xab\x14\xbc\x27\x11\x50\x9d\x80\x19\x57\xef\xf8\x3d\xa9\xea\x33\xbc\x0f\x9f\xd1\xc5\x8b\x11\x72\xe8\x88\x09\xd9\xd0\x7f\xdb\x33\x2a\x78\xc4\x87\x0e\xbe\xc4\xea\xb3\xec\x23\x4d\x40\x55\x51\x7c\x53\x99\xcd\x29\x12\x6a\x2f\x8a\xfa\x3a\x0c\x53\x8b\xd9\x14\x4e\x7d\x2f\x93\xa7\x17\x15\xb6\xf6\xb9\x4a\x44\xd2\x87\xfc\x9e\x88\x80\x55\xd7\xe3\xb3\x2a\xe4\x9f\x23\x70\xae\x04\x1b\x38\xf0\xe0\x7f\x5c\xd3\x29\x35\xf7\xb5\xf8\x0f\x22\x57\xb2\x44\x22\xc4\x09\xc7\xc5\xe9\xcc\x07\x7f\x25\x0f\x86\xe0\x38\x18\xeb\x24\xc1\xb6\x6d\xab\x8a\xf5\x3b\xab\xbb\x4c\x2d\x33\xab\x34\xbd\x58\x37\xb6\x8e\x75\xc7\x69\xf2\x60\xd6\x47\xce\xf7\x60\x44\xf9\x8d\xd5\x63\x5c\xf7\x16\x9c\xe8\x6b\x9f\xfb\x30\xd1\xc9\xd7\x8e\xab\xe6\xa0\x0a\x6b\x56\x32\x16\xb7\x43\x8e\x6e\xfa\x70\x0c\xda\x78\x7f\x5b\x8c\x92\x64\xb7\xb2\xcd\xe3\x99\xa0\x13\xab\x2e\xd1\x72\x7c\xf5\x13\xc3\x0c\xdd\x15\xb0\xb3\x76\xc7\xf3\x34\xd4\xeb\xe5\xd1\xfb\x49\x37\x85\x70\xcd\xe7\x2b\x23\xb6\xc6\x05\xee\x40\xf2\x46\x20\x79\x3b\x4e\xe5\x28\xe2\xac\x1d\xf1\x41\xdc\x08\x62\xd1\x67\xa2\xed\x39\x18\x74\xc7\x18\xfa\x66\x68\x50\x14\x1b\xd0\xc6\x80\xb6\x75\x89\xf0\x8a\x0a\xd9\x18\x8b\xf6\x8e\x26\xf1\x18\x83\xec\x23\xe7\x43\x9c\x8a\x90\xe9\xb6\xf8\xea\xb7\xbd\x91\xbe\xdc\xdc\x5d\xdc\x26\xae\xad\x6d\xc7\x1a\x7d\x63\x1c\x64\x75\x68\x62\xc4\x52\x1b\x92\x71\x4e\xb9\x88\x86\x57\xb2\xed\x35\x74\x2f\x8f\x53\xa9\x36\x72\x98\xa1\x51\x1f\x9c\x34\x61\xa2\x9d\xb0\x11\x0b\xa5\x03\x4e\xc4\x23\x19\xd1\x51\xf1\xb5\x3d\x8e\x7f\xb6\x7f\x01\x32\x63\xc1\x4d\x24\x7f\x01\x95\x11\x12\xc6\xa3\x58\x38\xe0\xfc\x5b\x18\x86\x95\x71\xfe\x07\x29\xbc\xf2\x66\xb8\xe3\x95\xcd\x19\xb6\x07\xb4\xcf\xfa\x95\x21\x4a\x58\x18\xf3\x3e\x15\x77\x0e\x86\xcf\x14\xbd\xa1\xe8\x54\xc7\x6d\x61\x0c\xd3\x3e\x72\x8e\xb5\x9b\xa0\x11\xdc\x35\xe4\x55\x94\x34\x46\x34\x60\x23\xab\x6a\xa7\xa5\x07\xa5\x32\x87\xde\xda\xd2\xf0\xbf\x3d\xc8\x9c\x04\xc9\x03\xce\x66\x3d\xe3\x75\x20\x4e\xeb\x6d\x84\x3e\xd5\x04\xdd\x4f\xd6\x40\x1e\xa7\x88\xbb\xc1\x7b\x37\x78\x5b\x09\x48\xf8\x9b\x73\xed\x2d\x45\x4b\xcb\xb8\x90\x9a\xbe\x19\x8f\xb0\x97\x79\x35\x06\x52\xd5\x78\x85\xb5\xe1\x6b\xf5\x04\x0d\xd8\x68\xd4\x4e\x46\x34\xb9\x6a\xc7\xcb\x53\xd4\x34\xd3\xcc\x51\x73\x76\x07\x36\x91\xdb\xa7\x7c\xa8\x3a\xb6\x42\xb0\xdd\x5d\x4e\x4b\xfc\x06\x25\xeb\xe8\xe8\x6b\x42\xac\xde\xfd\x56\x5d\xd9\xd7\xfd\xda\x19\xdd\x1f\xd5\xef\xc2\xda\x01\xa2\x21\x57\x53\x73\xd0\x0e\x19\x57\x93\xa1\xa8\xd2\xcc\x81\x57\x8a\x15\x06\xa7\xb5\x59\xf0\x64\x05\x33\xbf\x52\x6d\x76\x40\xdf\x6e\xc5\xe1\x4d\xdf\x86\x7f\xbf\x59\xc4\x7a\x47\x37\x9a\x79\xff\x5c\xad\x96\x70\xdb\x87\x97\x29\x06\x5b\x72\x3e\xd7\x27\x5d\xb5\x7a\xb0\x63\x12\xcd\xeb\x1b\x91\x0a\x4d\xa1\x12\xc7\x64\xb7\x63\xd5\x84\xaa\xd8\x98\xb8\x1b\x0c\x32\x53\x93\xac\xda\x98\xf2\x19\x17\x8c\x61\x9d\x8d\x49\x1b\x6c\x17\xd5\xd8\x8c\xaf\x76\xf5\xa7\x88\xeb\x74\x1c\xa5\xf5\x35\xac\xfa\xbf\xfb\x8a\x75\x4c\x52\x99\xf5\xf1\x57\x9a\xf7\xf5\xfb\x7e\x65\x32\x88\x70\x69\x70\x6e\xd8\x5d\x3f\x9e\xf1\x62\x74\xde\x55\x46\x47\xae\x2c\x90\x4e\xd6\x80\xb3\x70\x8d\x38\x11\xc6\xa3\x46\x18\x8f\xda\x34\x95\x71\xc9\x7c\x7f\x93\x47\xc7\x1b\xd7\xbe\xe1\x5f\x33\xf4\xb1\x0f\x5b\x3a\xc0\xa5\x98\xa0\xda\xa7\xbb\x96\x31\x56\x17\xa4\x8d\xa5\x53\xc1\xe2\xfc\x3f\xff\x77\xce\xeb\x2a\x13\x3d\x0d\x37\x8a\x70\x34\xdc\x28\x20\x86\xcb\x1d\x3b\x8e\xd3\x84\xe9\x95\xb6\x2c\xf8\xc4\x6b\xc0\x47\x8c\x4e\xd9\x32\x78\x14\x6e\x94\x1f\x07\xe1\x46\xa5\x29\x08\x37\xae\xb9\x24\xdc\xb8\x62\x47\xcb\xa4\x06\xa3\x74\x45\x9b\xfa\xff\x1b\x27\x4b\x3a\xad\x4f\x16\xfe\x37\x27\x8a\xc2\xf0\x7b\x13\x65\xba\xb9\x3b\xb7\x37\x4f\x94\xf1\xe6\xb1\xba\x0a\x37\x06\xa1\x0c\x37\x4f\x84\xc9\x66\xe4\xb3\x70\xa3\x02\x7b\x17\x6e\x56\x72\x6e\xc3\xff\xb5\xb6\xe0\x9a\xbe\xf6\xd0\xb6\x03\x84\x75\xcb\xc9\x4e\xdd\x70\x72\xb4\x6e\x3a\xf6\xa9\x64\x15\x19\xb0\x26\xd4\x97\xd6\x82\xb0\xd8\x17\xf4\x51\xbf\xf1\x00\xe2\x08\x79\x5a\x1b\x8f\xf1\x2a\xd1\x34\xa4\x23\xa6\x84\xb0\xef\x8d\x71\xcc\xe5\x55\x8e\x1a\x09\x22\xc1\x86\x13\xf1\xcc\x40\xb4\xaf\x74\xb6\xa2\x55\xab\x62\x22\xd8\xb4\xad\x81\x1a\xfd\xf6\x60\xc4\x6e\xb3\x6d\xdb\x4c\xd8\x67\xd3\xa2\xc8\xc7\x69\xb1\xc5\x0b\x11\xcf\x9c\xb5\xd2\x06\xe5\xc3\x11\x6b\x8f\xd8\x40\xaa\x5f\xbb\xb7\x8d\x30\x15\x49\x2c\xda\x93\x38\x32\x88\xb5\xf4\x71\x99\xb3\xd8\x92\x14\x43\xaa\xea\xb2\x3a\x29\x45\xd7\x5d\x2b\x45\xc8\xf8\xcd\xb2\xc0\xa3\xd7\x53\x38\xce\x84\x99\xdf\xf0\x9c\x39\xaf\x29\x4f\x95\xf4\x5a\x9d\x45\xce\x31\x0b\x84\xf5\x3e\x9f\x4d\xce\x5b\x2a\xc2\x2b\xa7\x3a\xa5\x9c\xa3\x89\x88\x46\x4e\x75\x5e\x39\x6f\x69\x5e\x78\xbf\xa8\x2b\xe5\xcc\xa9\x5a\x10\x9c\xd7\xe9\x28\x87\x7b\x58\xe0\x4b\x87\x69\x22\x9d\xaa\x69\xc1\xf9\xc0\x26\x92\x8d\x03\x26\x9c\xaa\x99\xde\x39\x0d\x65\x5c\xbe\x2e\xac\xf5\xce\x49\x3c\xcd\xe0\xab\x33\xda\x79\xc6\x42\xf3\xc1\x72\xc0\x09\xbc\xdc\xff\x5c\x4f\xd6\x75\x53\xe1\xf3\x7f\x7a\x2a\x68\x85\xe8\xd7\x73\xe1\xd2\x0c\xe8\x9b\xa9\xce\xc2\x51\x59\x22\xcf\xab\x8b\x2c\x16\xe0\x8c\xe9\xad\x39\x30\xe7\x40\x3d\xba\xf5\x3a\xac\x58\xc8\x5e\xf7\x74\x23\x8f\xeb\x33\x7f\x1c\xf7\xe9\xa8\xa1\x54\x9c\x46\x72\xa5\x5a\x91\x69\x4f\xfd\x28\x99\x8c\xe8\x9d\xa3\x36\xa0\x38\xbc\x59\xb5\x68\x74\xd1\x76\x3f\xa2\xa3\x78\xd8\xb0\x7f\x64\x3d\x56\x2e\xf7\xe5\x52\xa1\x49\x18\xb6\x1e\xa0\xbe\x56\xcb\xad\x25\x1c\xc5\x09\x6b\x8c\xf3\x2d\x4e\xef\x28\x21\xfa\x30\xb5\x77\x93\x5b\x67\xf5\xd2\x52\x98\x4d\xee\xe6\x1c\x6f\xda\x87\xbb\x10\x74\x14\xcb\xb4\x02\xac\x55\xfe\x86\xa2\x93\x46\xbc\xa4\x44\xf3\xa9\x0a\xe0\x44\xb6\xf7\x1a\x8a\xc9\x5c\xa7\x89\x8c\x06\x77\x79\xdb\x6a\xeb\x76\x86\xbe\xa9\x31\xf5\x54\x61\xfd\xd4\xc9\x87\x7b\x0d\x9d\x83\x38\x96\xab\x7b\x60\x3c\x6a\xef\x34\xea\x3b\x6c\x92\x86\x21\x4b\x12\xb5\xad\x6f\xe8\x98\xa7\x94\x87\x46\x19\xad\xee\xd7\x15\x94\x13\x11\x8d\x0b\xe5\x76\x16\xa2\xe7\x15\x14\x1f\x98\x6c\x3c\xa3\x92\x3d\x38\x8f\xc6\xcc\xda\xb4\xd7\x77\x38\x0d\x6f\xfa\x22\x9e\xd8\xb3\x2c\x9f\xf1\x7e\x0e\x6e\x66\x5d\x38\x8a\x26\x0e\x38\x82\x85\x12\x79\xfa\x92\x0a\x0f\x17\x53\x72\x12\x27\x91\xbe\xc0\x12\x9c\x41\x74\xbb\x61\x76\xe9\x8a\x72\xfd\xee\x17\xf4\x94\xa4\x58\xc6\xe5\xcd\x3b\xfb\xab\xcd\x32\xdc\xe9\x66\xa9\xe2\xfb\x66\xb9\xe0\x7c\x79\x91\x8b\x78\x96\x2c\xaf\xef\x2f\x9b\xf1\x9c\x85\x76\x8c\xc9\x53\xa9\xcf\xf5\x7c\x46\x17\x96\xb6\xec\x80\x76\x62\x38\xac\x1f\x49\x47\x0d\x9d\xb6\x90\xff\x1d\x5b\x81\xb0\xa6\xc5\x73\x85\x45\xf5\x63\x19\xfd\x19\xbc\x77\x83\x77\x3d\xe1\xff\x4d\xac\x33\x34\xdc\x56\xa4\x5d\x1b\x67\x89\x55\xc7\x19\x33\x37\x23\x38\xd5\xf1\x3a\xb6\xda\xaa\x03\x16\x44\x19\xb0\x20\x6c\x4b\xe3\x53\x09\x5b\x4a\xe9\x6c\x36\x45\xcf\x21\x8e\xaf\x9e\xe7\x73\xd1\x2b\xdf\xfd\x87\xe3\x3b\x5b\xfa\x0f\xd1\xdd\x65\x22\xbb\xdc\xe0\x4b\xa5\xc2\x93\xcd\x15\x82\x20\x6f\x19\xba\xa5\x35\x23\xae\x89\x95\xc8\xeb\xf2\x96\x6a\xaf\x06\x4d\x58\x71\x9a\x33\xf4\x63\x0a\xb7\x3a\xf1\xb1\x6e\xaa\x6d\x39\x0e\x33\x33\x6b\x35\x47\x4a\xe3\xff\x9c\x65\x55\x26\x6b\x71\x83\xd8\x78\xcc\xdc\xe0\x91\xf6\x99\x51\x97\xee\x6a\x73\x03\x75\xc3\xbd\x8a\x88\xf7\xff\xd9\x6d\x6a\xb2\x5d\xfd\xae\x33\xfd\x3b\x9b\x34\x25\xbd\x9d\x19\x56\x2b\x56\xea\x43\xbf\xd8\xc2\xe4\xef\xec\x1e\xec\xbf\xc0\xa2\x2d\x5f\xc3\x66\x6d\xeb\xd9\x2a\x95\xa5\x10\x2e\xb3\xa0\xe5\x9a\x68\x39\x61\xbc\x1f\xf1\xe1\x92\xb4\xc6\x6e\x27\xda\xf9\x6b\x1f\x55\xac\x72\xc6\x0f\x2b\x66\x49\x39\x0e\xd7\xdb\xf5\x77\xaa\x53\x4f\xe8\x98\xf9\x0d\xb3\xfb\x69\x67\x44\x30\x5e\xea\x90\x5f\xe1\x38\xea\xf7\x05\x4b\x92\x0a\x1a\xfa\x62\x49\x71\xfd\x18\x56\xec\x5c\x63\x63\xe7\x7a\x6f\x7c\xc5\x3f\xba\xb6\xcd\x30\x6b\xc6\xe9\xb6\x79\xd2\xdb\xf4\x28\x4d\x24\x13\x8d\x0f\x3a\xb9\xad\xa9\xc9\x8a\x12\xd0\x89\x46\x6c\xb7\x8a\xe3\x98\x75\xa6\xbd\x56\xab\x94\xa5\x41\x2c\xc6\x99\xde\x5f\x91\x55\xcb\x36\x86\xf1\xa8\x9d\x8c\x2b\x46\x4a\xd3\x5f\xce\x52\x17\x65\xa0\x1d\xaf\x3e\xb1\x55\x0f\xa0\x2a\xec\x7f\xae\x62\xd3\xee\xdf\xa9\x3a\xb7\xba\x22\x4e\x4c\x78\x67\x40\xfb\x43\xe6\xc0\x96\x57\xe9\xb1\xf5\x71\x15\xa6\x40\x2e\x3e\x39\x2b\x83\x2c\x32\x98\xdc\x97\xb7\x3a\xb4\x22\x03\xca\x2c\x1d\xda\xe6\xb8\x40\x8a\xca\x17\xa3\xcc\x73\x9c\x47\xe5\x32\xa5\xa0\x6e\x0f\xe0\x24\xcd\x22\xf1\x8a\x86\xff\x8e\xf2\x26\x18\xed\x2f\xa9\x6e\x09\x93\x6a\x3f\x5d\x5e\x48\xfd\x28\xa1\xc1\x48\xaf\x24\x24\x6d\x26\x50\x9d\x4e\x6c\xcd\x74\x62\xff\xca\xe9\xf4\x8e\x31\x51\x0e\xea\xf6\x9a\x41\xd5\x5d\xf4\x3a\x04\x9e\xcf\xa7\xaa\xb3\x6d\xc9\xd2\x90\x2d\x9f\xe1\xb4\x5c\x3e\xf6\xb2\xf9\xaf\x93\xfd\x71\x22\xa3\xdf\x5e\x07\xe7\x89\x62\x2b\x9f\x8b\x6e\x56\x7c\xc0\xa5\xef\x71\x16\x26\xfe\x0d\xd6\x2d\xf9\x4f\x4c\x24\x5a\xba\xe4\x26\xaa\x49\xed\xc8\xff\xa2\x06\x3c\x11\x94\x87\x57\xbf\xd9\x00\xe1\xd2\xb7\xab\x36\x8a\xff\x64\xd5\x69\x34\xea\x2b\x6d\xe1\xf7\x6b\x3f\xf9\x17\xd7\xfe\x31\x61\xe2\xf7\x6b\x3f\xfd\xd7\xd5\xfe\x22\xce\xc6\xf4\xf7\x6b\x7f\xf4\xaf\xab\xfd\x8c\x4d\xa3\xbf\x55\x79\xf0\xec\x5f\x57\xf9\xdf\x6d\x78\xf0\xd5\x36\x7f\x68\x6d\x9c\x9e\xbb\xc1\x04\xec\x15\x5e\xd9\x20\x63\x3e\x88\x86\x39\xfa\xf3\x82\x98\x49\x7b\xaf\x74\x00\xd3\xf0\x46\x51\xce\xfb\x0e\x38\xff\x36\x78\x38\x78\x38\x78\x5c\x7c\x1c\xc4\x5c\xb6\x07\x74\x1c\x8d\x94\xf0\x38\x8e\x79\x9c\x4c\x68\xc8\xca\x06\x7e\x2f\x6b\xe3\x16\x71\x97\xcb\x07\x31\x5e\xda\xa6\x4b\x73\x0c\x89\x70\x37\xfc\x94\x47\xf0\xd0\x22\x82\xa7\x88\x98\xd1\x76\x87\xef\x14\xf4\x31\xd3\xf0\x0b\xac\x16\x7a\x79\xdc\x1e\xa6\x52\x32\x91\x94\x64\x9d\xe8\xaf\x77\x53\xe4\x0c\x22\x36\xea\x27\x4c\xda\xfd\x7e\x1c\x89\x44\x36\xfa\xf4\xae\x11\x0f\x74\xcc\xdd\x8c\xb1\x9b\x62\x14\x66\xda\x62\xf5\xb6\xbf\x5c\xf6\x0e\xdd\x4e\xc1\x79\x1b\xf3\xbe\x92\xa5\xb7\x52\x37\x9c\x42\x2a\x31\x98\xf7\x1f\x52\xf3\xbe\x63\xee\x95\xd3\x9f\x0c\xc6\x17\xd5\x79\xa2\xad\x96\x2b\xad\xbd\xce\x49\x2c\x99\xdf\x38\xbf\x8a\x92\x86\xda\xab\x22\x3e\x6c\xa8\x47\x3a\x35\x09\x0a\x47\x71\x48\x47\x8d\x44\xc6\x82\x0e\x99\x22\xfe\x2e\x4e\x45\x23\x50\xaa\xaf\x91\x62\x0b\x63\x49\x2d\x18\x69\x86\xc2\x3e\xfc\x0c\xc1\x68\x9b\x67\xf5\xab\xe3\x56\x3a\x1d\xff\x34\x6a\xc8\x2b\xa3\x84\x3c\xd5\x81\x7b\xc1\xab\xe5\x80\xab\x5d\xdf\xe4\x7b\x12\x76\xfa\xa1\x57\xdb\xc0\x40\x02\x85\xad\xad\xb4\xb8\xa9\xbd\x2a\x30\xb0\x7e\x5d\x3a\x38\xa1\xe6\xd4\xff\x02\x71\x37\xdc\xae\x5f\xf0\x3e\x43\x57\x53\xd5\x80\x5d\xa0\x2e\xad\xdf\x22\x29\x8d\x76\x19\x7e\xc7\x2e\xfd\x6e\x4e\x17\x9f\x83\xbe\xe1\xec\xd6\x44\xc2\xbf\x5a\x33\x7b\xc6\x81\x5a\x0b\xd5\x79\x93\xbd\xdf\x71\x30\x98\x29\x34\x92\x4c\xb4\x03\x2a\xda\x45\x70\xa6\x3d\x99\x46\x99\x0a\xa1\x86\x3a\xed\x67\x47\x21\x29\xbc\x65\x7a\x4d\x4f\xe1\x3e\xfc\xaa\x4f\x49\x19\xc1\x02\xee\xd0\x68\x1b\x8e\xb6\x75\x2a\x2d\xb8\x45\xfd\x6d\xb8\xdb\x06\x73\xbf\xed\x25\xc6\x35\x97\x84\xc6\xf8\x92\xc1\x2d\x4a\xb6\xe1\x46\xea\x0c\x40\xdc\x0d\x23\xf5\xe7\x1b\xae\x47\xb4\x56\xa0\x6f\x25\x52\x1d\x05\x5f\x47\xe0\x38\x15\xf8\x87\xeb\x87\x60\x32\xb5\x13\x8c\x54\xa5\x88\x2f\x05\xff\xe0\x74\x1a\x28\xc1\x47\xff\xd3\x96\xf1\x70\x38\x62\x4a\x7e\x6a\x8f\xfb\xf9\xcb\x91\x36\xe4\x16\x71\x21\xe3\xa0\xbd\xdf\x98\xc8\xf6\x6e\x63\x12\xb4\x77\xeb\xd1\x27\x41\x2c\x65\x3c\x76\xc0\xe9\x4c\x6e\x1b\x49\x3c\x8a\xfa\x0d\x31\x0c\x28\xf2\xa0\x61\xfe\x73\x3b\x3b\xfb\xb8\x1c\xa6\x33\x8b\xad\xd6\x2c\x8f\xb6\x15\x25\x23\x25\x10\x94\xf7\xf3\x28\x88\x8a\x92\x32\x62\x42\x8e\x29\xa7\xc3\x72\x00\xb7\xeb\xa5\x39\x9d\x96\x02\xd7\xc7\x6d\xc4\x31\xfc\xdc\xc6\xab\xc4\xe6\x32\xff\xdd\x8e\x5f\x1d\xc5\x4c\x58\xac\xf5\xf5\xd2\x06\x12\xf1\x51\xc4\x2d\xa3\xed\x72\x8b\xd6\x38\x1c\x6b\xf1\x1d\x9c\xcd\x2a\x4c\x85\xcd\x1a\x76\x0c\x89\x92\x14\x8d\xc0\x58\x91\x19\x3f\xd5\x2c\x72\x75\x77\xf3\xb7\xda\xf7\xfa\x81\xb5\x1f\xb5\xef\xf5\x13\x6b\x4f\xec\xd9\xf4\x82\xa3\x48\x54\x8d\x34\xef\xc3\x4a\xe2\xa5\xca\x62\xcd\x9e\x3e\x6d\x83\x0e\x39\x0c\x5f\xe0\x96\x33\x8a\x82\x07\x41\x1c\xcb\x44\x0a\x3a\x69\xef\xb9\x9e\xeb\xb5\xe9\x68\x72\x45\xdd\x83\x76\x3f\x4a\xe4\x83\x30\x49\x4a\x00\x77\x1c\x71\x37\x54\xaa\xcb\xa7\x50\x0d\xe6\xa7\x6d\xe0\x06\x87\xde\xe3\xe8\x8c\x25\xf1\x98\xb5\xf7\xdc\x87\xae\xa7\x4b\xda\xaf\xcb\xc2\x3f\x6a\x85\xd9\x68\xdc\xee\x53\xc9\x26\x51\x78\xc3\x84\x2e\x58\x7d\x65\x8a\x7d\x0b\xeb\xda\x84\x51\x1c\xbe\xab\x5d\xfd\x31\x08\x37\x9c\xa9\x3f\x23\xdc\x2d\xb2\x01\xdf\x8b\xe2\x49\x2e\x65\x08\xa6\x16\xf7\x2d\x5e\x06\xab\x5e\x86\xd6\xcb\xa5\xbe\x7d\x1b\x22\xe1\x86\x9f\xea\xb2\x47\xb6\xa4\xd4\x52\x2d\x78\xe3\xcb\x50\x1b\x5e\x4a\x16\xb5\xe3\xeb\xeb\x17\x1a\xa2\x98\xd6\xe6\xb7\x5c\x54\x3e\xf3\xe2\x73\x34\x40\x3b\xda\xfc\xa6\x48\xca\xcb\xd6\xca\x94\xb9\x6b\xb6\x4d\xe6\x79\xc5\xe9\x97\x10\x04\x05\x02\x7d\x04\x78\x3d\xd6\xca\xf2\x5b\xd4\x91\x87\x56\x86\x99\xe2\x65\xa0\xaf\x78\x29\x04\x18\x93\x5a\xff\x5d\x08\x7f\x86\xf0\x22\x84\xaf\x21\xf0\x18\x44\x0c\x32\x06\x16\x43\x1a\x93\x67\x1c\x39\xe7\x34\xb9\x71\x30\xd0\x78\x5d\x12\xad\x34\x46\x45\x1e\xad\x2c\xdd\x56\xfd\xce\x28\x3d\xad\x4f\xf5\x96\x15\xbe\x83\xca\x4d\x76\xfa\x3a\x17\xf4\x45\xe8\x5b\xe9\x8a\x13\x5d\x02\xdd\xd3\x59\x79\xf1\x0b\xb3\xee\x3c\xa3\x84\xc1\x89\x58\x75\xc1\x54\x3c\x49\x24\x95\xcc\x01\x89\xe1\x3f\x4e\x84\xcb\xe9\x34\x1a\x52\x19\x0b\x37\x4d\x98\x38\x1a\x32\x2e\xcb\xcb\x8e\xce\x45\xd4\xd7\x66\xbd\x66\x73\x25\xb6\x2b\x9a\x5c\xe5\x81\x57\x12\xaf\x3e\x96\xd6\x15\x6e\x28\xc5\xe8\x4f\x76\x37\x9f\x0b\x77\xcc\x24\xcd\x1e\x93\xab\x68\x20\xf5\x73\xe7\x50\xed\xcf\xa9\x94\x31\x9f\xcf\xb9\x2b\xa9\x18\x32\xa9\x8f\x72\xc7\x33\x3e\x8a\x69\x7f\x3e\x47\xc2\x9d\x08\x7d\x1d\xf3\x33\x33\x17\x10\xd6\xc2\xc9\x95\x60\x03\x10\x44\x75\x0d\x70\xf2\x9c\x21\xa9\x8f\x02\xa1\x14\xf1\x66\x53\xb8\xc1\xcc\x4c\x17\x7d\xc9\x69\x10\x98\x1f\x81\xfe\x91\xb8\xd4\xfc\x4c\x5c\xda\x2b\x1c\x03\x7e\xe6\x89\x90\x0b\x73\xb6\x05\xc2\x27\xfe\xca\x08\x3f\x7d\xb9\x2a\x07\x5d\xb1\x54\x70\x8f\x7d\x73\x79\xea\x43\x5f\x5f\xb9\x1a\xee\xa8\x7f\x77\x34\x8a\x38\x4b\x76\x1f\xeb\x87\xdd\x05\x44\x31\x19\x71\x18\xc4\x84\x72\x08\xf4\x4b\x6f\x01\x89\x7e\x68\xef\x2c\x60\x14\x93\x24\x86\x69\x4c\x46\x31\x6c\xaf\x9b\x52\xf7\xf4\x99\x3f\x8d\x81\x3e\xd7\x91\xf3\x2f\xfd\x20\x06\xfa\x4a\xfd\x0d\x63\x5f\x00\xfd\xee\xbf\xd1\x29\xe0\xe8\x63\x3f\xcb\xbc\x46\xbf\xf9\x8e\x03\xa1\xf4\x8f\x81\xee\xe8\xa3\xdb\xc7\x3e\x87\xf0\xa5\xc2\x12\xdc\xf8\xc7\x10\x8c\x74\x0a\xb4\xa7\x5a\x54\xd1\x1f\x83\x3b\xff\x3e\x2b\xa6\x7f\x6a\xa8\x9f\xea\xcf\x53\x9d\xcb\xed\xa5\x2e\xa0\x8f\x57\x40\x70\xee\x7b\x8b\x05\x86\x71\xde\x9e\xab\xf5\x94\x3f\xd5\x34\x6b\x6a\x4b\x42\xb7\xfd\x71\x0c\xe1\x54\x11\xbf\xe7\x3f\xd3\x69\x56\x4d\xad\x6f\xfc\x20\xd6\x57\x18\xc4\xe4\x3e\x38\x53\x3f\x60\x12\xff\x46\xb2\x56\x0f\xda\x79\xd2\xd5\x98\x30\x0e\x77\x31\xb9\xa7\x43\x45\xf8\x77\x4d\xf7\xad\xfa\x7b\xa6\xfe\x7c\x50\x7f\xce\xd5\x9f\x97\x3a\x3f\xdc\xad\x6e\xc3\xc1\x02\x6e\xf4\xc3\xce\x02\x8e\xf2\xa1\xfb\x10\xaf\xbf\x47\xe1\xc0\xbe\x47\xe1\x79\x3e\xea\xd7\xfa\xe1\xd1\x02\x9e\xe6\x58\x5f\xc5\x1b\x2e\x4c\x44\x1c\x49\x73\x08\x33\x26\x2b\xf3\x11\x2e\x5f\x93\xca\xf1\xbd\x14\x77\xf7\x27\xa2\xb8\x15\x96\x88\xf2\x32\x26\xfb\xb2\x58\xc1\xd4\x72\x42\xfa\x68\x13\xc6\x0b\xf8\x1e\x93\x8f\x1c\xce\x63\xf2\x9a\xc3\x97\x98\x9c\xc7\x6a\x24\xce\x62\x72\x26\xe0\x78\x3d\x91\xf7\xf4\x95\x2f\x21\xb8\x52\x8d\xbd\x35\xad\x3d\x59\x3b\x20\xc2\x0d\x7a\x3a\x33\x61\x68\xb2\x25\xea\x0c\x42\x6f\xd6\x83\x9b\xa4\x86\xbc\x48\x68\x78\x12\xc3\xb1\x19\xc5\x8f\x9c\xc4\x2b\xf3\x01\x43\x08\x31\x44\x25\x75\xaf\x7d\x09\xf4\xb3\x1f\x03\xf5\xfc\x14\xe8\x81\x2f\x32\x62\x7f\xfa\x0c\x82\x53\x9f\x42\xf0\xde\x8f\x20\xf8\xe4\xeb\x94\xe4\x67\xeb\xaf\x8f\xbc\x0f\xfa\xaa\xa5\x6f\x15\x86\x77\x4a\x96\xc1\xf0\x39\x5e\x9d\x19\xf9\x21\x04\x3a\x33\xf2\xb3\x98\x0c\x18\x8a\x30\x44\x9b\xb2\x3a\x3e\x8b\x61\x86\x06\x26\x2d\x9b\xc9\x97\xf9\x3a\x26\x31\x87\x8f\xf1\xe6\x8b\x41\x62\x4e\x66\xe8\x75\xbc\xe1\xdc\xb7\x93\xf2\x89\x88\x43\x96\x24\xac\xef\xe4\xdb\x69\xc0\x50\x66\xae\xcd\xfd\x0b\xd6\x97\xec\x58\x9c\x93\xa4\x93\x89\x58\x2a\xb7\xb3\x24\xb5\x7e\x8c\x91\xf3\x91\xdf\xf0\x78\xc6\x1b\xf2\x6e\xc2\xfc\x86\xd3\xe2\x78\xa1\x96\x8d\xee\xcc\x3b\x14\x41\x99\x9d\xe5\xc9\x9d\x03\x9f\x63\xa4\xbe\xe9\x0f\x79\x5e\x97\xa5\xf7\x66\x5b\x8a\x39\x04\x0c\x9d\x09\x9d\xa4\xe5\x67\x6c\x90\x99\x54\x04\xb7\xb1\xfa\xb4\xa2\x7b\x82\xb1\x1e\x1a\x0c\x2f\xe3\xdf\xbc\xe4\xe4\xed\x86\xc9\x5d\xcb\x41\xaf\xc0\x3f\xc5\xd5\xbb\x1f\x79\x91\x73\x79\x7d\x56\x38\x9a\x5d\xc6\x97\x1d\x97\xc1\x76\x52\xbe\xec\xb4\x54\x76\xbb\xac\x0e\xe7\xd6\xbb\x3f\x70\x92\x2a\x79\x10\x5b\x65\xb5\x4e\x91\x5f\xaa\x60\x19\x99\x5f\xc6\x20\xe7\x73\x66\x22\xcc\x2b\xdf\x74\xe2\xab\xfc\x9b\x92\x72\xd4\x62\x87\x6f\x31\xf9\x14\xc3\x8f\xdf\xed\xa1\x27\xf1\xa6\x3c\xfd\x26\xa3\xd6\x50\x2d\x27\x69\x72\x69\xe9\xb5\xf1\x3e\xe7\xfd\xef\xe2\xf5\xd9\x44\x7f\xc4\xf0\x3e\x86\x5b\xf4\x24\xb6\x12\x83\x69\x69\x4c\xb3\xbd\x3f\x63\x82\xde\x85\x9a\x77\x7a\x2b\xd3\x1a\x9a\xdc\x82\x4a\xaf\x55\x4d\xef\xe5\x29\x0a\x3d\xc5\x5b\x6a\x69\xc0\xde\x19\x94\x2f\x62\xf2\x5a\xc0\xd7\xf8\x17\xf9\xdd\xcd\x90\xea\xc4\x63\xea\x6b\xaa\x24\x4f\x9d\xcb\x90\x42\xbb\x53\x64\x3e\xd6\xf5\x7d\x40\xa9\xce\xa5\x71\xe8\x65\xf9\xc5\x18\x84\x7e\xaa\xf3\x8b\xa5\x3a\xbf\x98\x50\x7d\x22\x81\xfa\xa9\x4b\x17\xb8\x9b\x12\xc4\x88\x46\xb4\x83\x7b\x28\xcb\x01\xdf\xea\x80\x24\xb2\xd5\x81\x0e\xf6\xb3\x77\xd4\x24\x85\x6f\x75\x30\xa4\x7a\xd4\x5e\x8b\x55\x9b\xc0\x8a\x5e\x99\xa2\xaf\xb1\x11\x5f\xed\x74\x6b\x6a\x55\xf2\x48\xb5\x5e\x44\xff\xcd\x53\x38\xd0\x36\x97\xb0\x32\x09\xcd\x39\x85\x14\x4c\xee\x74\x05\xa2\xc9\xa3\x3a\x0f\x10\xd3\x0d\x94\xd1\xea\x04\xcf\x22\x82\xa9\xbe\x8e\x04\x58\x44\x7e\x40\x1a\x91\x38\x05\xba\x12\xd8\xd3\xe7\x6a\x4d\x7c\x57\xb3\xb9\xf5\xe0\xe2\xaf\xe4\x36\x88\x2f\x1f\x98\x03\x5a\x5c\xdf\xe1\x48\x5a\x1c\x13\xc2\x75\xc2\x2e\x93\xf6\x39\x8c\xc8\xaa\xcc\x95\x8f\x0f\x4d\xf6\xc8\x55\x79\x27\xa9\x6c\x8c\xe3\x44\x36\x1e\x6f\x4c\x3b\x99\xdf\x82\x1f\x21\xc7\x73\x15\x67\x5c\x97\xf5\x72\x30\x8a\xa9\xac\xe5\xbc\x64\x11\xea\xb0\xdd\x3f\x84\x4e\xb9\x60\xa7\xaf\x84\x38\x22\xa9\x84\x28\xfa\x45\x4e\xfc\x46\x1c\x21\xde\x3a\xf0\xfe\x10\x7f\x1c\x78\x7f\x74\xd8\xae\x7a\x46\xb2\x4d\xb1\xfe\xa1\x90\xb3\x96\xbe\xdd\x6e\x50\xd9\xa0\x68\x79\x45\x58\x48\x38\xc4\x44\xac\x9f\x17\xe1\xdf\x63\x6d\x26\xd3\x9f\xda\xbc\x85\x1b\x2e\xf3\xb5\x74\x15\x5f\x4b\x0d\x5f\x33\x89\x38\xb5\x5d\xaf\xce\xdd\x52\x98\x21\x0a\x86\xff\x99\x0b\xd0\x21\x88\xd6\x5f\x77\x34\x30\xf3\xc9\x48\x12\x49\x44\x82\x08\x46\x9b\xc1\x9f\x14\xe0\xfd\x68\x29\x95\x75\xc6\x90\xd7\xf7\x11\xcb\xfa\x68\x6d\xff\xf0\x95\xab\x45\x6a\xf6\xaf\x17\x2e\x86\x69\x44\xfa\x11\x6c\x47\xbf\x79\xc5\xd7\x38\x22\xcf\x04\x5c\x45\x24\x48\x61\x18\x91\xb7\x30\x89\xc8\x11\x3c\x13\xcb\xf3\xbc\xd8\x4e\x8c\x49\xd7\xd8\xe1\xcd\x85\x01\xc5\xfd\x16\xa3\x14\x51\xeb\x56\x3f\x51\x36\x6d\xb7\xb3\xbf\x7b\xc0\x0e\xfe\x40\xac\xdd\x79\xfc\xd0\x53\x8a\x58\x96\xe2\x00\xa5\x87\xbb\xf3\xf9\x56\x3f\x45\x0c\xf7\x68\xbb\xe3\x53\xdc\x42\x53\xf5\xab\x3d\x4d\x91\x06\x2e\xa3\x75\x52\x35\x51\x45\x4b\xe2\x45\x26\xc0\xa4\xf5\x14\x17\xbb\x9d\x43\xda\xd3\x74\xf8\x22\x97\x5f\xac\x9b\x23\x1e\x1f\xd2\xf9\x7c\xe7\x31\x21\x84\x36\x9b\x59\xa5\x39\xf4\xce\xc1\xc3\x47\x7b\x6c\xbf\x6e\x4c\xad\x60\xdc\xf7\x1e\x3f\x3c\x28\x60\xca\x44\x19\x9e\x05\xf3\xf0\xe1\xc3\x03\x76\x50\xb7\x96\x57\xd0\x74\xbc\xdd\x83\x47\x05\xcc\xc1\x4a\x34\x9d\x5d\x6f\xef\xa0\xa4\xe7\xe1\x6a\x44\xfb\x07\xbb\x16\xd1\x8f\x56\x03\x3d\xda\xed\x1c\x3c\x2a\x80\x1e\xaf\xac\x6e\xc7\x7b\xfc\x78\x7f\xa7\x00\x2a\x73\x74\x54\x50\xed\xec\xee\x3f\x7a\x68\x41\x75\x56\xe3\x3a\xd8\x39\xd8\x2f\xbb\xa9\xb3\xb3\x1a\xd7\xa3\x47\xfb\xa6\x33\x6b\xc2\xa2\xcd\xf0\x74\xb4\xb0\x66\x78\xd7\x12\xa5\x26\x83\xe1\x62\x01\x33\x34\x8a\xac\x3f\x69\x84\xfa\xe8\x7b\x9e\xd7\x31\x4a\xd1\x1e\x86\x24\x45\x4e\xdb\xc1\xd6\xcb\x1d\xfb\xa5\xfe\xad\x3e\x6e\x58\x2a\x3b\xf6\x52\xb9\x8b\x7e\xff\xe6\xb7\x5c\xa6\x10\xd9\x95\xbf\x3a\x0f\x9f\x5a\x33\xc8\x03\xe9\xd2\x5c\x56\xdb\xb2\xee\xa8\x60\x44\xdf\x5b\x85\x18\xa1\xd8\x2d\xee\xde\x60\xd9\xe5\x2f\x33\x34\x8b\x40\xe8\x8b\x7f\x41\x9a\x3b\x5f\x6e\x23\xb2\x9d\xc2\xcd\x06\x95\x81\xff\x81\x14\x53\x6f\x99\x0b\x1a\x56\xec\x5d\xb5\xe4\x9b\x95\x2e\xb5\xff\x98\x5c\x99\x51\x96\xdc\x27\x45\xce\xb9\xb3\xdc\xad\xfe\xaa\xbe\xf6\xad\xbe\x86\x4a\x3d\x06\x67\x2a\x0d\x9c\xab\x10\x86\x11\x56\xef\x3c\xe3\xa9\xcc\xa1\xa7\xd1\x9a\x64\xf6\xaa\xdc\x37\x43\x48\x7d\x32\xdc\x44\xbf\x2a\xdf\x31\xe5\x5b\xa6\xfc\x4a\x98\x76\x06\xa3\xe6\xcb\xe5\x2f\xda\x56\x7e\x4f\x23\x34\x40\x51\x04\x1c\xbc\xfc\xff\x58\x49\xa8\xe6\x9c\xf5\x33\x81\xe1\x43\xb4\x41\x4b\x56\x42\x72\x45\x3e\x7e\x1e\xfd\xad\xbb\xdf\x8a\xab\x07\xad\xab\xd5\x74\x78\x94\x39\x19\xae\xa7\x52\x28\xf5\x2d\x1a\xf6\x15\x6a\xf9\x0a\xac\x83\x3e\x8f\xea\xa0\x8a\xa6\xeb\x15\x6b\x86\x70\xa4\xa8\xef\x40\xe8\x1f\x43\xdf\xef\x40\xe0\x7b\xaa\x11\x3a\x9b\xc2\xa2\x22\xd0\x6c\x4b\xa4\x31\xab\x2d\xfe\x18\xeb\x94\x2e\x6a\x07\x5b\x60\x78\xba\x0a\xf1\x0c\x5d\x47\x7a\x5b\x5d\xc2\xf1\x9d\xc1\x38\x05\x2d\xf8\x18\x24\x54\x21\x79\xbb\xac\xee\x9a\x4d\x48\x67\xb2\xb1\x91\x28\xad\xb4\x4c\x64\x2d\x33\xad\xd4\x0f\x98\x41\xa5\x95\xd3\x57\xd1\x1a\x15\xa0\xe0\x12\xed\x4e\xfd\xfe\x31\x60\xe6\x2a\xa1\xd3\x68\xfd\x5d\x38\x3a\x05\x65\xea\x6e\xcf\xe7\x4a\x69\x28\xde\x30\xf5\x86\xb9\x34\x4b\x5d\x99\x65\xf9\x34\x39\x3e\xed\x9c\x8f\x25\xb2\xec\xe2\x1c\xa6\x33\x7c\xb2\xa5\xd4\x90\x4c\x8b\xd0\x45\x6a\xc8\x3c\xf7\x26\x75\xfb\x40\xad\xa4\x90\x0a\x5b\x08\x69\x96\x93\x32\x26\x4a\x57\x89\x48\xaa\x93\x87\xa6\xfa\xc2\xd5\xd4\x65\xdd\x3a\x95\x35\x92\x62\x88\x8a\xdc\x97\x9a\x3a\x8a\x73\x02\xc3\x1a\x59\xea\x7b\x9e\xd5\x33\xa3\xb0\x68\x41\x41\x94\xc2\x47\x35\x51\x18\xbe\x47\xe4\x29\x9c\x47\xab\x2f\xbc\xa9\x5f\x15\x9b\x55\xa2\x00\x92\x18\x92\x18\x17\x37\x71\xd2\x82\x15\x4b\x9d\x51\x53\xea\x8c\x9a\xd2\x65\xdd\x22\x79\xd6\x77\xd5\x94\x14\xd7\x97\xd4\x14\x9d\x46\x99\xed\xe8\x0e\x9d\x9b\x11\x08\x31\xc4\xf5\x90\x36\x5d\x77\x96\xa9\x54\x7d\xad\x6d\x6e\x16\x9a\xb0\x44\x14\x63\x2d\xbd\x7d\x59\xd5\x3e\x49\x0a\x30\x73\x73\x8f\x1e\x82\xfc\x38\x6f\x79\x15\x68\x35\x1f\x69\x9e\x12\x54\x35\x50\xba\xfa\xba\xa0\xb3\x68\x8d\xa1\x68\x27\xb3\x43\x1d\xad\xd6\xab\x22\x06\xc3\x14\xce\x22\x73\x2d\xca\xf1\x1a\x2c\x8f\x21\xd4\x48\x4e\xd6\xd5\xb2\x0f\x43\x63\xed\x12\xab\xaa\x39\xd1\xec\xfa\x38\x42\x37\x12\x9b\x43\xbc\xb7\x4a\x6a\xd6\x5c\xf3\x4d\xa4\x74\xfb\xfd\x05\x7c\xae\xf4\x4f\xc5\xbe\xd0\x58\x5a\xf4\x9c\xcc\x50\xa4\x15\xdc\x7a\x4e\xe2\x80\x21\x96\xa5\xca\x80\x0c\xca\x54\x2f\xe1\x38\x42\x4c\x7b\xdd\xb8\x15\x6c\xaf\x39\xc5\x33\x93\xb7\x59\xb3\x07\x9d\xeb\x07\xde\x44\x9a\x67\x6d\x30\x7a\x18\xab\xdd\x1d\xfa\x1c\xe5\xa6\xbb\x37\x91\xbe\xe0\x29\xbb\xff\xf8\x59\x44\x0a\x43\x56\x9a\x38\x70\x26\x8c\x61\x8b\xf1\x7e\x72\x24\x1d\x78\x6b\x7e\xa6\x13\xc5\x9d\xfa\xd6\x9b\x44\x52\x21\x6d\x90\x41\xc4\x87\x4c\x4c\x44\xc4\xa5\xb6\x7a\xe9\x97\x79\xb6\xe2\x44\x9b\xcd\x7e\xe6\x66\x33\xca\x79\x2c\xb5\x71\x37\x71\xe0\x48\x9b\xd3\x6e\xd1\x53\x70\x86\x8c\x33\x41\x65\x2c\x3e\x9e\xbd\x71\xe0\x99\xd0\x5f\x6e\xa4\x29\xa4\x33\x2d\x14\xf0\x01\x43\x1f\x8b\x4c\x87\x18\xc3\xeb\xac\x21\x3a\x31\x8b\xa9\xee\x59\x84\xab\x54\x38\xf0\x33\x5e\x83\xeb\x58\x5f\x3e\x04\x1f\x57\xee\x8c\x84\xa3\x2a\xef\x97\xbe\xb9\xe5\x56\xe9\x3e\xaa\x17\x7f\x6e\x50\xca\x4e\x19\x7c\x8c\x74\x4e\x13\xa3\x97\xbd\x5c\x7f\x35\x63\x99\xc0\x1b\x19\x5d\x1a\xc3\xdb\x95\xc6\x03\xc6\xc3\xb8\xcf\x3e\x9e\xbd\x7a\x1a\x8f\x27\x31\x67\x26\x17\x3e\x7c\x52\xa8\x4f\x31\x7c\xdb\xb0\xbf\x6b\xef\xc4\xa9\x39\xd5\xaf\xcd\x84\x3f\xa2\x2c\xb6\x96\xfc\x87\x03\x3b\x3a\x8d\xe2\xd6\x7f\x38\xb0\xab\x9f\x88\x03\x9e\x79\x45\x1c\x7d\xa6\x09\x9e\x44\xe4\x3d\xbc\x8f\x56\x4e\x39\xdb\x26\x95\x12\x89\x04\x46\xf9\x9d\x63\xdb\xf8\x9e\xda\x77\x8e\x69\xf9\x94\xcf\xe7\x14\x52\xb5\xf1\x9a\x1d\x24\x75\x29\x84\x9a\xf1\x2b\x66\xaf\x75\xbb\xb0\x5c\x33\x5a\xdd\xd4\x45\x42\xc5\x44\xf5\xf2\x51\xbf\x04\x09\xdd\xcc\x04\x65\xe4\xd2\x77\x51\xf5\xca\x25\xb9\xc9\x04\x75\x8b\xde\x47\xe6\xb2\x10\x99\x29\xb0\x7f\x46\xeb\xcd\x80\xef\x14\x17\x5c\xc9\xa2\xa6\x11\x5c\xa7\x60\x62\x03\xb4\xf9\x4e\x73\x8c\x4e\x67\x01\x5f\x6b\x8a\x38\x5b\x49\x8e\x11\x10\xb4\x1d\x4f\x66\x06\x31\x8a\xcb\x3b\x06\xc4\xaa\x9b\x37\x18\xc6\xbe\xfe\xb4\x93\x5b\x14\x73\xcb\x5e\xa7\x76\x73\x00\x77\x83\x56\x47\x4b\x74\x6e\xf0\xbc\xd5\xc9\xaf\x10\xf0\xab\xa5\xb8\x4b\xcf\x5a\xf5\xa2\x22\x2f\x56\x5e\xe0\x81\x81\x0f\x54\xeb\x1e\x2e\x40\x0c\xd6\x0b\x87\xc5\xbe\x28\x2a\x17\xe1\xe5\x36\x96\x50\xdb\x58\xfa\xda\x77\x5c\xdf\xfc\x74\x12\xae\x7c\xfb\x33\x0a\x08\x5b\x71\x6d\x6f\x79\x55\x9e\x2c\xb7\x39\x03\x9e\x56\x24\xc6\x05\x06\x39\x58\x33\xac\x5b\x33\x24\x06\x5a\xbc\x73\xb7\x17\x18\xd8\x60\xfd\xf0\xcb\x41\x6e\x5d\x49\x07\x24\x5d\xe3\xd6\xa9\xae\x83\xd8\xb2\xcd\x66\x96\xd9\xd8\xb2\xcc\xe6\x66\x58\xd5\xdf\xb4\x62\x82\x5d\x74\x43\x82\x28\x41\x99\x15\x36\xfe\x95\x15\x36\xb6\xad\xb0\x4a\xee\x31\x16\x74\x3a\x20\xcb\xda\xc9\xb7\x08\x03\xfa\x1a\x92\x7b\xfa\xd5\xe7\x03\x08\x9f\xfa\xe8\x45\x48\xee\xc3\xa7\xfe\x6d\x0a\xe1\xb9\x76\xae\x9e\xf8\xb7\xe9\x02\xbb\xe1\x53\xf5\xe2\x45\xe8\x86\xe7\xea\xdd\x8b\xd0\x0d\x4e\xfe\x5f\xee\xde\x44\xbb\x69\xa4\x5b\x18\x7d\x15\xa3\xe3\x4b\x57\x75\xb6\x8d\x9d\x84\x49\xb4\x8e\x17\x24\x84\x84\x10\x02\x19\x08\xd0\x1f\x27\xab\x24\x95\x12\x25\xb2\x64\x4a\x25\x3b\x01\xfc\x2e\xf7\x59\xee\x93\xdd\x55\xbb\x4a\x83\x65\xc9\x49\xf7\x37\xfc\x67\xfd\xdf\xfa\x9a\xc8\xaa\x41\x35\xec\xda\x53\xed\x61\x0e\x2d\x90\xfb\x45\x95\x96\xd0\x8b\xb3\x15\x55\x3d\x74\x0d\x80\xbf\x78\x7d\xf6\x85\xe6\x6a\xb7\x1c\x94\x5d\x92\x05\xaa\xc8\xdb\x02\x0d\xb7\x06\x68\x87\x26\x1e\xb4\x8f\x6a\x77\xbb\x52\x4d\x94\xea\xe3\xb5\xc5\x5a\xa0\x46\x66\xf4\xb8\xb2\x76\xaa\x66\x84\x07\x20\xb0\x8b\x93\xa5\xb3\x65\x86\x56\x9c\x10\x9d\x65\x10\x85\xba\xd6\x80\x27\x15\xec\xa6\xe5\x30\x54\xf3\x5d\x63\x9c\xfe\xb9\xce\x80\x00\x64\xdf\x73\x7e\xb3\x7e\xd3\x22\x1f\xda\xdc\x5d\x72\x32\xa4\x30\x49\xc8\x90\x52\x90\xaa\xfb\x4a\xa6\x89\x41\x9e\x84\x62\xdf\xd3\x02\xde\x7e\x08\xfb\x1e\x7c\xce\x0c\xd9\xf3\x5a\xa1\x55\xcc\x29\x24\xc1\x4a\x65\xa1\x57\x80\x73\x10\xac\x94\x31\x3a\x33\x92\xa8\xa5\xea\x66\x5a\xa2\x4d\x02\xcc\xec\x87\xef\x66\xc4\x6f\x94\x6f\x5f\x67\x04\x89\x24\x72\xb2\x68\x45\x48\x5b\xeb\x1e\x67\xe4\xbd\x24\x99\xce\x79\xf7\x4d\xe7\x7f\x73\x83\x55\x6c\x4d\x3e\x92\xbb\xbe\xcd\x55\x9f\x33\xe2\x86\x38\x52\x37\xc4\xb0\xec\x6e\x88\xc8\xfe\xee\x11\x61\xeb\x04\x73\x81\x7c\xc3\x41\xa5\x6d\xeb\xa4\x98\xb8\x7b\xcc\xed\xc5\xdd\xeb\x79\xdf\x99\x99\x55\x85\x18\x0d\x67\xf5\x9f\x39\x85\x68\x61\x84\xcb\x9a\xf6\xbc\xfb\x4f\x8d\xd7\xb1\xac\x2e\x73\x20\xc9\x3b\x23\x7f\xc6\xdf\x14\x8b\x16\x04\xc5\x8c\xeb\x42\xc7\x62\xc5\xb4\x5a\x71\xc9\x7e\xd0\x0d\xf5\x64\xa5\xfe\x93\x95\xbf\xca\x3e\x6e\x89\x1b\xe4\x3d\x50\xaa\x95\xe5\xc0\xdb\x37\x6c\x67\x0e\xc2\x6c\x92\x1f\x38\x6f\x45\x53\x3a\x95\x8e\x49\x1d\x64\xfd\x23\x2e\x9e\x84\x49\xd6\x02\x6f\x85\x93\xa3\xc7\x4c\x1a\x55\x0a\x89\x13\xe7\xa7\x37\xb6\xcf\x33\x92\x3f\x5a\x73\x0b\xbc\xd7\x36\x0b\xc0\x3b\xb3\x2d\xb0\xc0\x1b\xd8\x7e\xa0\x50\xa3\xf5\xd3\x02\xef\xb1\x3d\x50\x48\x73\x4c\x55\xa5\x38\xe9\x7b\xaf\x55\xbd\xf3\x8c\xa8\xe7\x33\xaa\x6a\xab\xa7\x81\x6a\xa0\xdf\xba\xef\xa9\x6a\x76\x9f\x98\xe8\xcb\x69\x89\x6b\x4b\xbb\x3e\x9f\xe3\x87\x1e\x23\xd0\x27\x01\xb8\xc5\x27\xf0\xa7\xfe\xf4\x94\x44\x79\x89\x1a\xa8\x7e\xa9\xc7\xea\x16\x03\xd5\xdd\x50\xad\x39\xa2\x30\x55\x64\x64\x2f\x81\xad\x90\xbc\x15\x14\x8e\x32\x0a\xdd\xe0\xef\xe4\xca\x54\x9c\xea\xc8\x80\xb0\xa0\x98\x38\xdc\xe4\xcb\x1c\x07\xab\x18\xe3\x18\x83\x55\x52\xb8\x0c\xcc\x6d\xed\x45\xe0\xa4\x31\x4c\x82\x76\xe5\xe9\x66\x55\x79\x3a\x0b\x9a\x85\x42\x13\x74\x06\x6e\x83\xd5\xb9\xa3\x6f\x02\x63\x54\x73\x1d\x18\x13\x99\x97\x41\x6b\x52\xea\xe3\xa6\xa2\x8e\xb9\x40\x9a\xc3\xeb\x95\x33\xed\x6a\x3d\x12\xc6\x19\x2e\xe4\x80\xab\x55\xfc\x46\x2e\x91\x33\x25\x91\xb3\x3e\x2b\x78\xb0\x9e\xfa\xa4\xd7\xef\x3e\x7c\xa8\x1f\x98\x61\xca\xf4\x2f\x5f\x71\xe5\x61\x40\x54\x53\x0f\xf5\x3b\xb9\x75\xa2\x19\xcd\x5b\x45\xb9\xe6\x4d\x3f\xc4\xbc\x2a\xeb\xcb\x05\xa5\x4e\x35\x63\x4b\x9f\x83\xbe\xcd\xde\x5a\x5c\x91\x96\x74\x29\x39\x9b\x90\x26\xa5\x63\xb1\xf6\xad\x44\x6b\x7e\x0f\x6f\x7e\x7c\xa3\x10\xf2\x30\x0d\x89\x19\x7f\x56\x53\x05\xe9\xf5\xd9\x0a\x48\xa6\x18\xa1\x17\xcb\x2b\xa4\x79\x33\x4c\x80\x53\x94\x26\x0b\x23\x88\x9d\xa4\xaa\xcb\x3a\x0c\x21\xe9\xb3\x22\x39\xcc\x56\x40\x92\xbe\x8f\x97\x5e\xed\xdf\x9d\xc3\x5e\xd0\x94\x95\xb8\xce\x15\x9b\xf9\x56\x6f\x1e\xd1\xa2\x12\x18\x72\xc8\x1e\x72\xc8\x61\x40\x8e\x89\xa2\x68\x7f\x0c\x6a\x5b\xae\x1f\x16\x17\x40\x83\xc7\x8c\xec\x05\x98\x59\x4c\x2d\x82\x9e\x32\xeb\xfb\xd5\x09\xff\xfa\x95\xe4\xcb\x11\xaa\xe5\x10\xe5\x72\x84\x4b\x03\x0c\x16\x73\xe9\x1c\x86\x10\xf6\x19\x84\x7d\x17\xc2\xbe\x97\x7f\x2c\x54\xcb\x12\xd0\xf9\xea\xd1\x94\x16\xac\x87\xea\x9d\x4b\xae\x02\x58\x04\x6b\x34\x27\x6b\x5e\x3e\x35\xba\xbf\xb1\x7c\x17\xb8\x7c\x4a\xa0\x6f\xc8\x9d\x7c\x17\x4c\xc6\xa8\xae\xf1\xe7\x73\xe2\xe5\x64\x3c\x1f\xc7\x28\x4d\x6c\x5c\x0d\x59\x64\xdc\x61\x0a\x06\xbc\x6a\xf2\xec\xc3\xb0\x98\x9b\x59\x07\x3d\xc5\xf3\xa0\x51\xe3\xbc\xa7\x79\xb3\x17\x95\xe9\x62\xf8\x01\x5a\xe8\xd7\x17\xb5\x6d\xf5\xc4\x49\x73\x0a\x27\x41\xa3\x32\x43\x10\x23\xf0\x48\x5a\x0b\x25\x79\xae\xdf\xda\x3a\x39\x60\xa9\xde\xf8\xdc\x8a\x1b\x5d\x1b\x23\xad\x5a\x06\x49\x1e\x05\x4e\x26\x61\xe7\x0e\x66\xf2\x28\x20\x3f\xdd\x67\x4a\xec\x99\xd8\x84\x39\x19\x7c\x0e\x48\x5d\x6d\x76\x11\x00\xa6\x7e\x79\x56\x46\xf0\xd5\x5b\x33\x9a\x98\xec\x65\x5d\x59\x28\xc4\x14\x5f\xee\xdd\xd8\x02\xbc\x7d\x3b\x06\x6f\x03\x73\xe9\x3e\xb3\x25\xf8\xae\xfd\x60\x38\x37\x0a\xea\x39\x85\xf7\xad\x88\x77\x4a\x76\x02\xb0\xde\xbc\x3e\xb1\x14\x39\x82\xcb\x40\xf3\xc5\xef\x0c\xd9\xbb\x08\x08\xaa\xcf\x2c\x2e\x44\x22\x2c\xb8\x51\x34\x51\x11\xc1\xb3\x60\xc5\xed\x17\x11\xc6\x2a\x72\x3b\x70\xae\x62\x78\x1b\xac\x34\xb4\xde\x0e\x60\x46\xce\xcc\x3f\x5b\x1c\x62\x0a\x5d\x64\x44\x4e\xf8\x52\xc1\x44\x16\x79\x92\x4f\x57\xc8\xab\x6f\x03\xe4\x77\x3f\x09\x10\x70\x8d\x43\x21\x4d\x74\xc9\xac\x2c\xcb\xf4\xca\x7e\xe1\x8d\xae\xca\xc5\x1d\x4f\xe1\x97\x7c\x12\x8e\x79\x92\xc9\x0e\xbf\xf1\x38\xf7\xb9\x5f\xf7\xff\x7f\xcf\xe5\x2c\x11\xd7\x1d\xbd\x68\x2f\x2a\x6e\x57\x35\x69\x69\x1c\xc0\x15\x5e\xc1\x7c\xec\x7b\x01\x5d\xb3\x3a\xd6\x9a\xfe\xf1\x16\xde\x05\xea\xfd\x33\x5a\xbb\xf0\xb6\x4e\x63\x9e\x5b\xb8\x08\x9e\x4e\x92\x38\xe5\x9d\x40\x24\xe3\x0e\x9b\x84\x78\x8b\xd2\x67\x75\x6f\xe9\x03\x16\x05\x89\x18\x73\xbf\x93\x89\xc8\xd4\x41\xa7\x2d\x4d\x62\x7f\x34\x1f\xc4\x32\xba\x4a\x5c\x6a\x17\x75\xca\xb5\xd4\x5a\x3b\xcb\xb4\x7f\x41\x3e\x97\x53\x05\x28\xef\x15\xf7\x7b\x96\x90\xb7\xa1\xde\xa2\xdd\xfb\xf7\xbc\xba\xc3\x6d\xd3\xe1\x81\x01\xcb\x86\xcd\xcc\xeb\xc7\x6b\xd6\xa3\x25\xed\xab\xb6\x9e\x25\xab\x18\x1d\x4a\xe1\xd3\x0a\xa9\xf1\xb3\xbe\x57\xc4\xc3\xf1\xf5\x6f\x31\x80\xb7\xe4\x24\xd0\xc9\xd2\x71\x0a\xbb\x21\xf1\x30\xf8\x8d\xfe\x39\x0e\x88\x16\x00\x28\xdc\x48\x4a\xab\xa9\x49\x29\x7c\x5f\x1e\x58\xf5\xfa\x5b\x14\xd7\xdf\x0f\x86\xa5\x85\x17\x6a\xde\x35\x1f\x95\xe7\x5f\x34\x57\xda\xa8\xfe\x7c\x15\xb4\xe7\x67\xff\x1e\x34\x9b\xf2\xa1\xd0\xac\x87\xf4\x31\x70\x66\x31\x7c\x08\x9c\x4f\x31\xb1\x26\x5c\xa4\x61\x2a\xdf\x28\xd8\x78\x7d\x33\x61\xb1\xff\x32\x8a\x2c\xf8\x18\x50\xd8\x5f\x71\x4a\xcf\x0b\x31\xfc\x4d\x6b\xad\x13\x22\xe0\xa7\x27\xed\x1a\x13\x55\x0b\x0c\x73\x46\xfe\xd4\x80\x84\xb6\xa7\xdf\x96\x82\x71\xe7\x89\x19\xbb\x1c\x32\x8e\x19\x2e\x67\x64\x4f\x82\x12\x63\x62\x4a\xd5\x51\x38\xd7\xda\xcb\x2f\xc1\x1d\x4a\xea\x38\xbf\xeb\x8c\xdd\x95\x43\x76\xaf\xec\x22\x30\xff\x97\x00\xa6\x01\xc4\xfd\x5d\xa3\x22\x15\xcb\x4d\x8b\x5d\xbb\x41\x3a\xce\x2e\x90\x90\x1f\x23\x21\x3f\x42\x42\xbe\x0b\x89\x23\xfa\xe7\x98\x38\xe5\x5e\xc9\x19\x57\x27\x66\x14\xfd\x93\x3b\x53\x33\x66\xab\x53\x33\xb2\xbc\xd8\x24\x90\x84\x59\xa6\x28\xff\x52\xb2\x46\xb9\x90\xac\x31\xc1\x1f\x5e\x96\xca\x64\x8c\x00\x83\xb1\x27\xf8\xe8\x46\x12\x9d\x6b\x17\x73\x10\x7c\xab\xe5\x73\xe4\x74\x74\x44\x62\x58\xc8\xe9\xc8\xa9\x36\x4f\x97\xae\x12\x4c\x36\xe7\xc0\x5d\xe7\x4c\x10\x2b\x48\xbc\x2c\xb5\x28\x64\xcb\xcb\xdc\x7e\x6d\x7f\x4e\xf4\xb6\x85\x76\xdc\x67\x23\xcb\xb2\x45\xdf\x0d\x01\x77\xf1\x55\x60\x58\x71\xd1\x77\xaf\xe8\x48\xfd\x6b\x1f\x29\xe4\x7c\x95\x47\xaa\x99\x53\xb5\x1e\x48\x76\xa6\x92\x48\x97\x02\x77\x9b\xfd\x50\x4b\xf5\x41\x69\x1e\xd0\x38\x82\x77\x6a\xdd\x2a\x83\xe8\x06\x30\x09\x89\xa2\x7e\x6a\x10\x7f\xfd\x83\x1b\x8d\x13\x55\xfd\x0c\xe1\x73\x52\xb7\xd6\x32\x95\xd8\xfe\x62\xa5\x9a\xd4\x7d\x4e\x84\x29\x41\x85\xab\x6b\x3c\x27\x3c\xb7\x91\xfd\xc2\x1e\xcf\x6d\xcc\x90\xe8\x49\xda\x77\x47\x17\x44\x40\xfd\xe4\x52\x73\x13\x64\x52\xfe\x59\x98\x46\x45\xc1\x86\x45\x4b\x53\x86\x43\x22\xe1\x24\xd1\xfa\x9c\xa3\x04\x64\xdf\x53\xc7\x4c\xb8\xda\x61\xab\x58\x17\x66\xd6\x05\x21\xaf\xe7\xde\xf6\xd0\x41\xdc\xd8\x94\xcc\x29\x24\xee\x2a\xb4\x1a\xff\xe1\x0c\x4a\xa6\x73\x11\xcf\x76\x84\xc2\x30\xbd\xa1\xf6\x55\x56\xb3\x0f\xdd\xd5\x36\x4a\xd5\xde\x64\xbd\x37\x69\x2e\xc9\x45\x9f\x55\xfb\xc5\x90\xa1\x9e\xe2\x21\x25\xae\x70\xd0\x8a\x72\xde\x4b\x72\x4b\x42\x9c\x3f\xec\x68\x2d\xa2\xdb\x7c\x5f\x3f\xf8\x43\xe4\x16\x85\x87\x78\x45\xd4\xe8\xc1\xa8\xc6\xc7\xfb\x6e\x75\x84\xc5\xab\xbe\x5b\x60\x60\x5e\x11\x9d\x8d\x2f\x5f\x6e\x1e\xa8\x8a\x72\xae\x29\xaf\x62\x60\x4c\xdf\x92\xf1\x12\xf2\xcf\xc8\x9f\x59\x1f\x2f\xc4\xfa\xec\x5b\x01\xaf\x18\xbf\xc5\xcd\xbf\x98\x9b\x5a\xb8\xe0\x39\x6c\xa1\x29\xeb\xeb\x8b\x31\x4f\x37\x9f\xd7\xdd\x1c\xab\xfd\x3c\x7c\x58\x3c\xe6\x9d\x26\xd8\x69\xe8\x24\x26\x95\xaa\x8b\x52\xb6\xfe\x0c\x03\x56\xfd\x18\x6e\x46\x82\x56\xd2\x9e\x44\x09\x10\x1f\xb2\xfc\xc1\x83\x21\xdf\xf8\x23\x1e\xcd\x48\xe0\x82\xe8\x6d\x02\x53\x22\x85\xeb\x42\xbc\x36\x34\xbf\x31\xd1\x7d\xbe\x54\x85\xbf\xe3\xbc\x9c\x0d\x37\xb3\x28\xb4\x7b\x14\x52\xb7\x9d\x2d\x71\x5d\x6d\x6c\x81\xdc\x42\xd3\x96\x17\xd6\x17\xcb\xc4\x50\x67\x29\x96\x7d\x57\x1b\x50\xb9\xc0\xd7\x14\xd3\x9c\xb8\xc0\x7b\x08\x83\xfa\x94\xf8\xad\x9f\xd7\x89\x64\x62\x34\x21\x52\x35\xa7\xae\xf3\x01\xba\xed\xc4\xcd\xc8\xa8\xf9\x48\xf6\x85\x4e\x99\xdc\x18\xcd\xc0\x54\x7a\x70\x41\x74\xce\xf0\x87\x0f\x2f\x08\x37\xec\x07\x2a\xcd\x56\x2c\xca\xb9\x04\xbc\x0e\x36\xeb\x72\xe9\x36\xc9\xd2\x9a\x9b\xa1\x85\x46\x51\x0f\xb2\x8b\x9e\x6c\x5c\xbb\x35\xf9\x2e\xd9\x41\x41\x64\xec\xc2\x8c\x7c\x96\x30\x04\x49\xe1\x87\x49\x8c\x9d\x39\x68\x42\x0e\xc2\xd1\x69\xdf\x32\x90\xf4\xd1\xfa\xaf\x01\xc5\xe3\x3b\x23\xef\x12\x98\x91\xc8\x05\xa1\xb5\xb7\xcd\xfd\x65\xd8\x9f\x56\xc9\xc7\xce\xbe\x50\x5f\xcf\x9c\xf8\x51\xa6\x48\xce\x23\x09\x26\xc3\x4f\x9e\xb9\xb9\xeb\x12\x6e\x18\x46\x29\xe1\x8b\x24\xfd\xc7\x5a\x47\x0c\x98\x69\xf0\x56\x22\x8b\x5a\xda\x4f\x8c\x86\x8f\x36\x7e\x27\xd9\x9a\x5c\x23\x71\x4f\xd0\x47\x31\xb5\x07\x73\x0a\x17\xab\x90\x55\x8e\x1c\x72\x29\xfc\x01\xef\xb3\xbe\xfb\xeb\x57\x1d\x21\xe4\xc7\x98\xc1\xe2\x05\x36\x1a\x15\xa9\xe7\xdc\x1d\x23\x53\x2c\x68\xec\x78\x20\x1c\x0e\xd2\x39\x22\x52\x61\x7d\xa6\x48\x3c\x8f\x52\xae\xb1\xd2\x3e\x8a\x8c\x45\xe7\xe6\xea\x13\xa4\x62\x56\xe7\x14\x26\x6e\x93\x62\x71\x9b\xc4\xb4\x7f\x95\x84\x31\x92\x07\x98\xb5\xc1\x84\x95\x6b\xbd\xd5\x5f\xa1\xa8\xbb\xde\xfd\x51\x6c\x4f\x5c\x72\x4b\x2e\x5c\xbd\x0b\x7a\x6f\x0d\x06\xbd\x6d\xc1\xa0\x8b\x9d\xd1\x42\x45\xdd\x00\x53\xf9\x74\xd6\xfa\xeb\x8f\x7f\xef\x72\x32\x23\x33\x43\xa3\x7e\x27\xc3\x1e\xaa\x18\x6e\xee\x39\xe6\x41\x3e\xe4\xa1\x7d\x4b\x6e\x35\xa6\x9f\x91\x4b\xb7\xf0\x51\xbe\x76\x9d\x53\x78\xe9\x36\xde\x1d\xe5\x71\xc2\x0a\xb6\xe7\x69\x19\xe0\x8b\x57\x29\x29\xaf\x50\x52\xcd\x01\x5c\xd8\x52\x71\x00\xf7\xa5\xaa\xb5\x18\x20\xb7\xc4\xd3\x43\x3d\x21\x1c\x7e\xb2\xaf\xaa\x33\xb5\xfc\xbc\xcf\xbe\x82\x27\xed\x23\xc2\xfb\x1e\x82\x03\xda\x86\x29\xe9\xc7\xbd\xb6\x77\xe6\x8b\x6c\x12\x96\xbd\x58\xd5\x65\x56\xf4\x98\x73\x4a\x19\x55\x93\x91\xb5\xfe\x2a\xb9\xe4\xb0\xb9\x1b\xe9\xe9\x95\x2c\xd0\x66\xad\x06\x5b\x57\x35\xb4\xb7\x6d\xb5\xde\xe3\x7a\x4f\x5b\xf5\x9e\x36\xea\x3d\xed\xd7\x6b\x14\xd6\xe7\xcc\xc9\x27\xa3\xa6\xca\x30\xa7\xe7\x09\x61\xf0\xd3\xbd\xb6\x19\x86\x05\xf9\x4a\x47\x3b\xf6\x8c\x4c\x39\xc6\x5b\xd3\x2f\x58\xdf\xbd\xb6\x11\x71\x0f\x07\x3a\x8d\xd0\xb5\x0b\x37\xae\xae\x0e\x25\x9e\x6f\x14\x15\x5e\xa1\x42\x52\xad\xd0\x1c\x82\x8c\x82\xab\x3e\xe3\xed\xea\x3b\xa7\xea\x6c\x97\x22\xc2\x71\x64\xf8\x72\xcd\xd2\xb1\x6b\x0c\x22\x5e\xb7\x01\x32\x3a\xfc\x81\x77\xac\xaf\x23\xae\xdc\x55\x9e\x6a\xde\x4b\x5b\x80\xb7\xa7\xea\xbf\xb7\x25\x78\x43\x9b\xab\x46\x5b\xae\xf3\x55\xc0\x9e\xeb\x3c\xea\xff\xcf\x23\x38\x74\x9d\xef\x82\x0c\x1f\x0d\x28\x9c\xdf\x13\xde\x2b\x91\xf1\x36\xd0\x82\x8f\xf5\xbb\x23\x6e\xeb\x9d\xfb\x61\x23\x82\x3d\xc8\x40\x62\x00\x83\xea\x16\xd5\x01\x46\xfb\x57\xe3\xba\x7b\x12\xd4\xd4\x2c\x14\x75\xbd\x63\xdb\xb2\xe6\x90\xef\x06\xef\xbb\x3f\xd4\x3a\x16\x2e\xd7\x0a\xeb\xe6\x07\xe8\xd8\x1c\xa0\x5c\x48\x2a\x0e\xd0\xbd\x41\xab\x3c\x16\xec\xc5\xd2\xf8\xb2\xbb\x87\x97\x03\x4b\x7b\xa2\x6e\x77\x3c\x57\xa0\x94\x51\x6a\xe6\x42\x69\x23\xe4\xe6\x43\x44\xaf\xf2\x15\xc7\xc8\xdd\x5d\x3e\x1e\x4d\xe3\x37\xae\xea\xa6\xbb\x66\x9c\xe4\xde\xd8\x88\x14\x33\x23\x2a\x64\x74\x8e\x21\x94\x5a\x20\x75\x4e\xe1\x64\xb5\xac\x9e\x67\xca\xcc\x4d\x98\x3e\xbb\x2d\x7a\xdc\x22\x70\xd2\x73\x14\xc9\xd9\x1e\xca\xe4\xec\x1c\x42\xc5\xd7\xdc\x42\xa0\x7e\xed\x82\xeb\x64\x6b\xd6\x7f\x3d\xca\x75\x66\xa9\x23\x5c\xe2\x16\xa9\xef\x96\xf5\x89\x38\x79\x85\x6c\x77\x8d\xb8\x55\x2c\xd2\x00\x37\x3a\xc2\xeb\x20\xbf\x21\xa3\x66\x54\x8b\xea\x14\x3b\x11\x9a\x8f\x23\xf2\xf0\x76\xed\x53\x93\xfe\x13\x63\x33\x22\x43\xd2\xba\xe1\x97\x73\x08\x24\x35\x59\x42\xd1\x36\xb3\x2d\xbb\x4d\x89\x89\x77\x12\xb8\xd0\xba\x53\x0d\x6b\x99\xf6\x33\xb6\xe6\xc8\xa8\x05\x0a\xca\xbe\x06\x77\x7c\x54\x97\x4a\xe8\x06\xa4\x15\x51\x29\x1e\x03\x51\x15\xb2\x49\x15\xaf\xa4\x43\xc2\x90\x6e\x8b\x3a\xe1\x39\x24\x6e\x02\x6c\x39\xe6\xa5\x87\x6f\x97\x20\xe5\x0b\x27\x91\x9a\x05\x06\xb3\xa2\x2f\x96\xb6\xc5\x57\x23\xd8\xb3\x23\x60\xcf\x6d\xbf\xef\x2e\x62\x08\xb5\x30\x53\x67\x46\xde\x04\x20\x81\x51\xe8\x3a\x33\x12\xbb\x20\x21\x59\xea\x69\x31\x08\x84\xec\xb3\x8b\x51\x60\x7b\x18\x0b\x02\x7f\x78\x89\xed\x01\x3b\xb7\xbb\xea\x43\x53\x05\xb9\x48\x9b\xb1\x6c\x46\xce\xcd\x06\xbe\xe1\x10\x67\xea\x74\xec\x06\x28\x1b\x52\xbb\x2c\xdb\xcf\xcb\x7e\x98\x32\x28\xcb\x78\x86\x65\x07\x01\xe1\x55\x6a\x5d\x39\xcc\x42\x1f\x39\xa1\x8e\x5c\x4a\x4e\x88\x84\x9f\x98\x4f\x16\xad\x6e\xcb\x38\x65\x4f\x57\xb6\x38\x6e\x68\x31\x1c\xd4\x60\xdd\x3d\xa9\xc3\xfa\x26\x2e\xe4\xd8\x99\x91\x0c\xcd\x95\x20\x51\x4b\x39\x46\xc8\x1f\xf7\x5d\xb8\x74\xc6\x7d\x0f\x2e\x9c\x72\x3e\x42\xcf\xe7\x92\xc2\xd8\x49\xc9\x8c\x9c\xb8\xd0\xed\xbb\x57\x38\xeb\x0b\xc7\x1f\x55\x18\x1b\x1c\xdf\x98\xc2\xc5\x37\x4a\xed\x8b\xfa\xb6\xa8\x05\xc7\x88\x18\x14\x2e\x0a\x24\x7c\xe1\xdc\x90\x97\x2e\xb8\x68\x96\xc8\xd4\xc6\x5e\x3a\x17\xd5\x14\xdd\xba\xed\x73\x7b\xea\x5c\xe0\x4c\xca\x81\xc9\x7c\x60\x05\xae\x9b\x3a\x37\xe4\xbc\xec\x2c\x54\x9d\x4d\x97\x3a\x73\x6f\xed\x69\xad\x2b\x5e\xeb\xea\x59\x1d\x67\xbc\xae\xaf\xe3\x73\x5c\xc7\x49\x55\x01\x5a\x82\x1f\x9a\x7e\x4d\x40\xf4\xd9\x36\x1d\xcd\xc8\x7e\xf1\xc3\x9e\x91\x4f\xc5\x0f\xf0\x12\x8d\x80\x3f\x04\xe4\xc1\xb0\x82\x5a\x2f\x11\x13\x4d\x9c\x0d\xc7\x71\xc8\xc4\x39\x24\x81\x1a\x99\xa2\xa6\x0f\x1f\x4e\xfa\xee\x08\xf1\xcd\x67\x25\x85\xec\x0b\x32\x41\x6a\x4a\xa9\x3d\x4d\x96\x07\x32\x51\xdf\xb8\x2c\x20\xfc\x43\x40\x2e\xd5\xb4\x59\x02\x59\x06\x5b\x9c\x5c\x1a\x2d\xcd\x9c\xc2\x91\xeb\xcc\x62\xd8\x71\xab\xaa\x66\x0c\x09\xb8\xcd\x6e\x0f\x83\x33\xce\xaf\x2d\x38\x72\x29\xbc\x6f\x93\x3c\x63\xe0\xed\xd9\x88\xf3\xf8\x80\x8b\xb6\x26\x79\x74\xc0\x56\x8b\x93\x81\xc9\x36\xcc\x47\x45\xd5\xbc\xa7\xda\x64\xbd\xa9\xcd\x8b\x79\xea\x19\x4a\x35\xc3\x5d\x4e\xb4\x07\x00\x53\xe5\x94\xc2\x8e\x4b\x0a\x23\xb0\x77\x2b\xd9\x24\x93\x5b\xaa\xea\xf8\x82\xc1\x2e\x5c\x63\xaa\xf1\x55\xac\x34\xcb\x0e\xb4\x69\xfe\x9c\xc2\xb6\xeb\xcc\xc8\x57\xad\x85\x33\x9a\xe2\xbd\x6d\xeb\x1b\x45\xff\x9b\xb7\xae\x73\x13\xc3\x69\x2b\xed\xfc\x20\x49\xfc\x08\x25\xec\x1f\xee\xfd\x1d\xf4\xc4\x5a\x5c\x77\x09\xd1\x37\xfc\xbc\xef\xbe\x47\x27\xfd\xb2\x26\xd7\x1e\x51\x20\x1c\x91\xfb\xde\xed\xb6\x6e\x72\x95\x30\xfd\xd0\x18\x64\x46\x4e\x5d\xf8\x82\x32\xdd\x13\xbe\x69\x42\x77\x1c\xb4\x4e\x28\x66\x64\x46\x76\x8d\x48\xd5\x67\x9f\xe6\x14\x3e\xb9\xed\xd7\x17\x17\x21\xac\x6f\xea\x6f\x14\xcd\xe0\xc9\x00\xf7\xef\xeb\xea\x76\x43\xbe\xa1\xc7\x85\xb7\x3b\xab\xeb\x3e\x19\x94\xdd\xab\xea\xaf\xda\xd5\xda\x8b\xe3\x4f\xfe\x6f\x49\x42\x4f\xe1\xa3\xdb\x7e\xdb\x3c\xf8\x23\x1e\xdd\x92\x8f\x2e\xc4\xff\xfd\xdf\x43\x38\x22\x42\x6d\xc3\xf0\x61\x3c\x3a\x22\x12\x04\xb5\x25\xb5\x95\x2c\xf3\x61\x85\xc2\xe8\xa3\x96\x28\x51\xc7\x0c\xfb\x2b\xbe\x75\x44\x66\xe4\x83\x0b\x71\xaf\xcb\x89\xa4\xf0\x12\xb7\x50\x83\xd5\x9b\x15\xfd\xef\xab\xfe\xad\x01\xa6\xbb\xd0\x9b\xf8\xe5\x1e\x7b\x5e\xc0\xee\x90\x6f\x68\xa6\x2c\xbd\x1f\xe4\xba\x0a\x75\x7f\x17\xf7\x74\x44\x17\xa9\x33\x23\xdf\x05\x0c\x30\xd5\x52\xea\x08\x06\x5c\xbd\x3a\x0b\xe0\xc8\x05\x99\x52\xc8\xd2\x15\xe8\x44\x02\x51\x18\xb6\xf5\xd6\x6c\x92\xa5\x97\x44\x5f\x74\x8a\x39\xa5\xf0\xe7\xb7\xdc\x37\x57\xed\x0a\x4b\xef\x19\xe1\xc4\x4b\xdb\x77\x45\xdb\x30\x7c\x38\x3c\x36\x46\x0c\xda\x64\x19\x92\xe5\xbe\xe3\xfa\xed\x73\x91\x35\x44\x67\x0a\xd9\x47\x23\xd1\x43\x62\x99\xfb\x93\xd4\x82\x19\xc9\x52\x78\x93\x01\x11\x8e\xa0\x7d\xf7\x4a\x5f\x66\x95\x5e\x40\x3c\x25\x02\x73\x5e\xaa\xd7\xb9\xf7\x10\xbe\x64\x67\xfa\xa5\xce\xee\x81\x21\x74\x8e\x5c\xf5\xfe\xd4\xbc\x4f\xc6\x63\x1e\xcb\xfc\xed\xb1\xb9\x8d\xf3\x2d\x9d\xbf\xfb\xad\xab\x73\x7a\x1f\xe1\xc5\x93\x67\x32\xea\x08\x67\x46\x58\x0a\x16\x9b\x4c\xa2\x50\xc7\x89\x7a\x74\x95\x26\x98\xad\xf1\x75\x4d\xfd\x77\x1a\x90\x5b\xe2\xa5\xb8\x22\xdb\x5a\x45\x1b\xb6\xae\x37\x3b\x56\x8c\xc4\x31\xb0\x53\x1b\xd3\xbf\x4f\x6d\x01\xa9\x1d\xf7\x53\x60\x67\x36\xa6\x80\x77\x3d\x1b\x53\xab\xb8\x87\x36\x66\x86\x47\xb1\x76\x4e\x21\x68\x05\x8f\x24\x24\x08\xc1\x6b\x5c\x7b\xb0\x51\x70\x53\xa7\x51\x12\x90\x22\x1c\x13\x3a\x87\x34\x35\x4e\x39\x33\x0b\x9e\x0c\x36\x9f\xf1\xc7\xb8\x28\xbe\x05\x18\xb4\x00\x7f\x5c\x5a\xb0\xf1\xc4\x3c\x8f\x2d\x8d\xd9\xd5\x96\x58\x78\x50\xbe\x51\x88\xd2\x95\x87\xeb\xe9\x92\xaf\x1a\xbe\xac\xb8\x9d\xfd\xfa\xf5\x74\xae\x86\xfd\xb4\x77\x1f\xeb\xd4\xff\x44\xda\x3a\xb3\x7c\x7e\xda\x42\xfd\x32\x46\x62\x3a\x1a\xda\x83\x22\xde\xef\x5d\x36\xb4\x1b\xc3\xda\x30\x1f\x3f\x5f\x93\xb5\x91\x3e\x1f\xe4\xaf\x8a\xc1\x0e\xd7\x8b\x77\xc5\x80\x87\x8f\x87\xf9\xbb\x82\x36\x0c\x9f\x15\xef\x0a\xfa\xb0\x3e\x5c\xcf\xdf\x15\x34\x62\x7d\x73\x23\x7f\x57\xd0\x89\xf5\xa7\xc5\xbb\x4a\x30\x84\xc1\xe6\x9a\xac\xaf\xcc\xc6\xc6\xe6\x1a\x5a\x33\x4c\xd3\xbb\xb9\x94\x6e\x6b\x1d\xa6\x56\x6f\x6d\x46\x7c\x3c\x2b\x74\x4d\xe1\xcf\x71\xda\x46\x66\x97\x96\x76\xa3\x0e\x01\x7a\x37\xd6\x9f\xdb\xeb\xcf\xea\xb0\x50\xa7\xb9\x1b\x83\x3a\xd1\x1d\xd6\xc3\x57\x0c\xf2\xa5\xad\x47\xa3\xa8\x47\x9e\x18\xd4\xa3\x4c\x0c\x97\x17\x71\x69\x05\x91\xbe\x5e\xa6\x77\x5e\x08\xcc\xc8\x58\xaf\x0e\x64\x8e\xc7\x8c\xb9\xe4\x70\xfd\x0f\x27\xfb\xf5\xeb\x18\xbd\xbb\xca\x1b\xce\x9f\xec\x93\x2d\x81\x25\xb6\x00\x77\x68\xc7\x73\xc3\xc4\x25\x8c\x64\x6b\x43\x0a\xd2\x91\x3d\x64\x58\x2f\xd2\xf6\xab\xa5\x19\x99\x9a\xef\xe1\xf9\x14\xe6\xde\x67\x92\x1a\x0b\x20\xd6\xac\x52\x60\x89\x12\x9f\x4c\xa5\x49\xda\x50\x89\x78\xb8\xdb\xeb\xf4\xd1\xc6\x2f\x25\xf1\xdd\xb6\x9c\x29\xe0\x8e\x78\xd1\x76\xe6\xa5\x13\xaa\x35\x00\xc6\x88\xa4\x6b\xc3\x25\x9f\x80\x6e\x0a\xba\x02\x91\xce\x2c\x55\x0f\x09\x23\x1b\xbf\xcb\xde\x3a\x6d\x88\x02\x5d\xd4\x9e\xa8\xaa\x85\xd0\xb8\x08\x18\xbc\x37\x23\x51\x8a\x18\xbe\x06\x21\xa6\x64\x58\x94\x3c\xa9\x95\xac\x17\x25\x4f\x6b\x25\x1b\x45\xc9\xb3\x5a\xc9\x66\x51\xf2\xbc\x56\xf2\xb8\x28\x29\x01\xcb\x14\x3d\x51\x45\x35\x08\x43\xf2\x7d\xb3\x82\x7c\x1f\xa3\x09\xd5\x1f\x83\x51\x6c\x0f\xfe\x38\x46\xa6\x6d\x24\x90\x67\xbb\x5e\xd1\xaa\x7e\x66\x6f\xc9\x8d\x5a\x82\x12\x4c\x75\xec\x8d\x15\xac\xcb\x75\x0a\x98\x70\xd2\x00\xd9\x2b\x37\x7f\x3a\x28\x99\xed\xe3\x74\x95\x33\x8e\x22\x43\xbf\xc7\x6b\x4f\xf8\xe6\xef\x02\x83\x35\xc9\x35\x25\xea\xbd\x6e\xfd\xe8\x0d\x39\x56\x1f\xfd\x54\x7c\xea\x7b\xf1\xf4\xa5\x78\xfa\x5a\x7e\xfe\xaa\x0d\xdf\x23\x59\x35\xf4\x8a\xb8\x8a\xfb\x7b\xa9\x27\xa2\x96\xe4\xb5\x7e\xec\x49\xfa\xe8\x09\xdf\x44\x20\xdf\x4a\x5b\xef\x86\x5d\x05\x79\x6b\x12\x32\x67\x46\xae\x54\xcb\x24\x24\x1c\x39\x0d\xf5\xd0\x53\x93\xcb\x28\x9a\x22\x5c\xa5\x85\x69\xee\x05\xc9\xa0\xbc\xaf\x13\x2f\x78\x59\x59\x56\xbc\x37\x25\x98\x56\x9c\xd2\x11\x47\xa7\x87\xbd\x15\x7b\x7a\x4b\xb6\x52\xbc\xd7\xba\x4d\xd1\x7e\xf3\x65\x8a\xc5\x14\x06\x68\x13\xdd\xd4\xb2\x9e\x98\xa9\x38\x14\x39\x01\xd9\xb4\xeb\x9d\x9b\x5e\xa1\xdc\x0c\xfc\x89\x9b\x61\x9e\xbe\x98\xa7\x41\xa1\xbf\xda\xf8\xeb\xdd\x0c\x2a\xcd\xd7\xef\xdb\x7c\xb0\xd0\x6c\x58\x36\xdb\x4b\x61\x38\xd4\x81\x08\x16\xd1\x07\x16\xad\x57\x4a\x06\x0b\x25\x83\x4a\x49\xad\xbb\x4a\xc9\xc6\x42\xc9\x46\xa5\x64\x73\xa1\x64\xb3\x52\xf2\x78\xa1\xe4\x71\xa5\xe4\xc9\x42\xc9\x93\x4a\xc9\xd3\x85\x92\xa7\x95\x92\x67\x0b\x25\xcf\x2a\x25\xcf\x17\x4a\x9e\xeb\x92\x1a\x92\xd1\x13\x32\xde\x8a\x73\x0a\xe7\xed\xf0\x8e\x4c\x75\xca\x4a\x7e\xef\x27\x0b\x6d\x01\xec\xca\xce\x4b\x04\x05\x6f\xaa\x09\x58\x0c\xec\x3b\x6a\x6b\x6f\xd5\xe3\x0f\xad\x67\x39\x41\x21\xe9\x73\xba\x32\xe6\xe2\xb1\x7d\x86\xb6\x04\xec\x54\x3d\xc4\x14\xa6\xf6\x2d\x39\x4f\x01\x7d\x76\x01\x2d\x9c\x82\x14\x9e\xae\xf3\xc7\x08\xe1\x9c\x42\xaa\x2a\x22\xeb\x6f\x59\xc0\x19\xc1\x68\x4f\x94\x2a\xfe\xfb\x8c\x93\x93\x74\xb1\x05\x55\xfc\xf8\xb5\x54\xdc\xb8\x2e\x55\xbd\xe0\xbd\xc9\x9c\xc2\x51\x2b\x06\x42\xc4\xd1\xfb\x92\x11\xd4\xff\xec\xa4\x77\x38\xe4\x18\x03\xd2\x4a\x8c\x97\xf7\x69\xbb\x02\xc6\xd3\xb9\x27\x4e\xd1\x5d\x45\xc9\x12\x0e\x0a\x0a\x9e\x83\xf2\x43\xe2\x1c\x65\x24\x62\x44\xa6\x84\x69\x7b\x87\xe2\xb7\x57\xea\xf4\xcd\xca\x65\xf9\xca\xf1\x62\xe5\x4c\xfc\xd5\xfa\x32\xcd\xc8\x51\xaa\xfd\x26\xaa\x6b\xe5\xe9\x05\xd2\xab\x5d\xac\x11\xab\xac\xd1\x4d\xec\xb0\xd6\x00\xb5\xd5\x7d\xcc\xd4\x48\xb8\xea\x5a\xaa\x2e\x19\xb8\x57\x4a\x2e\xc5\x58\x02\x1f\x6d\x0f\xdc\x4f\x36\x46\x3c\xbe\x5a\x05\x0f\x6e\x60\x73\x70\x2f\x54\x17\x63\xd5\xfa\xb3\x16\x6b\x3f\x0a\x07\x03\x40\x84\xe9\xeb\xef\x19\x8b\x30\xf6\x43\x92\x20\x7c\x3c\x18\x98\x28\x0e\x61\x7a\xc4\x2f\xf8\x8d\x05\x89\x09\xe2\x30\x65\x51\xc6\xcb\x58\x13\xd5\x78\xab\x57\xfa\x02\x77\x47\x38\xd5\x30\xad\x77\x05\xa1\xcd\xf3\xd2\xfd\xa5\x00\xb4\x79\x92\xbb\xbf\x1f\x7d\xb6\x25\x3e\xec\x07\x63\x8e\xfd\x2e\x75\xda\x63\x70\x60\xd0\x8e\x1d\x13\xb4\x43\xc9\xcc\xf9\x6a\x14\x52\x75\xf1\xa2\x14\xbf\xf3\x57\xb5\x20\x1f\x4b\x21\x3d\x4a\xe9\xff\x2c\x21\x1f\x05\x8e\xf4\xa6\x0c\xba\x01\x67\x8d\x34\xac\x45\xb7\x60\x81\xf8\x56\xcd\x7a\x79\x9e\x80\x84\xd2\xba\xfd\x5d\xaa\x35\xbe\xdb\xa9\x43\x44\xe2\x24\x61\x3d\x58\x74\x4c\xae\x63\x22\x12\xb2\xcd\x24\xef\xc7\xc9\x8c\xe8\xcb\x58\x0a\x6f\xd3\xaa\x46\xdc\x44\x5a\xdf\x52\xb3\x4d\x84\xd6\x88\x9f\xa6\x77\xc5\x9f\xcc\x1d\x79\x4c\x5c\x9b\x91\x09\x31\x34\x32\x21\x92\x46\xc6\xd5\x6a\x34\x91\x64\x4a\x8c\x67\x4c\x9f\x01\x47\x73\x3d\x86\x99\x5d\xd0\x23\x85\x19\xcf\x94\xcc\xfc\xe5\xe6\xaf\x34\x7f\x0d\xf2\xf8\x2e\x94\x78\x7f\x43\xae\xd4\xd9\xb5\x2c\x78\x30\x34\x90\xae\xc4\xf6\x1f\xa9\xa3\x8e\x9b\x65\xa9\xf3\xa6\xfe\x3d\xb3\x75\xb2\x74\x83\xe8\xae\xec\xef\x42\x1d\x3b\x7c\x39\x87\xdd\x26\xf5\xce\xa1\x42\x28\x31\x85\x88\x21\x23\x8d\xc1\x67\xd0\x55\x4c\x67\x21\x28\x6f\x1e\x5f\xe0\x55\x66\x79\x45\xa2\x38\xcf\x2f\x78\x97\x89\x39\x58\x50\xb1\xa7\x10\xa0\xb5\xc5\xe2\xdf\x64\xc7\xe5\x9d\x30\xc6\xdc\x43\x13\x96\x4a\x4b\x23\x44\xd4\x26\x37\xa3\x43\xe6\x08\x85\x0e\xd1\xea\xed\x58\x9b\x74\x9f\xa2\x49\xb7\x7b\x88\xb6\x9d\xec\x2c\xff\xf0\x51\x46\x02\x72\x9a\xc2\x4a\xcf\xa1\x13\xf2\x23\x05\xb5\x38\x42\xad\x0d\xc6\xcb\xce\x16\x71\x11\xc7\x9b\xeb\x31\x4e\x7e\xca\x88\xec\xbb\x9f\xf1\x81\xe3\x43\xc4\x48\x86\x0f\x33\xb2\x9b\x82\x7a\x04\xa1\x7e\x23\xe4\x7d\x6a\x25\x02\xc7\xf8\x0b\x39\x73\xc5\xba\x7d\x6d\x27\xaa\xb2\x55\x52\xba\x25\x5f\x15\xab\x31\x5c\xff\x5d\x00\xaf\xd9\x2f\x04\x4c\xa1\x77\xe6\x0c\xd7\x7f\x57\xe3\x1b\xf6\x86\x74\x4d\x89\x67\x59\x9f\x25\x54\xfd\x10\xe0\x29\xa9\x51\xeb\xcc\xd7\x81\x51\x25\x3e\x32\x07\x65\x43\x06\xc3\x75\x25\x7a\x95\xf2\x00\x33\xf2\x80\x22\x06\x6b\x33\xf2\x49\xcd\x94\x7d\xd2\x22\x01\x12\x88\x1a\xe7\xc4\xd7\x9e\xfe\x2e\xea\x18\x8b\xaf\x21\x62\xfe\xde\x4c\xdd\x97\x02\xc0\x2d\xf1\x9d\x5a\xe7\xc5\xe9\x5a\x21\x24\x6d\xda\xb1\x33\x7c\x0c\xc2\xc1\x58\xb1\x26\x60\x4b\x43\x54\x8f\x8d\xbc\x1e\xca\x13\xed\xf5\xd6\xf3\x7a\x28\x7e\xac\xa8\x38\x5c\x60\x3a\x25\xe0\x56\x6c\xe4\xdc\xa7\x9a\x8f\x02\x88\xd7\xe6\x71\x89\xad\x2c\xdb\x0c\x57\xb7\x19\xd8\xb1\xb3\xae\xe6\xb7\xbe\x6a\x34\xa6\xd2\xaa\x25\xc0\x15\x18\x82\x70\x9e\x36\x57\x2a\x23\x9d\xac\xa8\x85\xce\x2d\x2b\x24\xb0\xef\xa9\x02\xa5\x58\x67\xca\x57\x24\xf8\x8e\xba\x1b\x95\xba\x1f\xda\x4f\xc0\x2d\x39\x4c\xf3\x48\x61\x85\xfc\xc3\x41\xd2\x91\xb4\xb1\xa3\x18\x97\x11\x1d\x14\xf7\x1b\x50\x57\xcf\xd1\x42\x40\x5a\x09\xb7\xb7\x3c\xd8\x37\x2d\x2d\xbf\x37\xb6\xac\x0e\xfd\x4b\xad\x65\x96\x5b\x74\x34\x28\x7e\xf2\x30\xb2\x7c\xc1\x12\x63\xb7\xc5\xeb\x44\x67\x0a\x98\x57\x3c\x82\xe3\xd1\xa1\x3a\xc3\x3f\x80\xab\x3f\x57\x94\xda\x87\x04\x1f\x7f\x50\xc0\x17\x73\x2d\x50\x8e\x03\xd0\x18\x1e\x0f\xfc\x8b\x76\xef\x8c\x13\x92\xc1\x4f\x96\x28\x76\xee\x92\x69\xdf\xc2\x9a\x44\x53\xa9\x81\x3b\xb1\xae\x26\xae\x57\x61\x08\xbd\xa1\xfa\x55\xbe\x97\x9a\x84\xd6\x20\x5e\x77\xf1\xbd\xb4\x5e\xa8\x09\x40\x79\xf9\xb5\x9c\xd3\xaa\x4f\x1f\x6f\x32\x2f\xae\x78\xe9\x5e\x60\x64\xee\xc2\x08\xfa\x45\x89\x28\x88\x50\x82\x3c\x96\xab\x05\xf9\x92\x11\xf5\x12\x1f\x70\xb0\x43\x1c\x77\xec\x28\xd6\x1e\x59\xf6\x1e\x32\xfe\x62\x4d\x7b\x57\xb2\xd6\xeb\xa0\x7e\x77\x24\xb4\x17\x05\xc7\x8b\x50\xad\x4d\xf0\x9c\xa5\x84\x46\x59\x9f\x7d\xaf\x85\x68\xc3\xad\xbb\x55\x5b\x12\x56\xd2\x5c\x83\xa2\xad\x95\x82\xc2\x07\x8b\xe5\xce\xf4\x45\xaa\x9f\xa2\x1f\x9d\xdc\xfb\x5a\xe6\x93\x5f\x20\xb8\x79\x31\x72\xe8\x74\x6e\xdc\xd9\x5b\x7b\xd0\x29\xb6\x2b\x56\xec\xe7\x21\xe8\xb5\x13\x7a\x71\x2e\x10\x2e\xe8\x08\x1b\x69\x09\x4b\x20\xe0\x2d\x7c\x86\x50\x48\x30\x6c\x96\xe7\x78\xe5\x85\xb1\xde\xd9\xd0\xf6\xb4\x14\xa8\x24\x00\x04\x53\x25\xf6\x25\x4a\xec\x53\xef\x12\x40\x00\x9e\x17\xd2\xaf\x76\x1d\x57\x6b\x13\xa0\x7b\x90\x8b\x9e\xfa\xed\x12\xd5\x1f\x83\xd1\xc0\x46\x52\x66\x0c\xd3\x13\xe7\x96\x08\x08\x21\x68\xbb\x3d\x8b\x47\x33\xf2\x26\x85\x19\x49\xe1\xc9\x00\x30\x72\xb6\x3d\x23\xfb\xfa\xcd\xfa\xa6\x7e\x33\x2f\x99\x4b\x3d\x8f\x2b\x3b\xe9\xbb\x6a\xd4\x09\x46\xb3\x2f\xd2\x3d\xe1\xf0\x02\xfd\xcd\xca\xb0\x97\x5c\x60\xf2\xd3\x2f\x17\x7c\xff\xea\x8b\xad\xd8\x61\xe4\x0b\x8a\xf8\x16\x33\x12\x8f\x3e\xa6\xf6\xab\x14\x5c\x0d\x70\x4b\xe3\x0a\xf4\xb8\x82\x22\x9a\x56\xd4\xae\x0b\xaa\x73\xa6\x13\xfc\x40\xce\x81\xd2\x26\xee\xf2\xca\xc4\x2a\x14\x91\xb3\x2b\x40\x46\xce\x81\x00\x1e\xb5\x5e\x78\x1d\xd9\xa2\xcf\x8e\xd0\x32\xbe\xef\x52\xf0\x6c\x81\xd1\xbf\x84\x0e\x1c\x06\xee\x6b\x5b\xe8\xe8\x5f\xa2\xcf\xd4\x68\xb3\xc8\xf9\x21\x80\x45\xce\xa9\x00\x2f\x6a\x3a\xf4\xb7\x84\x45\x30\x1c\x0c\x71\x83\x7f\xfd\xc2\x9f\x4f\x9e\x6b\xbd\x5c\x2e\x25\x63\x04\x2b\x2c\xd9\xdc\xc0\x08\xaf\xa6\xde\xe6\x63\xfc\x35\x92\x6b\x43\x5b\xe2\x35\x5f\x16\xe9\x08\xb0\x15\x6d\x9a\xa0\xa3\x9e\x28\xd7\x3c\x56\x40\x14\xb5\x6b\x38\x55\xb7\x4f\xf0\xf3\xa3\x19\xf1\x22\xc0\x3e\xd1\x19\xc5\x80\x92\x17\xe5\xb0\x18\x46\x2b\x6d\x1e\x39\x3a\x6f\x70\x6d\xab\xd1\x14\x49\x1e\xdd\xd1\x3e\x24\x60\x82\xc2\xbe\x10\x55\x54\xa7\x86\xce\x4c\xa4\x29\x9d\x8c\x41\xd2\x3f\x8a\x06\x71\x35\xda\x94\xf6\x6f\xe0\x51\x11\x7d\x34\x88\x56\x47\x79\xac\x26\xa8\x30\x85\x78\xd1\x18\xb5\x8a\x3e\x3a\x6a\xbe\x12\x5f\x12\x67\x46\x92\x08\x3c\x9d\xa1\x32\x0c\x48\x52\x5a\x9f\xe4\xb3\xba\x21\x41\x04\x0c\x01\xa1\xcf\x8e\x7a\x24\x59\x63\x18\xf3\x1d\x58\xdf\xa3\x79\xca\x89\xbe\x0b\x49\x73\xac\x2f\xc4\xbd\xba\x96\x57\xa9\x33\x25\xa1\x5a\x7a\x1d\x40\x44\x8d\xea\x45\xe1\x52\xd5\xbc\xae\x4c\x2d\xd3\x8b\xcc\x6c\x04\x0b\x09\x46\xf7\xc2\x0f\x6b\x29\xec\x45\x25\xd8\x0a\xc7\xa8\x06\xf5\xd6\xc5\x22\x67\x28\x9e\xe9\x65\x4e\x40\x3b\x81\xb7\x9a\x0b\x74\xb8\xf3\x93\xbd\xc3\x53\x46\x81\x7d\xb1\x63\x60\x43\x25\xfd\x64\x92\x82\xcb\xcc\x7b\x97\xab\x57\x52\x52\xf0\xb6\x6d\x01\x6e\xac\xdf\xcf\xeb\xf9\x1a\x34\x48\x3e\x2b\x22\xa5\x55\xac\x4a\xf1\x5c\x88\x92\x9f\xd7\x67\x69\x7d\xa0\xf3\xca\xd1\x11\xae\x18\xef\x7b\xdb\xc0\xfb\x2e\xc3\xa0\x49\x32\xd2\xe6\x74\x14\x62\x35\x39\x55\x1f\x55\xa4\xcb\xf5\x63\xe4\x64\x45\x04\xcf\xea\x2d\x9e\x3f\x6b\x6a\xc0\xde\xe5\x0d\xd6\x2b\x0d\x02\xe2\x96\x35\xbe\xa8\x7e\xb9\x7a\x1a\xc2\x21\x11\x30\xa8\x86\x44\x69\xad\x89\x7d\x0e\x07\xc5\x02\xa8\x36\xda\x1c\x6a\x4e\x61\x57\x68\x4b\x8c\x2b\xb8\xa2\x90\x46\xce\x42\xe8\xba\x2f\x52\x55\xf8\x3b\x71\xd8\x52\x13\x87\x2d\x4d\x15\xf3\x70\xd0\x10\xee\xc9\xfc\xb3\x1f\xc2\x00\x1a\x12\x98\x54\xa2\x87\x67\x61\x93\x95\xee\x5e\x46\xe2\x35\x9d\xbb\x4b\xf5\x94\x46\xe0\x07\x14\x23\x8a\x6f\xe1\xb7\xf3\xf0\x54\x26\xa6\x52\x14\x15\x31\x95\x0e\x04\x85\x9d\x94\x4c\x25\xb1\xce\x44\x12\x5f\x74\xfc\x4c\xa0\x51\x43\x47\xe7\x0b\xc7\x40\xdf\x7e\xb4\xd2\x7c\x9b\xf9\xf6\x38\x01\xf7\xb3\x1d\xa3\x0c\x3c\x8d\x4c\x9c\xa2\xee\x9d\xcd\x08\x46\x38\x45\x41\x58\x51\x18\x9d\xce\xd6\x9e\x46\x5a\x96\xae\x35\x97\x8d\xee\xc3\x7a\xe9\x23\x46\x98\x83\xfb\xc9\xd5\xb3\xec\xb3\x33\x14\xb7\xb3\x65\x8e\xeb\x96\xc4\x11\x1c\xa5\x28\x0a\xd4\x38\x2f\xd9\x4f\xfb\xee\xe7\x17\xb1\xc3\x19\xa9\xc6\xeb\x53\xec\x9c\x2e\x43\x5b\x4f\x52\xa1\xab\x12\x7e\xa6\xf6\x8c\xf8\x11\x64\x20\xfb\x29\xaa\x3a\xf1\xa7\xa2\x92\xee\x61\x85\x4f\x36\xf9\x63\xd0\x3c\x1c\x87\xe8\x1e\xe2\x10\x71\xc0\xec\xff\xc4\x50\xd9\x59\x39\x54\x76\x56\xe1\xc7\xdb\x86\xea\x39\x51\xa4\x87\x4a\x32\x3d\xb8\x20\x05\x0f\x4d\x9e\xd4\x57\x71\xcd\xed\x93\x14\x95\x50\x4d\xdf\x65\x8b\xdf\xcd\x16\xbf\xbb\x61\xd7\x9b\x74\x23\x88\xa2\x4a\x9b\x6e\x04\xbb\x69\x39\x1c\x6c\xad\x17\x5c\xd5\xac\xad\xf8\xe6\x42\x77\xec\x54\x7f\x33\xd6\xec\xcb\x69\x85\x8d\x5c\xaa\xd6\x8d\x60\x5a\xab\xf5\x64\xb1\xd6\xf1\x42\x67\xc7\x45\xb5\xa7\xcb\xd5\x8a\xce\xca\x5a\xcf\x6c\xed\xf7\xba\xb4\xdf\x87\x44\xf6\xa7\x4a\x6c\xc3\x3f\x57\xad\xea\xb3\x73\x5c\x03\x9c\x3f\xae\x4e\x29\x25\xa0\x9c\xb0\xc0\xe6\x9f\x93\x7c\x75\xb4\x06\x5e\x68\x57\x32\xbd\x94\xfa\xb7\x29\x89\x8b\x92\x28\x82\x06\x15\xbf\x00\xa3\x81\x5d\x86\x2a\xcf\x49\xfa\x9e\xda\x23\x8e\x6e\xbc\xee\xa1\x2d\x9c\xa4\xcf\xb4\xaa\xbf\xb8\x45\x4a\x1c\xe2\x55\xe1\x49\x43\x8d\x9a\xb0\x12\x95\x3c\x75\x7a\xbd\xfc\x4a\xc2\x1c\xe0\xc5\x0f\x15\xf7\x10\x1e\xae\x90\x37\xa5\xda\x06\x6a\x49\xeb\x8d\xb5\xf5\xc7\xd1\xba\x22\x5a\xe5\xa5\x54\xf5\x07\xd1\x6a\xb1\x19\x39\x48\x01\xfd\x4a\x44\x9f\x6d\x16\x81\x84\x6b\x36\xd4\x5d\xfb\x16\x11\xd6\x39\x05\xb6\x69\xcf\xf0\x79\x93\x82\xfb\xce\xfe\xc2\x89\xb5\x95\x64\x91\xdf\x89\x13\xd9\x49\x33\x77\x1c\x4a\xd4\x5d\x2a\x9c\x0a\x79\x6e\xe1\x4e\x98\x62\xf9\x2d\x97\x1d\x0c\xc4\xdf\xb7\x72\x1f\x9f\xac\x1a\xb1\x38\xb7\x4d\x7f\x67\x7b\x49\xc5\x8e\x39\x37\x50\xff\x98\xc1\x2e\xf2\x6c\x49\xaa\x83\x16\xc2\x5b\x3c\x81\xa7\xc6\x9c\xfb\x15\x9a\x73\xe3\x8b\x8a\xcf\xc1\x73\x73\xc8\x37\x1e\x38\x6a\x57\x14\x0a\xe9\x77\x47\x9f\x13\xbb\xb4\xd6\x97\x6b\xd5\x1c\xc9\xd6\x9a\x57\x39\xd5\x95\x31\x29\x39\xb8\x38\x25\xac\x6a\x06\xac\x9d\x13\x98\x19\xe7\x2d\x79\xe7\x1a\x4f\x12\xf7\x4a\xb1\x4c\xc7\x14\x07\xbe\x9d\x36\x78\x35\x34\x24\xbd\x44\x52\x5a\xa6\xbd\xec\x7b\x53\xb5\xec\x37\xe4\x73\x6a\x62\x6e\x78\x18\x7b\x55\x20\x58\x78\x3b\x36\xce\x41\x67\xc3\x6c\xf4\x1a\x14\x70\x4b\xce\xd4\x92\xe1\xec\xf5\x18\x3f\xe0\x5a\x2e\x99\xfa\x6f\x3c\x30\x82\xf6\x48\x35\xfb\x9c\x28\xa9\xb6\x61\x84\xc8\x16\x48\x93\x7e\x5f\xf1\x84\xda\x5a\x71\x69\xcc\x33\xf2\x3e\x45\x3e\xbd\x36\x52\xd1\x77\xdf\xcd\x61\xd1\x42\xfd\xcc\xa5\x15\x83\x15\xcd\x39\x37\x80\xa7\xb7\x0c\x9e\x7b\x1a\x0c\x59\x8c\x40\xe6\x87\xe9\x24\x62\xb7\x1d\x16\x04\x3a\x76\x11\xe6\xf2\x4e\x57\x02\x23\xb4\x80\x78\x01\xa4\xde\xb2\x87\xc3\x9e\x06\xd2\x12\x3c\xbf\xe6\xe0\x89\xbe\x32\x3e\xea\xc1\xaf\xe8\x72\x58\x8c\xb2\x83\x78\x85\x43\xa8\xaa\xb1\xb5\xec\xa4\xb5\xd4\x87\xda\x13\xb5\xd0\x63\x8d\xa4\x8b\x93\xe9\x26\x0b\x6e\x7c\x03\x83\x89\x67\xe4\x4b\x5a\x54\xec\x4f\x97\xdc\x86\x54\x57\x21\xae\xf9\x26\x24\x0b\x8e\x78\xc3\xa1\x9d\x95\x1e\x33\x7a\xa9\x12\x8c\x21\xbe\xe4\xe2\x82\xf7\x35\x0a\x16\x16\xd6\xe7\xbb\x5e\x9f\xa4\xc9\x7f\x2e\x77\x5f\x28\xe6\x8b\x36\x5b\xd1\xea\x50\x62\xdb\xaf\xdf\xbd\x3e\x79\xbd\x18\x4d\x6c\x12\x55\xdc\x0c\xf4\xdd\x9d\xf1\x31\x98\x45\x7f\xed\x26\xad\xef\x7a\x2d\x97\x69\x17\x11\x08\x98\x44\xfa\x4a\xe3\xf6\xaf\x74\xdb\x10\x7c\x6a\xe9\x96\xee\x2c\x21\x78\x51\x87\x2e\xdb\xad\x2b\x70\xa1\x43\x79\xb9\x1f\xb4\x77\xc5\x75\xd4\x68\x80\xfc\x92\xe9\xcc\x08\xb9\xcf\xeb\xa5\xbd\x2f\x48\x4c\x81\xdd\xd8\x31\xb8\x27\xfa\x62\xf8\x65\xd4\x6a\x7e\xb8\x9c\x21\x9c\x65\xf9\x45\x7d\x0d\x22\xdd\xa4\xa6\x26\xf5\x92\xfa\x36\x7f\x31\x29\x57\xe6\x14\x8e\x23\xe7\x8c\xfc\x39\x84\x75\x18\x7c\xa3\xf0\xba\x4d\x23\xd0\x90\x3e\xbf\x06\x6c\x37\xf6\x8c\xbc\x8c\x9a\xb5\xc5\xe7\x1c\xae\x23\x35\xdd\xe3\x88\xce\x21\xae\xf9\xdb\x2e\x9d\xc7\x73\xdb\x38\xd3\x69\xd8\x76\xf7\x10\xeb\xdd\xe8\x63\x7e\x4b\x6e\x91\x8d\x83\xeb\xe5\xa9\xe7\x24\x62\x4f\x11\x77\xc3\x9d\xf7\x5d\x8f\x2e\xfb\xd1\x6a\x56\xd3\x5b\x24\x1d\x79\xe3\xca\xd7\x34\xe5\xbb\x25\xb3\x08\xad\x04\xa6\x92\xbc\x54\xd2\x8c\x37\x2a\x69\x16\xaf\xd2\x2c\x8b\xda\x9f\x93\x92\xe4\x6d\x16\x87\x7d\xe1\xb4\x86\xc8\xa9\x30\x64\x5c\x3c\xed\xdc\x76\xc3\x20\xe9\x7b\x14\xd5\x13\xc2\xad\x75\x0a\xe8\xe7\x16\xe6\x7e\x6e\x9e\xc3\x96\xfc\xdc\x12\x0a\xea\x94\xd8\x0d\x48\x20\x9c\x63\x44\xcc\xe6\xa3\x5e\xf5\xc9\x43\x83\x86\x68\x85\xc1\x55\xfb\xee\x2f\xa1\xd0\xe1\x9d\x28\x74\x73\x79\xd7\xd0\x65\x7a\x19\xc7\x56\x69\x61\x05\xdb\xba\xfb\x36\x9a\x9e\xe8\x36\x76\x95\x1c\xa8\x32\x96\x11\x6e\x88\x62\x8e\xfd\x8e\x99\x26\x0a\x52\x11\x05\x9e\x13\x05\x14\xc5\xab\xca\xfd\xac\xc1\x8f\x4e\x8d\x6e\x88\xe0\x77\x96\x82\x84\x0c\x5e\xb3\x0a\xfa\x5c\xd5\xa2\xdc\xa3\x45\xde\x26\xd3\x9e\x6e\x3f\xc4\x4a\x93\xa2\x8f\x76\x0c\xec\xc4\x96\xe0\x9e\xd9\x1c\xdc\xaf\x1a\x53\x9c\x1a\x5b\x8f\x44\x84\x17\x61\xcc\xa2\x55\xa9\x77\x27\xc6\xb4\xe2\xfb\xaa\x24\xca\x68\x9f\xf2\x5d\x7d\xe6\xa3\xf1\xd9\x6f\x55\xa1\xbe\xb1\x05\x98\x7c\xbe\xb0\x6b\xc6\xc1\x7c\x5f\xf0\x34\x6d\xb7\x4f\xd1\x2a\x88\xd5\x16\x29\x82\x33\xff\xb6\xc9\x1e\x25\xe5\x52\x46\x35\xb3\x93\xdc\x22\xc5\x0f\x53\xe6\x46\xff\x54\x42\xe4\x2b\x6d\x8e\x33\xe1\x68\xfd\xb1\x2d\xc8\x59\x42\x76\x95\x8c\x92\x27\x63\xc9\x4d\x4f\x0e\x84\xce\xdc\xa2\x27\x57\x66\x6c\x71\x39\xf9\xae\xd3\x21\xef\x8a\x3b\x8c\x2f\x7e\xb2\x03\x5b\x02\x7b\x6f\x67\xc0\x0e\x6d\x0e\xec\x99\xcd\xc0\x45\xfd\xd9\x17\xbd\xa4\x07\x66\x49\x2f\x92\x4f\x5c\xa4\x61\x12\x97\x8b\xea\x66\x61\xe4\x6f\xa3\x75\xcf\xc2\xab\xd3\x94\x8b\xca\x2b\xc1\x62\xef\xb2\x9a\x96\x66\x1a\x2e\xf6\x33\xad\x74\xec\x72\x9c\xab\xb6\x71\xd9\x8a\x72\xeb\x1b\x19\xaa\x29\x1e\xe4\xe6\x35\x71\x10\x5e\x58\x70\x2a\x16\xda\xef\xc5\x41\x82\x8b\xa2\x2b\x45\x59\x8a\x11\xe1\xae\x54\x9f\x3f\x74\x9f\xb0\x17\xdd\xc3\x1f\x27\xe7\x0e\xb4\x0b\xfa\xfb\x00\x62\xd8\x8a\x16\xe9\xb2\x50\x74\x59\x5b\x83\x1f\x46\xab\xae\xc0\xb4\x00\xb0\x17\x41\xc1\x60\x5f\x31\xf8\xaa\x18\xec\x1c\x41\xb8\x1f\xed\x9f\x2e\xe6\xff\x98\x1b\xb4\x47\xe1\x3c\x6a\x8f\xce\xf7\x54\x47\x30\xda\x5c\xbe\xaa\x7c\x62\xb0\xc5\x94\x7c\x76\xe1\x5a\x1d\x72\x77\xc3\xb0\xf6\x1c\x24\xd5\x41\xa5\xea\x58\x61\x03\x99\x32\x2f\xb3\x3d\xf0\x3e\xd9\xa7\x9c\x20\x79\x62\x25\x6f\x87\x0c\x37\x24\xce\x94\xbc\x8e\xe0\x25\xa2\x8e\x73\xd3\xab\xc2\x0c\x9a\x6c\xd4\xfd\x4d\x4d\x77\x1f\x55\x77\xe0\x9d\xeb\x6b\x20\x8d\xf4\x0e\x38\xb0\x65\x9b\xd4\xbc\xe1\x27\x7b\x4b\x33\x87\x85\x04\xf0\x55\xfb\xa8\xe6\x2e\xaa\x35\x05\x02\x7a\x32\x3b\xb7\xe4\x2a\x02\x13\x50\x3c\x99\xab\x21\x76\xf5\xd0\xc2\xe5\xa1\x7d\xb2\x3f\x70\xa2\xc8\x53\xd7\x0e\xcb\x51\x7d\xaa\x8e\x6a\x60\x87\x0d\x08\xd4\xfb\x64\xbf\xe2\x24\xa4\xf9\xf0\x70\x37\x8f\x31\x47\x6d\xa8\xe5\xb8\x2d\x55\xbc\x28\x5e\x06\xcd\x1d\x7d\xe7\x24\xa0\x8b\xf3\xdc\xe5\xe4\x87\x7a\xdb\xe8\xe2\x9e\xb7\x7b\xbd\xc8\xbc\x6f\x36\x3b\xc1\xc7\x25\x1b\x36\x5c\x10\xf7\x0e\x13\x52\x2d\x6b\xf1\xba\x97\x95\xc8\x11\xcb\xc6\xc7\xe5\x60\xae\x92\x46\xfa\x39\x23\x87\x46\xe4\x28\x08\x67\xa0\x61\x52\x73\x1c\xee\x86\xfa\x4c\x96\xc3\x64\xd0\x04\x93\x78\x3d\x57\x40\xa1\x26\x83\xae\x06\x41\xdd\x89\x77\xae\x3b\x31\x20\xe8\x2e\xef\xf3\xb9\xed\x36\x03\xdd\x63\xdb\x55\xc7\x3a\x1f\x8e\xf7\xb9\xa5\x83\xcf\xa6\x03\xb6\xc0\xa9\xa5\x1a\xd8\x4c\x5b\x03\x66\xe9\x72\xeb\xae\x9d\x36\x43\xd7\xba\x9d\x3a\x37\xe4\xb2\xe8\xe2\x6b\x3e\x87\x86\x4e\xbe\x9a\x4e\x8a\x21\xd4\x41\xc2\x7d\xbe\xc4\xe8\xd4\xbd\xf3\xbd\xd9\x52\x95\xe7\xf5\x2a\x51\xbd\xca\x7a\x9d\x87\xf5\xc2\x4a\x95\x16\x29\x2d\x59\x64\xdd\x4e\xa2\xd5\xb9\x13\x67\x36\x87\x1f\xb6\x84\x4c\x27\xc4\xca\xe0\xa5\xa6\x38\x9f\xa3\x66\xff\x3c\xf7\xe1\x43\x62\x59\x9a\xf7\xd2\xda\x40\x37\xcf\x66\xd0\x67\xf0\x39\x42\xf5\x08\xb5\x77\xe6\x70\xd4\x8a\x92\x6f\x64\x7e\x73\x38\xd2\xa1\x6e\xed\x3c\x1d\x82\x09\x44\xbc\xb3\xf8\x71\x29\x6e\x2b\x4d\x7d\xde\x90\xfa\x8b\xce\x3d\x66\xd8\x86\x22\xcf\xd2\x7c\x0e\xef\xdb\x50\xb8\x8e\xfa\xea\x58\x26\x31\x9d\x54\xf3\x92\x3a\x30\xe0\x03\x99\x47\x05\x8c\x1d\xf5\xc8\x40\x3a\x3b\x11\x5e\x31\xe7\x77\x73\x95\x34\x87\xe2\x85\x44\xff\xf5\x58\xd5\x89\x17\x2e\xdf\xb4\xf1\x85\x8e\xf6\x0b\x47\x91\x0e\x8f\x52\xcd\x20\x40\xe1\x5d\xeb\x1a\xb5\xc4\x67\x7f\x10\x97\x0e\xf4\x26\x93\xd4\x62\x8e\x00\xd1\xcf\xf2\x08\x8d\xb4\x5c\x33\xbc\xda\x78\x91\xc7\x2c\xe3\x7d\xf6\xf0\xe1\x62\x14\xb7\xa2\x4e\xec\xc8\xf9\x9c\xc4\x64\x4a\x4e\x22\xd8\x59\x72\x90\xdc\xd3\x34\xba\x9c\xa8\xde\xf1\x87\x0f\x75\x94\xb2\x3e\x1b\xc5\x7d\xd7\x8e\xe9\x5c\xc9\x92\x92\x82\x5a\x07\x82\x39\xb3\x8f\x68\xbf\x3b\x8a\x12\x1b\x03\x0f\xbc\x8f\x20\x4a\x4c\xe8\xdd\x87\x96\x46\x95\x6a\xf7\x1f\x43\x26\xb5\x6c\x7d\x16\xad\x48\xf7\x90\xb3\x4d\x17\xb6\x84\x73\x05\xb8\x37\x36\x87\x23\xdb\x83\x63\x9b\xc1\x89\x9d\xc1\xae\x06\xe2\xed\xe8\x2f\xe4\x41\x93\xa4\x88\xd3\x2b\x02\xa3\x64\xc6\x5c\xfe\x2b\xf4\x1c\x27\x11\x88\xfe\x4b\x10\xfd\x0c\x44\xff\x07\x88\xfe\x0c\xf2\x7b\x24\x4c\x4b\x18\x2d\x64\x6a\x2f\xf2\xb4\xb7\xe7\x1f\x8f\xfb\x2f\x71\x2b\x33\x34\xa0\xff\x81\xf6\xf3\xb3\x17\x55\x99\xf9\x2d\x2e\xf8\x67\x0a\x9e\xde\xa3\x92\x9b\xd4\xc3\x6d\x4e\xed\x38\x23\xdb\x11\x7a\xd2\xec\x25\xb0\x93\xc1\x6e\x48\xf4\xc5\x1b\x83\x49\x48\xac\x80\x45\x29\xb7\xb4\x4e\x1d\x4e\xa3\x15\xd1\x33\x1a\xd3\xcb\x17\xdf\x78\x97\x28\x7a\x82\xc7\x71\xe5\x4d\xf2\x8c\x9c\xaa\xd1\x10\xe9\x88\xc6\xeb\xcb\x1f\x95\xa8\x9e\x38\x49\xb5\x2c\x6a\x51\x04\xc4\xfd\x19\x10\xbc\x36\xfd\x4c\x09\x5a\xf6\xd0\x6f\x68\x61\x92\x3b\x92\xef\x56\xe6\xef\xad\x48\x34\xd7\xb8\xd2\xda\x51\xe1\xb3\x41\x09\x1a\x03\x60\x66\xbd\x92\x2a\x5c\x90\x18\x3c\x3a\xca\x47\x56\x24\x72\x29\xb6\xe1\x9b\xc2\x7e\x45\xd4\xce\x39\x1c\xac\xd8\x8f\xc6\xf4\x29\xae\xc1\xab\x79\x2a\x44\x94\x55\xe7\x4a\xbc\x9b\x91\x2d\x38\x53\xdf\xfc\x2e\xee\xf8\x67\x57\x90\x3c\xa0\x3a\x85\x83\x22\x32\x33\xc5\x1f\x3a\x00\x33\x5e\xb2\x6c\x47\xb0\x14\x82\x39\x07\x92\x09\x26\x02\xa5\xba\x4d\x11\xc8\x99\x2a\x10\xab\x84\x93\x36\xbf\xcb\xd8\xd1\xe6\x85\xf1\x17\x30\x62\x9e\x1a\x8f\x92\xd8\xc2\xf8\x42\x8d\xa8\x96\x60\x30\xf9\x67\xf7\x49\x9f\x2b\x8d\x87\x31\xeb\xaa\x93\x10\xcf\x60\xf6\x02\x55\xef\x68\x54\x5d\x87\x2d\x13\xb1\x55\x50\x88\xf5\x0e\x12\x89\x30\x55\x6e\xa1\x92\x06\x67\x4a\x9c\x39\x3e\x39\xda\x7b\xff\xc6\x82\x1b\x89\x22\x3a\x82\xf1\x6e\xb9\x18\x6a\x66\xf8\xee\x0a\xdf\x72\x3f\xd4\xd7\xd6\xdb\x62\x31\x65\x6a\x5c\x1a\x8b\xc6\x90\x39\x72\xe5\x99\x12\x80\x4e\x43\x59\x7e\xaa\xc2\x15\x7e\xfb\xf9\x4d\x99\xa0\xe0\x5e\xd9\xb7\x6a\x1f\xbf\x04\x64\x1a\x50\x18\x07\x64\x87\x9a\xf4\x8e\x61\xcb\xc0\x0d\xd8\xc4\x7c\x66\x51\xb8\x25\xdb\x02\xc2\x2a\xe8\xa8\xe7\xdc\xa7\x42\x43\x05\xc2\x63\xc3\xa0\x5d\x72\xa6\x80\xfb\x5a\xc2\x83\xa1\xfa\x37\xff\x3f\xe6\x64\x32\x5f\xa9\x7c\xb9\x0a\x9e\x7a\xa9\x97\x47\x77\x85\x07\x1b\x5f\x6a\xc1\x90\xc2\x8f\xc8\x21\x32\x71\x50\xe1\xb5\x05\x1f\x39\x1c\xa8\x49\x6c\xc1\x56\x82\xe6\x11\xea\xf1\x2a\x81\x03\xf3\xf8\x8a\x43\xa8\x9f\x3e\x70\xd5\x9b\x7a\xfa\xce\xe1\x87\x29\x3e\xe5\x70\x6a\x1e\x75\x14\xc3\x86\x69\x69\x5e\x65\x4e\xbf\xd1\x26\xdb\x08\xc4\x7e\x4d\x2c\x13\x9a\xbf\x48\x94\x28\x77\x23\xe7\x95\x80\x83\xc8\x44\x4a\xfc\xa4\x0d\x14\x36\xe6\xf0\x55\x3f\xad\xcf\xe1\xbb\x7e\x7a\x32\x87\x57\xd1\x7d\xb2\x41\x6d\xb5\xd8\x21\x8f\x81\x3b\x7b\x4c\x47\x91\x9d\x91\x57\x91\xb6\x43\x0a\x13\x9d\x04\xd7\x9a\x88\xc4\xcf\xb0\x91\x05\x49\x82\xb1\xd4\x94\x64\x3a\x52\x54\xdb\x56\x14\xde\x6b\x6a\x75\x51\x8b\xeb\x9f\xb7\x4c\x9c\x3c\xf6\x91\xe3\xe8\x76\xf9\xef\x6a\xeb\xa0\x16\xaa\xe9\x46\xb7\x1e\x0d\xed\x01\x84\x0d\x71\x99\x78\xc5\xba\xa2\xe6\x01\xbd\x94\xda\xa2\x73\x9b\x98\x38\x4c\xfa\xf3\x0b\x1f\xf6\x6b\x1e\x31\xe6\xc3\x90\x39\x6f\x39\xc9\xe7\x7c\x29\xe5\xc4\x7e\xf4\x28\x4a\x3c\x16\x5d\x26\xa9\xb4\x9f\x0f\x9e\x6f\x3c\xb2\xaa\x1a\x87\x08\x0e\xb5\x2b\xf7\xd8\xf9\xa9\x6f\xda\x76\xf0\xde\x4c\x96\xda\x68\x37\x01\xf7\xc4\x1e\x56\xe0\xa7\xbb\x8c\xdb\xa6\xcb\xaf\xfc\xe5\x57\xd1\xf2\xab\x74\xf9\x95\xbb\xfc\x2a\x58\x7e\x15\x2e\xbf\x6a\xc0\xb9\x0d\xe4\x92\x2d\xbf\xca\x96\x5f\x35\x24\x74\x6d\x60\xb5\xc4\x0a\xee\x4b\x89\x9b\x3e\xb8\x9b\x76\x08\xee\x53\x3b\x00\xf7\xb9\xed\x81\x17\xda\x1c\xbc\xc8\xce\xc0\x4b\x6c\x09\x5e\x66\xa7\xe0\xcd\x6c\x86\x77\x95\xe0\xbd\xb1\x13\xf0\x3e\xd9\x11\x78\x9f\xed\x18\xbc\xaf\xf6\x14\xbc\x73\x7b\x0c\x5e\xd7\xee\x82\xfb\xd1\x76\xe7\xf5\xff\x2d\x5e\x96\xe6\x3b\xf7\x60\x08\xee\x3e\xde\xc8\x91\x19\xb9\x4c\x30\x5e\x9f\x7a\xec\xaa\x47\x8f\x52\xc2\x29\x09\x29\xb9\x48\xa8\x66\x6a\x49\x46\x09\xa3\xc4\x4b\xca\xff\x62\x4a\x3c\x4a\x24\x25\x3f\xbd\xa9\x9d\xcc\x35\x0b\xfb\x4a\x38\x6f\x63\x05\x22\x1f\x5b\x4f\x72\x29\x26\x88\x35\x62\xd9\xd6\xda\x55\x91\xd0\x07\x3e\xac\xb2\x89\x35\xe2\x93\xb4\x8f\x88\x84\x23\x52\x8a\x4e\xfb\x51\x3b\xd3\xe5\x29\xd2\x73\xe4\x9a\xc8\x43\x6f\x22\x67\x3f\x22\x96\x17\xb1\x34\x7d\xcf\xc6\xdc\xa2\xf0\x25\xca\x1d\xaf\xdc\x2b\x25\xc2\xc5\xbe\xf3\x26\x26\x96\x1f\x4e\x2d\x0a\x42\xff\x48\x27\x2c\xb6\x28\x48\xdf\xd9\x8f\x81\xfb\xce\x8c\xc4\x3e\xec\x00\xa2\x61\x61\x9e\xa4\x4f\xac\x77\x09\xf3\xc3\xf8\xa2\xdf\xef\x5b\xf4\x9b\x0e\x59\x93\xf9\x4e\x2c\x80\xf9\x2d\x91\x5f\x92\xd3\xc9\x84\x8b\x2d\x96\x72\x42\xe7\xe0\xf9\x7f\xe1\xde\x4b\x5f\x7a\xe5\x1a\x82\xc5\x8b\x02\xee\xd7\x71\xc5\x7b\x56\xdc\x74\x25\x7a\x52\x6e\x26\x65\xa2\xa6\x15\xfa\xf7\x0c\x96\x14\xe8\x86\xa1\x45\xc1\xf5\x9d\x4c\x40\xea\xb7\x2f\xbc\xeb\x43\x0c\xa6\x07\x81\x36\x65\x91\xef\x30\x01\xbe\xdf\xb0\xc5\xb1\x13\x8f\xac\x80\xf5\xc6\x61\x9c\xa5\x96\xad\x1e\x27\x51\x96\x5a\x95\xcc\x99\xbe\x5a\xe4\x33\xc4\x45\x6f\x22\x62\xb9\x32\xee\xb8\x32\xee\x25\x99\x8c\xc2\x98\xf7\xc2\x38\x48\x3a\x6e\x22\x7c\x2e\x7a\x83\xce\x58\xf4\x86\x9d\xb1\xdb\x1b\x22\x91\x8f\x7c\xb0\xc6\x4c\x5c\x84\x71\x2f\xe2\x81\xb4\xc0\xea\x6d\x08\x3e\x56\x7b\xa4\xf7\x30\xc0\xce\x55\xb7\x01\x43\x85\x38\x7e\x62\x2c\x7a\xeb\x58\x67\x47\x6d\xbd\x62\xba\xc6\xc6\x98\x6f\xea\x23\x18\xc9\x50\x46\x0a\x84\xba\x7a\x5d\xb2\xc8\xa2\x30\xd6\xcf\xcc\xa2\x70\xe9\x6b\x4b\xbf\xd6\x25\x3a\xe6\x79\x28\xba\xd6\x2a\xdf\x03\x53\x85\x9c\x91\x3f\x73\x7c\x6d\x01\x3e\xa5\xea\xf1\x9b\xe2\x1f\x2e\xfc\x95\x29\x72\xe2\x6a\xea\x06\x13\xde\x43\x7b\x29\x83\xb1\x19\x71\x2e\x7d\x92\x61\x76\x8f\x91\x3e\x66\xac\xdf\x1d\x11\xf4\xd8\x28\x92\xab\xc5\x8e\x89\xf4\x3f\x91\x04\x83\x6c\x7a\x12\xba\x92\xc8\x35\xab\x63\x51\x40\xc5\x32\x69\xa9\x83\x35\x44\x53\x0d\x41\x31\xcf\x47\xf5\xab\xb5\xaf\x75\x25\xc9\x07\xa2\x73\x44\x65\xf8\xb1\x4a\x4f\xfa\x03\x6b\xa6\xb7\x85\xf7\xfa\x1d\xc6\x50\x6f\x3c\x81\x67\x24\x8f\xbe\xd4\x4f\x27\x51\x28\xc9\xa3\x7f\xa4\x6b\x8f\x2e\x30\x7d\xac\xd9\x63\x26\x2e\xb8\x54\xcc\xa1\xde\x58\xe9\x5b\x14\x6e\xcc\xf3\xa5\x45\xe1\xda\x3c\x2b\x6e\xf1\xa5\xdf\x7e\x39\x1e\xa3\x53\x46\x9f\x0d\x28\x1d\x55\xc0\xf9\x5a\xde\x07\x9e\x73\xc1\xa2\x11\x60\xf3\x73\xd3\xa9\x01\xac\x85\xf7\x12\x0a\x64\xed\xfc\x83\x77\x7e\xc9\xa2\x70\xc6\x48\x31\xd2\xb6\xef\xa9\xc3\xd9\xfe\xb9\x39\x85\x63\xbd\x2a\x91\xc2\x15\xaf\x17\x56\x85\x43\x21\xe6\xb4\xa7\x4a\x10\xd5\x54\x09\x4a\x16\x18\x1d\xe6\x19\x70\x31\x07\x9a\x7d\x88\xd1\x35\x8c\x18\x5a\x49\x8e\x82\x9a\x16\x01\x87\x64\x07\x34\xef\x4f\xe1\x6a\xe5\xe1\xa8\x27\x65\x8c\x0b\x5f\x25\xff\x85\x62\xac\xae\x7c\xed\x33\xd0\xe7\x34\x4f\x1e\xb5\x65\x76\x9c\xb9\x78\xfa\xf7\x7c\x47\x30\x38\xf4\xdb\x9c\xe3\x20\x76\x8e\x08\xe1\xce\x8c\xbc\xf6\x4d\x58\x53\x09\x1f\x25\xa9\x64\x7e\xa1\xd5\x88\xe5\xad\xb8\xe0\x55\x81\x0b\x62\x8a\x61\xcb\x03\x4c\x60\x71\xa9\x04\x13\x74\x4b\x76\x2b\x8c\xdb\xb1\xaf\x77\x4e\xe1\xbe\x49\x92\x86\x9a\xed\x45\x49\x22\xf4\x2c\x0d\x73\x2c\x0a\x2f\xe2\x5e\x28\xf9\x38\xed\xa1\x1b\x78\x27\x0a\x53\xd9\xd3\xb1\xed\xd5\xeb\x12\x00\x27\x0a\xa9\xba\xbd\xcd\x12\x04\xe3\x02\x24\x66\xbd\xe1\x00\x4b\xd7\x3b\x7e\x2f\x88\xf8\x4d\x67\xa9\xe3\xbc\xd9\x77\x25\x68\xc2\xe0\x8f\xaf\x68\x65\xf8\x56\x9d\x84\xcc\x6f\x94\x20\x32\xf2\xb3\x6b\x3f\xc3\xec\x64\x28\x42\xbd\xf4\x75\x34\x14\x5b\x41\x9a\x45\x81\x60\x08\xcd\x27\x68\xc6\x88\x6f\xec\x1f\x4c\x5b\xf6\x7f\xc2\x6f\xec\x64\x6a\x6d\x3e\xf6\x5d\xbf\x52\x65\x46\xc6\xf7\x3b\x06\x1d\xc9\x6f\x24\xbe\x6a\x3f\x73\xfc\x96\xf7\xd2\x88\xa5\x97\x0d\x07\xa1\xd0\x0b\x28\x16\x60\x51\x9f\x58\xdc\x70\x8c\xb4\x00\xe8\x58\x6b\x07\x21\xf9\x94\x2d\x1b\xb6\x97\x3e\xc4\x21\x2a\x9a\x07\x26\xb7\x55\xa0\xf8\x25\xbd\xed\x95\x64\x7a\xf7\x3e\xdf\xdb\x0c\x23\xad\x36\x4f\x2a\x0a\xe3\xeb\xa5\xf9\xbc\x0b\xe3\x6b\x7d\xb0\x09\x26\xa7\x82\x0b\x22\xd0\xa7\x2c\xc7\x66\x5b\x45\x2f\x78\x28\x3a\x05\x44\x0c\xb1\x1b\x9c\xd9\x29\xd3\xf0\xca\xde\xd2\x72\x17\x0d\x23\x85\x15\x76\xd1\x4f\x43\xa3\x8f\x73\x7f\x85\x26\xb4\x48\x5c\x33\x6a\xf7\x05\xd0\x96\xb8\xe5\xf2\xc4\xd5\xe5\xd1\x10\x5e\x65\x12\xf2\xe5\x10\x0b\xab\x88\x40\x30\xce\x70\x23\x0d\x1b\x91\xa5\x5c\xf4\x52\x1e\x71\x4f\xb1\x11\x61\x1c\xca\x90\x45\x45\x69\x6f\x9c\xfc\xe8\xdd\x51\x65\xc6\xdd\xeb\x50\xde\x51\xcb\x6c\x97\x97\x44\x4a\x76\xb3\xfe\xeb\xb1\xeb\x0d\xfc\x02\xff\x4b\x9f\x88\xb5\xdf\x1c\xeb\xb7\xb5\x78\xed\x37\xeb\x37\xdc\x92\xbb\x30\xbc\x46\xec\xef\x18\x39\x24\x5a\xcd\x0b\x53\x9f\x58\x3b\x08\x82\x1d\xf7\xb6\x23\x2f\xc3\xb4\x13\x31\x97\x47\x95\xaf\x58\x6b\x39\x1f\x3b\x07\x4e\xed\x86\x25\x52\x9f\x49\xb9\x97\xc4\x3e\x13\xb7\xcb\x2b\xaa\xfa\x78\x9f\xc8\x0e\x2e\xb8\x39\x0f\xdf\x14\x22\xf5\x7e\xfd\xc2\x68\xcc\x18\x6f\x86\x39\xab\xd1\xc0\xf3\x1c\x0d\xdc\x12\x5f\xa1\x01\x86\xc6\x3f\xd2\xd9\x17\x04\x83\x33\xe1\xf5\xc4\xe8\x4a\x12\xa9\x38\x04\x3c\x5d\x96\xbd\xf0\x33\xb5\x40\x3a\xb5\xe1\x8f\x23\x3d\xd8\x41\xb1\xea\xb3\xcb\x50\xf2\x5e\x3a\x61\x1e\xb7\xc0\x8a\x93\x99\x60\x93\xca\x4c\xa4\x1e\xfd\x02\x50\xed\xd4\x91\xe1\xd8\xed\x6d\x18\xa0\xf7\x24\x30\x38\x22\x89\x0e\x58\x23\x46\x33\xd2\x2d\xaa\x95\x88\xd6\x0c\x21\x3f\x26\x33\x72\xe8\x03\x66\xfb\xcc\x8a\x63\xa2\xcf\xc4\x89\xdf\xae\x93\xc7\x64\xb0\xea\x98\xa4\x09\xa4\xda\x48\xd2\xaf\x5a\xd3\xe8\xd3\xa3\xdd\x90\x62\x93\xc3\x2a\xae\xde\x85\x54\xa7\x30\x89\x7a\x8f\xcd\x80\x8e\x64\x1b\x39\x72\xc9\xb9\x8f\x87\x12\xd3\xf6\x05\x78\x95\x72\x89\xf7\x19\x7b\x98\xa6\x7e\x8e\xea\x86\x8a\x71\xbc\x7b\x83\xcc\x26\xc3\x0a\x0e\xe2\x30\xa6\x9a\xbc\x58\xec\x70\x46\x4e\x7c\x8c\x02\xa5\x68\x1d\x28\xa4\x97\x15\x6a\xc9\xf7\x4c\x41\x93\xde\x51\x0d\x52\x69\x27\x48\xb2\xd8\x47\xdb\x6d\x26\xee\x90\x02\x3f\x06\x46\x0a\x3c\x52\x82\x08\xb1\xbc\x4b\xee\x5d\xe3\xd9\xde\x31\x82\x4d\x3c\xc9\x14\xab\xf7\xde\x30\x2f\xfa\x34\xc0\x3b\xbf\x34\xe0\x34\xec\x20\x14\x8d\xbf\x51\xd4\x08\x9d\x19\x6e\xf1\x76\xa2\x58\x82\x6d\xbf\x5d\x8c\xcd\x49\xb3\x5a\xe9\x98\x4d\x91\x4a\x96\x08\xe8\x7d\x09\x48\x52\x81\xa7\x82\x51\xad\xab\xee\x79\x49\x2c\x45\x12\x15\x3f\xd5\x00\xdc\xe4\xa6\x6c\xbb\xa3\x79\x4a\xdf\xcc\x0c\xcb\x90\xce\x2f\x76\xd0\xcb\xa7\x79\xe4\x97\x09\x11\x29\x85\x57\x4c\xdf\x8f\x48\x88\x33\x6a\xb0\x7f\xf5\xbc\x2c\xf5\xe2\x87\x1e\xea\x96\xee\xae\xeb\xf3\xd4\x13\xe1\x04\x79\x90\xf2\x3c\xc5\x06\xb7\x68\xf0\x7e\xeb\xaf\x76\x75\x6c\x5f\x35\x93\x1b\xa8\xfa\x7d\x55\x05\x49\x59\x85\x71\xe6\x25\x1b\x6c\x90\x2c\xf3\xae\x15\x14\xc5\xbe\x05\x96\x14\x2c\x4e\x27\x4c\xa0\xa6\xd7\xe0\x83\x20\x89\x35\x72\xbe\xe4\x22\x2c\x5f\x7b\x99\x48\x11\x2d\x4f\x92\x30\xd6\x6a\x62\x5d\x60\xf0\x2d\xe2\x8e\x98\x9b\xc5\xcf\x87\xa2\x11\x30\x5e\x14\xe1\x60\xf4\xac\x4f\xfd\x7b\x86\x00\xfe\xa1\x61\xb6\xb0\xf2\xa2\xb0\xeb\x3b\xbf\xf1\x78\xea\x54\xf5\x9a\xbf\xc1\x81\x06\xc4\x50\xd5\xf8\xe4\x3b\xcf\xe0\xab\xef\x0c\x37\xe0\xbb\x82\xe1\x43\xa9\x39\xcc\x1b\x09\x53\x89\x9e\xe5\xf0\xea\x1e\x12\xfc\xb0\x90\xe0\x3f\x36\x9d\x04\x1d\x90\xc9\x98\x34\x7f\x50\x35\x82\x04\xac\x6b\x7e\xbb\x95\xf8\xdc\x02\x0c\xca\x8e\xa7\xd3\x78\xf4\xa5\x85\xfb\x9d\x1f\x54\x5d\xf0\x58\x50\x3a\xc9\xed\xfb\x85\x93\x1c\x13\x3a\xe7\xf2\x1b\xa3\x8a\x19\xb3\x48\x1d\xca\x2f\x7a\x9e\xfa\xe3\x14\xe2\x69\x6b\x60\x2e\xef\x8b\x16\x60\xaf\x74\x08\x9a\x10\xd3\x8a\xb2\x7d\xf0\x9c\x7d\x25\xf4\x42\xe2\x9c\x31\x7d\x10\xa4\x09\x3a\x1c\xfa\x18\x7f\xca\x53\x68\x3b\x74\xbe\xfb\x84\x53\x08\x9c\x0b\xe2\xe1\x92\x99\xec\x20\xfb\x0c\x50\x24\x30\xd9\x58\x2c\xcb\xf6\xfa\xdd\x91\x75\xc9\xd2\x9e\xcf\xe2\x0b\x2e\x2c\x1b\x7f\xa4\x99\xe7\xf1\xb4\xaa\xca\x28\x31\xad\x48\x66\x9d\x38\xe9\x5d\x64\x52\x72\x91\xb6\xb0\xcd\x47\x26\x8b\x89\xa7\xbe\x57\xa3\x36\x5e\x12\x75\xac\x35\x51\x28\x39\xc2\xb8\x37\x0b\x7d\x79\x69\x81\x1c\x59\x1b\x83\xc1\xe4\xc6\xb2\xad\x75\xfc\xdb\xc0\xb8\x37\x7e\x5e\x9d\x59\x1e\xcb\x5e\x2a\x05\x97\xde\x65\x53\x3b\xf5\x55\x44\x22\x3d\x73\xab\xb6\x88\x81\x0e\xfc\xe6\x6c\x9c\x78\x1c\x82\x44\x14\x78\x01\xb7\x11\x65\x74\x8f\xd4\x7c\x65\xd5\x99\xfe\xea\x57\xbc\x8a\x1b\xb7\x27\x4f\x3b\xf6\xc9\xa7\x26\x3b\xd9\x03\xc7\xc9\xf2\x46\x1f\x99\xaa\xf7\x22\x76\x0e\x09\x83\xb0\xe2\xa5\x67\x8c\x38\xba\x23\xe9\xda\x33\x72\xea\xc3\x03\x64\xad\xfb\xac\xa0\x34\xd2\x9d\x53\x90\x1e\x69\x49\x04\xfc\xc9\xa7\x23\xd5\xfb\x90\xda\x58\x33\xf6\xc8\x07\xd6\x80\x06\x2b\x8b\xd4\x73\x65\x5c\x2e\xd4\x32\xaf\x36\x11\xe1\x98\x89\x5b\x4b\x9d\x74\x12\x50\x48\x1a\x98\x30\xc5\xe7\xc9\x51\x7d\x27\xbc\x24\xea\xb1\x4c\x26\x9d\xda\xd7\x14\xf1\x58\x6f\xda\xbe\xc6\xad\x1b\xdf\xc5\x3d\x6e\x33\xf2\x86\x19\xec\xd5\x28\x3a\xb8\x3c\x8a\xb4\x40\xd4\x4b\x96\x44\x08\xe3\xe9\x52\x99\x49\xc9\xdb\xc0\x8c\xbc\x29\xfb\x52\xf0\xa1\x18\xef\x05\xee\xbb\x5c\x8c\x2d\xa4\x2c\x1d\x03\x54\xd0\xe1\xfd\x8b\xbe\xd5\xcc\xfe\x22\x01\x40\x06\x75\x6c\x60\xdb\x65\x29\x47\x0c\x8d\xb8\xf8\x03\x23\xbb\x3e\x2d\xfb\xde\xf5\xcb\xd1\x99\x4c\x14\x62\x7a\x4f\xd5\xa8\x9c\x36\x98\x77\xe6\x4a\x1f\x4a\x47\x45\x28\x39\x6b\x49\xa7\xa9\x86\xec\x8b\x64\xe2\x27\x33\x7d\xf8\xb5\x06\x12\x91\x92\x98\x62\x5c\x80\xca\x20\x85\x21\x1c\x7c\xba\x8a\xc5\x28\xd9\x88\xe0\x49\xc7\x0f\xdd\xce\xd8\x5d\xef\x8c\x45\xa3\x80\xee\x71\x4d\xc4\x56\xb2\x11\x47\xea\xcb\x8a\x45\x90\x0d\x60\x3e\xa9\x00\x9a\xda\x23\x9c\x40\x41\xd8\xb3\xe9\x3d\x49\x1c\x9b\x3a\xcf\xc0\x9b\x3a\x9b\x03\x48\xa6\x8a\x68\x85\x53\x67\xe3\x19\x04\xd3\x7b\xe6\xff\xce\x8d\x83\x8a\x04\xe0\x17\x3a\x45\x67\x81\x45\x76\x32\x22\x69\x35\x0d\xb8\x3b\x5d\x4a\x03\x9e\x53\x0d\xf6\x15\xef\xde\xdd\x6b\x54\x94\xba\x11\x32\xaf\x9e\xd4\xf6\xa7\xfb\xe8\x9d\x30\x0e\x40\xd3\x06\xc4\x4a\xad\x64\x34\x98\xe6\x6a\x9b\xf7\x12\x63\xcb\x66\x68\xdf\x2b\x46\x4c\x61\x1f\xbd\xc3\x52\xdf\x50\x8f\x03\xd8\xc9\xfe\x4a\x8f\x1c\x7b\x6b\x22\x2d\x77\xe0\xe7\x7a\xfe\xc2\x16\xdc\x2c\xdb\x70\xb3\x37\x2d\x56\x35\xf5\x48\x58\x60\xe2\x70\xe1\x7d\x52\xbc\x4f\xca\xf7\x4a\x10\x1a\x07\xc0\x0c\x36\x17\x53\xc4\xe6\x19\x6a\x29\xf3\xd5\xc8\x9b\xb1\x69\x05\xb1\xcb\xbc\x07\xd7\x2b\x10\xfb\x77\x9f\x30\x74\xea\x28\x91\xbb\xf1\xd8\xd5\xcb\x9b\x4d\x35\x72\x67\x15\xe4\xce\x56\x23\x77\x36\xa5\x23\xf5\x85\x21\xb5\x99\x41\xee\xa1\x87\x0a\xa7\x00\xbf\xab\xb8\x15\x64\x0e\xbd\x2c\xb5\xc0\xe5\x6a\x4f\x29\x44\x1e\x09\xb0\xd1\xbf\x90\x0c\x08\xc4\x02\x31\x6d\x26\x05\x73\x0a\x69\x3b\xd7\x63\xe0\xd7\x93\x90\x39\x0f\x50\xd4\x96\x10\xf7\xbd\x5d\xaa\x04\xef\x57\x41\x6e\xfa\xc9\x94\x94\xac\x98\x97\x6c\x05\xeb\x82\xe1\xaa\x1a\x44\x5c\x74\x08\xe2\x53\xb0\x5e\xc7\xa8\xf6\xd1\x5c\xbf\x96\xcc\xd0\xaa\x46\x40\xe6\x55\x3d\xcb\xc4\xa8\x14\x9a\x25\xfc\x5d\x4e\xc8\x6f\xe7\x84\x58\x03\x27\x54\xe7\x7f\xdc\x29\x8a\xc2\x80\xcb\x50\x51\xc2\x02\x57\x32\xc4\xfd\x29\xd1\x3b\x25\x1e\x76\xae\xf9\x6d\x27\x48\x44\x31\xe9\x5c\xdf\x60\xd4\xee\xff\xa2\xee\xfe\x29\x3a\x17\x7a\x8b\xba\xe6\xf2\x9b\x95\xb7\x86\xbd\x20\xdc\x21\x99\x13\x53\x85\xf1\x62\x27\x53\xf8\xaf\xba\xca\x8a\xcf\xf0\x92\xf1\x24\xe2\x92\xf7\xc6\x3c\xce\x3a\xd6\x1a\x21\x59\x9f\xad\xff\xfa\x95\xf5\xdd\x2d\xfa\xf0\xa1\x3a\x7a\x56\x7a\x99\xcc\x14\xad\xa3\x14\x3c\x8f\x30\x3c\x37\x14\x12\xfd\x38\xa4\x0d\x4c\x49\x41\x01\x55\xaf\xa5\x3a\x44\x4e\x15\x8e\xd3\x7a\xc1\x9c\x2e\x23\xf3\x30\xa7\x10\x4d\x9d\x9f\x5d\xfb\xc9\x1c\xfc\x3b\x88\x6f\x23\x91\x2d\x25\x43\xbb\xa9\x7c\x95\xb4\x77\xc6\xc8\xd4\x23\xa2\xef\x1d\x2f\xe9\x83\xe2\x9c\x4c\x57\x2e\x0b\xa7\x26\x31\x55\xb7\x81\x10\x6a\xa4\x1f\x2b\xb4\x5f\x26\x3a\x6e\x4f\x4a\x29\xef\x48\x6d\x1a\xf7\xbd\xe3\x79\x21\xe4\x7d\x94\x3a\xae\x80\x58\x54\x15\x2f\x4b\xd2\x8a\x55\x54\x5b\xab\x01\x4c\x80\x25\x99\xbb\x17\xfb\xfc\x06\x93\xf6\x0c\x69\xbe\x16\x95\x8b\x04\xc1\x23\xa6\x17\xb0\x45\xf8\x5d\xdc\x62\xb5\x64\x53\x73\xad\x8a\x8a\x0d\xd1\xdb\x5c\xb1\xc4\x25\x88\x1e\x19\x1b\x3c\xbb\x63\xad\x69\x53\x2f\xb4\xc2\x11\x15\x2c\x38\x9e\xb6\x1b\x0d\xb9\x6f\x7f\xfd\x12\x7d\x77\x6b\xa4\x00\x5b\x3a\x82\x2a\xc4\x88\x79\xb7\x76\x51\xed\xa8\x69\x38\x1a\x3b\x5e\xff\x25\x42\x2e\x0d\x21\xf7\x0a\xb2\x2d\xef\xdf\x5e\xea\xb6\xab\xb6\x02\x57\xa8\xb3\x7c\xde\xf0\x64\xe1\xa9\xea\x96\xa7\xaa\xab\x4f\xd5\x3f\xb1\x4b\x25\x67\x50\x4f\xcc\xaa\xd8\x00\x8e\xf4\x2d\x9a\x22\x31\xbc\xf4\x96\x98\x82\xaa\x99\xe8\x94\x8e\xc6\x1e\xf1\xa8\x6d\xd8\x01\xf5\x8b\xe9\x5f\xc9\x94\x8e\xa6\x9e\x96\xb5\x39\xb4\xc5\x54\xd4\x30\x9c\x51\x4a\xed\x63\x77\x5e\x5c\xc5\x97\xe0\x62\xdc\xe9\x2d\xb0\xdc\x28\xf1\xae\x4b\xf5\xad\xc1\xf7\xc3\xc1\xe0\xff\x29\x95\x52\x2d\x28\xa6\xb3\xf0\xab\x27\xc2\x8b\x4b\x59\xa2\x1d\x7f\x8a\x9a\x65\x8d\x6f\xec\x19\xe9\x4e\xd1\x52\xdf\xfd\x41\xcb\xfb\x73\x60\xe0\xcd\x29\x5c\xae\xa0\xc1\xbb\x3a\x24\xf8\x73\x7d\xd9\x7e\xae\x6f\xdb\x6f\xb5\xb7\xed\x09\xa0\x0f\xd1\x6b\x1d\x8e\x6e\x1b\x2f\x2c\xbc\x64\xb5\x72\xd9\x63\xc2\xef\x54\xc9\x6f\xbd\xb0\x77\xc9\x99\x5f\x65\xe6\xbb\x55\x00\xeb\x28\x20\x93\xcc\x4d\x3b\x95\xba\xf8\x22\x6f\x70\x43\xde\xfa\x30\x00\x4f\xa3\x90\x13\x86\xe9\x17\xcd\x49\xdc\x31\xf6\x8a\x8a\x4c\x60\xbd\x61\x63\x3d\xd1\x67\x17\x23\x0b\x2d\x6b\x3b\x44\x73\x04\xd4\xb2\xf5\x8b\x5c\xdc\xcb\xfc\x85\xfc\x8e\x33\x32\x9e\x6a\x3d\x32\xa3\x68\x85\xe9\x43\x2e\x30\xfa\x16\x88\xfe\x09\x1c\xb1\xfc\x7d\x79\xb7\x06\xa2\x7f\x0c\x9f\x59\x4e\xb9\x96\x96\xc2\x00\x87\x9e\x98\x37\x2a\xbf\x2a\xf3\xaf\xa6\x53\xd4\x5f\xb3\x0b\xdc\xe3\xbc\x58\xe4\xc5\xf1\x14\x7e\x7a\x5f\xec\x07\x03\x84\xc6\xea\x67\x76\xda\xd8\xb7\x5c\x66\x2e\xaf\x44\x96\x4c\x52\x36\x34\x01\xd1\x37\x1b\x43\x25\x00\x3d\x88\xe7\x86\x22\xc6\xa3\x55\x77\xfb\x1b\x55\x81\x7a\x2b\x89\x22\x36\x49\x79\x87\x45\x91\xd1\x85\x5b\xf4\x9b\xbd\xe2\xae\x7e\xa1\xb9\x36\x68\x5c\x6c\x9c\x4f\xd0\xf3\xe1\x96\x7c\xf6\x21\x81\x50\xf1\x49\xd2\xa0\xd9\x8b\x29\x2a\xe0\x2e\x87\x16\x85\xc9\x74\xc1\x06\xea\x62\x5a\xda\x40\xc5\x89\xcc\x95\xf2\x79\x8f\xba\x21\xe2\x93\x54\xdb\x52\x4c\x51\x79\x77\x29\xc7\xd1\x4e\xa2\x28\xea\xcd\x3d\xa5\xd9\xbb\x94\xe2\x82\xf9\x61\xf2\x4f\x69\xc4\x05\x22\xbc\x18\x31\x9d\xfc\x4f\x6a\xc1\xe7\x14\xae\xa7\xc6\x48\xf6\xe5\xd4\x04\x73\x3c\xc6\x87\xe1\x60\x0e\xaf\xf1\xe9\xd9\x1c\xae\xf0\x61\x73\x0e\x5b\xd3\x56\x17\xdb\x8a\x7c\x3c\xf8\xc3\xc1\xd8\xde\x3a\xae\xb7\x12\x55\xf2\xcb\x14\x49\x32\x8d\xcd\x3c\x27\x86\xc4\x11\x10\x3a\x12\x02\x87\x83\xeb\x98\xbb\xab\x8c\x42\xea\x98\xf0\xbd\x02\xd3\xfb\xbe\x88\x1d\x0f\x84\x93\x80\x74\x42\xe0\x4e\x00\x99\xe3\x02\x73\x52\x25\x4c\xef\x4d\x57\x7a\x5d\x75\x02\xb2\x35\x45\x77\x8c\x37\x12\xb3\xf5\xa0\xf3\xf8\x0e\xdc\x92\x0f\x29\x06\x03\xd4\xa1\x0b\x0f\xa7\x6d\x26\x70\x26\x53\x59\x2d\xe5\xe4\x70\x73\x73\x40\xe9\x52\x52\xaa\x9a\xef\xfd\x66\x3d\xdd\x57\x2d\xe0\xc2\x93\x9a\x27\xe1\xa0\x9e\xe4\xab\x6e\x4e\xb7\x8e\x79\x36\xda\x50\xff\x25\x46\x22\xe7\xce\x94\xec\x4d\x61\x38\x04\x8c\x64\x2b\x81\x64\xce\x8d\x22\x7f\x87\x53\x13\x87\xb7\x88\xd1\xfb\xb4\xa7\xbd\xc3\x46\x99\x3d\x44\x75\xf2\x53\x3b\xeb\x0d\x69\x1e\xae\x17\x32\xa7\x8c\xdf\xab\x60\xd3\x21\x71\xb5\x2b\xad\x51\x28\x02\xfe\x9a\xbe\x9e\x3a\x8e\x13\x8f\x06\x76\x8c\x7d\xe2\x93\xe9\x33\xa3\xb0\x30\xb4\xb8\xaa\x45\x90\x8b\xc3\xce\x40\x52\xb4\xb6\x3c\x22\xf9\x1e\x9d\xb4\x32\x5c\x18\xcb\xe3\x8f\xa7\x23\x61\xcf\xc8\xc9\x54\x31\x93\x2e\x26\x97\x83\x19\x79\x9f\x80\xd0\xa7\x32\xd5\xef\x8c\x8a\xed\x33\xc2\xf3\xc6\x1c\x8e\xa6\x77\xda\x7e\xfe\xfa\x65\x3c\xd1\x8c\x9b\xc8\x42\x4c\xd4\x39\x85\x9d\x85\x2e\x24\xa6\xde\x31\x36\x01\x17\xe4\xda\x53\x9b\x72\xed\xa9\x43\x30\xb2\xf0\xc2\x7b\x9c\xc4\xf2\x52\xc9\x29\x90\xb5\x5f\x83\x14\xb1\x76\xe3\x22\xfc\xab\xa0\x2b\x43\x10\x3f\x88\xfb\xdd\x87\x0f\x87\x68\x17\x9e\xa1\xe9\x06\xa7\x23\x61\x5b\xd6\x5c\xf3\x8b\x38\xe0\x10\xac\x0e\x57\x58\x12\x75\x49\xf8\xea\x3b\x58\x9d\x71\x92\xa5\x3c\x41\xa7\x13\x54\x09\x61\xc1\x2d\x58\x1d\x23\x06\x43\xdc\x1c\xba\x8c\xdd\x62\xa7\x6d\x61\xcb\x2c\xab\xb8\x8a\x75\xd1\xbe\xea\x90\x98\xb1\xe1\xe8\x14\x4d\xc6\x48\x77\xd8\x5e\xf4\xd9\xaf\x5f\xeb\x18\x82\xd6\x2d\xdb\x9b\x87\x8e\xcb\xe5\x8c\xf3\xd8\x9a\x13\x9a\xb3\xd0\x87\x04\x33\x1a\x28\x1a\x7d\x34\x85\xd6\xac\x66\x21\xa0\x8b\x77\x39\xd8\x46\xcd\x95\xcf\x24\xef\xb8\xcc\xbb\xb6\xd6\x08\xeb\x33\xf5\x8f\xbb\x16\xd3\x46\xa1\x51\x55\x0d\x44\x12\x4b\x6b\x2d\x59\x23\xe1\x1a\xf1\xd6\x4c\x8c\xb5\xd4\x07\xab\x5c\x4b\x70\xd1\x97\x3b\x4f\x37\xac\x08\xf0\xe7\x69\x89\x83\xaf\x14\x28\x1d\x60\x14\x71\x4e\x2b\x92\xc5\xfb\x95\x64\x69\x81\x43\xdb\xc9\xaf\xef\x77\xa6\x79\x06\x2e\xd3\xc9\xbb\x69\xab\x3f\xe6\x39\x4a\x04\x18\x8a\x4a\xfd\x3c\x99\x82\x84\x1d\xda\x76\x53\x74\x95\xa5\x32\x0c\x6e\x8b\x1b\x9a\xba\xe2\xb6\x62\x91\xc5\xf9\x75\x85\x13\xc4\x91\xbd\xf4\xe0\xcc\xc4\xa9\xf6\xa6\xa3\x3f\xad\xe3\x4c\x09\x08\x07\x28\x26\x9c\x64\x4a\x22\x38\x53\x4c\x95\x75\x72\x99\x59\x60\xed\x88\xd0\x02\xeb\x98\x49\xeb\x9b\xfd\xe7\x3d\x6a\xa1\xf3\x83\x36\x46\xad\x6f\x10\x8e\x97\x85\xb1\xb9\x28\x2d\x37\x26\x93\xb8\x2f\x57\x53\x5a\xb1\x7e\x78\x5f\x5f\xbe\x33\xc4\x0f\x83\x39\x6c\x4f\x4d\x68\xcb\xb7\x9a\x9f\x70\x55\x7f\xa7\xfa\x79\x62\x51\xf8\xd1\xbe\x59\x3f\xbb\xf6\x93\x6a\xce\x68\x8c\xd8\xb1\xb2\xfa\xe3\x7a\xf5\x03\xfd\xa1\x54\x8a\x24\xbe\xb0\xe8\x8a\x2c\xd3\x4b\xd1\x3d\x4c\xa2\xb2\xb8\xdf\x1d\x9d\x26\xb6\xcb\xa9\x76\x6f\x83\x19\x09\x79\x91\xda\xaf\xdd\x7a\x21\x4f\x46\x54\xc9\x43\x44\x2c\xaf\x88\x70\xe7\x25\xf1\x94\x0b\xd9\x49\xa5\x08\x71\x64\x9f\xa6\xfa\x92\xf6\xa3\xe1\x67\x67\x1c\x98\xa0\x14\xbe\x4e\xdb\x1c\x43\xeb\x80\x26\xc3\x31\x9f\x84\xde\x75\x15\xb8\x0e\xa6\x79\x8d\x34\x73\xaf\xb8\x27\xab\x86\x31\x23\xeb\x75\xec\x5b\xb6\x75\x9c\xab\xea\x16\xa1\xe0\x32\xc9\x44\x93\xd2\x33\x9b\xf4\xb4\x11\xba\xb9\x96\x28\x20\x5a\x9d\xcf\x5b\xf2\x43\x1d\x87\x01\x0c\xdb\xaf\x9c\x58\x7c\x11\xf1\x9e\xd1\x70\xef\x98\xef\xee\x98\x2b\x7e\x05\x68\x6e\x94\x09\x4b\x2f\xf4\x8c\xec\x62\x7f\x6a\x81\x28\x32\x79\x15\x3c\x1a\x06\x44\xe6\xd8\x54\xf4\xd9\x55\x35\xa2\xa7\x35\x40\x63\xa2\x3c\x41\x81\x76\x55\xc0\x6a\x3f\xee\xac\x46\x28\x85\xd7\x1e\x59\xd7\x5c\xe7\x34\xe4\xb3\xfa\x54\x73\xae\xb1\x72\x17\xa4\x84\xd2\x7b\xac\x4b\xef\xce\x85\x51\x3d\x95\x4b\xb3\x24\x24\x25\xd1\xf2\x17\x4a\xc5\x8b\xbd\xbc\x8f\x4a\x12\x91\xfc\xef\xee\xe4\xf0\x5f\xbc\x93\xc3\xbf\xbf\x93\xdf\xef\xb7\x93\xdf\x57\xee\xe4\xdf\xdf\xbb\xe1\x3f\xbd\x77\xea\x84\xb6\x40\x93\xde\x3e\x12\xb7\xa3\x27\xed\x2f\xd9\xa6\x7e\x51\xe8\xc6\xb2\xec\x19\x99\x24\xf0\x0c\x04\xb2\x3d\x73\x83\xa4\xf0\xf2\x59\x71\x5c\x57\xea\x9f\x50\x49\xcd\xf8\xf3\x87\xfa\xe7\xb6\xa0\x9c\xc8\x6a\xc4\x73\x0a\xdf\xa7\x4d\x81\xe2\x8d\x03\x50\x79\xb7\xdf\x60\x52\x38\xee\xb8\x17\xbd\x80\xf9\xdc\x5f\x36\x31\x5c\x47\x5b\xe3\x16\x93\x4c\xc9\x6f\xe4\x2a\x7b\xcc\xc5\xf2\x46\x63\x4c\x5d\x65\xf1\xc6\xf5\x45\xbc\x18\x61\x3a\xf9\x9b\x43\x3e\xab\x1a\xdf\x96\xf3\x6e\xbc\x12\x10\x25\x23\xf2\x6a\xda\x9a\xa6\xb5\xcd\x6c\x58\xc7\x16\x72\x74\x6c\x21\x8c\xd1\xe9\xa2\x1d\xe7\x11\x21\xf2\x0e\x5b\xf4\xdc\x9a\x0f\xcd\xca\x65\xdf\x5d\xe6\xd3\x56\xdb\x95\xff\x3d\xab\xf1\x8a\x8d\xb8\x30\x36\xe2\xc2\xd8\x88\xbf\xf4\x91\x29\x28\x6c\xbf\xd1\x49\xa0\x66\xfb\xad\x64\x00\x6d\x9b\xbc\xe8\x69\x71\x7f\xdb\x64\xd1\x6e\x9b\xfc\x7d\x8a\x8e\x1a\x3c\xdf\x8f\x8f\xd3\xf6\xe3\x55\x31\xa4\x64\x03\xfd\xb9\xf2\x4b\xaf\xa6\x4a\x08\xd1\x66\x88\x1f\xa6\xf7\xf1\xf6\x5a\x4a\x9d\xb2\xb4\x88\x66\xe1\x64\xdf\x1d\xfd\x39\x23\xa7\xd3\x9c\x2b\x3d\xa8\x68\x65\x5e\xe6\xa1\x47\xf5\x7d\x92\xdd\x41\xb7\xbb\x7d\x81\x69\x43\x0a\x04\xf3\x71\x8a\x0e\x16\xa8\x46\x6a\xee\xe7\x7d\x52\x46\x31\xad\xdc\x74\x99\x73\x5f\x4b\x13\x8c\xeb\x58\x93\xc4\x2b\x9e\x69\xad\xb3\xd2\x06\x9e\xf8\x6f\x6f\xc6\x44\x8c\x7c\xcd\xa2\x19\xae\x62\xc9\xf6\xa7\xed\x11\xfa\x1a\xfc\xe5\xaa\xc3\x19\x94\x21\x83\x32\x1f\xb6\x3c\x98\x91\x0f\x7a\xee\xf5\xbc\x38\xc6\x6f\x6e\x29\x51\x96\xaf\x46\xf0\x66\xea\x58\x63\xd9\x7b\x6c\xc1\x97\x65\x4b\x82\xe5\x89\x1d\x91\x37\x53\x30\xfe\x26\x23\xab\xb3\x70\xdf\x6a\xd1\x9a\x0d\xe8\xed\xb4\xd9\x8a\xaa\xc2\x90\x15\xbb\x72\x60\x52\xf6\x75\x34\xcd\xc6\x0d\xc7\xcd\x29\x37\x2b\x37\xf1\x4e\x2b\x86\x38\x5a\x13\xfb\x3d\xab\xe8\x3a\x87\x88\xea\x61\x91\xa0\x6a\x51\xb0\x0c\x7f\xae\x17\xb2\xa0\xb1\x4d\x5b\x58\x35\x25\xe8\x05\x9c\xfb\x28\xd0\xd5\xb4\x5e\x73\x62\x8e\x53\xdc\x35\xac\xbd\xe8\x3a\x2b\xd5\xaa\xe6\x42\x1a\xf1\xe8\x99\x5b\xe1\x56\x3e\x08\x4d\x0f\x5f\x96\x30\x09\xb2\x7b\x9f\x43\xaa\xf0\x53\xc7\x5a\x7b\x93\x8b\x83\xa2\x5b\x73\xd8\x40\x96\x00\xfd\xa8\xb9\x65\x5b\xa7\x98\x8f\xb1\xd1\x88\x67\x1c\xf5\xd6\x3b\xcb\x17\xf9\x0a\xe5\x77\x97\x66\x1e\x2f\x32\x0e\x0b\x6d\x0d\x50\x60\x53\xb1\x70\x17\x86\xea\xd3\x82\x2a\x70\x5c\xb6\xe7\x73\xc8\xba\x46\x87\xc2\xba\x77\x24\xfe\x60\x7d\xe6\xe7\xf1\xdb\xbc\x4a\xec\xed\x26\x39\x5c\x9b\x0a\x69\xfe\x22\x89\xb2\xb1\x4e\x4b\x60\xac\xd6\x2b\xf6\x01\x1d\x6b\x4d\x56\x81\x77\x19\x6d\x88\x0a\x0f\xa3\x5e\x7d\xf1\x95\x50\xff\x19\x95\xac\xfa\x6e\x29\x5b\xb6\x40\xe9\x2c\xc0\x50\xb1\xf5\x39\x4b\xb4\x74\x90\xef\x3b\xf2\x7f\xcf\x68\x2b\x03\xcb\x51\x85\xf6\x53\xf1\x9a\x0f\xc8\xdd\xab\xab\xa1\xe0\x3f\xb2\xb8\x39\xc0\x2d\xdf\x99\xdd\x75\x8e\x93\x1c\x1a\xe7\x14\xbc\xee\x1d\xda\x92\x8a\x88\x59\x39\x70\x01\x61\x5d\xd8\xf1\xc1\x48\x8f\xa0\x04\x93\x5e\x74\xd1\xdb\x44\xcb\xc6\x27\x26\x36\xcb\xb9\x07\x33\x8f\xc2\xcc\x23\x59\x97\x42\x4c\xa1\x68\xb6\x6d\x92\x66\x94\x2d\x37\x16\x5a\xee\x2d\xb4\x94\x95\x96\xaf\xd1\xf2\xbb\xf1\x73\x87\x0b\x8d\x44\xc3\xa2\xe8\x8d\x32\x8d\x87\xa6\xf1\xd2\x4e\xa9\x63\xfb\xff\xfd\xbf\xd6\x92\x8b\xce\xc2\x5e\xd4\xd1\x9c\xc1\x1d\x1d\x7d\x2f\xde\x5b\x30\x3c\x98\x79\x84\x77\xdb\x85\x07\x8f\x45\x5c\x71\x9d\x0b\xa2\xc3\x37\x7a\x97\x7f\x44\x22\xe0\xd1\xff\x90\x24\xfe\xa5\xc6\xf5\x12\x6b\x74\xe9\xa3\xb0\x2f\x79\x2a\x49\xec\xc4\x74\x64\xf9\x4c\xb2\x9e\xb5\x16\xdb\x31\x3c\xfa\x9f\x7f\xa4\xbf\x93\x2b\x36\x65\xfa\x42\xc3\xfe\xa5\x0a\x6d\xc5\xc4\xfe\xe3\xd1\xa5\x1c\x47\x45\x53\xe1\x08\xb4\x2e\xd2\x19\xbf\x14\x72\x67\x02\x74\x57\x17\x82\x8d\xc7\xe7\xdc\x0f\x31\x3c\x45\x1e\x8e\x09\xc2\x6e\x9b\x9f\x6e\xee\xa0\x2b\xfe\x11\xff\xfa\x87\xf8\xf5\x8f\x58\xfb\xe9\x06\x5d\xed\x8d\xc9\x6f\x24\x13\x9c\x59\x14\xdc\xee\xca\x6c\x4e\x98\x2b\x7f\x03\x86\x8f\x61\x5f\x90\xb0\xab\x73\x83\xe2\x4d\x7e\xf6\xef\x41\x8e\x62\xf5\xf9\x8d\xcb\xf3\x1b\x74\xcd\x01\xce\xf2\x03\x2c\xf1\x00\xf3\x7b\x63\x47\x38\xf1\x08\x53\x4b\xfd\x2f\x40\x93\xff\x96\x71\x37\x0d\xf1\x5f\x8d\x30\xff\x03\x0b\x5e\x90\xe8\x85\xc9\xfc\x6d\xd4\x99\x76\x1b\x9d\x5f\x74\x54\x55\xde\x67\x9b\x80\x59\xc8\xce\xc1\x53\x7f\xba\x80\xee\x8f\xef\x20\x54\xbf\xf6\x30\x37\x19\xdb\xc2\x88\x51\xdd\xd1\x21\xb1\xde\xf3\x59\x9e\x5a\x40\xa1\xb3\xe3\x04\x53\xa9\x51\xfb\x90\x58\xaf\xfd\x50\x96\x65\x3f\x38\x31\x61\xe8\xaa\xd1\xbe\x5a\x6e\x76\x73\x01\xeb\x96\x78\x5d\xc8\xfa\xee\x21\x64\x7d\x76\x06\x59\x3f\x55\xf3\xfe\x32\x05\x0f\x58\x05\xb9\x16\x91\x67\xde\x4c\x35\x4a\x9d\x14\x28\xf5\xe5\x14\x93\x5e\x9e\x52\x98\x12\xb7\x0b\xd6\x56\x9e\xb5\x3a\xaf\x7a\x51\x54\xbd\xd6\x55\x8f\x17\x96\xb6\x20\x1d\xb7\x64\x7f\x0a\x1c\x12\x1d\xe6\x48\x76\x51\x4a\x54\x63\xbc\x52\x44\x4f\xa3\xbd\xa8\xeb\x48\x01\x7e\xd7\xe1\x02\xa6\x8d\xc8\xe5\x4b\x4c\x42\x81\x6a\x0e\x13\xc3\xa1\xdb\xbd\xa7\xc5\xf2\xb8\xb5\xa2\xfb\x0c\xc5\xc8\x97\x82\x77\x6e\x93\xac\x93\x66\xe6\x61\xc6\x62\xd9\x91\x49\x47\x27\x1d\x5f\x60\xc9\x47\x16\x05\xb6\x61\xaf\xb6\x0a\x3d\x63\xe4\x80\x93\x19\xe9\xea\xb9\x56\x8d\xf9\xb6\x92\x38\x08\xc5\x58\xd3\x1a\x37\xb1\x0f\x38\x79\x99\x50\xf0\x36\x6d\xeb\xb5\xfe\x5a\xbe\xef\x98\x96\x65\x05\x73\xdc\xa4\x97\x30\x96\x85\x29\x8f\x02\xa3\x6d\x2a\x6d\xfc\xcd\xb1\x5a\x43\xcb\xef\x3d\xdf\x04\xa8\x43\x9b\x81\xd6\xaf\xa0\x2f\xc1\x1a\xb1\x1e\x9a\xf0\x5a\xda\x0d\x5a\x0b\xc6\x13\x8d\xd2\x2f\x1f\x2b\x29\x7c\xc5\x38\xff\x77\xfa\xb8\x2f\x07\x75\x32\x79\x11\x96\xe9\xc9\x65\x17\x99\x90\xd4\x02\xd1\x67\x67\x75\xd1\x53\x97\x23\x57\x84\x35\xdc\xc3\x25\x31\xd4\x74\x61\x32\xd8\xeb\x5e\x30\x26\x14\xaa\x48\x5c\x25\xd7\xfe\x25\xc7\xf0\xc5\xc0\xec\xb2\x02\x5d\x9f\x8c\xc6\x93\x52\x38\xf2\x88\xa0\x8d\xf1\xcb\x17\xe6\xb9\x5a\x0f\xb0\x4a\xcc\x33\x88\x7c\x31\x26\xc5\x01\x27\x9f\x3d\x12\x2f\x80\xbc\x9e\xb8\xb5\x40\x44\xfe\x95\xfd\x6f\xf3\x88\x6b\x85\xb7\x5a\x55\xb1\xa4\x86\x2d\xf5\x3a\xef\x3d\xb5\x41\x57\x94\xc2\x3b\x8f\xc4\x23\xbc\x9b\x1e\x77\x41\xc0\x83\x21\xa5\xf6\xb5\x2c\xd4\xa2\x72\x4e\xe1\xb6\xdb\x10\x1b\xcf\xac\xa1\xac\xa9\x7e\x78\x25\x7c\x1f\x06\x91\x30\xe1\x3a\xdb\x6c\xe7\xd8\xcd\x5c\x1b\x93\xae\xb4\x12\x75\x4f\xe6\xc6\x3a\x14\x2f\x3c\x29\x45\x67\xfa\x69\x77\xd9\x3d\xb9\x9c\x62\x43\x47\x87\x6a\xcb\x15\x90\xcd\xba\x79\x4c\x00\xf5\x82\xa2\xc5\xee\x1c\xc3\xb4\x18\x9f\xdd\x1c\xaa\x72\xd3\xa0\x65\x2d\x8b\x6c\xd5\xb2\xdc\x74\xab\x99\x39\xae\x57\x48\x19\x68\x96\x26\xf3\x98\x9f\x67\x1e\x1c\xf0\xba\x2e\x43\x14\x97\x03\x95\x98\x88\xd2\x27\x3b\x8c\x6c\x7b\xa8\x0d\x5b\x30\xe1\x71\x99\x7f\xc1\x3b\xf8\x6f\x6f\x12\x46\x51\x32\x33\x3f\xcc\x48\x0d\x1a\x40\x3c\x29\x93\xc9\x82\xcf\x95\xbe\x58\x8e\x0d\x0b\x3e\x6f\xf9\xdc\x37\xbc\x5b\x98\x53\x78\xd9\x5d\x52\x21\x85\x01\xd1\x29\x1f\x2a\xca\xf2\x16\x8b\xbe\x02\x71\x99\x7b\xd4\x6b\x05\x7c\x03\xb5\x78\xcb\xca\xeb\x7b\x75\xd1\x18\x89\x02\x7b\x8d\xfb\xde\x25\x28\x18\x2a\xee\x05\x8e\xff\x37\xd3\xc1\x4f\x78\xf5\x8f\x17\xba\x7d\xd7\x9b\xb7\x52\xc4\xd7\x5d\x63\x58\x75\xa5\xc9\x8f\x6b\x51\xd8\xba\x97\x0a\xa9\xc2\x87\x8a\x64\xd6\xe4\xcc\xad\xa4\xc3\x75\x94\x11\xb1\xee\x42\x9c\x85\x19\xb9\xea\x36\xf1\xa6\x0b\x37\x75\xbd\xe1\xe0\x6e\xdf\x2d\x51\xc8\x7a\x7b\xcd\xfc\xa4\x68\x0d\xb4\xf0\xa9\x88\x75\x6f\xe2\xad\x7c\xd0\xf7\xf0\xf4\x0e\xa6\xb0\x74\xec\x83\x5a\x6c\x85\xc2\x46\xf5\x08\x99\xea\xff\x34\xb9\xf8\xc4\xc9\xeb\xee\xbf\x8b\x56\x2c\x75\xbe\x48\x28\x64\xb9\x89\x5b\x5d\xb0\xf6\xb6\x2d\x13\x3f\xcc\x2b\xde\x69\xaa\xde\x61\x12\x8b\xf6\xb0\xf4\x90\x16\xc5\x8a\x29\x58\x28\x64\x67\x65\xa1\x56\x41\xfa\x8b\x8d\x3f\x95\xe5\x5a\x57\xe9\x77\xdc\x5b\xf3\x5d\x76\x5a\x96\xe5\xac\xb6\x2e\x38\xae\x0e\x48\x72\x7c\xad\xf0\x92\xd9\x99\xa2\x34\x57\x30\x5b\xb0\x68\xec\xb2\x97\xc0\x8e\x67\x6e\x47\x28\x48\x9d\x48\x06\x84\x22\x82\x5c\x13\xc1\xe3\x2e\x48\x78\x30\x28\x88\xe0\x9c\xc2\xa1\x61\xef\xd6\x2d\x0a\xe7\xfa\xd9\x4b\x7c\x05\x42\x27\xfa\xd7\x44\xed\x13\x7c\x36\xd5\x8c\x1d\x0b\x1c\xe9\xdf\x31\x9b\x5a\x14\x76\xba\xce\xcf\x1b\xbb\xcc\x16\x00\xee\xd8\x36\x3a\x6f\x6b\x0e\xef\x75\xa9\x7e\x6b\xcd\xe1\x5d\x5e\xbb\x08\xc7\x8b\x25\xc7\xf9\xaf\x39\x9c\x15\x35\x8a\x54\x43\x58\x23\xff\x35\x87\xed\xa2\x86\x49\x82\x82\xe5\xfa\x79\x0e\x6f\xbb\xed\x91\xbf\x1a\x41\x7f\x29\xcb\xff\x4e\xb7\x06\xf2\xef\xbb\x39\x91\x34\xec\x80\x31\x51\x5c\xb4\x4c\x3c\xeb\xd6\x92\x64\x6e\x77\x6b\x39\x1c\xde\x77\xeb\xf0\xfe\xae\x3b\x9f\xeb\x6b\xa8\x91\xd5\x29\x1d\x53\xe7\x14\x4e\xef\xe4\xb6\x17\x02\x30\xac\xcd\xc8\x5b\x83\x80\x97\xfd\x88\x2b\x81\x0f\xb6\x19\x11\xfd\x1b\x0c\x39\x63\x1c\x63\x6a\x6e\x32\x1a\x30\x7e\x20\xa1\xdf\xe9\xc2\x59\x17\xb6\xbb\xf0\xae\x0b\x6a\xc5\xf3\x50\x77\x13\x91\x8c\xb9\xbc\xe4\x59\xda\x0f\x93\x47\x7e\xe2\xa5\x7a\xf3\xc3\xf8\x42\x3f\x8c\x59\xcc\x2e\xb8\x78\xa4\xb7\x66\x97\x47\x13\x6b\xfe\x8d\xc2\xee\x6a\x1c\xbe\x60\x64\x6f\x64\x09\xe6\xfb\xe8\xbf\x66\x6d\xa2\xe7\x58\xee\xa6\x62\xa2\xf1\x88\xe5\x5b\x5a\x38\xf0\x48\xdc\xf7\x3e\xd5\xf1\xb6\x31\x7c\xea\x4c\x2a\x62\xc5\xae\x57\xb5\xf9\x3d\xe8\x3a\x89\x20\x96\xc0\x48\x23\x9f\xba\xed\xe9\xcc\x5f\x79\x60\xe1\x72\xaa\x3e\x30\x86\x13\x1c\x74\x89\x95\xca\xdb\x88\xa7\x97\x9c\xcb\xc2\xba\x2a\x4a\x98\x8f\x96\x55\x82\x30\x0c\xa1\x5c\x18\xc4\x71\x21\x12\x61\x8a\xbe\x70\x62\xed\xb0\x30\xe2\xbe\xa2\xc3\xaa\x4d\x67\xeb\xf8\xb8\x13\x88\x64\xac\x93\x02\x51\xe3\xca\xa8\xa3\x7a\x9e\xc5\xe4\xa7\xf7\xca\xde\x02\xef\xd0\x3e\x64\xe0\x7d\xb0\x1b\x19\xcb\xee\x48\x91\x11\x4d\x6c\xd9\xdc\x56\x3f\x36\x81\xd9\xb7\xe4\x43\x04\xd6\x7f\x59\x40\x62\x9d\x8b\x8c\x3d\x06\xfd\x6e\x64\x29\x7e\xe2\x08\x7d\xfb\x3e\x46\x8a\xad\x48\xe1\x48\x91\x86\xd9\xa8\x0c\x72\x68\x97\x91\x0f\xe3\xbe\xeb\x2a\xfe\xb2\xcf\x30\x68\xfe\x1c\xbc\x75\x7b\x2a\xc9\x2b\x41\xc1\x7b\x6a\x9f\x47\xe0\x3d\xb7\x9b\x42\xad\x3e\xb3\xcf\xc8\x9f\x1f\xf5\xd2\x23\x07\xf0\xb2\x02\x32\xd6\x7c\x3e\xa7\x2f\x78\xe2\xfc\x3c\x60\x61\x6c\xff\x0c\xe3\x50\xda\xaf\x04\x79\x17\x52\x32\x50\x1f\x89\xfb\xaf\xa3\xf1\x28\xef\xb7\x63\xcc\xa7\x82\x44\x10\xe4\xd1\x3b\x61\xdc\x91\x14\xff\x88\x11\x06\x73\xb2\x1c\x87\x8f\x2e\xc9\x13\x6a\xc7\x44\xfc\xc9\xbf\x81\xfc\x93\x7f\xa3\xb6\x7a\x74\xd4\xe3\x9c\x60\x97\xc0\x13\x6a\xe3\x93\xc3\x93\x39\x51\x6c\x10\x7d\xf1\xff\x07\x00\x00\xff\xff\xa8\x51\x24\xdf\x82\xae\x01\x00"), + compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\xdc\xbd\x0b\x7b\xdb\xb8\xae\x28\xfa\x57\x1c\xed\x6c\x2f\x72\x0c\xab\x76\x5e\x6d\xe5\x70\xf9\xa6\xef\xf7\x23\x49\x3b\x99\x66\x72\x72\x28\x99\x76\xd4\xd8\x94\x4b\x51\x71\xd2\xd8\xfb\x6f\xdc\x1f\x74\xff\xd8\xfd\x08\xea\xed\xc7\x74\xf6\x5e\xfb\x9c\x73\xef\x5a\xf3\xa5\xb2\x04\x82\x20\x09\x82\x00\x08\x82\x5b\xc3\x44\x06\x3a\x8c\x24\x91\xf4\xde\x49\x62\xd1\x88\xb5\x0a\x03\xed\xf4\xb2\x0f\x0d\x45\x24\x28\xd0\xf4\x5e\x09\x9d\x28\xd9\xd0\x2e\x67\x12\xb4\x3b\x64\x0a\xf4\x22\x07\x1b\x93\x02\x44\x91\x1d\xd0\x90\xa3\x56\xf9\x87\x72\x6d\x19\x3a\xa2\x40\xd2\xc5\x82\x16\xa8\x06\x44\x94\x50\xed\x82\x28\x50\xe9\x65\x54\x1b\xb1\x0b\xa2\xc1\xe2\x2f\x57\xa0\x49\x52\xaa\x60\x0f\x92\xa2\x02\xb1\x8c\xed\xef\xd6\x99\x10\x01\x79\xad\xe5\x6a\x03\xc2\x4b\xd5\xee\x03\x2f\xaa\x4d\x96\x11\xfe\x0b\x28\xe1\x24\x81\x32\x2d\x65\x62\x04\x09\x4a\xc4\x1c\x40\x50\x10\xc3\x97\x71\xfe\xf7\xd0\x17\x10\x0e\x35\x0a\xcb\x24\x26\x24\x2a\x91\xf8\x10\xa2\x82\xc4\x60\x19\xed\xff\x32\xaa\x23\x12\xc0\x32\xdd\x65\xc2\x39\x09\x4b\x84\x3f\x82\xb0\x20\x3c\x5a\xc6\xfc\xbf\xb3\x2d\x21\x89\x60\x65\x6b\xca\xcd\x89\xc8\xb0\xd4\x9c\xc7\x30\x2c\x9a\x13\x2e\x23\xff\x3f\xac\x85\x43\x12\xc2\xba\x36\x96\x1b\x39\xab\x89\xb9\x1d\xc6\x98\x74\x79\x5f\xba\x43\x62\xde\x7b\xa6\x1e\xa2\x4b\x25\xee\x6c\x09\x28\x88\xdc\xad\x96\x01\x91\x97\x22\xa2\x54\xf0\x36\x2b\x08\x45\x93\xf7\xea\x45\x21\x29\x17\x26\x49\xa9\xfc\x4d\x51\x1e\x8a\x7e\xdc\x5f\xc6\x00\xbc\x86\x83\xf0\x12\x9a\x61\x19\x0d\x14\x63\x74\xb0\x0a\x11\x04\xcb\xa8\x48\x50\xc2\xe6\x57\xb1\x41\xc1\x06\x0f\x57\xe3\x83\x68\x25\x46\x12\x95\x90\xc6\x75\xa4\x50\x30\xdc\xa3\x75\x68\x21\x5c\x87\x98\x84\x74\x71\xc3\x55\x23\x64\x03\x52\xb0\x8a\x1d\xf6\x61\xa4\x88\xf9\x26\xd8\x91\x52\xfc\x8e\x48\x0a\x09\xeb\xf4\x92\x43\xd9\x4b\x5a\x2d\x2a\xce\x93\x0b\xa6\x89\x6a\x25\xb4\x97\xad\x2d\x0b\x0a\xdb\x6c\x5c\xc1\x54\xe0\xd1\x05\x1e\xc1\x3a\x3d\x71\x28\x9b\x4d\xe5\xfa\x3d\xd1\x6a\x51\x7d\x2e\x2e\x98\x72\x39\x28\x66\x5e\xe5\x0b\xeb\x58\xc8\x91\xbe\x62\x02\x2e\xcd\xb2\x45\x17\x14\x26\x8c\xd4\x2b\xc8\x26\xe2\xb9\xbc\x58\x50\xd8\xd4\x90\x0c\x21\x24\x29\x2d\x82\x02\x67\x9d\x1e\x3f\x14\x3d\xde\x6a\xd1\xe4\x9c\x5f\x30\x7d\xce\x2f\x32\x0a\x92\x73\x79\xc1\x14\x24\x0b\x0a\xeb\x9b\xa5\x32\xac\x59\x4f\xe9\x56\x37\xeb\x2b\x5d\xf4\x95\x3a\x4f\x72\xbc\xe2\x5c\x5f\x30\x09\xe2\xd7\xe9\x35\xc8\x04\x22\x53\xcc\xcc\x4c\x7d\x9e\x5c\x80\xca\xbb\x5e\xfd\x22\xa6\x76\xb7\xd7\x39\x64\xa2\x27\xda\xed\x1c\x91\xa8\x21\xa2\xbd\xbf\xd3\xd6\x15\x2d\x95\xc4\xb4\xb5\xc2\x16\xff\xe5\x51\x41\x69\xd4\xe2\x60\x46\x27\xc7\x9c\xac\xc4\x9c\x73\x4f\x3c\x0e\x03\x81\x2d\xf8\x0b\x0a\x54\x41\x81\x6c\x0b\xe0\x19\x1d\x2d\x92\xe4\xd4\x1d\x26\xfd\xec\xd1\x4b\x28\x85\x80\x75\x7a\xc1\xa1\xe8\x05\xad\x16\xe5\xe7\x81\x19\xdf\xe0\xa2\x67\x70\xda\x2f\x49\xf6\xa5\x25\x0c\x4f\x05\xf9\xd8\xf3\x15\xcc\x54\x1a\xc5\x35\x9f\x82\x48\xc6\xd1\x58\xb8\xe3\x68\x44\x64\xcb\xf1\x1a\x4e\xeb\x8a\x50\x0a\x6a\x41\x0b\xcd\xf4\x8a\x64\xf0\xce\x61\x28\xb5\x50\x92\x8f\xe3\x7f\x3a\x85\xf8\x98\x9a\x75\x40\x5f\xa9\x68\xd6\x78\xae\x54\xa4\x88\x73\xa5\xf5\x34\xf6\x1e\x3c\x18\x85\xfa\x2a\xf1\xdd\x20\x9a\x3c\x10\xe3\xc9\x83\x20\x52\xe2\x81\x3f\x8e\xfc\x07\x5d\xb7\xe3\x76\x1e\x5c\x85\x52\xc7\x0f\x9c\x96\x6c\x39\xee\x64\xe0\x94\x24\xd2\xa8\xc6\x22\x20\xd8\xf9\x05\x24\xec\x1a\xbb\xb9\x03\x82\xf6\x92\x66\x93\x68\x26\xdc\x69\x34\x25\x94\xf6\xcc\x37\xed\x72\xd0\xae\x8f\xdf\x4b\xc3\x99\x63\xbd\x2e\x96\x92\x70\x48\xba\x9d\xce\xa1\xa6\x19\x3b\xb9\xd3\x24\xbe\x22\x97\x58\x2f\x85\xad\x4e\x2f\x1c\x12\xc9\x18\x53\x29\x84\x7d\xe3\x44\xfe\x77\x11\x68\x67\x8b\xe9\xbb\xa9\x88\x86\x0d\x39\x9f\xcb\x64\x3c\x36\x42\x32\x7f\xca\x8a\x38\x59\xc5\x0e\xcb\xc1\x9b\xcd\x29\xd9\xa7\xb0\xd5\xed\x65\x6d\x4b\x1a\xa1\x6c\x48\x77\xfb\xb0\xd3\x6c\x12\xc9\x7c\x6d\x64\x99\x32\xff\x1a\x42\x24\x0d\x87\x64\xeb\x9a\x48\x9c\x99\xe6\x8f\x6e\x75\x4d\xf3\x52\xaa\xba\xbd\x8c\x3c\x14\xb9\x47\x6c\x4c\x46\x14\x4e\xd8\xea\x01\xdf\x1a\xa5\x8c\x5b\x8c\xee\x69\xc6\xb8\x2b\x5b\x97\x75\x0f\xf6\x44\xbf\xe3\xc9\x43\xd5\x6f\x77\xbd\xae\xe9\x8b\x2d\xe9\x6e\xa7\xdf\x89\x66\xa7\x44\x1a\x49\xeb\x72\x4a\xe7\xf3\xf4\xb7\x0f\xca\xf5\x29\xed\x6b\xcf\xfc\x0a\x40\xb9\x01\xc5\x76\xf7\xa4\xeb\xa3\x9c\x6e\x36\xb7\xaa\x65\x7b\x92\x61\x39\x23\xb1\xf3\x21\xd4\xf3\xb9\xc1\xd6\xef\x7a\xca\xf5\x4d\xfd\x1d\xbb\xc0\x3c\x5f\xd3\x4c\xdb\x28\x7a\xd8\x59\x50\xf8\xb6\x56\xb4\xa7\x40\xdd\xf5\xd3\xa3\x73\x78\x9a\x4d\xf4\xb5\x10\x2c\x03\xa1\xf0\x7d\x99\x9c\xf4\x6b\x2e\x07\x0f\x3b\xfd\x1b\xed\xa9\xfe\x40\x7b\x63\xbd\xa0\xf0\x94\x75\x8a\xa1\xb8\x2c\xa3\xbe\xe7\x9e\x04\xdf\x53\x8b\x82\x7d\xdf\x54\x65\x51\x06\x01\x81\xa7\x4b\x50\xef\x2c\x96\x6c\xda\xdc\x2f\xb0\xc7\x35\x72\x19\xb5\x4b\xc4\xb9\xbe\x28\x5e\x2a\xfb\x52\x99\x97\xb9\x6c\x35\xa5\x3f\xb0\x31\x79\x55\xe2\x94\x57\x16\xb1\xe1\x13\x63\xbd\xca\x51\x89\xad\x73\x3e\x69\xa9\x94\x35\xfc\xec\x95\xea\x59\x21\xff\xda\x0e\x72\x3a\xc2\xbd\x92\xa4\x36\xdc\x60\xdf\x52\xc1\x84\xeb\x17\xa0\xd9\xf8\x23\x3d\x67\xec\x7e\xdb\xeb\x2c\x0a\x82\x5e\x57\xfa\x6b\xdb\xeb\x42\xde\x67\x06\xfe\x23\x1b\x93\xd7\x25\xfa\x8f\x8d\x98\xca\xaa\x55\xec\x0c\x34\x93\xa9\xec\xed\xe9\x76\xbb\x47\x95\xa9\xf8\x5c\x57\x17\xae\xbc\xf8\xd7\x6a\xf1\xf3\x8b\x12\xd9\xca\x4a\x0f\xe9\xf2\x52\x49\x03\xf7\x73\xa3\x0a\x74\x7e\xd1\xc3\x39\xa0\x8d\x76\x62\x38\x1e\x34\xd3\xa6\x17\x2c\x3a\x5c\x9d\x50\xa4\xf1\x42\x9c\x1d\x1b\x1d\x97\xc2\x33\x46\x74\x0d\xb3\x11\x6b\xb9\x50\x29\xe3\x6e\x36\x45\xb5\x02\xc0\x6e\xa6\x89\xad\xe6\xae\xa8\x06\x44\xb5\xaa\xc4\x54\x15\x2c\xd5\x63\x14\xea\xac\x26\xbe\x54\x53\xb3\x99\xac\xaa\x0e\x12\x96\xb8\x3e\xe5\xb6\xd2\xdb\x6a\xa5\x90\x54\x2b\xe6\xa6\x62\xb1\xa2\x62\xa3\x89\x67\x55\x07\xab\xab\x6e\x36\xf9\xfa\xfa\x81\x33\xee\xfa\x34\xb0\x54\xdc\x2c\x53\x01\xbc\x4a\x49\x50\x9b\xfd\x1a\x0a\xa3\xe7\x98\x18\xa6\x70\xe3\x48\xe9\x75\x22\x06\xa5\x39\x8a\xf2\x05\xfe\x07\x2f\x2b\xf2\xe8\x17\xb1\xb1\x99\x81\x2c\xf3\x25\x63\x6c\xac\xfb\x1d\xcf\x3c\xdc\x68\x94\xc9\x58\xc1\x3a\x41\x25\x5b\x46\x07\x83\x17\x6b\xa5\xa1\x6c\x6f\xd0\x13\xe4\x6f\x58\xfa\xf3\xfa\xd2\x0f\x36\x95\x7e\xa0\xe6\x1d\xfc\xfc\x9e\xeb\x2b\x77\x1a\xcd\xd6\xeb\x2a\xff\x2e\x57\xe1\xf9\x77\x26\xb3\x96\x77\xcc\x52\xdb\x9f\x92\x6e\x97\x7a\x9d\x43\xd5\x6c\xca\xc3\xce\x7c\xae\xcc\xea\xd9\x39\x94\x7d\xd5\x92\x5e\xaa\x6d\x62\x65\x5c\x73\xb9\x43\x51\x04\xbd\x67\xf8\x26\x10\xe1\x18\xbe\xd8\xe7\xe1\x38\x8a\x14\x3c\xb1\x3f\x54\x94\xc8\x01\xfc\xb0\x3f\xc6\xd1\xa8\xb7\xae\x39\xcd\xe6\xa6\xc6\xce\xe7\x9b\xbe\x6e\x31\x66\x94\x2b\x43\xcf\x27\xb6\x69\xac\x7a\x7f\xeb\xe3\xaf\xe9\xd2\x76\x55\xe0\x4c\xb9\xc1\x15\x57\x4f\xa3\x81\x38\xd2\x24\xa1\x3d\x7e\xb8\xbf\xbf\xf3\xf8\x60\x3e\xdf\x3f\xd8\xed\x3e\x3e\xe4\x7d\x52\xd6\xb8\xc1\xa8\xe0\x5e\xf9\x55\x4b\x9d\x27\xad\x2e\x7e\x61\x3b\x74\x91\xab\x50\xdf\xa3\x50\x12\xc7\xa1\x1b\x0d\x9b\xf3\x0b\xa8\xe8\xc6\xd6\x06\xc9\x49\x33\x2a\x4e\xb0\x44\x61\xd2\x6a\x41\x50\xa5\x32\x98\xcf\x09\x6f\xd9\x02\x86\x42\x90\x84\x53\x23\x0a\x70\x66\xf3\x9c\x2c\x5d\x22\xab\xf7\xb7\xec\xa2\x8c\x26\x6d\x69\xd2\xbf\x4c\x93\xce\x69\xb2\xd6\x90\x59\xcb\x16\x85\x36\x8e\xa3\xff\x96\xfd\x0a\x2d\xc6\xa4\x4a\xe9\x48\x18\x1a\x55\xbc\x4a\x87\xa0\x3d\x5b\x47\xc2\xf8\x3f\xd9\xfe\xc1\xee\x4e\xa7\xd9\xdc\x7f\xb8\xbb\xb7\xfb\x4f\xc6\xfb\xfa\xbc\xdd\x16\x17\xad\xc4\x4b\xaa\x14\xc0\xef\xeb\x78\x4f\xb9\xf1\x74\x1c\x1a\xa9\xb5\xa0\xf0\xc7\x7a\x28\xec\x53\x04\x92\x92\xfd\xa2\xb5\xf4\x0b\x5c\x6b\x17\xe2\xfb\xd4\x80\x32\x6b\x71\x52\xe5\x05\x4d\x51\x31\x27\x82\x25\xf5\xe6\x26\x7d\x75\xde\x6e\xeb\x8b\x96\xf0\x0a\xfd\xb8\xb3\xc8\x14\xe5\xb4\xdf\x95\x64\xff\x32\x32\xb6\x7e\x9d\x8e\xee\x22\x27\x88\x82\x96\xeb\xf4\xf3\xad\xff\x50\x6e\x28\x07\xe2\xf6\xe3\xd0\xf6\xae\x58\x07\x6a\x04\x61\x0d\x36\x59\x0b\x9b\xe9\x39\x87\x79\x0b\x8d\xf6\x3d\xe6\xb1\x7e\x9d\x23\x60\xf9\xb7\x76\x06\xbe\xa0\xc0\x97\x71\xda\xae\xca\x55\xa7\x70\x48\xf4\x61\x37\xd3\xf6\xce\x4a\x8a\x5d\x07\x50\x0d\x69\x77\x0f\x89\x28\xd3\x8a\xd6\x59\xa6\x7d\x08\x0a\xa2\xc5\x74\x55\xe7\xc0\x91\x0a\xd6\x35\xc7\xa8\x7a\x1d\x18\x64\xba\xde\x3a\x51\x69\xc0\xba\x20\x0a\xb0\xc2\xd3\xba\x04\xb7\x0b\x43\x4f\xc2\x28\x53\x1d\x87\x9b\xaa\xde\x43\xa5\xfb\xca\x93\x8b\x94\x50\x7f\x6d\xbf\xe3\xcf\x73\x75\x61\x46\x27\xde\x30\x4f\x52\x38\xd0\x08\x39\x96\xab\x95\xbb\x3a\x30\x88\x8b\xf5\xfa\xd9\x32\x30\x24\x17\x9b\xb4\xaa\x55\x05\x80\x63\x91\x64\x65\x91\x92\x2f\xb3\x56\x08\x02\x2c\xc6\xd7\x14\x2b\x39\x2d\x97\x0a\x42\x84\x45\xa3\xb5\x45\x4b\xae\xc9\x15\x85\x21\xbc\x58\xb5\xfa\x68\x75\x97\x15\xba\x31\xaf\xde\x9c\x7c\xfc\xe0\x4e\xb9\x8a\x05\x6a\x66\x01\xd7\xc1\x55\xc9\x99\x3d\xd1\x64\x46\xae\x34\x38\xa7\x57\x61\xdc\x08\xe3\x86\x8c\x74\xe3\x86\x8f\xc3\x41\xc3\x94\xdc\x6a\x38\x2d\xe9\x4e\x44\x1c\xf3\x91\x00\x83\xc0\x28\x47\x03\xc3\x05\x37\xb2\xc4\x66\x37\x69\xed\xf1\x2c\x44\xfc\xee\x36\xbd\x0f\x78\x2c\x1a\xbb\x5e\xea\x20\xf0\xa3\x68\x2c\x78\xc9\x3f\xa0\xfa\x33\xa3\x2c\x7a\x57\x92\x38\xbc\xf1\xe4\xe3\xc7\x77\x8e\xd1\xfa\xb0\xd4\x4e\x56\x4a\x26\x13\x5f\xa8\xc2\x4a\x57\x7d\x04\x97\x8d\xd7\x1f\x4e\x0d\xb8\x47\xd4\x21\x6b\xef\x74\xf7\x1e\xee\x3d\xda\x3d\xd8\x7b\x38\x9f\x17\xcf\x87\x4c\xcd\xe7\xa4\x33\x57\xd4\x68\x22\xb4\xd9\x24\x5b\x61\xfc\x22\x94\xa1\x36\x5d\x31\x9f\xab\x7f\xef\xd2\x3a\x3a\x24\xc9\xd2\xb0\x57\xa3\x61\x0d\xe1\x2f\xde\x7d\x3c\x3a\x2d\x28\x3f\xc8\x4a\xd5\xed\xc6\xac\x94\x6a\x84\x32\xd6\x5c\x06\xe6\xe5\x09\x02\xe1\x97\x96\xe3\x64\x28\x4f\x4e\x8f\x5f\x7f\x78\x59\xe0\x7c\xec\x65\xb2\x2d\x75\xba\x98\x02\xd2\x0d\x2c\xbc\x79\x59\xc0\xee\x67\xb0\xa5\x96\x3c\xcc\xde\xa1\x96\xe4\x86\xb1\xd5\x96\x14\xed\x6f\x4b\xeb\xba\x80\xe3\xac\xee\x77\xaf\x4f\x4a\xad\x79\xf4\xd7\x25\x27\x32\x2d\x2a\x1b\x47\xc7\xc7\x47\x7f\x14\x85\xbb\x1d\x2f\x93\x9f\x83\x95\x0e\x25\x55\xb8\x91\xe6\xf3\xad\xcc\x44\xcf\xc4\x6b\x8a\xf4\xe3\x93\x37\xcf\x9f\x9e\x36\x66\xa1\xbe\x6a\xf0\xc6\x30\x14\xe3\x41\x43\xf2\x89\x18\x34\xfe\xa7\xd3\xd2\x2d\xe7\x7f\x62\x85\x56\x0a\xdf\xa4\x44\x9d\xeb\xc2\xc5\x19\x0a\x22\x68\x5f\x78\xc8\xe8\x53\x6d\x66\x10\x5a\x3d\x96\xc4\xae\x67\xc8\x13\xb8\xc2\xd5\xdb\x58\x23\xa4\x68\x5d\x38\x24\x2a\x5f\x65\x74\x05\xac\xf1\xee\xe3\x87\x97\xcf\x8f\x1b\x1c\x71\x35\x3e\x08\x31\x68\xe0\x62\xd0\x40\x62\x1b\x7e\xa2\x1b\x91\x1c\xdf\x35\x62\x21\x1a\x4e\x2b\x43\xd3\x72\x1a\x42\x6a\x15\x8a\x18\x2b\xf8\x85\x96\x8c\xea\x2d\xd9\xf1\xfe\xb2\x8b\xff\xa2\x81\xb6\xa7\xf3\xee\x4c\x80\x33\xbb\xc4\x25\x76\x60\xb0\xd9\x57\x3c\xfe\x38\x93\x9f\x54\x34\x15\x4a\xdf\x91\x84\xd2\xfb\x12\xb5\xc9\x85\x55\x16\x90\x54\x5a\x16\x31\x53\x0d\x49\x4a\x2f\x67\xaf\xc9\x25\xb1\xbf\xa0\x50\x5e\x67\x9a\xbc\xd6\xa4\x68\xd0\xae\x57\x18\xbf\xd2\x1d\x42\xc4\xa4\x3b\x82\x90\x75\x7a\xe1\x61\x94\xaf\xc8\xad\x56\x4a\x40\x74\x1e\x5e\xa4\x83\x53\xad\x5e\xf4\x02\x16\x10\x53\x59\xa9\xa6\x20\xab\x65\xcf\x2b\xc8\xaf\xf5\x34\xbe\xbe\xc2\x92\x46\x34\x88\xb4\xc4\x7e\x4e\xd7\x90\x9d\x81\x6f\xa8\xea\xf9\xae\xdf\xf3\x99\xef\xfa\x29\x31\xbe\xf5\xe9\x84\x43\x52\x23\x65\xc8\x5e\x1b\x84\x30\xcc\x89\x99\x68\x72\x87\x2d\x1f\xd2\xbc\xe9\x5e\x4d\x38\x5b\x17\x51\xfa\xb5\x53\x9a\xe1\xd2\xb4\xaa\xf0\xdb\x6c\x6f\x76\xcd\xaf\xdc\x1c\xb8\xb7\x3d\x6c\x05\x38\xee\x0d\xa4\x3d\x18\x54\x07\x70\xa4\xcd\xb2\x83\x03\x88\xfb\x09\x81\xcb\x4b\xfd\x69\xac\x84\x92\x4f\x7b\x52\xde\x42\x9d\x91\x48\x40\x4e\xc6\x8a\x0d\x55\xbb\xf1\x54\x94\xbe\xaa\x6a\x16\xf9\x12\xf5\xfc\x76\x2a\x02\x1d\xca\x91\x59\x94\x70\x31\x2a\xfc\xf2\x32\xf7\xd8\x2d\x7b\xb2\xa5\xbb\x6d\x56\x80\xdc\x85\xbb\xd5\xed\x2d\xad\x53\x1d\xaf\xda\xf5\xd2\xe5\x06\x8f\xcb\x7b\xe9\x32\x96\xae\x4b\xe9\xd2\x90\xca\xfa\x8a\xc8\xdd\xea\xd4\xc5\xb5\x1b\x20\x8e\x20\x13\xc3\xa9\x4c\xcd\x26\x6c\x0a\x36\x95\xb9\xff\x38\x17\x9c\x39\x86\x01\x62\x18\x34\x9b\xcb\x50\x25\x5a\x05\x42\x89\x55\x50\xbb\x05\xd4\x10\xa1\x86\xcd\xe6\xc8\x40\x8d\x40\xb9\xa3\x62\x1a\xe4\x50\x57\x08\x75\xb5\x0a\x57\xbe\xb8\x94\x10\x94\xd8\x6f\xb4\x5e\x6b\xde\x2a\x54\xee\x62\x10\x4a\xea\x73\x4f\x1c\x6a\xdc\xc8\x34\xcc\x67\x2a\xc6\x4d\xb5\x73\x71\xb1\xce\xfb\x3f\x5b\xab\x88\xa2\xc2\x63\x57\xdf\x70\x78\x47\x14\x18\x01\x08\x92\xb6\x1c\xa7\xac\x18\xdf\x95\x39\x50\x22\xce\xdb\x4d\xe6\x9d\xdd\xc8\xd4\xa9\x1e\x7c\x2d\x99\x41\x5b\xa0\x3b\x2a\xa1\xbb\xdf\xf6\x3a\xc0\x8d\xd2\x9c\x7f\x3e\xa9\x7e\xee\xd6\x3e\x9f\x56\x3f\xef\x80\xef\x49\x08\x3c\x53\x85\xd5\xd2\x9f\x6f\xd0\xd2\x77\x11\x7a\x80\x8a\x3f\x7c\xdb\x00\xb8\x57\x02\xc4\x56\x7c\x97\x65\x9f\xfc\x53\x24\x42\xa2\xeb\x19\x84\xf7\x5d\xb6\x5a\xa9\xa9\x80\x3d\x78\xe5\x9d\x5f\x2c\x32\x09\x79\x66\x60\x41\x16\x2d\xb8\x2c\xcf\xf8\x53\x49\xca\xd3\x5c\x92\x23\x49\x9e\x1a\x00\x4a\xcb\xf3\xfc\x4d\x4a\xa0\x74\xaf\xac\x81\xa4\x28\x20\x62\x6c\xf2\xbb\x4a\x4b\xca\x63\x51\x43\xff\xc6\x7e\x05\x5b\x0d\x56\x61\x9b\xf7\x41\xb2\xad\x2e\xbc\x92\xc6\x32\xcb\x2b\xc5\x0a\x8c\x9c\x78\x25\x53\x4f\x36\x85\xad\x0f\xa9\xbf\xdb\x94\xe8\xf4\x24\x7b\x25\xdd\xf8\x2a\x1c\x6a\x42\x7b\x74\xab\x1c\x15\x82\x3b\x3a\xca\x1d\xa6\x16\xb3\x34\xf3\xc9\xdd\x36\x2c\xde\xb1\xfb\x62\x5d\xf3\x4f\x0e\x37\x32\xb6\xe7\x08\xc5\x8f\xec\x51\xe5\x8e\x98\xf9\x19\xa2\x88\x35\x93\xc7\x36\xc8\x20\xc4\x0f\x3e\x31\xe8\xcc\x72\x93\x41\x2e\xc4\x38\x16\x86\x5a\x0c\x1e\xc9\xf7\x19\xdc\xa1\x1b\x60\xd5\x7e\xa5\x27\x0c\x1e\x69\xba\x10\x5d\x10\xe1\x90\xec\x5b\x6a\x52\xf2\x94\x7b\x55\x9d\x83\x69\xc5\x43\xac\xf8\x2a\x6b\x32\xc5\x4a\x1b\x86\x06\xc3\x5d\xe8\x84\xec\x78\x5d\x63\x0a\x1a\x50\x08\x3d\xe5\x8e\x16\x90\x95\x1d\x2c\x16\x0b\x22\x69\x0f\x7b\x7b\xb1\xd8\x60\xcd\xbd\x36\x03\x25\x40\xba\xc1\x33\xf3\xe7\xb1\xf9\xb3\x57\x2c\x08\xcb\x31\x37\xf4\x7e\xb1\xa8\xec\xe0\xbd\xae\x19\x72\x76\xed\x9a\x91\x81\x04\x09\xaa\xaf\xdc\xe1\x98\x8f\x62\xef\x26\x0a\x07\x8d\x0e\xed\xe1\x2a\x36\x9f\x4f\x49\xea\x16\x8d\xd8\xfd\x02\x42\x46\x02\xa6\x09\x2e\x65\x66\x25\x66\x9c\xf8\x10\x9a\x45\x71\x85\xed\x0f\x02\xa5\x94\x30\x1a\xd0\x47\x99\x79\xa7\x3e\x1a\xf1\xd4\x4b\x5c\xde\x6c\x12\xa2\x99\x9e\xcf\xef\x17\xf4\x5c\x5c\xb0\xc4\xe5\x04\xcd\x24\x30\x10\x2b\x10\x0a\x76\x3f\x42\x8b\xda\x92\xb8\x80\x84\x49\x37\x00\x6e\x74\x64\x30\x7a\x8e\x40\x3d\x67\x98\x6f\x4f\xb9\x57\xec\xa9\x24\x33\xf2\x5c\xe6\x1d\xd5\x28\x87\x2c\xe1\x17\x09\xf7\xdb\xde\x3e\xf8\x5e\x99\x19\xec\xde\x8d\x74\x79\xc5\x9d\xec\x6e\xf7\xef\x08\x07\x81\xc2\xcd\x0b\x9a\xcd\xa8\x7f\x8b\x31\x7d\xca\x0d\x41\xb9\xdf\xcd\xdb\x3b\x7c\x11\xf4\x95\x6b\x86\xda\xbc\x32\xc3\x00\xd2\xf5\x29\x5d\x90\xb2\x7f\x4d\x2f\x48\x04\x7e\x69\x80\x7c\xdb\x54\x33\x26\x02\xa4\xe9\xd6\x21\x09\x8d\xae\x00\x8a\xc2\x4b\x49\x22\x08\x5c\x1f\x12\x12\xd2\x1c\x47\xf5\x2d\xf0\xfe\xfd\x34\x52\x3a\xf6\xf8\xc2\xbb\x4f\x77\xb7\x24\xbb\x5f\xe0\x00\x1e\xff\xaa\x4c\x50\xee\x88\xd4\x45\xc2\x9a\xf5\x62\x46\xde\x49\x90\xee\x15\xa4\x62\x5b\x55\x59\xee\xeb\xe6\x68\x30\x14\xe6\xd7\x9e\x82\xb1\x11\xe8\x85\x6c\xfb\x59\x97\xe8\x13\xf3\xdd\xb4\xe1\xd9\x46\x51\x2e\x3d\x09\x51\xcd\x87\xf3\x32\x5f\x8c\x90\x85\x20\xc9\x76\x3a\x91\x2d\x5f\x48\xb2\xd5\x01\x05\x09\x2e\x74\x14\xcc\xef\x2e\xe8\xfc\xb7\xa4\x6f\xd2\xf5\xf4\x7e\xdb\x73\x86\xb7\x0e\x70\x2f\x39\x17\x17\xf3\xf9\x7d\xe8\x9d\xc1\x77\xef\xac\x12\xb5\xf6\xa2\x34\x6f\x53\x2d\x49\xe5\x5a\x52\xd7\xb3\x13\x40\xb9\xd7\xc0\x19\xe1\x2c\x81\x88\x09\x98\x11\xd9\xff\x28\xcf\xf9\x85\x2b\x3c\xfb\xef\xb0\xa2\xe7\x15\x5b\x89\x51\x4f\xe1\x76\xd5\x0f\x6a\x44\xe7\xd4\xc8\x8d\x7c\x09\x36\x3a\x62\xb1\x1d\x65\x26\x08\xd1\xe7\xc9\x85\xa9\x86\x43\xc2\x48\x82\xce\x66\x5a\xa2\x1b\x64\x3f\x71\x43\xf6\x9a\x70\x48\xdc\x90\x7a\x89\xfb\x3d\xfd\xf1\x9d\x42\x42\x73\x67\x42\x61\x48\x28\x77\xd2\x0b\x5c\xdf\x98\x04\xae\x4f\xb1\xad\x86\x39\x4d\x6b\xd3\x8a\x7b\x15\xb7\x05\x92\x91\xf6\x89\x1b\x81\x86\xfb\xa9\xa7\x5c\x09\x3f\x3c\xb1\xb0\xcb\x14\x87\xa8\xe8\xbc\xcf\xd8\xdc\x8f\xf2\x5c\x5e\x34\x9b\x53\xb2\x5b\xea\xd7\xf7\x55\xae\x43\x48\x40\x48\x76\x2f\xbc\x2f\x12\x94\xa7\x80\x7b\x4f\xe4\x02\xbe\xe6\x6b\xe0\x97\xb5\x5a\x4e\x25\x6c\xe5\x49\x3e\xe1\x35\x04\xec\xfc\x02\x22\x86\x98\x5d\x65\xa4\x9d\x66\x1d\xa8\x4d\x0f\x3b\x18\xb1\xd0\xa7\xe1\x44\x44\x49\x49\x66\x67\xab\x35\xa5\x0b\xd0\xf9\x60\x94\x3e\x07\x63\xc1\x55\x56\x4c\xa1\x3f\x28\x83\xb2\x75\xfa\x2c\xb4\xed\x72\x83\x35\x7e\xff\x9e\xca\x36\x2d\x69\xa1\x02\x06\x90\xb0\x88\x28\xb4\x09\xad\x79\x92\xe9\x8f\x1c\xc3\xa2\xf8\x05\x29\x02\xe5\xc2\x05\x85\xfb\x38\xf1\xe3\x40\x85\xbe\xa8\x88\xbd\x20\x5b\xd5\x17\x90\xc8\xd5\x20\x44\x9a\x25\x20\x48\x1d\xf6\x94\x96\x5c\xcb\xf4\xb0\x33\x9f\x07\xb8\x31\x80\xbe\xfc\x2e\x5d\xd8\x59\xfb\x43\xf6\xd6\x48\x9e\x55\x06\x0d\xee\x85\xd2\x4c\xcd\xfa\x24\x99\x93\xc8\x81\x18\x86\x52\x0c\x0a\xdb\x7c\x10\x05\xc9\x44\x48\xdd\xcf\x1e\xbc\xfb\xd2\x8e\xff\xdb\x5c\x39\xe2\xd3\xa9\x90\x83\xa7\x57\xe1\x78\x60\x3a\x7c\xd5\x02\x2b\x98\x70\x65\x34\x10\xc5\xb2\x31\xe5\x4a\x48\xfd\x21\x1a\x08\x57\x89\xe9\x98\x07\xc2\x22\xd8\x56\x44\x96\x97\xdc\x05\x05\x41\xe1\xbe\x22\x6f\x7e\x5f\xa9\xcb\x9a\x96\xfc\x51\xe1\xc7\xb2\x5b\xf4\x2f\x36\xc1\x3a\xa5\x31\xbf\xcf\x64\x08\xef\x89\x16\x4b\x5c\x7f\x3e\xef\x40\xba\x97\x95\x14\x5b\x6c\xad\x62\x97\x0a\x85\x6c\xe0\x05\x30\xf0\x06\x0a\xe3\x1f\x3d\x0d\x43\x8f\x83\xef\x09\xd4\x10\x48\xba\xe2\x83\x54\xff\x1d\x04\xfe\x1a\x89\x3b\xbf\x44\xa2\xdd\x99\x51\x9b\xb4\xf4\xef\x9e\xc4\x25\xc5\xf7\xba\x2d\xa2\xb0\x72\x5a\x19\x20\xad\x6a\x65\xf6\xcd\xf2\x03\x13\x4f\xc1\x75\xa6\x59\x2c\xd6\x08\x0e\xad\xc8\xb9\x04\x75\xb1\x42\xef\xb2\x7a\x63\xca\xb4\x42\x6d\x30\x86\x2c\x0e\xd0\xab\xb0\x64\x61\xcf\x0b\x74\xa7\x2b\xb6\x41\x21\xcc\xf1\x80\x58\x85\xa9\x08\x87\x46\x5c\x5c\x31\xb2\x71\x03\xa0\x84\x0e\x92\x55\x08\xcb\x61\xd2\x8b\x5f\xd8\x20\xa8\x20\x04\xbe\x0a\x65\x35\x72\x7a\xf1\x4b\x5b\x08\x35\xb4\x10\xac\x42\x5c\x8f\xa5\x5e\xfc\xe2\x36\xc3\x12\x72\x88\x56\xa1\x5f\x0e\xae\x5e\xfc\xf2\x66\xc4\x8a\x2a\x20\x5c\x55\xc9\xaa\x60\xeb\xc5\xfa\xc8\x0d\xa3\x9f\xf0\x8e\x53\xd2\x81\x28\x04\x1b\x66\x8a\xc3\xbb\x65\x60\x88\x36\xc2\xee\x54\x60\xc3\x8d\xb0\xbb\x65\xd8\xde\xba\x79\x80\xa0\x7b\x06\x54\x41\xe4\xdd\x0f\xb1\x84\x5e\x54\xa6\xea\x50\x15\xb2\xd4\x31\x6b\xd1\x54\x3b\xc6\xa0\x72\xa6\x8e\x27\xd7\xcc\x51\xd3\x09\x68\xae\x6d\xf7\x67\x84\x2b\x30\x0a\x07\xd1\x4c\x82\x64\x63\x81\xfb\x8b\x11\x35\x02\x47\xb8\xdb\x46\x34\xf7\xef\x88\x2f\x40\x1e\xee\xf6\x63\xe5\x8d\x15\xc4\xc2\xa8\xbe\xc2\xe5\xd4\x9b\x91\xa1\x48\xfd\xc4\x0b\x4a\xbd\x34\x3e\x0d\x44\xb6\x9b\xa7\x20\x5e\xd7\x0f\x8d\x4b\x22\xed\x72\x6c\x44\xa2\x19\xb3\xb5\x3d\x16\xbc\xf3\x0c\x68\xf0\x8e\x02\x7f\xe2\x29\x97\x3f\x01\x7e\x63\xfe\xbd\xa9\x74\x05\xca\xc5\x92\x2e\x78\xbf\x28\x85\x95\xe5\x4e\x23\x0e\x82\x69\x77\x1b\x12\xa6\x5d\x89\x21\x00\x51\xcf\x0c\xde\x16\x63\xa2\x4f\x34\x53\xa8\xba\x12\xf3\x0f\x33\xab\x97\x19\x2c\xc6\x98\x68\x36\x9d\x60\xcc\xe3\xd8\xfc\x48\xfa\x37\x8a\x68\x7b\x5a\x01\x55\x48\x4e\x3d\xfb\xf5\x03\x9f\x88\x1c\x42\x59\x08\x85\x10\x8b\xe5\x30\xb8\x1b\x55\xd1\xbb\x99\x3c\x57\x17\x3d\xf3\x87\x89\xbe\x68\x39\x0d\xa7\xa5\xbd\xd2\x79\xb5\x6d\x55\x75\x7f\x6d\x67\x16\x78\xbe\x45\x60\x20\xdc\x6b\x8c\xf0\xbc\x66\xd2\x9d\x60\xfc\x31\xcd\xbc\x07\x39\xd8\x27\xe9\x06\x4a\x70\x2d\x4e\xc5\x2d\x2e\xe1\x36\xda\x2e\x1c\x92\x3d\x04\x2b\x79\x77\xa5\x7b\x8d\x26\xe4\xf7\x9e\xf9\x24\xdc\xed\x1e\x5d\xda\x03\x48\xfa\x09\x3b\x4f\x40\xb8\xdf\x2f\xbc\x6c\x27\xda\x28\xc8\x46\x69\xb8\xee\xd9\xb8\x8f\xfb\xef\x5e\x02\x53\x4f\x65\x0e\x1e\x12\xb0\x6d\x45\x04\x18\x1b\x59\x8c\x27\x97\xe2\x46\x48\x7d\x69\x54\x8c\x4b\x25\x86\x8c\x43\xb0\x08\x87\x64\xb7\x4c\xf5\x44\x11\x63\xc0\x5e\x11\xe9\x8e\x28\x28\x90\xee\x80\x42\xd0\xcb\x1d\xf8\xfd\xbc\x59\xcf\xc7\xc2\xa8\x3b\x1f\x4e\x88\x74\x87\x80\x1b\x59\xf5\x6f\xb8\xbd\xd5\xfb\x21\x9b\x4d\x87\x9b\xf9\xe2\x06\xcd\x66\xe0\xf2\xc1\xe0\xb9\x21\xe4\x5d\x18\x6b\x21\x85\x22\x4e\x30\x0e\x83\x6b\x07\x7e\x48\x12\x50\x0a\x86\x84\xb4\xe6\xdc\xb9\x18\xa1\x51\xbd\x62\xdb\xe0\xad\x24\x01\x6c\x2b\xd2\x35\x8d\xe8\x47\xe7\xe1\x85\x67\xfe\xe0\x46\x40\xae\x68\x06\x25\x9f\xb6\x5a\x72\xae\x1b\xf3\x4b\x97\x43\x56\x7a\x46\x24\x99\x81\xe8\xaf\xf4\x25\x30\xe9\xc6\xfa\x6e\x2c\x56\x46\xa4\x2e\x88\x84\x84\x7a\xe9\xe4\xaf\x62\x28\xdb\x7e\xd2\x0c\xc8\x8b\x18\xb9\x08\x9f\xcc\x34\x28\xcc\x41\x5d\x84\xf2\x88\x0b\x08\x98\x31\xf5\x0c\xeb\x70\x74\x03\x05\xf6\xaf\xfb\xc3\xdd\x66\x8c\x71\x34\xec\xdc\x1f\x8c\xf7\x82\x48\xea\x50\x26\x62\x21\x5d\x25\x26\xd1\x8d\xa8\x76\xb4\x30\x2b\x50\x50\x38\x34\x42\x30\x53\xb9\x74\xee\x27\xb3\x29\x86\xee\x0f\xd0\xec\x06\x45\x07\xc8\x6c\x0b\x44\xd3\x52\xaf\x41\x62\xc4\x98\xa2\xa0\x98\x76\x39\x70\x96\xf4\x93\xc3\xdd\xbe\x72\xb9\x67\x84\x88\xa7\x40\xb3\xae\x99\xa2\xca\xf5\xbd\x5d\xc6\x92\x66\x13\x65\x4a\xc0\x88\x6e\x36\x4d\x17\x46\xd3\x4f\x2a\x9a\xf2\x11\xb7\xcb\x0d\x90\x9d\x25\xf0\x1b\x6a\x40\xa7\x0a\x19\xf7\x99\x18\xf2\x64\xac\x09\x85\x90\xf6\x04\x0b\xdc\xef\x3d\x1b\xdc\xbb\x1c\xb5\x2e\x28\x67\x82\x70\xda\x43\x1f\x58\xc1\x44\xb9\x35\x12\xb5\xdb\x3d\x03\x73\x1e\x5d\x18\x30\x63\x47\x4c\x17\x01\xe1\xe8\x25\xc9\xd6\x6e\xf7\x07\x93\x30\x5c\x10\x05\x9c\x82\x5c\xe6\x5b\x01\x01\xf8\xaa\xd9\xbc\x9f\xf2\x38\x0e\x6f\x84\x37\x36\x75\x1e\xee\x18\xed\xc1\x08\xb6\xc0\xba\xe0\xd6\x8f\x85\x05\xcb\x54\x3d\x64\x11\xe4\x9d\xdd\x55\xdc\x97\xab\xba\x96\xe3\x4a\xb1\x3e\x3d\xd1\x97\x6e\x2c\xf4\x91\xd6\x2a\xf4\x13\x2d\x88\x3d\x62\x96\xd6\x5b\x7a\x4d\x17\x39\x7f\xee\xfd\xbd\x3a\x20\x61\xc2\x1d\xa2\xb4\x89\x96\xea\xfb\x70\x42\x12\x58\x5d\xa7\xfd\x54\xd4\x7b\xc3\xc7\x89\xc8\x45\xfd\x95\x08\xae\xc5\x20\xfd\x89\xce\x36\xc6\x12\x33\x27\xd0\x0d\x47\x17\x0b\xad\xee\xee\x67\xa1\x1c\x44\xb3\x15\x62\x43\x3b\x76\x57\xe0\x23\x8a\x4a\xd7\x9a\x66\xf9\xa6\xe6\xfd\x02\x9c\x74\x60\x1c\xb8\x1f\x09\xed\x95\x54\x1b\x5f\xb1\xad\x8e\x51\x4d\x8a\x50\x8a\xd2\xce\x55\x65\x09\x38\xcf\xa3\xcc\x47\xa9\xec\x80\x0e\x2d\x9f\x70\x9e\xaa\xb2\x09\x77\xbf\xed\x29\x50\x9e\x86\xd8\x13\xa0\x53\x3d\x1e\x92\x4c\xa1\xcf\x1d\x25\x45\x30\x51\x69\xeb\x45\x55\xce\x7b\x60\xec\x65\x26\x98\xa4\x51\x13\x8c\x36\x61\x26\x63\xb2\xc5\x98\x15\x05\xdd\x2d\xec\xb1\x1d\x7c\x51\xf6\x76\x4c\xcd\xda\xd9\x01\x81\x1b\xa2\x6c\xb5\x1f\xc7\xc8\xd4\x5f\x3c\xcc\x64\x96\xd7\xfc\x48\x5a\x6a\xc9\x49\xd7\x18\x4a\xd2\x1d\x80\xf0\x04\x0c\x3d\xb3\x0e\xf8\x9e\x74\xfd\xc5\xc2\x08\x06\xce\xba\x8b\xd4\xf7\xc4\x53\xcf\xd3\x7e\x65\x37\x78\x0c\x91\xa9\x1c\x42\x16\xe4\xfb\x8a\x2c\x64\x8c\xe5\x12\x7e\xd8\x6c\x86\x66\xa6\x0e\x59\x70\x1e\x1a\xe6\x30\xb2\xdd\x74\xc0\xb0\xdc\x56\xa2\x70\x21\xbe\xa6\x3d\xf3\xa0\xcc\x8a\x6c\x15\xa3\xda\xd8\xb9\xd7\xa0\xdc\x6b\xf0\xcd\xf8\x61\xb9\xce\xa1\x9f\x47\x97\x61\x7f\x75\x41\x80\x4f\xf3\xd0\x91\x8c\xd8\xd8\xac\xcd\x30\x66\xca\xfd\x0e\x03\xb6\xd5\x85\x1b\x53\x1d\x2e\xd6\x37\x66\xb1\x1e\xb0\xad\x0e\x2c\xad\xd8\x71\x3f\x66\xe7\x31\xdc\x98\x15\x3b\x4e\xc3\xb7\xcd\x8a\x7d\xc3\x6e\xdc\xeb\x7c\x65\xdb\x66\x2a\x45\xb5\xbd\x1e\xd5\xb8\x3f\x66\xe7\x63\xd8\x36\xa8\xc6\x16\xd5\xb6\x41\xb5\xcd\xb6\xdd\xeb\xac\x89\x83\x66\x33\x4e\x9b\xb3\xc5\xd8\x38\x7d\xec\xd7\xb9\xc1\x23\x64\xb0\x6e\xda\xb3\x4e\x4f\x1f\x16\x67\x0c\xec\x4e\x9e\x3c\xd7\x17\x86\x13\xcf\xf5\xc5\x8a\x6d\x3c\x12\xc3\x98\x7a\x31\x63\x6c\x4c\xe7\x73\xac\x67\x07\x04\x8c\x6d\x17\x9b\x7e\xbf\x81\x6d\xc3\xd2\xad\xee\xd2\xde\x37\x0e\x82\x74\x39\xee\x2b\xf2\x74\x0c\x76\xd1\x8d\xcd\x97\xb6\xd1\x11\xdd\x2c\x9f\x21\x70\x5b\x0f\x33\x5a\x82\xb8\xce\x20\x76\x3d\xdc\x3f\xbe\xc2\x7a\xae\xd6\x4e\x13\x9f\xdd\x19\x2e\x19\x80\x32\xaa\x87\x9f\xd2\xb3\x87\x3c\xd1\xf3\x99\x72\xc3\x62\xc3\xb5\xdc\x82\x0c\x72\xdf\x72\x4f\xd9\x81\x5c\xa2\x26\x49\x97\xf4\x5e\xbe\xa1\x6c\xd6\xb6\x74\x37\xb7\x4f\x08\x2f\xd7\x4e\x4b\x95\x73\x63\x8c\x66\x72\x81\x7a\x25\x8a\x4b\x67\x8e\x55\x4d\xc1\xc8\x2c\xb5\x22\xf4\x43\x52\xa3\xd6\x6c\xe1\xd2\x6a\xd4\x93\xec\x69\x37\x7f\xda\xc3\xa7\xbe\x0d\x14\xe9\x93\x88\xc9\xf3\xe4\x82\x32\xc6\x88\x0d\x73\xa6\xcd\x66\x2a\xbf\xd3\x12\x99\xfc\xb6\x32\x28\xd5\x79\x74\xb3\x49\x48\xc0\x22\x6a\x94\x13\x12\x31\x4e\xdd\x6d\xdc\x86\x0e\x5c\x0e\x51\x7a\xdc\x8a\x08\x26\xec\x7e\x8b\xd5\xeb\x2b\xbf\x75\x3f\x55\xc0\x74\xdf\x71\x32\x55\x4a\x9b\x0a\x76\xed\x5b\x2b\x4b\xd1\x56\x33\x62\x69\x08\x51\x26\x5e\xbd\xe5\x93\x3e\xe7\xc9\x85\x41\x63\x56\x0a\x2f\xed\xe4\xec\x68\x9a\xa9\x11\x12\xd3\xd9\x75\x82\xb0\xdb\xc2\x34\x62\x06\x7b\xaf\x42\x74\x68\x04\x62\x58\x3e\xdb\x59\x1c\xe2\x2e\xc9\xf0\x4c\x72\x0b\x94\xdc\x02\x24\x4b\x32\x41\xa7\x18\xcf\xa6\x99\x3a\x94\x7d\x1c\xd4\x03\x10\x70\x7f\xe3\x29\x08\x3d\x3c\xdc\xe0\xc9\x43\x95\xf2\xc1\x43\xfb\x49\x82\xf0\xf8\xa2\x50\x8b\x03\x26\x0f\x55\x1f\x2d\x57\xd6\xe9\x45\x87\x41\x2f\xca\x82\x41\x42\x96\x9c\x47\x17\xbd\x91\x22\x21\xf0\xf3\xe8\x02\x34\xb4\x5a\x36\x76\x35\x44\x67\x54\x89\x4b\xaf\xd5\xea\x03\x39\xc0\xd9\xfd\x22\xf3\x45\x5b\x05\xdc\x34\x63\x98\x0b\x68\xf0\x59\x98\x3d\xc6\xac\x03\x63\xd6\x81\x01\x13\xbd\xf8\x70\xd8\x6c\x8e\x0f\xfd\x74\x83\xf5\x06\xb6\x19\xb9\x61\xd1\x79\x7c\x41\x5d\x0e\x13\x46\x9e\xb3\xf0\x7c\x8c\x3f\xae\xd8\x8d\xeb\xc3\x94\x3d\x77\x7d\x23\xd8\xb7\xb7\x18\x9b\xd8\x52\x23\x98\xc1\x1d\xdc\xc2\x35\x1c\xc1\x89\x29\xdc\xea\x5e\xc0\xa9\x29\xd8\xea\xe2\x22\x70\xd2\x6c\x92\x19\x3b\x71\x7d\xb8\x63\x13\xc3\xa6\x23\x76\x62\xf8\x0b\x4e\x9b\x4d\x72\xcd\x4e\x5d\x1f\x8e\x98\xd1\x90\xc9\x2d\x3b\xc5\x0f\x47\xcd\xe6\x1d\x1d\x29\x72\x05\xd7\x90\x40\xab\x35\xa0\x70\xa2\x30\xd9\xc4\x36\x4c\x61\x6c\x54\xb2\x41\x8b\x5d\x59\x4f\xe1\x69\xf6\x65\x66\x21\x07\x2d\x36\xb3\x5f\xe2\x16\xdb\x81\x71\x8b\xed\x58\xfd\x32\x1c\x92\x23\x3a\x68\xb5\x32\x5c\x93\x0c\x57\x5e\xd3\xa0\x8c\x37\x6e\xb1\x6e\xb5\xf4\x1d\xcd\xeb\xba\xca\xeb\x4a\xa1\x47\x8a\xcc\x60\x9a\x51\xbb\x4c\x43\xb7\x97\x6d\x2e\x6f\x9d\xcc\xe7\xa3\x2d\xc6\x6e\xa9\xaf\x04\xbf\xee\xd5\x71\xd6\xa9\xab\xd5\x71\xbd\xbe\x8e\x9d\x85\xd5\x64\xb1\x3d\x65\x5a\xf2\x16\xb5\x60\xdc\x6a\x2d\x70\x5b\x20\x3e\x1c\xf6\xb2\xf6\x94\x06\xdd\x8e\xf3\x72\x41\x7b\xbc\xb2\xe0\x95\xe7\xf0\x8d\x7d\x9b\xcf\xcf\x2f\x7a\x29\xbd\x25\x5e\x79\xee\xfa\x90\x2a\x54\xdf\x28\xd6\x48\x3a\x87\xd9\x94\x9a\xcf\x3b\x87\x41\xfe\xfc\x2d\x93\xa0\x8f\xcc\xcc\x99\x79\x09\xdc\x7a\x01\xdc\x79\xdf\xd2\x0d\x9f\x23\xc5\x9c\x4b\x31\x9e\xfc\x7e\xf0\xe4\x5d\x29\xa9\xcd\x89\x5a\xb5\x35\x8d\xe7\x0f\x4d\x0f\x07\xd9\xda\x91\x1e\x0a\xbb\x57\x5e\x02\x47\x5e\xc0\xee\x03\xaf\x03\x3f\x3d\x01\xe6\x45\x9c\x7b\x6f\x53\x3d\xc3\x94\x67\x01\x9a\x51\xc6\x4e\x0d\xdc\x80\xde\xd7\x30\x2c\x28\x04\x6e\xc0\x76\xd2\x1d\xee\x8a\xe2\x12\xb8\x3f\x41\x40\x04\x81\xab\x0c\x94\x62\x89\x45\x1b\xb8\xb1\x1b\xb3\xfb\x99\x17\x59\x0c\x8b\x8c\xfa\xd6\x91\xca\x5c\x99\x45\x70\xca\xf2\x4a\x94\xb7\x8b\xe7\xc1\x05\xdc\x92\x96\x11\x12\x54\x09\x11\xc0\xdd\x9f\x10\x40\x92\x2e\xee\x53\x45\x14\x3c\x86\xc4\x74\x70\x00\x47\x46\x34\x2d\x4e\xab\x34\xd8\x30\x84\x7b\xc5\x72\xd8\xcc\x83\x8e\x9d\x72\x1f\x78\xdd\x52\xbf\xa9\xf2\xda\xf9\xbc\x24\x95\xb6\x4a\xdb\xe6\x35\x8f\xa9\x15\x76\x46\x49\xed\xd9\x70\xbe\xd0\x55\x96\xa9\xcc\xda\xca\x53\xb6\xf2\x21\x66\xa1\x55\x9e\xcd\x10\xc4\xfd\xe7\x86\x1e\xed\x5e\x43\xe8\xc6\x10\x51\xef\x11\xbe\x25\xa1\xab\x99\x82\xd0\x4d\x58\x04\x9d\x43\x62\xe4\x5b\xec\xce\x68\xae\x26\xda\xea\x7d\xe8\xa4\xd5\x53\xef\xf1\x72\x41\x62\xea\x8a\xcd\xb2\x12\xbb\x47\x6e\xcc\x94\x45\xb5\x19\x11\xf5\xca\x38\x28\x6c\x11\xd3\xaa\x56\x0b\x37\x4b\x09\x36\x8b\xfe\x33\x67\xc0\x04\xb9\x78\xcc\xb4\x6d\xd3\x1e\xea\x5f\xb9\xf0\x1e\x30\x9d\x2a\x95\x03\xab\x54\x0e\x0a\x35\xd1\x54\x3c\xb0\x1d\xd8\xea\x42\x00\x6a\x85\x6b\xc8\xce\x91\x1b\xa6\x5d\xd1\xcb\x94\xd4\xe0\x2a\x1c\x0f\x3e\x44\x03\x11\xe7\xcb\xcf\x84\x75\x7a\x93\xc3\x9b\x6c\x21\x9b\x64\x6b\xcf\x95\xb1\xfc\xd9\xb8\x7f\x73\x3e\xb9\xf0\xcc\x1f\x94\xf0\xad\x16\x6f\x11\x3b\xf1\x71\x2a\xf0\x43\x36\x6c\x36\x87\x87\x6c\xda\x6c\x92\x84\x49\xb2\x7d\x3e\xb9\x80\xab\x74\x6c\xa7\x90\xf7\x41\xad\x07\xf2\x2e\xe8\x71\x36\x5d\xe4\xfd\x91\xd9\x66\xd0\x01\xe5\xfa\x50\x4e\xac\xf2\x4d\x2d\x6d\x5a\xa0\xab\x2e\x53\x91\xa5\x47\x4a\xcc\x06\xdf\xcd\xb3\x2e\x07\x26\x7e\x57\xab\x14\x65\x55\x56\x94\xeb\xb6\xb2\x06\x51\x8b\x1c\x59\xda\x87\xcf\xf5\xe0\x95\x4e\x22\x26\x4b\xdb\x7f\xa0\xd9\x36\x32\x2c\xed\xe9\x15\xe3\x35\x9f\x93\x55\xaf\xad\x97\xa9\x3e\xb6\x3d\xd1\x6c\xea\x2d\xc6\x64\xb3\x59\xdb\x56\xd4\x20\x4b\x47\x98\x71\xb7\x3c\x06\xe5\x26\xb5\x58\xfa\xd4\x89\xe6\x26\xe6\x3b\x85\xfa\x66\xbb\xcc\x90\x3e\xe3\x9a\x93\x0e\xc8\x5c\xe7\x29\x41\xe7\x6a\xbd\xed\x5a\x37\xae\x2b\xf3\xab\x48\xef\xaf\x7a\xe9\x7e\x67\xca\x8d\xbd\x55\x9f\xd8\xfd\x77\xcf\x34\x61\xea\x29\x37\x59\x64\x55\x1f\x78\xe5\xf3\x53\x71\x9a\x86\x45\xbb\x21\x86\x2e\x66\x1e\x0c\xdb\x23\xb2\xc4\xf6\xe7\xda\xbd\x29\x54\x3e\x99\x85\x83\x16\x0a\x13\x41\x7c\x14\xd5\xbd\x4a\x41\xc1\x4c\xd1\x9e\xc8\xd7\xaa\xb4\xa6\x50\xc6\x42\xe9\x27\x62\x18\x29\x41\xb6\x15\x49\x30\x5e\xd2\x4d\x28\xf0\x7a\x3d\x8f\x8d\x09\xb3\x95\xd6\x40\x0b\x27\x42\x79\x83\xb8\x44\xb6\xe9\x67\x2b\xc0\xb5\x7b\x54\x36\x56\x1a\x9d\x2d\xb3\x00\x29\xf4\xab\xad\x2d\x1c\xb8\x31\xb3\xb3\xc0\x9d\xe5\x43\xf6\x68\x15\xbb\x66\x5e\x12\xdb\x91\xd5\x2f\xe1\xb0\xec\x6e\xd0\x2c\x77\x0f\x3f\x4b\xb7\xca\x5f\x28\x3e\x42\x3f\x71\x9e\x0b\xa7\xdc\x3f\x99\xfa\x7c\x2e\x2e\xdc\xa3\xde\x5b\x69\x2c\x4b\xc6\x58\xe2\x06\xfd\xc4\x8d\x3d\xd3\x5f\xee\x4f\xec\xae\x52\x24\xd3\x82\x68\xf7\xce\x1e\xda\xcf\x1b\x50\x64\xae\x60\xda\xbd\x4d\x03\x13\x92\x72\x60\x42\xba\xce\x27\xe7\xdc\x68\xba\x81\x7b\x04\x11\xdb\x41\x47\x44\xd0\x8f\x6c\x5d\x51\x5a\x57\xaf\x36\x6c\x11\x54\x86\x3a\x70\xd5\x05\x5d\x88\x66\x13\x77\xfe\x45\x29\x30\xc6\xe6\x55\xa8\x1e\xef\x50\x6e\x4c\x24\xed\x0d\xac\x67\xd3\x9b\x92\x6e\x87\x2e\x16\x24\xc1\x74\x21\x0c\xa7\x28\x91\x4c\xe4\xed\x2b\x85\x6a\x3e\x55\x69\x04\xa4\x4d\x2e\x65\x58\xfe\xf4\x6e\x2a\x32\xd6\xf8\x5d\x12\xe9\x6a\x71\xab\x9f\x46\x52\x0b\x69\x8f\xff\x75\xb7\xd6\x80\x3a\x4e\xd1\x49\x59\xa2\x01\x9e\xb9\xf0\x62\xa8\x1f\xef\x2c\x9d\xee\x54\xec\x35\x99\x91\x50\x41\xe2\x4a\x3e\x11\x90\xb8\x68\x21\xe2\x8e\x48\x71\xe2\x5e\xba\x9a\x8f\x3e\xf0\x89\x70\x75\xf4\x2e\x9a\x09\xf5\x94\xc7\x82\x50\x08\xd8\x19\x5a\x16\x45\x07\x82\x28\xbc\x3f\x58\x57\xc0\x5e\x93\xa7\x8a\x44\xe7\xe2\x82\x42\x90\xf7\xe7\x1d\xf9\x03\x8f\xa9\x42\x50\x89\xa5\x50\x20\x41\x97\x36\x69\x31\xd4\x10\xf3\x78\x3c\x33\x7f\x1e\x9b\x3f\xa5\x60\x45\x3c\xcd\x9e\x45\xd8\x0f\x7c\x48\x58\x80\xdd\x03\x9c\x3d\x55\xa5\x48\x95\x77\x95\x78\x8b\xdc\x4d\x2e\x70\xa2\xb1\x2b\xa3\xa8\x2a\x33\x62\xdf\x14\x31\x6b\x97\x59\x3d\x8c\xc9\xb7\x28\x36\xe2\x2f\x2b\x5b\xe8\x96\x4c\x51\x25\x53\xac\x25\x53\x80\x2c\x85\x33\xdc\xa2\x6b\xfc\xd6\x1e\x01\x40\xb2\x03\x33\xb5\x74\xa8\xc7\x02\x22\xf3\xe8\x47\x83\x3b\x08\x4d\x13\xa2\xf5\x4d\xf8\x21\x59\x62\xc3\x18\x18\xc7\x33\xff\xec\x0f\x49\x1c\x53\xd4\xa1\xe4\x8c\x12\xe5\x06\x3c\x6d\x5d\x68\xd6\x9d\xc8\xb4\x2e\x82\x10\x0c\x5b\x43\xc8\x34\xfc\x90\xac\x03\x01\x3a\x55\x82\x83\x66\x93\x64\x44\x30\x3c\x0e\x7d\x40\x6d\xf3\xe1\x8d\x5a\x19\x38\xa3\xc4\x8f\x44\xc4\xfa\x48\x86\x13\xdc\x01\x78\xa1\xf8\x44\xf4\x57\xbe\xad\xc4\xfd\x94\xe2\x9d\x24\x74\xc5\xee\x83\x83\x0e\x2d\x45\xdc\xbc\x53\xc4\x7a\x62\x89\x4e\x4f\xc0\x94\xa3\xa3\x39\xa1\xf7\x09\x6a\x27\x49\xbf\xe3\x91\x37\x8a\x70\x0a\xb8\xd3\xda\xcd\x27\x59\xed\x24\x1e\x93\xa0\xfa\x04\x61\x50\xfc\xa0\xb2\xd2\x35\xea\x5a\xc7\xfe\xb4\x48\x12\xb6\x53\x8e\xa2\xff\xa0\x8a\x0d\xf4\xe7\xc2\x74\xcd\x38\x0a\xb0\x45\xee\x95\x59\x84\x5d\x3e\x9f\x4f\x49\x97\x2e\xd6\xc6\x33\x46\x11\x7c\x13\x95\x90\x2f\x7a\xaf\x9a\xcd\xab\x30\xd6\x91\xba\x73\x47\x11\x51\x14\x24\xb1\x69\x1a\xb0\xa5\xaf\xd6\xee\x02\xaf\xc6\x96\xa1\x32\x86\xc8\x89\xe6\x5a\xa0\xcf\xdc\x81\x12\x5e\x38\x53\x6b\x53\x28\x6c\x46\x9a\xea\x00\xeb\xf0\xde\xd7\xdd\xf9\x65\xef\xfc\x02\x56\xec\x98\x78\xd5\x80\x62\x78\xbd\x9a\xad\xec\x6e\x41\xdf\xfe\xe3\x9d\xa9\xf2\xbe\x7f\x35\xd0\xe4\x52\x92\x5a\x40\x5d\x39\xb7\x29\xbd\x7f\x2a\x49\x82\x01\x60\x45\x82\xd3\xe5\x1d\x20\x55\xdd\x01\xc2\xd3\xd2\x25\x42\xf5\x9a\xbd\x1f\x1b\x72\xb7\x2a\x86\x22\x3d\xc3\x53\x3e\xc5\xa4\x68\xff\x5a\xe3\x2e\xbe\x77\xa4\xcb\x7b\xf1\x1f\x53\x5e\x5f\x11\x3d\xab\xe8\xfd\x1b\x55\x1e\x17\x1b\xe8\x9e\x05\xa9\xb9\x23\xa1\xd3\x4d\xda\x27\x77\xaf\x07\x66\xae\x28\x22\xfb\x47\x92\x18\x99\x46\xbd\x13\x49\x06\xb8\xe1\x67\x27\x31\x46\xed\xaa\x6a\xd4\x6e\x11\x28\xf7\xb1\x2e\x5a\xb2\xd5\xea\x5c\x5d\x10\x0a\x4f\x37\xc5\xed\x6a\xb6\x1c\x70\xf2\x5a\xb9\x71\xa0\xa2\xf1\x18\x21\xe1\xe9\xa2\x1e\xf8\x58\x6d\x19\x86\x3a\x6a\x42\x4b\x67\x06\xf4\x86\x78\x8f\xf5\xe4\xa6\xb5\xbe\x13\x43\x63\x86\x65\x3f\x4f\xa3\x29\xd3\x69\x23\x0c\xee\xaf\x8a\xfd\x55\x82\x99\x5c\xe3\x0d\x98\x6a\xf1\xc3\xc2\xeb\x17\xb1\x4e\x2f\x68\x36\xa3\x43\x6e\x17\xd1\xd0\x68\x33\xc5\x21\x7d\x63\xde\x33\x79\x1e\xb5\x5a\xb8\x11\x76\xae\x5a\xad\x8b\x66\x93\x74\x3b\x8c\x85\x7d\xa2\x5b\x2d\x10\xac\x4b\x3d\x22\x5a\x2d\xc0\x34\x0e\x8c\x91\x83\xdd\xbd\x47\x8f\x9a\x21\xed\xd7\xca\x79\xdd\x62\xff\xfb\x0d\x09\xfa\xca\x6b\x77\xd3\x28\x2c\xf8\xb9\x21\x2a\x4c\x1d\xe6\x66\x51\xb5\x0a\x5d\xa5\x94\xf6\x25\xd1\x6e\x9c\xf8\xb1\x36\x86\xc9\x0e\xa5\x7d\xd5\xda\xf1\xda\x5d\x4f\x12\x7d\xae\x2e\x68\xdf\xf9\x53\xa2\xbb\xf6\x5c\x5d\xf4\xdb\x3b\x9e\x6a\x75\xcd\xd7\x76\x77\x41\xe1\xd9\xa6\xb0\xb4\x6a\x3d\x46\xbb\x59\x50\x78\xa9\x56\x66\x41\xe8\xc9\xc2\x0a\x93\x99\x22\xa7\xab\xa9\x0f\xec\xfe\xb5\x3e\xdc\x7b\x34\x9f\xef\x3f\x2c\x32\xa8\xc9\x42\xab\xa2\xf0\x42\x6d\x4c\x6f\xd1\xe9\x15\xfd\xd2\x53\x85\x72\x5a\x23\xb6\xbd\xf7\x08\xb7\xe7\x0e\x3b\xf3\xb9\x3c\x64\x49\xea\x89\x13\x4c\xfe\x26\x5a\xc9\x22\x8f\xc9\x51\x76\x1c\x3e\xab\x0d\xa9\x1d\x3a\x2b\xdb\x26\x56\xb5\x6d\xef\xd1\x3f\xc5\x7c\x2e\xfe\xb9\xff\x90\x86\x43\x72\xb0\x6f\x7f\x3d\xec\xa0\x7e\x28\x0e\x1f\x3f\x9c\xcf\xbb\x9d\x9d\x43\x91\x92\xa3\x59\xf7\xe0\x37\xdd\x12\xed\x47\x0f\xad\x5f\x2f\x7f\xb1\xbf\xdf\xab\xbe\xd8\x7b\x54\x10\x2d\x31\x1c\xb0\xf7\x57\xcc\x9f\x94\xf2\x26\x20\x43\xf3\xc3\x4e\x3f\x9b\x01\x1e\x6f\xc9\xc2\xef\x1d\xa4\xce\x99\xa8\x36\x0d\x5a\x2d\xda\x33\x4c\x1f\xf5\x89\x60\x5d\xd0\x36\x9d\xcb\x12\xd3\x47\xb4\xd9\x34\xb0\x8b\x9c\xcd\x79\xca\xe1\x36\x7d\x4e\xa5\x77\xcb\x11\x81\x35\x41\x69\xe3\x3a\x24\x93\x62\xd6\x38\x7b\xff\xee\x95\xd6\xd3\x63\xab\x86\x98\x91\x83\xd3\x21\xd1\x8c\x53\x63\x2d\x2f\xef\x41\x4f\x55\x34\x52\x22\x8e\x9d\x8a\x44\xc9\xda\xf8\x34\x9a\x4c\x13\xcd\xfd\xb1\x68\x36\x9f\x9a\xf9\xc2\xc9\x7d\x10\x78\x46\x19\xe0\x03\x31\x80\x60\xe0\x49\x57\x47\x9a\x8f\xed\x6a\xb0\x22\xc8\xc0\x11\x4a\x45\xca\xa9\xc4\xe5\x91\x13\x49\x8e\x86\x6b\x4b\x68\xab\x1e\x2d\x97\x39\x59\x5f\xc6\x10\x54\x2b\xb0\xca\xcc\x5b\x71\x74\x62\xc0\x71\xd7\x3f\x9e\x46\x32\x16\x5f\x8e\xdf\x81\x7f\xe2\xdd\x07\x57\x9e\x74\x63\xcd\x75\x12\x43\xf0\x2e\x7f\x3e\x15\xb7\x7a\x01\xc1\xcf\x15\x47\x5c\xb6\x23\x9b\xa0\xa4\xc8\xc2\x56\x4c\x05\x99\xe6\x78\x71\xfe\x54\x7f\x4a\x87\xc2\xea\x74\x33\xc0\x21\xb0\x46\x89\x31\xe1\x32\x26\x74\xbc\x86\x43\x7b\x9d\xc3\x08\xf5\xb6\x20\x95\x58\xa1\x1c\x91\x0e\x44\x46\x83\x2e\xbf\xda\x69\x45\x14\x14\xbb\x23\x1f\x86\xe5\xe4\xdf\xc5\x1a\x71\xad\xc9\xa9\xb1\x6a\xfb\xbc\xe5\x00\x66\x55\xe0\x1e\xa7\x0b\x3c\xc6\x9a\x87\x9e\x11\x69\x96\xd7\xa3\xf1\xf8\x38\xed\x95\x57\x82\x0f\x84\x8a\x09\xa5\x10\x94\x7b\xcb\x1e\xb9\xc2\xbd\x49\xdb\x3f\x87\x3b\x9d\xce\x7c\xbe\xdb\xe9\x1c\xb2\xec\x15\xcd\xc5\xa2\x51\xcd\x59\x51\xd8\xf4\x25\x9c\x48\x72\x3b\x34\xeb\x74\x4f\x31\x45\x74\x4d\x6b\x38\xb2\xb1\x7f\x1e\x59\x5b\x78\x46\xee\x86\x36\xa1\x98\x59\x3e\x89\x84\xc4\x0d\x70\xf3\x75\x41\x7b\x5a\xdd\xdd\x4b\x37\x9a\x0a\x49\x12\x37\x78\x0f\x89\x3b\xe0\xb0\xd5\x59\xce\x3e\x81\xbc\x75\x3d\x24\x06\xc0\xa0\xd9\x5a\x9f\xad\x26\xf8\xd9\xd3\xae\xdf\xb3\x59\xe3\x30\x88\x24\x9d\x69\xb6\x8b\xcc\x0c\xb1\xd9\xcd\x5c\xdf\x58\xda\x39\xb9\x77\x53\x14\x7c\xca\xf5\x41\xba\xb3\x50\x5f\x3d\x55\x62\x20\xa4\x0e\xf9\x38\x36\x36\xd0\xc0\xcc\x52\xe5\x06\xfb\xd4\x58\xcc\x6e\x3a\x03\x4c\x91\x7d\x97\xdb\x86\x65\xe9\x05\x12\x37\xe0\x45\x14\x46\x2c\xe4\x80\x3c\x1f\x12\x41\xfb\x64\x05\x3d\x4e\x6a\x3e\xb7\x0d\x05\x8e\x3d\xec\x2e\x5c\x9f\x7a\xf8\x54\x56\x42\x5c\xee\x47\x4a\x13\xba\xa8\x6b\x3b\xd5\x50\xd3\x0e\xf8\x9e\x76\x7d\xe0\x95\x29\x20\x99\x11\x0d\x85\xf3\x6d\x46\xbe\x0d\xf3\xfc\xf4\x48\xf7\x17\xb5\x26\xad\x8d\x33\x72\x7a\xd2\x0d\x3e\x36\x9b\x44\xb7\x98\x33\x71\xcc\xfc\x0e\x44\xfa\x33\x74\xec\x38\x16\xec\x7b\x2c\x46\xcf\x6f\xa7\x48\xd5\xf2\x48\x1e\x69\xa3\x59\x3f\x59\xaf\xf3\xcb\x64\x3c\x46\x83\x6f\x92\x96\xdc\x9c\x52\x16\xd2\x5d\xc4\x8e\x99\x99\x36\xd4\x24\x4b\xe1\x03\x11\x6b\x77\x7b\xbc\xd5\x3a\x94\xcd\x26\x86\xc3\x8a\x5b\x11\x90\x80\xd2\x66\x33\xda\x2a\x43\xf6\x0a\x84\x61\x1e\xc1\xd5\xee\xc2\x30\x0d\x57\x09\xcd\xf4\x0e\x33\x3f\x3f\x13\xe7\xe1\x45\x6f\x78\xde\x6e\x87\x17\xcc\x37\x8a\xb3\x8f\x6a\x73\x92\xa5\xf1\xfb\xee\x83\x38\xef\x5c\x80\xb0\x22\x02\x38\x1c\xe3\xa9\x7d\x1b\x8c\x92\x55\x9a\xcf\xe6\xe2\x15\xd3\x90\x26\x18\xd4\xe5\x65\xc5\x66\xcb\x4a\x3d\x48\x9d\x3c\xdf\x5d\x66\x00\xd5\x74\xcd\x70\x48\x82\x56\xeb\x9f\x2c\xc9\xb5\x90\x92\xe3\x85\xab\x11\x6a\xe7\x59\x18\x47\x7b\x17\xb2\x74\xd1\xca\x34\x52\xe5\x29\x9e\x72\xc8\x73\x75\xd1\xd3\xe7\xed\x36\x86\xb3\x5e\x6b\x22\xb0\xb1\x79\x9e\x7f\x6c\xae\x84\x02\x7c\xa9\x8a\x9d\x0b\x08\xe0\x38\x57\xf5\x29\xfc\xd8\xac\x09\xe9\x6c\x4c\xcb\x63\x59\x19\xd9\x9e\xcc\x65\x72\x90\x0d\xab\xa0\xe9\xfe\x99\xd5\x40\xf2\x00\x56\x7b\x78\x87\x43\x2a\xaf\x29\xad\x22\xce\xbd\xfb\x35\x78\x4a\x61\xe5\xc0\x7c\x5a\x6b\x01\xcb\xe6\xa6\x14\x75\x1b\x33\xe0\xfd\x8f\x0d\x25\xd5\xe1\xa1\x5c\xd0\xde\xd9\xda\x7a\xd5\x3f\xff\x29\x37\x64\x3a\xfe\x27\x7e\xee\x55\x12\x2c\x8a\x75\xc7\x21\xf3\xf3\x5e\xaf\xa5\x16\xea\x86\x8f\xcb\x56\xd0\x53\x49\xc4\xc6\xd3\x5e\x79\x21\x45\x6b\xe7\x71\xdf\x96\xa2\xdb\x1b\x33\xf2\x5d\x83\xf3\xa7\x6c\x34\x1a\x0d\x07\x66\xe4\x29\xfe\x72\x40\x96\xf7\x43\x7e\x2f\x97\xb8\x23\x97\x7a\x6d\x03\x5b\x46\xdd\xef\x18\x49\x96\x17\xfe\xa3\x5c\xf8\xf1\xc3\x43\x46\x24\x3b\xc3\xa3\x56\xcd\xa6\x3c\x64\xdd\x9d\x9d\x02\x56\xea\x02\x36\x07\x3b\x64\x8f\x3b\xcd\xe6\xc1\xfe\x21\x2b\xf9\x43\xd5\x6a\xc8\xfd\x87\xcd\xe6\xde\xa3\x0a\xa4\x2e\x41\x5a\x62\xe6\x73\xac\x67\x3e\x47\x24\xa5\xbb\x30\x74\x25\xbb\x40\x71\x01\x45\xe5\xbd\xcb\x4b\x37\x3c\xac\x29\x11\x94\xde\x3b\x0e\xda\x34\xa8\xf8\x6a\xf6\x11\x42\xcd\x26\x30\xd4\x8c\x54\x3c\x15\xa5\xc3\xc6\xca\x0d\x30\xbf\xf0\x00\x92\x75\x6c\x76\x47\x42\x0d\xd2\xdd\xee\x6b\x2f\xc1\xd8\x69\x6e\x46\xb8\xfc\x31\x01\xfc\xc7\x22\xb6\xe6\xc7\x9a\x73\x7d\x36\xaa\xb6\xbd\x83\x3b\x64\xdb\xd5\xac\xb8\x46\xf3\x32\x64\x48\xe0\xec\x0e\xdd\xeb\x3e\x68\x37\x80\x3b\x32\x44\xcc\xa0\x5d\x81\x39\x91\x13\x50\x8c\x83\x66\x02\xcf\x7f\xf8\xab\x34\xc7\x06\x16\x5a\x6b\x07\xce\x48\xa4\xc1\x66\x18\x46\xc3\x03\xce\x0c\x17\x41\xbc\x12\x95\x64\x12\x7e\x01\x9d\x2c\x63\x1a\x6b\xd6\x85\x81\x66\x3b\x70\xa3\x59\x07\xb6\xf5\x5a\xd9\xb1\xa0\x30\x59\xa9\xfb\xe6\x39\x20\xe0\x4a\x6f\x3a\x0f\x9c\xa7\xf9\xa5\x30\xdd\x00\xd8\x29\x03\x8e\x36\x00\x76\xcb\x80\xb3\x35\xa4\xa5\x27\xfe\xe0\x6e\xcd\xf7\x9d\xf4\xfb\xad\x66\x2f\xe1\xfa\x2f\x90\x1c\x69\x66\xea\x5d\xc0\x89\x66\x4a\xc2\xa9\x66\x1f\xe0\xb9\x66\x33\x09\xdf\x56\x0f\x48\xcb\x71\x16\xf0\x5d\xaf\xf7\x48\xfe\x01\x12\xbe\xda\x3c\xb3\xf0\x74\x2d\xdc\x31\x99\x91\xdf\xc1\xe6\x89\xa1\x70\xa9\xd9\x46\xa6\xdd\xd2\xf5\x34\xce\x02\xf3\xe9\x5a\x86\xc5\x3c\xff\x59\x4a\xe7\x32\x83\xc2\x1b\xcd\x7e\xc2\xbb\xbf\x40\xde\xcd\x52\x67\xe7\x2a\x79\x5a\x83\x84\x84\xa9\x76\x17\x6b\x88\x34\x96\xeb\x49\x26\x40\xb1\x04\x34\xe3\xa6\x82\x0f\x7a\xfd\xcc\x7d\x67\x67\xce\x99\x69\xe0\xab\x0d\x70\x6f\x0c\xdc\x8c\x7c\xd0\xd0\x81\xdf\x15\x51\xb4\xdd\xa5\x36\xd7\xf7\x59\x75\x08\xb2\xe0\xde\x92\x49\xdd\x29\x65\xf4\xae\x24\x00\x55\x7d\xe5\x75\x3b\x3b\x7b\xbf\x11\xd5\xc6\x0f\xb4\x55\x29\xd8\xa5\x6d\x4c\x16\xd9\x3a\xd8\xdf\xdf\x3d\x58\xc0\xeb\x35\x33\xf9\x52\x43\xa4\xd3\x89\xf5\xf1\xef\x90\x83\xae\xcd\x3a\x4d\x97\x44\x1a\x15\x4d\xa6\xeb\x7d\x97\x52\xcf\xbe\x6a\xc9\xf3\x6e\xf1\x7e\x87\x52\x54\x77\xe0\x78\x5d\xb7\x39\x7f\xca\x3f\x25\x71\x5a\xdf\x34\x91\xad\x2e\x6d\x11\x87\x36\x9c\xd6\x5b\x45\xbe\xea\x34\x91\x09\x7c\x5d\xd9\xa2\x19\xf9\x69\xba\xfb\x8c\x2e\xe0\xe7\x32\x72\xe9\xa5\x7c\xb1\x22\x03\x51\xf9\x08\x94\x44\xde\x5b\xf2\xc2\x7e\xcc\xf2\x71\x76\x6d\x32\x87\x22\xac\x36\x4f\xf7\x00\xb2\x74\x05\x08\xf9\x43\x11\xb3\x00\x70\xbb\x52\x29\xb3\x52\xce\xc8\x89\x06\xad\x4d\x87\x13\xa3\x35\x19\x5d\x1b\xd9\x2f\xe9\x3b\xae\xd3\xd2\x9e\x73\xfe\x0f\xcc\x56\xf6\x8f\x0b\xc7\x72\x3c\x37\x0c\x99\x1f\x11\x69\xe4\x1b\xf1\x59\xb4\x81\x0f\x01\x73\xce\x6d\x5f\xb9\x9c\xb6\x9c\x0b\xa7\x8c\x37\xd8\x84\x65\xc7\xcb\x4e\xe9\x70\xd3\xae\xc8\xf5\x71\xca\x44\xae\x9f\x9d\x0a\x0b\x19\x51\xae\xdf\x77\x4e\xaf\x44\xe3\x4d\x1c\x49\xf7\x99\x08\xa2\x81\x70\x23\x29\x3e\x0e\x1b\x5c\x37\xbe\xc7\x91\x74\x5a\x56\xfd\x70\xe0\x35\x36\xd3\x73\x96\x40\x1d\xda\x72\x1a\x43\x1e\x8e\x31\x27\x5b\x43\x5f\x89\xc6\x30\x1a\x8f\xa3\x99\xcd\x28\xf5\x4d\x93\xdf\x15\x89\xa8\x81\x9a\xf1\xbb\xd8\x73\x7a\x35\xc5\xc6\x28\x33\xd8\xa0\x10\x66\xe4\x95\x86\x63\x8d\xd1\x37\x0b\xc9\x38\x13\x2c\xc2\xdb\x57\x12\xa6\x4a\x4d\x4c\xb5\x50\xe7\x98\xcb\x46\x28\x75\xd4\xe0\x2b\x5a\x80\xa9\xed\x64\xd4\x98\x46\x71\x1c\xfa\xe1\x38\xd4\xa1\x88\x9d\x96\x6d\xf4\xfa\xf6\x6d\x39\xc5\xf6\x6f\x80\x03\x1f\x61\xb6\xf9\x74\xe0\x43\x86\xe5\x3f\xa9\xc8\x1f\x8b\x89\xad\xc4\x34\x19\x37\x59\xd7\x61\x6d\x39\x9e\x69\x26\x6a\x70\xde\x72\xd9\x51\x78\x23\xa4\xc5\x80\x70\x0e\x6d\x91\xb7\x8a\xcc\xc8\x73\x0d\x7b\x80\x5d\x97\xbe\x0e\x8c\xe8\x7a\xa6\x37\x1c\xfe\x2d\x2f\x5a\x10\x78\x1a\x06\x78\x50\x1a\x5e\xe2\x81\xec\x17\x9a\xbd\x87\xcf\x6b\x45\xda\x0f\xa2\xe8\x83\x1f\xd6\x88\x7c\xaf\xd9\x0b\x4d\x66\xe4\xb3\x86\x1d\xd8\xdd\xa1\x14\xbe\x68\x76\x4b\x9e\x19\x61\xf7\x5e\xc3\x4b\xf3\x1f\x85\x27\x9a\x85\xf0\x63\xfd\x42\x8d\x47\x9f\x21\x5c\xab\x8c\xdb\xba\x3e\x69\x76\x04\x6f\x35\xfb\x02\xbf\xaf\x5e\xbb\xb2\x6c\xb2\xf0\xc7\xda\xaa\xb2\x9b\x16\xfa\x98\xbd\x1b\xa4\x60\x2f\x40\x09\xb6\x0d\x5a\xac\x76\x54\xf7\x32\x83\x7d\x46\x94\x80\xdd\x1d\x90\xd6\x8f\xe5\x83\xb6\xf3\x2b\xed\x4a\xed\xf2\x45\x96\x01\x4f\x14\xab\xd9\x6b\x14\x19\x76\x55\x41\x23\x5d\xfc\x55\x3d\x2f\x34\x51\x0f\x76\x77\x72\x39\x93\xfb\xbd\x0b\x02\x5c\xde\x93\x6c\x46\xb4\x40\x49\x97\x61\x4e\x96\x31\x63\xd2\x40\x91\x61\xde\xdd\xf9\x4d\xb9\x02\x04\x7b\x9b\x8e\xd8\xee\x0e\xe8\x76\x97\x52\x90\x4c\xf6\x0d\x0f\xba\x23\xea\x29\x77\x04\x06\xbb\x30\xd8\x4d\xe9\x5e\x7e\x7e\xfb\x99\x86\xdf\x0d\xd4\x90\xb6\x34\xcc\xc8\x1f\x18\x3a\xff\xdb\x7b\x4d\x11\xb2\xc8\xaa\x57\x86\x4c\xb9\x00\x3f\x53\xe0\x82\x3d\x87\x40\x6c\xd8\x89\x49\x17\x6d\x75\xd8\x29\xda\x9d\x08\xd8\xea\xc2\xfd\xc8\x13\x20\x3c\xfd\x60\x77\x67\xde\x81\xa1\x97\xa4\x2e\x16\xce\x52\xcd\xea\x8e\x3c\xc1\x36\xa1\x03\xa6\x67\x54\x4b\xc5\x54\xdb\x34\x92\x69\x10\x76\xb4\x38\x08\x0a\x09\x4b\xf0\xc4\xf2\xca\x0e\xeb\x1c\xca\xfc\x14\xe9\xbf\xef\xee\x80\x60\x88\x57\x83\x6c\xeb\xd2\xfe\xde\x0d\x09\x84\xa9\xa9\xad\x4d\x0d\x12\xce\xa0\x08\xf7\xf8\xa2\x0d\x37\x8b\x15\x3c\xba\x25\xdd\xed\x05\x0c\x05\xf3\x25\xf8\x82\xc5\x12\x62\xb1\x59\x81\x1b\x57\xbf\xaf\x58\xb8\x32\xce\xae\x05\x68\x75\x6b\x81\x59\x3b\xb9\xbc\xca\x6e\x37\x5b\x2c\x60\x20\x18\xd7\x70\x23\xd8\xe6\xb3\xf8\xf7\xfc\xc0\xe3\xe0\x07\x9e\x02\x9e\x78\x02\xfc\xc4\xd3\xe0\xdf\x79\x12\x82\x53\x0f\xfb\x72\x5b\x30\x2d\x61\x22\x36\xcf\xcb\x2b\xc1\xa4\x84\xa9\x58\x2b\x08\x0e\xbb\x7d\xe5\xdd\x91\x2b\xc3\x7b\x13\x7b\x4c\xd1\xb0\xcd\x48\x30\x2e\x61\xb6\xb1\x9c\xe3\xd8\x82\x1d\x48\x6f\x52\xb9\x13\x6b\x8e\x4f\x75\x30\x4e\xa6\xa2\xd6\x80\x60\x7b\xbb\x78\x58\x61\x6f\x9f\x31\xdd\xef\x7a\x1d\x48\x98\xe8\x25\x45\x68\x53\xab\x55\xc4\xdf\xd6\x12\xb4\x63\x50\x64\xba\x4d\x95\x9f\xe6\x3a\xd2\x3d\xc5\xba\x9d\xdf\x54\x8b\x97\xb6\x63\x12\xc6\x44\xff\x48\x7b\xd7\x9a\xd8\x9a\xda\xca\x53\x74\x01\xb7\xeb\xe6\x44\x38\x24\x81\xa9\x64\x3e\x9f\x91\x6d\x01\xce\xff\xe5\x40\x42\x4b\x75\x58\x8a\x66\x64\x24\xc0\xf1\xcc\x37\xa4\x26\x5d\xc7\x39\xae\xe3\x55\xe0\x80\x71\x3c\xf7\x79\x27\xc8\x8c\x4c\x05\x04\xad\x2e\x66\xeb\x49\xc5\x0d\x2f\x4c\xc7\x23\xdd\xe3\x2c\xf7\xb9\x5e\x6b\x32\x24\x37\x02\x75\xd9\x99\xb0\xd1\xc4\x3c\x3b\xdd\xb2\x58\x02\x4a\xe0\x48\xe7\x5f\x29\x5c\x8b\x95\xab\x91\x6d\x9d\xa8\x37\x28\x49\x1b\xf4\xc0\x01\xeb\x81\x4a\x4c\x83\x12\x96\x14\xf9\xb4\x6e\xc8\xad\xa5\x65\x6a\x38\x55\x50\xc4\x88\x94\x25\x65\x82\x52\x30\x83\x29\x4b\x66\x01\x47\x62\x85\xc1\x60\x29\xd1\x75\x4a\x44\x4a\x49\xdf\x01\xab\xfa\x09\x9b\x66\x54\x14\x94\xdc\x92\x6b\x53\xc5\xb5\xb6\xfd\x29\x5a\x5d\xd0\xd4\xd0\x83\xd4\x08\x74\xd8\x56\x41\x6d\xcf\x18\x52\x4e\x56\x8a\xa0\x40\x97\xf2\xc3\xa6\x84\xe8\x94\x90\x7f\xcb\x92\xe0\x1a\x93\xe9\x5e\x33\x5d\x10\x72\x47\x8e\xca\x84\xe8\x56\x17\x93\xa1\x21\x19\xba\xbc\xd5\x91\x02\x1e\xa5\x97\xd2\x9d\x0a\x26\x24\x3c\x17\xab\xf5\xe9\x53\x01\x78\xcd\x96\xf7\xe0\x81\x03\x92\xf6\x67\xe4\xc4\x4c\x32\xac\xe2\x21\x48\x4a\xbd\x02\x26\xae\x00\x75\x2d\xd0\x23\x04\x32\xea\xfe\xb7\xe5\x19\xd9\xeb\xd1\xce\x02\xbe\x0b\x76\x24\x41\x4a\xf6\x5d\x98\xc9\xf8\x74\x2d\xb3\x88\x22\x41\x8c\x40\x2e\x16\xf6\xf8\x09\xcf\xde\x5b\xee\x8e\x18\xb7\xef\x23\x3b\x5a\x11\xc2\x46\x25\x58\x0b\x51\x4a\x9a\x92\xc0\x0c\x43\xce\x66\x18\xd1\x85\x97\x29\xb8\x1c\xf6\x3b\x9d\x43\xdd\x47\x33\xc9\x90\xf1\x5a\x1b\x15\xd5\xbb\x25\x4f\x85\xf5\x9a\xb4\xba\xa8\x84\x16\x5d\xbb\x8c\x4a\xad\xf9\xaa\xea\xaf\xab\x17\x27\x5c\x8a\xf5\x5b\xed\x79\xfd\x1d\xcb\x45\x6f\x2a\x5c\x54\xbe\xea\xe5\x8e\x5c\x0a\x58\x1f\x07\xa4\xc1\x5e\x02\x96\x3b\x57\x28\xbc\x13\xec\xb9\x84\x0f\x62\x5d\xd0\xc8\x8c\xbc\x13\xab\x76\xd8\xbe\x0b\xa2\x30\xea\xc6\xa2\x79\x55\x21\x5f\x63\xf0\xd9\x6a\x14\xea\x57\x50\xcf\xd2\x7b\x7f\xe9\xc2\x4c\x61\x5b\xc5\x99\x60\xc7\x12\x5e\xaf\x5d\x14\x2e\x25\x41\x8c\x67\xc2\xb6\x91\xda\x5b\x06\x36\x78\x9a\x3e\xac\xac\xbe\xb3\x00\xa2\xd8\x8c\xbc\x11\xf0\x3a\x45\x05\xd8\xaf\xaf\x04\x89\x34\x85\xef\x82\x9c\x51\x48\xad\x52\xb1\xa1\x02\x64\xee\x05\x85\x63\xb9\xde\xbb\xf2\x41\xa4\xcb\x57\xef\xa3\x74\x4f\x79\x7c\xcd\xee\x7d\x4f\x4a\x08\x3c\x2e\x8d\x92\x2e\x41\x78\xc7\x12\x86\xf9\xb9\xe4\x22\xa8\x48\x94\xf4\x07\xfe\xce\xdb\xea\x82\xff\xdd\x73\x1c\xf0\xaf\x2b\xd9\x4e\x8f\x2b\x70\xc2\x9b\x46\xe0\x7f\xab\x40\x7c\xad\x40\x9c\x7a\xc7\x82\x38\x0e\x05\xfe\x35\x7b\xba\xf1\xee\xf9\x77\xef\x48\x03\xbf\x36\x7f\x83\x5b\x4f\x02\x9f\xe2\x0b\x85\x7f\x7f\xe2\xdf\x23\x9c\xf4\x71\x5e\xfe\x2c\x7b\xf2\x07\xe6\xbb\xff\x39\xfb\xfd\xc6\xdb\xea\x96\xb3\xf7\x95\xea\x27\x33\x72\x1b\xa1\xf8\x46\xa9\x72\x17\x41\x17\x24\xf5\x24\x6d\x39\x0f\xf8\x34\x7c\x70\xb3\x53\xba\x1f\xf0\x99\xd8\x98\x06\xf6\x65\xf5\xf3\x7e\xed\xf3\x0b\xb1\xca\xcd\x56\x64\x97\xab\x7e\xde\xad\x7d\x7e\x2f\x36\xe6\x98\xfd\x52\xfd\xbc\x57\xfb\xfc\x64\x73\xdd\x3f\x36\xd7\xfd\x69\x33\xf2\xb7\x9b\xdb\xfd\xfb\x66\xca\xff\xd8\xdc\xa9\x32\xd9\x48\xb9\x4a\x36\x96\xd6\xc9\x46\xca\x45\xb2\x91\xf2\x24\xd9\xd8\x2d\xbc\x46\x5a\xbd\x65\xc1\xe6\xe2\x51\x52\xde\x2b\x47\x23\xc3\xb6\x0e\x56\xae\x93\x9f\x23\x63\xa4\xcc\xc8\xef\x51\xea\xf6\xb4\x4e\xa1\x02\x5f\x98\x6c\xbe\xd9\xf8\x8e\xbc\x47\x14\x76\xff\xb8\x48\xef\x91\x54\x12\x8e\x3e\x89\xea\xfb\x92\x23\x32\x41\xb1\xa4\x69\xe6\xb3\xbb\x13\xa5\x3d\x69\xe5\x6e\xf7\xa3\x84\xfc\xe3\xb5\xb4\x17\x4b\x84\x52\x8b\x91\x50\x5e\xc3\xf9\x47\x4b\xb6\xfe\xe1\xfc\x83\x7a\x41\x88\xf1\x06\xd9\xf2\x13\x25\x24\xcd\xf6\x2d\x06\xd6\x35\xa3\x69\x8b\x38\x8d\x41\x38\x0a\x75\x0c\x98\x46\x7f\x14\x69\xfb\x09\xeb\x36\xb2\x0f\x92\x90\xe8\x90\x28\xdc\x91\x2c\x52\x92\x26\x65\xe3\xa7\x74\x13\x70\x42\x64\x71\xd0\x96\xe3\xc9\x0b\x1f\x02\xb6\x85\xca\x6e\x6f\x45\x2f\xd9\xa6\xdd\x90\x69\x08\x09\x48\x0c\x28\xf0\x5f\x82\x74\xf9\x09\x6e\x9b\xe0\x5e\x26\xc7\x1b\x9f\xec\xad\x8e\x41\x7e\x00\x8e\x68\x68\x77\x8d\xf4\xa8\x0c\x11\xa7\xd4\xc3\x2e\x0f\xa0\x03\xf7\xfc\x04\x1d\x21\x45\x6a\x04\xdf\x58\x38\x2f\x3d\x61\x46\xdc\xe5\x8b\xf2\x88\x8c\xcb\x8c\x31\x0a\xc9\x8c\x5c\x85\x36\x0b\xec\x23\xe4\x8f\x72\xfb\x07\x65\x58\xd3\xb1\xd9\x30\x0c\xf8\x9d\x67\x7b\xb0\xb2\x43\x77\x53\xee\x2f\x32\x23\xb3\x10\xf6\x40\xd2\xf9\x7c\x0b\x9f\xbb\x9d\x0e\x48\xf4\x22\x9a\x0f\xf8\xa3\x54\x78\xbb\x54\x98\x10\xd9\x66\x5d\xfa\x60\x6f\xde\xa1\x6d\x22\x1f\x74\x3b\x9d\x79\x87\xb6\x88\x7c\xb0\x87\x4f\xa5\xd4\x3a\x7f\xc9\x97\xd7\xa6\x79\x6f\x23\x8c\x7e\x2d\xb2\x6d\x24\x15\x98\xe7\x61\x65\x40\xfc\x9b\x72\x7e\xf8\xa4\x32\x53\x9e\x86\x70\x1a\x56\xb6\x16\x47\xc9\x2f\xee\x4b\xde\x91\x57\x21\x26\xe4\xb7\x57\x06\x18\xc3\x33\xaa\x60\x9a\x95\x31\xc9\xbe\xa3\x55\x22\x1c\xcf\x19\xf2\x71\x2c\x4a\x2b\xc5\x5d\x05\xcc\xdd\x9e\xcf\x1d\x67\x2b\xbd\x7f\xda\xac\x5a\xc5\xb1\xf0\x32\xe0\x67\x33\xd6\x97\x11\xbc\x0f\xe1\x54\x13\xd9\x72\x98\x53\x61\xf6\xeb\x64\xed\x0e\xa5\x73\xe9\xd8\x14\xd5\xb5\xad\xca\xa3\x0a\x21\xac\x60\xa5\xc7\x96\x95\xaa\x13\x83\xc9\x4a\xf8\xca\xbf\x60\x56\xcc\xe7\x9d\x43\x76\x47\x64\xb8\x4a\xa8\xe9\x0a\xed\x0b\xc0\xbb\x10\xfe\xcb\x13\xa9\x97\x47\x7d\x95\x32\xb4\x57\x39\xe4\x93\xe9\x04\xa7\x7c\x4f\xef\x69\xb2\x71\x91\x7a\xbe\x79\x99\xf9\x56\x9e\x18\xd2\xdd\xee\x9f\x26\xde\xf3\x84\xda\x1b\x1c\x8a\xd3\x82\x9b\xeb\x78\xba\xb9\x8e\xcb\x6a\x13\xd4\x10\x24\xc8\x61\x39\xe1\x7a\x15\x20\x1b\x68\x8b\xa7\x04\xf7\xae\x0c\x17\x27\xe4\x4d\x52\x15\x11\x1f\xca\xf3\xf5\x28\x24\xc7\xe4\x7c\x46\x06\x21\x04\x21\xf9\x9e\x90\x0e\xa5\xf0\x2e\x21\x27\x09\xee\x93\x00\x7e\xc9\x3e\x3f\xc5\x97\x70\x99\x90\xe5\xc1\x76\xfe\xfc\xd3\x8e\xf2\x3a\x80\xad\xce\x22\xc3\xf7\x0b\xa8\xec\x39\xc5\xfc\x1a\x60\x7a\x51\x6e\xc1\xab\xda\xec\x4b\x1d\x22\xd5\xd1\x38\xab\x02\x61\x48\x90\x4c\x63\xe9\x8b\x94\xe4\x35\xa0\xe2\xcb\xc7\xa4\xbc\x87\xe5\x7f\x2b\x42\xc7\x82\x57\x2d\x63\x20\x0f\xc1\x71\x60\x46\x3e\x87\x90\x68\x38\x4b\x8c\x14\x1f\x02\x4e\xef\xd7\x09\x7c\x32\x75\x7d\xa4\x14\x7e\x84\x66\x71\xc3\x65\xb2\xa5\xcc\x3f\x25\x45\xba\x5a\xc3\x4f\xf4\x2a\x7d\xc1\xdd\xa3\x13\xcc\x14\x76\x8a\x93\xf7\x18\x33\x8b\xbf\xc2\xfd\x01\xce\x41\x2e\x6f\x2b\x4d\x86\x39\x3d\x01\x4d\xe3\x0a\xb7\x8b\x7b\xce\x64\x39\x8d\x38\x9a\x21\x48\xe5\xc7\xc4\xfe\x7b\xad\xe1\x4b\x48\x9c\x61\x38\xd6\x42\x19\x79\x94\x6e\x2d\x45\x6c\x46\x5e\x84\xab\x75\x95\xdb\x24\x13\xa2\x74\x01\xaf\x88\x84\x63\x72\x7e\x49\x9c\x38\x1c\x0b\x19\x88\x81\x83\x3e\x84\xc4\x52\xb5\x85\xee\x0c\x33\xd6\xc4\x09\xe5\x55\xe8\x87\x7a\x19\x42\x64\x10\x93\x64\xc5\xd7\x24\xfb\xca\x03\x8d\x69\x9d\xca\x9f\x3b\x46\x80\xd8\xcf\x4a\x04\x22\xbc\x11\xca\x81\xbb\x94\xbd\x88\x33\x52\x51\x32\x75\x20\x32\x1c\x94\xeb\x05\x9d\x43\xdc\x2c\xea\x3b\xfd\x6c\x0f\xa5\x69\x40\x3c\xa7\x24\xe2\xbf\x26\xa9\x7e\xd4\xb6\xc7\xe7\xb6\xe7\xf3\xf4\x69\x50\x7a\x16\x45\x4f\xcb\x5e\x01\x2c\x2c\x90\x7d\xe0\xc5\x28\x0f\x70\x90\x05\x64\xdb\xd3\x1a\xd3\xc7\x6b\x4c\x1f\xaf\x5d\x51\x38\xa7\x2e\x43\x63\xa8\xa0\x3c\x0e\x00\x7f\xda\xe3\xcd\x18\x13\x82\xc9\x62\x04\xcd\xde\x67\x49\x46\xf3\x94\xd6\x58\x51\x98\x56\x14\x62\x45\xa1\x1b\x80\x66\x84\xb3\xd0\x1d\x50\x53\x9e\x71\x57\x40\xc0\xc2\x5a\xa5\x1d\xe0\xae\x0f\x3c\xab\x74\x89\x86\xc8\xf5\x21\x72\x03\x88\xdc\x01\x44\x86\x06\x4d\x33\x48\x43\x86\x42\x32\x0a\x4b\xec\xbf\xd2\x87\x83\xac\x0f\x07\xcb\x7d\x68\xba\x40\x60\x03\x13\xbc\x32\x97\x33\xe1\x06\x10\x30\x61\xc8\x62\x62\xa9\x27\x43\x5c\x05\x87\x6c\x45\x6f\x6a\x5c\xdd\x8a\xde\xcc\x12\x0c\xd8\xde\x2c\x17\x14\x8c\xe0\x75\x58\x14\xab\x25\x66\x5e\x09\x8a\x95\x4b\xac\x5c\x62\xe5\x72\xa9\x47\xb3\x60\x99\x94\x16\xa2\xcc\x80\xd3\x95\x83\xd9\x85\x10\x86\x20\x96\x68\x29\xf5\xe9\xb3\x25\x5d\xe7\xcb\x10\x26\x55\x2d\xe6\x65\x15\x66\xe2\xe7\x9b\xf4\x85\xb5\x5a\xd3\xa9\x7c\xb8\x0f\x84\x31\xfa\x83\x8f\xc6\x9a\xae\xc0\x7e\xae\xc2\x7e\x43\x2d\x21\x32\x0d\xc1\x7f\x8d\xda\x31\x1d\xc2\xa5\x0f\x2f\x12\xe2\x9c\xb7\xcf\xff\xfc\xf3\xe2\x7e\x41\xe8\x6f\xad\xbe\x0b\x7f\xfe\xf9\xe7\x9f\xff\x63\x7b\xfe\x6f\x7f\xfe\x19\x5f\x38\x94\xc2\x1d\x79\xe3\xa3\xdf\xf5\x32\x5a\x7d\x18\x27\x78\xbb\x30\xda\x91\x59\x04\xa8\x75\x8b\xd4\x0d\xa1\xf7\x75\xc9\xee\xdc\x67\xd3\x18\x30\x76\xed\x8d\x30\xd2\x4d\x52\xda\x72\x16\x4e\x65\x52\x7f\xd9\x6c\xb2\x3d\xa9\xad\xe0\xf5\x25\xfc\x47\xcd\x22\xac\xaf\xe1\x9f\xaa\xdf\x1f\xd7\x0d\xe9\xea\xe7\x47\x75\x43\xfa\xd7\xb5\xd8\x5b\x59\xd5\x62\xef\xab\x03\xf6\x47\x19\xd3\xef\x09\xb1\xe2\x59\xf2\x89\x70\xe0\xcc\x27\x66\xd4\xac\x74\xb4\x69\x9d\xd2\x77\xdf\x52\x21\x1d\x1f\x8b\x91\xb8\x75\xe0\xad\x59\x48\xfc\xab\xec\xed\xf3\x1f\x09\x1f\x9b\xee\x9d\x0e\xe1\x99\x6f\x97\xbe\xb7\x43\x43\xc3\x88\xd6\x16\x68\xc9\x57\x87\xc3\x29\x5e\x2c\x79\x33\xf2\xd2\x30\x42\x77\x6f\xaf\x43\x5b\x0f\xbb\x8f\xf7\x0e\x1e\x19\x09\xa5\x0e\x3b\x7d\xd5\xee\xee\x1d\x74\x1e\x1f\x78\x8a\x3e\xc0\xa7\x87\xf3\x8e\x99\xa5\xf6\xf5\xc3\xdf\xb4\x99\x7e\xa2\x4d\x04\x7e\x45\xb3\x44\x3c\xd8\x3d\xd8\xdf\xb1\xc6\x8a\x7d\xfd\xf8\x60\xde\xa1\xd4\xbc\x9e\x67\xd1\xb9\xf7\xfc\x77\x8f\x48\x26\xda\x64\xf7\x60\xff\xb7\xa4\x45\x92\xd4\xbc\x49\x52\xf3\x86\xd2\x36\x21\xdd\xfd\xdd\xdf\x88\x62\x64\xff\x37\xd9\xda\xa1\x0f\xba\xfb\xbb\xa6\x86\x1d\xfa\x60\xdf\xfc\xdb\x05\x3e\xf5\x04\x53\x2d\xa2\x0e\xbb\x9d\xfe\xae\xd7\x7e\x4c\xc1\xdf\xf5\x92\xd6\x5e\xa7\xf3\x9b\x6e\x91\x9d\x43\xd1\xef\x78\xdd\xb2\xa9\xa3\x79\x65\xfe\xc8\x18\xf6\x60\x46\x74\x0c\x22\x46\x2e\x25\x4e\xdb\x30\xb0\x8c\x61\x07\x56\x6d\xd6\xd5\xb7\xea\xba\xb5\xad\xba\x9d\xda\x56\xdd\x6e\x2d\x33\xc3\x5e\x2d\x8f\xc3\x7e\xed\x38\xfa\x41\xf5\xf6\xc3\xc6\xc3\xda\xcd\x83\x8f\x6a\x89\x00\x1e\xd7\xae\xca\xea\x76\xea\x57\x5f\x75\xbb\xf5\xdd\xc2\xee\xce\x62\x41\x66\xe4\x93\x9f\x36\xba\xda\xea\x19\x79\xef\x17\xbd\x71\x5a\x7a\xff\xa5\xf4\xde\x2b\xbd\xff\xb1\xe6\xbd\x2a\xf5\xaa\x9b\xbe\xdf\x85\x19\x79\x92\x57\x8c\xff\x6b\x39\xdf\x4a\x62\x41\xf0\x8a\x2a\x76\x65\x73\x98\x5a\x71\xef\x8f\xa8\x59\x86\x8a\x7d\x92\xfb\xe0\x95\x87\x72\xef\xa3\xb7\xa5\x9a\x4d\xbc\xdc\x7c\x3e\xd7\x7d\x7c\xde\xf1\x76\xbd\x2e\x3a\x44\x5d\xff\x5b\xd9\xd5\x54\x99\x11\x87\x9d\x54\x65\x1d\xc7\x64\xe5\x44\xaf\x07\x13\x49\x34\xc3\x54\x29\x36\xa8\x71\x49\xd4\x03\x31\xef\xf4\x65\x8b\x7c\xd3\xf6\x99\xb6\x88\x6e\x39\x0d\x87\x52\x0f\x77\xd9\x42\xbb\x7d\xb0\xc0\x09\xec\x00\x0f\x8d\x21\x00\x83\x98\xe2\x49\x8f\xc2\xcf\x55\xa2\x6d\x2b\x77\x19\x34\x9b\x5b\x65\x9f\x41\xe6\x41\xb0\x4e\x83\x92\x13\xac\xdc\x30\x33\xad\xac\xdf\xc0\xa8\xc8\x93\x18\x24\xec\xd1\x76\xfa\xd4\xed\x74\x68\x2b\x7b\xdb\xe9\x94\x29\x88\xf8\xff\x1f\xd9\xbd\xf0\xdc\x95\x9b\x87\x71\x0f\x5d\xc3\x89\xf7\xd5\x66\x75\x6a\xcd\xea\xd6\x9a\xb5\x53\x6b\xd6\x6e\xad\x59\x7b\xb5\x66\xed\xd7\x9a\x75\x50\x6b\xd6\xc3\x5a\xb3\x1e\xd5\x5b\xf5\xb8\x7e\x55\x5d\xb7\xb3\xd4\xcc\xb2\xdf\x7d\x58\x9a\x45\x44\xb0\x19\x99\xe1\xb0\xa3\xcc\xc6\xdb\x9a\xa4\x99\x51\xf8\xda\xe8\x6c\x28\xb0\xf1\xbd\xce\xc1\x8d\xaa\xd4\xdd\x3b\xe8\xe2\x6b\x51\x81\xcd\x95\x77\x23\x67\x55\xab\xdb\xe9\xfc\x26\x5b\x7b\xbf\xe9\x96\x70\x79\x8b\x08\xd7\xef\x77\xbd\xb2\x5b\xca\x2f\x53\xa3\x98\x21\x8e\x29\x26\x29\xdc\x73\xed\xc9\x76\xc0\x89\x42\xc1\xad\xca\x01\xd3\xa3\xd8\xe8\x85\xbb\xa8\x1e\x72\x5d\xc2\x16\x97\xd9\xfc\xd1\xc1\x9e\x40\x46\x7f\xd8\x7d\xdc\x3d\x28\xdf\x13\x33\xe6\x95\x25\xfc\x4d\x0c\xdd\x5d\x2b\x77\x4a\x2e\x3d\x5e\x8f\x09\xef\x4f\x34\x71\x4e\xae\xa2\x64\x3c\xc0\x1b\x7c\x7d\xd1\x10\x93\xa9\xbe\x73\xa8\x37\x23\xaf\x63\xd8\xd6\xc4\xf9\x5d\x45\x72\xd4\x78\x7d\xf2\xf1\xd1\x41\xa7\xdb\x18\x46\x6a\xc2\xb5\x43\x61\x5a\x33\xee\x6f\xca\x04\xbc\x23\xd7\x11\xdc\x1f\x1b\x41\xb3\xd5\xa1\x70\x92\x3d\x9c\x66\x0f\x5f\xb2\x87\x57\xe6\xe1\xbd\x31\xa9\xde\x08\x10\x1c\xe5\x64\xd9\xa7\xb0\x5d\x46\x1b\xa0\xdc\x92\x94\x6e\xa2\xbb\xea\xa6\x9a\x54\x04\x6c\xf0\x0a\xcd\xa0\x8f\x56\xb8\x7d\xab\x86\x4a\xa6\xd3\x44\xd7\xa5\xc0\x25\xd9\xea\xc2\x56\xa7\x9e\x1e\xd4\xbe\xee\xd6\xd3\x08\x5d\x92\xad\x0e\x42\xd7\x18\xd6\xbe\x37\xab\x33\xa1\x98\x97\x31\x97\xea\xfe\xc8\xf4\x41\xe2\xfa\x14\xfc\x2b\x4f\x82\x1f\x79\xca\x08\x72\x51\xf6\x59\x56\xd7\x89\x2c\xb7\x56\xea\xfb\xf3\xbf\x63\x87\x7c\x1a\x0b\x43\x4a\x10\x4d\xa6\x63\xa1\x45\x83\x0f\x06\xa1\x1c\x61\x08\x1e\x9e\x9e\x32\x56\xb6\xa7\x5c\xbf\x3f\xd3\xb6\xbf\x27\x78\x3f\xa8\x67\xca\xbe\xb7\x00\x71\x83\x2b\x81\x99\x32\x42\x25\x06\x65\x3f\xd6\xb4\xca\x60\xbf\x9b\x45\x4f\xc4\x70\x4b\x7e\xc4\xd0\xed\x1a\x2b\x2c\x06\xe4\xbb\x9d\x6c\xb9\x2b\x79\x49\x97\x99\xb3\x5b\x67\xce\x19\xdf\xb8\x1d\x73\x57\xed\x00\x6b\xce\xf2\xaf\x76\x20\x3f\xa3\xd7\x82\x9f\xa1\x41\x14\xa3\x41\x74\xd3\x2b\xb6\x01\x67\xe4\x6a\x0c\xdb\x46\x69\xe5\xa7\xb8\x1b\x98\xbd\x30\x26\xa4\x17\x40\x6c\x5f\xdc\x8c\x81\xe3\x66\x1f\xfe\xfa\x1c\x9b\xa9\xfd\x8d\x42\x82\xdb\x7e\x0a\xfc\xcf\xf6\xc3\x00\x23\xb1\x6a\x3b\x7f\xb7\xe5\x16\x1a\x2b\x92\x48\x86\x63\x46\xdd\xed\xfe\x64\xec\x99\xd6\x55\x9c\x44\xd7\x7c\xe3\xfe\xd1\xd1\xe6\xde\x38\x29\x57\x77\x35\x24\x47\xe3\xea\x64\x3c\xdd\x5c\xfc\x39\xdf\xec\x74\xdc\xfc\xf9\x7b\x45\xa3\xa8\x26\xd8\x29\x39\x1d\xab\x52\x10\x95\xf7\x33\xab\xbb\xcf\xc8\xe7\xb1\x8d\xb1\xc8\x6d\xb7\x3f\xff\xec\x5b\x0f\x90\xcb\x0f\x28\xa5\xae\xdf\xbf\xc4\x54\xf0\xca\xf5\xad\x03\x2d\x0b\x38\x55\xae\x8f\x31\x19\xd4\xbb\x24\xce\x03\x07\x8e\xf2\xf3\x47\x58\xc9\xf1\x18\x5e\x8c\xe1\x1d\x91\x70\xcf\x0f\x70\x2f\x37\xf1\x0c\x9e\xe0\xd4\x30\xfe\x82\xe2\x78\x7c\x8b\xcc\xbb\x92\x03\xb4\x44\x2a\x92\xdd\x5b\xca\x9c\x96\xad\x7d\x99\x6d\x66\xca\x57\x57\xc1\xd4\xac\x2a\x3e\xec\x78\x15\x73\xac\xf8\xb0\xeb\x95\x2f\xe9\x2f\x7f\xe9\x14\x5f\x0e\x2a\x1f\xf2\xa5\xf5\xc9\xb8\xb6\x96\x7e\x19\xd7\x16\xdb\x1f\xe3\xba\xe4\x79\x3f\x2e\x0d\xde\x1b\xbe\x71\x63\xf2\x1d\xdf\x6c\x87\x7e\xa8\x7e\x7f\x58\xfb\xfc\x8a\x6f\xb4\x33\xcf\xaa\x9f\x0f\x6a\x9f\x5f\x97\xc6\xe1\xa3\x2e\xed\x04\x76\xd3\x6b\x4d\xa4\x67\xcc\x23\x54\x49\xa9\x6b\x0c\xc1\x4f\x21\x44\x98\xd0\x99\x56\xef\xf5\xfd\x58\xb5\x7b\xf4\x00\x8e\xc9\xb9\x1c\x10\x87\x8f\x85\xd2\x0d\xfc\xdb\x9e\x71\x25\x43\x39\x72\xe8\x05\x35\x9f\x93\x01\x41\x02\xaa\x06\xe5\x71\x19\x93\x4a\x88\x36\x4b\x95\x3f\xc0\xb0\x4e\x54\xc7\x39\x7c\xf4\x3a\xa9\xde\xbd\xa8\x48\xbd\xaf\x55\x22\x06\x03\xc8\xee\x9d\x88\x45\x75\xba\xfe\xac\x42\xfe\x31\x06\xe7\x4a\x89\xa1\x03\x0f\xfe\xc7\x77\x7e\xc3\xed\xfd\x2f\xde\x83\xd0\xd5\x22\xd6\x84\x48\x26\x69\x7e\xda\xf3\xc1\x9f\xf1\x83\x11\x38\x0e\xa5\x98\x74\xb8\xec\xaf\xa9\x62\x7d\x23\xea\xfb\xaf\x25\x67\x6d\x62\x7b\xb1\xee\xb2\x9d\x62\xc7\x21\x79\x70\x3b\x20\xce\xa5\x3f\xe6\xf2\xba\xd4\x63\x12\x7b\x0b\x5e\x9b\x09\x3a\x1b\xc0\x1d\x26\x73\x3b\xab\x74\xc3\xcb\x8a\xe4\x36\xba\x98\x2c\x07\x30\x9d\x0c\xe0\x0c\x70\x27\xe0\x28\x1f\x25\x2d\x6e\x75\x5b\x46\x33\xc5\xa7\xa5\xba\x54\xcb\xf1\xcc\x4f\x0a\x33\x72\x9d\xc3\xce\xda\xdd\x4e\x07\xa1\x9e\x2d\x8f\xde\x0b\xbe\x29\x22\x6c\x3e\x5f\x19\x00\x36\xcd\x71\xfb\x5a\x36\x7c\x2d\xdb\x51\xa2\xc7\xa1\x14\xed\x50\x0e\xa3\x86\x1f\xa9\x81\x50\xed\x8e\x43\x01\x3b\xc6\xd2\x37\x23\x71\x5e\x6c\xc8\x1b\x43\xde\xc6\x12\xc1\x15\x57\xba\x31\x51\xed\x1d\x24\xf1\x8c\x42\x32\x20\xce\x49\x94\xa8\x40\x60\x5b\x3c\xf3\xbb\xbc\xce\x7e\xde\xdc\x5d\xba\x4c\x5c\x1b\x7d\xcc\x88\xbe\x31\xf1\xd3\x3a\x90\x18\xb1\xd4\x86\x78\x92\x51\xae\xc2\xd1\x95\x6e\x77\x1a\xd8\xcb\xd6\xed\x6d\xba\xf4\x66\x00\x4e\x12\x0b\xd5\x8e\xc5\x58\x04\xda\x01\x27\x94\xa1\x0e\xf9\x38\xff\xda\x9e\x44\x3f\xdb\x7f\x01\x32\x13\xfe\x75\xa8\xff\x02\x2a\x25\x24\x88\xc6\x91\x72\xc0\xf9\xb7\x20\x08\x2a\xe3\xfc\x0f\x96\x6f\xf1\xdb\xe1\x1e\xae\x6c\xce\xa8\x3d\xe4\x03\x31\xa8\x0c\x51\x2c\x82\x48\x0e\xb8\xba\x73\x28\x7c\xe5\xe4\x98\x93\x4b\x8c\x02\xa3\x14\x26\x03\xe2\xbc\xc0\xcd\x86\x86\x7f\xd7\xd0\x57\x61\xdc\x18\x73\x5f\x8c\x4b\x55\x3b\x2d\x1c\x94\x0a\x0f\xbd\x2f\x6b\xcd\xff\xf6\x20\xdd\x6a\x88\x1f\x48\x31\xeb\xdb\xbd\x0b\xe6\xb4\xde\x87\xe4\x7d\x4d\x21\xfe\x52\x1a\xc8\xb3\x84\x48\xd7\x3f\x71\xfd\x8f\x95\xe8\x86\xbf\xc9\x6b\xef\x39\x59\x9a\xc6\xb9\x52\xf5\xc4\x6e\x2f\x77\xd2\xbd\x11\x5f\x9b\x1a\x71\x0f\x68\x1d\x83\xfa\x62\x3c\x6e\xc7\x63\x1e\x5f\xb5\xa3\x65\x16\xb5\xcd\xb4\x3c\x6a\xcf\x02\xc1\x26\x72\x07\x5c\x8e\x4c\xc7\x56\x08\x2e\x77\x97\xd3\x52\xbf\x40\xc9\x3a\x3a\x06\x48\x48\xa9\x77\x9f\x54\x67\xf6\xf7\x41\xed\xcc\xef\x8f\xea\x77\x51\x5a\x01\xc2\x91\x34\xac\x39\x6c\x07\x42\x1a\x66\xc8\xab\xb4\x3c\x70\x69\x44\xa1\xff\xb9\xc6\x05\x9f\x56\x08\xf3\x2b\xd3\x66\x07\xf0\xb6\x2c\x09\xc7\x83\x32\xfc\xdb\xcd\x1a\xd8\xef\x7c\xa3\x3b\xf8\x8f\xd5\x56\x8b\x2c\x6f\x08\xa6\x76\xc3\x96\x9e\xcf\xf1\xe4\x2c\x5a\x0f\x3b\x36\x71\x3d\xde\xb0\x94\x1b\x12\x95\xa8\xa7\xa0\xd4\x8e\x55\x0c\x55\xf1\x45\x49\xd7\x1f\xa5\x2e\x29\x5d\xf5\x45\x65\x1c\xe7\x47\xb0\xce\x17\x85\x8e\xdd\x45\x35\xd0\x43\x95\xab\xbf\x24\x12\xd3\x7b\x14\xee\xca\xa0\xba\x99\x3e\x30\xa2\x63\x9a\xe8\xb4\x8f\x55\x90\xf5\xf5\xdb\x41\x85\x19\x44\xb0\x34\x38\xd7\xe2\x6e\x10\xcd\x64\x3e\x3a\xbf\x57\x46\x27\x59\x59\x20\x99\xae\x01\xe7\xc1\x1a\x75\x22\x88\xc6\x8d\x20\x1a\xb7\x79\xa2\xa3\x42\xf8\xfe\xa2\x8c\x1e\x6e\x9c\xfb\x56\x7e\xcd\xc8\xcb\x01\x6c\x61\xb4\x4c\xce\xa0\xb8\x41\xbc\x56\x30\x56\x27\x64\x19\x4b\xb7\x82\xc5\xf9\x7f\xfe\xef\x4c\xd6\x55\x18\x3d\x08\x36\x6a\x78\x51\xb0\x51\x7f\x0c\x97\x3b\x76\x12\x25\xb1\xc0\x99\xb6\xac\xf8\x0c\xd7\x80\x8f\x05\xbf\x11\xcb\xe0\x7e\xb0\x51\x7f\x8c\x83\x8d\x36\xd5\x38\xd8\x38\xe7\x06\xc1\xc6\x19\x7b\xb3\x4c\xaa\x3f\x4e\x56\xb4\x69\xfb\x7f\x23\xb3\x04\x37\x75\x66\x91\x7f\x93\x51\x0c\x86\x5f\x63\x94\xc9\xe6\xee\xbc\xda\xcc\x28\xd3\xcd\x63\x35\x0a\x36\x46\xb4\xcc\x36\x33\xc2\xdd\x66\xe4\xb7\xc1\x46\xfb\xf6\xba\x56\x77\xfd\xfb\x51\xf0\xbf\xd6\x67\x5c\x33\xe7\x1e\x96\xfd\x00\x41\xdd\xb1\xb2\x53\xf7\xab\x9c\xae\x63\xc7\x01\xd7\xa2\xa2\x03\xd6\x94\xfa\xc2\x5b\x10\xe4\xeb\x02\x1e\x1d\x9c\x0e\x61\x18\x92\x0e\x1a\xeb\x53\xba\x4a\x35\x0d\xf8\x58\x18\x25\xec\xb2\x31\x89\xa4\xbe\xca\x50\x13\xc5\x34\x94\xe1\x54\x34\xb3\x10\xed\x2b\xcc\x7e\xb4\x6a\x56\x4c\x95\xb8\x69\x23\x50\x63\xd0\x1e\x8e\xc5\x6d\xba\x6c\x5b\x86\xfd\x79\x93\x17\x79\x79\x93\x2f\xf1\x4a\x45\x33\x67\xad\xb6\xc1\xe5\x68\x2c\xda\x63\x31\xd4\xe6\xd7\xee\x6d\x23\x48\x54\x1c\xa9\xf6\x34\x0a\x2d\x62\xd4\x3e\x2e\x32\x11\x5b\x90\x62\x49\x35\x5d\x56\x27\x25\xef\xba\x6f\xc6\x10\xb2\xfb\x6b\x69\x14\xd3\xb3\x1b\x38\x4b\x95\x99\x5f\xd8\x61\x73\xde\x70\x99\x18\xed\xb5\xca\x45\xce\x0b\xe1\xab\xd2\xfb\x8c\x9b\x9c\xf7\x5c\x05\x57\x4e\x95\xa5\x9c\xa3\xa9\x0a\xc7\x4e\x95\xaf\x9c\xf7\x3c\x2b\xbc\x9f\xd7\x95\x48\xe1\x54\x1d\x0c\xce\x9b\x64\x9c\xc1\x3d\xcc\xf1\x25\xa3\x24\xd6\x4e\xd5\xf3\xe0\x9c\x88\xa9\x16\x13\x5f\x28\xa7\xea\xce\x77\x3e\x06\x3a\x2a\x5e\xe7\x5e\x7d\xe7\x43\x74\x93\xc2\x57\x39\xda\x79\x26\x02\xfb\xa1\xb4\x51\xa7\xe8\x72\xff\x4b\x64\xd6\x75\xac\xf0\xf5\x3f\xcd\x0a\x68\x10\xfd\x35\x2f\x5c\xd8\x01\x3d\xbe\xc1\xac\x1e\x95\x29\xf2\xad\x3a\xc9\x42\x05\xce\x84\xdf\xda\xf3\x77\x0e\xd4\x43\x65\xbf\x07\x15\x07\xda\x9b\x3e\x36\xf2\xac\xce\xf9\x93\x68\xc0\xc7\x0d\x63\xe2\x34\xe2\x2b\xd3\x8a\xd4\x7a\x1a\x84\xf1\x74\xcc\xef\x1c\xb3\x00\x45\xc1\xf5\xaa\x49\x83\x45\xdb\x83\x90\x8f\xa3\x51\xa3\xfc\x23\xed\xb1\x62\xba\x2f\x97\x0a\x6c\x02\xb2\xf5\x00\xf5\xb9\x5a\x2c\x2d\xc1\x38\x8a\x45\x63\x92\x2d\x71\x66\x54\x6e\x03\xf2\xfc\xa6\xbc\x9a\xdc\x3a\xab\xa7\x96\xc1\x6c\x73\x41\x67\x78\x83\x01\x5c\x07\x80\xd1\x2e\x37\x15\x60\x34\xf9\x1b\x86\x4e\x1e\xca\x82\x12\x94\x53\x15\xc0\xa9\x6e\xef\x35\x8c\x90\xf9\x9e\xc4\x3a\x1c\xde\x65\x6d\xab\xcd\xdb\x19\x79\x62\xc6\xb4\x63\x0a\xe3\x53\x37\x1b\xee\x35\x74\x0e\xa3\x48\xaf\xee\x81\xc9\xb8\xbd\xd3\xa8\xaf\xb0\x71\x12\x04\x22\x8e\xcd\xb2\xbe\xa1\x63\x9e\x72\x19\x58\x63\xb4\xba\x5e\x57\x50\x4e\x55\x38\xc9\x8d\xdb\xdb\x80\x7c\xab\xa0\x38\x11\xba\xf1\x8c\x6b\xf1\xe0\x34\x9c\x88\xd2\xa2\xbd\xbe\xc3\x79\x70\x3d\x50\xd1\xb4\xcc\x65\x19\xc7\x7b\x19\xb8\xe5\xba\x60\x1c\x4e\x1d\x70\x94\x08\x34\xe9\xe0\xa5\x17\x1d\x9a\xb3\xe4\x34\x8a\x43\xbc\x10\x13\x9c\x61\x78\xbb\x81\xbb\xb0\xa2\xcc\xbe\xfb\x0b\x7a\x0a\x52\x4a\x5e\xe5\xcd\x2b\xfb\xe5\x66\x1d\xee\xcd\x66\xad\xe2\xdd\x66\xbd\xe0\xc3\xf2\x24\x57\xd1\x2c\x5e\x9e\xdf\xaf\x36\xe3\x39\x0b\xca\xb1\x28\xdf\x35\x1e\x12\x3a\x26\xe7\x25\x6b\xd9\x01\xdc\xe3\x70\xc4\x20\xd4\x8e\x19\x3a\x74\xa0\xff\x1d\x5f\x81\x2a\xb1\xc5\x73\x83\xc5\xf4\x63\x11\x4a\xea\x9f\xb8\xfe\x71\x5f\x79\x7f\x13\xeb\x8c\xcc\xb6\x0d\x69\xd7\x76\x2f\xa5\x54\xc7\xb1\xb0\x37\x2d\x38\xd5\xf1\x7a\x5d\x6a\x2b\x06\x36\xa8\x22\xb0\x41\x95\x3d\x8d\xdf\x35\x6c\x19\xa3\xb3\xd9\x54\x7d\x87\x39\x9e\x79\x9e\xcf\x55\xbf\x78\xf7\x1f\x8e\xe7\x6c\xe1\x1f\x86\xdd\x65\x23\xc0\x5c\xff\x5b\xa5\xc2\x8f\x9b\x2b\x04\xc5\x3e\x0b\x72\xcd\x6b\x4e\x5c\x1b\x53\x91\xd5\xd5\x59\xaa\xbd\x1a\x5c\x51\x8a\xe7\x9c\x91\x1f\x37\x70\x8d\x89\x94\xb1\xa9\x65\xcf\x71\x90\xba\x59\xab\x39\x57\x1a\xff\xe7\x4c\xab\x22\xf9\x8b\xeb\xff\xb0\x1b\x6a\x6e\x60\x03\x3f\xb8\xcb\xf7\xd0\xdd\xc0\xdd\xe0\xa0\xa2\xe2\xfd\x7f\x76\x99\xba\xdb\xae\x7e\xc7\x9b\x03\x9c\x4d\x96\x12\x2e\x67\x56\xd4\xaa\x95\xf6\xd0\x5f\x2c\x61\xfa\x57\x56\x0f\xf1\x5f\x10\xd1\xa5\xbd\x86\xcd\xd6\xd6\xcf\x55\x26\x4b\xae\x5c\xa6\xc1\xcd\x35\xd5\x72\x2a\xe4\x20\x94\xa3\x25\x6d\x4d\xdc\x4e\x71\x6f\xb8\x84\xfe\x59\x55\x32\x3e\x5f\xc1\x25\xc5\x38\x7c\xdf\xae\xbf\x33\x9d\xfa\x81\x4f\x84\xd7\xb0\xab\x1f\x6e\x46\xf8\xd1\x52\x87\xfc\x15\x8e\xa3\xc1\x40\x89\x38\xae\xa0\xe1\xaf\x96\x0c\xd7\x97\x41\xc5\xcf\x15\x59\x3f\xd7\x89\xdd\x4a\x8e\x7b\x65\x9f\x61\xda\x8c\x37\xdb\xf6\x09\x97\xe9\x71\x12\x6b\xa1\x1a\x27\x98\x2c\xd7\xd6\x54\x0a\x22\xc0\xc4\x25\xe5\x6d\x15\xc7\xb1\xf3\x0c\x77\xad\x56\x19\x4b\xc3\x48\x4d\x52\xbb\xbf\xa2\xab\x16\x6d\x0c\xa2\x71\x3b\x9e\x54\x9c\x94\xb6\xbf\x9c\xa5\x2e\x4a\x41\xbb\x9d\x3a\x63\x9b\x1e\x20\x55\xd8\xff\x5c\xc5\xb6\xdd\xbf\x52\x75\xe6\x75\x25\x92\xd9\x30\x50\x9f\x0f\x46\xc2\x81\xad\x4e\xa5\xc7\xd6\x87\x5d\xd8\x02\x99\xfa\xe4\xac\x8c\xc1\x48\x61\xb2\xbd\xbc\xd5\x91\x17\x29\x50\xea\xe9\x40\x9f\xe3\x82\x18\x2a\xe5\x20\xdd\x58\xce\xa2\x77\x13\x63\xa0\x5e\x0d\xe1\x75\x92\x46\xec\xe5\x0d\xff\x15\xe3\x4d\x09\x3e\x58\x32\xdd\x62\xa1\xcd\x7a\xba\x3c\x91\x06\x61\xcc\xfd\x31\xce\x24\xa2\xcb\x42\xa0\xca\x4e\x62\x0d\x3b\x89\x7f\x25\x3b\x7d\x12\x42\x15\x83\x7a\xb5\x66\x50\xb1\x8b\x9e\x05\x20\x33\x7e\xaa\x6e\xb6\x2d\x79\x1a\xd2\xe9\x33\xbb\x29\xa6\x4f\x79\xda\xfc\xd7\xc9\xfe\x32\xd5\xe1\x2f\xcf\x83\x0f\xb1\x11\x2b\x7f\xe4\xdd\x6c\xe4\x80\xcb\x8f\x69\x1a\x4e\xbe\x0d\xeb\xa6\xfc\x57\xa1\x62\x54\x66\xa4\x8d\x7e\x32\x2b\xf2\xbf\xa8\x01\x4f\x14\x97\xc1\xd5\x2f\x36\x40\xb9\xfc\xc3\xaa\x85\xe2\x3f\x59\x75\x12\x8e\x07\xc6\x5a\xf8\xf5\xda\x3f\xfe\x8b\x6b\xff\x12\x0b\xf5\xeb\xb5\x7f\xfa\xd7\xd5\xfe\x32\x4a\xc7\xf4\xd7\x6b\x7f\xfc\xaf\xab\xfd\x58\xdc\x84\x7f\xab\x72\xff\xc5\xbf\xae\xf2\xbf\xdb\x70\xff\xb2\xec\xfe\x40\x6b\x9c\x7f\x71\x7d\x05\xe5\x19\x5e\x59\x20\x23\x39\x0c\x47\x19\xfa\x0f\x39\x31\xd3\xf6\x5e\xb1\x01\xcc\x83\x6b\x43\xb9\x1c\x38\xe0\xfc\xdb\xf0\xe1\xf0\xe1\xf0\x71\xfe\x71\x18\x49\xdd\x1e\xf2\x49\x38\x36\xca\xe3\x24\x92\x51\x3c\xe5\x81\x28\x1a\xf8\xae\xa8\x4d\x96\x88\xbb\x58\x3e\xb0\xf1\xb9\xec\xba\xb4\xc7\x95\x98\x74\x83\xb3\x2c\xc0\x87\xe7\x01\x3e\x79\x40\x0d\xfa\x1d\xde\x70\xc0\x33\xab\xc1\x37\x58\xad\xf4\xca\xa8\x3d\x4a\xb4\x16\x2a\x2e\xc8\xfa\x88\x5f\xaf\x6f\x88\x33\x0c\xc5\x78\x10\x0b\x5d\xee\xf7\x17\xa1\x8a\x75\x63\xc0\xef\x1a\xd1\x10\x43\xf2\x66\x42\x5c\xe7\xa3\x70\x8b\x1e\xab\xf7\x83\xe5\xb2\x77\xe4\xe8\x06\x9c\xf7\x91\x1c\x18\x5d\x7a\x2b\x71\x83\x5b\xe0\x9a\x82\x7d\x7f\x92\xd8\xf7\x5d\x7b\x4f\x1d\x7e\xb2\x18\xe5\x4d\x85\x4f\xd0\x6b\xb9\xd2\xdb\xeb\x7c\x88\xb4\xf0\x1a\xa7\x57\x61\xdc\x30\x6b\x55\x28\x47\x0d\xf3\xc8\x6f\x6c\xc2\xc3\x71\x14\xf0\x71\x23\xd6\x91\xe2\x23\x61\x88\xbf\x8b\x12\xd5\xf0\x8d\xe9\x6b\xb5\xd8\xdc\x59\x52\x8b\x55\x9a\x91\x70\x00\x2f\x02\xb0\xd6\xe6\x69\xfd\x2a\xba\x95\x9b\x8e\xef\xad\x19\xf2\xc6\x1a\x21\xcf\x30\xae\xcf\x7f\xbb\x1c\x8f\xb5\xeb\xd9\xf4\x51\xaa\x9c\xcd\xe8\x72\x1b\x04\x68\xe0\xb0\xb5\x95\xe4\x37\xbf\x57\x15\x06\x3e\xa8\x6b\x07\x1f\xb9\x4d\x21\xb0\x20\xd2\x0d\xba\xf5\x0b\xe3\x67\x64\x74\x63\x1a\xb0\x0f\xdc\xe5\xf5\x5b\x29\xb5\xb5\x2e\x83\x0e\x75\xf9\xb6\x3d\xaa\xfc\x15\xf0\xc6\xb4\x3b\x1b\x31\xff\x76\x0d\xf7\x4c\x7c\x33\x17\xaa\x7c\x93\xbe\xdf\x71\x28\x58\x16\x1a\x6b\xa1\xda\x3e\x57\xed\x3c\x76\xb3\xcc\x4c\xe3\xd4\x84\x30\x43\x1d\x0c\xd2\x03\x95\x1c\x3e\x0b\x9c\xd3\x37\x70\x1f\x5c\xe2\x69\x2a\xab\x58\xc0\x1d\xb9\xd9\x86\xd3\x6d\xcc\xcc\x05\xb7\x64\x7b\x1b\xae\xb7\xc1\xde\x97\x7b\x41\x69\x6d\x4b\x02\x31\xbe\x10\x70\x4b\x06\xdb\x70\xa4\x31\x9f\x90\x74\x03\x63\xf4\x07\xdb\xb4\x1e\xf0\x5a\x81\xbe\xd6\x84\x63\x34\xd6\x00\x1c\xa7\x02\xff\x70\xfd\x10\xdc\xdd\x94\xd3\x91\x54\xb5\x88\x57\xb9\xfc\x90\xfc\xc6\x37\x8a\x0f\xfe\xd3\xd6\xd1\x68\x34\x16\x46\x7f\x6a\x4f\x06\xd9\xcb\x31\x3a\x72\xf3\xb8\x90\x89\xdf\xde\x6f\x4c\x75\x7b\xb7\x31\xf5\xdb\xbb\xf5\xe8\x13\x3f\xd2\x3a\x9a\x38\xe0\x74\xa7\xb7\x8d\x38\x1a\x87\x83\x86\x1a\xf9\x9c\x74\xa0\x61\xff\x73\xbb\x3b\xfb\xb4\x18\xa6\xb3\x92\x58\xad\x79\x1e\xcb\x5e\x94\x94\x14\x5f\x71\x39\xc8\xa2\x20\x2a\x46\xca\x58\x28\x3d\xe1\x92\x8f\x8a\x01\xbc\xaa\x97\x96\xfc\xa6\x50\xb8\x5e\x6e\x13\x49\xe1\xc5\x36\x5d\xa5\x36\x17\xf9\xf4\x76\xbc\xea\x28\xa6\xca\x62\xad\xaf\x97\x16\x90\x50\x8e\x43\x59\x72\xda\x2e\xb7\x68\xcd\x86\x63\x2d\xbe\x43\x8a\x59\x45\xa8\x88\x59\xa3\x1c\x43\x62\x34\x45\xab\x30\x56\x74\xc6\x2f\x35\x8f\x5c\x7d\xbb\xf9\x49\xed\x7b\xfd\x60\xdb\x8f\xda\xf7\x7a\xc4\xe1\xa7\x32\x37\xfd\x21\xc9\x50\x55\x9d\x34\x6f\x83\x4a\x1a\xa7\xca\x64\x4d\x9f\xbe\x6c\x03\x86\x1c\x06\xaf\x69\xcb\x19\x87\xfe\x03\x3f\x8a\x74\xac\x15\x9f\xb6\xf7\xdc\x8e\xdb\x69\xf3\xf1\xf4\x8a\xbb\x07\xed\x41\x18\xeb\x07\x41\x1c\x17\x00\xee\x24\x94\x6e\x60\x4c\x97\x2f\x81\x19\xcc\x2f\xdb\x20\x2d\x0e\x5c\xe3\xf8\x4c\xc4\xd1\x44\xb4\xf7\xdc\x87\x6e\x07\x4b\x96\x5f\x17\x85\x7f\xd4\x0a\x8b\xf1\xa4\x3d\xe0\x5a\x4c\xc3\xe0\x5a\x28\x2c\x58\x7d\x65\x8b\x3d\x09\xea\xd6\x84\x35\x1c\xde\x10\xe5\x06\x78\x06\xf4\xce\xfc\x91\xb4\x97\x67\x17\xbe\x57\xf9\x93\x5e\xca\x38\xcc\x4b\xd2\x37\x7f\xe9\xaf\x7a\x19\x94\x5e\x2e\xf5\xed\xfb\xc0\xd4\x7f\x56\xd7\x3d\xd2\x29\x65\xa6\x6a\x2e\x1b\x3f\x07\xe8\x78\x29\x44\xd4\x8e\x87\xd7\x39\x34\x54\xce\xd6\xf6\xb7\x5e\x54\x3e\xcb\xfc\x73\x38\x24\x3b\xe8\x7e\x33\x24\x65\x65\x6b\x65\x8a\x44\x38\xdb\x36\x93\xbd\x91\xf4\x4b\x08\xfc\x1c\x01\x1e\x15\x5e\x8f\xb5\x32\xfd\x16\x75\xe4\x41\x29\x5d\x4d\xfe\xd2\xc7\x2b\x63\x72\x05\xc6\xa6\xea\xff\x3d\x80\x3f\x02\x90\x11\xa8\x08\x74\x04\x22\x82\x24\x02\x1e\x41\x10\xb1\xaf\x92\x38\xa7\x3c\xbe\x76\x28\x44\xd1\xba\x94\x5c\x41\x44\xf2\xac\x5c\x69\xf2\xae\xfa\x1d\x54\xc8\xd6\x9f\x71\xc9\x0a\x8e\xa1\x72\x33\x1e\x5e\x0f\x43\x3e\x28\xbc\xe5\x2e\x3f\xf9\xa5\xc8\x3d\xbf\x2d\x2e\x92\x11\xa5\x3b\xd4\x38\x13\xf0\x5a\xad\xba\xb0\x2a\x9a\xc6\x9a\x6b\xe1\x80\xa6\xf0\x1f\xaf\x95\x2b\xf9\x4d\x38\xe2\x3a\x52\x6e\x12\x0b\x75\x34\x12\x52\x17\x97\x27\x9d\xaa\x70\x80\x6e\xbd\x66\x73\x25\xb6\x2b\x1e\x5f\x65\x81\x57\x9a\xae\x3e\xbe\xd6\x53\x6e\xa0\xd5\xf8\xad\xb8\x9b\xcf\x95\x3b\x11\x9a\xa7\x8f\xf1\x55\x38\xd4\xf8\xdc\x3d\x34\xeb\x73\xa2\x75\x24\xe7\x73\xe9\x6a\xae\x46\x42\xe3\x91\xef\x68\x26\xc7\x11\x1f\xcc\xe7\x44\xb9\x53\x85\xd7\x3b\x3f\xb3\xbc\x40\x28\x2a\x27\x57\x4a\x0c\x41\x31\xd3\x35\x20\xd9\x73\x41\x34\x1e\x19\x22\x09\x91\xcd\xa6\x72\xfd\x3b\xcb\x2e\x77\xf8\x23\xb0\x3f\x02\xfc\x91\xb8\xdc\xfe\x4c\x5c\xde\xcf\x37\x06\xbc\x74\x27\x42\x2f\xec\xd1\x17\x08\x9e\x79\x2b\x23\xfc\xf0\xb2\x56\x09\x58\xb1\xa6\x0b\x18\xf8\x9e\xbd\x8c\xf5\xb1\x87\x57\xb8\x06\x7b\xe6\xdf\x3d\x83\x22\x8c\xd2\xe4\xf9\x43\x7c\xd8\x5d\x80\x1f\xb1\x81\x84\x38\x62\x81\x84\x31\xbe\xec\x2c\xe0\x06\x1f\xda\x3b\x0b\xd8\x8e\xd8\x4d\x04\x93\x88\x6d\x47\x70\xb5\x8e\xa5\xee\xf9\x73\x6f\x12\x01\x7f\x81\x81\xf5\xaf\xbd\x71\x04\xfc\x8d\xf9\x1b\xfc\xf0\x14\xf0\x6d\xef\x23\x26\x94\xf3\xb9\x97\xa6\x71\xe3\x97\x9e\xe3\x40\x70\xe3\x9d\x01\xdf\xc5\x23\xde\xaf\x3c\x09\xc1\x1b\x83\xc5\x1f\x7b\x67\xe0\x4f\x30\x9f\xda\x73\x54\x55\xf0\xa3\x7f\xe4\xdd\xa7\xc5\xf0\x27\x42\x3d\x31\x7f\x9e\x63\x62\xb8\x37\x58\xe0\x1d\x16\xf0\xbf\x7a\x1d\xbc\xe1\x20\x6b\xcf\x68\x3d\xe5\xcf\x90\x66\xa4\x36\x27\x94\x77\xbc\x69\x04\xc1\xad\x21\x7e\xdf\xfb\x8a\x59\x5b\x6d\xad\x1f\xbc\x71\x84\x37\x1d\x44\xec\xde\x3f\x35\x3f\xe0\x2e\xfa\x85\xdc\xaf\x1d\x68\xa7\x39\x5c\x6f\x23\x96\x48\xb8\x8e\xd8\x3d\xbf\xc2\xae\xe0\x48\x38\xe6\x97\x3b\x36\x7f\x4e\xcc\x9f\x53\xf3\xe7\x8b\xf9\xf3\x0a\x53\xce\x1d\x61\x4b\x0e\x16\x70\x82\x0f\x3b\x0b\x38\xcd\x06\xf0\x79\xb4\xfe\x76\x86\x83\xf2\xed\x0c\xdf\xb2\xb1\xff\x8e\x0f\x8f\x16\xf0\x34\xc3\x7a\x19\x6d\xb8\x86\x91\x48\xa2\x51\x50\xbc\x89\xd8\xca\x1c\x87\xcb\x97\xaf\x4a\x7a\xaf\xd5\xdd\xfd\x6b\x95\xdf\x35\xcb\x54\x71\xc5\x53\xf9\x0a\x5a\x25\xcc\xa4\x22\x78\xfe\x89\xd2\x05\xbc\x8b\xd8\x33\x09\x1f\x22\xf6\x53\xc2\xab\x88\x7d\x88\xcc\x78\x9c\x45\xec\x95\x82\xd7\xeb\x89\xbc\xe7\x6f\x3c\x0d\x7e\x68\x1a\xfb\xd3\xb6\xf6\xe3\xda\x61\x51\xae\xdf\xc7\x6c\x87\x91\xcd\xc0\x88\x49\x89\x8e\xd7\x83\xdb\x44\x89\x32\x4f\x92\xf8\x31\x82\x33\x3b\x96\xcf\x24\x8b\x56\x66\x19\x86\x00\x22\x08\x0b\xea\xde\x7a\x1a\xf8\x99\x17\x01\xef\x7a\x09\xf0\x87\x9e\x4a\x89\x7d\xe2\x09\xf0\x3f\x7b\x1c\xfc\x13\x2f\x04\xff\xcc\xc3\x44\xe7\xaf\xd4\xa6\x44\xe7\xbe\x30\x6d\x95\xa6\xe4\x47\x83\xe8\xd8\x28\x36\x14\xbe\x46\xab\xd3\x2e\x3f\x04\x1f\xd3\x2e\xff\x8c\x98\x2f\x48\x48\x21\xdc\x94\x30\xf2\x67\x04\x33\x12\xdb\x84\x6f\x36\x15\xe7\xb3\x88\x0d\x25\xbc\x8c\x36\xdf\x3a\x32\x94\x6c\x46\x9e\x45\x1b\x0e\x8b\x3b\x89\x9c\xaa\x28\x10\x71\x2c\x06\x4e\xb6\xb6\xc6\x82\xa4\xbe\xdb\x6c\xb3\xa1\xf4\x25\x35\xba\x9c\x38\x99\x4e\xd5\x52\xb9\x9d\x25\x15\xf6\x65\x44\x9c\x2f\xf2\x5a\x46\x33\xd9\xd0\x77\x53\xe1\x35\x9c\x96\xa4\x0b\x33\x7b\xb0\x4f\xef\x48\x08\x36\x9d\xcb\x93\x3b\x07\xbe\x46\xc4\xbc\xc7\x97\x79\x16\x98\xfa\x87\x2c\x7f\xcc\xd2\x7b\xbb\x70\x0d\x25\xc4\x82\xbc\x52\xe8\x11\x86\x17\x91\xad\xc2\x66\x35\x38\x8a\xcc\xb7\x15\x9d\xe6\x47\x38\x60\x14\x3e\x47\xbf\x78\xaf\xca\xfb\x0d\x9c\x5f\x4b\x7b\x6f\xc0\xbf\x44\xd5\xeb\x26\x65\x9e\xe5\x79\x7d\x16\x3a\x9e\xde\xff\x97\x9e\xa8\xa1\xe5\x24\x80\xe9\x79\xab\xf4\x42\x5b\x8c\xf8\x46\x05\x01\x24\x4b\x88\xf9\x50\x2a\x8b\x66\x47\x76\x8f\x43\xc9\x0f\xfd\x39\x02\x3d\x9f\x0b\x1b\x84\x5e\xf9\x86\x89\xb6\xb2\x6f\x46\x11\xa2\x78\xf3\x5b\xc4\xbe\x44\xf0\xe3\x57\x7b\xe8\x53\xb4\x69\xc6\xd8\x0c\x5e\xa1\x99\x31\x37\x36\x77\x17\xce\x98\xb7\xd9\xf2\xf0\x7b\xb4\x3e\x7d\xe9\x8f\x08\xde\x46\x70\x4b\x3e\x45\xa5\x44\x64\xa8\xb0\xa1\x4c\xfc\x23\x62\xe4\xf7\x00\x05\x6b\x67\x65\x1a\x45\x9b\xcb\xd0\x98\xbe\xa6\xe9\xfd\x2c\x25\x62\xc7\x08\x9e\x5a\xda\xb1\xdf\x2d\x4a\x19\xb2\x9f\x0a\x54\xf8\x17\x29\xe5\xed\x90\x62\xa2\x33\xf3\x35\x31\xca\x29\xe6\x4e\xe4\xd0\xee\xe6\xb9\x96\xb1\xbe\x53\x92\x60\x5a\x8e\xc3\x4e\x9a\xcf\x4c\x40\xe0\x25\x98\xcf\x2c\xc1\x7c\x66\xca\xf4\x89\x06\xee\x25\x2e\x5f\xd0\x5e\xc2\x88\x60\x88\x68\x87\xf6\x49\x9a\x76\xbe\xd5\x05\xcd\x74\xab\x0b\x5d\xea\xa5\xef\xb8\xcd\x43\xdf\xea\x52\x48\x70\xd4\x7e\xaa\x55\x2b\xc4\x8a\x5e\xb9\x21\x2a\xb4\x1a\x6e\x39\xbd\x9b\x99\xab\x1a\x5b\x2f\xc2\xff\x66\x16\xf6\xd1\x2d\x13\x54\x98\xd0\x1e\x65\x48\xc0\x66\x6b\x37\x20\x48\x1e\xc7\x94\x42\x02\x1b\x98\x84\xab\x53\x4a\x8b\x10\xb6\xf1\x06\x14\xe0\x21\x7b\x02\x41\xc8\xc2\x04\xa2\x95\xc0\x1d\x3c\x99\x6b\x43\xc0\x9a\xcd\xad\x07\xe7\x7f\xc6\xb7\x7e\x74\xf1\xc0\x9e\xe1\x92\x78\x6d\x24\x6b\x49\xca\x98\xc4\x04\x61\x36\xd1\x74\x18\xb2\x55\x99\x32\x1f\x1f\xda\x6c\x95\xab\xf2\x5c\x72\xdd\x98\x44\xb1\x6e\x3c\xde\x98\xe6\x32\xdd\xeb\x8f\x42\xe2\x74\x5c\x23\x2f\xd7\x65\xd9\x1c\x8e\x23\xae\x6b\x39\x36\x79\x48\xba\x62\xf7\x37\x7b\x4f\x67\x39\x5d\x26\x0c\x43\xc6\xcd\x8a\xfc\x17\x69\xf8\x1b\xc3\x90\xc8\xd6\x41\xe7\x37\xf5\xdb\x41\xe7\xb7\xae\xd8\x35\xcf\x44\xb7\x39\xc5\x1f\x06\xb9\x68\xe1\x85\x7a\x71\x65\xd9\xe2\xc5\xad\x64\x01\x93\x10\x31\xb5\x9e\x2f\x82\xbf\x27\xda\x6c\x66\x41\xb3\xb2\x2b\x37\x58\x96\x6b\xc9\x2a\xb9\x96\x58\xb9\x66\x13\x7f\xa2\xeb\xaf\x2e\xdd\x12\x98\x11\x0e\x56\xfe\xd9\x3b\xd7\x61\x1c\xae\xbf\x61\x29\xb6\xfc\x64\xd5\x8c\x41\xc8\xc6\x21\xdc\x6c\x06\xff\x91\x83\x6f\x87\x4b\xb9\xb3\x53\x81\xbc\xbe\x8f\x44\xda\x47\x6b\xfb\x47\xae\x9c\x2d\x1a\xc5\x3f\x4e\x5c\x0a\x93\x90\x6d\x87\x70\x15\xfe\xe2\xad\x62\xd3\x90\x7d\x55\x30\x0a\x59\x9c\xc0\x2c\x64\x9f\xe1\x2e\x64\x27\xf0\x55\x2d\xf3\x79\xbe\x9c\x58\xaf\xaf\x75\xd5\xdb\x2b\x0a\xf2\x2b\x35\x06\x09\xe1\xa5\x8b\x04\x55\xd1\xb4\xdd\xee\xfe\xee\x81\x38\xf8\x8d\x88\x76\xf7\xf1\xc3\x8e\xb1\xd5\xd2\x6c\x09\x24\x39\xdc\x9d\xcf\xb7\x6e\x12\x22\x68\x9f\xb7\xbb\x1e\xa7\x2d\xb2\x6d\x7e\xb5\xb7\x13\x82\xc0\x45\x40\x4f\x60\x18\x55\xb5\x34\x5d\xa4\x6a\x4d\x52\xcf\x96\xb1\xdb\x3d\xe4\x7d\xa4\xc3\x53\x99\x56\x53\xba\xac\xe2\xf1\x21\x9f\xcf\x77\x1e\x33\xc6\x78\xb3\x99\x56\x9a\x41\xef\x1c\x3c\x7c\xb4\x27\xf6\xeb\xfe\xd6\x0a\xc6\xfd\xce\xe3\x87\x07\x39\x4c\x91\x73\xa3\x53\x82\x79\xf8\xf0\xe1\x81\x38\xa8\x3b\xd4\x2b\x68\xba\x9d\xdd\x83\x47\x39\xcc\xc1\x4a\x34\xdd\xdd\xce\xde\x41\x41\xcf\xc3\xd5\x88\xf6\x0f\x76\x4b\x44\x3f\x5a\x0d\xf4\x68\xb7\x7b\xf0\x28\x07\x7a\xbc\xb2\xba\x9d\xce\xe3\xc7\xfb\x3b\x39\x50\x91\xee\xa3\x82\x6a\x67\x77\xff\xd1\xc3\x12\x54\x77\x35\xae\x83\x9d\x83\xfd\xa2\x9b\xba\x3b\xab\x71\x3d\x7a\xb4\x6f\x3b\xb3\xa6\x42\x96\x05\x1e\x06\x14\xa3\xc0\xfb\xa6\x49\x62\x33\x26\x2e\x16\x30\x23\x37\x61\xe9\x4f\x10\x92\x01\x79\x93\xe5\x91\x1c\x26\x64\x8f\xc2\x38\x21\x4e\xdb\xa1\xa5\x97\x3b\xe5\x97\xf8\x9b\x52\xb8\xdd\x30\x55\x76\xca\x53\xe5\x3a\xfc\xf5\xcb\xe6\x32\x9d\x42\xa5\xb7\x0c\x63\x4a\x3f\x33\x67\x48\x07\xb4\xcb\x33\x5d\x6d\xab\x74\x2b\x86\x60\x78\x55\x16\x11\x8c\x53\x37\xbf\xed\x43\xa4\xf7\xcd\xcc\xc8\xad\x59\xaa\x85\x99\xe8\xda\x5e\x33\x73\x14\xb2\x49\x02\x27\x1b\x0c\x09\xf9\x1b\x31\x42\xbd\x85\x16\xc4\xe9\x8a\xb5\xab\x96\xec\xb3\xd2\xa5\xe5\x3f\x36\x37\xa7\x6f\xf3\x04\x99\x2e\x3c\x75\x96\xbb\xd5\x5b\xd5\xd7\x5e\xa9\xaf\xa1\x52\x8f\xc5\xc9\xb5\x85\x73\x0d\xc2\x30\xa4\xe6\x5d\xc7\x6e\x66\x66\xd0\x93\x70\x4d\xf6\x7c\x53\xee\x9b\x25\xa4\xce\x0c\x27\xe1\x5f\x95\xef\xda\xf2\x2d\x5b\x7e\x25\x4c\x3b\x85\x31\xfc\x72\xf1\x17\x6d\x2b\xbe\x07\x21\x19\x12\xdf\xa8\x83\x9d\xec\xff\xd4\x68\xa8\xf6\x28\xf6\x57\x45\xe1\x79\xb8\xc1\x84\x36\x4a\x72\x45\x3f\xfe\x16\xfe\xad\xeb\xe6\xf2\xdb\x0e\x4b\xb7\xb9\x61\x04\x95\x3d\x3c\x8e\xac\x14\x69\xbc\xb7\xa3\x7c\x6b\x5b\x36\x03\xeb\xa0\xdf\xc2\x3a\xa8\xa1\xe9\xfb\x8a\x39\xc3\x24\x31\xd4\x77\x21\xf0\xce\x60\xe0\x75\xc1\xf7\x3a\xa6\x11\x98\x8f\x61\x51\x51\x68\x26\x9a\x20\x66\xb3\xc4\x9f\x51\x4c\x0a\x63\x56\xb0\x05\x85\xa7\xab\x10\xcf\xc8\x77\xd4\xae\x97\x71\xbc\x11\x70\x95\x00\x2a\x3e\x16\x09\x37\x48\x3e\x2f\x1b\xc1\x76\x11\xc2\xa4\x38\x65\x24\xc6\x56\x2d\x12\x67\xeb\xd4\x56\xf5\x62\x61\x51\xa1\xc9\x7a\xb9\xce\x04\xc8\xa5\x44\xbb\x5b\xbf\xf2\x0c\x84\xbd\xbd\xe8\x4d\xb8\xfe\xf6\x1d\xcc\x66\x99\xb8\xdb\xf3\xb9\x31\x1a\xf2\x37\xc2\xbc\x11\x2e\x4f\xb3\x60\xa6\x09\x43\x6d\xba\xd0\x72\xfa\xc8\x02\x59\x7a\x55\x8f\xc0\x64\xa1\x62\x29\xcb\xa4\x40\x15\x3a\xcf\x32\x99\xa5\xf1\xe4\xee\x00\x78\x29\xbf\xa4\xc1\x16\x40\x92\xa6\xb7\x8c\x98\xb1\x55\x42\x96\x60\x1e\xd2\x04\xef\x78\x4d\x5c\xd1\xab\x53\x59\x23\x29\x82\x30\x4f\xa3\x89\xd4\x71\x9a\x11\x18\xd4\xc8\x32\xdf\xb3\x04\xa1\x29\x85\x79\x0b\x72\xa2\x0c\x3e\x8e\x44\x51\x78\x17\xb2\xef\xf0\x21\x5c\x7d\xc5\x4e\xfd\x76\xda\xb4\x12\x03\x70\x13\xc1\x4d\x44\xf3\xcb\x3f\x79\x2e\x8a\x35\x26\xe7\xd4\x98\x9c\x53\xbb\xa2\x97\xe7\xe1\x7a\x67\x9a\x92\xd0\xfa\x94\xba\x21\x6f\xc2\xd4\xb1\x74\x47\x3e\xd8\x11\x08\x28\x44\xf5\xa8\x37\xac\x3b\x4d\x7a\x6a\xbe\xd6\x16\xb7\x12\x9a\xa0\x40\x14\x51\xd4\xde\x5e\xad\x6a\x9f\x66\x39\x98\xbd\x2b\x08\x87\x20\x3b\xf1\x5b\xdc\x3e\x5a\x4d\x6d\x9a\x65\x17\x35\x0d\xd4\x2e\x5e\x50\x74\x16\xae\x71\x1f\xed\xa4\xde\xa9\x93\xd5\x76\xd5\x50\xc0\x28\x81\xb3\xd0\xde\xc3\xf2\x7a\x0d\x96\xc7\x10\x20\x92\x8f\xeb\x6a\xd9\x87\x11\x02\x7c\x55\xab\xaa\xf9\x88\xe2\xfa\x75\x48\x8e\x34\xb5\xe7\x7c\xaf\x8d\xd6\x8c\x52\xf3\x38\x34\xb6\xfd\xfe\x02\xbe\x56\xfa\xa7\xe2\x5f\x68\x2c\x4d\x7a\xc9\x66\xc4\x8f\xd0\xbb\x58\x4b\x7e\x1c\x0b\x22\xd2\x6c\x1a\x90\x42\xd9\xea\x35\xbc\x0e\x89\xc0\x8d\x39\x59\x8a\xc7\x47\x49\xf1\xd5\xe6\x89\x46\xf1\x80\xd9\x82\xe0\x38\x34\xbd\xfa\x7d\x83\xd3\xc3\xfa\xf2\xee\xc8\xd7\x30\x73\xe8\x1d\x87\x78\xa5\x54\x7a\xe5\xf2\xcf\x90\xe5\x9e\xac\x24\x76\xe0\x95\xb2\x9e\x2d\x21\x07\xf1\x91\x76\xe0\xb3\xfd\x99\x4c\x8d\x74\x1a\x94\xde\xc4\x9a\x2b\x5d\x06\x19\x86\x72\x24\xd4\x54\x85\x52\xa3\xd7\x0b\x5f\x66\x89\x8f\x63\xf4\x9b\xbd\xc8\xfc\x66\x5c\xca\x48\xa3\xe7\x37\x76\xe0\x04\xfd\x69\xb7\xe4\x3b\x38\x23\x21\x85\xe2\x3a\x52\x5f\x8e\xdf\x39\xf0\x55\xe1\x97\x23\x6d\x0b\x61\x32\x86\x1c\x3e\x16\xe4\x59\x9e\x34\x91\x52\x78\x96\x36\x04\x73\xb7\xd8\xea\x7e\x86\xb4\x4a\x85\x03\x2f\xa2\x35\xb8\x5e\xe3\x6d\x47\xf0\x72\xe5\xca\xc8\x24\xa9\xca\x7e\xed\xd9\x8b\x75\x8d\xed\x63\x7a\xf1\xc5\x06\xa3\xec\x52\xc0\xcb\x10\xd3\x9e\x58\xbb\xec\xf3\xfa\xdb\x20\x8b\x84\xe1\xc4\xda\xd2\x14\xde\xaf\x74\x1e\x08\x19\x44\x03\xf1\xe5\xf8\xf5\xd3\x68\x32\x8d\xa4\xc0\x5c\xfc\x0b\xf8\x62\x50\x5f\x52\x78\xb2\x61\x7d\xc7\x0d\x8c\x8f\xf6\xe0\x3f\xba\x09\x7f\x84\x69\xf8\x2d\xfb\x0f\x07\x76\x30\x23\xe3\xd6\x7f\x38\xb0\x8b\x4f\xcc\x81\x8e\x7d\xc5\x1c\x3c\xf6\x04\x9f\x42\xf6\x09\xde\x86\x2b\x59\xae\xec\x93\x4a\x98\x26\x8a\x92\xec\x96\xb3\x6d\x7a\xcf\xcb\xb7\x9c\xa1\x7e\x2a\xe7\x73\x0e\x89\x59\x78\xed\x0a\x92\xb8\x1c\x02\x14\xfc\x46\xd8\xa3\x6d\x17\x14\x73\x06\xcd\x4d\x2c\x12\x18\x21\x8a\xd3\xc7\xfc\x52\x2c\x70\x53\x17\x94\xd5\x4b\x7f\x0f\xab\x77\x3c\xe9\x4d\x2e\xa8\x5b\xf2\x36\xb4\x97\x93\xe8\xd4\x80\xfd\x23\x5c\xef\x06\xfc\xdd\x48\xc1\x95\x22\x6a\x12\xc2\xb7\x04\x6c\xf8\x00\xba\xef\x86\xe8\x0d\xec\x2e\x40\x0d\xd9\x9a\xeb\xf4\x97\x15\x04\xf4\xe3\xe9\xd4\x21\xc6\x69\x71\xa7\x81\x5a\x75\xd3\x87\xa0\xd4\xc3\x4f\x3b\x99\x47\x31\xf3\xec\x75\x6b\x37\x15\x48\xd7\x6f\x75\x51\xa3\x73\xfd\x97\xad\x6e\x76\x65\x81\x57\x2d\x25\x5d\x7e\xd2\xaa\x17\x55\x59\xb1\xe2\xc2\x10\x0a\x1a\x5b\xf7\x70\x01\x62\xb8\x5e\x39\xcc\xd7\x45\x55\xb9\x7a\x2f\xf3\xb1\x04\xe8\x63\x19\xe0\xf6\x72\x7d\xf1\xc3\x34\x5e\xd9\xf2\x67\x0d\x10\xb1\xe2\xa6\xe0\xe2\x72\x3e\x5d\x2c\x73\x16\x3c\xa9\x68\x8c\x0b\x0a\xc9\x70\xcd\xb0\x6e\xcd\x88\x18\xa2\x7a\xe7\x6e\x2f\x28\xf0\xe1\xfa\xe1\x4f\x86\x99\x77\x25\x18\xb2\x64\xcd\x9e\x4f\x75\x1e\x44\x25\xdf\x6c\xea\x99\x8d\x4a\x9e\xd9\xcc\x0d\x6b\xfa\x9b\x57\x5c\xb0\x8b\x5e\xc0\x08\x67\x24\xf5\xc2\x46\x7f\xe5\x85\x8d\xca\x5e\x58\xa3\xf7\x58\x0f\x7a\x34\x64\xcb\xd6\xc9\x93\x90\x02\x51\x11\xbb\xe7\xdf\x3c\x3d\x84\xe0\xb9\x47\x64\xc4\xee\x83\xe7\xde\x75\x02\xc1\x57\xdc\x7f\xfd\xe4\x5d\x27\x0b\xea\x06\xcf\xcd\x0b\x19\xb9\xc1\x57\xf3\x4e\x46\xae\xff\x69\x01\x6b\x38\x57\x99\xaf\x05\xf7\x62\x6b\x55\xd9\x0f\x5d\xbf\x4d\x28\x72\xf9\x37\x9a\xb9\xdd\x32\x56\xf6\x49\x30\x34\x9f\x82\xe7\x60\xf9\x36\x65\xda\x6e\x9a\x5a\x7a\x80\x6e\x77\xaf\x04\xa6\x0a\xf7\xf1\xff\xcb\xdd\x9b\x68\xb7\x8d\x63\x0d\x83\xaf\xa2\xf0\xd3\xa4\x81\xf2\x95\x22\xd9\x4e\x52\x61\x8a\xbf\x8e\xb3\x3a\x8e\xb3\x78\x89\xe3\xb8\x3a\x9f\x0f\x48\x82\x36\x6d\x8a\x54\x40\x50\xb2\x9c\xe8\x5d\xe6\x59\xe6\xc9\xe6\xe0\x02\x5c\x44\x91\xb2\xab\xba\xbf\x9e\xef\x4c\x9f\xae\x98\x22\x16\x62\xb9\xb8\x1b\xee\xb2\xb1\x5c\x0b\xd4\xc8\x8c\x1e\x57\xd6\x4e\xd5\x8c\xb0\x00\x04\x76\x71\xb2\x72\xb6\xcc\xd0\x8a\x13\xa2\xf3\x1a\xa2\x50\xd7\x1a\x13\xa5\x82\xdd\xb4\x1c\x86\x6a\xbe\x1d\x0c\xf9\xbf\xd0\x19\x17\x80\x7c\xf3\x9c\x7f\x58\xff\xd0\x22\x1f\x9a\xe5\x4d\x38\x19\x52\x98\x27\x64\x48\x29\x64\xaa\xfb\x4a\x66\x8b\x41\x9e\xf4\xe2\x9b\xa7\x05\xbc\x6f\x21\x7c\xf3\xe0\x63\x66\xc8\x5e\xd0\x0a\xad\x62\x41\xc1\x0d\xd6\x2a\x0b\x83\x02\x9c\xd3\x60\xad\x8c\xd1\x99\x11\x57\x2d\xd5\x38\xd3\x12\xad\x1b\x60\x2a\x41\x7c\x37\x23\xdd\x46\xf9\xf6\x75\x46\x90\x48\x22\x27\x8b\x86\x86\xb4\xb5\xee\x71\x46\xde\x49\x92\xe9\x24\x7b\xdf\x75\xc2\xb9\x28\x58\xc7\xd6\xe4\x23\xb9\xeb\xdb\x5c\xf5\x39\x23\x51\x88\x23\x8d\x42\x8c\xf0\x1e\x21\x8e\xa5\x77\x8f\x08\x5b\x87\x98\x7b\xe4\x3b\x0e\xca\x6f\x5b\x27\xc5\xc4\xdd\x63\x6e\xcf\xef\x5e\xcf\xfb\xce\xcc\xac\x2a\xc4\x68\x5b\xab\xff\x2c\x28\x4c\x97\x46\xb8\xaa\x69\xcf\xbb\xff\xd2\x78\x49\xcb\xea\x32\x07\x92\xbc\x43\xf2\x67\xfc\x5d\xb1\x68\x69\x50\xcc\xb8\x2e\x74\x2c\x57\xf4\xab\x15\x57\x4c\x0c\xa3\x50\x4f\x56\xea\x3f\x59\xf9\xab\xec\x63\x4e\xa2\x20\xef\x81\x52\xad\x2c\x07\xde\xbe\x61\xa7\x0b\x10\x66\x93\xba\x81\x73\x2b\x9a\xd2\xb7\x74\x4c\xaa\x22\xeb\x9f\x71\xf1\x24\x4c\x72\x18\xb8\x15\x4e\x8e\x1e\x99\x34\xaa\x14\x22\x15\x56\x4c\xec\xbd\x8c\xe4\x8f\xd6\xc2\x02\xef\xad\x9d\x04\xe0\x7d\xb3\x2d\xb0\xc0\xdb\xb4\xbb\x81\x42\x8d\xd6\x4f\x0b\xbc\xa7\xf6\x40\x21\xcd\x84\xaa\x4a\x32\xe9\x7b\x6f\x55\xbd\xbd\x8c\xa8\xe7\x6f\x54\xd5\x56\x4f\x9b\xaa\x81\x7e\xeb\x7e\xa6\xaa\xd9\x7d\xc2\xab\xaf\x66\x42\xae\x2d\xed\xe6\x62\x81\x1f\x7a\x8a\x40\xef\x06\x90\x16\x9f\xd0\x4b\x8c\x9f\x9e\x92\x69\x5e\xa2\x06\xaa\x5f\xea\xb1\xa6\xc5\x40\x75\x37\x54\x6b\x8e\x28\x8c\x15\x19\x39\x4f\xe0\x65\x48\x6e\x05\x85\xdd\x8c\xc2\x65\xf0\x77\x92\x73\x2a\x4e\x75\x64\x40\x58\x50\xcc\x55\x6e\x12\x74\x4e\x82\x75\x8c\x71\x8c\xe1\x2e\x29\x5c\x04\xe6\xb6\x76\x16\x38\x51\x0c\xf3\xa0\x5d\x79\xba\x5d\x55\x9e\xde\x04\xcd\x42\xa1\x89\x4b\x03\xd7\xc1\xfa\x74\xd5\x3b\x81\xb1\xb8\x39\x0a\x8c\xfd\xcc\x71\xd0\x9a\x07\xfb\x75\x53\x51\xc7\x5c\x20\x2d\xe0\x6c\xed\x4c\xc7\x5a\x8f\x84\x91\x8a\x0b\x39\xe0\x6a\x1d\xbf\x91\x4b\xe4\x4c\x49\xe4\xac\xcf\x0a\x1e\xac\xa7\x3e\xe9\xf5\xbb\x0f\x1f\xea\x07\x66\x98\x32\xfd\xcb\x57\x5c\x79\x18\x10\xd5\xd4\x43\xfd\x4e\x6e\xc0\x68\x46\x73\xab\x28\xd7\xa2\xe9\x87\x58\x54\x65\x7d\xb9\xa4\xd4\xa9\x26\x7f\xe9\x73\xd0\xb7\xd9\x2f\x97\x57\xa4\x25\xf3\x4a\xa1\x3d\x49\x4a\xdf\x63\xed\x7e\x89\x06\xff\x1e\xde\xfc\xf8\x46\x21\xe4\x61\x46\x13\x33\xfe\xac\xa6\x0a\xd2\xeb\xf3\x32\x20\x99\x62\x84\x9e\xaf\xae\x90\xe6\xcd\x30\x97\x4e\x51\x9a\x2c\x8d\x20\x76\x92\xaa\x2e\x6b\x2f\x84\xa4\xcf\x8a\x3c\x33\x2f\x03\x92\xf4\x7d\xbc\xf4\x6a\xff\xee\x02\xce\x83\xa6\x3c\xc8\x75\xae\xd8\xcc\xb7\x7a\xf3\x88\x46\x97\xc0\x90\x43\xf6\x90\x43\x0e\x03\x72\x4c\x14\x45\xfb\x63\x50\xdb\x72\xfd\xb0\xbc\x00\x1a\x3c\x66\xe4\x3c\xc0\x4c\x66\x6a\x11\xf4\x94\x59\xdf\xaf\x4e\xf8\xd7\xaf\x24\x5f\x8e\x50\x2d\x87\x28\x97\x23\x5c\x19\x60\xb0\x9c\x96\x67\x2f\x84\xb0\xcf\x20\xec\xbb\x10\xf6\xbd\xfc\x63\xa1\x5a\x96\x80\x2e\xd6\x8f\xa6\x34\x72\xdd\x53\xef\x5c\x72\x15\xc0\x32\x58\xa3\xad\x59\xf3\xf2\xa9\xd1\xfd\x8d\xe5\xbb\xc0\xe5\x53\x02\x7d\x43\xb6\xe6\xbb\x60\x32\x46\x75\x8d\xbf\x58\x10\x2f\x27\xe3\xf9\x38\x46\xd3\xc4\xc6\xd5\x90\x45\xf2\x1e\xa6\x60\xc0\xab\xa6\xeb\xde\x0b\x8b\xb9\x99\x75\xd0\x53\xdc\x0f\x1a\x35\xce\xe7\x9a\x37\x7b\x5e\x99\x2e\x46\x28\xa0\x85\x7e\x7d\x59\xdb\x56\xcf\xc1\xb4\xa0\xf0\x31\x68\x54\x66\x08\x62\x04\x1e\x49\x6b\xd1\x26\xf7\xf5\x5b\x5b\x27\x23\x2c\xd5\x1b\xbb\xad\xb8\xd1\xb5\x31\x18\xab\x65\x90\xe4\x69\xe0\x30\x09\xef\xee\x60\x26\x4f\x03\xf2\xd3\x63\x4a\xec\x11\x36\x61\x4e\x06\xbb\x01\xa9\xab\xcd\x66\x01\xa0\x53\x49\xc9\xad\x9b\xad\x19\xcd\x4c\xb6\xb4\xb1\x2c\x14\x62\x8a\x2f\xf7\x6e\x6d\x01\xde\x07\x3b\x06\xef\xb1\xbd\x23\xc1\x67\xb6\x04\xdf\xb7\x1f\x0c\x17\x46\x41\xbd\xa0\xf0\xa9\x15\xf1\x4e\xc9\xbb\x00\xac\xb7\xaf\x8f\x2d\x45\x8e\xe0\x22\xd0\x7c\xf1\xa1\x21\x7b\xb3\x80\xa0\xfa\xcc\xe2\x42\x24\xc2\x82\x1d\x45\x13\x15\x11\x3c\x09\xd6\xdc\x7e\x11\x61\x4c\x26\x6f\x03\xe7\x2c\x86\x57\xc1\x5a\x5b\xec\xdb\x00\x66\xe4\xc4\xfc\x73\xc5\x21\xa6\x30\x46\x46\x64\x9f\xaf\x14\xcc\x64\x91\x98\xf9\xed\x1a\x79\xf5\x55\x80\xfc\xee\x07\x01\x02\x76\x70\x28\xa4\x89\x2e\x99\x95\xf5\x32\xbd\xb2\x71\xd6\xe8\xcd\x5c\xdc\xf1\x14\xae\xcb\xc7\xe1\x98\x27\x99\xec\xf0\x1b\x8f\x73\x9f\xfb\xf5\x10\x01\x1f\xb9\x9c\x25\xe2\xba\xa3\x17\xed\x79\xc5\x33\xab\x26\x2d\x4d\x02\x38\xc3\x2b\x98\xa3\xbe\x77\x49\x37\xac\x8e\xb5\xa1\x7f\xec\xc3\x61\x40\x84\x02\x83\xda\x85\xb7\xf5\x25\xe6\xb9\x85\x8b\xe0\xe9\x24\x89\x53\xde\x09\x44\x32\xee\xb0\x49\x88\xb7\x28\x7d\x56\x77\xa8\xfe\xc0\xa2\x20\x11\x63\xee\x77\x32\x11\x99\x3a\xe8\xd7\xa5\x49\xec\x9b\xe6\x83\x58\x06\x60\x89\x4b\xed\xa2\xce\xde\x96\x5a\x1b\x87\x99\x76\x41\xc8\xe7\xf2\x56\x01\xca\x27\xc5\xfd\x9e\x24\xe4\x55\xa8\xb7\xe8\xe0\xfe\x3d\xaf\xef\xf0\xd6\x74\xf8\xc1\x80\x65\xc3\x66\xe6\xf5\xe3\x0d\xeb\xd1\x8a\xf6\x55\x9b\xd6\x92\x75\x8c\x0e\xa5\xf0\x65\x8d\xd4\xb8\xab\xef\x15\xf1\x70\xbc\xf8\x5b\x0c\xe0\x9c\x7c\x0c\x74\x76\x76\x9c\xc2\x41\x48\x12\x8c\x8f\x63\x84\xe1\x80\x68\x01\x80\xc2\xb5\xa4\xb4\x9a\x0a\x95\xc2\x8f\xd5\x81\x55\xaf\xbf\x45\x71\xfd\xfd\x60\x58\x5a\x78\xa1\xe6\x5d\xf3\x51\x79\xbe\x47\x73\xa5\x8d\xea\xcf\xcf\x41\x7b\x42\xf8\x1f\x41\xb3\x29\x1f\x0a\xcd\x7a\x48\xef\x03\x67\x1e\xc3\xd7\xc0\xf9\x10\x13\x6b\xc2\x45\x1a\xa6\xf2\xad\x82\x8d\xd7\x37\x13\x16\xfb\x3b\x51\x64\xc1\xfb\x80\xc2\xb7\x35\xa7\x74\xbf\x10\xc3\x63\xb7\xad\xd6\x3e\x11\xf0\xd3\x9b\xda\x35\x26\xaa\x16\x3b\xe6\x90\xfc\xa9\x01\x09\x6d\x4f\xbf\xaf\xc4\xeb\xce\x13\x41\x8e\x39\x30\x8e\x19\x35\x67\xe4\xa5\x04\x25\xc6\xc4\x94\xe2\x51\x60\x5a\x7d\x29\xdc\x3b\xb4\xd4\x71\x7e\xd9\x29\xd7\x8f\xd9\xbd\xb6\x8b\xd8\xfe\xc2\x85\x71\x00\x71\x7f\xd7\xe8\x48\xf9\x6a\xd3\x62\xdb\x6e\x91\x90\xb3\x4b\xa4\xe4\x47\x48\xc9\x8f\x91\x92\x1f\x42\xe2\x88\xfe\x2e\x84\xaa\x58\xe7\x62\xb9\x57\x36\xc8\xf5\x99\x20\x45\xff\xcb\x9d\xb9\x20\xb3\xb5\xb9\x20\xd9\xfa\x5c\x90\x5e\x5e\x6c\xf2\x59\xc2\x3c\x23\x49\x43\x76\x48\xb9\x94\x1d\x32\xc4\x1f\x5e\x96\xca\x64\x8c\x60\x85\x41\x2c\xf8\xe8\x5a\x12\x9d\x01\x18\x73\x1d\x7c\xaf\x25\x90\xe4\x74\xb4\x4b\x62\x58\x4a\x22\xc9\xa9\xb6\x70\xcf\x5c\x25\xbe\x6c\x2f\x80\xb9\xce\xa1\x20\x56\x90\x78\x59\x6a\x51\xf0\x56\xf7\xa2\xfd\x72\x7f\x8f\xe8\xbd\xbd\xb2\xe3\x3e\x1b\x59\x96\x2d\xfa\xee\x15\xe0\x56\x7f\x0e\x0c\xc3\x2e\xfa\xee\x35\x1d\xa9\x7f\xed\x5d\x85\xda\xaf\xf3\x90\x37\x0b\xaa\xd6\x03\x89\x53\x57\x92\xcc\xa5\xc0\xdc\x66\x87\xd6\x52\xc9\x50\x1a\x11\x34\x8e\xe0\x93\x5a\xb7\xca\x20\x2e\x03\x98\x87\x44\xd1\x48\x35\x88\xbf\xfe\xc1\xad\xc6\x89\xaa\x7e\x86\xb0\x9b\xd4\x6d\xba\x4c\x25\xb6\xbf\x5c\xa9\x26\x9b\xef\x11\x61\x4a\x50\x2d\xeb\x1a\xe7\x8b\xd0\x6d\x64\xd2\x74\x8f\xcc\xc6\x9c\x8c\xde\x94\xf6\xdd\xd1\x05\x11\x50\x3f\xe0\xd4\x5c\x18\x99\x24\x83\x16\xe6\x6b\x51\xc0\x61\xd1\xd2\xe2\xe1\x9c\x48\xf8\x98\x68\xb5\xcf\x69\x02\xb2\xef\xed\xc2\x8c\x70\x57\xbb\x7e\x15\x0b\x93\x98\x85\x41\xd0\xeb\xb9\xf3\x1e\xba\x9a\x1b\xd3\x93\x05\x85\xc0\x5d\x87\x7d\xe3\x3f\x9c\x41\xc9\x9b\x2e\xa3\xe3\x8e\x50\x88\xa8\x37\xd4\x5e\xcf\x6a\xfa\xae\xbb\xde\x94\xa9\xda\x9b\xac\xf7\x26\xcd\x5d\xba\xe8\xb3\x6a\xbf\x18\x7c\x34\x51\xac\xa6\xc4\x25\x4e\x5b\x11\xd3\x3b\x49\xe6\xc4\xc5\xf9\xc3\xa9\x56\x36\xba\xcd\xd7\xfa\x83\x3f\x44\x6e\x78\x78\x8e\x37\x49\x8d\xbe\x90\x6a\x7c\xbc\xef\x56\x47\x58\xbc\xea\xbb\x05\xa2\xe6\x15\x09\xdb\x78\x05\xe6\x56\x84\xaa\x28\x67\xae\xf2\x2a\x06\xc8\xf4\x65\x1a\x2f\x41\xff\x90\xfc\x99\xf5\xf1\xde\xac\xcf\xbe\x17\x00\x8b\x91\x60\xdc\xfc\x8b\xb9\x45\x86\x0b\x9e\xc3\x96\x9a\xb2\xbe\xbe\x3f\xf3\x74\xf3\x45\xdd\x61\xb2\xda\xcf\xc3\x87\xc5\x63\xde\x69\x82\x9d\x86\x4e\x62\x92\xb7\xba\x28\x8c\xeb\xcf\x30\x60\xd5\x8f\xe1\x66\x24\x68\x4c\x9d\x48\x14\x14\xf1\x21\xcb\x1f\x3c\x18\xf2\xad\x3f\xe2\xd1\x8c\xa4\x2e\x88\xde\x36\x30\x25\x79\x44\x2e\xc4\x1b\x43\xf3\x1b\xf3\xef\xe7\x4b\x95\x8b\x52\x85\x0b\xe5\x21\xf9\x93\x9b\x59\x14\x4a\x40\x0a\xbe\xdb\xce\xbd\x44\xae\xb6\xc9\x40\x45\x69\xd3\x96\x17\x46\x1a\xab\x34\x53\x27\x4f\x96\x7d\x57\xdb\x59\xb9\xc0\x37\x14\x6f\x1d\xb8\xc0\x7b\x08\x83\xfa\x94\x74\x5b\x3f\xaf\x33\xd6\xc4\x68\x69\xa4\x6a\x8e\x5d\xe7\x3d\x5c\xb6\x93\x40\x23\xca\xe6\x23\xf9\x2a\x74\x26\xe7\xc6\xb8\x08\xa6\xd2\x83\x0b\xa2\x53\x99\x3f\x7c\x78\x41\xb8\xe1\x52\x50\xb7\xb6\x66\x51\xf6\x24\xe0\xad\xb1\x59\x97\x0b\xb7\x49\xe4\xd6\x4c\x0f\x2d\x14\x8f\x7a\x90\x63\xf4\x89\xe3\xda\x35\xaa\xeb\x92\x53\x94\x57\x26\x2e\xcc\xc8\x47\x09\x43\x90\x14\xde\x9a\x7c\xdd\x99\x83\x96\xe6\x20\x1c\x9d\x68\x2e\x03\x49\x1f\x6d\xfe\x1a\x50\x3c\xbe\x33\x72\x98\xc0\x8c\x4c\x5d\x10\x5a\xc9\xdb\xdc\x5f\x86\xfd\x69\xcd\x7d\xec\x7c\x15\xea\xeb\x99\x13\x3f\xca\x14\xcd\x79\x24\xc1\xa4\x12\xca\x33\x49\x5f\xba\x84\x1b\xbe\x92\x4b\x88\x39\xe9\x3f\xd6\xaa\x64\xc0\xdc\x86\x37\x12\x39\xd9\xd2\xcc\x62\x34\x7c\xb4\xf5\x1b\xc9\x36\xe4\x06\x89\x7b\x82\x3e\x8a\xa9\x3d\x58\x50\x98\xad\x43\x56\x39\x72\xc8\x85\xf5\x07\xbc\xcf\xfa\xee\xaf\x5f\x75\x84\x90\x1f\x63\x06\xcb\xf7\xdc\x68\x7b\xa4\x9e\x73\xaf\x8d\x4c\x71\xaa\xb1\xe3\x81\x70\x38\x48\x67\x97\x48\x85\xf5\x99\xa2\xf1\x3c\x4a\xb9\xc6\x4a\x5f\x51\xb2\x2c\x3a\x37\x37\xa4\x20\x15\x4f\xbb\xa0\x30\x77\x9b\xf4\x8f\x27\x24\xa6\xfd\xab\x24\x8c\x91\x3c\xc0\x4d\x1b\x4c\x58\xb9\x72\x5c\xfd\x15\x8a\xbc\xeb\xdd\x1f\xc5\xf6\xdc\x25\x73\x32\x73\xf5\x2e\xe8\xbd\x35\x18\xf4\xba\x05\x83\x2e\x77\x46\x0b\x4d\x76\x03\x4c\xe5\xd3\xd9\xe8\x6f\x3e\xfe\x6d\xcc\xc9\x8c\xdc\x18\x1a\xf5\x1b\x19\xf6\x50\x13\xb1\x73\xcf\x31\x0f\xf2\x21\x0f\xed\x39\xb9\xd6\x98\x7e\x46\x2e\xdc\xc2\xdb\xf9\xc8\x75\x5e\xc1\xb1\xdb\x78\xc5\x94\x47\x1c\x2b\xf8\x9e\xa7\x65\xa8\x30\x5e\xa5\xa4\xbc\x42\x49\x35\xc1\xbe\xb4\xa5\x62\x01\xee\x4b\x55\x6b\xd1\x44\xe6\x24\xd4\x43\xdd\x27\x1c\x7e\xb2\x73\xd5\x99\x5a\x7e\xde\x67\xe7\xe0\x4d\xed\x5d\xc2\xfb\x1e\xc6\x6d\x41\x13\x32\x25\x24\xb9\x91\x7d\xba\x58\xe6\x93\xb0\xec\xf9\xba\x2e\xb3\xa2\xc7\x9c\x55\xca\xa8\x9a\xcc\xb4\xd6\x5f\x25\x69\x1d\x36\x77\xc7\x7a\x7a\x25\x0f\xb4\x5d\xab\xc1\xb6\x54\x0d\xed\xb7\x5b\xad\xf7\xb8\xde\xd3\xeb\x7a\x4f\x5b\xf5\x9e\xf6\xeb\x35\x0a\x23\x75\xe6\xe4\x93\x51\x53\x65\x98\x45\x74\x9f\x30\xf8\xe9\x46\xb6\x87\x01\x46\xce\xe9\xe8\xd4\x9e\x91\x2e\xc7\xc8\x6d\xfa\x05\xeb\xbb\x91\x8d\x88\x7b\x38\xd0\x09\x89\x8e\x5c\xd8\x71\x75\x75\x28\xf1\x7c\xa3\x24\xf1\x19\xf5\x96\x6a\x85\x16\xe0\x66\x14\x52\xf5\x19\x6f\x4f\x5f\x4d\x55\x67\xbb\x12\x5b\x8e\x23\xc7\x97\x2b\xa0\x5e\xbb\xc6\x6e\xe2\xac\x0d\x90\xd1\x2f\x10\xbc\x2f\xfa\xd6\xe2\xca\x5d\xe7\xd0\xe6\xbd\xb4\x05\x78\xef\x55\xfd\xcf\xb6\x04\x6f\xcb\xe6\xaa\xd1\x4b\xd7\xf9\x22\xe0\xdc\x75\x1e\xf5\xff\xfb\x11\xec\xb9\xce\x0b\x41\x86\x8f\x06\x14\xf6\xef\x09\xef\x95\x18\x7b\x5b\x68\xe8\xc7\xfa\xdd\x11\xb7\xf5\xce\xbd\xb0\x11\xc1\x1e\x64\x20\x31\x14\x42\x75\x8b\xea\x00\xa3\x3d\xb5\x71\xdd\x13\x09\x6a\x6a\x16\x4a\xc4\xde\x17\xdb\xb2\x16\x90\xef\x06\xef\xbb\x2f\xd4\x3a\x16\xce\xdb\x88\x75\xcd\x01\x7a\x6d\x0e\x50\x2e\x25\x15\x07\xe8\xde\xa0\x55\x1e\x0b\xf6\x7c\x65\x7c\xd9\xdd\xc3\xcb\x81\xa5\x3d\x35\xb8\x9b\x2c\x14\x28\x65\x94\x9a\xb9\x50\xda\x08\xb9\xf9\x10\xd1\x3f\x7d\xcd\x31\x72\xf7\x56\x8f\x47\xd3\xf8\x8d\xd3\xbb\xe9\xae\x19\x27\xb9\xb7\x36\x22\xc5\xcc\x88\x0a\x19\x5d\x60\x30\xa6\x16\x48\x5d\x50\xf8\xb8\x5e\xa2\xcf\x53\x72\xe6\x96\x4e\xbb\x6e\x8b\xba\x37\x0f\xc1\xe4\x32\x14\xdc\xd9\x1e\x4a\xee\xac\x8b\xa2\xbb\xbb\x03\x81\xfa\xf5\x0e\x5c\x27\xdb\xb0\xfe\xeb\x51\xae\x5a\x4b\x1d\xee\x12\xb7\xc8\xb1\xb7\xaa\x76\xc4\xc9\x2b\x64\xfb\xce\xc8\x5b\xc5\x22\x0d\x70\xa3\x23\xbc\x35\xf2\x1b\x52\x77\x46\xb5\xf8\x50\xb1\x13\xa1\x95\x39\x22\x0f\x6f\xcf\x7e\x65\xf2\x8c\x62\x94\x47\x64\x48\x5a\x37\x3c\x5c\x80\x2b\xa9\x49\x47\x8a\x26\x9c\x6d\x79\x72\x4a\x4c\xfc\x2e\x81\x0b\xad\x62\xd5\xb0\x96\x69\x27\x65\x6b\x81\x8c\x9a\xab\xa0\xec\x45\x70\xc7\x47\xb1\xd4\x95\x70\x19\x90\x56\x44\xa5\x78\x0c\x44\x55\xc8\x26\x55\x9c\x97\xce\x89\x87\x74\x5b\xd4\x09\xcf\x39\x89\x12\x60\xab\xd1\x33\x43\x7c\xbb\x02\x29\x71\x46\x22\x35\x0b\x0c\x8b\x45\x9f\xaf\x6c\x8b\xaf\x46\xb0\x67\x47\xe0\x32\xdb\xef\xbb\xcb\x18\x42\x2d\xcc\xd4\x99\x91\xd8\x05\x09\x8c\x42\xd7\x99\x11\xa9\x9e\x93\x95\x9e\x96\xc3\x49\xc8\x3e\xbb\x1c\x05\x76\x88\x51\x25\xf0\x47\x98\xd8\x1e\xb0\xae\xdd\x55\x1f\x9a\x2a\xc8\x45\xda\x8c\x65\x33\xb2\x6f\x36\xf0\x1b\x07\x91\xa9\xd3\x71\x10\xa0\x6c\x48\xed\xb2\xec\x6b\x5e\xf6\xc6\x94\x41\x59\x96\x65\x58\xf6\x21\x20\xbc\x4a\xad\x2b\x87\x59\xe8\x23\x27\xd4\x91\x4b\xc9\x3e\x91\xf0\x13\x13\xd7\xa2\x71\x6e\x19\xf1\xec\xe9\xda\x16\x47\x0d\x2d\x7e\x5f\xdb\xe2\xb8\xa1\xc5\xb0\x7e\x3a\xdc\x93\xfa\xe9\xd8\xc6\xa5\x1f\x3b\x33\xe2\xa1\x1d\x14\x24\x6a\xf1\xc7\x78\x56\xc6\x7d\x17\x2e\x9d\x71\xdf\x83\x89\x53\xae\x80\xd4\x2b\x70\x49\x61\xec\xa4\x64\x46\x3e\xba\xd0\xed\xbb\xd7\xb8\x4e\x13\xc7\x1f\x55\x58\x21\x1c\xdf\x98\xc2\xe4\x3b\xa5\xf6\xa4\xbe\x91\xb8\x45\x27\xc8\x4c\x4f\x0a\xb4\x3d\x71\x6e\xc8\xb1\x0b\x2e\xda\x3b\x32\x05\x0a\x97\xce\xa4\x9a\x46\x5c\xcf\x83\xd9\x53\x67\x82\x33\x29\x07\xc6\xf3\x81\x15\xd8\x71\xea\xdc\x90\xfd\xb2\xb3\x50\x75\x36\x5d\xed\x6c\xc7\x9e\xd6\xba\xca\x6a\x5d\x3d\xab\x63\x99\x37\xf5\x75\x1c\x6a\x34\x73\x51\x55\xad\x96\x10\x8b\x46\x65\x17\x20\xfa\xec\x35\x1d\xcd\xc8\xb7\xe2\x87\x3d\x23\x5f\x8a\x1f\xe0\xfd\xd0\x38\xfb\x6b\x40\x1e\x0c\x2b\xd8\xf8\x12\x91\xd7\x85\xb3\xe5\x38\x0e\xb9\x70\xce\x49\xa0\x86\xa6\x08\xf0\xc3\x87\x17\x7d\x77\x84\x28\xea\xa3\x12\x5c\xbe\x0a\x72\x81\x04\x98\x52\x7b\x9c\xac\x0e\xe4\x42\x7d\xe3\xb2\x38\x14\x5f\x03\x72\xa9\xe6\x9d\x24\xc0\x32\xb8\xe2\xe4\xd2\x28\x76\x16\x14\x4e\x5d\x67\x1e\xc3\x3b\xb7\xaa\xc4\xc6\x78\x84\xaf\xd8\xfc\x53\xf0\x95\xf3\x6b\x0b\x4e\x5d\x0a\x9f\xda\x84\xd5\x18\x78\x7b\xa6\xe4\x3c\x38\xe1\xb2\x15\x4b\x1e\x9a\xb0\xd5\x96\x65\x60\x32\x21\xf3\x51\x51\x35\xef\xa9\x36\x59\xef\xc6\xe6\xc5\x3c\xf5\x0c\xa5\x9a\xe1\x1b\x4e\xb4\x6f\x01\x53\xe5\x94\xc2\x3b\x97\x14\xe6\x65\x87\x6b\x39\x2b\x93\xd8\xaa\xea\x52\x83\xc1\x35\x5c\x63\x04\xf2\x45\xac\x35\xf8\x4e\xb5\xd1\xff\x82\xc2\xad\xeb\xcc\xc8\x17\xad\xb8\x33\xba\xe7\x77\xaf\xac\xef\x14\x3d\x7b\x5e\xb9\xce\x75\x0c\x6f\x5b\xc9\xed\x7b\x49\xe2\x47\x28\x94\xbf\x71\xef\xef\xfa\x27\x36\xe2\xba\xb3\x89\xb6\x1d\xe0\x7d\xf7\x33\xba\xff\x97\x35\xb9\xf6\xb5\x02\xe1\x88\xdc\xab\xef\xa0\x75\x93\xab\xb4\xec\x8d\x46\x21\x33\xf2\xd6\x85\x18\x33\x96\x3f\xe1\xdb\x26\x54\xc8\x87\xf6\xbb\x03\x46\x66\xe4\xc0\x48\x61\x7d\xf6\x75\x41\xe1\x8b\xdb\x7e\x31\x32\x0b\x61\x73\x5b\x7f\xa3\x68\x06\x4f\x06\xb8\x7f\x2f\xd6\xb7\x1b\xf2\x2d\x3d\x2e\xbc\x37\x5a\x5f\xf7\xc9\xa0\xec\x5e\x55\xff\xdc\xaa\x0a\xaf\x8d\x7f\xf2\xff\x97\x4c\xf9\x14\xde\xbb\xed\xf7\xd8\x83\x3f\xe2\xd1\x9c\xbc\x77\x21\xfe\x3f\xff\x67\x08\xbb\x44\xa8\x6d\x18\x3e\x8c\x47\xbb\x44\x82\xa0\xb6\xa4\xb6\x12\x7f\xbe\xae\xd1\x31\xbd\xd7\x42\x28\xaa\xa5\xe1\xdb\x9a\x6f\xed\x92\x19\xf9\xea\x42\xdc\x1b\x73\x22\x29\x1c\xa1\xf6\x48\x83\x55\x9c\xb6\xf7\xff\x4d\xf5\x6f\x0d\x30\xd7\x86\xde\x44\x91\xde\xbd\xe7\x05\xec\x0e\xf9\x16\x36\x92\xe9\xfd\x20\xd7\xdd\x52\x10\x28\xee\xe9\xe2\xce\x53\x67\x46\x5e\x08\x18\x60\x9e\xa7\xd4\x91\x0c\x98\x7a\x75\x12\xc0\xa9\x0b\x59\x4a\xc1\x5b\x33\xb5\x73\x09\x44\x61\xd8\xd6\xeb\xb8\x49\x96\x5e\x12\x7d\x85\x2a\x16\x94\xc2\x9f\xdf\x73\xaf\x5f\xb5\x2b\x49\x7a\xcf\xd8\x29\x61\xda\xbe\x2b\xda\x3a\xe2\xf3\xa7\x23\x63\x1e\xa1\x8d\xa1\x21\x58\xed\x3b\xae\xdf\x6b\x17\x29\x4b\x74\x9a\x92\xaf\x68\x7e\x7a\x4e\x2c\x73\xe7\x92\x5a\x30\x23\x5e\x0a\xdf\x32\x20\xc2\x11\xb4\xef\x5e\xeb\x0b\xb0\xd2\xbf\x88\xa5\x44\x60\xc2\x4d\xf5\x3a\xf7\x4b\xc2\x97\xec\x54\xbf\xd4\xa9\x45\x30\x3a\xcf\xa9\xab\xde\x9f\x98\xf7\xc9\x78\xcc\x63\x99\xbf\x3d\x36\xf7\x7b\xbe\xa5\x73\x8b\xbf\x72\x75\xbe\xf1\x53\xbc\xac\xf2\x4d\x3a\x1f\xf4\x4f\x4d\xc1\x62\x93\x49\x14\xea\xf0\x54\x8f\xae\xd2\x04\x53\x45\xbe\xae\x69\x0c\xdf\x06\x64\x4e\xc2\x14\x57\xe4\x56\x6b\x75\xdd\xd6\xf5\x66\xc7\x8a\x93\x38\x06\x86\x9c\xd9\x09\x4c\x6d\x01\xa9\x1d\xf7\x53\x60\xa7\x36\xa6\xa7\x77\x7d\x1b\xf3\xba\xb8\x07\x36\x66\xad\xdf\x53\x92\xf0\x82\x42\xda\x0a\x1e\x41\x48\x10\x82\x37\x98\xf6\x8d\xa3\x10\xa5\x4e\xa3\xf0\x20\x45\x38\x26\x74\x01\x7e\x6a\xdc\x7d\x66\x16\x3c\x19\x6c\xff\xce\x1f\xe3\xa2\xf8\x16\x60\x38\x04\xfc\x71\x69\xc1\xd6\x13\xf3\x3c\xb6\x34\x66\x57\x5b\x62\xe1\x41\xf9\x4e\x61\xba\xfe\x70\x3d\x5d\xf1\x82\xc3\x97\x15\x87\xb6\x5f\xbf\x9e\x2e\xd4\xb0\x9f\xf6\xee\x63\xf7\xfa\x9f\xc8\x99\x67\x96\xaf\x9b\xb6\x50\x3f\xc6\x48\x4c\x47\x43\x7b\x50\x04\x1b\xbe\xcb\x3a\x77\x6b\x58\x1b\xe6\xe3\x67\x1b\xb2\x36\xd2\x67\x83\xfc\x55\x31\xd8\xe1\x66\xf1\xae\x18\xf0\xf0\xf1\x30\x7f\x57\xd0\x86\xe1\xef\xc5\xbb\x82\x3e\x6c\x0e\x37\xf3\x77\x05\x8d\xd8\xdc\xde\xca\xdf\x15\x74\x62\xf3\x69\xf1\xae\x12\x66\x61\xb0\xbd\x21\xeb\x2b\xb3\xb5\xb5\xbd\x81\x76\x12\xe3\xf4\x6e\x2e\xe5\xb2\xb5\x8e\xa7\x56\x6f\x63\x46\xba\x78\x56\xe8\x86\x62\x7d\x27\x69\x2b\x99\xad\x2f\xed\x56\x1d\x02\xf4\x6e\x6c\x3e\xb3\x37\x7f\xaf\xc3\x42\x9d\xe6\x6e\x0d\xea\x44\x77\x58\x0f\x8c\x31\xc8\x97\xb6\x1e\xe7\xa2\x1e\xd3\x62\x50\x8f\x5f\x31\x5c\x5d\xc4\x95\x15\x44\xfa\x7a\x91\xde\x79\x87\x30\x23\x13\xbd\x3a\x90\x39\x09\x33\x86\x98\xc3\xcd\x3f\x9c\xec\xd7\xaf\x63\xf4\x1b\x2b\x2f\x45\x7f\xb2\xaf\xb6\x04\x36\xb1\x05\xb8\x5b\x76\xbc\x30\x4c\x5c\xc8\x48\xb6\x31\xa4\x20\x1d\xd9\x43\x86\x75\x96\xb6\xdf\x46\xcd\xc8\xd8\x7c\x0f\xcf\xa7\x30\x57\x45\xf3\x54\xdb\x16\xb9\xac\x59\x0b\xc1\x26\x0b\x0a\x37\xa6\xd2\x3c\x6d\xa8\x44\x12\xdc\xed\x4d\xfa\x68\xeb\x97\x12\xf9\xae\x5b\xce\x14\x70\x47\x3c\x6f\x3b\xf3\xd2\x09\x90\x34\x7b\x8c\x48\xba\x31\x5c\xf1\x36\xb8\x4c\x41\x57\x20\xd2\xb9\x49\xd5\x43\xc8\xc8\xd6\x6f\xb2\xb7\x49\x1b\x42\x50\x17\xb5\xe7\xaa\x6a\x21\x35\x2e\x03\x06\xef\xcd\xc8\x34\x45\x0c\x5f\x83\x10\x53\x32\x2c\x4a\x9e\xd4\x4a\x36\x8b\x92\xa7\xb5\x92\xad\xa2\xe4\xf7\x5a\xc9\x76\x51\xf2\xac\x56\xf2\xb8\x28\x29\x01\xcb\x14\x3d\x51\x45\x35\x08\x43\xf2\xbd\xb3\x86\x7c\x1f\xa3\x71\xd6\x1f\x83\x51\x6c\x0f\xfe\x38\x46\xa6\x6d\x24\x90\x67\x3b\x5a\xd3\xaa\x7e\x66\xe7\x64\x47\x2d\x41\x09\xa6\x3a\xaa\xc7\x1a\xd6\xe5\x28\x05\xcc\x76\x69\x80\xec\xb3\x9b\x3f\x7d\x28\x99\xed\xd7\xe9\x3a\x37\x1f\x45\x86\x7e\x8b\x37\x9e\xf0\xed\xdf\x04\x86\x81\x92\x1b\x4a\xd4\x3b\x6b\xfd\xe8\x0d\x79\xad\x3e\xfa\xa5\xf8\xd4\x8f\xe2\x49\x14\x03\x79\x51\x7e\xfe\xaa\x0d\xdf\x23\x59\x35\xf4\x8a\xa4\x8a\xfb\x3b\xd6\xed\xd5\x92\x9c\xe9\xc7\x9e\xa4\x8f\x9e\xf0\x6d\x04\xf2\x97\x69\xeb\x75\x72\x8a\x14\x5a\x42\xa6\x78\x23\xd5\x32\x08\x09\x47\x4e\x43\x3d\xf4\xd4\xe4\x32\x8a\xd6\x0b\x57\x69\x61\xf4\x7b\x41\x32\x28\xaf\xf8\xc4\x73\x5e\x56\x96\x15\xbf\x50\x09\xa6\x15\xa7\x74\xc4\xd1\x9d\xe2\x7c\xcd\x9e\xce\xc9\xcb\x14\xaf\xc2\xae\x53\xb4\x0c\x3d\x4e\xb1\x98\xc2\x00\xad\xad\x9b\x5a\xd6\xb3\x42\x15\x87\x22\x27\x20\xdb\x76\xbd\x73\xd3\x2b\x94\x9b\x81\x3f\x71\x33\xcc\x93\x30\x55\x06\x85\x02\x6b\xeb\xaf\x77\x33\xa8\x34\xdf\xbc\x6f\xf3\xc1\x52\xb3\x61\xd9\xec\x3c\x85\xe1\x50\x87\x38\x58\x46\x1f\x58\xb4\x59\x29\x19\x2c\x95\x0c\x2a\x25\xb5\xee\x2a\x25\x5b\x4b\x25\x5b\x95\x92\xed\xa5\x92\xed\x4a\xc9\xe3\xa5\x92\xc7\x95\x92\x27\x4b\x25\x4f\x2a\x25\x4f\x97\x4a\x9e\x56\x4a\x7e\x5f\x2a\xf9\xbd\x52\xf2\x6c\xa9\xe4\x99\x2e\xa9\x07\xe1\xc7\x09\x19\x3f\xc8\x05\x85\xfd\x76\x78\x47\xa6\x3a\x62\x25\xbf\xf7\x93\x5d\xd9\x02\xd8\xb5\x9d\x97\x28\xbc\x7e\xa3\x09\x58\x0c\x4c\xa0\x82\xf7\x56\x3d\xee\x68\x3d\xcb\x47\x14\x92\x76\xd3\xb5\xd1\x1c\x8f\xed\x43\x34\x3f\x60\x27\xea\x21\xa6\x30\xb5\xe7\x64\x3f\x05\xf4\x06\x46\xeb\x3b\x92\xa6\xf0\x74\x93\x3f\x46\x08\xe7\x14\x52\x55\x11\x59\x7f\xcb\x82\x8c\x11\x8c\x23\x45\xa9\xe2\xbf\x0f\x39\xf9\x98\x2e\xb7\xa0\x8a\x1f\xdf\x91\x8a\x1b\xd7\xa5\xaa\x97\x3d\xfb\x01\xd2\xf3\xd3\x56\x0c\x84\x88\xa3\x17\x2b\xe4\xb9\xa0\xf0\x2e\xbd\xc3\xd5\xc7\x98\xa6\x56\xa2\xc7\x7c\x4a\xdb\x15\x30\xbe\x4e\x7c\x71\x82\x8e\x30\x4a\x96\x70\x50\x50\xf0\x1c\x94\x1f\x12\x67\x37\x23\x3e\x23\x59\x4a\x98\x36\x91\x28\x7e\x7b\xe5\x35\x80\x59\xb9\x2c\x5f\x39\x5e\xac\x9c\x89\xf7\x5a\x5f\xa6\x19\x39\x4d\xb5\x47\x46\x75\xad\x3c\xbd\x40\x7a\xb5\x8b\x35\x62\x95\x35\xba\x8e\x1d\xd6\x1a\x17\xb7\xba\x8f\x99\x1a\x09\x57\x5d\x4b\xd5\x25\x03\xf7\x5a\xc9\xa5\x07\x8a\xbb\x39\xb2\x3d\x70\x4f\x6d\x0c\xb7\x7c\xb6\x36\x1e\xee\x85\xcd\xc1\xbd\x54\x5d\x24\xaa\xf5\x99\x09\x09\x2a\x1c\x0c\x2d\x11\xa6\xaf\x7f\x64\x2c\xc2\xa8\x12\x41\x82\xf0\xf1\x60\x90\xc7\x7d\x4d\x0f\xf9\x05\xbf\xb1\x20\x30\xe1\x21\xa6\x2c\xca\x78\x19\xc5\xa2\x1a\xc9\xf5\x4c\xdf\xf9\x9e\x0a\xa7\x1a\x01\xf6\xae\xa0\xb7\x79\x52\xbc\xbf\x14\xf0\x36\xcf\xb0\xf7\xf7\xa3\xdd\xb6\x44\x9e\x3d\x34\x86\xde\x87\xa9\xd3\x1e\xdd\x03\xc3\x81\x9c\x9a\x70\x20\x4a\x66\xce\x57\xa3\x90\xaa\x8b\x17\xa5\xf8\x9d\xbf\xaa\x85\x0f\x59\x09\x16\x52\x4a\xff\x27\x09\xf9\x2c\x70\xa4\xd7\x65\x38\x0f\x38\x69\xa4\x61\x2d\xba\x05\x0b\xc4\xf7\x6a\xca\xcd\xfd\x04\x24\x94\x76\xf3\x87\xa9\xd6\xf8\xde\xa6\x0e\xe1\x89\x13\x84\xf5\x18\xd5\x31\xd9\x89\x09\x4f\xc8\x2b\x26\x79\x3f\x4e\x66\x44\xdf\xdf\x52\x78\x95\x56\x35\xe2\x26\xcc\xfb\x4b\x35\xdb\x44\x68\x8d\xf8\xdb\xf4\xae\xc8\x96\xb9\x8b\x90\x89\x98\x33\x32\xc1\x8b\x46\x26\xf8\xd2\xc8\x38\x71\x8d\x66\x92\x4c\x89\xf1\xb9\xe9\x33\xe0\x68\xe1\xc7\x30\xad\x0c\xfa\xba\x30\xe3\xf3\x92\x99\xbf\xdc\xfc\x95\xe6\xaf\x41\x1e\x2f\x84\x12\xef\x6f\xc8\x99\x3a\xbb\x96\x05\x0f\x86\x06\xd2\x95\xd8\xfe\x26\x75\xd4\x71\xb3\x2c\x75\xde\xd4\xbf\xa7\xb6\xce\xd4\x6e\x10\xdd\xb5\xfd\x42\xa8\x63\x87\x2f\x17\x70\xd0\xa4\xde\x39\x57\x08\x25\xa6\xe0\xa3\x72\x55\x87\xb5\x41\x27\x34\x9d\x02\xa1\xbc\xac\x7c\x8e\xb7\x9f\xe5\x15\x89\xe2\x3c\x15\x56\xc4\x3b\x0b\xa3\x9b\x55\x08\xd0\x7a\xc9\xe2\x7f\xc8\x8e\xcb\x3b\x61\x8c\x89\x8f\x26\x2c\x95\x96\x46\x88\xa8\x4d\x6e\x46\x87\x78\xb1\xec\x03\x1a\xca\x1d\x6b\x5b\x71\x9d\x5c\xc7\x3d\x40\x73\x50\x76\x9a\x7f\x78\x37\x23\x01\x79\x9b\xc2\x5a\x9f\xa4\x7d\xf2\x26\x05\xb5\x38\x42\xad\x0d\x86\xe9\xce\x96\x71\x11\xc7\xcb\xee\x4b\x9c\x7c\x97\x11\xd9\x77\xcf\xf0\x81\xe3\x83\xc2\xb2\xf8\x30\x23\x07\x29\xa8\x47\x10\xea\x37\x42\xde\x97\x56\x22\x70\x8c\xbf\x90\x33\x57\xac\xdb\x8b\x76\xa2\x2a\x5b\x25\xa5\x39\x79\xa1\x58\x8d\xe1\xe6\x6f\x02\x78\xcd\xe4\xc1\x65\x0a\xbd\x33\x67\xb8\xf9\x9b\x1a\xdf\x56\x6f\x48\x37\x94\x78\x96\xf5\xd9\x84\xaa\x1f\x02\x3c\x25\x35\x6a\x9d\xf9\x26\x30\xaa\xc4\x47\xe6\xa0\x6c\xc8\x60\xb8\xa9\x44\xaf\x52\x1e\x60\x46\x1e\x50\xc4\x60\x63\x46\xbe\xa8\x99\xb2\xaf\x5a\x24\x40\x02\x51\xe3\x9c\xf8\xc6\xd3\xdf\x44\x1d\x63\xf1\x0d\x44\xcc\x3f\x9a\xa9\xfb\x4a\x68\xb9\x15\xbe\x53\xeb\xbc\x38\xdd\x28\x84\xa4\x6d\x3b\x76\x86\x8f\x41\x38\x18\x85\xd6\x84\x82\x69\x88\x17\xb2\x95\xd7\x43\x79\xa2\xbd\xde\x66\x5e\x0f\xc5\x8f\x35\x15\x87\x4b\x4c\xa7\x04\xdc\x8a\xad\x9c\xfb\x54\xf3\x51\x00\x71\x66\x1e\x57\xd8\xca\xb2\xcd\x70\x7d\x9b\x81\x1d\x3b\x9b\x6a\x7e\x9b\xeb\x46\x63\x2a\xad\x5b\x02\x5c\x81\x21\x08\xe7\x69\x73\xa5\x32\x86\xca\x9a\x5a\x48\x56\xd7\x48\x60\x3f\x52\x05\x4a\xb1\x4e\xd3\xbf\xa0\xf0\xfe\x8e\xba\x5b\x95\xba\x5f\xdb\x4f\xc0\x9c\xec\xa5\x79\x0c\xb2\x42\xfe\xe1\x20\xe9\x48\xda\xd8\x51\x8c\xcb\x88\xae\x8f\xdf\x1a\x50\x57\xcf\x41\x21\x00\xbf\xf4\xbc\x75\xb0\x71\xd4\xdc\xf2\x47\x63\xcb\xea\xd0\x45\xad\x65\x96\x1b\x81\x34\x28\x7e\xf2\x00\xb5\x7c\xc9\x78\xe3\xa0\xc5\x8f\x45\x27\x28\x58\x54\x7c\x8d\xe3\xd1\xb9\x3a\xc3\x3b\xc0\xd5\x9f\x6b\x4a\xed\x73\x82\x8f\x3b\x14\xf0\xc5\x42\x0b\x94\x93\x00\x34\x86\xc7\x03\xff\xbc\xdd\xa3\x63\x9f\x64\xf0\x93\x4d\x14\x3b\x37\x61\xda\x6b\xb1\x26\xd1\x54\x6a\xe0\x4e\x6c\xaa\x89\xeb\x55\x18\x42\x6f\xa8\x7e\x95\xef\xa5\x26\xa1\x35\x88\xd7\x5d\x88\xd2\x7c\xa1\x26\x00\xe5\xe5\x3b\x72\x41\xab\xde\x82\xbc\xc9\x22\xb9\xe2\xff\x7b\x81\x31\xbf\x0b\xbb\xe9\xe7\x25\xa2\x20\x42\x09\xf2\x58\x8e\xd9\x59\x18\x51\x2f\xf1\x01\x07\x3b\xc4\x71\xc7\x8e\x62\xed\x91\x65\xef\x21\xe3\x2f\x36\xb4\xdf\x26\x6b\x65\xf2\xfb\xdd\x91\xd0\x8e\x17\x1c\x2f\x42\xb5\x36\xc1\x73\x56\xb2\x29\x65\x7d\x26\x6a\xc1\xdf\x70\xeb\x6e\xd5\x96\x5c\x55\x72\x6c\x83\xa2\xad\x95\x82\xc2\xbb\x8b\xe5\x6e\xfa\x45\x9e\xa1\xa2\x1f\x9d\x59\x7c\x47\xe6\x93\x5f\x22\xb8\x79\x31\x72\xe8\x74\x61\x1c\xe5\x5b\x7b\xd0\xf9\xbd\x2b\x86\xef\xfb\x21\xe8\xb5\x13\x7a\x71\x2e\x10\x2e\xe8\x08\x1b\x69\x09\x4b\x20\xe0\x2d\x7d\x86\x50\x48\x30\x20\x97\xe7\x78\xe5\x85\xb1\xde\xd9\x2b\xdb\xd3\x52\xa0\x92\x00\x10\x4c\x95\xd8\x97\x28\xb1\x4f\xbd\x4b\x00\x01\x78\x51\x48\xbf\xda\x29\x5d\xad\x4d\x80\x2e\x45\x2e\xc6\x00\x68\x97\xa8\xfe\x18\x8c\x06\x36\x92\x32\x63\xcb\x9e\x38\x73\x22\x20\x84\xa0\xed\xf6\x2c\x1e\xcd\x48\x1c\xc1\x8c\xa4\xf0\x64\x00\x18\x93\xdb\x9e\x91\x6f\x29\xbe\xd9\xdc\xd6\x6f\x16\x25\x73\xa9\xe7\x71\x6d\x27\x7d\x57\x8d\x3a\xc1\x38\xf9\x45\xae\x29\x1c\x5e\xa0\xbf\x59\x19\xf6\x8a\xd7\x4c\x7e\xfa\xe5\xb2\x57\x61\x6d\xb1\x15\x3b\x8c\x7c\x41\x11\x39\x63\x46\xe2\xd1\xfb\xd4\xfe\x9c\x82\xab\x01\x6e\x65\x5c\x81\x1e\x57\x50\xc4\xe9\x8a\xda\x75\x41\x75\xce\x74\x86\x1f\xc8\x39\x50\xda\xc4\x5d\x9e\x99\x28\x88\x3c\x72\xde\x08\xc8\x22\xe7\x40\x00\x8b\x5a\x2f\xbc\x8e\x6c\xd1\x67\x47\x68\x4c\xdf\x77\x29\x78\xb6\xc0\xb8\x62\x42\x87\x24\x03\xf7\xad\x2d\x74\x5c\x31\xd1\x67\x6a\xb4\x5e\xe4\xbc\x15\x90\x44\xce\x2b\x01\x61\xd4\x74\xe8\xe7\x24\x89\x60\x38\x18\xe2\x06\xff\xfa\x85\x3f\x9f\x3c\xd3\x7a\xb9\x5c\x4a\xc6\xd8\x58\x58\xb2\xbd\x85\xb1\x63\x4d\xbd\xed\xc7\xf8\x6b\x24\x37\x86\xb6\xc4\x6b\x3e\x2f\xd2\xb1\x65\x2b\xda\x34\x41\x47\x3d\x51\xae\x79\xbc\xa0\x10\x44\xed\x1a\x4e\xd5\xed\x13\xfc\xfc\x68\x46\x42\x05\x48\x5e\xa4\xfd\x57\x0c\x28\x85\x51\x0e\x8b\x6e\xb4\xd6\x4c\x92\xa3\xbf\x07\xd7\xb6\x1a\x4d\x31\xea\xd1\x85\xed\x6b\x02\x26\xdc\xec\x73\x51\x45\x75\x6a\xe8\xcc\xc4\xb0\xd2\x69\x1e\x24\xfd\xa3\x68\x10\x57\xe3\x58\x69\x97\x08\x16\x15\x71\x4d\xd3\x68\x7d\xfc\xc8\x6a\xea\x0b\x53\x88\x17\x8d\x51\xab\xe8\xa3\xe3\xf1\x2b\xf1\x25\x71\x66\x24\x88\xc0\xd3\xe9\x31\xc3\x80\x24\xa5\xf5\x49\x3e\xab\x1b\x92\x46\xc0\x10\x10\xfa\xec\xa8\x47\x92\x0d\x86\xd1\xe4\x81\xf5\x3d\x9a\x27\xb3\xe8\xbb\x90\x34\x47\x11\x43\xdc\xab\x6b\x79\x95\x3a\x53\xe2\xaa\xa5\xd7\xa1\x49\xd4\xa8\x9e\x17\x5e\x58\xcd\xeb\xca\xd4\x32\x3d\xcf\xcc\x46\x24\x21\xc1\xb8\x61\xf8\x61\x2d\x85\x3d\xaf\x84\x71\xe1\x18\x2f\xa1\xde\xba\x58\xe4\x0c\xc5\x33\xbd\xcc\x09\x68\xf7\xf2\x56\x73\x81\x0e\x77\x7e\xb2\x0f\x78\xca\x28\xb0\x33\x3b\x06\xb6\xa9\xa4\x1f\x26\x29\xb8\xae\x79\xef\x06\xea\x15\x97\x14\xbc\x37\xb6\x00\x77\xa2\xdf\x2f\xea\x99\x20\x34\x48\xfe\x5e\xc4\x60\xab\x18\xa2\xe2\xb9\x10\x25\x3f\xaf\xcf\xd2\xe6\x40\x27\xb5\xa3\x23\x5c\x31\xde\xf7\xde\x00\xef\xbb\x2e\x86\x63\xca\x22\x6d\x4f\x47\x21\x56\x93\x53\xf5\x51\x45\xba\x5a\x7f\x82\x9c\x2c\x8f\xe0\xf7\x7a\x8b\x67\xbf\x37\x35\x60\x1f\xf2\x06\x9b\x95\x06\x01\x89\xca\x1a\x67\xaa\xdf\x40\x3d\x6d\xc2\x39\x11\x30\xa8\x06\x5b\x69\xad\x89\x7d\x0e\x07\xc5\x02\xa8\x36\xda\x1c\x6a\x41\xe1\x8d\xd0\x96\x18\x67\xa0\x44\xb6\xc8\x59\x0a\x8a\x17\x73\x55\xe1\xef\x44\x78\x8b\x4c\x84\x37\x3f\x55\xcc\xc3\x41\x43\x20\x29\xf3\xcf\xb7\x10\x06\xd0\x90\x1a\xa5\x12\x97\xdc\x0b\x9b\x0c\x7b\x5f\x66\x24\xde\x40\x34\x82\x3d\xf9\x11\x74\x03\x8a\xb1\xca\xaf\xf0\xdb\x79\xe0\x2b\x13\xad\x69\x1a\x15\xd1\x9a\x0e\x04\x85\x77\x29\xe9\x4a\x62\x7d\x15\x49\x7c\xd1\xf1\x33\x81\x46\x0d\x1d\x9d\xac\x1c\x43\x88\x77\xa3\xb5\x16\xdf\x8c\xdb\x93\x04\xdc\x33\x3b\x46\x19\x78\x1c\x99\x08\x48\x97\x77\x36\x23\x18\x3b\x15\x05\x61\x75\x62\x74\x2e\x5d\x7b\x1c\x61\x3f\x93\x5a\x73\xd9\xe8\x72\x6c\x78\x68\x46\x98\x83\xfb\xc9\xd5\xb3\xec\xb3\x53\x14\xb7\xb3\x55\x8e\x6b\x4e\x64\x04\xa7\x29\x8a\x02\x35\xce\x4b\xf6\xd3\xbe\x7b\xf6\x3c\x76\x32\x54\x44\x2c\x05\xd8\xd5\x65\x68\xec\x49\x2a\x74\x55\xc2\xcf\xd4\x9e\x91\x6e\x04\x19\xc8\x7e\x8a\xaa\x4e\xfc\xa9\xa8\xa4\x7b\x50\xe1\x93\x4d\x66\x1a\xb4\x28\xc7\x21\xba\x07\x38\x44\x1c\x30\xfb\xff\x62\xa8\xec\xb4\x1c\x2a\x3b\xad\xf0\xe3\x6d\x43\xf5\x9c\x69\xa4\x87\x4a\x32\x3d\xb8\x34\x05\x0f\x4d\x9e\xd4\x57\x71\xcd\xed\x8f\x29\x2a\xa1\x9a\xbe\xcb\x96\xbf\x9b\x2d\x7f\x77\xcb\xae\x37\xb9\x8c\x60\x1a\x55\xda\x5c\x46\x70\x90\x96\xc3\xc1\xd6\x7a\xc1\x2f\x23\xf0\x6b\x2b\xbe\xbd\xd4\x1d\x3b\xd1\xdf\x8c\x35\xfb\x72\x52\x61\x23\x57\xaa\x5d\x46\xd0\xad\xd5\x7a\xb2\x5c\xeb\x78\xa9\xb3\xe3\xa2\xda\xd3\xd5\x6a\x45\x67\x65\xad\xdf\x6d\xed\x2a\xbb\xb2\xdf\xe7\x44\xf6\xa7\x4a\x6c\xc3\x3f\xd7\xad\xea\xb3\x3d\x5c\x03\x9c\x3f\xae\x4e\x29\x25\xa0\x9c\xb0\xc4\xe6\xef\x91\x7c\x75\xb4\x06\x5e\x68\xef\x33\xbd\x94\xfa\xb7\x29\x89\x8b\x92\x69\x04\x0d\x2a\x7e\x01\x46\x03\xbb\x0a\x55\x9e\x93\xf4\x3d\xb5\x47\x1c\x3d\x7f\xdd\x03\x5b\x38\x49\x9f\x69\x55\x7f\x71\x8b\x94\x38\xc4\xab\xc2\x93\x86\x1a\x35\x61\x25\x2a\x79\xea\xf4\x7a\xe6\x4a\x22\x3f\xc0\xcb\x1f\x2a\xee\x21\x3c\x5c\x21\xef\x86\x6a\x1b\xa8\x15\xad\x37\xd6\xd6\x1f\x47\xeb\x8a\x68\x9d\x63\x53\xd5\x85\x44\xab\xc5\x66\xe4\x43\x0a\xe8\x8a\x22\xfa\xec\x71\x11\xa2\xb8\x66\x43\x3d\xb0\x6f\x10\x61\x75\x29\xb0\xc7\xf6\x1c\x9f\x1f\x53\x70\x3f\xda\x71\x46\xac\x97\x49\x16\xf9\x9d\x38\x91\x9d\x34\x73\xc7\xa1\x44\xdd\xa5\xc2\xa9\x90\x27\x36\xee\x84\x29\x96\xcf\xb9\xec\x60\x88\xff\xbe\x95\xbb\x05\x65\xd5\x58\xc8\xb9\x71\xfa\x47\x3b\x4c\x2a\x76\xcc\xb9\x85\xfa\xe7\x0c\xde\x20\xcf\x16\xa4\x3a\x1c\x22\xbc\xc2\x13\x78\x62\xcc\xb9\x7f\xa0\x39\x37\xbe\xa8\xb8\x29\x3c\x33\x87\x7c\xeb\x81\xa3\x76\x45\xa1\x90\x7e\x77\xb4\x9b\xd8\xa5\xb9\xbe\xdc\xa8\x26\x68\xb6\x36\xbc\xca\xa9\x2e\xc7\xe4\x29\x39\xb8\x38\x25\xac\x6a\x06\xac\xbd\x13\x12\x33\xce\x39\x39\x74\x8d\xf3\x89\x7b\xad\x58\xa6\x63\x8a\x03\xbf\x4d\x1b\x1c\x21\x1a\x32\x6e\x22\x29\x2d\x73\x6e\x62\xb6\xfa\xc7\xf6\x0d\xd9\x4d\x4d\x14\x0f\x0f\xa3\xba\x0a\x04\x0b\x6f\xd7\xc6\x39\xe8\x54\x9c\x8d\x8e\x86\x02\xe6\xe4\x44\x2d\x19\xce\x5e\x8f\xf1\x3d\xae\xe5\x8a\x9b\xc5\xd6\x03\x23\x68\x8f\x54\xb3\xdd\x44\x49\xb5\x0d\x23\x44\xb6\x80\x9b\xdc\xff\x8a\x27\xd4\xd6\x8a\x2b\x63\x9e\x91\x4f\x29\xf2\xe9\xb5\x91\x8a\xbe\xfb\x71\x01\xcb\x16\xea\x27\x2e\xad\x18\xac\x68\xce\xb9\x01\x3c\xbd\x55\xf0\xdc\xd3\x60\xc8\x62\x04\x32\x3f\x4c\x27\x11\x9b\x77\x58\x10\xe8\xa8\x48\x98\x48\x3c\x5d\x0b\x8c\xd0\x02\xe2\x05\x90\x7a\xab\x1e\x0e\x7b\x1a\x48\x4b\xf0\xfc\x92\x83\x27\xba\xd7\x4c\x51\x0f\x7e\x4d\x57\x43\x69\x54\x86\xbd\xc6\x87\x54\xd5\x78\xb5\xea\xd7\xb5\xd2\x87\xda\x13\xb5\xd0\x13\x8d\xa4\x8b\x93\x19\x25\x0d\xae\x1a\x4a\x0a\x11\x65\xc5\xfe\x74\xc5\xd3\x48\x75\xe5\xe2\x9a\x3f\x86\x64\xc9\x77\x6f\x38\xb4\xb3\xd2\x65\x46\x2f\x55\x82\xd1\xc9\x57\x7c\x5c\xf0\xbe\x46\xc1\xc2\xd2\xfa\xbc\xd0\xeb\x93\x34\xb9\xdc\xe5\xee\x0b\xc5\x7c\xd1\x66\x2b\x5a\x1f\xa4\xec\xd5\xeb\xfd\xd7\xc7\xaf\x97\xe3\x94\xcd\xa3\x8a\x9b\x81\xbe\xbb\x33\x3e\x06\x37\xd1\x5f\xbb\x49\xeb\xbb\x7e\xcb\x65\xda\x2c\x02\x01\xf3\x48\x5f\x69\x5c\xff\x95\x6e\x1b\xc2\x5a\xad\xdc\xd2\x9d\x24\x04\x2f\xea\xd0\xcb\xbb\x75\x05\x2e\x74\x90\x30\xf7\x50\x7b\x57\x1c\x45\x8d\x06\xc8\x47\x4c\xe7\x5c\xc8\xdd\x64\xaf\xec\xaf\x82\xc4\x14\xd8\xdc\x8e\xc1\x3d\xd1\x17\xc3\xc7\x51\xab\xf9\xe1\x6a\x7a\x72\x2f\xcb\x2f\xea\x6b\x10\x19\x25\x35\x35\x69\x98\xd4\xb7\x39\xce\xb4\xfa\x64\x41\xe1\x75\xe4\x1c\x92\x3f\x87\xb0\x09\x83\xef\x14\xce\xda\x34\x02\x0d\xb9\xfb\x6b\xc0\x36\xb7\x67\xe4\x38\x6a\xd6\x16\xef\x71\x38\x8a\xd4\x74\x5f\x47\x74\x01\x71\xcd\x45\x77\xe5\x3c\x76\x6d\xe3\x7f\xa7\x61\xdb\x7d\x8f\x58\x6f\xae\x8f\xf9\x9c\x5c\x23\x1b\x07\x3b\xab\x53\xcf\x49\xc4\x7b\x45\xdc\x0d\x77\xde\x77\x7d\xba\xea\x7a\xab\x59\x4d\x6f\x99\x74\xe4\x8d\x2b\x5f\xd3\x94\x6f\x4e\x6e\x22\xb4\x12\xe8\x4a\x72\xac\xa4\x19\x6f\x54\xd2\x2c\x5e\xa5\x59\x16\xb5\x77\x93\x92\xe4\x6d\x17\x87\x7d\xe9\xb4\x86\xc8\xa9\x30\x64\x5c\x3c\xed\xdd\x76\xcd\x20\xe9\x7b\x14\xd5\x13\xdc\xad\x75\x0a\xe8\xe8\x16\xe6\x8e\x6e\x9e\xc3\x56\x1c\xdd\x12\x0a\xde\x77\x4a\xed\x06\x24\x10\x2e\x30\xd6\x66\xf3\x51\xaf\x3a\xe5\xa1\x7b\x77\xb4\xc6\xe0\xaa\x7d\xf7\x57\x50\xe8\xf0\x4e\x14\xba\xbd\xba\x6b\xe8\x65\xbd\x8a\x63\xab\xb4\xb0\x82\x6d\xdd\x0f\x36\x9a\x9e\xe8\x36\x76\x95\x1c\xa8\x32\x2f\x23\xdc\x10\xc5\x1c\xfb\x1d\x33\x4d\x14\xa4\x22\x0a\x3c\x27\x0a\x28\x8a\x57\x95\xfb\x59\x83\x1f\x9d\x1a\xdd\x10\xc1\xef\x24\x05\x09\x19\xbc\x66\x15\xf4\xb9\xae\x45\xb9\x47\xcb\xbc\x4d\xa6\x3d\xdd\xde\xae\x4d\xa9\xcc\x0e\xed\x18\xd8\x17\x5b\x82\xfb\xcd\xe6\xe0\x76\x35\xa6\x78\x65\x6c\x3d\x12\x11\x5e\x84\x31\x8b\xd6\x25\xf5\x15\xc6\xb4\xe2\x85\x68\x4f\xc1\xa1\xed\x53\x52\xf5\x99\x23\x93\x93\xbb\x55\x85\xba\x6b\x0b\x30\x99\x82\xe1\x8d\x19\x07\xf3\x7d\xc1\xd3\xb4\xdd\x3e\x45\xab\x20\xd6\x5b\xa4\x08\xce\xfc\x79\x93\x3d\x4a\xca\xa5\x8c\x6a\x66\x27\xb9\x45\x8a\x1f\xa6\xcc\x8d\xfe\xa5\x04\xcc\x67\xda\x1c\x67\xc2\xb5\xf5\x87\x20\x27\x09\x79\xa3\x64\x94\x3c\xcd\x4b\x6e\x7a\x72\x20\x74\x4e\x18\x3d\xb9\x32\x17\x4c\xca\xc9\x0b\xa1\x13\x2d\x8b\x3b\x8c\x2f\x7e\xb2\x8f\xb6\x04\xf6\xc9\xce\x80\x7d\xb6\x39\xb0\x67\x36\x03\x17\xf5\x67\xe7\x7a\x49\x0f\xcc\x92\x5e\x24\x27\x5c\xa4\x61\x12\x97\x8b\xea\x66\x61\xe4\xbf\x42\xeb\x9e\xa5\x57\x5f\x52\x2e\x2a\xaf\x04\x8b\xbd\xcb\x6a\xc2\x9b\x69\xb8\xdc\xcf\xb4\xd2\x71\xca\x71\xae\xda\xc6\xe5\x65\x94\x5b\xdf\xc8\x50\x4d\xf1\x20\x37\xaf\x89\x83\xf0\xc2\x82\x57\x62\xa9\xfd\xbb\x38\x48\x70\x51\x74\xa5\x28\x4b\x31\x8a\xdc\x99\xea\xf3\xad\xc9\x3c\x7d\x1e\xdd\xc3\x1f\x27\xe7\x0e\xb4\xd7\xfa\xa7\x00\x62\x78\x19\x2d\xd3\x65\xa1\xe8\xb2\xb6\x06\xdf\x8b\xd6\x5d\x81\x69\x01\xe0\x3c\x82\x82\xc1\x3e\x63\xf0\x45\x31\xd8\x39\x82\x70\x8f\xec\x9f\x2e\x3a\xbe\x2c\x0c\xda\xa3\xb0\x1f\xb5\x87\xfd\x7b\xa6\x83\x1e\x3d\x59\xbd\xaa\x7c\x62\xb0\xc5\x94\xec\xba\x70\xa4\x0e\xb9\xfb\xd8\xb0\xf6\x1c\x24\xd5\x71\xa8\xea\x58\xe1\x31\x32\x65\xde\xcc\xf6\xc0\x3b\xb5\x5f\x71\xe2\xd1\x8a\x38\x33\xd4\xe4\xc9\x83\xc4\x99\x92\xb3\x08\x8e\x11\x75\x0c\x4c\xaf\x0a\x33\x68\xb2\x51\xf7\x37\x35\xdd\x7d\x56\xdd\x81\x37\xd0\xd7\x40\x1a\xe9\x1d\x70\x60\xab\x36\xa9\x79\xc3\x53\xfb\xa5\x66\x0e\x0b\x09\xe0\x8b\xf6\x51\xcd\x5d\x54\x6b\x0a\x04\x74\x65\x76\xe6\xe4\x2a\x02\x13\xaa\x3c\x59\xa8\x21\x0e\xf5\xd0\xc2\xd5\xa1\x9d\xda\xef\x39\x51\xe4\x69\x68\x87\xe5\xa8\x3e\x54\x47\x35\xb0\xc3\x06\x04\xea\x9d\xda\x3f\x38\x09\x69\x3e\x3c\xdc\xcd\xd7\x98\xfd\xf6\x5a\xcb\x71\x57\xaa\x78\x59\xbc\x0c\x9a\x3b\x7a\xc1\x49\x40\x97\xe7\xf9\x86\x93\xb7\xea\x6d\xa3\x57\x7c\xde\xee\x6c\x99\x79\xdf\x6e\xf6\x82\x8f\x4b\x36\x6c\xb8\x24\xee\xed\x25\xa4\x5a\xd6\xe2\xa8\x2f\x2b\xc1\x26\x56\x8d\x8f\xcb\xc1\x5c\x25\x8d\xf4\x73\x46\xf6\x8c\x24\x51\x10\xce\x40\xc3\xa4\xe6\x38\xdc\xc7\xea\x33\xb3\x1c\x26\x83\x26\x98\xc4\xeb\xb9\x02\x0a\x35\x19\x74\x35\x08\xea\x4e\xbc\x81\xee\xc4\x80\xa0\xbb\xba\xcf\x03\xdb\x6d\x06\xba\xc7\xb6\xab\x8e\x75\x3e\x1c\xef\xac\xa5\x83\x33\xd3\x01\x5b\xe2\xd4\x52\x0d\x6c\xa6\xad\x01\xb3\x74\xb5\xf5\xd0\x4e\x9b\xa1\x6b\xd3\x4e\x9d\x1b\x72\x51\x74\xd1\xcd\xe7\xd0\xd0\x49\xd7\x74\x52\x0c\x61\x05\x24\xdc\x15\x46\xe7\xf7\x7a\x95\xf9\x4a\x95\xba\x07\xbf\x17\xd7\xab\x6c\xd6\x79\x58\xef\xba\x52\xa5\x45\x4a\xfb\xb1\xcc\xba\x7d\x8c\xd6\x67\x65\x9c\xd9\x1c\x6e\x6d\x09\x99\x4e\xb5\x95\xc1\x8e\xa6\x38\xbb\x51\xb3\x7f\x9e\xfb\xf0\x21\xb1\x2c\xcd\x7b\x69\x6d\xa0\x9b\xe7\x49\xe8\x33\xd8\x8d\x50\x3d\x42\xed\xd3\x05\x9c\xb6\xa2\xe4\x6b\x99\xdf\x1c\x8e\x74\x10\x5d\x3b\x4f\xb4\x60\x42\x1c\xbf\x5b\xfe\xb8\x14\xf3\x4a\x53\x9f\x37\x24\x15\xa3\x0b\x8f\x19\xb6\xa1\xc8\xe0\xb4\x58\xc0\xa7\x36\x14\xae\xe3\xc9\x3a\x96\x49\x79\x27\xd5\xbc\xa4\x8e\x25\xf8\x40\xe6\x81\x04\x63\x47\x3d\x32\x90\xce\xbb\x08\xaf\x98\xf3\xbb\xb9\x4a\x02\x45\xf1\x5c\xa2\xff\x7a\xac\xea\xc4\x4b\x97\x6f\xda\xf8\x42\xc7\x11\x86\xd3\x48\x47\x54\xa9\xe6\x26\xa0\x70\xd8\xba\x46\x2d\x91\xdf\x1f\xc4\xa5\x03\xbd\xc9\x51\xb5\x9c\x7d\x40\xf4\xb3\x3c\xa8\x23\x2d\xd7\x0c\xaf\x36\x9e\xe7\x61\xce\x78\x9f\x3d\x7c\xb8\x1c\xf8\xad\xa8\x13\x3b\x72\xb1\x20\x31\x99\x92\x8f\x11\x9c\xae\x38\x48\xbe\xd4\x34\xba\x9c\xa8\xde\xf1\x87\x0f\x75\x60\xb3\x3e\x1b\xc5\x7d\xd7\x8e\xe9\x42\xc9\x92\x19\x05\xb5\x0e\x04\xb3\x71\x1f\xd3\x7e\x77\xd4\x4d\x6c\x0c\x3c\xf0\x29\x82\x6e\x62\x82\xfa\x3e\xb4\x34\xaa\x54\xbb\xff\x04\x98\xd4\xb2\xf5\x49\x74\x2f\xa3\xec\x4b\xc5\x38\x31\x05\xbb\xb7\x36\x87\x43\x3b\x81\x23\x9b\xc1\xb1\xed\xc1\x17\x3b\x83\x5d\x0d\xcb\xb7\xd1\x5f\x48\xb4\x26\x49\x11\x07\x98\x07\x46\xd7\x8c\xdc\xf5\x1a\x75\xc7\xc7\x08\x44\x7f\x07\x44\x3f\x03\xd1\xbf\x05\xd1\x9f\x41\x7e\x9d\x84\x79\x0f\xa3\xa5\x54\xf0\x45\x22\xf8\xf6\x04\xe7\x71\x7f\x07\x77\x34\x43\x3b\xfa\x5b\x34\xa3\x9f\x3d\xaf\x8a\xce\xaf\x70\xdd\xcf\x28\x78\x7a\xab\x4a\xa6\x52\x0f\xb7\x39\x77\xe4\x8c\xdc\x46\xe8\x50\x73\x9e\xc0\x69\x06\x07\x21\xc1\xc7\x2b\x06\xf3\x90\x58\x01\x8b\x52\x6e\x69\xd5\x3a\xbc\x8d\xd6\x04\xd1\x68\xcc\x5f\x5f\x7c\xe3\x30\x51\x64\x05\x4f\xe5\xda\x0b\xe5\x19\x79\xab\x46\x43\xa4\x23\x1a\x6f\x31\x6f\x2b\xf1\x40\x71\x92\x6a\x59\xd4\xa2\x08\x88\xfb\x33\x20\x78\x7b\x7a\x46\x09\x1a\xf8\xd0\xef\x68\x68\x92\xfb\x93\xbf\xa9\xcc\xdf\x5b\x93\xc9\xae\x71\xa5\xb5\xbf\xc2\x99\xc1\x0c\x1a\x11\x60\xea\xbe\x92\x38\x5c\x90\x18\x3c\x3a\xca\x47\x56\x64\x8a\x29\xb6\xe1\xbb\x42\x82\x45\xbc\xcf\x05\x1c\xac\xd9\x8f\xc6\xfc\x2c\xae\x41\xaf\x79\xae\x45\x14\x59\x17\x4a\xca\x9b\x91\x2b\x38\x51\xdf\x7c\x21\xee\xf3\xcf\x1b\x41\xf2\xb0\xed\x14\x0e\x8a\xc8\xce\x14\x7f\xe8\x00\xce\x78\xe1\x72\x1b\xc1\x4a\x08\xe7\x1c\x52\xe6\x98\x6e\x94\xea\x36\x45\x20\x68\xaa\xe0\xac\x12\xac\xda\xfc\x2e\x23\x53\x9b\x17\x3a\x10\xb5\xf9\x61\x1c\x09\x8c\xfc\xa7\x06\xa7\x44\xb9\x30\xbe\x50\xc3\xab\xe5\x34\x4c\xfe\xd5\x9d\xd3\x27\x4d\x23\x68\x4c\xf4\xea\x24\xc4\x33\x28\xbf\xc0\xe1\xa7\x1a\x87\xd7\xa1\xcd\x44\x7f\x15\x14\x62\xbd\xa7\x44\x22\x94\x95\x9b\xaa\xc4\xc4\x99\x92\x73\x8e\x8e\x0f\xdf\x7d\x7c\x6b\xc1\xb5\x44\xd9\x1d\x01\xfb\x4d\xb9\x32\x6a\x66\xf8\xee\x0c\xdf\x72\x3f\xd4\xf7\xd9\x27\x62\x39\x4b\x6b\x5c\x5a\x91\xc6\x90\x39\x72\xed\x29\x13\x80\xde\x44\x59\x7e\xce\xc2\x35\x0e\xfd\xf9\x15\x9a\xa0\xe0\x5e\x2b\x14\x9c\x80\x70\xc9\x38\xa0\x30\x09\xc8\x29\x35\x19\x25\xc3\x96\x81\x1b\x18\x8a\xf9\xcc\x42\x5d\x87\x80\xb0\x0a\x47\xea\x39\x77\xb6\xd0\x20\x82\x10\xda\x30\xe8\x94\x9c\x28\x70\xdf\x91\xf0\x60\xa8\xfe\xad\xfc\x1f\x33\x41\x99\x0f\x55\x3e\x5e\x05\x57\xbd\xda\xab\x03\x3c\xc3\xd3\x8e\x2f\xb5\xd0\x48\xe1\x4d\xe4\x90\x2c\x71\x50\x19\x76\x05\x9f\x39\x1c\xa8\x79\x5c\xc1\xcb\x04\x4d\x27\xd4\xe3\x55\x02\x07\xe6\xf1\x07\x87\x50\x3f\xbd\xe7\x70\xa6\x9f\x5e\x70\x78\x6b\x8a\x5f\x71\x78\x65\x1e\x75\x50\xc4\x86\x99\x69\x3e\x66\x41\xbf\xd3\x26\xbb\x09\x44\x89\x4d\xec\x14\x9a\xc6\x64\x28\x6d\x1e\x44\xce\x0f\x01\x1f\x22\x13\x78\xf1\x8b\x36\x5e\xd8\x5a\xc0\x0b\xfd\xb4\xb9\x80\x1f\xfa\xe9\xc9\x02\x3e\x47\xf7\xc9\x41\x75\xd5\x62\xa3\x3c\x06\xee\xbc\x64\x3a\x28\xed\x8c\x7c\x8e\xb4\x8d\x92\x9b\xe8\xd4\xbb\xd6\x44\x24\x7e\x86\x8d\x2c\x08\x12\x0c\xcd\xa6\xa4\xd6\x91\xa2\xe8\xb6\xa2\xfe\x5e\x53\xab\x8b\x5a\x36\x81\xbc\x65\xe2\xe4\x71\x91\x1c\x47\xb7\xcb\x7f\x57\x5b\x07\xb5\x30\x4e\x3b\xba\xf5\x68\x68\x0f\x20\x6c\x88\xd9\xc4\x2b\x96\x17\x35\xef\xe8\x95\x84\x1a\x9d\xeb\xc4\xc4\x68\xd2\x9f\x5f\xfa\xb0\x5f\xf3\x96\x31\x1f\x86\xcc\xb9\xe5\x24\x9f\xf3\xa5\x94\x13\xfb\xd1\xa3\x28\xf1\x58\x74\x99\xa4\xd2\x7e\x36\x78\xb6\xf5\xc8\xaa\x6a\x23\x22\x38\xd7\x6e\xde\x63\xe7\xa7\xbe\x85\x3b\xc5\x3b\x35\x59\x6a\xaa\xa3\x04\xdc\x13\x7b\x58\x81\x9f\xee\x2a\x7a\x9b\xae\xbe\xf2\x57\x5f\x45\xab\xaf\xd2\xd5\x57\xee\xea\xab\x60\xf5\x55\xb8\xfa\xaa\x01\xed\x36\xd0\x50\xb6\xfa\x2a\x5b\x7d\xd5\x90\x46\xb6\x81\xff\x12\x6b\x58\x32\x25\x8a\xfa\xe0\x3e\xb1\x43\x70\x9f\xd9\x01\x78\xae\xed\x81\x77\x6d\x73\xf0\x62\x3b\x03\xef\x87\x2d\xc1\x9b\xd9\x29\x78\x73\x9b\xe1\x3d\x26\x78\xef\xec\x04\xbc\x53\x3b\x02\xef\xcc\x8e\xc1\xeb\xda\x53\xf0\x06\xf6\x18\xbc\xa1\xdd\x05\xf7\xc8\x76\x17\xf5\xff\x2d\x5f\xa4\xe6\x3b\xf7\x60\x08\xee\x07\xbc\xad\x23\x33\x72\x91\x60\xf8\x3f\xf5\x78\xa9\x1e\x3d\x4a\x09\xa7\x24\xa4\x64\x96\x50\xcd\xf0\x92\x8c\x12\x46\x49\x98\x94\xff\xc5\x94\x78\x94\x48\x4a\x7e\x7a\x37\x76\xb2\xd0\xec\xed\x0f\xe1\xdc\xc6\x0a\x44\xde\xb7\x9e\xe4\x52\x84\x10\x1b\xc4\xb2\xad\x8d\xb3\x22\x8d\x10\x7c\x5d\x67\x2f\x6b\x44\x2b\x69\xef\x12\x09\xbb\xa4\x14\xab\xbe\x45\xed\x9c\x58\xa2\xa8\xcf\xa9\x6b\xa2\x12\xc5\xbe\xf3\x2d\x22\x96\x17\xb1\x34\xfd\xc8\xc6\xdc\xa2\x20\xfc\xdc\x29\xcb\xbd\x56\xe2\x9d\xf4\x9d\x6f\x31\xb1\xfc\x70\x6a\x51\xe0\xfa\x47\x3a\x61\xb1\x45\x21\xf3\x9d\xaf\x31\x30\xdf\x99\x11\xe9\xc3\x29\x20\x1a\xe6\xe6\x29\xf3\x89\xb5\x9f\x30\x3f\x8c\x2f\xfa\xfd\xbe\x45\xbf\xeb\x70\x36\x9e\xef\x08\x01\x89\xdf\x12\x15\x26\xf9\x32\x99\x70\xf1\x92\xa5\x9c\xd0\x05\x84\xfe\x5f\xb8\x13\xd3\x17\x62\xb9\xf6\x60\xf9\x12\x81\xf9\x75\x5c\xf1\x89\x15\xb7\x60\x81\x9e\x94\x9b\x49\x99\xa8\x69\xb9\xfe\x3d\x03\x29\xa5\xba\x61\x68\x51\x88\x7c\x87\x09\xf0\xfd\xf6\x85\x8f\x7c\x88\xc1\xf4\x20\xd0\xde\x6c\xea\x3b\x9e\x80\xae\xdf\xb0\xc5\xb1\x13\x8f\xac\x80\xf5\xc6\x61\x9c\xa5\x96\xad\x1e\x27\x51\x96\x5a\x25\x22\x0a\x7c\xb5\xc8\x27\x88\x8b\x62\x9f\x58\xae\x8c\x3b\xae\x8c\x7b\x49\x26\xa3\x30\xe6\xbd\x30\x0e\x92\x8e\x9b\x08\x9f\x8b\xde\xa0\x33\x16\xbd\x61\x67\xec\xf6\x86\x48\xe7\xa7\x3e\x58\x63\x26\x2e\xc2\xb8\x17\xf1\x40\x5a\x60\xf5\xb6\x04\x1f\xab\x3d\xd2\x7b\x98\x62\xe7\xaa\xdb\x80\xa1\xb2\x1c\x3f\x31\x16\xbd\x4d\xac\x73\xaa\xb6\x5e\xf1\x5d\x89\x31\xf4\x1b\x6b\x30\x92\xa1\x8c\x14\x08\x5d\xea\x75\xc9\x22\x8b\xc2\x44\x3f\x33\x8b\xc2\x85\xaf\xad\x00\x5b\x97\xe8\x98\xe7\x61\xea\x5a\xab\xfc\x08\x4c\x15\x72\x48\xfe\xcc\xf1\xb5\x05\xf8\x94\xaa\xc7\xef\x68\x54\xe4\xaf\x4d\xcc\x13\x57\x33\x41\x98\xd0\x1f\xda\x83\x19\x8c\x3d\x89\x73\xe1\x93\x0c\xb3\x85\x8c\xf4\x31\x63\xfd\xee\x88\xa0\x37\x47\x91\xd2\x2d\x76\x4c\xe2\x80\x99\x24\x18\xb3\x33\x91\x30\x96\x44\x6e\x58\x1d\x75\x52\x28\xc5\x54\xc8\x8d\x75\xb0\x86\x68\xaa\x21\x28\xe6\x0d\xa9\x7e\xb5\xf6\xb5\xb1\x24\xf9\x40\x74\x66\xaa\x0c\x3f\x56\xe9\x49\x7f\x60\xc3\xf4\xb6\xf4\x5e\xbf\xc3\x90\xec\x8d\x27\xf0\x90\xe4\x91\x99\xfa\xe9\x24\x0a\x25\x79\xf4\xcf\x74\xe3\xd1\x85\x92\x13\x6f\xcc\x1e\x33\x71\xc1\xa5\x45\xe1\x5a\x6f\xac\xf4\x2d\x0a\x3b\xe6\xf9\xd2\xa2\x70\x64\x9e\x15\xc3\x78\xec\xb7\x5f\x9c\xc7\xe8\xb0\xd1\x67\x43\x4a\x47\x15\x70\xde\x91\xf7\x81\xe7\x5c\xb6\x68\x04\xd8\xfc\xdc\x74\x6a\x00\x6b\xe1\x9d\x85\x02\x59\x3b\xff\xe0\x9d\x5f\xb2\x28\x9c\x30\x52\x8c\xb4\xed\x7b\xea\x70\xb6\x7f\x6e\x41\xe1\xb5\x5e\x95\x48\xe1\x8a\xb3\xa5\x55\xe1\x50\x48\x3a\xed\x99\x17\x44\x35\xf3\x82\x12\x07\x46\xe7\x79\xde\x5d\xcc\xbc\x66\x9f\x63\xe4\x0d\x23\x9b\x56\x72\xad\xa0\x16\x46\xc0\x39\x39\x05\xcd\xfe\x53\xb8\x5a\x7b\x38\xea\xa9\x20\xe3\xc2\x8f\xc9\x7f\x8e\x97\x37\xbe\xf6\x27\xe8\x73\x9a\xa7\xac\x7a\x69\x76\x9c\xb9\x78\xfa\xcf\x7d\x47\x32\xd8\xf3\xdb\x1c\xe7\x20\x76\x76\x09\xe1\xce\x8c\x9c\xf9\x26\xe6\xa9\x84\xcf\x92\x54\x12\xc9\xd0\x6a\x00\xf4\x56\x5c\xf0\xb9\xc0\x05\x31\xc5\x28\xe8\x2e\xe6\xc3\x08\x95\x6c\x82\x2e\xcb\x6e\x85\x71\x7b\xed\xeb\x9d\x53\xb8\x6f\x92\xa4\xa1\x66\x7b\x51\x92\x08\x3d\x4b\xc3\x1c\x8b\xc2\x8b\xb8\x17\x4a\x3e\x4e\x7b\xe8\x22\xde\x89\xc2\x54\xf6\x74\xa8\x7c\xf5\xba\x04\xc0\x89\x42\xaa\x6e\x6f\xbb\x04\x41\x59\x80\xc4\xac\x37\x1c\x60\xe9\x66\xc7\xef\x05\x11\xbf\xe9\xac\x74\x9c\x37\xfb\xa1\x64\x4d\x18\xfc\xf1\x02\x2d\x10\xdf\xab\x93\xe0\xf9\x4d\x12\x84\xc8\xc8\xcf\xae\xfd\x0c\x73\xa2\xa1\x08\x75\xec\xeb\x48\x29\xb6\x82\x34\x8b\x02\xc1\xf0\x9a\x4f\x69\xbf\x3b\xd2\x6f\xec\x37\x4c\x5b\xfd\x7f\xc1\x6f\x9c\x66\x6a\x6d\x8e\xfa\x2e\xaf\x54\x41\x6a\xdd\x72\x0c\x7c\x16\x5f\x70\x51\x39\x08\x6d\xb0\xcf\xe7\xbc\x97\x46\x2c\xbd\x6c\x38\x00\x85\x7e\x40\x91\xfe\x62\x08\xf1\xbf\x7b\x08\x2e\x8f\xa2\x96\x31\x7c\xc8\x8a\xef\x2f\xeb\x38\x8b\x5b\x97\x91\x16\x3c\x1d\x6b\xe3\x43\x48\x3e\x64\xab\xc6\xf6\xa5\x5f\x73\x88\xca\xef\x81\xc9\xe0\xe5\x2a\x3e\x4d\x83\x5b\x25\x75\xe0\xbd\xf1\xca\x2d\xc3\xe8\xaf\xcd\x33\x8a\xc2\xf8\x7a\x65\x2e\xfb\x61\x7c\xad\x11\x0a\xc1\x24\x5b\x70\x41\x04\xfa\xb9\xe5\x58\xf4\x65\xd1\x0b\x1e\xc6\x4e\x01\x89\x43\xec\x06\x67\xf6\x96\xe9\x73\xc2\xde\xd3\x12\x7a\x0c\x03\xa7\x23\xe9\xa3\xef\x88\x46\x5b\xfb\xfe\x9a\x34\xbf\x45\xfe\x9d\x51\xbb\x7f\x82\xb6\x0e\x2e\x97\x47\x56\x97\x47\x9f\xac\x2a\x73\x92\x2f\xc7\x32\x4c\x48\x7e\x23\x7b\x46\xb7\x64\xd8\x97\x2c\xe5\xa2\x97\xf2\x88\x7b\x8a\x7d\x09\xe3\x50\x86\x2c\x2a\x4a\x7b\xe3\xe4\xb6\x77\x47\x95\x19\x77\xaf\x43\x79\x47\x2d\xb3\x5d\x5e\x12\x29\x99\xd1\xfa\xaf\xc7\xae\x37\xf0\x0b\xba\x93\xf9\x44\x6c\xfc\xc3\xb1\xfe\xb1\x11\x6f\xfc\xc3\xfa\x07\x6e\xc9\x5d\x94\x45\x13\x94\x43\x46\xce\x89\xd6\x39\xc3\xd8\x27\xd6\x1b\x04\xc1\x8e\x3b\xef\xc8\xcb\x30\xed\x44\xcc\xe5\x51\xe5\x2b\xd6\x46\xce\x3f\x2f\x80\x53\xbb\x61\x89\xd4\x67\x52\xee\x25\xb1\xcf\xc4\x7c\x75\x45\x55\x1f\x1f\x13\xd9\xc1\x05\x37\xe7\xe1\xbb\x42\xe0\xde\xaf\x5f\x18\x21\x1a\x63\xe0\x30\x67\x3d\xfa\x19\x0e\x72\xfc\x33\x27\x5d\x85\x7f\x18\x5a\x24\x49\xe7\xab\x20\x18\x31\x0a\xef\x4c\x46\x67\x92\x48\xc5\x9a\xe0\xf1\xb2\xec\xa5\x9f\xa9\x05\xd2\xa9\x8d\x7f\x1c\xe9\xd1\x0e\x8a\x65\x9f\x5d\x86\x92\xf7\xd2\x09\xf3\xb8\x05\x56\x9c\xcc\x04\x9b\x54\xa6\x22\xf5\xf0\x97\xa0\xea\xb4\x8e\x85\xc7\x6e\x6f\xcb\x40\x7d\x22\x81\xc1\x2e\x49\x74\x14\x1d\x31\x9a\x91\xcb\xa2\x5a\x89\xe1\xcd\x10\xf2\x73\x32\x23\x7b\x3e\x60\x72\xd3\xac\x38\x27\xfa\x50\x7c\xf4\xdb\x6f\x08\x30\xf7\xad\x3a\x27\xd3\x04\xa6\xda\x72\xd3\xaf\x9a\xf8\xe8\xe3\xa3\x7d\xa3\x62\x93\x8b\x2b\xae\x5e\xd0\x54\xa7\x30\x89\x7a\x8f\xcd\x80\x76\x65\x1b\x1d\x74\xc9\xbe\x8f\xa7\x12\x93\x14\xba\x78\xbf\x13\xe2\x25\xcb\x1e\x66\xe5\x5f\xa0\x9e\xa3\x62\xb1\xef\xde\x22\x97\xcb\xb0\x82\x83\x48\x8c\xa9\x26\xcf\x97\x3b\x9c\x91\x8f\x3e\x86\xa6\x52\x44\x16\x14\xd6\xcb\x0a\x95\xe8\x27\xa6\xc0\x49\xef\xa8\x86\xa9\xb4\x13\x24\x59\xec\xa3\x41\xb9\x27\xee\x10\x3f\xdf\x07\x46\xfc\x3c\x55\x12\x10\xb1\xbc\x4b\xee\x5d\xe3\xe1\x7e\x67\x24\xaa\x78\x92\x29\x1e\xf3\x93\xe1\x9a\xf4\x71\x80\x43\xbf\xb4\x2a\x35\x7c\x28\x14\x8d\xbf\x53\x54\x45\x9d\x18\x36\x75\x3e\x51\xbc\xc8\xad\xdf\x2e\x3f\xe7\x3c\x81\x5a\xe9\x98\x4d\x91\x3c\x97\x18\xe8\x53\x09\x48\x52\x81\xa7\x82\x51\xad\x34\xef\x79\x49\x2c\x45\x12\x15\x3f\xd5\x00\xdc\xe4\xa6\x6c\xfb\x4e\x33\xb3\xbe\x99\x19\x96\x21\x83\xb1\xdc\x41\x2f\x9f\xe6\xa9\x5f\xe6\x7d\xa4\x14\x3e\x33\x7d\x5b\x23\x41\x64\xd4\xa0\xff\xea\x79\x59\xe9\xc5\x0f\x3d\x54\x6a\xdd\x5d\xd7\xe7\xa9\x27\xc2\x09\x32\x3f\xe5\x79\x8a\x0d\x72\xd1\xe0\xfd\xca\x5f\xef\x7f\xd9\xbe\x6a\x26\xc7\x51\xf5\xfb\xaa\x0a\xd2\xb2\x0a\xc7\xce\x4b\xfe\xdb\x60\x59\xe6\x5d\x2b\x28\x8a\x7d\x0b\x2c\x29\x58\x9c\x4e\x98\x40\x2d\xb3\xc1\x07\x41\x12\x6b\xec\x7c\xc9\x45\x58\xbe\xf6\x32\x91\x22\x5e\x9e\x24\x61\xac\x55\xd4\xba\xc0\x20\x5c\xc4\x1d\x31\x37\x8b\x9f\x0f\x45\x63\x60\xbc\xb6\xc2\xc1\xe8\x59\xbf\xf5\xef\x19\x97\xf8\x8d\x86\xd9\xc2\xf4\x8c\xc2\x81\xef\xfc\x83\xc7\x53\xa7\xaa\x50\xfd\x07\x7c\xd0\x80\x18\xaa\x1a\x5f\x7c\xe7\x77\x78\xe1\x3b\xc3\x2d\xf8\x81\x42\xb0\xd4\xac\xed\xb5\x84\xae\x44\x77\x77\xf8\x7c\x0f\xd5\xc1\xb0\x50\x1d\xbc\x6f\x3a\x09\x3a\x4a\x94\xb1\xb3\xfe\xaa\x6a\xa4\x09\x58\xd7\x7c\xfe\x32\xf1\xb9\x05\x18\x29\x1e\x4f\xa7\x71\x33\xf4\x0b\x9f\xc0\x6e\x50\xf5\x0b\x4c\x82\xd2\x73\xef\x9b\x5f\x78\xee\x79\x42\xa7\x98\x8e\xa7\x5a\x07\x34\x66\x91\x3a\x94\x62\x8a\xf3\xd4\x1f\xa7\x20\xa7\xad\xd1\xc2\xbc\x73\x2d\x39\x5f\xeb\xb8\x38\x57\x98\x44\x95\xed\x83\xe7\x7c\x53\xd2\x36\x24\xce\x09\xd3\x07\x21\x33\x91\x90\x5d\x1f\x83\x62\x79\x0a\x6d\x87\xce\x0f\x9f\x70\x0a\x81\x73\x41\x3c\x5c\x32\x93\xe5\xe4\x1b\x03\x94\x45\x4c\x56\x19\xcb\xb2\xbd\x7e\x77\x64\x5d\xb2\xd4\x30\x90\x96\x8d\x3f\xd2\xcc\xf3\x78\x5a\xd5\xa1\x94\x98\x56\x24\xb3\x4e\x9c\xf4\x2e\x32\x29\xb9\x48\x5b\xf8\xf5\x5d\xcd\x1a\x32\x4f\x7d\xaf\x46\x6d\xbc\x24\xea\x58\x1b\xa2\xd0\xae\x84\x71\x6f\x16\xfa\xf2\xd2\x02\x39\xb2\xb6\x06\x83\xc9\x8d\x65\x5b\x9b\xf8\xb7\x41\x62\x68\xfc\xbc\x3a\xb3\x3c\x96\xbd\x54\x0a\x2e\xbd\xcb\xa6\x76\xea\xab\x88\x44\x7a\xe6\x7a\x6f\x19\x03\x7d\xf0\x9b\xd3\x8a\xe2\x71\x08\x12\x51\xe0\x05\xdc\x46\x4c\xba\xe5\x91\x9a\x03\xaf\x3a\xd3\x2f\xfc\x8a\xab\x73\xe3\xf6\xe4\xe9\xd3\xbe\xf8\xd4\x64\x59\x7b\xe0\x38\x59\xde\xe8\x3d\x53\xf5\x9e\xc7\xce\x39\x61\x10\x56\x5c\x07\x8d\x65\x49\x77\x94\xb9\xf6\x8c\xbc\xf5\xe1\x01\xf2\xd6\x7d\x56\x50\x9a\xcc\x5d\x50\xc8\x3c\xd2\x92\xf7\xf8\x8b\x4f\x47\xaa\xf7\x21\xb5\xb1\xa6\xf4\xc8\x57\xd6\x80\x06\x2b\x8b\xd4\x73\x65\x5c\x2e\xd4\x2a\xb3\x36\x11\xe1\x98\x89\xb9\xa5\x4e\x3a\x09\x28\x24\x0d\x5c\x98\x62\xf4\xe4\xa8\xbe\x13\x5e\x12\xf5\x58\x26\x93\x4e\xed\x6b\x8a\x78\x6c\x36\x6d\x5f\xe3\xd6\x4d\xee\x62\x1f\x95\xdc\xe0\x19\xec\x75\x87\x34\xd4\x4b\x56\x64\x08\xe3\x7e\x53\x99\x49\xc9\xdb\xc0\x8c\xc4\xd3\xa2\x2f\x05\x1f\x8a\xf3\x5e\x62\xbf\xcb\xc5\x78\x89\x94\xa5\x63\x80\x0a\x3a\xbc\x7f\xd1\xb7\x9a\xf9\x5f\x24\x00\xc8\xa1\x8e\x0d\x6c\xbb\x2c\xe5\x88\xa1\x11\x17\x7f\x65\xe4\xc0\xa7\x65\xdf\x07\x7e\x39\x3a\x93\x1e\x83\x4f\xef\xa9\x93\xcd\xa6\x0d\x36\xa7\xb9\xb6\x89\xd2\x51\x11\xdf\xce\x5a\x51\xa6\xaa\x21\xfb\x22\x99\xf8\xc9\x4c\x1f\x7e\xad\xfa\x44\xa4\xc4\xa7\x18\xac\xa0\x32\x48\x61\x08\x07\x9b\xae\x63\x31\x4a\x36\x22\x78\xd2\xf1\x43\xb7\x33\x76\x37\x3b\x63\xd1\xa8\x19\xf0\xb8\x26\x62\x6b\xd9\x88\x53\xf5\x65\xc5\x22\xc8\x06\x30\x9f\x54\x00\x4d\xed\x11\x4e\xa0\x20\xec\xde\xf4\x9e\x24\x2e\x99\x3a\xbf\x43\x38\x75\xb6\x07\x10\x4c\x15\xd1\x72\xa7\xce\xd6\xef\x90\x4e\xef\x99\xee\x3c\xb7\x58\x2a\xf2\x9d\x5f\xe8\x54\xa3\x05\x16\x39\xcd\x88\xa4\xd5\xac\xe7\xd1\x74\x25\xeb\x79\x4e\x35\xd8\x39\xde\xfb\xbb\x11\x6a\x68\xdd\x31\x32\xaf\xde\x54\x1b\xc5\xee\xa3\xcb\xc4\x24\x00\x4d\x1b\x74\x3c\xcf\x36\x32\x9a\x4e\x73\x7d\xd1\x3b\x89\x01\x6f\x33\x34\x3a\x16\xa3\x44\x61\x1f\xbd\xc3\x52\xdf\x8e\x4f\x02\x38\xcd\xfe\x4a\x8f\x1c\x7b\x6b\x22\x2d\x77\xe0\xe7\x7a\x1e\xc6\x16\xdc\x2c\xdb\x70\x73\x38\x2d\x56\xd5\xf7\x48\x58\x60\x62\x77\xe9\x7d\x52\xbc\x0f\xca\xf7\x4a\x10\x9a\x04\x90\x18\x6c\xce\xa7\x88\xcd\x33\x54\x8f\xe6\xab\x91\x37\x4b\xa6\x15\xc4\x2e\xf3\x1e\x22\xaf\x40\xec\x3f\x7c\xc2\xd0\xd3\xa4\x44\xee\xc6\x8d\x58\x2f\xaf\x37\xd5\xc8\x9d\x55\x90\x7b\xb2\x1e\xb9\x27\x53\x3a\x52\x5f\x18\x52\x3b\x31\xc8\xdd\xf5\x50\xd3\x95\xe2\x77\x15\xb7\x82\xcc\xa1\x97\xa5\x16\xa4\x5c\xed\x29\x85\xa9\x47\x52\x6c\xf4\x6f\x24\x03\x02\xb1\x40\x4c\x9b\x49\xc1\x82\x82\xdf\xce\xf5\x18\xf8\xf5\xa6\x90\x39\x0f\x50\xd6\x96\x10\xf7\xbd\x3d\xaa\x24\xef\xcf\x41\x6e\x8f\xea\x29\x29\x59\x31\x2f\xd9\x1a\xd6\x05\x63\x68\x35\x88\xb8\xe8\xa5\xc4\xa6\x60\xbd\x8e\x51\xef\xa3\xb9\x7e\x2d\x99\xa1\x79\x8f\x00\xcf\xab\xba\xbb\x89\x51\x29\x34\x4b\xf8\xbb\x9c\x50\xb7\x9d\x13\x62\x0d\x9c\x50\x9d\xff\x89\xa6\x28\x0a\x03\x2e\x43\x45\xfb\x0b\x5c\xc9\x10\xf7\xa7\x44\xfb\x4a\x3c\xec\x5c\xf3\x79\x27\x48\x44\x31\xe9\x5c\xdf\x60\xf4\xfd\xff\xa6\xee\xfe\x25\x3a\xe7\x7a\xcb\x4a\xee\xf2\x9b\x95\xb7\x86\xbd\x20\xdc\x21\x99\x13\x53\x85\xf1\x62\x27\x53\xf8\xaf\xba\xca\x8a\xcf\xf0\x92\xf1\x24\xe2\x92\xf7\xc6\x3c\xce\x3a\xd6\x06\x21\x59\x9f\x6d\xfd\xfa\x95\xf5\xdd\xd7\xf4\xe1\x43\x75\xf4\xac\xf4\x32\x99\x29\x5a\xa7\x18\x68\x8f\x24\x78\x6e\x28\x04\xfa\xb1\x72\xa1\x21\x57\x29\xa0\xea\xb5\x54\x87\x64\x53\x85\xe3\xb4\x62\x30\xa7\xcb\x6a\x65\xe5\x82\xc2\x74\xea\xfc\xec\xda\x4f\x16\xd0\xbd\x83\xf8\x36\x12\xd9\x52\x32\xb4\x9b\xca\xd7\x49\x7b\x27\x8c\x8c\x3d\x22\xfa\xde\x97\x15\x7d\x50\x9c\x93\xe9\xca\x2d\xe5\xd4\x64\xcb\xba\x6c\x20\x84\x1a\xe9\xc7\x0a\xed\x97\x09\x9b\x5b\x93\x6b\x5e\xcb\x3b\x52\xb4\xc6\x7d\xef\xcb\xa2\x10\xf2\x3e\x4b\x1d\xec\x40\x2c\xeb\x8a\x57\x25\x69\xc5\x2a\xaa\xad\xc5\x49\x87\x02\x2c\xc9\xdc\x77\xb1\xcf\x6f\x30\x93\xd0\x90\xe6\x6b\x51\xb9\xc1\x10\x3c\x62\x7a\x01\x5b\x84\xdf\xe5\x2d\x56\x4b\x36\x35\xf7\xb9\xa8\xd8\x10\xbd\xed\x35\x4b\x5c\x82\xe8\xa1\x31\x06\xb4\x3b\xd6\x86\x36\x33\x43\xf3\x1f\x51\xc1\x82\x93\x69\xbb\xb5\x92\xbb\xff\xeb\x97\xe8\xbb\xaf\x47\x0a\xb0\xa5\x23\xa8\x42\x8c\x98\x0c\x6c\x0f\xf5\x8e\x9a\x86\xa3\xe9\x65\xf4\x97\x08\xb9\x34\x84\xdc\x2b\xc8\xb6\xbc\x7f\x7b\xa9\xdb\xae\xdb\x0a\x5c\xa1\xce\xea\x79\xc3\x93\x85\xa7\xea\xb2\x3c\x55\x97\xfa\x54\xfd\x0b\xbb\x54\x72\x06\xf5\x04\xb3\x8a\x0d\xe0\x48\xdf\xa6\x53\x24\x86\x17\xde\x0a\x53\x50\xa1\x9e\xe1\x94\x8e\x26\x1e\xf1\xa8\x6d\xd8\x01\xf5\x8b\xe9\x5f\xc1\x94\x8e\xc6\x9e\x96\xb5\x39\xb4\x05\x7a\xd4\x30\x9c\x51\x4a\xed\xd7\xee\xa2\xb0\x01\x28\xc1\xc5\xf8\xf8\x5b\x60\xb9\x51\xe2\x5d\x97\xea\x5b\x83\xef\x87\x83\xc1\xff\x55\x2a\xa5\x5a\x50\x4c\x67\xe9\x57\x4f\x84\x17\x97\xb2\x44\x3b\x5d\x14\x4b\xa5\xc6\x37\xf6\x8c\x5c\x4e\xd1\x7d\xc0\x7d\x41\xcb\x8b\x7b\x60\xe0\x2d\x28\x5c\xac\xa1\xc1\xef\x34\x0f\xc9\xf4\x2d\x7f\x57\x5f\xf3\xef\x68\x17\xe0\x13\x40\xc7\xa6\x37\x3a\x46\xde\x6b\xbc\xb1\xf0\x7e\xac\x57\x2e\x7b\x4c\xf8\x9d\x2a\xf9\xad\x17\xf6\x2e\x39\xf3\xab\xcc\xfc\x65\x15\xc0\x3a\x0a\xc8\x24\x73\xd3\x4e\xa5\x2e\xbe\xc8\x1b\xdc\x90\x57\x3e\x0c\xc0\xd3\x28\x64\x9f\x61\x52\x48\x73\x12\xdf\x18\x43\x49\x45\x26\xb0\xde\xb0\xb1\x9e\xe8\xb3\xcb\x91\x85\x26\xbe\x1d\xa2\x39\x02\x6a\xd9\xfa\x45\x2e\xee\x79\xfe\x52\xd6\xc9\x19\x99\x4c\xb5\x1e\x99\x51\x0a\x73\x72\xeb\x43\x2e\x30\xfa\x16\x88\xfe\x17\x38\x65\xf9\xfb\xf2\x72\x0f\x44\xff\x08\x3e\x16\x05\xfa\xc6\x0d\x44\xff\x18\x76\x59\x4e\xce\x56\xd6\xc7\x40\x8c\x9e\xad\x37\x2a\x87\xc2\xf3\xa1\xf8\x53\x54\x6a\xb3\x4b\xdc\xf8\xbc\x58\xe6\xc5\x72\x0a\x3f\xbd\x73\xfb\xc1\x00\x41\xb4\xfa\x99\xd3\x36\x9e\x2e\x17\xa4\xcb\x8b\x92\x15\x03\x99\x2d\x4d\x55\xcc\x7d\xc7\x26\x30\xfb\x41\xbc\x30\x64\x32\x1e\xad\xb3\x34\xd8\xaa\x4a\xd9\x2f\x93\x28\x62\x93\x94\x77\x58\x14\x19\x05\xb9\x45\xbf\xdb\x6b\x2c\x07\x96\x9a\x6b\xf3\xca\xe5\xc6\xf9\x04\x43\x1f\xe6\x64\xd7\x87\x04\x42\xc5\x3c\x49\x83\x7b\x67\x5a\x2b\x77\x39\xb4\x28\xcc\xa7\x4b\x16\x59\xb3\x69\x69\x91\x15\x27\x32\xd7\xd4\x9b\x1e\x6f\x74\x43\x44\x32\xa9\xb6\xec\xd0\x1a\xbd\x4b\x39\x8e\xde\x24\x8a\xcc\xee\xdc\x53\xc4\xbd\x4b\x53\x2e\x98\x1f\x26\xff\x92\x9a\x5c\x20\x16\x8c\x11\xfd\xc9\xff\xa4\x6a\x7c\x41\xe1\x68\x6a\x4c\x76\x8f\xa7\x26\xec\xe4\x6b\x7c\x18\x0e\x16\x70\x86\x4f\xbf\x2f\xe0\x0a\x1f\xb6\x17\xf0\x72\xda\xea\x0c\x5c\x11\x9a\x07\x7f\x38\x18\x85\x5c\x47\x20\x57\xf2\x8b\x59\xd6\x77\x92\x64\x1a\xc5\x79\x4e\x0c\x89\x23\x20\x74\x24\x04\x0e\x07\xd7\x31\x17\x5a\x19\x85\xd4\x31\x81\x86\x05\xe6\x2e\x7e\x1e\x3b\x1e\x08\x27\x01\xe9\x84\xc0\x9d\x00\x32\xc7\x05\xe6\xa4\x4a\xc2\x3e\x9f\xae\xf5\x0f\xeb\x04\xe4\xe5\x14\x3d\x46\xbe\x49\xcc\x2b\x84\x6e\xee\xa7\x30\x27\x5f\x53\x0c\x5b\xa8\x83\x2c\xee\x4d\xdb\x0c\xf2\x4c\x4e\xb5\x5a\x72\xcc\xe1\xf6\xf6\x80\xd2\x95\xf4\x59\xb5\x28\x01\xdb\xf5\xc4\x64\xb5\xd0\x10\x4f\x6a\x3e\x8f\x83\x7a\x3a\xb2\xba\x71\xdf\x26\x66\x04\x69\xa3\x07\x13\x8c\x99\xce\x9d\x29\x39\x9f\xc2\x70\x08\x18\x73\x57\x02\xc9\x9c\x1d\x45\x13\xf7\xa6\x26\x62\x70\x11\x4d\xf8\x69\x4f\xfb\xb1\x8d\x32\x7b\x88\x3a\xe6\xa7\x76\xd6\x1b\xd2\x3c\xb0\x30\x64\x4e\x19\x69\x38\xa6\x10\x3b\x24\xae\x76\xa5\xd5\x0c\x45\x68\x62\xd3\xd7\x53\xc7\x71\xe2\xd1\xc0\x8e\xb1\x4f\x7c\x32\x7d\x66\x14\x96\x86\x16\x57\x55\x0b\x72\x79\xd8\x19\x48\x8a\xb6\x9f\xbb\x24\xdf\xa3\x8f\xad\x5c\x18\x46\x1d\xf9\xe3\xe9\x48\xd8\x33\xf2\x71\xaa\x44\x18\x17\xd3\xe0\xc1\x8c\x7c\x4a\x40\xe8\x53\xe9\xeb\x77\x46\xef\xb6\x8b\xf0\xbc\xb5\x80\xd3\xe9\x9d\x96\xa8\xbf\x7e\x19\x9f\x39\xe3\xc9\xb2\x14\xbd\x75\x41\xe1\xdd\x52\x17\x12\x93\x04\x19\x4b\x81\x0b\x72\xe4\xa9\x4d\x39\xf2\xd4\x21\x18\x59\x78\x0d\x3e\x4e\x62\x79\xa9\x84\x17\xc8\xda\xef\x46\x8a\xa8\xc0\x71\x11\xa8\x56\xd0\xb5\xc1\x92\x1f\xc4\xfd\xee\xc3\x87\x43\xb4\x52\xcf\xd0\xa0\x83\xd3\x91\xb0\x2d\x6b\xa1\x99\x48\x1c\xf0\x15\x58\x1d\xae\xb0\x24\x2a\x98\xf0\x95\x00\xab\x33\x4e\xb2\x94\x27\xe8\x12\x83\x7a\x22\x2c\xb8\x05\xab\x63\x64\x63\x88\x9b\x83\xac\xb1\x5b\xec\xb4\x2d\xc0\x9a\x65\x15\xf7\xb3\x2e\x5a\x7b\x9d\x13\x33\x36\x1c\x1d\xa8\x1f\x02\xc3\xaf\x63\xa0\x69\xf6\xeb\xd7\x26\x06\xcb\x75\xcb\xf6\xe6\xa1\xe3\x72\x39\xe3\x3c\xb6\x16\x84\xe6\x7c\xf5\x39\xc1\xdc\x0b\x8a\x3e\x9f\x4e\xa1\x35\xff\x5a\x08\xe8\x8c\x5e\x0e\xb6\x51\x9d\xe5\x33\xc9\x3b\x2e\xf3\xae\xad\x0d\xc2\xfa\x4c\xfd\xe3\x6e\xc4\xb4\x51\x92\x54\x55\x03\x91\xc4\xd2\xda\x48\x36\x48\xb8\x41\xbc\x0d\x13\x0d\xce\xf7\xc1\x2a\xd7\x12\x52\xf4\x3a\xcf\x13\x23\x2b\x02\xbc\x3b\x2d\x71\xf0\x99\x02\xa5\x0f\x18\xef\x9c\xd3\x8a\xb8\xf1\x69\x2d\x59\x5a\x62\xdb\x4e\xf3\x3b\xfd\x77\xd3\x3c\x57\x98\xe9\xe4\x70\xda\xea\x39\xba\x8f\x62\x02\x06\xcd\x52\x3f\x3f\x4e\x41\xc2\x29\x6d\xbb\x3e\xba\xca\x52\x19\x06\xf3\xe2\xda\xa6\xae\xcd\xad\xd8\x87\x71\x7e\x5d\x61\x0f\x71\x64\xc7\x1e\x1c\x9a\x88\xda\xde\xcd\xe8\x4f\xeb\x28\x53\x52\xc3\x07\x94\x1d\x8e\x33\x25\x26\x7c\x55\x4c\x95\x75\x7c\x99\x59\x60\xbd\x11\xa1\x05\xd6\x11\x93\xd6\x77\xfb\xcf\x7b\xd4\x42\x57\x0c\x63\x1a\x5b\xdb\x20\x1c\x2f\x0b\x63\x73\x7b\x5a\x6e\x4c\x26\x71\x5f\xae\xa6\xb4\x62\x12\xf1\xa9\xbe\x7c\x27\x88\x1f\x06\x0b\xb8\x9d\x9a\x20\x9c\xaf\x34\x3f\xe1\xaa\xfe\xde\xea\xe7\x89\x45\xe1\x4d\xfb\x66\xfd\xec\xda\x4f\xaa\xd9\xad\x31\x10\xc6\xda\xea\x8f\xeb\xd5\x3f\x98\x3b\x49\x29\x92\xf8\xc2\xa2\x6b\xf2\x61\xaf\xc4\x21\x31\x29\xd5\xe2\x7e\x77\xf4\x36\xb1\x53\x4e\xb5\x07\x9e\xc2\x8e\xbc\x48\x42\xd8\x6e\xd2\x90\xa7\x4d\xaa\x64\x4c\x22\x96\x57\xc4\xe2\xf3\x92\x78\xca\x85\xec\xa4\x52\x84\x7a\x64\x53\x7d\x73\xfb\xde\xf0\xb3\x73\x0e\x9e\xa0\x14\x5e\x4c\xdb\x7c\x57\xeb\x80\x26\xc3\x31\x9f\x84\xde\x75\x15\xb8\x3e\x14\xda\xad\x34\x73\xaf\xb8\x27\xab\xd6\x32\x23\xeb\x75\xec\x5b\xb6\x75\x94\xeb\xef\x96\xa1\xe0\x32\xc9\x44\x93\x26\x34\x9b\xf4\xb4\x49\xbc\xb9\xab\x28\x20\x5a\x9d\xcf\x39\x79\xa3\x8e\xc3\x00\xd6\x18\xc5\xb2\xf8\x22\xe2\x3d\xa3\xf6\x3e\x35\xdf\x7d\x67\xee\xfd\x15\xa0\xb9\x51\x26\x2c\xbd\xd0\x33\x72\x80\xfd\xa9\x05\xa2\xc8\xe4\x55\xf0\x68\x18\x10\x99\x63\x53\xd1\x67\xd7\xd5\xd8\xa3\xd6\x00\x2d\x8c\xf2\x54\x0a\xda\x71\x02\xab\xed\xdc\x59\x8d\x50\x0a\x67\x1e\xd9\xd4\x5c\xe7\x34\xe4\xb3\xfa\x54\x73\xae\xb1\x72\x41\xa4\x24\xd5\x7b\xac\x4b\xef\xce\x85\x51\x3d\x95\x4b\xb3\x22\x24\x25\xd1\xea\x17\x4a\x6d\x8c\xbd\xba\x8f\x4a\x12\x91\xfc\xef\xee\xe4\xf0\xdf\xbc\x93\xc3\xbf\xbf\x93\x3f\xee\xb7\x93\x3f\xd6\xee\xe4\xdf\xdf\xbb\xe1\xbf\xbc\x77\xea\x84\xb6\x40\x93\xde\x3e\x12\xb7\xa3\x27\xed\xc0\xd9\xa6\x93\x51\xe8\xc6\xb2\xec\x19\x99\x27\xf0\x3b\x48\x64\x7b\x16\x06\x49\xc9\x91\xe1\xb8\xae\xd5\x3f\x57\xa8\xf5\x56\x4f\x3b\xea\x9f\xdb\x82\x72\x22\xab\x11\x2f\x28\xfc\x98\x36\x85\xb4\x37\xee\x48\xe5\x85\x7f\x83\xa1\xe1\xb8\xe3\x5e\xf4\x02\xe6\x73\x7f\xd5\xf0\x70\x13\x2d\x9f\x5b\x0c\x35\x25\xbf\x91\xeb\xac\x34\x97\xcb\x1b\x4d\x34\x75\x95\xe5\x6b\xd8\xe7\xf1\x72\x2c\xec\xe0\x6f\x0e\xf9\xa4\x6a\x92\x5b\xce\xbb\xf1\x9e\x40\x94\x8c\xc8\xe7\x69\x6b\x42\xd9\x36\x63\x62\x1d\x05\xc9\xd1\x51\x90\x30\x9a\xa8\x8b\xd6\x9d\xbb\x84\xc8\x3b\x2c\xe3\x73\x13\x3f\x34\x72\x97\x7d\x77\x95\x4f\x5b\x6f\xe5\xfe\xf7\x6c\xd8\x2b\x16\xeb\xc2\x58\xac\x0b\x63\xb1\x7e\xec\x23\x53\x50\x58\xa2\xa3\xcb\x42\xcd\x12\x5d\xc9\x00\xda\x62\x79\xd9\xef\xe3\xfe\x16\xcb\xa2\xdd\x62\xf9\xc7\x14\xdd\x46\x78\xbe\x1f\xef\xa7\xed\xc7\xab\x62\x5d\xc9\x06\xfa\x73\xe5\x97\x3e\xa3\x6e\x41\xdb\x26\x7e\x9d\xde\xc7\xf7\x6c\x25\xc9\xcb\xca\x22\x9a\x85\x93\x7d\x77\xf4\xe7\x8c\xbc\x9d\xe6\x5c\xe9\x87\x8a\x56\x66\x27\x0f\x92\xaa\x2f\x99\xec\x0e\x3a\x01\x7e\x15\x98\xe0\xa4\x40\x30\xef\xa7\xe8\xee\x81\x6a\xa4\xe6\x7e\x3e\x26\x65\xbc\xd5\xca\xf5\x97\x39\xf7\xcb\x42\xb8\x5e\xc7\x7a\x1e\xeb\xd2\x4f\xae\x75\x56\xda\xea\x13\xff\xed\xcd\x98\x88\x91\xaf\x59\xb6\xcd\x55\x2c\xd9\xb7\x69\x7b\x2c\xc1\x06\xef\xbd\xea\x70\x06\x65\x70\x23\xcf\x87\x97\x1e\xcc\xc8\x57\x3d\xf7\x7a\x06\x1f\xe3\xc5\x57\x97\xff\x99\xaf\x46\x10\x77\x1d\x6b\x2c\x7b\x8f\x2d\x10\xdd\x15\xf3\x82\xd5\x89\xed\x92\xb8\x0b\xc6\xfb\x65\x64\x75\x96\x2e\x61\x2d\x5a\x33\x0c\xbd\x9e\x36\x9b\x56\x55\x18\xb2\x62\x57\x3e\x98\xe4\x82\x1d\x4d\xb3\x71\xc3\x71\x73\xca\xcd\xca\x0d\xbf\xd3\x8a\x75\x8e\x56\xcf\xbe\xc8\x2a\xba\xce\x21\xa2\x7a\x58\x26\xa8\x5a\x14\x2c\x10\xb6\x59\xc8\x82\xc6\x36\x6d\x61\xd5\xbe\xa0\x17\x70\xee\xa3\x40\x57\xd3\x7a\x2d\x88\x39\x4e\xb2\x6b\x58\x7b\xde\x75\xd6\xaa\x55\xcd\x2d\x35\xe2\xd1\x13\xb7\xc2\xad\x7c\x16\x9a\x1e\xee\x94\x30\x09\x59\xf7\x3e\x87\x54\xe1\xa7\x8e\xb5\x11\x77\x4d\x6f\xbc\x5b\x73\xe3\x40\x96\x00\xbd\xba\xb9\x65\x5b\x5f\x30\x73\x64\xa3\x65\xcf\x38\xea\x6d\x76\x56\x6f\xf7\x4f\x18\x91\xdd\x95\x99\xc7\xcb\x8c\xc3\x52\x5b\x03\x14\x5a\x4f\xbc\x74\x41\x86\xea\xd3\x82\x2a\x30\x5c\xb6\x67\x0b\xf0\xba\x46\x87\x92\x74\xef\x48\x51\xc2\xfa\x8c\xe7\x91\xe6\xbc\x4a\x94\xf0\x26\x39\x5c\xdb\x0f\x69\xfe\x22\x89\xb2\xb1\x4e\xa0\x60\x4c\xd9\x2b\x46\x03\x1d\x6b\x43\x56\x81\x77\x15\x6d\x88\x12\xc5\xe0\xad\xa9\x98\x2a\xa1\xfe\x0c\x95\xac\xfa\xc2\x29\x5b\x35\x4b\xe9\x2c\xc1\x50\xb1\xf5\x39\x4b\xb4\x72\x90\xef\x3b\xf2\xff\x99\xd1\x56\x06\x96\xa3\x0a\xed\xbd\xe2\x35\x1f\x90\xbb\x57\xd7\xf8\x26\xfd\x27\x16\x37\x07\xb8\xd5\x8b\xb4\xbb\xce\x71\x92\x43\xe3\x82\x42\xd8\xbd\x43\x5b\x52\x11\x31\x2b\x07\x2e\x20\x49\x17\xde\xf9\x60\xa4\x47\x50\x82\x49\x2f\xba\xe8\x6d\xa3\xb9\xe3\x13\x13\x39\x66\xdf\x83\x1b\x8f\xc2\x8d\x47\xbc\x2e\x85\x98\x42\xd1\xec\x95\x49\xef\x51\xb6\xdc\x5a\x6a\x79\xbe\xd4\x52\x56\x5a\xbe\x46\x73\xf0\xc6\xcf\xed\x2d\x35\x12\x0d\x8b\xa2\x37\xca\x34\x1e\x9a\xc6\x2b\x3b\xa5\x8e\xed\xff\xf3\x7f\x5b\x2b\x8e\x3b\x4b\x7b\x51\x47\x73\x06\x77\x74\xf4\x65\x79\x6f\xc9\x1a\xe1\xc6\x23\xac\xdb\x2e\x3c\x78\x2c\xe2\x8a\xeb\x5c\x12\x1d\xbe\xd3\xbb\x9c\x26\x42\x01\x8f\xfe\x9b\x24\xf1\x2f\x35\xae\x1d\xac\xd1\xa5\x8f\xc2\xbe\xe4\xa9\x24\xb1\x13\xd3\x91\xe5\x33\xc9\x7a\xd6\x46\x6c\xc7\xf0\xe8\xbf\xff\x99\xfe\x46\xae\xd8\x94\xe9\x0b\x0d\xfb\x97\x2a\xb4\x15\x13\xfb\xcf\x47\x97\x72\x1c\x15\x4d\x85\x23\xd0\xe4\x08\x43\x76\x07\x0a\xb9\x7b\x02\x74\x57\x17\x82\x8d\xc7\xe7\xdc\x0f\x31\x58\x46\x1e\x31\x0a\xdc\x6e\x9b\xd7\x70\xee\x2e\x2c\xfe\x19\xff\xfa\xa7\xf8\xf5\xcf\x58\x7b\x0d\xa7\x5d\xed\x1b\xca\x6f\x24\x13\x9c\x59\x14\xa2\xee\xda\xbc\x53\x98\xd5\x7f\x0b\x86\x8f\xe1\xab\x20\x6e\x57\x67\x31\xc5\xeb\xfd\xec\x7f\x06\x39\x8a\xf5\xe7\x37\x2e\xcf\x6f\xda\x35\x07\x38\xcb\x0f\xb0\xc4\x03\xcc\xef\x8d\x1d\xe1\xa3\x47\x98\x5a\xea\x7f\x03\x9a\xfc\x1f\x19\x77\xd3\x10\xff\xdd\x08\xf3\x3f\xb0\xe0\x05\x89\x5e\x9a\xcc\xdf\x46\x9d\x7e\xb7\xd1\x23\x46\xc7\x7f\xe5\x7d\xf6\x18\x30\x5f\x5a\x17\x3c\xf5\x67\x00\xe8\x14\xf9\x11\x42\xf5\x6b\x0f\xb3\xa8\xb1\x57\x18\xc2\xaa\x3b\x3a\x27\xd6\x47\x3e\xcb\x93\x20\x28\x74\xf6\x1a\x1d\x7d\x30\x42\xad\xf5\xda\x0f\x65\x59\xf6\x96\x13\x13\x30\xaf\x1a\x90\xac\xe5\x66\x37\x17\xb0\xe6\x24\xec\x42\xd6\x77\x0f\x20\xeb\xb3\x53\xc8\xfa\xa9\x9a\xb7\xe8\x82\x07\xac\x82\x5c\x8b\x38\x38\x71\xd7\xe8\x21\x0b\x94\x7a\x3c\xc5\xf4\x9c\x27\x14\xa6\x24\xea\x82\xf5\x32\xcf\xaf\x9d\x57\x9d\x15\x55\x8f\x74\xd5\xe3\xa5\xa5\x2d\x48\xc7\x9c\x7c\x9b\x02\x87\x44\x07\x5d\xca\xba\x28\x25\xaa\x31\x5e\x29\xa2\xa7\xd1\xde\xb4\xeb\x70\x01\xdd\xae\x93\x09\x18\x37\x22\x97\x58\x90\x40\xa0\x9a\xc3\x44\x94\xb8\xec\xde\xd3\x8c\x79\xd2\x5a\xd1\x63\x28\x46\xee\x08\xde\x99\x27\x59\x27\xcd\xcc\xc3\x8c\xc5\xb2\x23\x93\x8e\x4e\x8f\xbe\xc4\x92\x8f\x2c\x0a\x6c\xdb\x5e\x6f\x2a\x7a\xc2\xc8\x01\x27\x33\x72\xa9\xe7\x5a\xb5\xf0\x7b\x99\xc4\x41\x28\xc6\x9a\xd6\xb8\x3f\xec\x03\x4e\x8e\x13\x0a\xde\x13\xdb\x7a\xad\xbf\x96\xef\x3b\x26\x90\x59\xc3\x1c\x37\xe9\x25\x8c\xb9\x61\xca\xa3\xc0\x68\x9b\x4a\xc3\x7f\x73\xac\x36\xd0\x1c\xfc\xdc\x37\x31\xf4\xd0\x66\xa0\xf5\x2b\xe8\x60\xb0\x41\xac\x87\x26\xde\x97\x76\x8e\xd6\x82\xf1\x5c\xa3\xf4\xcb\xc7\x16\x85\x9b\x35\xe3\xfc\xdf\xe9\x71\xbf\x1a\x62\xca\x64\x70\x58\xa5\x27\x17\x5d\x64\x42\x52\x0b\x44\x9f\x9d\xd6\x45\x4f\x5d\x8e\x5c\x11\xd6\x70\x0f\x56\xc4\x50\xd3\x85\xc9\xb5\xaf\x7b\xc1\x08\x55\xa8\x22\x71\x7d\xf8\x8b\xee\xe2\xcb\x21\xe4\x65\x05\xba\x4e\x8c\xc6\x93\x52\x38\xf5\x88\xa0\x8d\x91\xd6\x97\xe6\xb9\x5e\x0f\xb0\x4e\xcc\x5b\xf1\xca\x37\x60\xbf\x8b\x4e\x29\x95\x41\xe9\x89\x5b\x4b\x44\xe4\xdf\xd9\xff\x2b\x1e\x71\xad\xf0\x56\xab\x2a\x56\xd4\xb0\xa5\x5e\xe7\x93\xa7\x36\xe8\x9a\x52\x38\xf4\x48\x3c\xc2\xbb\xe9\x49\x17\x04\x3c\x18\x52\x6a\xef\xc8\x42\x2d\x2a\x17\x14\xae\xbb\x0d\xc1\xfa\xcc\x1a\xca\x9a\xea\x87\x57\xe2\x09\x62\x48\x0b\x13\x51\xb4\xcd\xa0\x8e\xcd\x17\xda\xc2\x74\xad\xe9\xa8\x7b\xb2\x30\x26\xa3\x78\xe1\x49\x29\xba\xd8\x8f\xbb\xab\x3e\xcb\xe5\x14\x1b\x3a\x3a\x57\x5b\xae\x80\xec\xa6\x9b\x47\x0a\x50\x2f\x28\x9a\xf1\x2e\x30\x68\x8c\x71\xe4\xcd\xa1\x2a\x37\x0d\x5a\xd5\xb2\xc8\x56\x2d\xcb\x4e\xb7\x9a\x43\xe4\x68\x8d\x94\x81\xb6\x6a\x32\x0f\x4b\x7a\xe2\xc1\x01\xaf\xeb\x32\x44\x71\x39\x50\x09\xd2\x98\xf9\xe4\x1d\x23\xb7\x1e\x6a\xc3\x96\x4c\x78\x5c\xe6\x5f\xf0\x0e\xfe\xdb\x9b\x84\x51\x94\xcc\xcc\x0f\x33\x52\x83\x06\x10\x4f\xca\x64\xb2\xe4\x88\xa5\x2f\x96\x63\xc3\x82\x2f\x5a\x3e\xf7\x1d\xef\x16\x16\x14\x8e\x57\x55\x48\x61\x40\x74\x72\x8a\x8a\xb2\xbc\xc5\xcc\xaf\x40\x5c\xe6\x1e\xf5\x48\x01\xdf\x40\x2d\xde\xaa\xf2\xfa\x5e\x5d\x34\xc6\xa7\xc0\x5e\xe3\xbe\x77\x05\x0a\x86\x8a\x7b\x81\xd7\xff\x9b\xe9\xe0\x07\xbc\xfa\xc7\x0b\xdd\xbe\xeb\x2f\x5a\x29\xe2\x59\xd7\x18\x56\x5d\x69\xf2\xe3\x5a\x14\x5e\xde\x4b\x85\x54\xe1\x43\x45\x32\x6b\xf2\xf0\x56\xd2\xe1\x26\xca\x88\x58\x77\x29\xfa\xc2\x8c\x5c\x75\x9b\x78\xd3\xa5\x9b\xba\xde\x70\x70\xb7\x43\x97\x28\x64\xbd\xf3\x66\x7e\x52\xb4\x86\x5f\xf8\x50\x44\xe5\x37\xd1\x5f\xbe\xea\x7b\x78\x7a\x07\x53\x58\x7a\xfb\x41\x2d\xe0\x42\x61\xb8\x7a\x8a\x4c\xf5\x7f\x9a\x5c\x7c\xe0\xe4\xac\xfb\x3f\x45\x2b\x56\x3a\x5f\x26\x14\xb2\xdc\xc4\x97\x5d\xb0\xde\xbd\xb2\x4c\x34\x33\xbf\x78\xa7\xa9\x7a\x87\x49\x2c\x3a\xc7\xd2\x03\x5a\x14\x2b\xa6\x60\xa9\x90\x9d\x96\x85\x5a\x05\xe9\x2f\x37\xae\x94\x6b\x5d\xa5\xdf\x71\xe7\xe6\xbb\xec\xa4\x2c\xcb\x59\x6d\x5d\x70\x5c\x1d\x90\xe4\xf8\x5a\xe1\x25\xb3\x33\x45\x69\xae\x60\xb6\x60\xd9\xd8\xe5\x3c\x81\x77\x9e\xb9\x1d\xa1\x20\x75\xca\x1b\x10\x8a\x08\x72\x4d\x04\x5f\x77\x41\xc2\x83\x41\x41\x04\x17\x14\xf6\x0c\x7b\xb7\x69\x51\xd8\xd7\xcf\x5e\xe2\x2b\x10\xfa\xa8\x7f\x4d\xd4\x3e\xc1\xae\xa9\x66\xec\x58\xe0\x54\xff\x8e\xd9\xd4\xa2\xf0\xae\xeb\xfc\xbc\xb1\xcb\xbc\x06\xe0\x26\xb6\xd1\x79\x5b\x0b\xf8\xa4\x4b\xf5\x5b\x6b\x01\x87\x79\xed\x22\x3e\x30\x96\x1c\xe5\xbf\x16\x70\x52\xd4\x28\x92\x22\x61\x8d\xfc\xd7\x02\x6e\x8b\x1a\x26\x5d\x0b\x96\xeb\xe7\x05\xbc\xea\xb6\xc7\x21\x6b\x04\xfd\x6e\x1d\xec\xdf\x75\x6b\x20\xff\xa9\x9b\x13\x49\xc3\x0e\x18\x13\xc5\x65\xcb\xc4\x93\x6e\x2d\x9d\xe7\x6d\xb7\x96\x6d\xe2\x53\xb7\x0e\xef\x87\xdd\xc5\x42\x5f\x43\x8d\xac\x4e\xe9\xad\xba\xa0\xf0\xf6\x4e\x6e\x7b\x29\x2a\xc3\xc6\x8c\xbc\x32\x08\x78\xd5\xb9\xb8\x12\x0d\xe1\x96\x11\xd1\xbf\xc1\x40\x34\xc6\x5b\xa6\xe6\x3b\xa3\x01\xe3\x0d\x12\xfa\x77\x5d\x38\xe9\xc2\x6d\x17\x0e\xbb\xa0\x56\x3c\x0f\xbc\x37\x11\xc9\x98\xcb\x4b\x9e\xa5\xfd\x30\x79\xe4\x27\x5e\xaa\x37\x3f\x8c\x2f\xf4\xc3\x98\xc5\xec\x82\x8b\x47\x7a\x6b\x76\x79\x34\xb1\x16\xdf\x29\x1c\xac\xc7\xe1\x4b\x96\xf7\x46\x96\x60\xbe\x8f\x4e\x6d\xd6\x36\xba\x93\xe5\xbe\x2b\x26\x46\x8f\x58\xbd\xa5\x85\x0f\x1e\x89\xfb\xde\x69\x1d\x6f\x1b\xc3\xa7\xce\xa4\x22\x56\x1c\x78\x55\x9b\xdf\x0f\x5d\x27\x14\xc4\x12\x18\x7e\xe4\x4b\xb7\x3d\xf1\xfa\x67\x0f\x2c\x5c\x4e\xd5\x07\x46\x76\x82\x0f\x5d\x62\xa5\x72\x1e\xf1\xf4\x92\x73\x59\x58\x57\x45\x09\xf3\xd1\xb2\x4a\x10\x0f\x63\x3a\x17\x06\x71\x5c\x88\x44\x98\xa2\x38\x23\xd6\x1b\x16\x46\xdc\x57\x74\x58\xb5\xe9\xbc\x3c\x3a\xea\x04\x22\x19\xeb\xf4\x45\xd4\xf8\x37\xea\x18\xa3\x87\x31\xf9\xe9\xbd\xb2\xaf\xc0\x3b\xb0\xcf\x19\x78\x87\x76\x23\x63\xd9\x1d\x29\x32\xa2\x89\x2d\x5b\xd8\xea\xc7\x36\x30\x7b\x4e\xbe\x46\x60\xfd\x97\x05\x24\xd6\x59\xd3\xd8\x13\xd0\xef\x46\x96\xe2\x27\x8e\xd1\xe1\xef\x7d\xa4\xd8\x8a\x0c\x76\x15\x69\x98\x8f\xca\x90\x8b\x76\x19\x87\x31\xee\xbb\x9e\xe2\x2f\xfb\x2c\xc3\xf8\xf4\xe0\x6d\xdb\x5d\x49\x7e\x08\x0a\xde\x33\x7b\x3f\x02\xdf\x6d\x18\x99\xe2\x43\x0e\xc9\x9f\xef\xf5\xd2\x23\x07\xb0\x53\x01\x19\x6b\xb1\x58\xd0\xe7\x2c\x71\x7e\x7e\x60\x61\x6c\xff\x0c\xe3\x50\xda\x3f\x04\x39\x0c\x29\x19\xa8\x8f\xc4\xfd\xd7\xd1\x78\x94\xf7\xdb\x31\xe6\x53\x41\x22\x08\xf2\xe8\x9d\x30\xee\x48\x8a\x7f\xc4\x08\x43\x3c\x59\x8e\xc3\x47\x13\xf2\x84\xda\x31\x11\x7f\xf2\xef\x20\xff\xe4\xdf\xa9\xad\x1e\x1d\xf5\xb8\x20\xd8\x25\xb0\x84\xda\xf8\xe4\xb0\x64\x41\x14\x1b\x44\x9f\xff\xbf\x01\x00\x00\xff\xff\x01\x30\xcf\xe3\xfa\xaf\x01\x00"), }, "/templates": &vfsgen۰DirInfo{ name: "templates", @@ -163,9 +161,9 @@ var Assets = func() http.FileSystem { "/templates/default.tmpl": &vfsgen۰CompressedFileInfo{ name: "default.tmpl", modTime: time.Date(1970, 1, 1, 0, 0, 1, 0, time.UTC), - uncompressedSize: 5951, + uncompressedSize: 8101, - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\xec\x58\x4f\x6f\xbb\x46\x10\xbd\xf3\x29\x56\xce\x25\x3e\x98\xf4\x1c\x29\xaa\xa2\xaa\xed\x25\xaa\x2a\x47\xee\xa5\xaa\xd0\x1a\xc6\x64\xe3\xfd\x43\x76\x07\x27\x16\xe6\xbb\x57\x0b\xc4\x06\x2f\x38\x8b\xe3\xdf\xe9\xe7\x5b\xd8\xcc\xbc\x99\x7d\x6f\x98\x19\x5c\x14\x24\x81\x15\x93\x40\x26\x51\x44\x39\x68\x14\x54\xd2\x14\xf4\x84\x94\xe5\x63\xeb\xb9\x28\x08\xc8\x84\x94\x65\x30\xe8\xb2\x98\x3f\x59\xaf\xa2\x20\xe1\xef\x1f\x08\x5a\x52\xbe\x98\x3f\x91\xb2\xbc\xbb\xb9\xab\xec\xcc\xaf\x1a\x62\x60\x1b\xd0\x0f\xd6\x68\xde\x3c\x90\x1d\xc9\x35\x7f\xcb\x41\x6f\x6b\xf7\x26\x50\x37\x92\xc9\x97\xaf\x10\xa3\x8d\xf0\xaf\xf5\x7e\x46\x8a\xb9\x21\x3b\x82\x6a\x91\x65\xa0\x6b\x57\xb6\x22\xf0\xb6\xff\xe7\x64\xc5\x34\x93\xa9\xf5\xb9\xb7\x3e\xd5\x85\x4c\xf8\x47\x75\x4a\x76\x84\x83\x6c\x47\xfc\x8f\x58\xa3\x3f\xb5\xca\xb3\x27\xba\x04\x6e\xc2\x67\xa5\x11\x92\xbf\x29\xd3\x26\xfc\x87\xf2\x1c\x6c\xc0\x57\xc5\x24\x99\x10\x8b\x4a\xea\x90\x29\x92\x5b\x8b\x15\xfe\xa6\x84\x50\xb2\x76\x9e\x36\x67\x2d\xbc\x29\x29\xcb\xdb\xa2\x20\xef\x0c\x5f\xba\xc6\xe1\x1c\x84\xda\x40\x37\xfa\x5f\x54\x80\x69\x18\xed\x8b\xbe\x4f\x7c\xba\xff\x6b\x40\xa6\x04\x4c\xac\x59\x86\x4c\xc9\xc9\x09\x8e\x11\x3e\xb0\x96\x34\xe2\xcc\x60\x63\xaa\xa9\x4c\x81\x84\xa4\x2c\xeb\xbc\xee\x83\xc3\xa1\xcb\x93\x65\x65\x56\x11\x69\xd3\xb7\x4f\x0f\x64\x7f\x81\x26\xb1\x3a\xf8\xa3\x94\x0a\xa9\xcd\xa9\x03\xd9\x3a\x3e\x0f\xf7\x59\xe5\x3a\x86\xfb\x5a\x4c\x90\xa0\x29\x2a\x5d\x57\x62\xd0\x43\xd4\x49\x0a\x22\x41\xf5\x3a\x51\xef\xd2\xe1\x22\xf0\x25\xc3\x33\xeb\x60\x3c\x1d\xbe\xc8\x5e\x84\x04\xfd\x8c\x18\x4e\xe3\x75\x98\xc0\x8a\xe6\x1c\x43\x64\xc8\xa1\xa1\x02\x41\x64\x9c\x62\xf7\xe5\x0c\x87\x6a\xb0\x8b\x93\x1b\xdb\x1e\x44\x1f\x54\xb7\x09\x79\xe2\xad\x28\xe7\x4b\x1a\xaf\x1d\xbc\xde\xf4\x2d\x28\xd9\x91\xaf\x0c\x39\x93\x6b\xef\x0c\xe2\x26\x03\x96\x4c\xfc\x1c\x32\x0d\xb6\xd6\x3c\xad\x5b\x09\x9d\x64\xac\xea\xc1\x9e\x29\xb3\x58\x49\x10\xea\x95\x4d\xfc\xed\x73\xcd\x7d\x33\xf6\xbf\xdc\x4a\x29\xac\x27\x4e\xab\x06\xdb\xe6\x99\xbd\x5a\x92\xe3\x76\xef\xe2\x36\xb4\x71\xe5\xe8\x22\xc6\x9c\x81\xc4\xf3\x0b\x72\x08\xf1\x30\x15\xcf\xd3\xcc\xc5\x65\xd2\x20\x95\x31\x98\x1e\x5c\xa7\x83\x87\xc3\xac\xaa\xcc\xa4\x20\x19\xec\x81\x05\x18\x43\xd3\xf3\xde\x6f\x07\xcc\x55\xa8\x19\x78\x03\x0d\xad\x77\xc2\x05\x47\xf3\xb5\x33\xc0\xa7\xe4\x17\x32\xb3\x8d\xb3\x3a\x24\xf5\x61\xd5\x3a\x4f\x33\xd2\xdd\x02\xaa\x20\xb3\xd6\x8d\x7a\xe2\xcd\xc1\x28\xbe\x81\xe4\x28\xe2\xe7\xb1\x7f\xcc\x4f\x0f\x27\xea\xcc\x87\x52\x53\xf5\xf1\xf1\xd5\xd4\x51\xfd\x1d\xe2\x17\x8a\x63\x35\x0f\xae\xfa\x9d\xd0\xaf\xbd\x28\x2f\x34\x77\xf0\x7a\xf5\x19\x50\xfd\x48\x1f\x54\x91\x1d\x96\x83\x9d\xd4\x35\xcf\xa8\xc6\xed\x08\x7b\xa4\xa9\xaf\x35\x4d\x41\x62\x74\x3c\xe2\xba\xf5\xb5\x61\x31\x2a\xad\x32\x73\x28\x5b\xa4\x08\x51\xb7\xd0\xae\xb5\x34\xae\x17\xb8\xac\x82\x44\x86\xdb\x28\x61\x26\xe3\x74\x1b\x0d\x6c\x53\x5f\x37\x6e\x17\x59\x28\xc9\x50\x59\x42\x22\x54\x8a\x8f\x1c\x89\x9d\xd9\x95\x9b\x17\xb5\x01\x7d\x81\xfd\xd1\x81\xfa\xf1\xf5\x74\x99\x72\xf2\xaf\xa6\xcb\x15\x93\xbb\xd2\x9f\x62\xf2\xb0\xd3\x8d\x99\x29\xed\x6d\x4e\xb6\x5e\xf6\xc3\x67\xfa\xf8\x6f\x84\x16\xce\x55\xde\x31\xf2\xb6\x59\x44\xe0\x90\x6a\x2a\xfa\xa8\xfc\x69\x49\x49\x98\x89\x95\x4e\x2e\xd0\x88\x8e\x91\xae\xec\xda\x35\x61\x09\x1f\xd7\x57\xf7\xdb\x3c\x0a\x83\x40\x45\xbb\x99\x0a\x41\xf5\xf6\xac\x3a\x3d\xc6\x3a\xbf\xe2\x1d\xa4\xe6\xcb\xde\x47\xa6\x1b\x32\x4a\xa8\xd6\xcf\x6d\xdf\x56\x6c\x1f\xda\x57\xb3\x9e\xe0\x5f\x89\xf7\x7f\x00\x00\x00\xff\xff\xf6\x0e\x88\xb1\x3f\x17\x00\x00"), + compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\xec\x59\xcf\x6f\xeb\x36\x0c\xbe\xe7\xaf\x20\xfc\x76\x68\x0e\xf5\x1b\x76\x2c\x50\x0c\x0f\xc3\x7e\x1c\xba\x61\x68\xd1\x5d\x86\x21\x50\x6d\xc6\x55\x2b\x4b\xae\x44\x27\x0d\xd2\xfc\xef\x83\x6c\xc7\x91\x2d\x27\x91\xd3\xec\xb4\xdc\x12\x99\xfc\x48\x7f\x1f\x4d\xca\xf2\x7a\x0d\x29\xce\xb9\x44\x88\x66\x33\x26\x50\x53\xce\x24\xcb\x50\x47\xb0\xd9\x7c\x73\xfe\xaf\xd7\x80\x32\x85\xcd\x66\xb2\xd7\xe5\xf1\xfe\xce\x7a\xad\xd7\x10\xff\xfc\x4e\xa8\x25\x13\x8f\xf7\x77\xb0\xd9\x7c\xfd\xf2\xb5\xb2\x33\x3f\x6a\x4c\x90\x2f\x50\xdf\x5a\xa3\xfb\xe6\x0f\x7c\x40\xa9\xc5\x5b\x89\x7a\x55\xbb\x37\x81\xba\x91\x4c\xf9\xf4\x82\x09\xd9\x08\x7f\x5b\xef\x07\x62\x54\x1a\xf8\x00\x52\x8f\x45\x81\xba\x76\xe5\x73\xc0\xb7\xf6\x62\x34\xe7\x9a\xcb\xcc\xfa\xdc\x58\x9f\xea\x86\x4c\xfc\x4b\xb5\x0a\x1f\x20\x50\xba\x11\xff\x01\x6b\xf4\xab\x56\x65\x71\xc7\x9e\x50\x98\xf8\x41\x69\xc2\xf4\x4f\xc6\xb5\x89\xff\x62\xa2\x44\x1b\xf0\x45\x71\x09\x11\x58\x54\xa8\x43\x66\x04\x57\x16\x2b\xfe\x49\xe5\xb9\x92\xb5\xf3\xb4\x59\x73\xf0\xa6\xb0\xd9\x5c\xad\xd7\xb0\xe4\xf4\xdc\x35\x8e\xef\x31\x57\x0b\xec\x46\xff\x83\xe5\x68\x1a\x46\x87\xa2\xb7\x89\x4f\xdb\x5f\x7b\x64\x4a\xd1\x24\x9a\x17\xc4\x95\x8c\x0e\x70\x4c\xf8\x4e\xb5\xa4\x33\xc1\x0d\x35\xa6\x9a\xc9\x0c\x21\x86\xcd\xa6\xce\xeb\x66\xb2\x5b\xf4\x79\xb2\xac\x5c\x57\x44\xda\xf4\xed\xbf\x5b\x68\x6f\xa0\x49\xac\x0e\xfe\x4d\x4a\x45\xcc\xe6\xd4\x81\x74\x96\x4f\xc3\x7d\x50\xa5\x4e\xf0\xa6\x16\x13\x25\x6a\x46\x4a\xd7\x95\x38\x19\x20\xea\x20\x05\xb3\x9c\xe9\xd7\x54\x2d\xa5\xc7\xc5\x24\x94\x8c\xc0\xac\x27\xe3\xe9\x08\x45\x0e\x22\x64\x32\xcc\x88\x11\x2c\x79\x8d\x53\x9c\xb3\x52\x50\x4c\x9c\x04\x36\x54\x10\xe6\x85\x60\xd4\x7d\x38\xe3\x7d\x35\xd8\xc5\x29\x8d\x6d\x0f\xf9\x10\x54\xb7\x09\x05\xe2\xcd\x99\x10\x4f\x2c\x79\xf5\xf0\x06\xd3\xb7\xa0\xf0\x01\xc7\x0c\x05\x97\xaf\xc1\x19\x24\x4d\x06\x3c\x8d\xc2\x1c\x0a\x8d\xb6\xd6\x02\xad\x9d\x84\x0e\x32\x56\xf5\xe0\xc0\x94\x79\xa2\x24\xe6\xea\x85\x47\xe1\xf6\xa5\x16\xa1\x19\x87\xdf\xdc\x5c\x29\xaa\x27\x8e\x53\x83\xae\x79\x61\x6f\x2d\x2d\x69\xd5\xba\xf8\x0d\x6d\x5c\x39\xfa\x88\x89\xe0\x28\xe9\xf4\x82\xdc\x87\xb8\x9b\x8a\xa7\x69\xe6\xe3\x72\x69\x88\xc9\x04\xcd\x00\xae\xd7\xc1\xe3\xfd\xac\xaa\xc2\x64\x28\x39\xb6\xc0\x39\x1a\xc3\xb2\xd3\x9e\x6f\x0f\xcc\x57\xa8\x19\x78\x7b\x1a\xda\xe0\x84\x9b\xf4\xe6\x6b\x67\x80\x4f\xe1\x7b\xb8\xb6\x8d\xb3\x5a\x84\x7a\xb1\x6a\x9d\x87\x19\xe9\xee\x02\xaa\x20\xd7\xce\x1d\x0d\xc4\xbb\x47\xa3\xc4\x02\xd3\x5e\xc4\xed\x72\x78\xcc\xad\x87\x17\xf5\x3a\x84\x52\x53\xf5\xf1\xf1\xd5\xd4\x51\x7d\x89\xc9\x33\xa3\xb1\x9a\x4f\x2e\xfa\x1d\xd0\xcf\xdd\x28\x3f\x6a\xe1\xe1\x0d\xea\xb3\x47\xf5\x9e\x3e\xa4\x66\x76\x58\xee\xed\xa4\xbe\x79\xc1\x34\xad\x46\xd8\x13\xcb\x42\xad\x59\x86\x92\x66\xfd\x11\xd7\xad\xaf\x05\x4f\x48\x69\x55\x98\x5d\xd9\x12\x23\x9c\x75\x0b\xed\x52\x4b\xe3\x7a\x81\xcf\x2a\x4a\xe2\xb4\x9a\xa5\xdc\x14\x82\xad\x66\x7b\x76\x53\xc7\x1b\xb7\x8f\x9c\x2b\xc9\x49\x59\x42\x66\xa4\x94\x18\x39\x12\x3b\xb3\xab\x34\xcf\x6a\x81\xfa\x0c\xfb\x47\x0f\xea\xbf\xaf\xa7\xf3\x94\x53\x78\x35\x9d\xaf\x98\xfc\x2d\xfd\x21\x26\x77\x7b\xba\x31\x33\xc5\xdd\xcd\x49\xe7\x61\xdf\xbd\xa6\x8f\x7f\x47\x70\x70\x2e\xf2\x8e\x91\xd7\x65\x91\x50\x60\xa6\x59\x3e\x44\xe5\xff\x96\x94\x94\x9b\x44\xe9\x74\xb7\x37\x57\x92\x76\xdb\x7d\xbf\x14\xfb\xf6\xa7\x37\xae\x3e\xd2\x45\x0d\xbb\xad\x78\xc2\xf7\xcb\xa3\xfe\x69\x1e\x73\x43\xc8\x72\xb7\xf9\xe6\x39\xd3\xab\x93\xea\xb4\x8f\x75\x7a\xc5\x7b\x48\xcd\x49\x40\x88\x4c\x5f\x60\x94\x50\xce\xf1\xdc\xa7\x15\x6b\x43\x87\x6a\x36\x10\xfc\x04\xf1\x16\x3f\x9c\x8f\x72\x17\xeb\x42\xfa\x10\xe9\x2f\x5c\xb3\xb3\x3c\x2e\x1d\xa0\xde\x59\xc7\x85\xf3\x49\xf5\x1a\x33\xc8\x55\xa1\xb9\xd2\xdc\xbe\xa1\x5e\x37\x6f\x3b\xdf\x6d\x97\xe0\xe6\x16\xa2\x68\xfb\x12\xb4\x3d\xff\xee\xdc\xad\xf5\x01\x00\xa8\xfc\x0c\x2e\x70\xeb\xc7\x65\x8a\xef\xdb\x23\x78\x88\xb6\x97\xa2\x8e\x07\x9f\xc3\x15\xbe\x39\x8e\x51\xa2\x39\xf1\x84\x89\x68\xda\x1a\xb6\xf0\x6d\x5a\xb7\x10\xfd\xc6\xb3\xe7\x2e\x16\x0a\x83\x15\x20\x93\x69\x1f\x75\xc9\xb4\xe4\x32\x8b\xa6\x70\x25\xd1\x01\xaa\x61\xa6\x47\x62\xfd\x8e\x29\x2f\xf3\xf0\x68\x5c\xce\x95\x0d\x65\x57\x77\xa1\x8e\x86\xb9\x53\xcb\x5e\x0c\x99\xb6\x9a\xb8\xbf\xeb\x6f\x6a\x2e\x74\xc7\xad\xab\x53\x5b\x18\x5e\xec\x51\x6a\x8d\x56\x2c\x40\xb5\xb3\x2b\x17\xa4\xde\xf9\x14\x3c\xae\x62\x5f\xc9\x63\xca\xee\x90\xfa\x57\xdd\x56\xa7\x55\xf2\x8a\xd4\x3d\x36\x3a\x79\x52\x0d\x80\x31\xc1\x99\x39\xfd\xe0\x7d\x5f\x7a\x9f\xfe\x5a\x32\x00\x7c\xf8\x73\xc9\x80\xc3\xb1\x6f\x26\x43\xc9\x7b\x1f\x4e\xfe\x0d\x00\x00\xff\xff\x74\x5d\xc4\xb5\xa5\x1f\x00\x00"), }, "/templates/email.tmpl": &vfsgen۰CompressedFileInfo{ name: "email.tmpl", @@ -305,7 +303,7 @@ func (f *vfsgen۰CompressedFile) Read(p []byte) (n int, err error) { } if f.grPos < f.seekPos { // Fast-forward. - _, err = io.CopyN(ioutil.Discard, f.gr, f.seekPos-f.grPos) + _, err = io.CopyN(io.Discard, f.gr, f.seekPos-f.grPos) if err != nil { return 0, err } diff --git a/vendor/github.com/prometheus/alertmanager/cluster/channel.go b/vendor/github.com/prometheus/alertmanager/cluster/channel.go index ba0b834cd1..e608d464e7 100644 --- a/vendor/github.com/prometheus/alertmanager/cluster/channel.go +++ b/vendor/github.com/prometheus/alertmanager/cluster/channel.go @@ -14,11 +14,10 @@ package cluster import ( + "log/slog" "sync" "time" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/gogo/protobuf/proto" "github.com/hashicorp/memberlist" "github.com/prometheus/client_golang/prometheus" @@ -35,7 +34,7 @@ type Channel struct { sendOversize func(*memberlist.Node, []byte) error msgc chan []byte - logger log.Logger + logger *slog.Logger oversizeGossipMessageFailureTotal prometheus.Counter oversizeGossipMessageDroppedTotal prometheus.Counter @@ -50,7 +49,7 @@ func NewChannel( send func([]byte), peers func() []*memberlist.Node, sendOversize func(*memberlist.Node, []byte) error, - logger log.Logger, + logger *slog.Logger, stopc chan struct{}, reg prometheus.Registerer, ) *Channel { @@ -70,9 +69,13 @@ func NewChannel( ConstLabels: prometheus.Labels{"key": key}, }) oversizeGossipDuration := prometheus.NewHistogram(prometheus.HistogramOpts{ - Name: "alertmanager_oversize_gossip_message_duration_seconds", - Help: "Duration of oversized gossip message requests.", - ConstLabels: prometheus.Labels{"key": key}, + Name: "alertmanager_oversize_gossip_message_duration_seconds", + Help: "Duration of oversized gossip message requests.", + ConstLabels: prometheus.Labels{"key": key}, + Buckets: prometheus.DefBuckets, + NativeHistogramBucketFactor: 1.1, + NativeHistogramMaxBucketNumber: 100, + NativeHistogramMinResetDuration: 1 * time.Hour, }) reg.MustRegister(oversizeGossipDuration, oversizeGossipMessageFailureTotal, oversizeGossipMessageDroppedTotal, oversizeGossipMessageSentTotal) @@ -109,7 +112,7 @@ func (c *Channel) handleOverSizedMessages(stopc chan struct{}) { c.oversizeGossipMessageSentTotal.Inc() start := time.Now() if err := c.sendOversize(n, b); err != nil { - level.Debug(c.logger).Log("msg", "failed to send reliable", "key", c.key, "node", n, "err", err) + c.logger.Debug("failed to send reliable", "key", c.key, "node", n, "err", err) c.oversizeGossipMessageFailureTotal.Inc() return } @@ -135,7 +138,7 @@ func (c *Channel) Broadcast(b []byte) { select { case c.msgc <- b: default: - level.Debug(c.logger).Log("msg", "oversized gossip channel full") + c.logger.Debug("oversized gossip channel full") c.oversizeGossipMessageDroppedTotal.Inc() } } else { diff --git a/vendor/github.com/prometheus/alertmanager/cluster/cluster.go b/vendor/github.com/prometheus/alertmanager/cluster/cluster.go index d85ee7f533..14052b2d9b 100644 --- a/vendor/github.com/prometheus/alertmanager/cluster/cluster.go +++ b/vendor/github.com/prometheus/alertmanager/cluster/cluster.go @@ -17,6 +17,7 @@ import ( "context" "errors" "fmt" + "log/slog" "math/rand" "net" "sort" @@ -25,8 +26,6 @@ import ( "sync" "time" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/hashicorp/memberlist" "github.com/oklog/ulid" "github.com/prometheus/client_golang/prometheus" @@ -42,7 +41,7 @@ type ClusterPeer interface { Peers() []ClusterMember } -// ClusterMember interface that represents node peers in a cluster +// ClusterMember interface that represents node peers in a cluster. type ClusterMember interface { // Name returns the name of the node Name() string @@ -82,7 +81,7 @@ type Peer struct { peerUpdateCounter prometheus.Counter peerJoinCounter prometheus.Counter - logger log.Logger + logger *slog.Logger } // peer is an internal type used for bookkeeping. It holds the state of peers @@ -129,7 +128,7 @@ const ( ) func Create( - l log.Logger, + l *slog.Logger, reg prometheus.Registerer, bindAddr string, advertiseAddr string, @@ -171,19 +170,19 @@ func Create( if err != nil { return nil, fmt.Errorf("resolve peers: %w", err) } - level.Debug(l).Log("msg", "resolved peers to following addresses", "peers", strings.Join(resolvedPeers, ",")) + l.Debug("resolved peers to following addresses", "peers", strings.Join(resolvedPeers, ",")) // Initial validation of user-specified advertise address. addr, err := calculateAdvertiseAddress(bindHost, advertiseHost, allowInsecureAdvertise) if err != nil { - level.Warn(l).Log("err", "couldn't deduce an advertise address: "+err.Error()) + l.Warn("couldn't deduce an advertise address: " + err.Error()) } else if hasNonlocal(resolvedPeers) && isUnroutable(addr.String()) { - level.Warn(l).Log("err", "this node advertises itself on an unroutable address", "addr", addr.String()) - level.Warn(l).Log("err", "this node will be unreachable in the cluster") - level.Warn(l).Log("err", "provide --cluster.advertise-address as a routable IP address or hostname") + l.Warn("this node advertises itself on an unroutable address", "addr", addr.String()) + l.Warn("this node will be unreachable in the cluster") + l.Warn("provide --cluster.advertise-address as a routable IP address or hostname") } else if isAny(bindAddr) && advertiseHost == "" { // memberlist doesn't advertise properly when the bind address is empty or unspecified. - level.Info(l).Log("msg", "setting advertise address explicitly", "addr", addr.String(), "port", bindPort) + l.Info("setting advertise address explicitly", "addr", addr.String(), "port", bindPort) advertiseHost = addr.String() advertisePort = bindPort } @@ -225,7 +224,7 @@ func Create( cfg.TCPTimeout = tcpTimeout cfg.ProbeTimeout = probeTimeout cfg.ProbeInterval = probeInterval - cfg.LogOutput = &logWriter{l: l} + cfg.Logger = slog.NewLogLogger(l.Handler(), slog.LevelDebug) cfg.GossipNodes = retransmit cfg.UDPBufferSize = MaxGossipPacketSize cfg.Label = label @@ -239,7 +238,7 @@ func Create( } if tlsTransportConfig != nil { - level.Info(l).Log("msg", "using TLS for gossip") + l.Info("using TLS for gossip") cfg.Transport, err = NewTLSTransport(context.Background(), l, reg, cfg.BindAddr, cfg.BindPort, tlsTransportConfig) if err != nil { return nil, fmt.Errorf("tls transport: %w", err) @@ -260,12 +259,12 @@ func (p *Peer) Join( ) error { n, err := p.mlist.Join(p.resolvedPeers) if err != nil { - level.Warn(p.logger).Log("msg", "failed to join cluster", "err", err) + p.logger.Warn("failed to join cluster", "err", err) if reconnectInterval != 0 { - level.Info(p.logger).Log("msg", fmt.Sprintf("will retry joining cluster every %v", reconnectInterval.String())) + p.logger.Info(fmt.Sprintf("will retry joining cluster every %v", reconnectInterval.String())) } } else { - level.Debug(p.logger).Log("msg", "joined cluster", "peers", n) + p.logger.Debug("joined cluster", "peers", n) } if reconnectInterval != 0 { @@ -333,14 +332,6 @@ func (p *Peer) setInitialFailed(peers []string, myAddr string) { } } -type logWriter struct { - l log.Logger -} - -func (l *logWriter) Write(b []byte) (int, error) { - return len(b), level.Debug(l.l).Log("memberlist", string(b)) -} - func (p *Peer) register(reg prometheus.Registerer, name string) { peerInfo := prometheus.NewGauge( prometheus.GaugeOpts{ @@ -420,7 +411,7 @@ func (p *Peer) removeFailedPeers(timeout time.Duration) { if pr.leaveTime.Add(timeout).After(now) { keep = append(keep, pr) } else { - level.Debug(p.logger).Log("msg", "failed peer has timed out", "peer", pr.Node, "addr", pr.Address()) + p.logger.Debug("failed peer has timed out", "peer", pr.Node, "addr", pr.Address()) delete(p.peers, pr.Name) } } @@ -433,27 +424,27 @@ func (p *Peer) reconnect() { failedPeers := p.failedPeers p.peerLock.RUnlock() - logger := log.With(p.logger, "msg", "reconnect") + logger := p.logger.With("msg", "reconnect") for _, pr := range failedPeers { // No need to do book keeping on failedPeers here. If a // reconnect is successful, they will be announced in // peerJoin(). if _, err := p.mlist.Join([]string{pr.Address()}); err != nil { p.failedReconnectionsCounter.Inc() - level.Debug(logger).Log("result", "failure", "peer", pr.Node, "addr", pr.Address(), "err", err) + logger.Debug("failure", "peer", pr.Node, "addr", pr.Address(), "err", err) } else { p.reconnectionsCounter.Inc() - level.Debug(logger).Log("result", "success", "peer", pr.Node, "addr", pr.Address()) + logger.Debug("success", "peer", pr.Node, "addr", pr.Address()) } } } func (p *Peer) refresh() { - logger := log.With(p.logger, "msg", "refresh") + logger := p.logger.With("msg", "refresh") resolvedPeers, err := resolvePeers(context.Background(), p.knownPeers, p.advertiseAddr, &net.Resolver{}, false) if err != nil { - level.Debug(logger).Log("peers", p.knownPeers, "err", err) + logger.Debug(fmt.Sprintf("%v", p.knownPeers), "err", err) return } @@ -470,10 +461,10 @@ func (p *Peer) refresh() { if !isPeerFound { if _, err := p.mlist.Join([]string{peer}); err != nil { p.failedRefreshCounter.Inc() - level.Warn(logger).Log("result", "failure", "addr", peer, "err", err) + logger.Warn("failure", "addr", peer, "err", err) } else { p.refreshCounter.Inc() - level.Debug(logger).Log("result", "success", "addr", peer) + logger.Debug("success", "addr", peer) } } } @@ -502,7 +493,7 @@ func (p *Peer) peerJoin(n *memberlist.Node) { p.peerJoinCounter.Inc() if oldStatus == StatusFailed { - level.Debug(p.logger).Log("msg", "peer rejoined", "peer", pr.Node) + p.logger.Debug("peer rejoined", "peer", pr.Node) p.failedPeers = removeOldPeer(p.failedPeers, pr.Address()) } } @@ -524,7 +515,7 @@ func (p *Peer) peerLeave(n *memberlist.Node) { p.peers[n.Address()] = pr p.peerLeaveCounter.Inc() - level.Debug(p.logger).Log("msg", "peer left", "peer", pr.Node) + p.logger.Debug("peer left", "peer", pr.Node) } func (p *Peer) peerUpdate(n *memberlist.Node) { @@ -542,7 +533,7 @@ func (p *Peer) peerUpdate(n *memberlist.Node) { p.peers[n.Address()] = pr p.peerUpdateCounter.Inc() - level.Debug(p.logger).Log("msg", "peer updated", "peer", pr.Node) + p.logger.Debug("peer updated", "peer", pr.Node) } // AddState adds a new state that will be gossiped. It returns a channel to which @@ -574,7 +565,7 @@ func (p *Peer) AddState(key string, s State, reg prometheus.Registerer) ClusterC // Leave the cluster, waiting up to timeout. func (p *Peer) Leave(timeout time.Duration) error { close(p.stopc) - level.Debug(p.logger).Log("msg", "leaving cluster") + p.logger.Debug("leaving cluster") return p.mlist.Leave(timeout) } @@ -639,10 +630,10 @@ type Member struct { node *memberlist.Node } -// Name implements cluster.ClusterMember +// Name implements cluster.ClusterMember. func (m Member) Name() string { return m.node.Name } -// Address implements cluster.ClusterMember +// Address implements cluster.ClusterMember. func (m Member) Address() string { return m.node.Address() } // Peers returns the peers in the cluster. @@ -680,7 +671,7 @@ func (p *Peer) Position() int { // This is especially important for those that do not have persistent storage. func (p *Peer) Settle(ctx context.Context, interval time.Duration) { const NumOkayRequired = 3 - level.Info(p.logger).Log("msg", "Waiting for gossip to settle...", "interval", interval) + p.logger.Info("Waiting for gossip to settle...", "interval", interval) start := time.Now() nPeers := 0 nOkay := 0 @@ -689,7 +680,7 @@ func (p *Peer) Settle(ctx context.Context, interval time.Duration) { select { case <-ctx.Done(): elapsed := time.Since(start) - level.Info(p.logger).Log("msg", "gossip not settled but continuing anyway", "polls", totalPolls, "elapsed", elapsed) + p.logger.Info("gossip not settled but continuing anyway", "polls", totalPolls, "elapsed", elapsed) close(p.readyc) return case <-time.After(interval): @@ -697,15 +688,15 @@ func (p *Peer) Settle(ctx context.Context, interval time.Duration) { elapsed := time.Since(start) n := len(p.Peers()) if nOkay >= NumOkayRequired { - level.Info(p.logger).Log("msg", "gossip settled; proceeding", "elapsed", elapsed) + p.logger.Info("gossip settled; proceeding", "elapsed", elapsed) break } if n == nPeers { nOkay++ - level.Debug(p.logger).Log("msg", "gossip looks settled", "elapsed", elapsed) + p.logger.Debug("gossip looks settled", "elapsed", elapsed) } else { nOkay = 0 - level.Info(p.logger).Log("msg", "gossip not settled", "polls", totalPolls, "before", nPeers, "now", n, "elapsed", elapsed) + p.logger.Info("gossip not settled", "polls", totalPolls, "before", nPeers, "now", n, "elapsed", elapsed) } nPeers = n totalPolls++ diff --git a/vendor/github.com/prometheus/alertmanager/cluster/delegate.go b/vendor/github.com/prometheus/alertmanager/cluster/delegate.go index 9957f69b91..6783002589 100644 --- a/vendor/github.com/prometheus/alertmanager/cluster/delegate.go +++ b/vendor/github.com/prometheus/alertmanager/cluster/delegate.go @@ -14,10 +14,9 @@ package cluster import ( + "log/slog" "time" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/gogo/protobuf/proto" "github.com/hashicorp/memberlist" "github.com/prometheus/client_golang/prometheus" @@ -37,7 +36,7 @@ const ( type delegate struct { *Peer - logger log.Logger + logger *slog.Logger bcast *memberlist.TransmitLimitedQueue messagesReceived *prometheus.CounterVec @@ -49,7 +48,7 @@ type delegate struct { nodePingDuration *prometheus.HistogramVec } -func newDelegate(l log.Logger, reg prometheus.Registerer, p *Peer, retransmit int) *delegate { +func newDelegate(l *slog.Logger, reg prometheus.Registerer, p *Peer, retransmit int) *delegate { bcast := &memberlist.TransmitLimitedQueue{ NumNodes: p.ClusterSize, RetransmitMult: retransmit, @@ -104,9 +103,12 @@ func newDelegate(l log.Logger, reg prometheus.Registerer, p *Peer, retransmit in }, []string{"peer"}, ) nodePingDuration := prometheus.NewHistogramVec(prometheus.HistogramOpts{ - Name: "alertmanager_cluster_pings_seconds", - Help: "Histogram of latencies for ping messages.", - Buckets: []float64{.005, .01, .025, .05, .1, .25, .5}, + Name: "alertmanager_cluster_pings_seconds", + Help: "Histogram of latencies for ping messages.", + Buckets: []float64{.005, .01, .025, .05, .1, .25, .5}, + NativeHistogramBucketFactor: 1.1, + NativeHistogramMaxBucketNumber: 100, + NativeHistogramMinResetDuration: 1 * time.Hour, }, []string{"peer"}, ) @@ -154,7 +156,7 @@ func (d *delegate) NotifyMsg(b []byte) { var p clusterpb.Part if err := proto.Unmarshal(b, &p); err != nil { - level.Warn(d.logger).Log("msg", "decode broadcast", "err", err) + d.logger.Warn("decode broadcast", "err", err) return } @@ -166,7 +168,7 @@ func (d *delegate) NotifyMsg(b []byte) { return } if err := s.Merge(p.Data); err != nil { - level.Warn(d.logger).Log("msg", "merge broadcast", "err", err, "key", p.Key) + d.logger.Warn("merge broadcast", "err", err, "key", p.Key) return } } @@ -192,14 +194,14 @@ func (d *delegate) LocalState(_ bool) []byte { for key, s := range d.states { b, err := s.MarshalBinary() if err != nil { - level.Warn(d.logger).Log("msg", "encode local state", "err", err, "key", key) + d.logger.Warn("encode local state", "err", err, "key", key) return nil } all.Parts = append(all.Parts, clusterpb.Part{Key: key, Data: b}) } b, err := proto.Marshal(all) if err != nil { - level.Warn(d.logger).Log("msg", "encode local state", "err", err) + d.logger.Warn("encode local state", "err", err) return nil } d.messagesSent.WithLabelValues(fullState).Inc() @@ -213,7 +215,7 @@ func (d *delegate) MergeRemoteState(buf []byte, _ bool) { var fs clusterpb.FullState if err := proto.Unmarshal(buf, &fs); err != nil { - level.Warn(d.logger).Log("msg", "merge remote state", "err", err) + d.logger.Warn("merge remote state", "err", err) return } d.mtx.RLock() @@ -221,11 +223,11 @@ func (d *delegate) MergeRemoteState(buf []byte, _ bool) { for _, p := range fs.Parts { s, ok := d.states[p.Key] if !ok { - level.Warn(d.logger).Log("received", "unknown state key", "len", len(buf), "key", p.Key) + d.logger.Warn("unknown state key", "len", len(buf), "key", p.Key) continue } if err := s.Merge(p.Data); err != nil { - level.Warn(d.logger).Log("msg", "merge remote state", "err", err, "key", p.Key) + d.logger.Warn("merge remote state", "err", err, "key", p.Key) return } } @@ -233,19 +235,19 @@ func (d *delegate) MergeRemoteState(buf []byte, _ bool) { // NotifyJoin is called if a peer joins the cluster. func (d *delegate) NotifyJoin(n *memberlist.Node) { - level.Debug(d.logger).Log("received", "NotifyJoin", "node", n.Name, "addr", n.Address()) + d.logger.Debug("NotifyJoin", "node", n.Name, "addr", n.Address()) d.Peer.peerJoin(n) } // NotifyLeave is called if a peer leaves the cluster. func (d *delegate) NotifyLeave(n *memberlist.Node) { - level.Debug(d.logger).Log("received", "NotifyLeave", "node", n.Name, "addr", n.Address()) + d.logger.Debug("NotifyLeave", "node", n.Name, "addr", n.Address()) d.Peer.peerLeave(n) } // NotifyUpdate is called if a cluster peer gets updated. func (d *delegate) NotifyUpdate(n *memberlist.Node) { - level.Debug(d.logger).Log("received", "NotifyUpdate", "node", n.Name, "addr", n.Address()) + d.logger.Debug("NotifyUpdate", "node", n.Name, "addr", n.Address()) d.Peer.peerUpdate(n) } @@ -275,7 +277,7 @@ func (d *delegate) handleQueueDepth() { case <-time.After(15 * time.Minute): n := d.bcast.NumQueued() if n > maxQueueSize { - level.Warn(d.logger).Log("msg", "dropping messages because too many are queued", "current", n, "limit", maxQueueSize) + d.logger.Warn("dropping messages because too many are queued", "current", n, "limit", maxQueueSize) d.bcast.Prune(maxQueueSize) d.messagesPruned.Add(float64(n - maxQueueSize)) } diff --git a/vendor/github.com/prometheus/alertmanager/cluster/tls_transport.go b/vendor/github.com/prometheus/alertmanager/cluster/tls_transport.go index 7e39d6fed8..f7d4b13d1c 100644 --- a/vendor/github.com/prometheus/alertmanager/cluster/tls_transport.go +++ b/vendor/github.com/prometheus/alertmanager/cluster/tls_transport.go @@ -22,12 +22,11 @@ import ( "crypto/tls" "errors" "fmt" + "log/slog" "net" "strings" "time" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/hashicorp/go-sockaddr" "github.com/hashicorp/memberlist" "github.com/prometheus/client_golang/prometheus" @@ -46,7 +45,7 @@ const ( type TLSTransport struct { ctx context.Context cancel context.CancelFunc - logger log.Logger + logger *slog.Logger bindAddr string bindPort int done chan struct{} @@ -71,7 +70,7 @@ type TLSTransport struct { // a free port automatically. func NewTLSTransport( ctx context.Context, - logger log.Logger, + logger *slog.Logger, reg prometheus.Registerer, bindAddr string, bindPort int, @@ -188,7 +187,7 @@ func (t *TLSTransport) StreamCh() <-chan net.Conn { // Shutdown is called when memberlist is shutting down; this gives the // TLS Transport a chance to clean up the listener and other goroutines. func (t *TLSTransport) Shutdown() error { - level.Debug(t.logger).Log("msg", "shutting down tls transport") + t.logger.Debug("shutting down tls transport") t.cancel() err := t.listener.Close() t.connPool.shutdown() @@ -255,7 +254,7 @@ func (t *TLSTransport) listen() { return } t.readErrs.Inc() - level.Debug(t.logger).Log("msg", "error accepting connection", "err", err) + t.logger.Debug("error accepting connection", "err", err) } else { go t.handle(conn) @@ -268,7 +267,7 @@ func (t *TLSTransport) handle(conn net.Conn) { for { packet, err := rcvTLSConn(conn).read() if err != nil { - level.Debug(t.logger).Log("msg", "error reading from connection", "err", err) + t.logger.Debug("error reading from connection", "err", err) t.readErrs.Inc() return } diff --git a/vendor/github.com/prometheus/alertmanager/config/config.go b/vendor/github.com/prometheus/alertmanager/config/config.go index 7f3602e066..b8e484130a 100644 --- a/vendor/github.com/prometheus/alertmanager/config/config.go +++ b/vendor/github.com/prometheus/alertmanager/config/config.go @@ -30,7 +30,7 @@ import ( "github.com/prometheus/common/model" "gopkg.in/yaml.v2" - "github.com/prometheus/alertmanager/matchers/compat" + "github.com/prometheus/alertmanager/matcher/compat" "github.com/prometheus/alertmanager/pkg/labels" "github.com/prometheus/alertmanager/timeinterval" ) @@ -39,6 +39,11 @@ const secretToken = "" var secretTokenJSON string +// MarshalSecretValue if set to true will expose Secret type +// through the marshal interfaces. Useful for outside projects +// that load and marshal the Alertmanager config. +var MarshalSecretValue bool = commoncfg.MarshalSecretValue + func init() { b, err := json.Marshal(secretToken) if err != nil { @@ -52,6 +57,9 @@ type Secret string // MarshalYAML implements the yaml.Marshaler interface for Secret. func (s Secret) MarshalYAML() (interface{}, error) { + if MarshalSecretValue { + return string(s), nil + } if s != "" { return secretToken, nil } @@ -66,6 +74,12 @@ func (s *Secret) UnmarshalYAML(unmarshal func(interface{}) error) error { // MarshalJSON implements the json.Marshaler interface for Secret. func (s Secret) MarshalJSON() ([]byte, error) { + if MarshalSecretValue { + return json.Marshal(string(s)) + } + if len(s) == 0 { + return json.Marshal("") + } return json.Marshal(secretToken) } @@ -130,6 +144,9 @@ type SecretURL URL // MarshalYAML implements the yaml.Marshaler interface for SecretURL. func (s SecretURL) MarshalYAML() (interface{}, error) { if s.URL != nil { + if MarshalSecretValue { + return s.URL.String(), nil + } return secretToken, nil } return nil, nil @@ -153,6 +170,12 @@ func (s *SecretURL) UnmarshalYAML(unmarshal func(interface{}) error) error { // MarshalJSON implements the json.Marshaler interface for SecretURL. func (s SecretURL) MarshalJSON() ([]byte, error) { + if s.URL == nil { + return json.Marshal("") + } + if MarshalSecretValue { + return json.Marshal(s.URL.String()) + } return json.Marshal(secretToken) } @@ -165,7 +188,15 @@ func (s *SecretURL) UnmarshalJSON(data []byte) error { s.URL = &url.URL{} return nil } - return json.Unmarshal(data, (*URL)(s)) + // Redact the secret URL in case of errors + if err := json.Unmarshal(data, (*URL)(s)); err != nil { + if MarshalSecretValue { + return err + } + return errors.New(strings.ReplaceAll(err.Error(), string(data), "[REDACTED]")) + } + + return nil } // Load parses the YAML input s into a Config. @@ -258,6 +289,15 @@ func resolveFilepaths(baseDir string, cfg *Config) { for _, cfg := range receiver.MSTeamsConfigs { cfg.HTTPConfig.SetDirectory(baseDir) } + for _, cfg := range receiver.MSTeamsV2Configs { + cfg.HTTPConfig.SetDirectory(baseDir) + } + for _, cfg := range receiver.JiraConfigs { + cfg.HTTPConfig.SetDirectory(baseDir) + } + for _, cfg := range receiver.RocketchatConfigs { + cfg.HTTPConfig.SetDirectory(baseDir) + } } } @@ -274,7 +314,7 @@ func (mt *MuteTimeInterval) UnmarshalYAML(unmarshal func(interface{}) error) err return err } if mt.Name == "" { - return fmt.Errorf("missing name in mute time interval") + return errors.New("missing name in mute time interval") } return nil } @@ -292,7 +332,7 @@ func (ti *TimeInterval) UnmarshalYAML(unmarshal func(interface{}) error) error { return err } if ti.Name == "" { - return fmt.Errorf("missing name in time interval") + return errors.New("missing name in time interval") } return nil } @@ -338,19 +378,27 @@ func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error { } if c.Global.SlackAPIURL != nil && len(c.Global.SlackAPIURLFile) > 0 { - return fmt.Errorf("at most one of slack_api_url & slack_api_url_file must be configured") + return errors.New("at most one of slack_api_url & slack_api_url_file must be configured") } if c.Global.OpsGenieAPIKey != "" && len(c.Global.OpsGenieAPIKeyFile) > 0 { - return fmt.Errorf("at most one of opsgenie_api_key & opsgenie_api_key_file must be configured") + return errors.New("at most one of opsgenie_api_key & opsgenie_api_key_file must be configured") } if c.Global.VictorOpsAPIKey != "" && len(c.Global.VictorOpsAPIKeyFile) > 0 { - return fmt.Errorf("at most one of victorops_api_key & victorops_api_key_file must be configured") + return errors.New("at most one of victorops_api_key & victorops_api_key_file must be configured") } if len(c.Global.SMTPAuthPassword) > 0 && len(c.Global.SMTPAuthPasswordFile) > 0 { - return fmt.Errorf("at most one of smtp_auth_password & smtp_auth_password_file must be configured") + return errors.New("at most one of smtp_auth_password & smtp_auth_password_file must be configured") + } + + if c.Global.RocketchatToken != nil && len(c.Global.RocketchatTokenFile) > 0 { + return errors.New("at most one of rocketchat_token & rocketchat_token_file must be configured") + } + + if c.Global.RocketchatTokenID != nil && len(c.Global.RocketchatTokenIDFile) > 0 { + return errors.New("at most one of rocketchat_token_id & rocketchat_token_id_file must be configured") } names := map[string]struct{}{} @@ -365,15 +413,18 @@ func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error { } } for _, ec := range rcv.EmailConfigs { + if ec.TLSConfig == nil { + ec.TLSConfig = c.Global.SMTPTLSConfig + } if ec.Smarthost.String() == "" { if c.Global.SMTPSmarthost.String() == "" { - return fmt.Errorf("no global SMTP smarthost set") + return errors.New("no global SMTP smarthost set") } ec.Smarthost = c.Global.SMTPSmarthost } if ec.From == "" { if c.Global.SMTPFrom == "" { - return fmt.Errorf("no global SMTP from set") + return errors.New("no global SMTP from set") } ec.From = c.Global.SMTPFrom } @@ -404,7 +455,7 @@ func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error { } if sc.APIURL == nil && len(sc.APIURLFile) == 0 { if c.Global.SlackAPIURL == nil && len(c.Global.SlackAPIURLFile) == 0 { - return fmt.Errorf("no global Slack API URL set either inline or in a file") + return errors.New("no global Slack API URL set either inline or in a file") } sc.APIURL = c.Global.SlackAPIURL sc.APIURLFile = c.Global.SlackAPIURLFile @@ -421,7 +472,7 @@ func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error { } if pdc.URL == nil { if c.Global.PagerdutyURL == nil { - return fmt.Errorf("no global PagerDuty URL set") + return errors.New("no global PagerDuty URL set") } pdc.URL = c.Global.PagerdutyURL } @@ -432,7 +483,7 @@ func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error { } if ogc.APIURL == nil { if c.Global.OpsGenieAPIURL == nil { - return fmt.Errorf("no global OpsGenie URL set") + return errors.New("no global OpsGenie URL set") } ogc.APIURL = c.Global.OpsGenieAPIURL } @@ -441,7 +492,7 @@ func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error { } if ogc.APIKey == "" && len(ogc.APIKeyFile) == 0 { if c.Global.OpsGenieAPIKey == "" && len(c.Global.OpsGenieAPIKeyFile) == 0 { - return fmt.Errorf("no global OpsGenie API Key set either inline or in a file") + return errors.New("no global OpsGenie API Key set either inline or in a file") } ogc.APIKey = c.Global.OpsGenieAPIKey ogc.APIKeyFile = c.Global.OpsGenieAPIKeyFile @@ -454,21 +505,21 @@ func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error { if wcc.APIURL == nil { if c.Global.WeChatAPIURL == nil { - return fmt.Errorf("no global Wechat URL set") + return errors.New("no global Wechat URL set") } wcc.APIURL = c.Global.WeChatAPIURL } if wcc.APISecret == "" { if c.Global.WeChatAPISecret == "" { - return fmt.Errorf("no global Wechat ApiSecret set") + return errors.New("no global Wechat ApiSecret set") } wcc.APISecret = c.Global.WeChatAPISecret } if wcc.CorpID == "" { if c.Global.WeChatAPICorpID == "" { - return fmt.Errorf("no global Wechat CorpID set") + return errors.New("no global Wechat CorpID set") } wcc.CorpID = c.Global.WeChatAPICorpID } @@ -483,7 +534,7 @@ func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error { } if voc.APIURL == nil { if c.Global.VictorOpsAPIURL == nil { - return fmt.Errorf("no global VictorOps URL set") + return errors.New("no global VictorOps URL set") } voc.APIURL = c.Global.VictorOpsAPIURL } @@ -492,7 +543,7 @@ func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error { } if voc.APIKey == "" && len(voc.APIKeyFile) == 0 { if c.Global.VictorOpsAPIKey == "" && len(c.Global.VictorOpsAPIKeyFile) == 0 { - return fmt.Errorf("no global VictorOps API Key set") + return errors.New("no global VictorOps API Key set") } voc.APIKey = c.Global.VictorOpsAPIKey voc.APIKeyFile = c.Global.VictorOpsAPIKeyFile @@ -516,8 +567,8 @@ func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error { if discord.HTTPConfig == nil { discord.HTTPConfig = c.Global.HTTPConfig } - if discord.WebhookURL == nil { - return fmt.Errorf("no discord webhook URL provided") + if discord.WebhookURL == nil && len(discord.WebhookURLFile) == 0 { + return errors.New("no discord webhook URL or URLFile provided") } } for _, webex := range rcv.WebexConfigs { @@ -526,7 +577,7 @@ func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error { } if webex.APIURL == nil { if c.Global.WebexAPIURL == nil { - return fmt.Errorf("no global Webex URL set") + return errors.New("no global Webex URL set") } webex.APIURL = c.Global.WebexAPIURL @@ -536,8 +587,49 @@ func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error { if msteams.HTTPConfig == nil { msteams.HTTPConfig = c.Global.HTTPConfig } - if msteams.WebhookURL == nil { - return fmt.Errorf("no msteams webhook URL provided") + if msteams.WebhookURL == nil && len(msteams.WebhookURLFile) == 0 { + return errors.New("no msteams webhook URL or URLFile provided") + } + } + for _, msteamsv2 := range rcv.MSTeamsV2Configs { + if msteamsv2.HTTPConfig == nil { + msteamsv2.HTTPConfig = c.Global.HTTPConfig + } + if msteamsv2.WebhookURL == nil && len(msteamsv2.WebhookURLFile) == 0 { + return errors.New("no msteamsv2 webhook URL or URLFile provided") + } + } + for _, jira := range rcv.JiraConfigs { + if jira.HTTPConfig == nil { + jira.HTTPConfig = c.Global.HTTPConfig + } + if jira.APIURL == nil { + if c.Global.JiraAPIURL == nil { + return errors.New("no global Jira Cloud URL set") + } + jira.APIURL = c.Global.JiraAPIURL + } + } + for _, rocketchat := range rcv.RocketchatConfigs { + if rocketchat.HTTPConfig == nil { + rocketchat.HTTPConfig = c.Global.HTTPConfig + } + if rocketchat.APIURL == nil { + rocketchat.APIURL = c.Global.RocketchatAPIURL + } + if rocketchat.TokenID == nil && len(rocketchat.TokenIDFile) == 0 { + if c.Global.RocketchatTokenID == nil && len(c.Global.RocketchatTokenIDFile) == 0 { + return errors.New("no global Rocketchat TokenID set either inline or in a file") + } + rocketchat.TokenID = c.Global.RocketchatTokenID + rocketchat.TokenIDFile = c.Global.RocketchatTokenIDFile + } + if rocketchat.Token == nil && len(rocketchat.TokenFile) == 0 { + if c.Global.RocketchatToken == nil && len(c.Global.RocketchatTokenFile) == 0 { + return errors.New("no global Rocketchat Token set either inline or in a file") + } + rocketchat.Token = c.Global.RocketchatToken + rocketchat.TokenFile = c.Global.RocketchatTokenFile } } @@ -547,20 +639,20 @@ func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error { // The root route must not have any matchers as it is the fallback node // for all alerts. if c.Route == nil { - return fmt.Errorf("no routes provided") + return errors.New("no routes provided") } if len(c.Route.Receiver) == 0 { - return fmt.Errorf("root route must specify a default receiver") + return errors.New("root route must specify a default receiver") } if len(c.Route.Match) > 0 || len(c.Route.MatchRE) > 0 || len(c.Route.Matchers) > 0 { - return fmt.Errorf("root route must not have any matchers") + return errors.New("root route must not have any matchers") } if len(c.Route.MuteTimeIntervals) > 0 { - return fmt.Errorf("root route must not have any mute time intervals") + return errors.New("root route must not have any mute time intervals") } if len(c.Route.ActiveTimeIntervals) > 0 { - return fmt.Errorf("root route must not have any active time intervals") + return errors.New("root route must not have any active time intervals") } // Validate that all receivers used in the routing tree are defined. @@ -629,18 +721,21 @@ func checkTimeInterval(r *Route, timeIntervals map[string]struct{}) error { // DefaultGlobalConfig returns GlobalConfig with default values. func DefaultGlobalConfig() GlobalConfig { defaultHTTPConfig := commoncfg.DefaultHTTPClientConfig + defaultSMTPTLSConfig := commoncfg.TLSConfig{} + return GlobalConfig{ - ResolveTimeout: model.Duration(5 * time.Minute), - HTTPConfig: &defaultHTTPConfig, - - SMTPHello: "localhost", - SMTPRequireTLS: true, - PagerdutyURL: mustParseURL("https://events.pagerduty.com/v2/enqueue"), - OpsGenieAPIURL: mustParseURL("https://api.opsgenie.com/"), - WeChatAPIURL: mustParseURL("https://qyapi.weixin.qq.com/cgi-bin/"), - VictorOpsAPIURL: mustParseURL("https://alert.victorops.com/integrations/generic/20131114/alert/"), - TelegramAPIUrl: mustParseURL("https://api.telegram.org"), - WebexAPIURL: mustParseURL("https://webexapis.com/v1/messages"), + ResolveTimeout: model.Duration(5 * time.Minute), + HTTPConfig: &defaultHTTPConfig, + SMTPHello: "localhost", + SMTPRequireTLS: true, + SMTPTLSConfig: &defaultSMTPTLSConfig, + PagerdutyURL: mustParseURL("https://events.pagerduty.com/v2/enqueue"), + OpsGenieAPIURL: mustParseURL("https://api.opsgenie.com/"), + WeChatAPIURL: mustParseURL("https://qyapi.weixin.qq.com/cgi-bin/"), + VictorOpsAPIURL: mustParseURL("https://alert.victorops.com/integrations/generic/20131114/alert/"), + TelegramAPIUrl: mustParseURL("https://api.telegram.org"), + WebexAPIURL: mustParseURL("https://webexapis.com/v1/messages"), + RocketchatAPIURL: mustParseURL("https://open.rocket.chat/"), } } @@ -661,7 +756,7 @@ func parseURL(s string) (*URL, error) { return nil, fmt.Errorf("unsupported scheme %q for URL", u.Scheme) } if u.Host == "" { - return nil, fmt.Errorf("missing host for URL") + return nil, errors.New("missing host for URL") } return &URL{u}, nil } @@ -742,29 +837,36 @@ type GlobalConfig struct { HTTPConfig *commoncfg.HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"` - SMTPFrom string `yaml:"smtp_from,omitempty" json:"smtp_from,omitempty"` - SMTPHello string `yaml:"smtp_hello,omitempty" json:"smtp_hello,omitempty"` - SMTPSmarthost HostPort `yaml:"smtp_smarthost,omitempty" json:"smtp_smarthost,omitempty"` - SMTPAuthUsername string `yaml:"smtp_auth_username,omitempty" json:"smtp_auth_username,omitempty"` - SMTPAuthPassword Secret `yaml:"smtp_auth_password,omitempty" json:"smtp_auth_password,omitempty"` - SMTPAuthPasswordFile string `yaml:"smtp_auth_password_file,omitempty" json:"smtp_auth_password_file,omitempty"` - SMTPAuthSecret Secret `yaml:"smtp_auth_secret,omitempty" json:"smtp_auth_secret,omitempty"` - SMTPAuthIdentity string `yaml:"smtp_auth_identity,omitempty" json:"smtp_auth_identity,omitempty"` - SMTPRequireTLS bool `yaml:"smtp_require_tls" json:"smtp_require_tls,omitempty"` - SlackAPIURL *SecretURL `yaml:"slack_api_url,omitempty" json:"slack_api_url,omitempty"` - SlackAPIURLFile string `yaml:"slack_api_url_file,omitempty" json:"slack_api_url_file,omitempty"` - PagerdutyURL *URL `yaml:"pagerduty_url,omitempty" json:"pagerduty_url,omitempty"` - OpsGenieAPIURL *URL `yaml:"opsgenie_api_url,omitempty" json:"opsgenie_api_url,omitempty"` - OpsGenieAPIKey Secret `yaml:"opsgenie_api_key,omitempty" json:"opsgenie_api_key,omitempty"` - OpsGenieAPIKeyFile string `yaml:"opsgenie_api_key_file,omitempty" json:"opsgenie_api_key_file,omitempty"` - WeChatAPIURL *URL `yaml:"wechat_api_url,omitempty" json:"wechat_api_url,omitempty"` - WeChatAPISecret Secret `yaml:"wechat_api_secret,omitempty" json:"wechat_api_secret,omitempty"` - WeChatAPICorpID string `yaml:"wechat_api_corp_id,omitempty" json:"wechat_api_corp_id,omitempty"` - VictorOpsAPIURL *URL `yaml:"victorops_api_url,omitempty" json:"victorops_api_url,omitempty"` - VictorOpsAPIKey Secret `yaml:"victorops_api_key,omitempty" json:"victorops_api_key,omitempty"` - VictorOpsAPIKeyFile string `yaml:"victorops_api_key_file,omitempty" json:"victorops_api_key_file,omitempty"` - TelegramAPIUrl *URL `yaml:"telegram_api_url,omitempty" json:"telegram_api_url,omitempty"` - WebexAPIURL *URL `yaml:"webex_api_url,omitempty" json:"webex_api_url,omitempty"` + JiraAPIURL *URL `yaml:"jira_api_url,omitempty" json:"jira_api_url,omitempty"` + SMTPFrom string `yaml:"smtp_from,omitempty" json:"smtp_from,omitempty"` + SMTPHello string `yaml:"smtp_hello,omitempty" json:"smtp_hello,omitempty"` + SMTPSmarthost HostPort `yaml:"smtp_smarthost,omitempty" json:"smtp_smarthost,omitempty"` + SMTPAuthUsername string `yaml:"smtp_auth_username,omitempty" json:"smtp_auth_username,omitempty"` + SMTPAuthPassword Secret `yaml:"smtp_auth_password,omitempty" json:"smtp_auth_password,omitempty"` + SMTPAuthPasswordFile string `yaml:"smtp_auth_password_file,omitempty" json:"smtp_auth_password_file,omitempty"` + SMTPAuthSecret Secret `yaml:"smtp_auth_secret,omitempty" json:"smtp_auth_secret,omitempty"` + SMTPAuthIdentity string `yaml:"smtp_auth_identity,omitempty" json:"smtp_auth_identity,omitempty"` + SMTPRequireTLS bool `yaml:"smtp_require_tls" json:"smtp_require_tls,omitempty"` + SMTPTLSConfig *commoncfg.TLSConfig `yaml:"smtp_tls_config,omitempty" json:"smtp_tls_config,omitempty"` + SlackAPIURL *SecretURL `yaml:"slack_api_url,omitempty" json:"slack_api_url,omitempty"` + SlackAPIURLFile string `yaml:"slack_api_url_file,omitempty" json:"slack_api_url_file,omitempty"` + PagerdutyURL *URL `yaml:"pagerduty_url,omitempty" json:"pagerduty_url,omitempty"` + OpsGenieAPIURL *URL `yaml:"opsgenie_api_url,omitempty" json:"opsgenie_api_url,omitempty"` + OpsGenieAPIKey Secret `yaml:"opsgenie_api_key,omitempty" json:"opsgenie_api_key,omitempty"` + OpsGenieAPIKeyFile string `yaml:"opsgenie_api_key_file,omitempty" json:"opsgenie_api_key_file,omitempty"` + WeChatAPIURL *URL `yaml:"wechat_api_url,omitempty" json:"wechat_api_url,omitempty"` + WeChatAPISecret Secret `yaml:"wechat_api_secret,omitempty" json:"wechat_api_secret,omitempty"` + WeChatAPICorpID string `yaml:"wechat_api_corp_id,omitempty" json:"wechat_api_corp_id,omitempty"` + VictorOpsAPIURL *URL `yaml:"victorops_api_url,omitempty" json:"victorops_api_url,omitempty"` + VictorOpsAPIKey Secret `yaml:"victorops_api_key,omitempty" json:"victorops_api_key,omitempty"` + VictorOpsAPIKeyFile string `yaml:"victorops_api_key_file,omitempty" json:"victorops_api_key_file,omitempty"` + TelegramAPIUrl *URL `yaml:"telegram_api_url,omitempty" json:"telegram_api_url,omitempty"` + WebexAPIURL *URL `yaml:"webex_api_url,omitempty" json:"webex_api_url,omitempty"` + RocketchatAPIURL *URL `yaml:"rocketchat_api_url,omitempty" json:"rocketchat_api_url,omitempty"` + RocketchatToken *Secret `yaml:"rocketchat_token,omitempty" json:"rocketchat_token,omitempty"` + RocketchatTokenFile string `yaml:"rocketchat_token_file,omitempty" json:"rocketchat_token_file,omitempty"` + RocketchatTokenID *Secret `yaml:"rocketchat_token_id,omitempty" json:"rocketchat_token_id,omitempty"` + RocketchatTokenIDFile string `yaml:"rocketchat_token_id_file,omitempty" json:"rocketchat_token_id_file,omitempty"` } // UnmarshalYAML implements the yaml.Unmarshaler interface for GlobalConfig. @@ -822,7 +924,7 @@ func (r *Route) UnmarshalYAML(unmarshal func(interface{}) error) error { } if len(r.GroupBy) > 0 && r.GroupByAll { - return fmt.Errorf("cannot have wildcard group_by (`...`) and other other labels at the same time") + return errors.New("cannot have wildcard group_by (`...`) and other other labels at the same time") } groupBy := map[model.LabelName]struct{}{} @@ -835,10 +937,10 @@ func (r *Route) UnmarshalYAML(unmarshal func(interface{}) error) error { } if r.GroupInterval != nil && time.Duration(*r.GroupInterval) == time.Duration(0) { - return fmt.Errorf("group_interval cannot be zero") + return errors.New("group_interval cannot be zero") } if r.RepeatInterval != nil && time.Duration(*r.RepeatInterval) == time.Duration(0) { - return fmt.Errorf("repeat_interval cannot be zero") + return errors.New("repeat_interval cannot be zero") } return nil @@ -866,7 +968,11 @@ type InhibitRule struct { TargetMatchers Matchers `yaml:"target_matchers,omitempty" json:"target_matchers,omitempty"` // A set of labels that must be equal between the source and target alert // for them to be a match. - Equal model.LabelNames `yaml:"equal,omitempty" json:"equal,omitempty"` + Equal model.LabelNames `yaml:"-" json:"-"` + // EqualStr allows us to validate the label depending on whether UTF-8 is + // enabled or disabled. It should be removed when Alertmanager is updated + // to use the validation modes in recent versions of prometheus/common. + EqualStr []string `yaml:"equal,omitempty" json:"equal,omitempty"` } // UnmarshalYAML implements the yaml.Unmarshaler interface for InhibitRule. @@ -888,6 +994,14 @@ func (r *InhibitRule) UnmarshalYAML(unmarshal func(interface{}) error) error { } } + for _, l := range r.EqualStr { + labelName := model.LabelName(l) + if !compat.IsValidLabelName(labelName) { + return fmt.Errorf("invalid label name %q in equal list", l) + } + r.Equal = append(r.Equal, labelName) + } + return nil } @@ -896,19 +1010,22 @@ type Receiver struct { // A unique identifier for this receiver. Name string `yaml:"name" json:"name"` - DiscordConfigs []*DiscordConfig `yaml:"discord_configs,omitempty" json:"discord_configs,omitempty"` - EmailConfigs []*EmailConfig `yaml:"email_configs,omitempty" json:"email_configs,omitempty"` - PagerdutyConfigs []*PagerdutyConfig `yaml:"pagerduty_configs,omitempty" json:"pagerduty_configs,omitempty"` - SlackConfigs []*SlackConfig `yaml:"slack_configs,omitempty" json:"slack_configs,omitempty"` - WebhookConfigs []*WebhookConfig `yaml:"webhook_configs,omitempty" json:"webhook_configs,omitempty"` - OpsGenieConfigs []*OpsGenieConfig `yaml:"opsgenie_configs,omitempty" json:"opsgenie_configs,omitempty"` - WechatConfigs []*WechatConfig `yaml:"wechat_configs,omitempty" json:"wechat_configs,omitempty"` - PushoverConfigs []*PushoverConfig `yaml:"pushover_configs,omitempty" json:"pushover_configs,omitempty"` - VictorOpsConfigs []*VictorOpsConfig `yaml:"victorops_configs,omitempty" json:"victorops_configs,omitempty"` - SNSConfigs []*SNSConfig `yaml:"sns_configs,omitempty" json:"sns_configs,omitempty"` - TelegramConfigs []*TelegramConfig `yaml:"telegram_configs,omitempty" json:"telegram_configs,omitempty"` - WebexConfigs []*WebexConfig `yaml:"webex_configs,omitempty" json:"webex_configs,omitempty"` - MSTeamsConfigs []*MSTeamsConfig `yaml:"msteams_configs,omitempty" json:"msteams_configs,omitempty"` + DiscordConfigs []*DiscordConfig `yaml:"discord_configs,omitempty" json:"discord_configs,omitempty"` + EmailConfigs []*EmailConfig `yaml:"email_configs,omitempty" json:"email_configs,omitempty"` + PagerdutyConfigs []*PagerdutyConfig `yaml:"pagerduty_configs,omitempty" json:"pagerduty_configs,omitempty"` + SlackConfigs []*SlackConfig `yaml:"slack_configs,omitempty" json:"slack_configs,omitempty"` + WebhookConfigs []*WebhookConfig `yaml:"webhook_configs,omitempty" json:"webhook_configs,omitempty"` + OpsGenieConfigs []*OpsGenieConfig `yaml:"opsgenie_configs,omitempty" json:"opsgenie_configs,omitempty"` + WechatConfigs []*WechatConfig `yaml:"wechat_configs,omitempty" json:"wechat_configs,omitempty"` + PushoverConfigs []*PushoverConfig `yaml:"pushover_configs,omitempty" json:"pushover_configs,omitempty"` + VictorOpsConfigs []*VictorOpsConfig `yaml:"victorops_configs,omitempty" json:"victorops_configs,omitempty"` + SNSConfigs []*SNSConfig `yaml:"sns_configs,omitempty" json:"sns_configs,omitempty"` + TelegramConfigs []*TelegramConfig `yaml:"telegram_configs,omitempty" json:"telegram_configs,omitempty"` + WebexConfigs []*WebexConfig `yaml:"webex_configs,omitempty" json:"webex_configs,omitempty"` + MSTeamsConfigs []*MSTeamsConfig `yaml:"msteams_configs,omitempty" json:"msteams_configs,omitempty"` + MSTeamsV2Configs []*MSTeamsV2Config `yaml:"msteamsv2_configs,omitempty" json:"msteamsv2_configs,omitempty"` + JiraConfigs []*JiraConfig `yaml:"jira_configs,omitempty" json:"jira_configs,omitempty"` + RocketchatConfigs []*RocketchatConfig `yaml:"rocketchat_configs,omitempty" json:"rocketchat_configs,omitempty"` } // UnmarshalYAML implements the yaml.Unmarshaler interface for Receiver. @@ -918,7 +1035,7 @@ func (c *Receiver) UnmarshalYAML(unmarshal func(interface{}) error) error { return err } if c.Name == "" { - return fmt.Errorf("missing name in receiver") + return errors.New("missing name in receiver") } return nil } @@ -972,7 +1089,7 @@ func (re Regexp) MarshalYAML() (interface{}, error) { return nil, nil } -// UnmarshalJSON implements the json.Unmarshaler interface for Regexp +// UnmarshalJSON implements the json.Unmarshaler interface for Regexp. func (re *Regexp) UnmarshalJSON(data []byte) error { var s string if err := json.Unmarshal(data, &s); err != nil { diff --git a/vendor/github.com/prometheus/alertmanager/config/coordinator.go b/vendor/github.com/prometheus/alertmanager/config/coordinator.go index 5ee44f81d8..48728c4e76 100644 --- a/vendor/github.com/prometheus/alertmanager/config/coordinator.go +++ b/vendor/github.com/prometheus/alertmanager/config/coordinator.go @@ -16,10 +16,9 @@ package config import ( "crypto/md5" "encoding/binary" + "log/slog" "sync" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/prometheus/client_golang/prometheus" ) @@ -27,7 +26,7 @@ import ( // single configuration. type Coordinator struct { configFilePath string - logger log.Logger + logger *slog.Logger // Protects config and subscribers mutex sync.Mutex @@ -42,7 +41,7 @@ type Coordinator struct { // NewCoordinator returns a new coordinator with the given configuration file // path. It does not yet load the configuration from file. This is done in // `Reload()`. -func NewCoordinator(configFilePath string, r prometheus.Registerer, l log.Logger) *Coordinator { +func NewCoordinator(configFilePath string, r prometheus.Registerer, l *slog.Logger) *Coordinator { c := &Coordinator{ configFilePath: configFilePath, logger: l, @@ -110,27 +109,27 @@ func (c *Coordinator) Reload() error { c.mutex.Lock() defer c.mutex.Unlock() - level.Info(c.logger).Log( - "msg", "Loading configuration file", + c.logger.Info( + "Loading configuration file", "file", c.configFilePath, ) if err := c.loadFromFile(); err != nil { - level.Error(c.logger).Log( - "msg", "Loading configuration file failed", + c.logger.Error( + "Loading configuration file failed", "file", c.configFilePath, "err", err, ) c.configSuccessMetric.Set(0) return err } - level.Info(c.logger).Log( - "msg", "Completed loading of configuration file", + c.logger.Info( + "Completed loading of configuration file", "file", c.configFilePath, ) if err := c.notifySubscribers(); err != nil { - c.logger.Log( - "msg", "one or more config change subscribers failed to apply new config", + c.logger.Error( + "one or more config change subscribers failed to apply new config", "file", c.configFilePath, "err", err, ) diff --git a/vendor/github.com/prometheus/alertmanager/config/notifiers.go b/vendor/github.com/prometheus/alertmanager/config/notifiers.go index d79c8b5057..74c74a92a0 100644 --- a/vendor/github.com/prometheus/alertmanager/config/notifiers.go +++ b/vendor/github.com/prometheus/alertmanager/config/notifiers.go @@ -14,6 +14,7 @@ package config import ( + "errors" "fmt" "net/textproto" "regexp" @@ -22,6 +23,7 @@ import ( "time" commoncfg "github.com/prometheus/common/config" + "github.com/prometheus/common/model" "github.com/prometheus/common/sigv4" ) @@ -97,6 +99,18 @@ var ( CallbackID: `{{ template "slack.default.callbackid" . }}`, Footer: `{{ template "slack.default.footer" . }}`, } + // DefaultRocketchatConfig defines default values for Rocketchat configurations. + DefaultRocketchatConfig = RocketchatConfig{ + NotifierConfig: NotifierConfig{ + VSendResolved: false, + }, + Color: `{{ if eq .Status "firing" }}red{{ else }}green{{ end }}`, + Emoji: `{{ template "rocketchat.default.emoji" . }}`, + IconURL: `{{ template "rocketchat.default.iconurl" . }}`, + Text: `{{ template "rocketchat.default.text" . }}`, + Title: `{{ template "rocketchat.default.title" . }}`, + TitleLink: `{{ template "rocketchat.default.titlelink" . }}`, + } // DefaultOpsGenieConfig defines default values for OpsGenie configurations. DefaultOpsGenieConfig = OpsGenieConfig{ @@ -172,6 +186,23 @@ var ( Summary: `{{ template "msteams.default.summary" . }}`, Text: `{{ template "msteams.default.text" . }}`, } + + DefaultMSTeamsV2Config = MSTeamsV2Config{ + NotifierConfig: NotifierConfig{ + VSendResolved: true, + }, + Title: `{{ template "msteamsv2.default.title" . }}`, + Text: `{{ template "msteamsv2.default.text" . }}`, + } + + DefaultJiraConfig = JiraConfig{ + NotifierConfig: NotifierConfig{ + VSendResolved: true, + }, + Summary: `{{ template "jira.default.summary" . }}`, + Description: `{{ template "jira.default.description" . }}`, + Priority: `{{ template "jira.default.priority" . }}`, + } ) // NotifierConfig contains base options common across all notifier configurations. @@ -202,11 +233,11 @@ func (c *WebexConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { } if c.RoomID == "" { - return fmt.Errorf("missing room_id on webex_config") + return errors.New("missing room_id on webex_config") } if c.HTTPConfig == nil || c.HTTPConfig.Authorization == nil { - return fmt.Errorf("missing webex_configs.http_config.authorization") + return errors.New("missing webex_configs.http_config.authorization") } return nil @@ -220,8 +251,11 @@ type DiscordConfig struct { WebhookURL *SecretURL `yaml:"webhook_url,omitempty" json:"webhook_url,omitempty"` WebhookURLFile string `yaml:"webhook_url_file,omitempty" json:"webhook_url_file,omitempty"` - Title string `yaml:"title,omitempty" json:"title,omitempty"` - Message string `yaml:"message,omitempty" json:"message,omitempty"` + Content string `yaml:"content,omitempty" json:"content,omitempty"` + Title string `yaml:"title,omitempty" json:"title,omitempty"` + Message string `yaml:"message,omitempty" json:"message,omitempty"` + Username string `yaml:"username,omitempty" json:"username,omitempty"` + AvatarURL string `yaml:"avatar_url,omitempty" json:"avatar_url,omitempty"` } // UnmarshalYAML implements the yaml.Unmarshaler interface. @@ -233,11 +267,11 @@ func (c *DiscordConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { } if c.WebhookURL == nil && c.WebhookURLFile == "" { - return fmt.Errorf("one of webhook_url or webhook_url_file must be configured") + return errors.New("one of webhook_url or webhook_url_file must be configured") } if c.WebhookURL != nil && len(c.WebhookURLFile) > 0 { - return fmt.Errorf("at most one of webhook_url & webhook_url_file must be configured") + return errors.New("at most one of webhook_url & webhook_url_file must be configured") } return nil @@ -248,20 +282,20 @@ type EmailConfig struct { NotifierConfig `yaml:",inline" json:",inline"` // Email address to notify. - To string `yaml:"to,omitempty" json:"to,omitempty"` - From string `yaml:"from,omitempty" json:"from,omitempty"` - Hello string `yaml:"hello,omitempty" json:"hello,omitempty"` - Smarthost HostPort `yaml:"smarthost,omitempty" json:"smarthost,omitempty"` - AuthUsername string `yaml:"auth_username,omitempty" json:"auth_username,omitempty"` - AuthPassword Secret `yaml:"auth_password,omitempty" json:"auth_password,omitempty"` - AuthPasswordFile string `yaml:"auth_password_file,omitempty" json:"auth_password_file,omitempty"` - AuthSecret Secret `yaml:"auth_secret,omitempty" json:"auth_secret,omitempty"` - AuthIdentity string `yaml:"auth_identity,omitempty" json:"auth_identity,omitempty"` - Headers map[string]string `yaml:"headers,omitempty" json:"headers,omitempty"` - HTML string `yaml:"html,omitempty" json:"html,omitempty"` - Text string `yaml:"text,omitempty" json:"text,omitempty"` - RequireTLS *bool `yaml:"require_tls,omitempty" json:"require_tls,omitempty"` - TLSConfig commoncfg.TLSConfig `yaml:"tls_config,omitempty" json:"tls_config,omitempty"` + To string `yaml:"to,omitempty" json:"to,omitempty"` + From string `yaml:"from,omitempty" json:"from,omitempty"` + Hello string `yaml:"hello,omitempty" json:"hello,omitempty"` + Smarthost HostPort `yaml:"smarthost,omitempty" json:"smarthost,omitempty"` + AuthUsername string `yaml:"auth_username,omitempty" json:"auth_username,omitempty"` + AuthPassword Secret `yaml:"auth_password,omitempty" json:"auth_password,omitempty"` + AuthPasswordFile string `yaml:"auth_password_file,omitempty" json:"auth_password_file,omitempty"` + AuthSecret Secret `yaml:"auth_secret,omitempty" json:"auth_secret,omitempty"` + AuthIdentity string `yaml:"auth_identity,omitempty" json:"auth_identity,omitempty"` + Headers map[string]string `yaml:"headers,omitempty" json:"headers,omitempty"` + HTML string `yaml:"html,omitempty" json:"html,omitempty"` + Text string `yaml:"text,omitempty" json:"text,omitempty"` + RequireTLS *bool `yaml:"require_tls,omitempty" json:"require_tls,omitempty"` + TLSConfig *commoncfg.TLSConfig `yaml:"tls_config,omitempty" json:"tls_config,omitempty"` } // UnmarshalYAML implements the yaml.Unmarshaler interface. @@ -272,7 +306,7 @@ func (c *EmailConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { return err } if c.To == "" { - return fmt.Errorf("missing to address in email config") + return errors.New("missing to address in email config") } // Header names are case-insensitive, check for collisions. normalizedHeaders := map[string]string{} @@ -312,13 +346,13 @@ type PagerdutyConfig struct { Group string `yaml:"group,omitempty" json:"group,omitempty"` } -// PagerdutyLink is a link +// PagerdutyLink is a link. type PagerdutyLink struct { Href string `yaml:"href,omitempty" json:"href,omitempty"` Text string `yaml:"text,omitempty" json:"text,omitempty"` } -// PagerdutyImage is an image +// PagerdutyImage is an image. type PagerdutyImage struct { Src string `yaml:"src,omitempty" json:"src,omitempty"` Alt string `yaml:"alt,omitempty" json:"alt,omitempty"` @@ -333,13 +367,13 @@ func (c *PagerdutyConfig) UnmarshalYAML(unmarshal func(interface{}) error) error return err } if c.RoutingKey == "" && c.ServiceKey == "" && c.RoutingKeyFile == "" && c.ServiceKeyFile == "" { - return fmt.Errorf("missing service or routing key in PagerDuty config") + return errors.New("missing service or routing key in PagerDuty config") } if len(c.RoutingKey) > 0 && len(c.RoutingKeyFile) > 0 { - return fmt.Errorf("at most one of routing_key & routing_key_file must be configured") + return errors.New("at most one of routing_key & routing_key_file must be configured") } if len(c.ServiceKey) > 0 && len(c.ServiceKeyFile) > 0 { - return fmt.Errorf("at most one of service_key & service_key_file must be configured") + return errors.New("at most one of service_key & service_key_file must be configured") } if c.Details == nil { c.Details = make(map[string]string) @@ -375,10 +409,10 @@ func (c *SlackAction) UnmarshalYAML(unmarshal func(interface{}) error) error { return err } if c.Type == "" { - return fmt.Errorf("missing type in Slack action configuration") + return errors.New("missing type in Slack action configuration") } if c.Text == "" { - return fmt.Errorf("missing text in Slack action configuration") + return errors.New("missing text in Slack action configuration") } if c.URL != "" { // Clear all message action fields. @@ -388,7 +422,7 @@ func (c *SlackAction) UnmarshalYAML(unmarshal func(interface{}) error) error { } else if c.Name != "" { c.URL = "" } else { - return fmt.Errorf("missing name or url in Slack action configuration") + return errors.New("missing name or url in Slack action configuration") } return nil } @@ -410,7 +444,7 @@ func (c *SlackConfirmationField) UnmarshalYAML(unmarshal func(interface{}) error return err } if c.Text == "" { - return fmt.Errorf("missing text in Slack confirmation configuration") + return errors.New("missing text in Slack confirmation configuration") } return nil } @@ -432,10 +466,10 @@ func (c *SlackField) UnmarshalYAML(unmarshal func(interface{}) error) error { return err } if c.Title == "" { - return fmt.Errorf("missing title in Slack field configuration") + return errors.New("missing title in Slack field configuration") } if c.Value == "" { - return fmt.Errorf("missing value in Slack field configuration") + return errors.New("missing value in Slack field configuration") } return nil } @@ -481,7 +515,7 @@ func (c *SlackConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { } if c.APIURL != nil && len(c.APIURLFile) > 0 { - return fmt.Errorf("at most one of api_url & api_url_file must be configured") + return errors.New("at most one of api_url & api_url_file must be configured") } return nil @@ -501,6 +535,10 @@ type WebhookConfig struct { // Alerts exceeding this threshold will be truncated. Setting this to 0 // allows an unlimited number of alerts. MaxAlerts uint64 `yaml:"max_alerts" json:"max_alerts"` + + // Timeout is the maximum time allowed to invoke the webhook. Setting this to 0 + // does not impose a timeout. + Timeout time.Duration `yaml:"timeout" json:"timeout"` } // UnmarshalYAML implements the yaml.Unmarshaler interface. @@ -511,10 +549,10 @@ func (c *WebhookConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { return err } if c.URL == nil && c.URLFile == "" { - return fmt.Errorf("one of url or url_file must be configured") + return errors.New("one of url or url_file must be configured") } if c.URL != nil && c.URLFile != "" { - return fmt.Errorf("at most one of url & url_file must be configured") + return errors.New("at most one of url & url_file must be configured") } return nil } @@ -594,7 +632,7 @@ func (c *OpsGenieConfig) UnmarshalYAML(unmarshal func(interface{}) error) error } if c.APIKey != "" && len(c.APIKeyFile) > 0 { - return fmt.Errorf("at most one of api_key & api_key_file must be configured") + return errors.New("at most one of api_key & api_key_file must be configured") } for _, r := range c.Responders { @@ -653,10 +691,10 @@ func (c *VictorOpsConfig) UnmarshalYAML(unmarshal func(interface{}) error) error return err } if c.RoutingKey == "" { - return fmt.Errorf("missing Routing key in VictorOps config") + return errors.New("missing Routing key in VictorOps config") } if c.APIKey != "" && len(c.APIKeyFile) > 0 { - return fmt.Errorf("at most one of api_key & api_key_file must be configured") + return errors.New("at most one of api_key & api_key_file must be configured") } reservedFields := []string{"routing_key", "message_type", "state_message", "entity_display_name", "monitoring_tool", "entity_id", "entity_state"} @@ -714,16 +752,16 @@ func (c *PushoverConfig) UnmarshalYAML(unmarshal func(interface{}) error) error return err } if c.UserKey == "" && c.UserKeyFile == "" { - return fmt.Errorf("one of user_key or user_key_file must be configured") + return errors.New("one of user_key or user_key_file must be configured") } if c.UserKey != "" && c.UserKeyFile != "" { - return fmt.Errorf("at most one of user_key & user_key_file must be configured") + return errors.New("at most one of user_key & user_key_file must be configured") } if c.Token == "" && c.TokenFile == "" { - return fmt.Errorf("one of token or token_file must be configured") + return errors.New("one of token or token_file must be configured") } if c.Token != "" && c.TokenFile != "" { - return fmt.Errorf("at most one of token & token_file must be configured") + return errors.New("at most one of token & token_file must be configured") } return nil } @@ -751,7 +789,7 @@ func (c *SNSConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { return err } if (c.TargetARN == "") != (c.TopicARN == "") != (c.PhoneNumber == "") { - return fmt.Errorf("must provide either a Target ARN, Topic ARN, or Phone Number for SNS config") + return errors.New("must provide either a Target ARN, Topic ARN, or Phone Number for SNS config") } return nil } @@ -766,6 +804,7 @@ type TelegramConfig struct { BotToken Secret `yaml:"bot_token,omitempty" json:"token,omitempty"` BotTokenFile string `yaml:"bot_token_file,omitempty" json:"token_file,omitempty"` ChatID int64 `yaml:"chat_id,omitempty" json:"chat,omitempty"` + MessageThreadID int `yaml:"message_thread_id,omitempty" json:"message_thread_id,omitempty"` Message string `yaml:"message,omitempty" json:"message,omitempty"` DisableNotifications bool `yaml:"disable_notifications,omitempty" json:"disable_notifications,omitempty"` ParseMode string `yaml:"parse_mode,omitempty" json:"parse_mode,omitempty"` @@ -779,19 +818,19 @@ func (c *TelegramConfig) UnmarshalYAML(unmarshal func(interface{}) error) error return err } if c.BotToken == "" && c.BotTokenFile == "" { - return fmt.Errorf("missing bot_token or bot_token_file on telegram_config") + return errors.New("missing bot_token or bot_token_file on telegram_config") } if c.BotToken != "" && c.BotTokenFile != "" { - return fmt.Errorf("at most one of bot_token & bot_token_file must be configured") + return errors.New("at most one of bot_token & bot_token_file must be configured") } if c.ChatID == 0 { - return fmt.Errorf("missing chat_id on telegram_config") + return errors.New("missing chat_id on telegram_config") } if c.ParseMode != "" && c.ParseMode != "Markdown" && c.ParseMode != "MarkdownV2" && c.ParseMode != "HTML" { - return fmt.Errorf("unknown parse_mode on telegram_config, must be Markdown, MarkdownV2, HTML or empty string") + return errors.New("unknown parse_mode on telegram_config, must be Markdown, MarkdownV2, HTML or empty string") } return nil } @@ -815,12 +854,145 @@ func (c *MSTeamsConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { } if c.WebhookURL == nil && c.WebhookURLFile == "" { - return fmt.Errorf("one of webhook_url or webhook_url_file must be configured") + return errors.New("one of webhook_url or webhook_url_file must be configured") } if c.WebhookURL != nil && len(c.WebhookURLFile) > 0 { - return fmt.Errorf("at most one of webhook_url & webhook_url_file must be configured") + return errors.New("at most one of webhook_url & webhook_url_file must be configured") } return nil } + +type MSTeamsV2Config struct { + NotifierConfig `yaml:",inline" json:",inline"` + HTTPConfig *commoncfg.HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"` + WebhookURL *SecretURL `yaml:"webhook_url,omitempty" json:"webhook_url,omitempty"` + WebhookURLFile string `yaml:"webhook_url_file,omitempty" json:"webhook_url_file,omitempty"` + + Title string `yaml:"title,omitempty" json:"title,omitempty"` + Text string `yaml:"text,omitempty" json:"text,omitempty"` +} + +func (c *MSTeamsV2Config) UnmarshalYAML(unmarshal func(interface{}) error) error { + *c = DefaultMSTeamsV2Config + type plain MSTeamsV2Config + if err := unmarshal((*plain)(c)); err != nil { + return err + } + + if c.WebhookURL == nil && c.WebhookURLFile == "" { + return errors.New("one of webhook_url or webhook_url_file must be configured") + } + + if c.WebhookURL != nil && len(c.WebhookURLFile) > 0 { + return errors.New("at most one of webhook_url & webhook_url_file must be configured") + } + + return nil +} + +type JiraConfig struct { + NotifierConfig `yaml:",inline" json:",inline"` + HTTPConfig *commoncfg.HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"` + + APIURL *URL `yaml:"api_url,omitempty" json:"api_url,omitempty"` + + Project string `yaml:"project,omitempty" json:"project,omitempty"` + Summary string `yaml:"summary,omitempty" json:"summary,omitempty"` + Description string `yaml:"description,omitempty" json:"description,omitempty"` + Labels []string `yaml:"labels,omitempty" json:"labels,omitempty"` + Priority string `yaml:"priority,omitempty" json:"priority,omitempty"` + IssueType string `yaml:"issue_type,omitempty" json:"issue_type,omitempty"` + + ReopenTransition string `yaml:"reopen_transition,omitempty" json:"reopen_transition,omitempty"` + ResolveTransition string `yaml:"resolve_transition,omitempty" json:"resolve_transition,omitempty"` + WontFixResolution string `yaml:"wont_fix_resolution,omitempty" json:"wont_fix_resolution,omitempty"` + ReopenDuration model.Duration `yaml:"reopen_duration,omitempty" json:"reopen_duration,omitempty"` + + Fields map[string]any `yaml:"fields,omitempty" json:"custom_fields,omitempty"` +} + +func (c *JiraConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { + *c = DefaultJiraConfig + type plain JiraConfig + if err := unmarshal((*plain)(c)); err != nil { + return err + } + + if c.Project == "" { + return errors.New("missing project in jira_config") + } + if c.IssueType == "" { + return errors.New("missing issue_type in jira_config") + } + return nil +} + +type RocketchatAttachmentField struct { + Short *bool `json:"short"` + Title string `json:"title,omitempty"` + Value string `json:"value,omitempty"` +} + +const ( + ProcessingTypeSendMessage = "sendMessage" + ProcessingTypeRespondWithMessage = "respondWithMessage" +) + +type RocketchatAttachmentAction struct { + Type string `json:"type,omitempty"` + Text string `json:"text,omitempty"` + URL string `json:"url,omitempty"` + ImageURL string `json:"image_url,omitempty"` + IsWebView bool `json:"is_webview"` + WebviewHeightRatio string `json:"webview_height_ratio,omitempty"` + Msg string `json:"msg,omitempty"` + MsgInChatWindow bool `json:"msg_in_chat_window"` + MsgProcessingType string `json:"msg_processing_type,omitempty"` +} + +// RocketchatConfig configures notifications via Rocketchat. +type RocketchatConfig struct { + NotifierConfig `yaml:",inline" json:",inline"` + + HTTPConfig *commoncfg.HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"` + + APIURL *URL `yaml:"api_url,omitempty" json:"api_url,omitempty"` + TokenID *Secret `yaml:"token_id,omitempty" json:"token_id,omitempty"` + TokenIDFile string `yaml:"token_id_file,omitempty" json:"token_id_file,omitempty"` + Token *Secret `yaml:"token,omitempty" json:"token,omitempty"` + TokenFile string `yaml:"token_file,omitempty" json:"token_file,omitempty"` + + // RocketChat channel override, (like #other-channel or @username). + Channel string `yaml:"channel,omitempty" json:"channel,omitempty"` + + Color string `yaml:"color,omitempty" json:"color,omitempty"` + Title string `yaml:"title,omitempty" json:"title,omitempty"` + TitleLink string `yaml:"title_link,omitempty" json:"title_link,omitempty"` + Text string `yaml:"text,omitempty" json:"text,omitempty"` + Fields []*RocketchatAttachmentField `yaml:"fields,omitempty" json:"fields,omitempty"` + ShortFields bool `yaml:"short_fields" json:"short_fields,omitempty"` + Emoji string `yaml:"emoji,omitempty" json:"emoji,omitempty"` + IconURL string `yaml:"icon_url,omitempty" json:"icon_url,omitempty"` + ImageURL string `yaml:"image_url,omitempty" json:"image_url,omitempty"` + ThumbURL string `yaml:"thumb_url,omitempty" json:"thumb_url,omitempty"` + LinkNames bool `yaml:"link_names" json:"link_names,omitempty"` + Actions []*RocketchatAttachmentAction `yaml:"actions,omitempty" json:"actions,omitempty"` +} + +// UnmarshalYAML implements the yaml.Unmarshaler interface. +func (c *RocketchatConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { + *c = DefaultRocketchatConfig + type plain RocketchatConfig + if err := unmarshal((*plain)(c)); err != nil { + return err + } + if c.Token != nil && len(c.TokenFile) > 0 { + return errors.New("at most one of token & token_file must be configured") + } + if c.TokenID != nil && len(c.TokenIDFile) > 0 { + return errors.New("at most one of token_id & token_id_file must be configured") + } + return nil +} diff --git a/vendor/github.com/prometheus/alertmanager/dispatch/dispatch.go b/vendor/github.com/prometheus/alertmanager/dispatch/dispatch.go index 640b22abe2..6883786dca 100644 --- a/vendor/github.com/prometheus/alertmanager/dispatch/dispatch.go +++ b/vendor/github.com/prometheus/alertmanager/dispatch/dispatch.go @@ -17,12 +17,11 @@ import ( "context" "errors" "fmt" + "log/slog" "sort" "sync" "time" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/common/model" @@ -78,6 +77,7 @@ type Dispatcher struct { route *Route alerts provider.Alerts stage notify.Stage + marker types.GroupMarker metrics *DispatcherMetrics limits Limits @@ -91,7 +91,7 @@ type Dispatcher struct { ctx context.Context cancel func() - logger log.Logger + logger *slog.Logger } // Limits describes limits used by Dispatcher. @@ -107,10 +107,10 @@ func NewDispatcher( ap provider.Alerts, r *Route, s notify.Stage, - mk types.Marker, + mk types.GroupMarker, to func(time.Duration) time.Duration, lim Limits, - l log.Logger, + l *slog.Logger, m *DispatcherMetrics, ) *Dispatcher { if lim == nil { @@ -121,8 +121,9 @@ func NewDispatcher( alerts: ap, stage: s, route: r, + marker: mk, timeout: to, - logger: log.With(l, "component", "dispatcher"), + logger: l.With("component", "dispatcher"), metrics: m, limits: lim, } @@ -145,8 +146,8 @@ func (d *Dispatcher) Run() { } func (d *Dispatcher) run(it provider.AlertIterator) { - cleanup := time.NewTicker(30 * time.Second) - defer cleanup.Stop() + maintenance := time.NewTicker(30 * time.Second) + defer maintenance.Stop() defer it.Close() @@ -156,16 +157,16 @@ func (d *Dispatcher) run(it provider.AlertIterator) { if !ok { // Iterator exhausted for some reason. if err := it.Err(); err != nil { - level.Error(d.logger).Log("msg", "Error on alert update", "err", err) + d.logger.Error("Error on alert update", "err", err) } return } - level.Debug(d.logger).Log("msg", "Received alert", "alert", alert) + d.logger.Debug("Received alert", "alert", alert) // Log errors but keep trying. if err := it.Err(); err != nil { - level.Error(d.logger).Log("msg", "Error on alert update", "err", err) + d.logger.Error("Error on alert update", "err", err) continue } @@ -175,33 +176,37 @@ func (d *Dispatcher) run(it provider.AlertIterator) { } d.metrics.processingDuration.Observe(time.Since(now).Seconds()) - case <-cleanup.C: - d.mtx.Lock() - - for _, groups := range d.aggrGroupsPerRoute { - for _, ag := range groups { - if ag.empty() { - ag.stop() - delete(groups, ag.fingerprint()) - d.aggrGroupsNum-- - d.metrics.aggrGroups.Dec() - } - } - } - - d.mtx.Unlock() - + case <-maintenance.C: + d.doMaintenance() case <-d.ctx.Done(): return } } } +func (d *Dispatcher) doMaintenance() { + d.mtx.Lock() + defer d.mtx.Unlock() + for _, groups := range d.aggrGroupsPerRoute { + for _, ag := range groups { + if ag.empty() { + ag.stop() + d.marker.DeleteByGroupKey(ag.routeID, ag.GroupKey()) + delete(groups, ag.fingerprint()) + d.aggrGroupsNum-- + d.metrics.aggrGroups.Dec() + } + } + } +} + // AlertGroup represents how alerts exist within an aggrGroup. type AlertGroup struct { Alerts types.AlertSlice Labels model.LabelSet Receiver string + GroupKey string + RouteID string } type AlertGroups []*AlertGroup @@ -238,6 +243,8 @@ func (d *Dispatcher) Groups(routeFilter func(*Route) bool, alertFilter func(*typ alertGroup := &AlertGroup{ Labels: ag.labels, Receiver: receiver, + GroupKey: ag.GroupKey(), + RouteID: ag.routeID, } alerts := ag.alerts.List() @@ -326,7 +333,7 @@ func (d *Dispatcher) processAlert(alert *types.Alert, route *Route) { // If the group does not exist, create it. But check the limit first. if limit := d.limits.MaxNumberOfAggregationGroups(); limit > 0 && d.aggrGroupsNum >= limit { d.metrics.aggrGroupLimitReached.Inc() - level.Error(d.logger).Log("msg", "Too many aggregation groups, cannot create new group for alert", "groups", d.aggrGroupsNum, "limit", limit, "alert", alert.Name()) + d.logger.Error("Too many aggregation groups, cannot create new group for alert", "groups", d.aggrGroupsNum, "limit", limit, "alert", alert.Name()) return } @@ -343,14 +350,15 @@ func (d *Dispatcher) processAlert(alert *types.Alert, route *Route) { go ag.run(func(ctx context.Context, alerts ...*types.Alert) bool { _, _, err := d.stage.Exec(ctx, d.logger, alerts...) if err != nil { - lvl := level.Error(d.logger) + logger := d.logger.With("num_alerts", len(alerts), "err", err) if errors.Is(ctx.Err(), context.Canceled) { // It is expected for the context to be canceled on // configuration reload or shutdown. In this case, the // message should only be logged at the debug level. - lvl = level.Debug(d.logger) + logger.Debug("Notify for alerts failed") + } else { + logger.Error("Notify for alerts failed") } - lvl.Log("msg", "Notify for alerts failed", "num_alerts", len(alerts), "err", err) } return err == nil }) @@ -373,7 +381,8 @@ func getGroupLabels(alert *types.Alert, route *Route) model.LabelSet { type aggrGroup struct { labels model.LabelSet opts *RouteOpts - logger log.Logger + logger *slog.Logger + routeID string routeKey string alerts *store.Alerts @@ -388,12 +397,13 @@ type aggrGroup struct { } // newAggrGroup returns a new aggregation group. -func newAggrGroup(ctx context.Context, labels model.LabelSet, r *Route, to func(time.Duration) time.Duration, logger log.Logger) *aggrGroup { +func newAggrGroup(ctx context.Context, labels model.LabelSet, r *Route, to func(time.Duration) time.Duration, logger *slog.Logger) *aggrGroup { if to == nil { to = func(d time.Duration) time.Duration { return d } } ag := &aggrGroup{ labels: labels, + routeID: r.ID(), routeKey: r.Key(), opts: &r.RouteOpts, timeout: to, @@ -402,7 +412,7 @@ func newAggrGroup(ctx context.Context, labels model.LabelSet, r *Route, to func( } ag.ctx, ag.cancel = context.WithCancel(ctx) - ag.logger = log.With(logger, "aggrGroup", ag) + ag.logger = logger.With("aggrGroup", ag) // Set an initial one-time wait before flushing // the first batch of notifications. @@ -447,6 +457,7 @@ func (ag *aggrGroup) run(nf notifyFunc) { ctx = notify.WithRepeatInterval(ctx, ag.opts.RepeatInterval) ctx = notify.WithMuteTimeIntervals(ctx, ag.opts.MuteTimeIntervals) ctx = notify.WithActiveTimeIntervals(ctx, ag.opts.ActiveTimeIntervals) + ctx = notify.WithRouteID(ctx, ag.routeID) // Wait the configured interval before calling flush again. ag.mtx.Lock() @@ -476,7 +487,7 @@ func (ag *aggrGroup) stop() { // insert inserts the alert into the aggregation group. func (ag *aggrGroup) insert(alert *types.Alert) { if err := ag.alerts.Set(alert); err != nil { - level.Error(ag.logger).Log("msg", "error on set alert", "err", err) + ag.logger.Error("error on set alert", "err", err) } // Immediately trigger a flush if the wait duration for this @@ -499,38 +510,32 @@ func (ag *aggrGroup) flush(notify func(...*types.Alert) bool) { } var ( - alerts = ag.alerts.List() - alertsSlice = make(types.AlertSlice, 0, len(alerts)) - now = time.Now() + alerts = ag.alerts.List() + alertsSlice = make(types.AlertSlice, 0, len(alerts)) + resolvedSlice = make(types.AlertSlice, 0, len(alerts)) + now = time.Now() ) for _, alert := range alerts { a := *alert // Ensure that alerts don't resolve as time move forwards. - if !a.ResolvedAt(now) { + if a.ResolvedAt(now) { + resolvedSlice = append(resolvedSlice, &a) + } else { a.EndsAt = time.Time{} } alertsSlice = append(alertsSlice, &a) } sort.Stable(alertsSlice) - level.Debug(ag.logger).Log("msg", "flushing", "alerts", fmt.Sprintf("%v", alertsSlice)) + ag.logger.Debug("flushing", "alerts", fmt.Sprintf("%v", alertsSlice)) if notify(alertsSlice...) { - for _, a := range alertsSlice { - // Only delete if the fingerprint has not been inserted - // again since we notified about it. - fp := a.Fingerprint() - got, err := ag.alerts.Get(fp) - if err != nil { - // This should never happen. - level.Error(ag.logger).Log("msg", "failed to get alert", "err", err, "alert", a.String()) - continue - } - if a.Resolved() && got.UpdatedAt == a.UpdatedAt { - if err := ag.alerts.Delete(fp); err != nil { - level.Error(ag.logger).Log("msg", "error on delete alert", "err", err, "alert", a.String()) - } - } + // Delete all resolved alerts as we just sent a notification for them, + // and we don't want to send another one. However, we need to make sure + // that each resolved alert has not fired again during the flush as then + // we would delete an active alert thinking it was resolved. + if err := ag.alerts.DeleteIfNotModified(resolvedSlice); err != nil { + ag.logger.Error("error on delete alerts", "err", err) } } } diff --git a/vendor/github.com/prometheus/alertmanager/dispatch/route.go b/vendor/github.com/prometheus/alertmanager/dispatch/route.go index 5ada178dab..e174672d3f 100644 --- a/vendor/github.com/prometheus/alertmanager/dispatch/route.go +++ b/vendor/github.com/prometheus/alertmanager/dispatch/route.go @@ -17,6 +17,7 @@ import ( "encoding/json" "fmt" "sort" + "strconv" "strings" "time" @@ -184,22 +185,23 @@ func (r *Route) Key() string { func (r *Route) ID() string { b := strings.Builder{} - position := -1 if r.parent != nil { - // Find the position in the same level leaf. - for i, cr := range r.parent.Routes { - if cr == r { - position = i + b.WriteString(r.parent.ID()) + b.WriteRune('/') + } + + b.WriteString(r.Matchers.String()) + + if r.parent != nil { + for i := range r.parent.Routes { + if r == r.parent.Routes[i] { + b.WriteRune('/') + b.WriteString(strconv.Itoa(i)) break } } } - b.WriteString(r.Key()) - if position > -1 { - b.WriteRune('/') - b.WriteString(fmt.Sprint(position)) - } return b.String() } diff --git a/vendor/github.com/prometheus/alertmanager/featurecontrol/featurecontrol.go b/vendor/github.com/prometheus/alertmanager/featurecontrol/featurecontrol.go index 9ff7a2d8fd..1df215acb1 100644 --- a/vendor/github.com/prometheus/alertmanager/featurecontrol/featurecontrol.go +++ b/vendor/github.com/prometheus/alertmanager/featurecontrol/featurecontrol.go @@ -16,35 +16,41 @@ package featurecontrol import ( "errors" "fmt" + "log/slog" "strings" - - "github.com/go-kit/log" - "github.com/go-kit/log/level" ) const ( FeatureReceiverNameInMetrics = "receiver-name-in-metrics" FeatureClassicMode = "classic-mode" FeatureUTF8StrictMode = "utf8-strict-mode" + FeatureAutoGOMEMLIMIT = "auto-gomemlimit" + FeatureAutoGOMAXPROCS = "auto-gomaxprocs" ) var AllowedFlags = []string{ FeatureReceiverNameInMetrics, FeatureClassicMode, FeatureUTF8StrictMode, + FeatureAutoGOMEMLIMIT, + FeatureAutoGOMAXPROCS, } type Flagger interface { EnableReceiverNamesInMetrics() bool ClassicMode() bool UTF8StrictMode() bool + EnableAutoGOMEMLIMIT() bool + EnableAutoGOMAXPROCS() bool } type Flags struct { - logger log.Logger + logger *slog.Logger enableReceiverNamesInMetrics bool classicMode bool utf8StrictMode bool + enableAutoGOMEMLIMIT bool + enableAutoGOMAXPROCS bool } func (f *Flags) EnableReceiverNamesInMetrics() bool { @@ -59,6 +65,14 @@ func (f *Flags) UTF8StrictMode() bool { return f.utf8StrictMode } +func (f *Flags) EnableAutoGOMEMLIMIT() bool { + return f.enableAutoGOMEMLIMIT +} + +func (f *Flags) EnableAutoGOMAXPROCS() bool { + return f.enableAutoGOMAXPROCS +} + type flagOption func(flags *Flags) func enableReceiverNameInMetrics() flagOption { @@ -79,7 +93,19 @@ func enableUTF8StrictMode() flagOption { } } -func NewFlags(logger log.Logger, features string) (Flagger, error) { +func enableAutoGOMEMLIMIT() flagOption { + return func(configs *Flags) { + configs.enableAutoGOMEMLIMIT = true + } +} + +func enableAutoGOMAXPROCS() flagOption { + return func(configs *Flags) { + configs.enableAutoGOMAXPROCS = true + } +} + +func NewFlags(logger *slog.Logger, features string) (Flagger, error) { fc := &Flags{logger: logger} opts := []flagOption{} @@ -91,13 +117,19 @@ func NewFlags(logger log.Logger, features string) (Flagger, error) { switch feature { case FeatureReceiverNameInMetrics: opts = append(opts, enableReceiverNameInMetrics()) - level.Warn(logger).Log("msg", "Experimental receiver name in metrics enabled") + logger.Warn("Experimental receiver name in metrics enabled") case FeatureClassicMode: opts = append(opts, enableClassicMode()) - level.Warn(logger).Log("msg", "Classic mode enabled") + logger.Warn("Classic mode enabled") case FeatureUTF8StrictMode: opts = append(opts, enableUTF8StrictMode()) - level.Warn(logger).Log("msg", "UTF-8 strict mode enabled") + logger.Warn("UTF-8 strict mode enabled") + case FeatureAutoGOMEMLIMIT: + opts = append(opts, enableAutoGOMEMLIMIT()) + logger.Warn("Automatically set GOMEMLIMIT to match the Linux container or system memory limit.") + case FeatureAutoGOMAXPROCS: + opts = append(opts, enableAutoGOMAXPROCS()) + logger.Warn("Automatically set GOMAXPROCS to match Linux container CPU quota") default: return nil, fmt.Errorf("Unknown option '%s' for --enable-feature", feature) } @@ -121,3 +153,7 @@ func (n NoopFlags) EnableReceiverNamesInMetrics() bool { return false } func (n NoopFlags) ClassicMode() bool { return false } func (n NoopFlags) UTF8StrictMode() bool { return false } + +func (n NoopFlags) EnableAutoGOMEMLIMIT() bool { return false } + +func (n NoopFlags) EnableAutoGOMAXPROCS() bool { return false } diff --git a/vendor/github.com/prometheus/alertmanager/inhibit/inhibit.go b/vendor/github.com/prometheus/alertmanager/inhibit/inhibit.go index a8f96f28aa..eb1f7752ea 100644 --- a/vendor/github.com/prometheus/alertmanager/inhibit/inhibit.go +++ b/vendor/github.com/prometheus/alertmanager/inhibit/inhibit.go @@ -15,11 +15,10 @@ package inhibit import ( "context" + "log/slog" "sync" "time" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/oklog/run" "github.com/prometheus/common/model" @@ -36,15 +35,15 @@ import ( type Inhibitor struct { alerts provider.Alerts rules []*InhibitRule - marker types.Marker - logger log.Logger + marker types.AlertMarker + logger *slog.Logger mtx sync.RWMutex cancel func() } // NewInhibitor returns a new Inhibitor. -func NewInhibitor(ap provider.Alerts, rs []config.InhibitRule, mk types.Marker, logger log.Logger) *Inhibitor { +func NewInhibitor(ap provider.Alerts, rs []config.InhibitRule, mk types.AlertMarker, logger *slog.Logger) *Inhibitor { ih := &Inhibitor{ alerts: ap, marker: mk, @@ -67,14 +66,14 @@ func (ih *Inhibitor) run(ctx context.Context) { return case a := <-it.Next(): if err := it.Err(); err != nil { - level.Error(ih.logger).Log("msg", "Error iterating alerts", "err", err) + ih.logger.Error("Error iterating alerts", "err", err) continue } // Update the inhibition rules' cache. for _, r := range ih.rules { if r.SourceMatchers.Matches(a.Labels) { if err := r.scache.Set(a); err != nil { - level.Error(ih.logger).Log("msg", "error on set alert", "err", err) + ih.logger.Error("error on set alert", "err", err) } } } @@ -106,7 +105,7 @@ func (ih *Inhibitor) Run() { }) if err := g.Run(); err != nil { - level.Warn(ih.logger).Log("msg", "error running inhibitor", "err", err) + ih.logger.Warn("error running inhibitor", "err", err) } } diff --git a/vendor/github.com/prometheus/alertmanager/matchers/compat/parse.go b/vendor/github.com/prometheus/alertmanager/matcher/compat/parse.go similarity index 65% rename from vendor/github.com/prometheus/alertmanager/matchers/compat/parse.go rename to vendor/github.com/prometheus/alertmanager/matcher/compat/parse.go index 0c0dfffb1f..667b6cd7cb 100644 --- a/vendor/github.com/prometheus/alertmanager/matchers/compat/parse.go +++ b/vendor/github.com/prometheus/alertmanager/matcher/compat/parse.go @@ -15,23 +15,23 @@ package compat import ( "fmt" + "log/slog" "reflect" "strings" "unicode/utf8" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/prometheus/common/model" + "github.com/prometheus/common/promslog" "github.com/prometheus/alertmanager/featurecontrol" - "github.com/prometheus/alertmanager/matchers/parse" + "github.com/prometheus/alertmanager/matcher/parse" "github.com/prometheus/alertmanager/pkg/labels" ) var ( - isValidLabelName = isValidClassicLabelName(log.NewNopLogger()) - parseMatcher = ClassicMatcherParser(log.NewNopLogger()) - parseMatchers = ClassicMatchersParser(log.NewNopLogger()) + isValidLabelName = isValidClassicLabelName(promslog.NewNopLogger()) + parseMatcher = ClassicMatcherParser(promslog.NewNopLogger()) + parseMatchers = ClassicMatchersParser(promslog.NewNopLogger()) ) // IsValidLabelName returns true if the string is a valid label name. @@ -56,7 +56,7 @@ func Matchers(input, origin string) (labels.Matchers, error) { } // InitFromFlags initializes the compat package from the flagger. -func InitFromFlags(l log.Logger, f featurecontrol.Flagger) { +func InitFromFlags(l *slog.Logger, f featurecontrol.Flagger) { if f.ClassicMode() { isValidLabelName = isValidClassicLabelName(l) parseMatcher = ClassicMatcherParser(l) @@ -74,27 +74,27 @@ func InitFromFlags(l log.Logger, f featurecontrol.Flagger) { // ClassicMatcherParser uses the pkg/labels parser to parse the matcher in // the input string. -func ClassicMatcherParser(l log.Logger) ParseMatcher { +func ClassicMatcherParser(l *slog.Logger) ParseMatcher { return func(input, origin string) (matcher *labels.Matcher, err error) { - level.Debug(l).Log("msg", "Parsing with classic matchers parser", "input", input, "origin", origin) + l.Debug("Parsing with classic matchers parser", "input", input, "origin", origin) return labels.ParseMatcher(input) } } // ClassicMatchersParser uses the pkg/labels parser to parse zero or more // matchers in the input string. It returns an error if the input is invalid. -func ClassicMatchersParser(l log.Logger) ParseMatchers { +func ClassicMatchersParser(l *slog.Logger) ParseMatchers { return func(input, origin string) (matchers labels.Matchers, err error) { - level.Debug(l).Log("msg", "Parsing with classic matchers parser", "input", input, "origin", origin) + l.Debug("Parsing with classic matchers parser", "input", input, "origin", origin) return labels.ParseMatchers(input) } } -// UTF8MatcherParser uses the new matchers/parse parser to parse the matcher +// UTF8MatcherParser uses the new matcher/parse parser to parse the matcher // in the input string. If this fails it does not revert to the pkg/labels parser. -func UTF8MatcherParser(l log.Logger) ParseMatcher { +func UTF8MatcherParser(l *slog.Logger) ParseMatcher { return func(input, origin string) (matcher *labels.Matcher, err error) { - level.Debug(l).Log("msg", "Parsing with UTF-8 matchers parser", "input", input, "origin", origin) + l.Debug("Parsing with UTF-8 matchers parser", "input", input, "origin", origin) if strings.HasPrefix(input, "{") || strings.HasSuffix(input, "}") { return nil, fmt.Errorf("unexpected open or close brace: %s", input) } @@ -102,22 +102,22 @@ func UTF8MatcherParser(l log.Logger) ParseMatcher { } } -// UTF8MatchersParser uses the new matchers/parse parser to parse zero or more +// UTF8MatchersParser uses the new matcher/parse parser to parse zero or more // matchers in the input string. If this fails it does not revert to the // pkg/labels parser. -func UTF8MatchersParser(l log.Logger) ParseMatchers { +func UTF8MatchersParser(l *slog.Logger) ParseMatchers { return func(input, origin string) (matchers labels.Matchers, err error) { - level.Debug(l).Log("msg", "Parsing with UTF-8 matchers parser", "input", input, "origin", origin) + l.Debug("Parsing with UTF-8 matchers parser", "input", input, "origin", origin) return parse.Matchers(input) } } -// FallbackMatcherParser uses the new matchers/parse parser to parse zero or more +// FallbackMatcherParser uses the new matcher/parse parser to parse zero or more // matchers in the string. If this fails it reverts to the pkg/labels parser and // emits a warning log line. -func FallbackMatcherParser(l log.Logger) ParseMatcher { +func FallbackMatcherParser(l *slog.Logger) ParseMatcher { return func(input, origin string) (matcher *labels.Matcher, err error) { - level.Debug(l).Log("msg", "Parsing with UTF-8 matchers parser, with fallback to classic matchers parser", "input", input, "origin", origin) + l.Debug("Parsing with UTF-8 matchers parser, with fallback to classic matchers parser", "input", input, "origin", origin) if strings.HasPrefix(input, "{") || strings.HasSuffix(input, "}") { return nil, fmt.Errorf("unexpected open or close brace: %s", input) } @@ -130,28 +130,28 @@ func FallbackMatcherParser(l log.Logger) ParseMatcher { if cErr != nil { return nil, cErr } - // The input is valid in the pkg/labels parser, but not the matchers/parse + // The input is valid in the pkg/labels parser, but not the matcher/parse // parser. This means the input is not forwards compatible. suggestion := cMatcher.String() - level.Warn(l).Log("msg", "Alertmanager is moving to a new parser for labels and matchers, and this input is incompatible. Alertmanager has instead parsed the input using the classic matchers parser as a fallback. To make this input compatible with the UTF-8 matchers parser please make sure all regular expressions and values are double-quoted. If you are still seeing this message please open an issue.", "input", input, "origin", origin, "err", nErr, "suggestion", suggestion) + l.Warn("Alertmanager is moving to a new parser for labels and matchers, and this input is incompatible. Alertmanager has instead parsed the input using the classic matchers parser as a fallback. To make this input compatible with the UTF-8 matchers parser please make sure all regular expressions and values are double-quoted and backslashes are escaped. If you are still seeing this message please open an issue.", "input", input, "origin", origin, "err", nErr, "suggestion", suggestion) return cMatcher, nil } // If the input is valid in both parsers, but produces different results, // then there is disagreement. if nErr == nil && cErr == nil && !reflect.DeepEqual(nMatcher, cMatcher) { - level.Warn(l).Log("msg", "Matchers input has disagreement", "input", input, "origin", origin) + l.Warn("Matchers input has disagreement", "input", input, "origin", origin) return cMatcher, nil } return nMatcher, nil } } -// FallbackMatchersParser uses the new matchers/parse parser to parse the +// FallbackMatchersParser uses the new matcher/parse parser to parse the // matcher in the input string. If this fails it falls back to the pkg/labels // parser and emits a warning log line. -func FallbackMatchersParser(l log.Logger) ParseMatchers { +func FallbackMatchersParser(l *slog.Logger) ParseMatchers { return func(input, origin string) (matchers labels.Matchers, err error) { - level.Debug(l).Log("msg", "Parsing with UTF-8 matchers parser, with fallback to classic matchers parser", "input", input, "origin", origin) + l.Debug("Parsing with UTF-8 matchers parser, with fallback to classic matchers parser", "input", input, "origin", origin) // Parse the input in both parsers to look for disagreement and incompatible // inputs. nMatchers, nErr := parse.Matchers(input) @@ -161,7 +161,7 @@ func FallbackMatchersParser(l log.Logger) ParseMatchers { if cErr != nil { return nil, cErr } - // The input is valid in the pkg/labels parser, but not the matchers/parse + // The input is valid in the pkg/labels parser, but not the matcher/parse // parser. This means the input is not forwards compatible. var sb strings.Builder for i, n := range cMatchers { @@ -172,15 +172,15 @@ func FallbackMatchersParser(l log.Logger) ParseMatchers { } suggestion := sb.String() // The input is valid in the pkg/labels parser, but not the - // new matchers/parse parser. - level.Warn(l).Log("msg", "Alertmanager is moving to a new parser for labels and matchers, and this input is incompatible. Alertmanager has instead parsed the input using the classic matchers parser as a fallback. To make this input compatible with the UTF-8 matchers parser please make sure all regular expressions and values are double-quoted. If you are still seeing this message please open an issue.", "input", input, "origin", origin, "err", nErr, "suggestion", suggestion) + // new matcher/parse parser. + l.Warn("Alertmanager is moving to a new parser for labels and matchers, and this input is incompatible. Alertmanager has instead parsed the input using the classic matchers parser as a fallback. To make this input compatible with the UTF-8 matchers parser please make sure all regular expressions and values are double-quoted and backslashes are escaped. If you are still seeing this message please open an issue.", "input", input, "origin", origin, "err", nErr, "suggestion", suggestion) return cMatchers, nil } // If the input is valid in both parsers, but produces different results, // then there is disagreement. We need to compare to labels.Matchers(cMatchers) // as cMatchers is a []*labels.Matcher not labels.Matchers. if nErr == nil && cErr == nil && !reflect.DeepEqual(nMatchers, labels.Matchers(cMatchers)) { - level.Warn(l).Log("msg", "Matchers input has disagreement", "input", input, "origin", origin) + l.Warn("Matchers input has disagreement", "input", input, "origin", origin) return cMatchers, nil } return nMatchers, nil @@ -188,14 +188,14 @@ func FallbackMatchersParser(l log.Logger) ParseMatchers { } // isValidClassicLabelName returns true if the string is a valid classic label name. -func isValidClassicLabelName(_ log.Logger) func(model.LabelName) bool { +func isValidClassicLabelName(_ *slog.Logger) func(model.LabelName) bool { return func(name model.LabelName) bool { return name.IsValid() } } // isValidUTF8LabelName returns true if the string is a valid UTF-8 label name. -func isValidUTF8LabelName(_ log.Logger) func(model.LabelName) bool { +func isValidUTF8LabelName(_ *slog.Logger) func(model.LabelName) bool { return func(name model.LabelName) bool { if len(name) == 0 { return false diff --git a/vendor/github.com/prometheus/alertmanager/matchers/parse/lexer.go b/vendor/github.com/prometheus/alertmanager/matcher/parse/lexer.go similarity index 99% rename from vendor/github.com/prometheus/alertmanager/matchers/parse/lexer.go rename to vendor/github.com/prometheus/alertmanager/matcher/parse/lexer.go index d6daa6a9e8..52bb03925e 100644 --- a/vendor/github.com/prometheus/alertmanager/matchers/parse/lexer.go +++ b/vendor/github.com/prometheus/alertmanager/matcher/parse/lexer.go @@ -251,7 +251,7 @@ func (l *lexer) accept(valid string) bool { } // expect consumes the next rune if its one of the valid runes. -// it returns nil if the next rune is valid, otherwise an expectedError +// It returns nil if the next rune is valid, otherwise an expectedError // error. func (l *lexer) expect(valid string) error { if strings.ContainsRune(valid, l.next()) { diff --git a/vendor/github.com/prometheus/alertmanager/matchers/parse/parse.go b/vendor/github.com/prometheus/alertmanager/matcher/parse/parse.go similarity index 98% rename from vendor/github.com/prometheus/alertmanager/matchers/parse/parse.go rename to vendor/github.com/prometheus/alertmanager/matcher/parse/parse.go index 30a95b2554..34e36203c0 100644 --- a/vendor/github.com/prometheus/alertmanager/matchers/parse/parse.go +++ b/vendor/github.com/prometheus/alertmanager/matcher/parse/parse.go @@ -196,7 +196,7 @@ func (p *parser) parseEndOfMatcher(l *lexer) (parseFunc, error) { if err != nil { if errors.Is(err, errEOF) { // If this is the end of input we still need to check if the optional - // open brace has a matching close brace + // open brace has a matching close brace. return p.parseCloseBrace, nil } return nil, fmt.Errorf("%w: %w", err, errExpectedCommaOrCloseBrace) @@ -220,7 +220,7 @@ func (p *parser) parseComma(l *lexer) (parseFunc, error) { if err != nil { if errors.Is(err, errEOF) { // If this is the end of input we still need to check if the optional - // open brace has a matching close brace + // open brace has a matching close brace. return p.parseCloseBrace, nil } return nil, fmt.Errorf("%w: %w", err, errExpectedMatcherOrCloseBrace) @@ -242,6 +242,7 @@ func (p *parser) parseEOF(l *lexer) (parseFunc, error) { return nil, nil } +// nolint:godot // accept returns true if the next token is one of the specified kinds, // otherwise false. If the token is accepted it is consumed. tokenEOF is // not an accepted kind and instead accept returns ErrEOF if there is no @@ -256,6 +257,7 @@ func (p *parser) accept(l *lexer, kinds ...tokenKind) (ok bool, err error) { return ok, err } +// nolint:godot // acceptPeek returns true if the next token is one of the specified kinds, // otherwise false. However, unlike accept, acceptPeek does not consume accepted // tokens. tokenEOF is not an accepted kind and instead accept returns ErrEOF @@ -271,6 +273,7 @@ func (p *parser) acceptPeek(l *lexer, kinds ...tokenKind) (bool, error) { return t.isOneOf(kinds...), nil } +// nolint:godot // expect returns the next token if it is one of the specified kinds, otherwise // it returns an error. If the token is expected it is consumed. tokenEOF is not // an accepted kind and instead expect returns ErrEOF if there is no more input. @@ -285,6 +288,7 @@ func (p *parser) expect(l *lexer, kind ...tokenKind) (token, error) { return t, nil } +// nolint:godot // expect returns the next token if it is one of the specified kinds, otherwise // it returns an error. However, unlike expect, expectPeek does not consume tokens. // tokenEOF is not an accepted kind and instead expect returns ErrEOF if there is no diff --git a/vendor/github.com/prometheus/alertmanager/matchers/parse/token.go b/vendor/github.com/prometheus/alertmanager/matcher/parse/token.go similarity index 100% rename from vendor/github.com/prometheus/alertmanager/matchers/parse/token.go rename to vendor/github.com/prometheus/alertmanager/matcher/parse/token.go diff --git a/vendor/github.com/prometheus/alertmanager/nflog/nflog.go b/vendor/github.com/prometheus/alertmanager/nflog/nflog.go index c533dd0e66..a6c3160880 100644 --- a/vendor/github.com/prometheus/alertmanager/nflog/nflog.go +++ b/vendor/github.com/prometheus/alertmanager/nflog/nflog.go @@ -22,16 +22,16 @@ import ( "errors" "fmt" "io" + "log/slog" "math/rand" "os" "sync" "time" - "github.com/benbjohnson/clock" - "github.com/go-kit/log" - "github.com/go-kit/log/level" + "github.com/coder/quartz" "github.com/matttproud/golang_protobuf_extensions/pbutil" "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/common/promslog" "github.com/prometheus/alertmanager/cluster" pb "github.com/prometheus/alertmanager/nflog/nflogpb" @@ -46,7 +46,7 @@ var ErrInvalidState = errors.New("invalid state") // query currently allows filtering by and/or receiver group key. // It is configured via QueryParameter functions. // -// TODO(fabxc): Future versions could allow querying a certain receiver +// TODO(fabxc): Future versions could allow querying a certain receiver, // group or a given time interval. type query struct { recv *pb.Receiver @@ -76,9 +76,9 @@ func QGroupKey(gk string) QueryParam { // Log holds the notification log state for alerts that have been notified. type Log struct { - clock clock.Clock + clock quartz.Clock - logger log.Logger + logger *slog.Logger metrics *metrics retention time.Duration @@ -139,8 +139,12 @@ func newMetrics(r prometheus.Registerer) *metrics { Help: "Number notification log received queries that failed.", }) m.queryDuration = prometheus.NewHistogram(prometheus.HistogramOpts{ - Name: "alertmanager_nflog_query_duration_seconds", - Help: "Duration of notification log query evaluation.", + Name: "alertmanager_nflog_query_duration_seconds", + Help: "Duration of notification log query evaluation.", + Buckets: prometheus.DefBuckets, + NativeHistogramBucketFactor: 1.1, + NativeHistogramMaxBucketNumber: 100, + NativeHistogramMinResetDuration: 1 * time.Hour, }) m.propagatedMessagesTotal = prometheus.NewCounter(prometheus.CounterOpts{ Name: "alertmanager_nflog_gossip_messages_propagated_total", @@ -235,7 +239,7 @@ type Options struct { Retention time.Duration - Logger log.Logger + Logger *slog.Logger Metrics prometheus.Registerer } @@ -255,9 +259,9 @@ func New(o Options) (*Log, error) { } l := &Log{ - clock: clock.New(), + clock: quartz.NewReal(), retention: o.Retention, - logger: log.NewNopLogger(), + logger: promslog.NewNopLogger(), st: state{}, broadcast: func([]byte) {}, metrics: newMetrics(o.Metrics), @@ -272,7 +276,7 @@ func New(o Options) (*Log, error) { if !os.IsNotExist(err) { return nil, err } - level.Debug(l.logger).Log("msg", "notification log snapshot file doesn't exist", "err", err) + l.logger.Debug("notification log snapshot file doesn't exist", "err", err) } else { o.SnapshotReader = r defer r.Close() @@ -298,10 +302,10 @@ func (l *Log) now() time.Time { // If not nil, the last argument is an override for what to do as part of the maintenance - for advanced usage. func (l *Log) Maintenance(interval time.Duration, snapf string, stopc <-chan struct{}, override MaintenanceFunc) { if interval == 0 || stopc == nil { - level.Error(l.logger).Log("msg", "interval or stop signal are missing - not running maintenance") + l.logger.Error("interval or stop signal are missing - not running maintenance") return } - t := l.clock.Ticker(interval) + t := l.clock.NewTicker(interval) defer t.Stop() var doMaintenance MaintenanceFunc @@ -331,14 +335,14 @@ func (l *Log) Maintenance(interval time.Duration, snapf string, stopc <-chan str runMaintenance := func(do func() (int64, error)) error { l.metrics.maintenanceTotal.Inc() start := l.now().UTC() - level.Debug(l.logger).Log("msg", "Running maintenance") + l.logger.Debug("Running maintenance") size, err := do() l.metrics.snapshotSize.Set(float64(size)) if err != nil { l.metrics.maintenanceErrorsTotal.Inc() return err } - level.Debug(l.logger).Log("msg", "Maintenance done", "duration", l.now().Sub(start), "size", size) + l.logger.Debug("Maintenance done", "duration", l.now().Sub(start), "size", size) return nil } @@ -349,7 +353,7 @@ Loop: break Loop case <-t.C: if err := runMaintenance(doMaintenance); err != nil { - level.Error(l.logger).Log("msg", "Running maintenance failed", "err", err) + l.logger.Error("Running maintenance failed", "err", err) } } } @@ -359,7 +363,7 @@ Loop: return } if err := runMaintenance(doMaintenance); err != nil { - level.Error(l.logger).Log("msg", "Creating shutdown snapshot failed", "err", err) + l.logger.Error("Creating shutdown snapshot failed", "err", err) } } @@ -530,7 +534,7 @@ func (l *Log) Merge(b []byte) error { // all nodes already. l.broadcast(b) l.metrics.propagatedMessagesTotal.Inc() - level.Debug(l.logger).Log("msg", "gossiping new entry", "entry", e) + l.logger.Debug("gossiping new entry", "entry", e) } } return nil diff --git a/vendor/github.com/prometheus/alertmanager/notify/discord/discord.go b/vendor/github.com/prometheus/alertmanager/notify/discord/discord.go index 69aff39957..42d8724e67 100644 --- a/vendor/github.com/prometheus/alertmanager/notify/discord/discord.go +++ b/vendor/github.com/prometheus/alertmanager/notify/discord/discord.go @@ -18,12 +18,12 @@ import ( "context" "encoding/json" "fmt" + "log/slog" "net/http" + netUrl "net/url" "os" "strings" - "github.com/go-kit/log" - "github.com/go-kit/log/level" commoncfg "github.com/prometheus/common/config" "github.com/prometheus/common/model" @@ -38,6 +38,8 @@ const ( maxTitleLenRunes = 256 // https://discord.com/developers/docs/resources/channel#embed-object-embed-limits - 4096 characters or runes. maxDescriptionLenRunes = 4096 + + maxContentLenRunes = 2000 ) const ( @@ -50,14 +52,14 @@ const ( type Notifier struct { conf *config.DiscordConfig tmpl *template.Template - logger log.Logger + logger *slog.Logger client *http.Client retrier *notify.Retrier webhookURL *config.SecretURL } // New returns a new Discord notifier. -func New(c *config.DiscordConfig, t *template.Template, l log.Logger, httpOpts ...commoncfg.HTTPClientOption) (*Notifier, error) { +func New(c *config.DiscordConfig, t *template.Template, l *slog.Logger, httpOpts ...commoncfg.HTTPClientOption) (*Notifier, error) { client, err := commoncfg.NewClientFromConfig(*c.HTTPConfig, "discord", httpOpts...) if err != nil { return nil, err @@ -74,8 +76,10 @@ func New(c *config.DiscordConfig, t *template.Template, l log.Logger, httpOpts . } type webhook struct { - Content string `json:"content"` - Embeds []webhookEmbed `json:"embeds"` + Content string `json:"content"` + Embeds []webhookEmbed `json:"embeds"` + Username string `json:"username,omitempty"` + AvatarURL string `json:"avatar_url,omitempty"` } type webhookEmbed struct { @@ -91,7 +95,7 @@ func (n *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error) return false, err } - level.Debug(n.logger).Log("incident", key) + n.logger.Debug("extracted group key", "key", key) alerts := types.Alerts(as...) data := notify.GetTemplateData(ctx, n.tmpl, as, n.logger) @@ -105,14 +109,22 @@ func (n *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error) return false, err } if truncated { - level.Warn(n.logger).Log("msg", "Truncated title", "key", key, "max_runes", maxTitleLenRunes) + n.logger.Warn("Truncated title", "key", key, "max_runes", maxTitleLenRunes) } description, truncated := notify.TruncateInRunes(tmpl(n.conf.Message), maxDescriptionLenRunes) if err != nil { return false, err } if truncated { - level.Warn(n.logger).Log("msg", "Truncated message", "key", key, "max_runes", maxDescriptionLenRunes) + n.logger.Warn("Truncated message", "key", key, "max_runes", maxDescriptionLenRunes) + } + + content, truncated := notify.TruncateInRunes(tmpl(n.conf.Content), maxContentLenRunes) + if err != nil { + return false, err + } + if truncated { + n.logger.Warn("Truncated message", "key", key, "max_runes", maxContentLenRunes) } color := colorGrey @@ -127,14 +139,16 @@ func (n *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error) if n.conf.WebhookURL != nil { url = n.conf.WebhookURL.String() } else { - content, err := os.ReadFile(n.conf.WebhookURLFile) + b, err := os.ReadFile(n.conf.WebhookURLFile) if err != nil { return false, fmt.Errorf("read webhook_url_file: %w", err) } - url = strings.TrimSpace(string(content)) + url = strings.TrimSpace(string(b)) } w := webhook{ + Content: content, + Username: n.conf.Username, Embeds: []webhookEmbed{{ Title: title, Description: description, @@ -142,6 +156,14 @@ func (n *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error) }}, } + if len(n.conf.AvatarURL) != 0 { + if _, err := netUrl.Parse(n.conf.AvatarURL); err == nil { + w.AvatarURL = n.conf.AvatarURL + } else { + n.logger.Warn("Bad avatar url", "key", key) + } + } + var payload bytes.Buffer if err = json.NewEncoder(&payload).Encode(w); err != nil { return false, err diff --git a/vendor/github.com/prometheus/alertmanager/notify/email/email.go b/vendor/github.com/prometheus/alertmanager/notify/email/email.go index d7e5be7bdc..7bd910ca6b 100644 --- a/vendor/github.com/prometheus/alertmanager/notify/email/email.go +++ b/vendor/github.com/prometheus/alertmanager/notify/email/email.go @@ -19,6 +19,7 @@ import ( "crypto/tls" "errors" "fmt" + "log/slog" "math/rand" "mime" "mime/multipart" @@ -29,10 +30,9 @@ import ( "net/textproto" "os" "strings" + "sync" "time" - "github.com/go-kit/log" - "github.com/go-kit/log/level" commoncfg "github.com/prometheus/common/config" "github.com/prometheus/alertmanager/config" @@ -45,12 +45,12 @@ import ( type Email struct { conf *config.EmailConfig tmpl *template.Template - logger log.Logger + logger *slog.Logger hostname string } // New returns a new Email notifier. -func New(c *config.EmailConfig, t *template.Template, l log.Logger) *Email { +func New(c *config.EmailConfig, t *template.Template, l *slog.Logger) *Email { if _, ok := c.Headers["Subject"]; !ok { c.Headers["Subject"] = config.DefaultEmailSubject } @@ -75,7 +75,7 @@ func (n *Email) auth(mechs string) (smtp.Auth, error) { // If no username is set, keep going without authentication. if n.conf.AuthUsername == "" { - level.Debug(n.logger).Log("msg", "smtp_auth_username is not configured. Attempting to send email without authenticating") + n.logger.Debug("smtp_auth_username is not configured. Attempting to send email without authenticating") return nil, nil } @@ -131,7 +131,7 @@ func (n *Email) Notify(ctx context.Context, as ...*types.Alert) (bool, error) { success = false ) if n.conf.Smarthost.Port == "465" { - tlsConfig, err := commoncfg.NewTLSConfig(&n.conf.TLSConfig) + tlsConfig, err := commoncfg.NewTLSConfig(n.conf.TLSConfig) if err != nil { return false, fmt.Errorf("parse TLS configuration: %w", err) } @@ -161,7 +161,7 @@ func (n *Email) Notify(ctx context.Context, as ...*types.Alert) (bool, error) { defer func() { // Try to clean up after ourselves but don't log anything if something has failed. if err := c.Quit(); success && err != nil { - level.Warn(n.logger).Log("msg", "failed to close SMTP connection", "err", err) + n.logger.Warn("failed to close SMTP connection", "err", err) } }() @@ -178,7 +178,7 @@ func (n *Email) Notify(ctx context.Context, as ...*types.Alert) (bool, error) { return true, fmt.Errorf("'require_tls' is true (default) but %q does not advertise the STARTTLS extension", n.conf.Smarthost) } - tlsConf, err := commoncfg.NewTLSConfig(&n.conf.TLSConfig) + tlsConf, err := commoncfg.NewTLSConfig(n.conf.TLSConfig) if err != nil { return false, fmt.Errorf("parse TLS configuration: %w", err) } @@ -242,7 +242,15 @@ func (n *Email) Notify(ctx context.Context, as ...*types.Alert) (bool, error) { if err != nil { return true, fmt.Errorf("send DATA command: %w", err) } - defer message.Close() + closeOnce := sync.OnceValue(func() error { + return message.Close() + }) + // Close the message when this method exits in order to not leak resources. Even though we're calling this explicitly + // further down, the method may exit before then. + defer func() { + // If we try close an already-closed writer, it'll send a subsequent request to the server which is invalid. + _ = closeOnce() + }() buffer := &bytes.Buffer{} for header, t := range n.conf.Headers { @@ -331,6 +339,11 @@ func (n *Email) Notify(ctx context.Context, as ...*types.Alert) (bool, error) { return false, fmt.Errorf("write body buffer: %w", err) } + // Complete the message and await response. + if err = closeOnce(); err != nil { + return true, fmt.Errorf("delivery failure: %w", err) + } + success = true return false, nil } @@ -347,7 +360,7 @@ func (a *loginAuth) Start(server *smtp.ServerInfo) (string, []byte, error) { return "LOGIN", []byte{}, nil } -// Used for AUTH LOGIN. (Maybe password should be encrypted) +// Used for AUTH LOGIN. (Maybe password should be encrypted). func (a *loginAuth) Next(fromServer []byte, more bool) ([]byte, error) { if more { switch strings.ToLower(string(fromServer)) { diff --git a/vendor/github.com/prometheus/alertmanager/notify/jira/jira.go b/vendor/github.com/prometheus/alertmanager/notify/jira/jira.go new file mode 100644 index 0000000000..02c75295f6 --- /dev/null +++ b/vendor/github.com/prometheus/alertmanager/notify/jira/jira.go @@ -0,0 +1,351 @@ +// Copyright 2023 Prometheus Team +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package jira + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "log/slog" + "net/http" + "sort" + "strings" + "time" + + commoncfg "github.com/prometheus/common/config" + "github.com/prometheus/common/model" + "github.com/trivago/tgo/tcontainer" + + "github.com/prometheus/alertmanager/config" + "github.com/prometheus/alertmanager/notify" + "github.com/prometheus/alertmanager/template" + "github.com/prometheus/alertmanager/types" +) + +const ( + maxSummaryLenRunes = 255 + maxDescriptionLenRunes = 32767 +) + +// Notifier implements a Notifier for JIRA notifications. +type Notifier struct { + conf *config.JiraConfig + tmpl *template.Template + logger *slog.Logger + client *http.Client + retrier *notify.Retrier +} + +func New(c *config.JiraConfig, t *template.Template, l *slog.Logger, httpOpts ...commoncfg.HTTPClientOption) (*Notifier, error) { + client, err := commoncfg.NewClientFromConfig(*c.HTTPConfig, "jira", httpOpts...) + if err != nil { + return nil, err + } + + return &Notifier{ + conf: c, + tmpl: t, + logger: l, + client: client, + retrier: ¬ify.Retrier{RetryCodes: []int{http.StatusTooManyRequests}}, + }, nil +} + +// Notify implements the Notifier interface. +func (n *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error) { + key, err := notify.ExtractGroupKey(ctx) + if err != nil { + return false, err + } + + logger := n.logger.With("group_key", key.String()) + + var ( + alerts = types.Alerts(as...) + + tmplTextErr error + data = notify.GetTemplateData(ctx, n.tmpl, as, logger) + tmplText = notify.TmplText(n.tmpl, data, &tmplTextErr) + tmplTextFunc = func(tmpl string) (string, error) { + return tmplText(tmpl), tmplTextErr + } + + path = "issue" + method = http.MethodPost + ) + + existingIssue, shouldRetry, err := n.searchExistingIssue(ctx, logger, key.Hash(), alerts.HasFiring()) + if err != nil { + return shouldRetry, fmt.Errorf("failed to look up existing issues: %w", err) + } + + if existingIssue == nil { + // Do not create new issues for resolved alerts + if alerts.Status() == model.AlertResolved { + return false, nil + } + + logger.Debug("create new issue") + } else { + path = "issue/" + existingIssue.Key + method = http.MethodPut + + logger.Debug("updating existing issue", "issue_key", existingIssue.Key) + } + + requestBody, err := n.prepareIssueRequestBody(ctx, logger, key.Hash(), tmplTextFunc) + if err != nil { + return false, err + } + + _, shouldRetry, err = n.doAPIRequest(ctx, method, path, requestBody) + if err != nil { + return shouldRetry, fmt.Errorf("failed to %s request to %q: %w", method, path, err) + } + + return n.transitionIssue(ctx, logger, existingIssue, alerts.HasFiring()) +} + +func (n *Notifier) prepareIssueRequestBody(ctx context.Context, logger *slog.Logger, groupID string, tmplTextFunc templateFunc) (issue, error) { + summary, err := tmplTextFunc(n.conf.Summary) + if err != nil { + return issue{}, fmt.Errorf("summary template: %w", err) + } + project, err := tmplTextFunc(n.conf.Project) + if err != nil { + return issue{}, fmt.Errorf("project template: %w", err) + } + issueType, err := tmplTextFunc(n.conf.IssueType) + if err != nil { + return issue{}, fmt.Errorf("issue_type template: %w", err) + } + + // Recursively convert any maps to map[string]interface{}, filtering out all non-string keys, so the json encoder + // doesn't blow up when marshaling JIRA requests. + fieldsWithStringKeys, err := tcontainer.ConvertToMarshalMap(n.conf.Fields, func(v string) string { return v }) + if err != nil { + return issue{}, fmt.Errorf("convertToMarshalMap: %w", err) + } + + summary, truncated := notify.TruncateInRunes(summary, maxSummaryLenRunes) + if truncated { + logger.Warn("Truncated summary", "max_runes", maxSummaryLenRunes) + } + + requestBody := issue{Fields: &issueFields{ + Project: &issueProject{Key: project}, + Issuetype: &idNameValue{Name: issueType}, + Summary: summary, + Labels: make([]string, 0, len(n.conf.Labels)+1), + Fields: fieldsWithStringKeys, + }} + + issueDescriptionString, err := tmplTextFunc(n.conf.Description) + if err != nil { + return issue{}, fmt.Errorf("description template: %w", err) + } + + issueDescriptionString, truncated = notify.TruncateInRunes(issueDescriptionString, maxDescriptionLenRunes) + if truncated { + logger.Warn("Truncated description", "max_runes", maxDescriptionLenRunes) + } + + requestBody.Fields.Description = issueDescriptionString + if strings.HasSuffix(n.conf.APIURL.Path, "/3") { + var issueDescription any + if err := json.Unmarshal([]byte(issueDescriptionString), &issueDescription); err != nil { + return issue{}, fmt.Errorf("description unmarshaling: %w", err) + } + requestBody.Fields.Description = issueDescription + } + + for i, label := range n.conf.Labels { + label, err = tmplTextFunc(label) + if err != nil { + return issue{}, fmt.Errorf("labels[%d] template: %w", i, err) + } + requestBody.Fields.Labels = append(requestBody.Fields.Labels, label) + } + requestBody.Fields.Labels = append(requestBody.Fields.Labels, fmt.Sprintf("ALERT{%s}", groupID)) + sort.Strings(requestBody.Fields.Labels) + + priority, err := tmplTextFunc(n.conf.Priority) + if err != nil { + return issue{}, fmt.Errorf("priority template: %w", err) + } + + if priority != "" { + requestBody.Fields.Priority = &idNameValue{Name: priority} + } + + return requestBody, nil +} + +func (n *Notifier) searchExistingIssue(ctx context.Context, logger *slog.Logger, groupID string, firing bool) (*issue, bool, error) { + jql := strings.Builder{} + + if n.conf.WontFixResolution != "" { + jql.WriteString(fmt.Sprintf(`resolution != %q and `, n.conf.WontFixResolution)) + } + + // If the group is firing, do not search for closed issues unless a reopen transition is defined. + if firing { + if n.conf.ReopenTransition == "" { + jql.WriteString(`statusCategory != Done and `) + } + } else { + reopenDuration := int64(time.Duration(n.conf.ReopenDuration).Minutes()) + if reopenDuration != 0 { + jql.WriteString(fmt.Sprintf(`(resolutiondate is EMPTY OR resolutiondate >= -%dm) and `, reopenDuration)) + } + } + + alertLabel := fmt.Sprintf("ALERT{%s}", groupID) + jql.WriteString(fmt.Sprintf(`project=%q and labels=%q order by status ASC,resolutiondate DESC`, n.conf.Project, alertLabel)) + + requestBody := issueSearch{ + JQL: jql.String(), + MaxResults: 2, + Fields: []string{"status"}, + Expand: []string{}, + } + + logger.Debug("search for recent issues", "jql", requestBody.JQL) + + responseBody, shouldRetry, err := n.doAPIRequest(ctx, http.MethodPost, "search", requestBody) + if err != nil { + return nil, shouldRetry, fmt.Errorf("HTTP request to JIRA API: %w", err) + } + + var issueSearchResult issueSearchResult + err = json.Unmarshal(responseBody, &issueSearchResult) + if err != nil { + return nil, false, err + } + + if issueSearchResult.Total == 0 { + logger.Debug("found no existing issue") + return nil, false, nil + } + + if issueSearchResult.Total > 1 { + logger.Warn("more than one issue matched, selecting the most recently resolved", "selected_issue", issueSearchResult.Issues[0].Key) + } + + return &issueSearchResult.Issues[0], false, nil +} + +func (n *Notifier) getIssueTransitionByName(ctx context.Context, issueKey, transitionName string) (string, bool, error) { + path := fmt.Sprintf("issue/%s/transitions", issueKey) + + responseBody, shouldRetry, err := n.doAPIRequest(ctx, http.MethodGet, path, nil) + if err != nil { + return "", shouldRetry, err + } + + var issueTransitions issueTransitions + err = json.Unmarshal(responseBody, &issueTransitions) + if err != nil { + return "", false, err + } + + for _, issueTransition := range issueTransitions.Transitions { + if issueTransition.Name == transitionName { + return issueTransition.ID, false, nil + } + } + + return "", false, fmt.Errorf("can't find transition %s for issue %s", transitionName, issueKey) +} + +func (n *Notifier) transitionIssue(ctx context.Context, logger *slog.Logger, i *issue, firing bool) (bool, error) { + if i == nil || i.Key == "" || i.Fields == nil || i.Fields.Status == nil { + return false, nil + } + + var transition string + if firing { + if i.Fields.Status.StatusCategory.Key != "done" { + return false, nil + } + + transition = n.conf.ReopenTransition + } else { + if i.Fields.Status.StatusCategory.Key == "done" { + return false, nil + } + + transition = n.conf.ResolveTransition + } + + transitionID, shouldRetry, err := n.getIssueTransitionByName(ctx, i.Key, transition) + if err != nil { + return shouldRetry, err + } + + requestBody := issue{ + Transition: &idNameValue{ + ID: transitionID, + }, + } + + path := fmt.Sprintf("issue/%s/transitions", i.Key) + + logger.Debug("transitions jira issue", "issue_key", i.Key, "transition", transition) + _, shouldRetry, err = n.doAPIRequest(ctx, http.MethodPost, path, requestBody) + + return shouldRetry, err +} + +func (n *Notifier) doAPIRequest(ctx context.Context, method, path string, requestBody any) ([]byte, bool, error) { + var body io.Reader + if requestBody != nil { + var buf bytes.Buffer + if err := json.NewEncoder(&buf).Encode(requestBody); err != nil { + return nil, false, err + } + + body = &buf + } + + url := n.conf.APIURL.JoinPath(path) + req, err := http.NewRequestWithContext(ctx, method, url.String(), body) + if err != nil { + return nil, false, err + } + + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Accept-Language", "en") + + resp, err := n.client.Do(req) + if err != nil { + return nil, false, err + } + + defer notify.Drain(resp) + + responseBody, err := io.ReadAll(resp.Body) + if err != nil { + return nil, false, err + } + + shouldRetry, err := n.retrier.Check(resp.StatusCode, bytes.NewReader(responseBody)) + if err != nil { + return nil, shouldRetry, notify.NewErrorWithReason(notify.GetFailureReasonFromStatusCode(resp.StatusCode), err) + } + + return responseBody, false, nil +} diff --git a/vendor/github.com/prometheus/alertmanager/notify/jira/types.go b/vendor/github.com/prometheus/alertmanager/notify/jira/types.go new file mode 100644 index 0000000000..1a226c71ab --- /dev/null +++ b/vendor/github.com/prometheus/alertmanager/notify/jira/types.go @@ -0,0 +1,110 @@ +// Copyright 2023 Prometheus Team +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package jira + +import ( + "encoding/json" +) + +type templateFunc func(string) (string, error) + +type issue struct { + Key string `json:"key,omitempty"` + Fields *issueFields `json:"fields,omitempty"` + Transition *idNameValue `json:"transition,omitempty"` +} + +type issueFields struct { + Description any `json:"description"` + Issuetype *idNameValue `json:"issuetype,omitempty"` + Labels []string `json:"labels,omitempty"` + Priority *idNameValue `json:"priority,omitempty"` + Project *issueProject `json:"project,omitempty"` + Resolution *idNameValue `json:"resolution,omitempty"` + Summary string `json:"summary"` + Status *issueStatus `json:"status,omitempty"` + + Fields map[string]any `json:"-"` +} + +type idNameValue struct { + ID string `json:"id,omitempty"` + Name string `json:"name,omitempty"` +} + +type issueProject struct { + Key string `json:"key"` +} + +type issueStatus struct { + Name string `json:"name"` + StatusCategory struct { + Key string `json:"key"` + } `json:"statusCategory"` +} + +type issueSearch struct { + Expand []string `json:"expand"` + Fields []string `json:"fields"` + JQL string `json:"jql"` + MaxResults int `json:"maxResults"` + StartAt int `json:"startAt"` +} + +type issueSearchResult struct { + Total int `json:"total"` + Issues []issue `json:"issues"` +} + +type issueTransitions struct { + Transitions []idNameValue `json:"transitions"` +} + +// MarshalJSON merges the struct issueFields and issueFields.CustomField together. +func (i issueFields) MarshalJSON() ([]byte, error) { + jsonFields := map[string]interface{}{ + "description": i.Description, + "summary": i.Summary, + } + + if i.Issuetype != nil { + jsonFields["issuetype"] = i.Issuetype + } + + if i.Labels != nil { + jsonFields["labels"] = i.Labels + } + + if i.Priority != nil { + jsonFields["priority"] = i.Priority + } + + if i.Project != nil { + jsonFields["project"] = i.Project + } + + if i.Resolution != nil { + jsonFields["resolution"] = i.Resolution + } + + if i.Status != nil { + jsonFields["status"] = i.Status + } + + for key, field := range i.Fields { + jsonFields[key] = field + } + + return json.Marshal(jsonFields) +} diff --git a/vendor/github.com/prometheus/alertmanager/notify/msteams/msteams.go b/vendor/github.com/prometheus/alertmanager/notify/msteams/msteams.go index d71e108144..f53f020b3b 100644 --- a/vendor/github.com/prometheus/alertmanager/notify/msteams/msteams.go +++ b/vendor/github.com/prometheus/alertmanager/notify/msteams/msteams.go @@ -19,12 +19,11 @@ import ( "encoding/json" "fmt" "io" + "log/slog" "net/http" "os" "strings" - "github.com/go-kit/log" - "github.com/go-kit/log/level" commoncfg "github.com/prometheus/common/config" "github.com/prometheus/common/model" @@ -43,7 +42,7 @@ const ( type Notifier struct { conf *config.MSTeamsConfig tmpl *template.Template - logger log.Logger + logger *slog.Logger client *http.Client retrier *notify.Retrier webhookURL *config.SecretURL @@ -61,7 +60,7 @@ type teamsMessage struct { } // New returns a new notifier that uses the Microsoft Teams Webhook API. -func New(c *config.MSTeamsConfig, t *template.Template, l log.Logger, httpOpts ...commoncfg.HTTPClientOption) (*Notifier, error) { +func New(c *config.MSTeamsConfig, t *template.Template, l *slog.Logger, httpOpts ...commoncfg.HTTPClientOption) (*Notifier, error) { client, err := commoncfg.NewClientFromConfig(*c.HTTPConfig, "msteams", httpOpts...) if err != nil { return nil, err @@ -86,7 +85,7 @@ func (n *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error) return false, err } - level.Debug(n.logger).Log("incident", key) + n.logger.Debug("extracted group key", "key", key) data := notify.GetTemplateData(ctx, n.tmpl, as, n.logger) tmpl := notify.TmplText(n.tmpl, data, &err) diff --git a/vendor/github.com/prometheus/alertmanager/notify/msteamsv2/msteamsv2.go b/vendor/github.com/prometheus/alertmanager/notify/msteamsv2/msteamsv2.go new file mode 100644 index 0000000000..84f5d926f0 --- /dev/null +++ b/vendor/github.com/prometheus/alertmanager/notify/msteamsv2/msteamsv2.go @@ -0,0 +1,200 @@ +// Copyright 2024 Prometheus Team +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package msteamsv2 + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "log/slog" + "net/http" + "os" + "strings" + + commoncfg "github.com/prometheus/common/config" + "github.com/prometheus/common/model" + + "github.com/prometheus/alertmanager/config" + "github.com/prometheus/alertmanager/notify" + "github.com/prometheus/alertmanager/template" + "github.com/prometheus/alertmanager/types" +) + +const ( + colorRed = "Attention" + colorGreen = "Good" + colorGrey = "Warning" +) + +type Notifier struct { + conf *config.MSTeamsV2Config + tmpl *template.Template + logger *slog.Logger + client *http.Client + retrier *notify.Retrier + webhookURL *config.SecretURL + postJSONFunc func(ctx context.Context, client *http.Client, url string, body io.Reader) (*http.Response, error) +} + +// https://learn.microsoft.com/en-us/connectors/teams/?tabs=text1#adaptivecarditemschema +type Content struct { + Schema string `json:"$schema"` + Type string `json:"type"` + Version string `json:"version"` + Body []Body `json:"body"` + Msteams Msteams `json:"msteams,omitempty"` +} + +type Body struct { + Type string `json:"type"` + Text string `json:"text"` + Weight string `json:"weigth,omitempty"` + Size string `json:"size,omitempty"` + Wrap bool `json:"wrap,omitempty"` + Style string `json:"style,omitempty"` + Color string `json:"color,omitempty"` +} + +type Msteams struct { + Width string `json:"width"` +} + +type Attachment struct { + ContentType string `json:"contentType"` + ContentURL *string `json:"contentUrl"` // Use a pointer to handle null values + Content Content `json:"content"` +} + +type teamsMessage struct { + Type string `json:"type"` + Attachments []Attachment `json:"attachments"` +} + +// New returns a new notifier that uses the Microsoft Teams Power Platform connector. +func New(c *config.MSTeamsV2Config, t *template.Template, l *slog.Logger, httpOpts ...commoncfg.HTTPClientOption) (*Notifier, error) { + client, err := commoncfg.NewClientFromConfig(*c.HTTPConfig, "msteamsv2", httpOpts...) + if err != nil { + return nil, err + } + + n := &Notifier{ + conf: c, + tmpl: t, + logger: l, + client: client, + retrier: ¬ify.Retrier{}, + webhookURL: c.WebhookURL, + postJSONFunc: notify.PostJSON, + } + + return n, nil +} + +func (n *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error) { + key, err := notify.ExtractGroupKey(ctx) + if err != nil { + return false, err + } + + n.logger.Debug("extracted group key", "key", key) + + data := notify.GetTemplateData(ctx, n.tmpl, as, n.logger) + tmpl := notify.TmplText(n.tmpl, data, &err) + if err != nil { + return false, err + } + + title := tmpl(n.conf.Title) + if err != nil { + return false, err + } + text := tmpl(n.conf.Text) + if err != nil { + return false, err + } + + alerts := types.Alerts(as...) + color := colorGrey + switch alerts.Status() { + case model.AlertFiring: + color = colorRed + case model.AlertResolved: + color = colorGreen + } + + var url string + if n.conf.WebhookURL != nil { + url = n.conf.WebhookURL.String() + } else { + content, err := os.ReadFile(n.conf.WebhookURLFile) + if err != nil { + return false, fmt.Errorf("read webhook_url_file: %w", err) + } + url = strings.TrimSpace(string(content)) + } + + // A message as referenced in https://learn.microsoft.com/en-us/connectors/teams/?tabs=text1%2Cdotnet#request-body-schema + t := teamsMessage{ + Type: "message", + Attachments: []Attachment{ + { + ContentType: "application/vnd.microsoft.card.adaptive", + ContentURL: nil, + Content: Content{ + Schema: "http://adaptivecards.io/schemas/adaptive-card.json", + Type: "AdaptiveCard", + Version: "1.2", + Body: []Body{ + { + Type: "TextBlock", + Text: title, + Weight: "Bolder", + Size: "Medium", + Wrap: true, + Style: "heading", + Color: color, + }, + { + Type: "TextBlock", + Text: text, + }, + }, + Msteams: Msteams{ + Width: "full", + }, + }, + }, + }, + } + + var payload bytes.Buffer + if err = json.NewEncoder(&payload).Encode(t); err != nil { + return false, err + } + + resp, err := n.postJSONFunc(ctx, n.client, url, &payload) + if err != nil { + return true, notify.RedactURL(err) + } + defer notify.Drain(resp) + + // https://learn.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/connectors-using?tabs=cURL#rate-limiting-for-connectors + shouldRetry, err := n.retrier.Check(resp.StatusCode, resp.Body) + if err != nil { + return shouldRetry, notify.NewErrorWithReason(notify.GetFailureReasonFromStatusCode(resp.StatusCode), err) + } + return shouldRetry, err +} diff --git a/vendor/github.com/prometheus/alertmanager/notify/notify.go b/vendor/github.com/prometheus/alertmanager/notify/notify.go index 30861a3027..8fa85c0a21 100644 --- a/vendor/github.com/prometheus/alertmanager/notify/notify.go +++ b/vendor/github.com/prometheus/alertmanager/notify/notify.go @@ -17,14 +17,13 @@ import ( "context" "errors" "fmt" + "log/slog" "sort" "sync" "time" "github.com/cenkalti/backoff/v4" "github.com/cespare/xxhash/v2" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/common/model" @@ -119,6 +118,7 @@ const ( keyNow keyMuteTimeIntervals keyActiveTimeIntervals + keyRouteID ) // WithReceiverName populates a context with a receiver name. @@ -165,6 +165,10 @@ func WithActiveTimeIntervals(ctx context.Context, at []string) context.Context { return context.WithValue(ctx, keyActiveTimeIntervals, at) } +func WithRouteID(ctx context.Context, routeID string) context.Context { + return context.WithValue(ctx, keyRouteID, routeID) +} + // RepeatInterval extracts a repeat interval from the context. Iff none exists, the // second argument is false. func RepeatInterval(ctx context.Context) (time.Duration, bool) { @@ -228,16 +232,23 @@ func ActiveTimeIntervalNames(ctx context.Context) ([]string, bool) { return v, ok } +// RouteID extracts a RouteID from the context. Iff none exists, the +// // second argument is false. +func RouteID(ctx context.Context) (string, bool) { + v, ok := ctx.Value(keyRouteID).(string) + return v, ok +} + // A Stage processes alerts under the constraints of the given context. type Stage interface { - Exec(ctx context.Context, l log.Logger, alerts ...*types.Alert) (context.Context, []*types.Alert, error) + Exec(ctx context.Context, l *slog.Logger, alerts ...*types.Alert) (context.Context, []*types.Alert, error) } // StageFunc wraps a function to represent a Stage. -type StageFunc func(ctx context.Context, l log.Logger, alerts ...*types.Alert) (context.Context, []*types.Alert, error) +type StageFunc func(ctx context.Context, l *slog.Logger, alerts ...*types.Alert) (context.Context, []*types.Alert, error) // Exec implements Stage interface. -func (f StageFunc) Exec(ctx context.Context, l log.Logger, alerts ...*types.Alert) (context.Context, []*types.Alert, error) { +func (f StageFunc) Exec(ctx context.Context, l *slog.Logger, alerts ...*types.Alert) (context.Context, []*types.Alert, error) { return f(ctx, l, alerts...) } @@ -288,13 +299,16 @@ func NewMetrics(r prometheus.Registerer, ff featurecontrol.Flagger) *Metrics { numNotificationSuppressedTotal: prometheus.NewCounterVec(prometheus.CounterOpts{ Namespace: "alertmanager", Name: "notifications_suppressed_total", - Help: "The total number of notifications suppressed for being outside of active time intervals or within muted time intervals.", + Help: "The total number of notifications suppressed for being silenced, inhibited, outside of active time intervals or within muted time intervals.", }, []string{"reason"}), notificationLatencySeconds: prometheus.NewHistogramVec(prometheus.HistogramOpts{ - Namespace: "alertmanager", - Name: "notification_latency_seconds", - Help: "The latency of notifications in seconds.", - Buckets: []float64{1, 5, 10, 15, 20}, + Namespace: "alertmanager", + Name: "notification_latency_seconds", + Help: "The latency of notifications in seconds.", + Buckets: []float64{1, 5, 10, 15, 20}, + NativeHistogramBucketFactor: 1.1, + NativeHistogramMaxBucketNumber: 100, + NativeHistogramMinResetDuration: 1 * time.Hour, }, labels), ff: ff, } @@ -350,6 +364,9 @@ func (m *Metrics) InitializeFor(receiver map[string][]Integration) { "discord", "webex", "msteams", + "msteamsv2", + "jira", + "rocketchat", } { m.numNotifications.WithLabelValues(integration) m.numNotificationRequestsTotal.WithLabelValues(integration) @@ -381,6 +398,7 @@ func (pb *PipelineBuilder) New( inhibitor *inhibit.Inhibitor, silencer *silence.Silencer, intervener *timeinterval.Intervener, + marker types.GroupMarker, notificationLog NotificationLog, peer Peer, ) RoutingStage { @@ -388,8 +406,8 @@ func (pb *PipelineBuilder) New( ms := NewGossipSettleStage(peer) is := NewMuteStage(inhibitor, pb.metrics) - tas := NewTimeActiveStage(intervener, pb.metrics) - tms := NewTimeMuteStage(intervener, pb.metrics) + tas := NewTimeActiveStage(intervener, marker, pb.metrics) + tms := NewTimeMuteStage(intervener, marker, pb.metrics) ss := NewMuteStage(silencer, pb.metrics) for name := range receivers { @@ -433,7 +451,7 @@ func createReceiverStage( type RoutingStage map[string]Stage // Exec implements the Stage interface. -func (rs RoutingStage) Exec(ctx context.Context, l log.Logger, alerts ...*types.Alert) (context.Context, []*types.Alert, error) { +func (rs RoutingStage) Exec(ctx context.Context, l *slog.Logger, alerts ...*types.Alert) (context.Context, []*types.Alert, error) { receiver, ok := ReceiverName(ctx) if !ok { return ctx, nil, errors.New("receiver missing") @@ -451,7 +469,7 @@ func (rs RoutingStage) Exec(ctx context.Context, l log.Logger, alerts ...*types. type MultiStage []Stage // Exec implements the Stage interface. -func (ms MultiStage) Exec(ctx context.Context, l log.Logger, alerts ...*types.Alert) (context.Context, []*types.Alert, error) { +func (ms MultiStage) Exec(ctx context.Context, l *slog.Logger, alerts ...*types.Alert) (context.Context, []*types.Alert, error) { var err error for _, s := range ms { if len(alerts) == 0 { @@ -466,12 +484,12 @@ func (ms MultiStage) Exec(ctx context.Context, l log.Logger, alerts ...*types.Al return ctx, alerts, nil } -// FanoutStage executes its stages concurrently +// FanoutStage executes its stages concurrently. type FanoutStage []Stage // Exec attempts to execute all stages concurrently and discards the results. // It returns its input alerts and a types.MultiError if one or more stages fail. -func (fs FanoutStage) Exec(ctx context.Context, l log.Logger, alerts ...*types.Alert) (context.Context, []*types.Alert, error) { +func (fs FanoutStage) Exec(ctx context.Context, l *slog.Logger, alerts ...*types.Alert) (context.Context, []*types.Alert, error) { var ( wg sync.WaitGroup me types.MultiError @@ -504,7 +522,7 @@ func NewGossipSettleStage(p Peer) *GossipSettleStage { return &GossipSettleStage{peer: p} } -func (n *GossipSettleStage) Exec(ctx context.Context, _ log.Logger, alerts ...*types.Alert) (context.Context, []*types.Alert, error) { +func (n *GossipSettleStage) Exec(ctx context.Context, _ *slog.Logger, alerts ...*types.Alert) (context.Context, []*types.Alert, error) { if n.peer != nil { if err := n.peer.WaitReady(ctx); err != nil { return ctx, nil, err @@ -514,10 +532,10 @@ func (n *GossipSettleStage) Exec(ctx context.Context, _ log.Logger, alerts ...*t } const ( - suppressedReasonSilence = "silence" - suppressedReasonInhibition = "inhibition" - suppressedReasonMuteTimeInterval = "mute_time_interval" - suppressedReasonActiveTimeInterval = "active_time_interval" + SuppressedReasonSilence = "silence" + SuppressedReasonInhibition = "inhibition" + SuppressedReasonMuteTimeInterval = "mute_time_interval" + SuppressedReasonActiveTimeInterval = "active_time_interval" ) // MuteStage filters alerts through a Muter. @@ -532,7 +550,7 @@ func NewMuteStage(m types.Muter, metrics *Metrics) *MuteStage { } // Exec implements the Stage interface. -func (n *MuteStage) Exec(ctx context.Context, logger log.Logger, alerts ...*types.Alert) (context.Context, []*types.Alert, error) { +func (n *MuteStage) Exec(ctx context.Context, logger *slog.Logger, alerts ...*types.Alert) (context.Context, []*types.Alert, error) { var ( filtered []*types.Alert muted []*types.Alert @@ -548,17 +566,17 @@ func (n *MuteStage) Exec(ctx context.Context, logger log.Logger, alerts ...*type // TODO(fabxc): increment muted alerts counter if muted. } if len(muted) > 0 { - level.Debug(logger).Log("msg", "Notifications will not be sent for muted alerts", "alerts", fmt.Sprintf("%v", muted)) var reason string switch n.muter.(type) { case *silence.Silencer: - reason = suppressedReasonSilence + reason = SuppressedReasonSilence case *inhibit.Inhibitor: - reason = suppressedReasonInhibition + reason = SuppressedReasonInhibition default: } n.metrics.numNotificationSuppressedTotal.WithLabelValues(reason).Add(float64(len(muted))) + logger.Debug("Notifications will not be sent for muted alerts", "alerts", fmt.Sprintf("%v", muted), "reason", reason) } return ctx, filtered, nil @@ -578,7 +596,7 @@ func NewWaitStage(wait func() time.Duration) *WaitStage { } // Exec implements the Stage interface. -func (ws *WaitStage) Exec(ctx context.Context, _ log.Logger, alerts ...*types.Alert) (context.Context, []*types.Alert, error) { +func (ws *WaitStage) Exec(ctx context.Context, _ *slog.Logger, alerts ...*types.Alert) (context.Context, []*types.Alert, error) { select { case <-time.After(ws.wait()): case <-ctx.Done(): @@ -613,7 +631,7 @@ func utcNow() time.Time { return time.Now().UTC() } -// Wrap a slice in a struct so we can store a pointer in sync.Pool +// Wrap a slice in a struct so we can store a pointer in sync.Pool. type hashBuffer struct { buf []byte } @@ -679,7 +697,7 @@ func (n *DedupStage) needsUpdate(entry *nflogpb.Entry, firing, resolved map[uint } // Exec implements the Stage interface. -func (n *DedupStage) Exec(ctx context.Context, _ log.Logger, alerts ...*types.Alert) (context.Context, []*types.Alert, error) { +func (n *DedupStage) Exec(ctx context.Context, _ *slog.Logger, alerts ...*types.Alert) (context.Context, []*types.Alert, error) { gkey, ok := GroupKey(ctx) if !ok { return ctx, nil, errors.New("group key missing") @@ -755,7 +773,7 @@ func NewRetryStage(i Integration, groupName string, metrics *Metrics) *RetryStag } } -func (r RetryStage) Exec(ctx context.Context, l log.Logger, alerts ...*types.Alert) (context.Context, []*types.Alert, error) { +func (r RetryStage) Exec(ctx context.Context, l *slog.Logger, alerts ...*types.Alert) (context.Context, []*types.Alert, error) { r.metrics.numNotifications.WithLabelValues(r.labelValues...).Inc() ctx, alerts, err := r.exec(ctx, l, alerts...) @@ -770,7 +788,7 @@ func (r RetryStage) Exec(ctx context.Context, l log.Logger, alerts ...*types.Ale return ctx, alerts, err } -func (r RetryStage) exec(ctx context.Context, l log.Logger, alerts ...*types.Alert) (context.Context, []*types.Alert, error) { +func (r RetryStage) exec(ctx context.Context, l *slog.Logger, alerts ...*types.Alert) (context.Context, []*types.Alert, error) { var sent []*types.Alert // If we shouldn't send notifications for resolved alerts, but there are only @@ -804,9 +822,9 @@ func (r RetryStage) exec(ctx context.Context, l log.Logger, alerts ...*types.Ale iErr error ) - l = log.With(l, "receiver", r.groupName, "integration", r.integration.String()) + l = l.With("receiver", r.groupName, "integration", r.integration.String()) if groupKey, ok := GroupKey(ctx); ok { - l = log.With(l, "aggrGroup", groupKey) + l = l.With("aggrGroup", groupKey) } for { @@ -845,19 +863,21 @@ func (r RetryStage) exec(ctx context.Context, l log.Logger, alerts ...*types.Ale if ctx.Err() == nil { if iErr == nil || err.Error() != iErr.Error() { // Log the error if the context isn't done and the error isn't the same as before. - level.Warn(l).Log("msg", "Notify attempt failed, will retry later", "attempts", i, "err", err) + l.Warn("Notify attempt failed, will retry later", "attempts", i, "err", err) } // Save this error to be able to return the last seen error by an // integration upon context timeout. iErr = err } } else { - lvl := level.Info(l) + l := l.With("attempts", i, "duration", dur) if i <= 1 { - lvl = level.Debug(log.With(l, "alerts", fmt.Sprintf("%v", alerts))) + l = l.With("alerts", fmt.Sprintf("%v", alerts)) + l.Debug("Notify success") + } else { + l.Info("Notify success") } - lvl.Log("msg", "Notify success", "attempts", i, "duration", dur) return ctx, alerts, nil } case <-ctx.Done(): @@ -893,7 +913,7 @@ func NewSetNotifiesStage(l NotificationLog, recv *nflogpb.Receiver) *SetNotifies } // Exec implements the Stage interface. -func (n SetNotifiesStage) Exec(ctx context.Context, l log.Logger, alerts ...*types.Alert) (context.Context, []*types.Alert, error) { +func (n SetNotifiesStage) Exec(ctx context.Context, l *slog.Logger, alerts ...*types.Alert) (context.Context, []*types.Alert, error) { gkey, ok := GroupKey(ctx) if !ok { return ctx, nil, errors.New("group key missing") @@ -920,18 +940,29 @@ func (n SetNotifiesStage) Exec(ctx context.Context, l log.Logger, alerts ...*typ type timeStage struct { muter types.TimeMuter + marker types.GroupMarker metrics *Metrics } type TimeMuteStage timeStage -func NewTimeMuteStage(m types.TimeMuter, metrics *Metrics) *TimeMuteStage { - return &TimeMuteStage{m, metrics} +func NewTimeMuteStage(muter types.TimeMuter, marker types.GroupMarker, metrics *Metrics) *TimeMuteStage { + return &TimeMuteStage{muter, marker, metrics} } // Exec implements the stage interface for TimeMuteStage. // TimeMuteStage is responsible for muting alerts whose route is not in an active time. -func (tms TimeMuteStage) Exec(ctx context.Context, l log.Logger, alerts ...*types.Alert) (context.Context, []*types.Alert, error) { +func (tms TimeMuteStage) Exec(ctx context.Context, l *slog.Logger, alerts ...*types.Alert) (context.Context, []*types.Alert, error) { + routeID, ok := RouteID(ctx) + if !ok { + return ctx, nil, errors.New("route ID missing") + } + + gkey, ok := GroupKey(ctx) + if !ok { + return ctx, nil, errors.New("group key missing") + } + muteTimeIntervalNames, ok := MuteTimeIntervalNames(ctx) if !ok { return ctx, alerts, nil @@ -946,29 +977,42 @@ func (tms TimeMuteStage) Exec(ctx context.Context, l log.Logger, alerts ...*type return ctx, alerts, nil } - muted, err := tms.muter.Mutes(muteTimeIntervalNames, now) + muted, mutedBy, err := tms.muter.Mutes(muteTimeIntervalNames, now) if err != nil { return ctx, alerts, err } + // If muted is false then mutedBy is nil and the muted marker is removed. + tms.marker.SetMuted(routeID, gkey, mutedBy) // If the current time is inside a mute time, all alerts are removed from the pipeline. if muted { - tms.metrics.numNotificationSuppressedTotal.WithLabelValues(suppressedReasonMuteTimeInterval).Add(float64(len(alerts))) - level.Debug(l).Log("msg", "Notifications not sent, route is within mute time", "alerts", len(alerts)) + tms.metrics.numNotificationSuppressedTotal.WithLabelValues(SuppressedReasonMuteTimeInterval).Add(float64(len(alerts))) + l.Debug("Notifications not sent, route is within mute time", "alerts", len(alerts)) return ctx, nil, nil } + return ctx, alerts, nil } type TimeActiveStage timeStage -func NewTimeActiveStage(m types.TimeMuter, metrics *Metrics) *TimeActiveStage { - return &TimeActiveStage{m, metrics} +func NewTimeActiveStage(muter types.TimeMuter, marker types.GroupMarker, metrics *Metrics) *TimeActiveStage { + return &TimeActiveStage{muter, marker, metrics} } // Exec implements the stage interface for TimeActiveStage. // TimeActiveStage is responsible for muting alerts whose route is not in an active time. -func (tas TimeActiveStage) Exec(ctx context.Context, l log.Logger, alerts ...*types.Alert) (context.Context, []*types.Alert, error) { +func (tas TimeActiveStage) Exec(ctx context.Context, l *slog.Logger, alerts ...*types.Alert) (context.Context, []*types.Alert, error) { + routeID, ok := RouteID(ctx) + if !ok { + return ctx, nil, errors.New("route ID missing") + } + + gkey, ok := GroupKey(ctx) + if !ok { + return ctx, nil, errors.New("group key missing") + } + activeTimeIntervalNames, ok := ActiveTimeIntervalNames(ctx) if !ok { return ctx, alerts, nil @@ -984,15 +1028,24 @@ func (tas TimeActiveStage) Exec(ctx context.Context, l log.Logger, alerts ...*ty return ctx, alerts, errors.New("missing now timestamp") } - muted, err := tas.muter.Mutes(activeTimeIntervalNames, now) + active, _, err := tas.muter.Mutes(activeTimeIntervalNames, now) if err != nil { return ctx, alerts, err } + var mutedBy []string + if !active { + // If the group is muted, then it must be muted by all active time intervals. + // Otherwise, the group must be in at least one active time interval for it + // to be active. + mutedBy = activeTimeIntervalNames + } + tas.marker.SetMuted(routeID, gkey, mutedBy) + // If the current time is not inside an active time, all alerts are removed from the pipeline - if !muted { - tas.metrics.numNotificationSuppressedTotal.WithLabelValues(suppressedReasonActiveTimeInterval).Add(float64(len(alerts))) - level.Debug(l).Log("msg", "Notifications not sent, route is not within active time", "alerts", len(alerts)) + if !active { + tas.metrics.numNotificationSuppressedTotal.WithLabelValues(SuppressedReasonActiveTimeInterval).Add(float64(len(alerts))) + l.Debug("Notifications not sent, route is not within active time", "alerts", len(alerts)) return ctx, nil, nil } diff --git a/vendor/github.com/prometheus/alertmanager/notify/opsgenie/opsgenie.go b/vendor/github.com/prometheus/alertmanager/notify/opsgenie/opsgenie.go index 4421cd6545..7554984639 100644 --- a/vendor/github.com/prometheus/alertmanager/notify/opsgenie/opsgenie.go +++ b/vendor/github.com/prometheus/alertmanager/notify/opsgenie/opsgenie.go @@ -18,12 +18,11 @@ import ( "context" "encoding/json" "fmt" + "log/slog" "net/http" "os" "strings" - "github.com/go-kit/log" - "github.com/go-kit/log/level" commoncfg "github.com/prometheus/common/config" "github.com/prometheus/common/model" @@ -40,13 +39,13 @@ const maxMessageLenRunes = 130 type Notifier struct { conf *config.OpsGenieConfig tmpl *template.Template - logger log.Logger + logger *slog.Logger client *http.Client retrier *notify.Retrier } // New returns a new OpsGenie notifier. -func New(c *config.OpsGenieConfig, t *template.Template, l log.Logger, httpOpts ...commoncfg.HTTPClientOption) (*Notifier, error) { +func New(c *config.OpsGenieConfig, t *template.Template, l *slog.Logger, httpOpts ...commoncfg.HTTPClientOption) (*Notifier, error) { client, err := commoncfg.NewClientFromConfig(*c.HTTPConfig, "opsgenie", httpOpts...) if err != nil { return nil, err @@ -135,7 +134,7 @@ func (n *Notifier) createRequests(ctx context.Context, as ...*types.Alert) ([]*h } data := notify.GetTemplateData(ctx, n.tmpl, as, n.logger) - level.Debug(n.logger).Log("alert", key) + n.logger.Debug("extracted group key", "key", key) tmpl := notify.TmplText(n.tmpl, data, &err) @@ -175,7 +174,7 @@ func (n *Notifier) createRequests(ctx context.Context, as ...*types.Alert) ([]*h default: message, truncated := notify.TruncateInRunes(tmpl(n.conf.Message), maxMessageLenRunes) if truncated { - level.Warn(n.logger).Log("msg", "Truncated message", "alert", key, "max_runes", maxMessageLenRunes) + n.logger.Warn("Truncated message", "alert", key, "max_runes", maxMessageLenRunes) } createEndpointURL := n.conf.APIURL.Copy() diff --git a/vendor/github.com/prometheus/alertmanager/notify/pagerduty/pagerduty.go b/vendor/github.com/prometheus/alertmanager/notify/pagerduty/pagerduty.go index 6f6e1a20a8..abab5a70be 100644 --- a/vendor/github.com/prometheus/alertmanager/notify/pagerduty/pagerduty.go +++ b/vendor/github.com/prometheus/alertmanager/notify/pagerduty/pagerduty.go @@ -20,13 +20,12 @@ import ( "errors" "fmt" "io" + "log/slog" "net/http" "os" "strings" "github.com/alecthomas/units" - "github.com/go-kit/log" - "github.com/go-kit/log/level" commoncfg "github.com/prometheus/common/config" "github.com/prometheus/common/model" @@ -48,14 +47,14 @@ const ( type Notifier struct { conf *config.PagerdutyConfig tmpl *template.Template - logger log.Logger + logger *slog.Logger apiV1 string // for tests. client *http.Client retrier *notify.Retrier } // New returns a new PagerDuty notifier. -func New(c *config.PagerdutyConfig, t *template.Template, l log.Logger, httpOpts ...commoncfg.HTTPClientOption) (*Notifier, error) { +func New(c *config.PagerdutyConfig, t *template.Template, l *slog.Logger, httpOpts ...commoncfg.HTTPClientOption) (*Notifier, error) { client, err := commoncfg.NewClientFromConfig(*c.HTTPConfig, "pagerduty", httpOpts...) if err != nil { return nil, err @@ -133,7 +132,7 @@ func (n *Notifier) encodeMessage(msg *pagerDutyMessage) (bytes.Buffer, error) { } warningMsg := fmt.Sprintf("Truncated Details because message of size %s exceeds limit %s", units.MetricBytes(buf.Len()).String(), units.MetricBytes(maxEventSize).String()) - level.Warn(n.logger).Log("msg", warningMsg) + n.logger.Warn(warningMsg) buf.Reset() if err := json.NewEncoder(&buf).Encode(msg); err != nil { @@ -157,7 +156,7 @@ func (n *Notifier) notifyV1( description, truncated := notify.TruncateInRunes(tmpl(n.conf.Description), maxV1DescriptionLenRunes) if truncated { - level.Warn(n.logger).Log("msg", "Truncated description", "key", key, "max_runes", maxV1DescriptionLenRunes) + n.logger.Warn("Truncated description", "key", key, "max_runes", maxV1DescriptionLenRunes) } serviceKey := string(n.conf.ServiceKey) @@ -222,7 +221,7 @@ func (n *Notifier) notifyV2( summary, truncated := notify.TruncateInRunes(tmpl(n.conf.Description), maxV2SummaryLenRunes) if truncated { - level.Warn(n.logger).Log("msg", "Truncated summary", "key", key, "max_runes", maxV2SummaryLenRunes) + n.logger.Warn("Truncated summary", "key", key, "max_runes", maxV2SummaryLenRunes) } routingKey := string(n.conf.RoutingKey) @@ -319,7 +318,7 @@ func (n *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error) eventType = pagerDutyEventResolve } - level.Debug(n.logger).Log("incident", key, "eventType", eventType) + n.logger.Debug("extracted group key", "key", key, "eventType", eventType) details := make(map[string]string, len(n.conf.Details)) for k, v := range n.conf.Details { diff --git a/vendor/github.com/prometheus/alertmanager/notify/pushover/pushover.go b/vendor/github.com/prometheus/alertmanager/notify/pushover/pushover.go index a8f4644f65..5445b977cb 100644 --- a/vendor/github.com/prometheus/alertmanager/notify/pushover/pushover.go +++ b/vendor/github.com/prometheus/alertmanager/notify/pushover/pushover.go @@ -16,14 +16,13 @@ package pushover import ( "context" "fmt" + "log/slog" "net/http" "net/url" "os" "strings" "time" - "github.com/go-kit/log" - "github.com/go-kit/log/level" commoncfg "github.com/prometheus/common/config" "github.com/prometheus/alertmanager/config" @@ -45,14 +44,14 @@ const ( type Notifier struct { conf *config.PushoverConfig tmpl *template.Template - logger log.Logger + logger *slog.Logger client *http.Client retrier *notify.Retrier apiURL string // for tests. } // New returns a new Pushover notifier. -func New(c *config.PushoverConfig, t *template.Template, l log.Logger, httpOpts ...commoncfg.HTTPClientOption) (*Notifier, error) { +func New(c *config.PushoverConfig, t *template.Template, l *slog.Logger, httpOpts ...commoncfg.HTTPClientOption) (*Notifier, error) { client, err := commoncfg.NewClientFromConfig(*c.HTTPConfig, "pushover", httpOpts...) if err != nil { return nil, err @@ -75,7 +74,8 @@ func (n *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error) } data := notify.GetTemplateData(ctx, n.tmpl, as, n.logger) - level.Debug(n.logger).Log("incident", key) + // @tjhop: should this use `group` for the keyval like most other notify implementations? + n.logger.Debug("extracted group key", "incident", key) var ( err error @@ -113,7 +113,7 @@ func (n *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error) title, truncated := notify.TruncateInRunes(tmpl(n.conf.Title), maxTitleLenRunes) if truncated { - level.Warn(n.logger).Log("msg", "Truncated title", "incident", key, "max_runes", maxTitleLenRunes) + n.logger.Warn("Truncated title", "incident", key, "max_runes", maxTitleLenRunes) } parameters.Add("title", title) @@ -126,7 +126,7 @@ func (n *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error) message, truncated = notify.TruncateInRunes(message, maxMessageLenRunes) if truncated { - level.Warn(n.logger).Log("msg", "Truncated message", "incident", key, "max_runes", maxMessageLenRunes) + n.logger.Warn("Truncated message", "incident", key, "max_runes", maxMessageLenRunes) } message = strings.TrimSpace(message) if message == "" { @@ -137,7 +137,7 @@ func (n *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error) supplementaryURL, truncated := notify.TruncateInRunes(tmpl(n.conf.URL), maxURLLenRunes) if truncated { - level.Warn(n.logger).Log("msg", "Truncated URL", "incident", key, "max_runes", maxURLLenRunes) + n.logger.Warn("Truncated URL", "incident", key, "max_runes", maxURLLenRunes) } parameters.Add("url", supplementaryURL) parameters.Add("url_title", tmpl(n.conf.URLTitle)) @@ -163,7 +163,7 @@ func (n *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error) } u.RawQuery = parameters.Encode() // Don't log the URL as it contains secret data (see #1825). - level.Debug(n.logger).Log("msg", "Sending message", "incident", key) + n.logger.Debug("Sending message", "incident", key) resp, err := notify.PostText(ctx, n.client, u.String(), nil) if err != nil { return true, notify.RedactURL(err) diff --git a/vendor/github.com/prometheus/alertmanager/notify/rocketchat/rocketchat.go b/vendor/github.com/prometheus/alertmanager/notify/rocketchat/rocketchat.go new file mode 100644 index 0000000000..8f5b3ef886 --- /dev/null +++ b/vendor/github.com/prometheus/alertmanager/notify/rocketchat/rocketchat.go @@ -0,0 +1,269 @@ +// Copyright 2022 Prometheus Team +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package rocketchat + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "log/slog" + "net/http" + "os" + "strings" + + commoncfg "github.com/prometheus/common/config" + + "github.com/prometheus/alertmanager/config" + "github.com/prometheus/alertmanager/notify" + "github.com/prometheus/alertmanager/template" + "github.com/prometheus/alertmanager/types" +) + +const maxTitleLenRunes = 1024 + +type Notifier struct { + conf *config.RocketchatConfig + tmpl *template.Template + logger *slog.Logger + client *http.Client + retrier *notify.Retrier + token string + tokenID string + + postJSONFunc func(ctx context.Context, client *http.Client, url string, body io.Reader) (*http.Response, error) +} + +// PostMessage Payload for postmessage rest API +// +// https://rocket.chat/docs/developer-guides/rest-api/chat/postmessage/ +type Attachment struct { + Title string `json:"title,omitempty"` + TitleLink string `json:"title_link,omitempty"` + Text string `json:"text,omitempty"` + ImageURL string `json:"image_url,omitempty"` + ThumbURL string `json:"thumb_url,omitempty"` + Color string `json:"color,omitempty"` + Fields []config.RocketchatAttachmentField `json:"fields,omitempty"` + Actions []config.RocketchatAttachmentAction `json:"actions,omitempty"` +} + +// PostMessage Payload for postmessage rest API +// +// https://rocket.chat/docs/developer-guides/rest-api/chat/postmessage/ +type PostMessage struct { + Channel string `json:"channel,omitempty"` + Text string `json:"text,omitempty"` + ParseUrls bool `json:"parseUrls,omitempty"` + Alias string `json:"alias,omitempty"` + Emoji string `json:"emoji,omitempty"` + Avatar string `json:"avatar,omitempty"` + Attachments []Attachment `json:"attachments,omitempty"` + Actions []config.RocketchatAttachmentAction `json:"actions,omitempty"` +} + +type rocketchatRoundTripper struct { + wrapped http.RoundTripper + token string + tokenID string +} + +func (t *rocketchatRoundTripper) RoundTrip(req *http.Request) (res *http.Response, e error) { + req.Header.Set("X-Auth-Token", t.token) + req.Header.Set("X-User-Id", t.tokenID) + return t.wrapped.RoundTrip(req) +} + +// New returns a new Rocketchat notification handler. +func New(c *config.RocketchatConfig, t *template.Template, l *slog.Logger, httpOpts ...commoncfg.HTTPClientOption) (*Notifier, error) { + client, err := commoncfg.NewClientFromConfig(*c.HTTPConfig, "rocketchat", httpOpts...) + if err != nil { + return nil, err + } + token, err := getToken(c) + if err != nil { + return nil, err + } + tokenID, err := getTokenID(c) + if err != nil { + return nil, err + } + + client.Transport = &rocketchatRoundTripper{wrapped: client.Transport, token: token, tokenID: tokenID} + return &Notifier{ + conf: c, + tmpl: t, + logger: l, + client: client, + retrier: ¬ify.Retrier{}, + postJSONFunc: notify.PostJSON, + token: token, + tokenID: tokenID, + }, nil +} + +func getTokenID(c *config.RocketchatConfig) (string, error) { + if len(c.TokenIDFile) > 0 { + content, err := os.ReadFile(c.TokenIDFile) + if err != nil { + return "", fmt.Errorf("could not read %s: %w", c.TokenIDFile, err) + } + return strings.TrimSpace(string(content)), nil + } + return string(*c.TokenID), nil +} + +func getToken(c *config.RocketchatConfig) (string, error) { + if len(c.TokenFile) > 0 { + content, err := os.ReadFile(c.TokenFile) + if err != nil { + return "", fmt.Errorf("could not read %s: %w", c.TokenFile, err) + } + return strings.TrimSpace(string(content)), nil + } + return string(*c.Token), nil +} + +// Notify implements the Notifier interface. +func (n *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error) { + var err error + + data := notify.GetTemplateData(ctx, n.tmpl, as, n.logger) + tmplText := notify.TmplText(n.tmpl, data, &err) + if err != nil { + return false, err + } + title := tmplText(n.conf.Title) + if err != nil { + return false, err + } + + title, truncated := notify.TruncateInRunes(title, maxTitleLenRunes) + if truncated { + key, err := notify.ExtractGroupKey(ctx) + if err != nil { + return false, err + } + n.logger.Warn("Truncated title", "key", key, "max_runes", maxTitleLenRunes) + } + att := &Attachment{ + Title: title, + TitleLink: tmplText(n.conf.TitleLink), + Text: tmplText(n.conf.Text), + ImageURL: tmplText(n.conf.ImageURL), + ThumbURL: tmplText(n.conf.ThumbURL), + Color: tmplText(n.conf.Color), + } + numFields := len(n.conf.Fields) + if numFields > 0 { + fields := make([]config.RocketchatAttachmentField, numFields) + for index, field := range n.conf.Fields { + // Check if short was defined for the field otherwise fallback to the global setting + var short bool + if field.Short != nil { + short = *field.Short + } else { + short = n.conf.ShortFields + } + + // Rebuild the field by executing any templates and setting the new value for short + fields[index] = config.RocketchatAttachmentField{ + Title: tmplText(field.Title), + Value: tmplText(field.Value), + Short: &short, + } + } + att.Fields = fields + } + numActions := len(n.conf.Actions) + if numActions > 0 { + actions := make([]config.RocketchatAttachmentAction, numActions) + for index, action := range n.conf.Actions { + actions[index] = config.RocketchatAttachmentAction{ + Type: "button", // Only button type is supported + Text: tmplText(action.Text), + URL: tmplText(action.URL), + Msg: tmplText(action.Msg), + } + } + att.Actions = actions + } + + body := &PostMessage{ + Channel: n.conf.Channel, + Emoji: tmplText(n.conf.Emoji), + Avatar: tmplText(n.conf.IconURL), + Attachments: []Attachment{*att}, + } + if err != nil { + return false, err + } + + var buf bytes.Buffer + if err := json.NewEncoder(&buf).Encode(body); err != nil { + return false, err + } + url := n.conf.APIURL.JoinPath("api/v1/chat.postMessage").String() + resp, err := n.postJSONFunc(ctx, n.client, url, &buf) + if err != nil { + return true, notify.RedactURL(err) + } + defer notify.Drain(resp) + + // Use a retrier to generate an error message for non-200 responses and + // classify them as retriable or not. + retry, err := n.retrier.Check(resp.StatusCode, resp.Body) + if err != nil { + err = fmt.Errorf("channel %q: %w", body.Channel, err) + return retry, notify.NewErrorWithReason(notify.GetFailureReasonFromStatusCode(resp.StatusCode), err) + } + + // Rocketchat web API might return errors with a 200 response code. + retry, err = checkResponseError(resp) + if err != nil { + err = fmt.Errorf("channel %q: %w", body.Channel, err) + return retry, notify.NewErrorWithReason(notify.ClientErrorReason, err) + } + + return retry, nil +} + +// checkResponseError parses out the error message from Rocketchat API response. +func checkResponseError(resp *http.Response) (bool, error) { + body, err := io.ReadAll(resp.Body) + if err != nil { + return true, fmt.Errorf("could not read response body: %w", err) + } + + return checkJSONResponseError(body) +} + +// checkJSONResponseError classifies JSON responses from Rocketchat. +func checkJSONResponseError(body []byte) (bool, error) { + // response is for parsing out errors from the JSON response. + type response struct { + Success bool `json:"success"` + Error string `json:"error"` + } + + var data response + if err := json.Unmarshal(body, &data); err != nil { + return true, fmt.Errorf("could not unmarshal JSON response %q: %w", string(body), err) + } + if !data.Success { + return false, fmt.Errorf("error response from Rocketchat: %s", data.Error) + } + return false, nil +} diff --git a/vendor/github.com/prometheus/alertmanager/notify/slack/slack.go b/vendor/github.com/prometheus/alertmanager/notify/slack/slack.go index b4b5a195e9..c335ab5f00 100644 --- a/vendor/github.com/prometheus/alertmanager/notify/slack/slack.go +++ b/vendor/github.com/prometheus/alertmanager/notify/slack/slack.go @@ -19,12 +19,11 @@ import ( "encoding/json" "fmt" "io" + "log/slog" "net/http" "os" "strings" - "github.com/go-kit/log" - "github.com/go-kit/log/level" commoncfg "github.com/prometheus/common/config" "github.com/prometheus/alertmanager/config" @@ -40,7 +39,7 @@ const maxTitleLenRunes = 1024 type Notifier struct { conf *config.SlackConfig tmpl *template.Template - logger log.Logger + logger *slog.Logger client *http.Client retrier *notify.Retrier @@ -48,7 +47,7 @@ type Notifier struct { } // New returns a new Slack notification handler. -func New(c *config.SlackConfig, t *template.Template, l log.Logger, httpOpts ...commoncfg.HTTPClientOption) (*Notifier, error) { +func New(c *config.SlackConfig, t *template.Template, l *slog.Logger, httpOpts ...commoncfg.HTTPClientOption) (*Notifier, error) { client, err := commoncfg.NewClientFromConfig(*c.HTTPConfig, "slack", httpOpts...) if err != nil { return nil, err @@ -112,7 +111,7 @@ func (n *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error) if err != nil { return false, err } - level.Warn(n.logger).Log("msg", "Truncated title", "key", key, "max_runes", maxTitleLenRunes) + n.logger.Warn("Truncated title", "key", key, "max_runes", maxTitleLenRunes) } att := &attachment{ Title: title, diff --git a/vendor/github.com/prometheus/alertmanager/notify/sns/sns.go b/vendor/github.com/prometheus/alertmanager/notify/sns/sns.go index 996566c726..3b6026e546 100644 --- a/vendor/github.com/prometheus/alertmanager/notify/sns/sns.go +++ b/vendor/github.com/prometheus/alertmanager/notify/sns/sns.go @@ -17,6 +17,7 @@ import ( "context" "errors" "fmt" + "log/slog" "net/http" "strings" "unicode/utf8" @@ -27,8 +28,6 @@ import ( "github.com/aws/aws-sdk-go/aws/credentials/stscreds" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/sns" - "github.com/go-kit/log" - "github.com/go-kit/log/level" commoncfg "github.com/prometheus/common/config" "github.com/prometheus/alertmanager/config" @@ -41,13 +40,13 @@ import ( type Notifier struct { conf *config.SNSConfig tmpl *template.Template - logger log.Logger + logger *slog.Logger client *http.Client retrier *notify.Retrier } // New returns a new SNS notification handler. -func New(c *config.SNSConfig, t *template.Template, l log.Logger, httpOpts ...commoncfg.HTTPClientOption) (*Notifier, error) { +func New(c *config.SNSConfig, t *template.Template, l *slog.Logger, httpOpts ...commoncfg.HTTPClientOption) (*Notifier, error) { client, err := commoncfg.NewClientFromConfig(*c.HTTPConfig, "sns", httpOpts...) if err != nil { return nil, err @@ -63,12 +62,12 @@ func New(c *config.SNSConfig, t *template.Template, l log.Logger, httpOpts ...co func (n *Notifier) Notify(ctx context.Context, alert ...*types.Alert) (bool, error) { var ( - err error - data = notify.GetTemplateData(ctx, n.tmpl, alert, n.logger) - tmpl = notify.TmplText(n.tmpl, data, &err) + tmplErr error + data = notify.GetTemplateData(ctx, n.tmpl, alert, n.logger) + tmpl = notify.TmplText(n.tmpl, data, &tmplErr) ) - client, err := n.createSNSClient(tmpl) + client, err := n.createSNSClient(tmpl, &tmplErr) if err != nil { var e awserr.RequestFailure if errors.As(err, &e) { @@ -77,7 +76,7 @@ func (n *Notifier) Notify(ctx context.Context, alert ...*types.Alert) (bool, err return true, err } - publishInput, err := n.createPublishInput(ctx, tmpl) + publishInput, err := n.createPublishInput(ctx, tmpl, &tmplErr) if err != nil { return true, err } @@ -94,12 +93,12 @@ func (n *Notifier) Notify(ctx context.Context, alert ...*types.Alert) (bool, err return true, err } - level.Debug(n.logger).Log("msg", "SNS message successfully published", "message_id", publishOutput.MessageId, "sequence number", publishOutput.SequenceNumber) + n.logger.Debug("SNS message successfully published", "message_id", publishOutput.MessageId, "sequence number", publishOutput.SequenceNumber) return false, nil } -func (n *Notifier) createSNSClient(tmpl func(string) string) (*sns.SNS, error) { +func (n *Notifier) createSNSClient(tmpl func(string) string, tmplErr *error) (*sns.SNS, error) { var creds *credentials.Credentials // If there are provided sigV4 credentials we want to use those to create a session. if n.conf.Sigv4.AccessKey != "" && n.conf.Sigv4.SecretKey != "" { @@ -115,6 +114,9 @@ func (n *Notifier) createSNSClient(tmpl func(string) string) (*sns.SNS, error) { if err != nil { return nil, err } + if *tmplErr != nil { + return nil, notify.NewErrorWithReason(notify.ClientErrorReason, fmt.Errorf("execute 'api_url' template: %w", *tmplErr)) + } if n.conf.Sigv4.RoleARN != "" { var stsSess *session.Session @@ -144,13 +146,19 @@ func (n *Notifier) createSNSClient(tmpl func(string) string) (*sns.SNS, error) { return client, nil } -func (n *Notifier) createPublishInput(ctx context.Context, tmpl func(string) string) (*sns.PublishInput, error) { +func (n *Notifier) createPublishInput(ctx context.Context, tmpl func(string) string, tmplErr *error) (*sns.PublishInput, error) { publishInput := &sns.PublishInput{} messageAttributes := n.createMessageAttributes(tmpl) + if *tmplErr != nil { + return nil, notify.NewErrorWithReason(notify.ClientErrorReason, fmt.Errorf("execute 'attributes' template: %w", *tmplErr)) + } // Max message size for a message in a SNS publish request is 256KB, except for SMS messages where the limit is 1600 characters/runes. messageSizeLimit := 256 * 1024 if n.conf.TopicARN != "" { topicARN := tmpl(n.conf.TopicARN) + if *tmplErr != nil { + return nil, notify.NewErrorWithReason(notify.ClientErrorReason, fmt.Errorf("execute 'topic_arn' template: %w", *tmplErr)) + } publishInput.SetTopicArn(topicARN) // If we are using a topic ARN, it could be a FIFO topic specified by the topic's suffix ".fifo". if strings.HasSuffix(topicARN, ".fifo") { @@ -165,14 +173,24 @@ func (n *Notifier) createPublishInput(ctx context.Context, tmpl func(string) str } if n.conf.PhoneNumber != "" { publishInput.SetPhoneNumber(tmpl(n.conf.PhoneNumber)) + if *tmplErr != nil { + return nil, notify.NewErrorWithReason(notify.ClientErrorReason, fmt.Errorf("execute 'phone_number' template: %w", *tmplErr)) + } // If we have an SMS message, we need to truncate to 1600 characters/runes. messageSizeLimit = 1600 } if n.conf.TargetARN != "" { publishInput.SetTargetArn(tmpl(n.conf.TargetARN)) + if *tmplErr != nil { + return nil, notify.NewErrorWithReason(notify.ClientErrorReason, fmt.Errorf("execute 'target_arn' template: %w", *tmplErr)) + } } - messageToSend, isTrunc, err := validateAndTruncateMessage(tmpl(n.conf.Message), messageSizeLimit) + tmplMessage := tmpl(n.conf.Message) + if *tmplErr != nil { + return nil, notify.NewErrorWithReason(notify.ClientErrorReason, fmt.Errorf("execute 'message' template: %w", *tmplErr)) + } + messageToSend, isTrunc, err := validateAndTruncateMessage(tmplMessage, messageSizeLimit) if err != nil { return nil, err } @@ -186,6 +204,9 @@ func (n *Notifier) createPublishInput(ctx context.Context, tmpl func(string) str if n.conf.Subject != "" { publishInput.SetSubject(tmpl(n.conf.Subject)) + if *tmplErr != nil { + return nil, notify.NewErrorWithReason(notify.ClientErrorReason, fmt.Errorf("execute 'subject' template: %w", *tmplErr)) + } } return publishInput, nil diff --git a/vendor/github.com/prometheus/alertmanager/notify/telegram/telegram.go b/vendor/github.com/prometheus/alertmanager/notify/telegram/telegram.go index 3e60ab7641..3bd359cc7a 100644 --- a/vendor/github.com/prometheus/alertmanager/notify/telegram/telegram.go +++ b/vendor/github.com/prometheus/alertmanager/notify/telegram/telegram.go @@ -16,12 +16,11 @@ package telegram import ( "context" "fmt" + "log/slog" "net/http" "os" "strings" - "github.com/go-kit/log" - "github.com/go-kit/log/level" commoncfg "github.com/prometheus/common/config" "gopkg.in/telebot.v3" @@ -38,13 +37,13 @@ const maxMessageLenRunes = 4096 type Notifier struct { conf *config.TelegramConfig tmpl *template.Template - logger log.Logger + logger *slog.Logger client *telebot.Bot retrier *notify.Retrier } // New returns a new Telegram notification handler. -func New(conf *config.TelegramConfig, t *template.Template, l log.Logger, httpOpts ...commoncfg.HTTPClientOption) (*Notifier, error) { +func New(conf *config.TelegramConfig, t *template.Template, l *slog.Logger, httpOpts ...commoncfg.HTTPClientOption) (*Notifier, error) { httpclient, err := commoncfg.NewClientFromConfig(*conf.HTTPConfig, "telegram", httpOpts...) if err != nil { return nil, err @@ -82,7 +81,7 @@ func (n *Notifier) Notify(ctx context.Context, alert ...*types.Alert) (bool, err messageText, truncated := notify.TruncateInRunes(tmpl(n.conf.Message), maxMessageLenRunes) if truncated { - level.Warn(n.logger).Log("msg", "Truncated message", "alert", key, "max_runes", maxMessageLenRunes) + n.logger.Warn("Truncated message", "alert", key, "max_runes", maxMessageLenRunes) } n.client.Token, err = n.getBotToken() @@ -93,11 +92,13 @@ func (n *Notifier) Notify(ctx context.Context, alert ...*types.Alert) (bool, err message, err := n.client.Send(telebot.ChatID(n.conf.ChatID), messageText, &telebot.SendOptions{ DisableNotification: n.conf.DisableNotifications, DisableWebPagePreview: true, + ThreadID: n.conf.MessageThreadID, + ParseMode: n.conf.ParseMode, }) if err != nil { return true, err } - level.Debug(n.logger).Log("msg", "Telegram message successfully published", "message_id", message.ID, "chat_id", message.Chat.ID) + n.logger.Debug("Telegram message successfully published", "message_id", message.ID, "chat_id", message.Chat.ID) return false, nil } diff --git a/vendor/github.com/prometheus/alertmanager/notify/util.go b/vendor/github.com/prometheus/alertmanager/notify/util.go index bd54e2b47d..14e3050bdb 100644 --- a/vendor/github.com/prometheus/alertmanager/notify/util.go +++ b/vendor/github.com/prometheus/alertmanager/notify/util.go @@ -19,12 +19,11 @@ import ( "errors" "fmt" "io" + "log/slog" "net/http" "net/url" "strings" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/prometheus/common/version" "github.com/prometheus/alertmanager/template" @@ -34,7 +33,7 @@ import ( // truncationMarker is the character used to represent a truncation. const truncationMarker = "…" -// UserAgentHeader is the default User-Agent for notification requests +// UserAgentHeader is the default User-Agent for notification requests. var UserAgentHeader = fmt.Sprintf("Alertmanager/%s", version.Version) // RedactURL removes the URL part from an error of *url.Error type. @@ -47,7 +46,7 @@ func RedactURL(err error) error { return e } -// Get sends a GET request to the given URL +// Get sends a GET request to the given URL. func Get(ctx context.Context, client *http.Client, url string) (*http.Response, error) { return request(ctx, client, http.MethodGet, url, "", nil) } @@ -180,14 +179,14 @@ func (k Key) String() string { } // GetTemplateData creates the template data from the context and the alerts. -func GetTemplateData(ctx context.Context, tmpl *template.Template, alerts []*types.Alert, l log.Logger) *template.Data { +func GetTemplateData(ctx context.Context, tmpl *template.Template, alerts []*types.Alert, l *slog.Logger) *template.Data { recv, ok := ReceiverName(ctx) if !ok { - level.Error(l).Log("msg", "Missing receiver") + l.Error("Missing receiver") } groupLabels, ok := GroupLabels(ctx) if !ok { - level.Error(l).Log("msg", "Missing group labels") + l.Error("Missing group labels") } return tmpl.Data(recv, groupLabels, alerts...) } diff --git a/vendor/github.com/prometheus/alertmanager/notify/victorops/victorops.go b/vendor/github.com/prometheus/alertmanager/notify/victorops/victorops.go index 44088b4eb3..ac89222ece 100644 --- a/vendor/github.com/prometheus/alertmanager/notify/victorops/victorops.go +++ b/vendor/github.com/prometheus/alertmanager/notify/victorops/victorops.go @@ -18,12 +18,11 @@ import ( "context" "encoding/json" "fmt" + "log/slog" "net/http" "os" "strings" - "github.com/go-kit/log" - "github.com/go-kit/log/level" commoncfg "github.com/prometheus/common/config" "github.com/prometheus/common/model" @@ -40,13 +39,13 @@ const maxMessageLenRunes = 20480 type Notifier struct { conf *config.VictorOpsConfig tmpl *template.Template - logger log.Logger + logger *slog.Logger client *http.Client retrier *notify.Retrier } // New returns a new VictorOps notifier. -func New(c *config.VictorOpsConfig, t *template.Template, l log.Logger, httpOpts ...commoncfg.HTTPClientOption) (*Notifier, error) { +func New(c *config.VictorOpsConfig, t *template.Template, l *slog.Logger, httpOpts ...commoncfg.HTTPClientOption) (*Notifier, error) { client, err := commoncfg.NewClientFromConfig(*c.HTTPConfig, "victorops", httpOpts...) if err != nil { return nil, err @@ -142,7 +141,7 @@ func (n *Notifier) createVictorOpsPayload(ctx context.Context, as ...*types.Aler stateMessage, truncated := notify.TruncateInRunes(stateMessage, maxMessageLenRunes) if truncated { - level.Warn(n.logger).Log("msg", "Truncated state_message", "incident", key, "max_runes", maxMessageLenRunes) + n.logger.Warn("Truncated state_message", "incident", key, "max_runes", maxMessageLenRunes) } msg := map[string]string{ diff --git a/vendor/github.com/prometheus/alertmanager/notify/webex/webex.go b/vendor/github.com/prometheus/alertmanager/notify/webex/webex.go index 9e95e3ed9b..8ec916dfb6 100644 --- a/vendor/github.com/prometheus/alertmanager/notify/webex/webex.go +++ b/vendor/github.com/prometheus/alertmanager/notify/webex/webex.go @@ -17,10 +17,9 @@ import ( "bytes" "context" "encoding/json" + "log/slog" "net/http" - "github.com/go-kit/log" - "github.com/go-kit/log/level" commoncfg "github.com/prometheus/common/config" "github.com/prometheus/alertmanager/config" @@ -30,6 +29,7 @@ import ( ) const ( + // nolint:godot // maxMessageSize represents the maximum message length that Webex supports. maxMessageSize = 7439 ) @@ -37,13 +37,13 @@ const ( type Notifier struct { conf *config.WebexConfig tmpl *template.Template - logger log.Logger + logger *slog.Logger client *http.Client retrier *notify.Retrier } // New returns a new Webex notifier. -func New(c *config.WebexConfig, t *template.Template, l log.Logger, httpOpts ...commoncfg.HTTPClientOption) (*Notifier, error) { +func New(c *config.WebexConfig, t *template.Template, l *slog.Logger, httpOpts ...commoncfg.HTTPClientOption) (*Notifier, error) { client, err := commoncfg.NewClientFromConfig(*c.HTTPConfig, "webex", httpOpts...) if err != nil { return nil, err @@ -72,7 +72,7 @@ func (n *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error) return false, err } - level.Debug(n.logger).Log("incident", key) + n.logger.Debug("extracted group key", "key", key) data := notify.GetTemplateData(ctx, n.tmpl, as, n.logger) tmpl := notify.TmplText(n.tmpl, data, &err) @@ -87,12 +87,12 @@ func (n *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error) message, truncated := notify.TruncateInBytes(message, maxMessageSize) if truncated { - level.Debug(n.logger).Log("msg", "message truncated due to exceeding maximum allowed length by webex", "truncated_message", message) + n.logger.Debug("message truncated due to exceeding maximum allowed length by webex", "truncated_message", message) } w := webhook{ Markdown: message, - RoomID: n.conf.RoomID, + RoomID: tmpl(n.conf.RoomID), } var payload bytes.Buffer diff --git a/vendor/github.com/prometheus/alertmanager/notify/webhook/webhook.go b/vendor/github.com/prometheus/alertmanager/notify/webhook/webhook.go index 41241569ef..41b9f497dc 100644 --- a/vendor/github.com/prometheus/alertmanager/notify/webhook/webhook.go +++ b/vendor/github.com/prometheus/alertmanager/notify/webhook/webhook.go @@ -18,13 +18,11 @@ import ( "context" "encoding/json" "fmt" - "io" + "log/slog" "net/http" "os" "strings" - "github.com/go-kit/log" - "github.com/go-kit/log/level" commoncfg "github.com/prometheus/common/config" "github.com/prometheus/alertmanager/config" @@ -37,13 +35,13 @@ import ( type Notifier struct { conf *config.WebhookConfig tmpl *template.Template - logger log.Logger + logger *slog.Logger client *http.Client retrier *notify.Retrier } // New returns a new Webhook. -func New(conf *config.WebhookConfig, t *template.Template, l log.Logger, httpOpts ...commoncfg.HTTPClientOption) (*Notifier, error) { +func New(conf *config.WebhookConfig, t *template.Template, l *slog.Logger, httpOpts ...commoncfg.HTTPClientOption) (*Notifier, error) { client, err := commoncfg.NewClientFromConfig(*conf.HTTPConfig, "webhook", httpOpts...) if err != nil { return nil, err @@ -55,11 +53,7 @@ func New(conf *config.WebhookConfig, t *template.Template, l log.Logger, httpOpt client: client, // Webhooks are assumed to respond with 2xx response codes on a successful // request and 5xx response codes are assumed to be recoverable. - retrier: ¬ify.Retrier{ - CustomDetailsFunc: func(_ int, body io.Reader) string { - return errDetails(body, conf.URL.String()) - }, - }, + retrier: ¬ify.Retrier{}, }, nil } @@ -88,9 +82,13 @@ func (n *Notifier) Notify(ctx context.Context, alerts ...*types.Alert) (bool, er groupKey, err := notify.ExtractGroupKey(ctx) if err != nil { - level.Error(n.logger).Log("err", err) + // @tjhop: should we `return false, err` here as we do in most + // other Notify() implementations? + n.logger.Error("error extracting group key", "err", err) } + // @tjhop: should we debug log the key here like most other Notify() implementations? + msg := &Message{ Version: "4", Data: data, @@ -114,8 +112,17 @@ func (n *Notifier) Notify(ctx context.Context, alerts ...*types.Alert) (bool, er url = strings.TrimSpace(string(content)) } + if n.conf.Timeout > 0 { + postCtx, cancel := context.WithTimeoutCause(ctx, n.conf.Timeout, fmt.Errorf("configured webhook timeout reached (%s)", n.conf.Timeout)) + defer cancel() + ctx = postCtx + } + resp, err := notify.PostJSON(ctx, n.client, url, &buf) if err != nil { + if ctx.Err() != nil { + err = fmt.Errorf("%w: %w", err, context.Cause(ctx)) + } return true, notify.RedactURL(err) } defer notify.Drain(resp) @@ -126,14 +133,3 @@ func (n *Notifier) Notify(ctx context.Context, alerts ...*types.Alert) (bool, er } return shouldRetry, err } - -func errDetails(body io.Reader, url string) string { - if body == nil { - return url - } - bs, err := io.ReadAll(body) - if err != nil { - return url - } - return fmt.Sprintf("%s: %s", url, string(bs)) -} diff --git a/vendor/github.com/prometheus/alertmanager/notify/wechat/wechat.go b/vendor/github.com/prometheus/alertmanager/notify/wechat/wechat.go index 9eb77b66a5..4aab58ca30 100644 --- a/vendor/github.com/prometheus/alertmanager/notify/wechat/wechat.go +++ b/vendor/github.com/prometheus/alertmanager/notify/wechat/wechat.go @@ -20,12 +20,11 @@ import ( "errors" "fmt" "io" + "log/slog" "net/http" "net/url" "time" - "github.com/go-kit/log" - "github.com/go-kit/log/level" commoncfg "github.com/prometheus/common/config" "github.com/prometheus/alertmanager/config" @@ -38,7 +37,7 @@ import ( type Notifier struct { conf *config.WechatConfig tmpl *template.Template - logger log.Logger + logger *slog.Logger client *http.Client accessToken string @@ -71,7 +70,7 @@ type weChatResponse struct { } // New returns a new Wechat notifier. -func New(c *config.WechatConfig, t *template.Template, l log.Logger, httpOpts ...commoncfg.HTTPClientOption) (*Notifier, error) { +func New(c *config.WechatConfig, t *template.Template, l *slog.Logger, httpOpts ...commoncfg.HTTPClientOption) (*Notifier, error) { client, err := commoncfg.NewClientFromConfig(*c.HTTPConfig, "wechat", httpOpts...) if err != nil { return nil, err @@ -87,7 +86,7 @@ func (n *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error) return false, err } - level.Debug(n.logger).Log("incident", key) + n.logger.Debug("extracted group key", "key", key) data := notify.GetTemplateData(ctx, n.tmpl, as, n.logger) tmpl := notify.TmplText(n.tmpl, data, &err) @@ -175,7 +174,7 @@ func (n *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error) if err != nil { return true, err } - level.Debug(n.logger).Log("response", string(body), "incident", key) + n.logger.Debug(string(body), "incident", key) var weResp weChatResponse if err := json.Unmarshal(body, &weResp); err != nil { diff --git a/vendor/github.com/prometheus/alertmanager/pkg/labels/matcher.go b/vendor/github.com/prometheus/alertmanager/pkg/labels/matcher.go index f37fcb2173..eba6b4ca60 100644 --- a/vendor/github.com/prometheus/alertmanager/pkg/labels/matcher.go +++ b/vendor/github.com/prometheus/alertmanager/pkg/labels/matcher.go @@ -205,7 +205,7 @@ func (ms Matchers) String() string { return buf.String() } -// This is copied from matchers/parse/lexer.go. It will be removed when +// This is copied from matcher/parse/lexer.go. It will be removed when // the transition window from classic matchers to UTF-8 matchers is complete, // as then we can use double quotes when printing the label name for all // matchers. Until then, the classic parser does not understand double quotes diff --git a/vendor/github.com/prometheus/alertmanager/pkg/labels/parse.go b/vendor/github.com/prometheus/alertmanager/pkg/labels/parse.go index 5138716e0a..f84b29e84b 100644 --- a/vendor/github.com/prometheus/alertmanager/pkg/labels/parse.go +++ b/vendor/github.com/prometheus/alertmanager/pkg/labels/parse.go @@ -136,7 +136,7 @@ func ParseMatcher(s string) (_ *Matcher, err error) { return nil, fmt.Errorf("matcher value not valid UTF-8: %s", ms[3]) } - // Unescape the rawValue: + // Unescape the rawValue. for i, r := range rawValue { if escaped { escaped = false diff --git a/vendor/github.com/prometheus/alertmanager/provider/mem/mem.go b/vendor/github.com/prometheus/alertmanager/provider/mem/mem.go index 0e39ba1d5f..3948697d84 100644 --- a/vendor/github.com/prometheus/alertmanager/provider/mem/mem.go +++ b/vendor/github.com/prometheus/alertmanager/provider/mem/mem.go @@ -15,11 +15,10 @@ package mem import ( "context" + "log/slog" "sync" "time" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/common/model" @@ -34,16 +33,17 @@ const alertChannelLength = 200 type Alerts struct { cancel context.CancelFunc + mtx sync.Mutex + alerts *store.Alerts - marker types.Marker + marker types.AlertMarker - mtx sync.Mutex listeners map[int]listeningAlerts next int callback AlertStoreCallback - logger log.Logger + logger *slog.Logger } type AlertStoreCallback interface { @@ -85,7 +85,7 @@ func (a *Alerts) registerMetrics(r prometheus.Registerer) { } // NewAlerts returns a new alert provider. -func NewAlerts(ctx context.Context, m types.Marker, intervalGC time.Duration, alertCallback AlertStoreCallback, l log.Logger, r prometheus.Registerer) (*Alerts, error) { +func NewAlerts(ctx context.Context, m types.AlertMarker, intervalGC time.Duration, alertCallback AlertStoreCallback, l *slog.Logger, r prometheus.Registerer) (*Alerts, error) { if alertCallback == nil { alertCallback = noopCallback{} } @@ -97,40 +97,56 @@ func NewAlerts(ctx context.Context, m types.Marker, intervalGC time.Duration, al cancel: cancel, listeners: map[int]listeningAlerts{}, next: 0, - logger: log.With(l, "component", "provider"), + logger: l.With("component", "provider"), callback: alertCallback, } - a.alerts.SetGCCallback(func(alerts []*types.Alert) { - for _, alert := range alerts { - // As we don't persist alerts, we no longer consider them after - // they are resolved. Alerts waiting for resolved notifications are - // held in memory in aggregation groups redundantly. - m.Delete(alert.Fingerprint()) - a.callback.PostDelete(alert) - } - - a.mtx.Lock() - for i, l := range a.listeners { - select { - case <-l.done: - delete(a.listeners, i) - close(l.alerts) - default: - // listener is not closed yet, hence proceed. - } - } - a.mtx.Unlock() - }) if r != nil { a.registerMetrics(r) } - go a.alerts.Run(ctx, intervalGC) + go a.gcLoop(ctx, intervalGC) return a, nil } +func (a *Alerts) gcLoop(ctx context.Context, interval time.Duration) { + t := time.NewTicker(interval) + defer t.Stop() + for { + select { + case <-ctx.Done(): + return + case <-t.C: + a.gc() + } + } +} + +func (a *Alerts) gc() { + a.mtx.Lock() + defer a.mtx.Unlock() + + deleted := a.alerts.GC() + for _, alert := range deleted { + // As we don't persist alerts, we no longer consider them after + // they are resolved. Alerts waiting for resolved notifications are + // held in memory in aggregation groups redundantly. + a.marker.Delete(alert.Fingerprint()) + a.callback.PostDelete(&alert) + } + + for i, l := range a.listeners { + select { + case <-l.done: + delete(a.listeners, i) + close(l.alerts) + default: + // listener is not closed yet, hence proceed. + } + } +} + // Close the alert provider. func (a *Alerts) Close() { if a.cancel != nil { @@ -151,7 +167,6 @@ func max(a, b int) int { func (a *Alerts) Subscribe() provider.AlertIterator { a.mtx.Lock() defer a.mtx.Unlock() - var ( done = make(chan struct{}) alerts = a.alerts.List() @@ -175,11 +190,13 @@ func (a *Alerts) GetPending() provider.AlertIterator { ch = make(chan *types.Alert, alertChannelLength) done = make(chan struct{}) ) + a.mtx.Lock() + defer a.mtx.Unlock() + alerts := a.alerts.List() go func() { defer close(ch) - - for _, a := range a.alerts.List() { + for _, a := range alerts { select { case ch <- a: case <-done: @@ -193,11 +210,16 @@ func (a *Alerts) GetPending() provider.AlertIterator { // Get returns the alert for a given fingerprint. func (a *Alerts) Get(fp model.Fingerprint) (*types.Alert, error) { + a.mtx.Lock() + defer a.mtx.Unlock() return a.alerts.Get(fp) } // Put adds the given alert to the set. func (a *Alerts) Put(alerts ...*types.Alert) error { + a.mtx.Lock() + defer a.mtx.Unlock() + for _, alert := range alerts { fp := alert.Fingerprint() @@ -216,25 +238,23 @@ func (a *Alerts) Put(alerts ...*types.Alert) error { } if err := a.callback.PreStore(alert, existing); err != nil { - level.Error(a.logger).Log("msg", "pre-store callback returned error on set alert", "err", err) + a.logger.Error("pre-store callback returned error on set alert", "err", err) continue } if err := a.alerts.Set(alert); err != nil { - level.Error(a.logger).Log("msg", "error on set alert", "err", err) + a.logger.Error("error on set alert", "err", err) continue } a.callback.PostStore(alert, existing) - a.mtx.Lock() for _, l := range a.listeners { select { case l.alerts <- alert: case <-l.done: } } - a.mtx.Unlock() } return nil @@ -242,6 +262,9 @@ func (a *Alerts) Put(alerts ...*types.Alert) error { // count returns the number of non-resolved alerts we currently have stored filtered by the provided state. func (a *Alerts) count(state types.AlertState) int { + a.mtx.Lock() + defer a.mtx.Unlock() + var count int for _, alert := range a.alerts.List() { if alert.Resolved() { diff --git a/vendor/github.com/prometheus/alertmanager/provider/provider.go b/vendor/github.com/prometheus/alertmanager/provider/provider.go index 5fcce20360..4d1f76f206 100644 --- a/vendor/github.com/prometheus/alertmanager/provider/provider.go +++ b/vendor/github.com/prometheus/alertmanager/provider/provider.go @@ -47,7 +47,7 @@ type AlertIterator interface { Next() <-chan *types.Alert } -// NewAlertIterator returns a new AlertIterator based on the generic alertIterator type +// NewAlertIterator returns a new AlertIterator based on the generic alertIterator type. func NewAlertIterator(ch <-chan *types.Alert, done chan struct{}, err error) AlertIterator { return &alertIterator{ ch: ch, diff --git a/vendor/github.com/prometheus/alertmanager/silence/silence.go b/vendor/github.com/prometheus/alertmanager/silence/silence.go index 710323f747..7e25dc3b7b 100644 --- a/vendor/github.com/prometheus/alertmanager/silence/silence.go +++ b/vendor/github.com/prometheus/alertmanager/silence/silence.go @@ -20,6 +20,7 @@ import ( "errors" "fmt" "io" + "log/slog" "math/rand" "os" "reflect" @@ -28,34 +29,33 @@ import ( "sync" "time" - "github.com/benbjohnson/clock" - "github.com/go-kit/log" - "github.com/go-kit/log/level" + "github.com/coder/quartz" uuid "github.com/gofrs/uuid" "github.com/matttproud/golang_protobuf_extensions/pbutil" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/common/model" + "github.com/prometheus/common/promslog" "github.com/prometheus/alertmanager/cluster" - "github.com/prometheus/alertmanager/matchers/compat" + "github.com/prometheus/alertmanager/matcher/compat" "github.com/prometheus/alertmanager/pkg/labels" pb "github.com/prometheus/alertmanager/silence/silencepb" "github.com/prometheus/alertmanager/types" ) // ErrNotFound is returned if a silence was not found. -var ErrNotFound = fmt.Errorf("silence not found") +var ErrNotFound = errors.New("silence not found") // ErrInvalidState is returned if the state isn't valid. -var ErrInvalidState = fmt.Errorf("invalid state") +var ErrInvalidState = errors.New("invalid state") -type matcherCache map[*pb.Silence]labels.Matchers +type matcherCache map[string]labels.Matchers // Get retrieves the matchers for a given silence. If it is a missed cache // access, it compiles and adds the matchers of the requested silence to the // cache. func (c matcherCache) Get(s *pb.Silence) (labels.Matchers, error) { - if m, ok := c[s]; ok { + if m, ok := c[s.Id]; ok { return m, nil } return c.add(s) @@ -88,20 +88,20 @@ func (c matcherCache) add(s *pb.Silence) (labels.Matchers, error) { ms[i] = matcher } - c[s] = ms + c[s.Id] = ms return ms, nil } -// Silencer binds together a Marker and a Silences to implement the Muter +// Silencer binds together a AlertMarker and a Silences to implement the Muter // interface. type Silencer struct { silences *Silences - marker types.Marker - logger log.Logger + marker types.AlertMarker + logger *slog.Logger } // NewSilencer returns a new Silencer. -func NewSilencer(s *Silences, m types.Marker, l log.Logger) *Silencer { +func NewSilencer(s *Silences, m types.AlertMarker, l *slog.Logger) *Silencer { return &Silencer{ silences: s, marker: m, @@ -148,7 +148,7 @@ func (s *Silencer) Mutes(lset model.LabelSet) bool { ) } if err != nil { - level.Error(s.logger).Log("msg", "Querying silences failed, alerts might not get silenced correctly", "err", err) + s.logger.Error("Querying silences failed, alerts might not get silenced correctly", "err", err) } if len(allSils) == 0 { // Easy case, neither active nor pending silences anymore. @@ -171,8 +171,8 @@ func (s *Silencer) Mutes(lset model.LabelSet) bool { // Do nothing, silence has expired in the meantime. } } - level.Debug(s.logger).Log( - "msg", "determined current silences state", + s.logger.Debug( + "determined current silences state", "now", now, "total", len(allSils), "active", len(activeIDs), @@ -188,11 +188,12 @@ func (s *Silencer) Mutes(lset model.LabelSet) bool { // Silences holds a silence state that can be modified, queried, and snapshot. type Silences struct { - clock clock.Clock + clock quartz.Clock - logger log.Logger + logger *slog.Logger metrics *metrics retention time.Duration + limits Limits mtx sync.RWMutex st state @@ -201,6 +202,16 @@ type Silences struct { mc matcherCache } +// Limits contains the limits for silences. +type Limits struct { + // MaxSilences limits the maximum number of silences, including expired + // silences. + MaxSilences func() int + // MaxSilenceSizeBytes is the maximum size of an individual silence as + // stored on disk. + MaxSilenceSizeBytes func() int +} + // MaintenanceFunc represents the function to run as part of the periodic maintenance for silences. // It returns the size of the snapshot taken or an error if it failed. type MaintenanceFunc func() (int64, error) @@ -230,7 +241,7 @@ func newSilenceMetricByState(s *Silences, st types.SilenceState) prometheus.Gaug func() float64 { count, err := s.CountState(st) if err != nil { - level.Error(s.logger).Log("msg", "Counting silences failed", "err", err) + s.logger.Error("Counting silences failed", "err", err) } return float64(count) }, @@ -271,8 +282,12 @@ func newMetrics(r prometheus.Registerer, s *Silences) *metrics { Help: "How many silence received queries did not succeed.", }) m.queryDuration = prometheus.NewHistogram(prometheus.HistogramOpts{ - Name: "alertmanager_silences_query_duration_seconds", - Help: "Duration of silence query evaluation.", + Name: "alertmanager_silences_query_duration_seconds", + Help: "Duration of silence query evaluation.", + Buckets: prometheus.DefBuckets, + NativeHistogramBucketFactor: 1.1, + NativeHistogramMaxBucketNumber: 100, + NativeHistogramMinResetDuration: 1 * time.Hour, }) m.propagatedMessagesTotal = prometheus.NewCounter(prometheus.CounterOpts{ Name: "alertmanager_silences_gossip_messages_propagated_total", @@ -314,15 +329,16 @@ type Options struct { // Retention time for newly created Silences. Silences may be // garbage collected after the given duration after they ended. Retention time.Duration + Limits Limits // A logger used by background processing. - Logger log.Logger + Logger *slog.Logger Metrics prometheus.Registerer } func (o *Options) validate() error { if o.SnapshotFile != "" && o.SnapshotReader != nil { - return fmt.Errorf("only one of SnapshotFile and SnapshotReader must be set") + return errors.New("only one of SnapshotFile and SnapshotReader must be set") } return nil } @@ -334,10 +350,11 @@ func New(o Options) (*Silences, error) { } s := &Silences{ - clock: clock.New(), + clock: quartz.NewReal(), mc: matcherCache{}, - logger: log.NewNopLogger(), + logger: promslog.NewNopLogger(), retention: o.Retention, + limits: o.Limits, broadcast: func([]byte) {}, st: state{}, } @@ -352,7 +369,7 @@ func New(o Options) (*Silences, error) { if !os.IsNotExist(err) { return nil, err } - level.Debug(s.logger).Log("msg", "silences snapshot file doesn't exist", "err", err) + s.logger.Debug("silences snapshot file doesn't exist", "err", err) } else { o.SnapshotReader = r defer r.Close() @@ -377,10 +394,10 @@ func (s *Silences) nowUTC() time.Time { // If not nil, the last argument is an override for what to do as part of the maintenance - for advanced usage. func (s *Silences) Maintenance(interval time.Duration, snapf string, stopc <-chan struct{}, override MaintenanceFunc) { if interval == 0 || stopc == nil { - level.Error(s.logger).Log("msg", "interval or stop signal are missing - not running maintenance") + s.logger.Error("interval or stop signal are missing - not running maintenance") return } - t := s.clock.Ticker(interval) + t := s.clock.NewTicker(interval) defer t.Stop() var doMaintenance MaintenanceFunc @@ -410,7 +427,7 @@ func (s *Silences) Maintenance(interval time.Duration, snapf string, stopc <-cha runMaintenance := func(do MaintenanceFunc) error { s.metrics.maintenanceTotal.Inc() - level.Debug(s.logger).Log("msg", "Running maintenance") + s.logger.Debug("Running maintenance") start := s.nowUTC() size, err := do() s.metrics.snapshotSize.Set(float64(size)) @@ -418,7 +435,7 @@ func (s *Silences) Maintenance(interval time.Duration, snapf string, stopc <-cha s.metrics.maintenanceErrorsTotal.Inc() return err } - level.Debug(s.logger).Log("msg", "Maintenance done", "duration", s.clock.Since(start), "size", size) + s.logger.Debug("Maintenance done", "duration", s.clock.Since(start), "size", size) return nil } @@ -429,7 +446,8 @@ Loop: break Loop case <-t.C: if err := runMaintenance(doMaintenance); err != nil { - level.Info(s.logger).Log("msg", "Running maintenance failed", "err", err) + // @tjhop: this should probably log at error level + s.logger.Info("Running maintenance failed", "err", err) } } } @@ -439,7 +457,8 @@ Loop: return } if err := runMaintenance(doMaintenance); err != nil { - level.Info(s.logger).Log("msg", "Creating shutdown snapshot failed", "err", err) + // @tjhop: this should probably log at error level + s.logger.Info("Creating shutdown snapshot failed", "err", err) } } @@ -461,7 +480,7 @@ func (s *Silences) GC() (int, error) { } if !sil.ExpiresAt.After(now) { delete(s.st, id) - delete(s.mc, sil.Silence) + delete(s.mc, sil.Silence.Id) n++ } } @@ -501,9 +520,6 @@ func matchesEmpty(m *pb.Matcher) bool { } func validateSilence(s *pb.Silence) error { - if s.Id == "" { - return errors.New("ID missing") - } if len(s.Matchers) == 0 { return errors.New("at least one matcher required") } @@ -527,9 +543,6 @@ func validateSilence(s *pb.Silence) error { if s.EndsAt.Before(s.StartsAt) { return errors.New("end time must not be before start time") } - if s.UpdatedAt.IsZero() { - return errors.New("invalid zero update timestamp") - } return nil } @@ -539,6 +552,16 @@ func cloneSilence(sil *pb.Silence) *pb.Silence { return &s } +func (s *Silences) checkSizeLimits(msil *pb.MeshSilence) error { + if s.limits.MaxSilenceSizeBytes != nil { + n := msil.Size() + if m := s.limits.MaxSilenceSizeBytes(); m > 0 && n > m { + return fmt.Errorf("silence exceeded maximum size: %d bytes (limit: %d bytes)", n, m) + } + } + return nil +} + func (s *Silences) getSilence(id string) (*pb.Silence, bool) { msil, ok := s.st[id] if !ok { @@ -547,67 +570,89 @@ func (s *Silences) getSilence(id string) (*pb.Silence, bool) { return msil.Silence, true } -func (s *Silences) setSilence(sil *pb.Silence, now time.Time, skipValidate bool) error { - sil.UpdatedAt = now - - if !skipValidate { - if err := validateSilence(sil); err != nil { - return fmt.Errorf("silence invalid: %w", err) - } - } - - msil := &pb.MeshSilence{ +func (s *Silences) toMeshSilence(sil *pb.Silence) *pb.MeshSilence { + return &pb.MeshSilence{ Silence: sil, ExpiresAt: sil.EndsAt.Add(s.retention), } +} + +func (s *Silences) setSilence(msil *pb.MeshSilence, now time.Time) error { b, err := marshalMeshSilence(msil) if err != nil { return err } - - if s.st.merge(msil, now) { + _, added := s.st.merge(msil, now) + if added { s.version++ } s.broadcast(b) - return nil } // Set the specified silence. If a silence with the ID already exists and the modification // modifies history, the old silence gets expired and a new one is created. -func (s *Silences) Set(sil *pb.Silence) (string, error) { +func (s *Silences) Set(sil *pb.Silence) error { s.mtx.Lock() defer s.mtx.Unlock() now := s.nowUTC() - prev, ok := s.getSilence(sil.Id) + if sil.StartsAt.IsZero() { + sil.StartsAt = now + } + if err := validateSilence(sil); err != nil { + return fmt.Errorf("invalid silence: %w", err) + } + + prev, ok := s.getSilence(sil.Id) if sil.Id != "" && !ok { - return "", ErrNotFound + return ErrNotFound } - if ok { - if canUpdate(prev, sil, now) { - return sil.Id, s.setSilence(sil, now, false) + + if ok && canUpdate(prev, sil, now) { + sil.UpdatedAt = now + msil := s.toMeshSilence(sil) + if err := s.checkSizeLimits(msil); err != nil { + return err } - if getState(prev, s.nowUTC()) != types.SilenceStateExpired { - // We cannot update the silence, expire the old one. - if err := s.expire(prev.Id); err != nil { - return "", fmt.Errorf("expire previous silence: %w", err) - } + return s.setSilence(msil, now) + } + + // If we got here it's either a new silence or a replacing one (which would + // also create a new silence) so we need to make sure we have capacity for + // the new silence. + if s.limits.MaxSilences != nil { + if m := s.limits.MaxSilences(); m > 0 && len(s.st)+1 > m { + return fmt.Errorf("exceeded maximum number of silences: %d (limit: %d)", len(s.st), m) } } - // If we got here it's either a new silence or a replacing one. + uid, err := uuid.NewV4() if err != nil { - return "", fmt.Errorf("generate uuid: %w", err) + return fmt.Errorf("generate uuid: %w", err) } sil.Id = uid.String() if sil.StartsAt.Before(now) { sil.StartsAt = now } + sil.UpdatedAt = now + + msil := s.toMeshSilence(sil) + if err := s.checkSizeLimits(msil); err != nil { + return err + } + + if ok && getState(prev, s.nowUTC()) != types.SilenceStateExpired { + // We cannot update the silence, expire the old one to leave a history of + // the silence before modification. + if err := s.expire(prev.Id); err != nil { + return fmt.Errorf("expire previous silence: %w", err) + } + } - return sil.Id, s.setSilence(sil, now, false) + return s.setSilence(msil, now) } // canUpdate returns true if silence a can be updated to b without @@ -665,10 +710,8 @@ func (s *Silences) expire(id string) error { sil.StartsAt = now sil.EndsAt = now } - - // Skip validation of the silence when expiring it. Without this, silences created - // with valid UTF-8 matchers cannot be expired when Alertmanager is run in classic mode. - return s.setSilence(sil, now, true) + sil.UpdatedAt = now + return s.setSilence(s.toMeshSilence(sil), now) } // QueryParam expresses parameters along which silences are queried. @@ -751,6 +794,9 @@ func (s *Silences) QueryOne(params ...QueryParam) (*pb.Silence, error) { // Query for silences based on the given query parameters. It returns the // resulting silences and the state version the result is based on. func (s *Silences) Query(params ...QueryParam) ([]*pb.Silence, int, error) { + s.mtx.Lock() + defer s.mtx.Unlock() + s.metrics.queriesTotal.Inc() defer prometheus.NewTimer(s.metrics.queryDuration).ObserveDuration() @@ -790,9 +836,6 @@ func (s *Silences) query(q *query, now time.Time) ([]*pb.Silence, int, error) { // the use of post-filter functions is the trivial solution for now. var res []*pb.Silence - s.mtx.Lock() - defer s.mtx.Unlock() - if q.ids != nil { for _, id := range q.ids { if s, ok := s.st[id]; ok { @@ -887,8 +930,11 @@ func (s *Silences) Merge(b []byte) error { now := s.nowUTC() for _, e := range st { - if merged := s.st.merge(e, now); merged { - s.version++ + merged, added := s.st.merge(e, now) + if merged { + if added { + s.version++ + } if !cluster.OversizedMessage(b) { // If this is the first we've seen the message and it's // not oversized, gossip it to other nodes. We don't @@ -896,7 +942,7 @@ func (s *Silences) Merge(b []byte) error { // all nodes already. s.broadcast(b) s.metrics.propagatedMessagesTotal.Inc() - level.Debug(s.logger).Log("msg", "Gossiping new silence", "silence", e) + s.logger.Debug("Gossiping new silence", "silence", e) } } } @@ -913,10 +959,13 @@ func (s *Silences) SetBroadcast(f func([]byte)) { type state map[string]*pb.MeshSilence -func (s state) merge(e *pb.MeshSilence, now time.Time) bool { +// merge returns two bools: the first is true when merge caused a state change. The second +// is true if that state change added a new silence. In other words, the second return is +// true whenever a silence with a new ID has been added to the state as a result of merge. +func (s state) merge(e *pb.MeshSilence, now time.Time) (bool, bool) { id := e.Silence.Id if e.ExpiresAt.Before(now) { - return false + return false, false } // Comments list was moved to a single comment. Apply upgrade // on silences received from peers. @@ -929,9 +978,9 @@ func (s state) merge(e *pb.MeshSilence, now time.Time) bool { prev, ok := s[id] if !ok || prev.Silence.UpdatedAt.Before(e.Silence.UpdatedAt) { s[id] = e - return true + return true, !ok } - return false + return false, false } func (s state) MarshalBinary() ([]byte, error) { diff --git a/vendor/github.com/prometheus/alertmanager/store/store.go b/vendor/github.com/prometheus/alertmanager/store/store.go index 83f1495a0f..e4275aa1f5 100644 --- a/vendor/github.com/prometheus/alertmanager/store/store.go +++ b/vendor/github.com/prometheus/alertmanager/store/store.go @@ -34,21 +34,21 @@ var ErrNotFound = errors.New("alert not found") type Alerts struct { sync.Mutex c map[model.Fingerprint]*types.Alert - cb func([]*types.Alert) + cb func([]types.Alert) } // NewAlerts returns a new Alerts struct. func NewAlerts() *Alerts { a := &Alerts{ c: make(map[model.Fingerprint]*types.Alert), - cb: func(_ []*types.Alert) {}, + cb: func(_ []types.Alert) {}, } return a } // SetGCCallback sets a GC callback to be executed after each GC. -func (a *Alerts) SetGCCallback(cb func([]*types.Alert)) { +func (a *Alerts) SetGCCallback(cb func([]types.Alert)) { a.Lock() defer a.Unlock() @@ -64,23 +64,34 @@ func (a *Alerts) Run(ctx context.Context, interval time.Duration) { case <-ctx.Done(): return case <-t.C: - a.gc() + a.GC() } } } -func (a *Alerts) gc() { +// GC deletes resolved alerts and returns them. +func (a *Alerts) GC() []types.Alert { a.Lock() - defer a.Unlock() - - var resolved []*types.Alert + var resolved []types.Alert for fp, alert := range a.c { if alert.Resolved() { delete(a.c, fp) - resolved = append(resolved, alert) + resolved = append(resolved, types.Alert{ + Alert: model.Alert{ + Labels: alert.Labels.Clone(), + Annotations: alert.Annotations.Clone(), + StartsAt: alert.StartsAt, + EndsAt: alert.EndsAt, + GeneratorURL: alert.GeneratorURL, + }, + UpdatedAt: alert.UpdatedAt, + Timeout: alert.Timeout, + }) } } + a.Unlock() a.cb(resolved) + return resolved } // Get returns the Alert with the matching fingerprint, or an error if it is @@ -105,12 +116,17 @@ func (a *Alerts) Set(alert *types.Alert) error { return nil } -// Delete removes the Alert with the matching fingerprint from the store. -func (a *Alerts) Delete(fp model.Fingerprint) error { +// DeleteIfNotModified deletes the slice of Alerts from the store if not +// modified. +func (a *Alerts) DeleteIfNotModified(alerts types.AlertSlice) error { a.Lock() defer a.Unlock() - - delete(a.c, fp) + for _, alert := range alerts { + fp := alert.Fingerprint() + if other, ok := a.c[fp]; ok && alert.UpdatedAt == other.UpdatedAt { + delete(a.c, fp) + } + } return nil } diff --git a/vendor/github.com/prometheus/alertmanager/template/Dockerfile b/vendor/github.com/prometheus/alertmanager/template/Dockerfile index 19d4be51a9..43b728d18c 100644 --- a/vendor/github.com/prometheus/alertmanager/template/Dockerfile +++ b/vendor/github.com/prometheus/alertmanager/template/Dockerfile @@ -1,7 +1,7 @@ -FROM node +FROM node:20-alpine ENV NODE_PATH="/usr/local/lib/node_modules" -RUN npm install juice -g +RUN npm install juice@10.0.1 -g ENTRYPOINT [""] diff --git a/vendor/github.com/prometheus/alertmanager/template/Makefile b/vendor/github.com/prometheus/alertmanager/template/Makefile index 93e0d4985b..a62721b9fe 100644 --- a/vendor/github.com/prometheus/alertmanager/template/Makefile +++ b/vendor/github.com/prometheus/alertmanager/template/Makefile @@ -1,4 +1,4 @@ -DOCKER_IMG := altermanager-template +DOCKER_IMG := alertmanager-template DOCKER_RUN_CURRENT_USER := docker run --user=$(shell id -u $(USER)):$(shell id -g $(USER)) DOCKER_CMD := $(DOCKER_RUN_CURRENT_USER) --rm -t -v $(PWD):/app -w /app $(DOCKER_IMG) diff --git a/vendor/github.com/prometheus/alertmanager/template/default.tmpl b/vendor/github.com/prometheus/alertmanager/template/default.tmpl index 8b2bb7470e..57e877c0c2 100644 --- a/vendor/github.com/prometheus/alertmanager/template/default.tmpl +++ b/vendor/github.com/prometheus/alertmanager/template/default.tmpl @@ -123,6 +123,7 @@ Alerts Resolved: {{ end }} {{ end }} +{{ define "discord.default.content" }}{{ end }} {{ define "discord.default.title" }}{{ template "__subject" . }}{{ end }} {{ define "discord.default.message" }} {{ if gt (len .Alerts.Firing) 0 }} @@ -158,3 +159,61 @@ Alerts Resolved: {{ template "__text_alert_list_markdown" .Alerts.Resolved }} {{ end }} {{ end }} + +{{ define "msteamsv2.default.title" }}{{ template "__subject" . }}{{ end }} +{{ define "msteamsv2.default.text" }} +{{ if gt (len .Alerts.Firing) 0 }} +# Alerts Firing: +{{ template "__text_alert_list_markdown" .Alerts.Firing }} +{{ end }} +{{ if gt (len .Alerts.Resolved) 0 }} +# Alerts Resolved: +{{ template "__text_alert_list_markdown" .Alerts.Resolved }} +{{ end }} +{{ end }} + +{{ define "jira.default.summary" }}{{ template "__subject" . }}{{ end }} +{{ define "jira.default.description" }} +{{ if gt (len .Alerts.Firing) 0 }} +# Alerts Firing: +{{ template "__text_alert_list_markdown" .Alerts.Firing }} +{{ end }} +{{ if gt (len .Alerts.Resolved) 0 }} +# Alerts Resolved: +{{ template "__text_alert_list_markdown" .Alerts.Resolved }} +{{ end }} +{{ end }} + +{{- define "jira.default.priority" -}} +{{- $priority := "" }} +{{- range .Alerts.Firing -}} + {{- $severity := index .Labels "severity" -}} + {{- if (eq $severity "critical") -}} + {{- $priority = "High" -}} + {{- else if (and (eq $severity "warning") (ne $priority "High")) -}} + {{- $priority = "Medium" -}} + {{- else if (and (eq $severity "info") (eq $priority "")) -}} + {{- $priority = "Low" -}} + {{- end -}} +{{- end -}} +{{- if eq $priority "" -}} + {{- range .Alerts.Resolved -}} + {{- $severity := index .Labels "severity" -}} + {{- if (eq $severity "critical") -}} + {{- $priority = "High" -}} + {{- else if (and (eq $severity "warning") (ne $priority "High")) -}} + {{- $priority = "Medium" -}} + {{- else if (and (eq $severity "info") (eq $priority "")) -}} + {{- $priority = "Low" -}} + {{- end -}} + {{- end -}} +{{- end -}} +{{- $priority -}} +{{- end -}} + +{{ define "rocketchat.default.title" }}{{ template "__subject" . }}{{ end }} +{{ define "rocketchat.default.alias" }}{{ template "__alertmanager" . }}{{ end }} +{{ define "rocketchat.default.titlelink" }}{{ template "__alertmanagerURL" . }}{{ end }} +{{ define "rocketchat.default.emoji" }}{{ end }} +{{ define "rocketchat.default.iconurl" }}{{ end }} +{{ define "rocketchat.default.text" }}{{ end }} diff --git a/vendor/github.com/prometheus/alertmanager/template/template.go b/vendor/github.com/prometheus/alertmanager/template/template.go index 5172ba92a7..92f8323e25 100644 --- a/vendor/github.com/prometheus/alertmanager/template/template.go +++ b/vendor/github.com/prometheus/alertmanager/template/template.go @@ -26,6 +26,7 @@ import ( tmpltext "text/template" "time" + commonTemplates "github.com/prometheus/common/helpers/templates" "github.com/prometheus/common/model" "golang.org/x/text/cases" "golang.org/x/text/language" @@ -192,6 +193,20 @@ var DefaultFuncs = FuncMap{ "stringSlice": func(s ...string) []string { return s }, + // date returns the text representation of the time in the specified format. + "date": func(fmt string, t time.Time) string { + return t.Format(fmt) + }, + // tz returns the time in the timezone. + "tz": func(name string, t time.Time) (time.Time, error) { + loc, err := time.LoadLocation(name) + if err != nil { + return time.Time{}, err + } + return t.In(loc), nil + }, + "since": time.Since, + "humanizeDuration": commonTemplates.HumanizeDuration, } // Pair is a key/value string pair. diff --git a/vendor/github.com/prometheus/alertmanager/timeinterval/timeinterval.go b/vendor/github.com/prometheus/alertmanager/timeinterval/timeinterval.go index fe8c97d729..caf81485ce 100644 --- a/vendor/github.com/prometheus/alertmanager/timeinterval/timeinterval.go +++ b/vendor/github.com/prometheus/alertmanager/timeinterval/timeinterval.go @@ -33,21 +33,23 @@ type Intervener struct { intervals map[string][]TimeInterval } -func (i *Intervener) Mutes(names []string, now time.Time) (bool, error) { +// Mutes implements the TimeMuter interface. +func (i *Intervener) Mutes(names []string, now time.Time) (bool, []string, error) { + var in []string for _, name := range names { interval, ok := i.intervals[name] if !ok { - return false, fmt.Errorf("time interval %s doesn't exist in config", name) + return false, nil, fmt.Errorf("time interval %s doesn't exist in config", name) } for _, ti := range interval { if ti.ContainsTime(now.UTC()) { - return true, nil + in = append(in, name) } } } - return false, nil + return len(in) > 0, in, nil } func NewIntervener(ti map[string][]TimeInterval) *Intervener { @@ -383,7 +385,7 @@ func (r WeekdayRange) MarshalYAML() (interface{}, error) { // MarshalText implements the econding.TextMarshaler interface for WeekdayRange. // It converts the range into a colon-separated string, or a single weekday if possible. -// e.g. "monday:friday" or "saturday". +// E.g. "monday:friday" or "saturday". func (r WeekdayRange) MarshalText() ([]byte, error) { beginStr, ok := daysOfWeekInv[r.Begin] if !ok { @@ -450,7 +452,7 @@ func (tz Location) MarshalJSON() (out []byte, err error) { // MarshalText implements the encoding.TextMarshaler interface for InclusiveRange. // It converts the struct into a colon-separated string, or a single element if -// appropriate. e.g. "monday:friday" or "monday" +// appropriate. E.g. "monday:friday" or "monday". func (ir InclusiveRange) MarshalText() ([]byte, error) { if ir.Begin == ir.End { return []byte(strconv.Itoa(ir.Begin)), nil diff --git a/vendor/github.com/prometheus/alertmanager/types/types.go b/vendor/github.com/prometheus/alertmanager/types/types.go index 54a889ab9c..727ac320e3 100644 --- a/vendor/github.com/prometheus/alertmanager/types/types.go +++ b/vendor/github.com/prometheus/alertmanager/types/types.go @@ -22,7 +22,7 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/common/model" - "github.com/prometheus/alertmanager/matchers/compat" + "github.com/prometheus/alertmanager/matcher/compat" "github.com/prometheus/alertmanager/pkg/labels" ) @@ -52,9 +52,17 @@ type AlertStatus struct { silencesVersion int } -// Marker helps to mark alerts as silenced and/or inhibited. +// groupStatus stores the state of the group, and, as applicable, the names +// of all active and mute time intervals that are muting it. +type groupStatus struct { + // mutedBy contains the names of all active and mute time intervals that + // are muting it. + mutedBy []string +} + +// AlertMarker helps to mark alerts as silenced and/or inhibited. // All methods are goroutine-safe. -type Marker interface { +type AlertMarker interface { // SetActiveOrSilenced replaces the previous SilencedBy by the provided IDs of // active and pending silences, including the version number of the // silences state. The set of provided IDs is supposed to represent the @@ -92,24 +100,74 @@ type Marker interface { Inhibited(model.Fingerprint) ([]string, bool) } -// NewMarker returns an instance of a Marker implementation. -func NewMarker(r prometheus.Registerer) Marker { - m := &memMarker{ - m: map[model.Fingerprint]*AlertStatus{}, - } +// GroupMarker helps to mark groups as active or muted. +// All methods are goroutine-safe. +// +// TODO(grobinson): routeID is used in Muted and SetMuted because groupKey +// is not unique (see #3817). Once groupKey uniqueness is fixed routeID can +// be removed from the GroupMarker interface. +type GroupMarker interface { + // Muted returns true if the group is muted, otherwise false. If the group + // is muted then it also returns the names of the time intervals that muted + // it. + Muted(routeID, groupKey string) ([]string, bool) + + // SetMuted marks the group as muted, and sets the names of the time + // intervals that mute it. If the list of names is nil or the empty slice + // then the muted marker is removed. + SetMuted(routeID, groupKey string, timeIntervalNames []string) + + // DeleteByGroupKey removes all markers for the GroupKey. + DeleteByGroupKey(routeID, groupKey string) +} +// NewMarker returns an instance of a AlertMarker implementation. +func NewMarker(r prometheus.Registerer) *MemMarker { + m := &MemMarker{ + alerts: map[model.Fingerprint]*AlertStatus{}, + groups: map[string]*groupStatus{}, + } m.registerMetrics(r) - return m } -type memMarker struct { - m map[model.Fingerprint]*AlertStatus +type MemMarker struct { + alerts map[model.Fingerprint]*AlertStatus + groups map[string]*groupStatus mtx sync.RWMutex } -func (m *memMarker) registerMetrics(r prometheus.Registerer) { +// Muted implements GroupMarker. +func (m *MemMarker) Muted(routeID, groupKey string) ([]string, bool) { + m.mtx.Lock() + defer m.mtx.Unlock() + status, ok := m.groups[routeID+groupKey] + if !ok { + return nil, false + } + return status.mutedBy, len(status.mutedBy) > 0 +} + +// SetMuted implements GroupMarker. +func (m *MemMarker) SetMuted(routeID, groupKey string, timeIntervalNames []string) { + m.mtx.Lock() + defer m.mtx.Unlock() + status, ok := m.groups[routeID+groupKey] + if !ok { + status = &groupStatus{} + m.groups[routeID+groupKey] = status + } + status.mutedBy = timeIntervalNames +} + +func (m *MemMarker) DeleteByGroupKey(routeID, groupKey string) { + m.mtx.Lock() + defer m.mtx.Unlock() + delete(m.groups, routeID+groupKey) +} + +func (m *MemMarker) registerMetrics(r prometheus.Registerer) { newMarkedAlertMetricByState := func(st AlertState) prometheus.GaugeFunc { return prometheus.NewGaugeFunc( prometheus.GaugeOpts{ @@ -132,17 +190,17 @@ func (m *memMarker) registerMetrics(r prometheus.Registerer) { r.MustRegister(alertStateUnprocessed) } -// Count implements Marker. -func (m *memMarker) Count(states ...AlertState) int { +// Count implements AlertMarker. +func (m *MemMarker) Count(states ...AlertState) int { m.mtx.RLock() defer m.mtx.RUnlock() if len(states) == 0 { - return len(m.m) + return len(m.alerts) } var count int - for _, status := range m.m { + for _, status := range m.alerts { for _, state := range states { if status.State == state { count++ @@ -152,15 +210,15 @@ func (m *memMarker) Count(states ...AlertState) int { return count } -// SetActiveOrSilenced implements Marker. -func (m *memMarker) SetActiveOrSilenced(alert model.Fingerprint, version int, activeIDs, pendingIDs []string) { +// SetActiveOrSilenced implements AlertMarker. +func (m *MemMarker) SetActiveOrSilenced(alert model.Fingerprint, version int, activeIDs, pendingIDs []string) { m.mtx.Lock() defer m.mtx.Unlock() - s, found := m.m[alert] + s, found := m.alerts[alert] if !found { s = &AlertStatus{} - m.m[alert] = s + m.alerts[alert] = s } s.SilencedBy = activeIDs s.pendingSilences = pendingIDs @@ -177,15 +235,15 @@ func (m *memMarker) SetActiveOrSilenced(alert model.Fingerprint, version int, ac s.State = AlertStateSuppressed } -// SetInhibited implements Marker. -func (m *memMarker) SetInhibited(alert model.Fingerprint, ids ...string) { +// SetInhibited implements AlertMarker. +func (m *MemMarker) SetInhibited(alert model.Fingerprint, ids ...string) { m.mtx.Lock() defer m.mtx.Unlock() - s, found := m.m[alert] + s, found := m.alerts[alert] if !found { s = &AlertStatus{} - m.m[alert] = s + m.alerts[alert] = s } s.InhibitedBy = ids @@ -200,12 +258,12 @@ func (m *memMarker) SetInhibited(alert model.Fingerprint, ids ...string) { s.State = AlertStateSuppressed } -// Status implements Marker. -func (m *memMarker) Status(alert model.Fingerprint) AlertStatus { +// Status implements AlertMarker. +func (m *MemMarker) Status(alert model.Fingerprint) AlertStatus { m.mtx.RLock() defer m.mtx.RUnlock() - if s, found := m.m[alert]; found { + if s, found := m.alerts[alert]; found { return *s } return AlertStatus{ @@ -215,26 +273,26 @@ func (m *memMarker) Status(alert model.Fingerprint) AlertStatus { } } -// Delete implements Marker. -func (m *memMarker) Delete(alert model.Fingerprint) { +// Delete implements AlertMarker. +func (m *MemMarker) Delete(alert model.Fingerprint) { m.mtx.Lock() defer m.mtx.Unlock() - delete(m.m, alert) + delete(m.alerts, alert) } -// Unprocessed implements Marker. -func (m *memMarker) Unprocessed(alert model.Fingerprint) bool { +// Unprocessed implements AlertMarker. +func (m *MemMarker) Unprocessed(alert model.Fingerprint) bool { return m.Status(alert).State == AlertStateUnprocessed } -// Active implements Marker. -func (m *memMarker) Active(alert model.Fingerprint) bool { +// Active implements AlertMarker. +func (m *MemMarker) Active(alert model.Fingerprint) bool { return m.Status(alert).State == AlertStateActive } -// Inhibited implements Marker. -func (m *memMarker) Inhibited(alert model.Fingerprint) ([]string, bool) { +// Inhibited implements AlertMarker. +func (m *MemMarker) Inhibited(alert model.Fingerprint) ([]string, bool) { s := m.Status(alert) return s.InhibitedBy, s.State == AlertStateSuppressed && len(s.InhibitedBy) > 0 @@ -243,7 +301,7 @@ func (m *memMarker) Inhibited(alert model.Fingerprint) ([]string, bool) { // Silenced returns whether the alert for the given Fingerprint is in the // Silenced state, any associated silence IDs, and the silences state version // the result is based on. -func (m *memMarker) Silenced(alert model.Fingerprint) (activeIDs, pendingIDs []string, version int, silenced bool) { +func (m *MemMarker) Silenced(alert model.Fingerprint) (activeIDs, pendingIDs []string, version int, silenced bool) { s := m.Status(alert) return s.SilencedBy, s.pendingSilences, s.silencesVersion, s.State == AlertStateSuppressed && len(s.SilencedBy) > 0 @@ -410,15 +468,17 @@ func (a *Alert) Merge(o *Alert) *Alert { } // A Muter determines whether a given label set is muted. Implementers that -// maintain an underlying Marker are expected to update it during a call of +// maintain an underlying AlertMarker are expected to update it during a call of // Mutes. type Muter interface { Mutes(model.LabelSet) bool } -// TimeMuter determines if alerts should be muted based on the specified current time and active time interval on the route. +// A TimeMuter determines if the time is muted by one or more active or mute +// time intervals. If the time is muted, it returns true and the names of the +// time intervals that muted it. Otherwise, it returns false and a nil slice. type TimeMuter interface { - Mutes(timeIntervalName []string, now time.Time) (bool, error) + Mutes(timeIntervalNames []string, now time.Time) (bool, []string, error) } // A MuteFunc is a function that implements the Muter interface. @@ -458,7 +518,7 @@ type Silence struct { } // Expired return if the silence is expired -// meaning that both StartsAt and EndsAt are equal +// meaning that both StartsAt and EndsAt are equal. func (s *Silence) Expired() bool { return s.StartsAt.Equal(s.EndsAt) } diff --git a/vendor/github.com/prometheus/alertmanager/ui/web.go b/vendor/github.com/prometheus/alertmanager/ui/web.go index 3820625925..3b81135237 100644 --- a/vendor/github.com/prometheus/alertmanager/ui/web.go +++ b/vendor/github.com/prometheus/alertmanager/ui/web.go @@ -15,11 +15,11 @@ package ui import ( "fmt" + "log/slog" "net/http" _ "net/http/pprof" // Comment this line to disable pprof endpoint. "path" - "github.com/go-kit/log" "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/common/route" @@ -27,7 +27,7 @@ import ( ) // Register registers handlers to serve files for the web interface. -func Register(r *route.Router, reloadCh chan<- chan error, logger log.Logger) { +func Register(r *route.Router, reloadCh chan<- chan error, logger *slog.Logger) { r.Get("/metrics", promhttp.Handler().ServeHTTP) r.Get("/", func(w http.ResponseWriter, req *http.Request) { diff --git a/vendor/github.com/prometheus/common/promslog/slog.go b/vendor/github.com/prometheus/common/promslog/slog.go new file mode 100644 index 0000000000..6e8fbabce5 --- /dev/null +++ b/vendor/github.com/prometheus/common/promslog/slog.go @@ -0,0 +1,201 @@ +// Copyright 2024 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package promslog defines standardised ways to initialize the Go standard +// library's log/slog logger. +// It should typically only ever be imported by main packages. + +package promslog + +import ( + "fmt" + "io" + "log/slog" + "os" + "path/filepath" + "strconv" + "strings" +) + +type LogStyle string + +const ( + SlogStyle LogStyle = "slog" + GoKitStyle LogStyle = "go-kit" +) + +var ( + LevelFlagOptions = []string{"debug", "info", "warn", "error"} + FormatFlagOptions = []string{"logfmt", "json"} + + callerAddFunc = false + defaultWriter = os.Stderr + goKitStyleReplaceAttrFunc = func(groups []string, a slog.Attr) slog.Attr { + key := a.Key + switch key { + case slog.TimeKey: + a.Key = "ts" + + // This timestamp format differs from RFC3339Nano by using .000 instead + // of .999999999 which changes the timestamp from 9 variable to 3 fixed + // decimals (.130 instead of .130987456). + t := a.Value.Time() + a.Value = slog.StringValue(t.UTC().Format("2006-01-02T15:04:05.000Z07:00")) + case slog.SourceKey: + a.Key = "caller" + src, _ := a.Value.Any().(*slog.Source) + + switch callerAddFunc { + case true: + a.Value = slog.StringValue(filepath.Base(src.File) + "(" + filepath.Base(src.Function) + "):" + strconv.Itoa(src.Line)) + default: + a.Value = slog.StringValue(filepath.Base(src.File) + ":" + strconv.Itoa(src.Line)) + } + case slog.LevelKey: + a.Value = slog.StringValue(strings.ToLower(a.Value.String())) + default: + } + + return a + } + defaultReplaceAttrFunc = func(groups []string, a slog.Attr) slog.Attr { + key := a.Key + switch key { + case slog.TimeKey: + t := a.Value.Time() + a.Value = slog.TimeValue(t.UTC()) + case slog.SourceKey: + src, _ := a.Value.Any().(*slog.Source) + a.Value = slog.StringValue(filepath.Base(src.File) + ":" + strconv.Itoa(src.Line)) + default: + } + + return a + } +) + +// AllowedLevel is a settable identifier for the minimum level a log entry +// must be have. +type AllowedLevel struct { + s string + lvl *slog.LevelVar +} + +func (l *AllowedLevel) UnmarshalYAML(unmarshal func(interface{}) error) error { + var s string + type plain string + if err := unmarshal((*plain)(&s)); err != nil { + return err + } + if s == "" { + return nil + } + lo := &AllowedLevel{} + if err := lo.Set(s); err != nil { + return err + } + *l = *lo + return nil +} + +func (l *AllowedLevel) String() string { + return l.s +} + +// Set updates the value of the allowed level. +func (l *AllowedLevel) Set(s string) error { + if l.lvl == nil { + l.lvl = &slog.LevelVar{} + } + + switch strings.ToLower(s) { + case "debug": + l.lvl.Set(slog.LevelDebug) + callerAddFunc = true + case "info": + l.lvl.Set(slog.LevelInfo) + callerAddFunc = false + case "warn": + l.lvl.Set(slog.LevelWarn) + callerAddFunc = false + case "error": + l.lvl.Set(slog.LevelError) + callerAddFunc = false + default: + return fmt.Errorf("unrecognized log level %s", s) + } + l.s = s + return nil +} + +// AllowedFormat is a settable identifier for the output format that the logger can have. +type AllowedFormat struct { + s string +} + +func (f *AllowedFormat) String() string { + return f.s +} + +// Set updates the value of the allowed format. +func (f *AllowedFormat) Set(s string) error { + switch s { + case "logfmt", "json": + f.s = s + default: + return fmt.Errorf("unrecognized log format %s", s) + } + return nil +} + +// Config is a struct containing configurable settings for the logger +type Config struct { + Level *AllowedLevel + Format *AllowedFormat + Style LogStyle + Writer io.Writer +} + +// New returns a new slog.Logger. Each logged line will be annotated +// with a timestamp. The output always goes to stderr. +func New(config *Config) *slog.Logger { + if config.Level == nil { + config.Level = &AllowedLevel{} + _ = config.Level.Set("info") + } + + if config.Writer == nil { + config.Writer = defaultWriter + } + + logHandlerOpts := &slog.HandlerOptions{ + Level: config.Level.lvl, + AddSource: true, + ReplaceAttr: defaultReplaceAttrFunc, + } + + if config.Style == GoKitStyle { + logHandlerOpts.ReplaceAttr = goKitStyleReplaceAttrFunc + } + + if config.Format != nil && config.Format.s == "json" { + return slog.New(slog.NewJSONHandler(config.Writer, logHandlerOpts)) + } + return slog.New(slog.NewTextHandler(config.Writer, logHandlerOpts)) +} + +// NewNopLogger is a convenience function to return an slog.Logger that writes +// to io.Discard. +func NewNopLogger() *slog.Logger { + return slog.New(slog.NewTextHandler(io.Discard, nil)) +} diff --git a/vendor/github.com/prometheus/exporter-toolkit/web/handler.go b/vendor/github.com/prometheus/exporter-toolkit/web/handler.go index c607a163a3..51da762c95 100644 --- a/vendor/github.com/prometheus/exporter-toolkit/web/handler.go +++ b/vendor/github.com/prometheus/exporter-toolkit/web/handler.go @@ -18,11 +18,11 @@ package web import ( "encoding/hex" "fmt" + "log/slog" "net/http" "strings" "sync" - "github.com/go-kit/log" "golang.org/x/crypto/bcrypt" ) @@ -78,7 +78,7 @@ HeadersLoop: type webHandler struct { tlsConfigPath string handler http.Handler - logger log.Logger + logger *slog.Logger cache *cache // bcryptMtx is there to ensure that bcrypt.CompareHashAndPassword is run // only once in parallel as this is CPU intensive. @@ -88,7 +88,7 @@ type webHandler struct { func (u *webHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { c, err := getConfig(u.tlsConfigPath) if err != nil { - u.logger.Log("msg", "Unable to parse configuration", "err", err) + u.logger.Error("Unable to parse configuration", "err", err.Error()) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } diff --git a/vendor/github.com/prometheus/exporter-toolkit/web/landing_page.go b/vendor/github.com/prometheus/exporter-toolkit/web/landing_page.go index 68266213ed..d417c15e0d 100644 --- a/vendor/github.com/prometheus/exporter-toolkit/web/landing_page.go +++ b/vendor/github.com/prometheus/exporter-toolkit/web/landing_page.go @@ -106,6 +106,10 @@ func NewLandingPage(c LandingConfig) (*LandingPageHandler, error) { } func (h *LandingPageHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != "/" { + http.NotFound(w, r) + return + } w.Header().Add("Content-Type", "text/html; charset=UTF-8") w.Write(h.landingPage) } diff --git a/vendor/github.com/prometheus/exporter-toolkit/web/tls_config.go b/vendor/github.com/prometheus/exporter-toolkit/web/tls_config.go index b16ded1d65..0730a938fd 100644 --- a/vendor/github.com/prometheus/exporter-toolkit/web/tls_config.go +++ b/vendor/github.com/prometheus/exporter-toolkit/web/tls_config.go @@ -18,6 +18,7 @@ import ( "crypto/x509" "errors" "fmt" + "log/slog" "net" "net/http" "net/url" @@ -27,8 +28,6 @@ import ( "strings" "github.com/coreos/go-systemd/v22/activation" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/mdlayher/vsock" config_util "github.com/prometheus/common/config" "golang.org/x/sync/errgroup" @@ -267,7 +266,7 @@ func ConfigToTLSConfig(c *TLSConfig) (*tls.Config, error) { // ServeMultiple starts the server on the given listeners. The FlagConfig is // also passed on to Serve. -func ServeMultiple(listeners []net.Listener, server *http.Server, flags *FlagConfig, logger log.Logger) error { +func ServeMultiple(listeners []net.Listener, server *http.Server, flags *FlagConfig, logger *slog.Logger) error { errs := new(errgroup.Group) for _, l := range listeners { l := l @@ -284,13 +283,13 @@ func ServeMultiple(listeners []net.Listener, server *http.Server, flags *FlagCon // Or instead uses systemd socket activated listeners if WebSystemdSocket in the // FlagConfig is true. // The FlagConfig is also passed on to ServeMultiple. -func ListenAndServe(server *http.Server, flags *FlagConfig, logger log.Logger) error { +func ListenAndServe(server *http.Server, flags *FlagConfig, logger *slog.Logger) error { if flags.WebSystemdSocket == nil && (flags.WebListenAddresses == nil || len(*flags.WebListenAddresses) == 0) { return ErrNoListeners } if flags.WebSystemdSocket != nil && *flags.WebSystemdSocket { - level.Info(logger).Log("msg", "Listening on systemd activated listeners instead of port listeners.") + logger.Info("Listening on systemd activated listeners instead of port listeners.") listeners, err := activation.Listeners() if err != nil { return err @@ -344,11 +343,11 @@ func parseVsockPort(address string) (uint32, error) { // Server starts the server on the given listener. Based on the file path // WebConfigFile in the FlagConfig, TLS or basic auth could be enabled. -func Serve(l net.Listener, server *http.Server, flags *FlagConfig, logger log.Logger) error { - level.Info(logger).Log("msg", "Listening on", "address", l.Addr().String()) +func Serve(l net.Listener, server *http.Server, flags *FlagConfig, logger *slog.Logger) error { + logger.Info("Listening on", "address", l.Addr().String()) tlsConfigPath := *flags.WebConfigFile if tlsConfigPath == "" { - level.Info(logger).Log("msg", "TLS is disabled.", "http2", false, "address", l.Addr().String()) + logger.Info("TLS is disabled.", "http2", false, "address", l.Addr().String()) return server.Serve(l) } @@ -381,10 +380,10 @@ func Serve(l net.Listener, server *http.Server, flags *FlagConfig, logger log.Lo server.TLSNextProto = make(map[string]func(*http.Server, *tls.Conn, http.Handler)) } // Valid TLS config. - level.Info(logger).Log("msg", "TLS is enabled.", "http2", c.HTTPConfig.HTTP2, "address", l.Addr().String()) + logger.Info("TLS is enabled.", "http2", c.HTTPConfig.HTTP2, "address", l.Addr().String()) case errNoTLSConfig: // No TLS config, back to plain HTTP. - level.Info(logger).Log("msg", "TLS is disabled.", "http2", false, "address", l.Addr().String()) + logger.Info("TLS is disabled.", "http2", false, "address", l.Addr().String()) return server.Serve(l) default: // Invalid TLS config. @@ -512,6 +511,6 @@ func (tv *TLSVersion) MarshalYAML() (interface{}, error) { // tlsConfigPath, TLS or basic auth could be enabled. // // Deprecated: Use ListenAndServe instead. -func Listen(server *http.Server, flags *FlagConfig, logger log.Logger) error { +func Listen(server *http.Server, flags *FlagConfig, logger *slog.Logger) error { return ListenAndServe(server, flags, logger) } diff --git a/vendor/github.com/rs/cors/cors.go b/vendor/github.com/rs/cors/cors.go index da80d343b3..724f242ac6 100644 --- a/vendor/github.com/rs/cors/cors.go +++ b/vendor/github.com/rs/cors/cors.go @@ -364,9 +364,11 @@ func (c *Cors) handlePreflight(w http.ResponseWriter, r *http.Request) { // Note: the Fetch standard guarantees that at most one // Access-Control-Request-Headers header is present in the preflight request; // see step 5.2 in https://fetch.spec.whatwg.org/#cors-preflight-fetch-0. - reqHeaders, found := first(r.Header, "Access-Control-Request-Headers") - if found && !c.allowedHeadersAll && !c.allowedHeaders.Subsumes(reqHeaders[0]) { - c.logf(" Preflight aborted: headers '%v' not allowed", reqHeaders[0]) + // However, some gateways split that header into multiple headers of the same name; + // see https://github.com/rs/cors/issues/184. + reqHeaders, found := r.Header["Access-Control-Request-Headers"] + if found && !c.allowedHeadersAll && !c.allowedHeaders.Accepts(reqHeaders) { + c.logf(" Preflight aborted: headers '%v' not allowed", reqHeaders) return } if c.allowedOriginsAll { @@ -391,9 +393,7 @@ func (c *Cors) handlePreflight(w http.ResponseWriter, r *http.Request) { if len(c.maxAge) > 0 { headers["Access-Control-Max-Age"] = c.maxAge } - if c.Log != nil { - c.logf(" Preflight response headers: %v", headers) - } + c.logf(" Preflight response headers: %v", headers) } // handleActualRequest handles simple cross-origin requests, actual request or redirects @@ -440,9 +440,7 @@ func (c *Cors) handleActualRequest(w http.ResponseWriter, r *http.Request) { if c.allowCredentials { headers["Access-Control-Allow-Credentials"] = headerTrue } - if c.Log != nil { - c.logf(" Actual response added headers: %v", headers) - } + c.logf(" Actual response added headers: %v", headers) } // convenience method. checks if a logger is set. diff --git a/vendor/github.com/rs/cors/internal/sortedset.go b/vendor/github.com/rs/cors/internal/sortedset.go index 513da20f7d..844f3f9e03 100644 --- a/vendor/github.com/rs/cors/internal/sortedset.go +++ b/vendor/github.com/rs/cors/internal/sortedset.go @@ -52,46 +52,134 @@ func (set SortedSet) String() string { return strings.Join(elems, ",") } -// Subsumes reports whether csv is a sequence of comma-separated names that are -// - all elements of set, -// - sorted in lexicographically order, +// Accepts reports whether values is a sequence of list-based field values +// whose elements are +// - all members of set, +// - sorted in lexicographical order, // - unique. -func (set SortedSet) Subsumes(csv string) bool { - if csv == "" { - return true +func (set SortedSet) Accepts(values []string) bool { + var ( // effectively constant + maxLen = maxOWSBytes + set.maxLen + maxOWSBytes + 1 // +1 for comma + ) + var ( + posOfLastNameSeen = -1 + name string + commaFound bool + emptyElements int + ok bool + ) + for _, s := range values { + for { + // As a defense against maliciously long names in s, + // we process only a small number of s's leading bytes per iteration. + name, s, commaFound = cutAtComma(s, maxLen) + name, ok = trimOWS(name, maxOWSBytes) + if !ok { + return false + } + if name == "" { + // RFC 9110 requires recipients to tolerate + // "a reasonable number of empty list elements"; see + // https://httpwg.org/specs/rfc9110.html#abnf.extension.recipient. + emptyElements++ + if emptyElements > maxEmptyElements { + return false + } + if !commaFound { // We have now exhausted the names in s. + break + } + continue + } + pos, ok := set.m[name] + if !ok { + return false + } + // The names in s are expected to be sorted in lexicographical order + // and to each appear at most once. + // Therefore, the positions (in set) of the names that + // appear in s should form a strictly increasing sequence. + // If that's not actually the case, bail out. + if pos <= posOfLastNameSeen { + return false + } + posOfLastNameSeen = pos + if !commaFound { // We have now exhausted the names in s. + break + } + } + } + return true +} + +const ( + maxOWSBytes = 1 // number of leading/trailing OWS bytes tolerated + maxEmptyElements = 16 // number of empty list elements tolerated +) + +func cutAtComma(s string, n int) (before, after string, found bool) { + // Note: this implementation draws inspiration from strings.Cut's. + end := min(len(s), n) + if i := strings.IndexByte(s[:end], ','); i >= 0 { + after = s[i+1:] // deal with this first to save one bounds check + return s[:i], after, true + } + return s, "", false +} + +// TrimOWS trims up to n bytes of [optional whitespace (OWS)] +// from the start of and/or the end of s. +// If no more than n bytes of OWS are found at the start of s +// and no more than n bytes of OWS are found at the end of s, +// it returns the trimmed result and true. +// Otherwise, it returns the original string and false. +// +// [optional whitespace (OWS)]: https://httpwg.org/specs/rfc9110.html#whitespace +func trimOWS(s string, n int) (trimmed string, ok bool) { + if s == "" { + return s, true + } + trimmed, ok = trimRightOWS(s, n) + if !ok { + return s, false } - posOfLastNameSeen := -1 - chunkSize := set.maxLen + 1 // (to accommodate for at least one comma) - for { - // As a defense against maliciously long names in csv, - // we only process at most chunkSize bytes per iteration. - end := min(len(csv), chunkSize) - comma := strings.IndexByte(csv[:end], ',') - var name string - if comma == -1 { - name = csv - } else { - name = csv[:comma] + trimmed, ok = trimLeftOWS(trimmed, n) + if !ok { + return s, false + } + return trimmed, true +} + +func trimLeftOWS(s string, n int) (string, bool) { + sCopy := s + var i int + for len(s) > 0 { + if i > n { + return sCopy, false } - pos, found := set.m[name] - if !found { - return false + if !(s[0] == ' ' || s[0] == '\t') { + break } - // The names in csv are expected to be sorted in lexicographical order - // and appear at most once in csv. - // Therefore, the positions (in set) of the names that - // appear in csv should form a strictly increasing sequence. - // If that's not actually the case, bail out. - if pos <= posOfLastNameSeen { - return false + s = s[1:] + i++ + } + return s, true +} + +func trimRightOWS(s string, n int) (string, bool) { + sCopy := s + var i int + for len(s) > 0 { + if i > n { + return sCopy, false } - posOfLastNameSeen = pos - if comma < 0 { // We've now processed all the names in csv. + last := len(s) - 1 + if !(s[last] == ' ' || s[last] == '\t') { break } - csv = csv[comma+1:] + s = s[:last] + i++ } - return true + return s, true } // TODO: when updating go directive to 1.21 or later, diff --git a/vendor/github.com/rs/cors/utils.go b/vendor/github.com/rs/cors/utils.go index 7019f45cd9..41b0c2836a 100644 --- a/vendor/github.com/rs/cors/utils.go +++ b/vendor/github.com/rs/cors/utils.go @@ -1,7 +1,6 @@ package cors import ( - "net/http" "strings" ) @@ -24,11 +23,3 @@ func convert(s []string, f func(string) string) []string { } return out } - -func first(hdrs http.Header, k string) ([]string, bool) { - v, found := hdrs[k] - if !found || len(v) == 0 { - return nil, false - } - return v[:1], true -} diff --git a/vendor/github.com/shurcooL/vfsgen/.travis.yml b/vendor/github.com/shurcooL/vfsgen/.travis.yml deleted file mode 100644 index 6452acb283..0000000000 --- a/vendor/github.com/shurcooL/vfsgen/.travis.yml +++ /dev/null @@ -1,16 +0,0 @@ -sudo: false -language: go -go: - - 1.x - - master -matrix: - allow_failures: - - go: master - fast_finish: true -install: - - # Do nothing. This is needed to prevent default install action "go get -t -v ./..." from happening here (we want it to happen inside script step). -script: - - go get -t -v ./... - - diff -n <(echo -n) <(gofmt -d -s .) - - go vet ./... - - go test -v -race ./... diff --git a/vendor/github.com/shurcooL/vfsgen/README.md b/vendor/github.com/shurcooL/vfsgen/README.md index 659a0a0344..bb4df2773f 100644 --- a/vendor/github.com/shurcooL/vfsgen/README.md +++ b/vendor/github.com/shurcooL/vfsgen/README.md @@ -1,7 +1,7 @@ vfsgen ====== -[![Build Status](https://travis-ci.org/shurcooL/vfsgen.svg?branch=master)](https://travis-ci.org/shurcooL/vfsgen) [![GoDoc](https://godoc.org/github.com/shurcooL/vfsgen?status.svg)](https://godoc.org/github.com/shurcooL/vfsgen) +[![Go Reference](https://pkg.go.dev/badge/github.com/shurcooL/vfsgen.svg)](https://pkg.go.dev/github.com/shurcooL/vfsgen) Package vfsgen takes an http.FileSystem (likely at `go generate` time) and generates Go code that statically implements the provided http.FileSystem. @@ -19,8 +19,8 @@ Features: Installation ------------ -```bash -go get -u github.com/shurcooL/vfsgen +```sh +go get github.com/shurcooL/vfsgen ``` Usage @@ -81,7 +81,7 @@ By using build tags, you can create a development mode where assets are loaded d For example, suppose your source filesystem is defined in a package with import path "example.com/project/data" as: ```Go -// +build dev +//go:build dev package data @@ -96,7 +96,7 @@ When built with the "dev" build tag, accessing `data.Assets` will read from disk A generate helper file assets_generate.go can be invoked via "//go:generate go run -tags=dev assets_generate.go" directive: ```Go -// +build ignore +//go:build ignore package main @@ -177,6 +177,7 @@ It strives to be the best in its class in terms of code quality and efficiency o ### Alternatives +- [`embed`](https://go.dev/pkg/embed) - Package embed provides access to files embedded in the running Go program. - [`go-bindata`](https://github.com/jteeuwen/go-bindata) - Reads from disk, generates Go code that provides access to data via a [custom API](https://github.com/jteeuwen/go-bindata#accessing-an-asset). - [`go-bindata-assetfs`](https://github.com/elazarl/go-bindata-assetfs) - Takes output of go-bindata and provides a wrapper that implements `http.FileSystem` interface (the same as what vfsgen outputs directly). - [`becky`](https://github.com/tv42/becky) - Embeds assets as string literals in Go source. @@ -195,6 +196,13 @@ Attribution This package was originally based on the excellent work by [@jteeuwen](https://github.com/jteeuwen) on [`go-bindata`](https://github.com/jteeuwen/go-bindata) and [@elazarl](https://github.com/elazarl) on [`go-bindata-assetfs`](https://github.com/elazarl/go-bindata-assetfs). +Directories +----------- + +| Path | Synopsis | +|------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------| +| [cmd/vfsgendev](https://pkg.go.dev/github.com/shurcooL/vfsgen/cmd/vfsgendev) | vfsgendev is a convenience tool for using vfsgen in a common development configuration. | + License ------- diff --git a/vendor/github.com/shurcooL/vfsgen/generator.go b/vendor/github.com/shurcooL/vfsgen/generator.go index c0067c5d36..99d910b7ea 100644 --- a/vendor/github.com/shurcooL/vfsgen/generator.go +++ b/vendor/github.com/shurcooL/vfsgen/generator.go @@ -5,7 +5,6 @@ import ( "compress/gzip" "errors" "io" - "io/ioutil" "net/http" "os" pathpkg "path" @@ -47,7 +46,7 @@ func Generate(input http.FileSystem, opt Options) error { } // Write output file (all at once). - err = ioutil.WriteFile(opt.Filename, buf.Bytes(), 0644) + err = os.WriteFile(opt.Filename, buf.Bytes(), 0644) return err } @@ -217,7 +216,7 @@ var t = template.Must(template.New("").Funcs(template.FuncMap{ }, }).Parse(`{{define "Header"}}// Code generated by vfsgen; DO NOT EDIT. -{{with .BuildTags}}// +build {{.}} +{{with .BuildTags}}//go:build {{.}} {{end}}package {{.PackageName}} @@ -226,7 +225,6 @@ import ( "compress/gzip" "fmt" "io" - "io/ioutil" "net/http" "os" pathpkg "path" @@ -358,7 +356,7 @@ func (f *vfsgen۰CompressedFile) Read(p []byte) (n int, err error) { } if f.grPos < f.seekPos { // Fast-forward. - _, err = io.CopyN(ioutil.Discard, f.gr, f.seekPos-f.grPos) + _, err = io.CopyN(io.Discard, f.gr, f.seekPos-f.grPos) if err != nil { return 0, err } @@ -386,9 +384,8 @@ func (f *vfsgen۰CompressedFile) Close() error { return f.gr.Close() } {{else}} -// We already imported "compress/gzip" and "io/ioutil", but ended up not using them. Avoid unused import error. -var _ = gzip.Reader{} -var _ = ioutil.Discard +// We already imported "compress/gzip" but ended up not using it. Avoid unused import error. +var _ *gzip.Reader {{end}}{{if .HasFile}} // vfsgen۰FileInfo is a static definition of an uncompressed file (because it's not worth gzip compressing). type vfsgen۰FileInfo struct { diff --git a/vendor/github.com/trivago/tgo/LICENSE b/vendor/github.com/trivago/tgo/LICENSE new file mode 100644 index 0000000000..8f71f43fee --- /dev/null +++ b/vendor/github.com/trivago/tgo/LICENSE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/vendor/github.com/trivago/tgo/tcontainer/arrays.go b/vendor/github.com/trivago/tgo/tcontainer/arrays.go new file mode 100644 index 0000000000..ede9aed3ed --- /dev/null +++ b/vendor/github.com/trivago/tgo/tcontainer/arrays.go @@ -0,0 +1,113 @@ +// Copyright 2015-2018 trivago N.V. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package tcontainer + +import "sort" + +// Int64Slice is a typedef to allow sortable int64 slices +type Int64Slice []int64 + +func (s Int64Slice) Len() int { + return len(s) +} + +func (s Int64Slice) Less(i, j int) bool { + return s[i] < s[j] +} + +func (s Int64Slice) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} + +// Sort is a shortcut for sort.Sort(s) +func (s Int64Slice) Sort() { + sort.Sort(s) +} + +// IsSorted is a shortcut for sort.IsSorted(s) +func (s Int64Slice) IsSorted() bool { + return sort.IsSorted(s) +} + +// Set sets all values in this slice to the given value +func (s Int64Slice) Set(v int64) { + for i := range s { + s[i] = v + } +} + +// Uint64Slice is a typedef to allow sortable uint64 slices +type Uint64Slice []uint64 + +func (s Uint64Slice) Len() int { + return len(s) +} + +func (s Uint64Slice) Less(i, j int) bool { + return s[i] < s[j] +} + +func (s Uint64Slice) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} + +// Sort is a shortcut for sort.Sort(s) +func (s Uint64Slice) Sort() { + sort.Sort(s) +} + +// IsSorted is a shortcut for sort.IsSorted(s) +func (s Uint64Slice) IsSorted() bool { + return sort.IsSorted(s) +} + +// Set sets all values in this slice to the given value +func (s Uint64Slice) Set(v uint64) { + for i := range s { + s[i] = v + } +} + +// Float32Slice is a typedef to allow sortable float32 slices +type Float32Slice []float32 + +func (s Float32Slice) Len() int { + return len(s) +} + +func (s Float32Slice) Less(i, j int) bool { + return s[i] < s[j] +} + +func (s Float32Slice) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} + +// Sort is a shortcut for sort.Sort(s) +func (s Float32Slice) Sort() { + sort.Sort(s) +} + +// IsSorted is a shortcut for sort.IsSorted(s) +func (s Float32Slice) IsSorted() bool { + return sort.IsSorted(s) +} + +// Set sets all values in this slice to the given value +func (s Float32Slice) Set(v float32) { + for i := range s { + s[i] = v + } +} diff --git a/vendor/github.com/trivago/tgo/tcontainer/bytepool.go b/vendor/github.com/trivago/tgo/tcontainer/bytepool.go new file mode 100644 index 0000000000..c1290b766d --- /dev/null +++ b/vendor/github.com/trivago/tgo/tcontainer/bytepool.go @@ -0,0 +1,157 @@ +// Copyright 2015-2018 trivago N.V. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package tcontainer + +import ( + "reflect" + "runtime" + "sync/atomic" + "unsafe" +) + +const ( + tiny = 64 + small = 512 + medium = 1024 + large = 1024 * 10 + huge = 1024 * 100 + + tinyCount = 16384 // 1 MB + smallCount = 2048 // 1 MB + mediumCount = 1024 // 1 MB + largeCount = 102 // ~1 MB + hugeCount = 10 // ~1 MB +) + +type byteSlab struct { + buffer []byte + bufferSize uintptr + stride uintptr + basePtr *uintptr + nextPtr *uintptr +} + +// BytePool is a fragmentation friendly way to allocated byte slices. +type BytePool struct { + tinySlab byteSlab + smallSlab byteSlab + mediumSlab byteSlab + largeSlab byteSlab + hugeSlab byteSlab +} + +func newByteSlab(size, count int) byteSlab { + bufferSize := count * size + buffer := make([]byte, bufferSize) + basePtr := (*reflect.SliceHeader)(unsafe.Pointer(&buffer)).Data + nextPtr := basePtr + uintptr(bufferSize) + + return byteSlab{ + buffer: buffer, + bufferSize: uintptr(bufferSize), + stride: uintptr(size), + basePtr: &basePtr, + nextPtr: &nextPtr, + } +} + +func (slab *byteSlab) getSlice(size int) (chunk []byte) { + chunkHeader := (*reflect.SliceHeader)(unsafe.Pointer(&chunk)) + chunkHeader.Len = size + chunkHeader.Cap = int(slab.stride) + + for { + // WARNING: The following two lines are order sensitive + basePtr := atomic.LoadUintptr(slab.basePtr) + nextPtr := atomic.AddUintptr(slab.nextPtr, -slab.stride) + lastPtr := basePtr + slab.bufferSize + + switch { + case nextPtr < basePtr || nextPtr >= lastPtr: + // out of range either means alloc while realloc or race between + // base and next during realloc. In the latter case we lose a chunk. + runtime.Gosched() + + case nextPtr == basePtr: + // Last item: realloc + slab.buffer = make([]byte, slab.bufferSize) + dataPtr := (*reflect.SliceHeader)(unsafe.Pointer(&slab.buffer)).Data + + // WARNING: The following two lines are order sensitive + atomic.StoreUintptr(slab.nextPtr, dataPtr+slab.bufferSize) + atomic.StoreUintptr(slab.basePtr, dataPtr) + fallthrough + + default: + chunkHeader.Data = nextPtr + return + } + } +} + +// NewBytePool creates a new BytePool with each slab using 1 MB of storage. +// The pool contains 5 slabs of different sizes: 64B, 512B, 1KB, 10KB and 100KB. +// Allocations above 100KB will be allocated directly. +func NewBytePool() BytePool { + return BytePool{ + tinySlab: newByteSlab(tiny, tinyCount), + smallSlab: newByteSlab(small, smallCount), + mediumSlab: newByteSlab(medium, mediumCount), + largeSlab: newByteSlab(large, largeCount), + hugeSlab: newByteSlab(huge, hugeCount), + } +} + +// NewBytePoolWithSize creates a new BytePool with each slab size using n MB of +// storage. See NewBytePool() for slab size details. +func NewBytePoolWithSize(n int) BytePool { + if n <= 0 { + n = 1 + } + return BytePool{ + tinySlab: newByteSlab(tiny, tinyCount*n), + smallSlab: newByteSlab(small, smallCount*n), + mediumSlab: newByteSlab(medium, mediumCount*n), + largeSlab: newByteSlab(large, largeCount*n), + hugeSlab: newByteSlab(huge, hugeCount*n), + } +} + +// Get returns a slice allocated to a normalized size. +// Sizes are organized in evenly sized buckets so that fragmentation is kept low. +func (b *BytePool) Get(size int) []byte { + switch { + case size == 0: + return []byte{} + + case size <= tiny: + return b.tinySlab.getSlice(size) + + case size <= small: + return b.smallSlab.getSlice(size) + + case size <= medium: + return b.mediumSlab.getSlice(size) + + case size <= large: + return b.largeSlab.getSlice(size) + + case size <= huge: + return b.hugeSlab.getSlice(size) + + default: + return make([]byte, size) + } +} diff --git a/vendor/github.com/trivago/tgo/tcontainer/marshalmap.go b/vendor/github.com/trivago/tgo/tcontainer/marshalmap.go new file mode 100644 index 0000000000..7564f3b5f7 --- /dev/null +++ b/vendor/github.com/trivago/tgo/tcontainer/marshalmap.go @@ -0,0 +1,565 @@ +// Copyright 2015-2018 trivago N.V. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package tcontainer + +import ( + "fmt" + "reflect" + "strconv" + "strings" + "time" + + "github.com/trivago/tgo/treflect" +) + +// MarshalMap is a wrapper type to attach converter methods to maps normally +// returned by marshalling methods, i.e. key/value parsers. +// All methods that do a conversion will return an error if the value stored +// behind key is not of the expected type or if the key is not existing in the +// map. +type MarshalMap map[string]interface{} + +const ( + // MarshalMapSeparator defines the rune used for path separation + MarshalMapSeparator = '/' + // MarshalMapArrayBegin defines the rune starting array index notation + MarshalMapArrayBegin = '[' + // MarshalMapArrayEnd defines the rune ending array index notation + MarshalMapArrayEnd = ']' +) + +// NewMarshalMap creates a new marshal map (string -> interface{}) +func NewMarshalMap() MarshalMap { + return make(map[string]interface{}) +} + +// TryConvertToMarshalMap converts collections to MarshalMap if possible. +// This is a deep conversion, i.e. each element in the collection will be +// traversed. You can pass a formatKey function that will be applied to all +// string keys that are detected. +func TryConvertToMarshalMap(value interface{}, formatKey func(string) string) interface{} { + valueMeta := reflect.ValueOf(value) + switch valueMeta.Kind() { + default: + return value + + case reflect.Array, reflect.Slice: + arrayLen := valueMeta.Len() + converted := make([]interface{}, arrayLen) + for i := 0; i < arrayLen; i++ { + converted[i] = TryConvertToMarshalMap(valueMeta.Index(i).Interface(), formatKey) + } + return converted + + case reflect.Map: + converted := NewMarshalMap() + keys := valueMeta.MapKeys() + + for _, keyMeta := range keys { + strKey, isString := keyMeta.Interface().(string) + if !isString { + continue + } + if formatKey != nil { + strKey = formatKey(strKey) + } + val := valueMeta.MapIndex(keyMeta).Interface() + converted[strKey] = TryConvertToMarshalMap(val, formatKey) + } + return converted // ### return, converted MarshalMap ### + } +} + +// ConvertToMarshalMap tries to convert a compatible map type to a marshal map. +// Compatible types are map[interface{}]interface{}, map[string]interface{} and of +// course MarshalMap. The same rules as for ConvertValueToMarshalMap apply. +func ConvertToMarshalMap(value interface{}, formatKey func(string) string) (MarshalMap, error) { + converted := TryConvertToMarshalMap(value, formatKey) + if result, isMap := converted.(MarshalMap); isMap { + return result, nil + } + return nil, fmt.Errorf("Root value cannot be converted to MarshalMap") +} + +// Clone creates a copy of the given MarshalMap. +func (mmap MarshalMap) Clone() MarshalMap { + clone := cloneMap(reflect.ValueOf(mmap)) + return clone.Interface().(MarshalMap) +} + +func cloneMap(mapValue reflect.Value) reflect.Value { + clone := reflect.MakeMap(mapValue.Type()) + keys := mapValue.MapKeys() + + for _, k := range keys { + v := mapValue.MapIndex(k) + switch k.Kind() { + default: + clone.SetMapIndex(k, v) + + case reflect.Array, reflect.Slice: + if v.Type().Elem().Kind() == reflect.Map { + sliceCopy := reflect.MakeSlice(v.Type(), v.Len(), v.Len()) + for i := 0; i < v.Len(); i++ { + element := v.Index(i) + sliceCopy.Index(i).Set(cloneMap(element)) + } + } else { + sliceCopy := reflect.MakeSlice(v.Type(), 0, v.Len()) + reflect.Copy(sliceCopy, v) + clone.SetMapIndex(k, sliceCopy) + } + + case reflect.Map: + vClone := cloneMap(v) + clone.SetMapIndex(k, vClone) + } + } + + return clone +} + +// Bool returns a value at key that is expected to be a boolean +func (mmap MarshalMap) Bool(key string) (bool, error) { + val, exists := mmap.Value(key) + if !exists { + return false, fmt.Errorf(`"%s" is not set`, key) + } + + boolValue, isBool := val.(bool) + if !isBool { + return false, fmt.Errorf(`"%s" is expected to be a boolean`, key) + } + return boolValue, nil +} + +// Uint returns a value at key that is expected to be an uint64 or compatible +// integer value. +func (mmap MarshalMap) Uint(key string) (uint64, error) { + val, exists := mmap.Value(key) + if !exists { + return 0, fmt.Errorf(`"%s" is not set`, key) + } + + if intVal, isNumber := treflect.Uint64(val); isNumber { + return intVal, nil + } + + return 0, fmt.Errorf(`"%s" is expected to be an unsigned number type`, key) +} + +// Int returns a value at key that is expected to be an int64 or compatible +// integer value. +func (mmap MarshalMap) Int(key string) (int64, error) { + val, exists := mmap.Value(key) + if !exists { + return 0, fmt.Errorf(`"%s" is not set`, key) + } + + if intVal, isNumber := treflect.Int64(val); isNumber { + return intVal, nil + } + + return 0, fmt.Errorf(`"%s" is expected to be a signed number type`, key) +} + +// Float returns a value at key that is expected to be a float64 or compatible +// float value. +func (mmap MarshalMap) Float(key string) (float64, error) { + val, exists := mmap.Value(key) + if !exists { + return 0, fmt.Errorf(`"%s" is not set`, key) + } + + if floatVal, isNumber := treflect.Float64(val); isNumber { + return floatVal, nil + } + + return 0, fmt.Errorf(`"%s" is expected to be a signed number type`, key) +} + +// Duration returns a value at key that is expected to be a string +func (mmap MarshalMap) Duration(key string) (time.Duration, error) { + val, exists := mmap.Value(key) + if !exists { + return time.Duration(0), fmt.Errorf(`"%s" is not set`, key) + } + + switch val.(type) { + case time.Duration: + return val.(time.Duration), nil + case string: + return time.ParseDuration(val.(string)) + } + + return time.Duration(0), fmt.Errorf(`"%s" is expected to be a duration or string`, key) +} + +// String returns a value at key that is expected to be a string +func (mmap MarshalMap) String(key string) (string, error) { + val, exists := mmap.Value(key) + if !exists { + return "", fmt.Errorf(`"%s" is not set`, key) + } + + strValue, isString := val.(string) + if !isString { + return "", fmt.Errorf(`"%s" is expected to be a string`, key) + } + return strValue, nil +} + +// Bytes returns a value at key that is expected to be a []byte +func (mmap MarshalMap) Bytes(key string) ([]byte, error) { + val, exists := mmap.Value(key) + if !exists { + return []byte{}, fmt.Errorf(`"%s" is not set`, key) + } + + bytesValue, isBytes := val.([]byte) + if !isBytes { + return []byte{}, fmt.Errorf(`"%s" is expected to be a []byte`, key) + } + return bytesValue, nil +} + +// Slice is an alias for Array +func (mmap MarshalMap) Slice(key string) ([]interface{}, error) { + return mmap.Array(key) +} + +// Array returns a value at key that is expected to be a []interface{} +func (mmap MarshalMap) Array(key string) ([]interface{}, error) { + val, exists := mmap.Value(key) + if !exists { + return nil, fmt.Errorf(`"%s" is not set`, key) + } + + arrayValue, isArray := val.([]interface{}) + if !isArray { + return nil, fmt.Errorf(`"%s" is expected to be an array`, key) + } + return arrayValue, nil +} + +// Map returns a value at key that is expected to be a +// map[interface{}]interface{}. +func (mmap MarshalMap) Map(key string) (map[interface{}]interface{}, error) { + val, exists := mmap.Value(key) + if !exists { + return nil, fmt.Errorf(`"%s" is not set`, key) + } + + mapValue, isMap := val.(map[interface{}]interface{}) + if !isMap { + return nil, fmt.Errorf(`"%s" is expected to be a map`, key) + } + return mapValue, nil +} + +func castToStringArray(key string, value interface{}) ([]string, error) { + switch value.(type) { + case string: + return []string{value.(string)}, nil + + case []interface{}: + arrayVal := value.([]interface{}) + stringArray := make([]string, 0, len(arrayVal)) + + for _, val := range arrayVal { + strValue, isString := val.(string) + if !isString { + return nil, fmt.Errorf(`"%s" does not contain string keys`, key) + } + stringArray = append(stringArray, strValue) + } + return stringArray, nil + + case []string: + return value.([]string), nil + + default: + return nil, fmt.Errorf(`"%s" is not a valid string array type`, key) + } +} + +// StringSlice is an alias for StringArray +func (mmap MarshalMap) StringSlice(key string) ([]string, error) { + return mmap.StringArray(key) +} + +// StringArray returns a value at key that is expected to be a []string +// This function supports conversion (by copy) from +// * []interface{} +func (mmap MarshalMap) StringArray(key string) ([]string, error) { + val, exists := mmap.Value(key) + if !exists { + return nil, fmt.Errorf(`"%s" is not set`, key) + } + + return castToStringArray(key, val) +} + +func castToInt64Array(key string, value interface{}) ([]int64, error) { + switch value.(type) { + case int: + return []int64{value.(int64)}, nil + + case []interface{}: + arrayVal := value.([]interface{}) + intArray := make([]int64, 0, len(arrayVal)) + + for _, val := range arrayVal { + intValue, isInt := val.(int64) + if !isInt { + return nil, fmt.Errorf(`"%s" does not contain int keys`, key) + } + intArray = append(intArray, intValue) + } + return intArray, nil + + case []int64: + return value.([]int64), nil + + default: + return nil, fmt.Errorf(`"%s" is not a valid string array type`, key) + } +} + +// Int64Slice is an alias for Int64Array +func (mmap MarshalMap) Int64Slice(key string) ([]int64, error) { + return mmap.Int64Array(key) +} + +// Int64Array returns a value at key that is expected to be a []int64 +// This function supports conversion (by copy) from +// * []interface{} +func (mmap MarshalMap) Int64Array(key string) ([]int64, error) { + val, exists := mmap.Value(key) + if !exists { + return nil, fmt.Errorf(`"%s" is not set`, key) + } + + return castToInt64Array(key, val) +} + +// StringMap returns a value at key that is expected to be a map[string]string. +// This function supports conversion (by copy) from +// * map[interface{}]interface{} +// * map[string]interface{} +func (mmap MarshalMap) StringMap(key string) (map[string]string, error) { + val, exists := mmap.Value(key) + if !exists { + return nil, fmt.Errorf(`"%s" is not set`, key) + } + + switch val.(type) { + case map[string]string: + return val.(map[string]string), nil + + default: + valueMeta := reflect.ValueOf(val) + if valueMeta.Kind() != reflect.Map { + return nil, fmt.Errorf(`"%s" is expected to be a map[string]string but is %T`, key, val) + } + + result := make(map[string]string) + for _, keyMeta := range valueMeta.MapKeys() { + strKey, isString := keyMeta.Interface().(string) + if !isString { + return nil, fmt.Errorf(`"%s" is expected to be a map[string]string. Key is not a string`, key) + } + + value := valueMeta.MapIndex(keyMeta) + strValue, isString := value.Interface().(string) + if !isString { + return nil, fmt.Errorf(`"%s" is expected to be a map[string]string. Value is not a string`, key) + } + + result[strKey] = strValue + } + + return result, nil + } +} + +// StringSliceMap is an alias for StringArrayMap +func (mmap MarshalMap) StringSliceMap(key string) (map[string][]string, error) { + return mmap.StringArrayMap(key) +} + +// StringArrayMap returns a value at key that is expected to be a +// map[string][]string. This function supports conversion (by copy) from +// * map[interface{}][]interface{} +// * map[interface{}]interface{} +// * map[string]interface{} +func (mmap MarshalMap) StringArrayMap(key string) (map[string][]string, error) { + val, exists := mmap.Value(key) + if !exists { + return nil, fmt.Errorf(`"%s" is not set`, key) + } + + switch val.(type) { + case map[string][]string: + return val.(map[string][]string), nil + + default: + valueMeta := reflect.ValueOf(val) + if valueMeta.Kind() != reflect.Map { + return nil, fmt.Errorf(`"%s" is expected to be a map[string][]string but is %T`, key, val) + } + + result := make(map[string][]string) + for _, keyMeta := range valueMeta.MapKeys() { + strKey, isString := keyMeta.Interface().(string) + if !isString { + return nil, fmt.Errorf(`"%s" is expected to be a map[string][]string. Key is not a string`, key) + } + + value := valueMeta.MapIndex(keyMeta) + arrayValue, err := castToStringArray(strKey, value.Interface()) + if err != nil { + return nil, fmt.Errorf(`"%s" is expected to be a map[string][]string. Value is not a []string`, key) + } + + result[strKey] = arrayValue + } + + return result, nil + } +} + +// MarshalMap returns a value at key that is expected to be another MarshalMap +// This function supports conversion (by copy) from +// * map[interface{}]interface{} +func (mmap MarshalMap) MarshalMap(key string) (MarshalMap, error) { + val, exists := mmap.Value(key) + if !exists { + return nil, fmt.Errorf(`"%s" is not set`, key) + } + + return ConvertToMarshalMap(val, nil) +} + +// Value returns a value from a given value path. +// Fields can be accessed by their name. Nested fields can be accessed by using +// "/" as a separator. Arrays can be addressed using the standard array +// notation "[]". +// Examples: +// "key" -> mmap["key"] single value +// "key1/key2" -> mmap["key1"]["key2"] nested map +// "key1[0]" -> mmap["key1"][0] nested array +// "key1[0]key2" -> mmap["key1"][0]["key2"] nested array, nested map +func (mmap MarshalMap) Value(key string) (val interface{}, exists bool) { + exists = mmap.resolvePath(key, mmap, func(p, k reflect.Value, v interface{}) { + val = v + }) + return val, exists +} + +// Delete a value from a given path. +// The path must point to a map key. Deleting from arrays is not supported. +func (mmap MarshalMap) Delete(key string) { + mmap.resolvePath(key, mmap, func(p, k reflect.Value, v interface{}) { + if v != nil { + p.SetMapIndex(k, reflect.Value{}) + } + }) +} + +// Set a value for a given path. +// The path must point to a map key. Setting array elements is not supported. +func (mmap MarshalMap) Set(key string, val interface{}) { + mmap.resolvePath(key, mmap, func(p, k reflect.Value, v interface{}) { + p.SetMapIndex(k, reflect.ValueOf(val)) + }) +} + +func (mmap MarshalMap) resolvePathKey(key string) (int, int) { + keyEnd := len(key) + nextKeyStart := keyEnd + pathIdx := strings.IndexRune(key, MarshalMapSeparator) + arrayIdx := strings.IndexRune(key, MarshalMapArrayBegin) + + if pathIdx > -1 && pathIdx < keyEnd { + keyEnd = pathIdx + nextKeyStart = pathIdx + 1 // don't include slash + } + if arrayIdx > -1 && arrayIdx < keyEnd { + keyEnd = arrayIdx + nextKeyStart = arrayIdx // include bracket because of multidimensional arrays + } + + // a -> key: "a", remain: "" -- value + // a/b/c -> key: "a", remain: "b/c" -- nested map + // a[1]b/c -> key: "a", remain: "[1]b/c" -- nested array + + return keyEnd, nextKeyStart +} + +func (mmap MarshalMap) resolvePath(k string, v interface{}, action func(p, k reflect.Value, v interface{})) bool { + if len(k) == 0 { + action(reflect.Value{}, reflect.ValueOf(k), v) // ### return, found requested value ### + return true + } + + vValue := reflect.ValueOf(v) + switch vValue.Kind() { + case reflect.Array, reflect.Slice: + startIdx := strings.IndexRune(k, MarshalMapArrayBegin) // Must be first char, otherwise malformed + endIdx := strings.IndexRune(k, MarshalMapArrayEnd) // Must be > startIdx, otherwise malformed + + if startIdx == -1 || endIdx == -1 { + return false + } + + if startIdx == 0 && endIdx > startIdx { + index, err := strconv.Atoi(k[startIdx+1 : endIdx]) + + // [1] -> index: "1", remain: "" -- value + // [1]a/b -> index: "1", remain: "a/b" -- nested map + // [1][2] -> index: "1", remain: "[2]" -- nested array + + if err == nil && index < vValue.Len() { + item := vValue.Index(index).Interface() + key := k[endIdx+1:] + return mmap.resolvePath(key, item, action) // ### return, nested array ### + } + } + + case reflect.Map: + kValue := reflect.ValueOf(k) + if storedValue := vValue.MapIndex(kValue); storedValue.IsValid() { + action(vValue, kValue, storedValue.Interface()) + return true + } + + keyEnd, nextKeyStart := mmap.resolvePathKey(k) + if keyEnd == len(k) { + action(vValue, kValue, nil) // call action to support setting non-existing keys + return false // ### return, key not found ### + } + + nextKey := k[:keyEnd] + nkValue := reflect.ValueOf(nextKey) + + if storedValue := vValue.MapIndex(nkValue); storedValue.IsValid() { + remain := k[nextKeyStart:] + return mmap.resolvePath(remain, storedValue.Interface(), action) // ### return, nested map ### + } + } + + return false +} diff --git a/vendor/github.com/trivago/tgo/tcontainer/trie.go b/vendor/github.com/trivago/tgo/tcontainer/trie.go new file mode 100644 index 0000000000..f72170937c --- /dev/null +++ b/vendor/github.com/trivago/tgo/tcontainer/trie.go @@ -0,0 +1,227 @@ +// Copyright 2015-2018 trivago N.V. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package tcontainer + +// TrieNode represents a single node inside a trie. +// Each node can contain a payload which can be retrieved after a successfull +// match. In addition to that PathLen will contain the length of the match. +type TrieNode struct { + suffix []byte + children []*TrieNode + longestPath int + PathLen int + Payload interface{} +} + +// NewTrie creates a new root TrieNode +func NewTrie(data []byte, payload interface{}) *TrieNode { + return &TrieNode{ + suffix: data, + children: []*TrieNode{}, + longestPath: len(data), + PathLen: len(data), + Payload: payload, + } +} + +func (node *TrieNode) addNewChild(data []byte, payload interface{}, pathLen int) { + if node.longestPath < pathLen { + node.longestPath = pathLen + } + + idx := len(node.children) + node.children = append(node.children, nil) + + for idx > 0 { + nextIdx := idx - 1 + if node.children[nextIdx].longestPath > pathLen { + break + } + node.children[idx] = node.children[nextIdx] + idx = nextIdx + } + + node.children[idx] = &TrieNode{ + suffix: data, + children: []*TrieNode{}, + longestPath: pathLen, + PathLen: pathLen, + Payload: payload, + } +} + +func (node *TrieNode) replace(oldChild *TrieNode, newChild *TrieNode) { + for i, child := range node.children { + if child == oldChild { + node.children[i] = newChild + return // ### return, replaced ### + } + } +} + +// ForEach applies a function to each node in the tree including and below the +// passed node. +func (node *TrieNode) ForEach(callback func(*TrieNode)) { + callback(node) + for _, child := range node.children { + child.ForEach(callback) + } +} + +// Add adds a new data path to the trie. +// The TrieNode returned is the (new) root node so you should always reassign +// the root with the return value of Add. +func (node *TrieNode) Add(data []byte, payload interface{}) *TrieNode { + return node.addPath(data, payload, len(data), nil) +} + +func (node *TrieNode) addPath(data []byte, payload interface{}, pathLen int, parent *TrieNode) *TrieNode { + dataLen := len(data) + suffixLen := len(node.suffix) + testLen := suffixLen + if dataLen < suffixLen { + testLen = dataLen + } + + var splitIdx int + for splitIdx = 0; splitIdx < testLen; splitIdx++ { + if data[splitIdx] != node.suffix[splitIdx] { + break // ### break, split found ### + } + } + + if splitIdx == suffixLen { + // Continue down or stop here (full suffix match) + + if splitIdx == dataLen { + node.Payload = payload // may overwrite + return node // ### return, path already stored ### + } + + data = data[splitIdx:] + if suffixLen > 0 { + for _, child := range node.children { + if child.suffix[0] == data[0] { + child.addPath(data, payload, pathLen, node) + return node // ### return, continue on path ### + } + } + } + + node.addNewChild(data, payload, pathLen) + return node // ### return, new leaf ### + } + + if splitIdx == dataLen { + // Make current node a subpath of new data node (full data match) + // This case implies that dataLen < suffixLen as splitIdx == suffixLen + // did not match. + + node.suffix = node.suffix[splitIdx:] + + newParent := NewTrie(data, payload) + newParent.PathLen = pathLen + newParent.longestPath = node.longestPath + newParent.children = []*TrieNode{node} + + if parent != nil { + parent.replace(node, newParent) + } + return newParent // ### return, rotation ### + } + + // New parent required with both nodes as children (partial match) + + node.suffix = node.suffix[splitIdx:] + + newParent := NewTrie(data[:splitIdx], nil) + newParent.PathLen = 0 + newParent.longestPath = node.longestPath + newParent.children = []*TrieNode{node} + newParent.addNewChild(data[splitIdx:], payload, pathLen) + + if parent != nil { + parent.replace(node, newParent) + } + return newParent // ### return, new parent ### +} + +// Match compares the trie to the given data stream. +// Match returns true if data can be completely matched to the trie. +func (node *TrieNode) Match(data []byte) *TrieNode { + dataLen := len(data) + suffixLen := len(node.suffix) + if dataLen < suffixLen { + return nil // ### return, cannot be fully matched ### + } + + for i := 0; i < suffixLen; i++ { + if data[i] != node.suffix[i] { + return nil // ### return, no match ### + } + } + + if dataLen == suffixLen { + if node.PathLen > 0 { + return node // ### return, full match ### + } + return nil // ### return, invalid match ### + } + + data = data[suffixLen:] + numChildren := len(node.children) + for i := 0; i < numChildren; i++ { + matchedNode := node.children[i].Match(data) + if matchedNode != nil { + return matchedNode // ### return, match found ### + } + } + + return nil // ### return, no valid path ### +} + +// MatchStart compares the trie to the beginning of the given data stream. +// MatchStart returns true if the beginning of data can be matched to the trie. +func (node *TrieNode) MatchStart(data []byte) *TrieNode { + dataLen := len(data) + suffixLen := len(node.suffix) + if dataLen < suffixLen { + return nil // ### return, cannot be fully matched ### + } + + for i := 0; i < suffixLen; i++ { + if data[i] != node.suffix[i] { + return nil // ### return, no match ### + } + } + + // Match longest path first + + data = data[suffixLen:] + numChildren := len(node.children) + for i := 0; i < numChildren; i++ { + matchedNode := node.children[i].MatchStart(data) + if matchedNode != nil { + return matchedNode // ### return, match found ### + } + } + + // May be only a part of data but we have a valid match + + if node.PathLen > 0 { + return node // ### return, full match ### + } + return nil // ### return, no valid path ### +} diff --git a/vendor/github.com/trivago/tgo/treflect/clone.go b/vendor/github.com/trivago/tgo/treflect/clone.go new file mode 100644 index 0000000000..b887126263 --- /dev/null +++ b/vendor/github.com/trivago/tgo/treflect/clone.go @@ -0,0 +1,80 @@ +// Copyright 2015-2018 trivago N.V. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package treflect + +import ( + "reflect" +) + +// Clone does a deep copy of the given value. +// Please note that field have to be public in order to be copied. +// Private fields will be ignored. +func Clone(v interface{}) interface{} { + value := reflect.ValueOf(v) + copy := clone(value) + return copy.Interface() +} + +func clone(v reflect.Value) reflect.Value { + switch v.Kind() { + case reflect.Struct: + copy := reflect.New(v.Type()) + + for i := 0; i < v.Type().NumField(); i++ { + field := v.Field(i) + targetField := copy.Elem().Field(i) + if !targetField.CanSet() { + continue // ignore private fields + } + fieldCopy := clone(field) + targetField.Set(fieldCopy) + } + return copy.Elem() + + case reflect.Chan: + copy := reflect.MakeChan(v.Type(), v.Len()) + return copy + + case reflect.Map: + copy := reflect.MakeMap(v.Type()) + keys := v.MapKeys() + for _, k := range keys { + fieldCopy := clone(v.MapIndex(k)) + copy.SetMapIndex(k, fieldCopy) + } + return copy + + case reflect.Slice: + copy := reflect.MakeSlice(v.Type(), v.Len(), v.Len()) + for i := 0; i < v.Len(); i++ { + elementCopy := clone(v.Index(i)) + copy.Index(i).Set(elementCopy) + } + return copy + + case reflect.Array: + copy := reflect.New(v.Type()).Elem() + for i := 0; i < v.Len(); i++ { + elementCopy := clone(v.Index(i)) + copy.Index(i).Set(elementCopy) + } + return copy + + default: + copy := reflect.New(v.Type()) + copy.Elem().Set(v) + return copy.Elem() + } +} diff --git a/vendor/github.com/trivago/tgo/treflect/reflection.go b/vendor/github.com/trivago/tgo/treflect/reflection.go new file mode 100644 index 0000000000..48a11f477e --- /dev/null +++ b/vendor/github.com/trivago/tgo/treflect/reflection.go @@ -0,0 +1,371 @@ +// Copyright 2015-2018 trivago N.V. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package treflect + +import ( + "fmt" + "reflect" + "unsafe" +) + +// GetMissingMethods checks if a given object implements all methods of a +// given interface. It returns the interface coverage [0..1] as well as an array +// of error messages. If the interface is correctly implemented the coverage is +// 1 and the error message array is empty. +func GetMissingMethods(objType reflect.Type, ifaceType reflect.Type) (float32, []string) { + missing := []string{} + if objType.Implements(ifaceType) { + return 1.0, missing + } + + methodCount := ifaceType.NumMethod() + for mIdx := 0; mIdx < methodCount; mIdx++ { + ifaceMethod := ifaceType.Method(mIdx) + objMethod, exists := objType.MethodByName(ifaceMethod.Name) + signatureMismatch := false + + switch { + case !exists: + missing = append(missing, fmt.Sprintf("Missing: \"%s\" %v", ifaceMethod.Name, ifaceMethod.Type)) + continue // ### continue, error found ### + + case ifaceMethod.Type.NumOut() != objMethod.Type.NumOut(): + signatureMismatch = true + + case ifaceMethod.Type.NumIn()+1 != objMethod.Type.NumIn(): + signatureMismatch = true + + default: + for oIdx := 0; !signatureMismatch && oIdx < ifaceMethod.Type.NumOut(); oIdx++ { + signatureMismatch = ifaceMethod.Type.Out(oIdx) != objMethod.Type.Out(oIdx) + } + for iIdx := 0; !signatureMismatch && iIdx < ifaceMethod.Type.NumIn(); iIdx++ { + signatureMismatch = ifaceMethod.Type.In(iIdx) != objMethod.Type.In(iIdx+1) + } + } + + if signatureMismatch { + missing = append(missing, fmt.Sprintf("Invalid: \"%s\" %v is not %v", ifaceMethod.Name, objMethod.Type, ifaceMethod.Type)) + } + } + + return float32(methodCount-len(missing)) / float32(methodCount), missing +} + +// Int64 converts any signed number type to an int64. +// The second parameter is returned as false if a non-number type was given. +func Int64(v interface{}) (int64, bool) { + + switch reflect.TypeOf(v).Kind() { + case reflect.Int: + return int64(v.(int)), true + case reflect.Int8: + return int64(v.(int8)), true + case reflect.Int16: + return int64(v.(int16)), true + case reflect.Int32: + return int64(v.(int32)), true + case reflect.Int64: + return v.(int64), true + case reflect.Float32: + return int64(v.(float32)), true + case reflect.Float64: + return int64(v.(float64)), true + } + + return 0, false +} + +// Uint64 converts any unsigned number type to an uint64. +// The second parameter is returned as false if a non-number type was given. +func Uint64(v interface{}) (uint64, bool) { + + switch reflect.TypeOf(v).Kind() { + case reflect.Uint: + return uint64(v.(uint)), true + case reflect.Uint8: + return uint64(v.(uint8)), true + case reflect.Uint16: + return uint64(v.(uint16)), true + case reflect.Uint32: + return uint64(v.(uint32)), true + case reflect.Uint64: + return v.(uint64), true + } + + return 0, false +} + +// Float32 converts any number type to an float32. +// The second parameter is returned as false if a non-number type was given. +func Float32(v interface{}) (float32, bool) { + + switch reflect.TypeOf(v).Kind() { + case reflect.Int: + return float32(v.(int)), true + case reflect.Uint: + return float32(v.(uint)), true + case reflect.Int8: + return float32(v.(int8)), true + case reflect.Uint8: + return float32(v.(uint8)), true + case reflect.Int16: + return float32(v.(int16)), true + case reflect.Uint16: + return float32(v.(uint16)), true + case reflect.Int32: + return float32(v.(int32)), true + case reflect.Uint32: + return float32(v.(uint32)), true + case reflect.Int64: + return float32(v.(int64)), true + case reflect.Uint64: + return float32(v.(uint64)), true + case reflect.Float32: + return v.(float32), true + case reflect.Float64: + return float32(v.(float64)), true + } + + return 0, false +} + +// Float64 converts any number type to an float64. +// The second parameter is returned as false if a non-number type was given. +func Float64(v interface{}) (float64, bool) { + + switch reflect.TypeOf(v).Kind() { + case reflect.Int: + return float64(v.(int)), true + case reflect.Uint: + return float64(v.(uint)), true + case reflect.Int8: + return float64(v.(int8)), true + case reflect.Uint8: + return float64(v.(uint8)), true + case reflect.Int16: + return float64(v.(int16)), true + case reflect.Uint16: + return float64(v.(uint16)), true + case reflect.Int32: + return float64(v.(int32)), true + case reflect.Uint32: + return float64(v.(uint32)), true + case reflect.Int64: + return float64(v.(int64)), true + case reflect.Uint64: + return float64(v.(uint64)), true + case reflect.Float32: + return float64(v.(float32)), true + case reflect.Float64: + return v.(float64), true + } + + return 0, false +} + +// RemovePtrFromType will return the type of t and strips away any pointer(s) +// in front of the actual type. +func RemovePtrFromType(t interface{}) reflect.Type { + var v reflect.Type + if rt, isType := t.(reflect.Type); isType { + v = rt + } else { + v = reflect.TypeOf(t) + } + for v.Kind() == reflect.Ptr { + v = v.Elem() + } + return v +} + +// RemovePtrFromValue will return the value of t and strips away any pointer(s) +// in front of the actual type. +func RemovePtrFromValue(t interface{}) reflect.Value { + var v reflect.Value + if rv, isValue := t.(reflect.Value); isValue { + v = rv + } else { + v = reflect.ValueOf(t) + } + for v.Type().Kind() == reflect.Ptr { + v = v.Elem() + } + return v +} + +// UnsafeCopy will copy data from src to dst while ignoring type information. +// Both types need to be of the same size and dst and src have to be pointers. +// UnsafeCopy will panic if these requirements are not met. +func UnsafeCopy(dst, src interface{}) { + dstValue := reflect.ValueOf(dst) + srcValue := reflect.ValueOf(src) + UnsafeCopyValue(dstValue, srcValue) +} + +// UnsafeCopyValue will copy data from src to dst while ignoring type +// information. Both types need to be of the same size or this function will +// panic. Also both types must support dereferencing via reflect.Elem() +func UnsafeCopyValue(dstValue reflect.Value, srcValue reflect.Value) { + dstType := dstValue.Elem().Type() + srcType := srcValue.Type() + + var srcPtr uintptr + if srcValue.Kind() != reflect.Ptr { + // If we don't get a pointer to our source data we need to forcefully + // retrieve it by accessing the interface pointer. This is ok as we + // only read from it. + iface := srcValue.Interface() + srcPtr = reflect.ValueOf(&iface).Elem().InterfaceData()[1] // Pointer to data + } else { + srcType = srcValue.Elem().Type() + srcPtr = srcValue.Pointer() + } + + if dstType.Size() != srcType.Size() { + panic("Type size mismatch between " + dstType.String() + " and " + srcType.String()) + } + + dstAsSlice := *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{ + Data: dstValue.Pointer(), + Len: int(dstType.Size()), + Cap: int(dstType.Size()), + })) + + srcAsSlice := *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{ + Data: srcPtr, + Len: int(srcType.Size()), + Cap: int(srcType.Size()), + })) + + copy(dstAsSlice, srcAsSlice) +} + +// SetMemberByName sets member name of the given pointer-to-struct to the data +// passed to this function. The member may be private, too. +func SetMemberByName(ptrToStruct interface{}, name string, data interface{}) { + structVal := reflect.Indirect(reflect.ValueOf(ptrToStruct)) + member := structVal.FieldByName(name) + + SetValue(member, data) +} + +// SetMemberByIndex sets member idx of the given pointer-to-struct to the data +// passed to this function. The member may be private, too. +func SetMemberByIndex(ptrToStruct interface{}, idx int, data interface{}) { + structVal := reflect.Indirect(reflect.ValueOf(ptrToStruct)) + member := structVal.Field(idx) + + SetValue(member, data) +} + +// SetValue sets an addressable value to the data passed to this function. +// In contrast to golangs reflect package this will also work with private +// variables. Please note that this function may not support all types, yet. +func SetValue(member reflect.Value, data interface{}) { + if member.CanSet() { + member.Set(reflect.ValueOf(data).Convert(member.Type())) + return // ### return, easy way ### + } + + if !member.CanAddr() { + panic("SetValue requires addressable member type") + } + + ptrToMember := unsafe.Pointer(member.UnsafeAddr()) + dataValue := reflect.ValueOf(data) + + switch member.Kind() { + case reflect.Bool: + *(*bool)(ptrToMember) = dataValue.Bool() + + case reflect.Uint: + *(*uint)(ptrToMember) = uint(dataValue.Uint()) + + case reflect.Uint8: + *(*uint8)(ptrToMember) = uint8(dataValue.Uint()) + + case reflect.Uint16: + *(*uint16)(ptrToMember) = uint16(dataValue.Uint()) + + case reflect.Uint32: + *(*uint32)(ptrToMember) = uint32(dataValue.Uint()) + + case reflect.Uint64: + *(*uint64)(ptrToMember) = dataValue.Uint() + + case reflect.Int: + *(*int)(ptrToMember) = int(dataValue.Int()) + + case reflect.Int8: + *(*int8)(ptrToMember) = int8(dataValue.Int()) + + case reflect.Int16: + *(*int16)(ptrToMember) = int16(dataValue.Int()) + + case reflect.Int32: + *(*int32)(ptrToMember) = int32(dataValue.Int()) + + case reflect.Int64: + *(*int64)(ptrToMember) = dataValue.Int() + + case reflect.Float32: + *(*float32)(ptrToMember) = float32(dataValue.Float()) + + case reflect.Float64: + *(*float64)(ptrToMember) = dataValue.Float() + + case reflect.Complex64: + *(*complex64)(ptrToMember) = complex64(dataValue.Complex()) + + case reflect.Complex128: + *(*complex128)(ptrToMember) = dataValue.Complex() + + case reflect.String: + *(*string)(ptrToMember) = dataValue.String() + + case reflect.Map, reflect.Chan: + // Exploit the fact that "map" is actually "*runtime.hmap" and force + // overwrite that pointer in the passed struct. + // Same foes for "chan" which is actually "*runtime.hchan". + + // Note: Assigning a map or channel to another variable does NOT copy + // the contents so copying the pointer follows go's standard behavior. + dataAsPtr := unsafe.Pointer(dataValue.Pointer()) + *(**uintptr)(ptrToMember) = (*uintptr)(dataAsPtr) + + case reflect.Interface: + // Interfaces are basically two pointers, see runtime.iface. + // We want to modify exactly that data, which is returned by + // the InterfaceData() method. + + if dataValue.Kind() != reflect.Interface { + // A type reference was passed. In order to overwrite the memory + // Representation of an interface we need to generate it first. + // Reflect does not allow us to do that unless we use the + // InterfaceData method which exposes the internal representation + // of an interface. + interfaceData := reflect.ValueOf(&data).Elem().InterfaceData() + dataValue = reflect.ValueOf(interfaceData) + } + fallthrough + + default: + // Complex types are assigned memcpy style. + // Note: This should not break the garbage collector although we cannot + // be 100% sure on this. + UnsafeCopyValue(member.Addr(), dataValue) + } +} diff --git a/vendor/github.com/trivago/tgo/treflect/typeregistry.go b/vendor/github.com/trivago/tgo/treflect/typeregistry.go new file mode 100644 index 0000000000..d3e3d7eff8 --- /dev/null +++ b/vendor/github.com/trivago/tgo/treflect/typeregistry.go @@ -0,0 +1,97 @@ +// Copyright 2015-2018 trivago N.V. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package treflect + +import ( + "fmt" + "reflect" + "strings" +) + +// TypeRegistry is a name to type registry used to create objects by name. +type TypeRegistry struct { + namedType map[string]reflect.Type +} + +// NewTypeRegistry creates a new TypeRegistry. Note that there is a global type +// registry available in the main tgo package (tgo.TypeRegistry). +func NewTypeRegistry() TypeRegistry { + return TypeRegistry{ + namedType: make(map[string]reflect.Type), + } +} + +// Register a plugin to the TypeRegistry by passing an uninitialized object. +func (registry TypeRegistry) Register(typeInstance interface{}) { + registry.RegisterWithDepth(typeInstance, 1) +} + +// RegisterWithDepth to register a plugin to the TypeRegistry by passing an uninitialized object. +func (registry TypeRegistry) RegisterWithDepth(typeInstance interface{}, depth int) { + structType := reflect.TypeOf(typeInstance) + packageName := structType.PkgPath() + typeName := structType.Name() + + pathTokens := strings.Split(packageName, "/") + maxDepth := 3 + if len(pathTokens) < maxDepth { + maxDepth = len(pathTokens) + } + + for n := depth; n <= maxDepth; n++ { + shortTypeName := strings.Join(pathTokens[len(pathTokens)-n:], ".") + "." + typeName + registry.namedType[shortTypeName] = structType + } +} + +// New creates an uninitialized object by class name. +// The class name has to be "package.class" or "package/subpackage.class". +// The gollum package is omitted from the package path. +func (registry TypeRegistry) New(typeName string) (interface{}, error) { + structType, exists := registry.namedType[typeName] + if exists { + return reflect.New(structType).Interface(), nil + } + return nil, fmt.Errorf("Unknown class: %s", typeName) +} + +// GetTypeOf returns only the type asscociated with the given name. +// If the name is not registered, nil is returned. +// The type returned will be a pointer type. +func (registry TypeRegistry) GetTypeOf(typeName string) reflect.Type { + if structType, exists := registry.namedType[typeName]; exists { + return reflect.PtrTo(structType) + } + return nil +} + +// IsTypeRegistered returns true if a type is registered to this registry. +// Note that GetTypeOf can do the same thing by checking for nil but also +// returns the type, so in many cases you will want to call this function. +func (registry TypeRegistry) IsTypeRegistered(typeName string) bool { + _, exists := registry.namedType[typeName] + return exists +} + +// GetRegistered returns the names of all registered types for a given package +func (registry TypeRegistry) GetRegistered(packageName string) []string { + var result []string + for key := range registry.namedType { + if strings.HasPrefix(key, packageName) { + result = append(result, key) + } + } + return result +} diff --git a/vendor/golang.org/x/tools/go/gcexportdata/gcexportdata.go b/vendor/golang.org/x/tools/go/gcexportdata/gcexportdata.go index 137cc8df1d..65fe2628e9 100644 --- a/vendor/golang.org/x/tools/go/gcexportdata/gcexportdata.go +++ b/vendor/golang.org/x/tools/go/gcexportdata/gcexportdata.go @@ -2,22 +2,64 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Package gcexportdata provides functions for locating, reading, and -// writing export data files containing type information produced by the -// gc compiler. This package supports go1.7 export data format and all -// later versions. -// -// Although it might seem convenient for this package to live alongside -// go/types in the standard library, this would cause version skew -// problems for developer tools that use it, since they must be able to -// consume the outputs of the gc compiler both before and after a Go -// update such as from Go 1.7 to Go 1.8. Because this package lives in -// golang.org/x/tools, sites can update their version of this repo some -// time before the Go 1.8 release and rebuild and redeploy their -// developer tools, which will then be able to consume both Go 1.7 and -// Go 1.8 export data files, so they will work before and after the -// Go update. (See discussion at https://golang.org/issue/15651.) -package gcexportdata // import "golang.org/x/tools/go/gcexportdata" +// Package gcexportdata provides functions for reading and writing +// export data, which is a serialized description of the API of a Go +// package including the names, kinds, types, and locations of all +// exported declarations. +// +// The standard Go compiler (cmd/compile) writes an export data file +// for each package it compiles, which it later reads when compiling +// packages that import the earlier one. The compiler must thus +// contain logic to both write and read export data. +// (See the "Export" section in the cmd/compile/README file.) +// +// The [Read] function in this package can read files produced by the +// compiler, producing [go/types] data structures. As a matter of +// policy, Read supports export data files produced by only the last +// two Go releases plus tip; see https://go.dev/issue/68898. The +// export data files produced by the compiler contain additional +// details related to generics, inlining, and other optimizations that +// cannot be decoded by the [Read] function. +// +// In files written by the compiler, the export data is not at the +// start of the file. Before calling Read, use [NewReader] to locate +// the desired portion of the file. +// +// The [Write] function in this package encodes the exported API of a +// Go package ([types.Package]) as a file. Such files can be later +// decoded by Read, but cannot be consumed by the compiler. +// +// # Future changes +// +// Although Read supports the formats written by both Write and the +// compiler, the two are quite different, and there is an open +// proposal (https://go.dev/issue/69491) to separate these APIs. +// +// Under that proposal, this package would ultimately provide only the +// Read operation for compiler export data, which must be defined in +// this module (golang.org/x/tools), not in the standard library, to +// avoid version skew for developer tools that need to read compiler +// export data both before and after a Go release, such as from Go +// 1.23 to Go 1.24. Because this package lives in the tools module, +// clients can update their version of the module some time before the +// Go 1.24 release and rebuild and redeploy their tools, which will +// then be able to consume both Go 1.23 and Go 1.24 export data files, +// so they will work before and after the Go update. (See discussion +// at https://go.dev/issue/15651.) +// +// The operations to import and export [go/types] data structures +// would be defined in the go/types package as Import and Export. +// [Write] would (eventually) delegate to Export, +// and [Read], when it detects a file produced by Export, +// would delegate to Import. +// +// # Deprecations +// +// The [NewImporter] and [Find] functions are deprecated and should +// not be used in new code. The [WriteBundle] and [ReadBundle] +// functions are experimental, and there is an open proposal to +// deprecate them (https://go.dev/issue/69573). +package gcexportdata import ( "bufio" @@ -64,24 +106,18 @@ func Find(importPath, srcDir string) (filename, path string) { // additional trailing data beyond the end of the export data. func NewReader(r io.Reader) (io.Reader, error) { buf := bufio.NewReader(r) - _, size, err := gcimporter.FindExportData(buf) + size, err := gcimporter.FindExportData(buf) if err != nil { return nil, err } - if size >= 0 { - // We were given an archive and found the __.PKGDEF in it. - // This tells us the size of the export data, and we don't - // need to return the entire file. - return &io.LimitedReader{ - R: buf, - N: size, - }, nil - } else { - // We were given an object file. As such, we don't know how large - // the export data is and must return the entire file. - return buf, nil - } + // We were given an archive and found the __.PKGDEF in it. + // This tells us the size of the export data, and we don't + // need to return the entire file. + return &io.LimitedReader{ + R: buf, + N: size, + }, nil } // readAll works the same way as io.ReadAll, but avoids allocations and copies @@ -100,6 +136,11 @@ func readAll(r io.Reader) ([]byte, error) { // Read reads export data from in, decodes it, and returns type // information for the package. // +// Read is capable of reading export data produced by [Write] at the +// same source code version, or by the last two Go releases (plus tip) +// of the standard Go compiler. Reading files from older compilers may +// produce an error. +// // The package path (effectively its linker symbol prefix) is // specified by path, since unlike the package name, this information // may not be recorded in the export data. @@ -128,14 +169,26 @@ func Read(in io.Reader, fset *token.FileSet, imports map[string]*types.Package, // (from "version"). Select appropriate importer. if len(data) > 0 { switch data[0] { - case 'v', 'c', 'd': // binary, till go1.10 + case 'v', 'c', 'd': + // binary, produced by cmd/compile till go1.10 return nil, fmt.Errorf("binary (%c) import format is no longer supported", data[0]) - case 'i': // indexed, till go1.19 + case 'i': + // indexed, produced by cmd/compile till go1.19, + // and also by [Write]. + // + // If proposal #69491 is accepted, go/types + // serialization will be implemented by + // types.Export, to which Write would eventually + // delegate (explicitly dropping any pretence at + // inter-version Write-Read compatibility). + // This [Read] function would delegate to types.Import + // when it detects that the file was produced by Export. _, pkg, err := gcimporter.IImportData(fset, imports, data[1:], path) return pkg, err - case 'u': // unified, from go1.20 + case 'u': + // unified, produced by cmd/compile since go1.20 _, pkg, err := gcimporter.UImportData(fset, imports, data[1:], path) return pkg, err diff --git a/vendor/golang.org/x/tools/go/packages/doc.go b/vendor/golang.org/x/tools/go/packages/doc.go index 3531ac8f5f..f1931d10ee 100644 --- a/vendor/golang.org/x/tools/go/packages/doc.go +++ b/vendor/golang.org/x/tools/go/packages/doc.go @@ -64,7 +64,7 @@ graph using the Imports fields. The Load function can be configured by passing a pointer to a Config as the first argument. A nil Config is equivalent to the zero Config, which -causes Load to run in LoadFiles mode, collecting minimal information. +causes Load to run in [LoadFiles] mode, collecting minimal information. See the documentation for type Config for details. As noted earlier, the Config.Mode controls the amount of detail @@ -72,14 +72,14 @@ reported about the loaded packages. See the documentation for type LoadMode for details. Most tools should pass their command-line arguments (after any flags) -uninterpreted to [Load], so that it can interpret them +uninterpreted to Load, so that it can interpret them according to the conventions of the underlying build system. See the Example function for typical usage. # The driver protocol -[Load] may be used to load Go packages even in Go projects that use +Load may be used to load Go packages even in Go projects that use alternative build systems, by installing an appropriate "driver" program for the build system and specifying its location in the GOPACKAGESDRIVER environment variable. @@ -97,6 +97,15 @@ JSON-encoded [DriverRequest] message providing additional information is written to the driver's standard input. The driver must write a JSON-encoded [DriverResponse] message to its standard output. (This message differs from the JSON schema produced by 'go list'.) + +The value of the PWD environment variable seen by the driver process +is the preferred name of its working directory. (The working directory +may have other aliases due to symbolic links; see the comment on the +Dir field of [exec.Cmd] for related information.) +When the driver process emits in its response the name of a file +that is a descendant of this directory, it must use an absolute path +that has the value of PWD as a prefix, to ensure that the returned +filenames satisfy the original query. */ package packages // import "golang.org/x/tools/go/packages" diff --git a/vendor/golang.org/x/tools/go/packages/external.go b/vendor/golang.org/x/tools/go/packages/external.go index c2b4b711b5..91bd62e83b 100644 --- a/vendor/golang.org/x/tools/go/packages/external.go +++ b/vendor/golang.org/x/tools/go/packages/external.go @@ -13,6 +13,7 @@ import ( "fmt" "os" "os/exec" + "slices" "strings" ) @@ -79,10 +80,10 @@ type DriverResponse struct { // driver is the type for functions that query the build system for the // packages named by the patterns. -type driver func(cfg *Config, patterns ...string) (*DriverResponse, error) +type driver func(cfg *Config, patterns []string) (*DriverResponse, error) // findExternalDriver returns the file path of a tool that supplies -// the build system package structure, or "" if not found." +// the build system package structure, or "" if not found. // If GOPACKAGESDRIVER is set in the environment findExternalTool returns its // value, otherwise it searches for a binary named gopackagesdriver on the PATH. func findExternalDriver(cfg *Config) driver { @@ -103,7 +104,7 @@ func findExternalDriver(cfg *Config) driver { return nil } } - return func(cfg *Config, words ...string) (*DriverResponse, error) { + return func(cfg *Config, patterns []string) (*DriverResponse, error) { req, err := json.Marshal(DriverRequest{ Mode: cfg.Mode, Env: cfg.Env, @@ -117,7 +118,7 @@ func findExternalDriver(cfg *Config) driver { buf := new(bytes.Buffer) stderr := new(bytes.Buffer) - cmd := exec.CommandContext(cfg.Context, tool, words...) + cmd := exec.CommandContext(cfg.Context, tool, patterns...) cmd.Dir = cfg.Dir // The cwd gets resolved to the real path. On Darwin, where // /tmp is a symlink, this breaks anything that expects the @@ -131,7 +132,7 @@ func findExternalDriver(cfg *Config) driver { // command. // // (See similar trick in Invocation.run in ../../internal/gocommand/invoke.go) - cmd.Env = append(slicesClip(cfg.Env), "PWD="+cfg.Dir) + cmd.Env = append(slices.Clip(cfg.Env), "PWD="+cfg.Dir) cmd.Stdin = bytes.NewReader(req) cmd.Stdout = buf cmd.Stderr = stderr @@ -150,7 +151,3 @@ func findExternalDriver(cfg *Config) driver { return &response, nil } } - -// slicesClip removes unused capacity from the slice, returning s[:len(s):len(s)]. -// TODO(adonovan): use go1.21 slices.Clip. -func slicesClip[S ~[]E, E any](s S) S { return s[:len(s):len(s)] } diff --git a/vendor/golang.org/x/tools/go/packages/golist.go b/vendor/golang.org/x/tools/go/packages/golist.go index 1a3a5b44f5..870271ed51 100644 --- a/vendor/golang.org/x/tools/go/packages/golist.go +++ b/vendor/golang.org/x/tools/go/packages/golist.go @@ -80,6 +80,12 @@ type golistState struct { cfg *Config ctx context.Context + runner *gocommand.Runner + + // overlay is the JSON file that encodes the Config.Overlay + // mapping, used by 'go list -overlay=...'. + overlay string + envOnce sync.Once goEnvError error goEnv map[string]string @@ -127,7 +133,10 @@ func (state *golistState) mustGetEnv() map[string]string { // goListDriver uses the go list command to interpret the patterns and produce // the build system package structure. // See driver for more details. -func goListDriver(cfg *Config, patterns ...string) (_ *DriverResponse, err error) { +// +// overlay is the JSON file that encodes the cfg.Overlay +// mapping, used by 'go list -overlay=...' +func goListDriver(cfg *Config, runner *gocommand.Runner, overlay string, patterns []string) (_ *DriverResponse, err error) { // Make sure that any asynchronous go commands are killed when we return. parentCtx := cfg.Context if parentCtx == nil { @@ -142,13 +151,15 @@ func goListDriver(cfg *Config, patterns ...string) (_ *DriverResponse, err error cfg: cfg, ctx: ctx, vendorDirs: map[string]bool{}, + overlay: overlay, + runner: runner, } // Fill in response.Sizes asynchronously if necessary. - if cfg.Mode&NeedTypesSizes != 0 || cfg.Mode&NeedTypes != 0 { + if cfg.Mode&NeedTypesSizes != 0 || cfg.Mode&(NeedTypes|NeedTypesInfo) != 0 { errCh := make(chan error) go func() { - compiler, arch, err := getSizesForArgs(ctx, state.cfgInvocation(), cfg.gocmdRunner) + compiler, arch, err := getSizesForArgs(ctx, state.cfgInvocation(), runner) response.dr.Compiler = compiler response.dr.Arch = arch errCh <- err @@ -494,13 +505,14 @@ func (state *golistState) createDriverResponse(words ...string) (*DriverResponse pkg := &Package{ Name: p.Name, ID: p.ImportPath, + Dir: p.Dir, GoFiles: absJoin(p.Dir, p.GoFiles, p.CgoFiles), CompiledGoFiles: absJoin(p.Dir, p.CompiledGoFiles), OtherFiles: absJoin(p.Dir, otherFiles(p)...), EmbedFiles: absJoin(p.Dir, p.EmbedFiles), EmbedPatterns: absJoin(p.Dir, p.EmbedPatterns), IgnoredFiles: absJoin(p.Dir, p.IgnoredGoFiles, p.IgnoredOtherFiles), - forTest: p.ForTest, + ForTest: p.ForTest, depsErrors: p.DepsErrors, Module: p.Module, } @@ -681,7 +693,7 @@ func (state *golistState) shouldAddFilenameFromError(p *jsonPackage) bool { // getGoVersion returns the effective minor version of the go command. func (state *golistState) getGoVersion() (int, error) { state.goVersionOnce.Do(func() { - state.goVersion, state.goVersionError = gocommand.GoVersion(state.ctx, state.cfgInvocation(), state.cfg.gocmdRunner) + state.goVersion, state.goVersionError = gocommand.GoVersion(state.ctx, state.cfgInvocation(), state.runner) }) return state.goVersion, state.goVersionError } @@ -751,7 +763,7 @@ func jsonFlag(cfg *Config, goVersion int) string { } } addFields("Name", "ImportPath", "Error") // These fields are always needed - if cfg.Mode&NeedFiles != 0 || cfg.Mode&NeedTypes != 0 { + if cfg.Mode&NeedFiles != 0 || cfg.Mode&(NeedTypes|NeedTypesInfo) != 0 { addFields("Dir", "GoFiles", "IgnoredGoFiles", "IgnoredOtherFiles", "CFiles", "CgoFiles", "CXXFiles", "MFiles", "HFiles", "FFiles", "SFiles", "SwigFiles", "SwigCXXFiles", "SysoFiles") @@ -759,7 +771,7 @@ func jsonFlag(cfg *Config, goVersion int) string { addFields("TestGoFiles", "XTestGoFiles") } } - if cfg.Mode&NeedTypes != 0 { + if cfg.Mode&(NeedTypes|NeedTypesInfo) != 0 { // CompiledGoFiles seems to be required for the test case TestCgoNoSyntax, // even when -compiled isn't passed in. // TODO(#52435): Should we make the test ask for -compiled, or automatically @@ -784,7 +796,7 @@ func jsonFlag(cfg *Config, goVersion int) string { // Request Dir in the unlikely case Export is not absolute. addFields("Dir", "Export") } - if cfg.Mode&needInternalForTest != 0 { + if cfg.Mode&NeedForTest != 0 { addFields("ForTest") } if cfg.Mode&needInternalDepsErrors != 0 { @@ -840,7 +852,7 @@ func (state *golistState) cfgInvocation() gocommand.Invocation { Env: cfg.Env, Logf: cfg.Logf, WorkingDir: cfg.Dir, - Overlay: cfg.goListOverlayFile, + Overlay: state.overlay, } } @@ -851,11 +863,8 @@ func (state *golistState) invokeGo(verb string, args ...string) (*bytes.Buffer, inv := state.cfgInvocation() inv.Verb = verb inv.Args = args - gocmdRunner := cfg.gocmdRunner - if gocmdRunner == nil { - gocmdRunner = &gocommand.Runner{} - } - stdout, stderr, friendlyErr, err := gocmdRunner.RunRaw(cfg.Context, inv) + + stdout, stderr, friendlyErr, err := state.runner.RunRaw(cfg.Context, inv) if err != nil { // Check for 'go' executable not being found. if ee, ok := err.(*exec.Error); ok && ee.Err == exec.ErrNotFound { @@ -879,6 +888,12 @@ func (state *golistState) invokeGo(verb string, args ...string) (*bytes.Buffer, return nil, friendlyErr } + // Return an error if 'go list' failed due to missing tools in + // $GOROOT/pkg/tool/$GOOS_$GOARCH (#69606). + if len(stderr.String()) > 0 && strings.Contains(stderr.String(), `go: no such tool`) { + return nil, friendlyErr + } + // Is there an error running the C compiler in cgo? This will be reported in the "Error" field // and should be suppressed by go list -e. // diff --git a/vendor/golang.org/x/tools/go/packages/loadmode_string.go b/vendor/golang.org/x/tools/go/packages/loadmode_string.go index 5c080d21b5..969da4c263 100644 --- a/vendor/golang.org/x/tools/go/packages/loadmode_string.go +++ b/vendor/golang.org/x/tools/go/packages/loadmode_string.go @@ -9,49 +9,47 @@ import ( "strings" ) -var allModes = []LoadMode{ - NeedName, - NeedFiles, - NeedCompiledGoFiles, - NeedImports, - NeedDeps, - NeedExportFile, - NeedTypes, - NeedSyntax, - NeedTypesInfo, - NeedTypesSizes, +var modes = [...]struct { + mode LoadMode + name string +}{ + {NeedName, "NeedName"}, + {NeedFiles, "NeedFiles"}, + {NeedCompiledGoFiles, "NeedCompiledGoFiles"}, + {NeedImports, "NeedImports"}, + {NeedDeps, "NeedDeps"}, + {NeedExportFile, "NeedExportFile"}, + {NeedTypes, "NeedTypes"}, + {NeedSyntax, "NeedSyntax"}, + {NeedTypesInfo, "NeedTypesInfo"}, + {NeedTypesSizes, "NeedTypesSizes"}, + {NeedForTest, "NeedForTest"}, + {NeedModule, "NeedModule"}, + {NeedEmbedFiles, "NeedEmbedFiles"}, + {NeedEmbedPatterns, "NeedEmbedPatterns"}, } -var modeStrings = []string{ - "NeedName", - "NeedFiles", - "NeedCompiledGoFiles", - "NeedImports", - "NeedDeps", - "NeedExportFile", - "NeedTypes", - "NeedSyntax", - "NeedTypesInfo", - "NeedTypesSizes", -} - -func (mod LoadMode) String() string { - m := mod - if m == 0 { +func (mode LoadMode) String() string { + if mode == 0 { return "LoadMode(0)" } var out []string - for i, x := range allModes { - if x > m { - break + // named bits + for _, item := range modes { + if (mode & item.mode) != 0 { + mode ^= item.mode + out = append(out, item.name) } - if (m & x) != 0 { - out = append(out, modeStrings[i]) - m = m ^ x + } + // unnamed residue + if mode != 0 { + if out == nil { + return fmt.Sprintf("LoadMode(%#x)", int(mode)) } + out = append(out, fmt.Sprintf("%#x", int(mode))) } - if m != 0 { - out = append(out, "Unknown") + if len(out) == 1 { + return out[0] } - return fmt.Sprintf("LoadMode(%s)", strings.Join(out, "|")) + return "(" + strings.Join(out, "|") + ")" } diff --git a/vendor/golang.org/x/tools/go/packages/packages.go b/vendor/golang.org/x/tools/go/packages/packages.go index 0b6bfaff80..9dedf9777d 100644 --- a/vendor/golang.org/x/tools/go/packages/packages.go +++ b/vendor/golang.org/x/tools/go/packages/packages.go @@ -16,13 +16,13 @@ import ( "go/scanner" "go/token" "go/types" - "io" "log" "os" "path/filepath" "runtime" "strings" "sync" + "sync/atomic" "time" "golang.org/x/sync/errgroup" @@ -31,7 +31,6 @@ import ( "golang.org/x/tools/internal/gocommand" "golang.org/x/tools/internal/packagesinternal" "golang.org/x/tools/internal/typesinternal" - "golang.org/x/tools/internal/versions" ) // A LoadMode controls the amount of detail to return when loading. @@ -44,19 +43,33 @@ import ( // ID and Errors (if present) will always be filled. // [Load] may return more information than requested. // +// The Mode flag is a union of several bits named NeedName, +// NeedFiles, and so on, each of which determines whether +// a given field of Package (Name, Files, etc) should be +// populated. +// +// For convenience, we provide named constants for the most +// common combinations of Need flags: +// +// [LoadFiles] lists of files in each package +// [LoadImports] ... plus imports +// [LoadTypes] ... plus type information +// [LoadSyntax] ... plus type-annotated syntax +// [LoadAllSyntax] ... for all dependencies +// // Unfortunately there are a number of open bugs related to // interactions among the LoadMode bits: -// - https://github.com/golang/go/issues/56633 -// - https://github.com/golang/go/issues/56677 -// - https://github.com/golang/go/issues/58726 -// - https://github.com/golang/go/issues/63517 +// - https://github.com/golang/go/issues/56633 +// - https://github.com/golang/go/issues/56677 +// - https://github.com/golang/go/issues/58726 +// - https://github.com/golang/go/issues/63517 type LoadMode int const ( // NeedName adds Name and PkgPath. NeedName LoadMode = 1 << iota - // NeedFiles adds GoFiles and OtherFiles. + // NeedFiles adds Dir, GoFiles, OtherFiles, and IgnoredFiles NeedFiles // NeedCompiledGoFiles adds CompiledGoFiles. @@ -78,7 +91,7 @@ const ( // NeedSyntax adds Syntax and Fset. NeedSyntax - // NeedTypesInfo adds TypesInfo. + // NeedTypesInfo adds TypesInfo and Fset. NeedTypesInfo // NeedTypesSizes adds TypesSizes. @@ -87,9 +100,10 @@ const ( // needInternalDepsErrors adds the internal deps errors field for use by gopls. needInternalDepsErrors - // needInternalForTest adds the internal forTest field. + // NeedForTest adds ForTest. + // // Tests must also be set on the context for this field to be populated. - needInternalForTest + NeedForTest // typecheckCgo enables full support for type checking cgo. Requires Go 1.15+. // Modifies CompiledGoFiles and Types, and has no effect on its own. @@ -103,27 +117,24 @@ const ( // NeedEmbedPatterns adds EmbedPatterns. NeedEmbedPatterns + + // Be sure to update loadmode_string.go when adding new items! ) const ( - // Deprecated: LoadFiles exists for historical compatibility - // and should not be used. Please directly specify the needed fields using the Need values. + // LoadFiles loads the name and file names for the initial packages. LoadFiles = NeedName | NeedFiles | NeedCompiledGoFiles - // Deprecated: LoadImports exists for historical compatibility - // and should not be used. Please directly specify the needed fields using the Need values. + // LoadImports loads the name, file names, and import mapping for the initial packages. LoadImports = LoadFiles | NeedImports - // Deprecated: LoadTypes exists for historical compatibility - // and should not be used. Please directly specify the needed fields using the Need values. + // LoadTypes loads exported type information for the initial packages. LoadTypes = LoadImports | NeedTypes | NeedTypesSizes - // Deprecated: LoadSyntax exists for historical compatibility - // and should not be used. Please directly specify the needed fields using the Need values. + // LoadSyntax loads typed syntax for the initial packages. LoadSyntax = LoadTypes | NeedSyntax | NeedTypesInfo - // Deprecated: LoadAllSyntax exists for historical compatibility - // and should not be used. Please directly specify the needed fields using the Need values. + // LoadAllSyntax loads typed syntax for the initial packages and all dependencies. LoadAllSyntax = LoadSyntax | NeedDeps // Deprecated: NeedExportsFile is a historical misspelling of NeedExportFile. @@ -133,13 +144,7 @@ const ( // A Config specifies details about how packages should be loaded. // The zero value is a valid configuration. // -// Calls to Load do not modify this struct. -// -// TODO(adonovan): #67702: this is currently false: in fact, -// calls to [Load] do not modify the public fields of this struct, but -// may modify hidden fields, so concurrent calls to [Load] must not -// use the same Config. But perhaps we should reestablish the -// documented invariant. +// Calls to [Load] do not modify this struct. type Config struct { // Mode controls the level of information returned for each package. Mode LoadMode @@ -170,19 +175,10 @@ type Config struct { // Env []string - // gocmdRunner guards go command calls from concurrency errors. - gocmdRunner *gocommand.Runner - // BuildFlags is a list of command-line flags to be passed through to // the build system's query tool. BuildFlags []string - // modFile will be used for -modfile in go command invocations. - modFile string - - // modFlag will be used for -modfile in go command invocations. - modFlag string - // Fset provides source position information for syntax trees and types. // If Fset is nil, Load will use a new fileset, but preserve Fset's value. Fset *token.FileSet @@ -229,21 +225,24 @@ type Config struct { // drivers may vary in their level of support for overlays. Overlay map[string][]byte - // goListOverlayFile is the JSON file that encodes the Overlay - // mapping, used by 'go list -overlay=...' - goListOverlayFile string + // -- Hidden configuration fields only for use in x/tools -- + + // modFile will be used for -modfile in go command invocations. + modFile string + + // modFlag will be used for -modfile in go command invocations. + modFlag string } // Load loads and returns the Go packages named by the given patterns. // -// Config specifies loading options; -// nil behaves the same as an empty Config. +// The cfg parameter specifies loading options; nil behaves the same as an empty [Config]. // // The [Config.Mode] field is a set of bits that determine what kinds // of information should be computed and returned. Modes that require // more information tend to be slower. See [LoadMode] for details // and important caveats. Its zero value is equivalent to -// NeedName | NeedFiles | NeedCompiledGoFiles. +// [NeedName] | [NeedFiles] | [NeedCompiledGoFiles]. // // Each call to Load returns a new set of [Package] instances. // The Packages and their Imports form a directed acyclic graph. @@ -260,7 +259,7 @@ type Config struct { // Errors associated with a particular package are recorded in the // corresponding Package's Errors list, and do not cause Load to // return an error. Clients may need to handle such errors before -// proceeding with further analysis. The PrintErrors function is +// proceeding with further analysis. The [PrintErrors] function is // provided for convenient display of all errors. func Load(cfg *Config, patterns ...string) ([]*Package, error) { ld := newLoader(cfg) @@ -323,21 +322,24 @@ func defaultDriver(cfg *Config, patterns ...string) (*DriverResponse, bool, erro } else if !response.NotHandled { return response, true, nil } - // (fall through) + // not handled: fall through } // go list fallback - // + // Write overlays once, as there are many calls // to 'go list' (one per chunk plus others too). - overlay, cleanupOverlay, err := gocommand.WriteOverlays(cfg.Overlay) + overlayFile, cleanupOverlay, err := gocommand.WriteOverlays(cfg.Overlay) if err != nil { return nil, false, err } defer cleanupOverlay() - cfg.goListOverlayFile = overlay - response, err := callDriverOnChunks(goListDriver, cfg, chunks) + var runner gocommand.Runner // (shared across many 'go list' calls) + driver := func(cfg *Config, patterns []string) (*DriverResponse, error) { + return goListDriver(cfg, &runner, overlayFile, patterns) + } + response, err := callDriverOnChunks(driver, cfg, chunks) if err != nil { return nil, false, err } @@ -375,16 +377,14 @@ func splitIntoChunks(patterns []string, argMax int) ([][]string, error) { func callDriverOnChunks(driver driver, cfg *Config, chunks [][]string) (*DriverResponse, error) { if len(chunks) == 0 { - return driver(cfg) + return driver(cfg, nil) } responses := make([]*DriverResponse, len(chunks)) errNotHandled := errors.New("driver returned NotHandled") var g errgroup.Group for i, chunk := range chunks { - i := i - chunk := chunk g.Go(func() (err error) { - responses[i], err = driver(cfg, chunk...) + responses[i], err = driver(cfg, chunk) if responses[i] != nil && responses[i].NotHandled { err = errNotHandled } @@ -434,6 +434,12 @@ type Package struct { // PkgPath is the package path as used by the go/types package. PkgPath string + // Dir is the directory associated with the package, if it exists. + // + // For packages listed by the go command, this is the directory containing + // the package files. + Dir string + // Errors contains any errors encountered querying the metadata // of the package, or while parsing or type-checking its files. Errors []Error @@ -521,8 +527,8 @@ type Package struct { // -- internal -- - // forTest is the package under test, if any. - forTest string + // ForTest is the package under test, if any. + ForTest string // depsErrors is the DepsErrors field from the go list response, if any. depsErrors []*packagesinternal.PackageError @@ -551,9 +557,6 @@ type ModuleError struct { } func init() { - packagesinternal.GetForTest = func(p interface{}) string { - return p.(*Package).forTest - } packagesinternal.GetDepsErrors = func(p interface{}) []*packagesinternal.PackageError { return p.(*Package).depsErrors } @@ -565,7 +568,6 @@ func init() { } packagesinternal.TypecheckCgo = int(typecheckCgo) packagesinternal.DepsErrors = int(needInternalDepsErrors) - packagesinternal.ForTest = int(needInternalForTest) } // An Error describes a problem with a package's metadata, syntax, or types. @@ -681,18 +683,19 @@ func (p *Package) String() string { return p.ID } // loaderPackage augments Package with state used during the loading phase type loaderPackage struct { *Package - importErrors map[string]error // maps each bad import to its error - loadOnce sync.Once - color uint8 // for cycle detection - needsrc bool // load from source (Mode >= LoadTypes) - needtypes bool // type information is either requested or depended on - initial bool // package was matched by a pattern - goVersion int // minor version number of go command on PATH + importErrors map[string]error // maps each bad import to its error + preds []*loaderPackage // packages that import this one + unfinishedSuccs atomic.Int32 // number of direct imports not yet loaded + color uint8 // for cycle detection + needsrc bool // load from source (Mode >= LoadTypes) + needtypes bool // type information is either requested or depended on + initial bool // package was matched by a pattern + goVersion int // minor version number of go command on PATH } // loader holds the working state of a single call to load. type loader struct { - pkgs map[string]*loaderPackage + pkgs map[string]*loaderPackage // keyed by Package.ID Config sizes types.Sizes // non-nil if needed by mode parseCache map[string]*parseValue @@ -738,9 +741,6 @@ func newLoader(cfg *Config) *loader { if ld.Config.Env == nil { ld.Config.Env = os.Environ() } - if ld.Config.gocmdRunner == nil { - ld.Config.gocmdRunner = &gocommand.Runner{} - } if ld.Context == nil { ld.Context = context.Background() } @@ -754,7 +754,7 @@ func newLoader(cfg *Config) *loader { ld.requestedMode = ld.Mode ld.Mode = impliedLoadMode(ld.Mode) - if ld.Mode&NeedTypes != 0 || ld.Mode&NeedSyntax != 0 { + if ld.Mode&(NeedSyntax|NeedTypes|NeedTypesInfo) != 0 { if ld.Fset == nil { ld.Fset = token.NewFileSet() } @@ -763,6 +763,7 @@ func newLoader(cfg *Config) *loader { // because we load source if export data is missing. if ld.ParseFile == nil { ld.ParseFile = func(fset *token.FileSet, filename string, src []byte) (*ast.File, error) { + // We implicitly promise to keep doing ast.Object resolution. :( const mode = parser.AllErrors | parser.ParseComments return parser.ParseFile(fset, filename, src, mode) } @@ -794,7 +795,7 @@ func (ld *loader) refine(response *DriverResponse) ([]*Package, error) { exportDataInvalid := len(ld.Overlay) > 0 || pkg.ExportFile == "" && pkg.PkgPath != "unsafe" // This package needs type information if the caller requested types and the package is // either a root, or it's a non-root and the user requested dependencies ... - needtypes := (ld.Mode&NeedTypes|NeedTypesInfo != 0 && (rootIndex >= 0 || ld.Mode&NeedDeps != 0)) + needtypes := (ld.Mode&(NeedTypes|NeedTypesInfo) != 0 && (rootIndex >= 0 || ld.Mode&NeedDeps != 0)) // This package needs source if the call requested source (or types info, which implies source) // and the package is either a root, or itas a non- root and the user requested dependencies... needsrc := ((ld.Mode&(NeedSyntax|NeedTypesInfo) != 0 && (rootIndex >= 0 || ld.Mode&NeedDeps != 0)) || @@ -819,9 +820,10 @@ func (ld *loader) refine(response *DriverResponse) ([]*Package, error) { } } - if ld.Mode&NeedImports != 0 { - // Materialize the import graph. - + // Materialize the import graph if it is needed (NeedImports), + // or if we'll be using loadPackages (Need{Syntax|Types|TypesInfo}). + var leaves []*loaderPackage // packages with no unfinished successors + if ld.Mode&(NeedImports|NeedSyntax|NeedTypes|NeedTypesInfo) != 0 { const ( white = 0 // new grey = 1 // in progress @@ -840,63 +842,76 @@ func (ld *loader) refine(response *DriverResponse) ([]*Package, error) { // dependency on a package that does. These are the only packages // for which we load source code. var stack []*loaderPackage - var visit func(lpkg *loaderPackage) bool - visit = func(lpkg *loaderPackage) bool { - switch lpkg.color { - case black: - return lpkg.needsrc - case grey: + var visit func(from, lpkg *loaderPackage) bool + visit = func(from, lpkg *loaderPackage) bool { + if lpkg.color == grey { panic("internal error: grey node") } - lpkg.color = grey - stack = append(stack, lpkg) // push - stubs := lpkg.Imports // the structure form has only stubs with the ID in the Imports - lpkg.Imports = make(map[string]*Package, len(stubs)) - for importPath, ipkg := range stubs { - var importErr error - imp := ld.pkgs[ipkg.ID] - if imp == nil { - // (includes package "C" when DisableCgo) - importErr = fmt.Errorf("missing package: %q", ipkg.ID) - } else if imp.color == grey { - importErr = fmt.Errorf("import cycle: %s", stack) + if lpkg.color == white { + lpkg.color = grey + stack = append(stack, lpkg) // push + stubs := lpkg.Imports // the structure form has only stubs with the ID in the Imports + lpkg.Imports = make(map[string]*Package, len(stubs)) + for importPath, ipkg := range stubs { + var importErr error + imp := ld.pkgs[ipkg.ID] + if imp == nil { + // (includes package "C" when DisableCgo) + importErr = fmt.Errorf("missing package: %q", ipkg.ID) + } else if imp.color == grey { + importErr = fmt.Errorf("import cycle: %s", stack) + } + if importErr != nil { + if lpkg.importErrors == nil { + lpkg.importErrors = make(map[string]error) + } + lpkg.importErrors[importPath] = importErr + continue + } + + if visit(lpkg, imp) { + lpkg.needsrc = true + } + lpkg.Imports[importPath] = imp.Package } - if importErr != nil { - if lpkg.importErrors == nil { - lpkg.importErrors = make(map[string]error) + + // -- postorder -- + + // Complete type information is required for the + // immediate dependencies of each source package. + if lpkg.needsrc && ld.Mode&NeedTypes != 0 { + for _, ipkg := range lpkg.Imports { + ld.pkgs[ipkg.ID].needtypes = true } - lpkg.importErrors[importPath] = importErr - continue } - if visit(imp) { - lpkg.needsrc = true + // NeedTypeSizes causes TypeSizes to be set even + // on packages for which types aren't needed. + if ld.Mode&NeedTypesSizes != 0 { + lpkg.TypesSizes = ld.sizes } - lpkg.Imports[importPath] = imp.Package - } - // Complete type information is required for the - // immediate dependencies of each source package. - if lpkg.needsrc && ld.Mode&NeedTypes != 0 { - for _, ipkg := range lpkg.Imports { - ld.pkgs[ipkg.ID].needtypes = true + // Add packages with no imports directly to the queue of leaves. + if len(lpkg.Imports) == 0 { + leaves = append(leaves, lpkg) } + + stack = stack[:len(stack)-1] // pop + lpkg.color = black } - // NeedTypeSizes causes TypeSizes to be set even - // on packages for which types aren't needed. - if ld.Mode&NeedTypesSizes != 0 { - lpkg.TypesSizes = ld.sizes + // Add edge from predecessor. + if from != nil { + from.unfinishedSuccs.Add(+1) // incref + lpkg.preds = append(lpkg.preds, from) } - stack = stack[:len(stack)-1] // pop - lpkg.color = black return lpkg.needsrc } // For each initial package, create its import DAG. for _, lpkg := range initial { - visit(lpkg) + visit(nil, lpkg) } } else { @@ -909,16 +924,45 @@ func (ld *loader) refine(response *DriverResponse) ([]*Package, error) { // Load type data and syntax if needed, starting at // the initial packages (roots of the import DAG). - if ld.Mode&NeedTypes != 0 || ld.Mode&NeedSyntax != 0 { - var wg sync.WaitGroup - for _, lpkg := range initial { - wg.Add(1) - go func(lpkg *loaderPackage) { - ld.loadRecursive(lpkg) - wg.Done() - }(lpkg) + if ld.Mode&(NeedSyntax|NeedTypes|NeedTypesInfo) != 0 { + + // We avoid using g.SetLimit to limit concurrency as + // it makes g.Go stop accepting work, which prevents + // workers from enqeuing, and thus finishing, and thus + // allowing the group to make progress: deadlock. + // + // Instead we use the ioLimit and cpuLimit semaphores. + g, _ := errgroup.WithContext(ld.Context) + + // enqueues adds a package to the type-checking queue. + // It must have no unfinished successors. + var enqueue func(*loaderPackage) + enqueue = func(lpkg *loaderPackage) { + g.Go(func() error { + // Parse and type-check. + ld.loadPackage(lpkg) + + // Notify each waiting predecessor, + // and enqueue it when it becomes a leaf. + for _, pred := range lpkg.preds { + if pred.unfinishedSuccs.Add(-1) == 0 { // decref + enqueue(pred) + } + } + + return nil + }) + } + + // Load leaves first, adding new packages + // to the queue as they become leaves. + for _, leaf := range leaves { + enqueue(leaf) + } + + if err := g.Wait(); err != nil { + return nil, err // cancelled } - wg.Wait() } // If the context is done, return its error and @@ -965,7 +1009,7 @@ func (ld *loader) refine(response *DriverResponse) ([]*Package, error) { if ld.requestedMode&NeedSyntax == 0 { ld.pkgs[i].Syntax = nil } - if ld.requestedMode&NeedTypes == 0 && ld.requestedMode&NeedSyntax == 0 { + if ld.requestedMode&(NeedSyntax|NeedTypes|NeedTypesInfo) == 0 { ld.pkgs[i].Fset = nil } if ld.requestedMode&NeedTypesInfo == 0 { @@ -982,31 +1026,10 @@ func (ld *loader) refine(response *DriverResponse) ([]*Package, error) { return result, nil } -// loadRecursive loads the specified package and its dependencies, -// recursively, in parallel, in topological order. -// It is atomic and idempotent. -// Precondition: ld.Mode&NeedTypes. -func (ld *loader) loadRecursive(lpkg *loaderPackage) { - lpkg.loadOnce.Do(func() { - // Load the direct dependencies, in parallel. - var wg sync.WaitGroup - for _, ipkg := range lpkg.Imports { - imp := ld.pkgs[ipkg.ID] - wg.Add(1) - go func(imp *loaderPackage) { - ld.loadRecursive(imp) - wg.Done() - }(imp) - } - wg.Wait() - ld.loadPackage(lpkg) - }) -} - -// loadPackage loads the specified package. +// loadPackage loads/parses/typechecks the specified package. // It must be called only once per Package, // after immediate dependencies are loaded. -// Precondition: ld.Mode & NeedTypes. +// Precondition: ld.Mode&(NeedSyntax|NeedTypes|NeedTypesInfo) != 0. func (ld *loader) loadPackage(lpkg *loaderPackage) { if lpkg.PkgPath == "unsafe" { // Fill in the blanks to avoid surprises. @@ -1042,6 +1065,10 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) { if !lpkg.needtypes && !lpkg.needsrc { return } + + // TODO(adonovan): this condition looks wrong: + // I think it should be lpkg.needtypes && !lpg.needsrc, + // so that NeedSyntax without NeedTypes can be satisfied by export data. if !lpkg.needsrc { if err := ld.loadFromExportData(lpkg); err != nil { lpkg.Errors = append(lpkg.Errors, Error{ @@ -1147,7 +1174,7 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) { } lpkg.Syntax = files - if ld.Config.Mode&NeedTypes == 0 { + if ld.Config.Mode&(NeedTypes|NeedTypesInfo) == 0 { return } @@ -1158,16 +1185,20 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) { return } - lpkg.TypesInfo = &types.Info{ - Types: make(map[ast.Expr]types.TypeAndValue), - Defs: make(map[*ast.Ident]types.Object), - Uses: make(map[*ast.Ident]types.Object), - Implicits: make(map[ast.Node]types.Object), - Instances: make(map[*ast.Ident]types.Instance), - Scopes: make(map[ast.Node]*types.Scope), - Selections: make(map[*ast.SelectorExpr]*types.Selection), + // Populate TypesInfo only if needed, as it + // causes the type checker to work much harder. + if ld.Config.Mode&NeedTypesInfo != 0 { + lpkg.TypesInfo = &types.Info{ + Types: make(map[ast.Expr]types.TypeAndValue), + Defs: make(map[*ast.Ident]types.Object), + Uses: make(map[*ast.Ident]types.Object), + Implicits: make(map[ast.Node]types.Object), + Instances: make(map[*ast.Ident]types.Instance), + Scopes: make(map[ast.Node]*types.Scope), + Selections: make(map[*ast.SelectorExpr]*types.Selection), + FileVersions: make(map[*ast.File]string), + } } - versions.InitFileVersions(lpkg.TypesInfo) lpkg.TypesSizes = ld.sizes importer := importerFunc(func(path string) (*types.Package, error) { @@ -1220,6 +1251,10 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) { } } + // Type-checking is CPU intensive. + cpuLimit <- unit{} // acquire a token + defer func() { <-cpuLimit }() // release a token + typErr := types.NewChecker(tc, ld.Fset, lpkg.Types, lpkg.TypesInfo).Files(lpkg.Syntax) lpkg.importErrors = nil // no longer needed @@ -1284,8 +1319,11 @@ type importerFunc func(path string) (*types.Package, error) func (f importerFunc) Import(path string) (*types.Package, error) { return f(path) } // We use a counting semaphore to limit -// the number of parallel I/O calls per process. -var ioLimit = make(chan bool, 20) +// the number of parallel I/O calls or CPU threads per process. +var ( + ioLimit = make(chan unit, 20) + cpuLimit = make(chan unit, runtime.GOMAXPROCS(0)) +) func (ld *loader) parseFile(filename string) (*ast.File, error) { ld.parseCacheMu.Lock() @@ -1302,20 +1340,28 @@ func (ld *loader) parseFile(filename string) (*ast.File, error) { var src []byte for f, contents := range ld.Config.Overlay { + // TODO(adonovan): Inefficient for large overlays. + // Do an exact name-based map lookup + // (for nonexistent files) followed by a + // FileID-based map lookup (for existing ones). if sameFile(f, filename) { src = contents + break } } var err error if src == nil { - ioLimit <- true // wait + ioLimit <- unit{} // acquire a token src, err = os.ReadFile(filename) - <-ioLimit // signal + <-ioLimit // release a token } if err != nil { v.err = err } else { + // Parsing is CPU intensive. + cpuLimit <- unit{} // acquire a token v.f, v.err = ld.ParseFile(ld.Fset, filename, src) + <-cpuLimit // release a token } close(v.ready) @@ -1330,18 +1376,21 @@ func (ld *loader) parseFile(filename string) (*ast.File, error) { // Because files are scanned in parallel, the token.Pos // positions of the resulting ast.Files are not ordered. func (ld *loader) parseFiles(filenames []string) ([]*ast.File, []error) { - var wg sync.WaitGroup - n := len(filenames) - parsed := make([]*ast.File, n) - errors := make([]error, n) - for i, file := range filenames { - wg.Add(1) - go func(i int, filename string) { + var ( + n = len(filenames) + parsed = make([]*ast.File, n) + errors = make([]error, n) + ) + var g errgroup.Group + for i, filename := range filenames { + // This creates goroutines unnecessarily in the + // cache-hit case, but that case is uncommon. + g.Go(func() error { parsed[i], errors[i] = ld.parseFile(filename) - wg.Done() - }(i, file) + return nil + }) } - wg.Wait() + g.Wait() // Eliminate nils, preserving order. var o int @@ -1512,4 +1561,4 @@ func usesExportData(cfg *Config) bool { return cfg.Mode&NeedExportFile != 0 || cfg.Mode&NeedTypes != 0 && cfg.Mode&NeedDeps == 0 } -var _ interface{} = io.Discard // assert build toolchain is go1.16 or later +type unit struct{} diff --git a/vendor/golang.org/x/tools/go/types/objectpath/objectpath.go b/vendor/golang.org/x/tools/go/types/objectpath/objectpath.go index 9ada177758..16ed3c1780 100644 --- a/vendor/golang.org/x/tools/go/types/objectpath/objectpath.go +++ b/vendor/golang.org/x/tools/go/types/objectpath/objectpath.go @@ -228,7 +228,7 @@ func (enc *Encoder) For(obj types.Object) (Path, error) { // Reject obviously non-viable cases. switch obj := obj.(type) { case *types.TypeName: - if _, ok := aliases.Unalias(obj.Type()).(*types.TypeParam); !ok { + if _, ok := types.Unalias(obj.Type()).(*types.TypeParam); !ok { // With the exception of type parameters, only package-level type names // have a path. return "", fmt.Errorf("no path for %v", obj) @@ -280,26 +280,26 @@ func (enc *Encoder) For(obj types.Object) (Path, error) { path = append(path, opType) T := o.Type() - if alias, ok := T.(*aliases.Alias); ok { - if r := findTypeParam(obj, aliases.TypeParams(alias), path, opTypeParam, nil); r != nil { + if alias, ok := T.(*types.Alias); ok { + if r := findTypeParam(obj, aliases.TypeParams(alias), path, opTypeParam); r != nil { return Path(r), nil } - if r := find(obj, aliases.Rhs(alias), append(path, opRhs), nil); r != nil { + if r := find(obj, aliases.Rhs(alias), append(path, opRhs)); r != nil { return Path(r), nil } } else if tname.IsAlias() { // legacy alias - if r := find(obj, T, path, nil); r != nil { + if r := find(obj, T, path); r != nil { return Path(r), nil } } else if named, ok := T.(*types.Named); ok { // defined (named) type - if r := findTypeParam(obj, named.TypeParams(), path, opTypeParam, nil); r != nil { + if r := findTypeParam(obj, named.TypeParams(), path, opTypeParam); r != nil { return Path(r), nil } - if r := find(obj, named.Underlying(), append(path, opUnderlying), nil); r != nil { + if r := find(obj, named.Underlying(), append(path, opUnderlying)); r != nil { return Path(r), nil } } @@ -312,7 +312,7 @@ func (enc *Encoder) For(obj types.Object) (Path, error) { if _, ok := o.(*types.TypeName); !ok { if o.Exported() { // exported non-type (const, var, func) - if r := find(obj, o.Type(), append(path, opType), nil); r != nil { + if r := find(obj, o.Type(), append(path, opType)); r != nil { return Path(r), nil } } @@ -320,7 +320,7 @@ func (enc *Encoder) For(obj types.Object) (Path, error) { } // Inspect declared methods of defined types. - if T, ok := aliases.Unalias(o.Type()).(*types.Named); ok { + if T, ok := types.Unalias(o.Type()).(*types.Named); ok { path = append(path, opType) // The method index here is always with respect // to the underlying go/types data structures, @@ -332,7 +332,7 @@ func (enc *Encoder) For(obj types.Object) (Path, error) { if m == obj { return Path(path2), nil // found declared method } - if r := find(obj, m.Type(), append(path2, opType), nil); r != nil { + if r := find(obj, m.Type(), append(path2, opType)); r != nil { return Path(r), nil } } @@ -447,46 +447,64 @@ func (enc *Encoder) concreteMethod(meth *types.Func) (Path, bool) { // // The seen map is used to short circuit cycles through type parameters. If // nil, it will be allocated as necessary. -func find(obj types.Object, T types.Type, path []byte, seen map[*types.TypeName]bool) []byte { +// +// The seenMethods map is used internally to short circuit cycles through +// interface methods, such as occur in the following example: +// +// type I interface { f() interface{I} } +// +// See golang/go#68046 for details. +func find(obj types.Object, T types.Type, path []byte) []byte { + return (&finder{obj: obj}).find(T, path) +} + +// finder closes over search state for a call to find. +type finder struct { + obj types.Object // the sought object + seenTParamNames map[*types.TypeName]bool // for cycle breaking through type parameters + seenMethods map[*types.Func]bool // for cycle breaking through recursive interfaces +} + +func (f *finder) find(T types.Type, path []byte) []byte { switch T := T.(type) { - case *aliases.Alias: - return find(obj, aliases.Unalias(T), path, seen) + case *types.Alias: + return f.find(types.Unalias(T), path) case *types.Basic, *types.Named: // Named types belonging to pkg were handled already, // so T must belong to another package. No path. return nil case *types.Pointer: - return find(obj, T.Elem(), append(path, opElem), seen) + return f.find(T.Elem(), append(path, opElem)) case *types.Slice: - return find(obj, T.Elem(), append(path, opElem), seen) + return f.find(T.Elem(), append(path, opElem)) case *types.Array: - return find(obj, T.Elem(), append(path, opElem), seen) + return f.find(T.Elem(), append(path, opElem)) case *types.Chan: - return find(obj, T.Elem(), append(path, opElem), seen) + return f.find(T.Elem(), append(path, opElem)) case *types.Map: - if r := find(obj, T.Key(), append(path, opKey), seen); r != nil { + if r := f.find(T.Key(), append(path, opKey)); r != nil { return r } - return find(obj, T.Elem(), append(path, opElem), seen) + return f.find(T.Elem(), append(path, opElem)) case *types.Signature: - if r := findTypeParam(obj, T.RecvTypeParams(), path, opRecvTypeParam, nil); r != nil { + if r := f.findTypeParam(T.RecvTypeParams(), path, opRecvTypeParam); r != nil { return r } - if r := findTypeParam(obj, T.TypeParams(), path, opTypeParam, seen); r != nil { + if r := f.findTypeParam(T.TypeParams(), path, opTypeParam); r != nil { return r } - if r := find(obj, T.Params(), append(path, opParams), seen); r != nil { + if r := f.find(T.Params(), append(path, opParams)); r != nil { return r } - return find(obj, T.Results(), append(path, opResults), seen) + return f.find(T.Results(), append(path, opResults)) case *types.Struct: for i := 0; i < T.NumFields(); i++ { fld := T.Field(i) path2 := appendOpArg(path, opField, i) - if fld == obj { + if fld == f.obj { return path2 // found field var } - if r := find(obj, fld.Type(), append(path2, opType), seen); r != nil { + if r := f.find(fld.Type(), append(path2, opType)); r != nil { return r } } @@ -495,10 +513,10 @@ func find(obj types.Object, T types.Type, path []byte, seen map[*types.TypeName] for i := 0; i < T.Len(); i++ { v := T.At(i) path2 := appendOpArg(path, opAt, i) - if v == obj { + if v == f.obj { return path2 // found param/result var } - if r := find(obj, v.Type(), append(path2, opType), seen); r != nil { + if r := f.find(v.Type(), append(path2, opType)); r != nil { return r } } @@ -506,28 +524,35 @@ func find(obj types.Object, T types.Type, path []byte, seen map[*types.TypeName] case *types.Interface: for i := 0; i < T.NumMethods(); i++ { m := T.Method(i) + if f.seenMethods[m] { + return nil + } path2 := appendOpArg(path, opMethod, i) - if m == obj { + if m == f.obj { return path2 // found interface method } - if r := find(obj, m.Type(), append(path2, opType), seen); r != nil { + if f.seenMethods == nil { + f.seenMethods = make(map[*types.Func]bool) + } + f.seenMethods[m] = true + if r := f.find(m.Type(), append(path2, opType)); r != nil { return r } } return nil case *types.TypeParam: name := T.Obj() - if name == obj { - return append(path, opObj) - } - if seen[name] { + if f.seenTParamNames[name] { return nil } - if seen == nil { - seen = make(map[*types.TypeName]bool) + if name == f.obj { + return append(path, opObj) } - seen[name] = true - if r := find(obj, T.Constraint(), append(path, opConstraint), seen); r != nil { + if f.seenTParamNames == nil { + f.seenTParamNames = make(map[*types.TypeName]bool) + } + f.seenTParamNames[name] = true + if r := f.find(T.Constraint(), append(path, opConstraint)); r != nil { return r } return nil @@ -535,11 +560,15 @@ func find(obj types.Object, T types.Type, path []byte, seen map[*types.TypeName] panic(T) } -func findTypeParam(obj types.Object, list *types.TypeParamList, path []byte, op byte, seen map[*types.TypeName]bool) []byte { +func findTypeParam(obj types.Object, list *types.TypeParamList, path []byte, op byte) []byte { + return (&finder{obj: obj}).findTypeParam(list, path, op) +} + +func (f *finder) findTypeParam(list *types.TypeParamList, path []byte, op byte) []byte { for i := 0; i < list.Len(); i++ { tparam := list.At(i) path2 := appendOpArg(path, op, i) - if r := find(obj, tparam, path2, seen); r != nil { + if r := f.find(tparam, path2); r != nil { return r } } @@ -626,7 +655,7 @@ func Object(pkg *types.Package, p Path) (types.Object, error) { // Inv: t != nil, obj == nil - t = aliases.Unalias(t) + t = types.Unalias(t) switch code { case opElem: hasElem, ok := t.(hasElem) // Pointer, Slice, Array, Chan, Map @@ -664,7 +693,7 @@ func Object(pkg *types.Package, p Path) (types.Object, error) { t = named.Underlying() case opRhs: - if alias, ok := t.(*aliases.Alias); ok { + if alias, ok := t.(*types.Alias); ok { t = aliases.Rhs(alias) } else if false && aliases.Enabled() { // The Enabled check is too expensive, so for now we diff --git a/vendor/golang.org/x/tools/go/types/typeutil/callee.go b/vendor/golang.org/x/tools/go/types/typeutil/callee.go new file mode 100644 index 0000000000..754380351e --- /dev/null +++ b/vendor/golang.org/x/tools/go/types/typeutil/callee.go @@ -0,0 +1,68 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package typeutil + +import ( + "go/ast" + "go/types" + + "golang.org/x/tools/internal/typeparams" +) + +// Callee returns the named target of a function call, if any: +// a function, method, builtin, or variable. +// +// Functions and methods may potentially have type parameters. +func Callee(info *types.Info, call *ast.CallExpr) types.Object { + fun := ast.Unparen(call.Fun) + + // Look through type instantiation if necessary. + isInstance := false + switch fun.(type) { + case *ast.IndexExpr, *ast.IndexListExpr: + // When extracting the callee from an *IndexExpr, we need to check that + // it is a *types.Func and not a *types.Var. + // Example: Don't match a slice m within the expression `m[0]()`. + isInstance = true + fun, _, _, _ = typeparams.UnpackIndexExpr(fun) + } + + var obj types.Object + switch fun := fun.(type) { + case *ast.Ident: + obj = info.Uses[fun] // type, var, builtin, or declared func + case *ast.SelectorExpr: + if sel, ok := info.Selections[fun]; ok { + obj = sel.Obj() // method or field + } else { + obj = info.Uses[fun.Sel] // qualified identifier? + } + } + if _, ok := obj.(*types.TypeName); ok { + return nil // T(x) is a conversion, not a call + } + // A Func is required to match instantiations. + if _, ok := obj.(*types.Func); isInstance && !ok { + return nil // Was not a Func. + } + return obj +} + +// StaticCallee returns the target (function or method) of a static function +// call, if any. It returns nil for calls to builtins. +// +// Note: for calls of instantiated functions and methods, StaticCallee returns +// the corresponding generic function or method on the generic type. +func StaticCallee(info *types.Info, call *ast.CallExpr) *types.Func { + if f, ok := Callee(info, call).(*types.Func); ok && !interfaceMethod(f) { + return f + } + return nil +} + +func interfaceMethod(f *types.Func) bool { + recv := f.Type().(*types.Signature).Recv() + return recv != nil && types.IsInterface(recv.Type()) +} diff --git a/vendor/golang.org/x/tools/go/types/typeutil/imports.go b/vendor/golang.org/x/tools/go/types/typeutil/imports.go new file mode 100644 index 0000000000..b81ce0c330 --- /dev/null +++ b/vendor/golang.org/x/tools/go/types/typeutil/imports.go @@ -0,0 +1,30 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package typeutil + +import "go/types" + +// Dependencies returns all dependencies of the specified packages. +// +// Dependent packages appear in topological order: if package P imports +// package Q, Q appears earlier than P in the result. +// The algorithm follows import statements in the order they +// appear in the source code, so the result is a total order. +func Dependencies(pkgs ...*types.Package) []*types.Package { + var result []*types.Package + seen := make(map[*types.Package]bool) + var visit func(pkgs []*types.Package) + visit = func(pkgs []*types.Package) { + for _, p := range pkgs { + if !seen[p] { + seen[p] = true + visit(p.Imports()) + result = append(result, p) + } + } + } + visit(pkgs) + return result +} diff --git a/vendor/golang.org/x/tools/go/types/typeutil/map.go b/vendor/golang.org/x/tools/go/types/typeutil/map.go new file mode 100644 index 0000000000..8d824f7140 --- /dev/null +++ b/vendor/golang.org/x/tools/go/types/typeutil/map.go @@ -0,0 +1,517 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package typeutil defines various utilities for types, such as Map, +// a mapping from types.Type to any values. +package typeutil // import "golang.org/x/tools/go/types/typeutil" + +import ( + "bytes" + "fmt" + "go/types" + "reflect" + + "golang.org/x/tools/internal/typeparams" +) + +// Map is a hash-table-based mapping from types (types.Type) to +// arbitrary any values. The concrete types that implement +// the Type interface are pointers. Since they are not canonicalized, +// == cannot be used to check for equivalence, and thus we cannot +// simply use a Go map. +// +// Just as with map[K]V, a nil *Map is a valid empty map. +// +// Not thread-safe. +type Map struct { + hasher Hasher // shared by many Maps + table map[uint32][]entry // maps hash to bucket; entry.key==nil means unused + length int // number of map entries +} + +// entry is an entry (key/value association) in a hash bucket. +type entry struct { + key types.Type + value any +} + +// SetHasher sets the hasher used by Map. +// +// All Hashers are functionally equivalent but contain internal state +// used to cache the results of hashing previously seen types. +// +// A single Hasher created by MakeHasher() may be shared among many +// Maps. This is recommended if the instances have many keys in +// common, as it will amortize the cost of hash computation. +// +// A Hasher may grow without bound as new types are seen. Even when a +// type is deleted from the map, the Hasher never shrinks, since other +// types in the map may reference the deleted type indirectly. +// +// Hashers are not thread-safe, and read-only operations such as +// Map.Lookup require updates to the hasher, so a full Mutex lock (not a +// read-lock) is require around all Map operations if a shared +// hasher is accessed from multiple threads. +// +// If SetHasher is not called, the Map will create a private hasher at +// the first call to Insert. +func (m *Map) SetHasher(hasher Hasher) { + m.hasher = hasher +} + +// Delete removes the entry with the given key, if any. +// It returns true if the entry was found. +func (m *Map) Delete(key types.Type) bool { + if m != nil && m.table != nil { + hash := m.hasher.Hash(key) + bucket := m.table[hash] + for i, e := range bucket { + if e.key != nil && types.Identical(key, e.key) { + // We can't compact the bucket as it + // would disturb iterators. + bucket[i] = entry{} + m.length-- + return true + } + } + } + return false +} + +// At returns the map entry for the given key. +// The result is nil if the entry is not present. +func (m *Map) At(key types.Type) any { + if m != nil && m.table != nil { + for _, e := range m.table[m.hasher.Hash(key)] { + if e.key != nil && types.Identical(key, e.key) { + return e.value + } + } + } + return nil +} + +// Set sets the map entry for key to val, +// and returns the previous entry, if any. +func (m *Map) Set(key types.Type, value any) (prev any) { + if m.table != nil { + hash := m.hasher.Hash(key) + bucket := m.table[hash] + var hole *entry + for i, e := range bucket { + if e.key == nil { + hole = &bucket[i] + } else if types.Identical(key, e.key) { + prev = e.value + bucket[i].value = value + return + } + } + + if hole != nil { + *hole = entry{key, value} // overwrite deleted entry + } else { + m.table[hash] = append(bucket, entry{key, value}) + } + } else { + if m.hasher.memo == nil { + m.hasher = MakeHasher() + } + hash := m.hasher.Hash(key) + m.table = map[uint32][]entry{hash: {entry{key, value}}} + } + + m.length++ + return +} + +// Len returns the number of map entries. +func (m *Map) Len() int { + if m != nil { + return m.length + } + return 0 +} + +// Iterate calls function f on each entry in the map in unspecified order. +// +// If f should mutate the map, Iterate provides the same guarantees as +// Go maps: if f deletes a map entry that Iterate has not yet reached, +// f will not be invoked for it, but if f inserts a map entry that +// Iterate has not yet reached, whether or not f will be invoked for +// it is unspecified. +func (m *Map) Iterate(f func(key types.Type, value any)) { + if m != nil { + for _, bucket := range m.table { + for _, e := range bucket { + if e.key != nil { + f(e.key, e.value) + } + } + } + } +} + +// Keys returns a new slice containing the set of map keys. +// The order is unspecified. +func (m *Map) Keys() []types.Type { + keys := make([]types.Type, 0, m.Len()) + m.Iterate(func(key types.Type, _ any) { + keys = append(keys, key) + }) + return keys +} + +func (m *Map) toString(values bool) string { + if m == nil { + return "{}" + } + var buf bytes.Buffer + fmt.Fprint(&buf, "{") + sep := "" + m.Iterate(func(key types.Type, value any) { + fmt.Fprint(&buf, sep) + sep = ", " + fmt.Fprint(&buf, key) + if values { + fmt.Fprintf(&buf, ": %q", value) + } + }) + fmt.Fprint(&buf, "}") + return buf.String() +} + +// String returns a string representation of the map's entries. +// Values are printed using fmt.Sprintf("%v", v). +// Order is unspecified. +func (m *Map) String() string { + return m.toString(true) +} + +// KeysString returns a string representation of the map's key set. +// Order is unspecified. +func (m *Map) KeysString() string { + return m.toString(false) +} + +//////////////////////////////////////////////////////////////////////// +// Hasher + +// A Hasher maps each type to its hash value. +// For efficiency, a hasher uses memoization; thus its memory +// footprint grows monotonically over time. +// Hashers are not thread-safe. +// Hashers have reference semantics. +// Call MakeHasher to create a Hasher. +type Hasher struct { + memo map[types.Type]uint32 + + // ptrMap records pointer identity. + ptrMap map[any]uint32 + + // sigTParams holds type parameters from the signature being hashed. + // Signatures are considered identical modulo renaming of type parameters, so + // within the scope of a signature type the identity of the signature's type + // parameters is just their index. + // + // Since the language does not currently support referring to uninstantiated + // generic types or functions, and instantiated signatures do not have type + // parameter lists, we should never encounter a second non-empty type + // parameter list when hashing a generic signature. + sigTParams *types.TypeParamList +} + +// MakeHasher returns a new Hasher instance. +func MakeHasher() Hasher { + return Hasher{ + memo: make(map[types.Type]uint32), + ptrMap: make(map[any]uint32), + sigTParams: nil, + } +} + +// Hash computes a hash value for the given type t such that +// Identical(t, t') => Hash(t) == Hash(t'). +func (h Hasher) Hash(t types.Type) uint32 { + hash, ok := h.memo[t] + if !ok { + hash = h.hashFor(t) + h.memo[t] = hash + } + return hash +} + +// hashString computes the Fowler–Noll–Vo hash of s. +func hashString(s string) uint32 { + var h uint32 + for i := 0; i < len(s); i++ { + h ^= uint32(s[i]) + h *= 16777619 + } + return h +} + +// hashFor computes the hash of t. +func (h Hasher) hashFor(t types.Type) uint32 { + // See Identical for rationale. + switch t := t.(type) { + case *types.Basic: + return uint32(t.Kind()) + + case *types.Alias: + return h.Hash(types.Unalias(t)) + + case *types.Array: + return 9043 + 2*uint32(t.Len()) + 3*h.Hash(t.Elem()) + + case *types.Slice: + return 9049 + 2*h.Hash(t.Elem()) + + case *types.Struct: + var hash uint32 = 9059 + for i, n := 0, t.NumFields(); i < n; i++ { + f := t.Field(i) + if f.Anonymous() { + hash += 8861 + } + hash += hashString(t.Tag(i)) + hash += hashString(f.Name()) // (ignore f.Pkg) + hash += h.Hash(f.Type()) + } + return hash + + case *types.Pointer: + return 9067 + 2*h.Hash(t.Elem()) + + case *types.Signature: + var hash uint32 = 9091 + if t.Variadic() { + hash *= 8863 + } + + // Use a separate hasher for types inside of the signature, where type + // parameter identity is modified to be (index, constraint). We must use a + // new memo for this hasher as type identity may be affected by this + // masking. For example, in func[T any](*T), the identity of *T depends on + // whether we are mapping the argument in isolation, or recursively as part + // of hashing the signature. + // + // We should never encounter a generic signature while hashing another + // generic signature, but defensively set sigTParams only if h.mask is + // unset. + tparams := t.TypeParams() + if h.sigTParams == nil && tparams.Len() != 0 { + h = Hasher{ + // There may be something more efficient than discarding the existing + // memo, but it would require detecting whether types are 'tainted' by + // references to type parameters. + memo: make(map[types.Type]uint32), + // Re-using ptrMap ensures that pointer identity is preserved in this + // hasher. + ptrMap: h.ptrMap, + sigTParams: tparams, + } + } + + for i := 0; i < tparams.Len(); i++ { + tparam := tparams.At(i) + hash += 7 * h.Hash(tparam.Constraint()) + } + + return hash + 3*h.hashTuple(t.Params()) + 5*h.hashTuple(t.Results()) + + case *types.Union: + return h.hashUnion(t) + + case *types.Interface: + // Interfaces are identical if they have the same set of methods, with + // identical names and types, and they have the same set of type + // restrictions. See go/types.identical for more details. + var hash uint32 = 9103 + + // Hash methods. + for i, n := 0, t.NumMethods(); i < n; i++ { + // Method order is not significant. + // Ignore m.Pkg(). + m := t.Method(i) + // Use shallow hash on method signature to + // avoid anonymous interface cycles. + hash += 3*hashString(m.Name()) + 5*h.shallowHash(m.Type()) + } + + // Hash type restrictions. + terms, err := typeparams.InterfaceTermSet(t) + // if err != nil t has invalid type restrictions. + if err == nil { + hash += h.hashTermSet(terms) + } + + return hash + + case *types.Map: + return 9109 + 2*h.Hash(t.Key()) + 3*h.Hash(t.Elem()) + + case *types.Chan: + return 9127 + 2*uint32(t.Dir()) + 3*h.Hash(t.Elem()) + + case *types.Named: + hash := h.hashPtr(t.Obj()) + targs := t.TypeArgs() + for i := 0; i < targs.Len(); i++ { + targ := targs.At(i) + hash += 2 * h.Hash(targ) + } + return hash + + case *types.TypeParam: + return h.hashTypeParam(t) + + case *types.Tuple: + return h.hashTuple(t) + } + + panic(fmt.Sprintf("%T: %v", t, t)) +} + +func (h Hasher) hashTuple(tuple *types.Tuple) uint32 { + // See go/types.identicalTypes for rationale. + n := tuple.Len() + hash := 9137 + 2*uint32(n) + for i := 0; i < n; i++ { + hash += 3 * h.Hash(tuple.At(i).Type()) + } + return hash +} + +func (h Hasher) hashUnion(t *types.Union) uint32 { + // Hash type restrictions. + terms, err := typeparams.UnionTermSet(t) + // if err != nil t has invalid type restrictions. Fall back on a non-zero + // hash. + if err != nil { + return 9151 + } + return h.hashTermSet(terms) +} + +func (h Hasher) hashTermSet(terms []*types.Term) uint32 { + hash := 9157 + 2*uint32(len(terms)) + for _, term := range terms { + // term order is not significant. + termHash := h.Hash(term.Type()) + if term.Tilde() { + termHash *= 9161 + } + hash += 3 * termHash + } + return hash +} + +// hashTypeParam returns a hash of the type parameter t, with a hash value +// depending on whether t is contained in h.sigTParams. +// +// If h.sigTParams is set and contains t, then we are in the process of hashing +// a signature, and the hash value of t must depend only on t's index and +// constraint: signatures are considered identical modulo type parameter +// renaming. To avoid infinite recursion, we only hash the type parameter +// index, and rely on types.Identical to handle signatures where constraints +// are not identical. +// +// Otherwise the hash of t depends only on t's pointer identity. +func (h Hasher) hashTypeParam(t *types.TypeParam) uint32 { + if h.sigTParams != nil { + i := t.Index() + if i >= 0 && i < h.sigTParams.Len() && t == h.sigTParams.At(i) { + return 9173 + 3*uint32(i) + } + } + return h.hashPtr(t.Obj()) +} + +// hashPtr hashes the pointer identity of ptr. It uses h.ptrMap to ensure that +// pointers values are not dependent on the GC. +func (h Hasher) hashPtr(ptr any) uint32 { + if hash, ok := h.ptrMap[ptr]; ok { + return hash + } + hash := uint32(reflect.ValueOf(ptr).Pointer()) + h.ptrMap[ptr] = hash + return hash +} + +// shallowHash computes a hash of t without looking at any of its +// element Types, to avoid potential anonymous cycles in the types of +// interface methods. +// +// When an unnamed non-empty interface type appears anywhere among the +// arguments or results of an interface method, there is a potential +// for endless recursion. Consider: +// +// type X interface { m() []*interface { X } } +// +// The problem is that the Methods of the interface in m's result type +// include m itself; there is no mention of the named type X that +// might help us break the cycle. +// (See comment in go/types.identical, case *Interface, for more.) +func (h Hasher) shallowHash(t types.Type) uint32 { + // t is the type of an interface method (Signature), + // its params or results (Tuples), or their immediate + // elements (mostly Slice, Pointer, Basic, Named), + // so there's no need to optimize anything else. + switch t := t.(type) { + case *types.Alias: + return h.shallowHash(types.Unalias(t)) + + case *types.Signature: + var hash uint32 = 604171 + if t.Variadic() { + hash *= 971767 + } + // The Signature/Tuple recursion is always finite + // and invariably shallow. + return hash + 1062599*h.shallowHash(t.Params()) + 1282529*h.shallowHash(t.Results()) + + case *types.Tuple: + n := t.Len() + hash := 9137 + 2*uint32(n) + for i := 0; i < n; i++ { + hash += 53471161 * h.shallowHash(t.At(i).Type()) + } + return hash + + case *types.Basic: + return 45212177 * uint32(t.Kind()) + + case *types.Array: + return 1524181 + 2*uint32(t.Len()) + + case *types.Slice: + return 2690201 + + case *types.Struct: + return 3326489 + + case *types.Pointer: + return 4393139 + + case *types.Union: + return 562448657 + + case *types.Interface: + return 2124679 // no recursion here + + case *types.Map: + return 9109 + + case *types.Chan: + return 9127 + + case *types.Named: + return h.hashPtr(t.Obj()) + + case *types.TypeParam: + return h.hashPtr(t.Obj()) + } + panic(fmt.Sprintf("shallowHash: %T: %v", t, t)) +} diff --git a/vendor/golang.org/x/tools/go/types/typeutil/methodsetcache.go b/vendor/golang.org/x/tools/go/types/typeutil/methodsetcache.go new file mode 100644 index 0000000000..f7666028fe --- /dev/null +++ b/vendor/golang.org/x/tools/go/types/typeutil/methodsetcache.go @@ -0,0 +1,71 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file implements a cache of method sets. + +package typeutil + +import ( + "go/types" + "sync" +) + +// A MethodSetCache records the method set of each type T for which +// MethodSet(T) is called so that repeat queries are fast. +// The zero value is a ready-to-use cache instance. +type MethodSetCache struct { + mu sync.Mutex + named map[*types.Named]struct{ value, pointer *types.MethodSet } // method sets for named N and *N + others map[types.Type]*types.MethodSet // all other types +} + +// MethodSet returns the method set of type T. It is thread-safe. +// +// If cache is nil, this function is equivalent to types.NewMethodSet(T). +// Utility functions can thus expose an optional *MethodSetCache +// parameter to clients that care about performance. +func (cache *MethodSetCache) MethodSet(T types.Type) *types.MethodSet { + if cache == nil { + return types.NewMethodSet(T) + } + cache.mu.Lock() + defer cache.mu.Unlock() + + switch T := types.Unalias(T).(type) { + case *types.Named: + return cache.lookupNamed(T).value + + case *types.Pointer: + if N, ok := types.Unalias(T.Elem()).(*types.Named); ok { + return cache.lookupNamed(N).pointer + } + } + + // all other types + // (The map uses pointer equivalence, not type identity.) + mset := cache.others[T] + if mset == nil { + mset = types.NewMethodSet(T) + if cache.others == nil { + cache.others = make(map[types.Type]*types.MethodSet) + } + cache.others[T] = mset + } + return mset +} + +func (cache *MethodSetCache) lookupNamed(named *types.Named) struct{ value, pointer *types.MethodSet } { + if cache.named == nil { + cache.named = make(map[*types.Named]struct{ value, pointer *types.MethodSet }) + } + // Avoid recomputing mset(*T) for each distinct Pointer + // instance whose underlying type is a named type. + msets, ok := cache.named[named] + if !ok { + msets.value = types.NewMethodSet(named) + msets.pointer = types.NewMethodSet(types.NewPointer(named)) + cache.named[named] = msets + } + return msets +} diff --git a/vendor/golang.org/x/tools/go/types/typeutil/ui.go b/vendor/golang.org/x/tools/go/types/typeutil/ui.go new file mode 100644 index 0000000000..9dda6a25df --- /dev/null +++ b/vendor/golang.org/x/tools/go/types/typeutil/ui.go @@ -0,0 +1,53 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package typeutil + +// This file defines utilities for user interfaces that display types. + +import ( + "go/types" +) + +// IntuitiveMethodSet returns the intuitive method set of a type T, +// which is the set of methods you can call on an addressable value of +// that type. +// +// The result always contains MethodSet(T), and is exactly MethodSet(T) +// for interface types and for pointer-to-concrete types. +// For all other concrete types T, the result additionally +// contains each method belonging to *T if there is no identically +// named method on T itself. +// +// This corresponds to user intuition about method sets; +// this function is intended only for user interfaces. +// +// The order of the result is as for types.MethodSet(T). +func IntuitiveMethodSet(T types.Type, msets *MethodSetCache) []*types.Selection { + isPointerToConcrete := func(T types.Type) bool { + ptr, ok := types.Unalias(T).(*types.Pointer) + return ok && !types.IsInterface(ptr.Elem()) + } + + var result []*types.Selection + mset := msets.MethodSet(T) + if types.IsInterface(T) || isPointerToConcrete(T) { + for i, n := 0, mset.Len(); i < n; i++ { + result = append(result, mset.At(i)) + } + } else { + // T is some other concrete type. + // Report methods of T and *T, preferring those of T. + pmset := msets.MethodSet(types.NewPointer(T)) + for i, n := 0, pmset.Len(); i < n; i++ { + meth := pmset.At(i) + if m := mset.Lookup(meth.Obj().Pkg(), meth.Obj().Name()); m != nil { + meth = m + } + result = append(result, meth) + } + + } + return result +} diff --git a/vendor/golang.org/x/tools/internal/aliases/aliases.go b/vendor/golang.org/x/tools/internal/aliases/aliases.go index c24c2eee45..b9425f5a20 100644 --- a/vendor/golang.org/x/tools/internal/aliases/aliases.go +++ b/vendor/golang.org/x/tools/internal/aliases/aliases.go @@ -22,11 +22,17 @@ import ( // GODEBUG=gotypesalias=... by invoking the type checker. The Enabled // function is expensive and should be called once per task (e.g. // package import), not once per call to NewAlias. -func NewAlias(enabled bool, pos token.Pos, pkg *types.Package, name string, rhs types.Type) *types.TypeName { +// +// Precondition: enabled || len(tparams)==0. +// If materialized aliases are disabled, there must not be any type parameters. +func NewAlias(enabled bool, pos token.Pos, pkg *types.Package, name string, rhs types.Type, tparams []*types.TypeParam) *types.TypeName { if enabled { tname := types.NewTypeName(pos, pkg, name, nil) - newAlias(tname, rhs) + SetTypeParams(types.NewAlias(tname, rhs), tparams) return tname } + if len(tparams) > 0 { + panic("cannot create an alias with type parameters when gotypesalias is not enabled") + } return types.NewTypeName(pos, pkg, name, rhs) } diff --git a/vendor/golang.org/x/tools/internal/aliases/aliases_go121.go b/vendor/golang.org/x/tools/internal/aliases/aliases_go121.go deleted file mode 100644 index 6652f7db0f..0000000000 --- a/vendor/golang.org/x/tools/internal/aliases/aliases_go121.go +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2024 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !go1.22 -// +build !go1.22 - -package aliases - -import ( - "go/types" -) - -// Alias is a placeholder for a go/types.Alias for <=1.21. -// It will never be created by go/types. -type Alias struct{} - -func (*Alias) String() string { panic("unreachable") } -func (*Alias) Underlying() types.Type { panic("unreachable") } -func (*Alias) Obj() *types.TypeName { panic("unreachable") } -func Rhs(alias *Alias) types.Type { panic("unreachable") } -func TypeParams(alias *Alias) *types.TypeParamList { panic("unreachable") } -func SetTypeParams(alias *Alias, tparams []*types.TypeParam) { panic("unreachable") } -func TypeArgs(alias *Alias) *types.TypeList { panic("unreachable") } -func Origin(alias *Alias) *Alias { panic("unreachable") } - -// Unalias returns the type t for go <=1.21. -func Unalias(t types.Type) types.Type { return t } - -func newAlias(name *types.TypeName, rhs types.Type) *Alias { panic("unreachable") } - -// Enabled reports whether [NewAlias] should create [types.Alias] types. -// -// Before go1.22, this function always returns false. -func Enabled() bool { return false } diff --git a/vendor/golang.org/x/tools/internal/aliases/aliases_go122.go b/vendor/golang.org/x/tools/internal/aliases/aliases_go122.go index 3ef1afeb40..7716a3331d 100644 --- a/vendor/golang.org/x/tools/internal/aliases/aliases_go122.go +++ b/vendor/golang.org/x/tools/internal/aliases/aliases_go122.go @@ -2,9 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build go1.22 -// +build go1.22 - package aliases import ( @@ -14,22 +11,19 @@ import ( "go/types" ) -// Alias is an alias of types.Alias. -type Alias = types.Alias - // Rhs returns the type on the right-hand side of the alias declaration. -func Rhs(alias *Alias) types.Type { +func Rhs(alias *types.Alias) types.Type { if alias, ok := any(alias).(interface{ Rhs() types.Type }); ok { return alias.Rhs() // go1.23+ } // go1.22's Alias didn't have the Rhs method, // so Unalias is the best we can do. - return Unalias(alias) + return types.Unalias(alias) } // TypeParams returns the type parameter list of the alias. -func TypeParams(alias *Alias) *types.TypeParamList { +func TypeParams(alias *types.Alias) *types.TypeParamList { if alias, ok := any(alias).(interface{ TypeParams() *types.TypeParamList }); ok { return alias.TypeParams() // go1.23+ } @@ -37,7 +31,7 @@ func TypeParams(alias *Alias) *types.TypeParamList { } // SetTypeParams sets the type parameters of the alias type. -func SetTypeParams(alias *Alias, tparams []*types.TypeParam) { +func SetTypeParams(alias *types.Alias, tparams []*types.TypeParam) { if alias, ok := any(alias).(interface { SetTypeParams(tparams []*types.TypeParam) }); ok { @@ -48,7 +42,7 @@ func SetTypeParams(alias *Alias, tparams []*types.TypeParam) { } // TypeArgs returns the type arguments used to instantiate the Alias type. -func TypeArgs(alias *Alias) *types.TypeList { +func TypeArgs(alias *types.Alias) *types.TypeList { if alias, ok := any(alias).(interface{ TypeArgs() *types.TypeList }); ok { return alias.TypeArgs() // go1.23+ } @@ -57,26 +51,13 @@ func TypeArgs(alias *Alias) *types.TypeList { // Origin returns the generic Alias type of which alias is an instance. // If alias is not an instance of a generic alias, Origin returns alias. -func Origin(alias *Alias) *Alias { +func Origin(alias *types.Alias) *types.Alias { if alias, ok := any(alias).(interface{ Origin() *types.Alias }); ok { return alias.Origin() // go1.23+ } return alias // not an instance of a generic alias (go1.22) } -// Unalias is a wrapper of types.Unalias. -func Unalias(t types.Type) types.Type { return types.Unalias(t) } - -// newAlias is an internal alias around types.NewAlias. -// Direct usage is discouraged as the moment. -// Try to use NewAlias instead. -func newAlias(tname *types.TypeName, rhs types.Type) *Alias { - a := types.NewAlias(tname, rhs) - // TODO(go.dev/issue/65455): Remove kludgy workaround to set a.actual as a side-effect. - Unalias(a) - return a -} - // Enabled reports whether [NewAlias] should create [types.Alias] types. // // This function is expensive! Call it sparingly. @@ -92,7 +73,7 @@ func Enabled() bool { // many tests. Therefore any attempt to cache the result // is just incorrect. fset := token.NewFileSet() - f, _ := parser.ParseFile(fset, "a.go", "package p; type A = int", 0) + f, _ := parser.ParseFile(fset, "a.go", "package p; type A = int", parser.SkipObjectResolution) pkg, _ := new(types.Config).Check("p", fset, []*ast.File{f}, nil) _, enabled := pkg.Scope().Lookup("A").Type().(*types.Alias) return enabled diff --git a/vendor/golang.org/x/tools/internal/gcimporter/bimport.go b/vendor/golang.org/x/tools/internal/gcimporter/bimport.go index d98b0db2a9..d79a605ed1 100644 --- a/vendor/golang.org/x/tools/internal/gcimporter/bimport.go +++ b/vendor/golang.org/x/tools/internal/gcimporter/bimport.go @@ -87,64 +87,3 @@ func chanDir(d int) types.ChanDir { return 0 } } - -var predeclOnce sync.Once -var predecl []types.Type // initialized lazily - -func predeclared() []types.Type { - predeclOnce.Do(func() { - // initialize lazily to be sure that all - // elements have been initialized before - predecl = []types.Type{ // basic types - types.Typ[types.Bool], - types.Typ[types.Int], - types.Typ[types.Int8], - types.Typ[types.Int16], - types.Typ[types.Int32], - types.Typ[types.Int64], - types.Typ[types.Uint], - types.Typ[types.Uint8], - types.Typ[types.Uint16], - types.Typ[types.Uint32], - types.Typ[types.Uint64], - types.Typ[types.Uintptr], - types.Typ[types.Float32], - types.Typ[types.Float64], - types.Typ[types.Complex64], - types.Typ[types.Complex128], - types.Typ[types.String], - - // basic type aliases - types.Universe.Lookup("byte").Type(), - types.Universe.Lookup("rune").Type(), - - // error - types.Universe.Lookup("error").Type(), - - // untyped types - types.Typ[types.UntypedBool], - types.Typ[types.UntypedInt], - types.Typ[types.UntypedRune], - types.Typ[types.UntypedFloat], - types.Typ[types.UntypedComplex], - types.Typ[types.UntypedString], - types.Typ[types.UntypedNil], - - // package unsafe - types.Typ[types.UnsafePointer], - - // invalid type - types.Typ[types.Invalid], // only appears in packages with errors - - // used internally by gc; never used by this package or in .a files - anyType{}, - } - predecl = append(predecl, additionalPredeclared()...) - }) - return predecl -} - -type anyType struct{} - -func (t anyType) Underlying() types.Type { return t } -func (t anyType) String() string { return "any" } diff --git a/vendor/golang.org/x/tools/internal/gcimporter/exportdata.go b/vendor/golang.org/x/tools/internal/gcimporter/exportdata.go index f6437feb1c..6f5d8a2139 100644 --- a/vendor/golang.org/x/tools/internal/gcimporter/exportdata.go +++ b/vendor/golang.org/x/tools/internal/gcimporter/exportdata.go @@ -39,12 +39,15 @@ func readGopackHeader(r *bufio.Reader) (name string, size int64, err error) { } // FindExportData positions the reader r at the beginning of the -// export data section of an underlying GC-created object/archive +// export data section of an underlying cmd/compile created archive // file by reading from it. The reader must be positioned at the -// start of the file before calling this function. The hdr result -// is the string before the export data, either "$$" or "$$B". -// The size result is the length of the export data in bytes, or -1 if not known. -func FindExportData(r *bufio.Reader) (hdr string, size int64, err error) { +// start of the file before calling this function. +// The size result is the length of the export data in bytes. +// +// This function is needed by [gcexportdata.Read], which must +// accept inputs produced by the last two releases of cmd/compile, +// plus tip. +func FindExportData(r *bufio.Reader) (size int64, err error) { // Read first line to make sure this is an object file. line, err := r.ReadSlice('\n') if err != nil { @@ -52,27 +55,32 @@ func FindExportData(r *bufio.Reader) (hdr string, size int64, err error) { return } - if string(line) == "!\n" { - // Archive file. Scan to __.PKGDEF. - var name string - if name, size, err = readGopackHeader(r); err != nil { - return - } + // Is the first line an archive file signature? + if string(line) != "!\n" { + err = fmt.Errorf("not the start of an archive file (%q)", line) + return + } - // First entry should be __.PKGDEF. - if name != "__.PKGDEF" { - err = fmt.Errorf("go archive is missing __.PKGDEF") - return - } + // Archive file. Scan to __.PKGDEF. + var name string + if name, size, err = readGopackHeader(r); err != nil { + return + } + arsize := size - // Read first line of __.PKGDEF data, so that line - // is once again the first line of the input. - if line, err = r.ReadSlice('\n'); err != nil { - err = fmt.Errorf("can't find export data (%v)", err) - return - } - size -= int64(len(line)) + // First entry should be __.PKGDEF. + if name != "__.PKGDEF" { + err = fmt.Errorf("go archive is missing __.PKGDEF") + return + } + + // Read first line of __.PKGDEF data, so that line + // is once again the first line of the input. + if line, err = r.ReadSlice('\n'); err != nil { + err = fmt.Errorf("can't find export data (%v)", err) + return } + size -= int64(len(line)) // Now at __.PKGDEF in archive or still at beginning of file. // Either way, line should begin with "go object ". @@ -81,8 +89,8 @@ func FindExportData(r *bufio.Reader) (hdr string, size int64, err error) { return } - // Skip over object header to export data. - // Begins after first line starting with $$. + // Skip over object headers to get to the export data section header "$$B\n". + // Object headers are lines that do not start with '$'. for line[0] != '$' { if line, err = r.ReadSlice('\n'); err != nil { err = fmt.Errorf("can't find export data (%v)", err) @@ -90,9 +98,18 @@ func FindExportData(r *bufio.Reader) (hdr string, size int64, err error) { } size -= int64(len(line)) } - hdr = string(line) + + // Check for the binary export data section header "$$B\n". + hdr := string(line) + if hdr != "$$B\n" { + err = fmt.Errorf("unknown export data header: %q", hdr) + return + } + // TODO(taking): Remove end-of-section marker "\n$$\n" from size. + if size < 0 { - size = -1 + err = fmt.Errorf("invalid size (%d) in the archive file: %d bytes remain without section headers (recompile package)", arsize, size) + return } return diff --git a/vendor/golang.org/x/tools/internal/gcimporter/gcimporter.go b/vendor/golang.org/x/tools/internal/gcimporter/gcimporter.go index 39df91124a..dbbca86043 100644 --- a/vendor/golang.org/x/tools/internal/gcimporter/gcimporter.go +++ b/vendor/golang.org/x/tools/internal/gcimporter/gcimporter.go @@ -161,6 +161,8 @@ func FindPkg(path, srcDir string) (filename, id string) { // Import imports a gc-generated package given its import path and srcDir, adds // the corresponding package object to the packages map, and returns the object. // The packages map must contain all packages already imported. +// +// TODO(taking): Import is only used in tests. Move to gcimporter_test. func Import(packages map[string]*types.Package, path, srcDir string, lookup func(path string) (io.ReadCloser, error)) (pkg *types.Package, err error) { var rc io.ReadCloser var filename, id string @@ -210,53 +212,50 @@ func Import(packages map[string]*types.Package, path, srcDir string, lookup func } defer rc.Close() - var hdr string var size int64 buf := bufio.NewReader(rc) - if hdr, size, err = FindExportData(buf); err != nil { + if size, err = FindExportData(buf); err != nil { return } - switch hdr { - case "$$B\n": - var data []byte - data, err = io.ReadAll(buf) - if err != nil { - break - } + var data []byte + data, err = io.ReadAll(buf) + if err != nil { + return + } + if len(data) == 0 { + return nil, fmt.Errorf("no data to load a package from for path %s", id) + } - // TODO(gri): allow clients of go/importer to provide a FileSet. - // Or, define a new standard go/types/gcexportdata package. - fset := token.NewFileSet() + // TODO(gri): allow clients of go/importer to provide a FileSet. + // Or, define a new standard go/types/gcexportdata package. + fset := token.NewFileSet() - // Select appropriate importer. - if len(data) > 0 { - switch data[0] { - case 'v', 'c', 'd': // binary, till go1.10 - return nil, fmt.Errorf("binary (%c) import format is no longer supported", data[0]) + // Select appropriate importer. + switch data[0] { + case 'v', 'c', 'd': + // binary: emitted by cmd/compile till go1.10; obsolete. + return nil, fmt.Errorf("binary (%c) import format is no longer supported", data[0]) - case 'i': // indexed, till go1.19 - _, pkg, err := IImportData(fset, packages, data[1:], id) - return pkg, err + case 'i': + // indexed: emitted by cmd/compile till go1.19; + // now used only for serializing go/types. + // See https://github.com/golang/go/issues/69491. + _, pkg, err := IImportData(fset, packages, data[1:], id) + return pkg, err - case 'u': // unified, from go1.20 - _, pkg, err := UImportData(fset, packages, data[1:size], id) - return pkg, err - - default: - l := len(data) - if l > 10 { - l = 10 - } - return nil, fmt.Errorf("unexpected export data with prefix %q for path %s", string(data[:l]), id) - } - } + case 'u': + // unified: emitted by cmd/compile since go1.20. + _, pkg, err := UImportData(fset, packages, data[1:size], id) + return pkg, err default: - err = fmt.Errorf("unknown export data header: %q", hdr) + l := len(data) + if l > 10 { + l = 10 + } + return nil, fmt.Errorf("unexpected export data with prefix %q for path %s", string(data[:l]), id) } - - return } type byPath []*types.Package diff --git a/vendor/golang.org/x/tools/internal/gcimporter/iexport.go b/vendor/golang.org/x/tools/internal/gcimporter/iexport.go index deeb67f315..7dfc31a37d 100644 --- a/vendor/golang.org/x/tools/internal/gcimporter/iexport.go +++ b/vendor/golang.org/x/tools/internal/gcimporter/iexport.go @@ -2,9 +2,227 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Indexed binary package export. -// This file was derived from $GOROOT/src/cmd/compile/internal/gc/iexport.go; -// see that file for specification of the format. +// Indexed package export. +// +// The indexed export data format is an evolution of the previous +// binary export data format. Its chief contribution is introducing an +// index table, which allows efficient random access of individual +// declarations and inline function bodies. In turn, this allows +// avoiding unnecessary work for compilation units that import large +// packages. +// +// +// The top-level data format is structured as: +// +// Header struct { +// Tag byte // 'i' +// Version uvarint +// StringSize uvarint +// DataSize uvarint +// } +// +// Strings [StringSize]byte +// Data [DataSize]byte +// +// MainIndex []struct{ +// PkgPath stringOff +// PkgName stringOff +// PkgHeight uvarint +// +// Decls []struct{ +// Name stringOff +// Offset declOff +// } +// } +// +// Fingerprint [8]byte +// +// uvarint means a uint64 written out using uvarint encoding. +// +// []T means a uvarint followed by that many T objects. In other +// words: +// +// Len uvarint +// Elems [Len]T +// +// stringOff means a uvarint that indicates an offset within the +// Strings section. At that offset is another uvarint, followed by +// that many bytes, which form the string value. +// +// declOff means a uvarint that indicates an offset within the Data +// section where the associated declaration can be found. +// +// +// There are five kinds of declarations, distinguished by their first +// byte: +// +// type Var struct { +// Tag byte // 'V' +// Pos Pos +// Type typeOff +// } +// +// type Func struct { +// Tag byte // 'F' or 'G' +// Pos Pos +// TypeParams []typeOff // only present if Tag == 'G' +// Signature Signature +// } +// +// type Const struct { +// Tag byte // 'C' +// Pos Pos +// Value Value +// } +// +// type Type struct { +// Tag byte // 'T' or 'U' +// Pos Pos +// TypeParams []typeOff // only present if Tag == 'U' +// Underlying typeOff +// +// Methods []struct{ // omitted if Underlying is an interface type +// Pos Pos +// Name stringOff +// Recv Param +// Signature Signature +// } +// } +// +// type Alias struct { +// Tag byte // 'A' or 'B' +// Pos Pos +// TypeParams []typeOff // only present if Tag == 'B' +// Type typeOff +// } +// +// // "Automatic" declaration of each typeparam +// type TypeParam struct { +// Tag byte // 'P' +// Pos Pos +// Implicit bool +// Constraint typeOff +// } +// +// typeOff means a uvarint that either indicates a predeclared type, +// or an offset into the Data section. If the uvarint is less than +// predeclReserved, then it indicates the index into the predeclared +// types list (see predeclared in bexport.go for order). Otherwise, +// subtracting predeclReserved yields the offset of a type descriptor. +// +// Value means a type, kind, and type-specific value. See +// (*exportWriter).value for details. +// +// +// There are twelve kinds of type descriptors, distinguished by an itag: +// +// type DefinedType struct { +// Tag itag // definedType +// Name stringOff +// PkgPath stringOff +// } +// +// type PointerType struct { +// Tag itag // pointerType +// Elem typeOff +// } +// +// type SliceType struct { +// Tag itag // sliceType +// Elem typeOff +// } +// +// type ArrayType struct { +// Tag itag // arrayType +// Len uint64 +// Elem typeOff +// } +// +// type ChanType struct { +// Tag itag // chanType +// Dir uint64 // 1 RecvOnly; 2 SendOnly; 3 SendRecv +// Elem typeOff +// } +// +// type MapType struct { +// Tag itag // mapType +// Key typeOff +// Elem typeOff +// } +// +// type FuncType struct { +// Tag itag // signatureType +// PkgPath stringOff +// Signature Signature +// } +// +// type StructType struct { +// Tag itag // structType +// PkgPath stringOff +// Fields []struct { +// Pos Pos +// Name stringOff +// Type typeOff +// Embedded bool +// Note stringOff +// } +// } +// +// type InterfaceType struct { +// Tag itag // interfaceType +// PkgPath stringOff +// Embeddeds []struct { +// Pos Pos +// Type typeOff +// } +// Methods []struct { +// Pos Pos +// Name stringOff +// Signature Signature +// } +// } +// +// // Reference to a type param declaration +// type TypeParamType struct { +// Tag itag // typeParamType +// Name stringOff +// PkgPath stringOff +// } +// +// // Instantiation of a generic type (like List[T2] or List[int]) +// type InstanceType struct { +// Tag itag // instanceType +// Pos pos +// TypeArgs []typeOff +// BaseType typeOff +// } +// +// type UnionType struct { +// Tag itag // interfaceType +// Terms []struct { +// tilde bool +// Type typeOff +// } +// } +// +// +// +// type Signature struct { +// Params []Param +// Results []Param +// Variadic bool // omitted if Results is empty +// } +// +// type Param struct { +// Pos Pos +// Name stringOff +// Type typOff +// } +// +// +// Pos encodes a file:line:column triple, incorporating a simple delta +// encoding scheme within a data object. See exportWriter.pos for +// details. package gcimporter @@ -24,11 +242,30 @@ import ( "golang.org/x/tools/go/types/objectpath" "golang.org/x/tools/internal/aliases" - "golang.org/x/tools/internal/tokeninternal" ) // IExportShallow encodes "shallow" export data for the specified package. // +// For types, we use "shallow" export data. Historically, the Go +// compiler always produced a summary of the types for a given package +// that included types from other packages that it indirectly +// referenced: "deep" export data. This had the advantage that the +// compiler (and analogous tools such as gopls) need only load one +// file per direct import. However, it meant that the files tended to +// get larger based on the level of the package in the import +// graph. For example, higher-level packages in the kubernetes module +// have over 1MB of "deep" export data, even when they have almost no +// content of their own, merely because they mention a major type that +// references many others. In pathological cases the export data was +// 300x larger than the source for a package due to this quadratic +// growth. +// +// "Shallow" export data means that the serialized types describe only +// a single package. If those types mention types from other packages, +// the type checker may need to request additional packages beyond +// just the direct imports. Type information for the entire transitive +// closure of imports is provided (lazily) by the DAG. +// // No promises are made about the encoding other than that it can be decoded by // the same version of IIExportShallow. If you plan to save export data in the // file system, be sure to include a cryptographic digest of the executable in @@ -51,8 +288,8 @@ func IExportShallow(fset *token.FileSet, pkg *types.Package, reportf ReportFunc) } // IImportShallow decodes "shallow" types.Package data encoded by -// IExportShallow in the same executable. This function cannot import data from -// cmd/compile or gcexportdata.Write. +// [IExportShallow] in the same executable. This function cannot import data +// from cmd/compile or gcexportdata.Write. // // The importer calls getPackages to obtain package symbols for all // packages mentioned in the export data, including the one being @@ -223,7 +460,7 @@ func (p *iexporter) encodeFile(w *intWriter, file *token.File, needed []uint64) // Sort the set of needed offsets. Duplicates are harmless. sort.Slice(needed, func(i, j int) bool { return needed[i] < needed[j] }) - lines := tokeninternal.GetLines(file) // byte offset of each line start + lines := file.Lines() // byte offset of each line start w.uint64(uint64(len(lines))) // Rather than record the entire array of line start offsets, @@ -507,13 +744,13 @@ func (p *iexporter) doDecl(obj types.Object) { case *types.TypeName: t := obj.Type() - if tparam, ok := aliases.Unalias(t).(*types.TypeParam); ok { + if tparam, ok := types.Unalias(t).(*types.TypeParam); ok { w.tag(typeParamTag) w.pos(obj.Pos()) constraint := tparam.Constraint() if p.version >= iexportVersionGo1_18 { implicit := false - if iface, _ := aliases.Unalias(constraint).(*types.Interface); iface != nil { + if iface, _ := types.Unalias(constraint).(*types.Interface); iface != nil { implicit = iface.IsImplicit() } w.bool(implicit) @@ -523,9 +760,22 @@ func (p *iexporter) doDecl(obj types.Object) { } if obj.IsAlias() { - w.tag(aliasTag) + alias, materialized := t.(*types.Alias) // may fail when aliases are not enabled + + var tparams *types.TypeParamList + if materialized { + tparams = aliases.TypeParams(alias) + } + if tparams.Len() == 0 { + w.tag(aliasTag) + } else { + w.tag(genericAliasTag) + } w.pos(obj.Pos()) - if alias, ok := t.(*aliases.Alias); ok { + if tparams.Len() > 0 { + w.tparamList(obj.Name(), tparams, obj.Pkg()) + } + if materialized { // Preserve materialized aliases, // even of non-exported types. t = aliases.Rhs(alias) @@ -744,8 +994,14 @@ func (w *exportWriter) doTyp(t types.Type, pkg *types.Package) { }() } switch t := t.(type) { - case *aliases.Alias: - // TODO(adonovan): support parameterized aliases, following *types.Named. + case *types.Alias: + if targs := aliases.TypeArgs(t); targs.Len() > 0 { + w.startType(instanceType) + w.pos(t.Obj().Pos()) + w.typeList(targs, pkg) + w.typ(aliases.Origin(t), pkg) + return + } w.startType(aliasType) w.qualifiedType(t.Obj()) @@ -854,7 +1110,7 @@ func (w *exportWriter) doTyp(t types.Type, pkg *types.Package) { for i := 0; i < n; i++ { ft := t.EmbeddedType(i) tPkg := pkg - if named, _ := aliases.Unalias(ft).(*types.Named); named != nil { + if named, _ := types.Unalias(ft).(*types.Named); named != nil { w.pos(named.Obj().Pos()) } else { w.pos(token.NoPos) diff --git a/vendor/golang.org/x/tools/internal/gcimporter/iimport.go b/vendor/golang.org/x/tools/internal/gcimporter/iimport.go index 136aa03653..e260c0e8db 100644 --- a/vendor/golang.org/x/tools/internal/gcimporter/iimport.go +++ b/vendor/golang.org/x/tools/internal/gcimporter/iimport.go @@ -3,7 +3,7 @@ // license that can be found in the LICENSE file. // Indexed package import. -// See cmd/compile/internal/gc/iexport.go for the export data format. +// See iexport.go for the export data format. // This file is a copy of $GOROOT/src/go/internal/gcimporter/iimport.go. @@ -53,6 +53,7 @@ const ( iexportVersionPosCol = 1 iexportVersionGo1_18 = 2 iexportVersionGenerics = 2 + iexportVersion = iexportVersionGenerics iexportVersionCurrent = 2 ) @@ -540,7 +541,7 @@ func canReuse(def *types.Named, rhs types.Type) bool { if def == nil { return true } - iface, _ := aliases.Unalias(rhs).(*types.Interface) + iface, _ := types.Unalias(rhs).(*types.Interface) if iface == nil { return true } @@ -557,19 +558,28 @@ type importReader struct { prevColumn int64 } +// markBlack is redefined in iimport_go123.go, to work around golang/go#69912. +// +// If TypeNames are not marked black (in the sense of go/types cycle +// detection), they may be mutated when dot-imported. Fix this by punching a +// hole through the type, when compiling with Go 1.23. (The bug has been fixed +// for 1.24, but the fix was not worth back-porting). +var markBlack = func(name *types.TypeName) {} + func (r *importReader) obj(name string) { tag := r.byte() pos := r.pos() switch tag { - case aliasTag: + case aliasTag, genericAliasTag: + var tparams []*types.TypeParam + if tag == genericAliasTag { + tparams = r.tparamList() + } typ := r.typ() - // TODO(adonovan): support generic aliases: - // if tag == genericAliasTag { - // tparams := r.tparamList() - // alias.SetTypeParams(tparams) - // } - r.declare(aliases.NewAlias(r.p.aliases, pos, r.currPkg, name, typ)) + obj := aliases.NewAlias(r.p.aliases, pos, r.currPkg, name, typ, tparams) + markBlack(obj) // workaround for golang/go#69912 + r.declare(obj) case constTag: typ, val := r.value() @@ -589,6 +599,9 @@ func (r *importReader) obj(name string) { // declaration before recursing. obj := types.NewTypeName(pos, r.currPkg, name, nil) named := types.NewNamed(obj, nil, nil) + + markBlack(obj) // workaround for golang/go#69912 + // Declare obj before calling r.tparamList, so the new type name is recognized // if used in the constraint of one of its own typeparams (see #48280). r.declare(obj) @@ -615,7 +628,7 @@ func (r *importReader) obj(name string) { if targs.Len() > 0 { rparams = make([]*types.TypeParam, targs.Len()) for i := range rparams { - rparams[i] = aliases.Unalias(targs.At(i)).(*types.TypeParam) + rparams[i] = types.Unalias(targs.At(i)).(*types.TypeParam) } } msig := r.signature(recv, rparams, nil) @@ -645,7 +658,7 @@ func (r *importReader) obj(name string) { } constraint := r.typ() if implicit { - iface, _ := aliases.Unalias(constraint).(*types.Interface) + iface, _ := types.Unalias(constraint).(*types.Interface) if iface == nil { errorf("non-interface constraint marked implicit") } @@ -852,7 +865,7 @@ func (r *importReader) typ() types.Type { } func isInterface(t types.Type) bool { - _, ok := aliases.Unalias(t).(*types.Interface) + _, ok := types.Unalias(t).(*types.Interface) return ok } @@ -862,7 +875,7 @@ func (r *importReader) string() string { return r.p.stringAt(r.uint64()) } func (r *importReader) doType(base *types.Named) (res types.Type) { k := r.kind() if debug { - r.p.trace("importing type %d (base: %s)", k, base) + r.p.trace("importing type %d (base: %v)", k, base) r.p.indent++ defer func() { r.p.indent-- @@ -959,7 +972,7 @@ func (r *importReader) doType(base *types.Named) (res types.Type) { methods[i] = method } - typ := newInterface(methods, embeddeds) + typ := types.NewInterfaceType(methods, embeddeds) r.p.interfaceList = append(r.p.interfaceList, typ) return typ @@ -1051,7 +1064,7 @@ func (r *importReader) tparamList() []*types.TypeParam { for i := range xs { // Note: the standard library importer is tolerant of nil types here, // though would panic in SetTypeParams. - xs[i] = aliases.Unalias(r.typ()).(*types.TypeParam) + xs[i] = types.Unalias(r.typ()).(*types.TypeParam) } return xs } diff --git a/vendor/golang.org/x/tools/internal/gcimporter/iimport_go122.go b/vendor/golang.org/x/tools/internal/gcimporter/iimport_go122.go new file mode 100644 index 0000000000..7586bfaca6 --- /dev/null +++ b/vendor/golang.org/x/tools/internal/gcimporter/iimport_go122.go @@ -0,0 +1,53 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build go1.22 && !go1.24 + +package gcimporter + +import ( + "go/token" + "go/types" + "unsafe" +) + +// TODO(rfindley): delete this workaround once go1.24 is assured. + +func init() { + // Update markBlack so that it correctly sets the color + // of imported TypeNames. + // + // See the doc comment for markBlack for details. + + type color uint32 + const ( + white color = iota + black + grey + ) + type object struct { + _ *types.Scope + _ token.Pos + _ *types.Package + _ string + _ types.Type + _ uint32 + color_ color + _ token.Pos + } + type typeName struct { + object + } + + // If the size of types.TypeName changes, this will fail to compile. + const delta = int64(unsafe.Sizeof(typeName{})) - int64(unsafe.Sizeof(types.TypeName{})) + var _ [-delta * delta]int + + markBlack = func(obj *types.TypeName) { + type uP = unsafe.Pointer + var ptr *typeName + *(*uP)(uP(&ptr)) = uP(obj) + ptr.color_ = black + } +} diff --git a/vendor/golang.org/x/tools/internal/gcimporter/newInterface10.go b/vendor/golang.org/x/tools/internal/gcimporter/newInterface10.go deleted file mode 100644 index 8b163e3d05..0000000000 --- a/vendor/golang.org/x/tools/internal/gcimporter/newInterface10.go +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !go1.11 -// +build !go1.11 - -package gcimporter - -import "go/types" - -func newInterface(methods []*types.Func, embeddeds []types.Type) *types.Interface { - named := make([]*types.Named, len(embeddeds)) - for i, e := range embeddeds { - var ok bool - named[i], ok = e.(*types.Named) - if !ok { - panic("embedding of non-defined interfaces in interfaces is not supported before Go 1.11") - } - } - return types.NewInterface(methods, named) -} diff --git a/vendor/golang.org/x/tools/internal/gcimporter/newInterface11.go b/vendor/golang.org/x/tools/internal/gcimporter/newInterface11.go deleted file mode 100644 index 49984f40fd..0000000000 --- a/vendor/golang.org/x/tools/internal/gcimporter/newInterface11.go +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build go1.11 -// +build go1.11 - -package gcimporter - -import "go/types" - -func newInterface(methods []*types.Func, embeddeds []types.Type) *types.Interface { - return types.NewInterfaceType(methods, embeddeds) -} diff --git a/vendor/golang.org/x/tools/internal/gcimporter/predeclared.go b/vendor/golang.org/x/tools/internal/gcimporter/predeclared.go new file mode 100644 index 0000000000..907c8557a5 --- /dev/null +++ b/vendor/golang.org/x/tools/internal/gcimporter/predeclared.go @@ -0,0 +1,91 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gcimporter + +import ( + "go/types" + "sync" +) + +// predecl is a cache for the predeclared types in types.Universe. +// +// Cache a distinct result based on the runtime value of any. +// The pointer value of the any type varies based on GODEBUG settings. +var predeclMu sync.Mutex +var predecl map[types.Type][]types.Type + +func predeclared() []types.Type { + anyt := types.Universe.Lookup("any").Type() + + predeclMu.Lock() + defer predeclMu.Unlock() + + if pre, ok := predecl[anyt]; ok { + return pre + } + + if predecl == nil { + predecl = make(map[types.Type][]types.Type) + } + + decls := []types.Type{ // basic types + types.Typ[types.Bool], + types.Typ[types.Int], + types.Typ[types.Int8], + types.Typ[types.Int16], + types.Typ[types.Int32], + types.Typ[types.Int64], + types.Typ[types.Uint], + types.Typ[types.Uint8], + types.Typ[types.Uint16], + types.Typ[types.Uint32], + types.Typ[types.Uint64], + types.Typ[types.Uintptr], + types.Typ[types.Float32], + types.Typ[types.Float64], + types.Typ[types.Complex64], + types.Typ[types.Complex128], + types.Typ[types.String], + + // basic type aliases + types.Universe.Lookup("byte").Type(), + types.Universe.Lookup("rune").Type(), + + // error + types.Universe.Lookup("error").Type(), + + // untyped types + types.Typ[types.UntypedBool], + types.Typ[types.UntypedInt], + types.Typ[types.UntypedRune], + types.Typ[types.UntypedFloat], + types.Typ[types.UntypedComplex], + types.Typ[types.UntypedString], + types.Typ[types.UntypedNil], + + // package unsafe + types.Typ[types.UnsafePointer], + + // invalid type + types.Typ[types.Invalid], // only appears in packages with errors + + // used internally by gc; never used by this package or in .a files + anyType{}, + + // comparable + types.Universe.Lookup("comparable").Type(), + + // any + anyt, + } + + predecl[anyt] = decls + return decls +} + +type anyType struct{} + +func (t anyType) Underlying() types.Type { return t } +func (t anyType) String() string { return "any" } diff --git a/vendor/golang.org/x/tools/internal/gcimporter/support_go118.go b/vendor/golang.org/x/tools/internal/gcimporter/support_go118.go deleted file mode 100644 index 0cd3b91b65..0000000000 --- a/vendor/golang.org/x/tools/internal/gcimporter/support_go118.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package gcimporter - -import "go/types" - -const iexportVersion = iexportVersionGenerics - -// additionalPredeclared returns additional predeclared types in go.1.18. -func additionalPredeclared() []types.Type { - return []types.Type{ - // comparable - types.Universe.Lookup("comparable").Type(), - - // any - types.Universe.Lookup("any").Type(), - } -} - -// See cmd/compile/internal/types.SplitVargenSuffix. -func splitVargenSuffix(name string) (base, suffix string) { - i := len(name) - for i > 0 && name[i-1] >= '0' && name[i-1] <= '9' { - i-- - } - const dot = "·" - if i >= len(dot) && name[i-len(dot):i] == dot { - i -= len(dot) - return name[:i], name[i:] - } - return name, "" -} diff --git a/vendor/golang.org/x/tools/internal/gcimporter/unified_no.go b/vendor/golang.org/x/tools/internal/gcimporter/unified_no.go deleted file mode 100644 index 38b624cada..0000000000 --- a/vendor/golang.org/x/tools/internal/gcimporter/unified_no.go +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2022 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !goexperiment.unified -// +build !goexperiment.unified - -package gcimporter - -const unifiedIR = false diff --git a/vendor/golang.org/x/tools/internal/gcimporter/unified_yes.go b/vendor/golang.org/x/tools/internal/gcimporter/unified_yes.go deleted file mode 100644 index b5118d0b3a..0000000000 --- a/vendor/golang.org/x/tools/internal/gcimporter/unified_yes.go +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2022 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build goexperiment.unified -// +build goexperiment.unified - -package gcimporter - -const unifiedIR = true diff --git a/vendor/golang.org/x/tools/internal/gcimporter/ureader_yes.go b/vendor/golang.org/x/tools/internal/gcimporter/ureader_yes.go index 2c07706887..1db408613c 100644 --- a/vendor/golang.org/x/tools/internal/gcimporter/ureader_yes.go +++ b/vendor/golang.org/x/tools/internal/gcimporter/ureader_yes.go @@ -52,8 +52,7 @@ func (pr *pkgReader) later(fn func()) { // See cmd/compile/internal/noder.derivedInfo. type derivedInfo struct { - idx pkgbits.Index - needed bool + idx pkgbits.Index } // See cmd/compile/internal/noder.typeInfo. @@ -110,13 +109,17 @@ func readUnifiedPackage(fset *token.FileSet, ctxt *types.Context, imports map[st r := pr.newReader(pkgbits.RelocMeta, pkgbits.PublicRootIdx, pkgbits.SyncPublic) pkg := r.pkg() - r.Bool() // has init + if r.Version().Has(pkgbits.HasInit) { + r.Bool() + } for i, n := 0, r.Len(); i < n; i++ { // As if r.obj(), but avoiding the Scope.Lookup call, // to avoid eager loading of imports. r.Sync(pkgbits.SyncObject) - assert(!r.Bool()) + if r.Version().Has(pkgbits.DerivedFuncInstance) { + assert(!r.Bool()) + } r.p.objIdx(r.Reloc(pkgbits.RelocObj)) assert(r.Len() == 0) } @@ -165,7 +168,7 @@ type readerDict struct { // tparams is a slice of the constructed TypeParams for the element. tparams []*types.TypeParam - // devived is a slice of types derived from tparams, which may be + // derived is a slice of types derived from tparams, which may be // instantiated while reading the current element. derived []derivedInfo derivedTypes []types.Type // lazily instantiated from derived @@ -471,7 +474,9 @@ func (r *reader) param() *types.Var { func (r *reader) obj() (types.Object, []types.Type) { r.Sync(pkgbits.SyncObject) - assert(!r.Bool()) + if r.Version().Has(pkgbits.DerivedFuncInstance) { + assert(!r.Bool()) + } pkg, name := r.p.objIdx(r.Reloc(pkgbits.RelocObj)) obj := pkgScope(pkg).Lookup(name) @@ -525,8 +530,12 @@ func (pr *pkgReader) objIdx(idx pkgbits.Index) (*types.Package, string) { case pkgbits.ObjAlias: pos := r.pos() + var tparams []*types.TypeParam + if r.Version().Has(pkgbits.AliasTypeParamNames) { + tparams = r.typeParamNames() + } typ := r.typ() - declare(aliases.NewAlias(r.p.aliases, pos, objPkg, objName, typ)) + declare(aliases.NewAlias(r.p.aliases, pos, objPkg, objName, typ, tparams)) case pkgbits.ObjConst: pos := r.pos() @@ -553,7 +562,7 @@ func (pr *pkgReader) objIdx(idx pkgbits.Index) (*types.Package, string) { // If the underlying type is an interface, we need to // duplicate its methods so we can replace the receiver // parameter's type (#49906). - if iface, ok := aliases.Unalias(underlying).(*types.Interface); ok && iface.NumExplicitMethods() != 0 { + if iface, ok := types.Unalias(underlying).(*types.Interface); ok && iface.NumExplicitMethods() != 0 { methods := make([]*types.Func, iface.NumExplicitMethods()) for i := range methods { fn := iface.ExplicitMethod(i) @@ -632,7 +641,10 @@ func (pr *pkgReader) objDictIdx(idx pkgbits.Index) *readerDict { dict.derived = make([]derivedInfo, r.Len()) dict.derivedTypes = make([]types.Type, len(dict.derived)) for i := range dict.derived { - dict.derived[i] = derivedInfo{r.Reloc(pkgbits.RelocType), r.Bool()} + dict.derived[i] = derivedInfo{idx: r.Reloc(pkgbits.RelocType)} + if r.Version().Has(pkgbits.DerivedInfoNeeded) { + assert(!r.Bool()) + } } pr.retireReader(r) @@ -726,3 +738,17 @@ func pkgScope(pkg *types.Package) *types.Scope { } return types.Universe } + +// See cmd/compile/internal/types.SplitVargenSuffix. +func splitVargenSuffix(name string) (base, suffix string) { + i := len(name) + for i > 0 && name[i-1] >= '0' && name[i-1] <= '9' { + i-- + } + const dot = "·" + if i >= len(dot) && name[i-len(dot):i] == dot { + i -= len(dot) + return name[:i], name[i:] + } + return name, "" +} diff --git a/vendor/golang.org/x/tools/internal/gocommand/invoke.go b/vendor/golang.org/x/tools/internal/gocommand/invoke.go index 2e59ff8558..e333efc87f 100644 --- a/vendor/golang.org/x/tools/internal/gocommand/invoke.go +++ b/vendor/golang.org/x/tools/internal/gocommand/invoke.go @@ -16,7 +16,6 @@ import ( "os" "os/exec" "path/filepath" - "reflect" "regexp" "runtime" "strconv" @@ -250,16 +249,13 @@ func (i *Invocation) run(ctx context.Context, stdout, stderr io.Writer) error { cmd.Stdout = stdout cmd.Stderr = stderr - // cmd.WaitDelay was added only in go1.20 (see #50436). - if waitDelay := reflect.ValueOf(cmd).Elem().FieldByName("WaitDelay"); waitDelay.IsValid() { - // https://go.dev/issue/59541: don't wait forever copying stderr - // after the command has exited. - // After CL 484741 we copy stdout manually, so we we'll stop reading that as - // soon as ctx is done. However, we also don't want to wait around forever - // for stderr. Give a much-longer-than-reasonable delay and then assume that - // something has wedged in the kernel or runtime. - waitDelay.Set(reflect.ValueOf(30 * time.Second)) - } + // https://go.dev/issue/59541: don't wait forever copying stderr + // after the command has exited. + // After CL 484741 we copy stdout manually, so we we'll stop reading that as + // soon as ctx is done. However, we also don't want to wait around forever + // for stderr. Give a much-longer-than-reasonable delay and then assume that + // something has wedged in the kernel or runtime. + cmd.WaitDelay = 30 * time.Second // The cwd gets resolved to the real path. On Darwin, where // /tmp is a symlink, this breaks anything that expects the diff --git a/vendor/golang.org/x/tools/internal/packagesinternal/packages.go b/vendor/golang.org/x/tools/internal/packagesinternal/packages.go index 44719de173..66e69b4389 100644 --- a/vendor/golang.org/x/tools/internal/packagesinternal/packages.go +++ b/vendor/golang.org/x/tools/internal/packagesinternal/packages.go @@ -5,7 +5,6 @@ // Package packagesinternal exposes internal-only fields from go/packages. package packagesinternal -var GetForTest = func(p interface{}) string { return "" } var GetDepsErrors = func(p interface{}) []*PackageError { return nil } type PackageError struct { @@ -16,7 +15,6 @@ type PackageError struct { var TypecheckCgo int var DepsErrors int // must be set as a LoadMode to call GetDepsErrors -var ForTest int // must be set as a LoadMode to call GetForTest var SetModFlag = func(config interface{}, value string) {} var SetModFile = func(config interface{}, value string) {} diff --git a/vendor/golang.org/x/tools/internal/pkgbits/decoder.go b/vendor/golang.org/x/tools/internal/pkgbits/decoder.go index b92e8e6eb3..f6cb37c5c3 100644 --- a/vendor/golang.org/x/tools/internal/pkgbits/decoder.go +++ b/vendor/golang.org/x/tools/internal/pkgbits/decoder.go @@ -21,7 +21,7 @@ import ( // export data. type PkgDecoder struct { // version is the file format version. - version uint32 + version Version // sync indicates whether the file uses sync markers. sync bool @@ -68,8 +68,6 @@ func (pr *PkgDecoder) SyncMarkers() bool { return pr.sync } // NewPkgDecoder returns a PkgDecoder initialized to read the Unified // IR export data from input. pkgPath is the package path for the // compilation unit that produced the export data. -// -// TODO(mdempsky): Remove pkgPath parameter; unneeded since CL 391014. func NewPkgDecoder(pkgPath, input string) PkgDecoder { pr := PkgDecoder{ pkgPath: pkgPath, @@ -80,14 +78,15 @@ func NewPkgDecoder(pkgPath, input string) PkgDecoder { r := strings.NewReader(input) - assert(binary.Read(r, binary.LittleEndian, &pr.version) == nil) + var ver uint32 + assert(binary.Read(r, binary.LittleEndian, &ver) == nil) + pr.version = Version(ver) - switch pr.version { - default: - panic(fmt.Errorf("unsupported version: %v", pr.version)) - case 0: - // no flags - case 1: + if pr.version >= numVersions { + panic(fmt.Errorf("cannot decode %q, export data version %d is greater than maximum supported version %d", pkgPath, pr.version, numVersions-1)) + } + + if pr.version.Has(Flags) { var flags uint32 assert(binary.Read(r, binary.LittleEndian, &flags) == nil) pr.sync = flags&flagSyncMarkers != 0 @@ -102,7 +101,9 @@ func NewPkgDecoder(pkgPath, input string) PkgDecoder { assert(err == nil) pr.elemData = input[pos:] - assert(len(pr.elemData)-8 == int(pr.elemEnds[len(pr.elemEnds)-1])) + + const fingerprintSize = 8 + assert(len(pr.elemData)-fingerprintSize == int(pr.elemEnds[len(pr.elemEnds)-1])) return pr } @@ -136,7 +137,7 @@ func (pr *PkgDecoder) AbsIdx(k RelocKind, idx Index) int { absIdx += int(pr.elemEndsEnds[k-1]) } if absIdx >= int(pr.elemEndsEnds[k]) { - errorf("%v:%v is out of bounds; %v", k, idx, pr.elemEndsEnds) + panicf("%v:%v is out of bounds; %v", k, idx, pr.elemEndsEnds) } return absIdx } @@ -193,9 +194,7 @@ func (pr *PkgDecoder) NewDecoderRaw(k RelocKind, idx Index) Decoder { Idx: idx, } - // TODO(mdempsky) r.data.Reset(...) after #44505 is resolved. - r.Data = *strings.NewReader(pr.DataIdx(k, idx)) - + r.Data.Reset(pr.DataIdx(k, idx)) r.Sync(SyncRelocs) r.Relocs = make([]RelocEnt, r.Len()) for i := range r.Relocs { @@ -244,7 +243,7 @@ type Decoder struct { func (r *Decoder) checkErr(err error) { if err != nil { - errorf("unexpected decoding error: %w", err) + panicf("unexpected decoding error: %w", err) } } @@ -515,3 +514,6 @@ func (pr *PkgDecoder) PeekObj(idx Index) (string, string, CodeObj) { return path, name, tag } + +// Version reports the version of the bitstream. +func (w *Decoder) Version() Version { return w.common.version } diff --git a/vendor/golang.org/x/tools/internal/pkgbits/encoder.go b/vendor/golang.org/x/tools/internal/pkgbits/encoder.go index 6482617a4f..c17a12399d 100644 --- a/vendor/golang.org/x/tools/internal/pkgbits/encoder.go +++ b/vendor/golang.org/x/tools/internal/pkgbits/encoder.go @@ -12,18 +12,15 @@ import ( "io" "math/big" "runtime" + "strings" ) -// currentVersion is the current version number. -// -// - v0: initial prototype -// -// - v1: adds the flags uint32 word -const currentVersion uint32 = 1 - // A PkgEncoder provides methods for encoding a package's Unified IR // export data. type PkgEncoder struct { + // version of the bitstream. + version Version + // elems holds the bitstream for previously encoded elements. elems [numRelocs][]string @@ -47,8 +44,9 @@ func (pw *PkgEncoder) SyncMarkers() bool { return pw.syncFrames >= 0 } // export data files, but can help diagnosing desync errors in // higher-level Unified IR reader/writer code. If syncFrames is // negative, then sync markers are omitted entirely. -func NewPkgEncoder(syncFrames int) PkgEncoder { +func NewPkgEncoder(version Version, syncFrames int) PkgEncoder { return PkgEncoder{ + version: version, stringsIdx: make(map[string]Index), syncFrames: syncFrames, } @@ -64,13 +62,15 @@ func (pw *PkgEncoder) DumpTo(out0 io.Writer) (fingerprint [8]byte) { assert(binary.Write(out, binary.LittleEndian, x) == nil) } - writeUint32(currentVersion) + writeUint32(uint32(pw.version)) - var flags uint32 - if pw.SyncMarkers() { - flags |= flagSyncMarkers + if pw.version.Has(Flags) { + var flags uint32 + if pw.SyncMarkers() { + flags |= flagSyncMarkers + } + writeUint32(flags) } - writeUint32(flags) // Write elemEndsEnds. var sum uint32 @@ -159,7 +159,7 @@ type Encoder struct { // Flush finalizes the element's bitstream and returns its Index. func (w *Encoder) Flush() Index { - var sb bytes.Buffer // TODO(mdempsky): strings.Builder after #44505 is resolved + var sb strings.Builder // Backup the data so we write the relocations at the front. var tmp bytes.Buffer @@ -189,7 +189,7 @@ func (w *Encoder) Flush() Index { func (w *Encoder) checkErr(err error) { if err != nil { - errorf("unexpected encoding error: %v", err) + panicf("unexpected encoding error: %v", err) } } @@ -320,8 +320,14 @@ func (w *Encoder) Code(c Code) { // section (if not already present), and then writing a relocation // into the element bitstream. func (w *Encoder) String(s string) { + w.StringRef(w.p.StringIdx(s)) +} + +// StringRef writes a reference to the given index, which must be a +// previously encoded string value. +func (w *Encoder) StringRef(idx Index) { w.Sync(SyncString) - w.Reloc(RelocString, w.p.StringIdx(s)) + w.Reloc(RelocString, idx) } // Strings encodes and writes a variable-length slice of strings into @@ -348,7 +354,7 @@ func (w *Encoder) Value(val constant.Value) { func (w *Encoder) scalar(val constant.Value) { switch v := constant.Val(val).(type) { default: - errorf("unhandled %v (%v)", val, val.Kind()) + panicf("unhandled %v (%v)", val, val.Kind()) case bool: w.Code(ValBool) w.Bool(v) @@ -381,3 +387,6 @@ func (w *Encoder) bigFloat(v *big.Float) { b := v.Append(nil, 'p', -1) w.String(string(b)) // TODO: More efficient encoding. } + +// Version reports the version of the bitstream. +func (w *Encoder) Version() Version { return w.p.version } diff --git a/vendor/golang.org/x/tools/internal/pkgbits/frames_go1.go b/vendor/golang.org/x/tools/internal/pkgbits/frames_go1.go deleted file mode 100644 index 5294f6a63e..0000000000 --- a/vendor/golang.org/x/tools/internal/pkgbits/frames_go1.go +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !go1.7 -// +build !go1.7 - -// TODO(mdempsky): Remove after #44505 is resolved - -package pkgbits - -import "runtime" - -func walkFrames(pcs []uintptr, visit frameVisitor) { - for _, pc := range pcs { - fn := runtime.FuncForPC(pc) - file, line := fn.FileLine(pc) - - visit(file, line, fn.Name(), pc-fn.Entry()) - } -} diff --git a/vendor/golang.org/x/tools/internal/pkgbits/frames_go17.go b/vendor/golang.org/x/tools/internal/pkgbits/frames_go17.go deleted file mode 100644 index 2324ae7adf..0000000000 --- a/vendor/golang.org/x/tools/internal/pkgbits/frames_go17.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build go1.7 -// +build go1.7 - -package pkgbits - -import "runtime" - -// walkFrames calls visit for each call frame represented by pcs. -// -// pcs should be a slice of PCs, as returned by runtime.Callers. -func walkFrames(pcs []uintptr, visit frameVisitor) { - if len(pcs) == 0 { - return - } - - frames := runtime.CallersFrames(pcs) - for { - frame, more := frames.Next() - visit(frame.File, frame.Line, frame.Function, frame.PC-frame.Entry) - if !more { - return - } - } -} diff --git a/vendor/golang.org/x/tools/internal/pkgbits/support.go b/vendor/golang.org/x/tools/internal/pkgbits/support.go index ad26d3b28c..50534a2955 100644 --- a/vendor/golang.org/x/tools/internal/pkgbits/support.go +++ b/vendor/golang.org/x/tools/internal/pkgbits/support.go @@ -12,6 +12,6 @@ func assert(b bool) { } } -func errorf(format string, args ...interface{}) { +func panicf(format string, args ...any) { panic(fmt.Errorf(format, args...)) } diff --git a/vendor/golang.org/x/tools/internal/pkgbits/sync.go b/vendor/golang.org/x/tools/internal/pkgbits/sync.go index 5bd51ef717..1520b73afb 100644 --- a/vendor/golang.org/x/tools/internal/pkgbits/sync.go +++ b/vendor/golang.org/x/tools/internal/pkgbits/sync.go @@ -6,6 +6,7 @@ package pkgbits import ( "fmt" + "runtime" "strings" ) @@ -23,6 +24,24 @@ func fmtFrames(pcs ...uintptr) []string { type frameVisitor func(file string, line int, name string, offset uintptr) +// walkFrames calls visit for each call frame represented by pcs. +// +// pcs should be a slice of PCs, as returned by runtime.Callers. +func walkFrames(pcs []uintptr, visit frameVisitor) { + if len(pcs) == 0 { + return + } + + frames := runtime.CallersFrames(pcs) + for { + frame, more := frames.Next() + visit(frame.File, frame.Line, frame.Function, frame.PC-frame.Entry) + if !more { + return + } + } +} + // SyncMarker is an enum type that represents markers that may be // written to export data to ensure the reader and writer stay // synchronized. @@ -110,4 +129,8 @@ const ( SyncStmtsEnd SyncLabel SyncOptLabel + + SyncMultiExpr + SyncRType + SyncConvRTTI ) diff --git a/vendor/golang.org/x/tools/internal/pkgbits/syncmarker_string.go b/vendor/golang.org/x/tools/internal/pkgbits/syncmarker_string.go index 4a5b0ca5f2..582ad56d3e 100644 --- a/vendor/golang.org/x/tools/internal/pkgbits/syncmarker_string.go +++ b/vendor/golang.org/x/tools/internal/pkgbits/syncmarker_string.go @@ -74,11 +74,14 @@ func _() { _ = x[SyncStmtsEnd-64] _ = x[SyncLabel-65] _ = x[SyncOptLabel-66] + _ = x[SyncMultiExpr-67] + _ = x[SyncRType-68] + _ = x[SyncConvRTTI-69] } -const _SyncMarker_name = "EOFBoolInt64Uint64StringValueValRelocsRelocUseRelocPublicPosPosBaseObjectObject1PkgPkgDefMethodTypeTypeIdxTypeParamNamesSignatureParamsParamCodeObjSymLocalIdentSelectorPrivateFuncExtVarExtTypeExtPragmaExprListExprsExprExprTypeAssignOpFuncLitCompLitDeclFuncBodyOpenScopeCloseScopeCloseAnotherScopeDeclNamesDeclNameStmtsBlockStmtIfStmtForStmtSwitchStmtRangeStmtCaseClauseCommClauseSelectStmtDeclsLabeledStmtUseObjLocalAddLocalLinknameStmt1StmtsEndLabelOptLabel" +const _SyncMarker_name = "EOFBoolInt64Uint64StringValueValRelocsRelocUseRelocPublicPosPosBaseObjectObject1PkgPkgDefMethodTypeTypeIdxTypeParamNamesSignatureParamsParamCodeObjSymLocalIdentSelectorPrivateFuncExtVarExtTypeExtPragmaExprListExprsExprExprTypeAssignOpFuncLitCompLitDeclFuncBodyOpenScopeCloseScopeCloseAnotherScopeDeclNamesDeclNameStmtsBlockStmtIfStmtForStmtSwitchStmtRangeStmtCaseClauseCommClauseSelectStmtDeclsLabeledStmtUseObjLocalAddLocalLinknameStmt1StmtsEndLabelOptLabelMultiExprRTypeConvRTTI" -var _SyncMarker_index = [...]uint16{0, 3, 7, 12, 18, 24, 29, 32, 38, 43, 51, 57, 60, 67, 73, 80, 83, 89, 95, 99, 106, 120, 129, 135, 140, 147, 150, 160, 168, 175, 182, 188, 195, 201, 209, 214, 218, 226, 232, 234, 241, 248, 252, 260, 269, 279, 296, 305, 313, 318, 327, 333, 340, 350, 359, 369, 379, 389, 394, 405, 416, 424, 432, 437, 445, 450, 458} +var _SyncMarker_index = [...]uint16{0, 3, 7, 12, 18, 24, 29, 32, 38, 43, 51, 57, 60, 67, 73, 80, 83, 89, 95, 99, 106, 120, 129, 135, 140, 147, 150, 160, 168, 175, 182, 188, 195, 201, 209, 214, 218, 226, 232, 234, 241, 248, 252, 260, 269, 279, 296, 305, 313, 318, 327, 333, 340, 350, 359, 369, 379, 389, 394, 405, 416, 424, 432, 437, 445, 450, 458, 467, 472, 480} func (i SyncMarker) String() string { i -= 1 diff --git a/vendor/golang.org/x/tools/internal/pkgbits/version.go b/vendor/golang.org/x/tools/internal/pkgbits/version.go new file mode 100644 index 0000000000..53af9df22b --- /dev/null +++ b/vendor/golang.org/x/tools/internal/pkgbits/version.go @@ -0,0 +1,85 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pkgbits + +// Version indicates a version of a unified IR bitstream. +// Each Version indicates the addition, removal, or change of +// new data in the bitstream. +// +// These are serialized to disk and the interpretation remains fixed. +type Version uint32 + +const ( + // V0: initial prototype. + // + // All data that is not assigned a Field is in version V0 + // and has not been deprecated. + V0 Version = iota + + // V1: adds the Flags uint32 word + V1 + + // V2: removes unused legacy fields and supports type parameters for aliases. + // - remove the legacy "has init" bool from the public root + // - remove obj's "derived func instance" bool + // - add a TypeParamNames field to ObjAlias + // - remove derived info "needed" bool + V2 + + numVersions = iota +) + +// Field denotes a unit of data in the serialized unified IR bitstream. +// It is conceptually a like field in a structure. +// +// We only really need Fields when the data may or may not be present +// in a stream based on the Version of the bitstream. +// +// Unlike much of pkgbits, Fields are not serialized and +// can change values as needed. +type Field int + +const ( + // Flags in a uint32 in the header of a bitstream + // that is used to indicate whether optional features are enabled. + Flags Field = iota + + // Deprecated: HasInit was a bool indicating whether a package + // has any init functions. + HasInit + + // Deprecated: DerivedFuncInstance was a bool indicating + // whether an object was a function instance. + DerivedFuncInstance + + // ObjAlias has a list of TypeParamNames. + AliasTypeParamNames + + // Deprecated: DerivedInfoNeeded was a bool indicating + // whether a type was a derived type. + DerivedInfoNeeded + + numFields = iota +) + +// introduced is the version a field was added. +var introduced = [numFields]Version{ + Flags: V1, + AliasTypeParamNames: V2, +} + +// removed is the version a field was removed in or 0 for fields +// that have not yet been deprecated. +// (So removed[f]-1 is the last version it is included in.) +var removed = [numFields]Version{ + HasInit: V2, + DerivedFuncInstance: V2, + DerivedInfoNeeded: V2, +} + +// Has reports whether field f is present in a bitstream at version v. +func (v Version) Has(f Field) bool { + return introduced[f] <= v && (v < removed[f] || removed[f] == V0) +} diff --git a/vendor/golang.org/x/tools/internal/stdlib/manifest.go b/vendor/golang.org/x/tools/internal/stdlib/manifest.go index a928acf29f..cdaac9ab34 100644 --- a/vendor/golang.org/x/tools/internal/stdlib/manifest.go +++ b/vendor/golang.org/x/tools/internal/stdlib/manifest.go @@ -951,7 +951,7 @@ var PackageSymbols = map[string][]Symbol{ {"ParseSessionState", Func, 21}, {"QUICClient", Func, 21}, {"QUICConfig", Type, 21}, - {"QUICConfig.EnableStoreSessionEvent", Field, 23}, + {"QUICConfig.EnableSessionEvents", Field, 23}, {"QUICConfig.TLSConfig", Field, 21}, {"QUICConn", Type, 21}, {"QUICEncryptionLevel", Type, 21}, diff --git a/vendor/golang.org/x/tools/internal/tokeninternal/tokeninternal.go b/vendor/golang.org/x/tools/internal/tokeninternal/tokeninternal.go deleted file mode 100644 index ff9437a36c..0000000000 --- a/vendor/golang.org/x/tools/internal/tokeninternal/tokeninternal.go +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright 2023 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// package tokeninternal provides access to some internal features of the token -// package. -package tokeninternal - -import ( - "fmt" - "go/token" - "sort" - "sync" - "unsafe" -) - -// GetLines returns the table of line-start offsets from a token.File. -func GetLines(file *token.File) []int { - // token.File has a Lines method on Go 1.21 and later. - if file, ok := (interface{})(file).(interface{ Lines() []int }); ok { - return file.Lines() - } - - // This declaration must match that of token.File. - // This creates a risk of dependency skew. - // For now we check that the size of the two - // declarations is the same, on the (fragile) assumption - // that future changes would add fields. - type tokenFile119 struct { - _ string - _ int - _ int - mu sync.Mutex // we're not complete monsters - lines []int - _ []struct{} - } - - if unsafe.Sizeof(*file) != unsafe.Sizeof(tokenFile119{}) { - panic("unexpected token.File size") - } - var ptr *tokenFile119 - type uP = unsafe.Pointer - *(*uP)(uP(&ptr)) = uP(file) - ptr.mu.Lock() - defer ptr.mu.Unlock() - return ptr.lines -} - -// AddExistingFiles adds the specified files to the FileSet if they -// are not already present. It panics if any pair of files in the -// resulting FileSet would overlap. -func AddExistingFiles(fset *token.FileSet, files []*token.File) { - // Punch through the FileSet encapsulation. - type tokenFileSet struct { - // This type remained essentially consistent from go1.16 to go1.21. - mutex sync.RWMutex - base int - files []*token.File - _ *token.File // changed to atomic.Pointer[token.File] in go1.19 - } - - // If the size of token.FileSet changes, this will fail to compile. - const delta = int64(unsafe.Sizeof(tokenFileSet{})) - int64(unsafe.Sizeof(token.FileSet{})) - var _ [-delta * delta]int - - type uP = unsafe.Pointer - var ptr *tokenFileSet - *(*uP)(uP(&ptr)) = uP(fset) - ptr.mutex.Lock() - defer ptr.mutex.Unlock() - - // Merge and sort. - newFiles := append(ptr.files, files...) - sort.Slice(newFiles, func(i, j int) bool { - return newFiles[i].Base() < newFiles[j].Base() - }) - - // Reject overlapping files. - // Discard adjacent identical files. - out := newFiles[:0] - for i, file := range newFiles { - if i > 0 { - prev := newFiles[i-1] - if file == prev { - continue - } - if prev.Base()+prev.Size()+1 > file.Base() { - panic(fmt.Sprintf("file %s (%d-%d) overlaps with file %s (%d-%d)", - prev.Name(), prev.Base(), prev.Base()+prev.Size(), - file.Name(), file.Base(), file.Base()+file.Size())) - } - } - out = append(out, file) - } - newFiles = out - - ptr.files = newFiles - - // Advance FileSet.Base(). - if len(newFiles) > 0 { - last := newFiles[len(newFiles)-1] - newBase := last.Base() + last.Size() + 1 - if ptr.base < newBase { - ptr.base = newBase - } - } -} - -// FileSetFor returns a new FileSet containing a sequence of new Files with -// the same base, size, and line as the input files, for use in APIs that -// require a FileSet. -// -// Precondition: the input files must be non-overlapping, and sorted in order -// of their Base. -func FileSetFor(files ...*token.File) *token.FileSet { - fset := token.NewFileSet() - for _, f := range files { - f2 := fset.AddFile(f.Name(), f.Base(), f.Size()) - lines := GetLines(f) - f2.SetLines(lines) - } - return fset -} - -// CloneFileSet creates a new FileSet holding all files in fset. It does not -// create copies of the token.Files in fset: they are added to the resulting -// FileSet unmodified. -func CloneFileSet(fset *token.FileSet) *token.FileSet { - var files []*token.File - fset.Iterate(func(f *token.File) bool { - files = append(files, f) - return true - }) - newFileSet := token.NewFileSet() - AddExistingFiles(newFileSet, files) - return newFileSet -} diff --git a/vendor/golang.org/x/tools/internal/typeparams/common.go b/vendor/golang.org/x/tools/internal/typeparams/common.go new file mode 100644 index 0000000000..0b84acc5c7 --- /dev/null +++ b/vendor/golang.org/x/tools/internal/typeparams/common.go @@ -0,0 +1,140 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package typeparams contains common utilities for writing tools that +// interact with generic Go code, as introduced with Go 1.18. It +// supplements the standard library APIs. Notably, the StructuralTerms +// API computes a minimal representation of the structural +// restrictions on a type parameter. +// +// An external version of these APIs is available in the +// golang.org/x/exp/typeparams module. +package typeparams + +import ( + "go/ast" + "go/token" + "go/types" +) + +// UnpackIndexExpr extracts data from AST nodes that represent index +// expressions. +// +// For an ast.IndexExpr, the resulting indices slice will contain exactly one +// index expression. For an ast.IndexListExpr (go1.18+), it may have a variable +// number of index expressions. +// +// For nodes that don't represent index expressions, the first return value of +// UnpackIndexExpr will be nil. +func UnpackIndexExpr(n ast.Node) (x ast.Expr, lbrack token.Pos, indices []ast.Expr, rbrack token.Pos) { + switch e := n.(type) { + case *ast.IndexExpr: + return e.X, e.Lbrack, []ast.Expr{e.Index}, e.Rbrack + case *ast.IndexListExpr: + return e.X, e.Lbrack, e.Indices, e.Rbrack + } + return nil, token.NoPos, nil, token.NoPos +} + +// PackIndexExpr returns an *ast.IndexExpr or *ast.IndexListExpr, depending on +// the cardinality of indices. Calling PackIndexExpr with len(indices) == 0 +// will panic. +func PackIndexExpr(x ast.Expr, lbrack token.Pos, indices []ast.Expr, rbrack token.Pos) ast.Expr { + switch len(indices) { + case 0: + panic("empty indices") + case 1: + return &ast.IndexExpr{ + X: x, + Lbrack: lbrack, + Index: indices[0], + Rbrack: rbrack, + } + default: + return &ast.IndexListExpr{ + X: x, + Lbrack: lbrack, + Indices: indices, + Rbrack: rbrack, + } + } +} + +// IsTypeParam reports whether t is a type parameter (or an alias of one). +func IsTypeParam(t types.Type) bool { + _, ok := types.Unalias(t).(*types.TypeParam) + return ok +} + +// GenericAssignableTo is a generalization of types.AssignableTo that +// implements the following rule for uninstantiated generic types: +// +// If V and T are generic named types, then V is considered assignable to T if, +// for every possible instantiation of V[A_1, ..., A_N], the instantiation +// T[A_1, ..., A_N] is valid and V[A_1, ..., A_N] implements T[A_1, ..., A_N]. +// +// If T has structural constraints, they must be satisfied by V. +// +// For example, consider the following type declarations: +// +// type Interface[T any] interface { +// Accept(T) +// } +// +// type Container[T any] struct { +// Element T +// } +// +// func (c Container[T]) Accept(t T) { c.Element = t } +// +// In this case, GenericAssignableTo reports that instantiations of Container +// are assignable to the corresponding instantiation of Interface. +func GenericAssignableTo(ctxt *types.Context, V, T types.Type) bool { + V = types.Unalias(V) + T = types.Unalias(T) + + // If V and T are not both named, or do not have matching non-empty type + // parameter lists, fall back on types.AssignableTo. + + VN, Vnamed := V.(*types.Named) + TN, Tnamed := T.(*types.Named) + if !Vnamed || !Tnamed { + return types.AssignableTo(V, T) + } + + vtparams := VN.TypeParams() + ttparams := TN.TypeParams() + if vtparams.Len() == 0 || vtparams.Len() != ttparams.Len() || VN.TypeArgs().Len() != 0 || TN.TypeArgs().Len() != 0 { + return types.AssignableTo(V, T) + } + + // V and T have the same (non-zero) number of type params. Instantiate both + // with the type parameters of V. This must always succeed for V, and will + // succeed for T if and only if the type set of each type parameter of V is a + // subset of the type set of the corresponding type parameter of T, meaning + // that every instantiation of V corresponds to a valid instantiation of T. + + // Minor optimization: ensure we share a context across the two + // instantiations below. + if ctxt == nil { + ctxt = types.NewContext() + } + + var targs []types.Type + for i := 0; i < vtparams.Len(); i++ { + targs = append(targs, vtparams.At(i)) + } + + vinst, err := types.Instantiate(ctxt, V, targs, true) + if err != nil { + panic("type parameters should satisfy their own constraints") + } + + tinst, err := types.Instantiate(ctxt, T, targs, true) + if err != nil { + return false + } + + return types.AssignableTo(vinst, tinst) +} diff --git a/vendor/golang.org/x/tools/internal/typeparams/coretype.go b/vendor/golang.org/x/tools/internal/typeparams/coretype.go new file mode 100644 index 0000000000..6e83c6fb1a --- /dev/null +++ b/vendor/golang.org/x/tools/internal/typeparams/coretype.go @@ -0,0 +1,150 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package typeparams + +import ( + "fmt" + "go/types" +) + +// CoreType returns the core type of T or nil if T does not have a core type. +// +// See https://go.dev/ref/spec#Core_types for the definition of a core type. +func CoreType(T types.Type) types.Type { + U := T.Underlying() + if _, ok := U.(*types.Interface); !ok { + return U // for non-interface types, + } + + terms, err := NormalTerms(U) + if len(terms) == 0 || err != nil { + // len(terms) -> empty type set of interface. + // err != nil => U is invalid, exceeds complexity bounds, or has an empty type set. + return nil // no core type. + } + + U = terms[0].Type().Underlying() + var identical int // i in [0,identical) => Identical(U, terms[i].Type().Underlying()) + for identical = 1; identical < len(terms); identical++ { + if !types.Identical(U, terms[identical].Type().Underlying()) { + break + } + } + + if identical == len(terms) { + // https://go.dev/ref/spec#Core_types + // "There is a single type U which is the underlying type of all types in the type set of T" + return U + } + ch, ok := U.(*types.Chan) + if !ok { + return nil // no core type as identical < len(terms) and U is not a channel. + } + // https://go.dev/ref/spec#Core_types + // "the type chan E if T contains only bidirectional channels, or the type chan<- E or + // <-chan E depending on the direction of the directional channels present." + for chans := identical; chans < len(terms); chans++ { + curr, ok := terms[chans].Type().Underlying().(*types.Chan) + if !ok { + return nil + } + if !types.Identical(ch.Elem(), curr.Elem()) { + return nil // channel elements are not identical. + } + if ch.Dir() == types.SendRecv { + // ch is bidirectional. We can safely always use curr's direction. + ch = curr + } else if curr.Dir() != types.SendRecv && ch.Dir() != curr.Dir() { + // ch and curr are not bidirectional and not the same direction. + return nil + } + } + return ch +} + +// NormalTerms returns a slice of terms representing the normalized structural +// type restrictions of a type, if any. +// +// For all types other than *types.TypeParam, *types.Interface, and +// *types.Union, this is just a single term with Tilde() == false and +// Type() == typ. For *types.TypeParam, *types.Interface, and *types.Union, see +// below. +// +// Structural type restrictions of a type parameter are created via +// non-interface types embedded in its constraint interface (directly, or via a +// chain of interface embeddings). For example, in the declaration type +// T[P interface{~int; m()}] int the structural restriction of the type +// parameter P is ~int. +// +// With interface embedding and unions, the specification of structural type +// restrictions may be arbitrarily complex. For example, consider the +// following: +// +// type A interface{ ~string|~[]byte } +// +// type B interface{ int|string } +// +// type C interface { ~string|~int } +// +// type T[P interface{ A|B; C }] int +// +// In this example, the structural type restriction of P is ~string|int: A|B +// expands to ~string|~[]byte|int|string, which reduces to ~string|~[]byte|int, +// which when intersected with C (~string|~int) yields ~string|int. +// +// NormalTerms computes these expansions and reductions, producing a +// "normalized" form of the embeddings. A structural restriction is normalized +// if it is a single union containing no interface terms, and is minimal in the +// sense that removing any term changes the set of types satisfying the +// constraint. It is left as a proof for the reader that, modulo sorting, there +// is exactly one such normalized form. +// +// Because the minimal representation always takes this form, NormalTerms +// returns a slice of tilde terms corresponding to the terms of the union in +// the normalized structural restriction. An error is returned if the type is +// invalid, exceeds complexity bounds, or has an empty type set. In the latter +// case, NormalTerms returns ErrEmptyTypeSet. +// +// NormalTerms makes no guarantees about the order of terms, except that it +// is deterministic. +func NormalTerms(typ types.Type) ([]*types.Term, error) { + switch typ := typ.Underlying().(type) { + case *types.TypeParam: + return StructuralTerms(typ) + case *types.Union: + return UnionTermSet(typ) + case *types.Interface: + return InterfaceTermSet(typ) + default: + return []*types.Term{types.NewTerm(false, typ)}, nil + } +} + +// Deref returns the type of the variable pointed to by t, +// if t's core type is a pointer; otherwise it returns t. +// +// Do not assume that Deref(T)==T implies T is not a pointer: +// consider "type T *T", for example. +// +// TODO(adonovan): ideally this would live in typesinternal, but that +// creates an import cycle. Move there when we melt this package down. +func Deref(t types.Type) types.Type { + if ptr, ok := CoreType(t).(*types.Pointer); ok { + return ptr.Elem() + } + return t +} + +// MustDeref returns the type of the variable pointed to by t. +// It panics if t's core type is not a pointer. +// +// TODO(adonovan): ideally this would live in typesinternal, but that +// creates an import cycle. Move there when we melt this package down. +func MustDeref(t types.Type) types.Type { + if ptr, ok := CoreType(t).(*types.Pointer); ok { + return ptr.Elem() + } + panic(fmt.Sprintf("%v is not a pointer", t)) +} diff --git a/vendor/golang.org/x/tools/internal/typeparams/free.go b/vendor/golang.org/x/tools/internal/typeparams/free.go new file mode 100644 index 0000000000..0ade5c2949 --- /dev/null +++ b/vendor/golang.org/x/tools/internal/typeparams/free.go @@ -0,0 +1,131 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package typeparams + +import ( + "go/types" + + "golang.org/x/tools/internal/aliases" +) + +// Free is a memoization of the set of free type parameters within a +// type. It makes a sequence of calls to [Free.Has] for overlapping +// types more efficient. The zero value is ready for use. +// +// NOTE: Adapted from go/types/infer.go. If it is later exported, factor. +type Free struct { + seen map[types.Type]bool +} + +// Has reports whether the specified type has a free type parameter. +func (w *Free) Has(typ types.Type) (res bool) { + // detect cycles + if x, ok := w.seen[typ]; ok { + return x + } + if w.seen == nil { + w.seen = make(map[types.Type]bool) + } + w.seen[typ] = false + defer func() { + w.seen[typ] = res + }() + + switch t := typ.(type) { + case nil, *types.Basic: // TODO(gri) should nil be handled here? + break + + case *types.Alias: + if aliases.TypeParams(t).Len() > aliases.TypeArgs(t).Len() { + return true // This is an uninstantiated Alias. + } + // The expansion of an alias can have free type parameters, + // whether or not the alias itself has type parameters: + // + // func _[K comparable]() { + // type Set = map[K]bool // free(Set) = {K} + // type MapTo[V] = map[K]V // free(Map[foo]) = {V} + // } + // + // So, we must Unalias. + return w.Has(types.Unalias(t)) + + case *types.Array: + return w.Has(t.Elem()) + + case *types.Slice: + return w.Has(t.Elem()) + + case *types.Struct: + for i, n := 0, t.NumFields(); i < n; i++ { + if w.Has(t.Field(i).Type()) { + return true + } + } + + case *types.Pointer: + return w.Has(t.Elem()) + + case *types.Tuple: + n := t.Len() + for i := 0; i < n; i++ { + if w.Has(t.At(i).Type()) { + return true + } + } + + case *types.Signature: + // t.tparams may not be nil if we are looking at a signature + // of a generic function type (or an interface method) that is + // part of the type we're testing. We don't care about these type + // parameters. + // Similarly, the receiver of a method may declare (rather than + // use) type parameters, we don't care about those either. + // Thus, we only need to look at the input and result parameters. + return w.Has(t.Params()) || w.Has(t.Results()) + + case *types.Interface: + for i, n := 0, t.NumMethods(); i < n; i++ { + if w.Has(t.Method(i).Type()) { + return true + } + } + terms, err := InterfaceTermSet(t) + if err != nil { + return false // ill typed + } + for _, term := range terms { + if w.Has(term.Type()) { + return true + } + } + + case *types.Map: + return w.Has(t.Key()) || w.Has(t.Elem()) + + case *types.Chan: + return w.Has(t.Elem()) + + case *types.Named: + args := t.TypeArgs() + if params := t.TypeParams(); params.Len() > args.Len() { + return true // this is an uninstantiated named type. + } + for i, n := 0, args.Len(); i < n; i++ { + if w.Has(args.At(i)) { + return true + } + } + return w.Has(t.Underlying()) // recurse for types local to parameterized functions + + case *types.TypeParam: + return true + + default: + panic(t) // unreachable + } + + return false +} diff --git a/vendor/golang.org/x/tools/internal/typeparams/normalize.go b/vendor/golang.org/x/tools/internal/typeparams/normalize.go new file mode 100644 index 0000000000..93c80fdc96 --- /dev/null +++ b/vendor/golang.org/x/tools/internal/typeparams/normalize.go @@ -0,0 +1,218 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package typeparams + +import ( + "errors" + "fmt" + "go/types" + "os" + "strings" +) + +//go:generate go run copytermlist.go + +const debug = false + +var ErrEmptyTypeSet = errors.New("empty type set") + +// StructuralTerms returns a slice of terms representing the normalized +// structural type restrictions of a type parameter, if any. +// +// Structural type restrictions of a type parameter are created via +// non-interface types embedded in its constraint interface (directly, or via a +// chain of interface embeddings). For example, in the declaration +// +// type T[P interface{~int; m()}] int +// +// the structural restriction of the type parameter P is ~int. +// +// With interface embedding and unions, the specification of structural type +// restrictions may be arbitrarily complex. For example, consider the +// following: +// +// type A interface{ ~string|~[]byte } +// +// type B interface{ int|string } +// +// type C interface { ~string|~int } +// +// type T[P interface{ A|B; C }] int +// +// In this example, the structural type restriction of P is ~string|int: A|B +// expands to ~string|~[]byte|int|string, which reduces to ~string|~[]byte|int, +// which when intersected with C (~string|~int) yields ~string|int. +// +// StructuralTerms computes these expansions and reductions, producing a +// "normalized" form of the embeddings. A structural restriction is normalized +// if it is a single union containing no interface terms, and is minimal in the +// sense that removing any term changes the set of types satisfying the +// constraint. It is left as a proof for the reader that, modulo sorting, there +// is exactly one such normalized form. +// +// Because the minimal representation always takes this form, StructuralTerms +// returns a slice of tilde terms corresponding to the terms of the union in +// the normalized structural restriction. An error is returned if the +// constraint interface is invalid, exceeds complexity bounds, or has an empty +// type set. In the latter case, StructuralTerms returns ErrEmptyTypeSet. +// +// StructuralTerms makes no guarantees about the order of terms, except that it +// is deterministic. +func StructuralTerms(tparam *types.TypeParam) ([]*types.Term, error) { + constraint := tparam.Constraint() + if constraint == nil { + return nil, fmt.Errorf("%s has nil constraint", tparam) + } + iface, _ := constraint.Underlying().(*types.Interface) + if iface == nil { + return nil, fmt.Errorf("constraint is %T, not *types.Interface", constraint.Underlying()) + } + return InterfaceTermSet(iface) +} + +// InterfaceTermSet computes the normalized terms for a constraint interface, +// returning an error if the term set cannot be computed or is empty. In the +// latter case, the error will be ErrEmptyTypeSet. +// +// See the documentation of StructuralTerms for more information on +// normalization. +func InterfaceTermSet(iface *types.Interface) ([]*types.Term, error) { + return computeTermSet(iface) +} + +// UnionTermSet computes the normalized terms for a union, returning an error +// if the term set cannot be computed or is empty. In the latter case, the +// error will be ErrEmptyTypeSet. +// +// See the documentation of StructuralTerms for more information on +// normalization. +func UnionTermSet(union *types.Union) ([]*types.Term, error) { + return computeTermSet(union) +} + +func computeTermSet(typ types.Type) ([]*types.Term, error) { + tset, err := computeTermSetInternal(typ, make(map[types.Type]*termSet), 0) + if err != nil { + return nil, err + } + if tset.terms.isEmpty() { + return nil, ErrEmptyTypeSet + } + if tset.terms.isAll() { + return nil, nil + } + var terms []*types.Term + for _, term := range tset.terms { + terms = append(terms, types.NewTerm(term.tilde, term.typ)) + } + return terms, nil +} + +// A termSet holds the normalized set of terms for a given type. +// +// The name termSet is intentionally distinct from 'type set': a type set is +// all types that implement a type (and includes method restrictions), whereas +// a term set just represents the structural restrictions on a type. +type termSet struct { + complete bool + terms termlist +} + +func indentf(depth int, format string, args ...interface{}) { + fmt.Fprintf(os.Stderr, strings.Repeat(".", depth)+format+"\n", args...) +} + +func computeTermSetInternal(t types.Type, seen map[types.Type]*termSet, depth int) (res *termSet, err error) { + if t == nil { + panic("nil type") + } + + if debug { + indentf(depth, "%s", t.String()) + defer func() { + if err != nil { + indentf(depth, "=> %s", err) + } else { + indentf(depth, "=> %s", res.terms.String()) + } + }() + } + + const maxTermCount = 100 + if tset, ok := seen[t]; ok { + if !tset.complete { + return nil, fmt.Errorf("cycle detected in the declaration of %s", t) + } + return tset, nil + } + + // Mark the current type as seen to avoid infinite recursion. + tset := new(termSet) + defer func() { + tset.complete = true + }() + seen[t] = tset + + switch u := t.Underlying().(type) { + case *types.Interface: + // The term set of an interface is the intersection of the term sets of its + // embedded types. + tset.terms = allTermlist + for i := 0; i < u.NumEmbeddeds(); i++ { + embedded := u.EmbeddedType(i) + if _, ok := embedded.Underlying().(*types.TypeParam); ok { + return nil, fmt.Errorf("invalid embedded type %T", embedded) + } + tset2, err := computeTermSetInternal(embedded, seen, depth+1) + if err != nil { + return nil, err + } + tset.terms = tset.terms.intersect(tset2.terms) + } + case *types.Union: + // The term set of a union is the union of term sets of its terms. + tset.terms = nil + for i := 0; i < u.Len(); i++ { + t := u.Term(i) + var terms termlist + switch t.Type().Underlying().(type) { + case *types.Interface: + tset2, err := computeTermSetInternal(t.Type(), seen, depth+1) + if err != nil { + return nil, err + } + terms = tset2.terms + case *types.TypeParam, *types.Union: + // A stand-alone type parameter or union is not permitted as union + // term. + return nil, fmt.Errorf("invalid union term %T", t) + default: + if t.Type() == types.Typ[types.Invalid] { + continue + } + terms = termlist{{t.Tilde(), t.Type()}} + } + tset.terms = tset.terms.union(terms) + if len(tset.terms) > maxTermCount { + return nil, fmt.Errorf("exceeded max term count %d", maxTermCount) + } + } + case *types.TypeParam: + panic("unreachable") + default: + // For all other types, the term set is just a single non-tilde term + // holding the type itself. + if u != types.Typ[types.Invalid] { + tset.terms = termlist{{false, t}} + } + } + return tset, nil +} + +// under is a facade for the go/types internal function of the same name. It is +// used by typeterm.go. +func under(t types.Type) types.Type { + return t.Underlying() +} diff --git a/vendor/golang.org/x/tools/internal/typeparams/termlist.go b/vendor/golang.org/x/tools/internal/typeparams/termlist.go new file mode 100644 index 0000000000..cbd12f8013 --- /dev/null +++ b/vendor/golang.org/x/tools/internal/typeparams/termlist.go @@ -0,0 +1,163 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Code generated by copytermlist.go DO NOT EDIT. + +package typeparams + +import ( + "bytes" + "go/types" +) + +// A termlist represents the type set represented by the union +// t1 ∪ y2 ∪ ... tn of the type sets of the terms t1 to tn. +// A termlist is in normal form if all terms are disjoint. +// termlist operations don't require the operands to be in +// normal form. +type termlist []*term + +// allTermlist represents the set of all types. +// It is in normal form. +var allTermlist = termlist{new(term)} + +// String prints the termlist exactly (without normalization). +func (xl termlist) String() string { + if len(xl) == 0 { + return "∅" + } + var buf bytes.Buffer + for i, x := range xl { + if i > 0 { + buf.WriteString(" | ") + } + buf.WriteString(x.String()) + } + return buf.String() +} + +// isEmpty reports whether the termlist xl represents the empty set of types. +func (xl termlist) isEmpty() bool { + // If there's a non-nil term, the entire list is not empty. + // If the termlist is in normal form, this requires at most + // one iteration. + for _, x := range xl { + if x != nil { + return false + } + } + return true +} + +// isAll reports whether the termlist xl represents the set of all types. +func (xl termlist) isAll() bool { + // If there's a 𝓤 term, the entire list is 𝓤. + // If the termlist is in normal form, this requires at most + // one iteration. + for _, x := range xl { + if x != nil && x.typ == nil { + return true + } + } + return false +} + +// norm returns the normal form of xl. +func (xl termlist) norm() termlist { + // Quadratic algorithm, but good enough for now. + // TODO(gri) fix asymptotic performance + used := make([]bool, len(xl)) + var rl termlist + for i, xi := range xl { + if xi == nil || used[i] { + continue + } + for j := i + 1; j < len(xl); j++ { + xj := xl[j] + if xj == nil || used[j] { + continue + } + if u1, u2 := xi.union(xj); u2 == nil { + // If we encounter a 𝓤 term, the entire list is 𝓤. + // Exit early. + // (Note that this is not just an optimization; + // if we continue, we may end up with a 𝓤 term + // and other terms and the result would not be + // in normal form.) + if u1.typ == nil { + return allTermlist + } + xi = u1 + used[j] = true // xj is now unioned into xi - ignore it in future iterations + } + } + rl = append(rl, xi) + } + return rl +} + +// union returns the union xl ∪ yl. +func (xl termlist) union(yl termlist) termlist { + return append(xl, yl...).norm() +} + +// intersect returns the intersection xl ∩ yl. +func (xl termlist) intersect(yl termlist) termlist { + if xl.isEmpty() || yl.isEmpty() { + return nil + } + + // Quadratic algorithm, but good enough for now. + // TODO(gri) fix asymptotic performance + var rl termlist + for _, x := range xl { + for _, y := range yl { + if r := x.intersect(y); r != nil { + rl = append(rl, r) + } + } + } + return rl.norm() +} + +// equal reports whether xl and yl represent the same type set. +func (xl termlist) equal(yl termlist) bool { + // TODO(gri) this should be more efficient + return xl.subsetOf(yl) && yl.subsetOf(xl) +} + +// includes reports whether t ∈ xl. +func (xl termlist) includes(t types.Type) bool { + for _, x := range xl { + if x.includes(t) { + return true + } + } + return false +} + +// supersetOf reports whether y ⊆ xl. +func (xl termlist) supersetOf(y *term) bool { + for _, x := range xl { + if y.subsetOf(x) { + return true + } + } + return false +} + +// subsetOf reports whether xl ⊆ yl. +func (xl termlist) subsetOf(yl termlist) bool { + if yl.isEmpty() { + return xl.isEmpty() + } + + // each term x of xl must be a subset of yl + for _, x := range xl { + if !yl.supersetOf(x) { + return false // x is not a subset yl + } + } + return true +} diff --git a/vendor/golang.org/x/tools/internal/typeparams/typeterm.go b/vendor/golang.org/x/tools/internal/typeparams/typeterm.go new file mode 100644 index 0000000000..7350bb702a --- /dev/null +++ b/vendor/golang.org/x/tools/internal/typeparams/typeterm.go @@ -0,0 +1,169 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Code generated by copytermlist.go DO NOT EDIT. + +package typeparams + +import "go/types" + +// A term describes elementary type sets: +// +// ∅: (*term)(nil) == ∅ // set of no types (empty set) +// 𝓤: &term{} == 𝓤 // set of all types (𝓤niverse) +// T: &term{false, T} == {T} // set of type T +// ~t: &term{true, t} == {t' | under(t') == t} // set of types with underlying type t +type term struct { + tilde bool // valid if typ != nil + typ types.Type +} + +func (x *term) String() string { + switch { + case x == nil: + return "∅" + case x.typ == nil: + return "𝓤" + case x.tilde: + return "~" + x.typ.String() + default: + return x.typ.String() + } +} + +// equal reports whether x and y represent the same type set. +func (x *term) equal(y *term) bool { + // easy cases + switch { + case x == nil || y == nil: + return x == y + case x.typ == nil || y.typ == nil: + return x.typ == y.typ + } + // ∅ ⊂ x, y ⊂ 𝓤 + + return x.tilde == y.tilde && types.Identical(x.typ, y.typ) +} + +// union returns the union x ∪ y: zero, one, or two non-nil terms. +func (x *term) union(y *term) (_, _ *term) { + // easy cases + switch { + case x == nil && y == nil: + return nil, nil // ∅ ∪ ∅ == ∅ + case x == nil: + return y, nil // ∅ ∪ y == y + case y == nil: + return x, nil // x ∪ ∅ == x + case x.typ == nil: + return x, nil // 𝓤 ∪ y == 𝓤 + case y.typ == nil: + return y, nil // x ∪ 𝓤 == 𝓤 + } + // ∅ ⊂ x, y ⊂ 𝓤 + + if x.disjoint(y) { + return x, y // x ∪ y == (x, y) if x ∩ y == ∅ + } + // x.typ == y.typ + + // ~t ∪ ~t == ~t + // ~t ∪ T == ~t + // T ∪ ~t == ~t + // T ∪ T == T + if x.tilde || !y.tilde { + return x, nil + } + return y, nil +} + +// intersect returns the intersection x ∩ y. +func (x *term) intersect(y *term) *term { + // easy cases + switch { + case x == nil || y == nil: + return nil // ∅ ∩ y == ∅ and ∩ ∅ == ∅ + case x.typ == nil: + return y // 𝓤 ∩ y == y + case y.typ == nil: + return x // x ∩ 𝓤 == x + } + // ∅ ⊂ x, y ⊂ 𝓤 + + if x.disjoint(y) { + return nil // x ∩ y == ∅ if x ∩ y == ∅ + } + // x.typ == y.typ + + // ~t ∩ ~t == ~t + // ~t ∩ T == T + // T ∩ ~t == T + // T ∩ T == T + if !x.tilde || y.tilde { + return x + } + return y +} + +// includes reports whether t ∈ x. +func (x *term) includes(t types.Type) bool { + // easy cases + switch { + case x == nil: + return false // t ∈ ∅ == false + case x.typ == nil: + return true // t ∈ 𝓤 == true + } + // ∅ ⊂ x ⊂ 𝓤 + + u := t + if x.tilde { + u = under(u) + } + return types.Identical(x.typ, u) +} + +// subsetOf reports whether x ⊆ y. +func (x *term) subsetOf(y *term) bool { + // easy cases + switch { + case x == nil: + return true // ∅ ⊆ y == true + case y == nil: + return false // x ⊆ ∅ == false since x != ∅ + case y.typ == nil: + return true // x ⊆ 𝓤 == true + case x.typ == nil: + return false // 𝓤 ⊆ y == false since y != 𝓤 + } + // ∅ ⊂ x, y ⊂ 𝓤 + + if x.disjoint(y) { + return false // x ⊆ y == false if x ∩ y == ∅ + } + // x.typ == y.typ + + // ~t ⊆ ~t == true + // ~t ⊆ T == false + // T ⊆ ~t == true + // T ⊆ T == true + return !x.tilde || y.tilde +} + +// disjoint reports whether x ∩ y == ∅. +// x.typ and y.typ must not be nil. +func (x *term) disjoint(y *term) bool { + if debug && (x.typ == nil || y.typ == nil) { + panic("invalid argument(s)") + } + ux := x.typ + if y.tilde { + ux = under(ux) + } + uy := y.typ + if x.tilde { + uy = under(uy) + } + return !types.Identical(ux, uy) +} diff --git a/vendor/golang.org/x/tools/internal/typesinternal/element.go b/vendor/golang.org/x/tools/internal/typesinternal/element.go new file mode 100644 index 0000000000..4957f02164 --- /dev/null +++ b/vendor/golang.org/x/tools/internal/typesinternal/element.go @@ -0,0 +1,133 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package typesinternal + +import ( + "fmt" + "go/types" + + "golang.org/x/tools/go/types/typeutil" +) + +// ForEachElement calls f for type T and each type reachable from its +// type through reflection. It does this by recursively stripping off +// type constructors; in addition, for each named type N, the type *N +// is added to the result as it may have additional methods. +// +// The caller must provide an initially empty set used to de-duplicate +// identical types, potentially across multiple calls to ForEachElement. +// (Its final value holds all the elements seen, matching the arguments +// passed to f.) +// +// TODO(adonovan): share/harmonize with go/callgraph/rta. +func ForEachElement(rtypes *typeutil.Map, msets *typeutil.MethodSetCache, T types.Type, f func(types.Type)) { + var visit func(T types.Type, skip bool) + visit = func(T types.Type, skip bool) { + if !skip { + if seen, _ := rtypes.Set(T, true).(bool); seen { + return // de-dup + } + + f(T) // notify caller of new element type + } + + // Recursion over signatures of each method. + tmset := msets.MethodSet(T) + for i := 0; i < tmset.Len(); i++ { + sig := tmset.At(i).Type().(*types.Signature) + // It is tempting to call visit(sig, false) + // but, as noted in golang.org/cl/65450043, + // the Signature.Recv field is ignored by + // types.Identical and typeutil.Map, which + // is confusing at best. + // + // More importantly, the true signature rtype + // reachable from a method using reflection + // has no receiver but an extra ordinary parameter. + // For the Read method of io.Reader we want: + // func(Reader, []byte) (int, error) + // but here sig is: + // func([]byte) (int, error) + // with .Recv = Reader (though it is hard to + // notice because it doesn't affect Signature.String + // or types.Identical). + // + // TODO(adonovan): construct and visit the correct + // non-method signature with an extra parameter + // (though since unnamed func types have no methods + // there is essentially no actual demand for this). + // + // TODO(adonovan): document whether or not it is + // safe to skip non-exported methods (as RTA does). + visit(sig.Params(), true) // skip the Tuple + visit(sig.Results(), true) // skip the Tuple + } + + switch T := T.(type) { + case *types.Alias: + visit(types.Unalias(T), skip) // emulates the pre-Alias behavior + + case *types.Basic: + // nop + + case *types.Interface: + // nop---handled by recursion over method set. + + case *types.Pointer: + visit(T.Elem(), false) + + case *types.Slice: + visit(T.Elem(), false) + + case *types.Chan: + visit(T.Elem(), false) + + case *types.Map: + visit(T.Key(), false) + visit(T.Elem(), false) + + case *types.Signature: + if T.Recv() != nil { + panic(fmt.Sprintf("Signature %s has Recv %s", T, T.Recv())) + } + visit(T.Params(), true) // skip the Tuple + visit(T.Results(), true) // skip the Tuple + + case *types.Named: + // A pointer-to-named type can be derived from a named + // type via reflection. It may have methods too. + visit(types.NewPointer(T), false) + + // Consider 'type T struct{S}' where S has methods. + // Reflection provides no way to get from T to struct{S}, + // only to S, so the method set of struct{S} is unwanted, + // so set 'skip' flag during recursion. + visit(T.Underlying(), true) // skip the unnamed type + + case *types.Array: + visit(T.Elem(), false) + + case *types.Struct: + for i, n := 0, T.NumFields(); i < n; i++ { + // TODO(adonovan): document whether or not + // it is safe to skip non-exported fields. + visit(T.Field(i).Type(), false) + } + + case *types.Tuple: + for i, n := 0, T.Len(); i < n; i++ { + visit(T.At(i).Type(), false) + } + + case *types.TypeParam, *types.Union: + // forEachReachable must not be called on parameterized types. + panic(T) + + default: + panic(T) + } + } + visit(T, false) +} diff --git a/vendor/golang.org/x/tools/internal/typesinternal/errorcode.go b/vendor/golang.org/x/tools/internal/typesinternal/errorcode.go index 834e05381c..131caab284 100644 --- a/vendor/golang.org/x/tools/internal/typesinternal/errorcode.go +++ b/vendor/golang.org/x/tools/internal/typesinternal/errorcode.go @@ -838,7 +838,7 @@ const ( // InvalidCap occurs when an argument to the cap built-in function is not of // supported type. // - // See https://golang.org/ref/spec#Lengthand_capacity for information on + // See https://golang.org/ref/spec#Length_and_capacity for information on // which underlying types are supported as arguments to cap and len. // // Example: @@ -859,7 +859,7 @@ const ( // InvalidCopy occurs when the arguments are not of slice type or do not // have compatible type. // - // See https://golang.org/ref/spec#Appendingand_copying_slices for more + // See https://golang.org/ref/spec#Appending_and_copying_slices for more // information on the type requirements for the copy built-in. // // Example: @@ -897,7 +897,7 @@ const ( // InvalidLen occurs when an argument to the len built-in function is not of // supported type. // - // See https://golang.org/ref/spec#Lengthand_capacity for information on + // See https://golang.org/ref/spec#Length_and_capacity for information on // which underlying types are supported as arguments to cap and len. // // Example: @@ -914,7 +914,7 @@ const ( // InvalidMake occurs when make is called with an unsupported type argument. // - // See https://golang.org/ref/spec#Makingslices_maps_and_channels for + // See https://golang.org/ref/spec#Making_slices_maps_and_channels for // information on the types that may be created using make. // // Example: diff --git a/vendor/golang.org/x/tools/internal/typesinternal/recv.go b/vendor/golang.org/x/tools/internal/typesinternal/recv.go index fea7c8b75e..ba6f4f4ebd 100644 --- a/vendor/golang.org/x/tools/internal/typesinternal/recv.go +++ b/vendor/golang.org/x/tools/internal/typesinternal/recv.go @@ -6,8 +6,6 @@ package typesinternal import ( "go/types" - - "golang.org/x/tools/internal/aliases" ) // ReceiverNamed returns the named type (if any) associated with the @@ -15,11 +13,11 @@ import ( // It also reports whether a Pointer was present. func ReceiverNamed(recv *types.Var) (isPtr bool, named *types.Named) { t := recv.Type() - if ptr, ok := aliases.Unalias(t).(*types.Pointer); ok { + if ptr, ok := types.Unalias(t).(*types.Pointer); ok { isPtr = true t = ptr.Elem() } - named, _ = aliases.Unalias(t).(*types.Named) + named, _ = types.Unalias(t).(*types.Named) return } @@ -36,7 +34,7 @@ func ReceiverNamed(recv *types.Var) (isPtr bool, named *types.Named) { // indirection from the type, regardless of named types (analogous to // a LOAD instruction). func Unpointer(t types.Type) types.Type { - if ptr, ok := aliases.Unalias(t).(*types.Pointer); ok { + if ptr, ok := types.Unalias(t).(*types.Pointer); ok { return ptr.Elem() } return t diff --git a/vendor/golang.org/x/tools/internal/typesinternal/types.go b/vendor/golang.org/x/tools/internal/typesinternal/types.go index 8392328612..df3ea52125 100644 --- a/vendor/golang.org/x/tools/internal/typesinternal/types.go +++ b/vendor/golang.org/x/tools/internal/typesinternal/types.go @@ -11,6 +11,8 @@ import ( "go/types" "reflect" "unsafe" + + "golang.org/x/tools/internal/aliases" ) func SetUsesCgo(conf *types.Config) bool { @@ -63,3 +65,57 @@ func NameRelativeTo(pkg *types.Package) types.Qualifier { return other.Name() } } + +// A NamedOrAlias is a [types.Type] that is named (as +// defined by the spec) and capable of bearing type parameters: it +// abstracts aliases ([types.Alias]) and defined types +// ([types.Named]). +// +// Every type declared by an explicit "type" declaration is a +// NamedOrAlias. (Built-in type symbols may additionally +// have type [types.Basic], which is not a NamedOrAlias, +// though the spec regards them as "named".) +// +// NamedOrAlias cannot expose the Origin method, because +// [types.Alias.Origin] and [types.Named.Origin] have different +// (covariant) result types; use [Origin] instead. +type NamedOrAlias interface { + types.Type + Obj() *types.TypeName +} + +// TypeParams is a light shim around t.TypeParams(). +// (go/types.Alias).TypeParams requires >= 1.23. +func TypeParams(t NamedOrAlias) *types.TypeParamList { + switch t := t.(type) { + case *types.Alias: + return aliases.TypeParams(t) + case *types.Named: + return t.TypeParams() + } + return nil +} + +// TypeArgs is a light shim around t.TypeArgs(). +// (go/types.Alias).TypeArgs requires >= 1.23. +func TypeArgs(t NamedOrAlias) *types.TypeList { + switch t := t.(type) { + case *types.Alias: + return aliases.TypeArgs(t) + case *types.Named: + return t.TypeArgs() + } + return nil +} + +// Origin returns the generic type of the Named or Alias type t if it +// is instantiated, otherwise it returns t. +func Origin(t NamedOrAlias) NamedOrAlias { + switch t := t.(type) { + case *types.Alias: + return aliases.Origin(t) + case *types.Named: + return t.Origin() + } + return t +} diff --git a/vendor/golang.org/x/tools/internal/typesinternal/zerovalue.go b/vendor/golang.org/x/tools/internal/typesinternal/zerovalue.go new file mode 100644 index 0000000000..1066980649 --- /dev/null +++ b/vendor/golang.org/x/tools/internal/typesinternal/zerovalue.go @@ -0,0 +1,282 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package typesinternal + +import ( + "fmt" + "go/ast" + "go/token" + "go/types" + "strconv" + "strings" +) + +// ZeroString returns the string representation of the "zero" value of the type t. +// This string can be used on the right-hand side of an assignment where the +// left-hand side has that explicit type. +// Exception: This does not apply to tuples. Their string representation is +// informational only and cannot be used in an assignment. +// When assigning to a wider type (such as 'any'), it's the caller's +// responsibility to handle any necessary type conversions. +// See [ZeroExpr] for a variant that returns an [ast.Expr]. +func ZeroString(t types.Type, qf types.Qualifier) string { + switch t := t.(type) { + case *types.Basic: + switch { + case t.Info()&types.IsBoolean != 0: + return "false" + case t.Info()&types.IsNumeric != 0: + return "0" + case t.Info()&types.IsString != 0: + return `""` + case t.Kind() == types.UnsafePointer: + fallthrough + case t.Kind() == types.UntypedNil: + return "nil" + default: + panic(fmt.Sprint("ZeroString for unexpected type:", t)) + } + + case *types.Pointer, *types.Slice, *types.Interface, *types.Chan, *types.Map, *types.Signature: + return "nil" + + case *types.Named, *types.Alias: + switch under := t.Underlying().(type) { + case *types.Struct, *types.Array: + return types.TypeString(t, qf) + "{}" + default: + return ZeroString(under, qf) + } + + case *types.Array, *types.Struct: + return types.TypeString(t, qf) + "{}" + + case *types.TypeParam: + // Assumes func new is not shadowed. + return "*new(" + types.TypeString(t, qf) + ")" + + case *types.Tuple: + // Tuples are not normal values. + // We are currently format as "(t[0], ..., t[n])". Could be something else. + components := make([]string, t.Len()) + for i := 0; i < t.Len(); i++ { + components[i] = ZeroString(t.At(i).Type(), qf) + } + return "(" + strings.Join(components, ", ") + ")" + + case *types.Union: + // Variables of these types cannot be created, so it makes + // no sense to ask for their zero value. + panic(fmt.Sprintf("invalid type for a variable: %v", t)) + + default: + panic(t) // unreachable. + } +} + +// ZeroExpr returns the ast.Expr representation of the "zero" value of the type t. +// ZeroExpr is defined for types that are suitable for variables. +// It may panic for other types such as Tuple or Union. +// See [ZeroString] for a variant that returns a string. +func ZeroExpr(f *ast.File, pkg *types.Package, typ types.Type) ast.Expr { + switch t := typ.(type) { + case *types.Basic: + switch { + case t.Info()&types.IsBoolean != 0: + return &ast.Ident{Name: "false"} + case t.Info()&types.IsNumeric != 0: + return &ast.BasicLit{Kind: token.INT, Value: "0"} + case t.Info()&types.IsString != 0: + return &ast.BasicLit{Kind: token.STRING, Value: `""`} + case t.Kind() == types.UnsafePointer: + fallthrough + case t.Kind() == types.UntypedNil: + return ast.NewIdent("nil") + default: + panic(fmt.Sprint("ZeroExpr for unexpected type:", t)) + } + + case *types.Pointer, *types.Slice, *types.Interface, *types.Chan, *types.Map, *types.Signature: + return ast.NewIdent("nil") + + case *types.Named, *types.Alias: + switch under := t.Underlying().(type) { + case *types.Struct, *types.Array: + return &ast.CompositeLit{ + Type: TypeExpr(f, pkg, typ), + } + default: + return ZeroExpr(f, pkg, under) + } + + case *types.Array, *types.Struct: + return &ast.CompositeLit{ + Type: TypeExpr(f, pkg, typ), + } + + case *types.TypeParam: + return &ast.StarExpr{ // *new(T) + X: &ast.CallExpr{ + // Assumes func new is not shadowed. + Fun: ast.NewIdent("new"), + Args: []ast.Expr{ + ast.NewIdent(t.Obj().Name()), + }, + }, + } + + case *types.Tuple: + // Unlike ZeroString, there is no ast.Expr can express tuple by + // "(t[0], ..., t[n])". + panic(fmt.Sprintf("invalid type for a variable: %v", t)) + + case *types.Union: + // Variables of these types cannot be created, so it makes + // no sense to ask for their zero value. + panic(fmt.Sprintf("invalid type for a variable: %v", t)) + + default: + panic(t) // unreachable. + } +} + +// IsZeroExpr uses simple syntactic heuristics to report whether expr +// is a obvious zero value, such as 0, "", nil, or false. +// It cannot do better without type information. +func IsZeroExpr(expr ast.Expr) bool { + switch e := expr.(type) { + case *ast.BasicLit: + return e.Value == "0" || e.Value == `""` + case *ast.Ident: + return e.Name == "nil" || e.Name == "false" + default: + return false + } +} + +// TypeExpr returns syntax for the specified type. References to named types +// from packages other than pkg are qualified by an appropriate package name, as +// defined by the import environment of file. +// It may panic for types such as Tuple or Union. +func TypeExpr(f *ast.File, pkg *types.Package, typ types.Type) ast.Expr { + switch t := typ.(type) { + case *types.Basic: + switch t.Kind() { + case types.UnsafePointer: + // TODO(hxjiang): replace the implementation with types.Qualifier. + return &ast.SelectorExpr{X: ast.NewIdent("unsafe"), Sel: ast.NewIdent("Pointer")} + default: + return ast.NewIdent(t.Name()) + } + + case *types.Pointer: + return &ast.UnaryExpr{ + Op: token.MUL, + X: TypeExpr(f, pkg, t.Elem()), + } + + case *types.Array: + return &ast.ArrayType{ + Len: &ast.BasicLit{ + Kind: token.INT, + Value: fmt.Sprintf("%d", t.Len()), + }, + Elt: TypeExpr(f, pkg, t.Elem()), + } + + case *types.Slice: + return &ast.ArrayType{ + Elt: TypeExpr(f, pkg, t.Elem()), + } + + case *types.Map: + return &ast.MapType{ + Key: TypeExpr(f, pkg, t.Key()), + Value: TypeExpr(f, pkg, t.Elem()), + } + + case *types.Chan: + dir := ast.ChanDir(t.Dir()) + if t.Dir() == types.SendRecv { + dir = ast.SEND | ast.RECV + } + return &ast.ChanType{ + Dir: dir, + Value: TypeExpr(f, pkg, t.Elem()), + } + + case *types.Signature: + var params []*ast.Field + for i := 0; i < t.Params().Len(); i++ { + params = append(params, &ast.Field{ + Type: TypeExpr(f, pkg, t.Params().At(i).Type()), + Names: []*ast.Ident{ + { + Name: t.Params().At(i).Name(), + }, + }, + }) + } + if t.Variadic() { + last := params[len(params)-1] + last.Type = &ast.Ellipsis{Elt: last.Type.(*ast.ArrayType).Elt} + } + var returns []*ast.Field + for i := 0; i < t.Results().Len(); i++ { + returns = append(returns, &ast.Field{ + Type: TypeExpr(f, pkg, t.Results().At(i).Type()), + }) + } + return &ast.FuncType{ + Params: &ast.FieldList{ + List: params, + }, + Results: &ast.FieldList{ + List: returns, + }, + } + + case interface{ Obj() *types.TypeName }: // *types.{Alias,Named,TypeParam} + switch t.Obj().Pkg() { + case pkg, nil: + return ast.NewIdent(t.Obj().Name()) + } + pkgName := t.Obj().Pkg().Name() + + // TODO(hxjiang): replace the implementation with types.Qualifier. + // If the file already imports the package under another name, use that. + for _, cand := range f.Imports { + if path, _ := strconv.Unquote(cand.Path.Value); path == t.Obj().Pkg().Path() { + if cand.Name != nil && cand.Name.Name != "" { + pkgName = cand.Name.Name + } + } + } + if pkgName == "." { + return ast.NewIdent(t.Obj().Name()) + } + return &ast.SelectorExpr{ + X: ast.NewIdent(pkgName), + Sel: ast.NewIdent(t.Obj().Name()), + } + + case *types.Struct: + return ast.NewIdent(t.String()) + + case *types.Interface: + return ast.NewIdent(t.String()) + + case *types.Union: + // TODO(hxjiang): handle the union through syntax (~A | ... | ~Z). + // Remove nil check when calling typesinternal.TypeExpr. + return nil + + case *types.Tuple: + panic("invalid input type types.Tuple") + + default: + panic("unreachable") + } +} diff --git a/vendor/golang.org/x/tools/internal/versions/constraint.go b/vendor/golang.org/x/tools/internal/versions/constraint.go deleted file mode 100644 index 179063d484..0000000000 --- a/vendor/golang.org/x/tools/internal/versions/constraint.go +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2024 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package versions - -import "go/build/constraint" - -// ConstraintGoVersion is constraint.GoVersion (if built with go1.21+). -// Otherwise nil. -// -// Deprecate once x/tools is after go1.21. -var ConstraintGoVersion func(x constraint.Expr) string diff --git a/vendor/golang.org/x/tools/internal/versions/constraint_go121.go b/vendor/golang.org/x/tools/internal/versions/constraint_go121.go deleted file mode 100644 index 38011407d5..0000000000 --- a/vendor/golang.org/x/tools/internal/versions/constraint_go121.go +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2024 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build go1.21 -// +build go1.21 - -package versions - -import "go/build/constraint" - -func init() { - ConstraintGoVersion = constraint.GoVersion -} diff --git a/vendor/golang.org/x/tools/internal/versions/toolchain.go b/vendor/golang.org/x/tools/internal/versions/toolchain.go deleted file mode 100644 index 377bf7a53b..0000000000 --- a/vendor/golang.org/x/tools/internal/versions/toolchain.go +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2024 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package versions - -// toolchain is maximum version (<1.22) that the go toolchain used -// to build the current tool is known to support. -// -// When a tool is built with >=1.22, the value of toolchain is unused. -// -// x/tools does not support building with go <1.18. So we take this -// as the minimum possible maximum. -var toolchain string = Go1_18 diff --git a/vendor/golang.org/x/tools/internal/versions/toolchain_go119.go b/vendor/golang.org/x/tools/internal/versions/toolchain_go119.go deleted file mode 100644 index f65beed9d8..0000000000 --- a/vendor/golang.org/x/tools/internal/versions/toolchain_go119.go +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2024 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build go1.19 -// +build go1.19 - -package versions - -func init() { - if Compare(toolchain, Go1_19) < 0 { - toolchain = Go1_19 - } -} diff --git a/vendor/golang.org/x/tools/internal/versions/toolchain_go120.go b/vendor/golang.org/x/tools/internal/versions/toolchain_go120.go deleted file mode 100644 index 1a9efa126c..0000000000 --- a/vendor/golang.org/x/tools/internal/versions/toolchain_go120.go +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2024 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build go1.20 -// +build go1.20 - -package versions - -func init() { - if Compare(toolchain, Go1_20) < 0 { - toolchain = Go1_20 - } -} diff --git a/vendor/golang.org/x/tools/internal/versions/toolchain_go121.go b/vendor/golang.org/x/tools/internal/versions/toolchain_go121.go deleted file mode 100644 index b7ef216dfe..0000000000 --- a/vendor/golang.org/x/tools/internal/versions/toolchain_go121.go +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2024 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build go1.21 -// +build go1.21 - -package versions - -func init() { - if Compare(toolchain, Go1_21) < 0 { - toolchain = Go1_21 - } -} diff --git a/vendor/golang.org/x/tools/internal/versions/types.go b/vendor/golang.org/x/tools/internal/versions/types.go index 562eef21fa..0fc10ce4eb 100644 --- a/vendor/golang.org/x/tools/internal/versions/types.go +++ b/vendor/golang.org/x/tools/internal/versions/types.go @@ -5,15 +5,29 @@ package versions import ( + "go/ast" "go/types" ) -// GoVersion returns the Go version of the type package. -// It returns zero if no version can be determined. -func GoVersion(pkg *types.Package) string { - // TODO(taking): x/tools can call GoVersion() [from 1.21] after 1.25. - if pkg, ok := any(pkg).(interface{ GoVersion() string }); ok { - return pkg.GoVersion() +// FileVersion returns a file's Go version. +// The reported version is an unknown Future version if a +// version cannot be determined. +func FileVersion(info *types.Info, file *ast.File) string { + // In tools built with Go >= 1.22, the Go version of a file + // follow a cascades of sources: + // 1) types.Info.FileVersion, which follows the cascade: + // 1.a) file version (ast.File.GoVersion), + // 1.b) the package version (types.Config.GoVersion), or + // 2) is some unknown Future version. + // + // File versions require a valid package version to be provided to types + // in Config.GoVersion. Config.GoVersion is either from the package's module + // or the toolchain (go run). This value should be provided by go/packages + // or unitchecker.Config.GoVersion. + if v := info.FileVersions[file]; IsValid(v) { + return v } - return "" + // Note: we could instead return runtime.Version() [if valid]. + // This would act as a max version on what a tool can support. + return Future } diff --git a/vendor/golang.org/x/tools/internal/versions/types_go121.go b/vendor/golang.org/x/tools/internal/versions/types_go121.go deleted file mode 100644 index b4345d3349..0000000000 --- a/vendor/golang.org/x/tools/internal/versions/types_go121.go +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2023 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !go1.22 -// +build !go1.22 - -package versions - -import ( - "go/ast" - "go/types" -) - -// FileVersion returns a language version (<=1.21) derived from runtime.Version() -// or an unknown future version. -func FileVersion(info *types.Info, file *ast.File) string { - // In x/tools built with Go <= 1.21, we do not have Info.FileVersions - // available. We use a go version derived from the toolchain used to - // compile the tool by default. - // This will be <= go1.21. We take this as the maximum version that - // this tool can support. - // - // There are no features currently in x/tools that need to tell fine grained - // differences for versions <1.22. - return toolchain -} - -// InitFileVersions is a noop when compiled with this Go version. -func InitFileVersions(*types.Info) {} diff --git a/vendor/golang.org/x/tools/internal/versions/types_go122.go b/vendor/golang.org/x/tools/internal/versions/types_go122.go deleted file mode 100644 index aac5db62c9..0000000000 --- a/vendor/golang.org/x/tools/internal/versions/types_go122.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2023 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build go1.22 -// +build go1.22 - -package versions - -import ( - "go/ast" - "go/types" -) - -// FileVersion returns a file's Go version. -// The reported version is an unknown Future version if a -// version cannot be determined. -func FileVersion(info *types.Info, file *ast.File) string { - // In tools built with Go >= 1.22, the Go version of a file - // follow a cascades of sources: - // 1) types.Info.FileVersion, which follows the cascade: - // 1.a) file version (ast.File.GoVersion), - // 1.b) the package version (types.Config.GoVersion), or - // 2) is some unknown Future version. - // - // File versions require a valid package version to be provided to types - // in Config.GoVersion. Config.GoVersion is either from the package's module - // or the toolchain (go run). This value should be provided by go/packages - // or unitchecker.Config.GoVersion. - if v := info.FileVersions[file]; IsValid(v) { - return v - } - // Note: we could instead return runtime.Version() [if valid]. - // This would act as a max version on what a tool can support. - return Future -} - -// InitFileVersions initializes info to record Go versions for Go files. -func InitFileVersions(info *types.Info) { - info.FileVersions = make(map[*ast.File]string) -} diff --git a/vendor/gopkg.in/telebot.v3/admin.go b/vendor/gopkg.in/telebot.v3/admin.go index cc8b33a287..22b64c207d 100644 --- a/vendor/gopkg.in/telebot.v3/admin.go +++ b/vendor/gopkg.in/telebot.v3/admin.go @@ -36,6 +36,10 @@ type Rights struct { CanSendVideoNotes bool `json:"can_send_video_notes"` CanSendVoiceNotes bool `json:"can_send_voice_notes"` + CanPostStories bool `json:"can_post_stories"` + CanEditStories bool `json:"can_edit_stories"` + CanDeleteStories bool `json:"can_delete_stories"` + // Independent defines whether the chat permissions are set independently. // If not, the can_send_other_messages and can_add_web_page_previews permissions // will imply the can_send_messages, can_send_audios, can_send_documents, can_send_photos, @@ -106,6 +110,9 @@ func AdminRights() Rights { CanSendVideos: true, CanSendVideoNotes: true, CanSendVoiceNotes: true, + CanPostStories: true, + CanEditStories: true, + CanDeleteStories: true, } } diff --git a/vendor/gopkg.in/telebot.v3/api.go b/vendor/gopkg.in/telebot.v3/api.go index 6ed4eb146f..a66f6211bb 100644 --- a/vendor/gopkg.in/telebot.v3/api.go +++ b/vendor/gopkg.in/telebot.v3/api.go @@ -27,18 +27,21 @@ func (b *Bot) Raw(method string, payload interface{}) ([]byte, error) { return nil, err } - // Cancel the request immediately without waiting for the timeout when bot is about to stop. + // Cancel the request immediately without waiting for the timeout + // when bot is about to stop. // This may become important if doing long polling with long timeout. - exit := make(chan struct{}) - defer close(exit) ctx, cancel := context.WithCancel(context.Background()) defer cancel() go func() { + b.stopMu.RLock() + stopCh := b.stopClient + b.stopMu.RUnlock() + select { - case <-b.stopClient: + case <-stopCh: cancel() - case <-exit: + case <-ctx.Done(): } }() @@ -160,6 +163,19 @@ func addFileToWriter(writer *multipart.Writer, filename, field string, file inte return err } +func (f *File) process(name string, files map[string]File) string { + switch { + case f.InCloud(): + return f.FileID + case f.FileURL != "": + return f.FileURL + case f.OnDisk() || f.FileReader != nil: + files[name] = *f + return "attach://" + name + } + return "" +} + func (b *Bot) sendText(to Recipient, text string, opt *SendOptions) (*Message, error) { params := map[string]string{ "chat_id": to.Recipient(), @@ -238,6 +254,37 @@ func (b *Bot) getUpdates(offset, limit int, timeout time.Duration, allowed []str return resp.Result, nil } +func (b *Bot) forwardCopyMany(to Recipient, msgs []Editable, key string, opts ...*SendOptions) ([]Message, error) { + params := map[string]string{ + "chat_id": to.Recipient(), + } + + embedMessages(params, msgs) + + if len(opts) > 0 { + b.embedSendOptions(params, opts[0]) + } + + data, err := b.Raw(key, params) + if err != nil { + return nil, err + } + + var resp struct { + Result []Message + } + if err := json.Unmarshal(data, &resp); err != nil { + var resp struct { + Result bool + } + if err := json.Unmarshal(data, &resp); err != nil { + return nil, wrapError(err) + } + return nil, wrapError(err) + } + return resp.Result, nil +} + // extractOk checks given result for error. If result is ok returns nil. // In other cases it extracts API error. If error is not presented // in errors.go, it will be prefixed with `unknown` keyword. diff --git a/vendor/gopkg.in/telebot.v3/boost.go b/vendor/gopkg.in/telebot.v3/boost.go new file mode 100644 index 0000000000..7a5897a821 --- /dev/null +++ b/vendor/gopkg.in/telebot.v3/boost.go @@ -0,0 +1,113 @@ +package telebot + +import ( + "encoding/json" + "time" +) + +// Boost contains information about a chat boost. +type Boost struct { + // Unique identifier of the boost. + ID string `json:"boost_id"` + + // Point in time (Unix timestamp) when the chat was boosted. + AddUnixtime int64 `json:"add_date"` + + // Point in time (Unix timestamp) when the boost will automatically expire, + // unless the booster's Telegram Premium subscription is prolonged. + ExpirationUnixtime int64 `json:"expiration_date"` + + // Source of the added boost. + Source *BoostSource `json:"source"` +} + +// AddDate returns the moment of time when the chat has been boosted in local time. +func (c *Boost) AddDate() time.Time { + return time.Unix(c.AddUnixtime, 0) +} + +// ExpirationDate returns the moment of time when the boost of the channel +// will expire in local time. +func (c *Boost) ExpirationDate() time.Time { + return time.Unix(c.ExpirationUnixtime, 0) +} + +// BoostSourceType describes a type of boost. +type BoostSourceType = string + +const ( + BoostPremium BoostSourceType = "premium" + BoostGiftCode BoostSourceType = "gift_code" + BoostGiveaway BoostSourceType = "giveaway" +) + +// BoostSource describes the source of a chat boost. +type BoostSource struct { + // Source of the boost, always (“premium”, “gift_code”, “giveaway”). + Source BoostSourceType `json:"source"` + + // User that boosted the chat. + Booster *User `json:"user"` + + // Identifier of a message in the chat with the giveaway; the message + // could have been deleted already. May be 0 if the message isn't sent yet. + GiveawayMessageID int `json:"giveaway_message_id,omitempty"` + + // (Optional) True, if the giveaway was completed, but there was + // no user to win the prize. + Unclaimed bool `json:"is_unclaimed,omitempty"` +} + +// BoostAdded represents a service message about a user boosting a chat. +type BoostAdded struct { + // Number of boosts added by the user. + Count int `json:"boost_count"` +} + +// BoostUpdated represents a boost added to a chat or changed. +type BoostUpdated struct { + // Chat which was boosted. + Chat *Chat `json:"chat"` + + // Information about the chat boost. + Boost *Boost `json:"boost"` +} + +// BoostRemoved represents a boost removed from a chat. +type BoostRemoved struct { + // Chat which was boosted. + Chat *Chat `json:"chat"` + + // Unique identifier of the boost. + BoostID string `json:"boost_id"` + + // Point in time (Unix timestamp) when the boost was removed. + RemoveUnixtime int64 `json:"remove_date"` + + // Source of the removed boost. + Source *BoostSource `json:"source"` +} + +// UserBoosts gets the list of boosts added to a chat by a user. +// Requires administrator rights in the chat. +func (b *Bot) UserBoosts(chat, user Recipient) ([]Boost, error) { + params := map[string]string{ + "chat_id": chat.Recipient(), + "user_id": user.Recipient(), + } + + data, err := b.Raw("getUserChatBoosts", params) + if err != nil { + return nil, err + } + + var resp struct { + Result struct { + Boosts []Boost `json:"boosts"` + } + } + if err := json.Unmarshal(data, &resp); err != nil { + return nil, wrapError(err) + } + return resp.Result.Boosts, nil +} diff --git a/vendor/gopkg.in/telebot.v3/bot.go b/vendor/gopkg.in/telebot.v3/bot.go index e3a9a41f26..72406a2051 100644 --- a/vendor/gopkg.in/telebot.v3/bot.go +++ b/vendor/gopkg.in/telebot.v3/bot.go @@ -10,6 +10,7 @@ import ( "regexp" "strconv" "strings" + "sync" "time" ) @@ -81,7 +82,9 @@ type Bot struct { parseMode ParseMode stop chan chan struct{} client *http.Client - stopClient chan struct{} + + stopMu sync.RWMutex + stopClient chan struct{} } // Settings represents a utility struct for passing certain @@ -172,22 +175,33 @@ var ( // // b.Handle("/ban", onBan, middleware.Whitelist(ids...)) func (b *Bot) Handle(endpoint interface{}, h HandlerFunc, m ...MiddlewareFunc) { + end := extractEndpoint(endpoint) + if end == "" { + panic("telebot: unsupported endpoint") + } + if len(b.group.middleware) > 0 { m = appendMiddleware(b.group.middleware, m) } - handler := func(c Context) error { + b.handlers[end] = func(c Context) error { return applyMiddleware(h, m...)(c) } +} - switch end := endpoint.(type) { - case string: - b.handlers[end] = handler - case CallbackEndpoint: - b.handlers[end.CallbackUnique()] = handler - default: - panic("telebot: unsupported endpoint") +// Trigger executes the registered handler by the endpoint. +func (b *Bot) Trigger(endpoint interface{}, c Context) error { + end := extractEndpoint(endpoint) + if end == "" { + return fmt.Errorf("telebot: unsupported endpoint") } + + handler, ok := b.handlers[end] + if !ok { + return fmt.Errorf("telebot: no handler found for given endpoint") + } + + return handler(c) } // Start brings bot into motion by consuming incoming @@ -198,10 +212,14 @@ func (b *Bot) Start() { } // do nothing if called twice + b.stopMu.Lock() if b.stopClient != nil { + b.stopMu.Unlock() return } + b.stopClient = make(chan struct{}) + b.stopMu.Unlock() stop := make(chan struct{}) stopConfirm := make(chan struct{}) @@ -221,7 +239,6 @@ func (b *Bot) Start() { close(stop) <-stopConfirm close(confirm) - b.stopClient = nil return } } @@ -229,9 +246,13 @@ func (b *Bot) Start() { // Stop gracefully shuts the poller down. func (b *Bot) Stop() { + b.stopMu.Lock() if b.stopClient != nil { close(b.stopClient) + b.stopClient = nil } + b.stopMu.Unlock() + confirm := make(chan struct{}) b.stop <- confirm <-confirm @@ -270,7 +291,7 @@ func (b *Bot) Send(to Recipient, what interface{}, opts ...interface{}) (*Messag return nil, ErrBadRecipient } - sendOpts := extractOptions(opts) + sendOpts := b.extractOptions(opts) switch object := what.(type) { case string: @@ -290,26 +311,13 @@ func (b *Bot) SendAlbum(to Recipient, a Album, opts ...interface{}) ([]Message, return nil, ErrBadRecipient } - sendOpts := extractOptions(opts) + sendOpts := b.extractOptions(opts) media := make([]string, len(a)) files := make(map[string]File) for i, x := range a { - var ( - repr string - data []byte - file = x.MediaFile() - ) - - switch { - case file.InCloud(): - repr = file.FileID - case file.FileURL != "": - repr = file.FileURL - case file.OnDisk() || file.FileReader != nil: - repr = "attach://" + strconv.Itoa(i) - files[strconv.Itoa(i)] = *file - default: + repr := x.MediaFile().process(strconv.Itoa(i), files) + if repr == "" { return nil, fmt.Errorf("telebot: album entry #%d does not exist", i) } @@ -322,7 +330,7 @@ func (b *Bot) SendAlbum(to Recipient, a Album, opts ...interface{}) ([]Message, im.ParseMode = sendOpts.ParseMode } - data, _ = json.Marshal(im) + data, _ := json.Marshal(im) media[i] = string(data) } @@ -369,7 +377,7 @@ func (b *Bot) SendAlbum(to Recipient, a Album, opts ...interface{}) ([]Message, // Reply behaves just like Send() with an exception of "reply-to" indicator. // This function will panic upon nil Message. func (b *Bot) Reply(to *Message, what interface{}, opts ...interface{}) (*Message, error) { - sendOpts := extractOptions(opts) + sendOpts := b.extractOptions(opts) if sendOpts == nil { sendOpts = &SendOptions{} } @@ -392,7 +400,7 @@ func (b *Bot) Forward(to Recipient, msg Editable, opts ...interface{}) (*Message "message_id": msgID, } - sendOpts := extractOptions(opts) + sendOpts := b.extractOptions(opts) b.embedSendOptions(params, sendOpts) data, err := b.Raw("forwardMessage", params) @@ -403,10 +411,21 @@ func (b *Bot) Forward(to Recipient, msg Editable, opts ...interface{}) (*Message return extractMessage(data) } +// ForwardMany method forwards multiple messages of any kind. +// If some of the specified messages can't be found or forwarded, they are skipped. +// Service messages and messages with protected content can't be forwarded. +// Album grouping is kept for forwarded messages. +func (b *Bot) ForwardMany(to Recipient, msgs []Editable, opts ...*SendOptions) ([]Message, error) { + if to == nil { + return nil, ErrBadRecipient + } + return b.forwardCopyMany(to, msgs, "forwardMessages", opts...) +} + // Copy behaves just like Forward() but the copied message doesn't have a link to the original message (see Bots API). // // This function will panic upon nil Editable. -func (b *Bot) Copy(to Recipient, msg Editable, options ...interface{}) (*Message, error) { +func (b *Bot) Copy(to Recipient, msg Editable, opts ...interface{}) (*Message, error) { if to == nil { return nil, ErrBadRecipient } @@ -418,7 +437,7 @@ func (b *Bot) Copy(to Recipient, msg Editable, options ...interface{}) (*Message "message_id": msgID, } - sendOpts := extractOptions(options) + sendOpts := b.extractOptions(opts) b.embedSendOptions(params, sendOpts) data, err := b.Raw("copyMessage", params) @@ -429,6 +448,20 @@ func (b *Bot) Copy(to Recipient, msg Editable, options ...interface{}) (*Message return extractMessage(data) } +// CopyMany this method makes a copy of messages of any kind. +// If some of the specified messages can't be found or copied, they are skipped. +// Service messages, giveaway messages, giveaway winners messages, and +// invoice messages can't be copied. A quiz poll can be copied only if the value of the field +// correct_option_id is known to the bot. The method is analogous +// to the method forwardMessages, but the copied messages don't have a link to the original message. +// Album grouping is kept for copied messages. +func (b *Bot) CopyMany(to Recipient, msgs []Editable, opts ...*SendOptions) ([]Message, error) { + if to == nil { + return nil, ErrBadRecipient + } + return b.forwardCopyMany(to, msgs, "copyMessages", opts...) +} + // Edit is magic, it lets you change already sent message. // This function will panic upon nil Editable. // @@ -485,7 +518,7 @@ func (b *Bot) Edit(msg Editable, what interface{}, opts ...interface{}) (*Messag params["message_id"] = msgID } - sendOpts := extractOptions(opts) + sendOpts := b.extractOptions(opts) b.embedSendOptions(params, sendOpts) data, err := b.Raw(method, params) @@ -549,7 +582,7 @@ func (b *Bot) EditCaption(msg Editable, caption string, opts ...interface{}) (*M params["message_id"] = msgID } - sendOpts := extractOptions(opts) + sendOpts := b.extractOptions(opts) b.embedSendOptions(params, sendOpts) data, err := b.Raw("editMessageCaption", params) @@ -613,7 +646,7 @@ func (b *Bot) EditMedia(msg Editable, media Inputtable, opts ...interface{}) (*M msgID, chatID := msg.MessageSig() params := make(map[string]string) - sendOpts := extractOptions(opts) + sendOpts := b.extractOptions(opts) b.embedSendOptions(params, sendOpts) im := media.InputMedia() @@ -671,6 +704,16 @@ func (b *Bot) Delete(msg Editable) error { return err } +// DeleteMany deletes multiple messages simultaneously. +// If some of the specified messages can't be found, they are skipped. +func (b *Bot) DeleteMany(msgs []Editable) error { + params := make(map[string]string) + embedMessages(params, msgs) + + _, err := b.Raw("deleteMessages", params) + return err +} + // Notify updates the chat action for recipient. // // Chat action is a status message that recipient would see where @@ -904,7 +947,7 @@ func (b *Bot) StopLiveLocation(msg Editable, opts ...interface{}) (*Message, err "message_id": msgID, } - sendOpts := extractOptions(opts) + sendOpts := b.extractOptions(opts) b.embedSendOptions(params, sendOpts) data, err := b.Raw("stopMessageLiveLocation", params) @@ -928,7 +971,7 @@ func (b *Bot) StopPoll(msg Editable, opts ...interface{}) (*Poll, error) { "message_id": msgID, } - sendOpts := extractOptions(opts) + sendOpts := b.extractOptions(opts) b.embedSendOptions(params, sendOpts) data, err := b.Raw("stopPoll", params) @@ -946,7 +989,7 @@ func (b *Bot) StopPoll(msg Editable, opts ...interface{}) (*Poll, error) { } // Leave makes bot leave a group, supergroup or channel. -func (b *Bot) Leave(chat *Chat) error { +func (b *Bot) Leave(chat Recipient) error { params := map[string]string{ "chat_id": chat.Recipient(), } @@ -967,7 +1010,7 @@ func (b *Bot) Pin(msg Editable, opts ...interface{}) error { "message_id": msgID, } - sendOpts := extractOptions(opts) + sendOpts := b.extractOptions(opts) b.embedSendOptions(params, sendOpts) _, err := b.Raw("pinChatMessage", params) @@ -976,7 +1019,7 @@ func (b *Bot) Pin(msg Editable, opts ...interface{}) error { // Unpin unpins a message in a supergroup or a channel. // It supports tb.Silent option. -func (b *Bot) Unpin(chat *Chat, messageID ...int) error { +func (b *Bot) Unpin(chat Recipient, messageID ...int) error { params := map[string]string{ "chat_id": chat.Recipient(), } @@ -990,7 +1033,7 @@ func (b *Bot) Unpin(chat *Chat, messageID ...int) error { // UnpinAll unpins all messages in a supergroup or a channel. // It supports tb.Silent option. -func (b *Bot) UnpinAll(chat *Chat) error { +func (b *Bot) UnpinAll(chat Recipient) error { params := map[string]string{ "chat_id": chat.Recipient(), } @@ -1151,3 +1194,89 @@ func (b *Bot) Close() (bool, error) { return resp.Result, nil } + +// BotInfo represents a single object of BotName, BotDescription, BotShortDescription instances. +type BotInfo struct { + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + ShortDescription string `json:"short_description,omitempty"` +} + +// SetMyName change's the bot name. +func (b *Bot) SetMyName(name, language string) error { + params := map[string]string{ + "name": name, + "language_code": language, + } + + _, err := b.Raw("setMyName", params) + return err +} + +// MyName returns the current bot name for the given user language. +func (b *Bot) MyName(language string) (*BotInfo, error) { + return b.botInfo(language, "getMyName") +} + +// SetMyDescription change's the bot description, which is shown in the chat +// with the bot if the chat is empty. +func (b *Bot) SetMyDescription(desc, language string) error { + params := map[string]string{ + "description": desc, + "language_code": language, + } + + _, err := b.Raw("setMyDescription", params) + return err +} + +// MyDescription the current bot description for the given user language. +func (b *Bot) MyDescription(language string) (*BotInfo, error) { + return b.botInfo(language, "getMyDescription") +} + +// SetMyShortDescription change's the bot short description, which is shown on +// the bot's profile page and is sent together with the link when users share the bot. +func (b *Bot) SetMyShortDescription(desc, language string) error { + params := map[string]string{ + "short_description": desc, + "language_code": language, + } + + _, err := b.Raw("setMyShortDescription", params) + return err +} + +// MyShortDescription the current bot short description for the given user language. +func (b *Bot) MyShortDescription(language string) (*BotInfo, error) { + return b.botInfo(language, "getMyShortDescription") +} + +func (b *Bot) botInfo(language, key string) (*BotInfo, error) { + params := map[string]string{ + "language_code": language, + } + + data, err := b.Raw(key, params) + if err != nil { + return nil, err + } + + var resp struct { + Result *BotInfo + } + if err := json.Unmarshal(data, &resp); err != nil { + return nil, wrapError(err) + } + return resp.Result, nil +} + +func extractEndpoint(endpoint interface{}) string { + switch end := endpoint.(type) { + case string: + return end + case CallbackEndpoint: + return end.CallbackUnique() + } + return "" +} diff --git a/vendor/gopkg.in/telebot.v3/callback.go b/vendor/gopkg.in/telebot.v3/callback.go index 4bce60a1e4..bfd8a66db8 100644 --- a/vendor/gopkg.in/telebot.v3/callback.go +++ b/vendor/gopkg.in/telebot.v3/callback.go @@ -26,6 +26,15 @@ type Callback struct { // a bad client can send arbitrary data in this field. Data string `json:"data"` + // ChatInstance is a global identifier, uniquely corresponding to + // the chat to which the message with the callback button was sent. + ChatInstance string `json:"chat_instance"` + + // GameShortName is a unique identifier of the game for which a URL + // is requested from the bot when a user presses the Play button of + // that game. GameShortName may be empty + GameShortName string `json:"game_short_name"` + // Unique displays an unique of the button from which the // callback was fired. Sets immediately before the handling, // while the Data field stores only with payload. diff --git a/vendor/gopkg.in/telebot.v3/chat.go b/vendor/gopkg.in/telebot.v3/chat.go index 74d6333bf0..0426739d1b 100644 --- a/vendor/gopkg.in/telebot.v3/chat.go +++ b/vendor/gopkg.in/telebot.v3/chat.go @@ -47,22 +47,32 @@ type Chat struct { Username string `json:"username"` // Returns only in getChat - Bio string `json:"bio,omitempty"` - Photo *ChatPhoto `json:"photo,omitempty"` - Description string `json:"description,omitempty"` - InviteLink string `json:"invite_link,omitempty"` - PinnedMessage *Message `json:"pinned_message,omitempty"` - Permissions *Rights `json:"permissions,omitempty"` - SlowMode int `json:"slow_mode_delay,omitempty"` - StickerSet string `json:"sticker_set_name,omitempty"` - CanSetStickerSet bool `json:"can_set_sticker_set,omitempty"` - LinkedChatID int64 `json:"linked_chat_id,omitempty"` - ChatLocation *ChatLocation `json:"location,omitempty"` - Private bool `json:"has_private_forwards,omitempty"` - Protected bool `json:"has_protected_content,omitempty"` - NoVoiceAndVideo bool `json:"has_restricted_voice_and_video_messages"` - HiddenMembers bool `json:"has_hidden_members,omitempty"` - AggressiveAntiSpam bool `json:"has_aggressive_anti_spam_enabled,omitempty"` + Bio string `json:"bio,omitempty"` + Photo *ChatPhoto `json:"photo,omitempty"` + Description string `json:"description,omitempty"` + InviteLink string `json:"invite_link,omitempty"` + PinnedMessage *Message `json:"pinned_message,omitempty"` + Permissions *Rights `json:"permissions,omitempty"` + Reactions []Reaction `json:"available_reactions"` + SlowMode int `json:"slow_mode_delay,omitempty"` + StickerSet string `json:"sticker_set_name,omitempty"` + CanSetStickerSet bool `json:"can_set_sticker_set,omitempty"` + CustomEmojiSetName string `json:"custom_emoji_sticker_set_name"` + LinkedChatID int64 `json:"linked_chat_id,omitempty"` + ChatLocation *ChatLocation `json:"location,omitempty"` + Private bool `json:"has_private_forwards,omitempty"` + Protected bool `json:"has_protected_content,omitempty"` + NoVoiceAndVideo bool `json:"has_restricted_voice_and_video_messages"` + HasHiddenMembers bool `json:"has_hidden_members,omitempty"` + AggressiveAntiSpam bool `json:"has_aggressive_anti_spam_enabled,omitempty"` + CustomEmojiID string `json:"emoji_status_custom_emoji_id"` + EmojiExpirationUnixtime int64 `json:"emoji_status_expiration_date"` + BackgroundEmojiID string `json:"background_custom_emoji_id"` + AccentColorID int `json:"accent_color_id"` + ProfileAccentColorID int `json:"profile_accent_color_id"` + ProfileBackgroundEmojiID string `json:"profile_background_custom_emoji_id"` + HasVisibleHistory bool `json:"has_visible_history"` + UnrestrictBoosts int `json:"unrestrict_boost_count"` } // Recipient returns chat ID (see Recipient interface). @@ -154,6 +164,9 @@ type ChatMemberUpdate struct { // (Optional) InviteLink which was used by the user to // join the chat; for joining by invite link events only. InviteLink *ChatInviteLink `json:"invite_link"` + + // (Optional) True, if the user joined the chat via a chat folder invite link. + ViaFolderLink bool `json:"via_chat_folder_invite_link"` } // Time returns the moment of the change in local time. @@ -240,6 +253,14 @@ type ChatInviteLink struct { PendingCount int `json:"pending_join_request_count"` } +type Story struct { + // Unique identifier for the story in the chat + ID int `json:"id"` + + // Chat that posted the story + Poster *Chat `json:"chat"` +} + // ExpireDate returns the moment of the link expiration in local time. func (c *ChatInviteLink) ExpireDate() time.Time { return time.Unix(c.ExpireUnixtime, 0) @@ -250,6 +271,11 @@ func (r ChatJoinRequest) Time() time.Time { return time.Unix(r.Unixtime, 0) } +// Time returns the moment of the emoji status expiration. +func (c *Chat) Time() time.Time { + return time.Unix(c.EmojiExpirationUnixtime, 0) +} + // InviteLink should be used to export chat's invite link. func (b *Bot) InviteLink(chat *Chat) (string, error) { params := map[string]string{ diff --git a/vendor/gopkg.in/telebot.v3/context.go b/vendor/gopkg.in/telebot.v3/context.go index 3306c8f737..f5c496561f 100644 --- a/vendor/gopkg.in/telebot.v3/context.go +++ b/vendor/gopkg.in/telebot.v3/context.go @@ -55,6 +55,12 @@ type Context interface { // Topic returns the topic changes. Topic() *Topic + // Boost returns the boost instance. + Boost() *BoostUpdated + + // BoostRemoved returns the boost removed from a chat instance. + BoostRemoved() *BoostRemoved + // Sender returns the current recipient, depending on the context type. // Returns nil if user is not presented. Sender() *User @@ -152,6 +158,12 @@ type Context interface { // See Respond from bot.go. Respond(resp ...*CallbackResponse) error + // RespondText sends a popup response for the current callback query. + RespondText(text string) error + + // RespondAlert sends an alert response for the current callback query. + RespondAlert(text string) error + // Get retrieves data from the context. Get(key string) interface{} @@ -259,6 +271,14 @@ func (c *nativeContext) Topic() *Topic { return nil } +func (c *nativeContext) Boost() *BoostUpdated { + return c.u.Boost +} + +func (c *nativeContext) BoostRemoved() *BoostRemoved { + return c.u.BoostRemoved +} + func (c *nativeContext) Sender() *User { switch { case c.u.Callback != nil: @@ -281,9 +301,16 @@ func (c *nativeContext) Sender() *User { return c.u.ChatMember.Sender case c.u.ChatJoinRequest != nil: return c.u.ChatJoinRequest.Sender - default: - return nil + case c.u.Boost != nil: + if b := c.u.Boost.Boost; b != nil && b.Source != nil { + return b.Source.Booster + } + case c.u.BoostRemoved != nil: + if b := c.u.BoostRemoved; b.Source != nil { + return b.Source.Booster + } } + return nil } func (c *nativeContext) Chat() *Chat { @@ -355,7 +382,7 @@ func (c *nativeContext) Args() []string { case c.u.Message != nil: payload := strings.Trim(c.u.Message.Payload, " ") if payload != "" { - return strings.Split(payload, " ") + return strings.Fields(payload) } case c.u.Callback != nil: return strings.Split(c.u.Callback.Data, "|") @@ -481,6 +508,14 @@ func (c *nativeContext) Respond(resp ...*CallbackResponse) error { return c.b.Respond(c.u.Callback, resp...) } +func (c *nativeContext) RespondText(text string) error { + return c.Respond(&CallbackResponse{Text: text}) +} + +func (c *nativeContext) RespondAlert(text string) error { + return c.Respond(&CallbackResponse{Text: text, ShowAlert: true}) +} + func (c *nativeContext) Answer(resp *QueryResponse) error { if c.u.Query == nil { return errors.New("telebot: context inline query is nil") diff --git a/vendor/gopkg.in/telebot.v3/errors.go b/vendor/gopkg.in/telebot.v3/errors.go index 0197e1934e..23dd3895fb 100644 --- a/vendor/gopkg.in/telebot.v3/errors.go +++ b/vendor/gopkg.in/telebot.v3/errors.go @@ -1,6 +1,7 @@ package telebot import ( + "errors" "fmt" "strings" ) @@ -132,6 +133,7 @@ var ( ErrKickedFromChannel = NewError(403, "Forbidden: bot was kicked from the channel chat") ErrNotStartedByUser = NewError(403, "Forbidden: bot can't initiate conversation with a user") ErrUserIsDeactivated = NewError(403, "Forbidden: user is deactivated") + ErrNotChannelMember = NewError(403, "Forbidden: bot is not a member of the channel chat") ) // Err returns Error instance by given description. @@ -249,11 +251,18 @@ func Err(s string) error { return ErrChannelsTooMuch case ErrChannelsTooMuchUser.ʔ(): return ErrChannelsTooMuchUser + case ErrNotChannelMember.ʔ(): + return ErrNotChannelMember default: return nil } } +// ErrIs checks if the error with given description matches an error err. +func ErrIs(s string, err error) bool { + return errors.Is(err, Err(s)) +} + // wrapError returns new wrapped telebot-related error. func wrapError(err error) error { return fmt.Errorf("telebot: %w", err) diff --git a/vendor/gopkg.in/telebot.v3/giveaway.go b/vendor/gopkg.in/telebot.v3/giveaway.go new file mode 100644 index 0000000000..a0b54e2a5a --- /dev/null +++ b/vendor/gopkg.in/telebot.v3/giveaway.go @@ -0,0 +1,103 @@ +package telebot + +import "time" + +// Giveaway represents a message about a scheduled giveaway. +type Giveaway struct { + // The list of chats which the user must join to participate in the giveaway. + Chats []Chat `json:"chats"` + + // Point in time (Unix timestamp) when winners of the giveaway will be selected. + SelectionUnixtime int64 `json:"winners_selection_date"` + + // The number of users which are supposed to be selected as winners of the giveaway. + WinnerCount int `json:"winner_count"` + + // (Optional) True, if only users who join the chats after the giveaway + // started should be eligible to win. + OnlyNewMembers bool `json:"only_new_members"` + + // (Optional) True, if the list of giveaway winners will be visible to everyone. + HasPublicWinners bool `json:"has_public_winners"` + + // (Optional) Description of additional giveaway prize. + PrizeDescription string `json:"prize_description"` + + // (Optional) A list of two-letter ISO 3166-1 alpha-2 country codes indicating + // the countries from which eligible users for the giveaway must come. + // If empty, then all users can participate in the giveaway. Users with a phone number + // that was bought on Fragment can always participate in giveaways. + CountryCodes []string `json:"country_codes"` + + // (Optional) The number of months the Telegram Premium subscription won from + // the giveaway will be active for. + PremiumMonthCount int `json:"premium_subscription_month_count"` +} + +// SelectionDate returns the moment of when winners of the giveaway were selected in local time. +func (g *Giveaway) SelectionDate() time.Time { + return time.Unix(g.SelectionUnixtime, 0) +} + +// GiveawayWinners object represents a message about the completion of a +// giveaway with public winners. +type GiveawayWinners struct { + // The chat that created the giveaway. + Chat *Chat `json:"chat"` + + // Identifier of the message with the giveaway in the chat. + MessageID int `json:"message_id"` + + // Point in time (Unix timestamp) when winners of the giveaway were selected. + SelectionUnixtime int64 `json:"winners_selection_date"` + + // The number of users which are supposed to be selected as winners of the giveaway. + WinnerCount int `json:"winner_count"` + + // List of up to 100 winners of the giveaway. + Winners []User `json:"winners"` + + // (Optional) The number of other chats the user had to join in order + // to be eligible for the giveaway. + AdditionalChats int `json:"additional_chat_count"` + + // (Optional) The number of months the Telegram Premium subscription won from + // the giveaway will be active for. + PremiumMonthCount int `json:"premium_subscription_month_count"` + + // (Optional) Number of undistributed prizes. + UnclaimedPrizes int `json:"unclaimed_prize_count"` + + // (Optional) True, if only users who had joined the chats after the giveaway started + // were eligible to win. + OnlyNewMembers bool `json:"only_new_members"` + + // (Optional) True, if the giveaway was canceled because the payment for it was refunded. + Refunded bool `json:"was_refunded"` + + // (Optional) Description of additional giveaway prize. + PrizeDescription string `json:"prize_description"` +} + +// SelectionDate returns the moment of when winners of the giveaway +// were selected in local time. +func (g *GiveawayWinners) SelectionDate() time.Time { + return time.Unix(g.SelectionUnixtime, 0) +} + +// GiveawayCreated represents a service message about the creation of a scheduled giveaway. +// Currently holds no information. +type GiveawayCreated struct{} + +// GiveawayCompleted represents a service message about the completion of a +// giveaway without public winners. +type GiveawayCompleted struct { + // Number of winners in the giveaway. + WinnerCount int `json:"winner_count"` + + // (Optional) Number of undistributed prizes. + UnclaimedPrizes int `json:"unclaimed_prize_count"` + + // (Optional) Message with the giveaway that was completed, if it wasn't deleted. + Message *Message `json:"giveaway_message"` +} diff --git a/vendor/gopkg.in/telebot.v3/inline.go b/vendor/gopkg.in/telebot.v3/inline.go index 2a88958220..6a7f43f978 100644 --- a/vendor/gopkg.in/telebot.v3/inline.go +++ b/vendor/gopkg.in/telebot.v3/inline.go @@ -61,6 +61,46 @@ type QueryResponse struct { // (Optional) Parameter for the start message sent to the bot when user // presses the switch button. SwitchPMParameter string `json:"switch_pm_parameter,omitempty"` + + // (Optional) A JSON-serialized object describing a button to be shown + // above inline query results. + Button *QueryResponseButton `json:"button,omitempty"` +} + +// QueryResponseButton represents a button to be shown above inline query results. +// You must use exactly one of the optional fields. +type QueryResponseButton struct { + // Label text on the button + Text string `json:"text"` + + // (Optional) Description of the Web App that will be launched when the + // user presses the button. The Web App will be able to switch back to the + // inline mode using the method switchInlineQuery inside the Web App. + WebApp *WebApp `json:"web_app"` + + // (Optional) Deep-linking parameter for the /start message sent to the bot + // when a user presses the button. 1-64 characters, only A-Z, a-z, 0-9, _ and - are allowed. + Start string `json:"start_parameter"` +} + +// SwitchInlineQuery represents an inline button that switches the current +// user to inline mode in a chosen chat, with an optional default inline query. +type SwitchInlineQuery struct { + // (Optional) The default inline query to be inserted in the input field. + // If left empty, only the bot's username will be inserted. + Query string `json:"query"` + + // (Optional) True, if private chats with users can be chosen. + AllowUserChats bool `json:"allow_user_chats"` + + // (Optional) True, if private chats with bots can be chosen. + AllowBotChats bool `json:"allow_bot_chats"` + + // (Optional) True, if group and supergroup chats can be chosen. + AllowGroupChats bool `json:"allow_group_chats"` + + // (Optional) True, if channel chats can be chosen. + AllowChannelChats bool `json:"allow_channel_chats"` } // InlineResult represents a result of an inline query that was chosen @@ -131,6 +171,8 @@ func inferIQR(result Result) error { r.Type = "voice" case *StickerResult: r.Type = "sticker" + case *GameResult: + r.Type = "game" default: return fmt.Errorf("telebot: result %v is not supported", result) } diff --git a/vendor/gopkg.in/telebot.v3/inline_types.go b/vendor/gopkg.in/telebot.v3/inline_types.go index d93cffc209..9b05751908 100644 --- a/vendor/gopkg.in/telebot.v3/inline_types.go +++ b/vendor/gopkg.in/telebot.v3/inline_types.go @@ -60,6 +60,16 @@ func (r *ResultBase) Process(b *Bot) { } } +// GameResult represents a game. Game is a content type +// supported by Telegram, which can be sent back to the +// user as a result for an inline query. +type GameResult struct { + ResultBase + + // ShortName is a unique identifier of the game. + ShortName string `json:"game_short_name"` +} + // ArticleResult represents a link to an article or web page. type ArticleResult struct { ResultBase @@ -81,13 +91,13 @@ type ArticleResult struct { Description string `json:"description,omitempty"` // Optional. URL of the thumbnail for the result. - ThumbURL string `json:"thumb_url,omitempty"` + ThumbURL string `json:"thumbnail_url,omitempty"` // Optional. Width of the thumbnail for the result. - ThumbWidth int `json:"thumb_width,omitempty"` + ThumbWidth int `json:"thumbnail_width,omitempty"` // Optional. Height of the thumbnail for the result. - ThumbHeight int `json:"thumb_height,omitempty"` + ThumbHeight int `json:"thumbnail_height,omitempty"` } // AudioResult represents a link to an mp3 audio file. @@ -130,13 +140,13 @@ type ContactResult struct { LastName string `json:"last_name,omitempty"` // Optional. URL of the thumbnail for the result. - ThumbURL string `json:"thumb_url,omitempty"` + ThumbURL string `json:"thumbnail_url,omitempty"` // Optional. Width of the thumbnail for the result. - ThumbWidth int `json:"thumb_width,omitempty"` + ThumbWidth int `json:"thumbnail_width,omitempty"` // Optional. Height of the thumbnail for the result. - ThumbHeight int `json:"thumb_height,omitempty"` + ThumbHeight int `json:"thumbnail_height,omitempty"` } // DocumentResult represents a link to a file. @@ -160,13 +170,13 @@ type DocumentResult struct { Description string `json:"description,omitempty"` // Optional. URL of the thumbnail (jpeg only) for the file. - ThumbURL string `json:"thumb_url,omitempty"` + ThumbURL string `json:"thumbnail_url,omitempty"` // Optional. Width of the thumbnail for the result. - ThumbWidth int `json:"thumb_width,omitempty"` + ThumbWidth int `json:"thumbnail_width,omitempty"` // Optional. Height of the thumbnail for the result. - ThumbHeight int `json:"thumb_height,omitempty"` + ThumbHeight int `json:"thumbnail_height,omitempty"` // If Cache != "", it'll be used instead Cache string `json:"document_file_id,omitempty"` @@ -189,11 +199,11 @@ type GifResult struct { Duration int `json:"gif_duration,omitempty"` // URL of the static thumbnail for the result (jpeg or gif). - ThumbURL string `json:"thumb_url"` + ThumbURL string `json:"thumbnail_url"` // Optional. MIME type of the thumbnail, must be one of // “image/jpeg”, “image/gif”, or “video/mp4”. - ThumbMIME string `json:"thumb_mime_type,omitempty"` + ThumbMIME string `json:"thumbnail_mime_type,omitempty"` // Optional. Title for the result. Title string `json:"title,omitempty"` @@ -215,7 +225,7 @@ type LocationResult struct { Title string `json:"title"` // Optional. Url of the thumbnail for the result. - ThumbURL string `json:"thumb_url,omitempty"` + ThumbURL string `json:"thumbnail_url,omitempty"` } // Mpeg4GifResult represents a link to a video animation @@ -236,11 +246,11 @@ type Mpeg4GifResult struct { Duration int `json:"mpeg4_duration,omitempty"` // URL of the static thumbnail (jpeg or gif) for the result. - ThumbURL string `json:"thumb_url,omitempty"` + ThumbURL string `json:"thumbnail_url,omitempty"` // Optional. MIME type of the thumbnail, must be one of // “image/jpeg”, “image/gif”, or “video/mp4”. - ThumbMIME string `json:"thumb_mime_type,omitempty"` + ThumbMIME string `json:"thumbnail_mime_type,omitempty"` // Optional. Title for the result. Title string `json:"title,omitempty"` @@ -276,7 +286,7 @@ type PhotoResult struct { Caption string `json:"caption,omitempty"` // URL of the thumbnail for the photo. - ThumbURL string `json:"thumb_url"` + ThumbURL string `json:"thumbnail_url"` // If Cache != "", it'll be used instead Cache string `json:"photo_file_id,omitempty"` @@ -298,13 +308,13 @@ type VenueResult struct { FoursquareID string `json:"foursquare_id,omitempty"` // Optional. URL of the thumbnail for the result. - ThumbURL string `json:"thumb_url,omitempty"` + ThumbURL string `json:"thumbnail_url,omitempty"` // Optional. Width of the thumbnail for the result. - ThumbWidth int `json:"thumb_width,omitempty"` + ThumbWidth int `json:"thumbnail_width,omitempty"` // Optional. Height of the thumbnail for the result. - ThumbHeight int `json:"thumb_height,omitempty"` + ThumbHeight int `json:"thumbnail_height,omitempty"` } // VideoResult represents a link to a page containing an embedded @@ -319,7 +329,7 @@ type VideoResult struct { MIME string `json:"mime_type"` // URL of the thumbnail (jpeg only) for the video. - ThumbURL string `json:"thumb_url"` + ThumbURL string `json:"thumbnail_url"` // Title for the result. Title string `json:"title"` diff --git a/vendor/gopkg.in/telebot.v3/input_types.go b/vendor/gopkg.in/telebot.v3/input_types.go index 8186c0727c..8e49a7f516 100644 --- a/vendor/gopkg.in/telebot.v3/input_types.go +++ b/vendor/gopkg.in/telebot.v3/input_types.go @@ -12,12 +12,12 @@ type InputTextMessageContent struct { // Text of the message to be sent, 1-4096 characters. Text string `json:"message_text"` - // Optional. Send Markdown or HTML, if you want Telegram apps to show + // (Optional) Send Markdown or HTML, if you want Telegram apps to show // bold, italic, fixed-width text or inline URLs in your bot's message. ParseMode string `json:"parse_mode,omitempty"` - // Optional. Disables link previews for links in the sent message. - DisablePreview bool `json:"disable_web_page_preview"` + // (Optional) Link preview generation options for the message. + PreviewOptions *PreviewOptions `json:"link_preview_options,omitempty"` } func (input *InputTextMessageContent) IsInputMessageContent() bool { diff --git a/vendor/gopkg.in/telebot.v3/markup.go b/vendor/gopkg.in/telebot.v3/markup.go index 29236db34e..b45990c521 100644 --- a/vendor/gopkg.in/telebot.v3/markup.go +++ b/vendor/gopkg.in/telebot.v3/markup.go @@ -220,7 +220,7 @@ type ReplyButton struct { Contact bool `json:"request_contact,omitempty"` Location bool `json:"request_location,omitempty"` Poll PollType `json:"request_poll,omitempty"` - User *ReplyRecipient `json:"request_user,omitempty"` + User *ReplyRecipient `json:"request_users,omitempty"` Chat *ReplyRecipient `json:"request_chat,omitempty"` WebApp *WebApp `json:"web_app,omitempty"` } @@ -244,8 +244,9 @@ func (pt PollType) MarshalJSON() ([]byte, error) { type ReplyRecipient struct { ID int32 `json:"request_id"` - Bot *bool `json:"user_is_bot,omitempty"` // user only, optional - Premium *bool `json:"user_is_premium,omitempty"` // user only, optional + Bot *bool `json:"user_is_bot,omitempty"` // user only, optional + Premium *bool `json:"user_is_premium,omitempty"` // user only, optional + Quantity int `json:"max_quantity,omitempty"` // user only, optional Channel bool `json:"chat_is_channel,omitempty"` // chat only, required Forum *bool `json:"chat_is_forum,omitempty"` // chat only, optional @@ -271,13 +272,14 @@ type InlineButton struct { // It will be used as a callback endpoint. Unique string `json:"unique,omitempty"` - Text string `json:"text"` - URL string `json:"url,omitempty"` - Data string `json:"callback_data,omitempty"` - InlineQuery string `json:"switch_inline_query,omitempty"` - InlineQueryChat string `json:"switch_inline_query_current_chat"` - Login *Login `json:"login_url,omitempty"` - WebApp *WebApp `json:"web_app,omitempty"` + Text string `json:"text"` + URL string `json:"url,omitempty"` + Data string `json:"callback_data,omitempty"` + InlineQuery string `json:"switch_inline_query,omitempty"` + InlineQueryChat string `json:"switch_inline_query_current_chat"` + InlineQueryChosenChat *SwitchInlineQuery `json:"switch_inline_query_chosen_chat,omitempty"` + Login *Login `json:"login_url,omitempty"` + WebApp *WebApp `json:"web_app,omitempty"` } // MarshalJSON implements json.Marshaler interface. diff --git a/vendor/gopkg.in/telebot.v3/media.go b/vendor/gopkg.in/telebot.v3/media.go index d161aa58a0..bdce55579c 100644 --- a/vendor/gopkg.in/telebot.v3/media.go +++ b/vendor/gopkg.in/telebot.v3/media.go @@ -19,7 +19,7 @@ type InputMedia struct { Type string `json:"type"` Media string `json:"media"` Caption string `json:"caption"` - Thumbnail string `json:"thumb,omitempty"` + Thumbnail string `json:"thumbnail,omitempty"` ParseMode string `json:"parse_mode,omitempty"` Entities Entities `json:"caption_entities,omitempty"` Width int `json:"width,omitempty"` @@ -45,6 +45,24 @@ type Inputtable interface { // Album lets you group multiple media into a single message. type Album []Inputtable +func (a Album) SetCaption(caption string) { + if len(a) < 1 { + return + } + switch a[0].MediaType() { + case "audio": + a[0].(*Audio).Caption = caption + case "video": + a[0].(*Video).Caption = caption + case "document": + a[0].(*Document).Caption = caption + case "photo": + a[0].(*Photo).Caption = caption + case "animation": + a[0].(*Animation).Caption = caption + } +} + // Photo object represents a single photo file. type Photo struct { File @@ -113,7 +131,7 @@ type Audio struct { // (Optional) Caption string `json:"caption,omitempty"` - Thumbnail *Photo `json:"thumb,omitempty"` + Thumbnail *Photo `json:"thumbnail,omitempty"` Title string `json:"title,omitempty"` Performer string `json:"performer,omitempty"` MIME string `json:"mime_type,omitempty"` @@ -145,7 +163,7 @@ type Document struct { File // (Optional) - Thumbnail *Photo `json:"thumb,omitempty"` + Thumbnail *Photo `json:"thumbnail,omitempty"` Caption string `json:"caption,omitempty"` MIME string `json:"mime_type"` FileName string `json:"file_name,omitempty"` @@ -179,7 +197,7 @@ type Video struct { // (Optional) Caption string `json:"caption,omitempty"` - Thumbnail *Photo `json:"thumb,omitempty"` + Thumbnail *Photo `json:"thumbnail,omitempty"` Streaming bool `json:"supports_streaming,omitempty"` MIME string `json:"mime_type,omitempty"` FileName string `json:"file_name,omitempty"` @@ -215,7 +233,7 @@ type Animation struct { // (Optional) Caption string `json:"caption,omitempty"` - Thumbnail *Photo `json:"thumb,omitempty"` + Thumbnail *Photo `json:"thumbnail,omitempty"` MIME string `json:"mime_type,omitempty"` FileName string `json:"file_name,omitempty"` } @@ -265,7 +283,7 @@ type VideoNote struct { Duration int `json:"duration"` // (Optional) - Thumbnail *Photo `json:"thumb,omitempty"` + Thumbnail *Photo `json:"thumbnail,omitempty"` Length int `json:"length,omitempty"` } @@ -280,17 +298,18 @@ func (v *VideoNote) MediaFile() *File { // Sticker object represents a WebP image, so-called sticker. type Sticker struct { File + Type StickerSetType `json:"type"` Width int `json:"width"` Height int `json:"height"` Animated bool `json:"is_animated"` Video bool `json:"is_video"` - Thumbnail *Photo `json:"thumb"` + Thumbnail *Photo `json:"thumbnail"` Emoji string `json:"emoji"` SetName string `json:"set_name"` - MaskPosition *MaskPosition `json:"mask_position"` PremiumAnimation *File `json:"premium_animation"` - Type StickerSetType `json:"type"` + MaskPosition *MaskPosition `json:"mask_position"` CustomEmoji string `json:"custom_emoji_id"` + Repaint bool `json:"needs_repainting"` } func (s *Sticker) MediaType() string { diff --git a/vendor/gopkg.in/telebot.v3/message.go b/vendor/gopkg.in/telebot.v3/message.go index 82997569a3..d7d239bc5e 100644 --- a/vendor/gopkg.in/telebot.v3/message.go +++ b/vendor/gopkg.in/telebot.v3/message.go @@ -46,6 +46,9 @@ type Message struct { // For forwarded messages, unixtime of the original message. OriginalUnixtime int `json:"forward_date"` + // For information about the original message for forwarded messages. + Origin *MessageOrigin `json:"forward_origin"` + // Message is a channel post that was automatically forwarded to the connected discussion group. AutomaticForward bool `json:"is_automatic_forward"` @@ -56,9 +59,23 @@ type Message struct { // itself is a reply. ReplyTo *Message `json:"reply_to_message"` + // (Optional) For replies to a story, the original story + Story *Story `json:"story"` + + // (Optional) Information about the message that is being replied to, + // which may come from another chat or forum topic. + ExternalReplyInfo *ExternalReplyInfo `json:"external_reply"` + + // (Optional) For replies that quote part of the original message, + // the quoted part of the message. + Quote *TextQuote `json:"quote"` + // Shows through which bot the message was sent. Via *User `json:"via_bot"` + // For replies to a story, the original story. + ReplyToStory *Story `json:"reply_to_story"` + // (Optional) Time of last edit in Unix. LastEdit int64 `json:"edit_date"` @@ -87,6 +104,10 @@ type Message struct { // etc. that appear in the text. Entities Entities `json:"entities,omitempty"` + // (Optional) PreviewOptions used for link preview generation for the message, + // if it is a text message and link preview options were changed. + PreviewOptions *PreviewOptions `json:"link_preview_options,omitempty"` + // Some messages containing media, may as well have a caption. Caption string `json:"caption,omitempty"` @@ -115,7 +136,7 @@ type Message struct { // For a video, information about it. Video *Video `json:"video"` - // For a animation, information about it. + // For an animation, information about it. Animation *Animation `json:"animation"` // For a contact, contact information itself. @@ -136,6 +157,18 @@ type Message struct { // For a dice, information about it. Dice *Dice `json:"dice"` + // (Optional) The message is a scheduled giveaway message. + Giveaway *Giveaway `json:"giveaway"` + + // (Optional) A giveaway with public winners was completed. + GiveawayWinners *GiveawayWinners `json:"giveaway_winners"` + + // (Optional) Service message: a scheduled giveaway was created. + GiveawayCreated *GiveawayCreated `json:"giveaway_created"` + + // (Optional) Service message: a giveaway without public winners was completed. + GiveawayCompleted *GiveawayCompleted `json:"giveaway_completed"` + // For a service message, represents a user, // that just got added to chat, this message came from. // @@ -230,7 +263,7 @@ type Message struct { Payment *Payment `json:"successful_payment"` // For a service message, a user was shared with the bot. - UserShared *RecipientShared `json:"user_shared,omitempty"` + UserShared *RecipientShared `json:"users_shared,omitempty"` // For a service message, a chat was shared with the bot. ChatShared *RecipientShared `json:"chat_shared,omitempty"` @@ -263,6 +296,13 @@ type Message struct { // Inline keyboard attached to the message. ReplyMarkup *ReplyMarkup `json:"reply_markup,omitempty"` + // Service message: user boosted the chat. + BoostAdded *BoostAdded `json:"boost_added"` + + // If the sender of the message boosted the chat, the number of boosts + // added by the user. + SenderBoostCount int `json:"sender_boost_count"` + // Service message: forum topic created TopicCreated *Topic `json:"forum_topic_created,omitempty"` @@ -336,9 +376,10 @@ const ( EntityTextLink EntityType = "text_link" EntitySpoiler EntityType = "spoiler" EntityCustomEmoji EntityType = "custom_emoji" + EntityBlockquote EntityType = "blockquote" ) -// Entities is used to set message's text entities as a send option. +// Entities are used to set message's text entities as a send option. type Entities []MessageEntity // ProximityAlert sent whenever a user in the chat triggers @@ -354,6 +395,11 @@ type AutoDeleteTimer struct { Unixtime int `json:"message_auto_delete_time"` } +// Inaccessible shows whether the message is InaccessibleMessage object. +func (m *Message) Inaccessible() bool { + return m.Sender == nil +} + // MessageSig satisfies Editable interface (see Editable.) func (m *Message) MessageSig() (string, int64) { return strconv.Itoa(m.ID), m.Chat.ID @@ -461,3 +507,215 @@ func (m *Message) Media() Media { return nil } } + +// MessageReaction object represents a change of a reaction on a message performed by a user. +type MessageReaction struct { + // The chat containing the message the user reacted to. + Chat *Chat `json:"chat"` + + // Unique identifier of the message inside the chat. + MessageID int `json:"message_id"` + + // (Optional) The user that changed the reaction, + // if the user isn't anonymous + User *User `json:"user"` + + // (Optional) The chat on behalf of which the reaction was changed, + // if the user is anonymous. + ActorChat *Chat `json:"actor_chat"` + + // Date of the change in Unix time. + DateUnixtime int64 `json:"date"` + + // Previous list of reaction types that were set by the user. + OldReaction []Reaction `json:"old_reaction"` + + // New list of reaction types that have been set by the user. + NewReaction []Reaction `json:"new_reaction"` +} + +func (mu *MessageReaction) Time() time.Time { + return time.Unix(mu.DateUnixtime, 0) +} + +// MessageReactionCount represents reaction changes on a message with +// anonymous reactions. +type MessageReactionCount struct { + // The chat containing the message. + Chat *Chat `json:"chat"` + + // Unique message identifier inside the chat. + MessageID int `json:"message_id"` + + // Date of the change in Unix time. + DateUnixtime int64 `json:"date"` + + // List of reactions that are present on the message. + Reactions *ReactionCount `json:"reactions"` +} + +// Time returns the moment of change in local time. +func (mc *MessageReactionCount) Time() time.Time { + return time.Unix(mc.DateUnixtime, 0) +} + +// TextQuote contains information about the quoted part of a message that is +// replied to by the given message. +type TextQuote struct { + // Text of the quoted part of a message that is replied to by the given message. + Text string `json:"text"` + + // (Optional) Special entities that appear in the quote. + // Currently, only bold, italic, underline, strikethrough, spoiler, + // and custom_emoji entities are kept in quotes. + Entities []MessageEntity `json:"entities"` + + // Approximate quote position in the original message in UTF-16 code units + // as specified by the sender. + Position int `json:"position"` + + // (Optional) True, if the quote was chosen manually by the message sender. + // Otherwise, the quote was added automatically by the server. + Manual bool `json:"is_manual"` +} + +// MessageOrigin a message reference that has been sent originally by a known user. +type MessageOrigin struct { + // Type of the message origin, always “channel”. + Type string `json:"type"` + + // Date the message was sent originally in Unix time. + DateUnixtime int64 `json:"date"` + + // User that sent the message originally. + Sender *User `json:"sender_user,omitempty"` + + // Name of the user that sent the message originally. + SenderUsername string `json:"sender_user_name,omitempty"` + + // Chat that sent the message originally. + SenderChat *Chat `json:"sender_chat,omitempty"` + + // Channel chat to which the message was originally sent. + Chat *Chat `json:"chat,omitempty"` + + // Unique message identifier inside the chat. + MessageID int `json:"message_id,omitempty"` + + // (Optional) For messages originally sent by an anonymous chat administrator, + // original message author signature. + Signature string `json:"author_signature,omitempty"` +} + +// Time returns the moment of message that was sent originally in local time. +func (mo *MessageOrigin) Time() time.Time { + return time.Unix(mo.DateUnixtime, 0) +} + +// ExternalReplyInfo contains information about a message that is being replied to, +// which may come from another chat or forum topic. +type ExternalReplyInfo struct { + // Origin of the message replied to by the given message. + Origin *MessageOrigin `json:"origin"` + + // (Optional) Chat the original message belongs to. + // Available only if the chat is a supergroup or a channel. + Chat *Chat `json:"chat"` + + // (Optional) Unique message identifier inside the original chat. + // Available only if the original chat is a supergroup or a channel. + MessageID int `json:"message_id"` + + // (Optional) ReactionOptions used for link preview generation for the original message, + // if it is a text message. + PreviewOptions *PreviewOptions `json:"link_preview_options"` + + // (Optional) Message is an animation, information about the animation. + Animation *Animation `json:"animation"` + + // (Optional) Message is an audio file, information about the file. + Audio *Audio `json:"audio"` + + // (Optional) Message is a general file, information about the file. + Document *Document `json:"document"` + + // (Optional) Message is a photo, available sizes of the photo. + Photo []Photo `json:"photo"` + + // (Optional) Message is a sticker, information about the sticker. + Sticker *Sticker `json:"sticker"` + + // (Optional) Message is a forwarded story. + Story *Story `json:"story"` + + // (Optional) Message is a video, information about the video. + Video *Video `json:"video"` + + // (Optional) Message is a video note, information about the video message. + Note *VideoNote `json:"video_note"` + + // (Optional) Message is a voice message, information about the file. + Voice *Voice `json:"voice"` + + // (Optional) True, if the message media is covered by a spoiler animation. + HasMediaSpoiler bool `json:"has_media_spoiler"` + + // (Optional) Message is a shared contact, information about the contact. + Contact *Contact `json:"contact"` + + // (Optional) Message is a dice with random value. + Dice *Dice `json:"dice"` + + //( Optional) Message is a game, information about the game. + Game *Game `json:"game"` + + // (Optional) Message is a venue, information about the venue. + Venue *Venue `json:"venue"` + + // (Optional) Message is a native poll, information about the poll. + Poll *Poll `json:"poll"` + + // (Optional) Message is a shared location, information about the location. + Location *Location `json:"location"` + + // (Optional) Message is an invoice for a payment, information about the invoice. + Invoice *Invoice `json:"invoice"` + + // (Optional) Message is a scheduled giveaway, information about the giveaway. + Giveaway *Giveaway `json:"giveaway"` + + // (Optional) A giveaway with public winners was completed. + GiveawayWinners *GiveawayWinners `json:"giveaway_winners"` +} + +// ReplyParams describes reply parameters for the message that is being sent. +type ReplyParams struct { + // Identifier of the message that will be replied to in the current chat, + // or in the chat chat_id if it is specified. + MessageID int `json:"message_id"` + + // (Optional) If the message to be replied to is from a different chat, + // unique identifier for the chat or username of the channel. + ChatID int64 `json:"chat_id"` + + // Optional. Pass True if the message should be sent even if the specified message + // to be replied to is not found; can be used only for replies in the + // same chat and forum topic. + AllowWithoutReply bool `json:"allow_sending_without_reply"` + + // (Optional) Quoted part of the message to be replied to; 0-1024 characters after + // entities parsing. The quote must be an exact substring of the message to be replied to, + // including bold, italic, underline, strikethrough, spoiler, and custom_emoji entities. + // The message will fail to send if the quote isn't found in the original message. + Quote string `json:"quote"` + + // (Optional) Mode for parsing entities in the quote. + QuoteParseMode ParseMode `json:"quote_parse_mode"` + + // (Optional) A JSON-serialized list of special entities that appear in the quote. + // It can be specified instead of quote_parse_mode. + QuoteEntities []MessageEntity `json:"quote_entities"` + + // (Optional) Position of the quote in the original message in UTF-16 code units. + QuotePosition int `json:"quote_position"` +} diff --git a/vendor/gopkg.in/telebot.v3/options.go b/vendor/gopkg.in/telebot.v3/options.go index 56e0d9c295..20c75dec6d 100644 --- a/vendor/gopkg.in/telebot.v3/options.go +++ b/vendor/gopkg.in/telebot.v3/options.go @@ -84,6 +84,8 @@ type SendOptions struct { // HasSpoiler marks the message as containing a spoiler. HasSpoiler bool + // ReplyParams Describes the message to reply to + ReplyParams *ReplyParams } func (og *SendOptions) copy() *SendOptions { @@ -94,8 +96,10 @@ func (og *SendOptions) copy() *SendOptions { return &cp } -func extractOptions(how []interface{}) *SendOptions { - opts := &SendOptions{} +func (b *Bot) extractOptions(how []interface{}) *SendOptions { + opts := &SendOptions{ + ParseMode: b.parseMode, + } for _, prop := range how { switch opt := prop.(type) { @@ -105,6 +109,8 @@ func extractOptions(how []interface{}) *SendOptions { if opt != nil { opts.ReplyMarkup = opt.copy() } + case *ReplyParams: + opts.ReplyParams = opt case Option: switch opt { case NoPreview: @@ -146,10 +152,6 @@ func extractOptions(how []interface{}) *SendOptions { } func (b *Bot) embedSendOptions(params map[string]string, opt *SendOptions) { - if b.parseMode != ModeDefault { - params["parse_mode"] = b.parseMode - } - if opt == nil { return } @@ -198,7 +200,7 @@ func (b *Bot) embedSendOptions(params map[string]string, opt *SendOptions) { if opt.ThreadID != 0 { params["message_thread_id"] = strconv.Itoa(opt.ThreadID) } - + if opt.HasSpoiler { params["spoiler"] = "true" } @@ -224,3 +226,45 @@ func processButtons(keys [][]InlineButton) { } } } + +// PreviewOptions describes the options used for link preview generation. +type PreviewOptions struct { + // (Optional) True, if the link preview is disabled. + Disabled bool `json:"is_disabled"` + + // (Optional) URL to use for the link preview. If empty, then the first URL + // found in the message text will be used. + URL string `json:"url"` + + // (Optional) True, if the media in the link preview is supposed to be shrunk; + // ignored if the URL isn't explicitly specified or media size change. + // isn't supported for the preview. + SmallMedia bool `json:"prefer_small_media"` + + // (Optional) True, if the media in the link preview is supposed to be enlarged; + // ignored if the URL isn't explicitly specified or media size change. + // isn't supported for the preview. + LargeMedia bool `json:"prefer_large_media"` + + // (Optional) True, if the link preview must be shown above the message text; + // otherwise, the link preview will be shown below the message text. + AboveText bool `json:"show_above_text"` +} + +func embedMessages(params map[string]string, msgs []Editable) { + ids := make([]string, 0, len(msgs)) + + _, chatID := msgs[0].MessageSig() + for _, msg := range msgs { + msgID, _ := msg.MessageSig() + ids = append(ids, msgID) + } + + data, err := json.Marshal(ids) + if err != nil { + return + } + + params["message_ids"] = string(data) + params["chat_id"] = strconv.FormatInt(chatID, 10) +} diff --git a/vendor/gopkg.in/telebot.v3/poll.go b/vendor/gopkg.in/telebot.v3/poll.go index 8e2e5091db..48018e7b0b 100644 --- a/vendor/gopkg.in/telebot.v3/poll.go +++ b/vendor/gopkg.in/telebot.v3/poll.go @@ -49,6 +49,7 @@ type PollOption struct { type PollAnswer struct { PollID string `json:"poll_id"` Sender *User `json:"user"` + Chat *Chat `json:"voter_chat"` Options []int `json:"option_ids"` } diff --git a/vendor/gopkg.in/telebot.v3/poller.go b/vendor/gopkg.in/telebot.v3/poller.go index d45f2a5435..60b4211101 100644 --- a/vendor/gopkg.in/telebot.v3/poller.go +++ b/vendor/gopkg.in/telebot.v3/poller.go @@ -2,12 +2,32 @@ package telebot import "time" +var AllowedUpdates = []string{ + "message", + "edited_message", + "channel_post", + "edited_channel_post", + "message_reaction", + "message_reaction_count", + "inline_query", + "chosen_inline_result", + "callback_query", + "shipping_query", + "pre_checkout_query", + "poll", + "poll_answer", + "my_chat_member", + "chat_member", + "chat_join_request", + "chat_boost", + "removed_chat_boost", +} + // Poller is a provider of Updates. // // All pollers must implement Poll(), which accepts bot // pointer and subscription channel and start polling // synchronously straight away. -// type Poller interface { // Poll is supposed to take the bot object // subscription channel and start polling @@ -70,7 +90,6 @@ func (p *LongPoller) Poll(b *Bot, dest chan Update, stop chan struct{}) { // handling, banning or whatever. // // For heavy middleware, use increased capacity. -// type MiddlewarePoller struct { Capacity int // Default: 1 Poller Poller diff --git a/vendor/gopkg.in/telebot.v3/react.go b/vendor/gopkg.in/telebot.v3/react.go new file mode 100644 index 0000000000..7b01ca7f67 --- /dev/null +++ b/vendor/gopkg.in/telebot.v3/react.go @@ -0,0 +1,67 @@ +package telebot + +import ( + "encoding/json" +) + +// Reaction describes the type of reaction. +// Describes an instance of ReactionTypeCustomEmoji and ReactionTypeEmoji. +type Reaction struct { + // Type of the reaction, always “emoji” + Type string `json:"type"` + + // Reaction emoji. + Emoji string `json:"emoji,omitempty"` + + // Custom emoji identifier. + CustomEmoji string `json:"custom_emoji_id,omitempty"` +} + +// ReactionCount represents a reaction added to a message along +// with the number of times it was added. +type ReactionCount struct { + // Type of the reaction. + Type Reaction `json:"type"` + + // Number of times the reaction was added. + Count int `json:"total_count"` +} + +// ReactionOptions represents an object of reaction options. +type ReactionOptions struct { + // List of reaction types to set on the message. + Reactions []Reaction `json:"reaction"` + + // Pass True to set the reaction with a big animation. + Big bool `json:"is_big"` +} + +// React changes the chosen reactions on a message. Service messages can't be +// reacted to. Automatically forwarded messages from a channel to its discussion group have +// the same available reactions as messages in the channel. +func (b *Bot) React(to Recipient, msg Editable, opts ...ReactionOptions) error { + if to == nil { + return ErrBadRecipient + } + msgID, _ := msg.MessageSig() + + params := map[string]string{ + "chat_id": to.Recipient(), + "message_id": msgID, + } + + if len(opts) > 0 { + opt := opts[0] + + if len(opt.Reactions) > 0 { + data, _ := json.Marshal(opt.Reactions) + params["reaction"] = string(data) + } + if opt.Big { + params["is_big"] = "true" + } + } + + _, err := b.Raw("setMessageReaction", params) + return err +} diff --git a/vendor/gopkg.in/telebot.v3/sendable.go b/vendor/gopkg.in/telebot.v3/sendable.go index ecaae7fed1..4d7d9a5e00 100644 --- a/vendor/gopkg.in/telebot.v3/sendable.go +++ b/vendor/gopkg.in/telebot.v3/sendable.go @@ -115,6 +115,7 @@ func (d *Document) Send(b *Bot, to Recipient, opt *SendOptions) (*Message, error func (s *Sticker) Send(b *Bot, to Recipient, opt *SendOptions) (*Message, error) { params := map[string]string{ "chat_id": to.Recipient(), + "emoji": s.Emoji, } b.embedSendOptions(params, opt) @@ -401,7 +402,7 @@ func (g *Game) Send(b *Bot, to Recipient, opt *SendOptions) (*Message, error) { func thumbnailToFilemap(thumb *Photo) map[string]File { if thumb != nil { - return map[string]File{"thumb": thumb.File} + return map[string]File{"thumbnail": thumb.File} } return nil } diff --git a/vendor/gopkg.in/telebot.v3/sticker.go b/vendor/gopkg.in/telebot.v3/sticker.go new file mode 100644 index 0000000000..7fd060c030 --- /dev/null +++ b/vendor/gopkg.in/telebot.v3/sticker.go @@ -0,0 +1,306 @@ +package telebot + +import ( + "encoding/json" + "errors" + "fmt" + "strconv" +) + +type ( + StickerSetType = string + StickerSetFormat = string + MaskFeature = string +) + +const ( + StickerRegular StickerSetType = "regular" + StickerMask StickerSetType = "mask" + StickerCustomEmoji StickerSetType = "custom_emoji" +) + +const ( + StickerStatic StickerSetFormat = "static" + StickerAnimated StickerSetFormat = "animated" + StickerVideo StickerSetFormat = "video" +) + +const ( + MaskForehead MaskFeature = "forehead" + MaskEyes MaskFeature = "eyes" + MaskMouth MaskFeature = "mouth" + MaskChin MaskFeature = "chin" +) + +// StickerSet represents a sticker set. +type StickerSet struct { + Type StickerSetType `json:"sticker_type"` + Format StickerSetFormat `json:"sticker_format"` + Name string `json:"name"` + Title string `json:"title"` + Animated bool `json:"is_animated"` + Video bool `json:"is_video"` + Stickers []Sticker `json:"stickers"` + Thumbnail *Photo `json:"thumbnail"` + Emojis string `json:"emojis"` + ContainsMasks bool `json:"contains_masks"` // FIXME: can be removed + MaskPosition *MaskPosition `json:"mask_position"` + Repaint bool `json:"needs_repainting"` + + // Input is a field used in createNewStickerSet method to specify a list + // of pre-defined stickers of type InputSticker to add to the set. + Input []InputSticker +} + +type InputSticker struct { + File + Sticker string `json:"sticker"` + MaskPosition *MaskPosition `json:"mask_position"` + Emojis []string `json:"emoji_list"` + Keywords []string `json:"keywords"` +} + +// MaskPosition describes the position on faces where +// a mask should be placed by default. +type MaskPosition struct { + Feature MaskFeature `json:"point"` + XShift float32 `json:"x_shift"` + YShift float32 `json:"y_shift"` + Scale float32 `json:"scale"` +} + +// UploadSticker uploads a sticker file for later use. +func (b *Bot) UploadSticker(to Recipient, format StickerSetFormat, f File) (*File, error) { + params := map[string]string{ + "user_id": to.Recipient(), + "sticker_format": format, + } + + data, err := b.sendFiles("uploadStickerFile", map[string]File{"0": f}, params) + if err != nil { + return nil, err + } + + var resp struct { + Result File + } + if err := json.Unmarshal(data, &resp); err != nil { + return nil, wrapError(err) + } + return &resp.Result, nil +} + +// StickerSet returns a sticker set on success. +func (b *Bot) StickerSet(name string) (*StickerSet, error) { + data, err := b.Raw("getStickerSet", map[string]string{"name": name}) + if err != nil { + return nil, err + } + + var resp struct { + Result *StickerSet + } + if err := json.Unmarshal(data, &resp); err != nil { + return nil, wrapError(err) + } + return resp.Result, nil +} + +// CreateStickerSet creates a new sticker set. +func (b *Bot) CreateStickerSet(of Recipient, set *StickerSet) error { + files := make(map[string]File) + for i, s := range set.Input { + repr := s.File.process(strconv.Itoa(i), files) + if repr == "" { + return fmt.Errorf("telebot: sticker #%d does not exist", i+1) + } + set.Input[i].Sticker = repr + } + + data, _ := json.Marshal(set.Input) + + params := map[string]string{ + "user_id": of.Recipient(), + "name": set.Name, + "title": set.Title, + "sticker_format": set.Format, + "stickers": string(data), + } + if set.Type != "" { + params["sticker_type"] = set.Type + } + if set.Repaint { + params["needs_repainting"] = "true" + } + + _, err := b.sendFiles("createNewStickerSet", files, params) + return err +} + +// AddStickerToSet adds a new sticker to the existing sticker set. +func (b *Bot) AddStickerToSet(of Recipient, name string, sticker InputSticker) error { + files := make(map[string]File) + repr := sticker.File.process("0", files) + if repr == "" { + return errors.New("telebot: sticker does not exist") + } + + sticker.Sticker = repr + data, _ := json.Marshal(sticker) + + params := map[string]string{ + "user_id": of.Recipient(), + "name": name, + "sticker": string(data), + } + + _, err := b.sendFiles("addStickerToSet", files, params) + return err +} + +// SetStickerPosition moves a sticker in set to a specific position. +func (b *Bot) SetStickerPosition(sticker string, position int) error { + params := map[string]string{ + "sticker": sticker, + "position": strconv.Itoa(position), + } + + _, err := b.Raw("setStickerPositionInSet", params) + return err +} + +// DeleteSticker deletes a sticker from a set created by the bot. +func (b *Bot) DeleteSticker(sticker string) error { + _, err := b.Raw("deleteStickerFromSet", map[string]string{"sticker": sticker}) + return err + +} + +// SetStickerSetThumb sets a thumbnail of the sticker set. +// Animated thumbnails can be set for animated sticker sets only. +// +// Thumbnail must be a PNG image, up to 128 kilobytes in size +// and have width and height exactly 100px, or a TGS animation +// up to 32 kilobytes in size. +// +// Animated sticker set thumbnail can't be uploaded via HTTP URL. +func (b *Bot) SetStickerSetThumb(of Recipient, set *StickerSet) error { + if set.Thumbnail == nil { + return errors.New("telebot: thumbnail is required") + } + + files := make(map[string]File) + repr := set.Thumbnail.File.process("thumb", files) + if repr == "" { + return errors.New("telebot: thumbnail does not exist") + } + + params := map[string]string{ + "user_id": of.Recipient(), + "name": set.Name, + "thumbnail": repr, + } + + _, err := b.sendFiles("setStickerSetThumbnail", files, params) + return err +} + +// SetStickerSetTitle sets the title of a created sticker set. +func (b *Bot) SetStickerSetTitle(s StickerSet) error { + params := map[string]string{ + "name": s.Name, + "title": s.Title, + } + + _, err := b.Raw("setStickerSetTitle", params) + return err +} + +// DeleteStickerSet deletes a sticker set that was created by the bot. +func (b *Bot) DeleteStickerSet(name string) error { + params := map[string]string{"name": name} + + _, err := b.Raw("deleteStickerSet", params) + return err +} + +// SetStickerEmojis changes the list of emoji assigned to a regular or custom emoji sticker. +func (b *Bot) SetStickerEmojis(sticker string, emojis []string) error { + data, err := json.Marshal(emojis) + if err != nil { + return err + } + + params := map[string]string{ + "sticker": sticker, + "emoji_list": string(data), + } + + _, err = b.Raw("setStickerEmojiList", params) + return err +} + +// SetStickerKeywords changes search keywords assigned to a regular or custom emoji sticker. +func (b *Bot) SetStickerKeywords(sticker string, keywords []string) error { + mk, err := json.Marshal(keywords) + if err != nil { + return err + } + + params := map[string]string{ + "sticker": sticker, + "keywords": string(mk), + } + + _, err = b.Raw("setStickerKeywords", params) + return err +} + +// SetStickerMaskPosition changes the mask position of a mask sticker. +func (b *Bot) SetStickerMaskPosition(sticker string, mask MaskPosition) error { + data, err := json.Marshal(mask) + if err != nil { + return err + } + + params := map[string]string{ + "sticker": sticker, + "mask_position": string(data), + } + + _, err = b.Raw("setStickerMaskPosition", params) + return err +} + +// CustomEmojiStickers returns the information about custom emoji stickers by their ids. +func (b *Bot) CustomEmojiStickers(ids []string) ([]Sticker, error) { + data, _ := json.Marshal(ids) + + params := map[string]string{ + "custom_emoji_ids": string(data), + } + + data, err := b.Raw("getCustomEmojiStickers", params) + if err != nil { + return nil, err + } + + var resp struct { + Result []Sticker + } + if err := json.Unmarshal(data, &resp); err != nil { + return nil, wrapError(err) + } + return resp.Result, nil +} + +// SetCustomEmojiStickerSetThumb sets the thumbnail of a custom emoji sticker set. +func (b *Bot) SetCustomEmojiStickerSetThumb(name, id string) error { + params := map[string]string{ + "name": name, + "custom_emoji_id": id, + } + + _, err := b.Raw("setCustomEmojiStickerSetThumbnail", params) + return err +} diff --git a/vendor/gopkg.in/telebot.v3/stickers.go b/vendor/gopkg.in/telebot.v3/stickers.go deleted file mode 100644 index 3e0a6264b0..0000000000 --- a/vendor/gopkg.in/telebot.v3/stickers.go +++ /dev/null @@ -1,212 +0,0 @@ -package telebot - -import ( - "encoding/json" - "strconv" -) - -type StickerSetType = string - -const ( - StickerRegular = "regular" - StickerMask = "mask" - StickerCustomEmoji = "custom_emoji" -) - -// StickerSet represents a sticker set. -type StickerSet struct { - Type StickerSetType `json:"sticker_type"` - Name string `json:"name"` - Title string `json:"title"` - Animated bool `json:"is_animated"` - Video bool `json:"is_video"` - Stickers []Sticker `json:"stickers"` - Thumbnail *Photo `json:"thumb"` - PNG *File `json:"png_sticker"` - TGS *File `json:"tgs_sticker"` - WebM *File `json:"webm_sticker"` - Emojis string `json:"emojis"` - ContainsMasks bool `json:"contains_masks"` // FIXME: can be removed - MaskPosition *MaskPosition `json:"mask_position"` -} - -// MaskPosition describes the position on faces where -// a mask should be placed by default. -type MaskPosition struct { - Feature MaskFeature `json:"point"` - XShift float32 `json:"x_shift"` - YShift float32 `json:"y_shift"` - Scale float32 `json:"scale"` -} - -// MaskFeature defines sticker mask position. -type MaskFeature string - -const ( - FeatureForehead MaskFeature = "forehead" - FeatureEyes MaskFeature = "eyes" - FeatureMouth MaskFeature = "mouth" - FeatureChin MaskFeature = "chin" -) - -// UploadSticker uploads a PNG file with a sticker for later use. -func (b *Bot) UploadSticker(to Recipient, png *File) (*File, error) { - files := map[string]File{ - "png_sticker": *png, - } - params := map[string]string{ - "user_id": to.Recipient(), - } - - data, err := b.sendFiles("uploadStickerFile", files, params) - if err != nil { - return nil, err - } - - var resp struct { - Result File - } - if err := json.Unmarshal(data, &resp); err != nil { - return nil, wrapError(err) - } - return &resp.Result, nil -} - -// StickerSet returns a sticker set on success. -func (b *Bot) StickerSet(name string) (*StickerSet, error) { - data, err := b.Raw("getStickerSet", map[string]string{"name": name}) - if err != nil { - return nil, err - } - - var resp struct { - Result *StickerSet - } - if err := json.Unmarshal(data, &resp); err != nil { - return nil, wrapError(err) - } - return resp.Result, nil -} - -// CreateStickerSet creates a new sticker set. -func (b *Bot) CreateStickerSet(to Recipient, s StickerSet) error { - files := make(map[string]File) - if s.PNG != nil { - files["png_sticker"] = *s.PNG - } - if s.TGS != nil { - files["tgs_sticker"] = *s.TGS - } - if s.WebM != nil { - files["webm_sticker"] = *s.WebM - } - - params := map[string]string{ - "user_id": to.Recipient(), - "sticker_type": s.Type, - "name": s.Name, - "title": s.Title, - "emojis": s.Emojis, - "contains_masks": strconv.FormatBool(s.ContainsMasks), - } - - if s.MaskPosition != nil { - data, _ := json.Marshal(&s.MaskPosition) - params["mask_position"] = string(data) - } - - _, err := b.sendFiles("createNewStickerSet", files, params) - return err -} - -// AddSticker adds a new sticker to the existing sticker set. -func (b *Bot) AddSticker(to Recipient, s StickerSet) error { - files := make(map[string]File) - if s.PNG != nil { - files["png_sticker"] = *s.PNG - } else if s.TGS != nil { - files["tgs_sticker"] = *s.TGS - } else if s.WebM != nil { - files["webm_sticker"] = *s.WebM - } - - params := map[string]string{ - "user_id": to.Recipient(), - "name": s.Name, - "emojis": s.Emojis, - } - - if s.MaskPosition != nil { - data, _ := json.Marshal(&s.MaskPosition) - params["mask_position"] = string(data) - } - - _, err := b.sendFiles("addStickerToSet", files, params) - return err -} - -// SetStickerPosition moves a sticker in set to a specific position. -func (b *Bot) SetStickerPosition(sticker string, position int) error { - params := map[string]string{ - "sticker": sticker, - "position": strconv.Itoa(position), - } - - _, err := b.Raw("setStickerPositionInSet", params) - return err -} - -// DeleteSticker deletes a sticker from a set created by the bot. -func (b *Bot) DeleteSticker(sticker string) error { - _, err := b.Raw("deleteStickerFromSet", map[string]string{"sticker": sticker}) - return err - -} - -// SetStickerSetThumb sets a thumbnail of the sticker set. -// Animated thumbnails can be set for animated sticker sets only. -// -// Thumbnail must be a PNG image, up to 128 kilobytes in size -// and have width and height exactly 100px, or a TGS animation -// up to 32 kilobytes in size. -// -// Animated sticker set thumbnail can't be uploaded via HTTP URL. -// -func (b *Bot) SetStickerSetThumb(to Recipient, s StickerSet) error { - files := make(map[string]File) - if s.PNG != nil { - files["thumb"] = *s.PNG - } else if s.TGS != nil { - files["thumb"] = *s.TGS - } - - params := map[string]string{ - "name": s.Name, - "user_id": to.Recipient(), - } - - _, err := b.sendFiles("setStickerSetThumb", files, params) - return err -} - -// CustomEmojiStickers returns the information about custom emoji stickers by their ids. -func (b *Bot) CustomEmojiStickers(ids []string) ([]Sticker, error) { - data, _ := json.Marshal(ids) - - params := map[string]string{ - "custom_emoji_ids": string(data), - } - - data, err := b.Raw("getCustomEmojiStickers", params) - if err != nil { - return nil, err - } - - var resp struct { - Result []Sticker - } - if err := json.Unmarshal(data, &resp); err != nil { - return nil, wrapError(err) - } - return resp.Result, nil -} diff --git a/vendor/gopkg.in/telebot.v3/telebot.go b/vendor/gopkg.in/telebot.v3/telebot.go index b8271e9a4d..4705587590 100644 --- a/vendor/gopkg.in/telebot.v3/telebot.go +++ b/vendor/gopkg.in/telebot.v3/telebot.go @@ -109,6 +109,9 @@ const ( OnVideoChatEnded = "\avideo_chat_ended" OnVideoChatParticipants = "\avideo_chat_participants_invited" OnVideoChatScheduled = "\avideo_chat_scheduled" + + OnBoost = "\aboost_updated" + OnBoostRemoved = "\aboost_removed" ) // ChatAction is a client-side status indicating bot activity. diff --git a/vendor/gopkg.in/telebot.v3/topic.go b/vendor/gopkg.in/telebot.v3/topic.go index d81fe9a943..4f6935dff8 100644 --- a/vendor/gopkg.in/telebot.v3/topic.go +++ b/vendor/gopkg.in/telebot.v3/topic.go @@ -6,10 +6,10 @@ import ( ) type Topic struct { - Name string `json:"name"` - IconColor int `json:"icon_color"` - IconCustomEmojiID string `json:"icon_custom_emoji_id"` - ThreadID int `json:"message_thread_id"` + Name string `json:"name"` + IconColor int `json:"icon_color"` + IconCustomEmoji string `json:"icon_custom_emoji_id"` + ThreadID int `json:"message_thread_id"` } // CreateTopic creates a topic in a forum supergroup chat. @@ -22,8 +22,8 @@ func (b *Bot) CreateTopic(chat *Chat, topic *Topic) (*Topic, error) { if topic.IconColor != 0 { params["icon_color"] = strconv.Itoa(topic.IconColor) } - if topic.IconCustomEmojiID != "" { - params["icon_custom_emoji_id"] = topic.IconCustomEmojiID + if topic.IconCustomEmoji != "" { + params["icon_custom_emoji_id"] = topic.IconCustomEmoji } data, err := b.Raw("createForumTopic", params) @@ -50,8 +50,8 @@ func (b *Bot) EditTopic(chat *Chat, topic *Topic) error { if topic.Name != "" { params["name"] = topic.Name } - if topic.IconCustomEmojiID != "" { - params["icon_custom_emoji_id"] = topic.IconCustomEmojiID + if topic.IconCustomEmoji != "" { + params["icon_custom_emoji_id"] = topic.IconCustomEmoji } _, err := b.Raw("editForumTopic", params) @@ -170,3 +170,15 @@ func (b *Bot) UnhideGeneralTopic(chat *Chat) error { _, err := b.Raw("unhideGeneralForumTopic", params) return err } + +// UnpinAllGeneralTopicMessages clears the list of pinned messages in a General forum topic. +// The bot must be an administrator in the chat for this to work and must have the +// can_pin_messages administrator right in the supergroup. +func (b *Bot) UnpinAllGeneralTopicMessages(chat *Chat) error { + params := map[string]interface{}{ + "chat_id": chat.Recipient(), + } + + _, err := b.Raw("unpinAllGeneralForumTopicMessages", params) + return err +} diff --git a/vendor/gopkg.in/telebot.v3/update.go b/vendor/gopkg.in/telebot.v3/update.go index 12a065ab22..63396dd4ba 100644 --- a/vendor/gopkg.in/telebot.v3/update.go +++ b/vendor/gopkg.in/telebot.v3/update.go @@ -6,20 +6,24 @@ import "strings" type Update struct { ID int `json:"update_id"` - Message *Message `json:"message,omitempty"` - EditedMessage *Message `json:"edited_message,omitempty"` - ChannelPost *Message `json:"channel_post,omitempty"` - EditedChannelPost *Message `json:"edited_channel_post,omitempty"` - Callback *Callback `json:"callback_query,omitempty"` - Query *Query `json:"inline_query,omitempty"` - InlineResult *InlineResult `json:"chosen_inline_result,omitempty"` - ShippingQuery *ShippingQuery `json:"shipping_query,omitempty"` - PreCheckoutQuery *PreCheckoutQuery `json:"pre_checkout_query,omitempty"` - Poll *Poll `json:"poll,omitempty"` - PollAnswer *PollAnswer `json:"poll_answer,omitempty"` - MyChatMember *ChatMemberUpdate `json:"my_chat_member,omitempty"` - ChatMember *ChatMemberUpdate `json:"chat_member,omitempty"` - ChatJoinRequest *ChatJoinRequest `json:"chat_join_request,omitempty"` + Message *Message `json:"message,omitempty"` + EditedMessage *Message `json:"edited_message,omitempty"` + ChannelPost *Message `json:"channel_post,omitempty"` + EditedChannelPost *Message `json:"edited_channel_post,omitempty"` + MessageReaction *MessageReaction `json:"message_reaction"` + MessageReactionCount *MessageReactionCount `json:"message_reaction_count"` + Callback *Callback `json:"callback_query,omitempty"` + Query *Query `json:"inline_query,omitempty"` + InlineResult *InlineResult `json:"chosen_inline_result,omitempty"` + ShippingQuery *ShippingQuery `json:"shipping_query,omitempty"` + PreCheckoutQuery *PreCheckoutQuery `json:"pre_checkout_query,omitempty"` + Poll *Poll `json:"poll,omitempty"` + PollAnswer *PollAnswer `json:"poll_answer,omitempty"` + MyChatMember *ChatMemberUpdate `json:"my_chat_member,omitempty"` + ChatMember *ChatMemberUpdate `json:"chat_member,omitempty"` + ChatJoinRequest *ChatJoinRequest `json:"chat_join_request,omitempty"` + Boost *BoostUpdated `json:"chat_boost"` + BoostRemoved *BoostRemoved `json:"removed_chat_boost"` } // ProcessUpdate processes a single incoming update. @@ -308,6 +312,16 @@ func (b *Bot) ProcessUpdate(u Update) { b.handle(OnChatJoinRequest, c) return } + + if u.Boost != nil { + b.handle(OnBoost, c) + return + } + + if u.BoostRemoved != nil { + b.handle(OnBoostRemoved, c) + return + } } func (b *Bot) handle(end string, c Context) bool { diff --git a/vendor/gopkg.in/telebot.v3/web_app.go b/vendor/gopkg.in/telebot.v3/web_app.go index e5c9070f2c..b0d3956176 100644 --- a/vendor/gopkg.in/telebot.v3/web_app.go +++ b/vendor/gopkg.in/telebot.v3/web_app.go @@ -20,5 +20,7 @@ type WebAppData struct { // WebAppAccessAllowed represents a service message about a user allowing // a bot to write messages after adding the bot to the attachment menu or launching a Web App from a link. type WriteAccessAllowed struct { - WebAppName string `json:"web_app_name,omitempty"` + WebAppName string `json:"web_app_name,omitempty"` + FromRequest bool `json:"from_request,omitempty"` + FromAttachmentMenu bool `json:"from_attachment_menu,omitempty"` } diff --git a/vendor/modules.txt b/vendor/modules.txt index e5ece82bb4..6619fc8e04 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -119,7 +119,7 @@ github.com/VictoriaMetrics/fastcache ## explicit github.com/alecthomas/template github.com/alecthomas/template/parse -# github.com/alecthomas/units v0.0.0-20240626203959-61d1e3462e30 +# github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b ## explicit; go 1.15 github.com/alecthomas/units # github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 @@ -277,7 +277,6 @@ github.com/aws/smithy-go/transport/http/internal/io github.com/bboreham/go-loser # github.com/benbjohnson/clock v1.3.5 ## explicit; go 1.15 -github.com/benbjohnson/clock # github.com/beorn7/perks v1.0.1 ## explicit; go 1.11 github.com/beorn7/perks/quantile @@ -296,6 +295,9 @@ github.com/cenkalti/backoff/v4 # github.com/cespare/xxhash/v2 v2.3.0 ## explicit; go 1.11 github.com/cespare/xxhash/v2 +# github.com/coder/quartz v0.1.2 +## explicit; go 1.21.8 +github.com/coder/quartz # github.com/coreos/go-semver v0.3.0 ## explicit github.com/coreos/go-semver/semver @@ -595,8 +597,8 @@ github.com/hashicorp/memberlist # github.com/hashicorp/serf v0.10.1 ## explicit; go 1.12 github.com/hashicorp/serf/coordinate -# github.com/jessevdk/go-flags v1.5.0 -## explicit; go 1.15 +# github.com/jessevdk/go-flags v1.6.1 +## explicit; go 1.20 github.com/jessevdk/go-flags # github.com/jmespath/go-jmespath v0.4.0 ## explicit; go 1.14 @@ -749,8 +751,8 @@ github.com/pmezard/go-difflib/difflib # github.com/prometheus-community/prom-label-proxy v0.8.1-0.20240127162815-c1195f9aabc0 ## explicit; go 1.20 github.com/prometheus-community/prom-label-proxy/injectproxy -# github.com/prometheus/alertmanager v0.27.0 -## explicit; go 1.21 +# github.com/prometheus/alertmanager v0.28.0 +## explicit; go 1.22.0 github.com/prometheus/alertmanager/api github.com/prometheus/alertmanager/api/metrics github.com/prometheus/alertmanager/api/v2 @@ -775,17 +777,20 @@ github.com/prometheus/alertmanager/config github.com/prometheus/alertmanager/dispatch github.com/prometheus/alertmanager/featurecontrol github.com/prometheus/alertmanager/inhibit -github.com/prometheus/alertmanager/matchers/compat -github.com/prometheus/alertmanager/matchers/parse +github.com/prometheus/alertmanager/matcher/compat +github.com/prometheus/alertmanager/matcher/parse github.com/prometheus/alertmanager/nflog github.com/prometheus/alertmanager/nflog/nflogpb github.com/prometheus/alertmanager/notify github.com/prometheus/alertmanager/notify/discord github.com/prometheus/alertmanager/notify/email +github.com/prometheus/alertmanager/notify/jira github.com/prometheus/alertmanager/notify/msteams +github.com/prometheus/alertmanager/notify/msteamsv2 github.com/prometheus/alertmanager/notify/opsgenie github.com/prometheus/alertmanager/notify/pagerduty github.com/prometheus/alertmanager/notify/pushover +github.com/prometheus/alertmanager/notify/rocketchat github.com/prometheus/alertmanager/notify/slack github.com/prometheus/alertmanager/notify/sns github.com/prometheus/alertmanager/notify/telegram @@ -828,12 +833,13 @@ github.com/prometheus/common/config github.com/prometheus/common/expfmt github.com/prometheus/common/helpers/templates github.com/prometheus/common/model +github.com/prometheus/common/promslog github.com/prometheus/common/route github.com/prometheus/common/version # github.com/prometheus/common/sigv4 v0.1.0 ## explicit; go 1.15 github.com/prometheus/common/sigv4 -# github.com/prometheus/exporter-toolkit v0.12.0 +# github.com/prometheus/exporter-toolkit v0.13.2 ## explicit; go 1.22 github.com/prometheus/exporter-toolkit/web # github.com/prometheus/procfs v0.15.1 @@ -904,7 +910,7 @@ github.com/prometheus/prometheus/web/api/v1 github.com/redis/rueidis github.com/redis/rueidis/internal/cmds github.com/redis/rueidis/internal/util -# github.com/rs/cors v1.11.0 +# github.com/rs/cors v1.11.1 ## explicit; go 1.13 github.com/rs/cors github.com/rs/cors/internal @@ -928,8 +934,8 @@ github.com/sercand/kuberesolver/v5 github.com/shurcooL/httpfs/filter github.com/shurcooL/httpfs/union github.com/shurcooL/httpfs/vfsutil -# github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546 -## explicit +# github.com/shurcooL/vfsgen v0.0.0-20230704071429-0000e147ea92 +## explicit; go 1.19 github.com/shurcooL/vfsgen # github.com/sirupsen/logrus v1.9.3 ## explicit; go 1.13 @@ -1048,6 +1054,10 @@ github.com/thanos-io/thanos/pkg/tracing/util/metautils # github.com/tjhop/slog-gokit v0.1.2 ## explicit; go 1.21 github.com/tjhop/slog-gokit +# github.com/trivago/tgo v1.0.7 +## explicit +github.com/trivago/tgo/tcontainer +github.com/trivago/tgo/treflect # github.com/uber/jaeger-client-go v2.30.0+incompatible ## explicit github.com/uber/jaeger-client-go @@ -1307,7 +1317,7 @@ golang.org/x/crypto/pkcs12/internal/rc2 ## explicit; go 1.20 golang.org/x/exp/constraints golang.org/x/exp/slices -# golang.org/x/mod v0.21.0 +# golang.org/x/mod v0.22.0 ## explicit; go 1.22.0 golang.org/x/mod/semver # golang.org/x/net v0.33.0 @@ -1368,11 +1378,12 @@ golang.org/x/text/unicode/norm # golang.org/x/time v0.8.0 ## explicit; go 1.18 golang.org/x/time/rate -# golang.org/x/tools v0.24.0 -## explicit; go 1.19 +# golang.org/x/tools v0.28.0 +## explicit; go 1.22.0 golang.org/x/tools/go/gcexportdata golang.org/x/tools/go/packages golang.org/x/tools/go/types/objectpath +golang.org/x/tools/go/types/typeutil golang.org/x/tools/internal/aliases golang.org/x/tools/internal/event golang.org/x/tools/internal/event/core @@ -1383,7 +1394,7 @@ golang.org/x/tools/internal/gocommand golang.org/x/tools/internal/packagesinternal golang.org/x/tools/internal/pkgbits golang.org/x/tools/internal/stdlib -golang.org/x/tools/internal/tokeninternal +golang.org/x/tools/internal/typeparams golang.org/x/tools/internal/typesinternal golang.org/x/tools/internal/versions # gonum.org/v1/gonum v0.15.0 @@ -1540,8 +1551,8 @@ google.golang.org/protobuf/types/known/wrapperspb # gopkg.in/alecthomas/kingpin.v2 v2.2.6 => github.com/alecthomas/kingpin v1.3.8-0.20210301060133-17f40c25f497 ## explicit; go 1.13 gopkg.in/alecthomas/kingpin.v2 -# gopkg.in/telebot.v3 v3.2.1 -## explicit; go 1.13 +# gopkg.in/telebot.v3 v3.3.8 +## explicit; go 1.16 gopkg.in/telebot.v3 # gopkg.in/yaml.v2 v2.4.0 ## explicit; go 1.15 From 650d43cdff2667263a09b4c0e69f868faa52e1c9 Mon Sep 17 00:00:00 2001 From: SungJin1212 Date: Sat, 22 Feb 2025 10:01:09 +0900 Subject: [PATCH 2/2] Fix marker to rely on iterface Signed-off-by: SungJin1212 --- pkg/alertmanager/alertmanager.go | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/pkg/alertmanager/alertmanager.go b/pkg/alertmanager/alertmanager.go index 4e31545ce6..51d37bed62 100644 --- a/pkg/alertmanager/alertmanager.go +++ b/pkg/alertmanager/alertmanager.go @@ -103,7 +103,8 @@ type Alertmanager struct { persister *statePersister nflog *nflog.Log silences *silence.Silences - marker *types.MemMarker + alertMarker types.AlertMarker + groupMarker types.GroupMarker alerts *mem.Alerts dispatcher *dispatch.Dispatcher inhibitor *inhibit.Inhibitor @@ -222,7 +223,9 @@ func New(cfg *Config, reg *prometheus.Registry) (*Alertmanager, error) { am.nflog.Maintenance(maintenancePeriod, notificationFile, am.stop, nil) am.wg.Done() }() - am.marker = types.NewMarker(am.registry) + memMarker := types.NewMarker(reg) + am.alertMarker = memMarker + am.groupMarker = memMarker silencesFile := filepath.Join(cfg.TenantDataDir, silencesSnapshot) am.silences, err = silence.New(silence.Options{ @@ -268,7 +271,7 @@ func New(cfg *Config, reg *prometheus.Registry) (*Alertmanager, error) { if am.cfg.Limits != nil { callback = newAlertsLimiter(am.cfg.UserID, am.cfg.Limits, reg) } - am.alerts, err = mem.NewAlerts(context.Background(), am.marker, am.cfg.GCInterval, callback, util_log.GoKitLogToSlog(am.logger), am.registry) + am.alerts, err = mem.NewAlerts(context.Background(), am.alertMarker, am.cfg.GCInterval, callback, util_log.GoKitLogToSlog(am.logger), am.registry) if err != nil { return nil, fmt.Errorf("failed to create alerts: %v", err) } @@ -276,8 +279,8 @@ func New(cfg *Config, reg *prometheus.Registry) (*Alertmanager, error) { am.api, err = api.New(api.Options{ Alerts: am.alerts, Silences: am.silences, - AlertStatusFunc: am.marker.Status, - GroupMutedFunc: am.marker.Muted, + AlertStatusFunc: am.alertMarker.Status, + GroupMutedFunc: am.groupMarker.Muted, // Cortex should not expose cluster information back to its tenants. Peer: &NilPeer{}, Registry: am.registry, @@ -360,7 +363,7 @@ func (am *Alertmanager) ApplyConfig(userID string, conf *config.Config, rawCfg s am.dispatcher.Stop() } - am.inhibitor = inhibit.NewInhibitor(am.alerts, conf.InhibitRules, am.marker, util_log.GoKitLogToSlog(log.With(am.logger, "component", "inhibitor"))) + am.inhibitor = inhibit.NewInhibitor(am.alerts, conf.InhibitRules, am.alertMarker, util_log.GoKitLogToSlog(log.With(am.logger, "component", "inhibitor"))) waitFunc := clusterWait(am.state.Position, am.cfg.PeerTimeout) @@ -403,9 +406,9 @@ func (am *Alertmanager) ApplyConfig(userID string, conf *config.Config, rawCfg s integrationsMap, waitFunc, am.inhibitor, - silence.NewSilencer(am.silences, am.marker, util_log.GoKitLogToSlog(am.logger)), + silence.NewSilencer(am.silences, am.alertMarker, util_log.GoKitLogToSlog(am.logger)), timeinterval.NewIntervener(timeIntervals), - am.marker, + am.groupMarker, am.nflog, am.state, ) @@ -414,7 +417,7 @@ func (am *Alertmanager) ApplyConfig(userID string, conf *config.Config, rawCfg s am.alerts, dispatch.NewRoute(conf.Route, nil), pipeline, - am.marker, + am.groupMarker, timeoutFunc, &dispatcherLimits{tenant: am.cfg.UserID, limits: am.cfg.Limits}, util_log.GoKitLogToSlog(log.With(am.logger, "component", "dispatcher")),